Newer
Older
Markus Kalijärvi
committed
import {Component, Element, h, Host, Listen, Prop, State} from '@stencil/core';
import {MenuType} from '../../../utils/utils';
Markus Kalijärvi
committed
@Component({
tag: 'hy-menu-level-container',
styleUrl: 'menu-level-container.scss',
Markus Kalijärvi
committed
})
export class MenuLevelContainer {
@Element() el: HTMLElement;
@Prop() labelFrontPage?: string;
Markus Kalijärvi
committed
@Prop() menuButtonSubmenuExpand?: string;
@Prop() menuLevel: number;
@Prop() menuType: MenuType = MenuType.mobile;
Markus Kalijärvi
committed
@Prop({reflect: true}) activeTrailTriggered: boolean = false;
@Prop({mutable: true, reflect: true}) depth: number = 0;
Markus Kalijärvi
committed
@Prop({mutable: true}) headingItem: any;
@Prop({mutable: true, reflect: true}) triggerItem: string;
/**
* Url to front page for panel first parent
*/
@Prop() frontUrl: string;
/**
* label for front page for panel first parent
*/
@Prop() frontLabel: string;
@State() menuIsOpen: boolean = false;
Markus Kalijärvi
committed
// Add/Remove is-hidden class from each upper level menu-items
// to make browsing more accessible.
assignMenuItemClass(parentMenu: Element, type: string) {
const items = Array.from(parentMenu.children);
items.forEach((item) => {
if (type === 'remove') {
item.classList.remove('is-hidden');
} else {
item.classList.add('is-hidden');
}
});
}
// Listener for assigning active-trail for parent menu level container.
Markus Kalijärvi
committed
@Listen('menuContainerActiveTrail', {target: 'document', capture: true})
menuContainerActiveTrail(data) {
const currentMenuContainer = this.el.getAttribute('trigger-item');
if (currentMenuContainer === data.detail.triggerItem) {
this.activeTrailTriggered = true;
this.menuIsOpen = true;
this.assignMenuItemClass(this.el.parentElement.closest('hy-menu-level-container'), 'add');
Markus Kalijärvi
committed
}
}
// Listener for opening and closing menu level container.
Markus Kalijärvi
committed
@Listen('menuContainerToggled', {target: 'document', capture: true})
menuContainerToggled(data) {
// Toggle submenu.
if (this.triggerItem == data.detail.triggerItem) {
this.activeTrailTriggered = false;
this.menuIsOpen = data.detail.triggerType != 'remove';
this.assignMenuItemClass(this.el.parentElement.closest('hy-menu-level-container'), data.detail.triggerType);
// Scroll to .hy-menu top.
let hyMenu = this.el.parentElement.closest('hy-menu');
hyMenu.shadowRoot.querySelector('.hy-menu').scrollTop = 0;
Markus Kalijärvi
committed
}
}
// Pass the menu type and menu-button-submenu-expand attributes to
// the child menu item component.
if (this.menuType) {
const items = Array.from(this.el.children);
items.forEach((item) => {
item.setAttribute('menu-type', this.menuType);
Markus Kalijärvi
committed
item.setAttribute('menu-button-submenu-expand', this.menuButtonSubmenuExpand);
Markus Kalijärvi
committed
componentWillRender() {
// Assign depth value to current menu level container instance;
// 1st level, 2nd level, etc.
Markus Kalijärvi
committed
let parentMenu = this.el.closest('hy-menu-level-container');
let nextParentMenu;
this.depth = 0;
while (parentMenu) {
nextParentMenu = parentMenu.parentElement.closest('hy-menu-level-container');
if (nextParentMenu === parentMenu) {
break;
} else {
parentMenu = nextParentMenu;
this.depth = this.depth + 1;
}
}
// Set trigger item for each mobile menu level container and handle only submenus.
Markus Kalijärvi
committed
// Add a heading element foreach level.
if (this.menuType === MenuType.mobile) {
if (this.menuLevel > 1) {
const parentMenuItem = this.el.closest('hy-menu-item');
this.triggerItem = parentMenuItem.getAttribute('menu-link-id');
this.headingItem = {
...this.headingItem,
url: parentMenuItem.getAttribute('url'),
};
this.headingItem = {
...this.headingItem,
label: parentMenuItem.getAttribute('label'),
};
this.headingItem = {
...this.headingItem,
isActive: window.location.pathname === this.headingItem.url,
};
} else {
this.triggerItem = 'home';
}
}
// Set is-active-child and menu-level attributes to all sibling menu-items.
if (this.menuType === MenuType.sidenav) {
const parentMenuItem = this.el.closest('hy-menu-item-sidebar');
if (parentMenuItem && parentMenuItem.classList.contains('is-active')) {
this.menuIsOpen = true;
const items = Array.from(this.el.children);
items.forEach((item) => {
item.setAttribute('is-active-child', 'true');
item.setAttribute('menu-level', this.menuLevel.toString());
});
}
Markus Kalijärvi
committed
}
}
render() {
let classAttributes = ['hy-menu-level-container', 'hy-menu-level-container--level-' + this.depth];
case MenuType.desktop:
classAttributes = [...classAttributes, 'hy-menu-level-container--desktop'];
<Host class={classAttributes.join(' ')}>
case MenuType.mobile:
classAttributes = [...classAttributes, 'hy-menu-level-container--mobile'];
if (this.depth === 1) {
classAttributes = [...classAttributes, 'is-open'];
return (
<Host aria-expanded={this.menuIsOpen.toString()} class={classAttributes.join(' ')} tabindex={'-1'}>
<slot />
</Host>
);
} else {
classAttributes = [...classAttributes, this.menuIsOpen ? 'is-open' : null];
<Host aria-expanded={this.menuIsOpen.toString()} class={classAttributes.join(' ')} tabindex={'-1'}>
<hy-menu-item
label={this.headingItem.label}
url={this.headingItem.url}
isHeading={true}
isActive={this.headingItem.isActive}
menu-type={'mobile'}
/>
<slot />
</Host>
);
}
case MenuType.sidenav:
const shouldBeHidden = this.depth != null && this.depth === 1 ? 'is-open' : '';
classAttributes = [...classAttributes, 'hy-menu-level-container--sidenav', shouldBeHidden];
<ul data-menu-level={this.depth} class={classAttributes.join(' ')}>
</ul>
const isMenuOpen = this.menuIsOpen ? 'is-open_is-open__sub-level' : null;
const shouldBeHidden = this.depth != null && this.depth === 1 ? 'is-open' : '';
const parentMenuItem = this.el.closest('hy-menu-item-sidebar') as any;
const parentClone = parentMenuItem.cloneNode(true) as any;
const parentLink = parentClone.querySelector('.hy-menu-item-sidebar__label');
classAttributes = [...classAttributes, 'hy-menu-level-container--sidenav', shouldBeHidden, isMenuOpen];
<ul data-menu-level={this.depth} class={classAttributes.join(' ')}>
{parentLink && (
<li class="hy-menu-item-sidebar is-parent is-parent--sub-level">
<hy-icon
class={'hy-menu-item-parent__icon'}
icon={'hy-icon-dot-arrow-right'}
fill={'currentColor'}
size={20}
/>
<span
aria-level={this.depth}
role="heading"
class="hy-menu-item-sidebar--label-container"
innerHTML={parentLink.outerHTML}
></span>
</li>
)}
</ul>
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
case MenuType.sidepanel:
//const shouldBeHidden = (this.depth != null && this.depth === 1) ? 'is-open' : '';
classAttributes = [...classAttributes, 'hy-menu-level-container--sidepanel'];
const parentMenuItem = this.el.closest('hy-menu-item-sidebar') as any;
const parentClone = parentMenuItem ? (parentMenuItem.cloneNode(true) as any) : null;
const parentLink = parentClone ? parentClone.querySelector('.hy-menu-item-sidebar__label') : null;
return (
<ul data-menu-level={this.depth} class={classAttributes.join(' ')}>
{parentLink ? (
<li class="hy-menu-item-sidebar is-parent is-parent--sub-level">
<hy-icon
class={'hy-menu-item-parent__icon'}
icon={'hy-icon-dot-arrow-right'}
fill={'currentColor'}
size={20}
/>
<span
aria-level={this.depth}
role="heading"
class="hy-menu-item-sidebar--label-container"
innerHTML={parentLink.outerHTML}
></span>
</li>
) : (
<li class="hy-menu-item-sidebar is-parent is-parent--sub-level is-parent--frontpage">
<span class={'hy-menu-item__parent__icon'}>
<hy-icon
class={'hy-menu-item__parent__icon__svg'}
icon={'hy-icon-caret-left'}
fill={'currentColor'}
size={10}
/>
</span>
<div class="hy-menu-item-sidebar--label-container">
<a class="hy-menu-item-sidebar__label" href={this.frontUrl}>
{this.frontLabel}
</a>
</div>
</li>
)}
<slot />
</ul>
);