<nav class="gov-navigation" aria-label="Hlavní navigace" id="main-navigation">
<ul>
<li class="gov-navigation--has-megamenu">
<gov-button type="base" color="primary" size="l" aria-expanded="false" aria-controls="subnav0" aria-label="Úřad">
Úřad
<gov-icon slot="icon-end" type="templates" name="chevron-down" size="l"></gov-icon>
</gov-button>
<ul class="gov-mega-menu" id="megamenu0" hidden aria-hidden="true">
<li class="gov-mega-menu__heading">
<a href="#">
<span>
<gov-icon type="templates" name="briefcase" size="m"></gov-icon>
Heading item
</span>
</a>
<ul>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li class="gov-mega-menu__more"><a href="#">+ Další</a></li>
</ul>
</li>
<li class="gov-mega-menu__heading">
<a href="#">
<span>
<gov-icon type="templates" name="briefcase" size="m"></gov-icon>
Heading item
</span>
</a>
<ul>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li class="gov-mega-menu__more"><a href="#">+ Další</a></li>
</ul>
</li>
<li class="gov-mega-menu__heading">
<a href="#">
<span>
<gov-icon type="templates" name="briefcase" size="m"></gov-icon>
Heading item
</span>
</a>
<ul>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
</ul>
</li>
<li class="gov-mega-menu__heading">
<a href="#">
<span>
<gov-icon type="templates" name="briefcase" size="m"></gov-icon>
Heading item
</span>
</a>
<ul>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li class="gov-mega-menu__more"><a href="#">+ Další</a></li>
</ul>
</li>
<li class="gov-mega-menu__heading">
<a href="#">
<span>
<gov-icon type="templates" name="briefcase" size="m"></gov-icon>
Heading item
</span>
</a>
<ul>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li class="gov-mega-menu__more"><a href="#">+ Další</a></li>
</ul>
</li>
<li class="gov-mega-menu__heading">
<a href="#">
<span>
<gov-icon type="templates" name="briefcase" size="m"></gov-icon>
Heading item
</span>
</a>
<ul>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
</ul>
</li>
<li class="gov-mega-menu__heading">
<a href="#">
<span>
<gov-icon type="templates" name="briefcase" size="m"></gov-icon>
Heading item
</span>
</a>
<ul>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
</ul>
</li>
<li class="gov-mega-menu__heading">
<a href="#">
<span>
<gov-icon type="templates" name="briefcase" size="m"></gov-icon>
Heading item
</span>
</a>
<ul>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Menu item</a>
</li>
<li>
<a href="#">Menu item</a>
</li>
<li>
<a href="#">Menu item</a>
</li>
<li>
<gov-button type="base" color="primary" size="l" aria-expanded="false" aria-controls="subnav4" aria-label="Tisk a média">
Tisk a média
<gov-icon slot="icon-end" type="templates" name="chevron-down" size="l"></gov-icon>
</gov-button>
<ul class="gov-subnavigation" id="subnav4" hidden aria-hidden="true">
<li><a href="#">Menu item</a></li>
<li><a href="#">Menu item</a></li>
</ul>
</li>
<li>
<a href="#">Kontakty</a>
</li>
<li class="gov-mobile-only">
<gov-button color="primary" size="l" type="base" aria-label="Click to change language to English">
English
</gov-button>
</li>
<li class="gov-navigation__item gov-mobile-only">
<gov-theme-switch size="m" display-label="true"></gov-theme-switch>
</li>
</ul>
</nav>
<nav class="gov-navigation"{{#if aria-label}} aria-label="{{{ aria-label }}}"{{/if}} id="main-navigation">
<ul>
{{#each navigation }}
<li{{#if megamenu}} class="gov-navigation--has-megamenu"{{/if}}>
{{#if href}}
<a href="{{ href }}">{{{ text }}}</a>
{{else}}
<gov-button type="base" color="primary" size="l"
aria-expanded="false" aria-controls="subnav{{@index}}" aria-label="{{{ label/otevrit }}}{{{ button }}}">
{{{ button }}}
<gov-icon slot="icon-end" type="templates" name="chevron-down" size="l"></gov-icon>
</gov-button>
{{/if}}
{{#if subnav}}
<ul class="gov-subnavigation" id="subnav{{@index}}" hidden aria-hidden="true">
{{#each subnav }}
<li><a href="#">{{{ name }}}</a></li>
{{/each}}
</ul>
{{/if}}
{{#if megamenu}}
<ul class="gov-mega-menu" id="megamenu{{@index}}" hidden aria-hidden="true">
{{#each megamenu }}
<li class="gov-mega-menu__heading">
<a href="#">
<span>
<gov-icon type="templates" name="briefcase" size="m"></gov-icon>
{{{ heading }}}
</span>
</a>
<ul>
{{#each link }}
<li><a href="#">{{{ name }}}</a></li>
{{/each}}
{{#if more }}
<li class="gov-mega-menu__more"><a href="#">{{ more }}</a></li>
{{/if}}
</ul>
</li>
{{/each}}
</ul>
{{/if}}
</li>
{{/each}}
<li class="gov-mobile-only">
{{ render '@button' lang }}
</li>
<li class="gov-navigation__item gov-mobile-only">
<gov-theme-switch size="m" display-label="true"></gov-theme-switch>
</li>
</ul>
</nav>
{
"aria-label": "Hlavní navigace",
"lang": {
"text": "English",
"color": "primary",
"type": "base",
"size": "l",
"aria-label": "Click to change language to English"
},
"label": {
"otevrit": "Otevřít menu"
},
"navigation": [
{
"button": "Úřad",
"megamenu": [
{
"heading": "Heading item",
"link": [
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
}
],
"more": "+ Další"
},
{
"heading": "Heading item",
"link": [
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
}
],
"more": "+ Další"
},
{
"heading": "Heading item",
"link": [
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
}
]
},
{
"heading": "Heading item",
"link": [
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
}
],
"more": "+ Další"
},
{
"heading": "Heading item",
"link": [
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
}
],
"more": "+ Další"
},
{
"heading": "Heading item",
"link": [
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
}
]
},
{
"heading": "Heading item",
"link": [
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
}
]
},
{
"heading": "Heading item",
"link": [
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
},
{
"name": "Menu item"
}
]
}
]
},
{
"text": "Menu item",
"href": "#"
},
{
"text": "Menu item",
"href": "#"
},
{
"text": "Menu item",
"href": "#"
},
{
"button": "Tisk a média",
"subnav": [
{
"name": "Menu item"
},
{
"name": "Menu item"
}
]
},
{
"text": "Kontakty",
"href": "#"
}
]
}
@import "../../../styles/mixins";
$class: ".gov-navigation";
$subnavigation: ".gov-subnavigation";
$megamenu: ".gov-mega-menu";
$deferred-navigation: ".gov-deferred-navigation";
// MIXINS
@mixin list-li() {
li {
margin-bottom: 0;
a {
@include flex($align: center);
@include typography("body-s");
min-height: var(--height-2xl);
padding: var(--spacing-xs) 0 var(--spacing-xs) var(--spacing-xs);
color: var(--text-secondary);
text-decoration: none;
&:hover {
color: var(--text-primary);
text-decoration: underline;
}
}
}
}
@mixin a-like-button() {
@include flex($align: center);
height: var(--height-component-l);
padding: 0 var(--spacing-m-nudge);
text-decoration: none;
@include typography("body-l");
font-weight: 500;
color: var(--text-primary-color);
white-space: nowrap;
border-radius: var(--corner-radius-s);
&:hover {
background-color: var(--button-outlined-primary-hover);
color: var(--text-primary-color);
text-decoration: none;
}
&:active {
background-color: var(--button-outlined-primary-active);
}
}
@mixin rounded-corners() {
background-color: var(--background-block-primary);
border-bottom-left-radius: var(--corner-radius-m);
border-bottom-right-radius: var(--corner-radius-m);
}
// MAIN NAVIGATION
#{$class} {
background-color: var(--component-nav-background);
ul {
margin: 0;
li {
margin: 0;
&:before {
content: none;
}
}
}
> ul {
@include flex($direction: column, $justify: center, $gap: var(--templates-margin-s));
margin: 0;
padding: 0;
}
> ul > li {
width: 100%;
}
> ul > li > a {
@include a-like-button();
}
> ul > li > gov-button,
> ul > li > .gov-button {
width: 100%;
.element {
@include flex($align: center, $justify: space-between, $gap: var(--spacing-m));
width: calc(100% + (var(--spacing-m-nudge) * 2));
font-weight: 500 !important;
}
}
&__item {
width: calc(100% - (var(--spacing-m-nudge) * 2));
height: var(--height-component-l);
padding: 0 var(--spacing-m-nudge);
}
&--has-megamenu {
position: static !important;
}
@include mq($from: md) {
background-color: transparent;
> ul {
@include flex($direction: row, $justify: space-between, $align: center, $gap: var(--spacing-m));
}
> ul > li {
position: relative;
display: flex;
width: auto;
&:last-child #{$deferred-navigation},
&:nth-last-child(3) #{$subnavigation} {
left: initial;
right: 0;
}
}
> ul > li > a {
justify-content: center;
width: 100%;
}
}
@include mq($until: md) {
gov-button, .gov-button {
width: 100%;
max-width: 100%;
.element {
width: calc(100% + (var(--spacing-m-nudge) * 2));
justify-content: space-between;
}
}
}
}
// SUB NAVIGATION
#{$subnavigation} {
@include flex($direction: column, $gap: var(--spacing-xs));
padding: var(--spacing-s) var(--spacing-m);
min-width: 280px;
background-color: var(--background-page);
@include list-li();
@include mq($from: md) {
position: absolute;
top: var(--height-component-l);
left: 0;
@include rounded-corners();
> li a {
min-height: auto;
}
}
}
// MEGA MENU
#{$megamenu} {
max-width: 100%;
width: 100%;
padding: var(--spacing-s) 0;
background-color: var(--background-page);
> li ul {
@include flex($direction: column, $gap: var(--spacing-xs));
padding: 0 var(--spacing-m);
@include list-li();
}
&__heading {
margin: 0;
span {
@include flex($align: center, $gap: var(--spacing-s-nudge));
padding: var(--spacing-s) var(--spacing-m);
@include typography("body-m");
font-weight: 700;
line-height: var(--height-line-m);
color: var(--text-primary-color);
}
a {
display: block;
text-decoration: none;
&:hover {
color: var(--interactive-timer-primary-solid);
text-decoration: underline;
}
}
}
li a {
min-height: auto;
}
&__more a {
color: var(--text-tertiary) !important;
}
@include mq($from: md) {
position: absolute;
top: var(--spacing-5xl);
left: 0;
max-width: 1240px;
width: 100vw;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--templates-margin-m) var(--templates-margin-l);
@include rounded-corners();
> li {
ul {
padding-left: var(--spacing-2xl);
padding-right: var(--spacing-m);
> li a {
min-height: auto;
}
}
}
}
}
// DEFERRED NAVIGATION
#{$deferred-navigation} {
@include mq($from: md) {
@include flex($direction: column, $gap: var(--spacing-xs));
padding: var(--spacing-s) var(--spacing-m);
min-width: 280px;
background-color: var(--background-page);
position: absolute;
top: var(--height-component-l);
left: 0;
@include rounded-corners();
@include list-li();
> li a {
min-height: auto;
@include a-like-button();
}
> li ul {
display: none;
}
gov-button, .gov-button {
width: 100%;
max-width: 100%;
.element {
width: calc(100% + (var(--spacing-m-nudge) * 2));
justify-content: space-between;
font-weight: 500;
}
gov-icon, .gov-icon {
display: none;
}
}
}
}
import {
blockRapidClicks,
createID,
debounce,
delay,
firstFocusableElement,
getDocument,
getWindow,
toBool
} from "@gov-design-system-ce/utils";
const TABLET_SIZE = 768;
class MainNavigation {
rootElement: Element;
constructor(rootElement: Element) {
this.rootElement = rootElement;
this.verifyAndFixAccesbillity(this.rootUlElement);
this.calculateSizeOfElements();
this.registerListeners();
this.accessibilityMobileNavigation().catch();
if (getWindow().innerWidth >= TABLET_SIZE) {
this.moveDeferredItems().catch()
this.controlHeaderNavigation();
} else {
this.controlHeaderNavigation(false);
}
}
private registerListeners() {
this.registerClickTriggers();
getWindow().addEventListener('resize', debounce(() => {
this.resetDeferredMenu();
if (getWindow().innerWidth >= TABLET_SIZE) {
this.controlHeaderNavigation();
this.moveDeferredItems().catch();
} else {
this.controlHeaderNavigation(false);
}
}, 50))
}
private async moveDeferredItems() {
const gap = 16;
let size = this.rootElement.getBoundingClientRect().width - 120;
this.firstLevelLiElements.forEach((liElement) => {
const itemSize = liElement.getBoundingClientRect().width + gap;
if ((size - itemSize) >= 0) {
size -= itemSize;
} else {
size -= itemSize;
this.createDeferredContainer();
this.deferredItemsContainer.removeAttribute('hidden');
this.deferredItemsContainer.setAttribute('aria-hidden', 'false');
this.temporaryListForDeferredItems.appendChild(liElement);
}
})
}
private get temporaryListForDeferredItems() {
return this.deferredItemsContainer.querySelector('ul');
}
private createDeferredContainer() {
if (this.deferredItemsContainer) {
return;
}
const controlId = createID('MenuDeferred');
const ul = getDocument().createElement('ul');
const li = getDocument().createElement('li');
const trigger = getDocument().createElement('gov-button');
const icon = getDocument().createElement('gov-icon');
const deferredName = this.rootElement.getAttribute('data-deferred-item-name');
li.classList.add('js-deferred-items-container');
trigger.setAttribute('type', 'base');
trigger.setAttribute('color', 'primary');
trigger.setAttribute('size', 'l');
trigger.setAttribute('aria-expanded', 'false');
trigger.setAttribute('aria-controls', controlId);
trigger.setAttribute('aria-label', 'Zobrazit další položky');
trigger.innerHTML = deferredName ?? 'Další';
icon.setAttribute('type', 'templates');
icon.setAttribute('name', 'chevron-down');
icon.setAttribute('size', 'l');
icon.setAttribute('slot', 'icon-end');
ul.setAttribute('id', controlId);
ul.setAttribute('hidden', 'hidden');
ul.setAttribute('aria-hidden', 'true');
ul.classList.add('gov-deferred-navigation');
trigger.appendChild(icon);
trigger.addEventListener('gov-click', blockRapidClicks(() => this.toggleMenu(trigger)));
li.appendChild(trigger);
li.appendChild(ul);
this.rootUlElement.appendChild(li);
}
private resetDeferredMenu() {
this.deferredItemsContainer?.querySelectorAll(':scope > ul > li').forEach((liElement) => this.rootUlElement.appendChild(liElement));
this.rootUlElement?.querySelectorAll(':scope > .gov-mobile-only').forEach((liElement) => this.rootUlElement.appendChild(liElement));
this.deferredItemsContainer?.remove();
}
private calculateSizeOfElements() {
this.firstLevelLiElements.forEach((liElement, i) => {
liElement.setAttribute('data-item-width', liElement.getBoundingClientRect().width.toString());
liElement.setAttribute('data-item-index', i.toString());
})
}
private registerClickTriggers() {
this.firstLevelLiElements.forEach((liElement) => {
const trigger = this.getTriggerInLiElement(liElement);
const innerList = this.getInnerListInLiElement(liElement);
if (!trigger || !innerList) {
return;
}
trigger.addEventListener('gov-click', blockRapidClicks(() => this.toggleMenu(trigger)));
trigger.addEventListener('click', blockRapidClicks(() => this.toggleMenu(trigger)));
})
}
private hideOpenedMenu(icons: string[]) {
this.firstLevelLiElements.forEach((liElement) => {
const trigger = this.getTriggerInLiElement(liElement);
const innerList = this.getInnerListInLiElement(liElement);
if (!trigger || !innerList) {
return;
}
const controlId = this.resolveTriggerType(trigger).getAttribute('aria-controls');
const isExpanded = toBool(this.resolveTriggerType(trigger).getAttribute('aria-expanded'));
const menuLevel = getDocument().querySelector('[id="' + controlId + '"]');
const iconElement = trigger.querySelector('gov-icon');
if (isExpanded) {
trigger.setAttribute('aria-expanded', 'false');
menuLevel.setAttribute('hidden', 'hidden');
menuLevel.setAttribute('aria-hidden', 'true');
iconElement && iconElement.setAttribute('name', icons[0]);
}
})
}
private toggleMenu(trigger: HTMLElement, icons = ['chevron-down', 'chevron-up']) {
this.hideOpenedMenu(icons);
const controlId = this.resolveTriggerType(trigger).getAttribute('aria-controls');
const isExpanded = toBool(this.resolveTriggerType(trigger).getAttribute('aria-expanded'));
const menuLevel = getDocument().querySelector('[id="' + controlId + '"]');
const iconElement = trigger.querySelector('gov-icon');
if (!menuLevel) {
return;
}
if (isExpanded) {
trigger.setAttribute('aria-expanded', 'false');
menuLevel.setAttribute('hidden', 'hidden');
menuLevel.setAttribute('aria-hidden', 'true');
iconElement && iconElement.setAttribute('name', icons[0]);
} else {
trigger.setAttribute('aria-expanded', 'true');
menuLevel.removeAttribute('hidden');
menuLevel.setAttribute('aria-hidden', 'false');
iconElement && iconElement.setAttribute('name', icons[1]);
}
const elementToFocus = firstFocusableElement(menuLevel);
if (elementToFocus) {
elementToFocus.focus && elementToFocus.focus();
}
}
private verifyAndFixAccesbillity(ul: HTMLUListElement) {
this.getLiElementsInUlElement(ul).forEach((liElement) => {
const trigger = this.getTriggerInLiElement(liElement);
const innerList = this.getInnerListInLiElement(liElement);
if (!trigger || !innerList) {
return;
}
const controlsId = createID('MainMenu');
if (trigger.getAttribute('aria-controls') !== innerList.getAttribute('id')) {
trigger.setAttribute('aria-controls', controlsId);
innerList.setAttribute('id', controlsId);
}
if (!trigger.getAttribute('aria-expanded')) {
trigger.setAttribute('aria-expanded', 'false')
}
if (!trigger.getAttribute('aria-label')) {
trigger.setAttribute('aria-label', 'Zobrazit / Skrýt položky sekce ' + trigger.textContent)
}
this.verifyAndFixAccesbillity(innerList);
})
}
private async accessibilityMobileNavigation() {
const trigger = getDocument().querySelector<HTMLElement>('.js-gov-header__navigation-trigger');
const menu = this.headerNavigationElement;
if (!trigger || !menu) {
return
}
await delay(500);
const controller = this.resolveTriggerType(trigger);
const controlsId = createID('MainMobileMenu');
if (controller.getAttribute('aria-controls') !== menu.getAttribute('id') || menu.getAttribute('id') === null) {
trigger.setAttribute('aria-controls', controlsId);
menu.setAttribute('id', controlsId);
}
if (!controller.getAttribute('aria-expanded')) {
trigger.setAttribute('aria-expanded', 'false')
}
if (!controller.getAttribute('aria-label')) {
trigger.setAttribute('aria-label', 'Zobrazit / Skrýt menu')
}
trigger.addEventListener('gov-click', blockRapidClicks(() => this.toggleMenu(trigger, ['list', 'x-lg'])));
trigger.addEventListener('click', blockRapidClicks(() => this.toggleMenu(trigger, ['list', 'x-lg'])));
}
private controlHeaderNavigation(show = true) {
if (show) {
this.headerNavigationElement?.removeAttribute('hidden')
this.headerNavigationElement?.removeAttribute('aria-hidden')
} else {
this.headerNavigationElement?.setAttribute('hidden', 'hidden')
this.headerNavigationElement?.setAttribute('aria-hidden', 'true')
}
}
private resolveTriggerType(trigger: HTMLElement) {
return trigger && trigger.nodeName === 'GOV-BUTTON' ? trigger.querySelector('button') : trigger;
}
private getTriggerInLiElement(liElement: HTMLLIElement): HTMLLIElement {
return liElement.querySelector(':scope > gov-button, :scope > button');
}
private getInnerListInLiElement(liElement: HTMLLIElement): HTMLUListElement {
return liElement.querySelector(':scope > ul');
}
private getLiElementsInUlElement(ulElement: HTMLUListElement): NodeListOf<HTMLLIElement> {
return ulElement.querySelectorAll(':scope > li');
}
private get firstLevelLiElements(): NodeListOf<HTMLLIElement> {
return this.rootUlElement.querySelectorAll(':scope > li:not(.gov-mobile-only)')
}
private get deferredItemsContainer() {
return this.rootElement.querySelector('.js-deferred-items-container')
}
private get rootUlElement(): HTMLUListElement {
return this.rootElement.querySelector(':scope > ul')
}
private get headerNavigationElement() {
return getDocument().querySelector<HTMLElement>('.js-gov-header__navigation');
}
}
export default MainNavigation;
No notes defined.