Skip to content
Snippets Groups Projects
Commit 11550ccd authored by Tuukka Turu's avatar Tuukka Turu
Browse files

Merge branch 'NXSTAGE-1001-upper-navigation-advanced' into 'development'

Nxstage 1001 upper navigation advanced

See merge request julkiset-sivut/design-system-lib!92
parents cf39da0f b45697a1
No related branches found
No related tags found
No related merge requests found
......@@ -57,7 +57,7 @@
position: relative;
.desktop-menu-link__heading__icon {
bottom: 14px;
bottom: 0;
display: block;
left: 0;
position: absolute;
......@@ -67,7 +67,7 @@
justify-content: center;
transform: rotateX(180deg);
svg {
padding: 4px 0;
padding: 8px 0;
}
}
}
......@@ -82,11 +82,11 @@
background-color: var(--grayscale-white);
display: flex;
flex-direction: row;
justify-content: center;
opacity: 1;
position: absolute;
left: 0;
top: 104px;
padding-left: 300px;
padding-bottom: 28px;
width: 100%;
z-index: 510;
}
......@@ -115,7 +115,13 @@
&__desktop-menu {
display: flex;
position: relative;
//position: relative;
&__menu-items {
//@todo check max-with in Specs
min-width: 450px;
max-width: 550px;
}
// first level link inside panel
&__first-level-menu-item {
......@@ -199,7 +205,7 @@
flex-direction: row;
letter-spacing: -0.47px;
padding-left: 24px;
padding-right: 24px;
padding-right: 48px;
text-transform: none;
}
......@@ -238,8 +244,14 @@
// Shortcuts
.shortcuts-panel {
position: absolute;
left: 0; // override in js
list-style: none;
margin-left: 48px;
min-width: 350px;
max-width: 400px;
//margin: 0 48px;
padding: 0 48px;
top: 0;
&__title {
@include font-size(18px, 22px);
......@@ -264,11 +276,13 @@
display: flex;
flex-direction: row;
font-family: var(--main-font-family);
justify-content: space-between;
letter-spacing: -0.5px;
padding: 19px 0;
text-decoration: none;
.icon {
padding-left: 24px;
svg {
padding: 4px;
}
......
......@@ -7,10 +7,13 @@ export interface ShortcutLinks {
export interface DesktopLinks {
label: string;
labelExtra: string;
url: string;
description: string;
menuLinkId: string;
isActive: string;
shortcutsTitle: string;
closeButtonTitle: string;
items: Array<DesktopLinks>;
shortcuts: Array<ShortcutLinks>;
}
......@@ -32,8 +35,14 @@ export class HyDesktopMenuLinks {
private _dataDesktopLinks: DesktopLinks[];
@State() firstLevelLinksList: Array<object> = [];
@State() menuLinkItems: Array<object> = [];
@State() hasToolbar: boolean = false;
@State() isDesktopMenuOpen: boolean = false;
@Watch('dataDesktopLinks') dataDesktopLinksWatcher(data: DesktopLinks[] | string) {
private _submenuLeftMargin: number = 32;
private _headerBorderOffset: number = 8;
@Watch('dataDesktopLinks')
dataDesktopLinksWatcher(data: DesktopLinks[] | string) {
this._dataDesktopLinks = typeof data === 'string' ? JSON.parse(data) : data;
}
......@@ -41,13 +50,33 @@ export class HyDesktopMenuLinks {
this.dataDesktopLinksWatcher(this.dataDesktopLinks);
}
showBackdropShadow(state = 'close', top = 0) {
let hyHeader = this.el.closest('.hy-site-header') as HTMLElement;
let hyBackdropDiv = hyHeader.children[0] as HTMLElement;
if (hyBackdropDiv) {
if (state === 'open') {
hyBackdropDiv.classList.add('is-active');
hyBackdropDiv.style.top = `${top}px`;
}
if (state === 'close') {
hyBackdropDiv.classList.remove('is-active');
hyBackdropDiv.style.top = '0';
}
}
}
handleDesktopMenuClose() {
this.isDesktopMenuOpen = false;
this.showBackdropShadow();
const menuItems = this.el.shadowRoot.querySelectorAll(`.desktop-menu-link`);
const menuPanelItems = this.el.shadowRoot.querySelectorAll('.hy-desktop-menu-panel');
const activeMenuItem = this.el.shadowRoot.querySelector(`.desktop-menu-link[aria-expanded="true"]`);
const activeMenuItem = this.el.shadowRoot.querySelector(`.desktop-menu-link[aria-expanded="true"]`) as HTMLElement;
// Return focus to the button of the last desktop panel that was active.
if (activeMenuItem !== null) (activeMenuItem as HTMLElement).focus();
if (activeMenuItem !== null) activeMenuItem.focus();
// Reset elements by removing the active classes.
menuItems.forEach((item) => {
......@@ -61,10 +90,11 @@ export class HyDesktopMenuLinks {
}
handleDesktopMenuToggle(id) {
this.isDesktopMenuOpen = true;
const menuItems = this.el.shadowRoot.querySelectorAll(`.desktop-menu-link`);
const menuPanelItems = this.el.shadowRoot.querySelectorAll('.hy-desktop-menu-panel'); // all panels
const activeMenuItem = this.el.shadowRoot.querySelector(`.desktop-menu-link[link-id="${id}"]`);
const activeMenuItemSibling = activeMenuItem.nextElementSibling; // current panel
const activeMenuItem = this.el.shadowRoot.querySelector(`.desktop-menu-link[link-id="${id}"]`) as HTMLElement;
const activeMenuItemSibling = activeMenuItem.nextElementSibling as HTMLElement; // current panel
// Reset elements by removing the active classes.
menuItems.forEach((item) => {
......@@ -80,109 +110,202 @@ export class HyDesktopMenuLinks {
activeMenuItem.classList.add('desktop-menu-link--is-active');
activeMenuItem.setAttribute('aria-expanded', 'true');
activeMenuItemSibling.classList.add('hy-desktop-menu-panel--is-active');
if (this.hasToolbar) {
activeMenuItemSibling.classList.add('hy-desktop-menu-panel--is-active--has-toolbar');
}
activeMenuItemSibling.setAttribute('aria-hidden', 'false');
// Add panels top value automatically with the correct header height
const headerHeight = `${
this.el.parentElement.offsetTop + this.el.parentElement.offsetHeight + this._headerBorderOffset
}px`;
activeMenuItemSibling.style.top = headerHeight;
// Add shadow backdrop
let rect = activeMenuItemSibling.getBoundingClientRect();
this.showBackdropShadow('open', rect.bottom);
// Position submenu block under the activated top menu item.
let activeButtonRect = activeMenuItem.getBoundingClientRect();
const menuPanelLeftPosition = activeButtonRect.left - this._submenuLeftMargin;
activeMenuItemSibling.style.paddingLeft = `${menuPanelLeftPosition}px`;
// Position shortcuts block.
let shortcutsDiv = activeMenuItemSibling.querySelectorAll('ul.shortcuts-panel')[0] as HTMLElement; // shortcuts block
if (shortcutsDiv) {
let subMenuDiv = activeMenuItemSibling.querySelectorAll(
'.hy-desktop-menu-panel__desktop-menu__menu-items'
)[0] as HTMLElement; // 2nd level menu block
let spaceLeftAfterSubmenu = subMenuDiv.getBoundingClientRect().right + shortcutsDiv.offsetWidth;
if (spaceLeftAfterSubmenu >= document.body.scrollWidth) {
// Shortcuts should be placed to the left.
let shortcutsLeftPosition = subMenuDiv.getBoundingClientRect().left - shortcutsDiv.offsetWidth;
shortcutsDiv.style.left = shortcutsLeftPosition.toString().concat('px');
} else {
// Shortcuts should be placed to the right.
let shortcutsLeftPosition = subMenuDiv.getBoundingClientRect().right;
shortcutsDiv.style.left = shortcutsLeftPosition.toString().concat('px');
}
}
}
handleDesktopMenuClick(id) {
const activeMenuItem = this.el.shadowRoot.querySelector(`.desktop-menu-link[link-id="${id}"]`);
const activeMenuItemSibling = activeMenuItem.nextElementSibling as HTMLElement; // current panel
if (!this.isDesktopMenuOpen) {
// Add active classes to the currently active item and its sibling element.
this.isDesktopMenuOpen = true;
activeMenuItem.classList.add('desktop-menu-link--is-active');
activeMenuItem.setAttribute('aria-expanded', 'true');
activeMenuItemSibling.classList.add('hy-desktop-menu-panel--is-active');
if (this.hasToolbar) {
activeMenuItemSibling.classList.add('hy-desktop-menu-panel--is-active--has-toolbar');
}
activeMenuItemSibling.setAttribute('aria-hidden', 'false');
let rect = activeMenuItemSibling.getBoundingClientRect();
this.showBackdropShadow('open', rect.bottom);
} else {
// Remove active classes to the currently active item and its sibling element.
this.isDesktopMenuOpen = false;
activeMenuItem.classList.remove('desktop-menu-link--is-active');
activeMenuItem.setAttribute('aria-expanded', 'false');
activeMenuItemSibling.classList.remove('hy-desktop-menu-panel--is-active');
if (this.hasToolbar) {
activeMenuItemSibling.classList.remove('hy-desktop-menu-panel--is-active--has-toolbar');
}
activeMenuItemSibling.setAttribute('aria-hidden', 'true');
this.showBackdropShadow();
}
}
componentDidLoad() {
let hyToolbar = document.querySelectorAll('#toolbar-administration')[0];
if (hyToolbar) {
this.hasToolbar = true;
}
const links = this._dataDesktopLinks as Array<DesktopLinks>;
let menuLinkItems = [];
links.map(({menuLinkId: id, shortcuts, items, url, description, label, isActive}) => {
let classAttributes = ['desktop-menu-link', isActive === 'true' ? 'desktop-menu-link--is-active-trail' : ''].join(
' '
);
menuLinkItems.push(
<li>
<button
type="button"
class={classAttributes}
link-id={id}
onMouseOver={() => this.handleDesktopMenuToggle(id)}
onFocus={() => this.handleDesktopMenuToggle(id)}
aria-expanded="false"
>
{label}
<span class="desktop-menu-link__heading__icon">
<hy-icon icon={'hy-icon-caret-down'} size={16} />
</span>
</button>
<div class="hy-desktop-menu-panel" onMouseLeave={() => this.handleDesktopMenuClose()} aria-hidden="true">
<div class="hy-desktop-menu-panel__desktop-menu">
<div class="hy-desktop-menu-panel__desktop-menu__menu-items">
<a
aria-current={label}
href={url}
class="hy-desktop-menu-panel__desktop-menu__first-level-menu-item"
menu-link-id={id}
>
<span class="heading-icon">
<hy-icon icon={'hy-icon-arrow-right'} size={40} />
</span>
<span class="label">{label}</span>
{description && <span class="description">{description}</span>}
</a>
<ul class={'hy-desktop-menu-panel__desktop-menu__second-level-menu'} menu-link-id={id}>
{items.map(({label, url}) => (
<li>
<a href={url}>
<span class="heading-icon">
<hy-icon icon={'hy-icon-caret-right'} size={12} />
</span>
<span class="label">{label}</span>
</a>
</li>
))}
</ul>
</div>
{shortcuts.length > 0 && (
<ul class="shortcuts-panel">
<h2 class="shortcuts-panel__title">{'Shortcuts'}</h2>
{shortcuts.map(({shortcut_title, shortcut_url, shortcut_is_external, shortcut_aria_label}, index) => {
let target = shortcut_is_external ? '_blank' : '_self';
let shortcutClass = [
'shortcuts-panel__shortcut-item',
index == 0 ? 'shortcuts-panel__shortcut-item__first' : '',
].join(' ');
return (
<li class={shortcutClass}>
<a
aria-current={shortcut_aria_label}
href={shortcut_url}
class="shortcut-item__link"
target={target}
aria-label={shortcut_aria_label}
>
<span class="label">{shortcut_title}</span>
<span class="icon">
<hy-icon icon={'hy-icon-arrow-right'} size={24} />
</span>
</a>
</li>
);
})}
</ul>
)}
</div>
links.map(
({
menuLinkId: id,
shortcuts,
items,
url,
description,
label,
labelExtra,
isActive,
shortcutsTitle,
closeButtonTitle,
}) => {
let classAttributes = [
'desktop-menu-link',
isActive === 'true' ? 'desktop-menu-link--is-active-trail' : '',
].join(' ');
menuLinkItems.push(
<li>
<button
onClick={() => this.handleDesktopMenuClose()}
class={{
'hy-desktop-menu-panel__panel-toggle': true,
}}
aria-label="Close menu"
type="button"
class={classAttributes}
link-id={id}
onClick={() => this.handleDesktopMenuClick(id)}
onMouseOver={() => this.handleDesktopMenuToggle(id)}
onFocus={() => this.handleDesktopMenuToggle(id)}
aria-expanded="false"
>
<span class="hy-desktop-menu-panel__panel-toggle__label">
<span class="hy-desktop-menu-panel__panel-toggle__label__title">CLOSE</span>
<hy-icon icon={'hy-icon-remove'} size={20} fill={ColorVariant.black} />
{label}
<span class="desktop-menu-link__heading__icon">
<hy-icon icon={'hy-icon-caret-down'} size={32} />
</span>
</button>
</div>
</li>
);
});
<div class="hy-desktop-menu-panel" onMouseLeave={() => this.handleDesktopMenuClose()} aria-hidden="true">
<div class="hy-desktop-menu-panel__desktop-menu">
<div class="hy-desktop-menu-panel__desktop-menu__menu-items">
<a
aria-current={label}
href={url}
class="hy-desktop-menu-panel__desktop-menu__first-level-menu-item"
menu-link-id={id}
>
<span class="heading-icon">
<hy-icon icon={'hy-icon-arrow-right'} size={40} />
</span>
{labelExtra ? <span class="label">{labelExtra}</span> : <span class="label">{label}</span>}
{description && <span class="description">{description}</span>}
</a>
<ul class={'hy-desktop-menu-panel__desktop-menu__second-level-menu'} menu-link-id={id}>
{items.map(({label, url}) => (
<li>
<a href={url}>
<span class="heading-icon">
<hy-icon icon={'hy-icon-caret-right'} size={12} />
</span>
<span class="label">{label}</span>
</a>
</li>
))}
</ul>
</div>
{shortcuts.length > 0 && (
<ul class="shortcuts-panel">
<h2 class="shortcuts-panel__title">{shortcutsTitle}</h2>
{shortcuts.map(
({shortcut_title, shortcut_url, shortcut_is_external, shortcut_aria_label}, index) => {
let target = shortcut_is_external ? '_blank' : '_self';
let shortcutClass = [
'shortcuts-panel__shortcut-item',
index == 0 ? 'shortcuts-panel__shortcut-item__first' : '',
].join(' ');
return (
<li class={shortcutClass}>
<a
aria-current={shortcut_aria_label}
href={shortcut_url}
class="shortcut-item__link"
target={target}
aria-label={shortcut_aria_label}
>
<span class="label">{shortcut_title}</span>
<span class="icon">
<hy-icon icon={'hy-icon-arrow-right'} size={24} />
</span>
</a>
</li>
);
}
)}
</ul>
)}
</div>
<button
onClick={() => this.handleDesktopMenuClose()}
class={{
'hy-desktop-menu-panel__panel-toggle': true,
}}
aria-label="Close menu"
>
<span class="hy-desktop-menu-panel__panel-toggle__label">
<span class="hy-desktop-menu-panel__panel-toggle__label__title">{closeButtonTitle}</span>
<hy-icon icon={'hy-icon-remove'} size={20} fill={ColorVariant.black} />
</span>
</button>
</div>
</li>
);
}
);
this.menuLinkItems = menuLinkItems;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment