diff --git a/src/components.d.ts b/src/components.d.ts index 3e9e50d2304551703ff7fb07c5d515a32c147f92..a2df908d1bfafdcacdd14dee11ac6634c8ebf20f 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -572,6 +572,7 @@ export namespace Components { interface HyMenuLanguageItem { abbr: string; isActive: boolean; + isDisabled: boolean; isMobile: boolean; label: string; langCode: string; @@ -1559,6 +1560,7 @@ declare namespace LocalJSX { } interface HyDesktopMenuLinks { dataDesktopLinks?: DesktopLinks[] | string; + onMenuDesktopToggled?: (event: CustomEvent<any>) => void; } interface HyDocsContainer {} interface HyDropdown { @@ -1860,10 +1862,12 @@ declare namespace LocalJSX { dataMenuLanguage?: MenuLanguage[] | string; isMobile?: boolean; labels?: ComponentLabels[] | string; + onMenuLanguageToggled?: (event: CustomEvent<any>) => void; } interface HyMenuLanguageItem { abbr?: string; isActive?: boolean; + isDisabled?: boolean; isMobile?: boolean; label?: string; langCode?: string; diff --git a/src/components/navigation/menu-language-item/menu-language-item.scss b/src/components/navigation/menu-language-item/menu-language-item.scss index 4f37d85c5ed2620e9207fc7fd6d2a7829012c405..86eaffe2e7fe41e1f246ce65812c697148ce81ca 100644 --- a/src/components/navigation/menu-language-item/menu-language-item.scss +++ b/src/components/navigation/menu-language-item/menu-language-item.scss @@ -3,37 +3,36 @@ } a { - @include font-size(12px, 20px); + @include font-size(12px, 16px); @include font-weight($regular); align-items: center; color: var(--brand-main-nearly-black); display: flex; font-family: var(--main-font-family); - letter-spacing: -0.4px; + letter-spacing: -0.07px; margin-left: 20px; + position: relative; text-decoration: none; text-transform: uppercase; @include breakpoint($wide) { - @include font-size(15px, 20px); + @include font-size(14px, 20px); + @include font-weight($semibold); color: var(--brand-main-light); - font-weight: 700; - padding: 0 40px 32px; + letter-spacing: -0.5px; + padding: 8px 12px; + text-transform: none; &:hover { background-color: var(--grayscale-background-box); color: var(--brand-main); } } - @include breakpoint($extrawide) { - @include font-size(15px, 20px); - color: var(--brand-main-light); - font-weight: 700; - padding: 0 32px 22px; - } + @include breakpoint($xlarge) { - @include font-size(18px, 22px); - padding: 0 40px 32px; + @include font-size(16px, 20px); + letter-spacing: -0.53px; + padding: 12px 12px; } &:focus { @@ -42,25 +41,37 @@ a { } &.is-active { - .hy-menu-language-item__label { - color: var(--grayscale-black); - font-weight: 700; - border-bottom: 2px solid var(--additional-orange); + @include font-weight($bold); + color: var(--grayscale-black); + + @include breakpoint($wide) { + @include font-weight($semibold); + border: 2px solid var(--grayscale-black); + } + + .hy-menu-language-item__label:after { + content: ' '; + border-bottom: 3px solid var(--additional-orange); + bottom: -6px; + left: 0; + position: absolute; + transform: scaleX(-1) scaleY(-1); + width: 100%; @include breakpoint($wide) { - border-bottom: 3px solid var(--grayscale-black); - padding-bottom: 6px; - } - @include breakpoint($extrawide) { - border-bottom: 3px solid var(--grayscale-black); - padding-bottom: 6px; - } - @include breakpoint($xlarge) { - padding-bottom: 8px; + border: none; } } } + &.is-disabled { + color: var(--link-disabled); + &:hover { + background-color: transparent; + color: var(--link-disabled); + } + } + &:not(.is-mobile) { margin: 0; } diff --git a/src/components/navigation/menu-language-item/menu-language-item.tsx b/src/components/navigation/menu-language-item/menu-language-item.tsx index 830850dab833972be03fba4309f0d81f88cd2233..9e949b30702258bb63e6222531acf0371f8b41c1 100644 --- a/src/components/navigation/menu-language-item/menu-language-item.tsx +++ b/src/components/navigation/menu-language-item/menu-language-item.tsx @@ -8,6 +8,7 @@ import {Component, h, Prop, Host} from '@stencil/core'; export class SiteLanguage { @Prop() abbr: string; @Prop() isActive: boolean = false; + @Prop() isDisabled: boolean = false; @Prop() isMobile: boolean = false; @Prop() label: string; @Prop() langCode: string; @@ -18,6 +19,7 @@ export class SiteLanguage { <Host class={{ 'is-active': this.isActive, + 'is-disabled': this.isDisabled, 'hy-menu-language-item': true, }} > @@ -25,6 +27,7 @@ export class SiteLanguage { href={this.url} class={{ 'is-active': this.isActive, + 'is-disabled': this.isDisabled, 'is-mobile': this.isMobile, }} > diff --git a/src/components/navigation/menu-language-item/readme.md b/src/components/navigation/menu-language-item/readme.md index 30f91df8c6c11c0a18f1caee311d36ab203b2cd0..efadc1e0d47be4775769aecda274af351eb90ddf 100644 --- a/src/components/navigation/menu-language-item/readme.md +++ b/src/components/navigation/menu-language-item/readme.md @@ -4,14 +4,15 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ---------- | ----------- | ----------- | --------- | ----------- | -| `abbr` | `abbr` | | `string` | `undefined` | -| `isActive` | `is-active` | | `boolean` | `false` | -| `isMobile` | `is-mobile` | | `boolean` | `false` | -| `label` | `label` | | `string` | `undefined` | -| `langCode` | `lang-code` | | `string` | `undefined` | -| `url` | `url` | | `string` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------ | ------------- | ----------- | --------- | ----------- | +| `abbr` | `abbr` | | `string` | `undefined` | +| `isActive` | `is-active` | | `boolean` | `false` | +| `isDisabled` | `is-disabled` | | `boolean` | `false` | +| `isMobile` | `is-mobile` | | `boolean` | `false` | +| `label` | `label` | | `string` | `undefined` | +| `langCode` | `lang-code` | | `string` | `undefined` | +| `url` | `url` | | `string` | `undefined` | ## Dependencies diff --git a/src/components/navigation/menu-language/menu-language.scss b/src/components/navigation/menu-language/menu-language.scss index 8765b635f3b6c732a8b00110968b66318bc112e9..ba583d5651d5cdd6c42f31b753987d324d4c7d15 100644 --- a/src/components/navigation/menu-language/menu-language.scss +++ b/src/components/navigation/menu-language/menu-language.scss @@ -30,8 +30,6 @@ width: 100%; position: absolute; bottom: 0; - //bottom: -24px; - //left: 0; } } } @@ -75,21 +73,6 @@ .menu--language__toggle__caret { transform: rotate(180deg); } - //background-color: var(--grayscale-background-box); - @include breakpoint($wide) { - //margin-bottom: -28px; - //margin-top: -26px; - //padding: 26px 8px 28px; - } - @include breakpoint($extrawide) { - //margin-bottom: -38px; - //margin-top: -26px; - //padding: 26px 8px 38px; - } - @include breakpoint($xlarge) { - //margin-bottom: -50px; - //padding: 26px 12px 50px; - } } > * { @@ -121,17 +104,15 @@ z-index: 100; @include breakpoint($wide) { - //@todo check side paddings - left: -32px; - padding: 32px 0 0; + left: -20px; + min-width: 160px; + padding: 6px 8px; } @include breakpoint($xlarge) { - left: -40px; - padding: 40px 0 0; + padding: 8px; } a { - //@todo check fonts @include font-size(16px, 20px); margin-left: 0; } @@ -152,21 +133,3 @@ width: 16px; } } - -/* -.hy-menu-backdrop { - bottom: 0; - left: 0; - position: absolute; - right: 0; - top: 0; - visibility: hidden; - - &.is-active { - background-color: rgba(0, 0, 0, 0.4); - transition: background-color 300ms; - visibility: visible; - z-index: 99; - } -} -*/ diff --git a/src/components/navigation/menu-language/menu-language.tsx b/src/components/navigation/menu-language/menu-language.tsx index 8ce89f96fb54b5cf8d59c77db73fed132ebfe2aa..ed0386d3465febec4893f8625cdcde3d9d50a4fc 100644 --- a/src/components/navigation/menu-language/menu-language.tsx +++ b/src/components/navigation/menu-language/menu-language.tsx @@ -6,9 +6,10 @@ export interface MenuLanguage { abbr: string; label: string; isActive?: boolean; + isDisabled?: boolean; } -import {Component, h, Prop, Host, Watch, Element, Listen, State} from '@stencil/core'; +import {Component, h, Prop, Host, Watch, Element, Listen, State, Event, EventEmitter} from '@stencil/core'; @Component({ tag: 'hy-menu-language', @@ -21,6 +22,9 @@ export class MenuLanguage { @Prop() isMobile: boolean = false; @Prop() labels?: ComponentLabels[] | string; @State() isMenuOpen: boolean = false; + + @Event() menuLanguageToggled: EventEmitter; + private _dataMenuLanguage: MenuLanguage[]; private _labels: ComponentLabels[]; @@ -32,12 +36,25 @@ export class MenuLanguage { this._labels = typeof data === 'string' ? JSON.parse(data) : data; } - @Listen('languageMenuLeave') languageMenuLeave() { + // Close the language menu if user clicks anywhere outside the Menu language component. + @Listen('click', {target: 'window'}) + handleOutsideMenuClick(event) { this.isMenuOpen = false; + event.stopPropagation(); } - @Listen('languageMenuToggle') languageMenuToggle() { - //this.isMenuOpen = !this.isMenuOpen; + // CLose the language menu if user opens the desktop menu panel. + @Listen('menuDesktopToggled', {target: 'document'}) + desktopMenuToggled() { + this.isMenuOpen = false; + } + + @Listen('focus') + handleComponentFocus(event) { + // Close desktop menu panel if it's open. + this.menuLanguageToggled.emit(); + + event.stopPropagation(); } @Listen('click') @@ -46,6 +63,9 @@ export class MenuLanguage { const languageMenuSelector = event.target as HTMLElement; if (this.isMenuOpen) { + // Close desktop menu panel if it's open. + this.menuLanguageToggled.emit(); + let hyHeader = this.el.closest('.hy-site-header') as HTMLElement; const headerHeight = `${ languageMenuSelector.offsetHeight + @@ -82,6 +102,7 @@ export class MenuLanguage { label={item.label} abbr={item.abbr} is-active={item.isActive} + is-disabled={item.isDisabled} is-mobile={this.isMobile} /> ); @@ -89,16 +110,12 @@ export class MenuLanguage { </Host> ) : ( <Host - //onMouseLeave={() => this.languageMenuLeave()} class={{ 'menu--language': true, 'menu--language__is-open': this.isMenuOpen, }} > <button - //onClick={() => this.languageMenuToggle()} - //onMouseOver={() => this.languageMenuToggle()} - //onFocus={() => this.languageMenuToggle()} class={{ 'menu--language__toggle': true, 'is-open': this.isMenuOpen, @@ -129,6 +146,7 @@ export class MenuLanguage { label={item.label} abbr={item.abbr} is-active={item.isActive} + is-disabled={item.isDisabled} is-mobile={this.isMobile} /> ); diff --git a/src/components/navigation/menu-language/readme.md b/src/components/navigation/menu-language/readme.md index 35ba8fbb916bab9cb105e35b26bb2e52440778a2..cd19087f41c16dbb1539d4d4f160d5dec500384d 100644 --- a/src/components/navigation/menu-language/readme.md +++ b/src/components/navigation/menu-language/readme.md @@ -10,6 +10,12 @@ | `isMobile` | `is-mobile` | | `boolean` | `false` | | `labels` | `labels` | | `ComponentLabels[] \| string` | `undefined` | +## Events + +| Event | Description | Type | +| --------------------- | ----------- | ------------------ | +| `menuLanguageToggled` | | `CustomEvent<any>` | + ## Dependencies ### Used by diff --git a/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.scss b/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.scss index 6b6c6e9f2bda67f69baf6918f8d26ffc966c5e43..d4926454f8e54f7e83752fd6a345406397bf2e26 100644 --- a/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.scss +++ b/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.scss @@ -28,6 +28,7 @@ display: flex; height: 100%; } + // First level menu items .desktop-menu-link { background-color: transparent; @@ -98,6 +99,15 @@ // On hover: heading icon is turned 180deg and grows bigger. &--is-active { position: relative; + &:hover, + &:focus { + color: var(--link-blue); + hy-icon { + svg { + fill: var(--link-blue); + } + } + } hy-icon { transform: rotateX(180deg); @@ -117,23 +127,29 @@ right: 0; width: 100%; } + + &:focus { + outline: none; + } } // Panel with second level menu items and shortcuts. .hy-desktop-menu-panel { display: none; + flex-direction: row; + opacity: 0; + transition: none; + position: absolute; + left: 0; + top: 0; + z-index: 510; + width: 100%; &--is-active { background: radial-gradient(circle, var(--grayscale-light) 0%, var(--grayscale-background-box) 100%); box-shadow: 0 0 20px 0 rgba(14, 104, 139, 0.1) inset; display: flex; - flex-direction: row; - opacity: 1; - position: absolute; - left: 0; padding-left: 300px; - width: 100%; - z-index: 510; } &__panel-toggle { @@ -177,11 +193,23 @@ text-decoration: none; width: 100%; + &:hover, + &:focus { + box-shadow: 0 0 16px 0 rgba(0, 83, 121, 0.2); + color: var(--link-blue); + + svg { + //height: 42px; + //width: 42px; + } + } + @include breakpoint($extrawide) { @include font-size(24px, 32px); align-items: flex-start; background-color: var(--grayscale-white); flex-direction: column; + justify-content: center; letter-spacing: -0.75px; margin-bottom: 4px; padding: 17px 32px 12px 32px; @@ -198,20 +226,29 @@ .label { border: none; margin-left: 14px; + + &:hover, + &:focus { + color: var(--link-blue); + } } + .description { @include font-size(14px, 18px); @include font-weight($regular); color: var(--grayscale-dark); letter-spacing: -0.2; margin-left: 14px; - margin-bottom: 20px; + margin-bottom: 12px; margin-top: 4px; @include breakpoint($xlarge) { @include font-size(16px, 20px); letter-spacing: -0.25; } + &:hover { + color: var(--grayscale-dark); + } } span.heading-icon { @@ -235,7 +272,7 @@ background-color: var(--grayscale-white); list-style: none; margin: 0; - padding: 0; + padding: 12px 0; li { a { @@ -268,6 +305,11 @@ outline-offset: -2px; } + &:hover, + &:focus { + color: var(--link-blue); + } + .label { @include breakpoint($extrawide) { border: none; diff --git a/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.tsx b/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.tsx index 3e7ac2be0ca0a9dc89dc12b79bf7105fb4374c28..275c759f05d730e88261273650747845fbb7a1c1 100644 --- a/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.tsx +++ b/src/components/site-header/hy-desktop-menu-links/hy-desktop-menu-links.tsx @@ -19,7 +19,7 @@ export interface DesktopLinks { } import {ColorVariant} from '../../../utils/utils'; -import {Component, h, Element, Prop, State, Watch} from '@stencil/core'; +import {Component, h, Element, Prop, State, Watch, EventEmitter, Event, Listen} from '@stencil/core'; @Component({ tag: 'hy-desktop-menu-links', @@ -39,9 +39,14 @@ export class HyDesktopMenuLinks { @State() isDesktopMenuOpen: boolean = false; @State() currenOpenMenuId: number = 0; + @Event() menuDesktopToggled: EventEmitter; + private _submenuLeftMargin: number = 32; private _headerBorderOffset: number = 0; + private _hoverTimer = null; + private _fadeOutTimer = null; + @Watch('dataDesktopLinks') dataDesktopLinksWatcher(data: DesktopLinks[] | string) { this._dataDesktopLinks = typeof data === 'string' ? JSON.parse(data) : data; @@ -67,6 +72,14 @@ export class HyDesktopMenuLinks { } showPanel(id) { + // Close menu lang menu if it's open + this.menuDesktopToggled.emit(); + + clearTimeout(this._fadeOutTimer); + + // Open desktop menu panel + 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}"]`) as HTMLElement; @@ -80,12 +93,16 @@ export class HyDesktopMenuLinks { menuPanelItems.forEach((item) => { item.classList.remove('hy-desktop-menu-panel--is-active'); item.setAttribute('aria-hidden', 'true'); + (item as HTMLElement).style.transition = 'none'; + (item as HTMLElement).style.opacity = '0'; }); // Add active classes to the currently active item and its sibling element. activeMenuItem.classList.add('desktop-menu-link--is-active'); activeMenuItem.setAttribute('aria-expanded', 'true'); activeMenuItemSibling.classList.add('hy-desktop-menu-panel--is-active'); + (activeMenuItemSibling as HTMLElement).style.opacity = '1'; + if (this.hasToolbar) { activeMenuItemSibling.classList.add('hy-desktop-menu-panel--is-active--has-toolbar'); } @@ -100,9 +117,9 @@ export class HyDesktopMenuLinks { } // Add panels top value automatically with the correct header height - const headerHeight = `${ - this.el.parentElement.offsetTop + this.el.parentElement.offsetHeight + this._headerBorderOffset - }px`; + const headerHeight = this.el.parentElement.classList.contains('hy-site-header--sticky-active') + ? `${this.el.parentElement.offsetHeight + this._headerBorderOffset}px` + : `${this.el.parentElement.offsetTop + this.el.parentElement.offsetHeight + this._headerBorderOffset}px`; activeMenuItemSibling.style.top = headerHeight; // Add shadow backdrop @@ -134,20 +151,17 @@ export class HyDesktopMenuLinks { } } - closePanel() { + closePanel(fadeOut = false) { this.isDesktopMenuOpen = false; this.currenOpenMenuId = 0; this.showBackdropShadow(); - this.clearPanelItemsStatus(); + this.clearPanelItemsStatus(fadeOut); + clearTimeout(this._hoverTimer); } - clearPanelItemsStatus() { + clearPanelItemsStatus(fadeOut = false) { 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"]`) as HTMLElement; - - // Return focus to the button of the last desktop panel that was active. - if (activeMenuItem !== null) activeMenuItem.focus(); //Show is-active-trail underlining const activeTrailMenuItem = this.el.shadowRoot.querySelector( @@ -162,20 +176,62 @@ export class HyDesktopMenuLinks { item.classList.remove('desktop-menu-link--is-active'); item.setAttribute('aria-expanded', 'false'); }); - menuPanelItems.forEach((item) => { - item.classList.remove('hy-desktop-menu-panel--is-active'); - item.setAttribute('aria-hidden', 'true'); - }); + + if (fadeOut) { + menuPanelItems.forEach((item) => { + (item as HTMLElement).style.opacity = '0'; + (item as HTMLElement).style.transition = 'opacity 1s'; + }); + + this._fadeOutTimer = setTimeout(() => { + menuPanelItems.forEach((item) => { + item.classList.remove('hy-desktop-menu-panel--is-active'); + item.setAttribute('aria-hidden', 'true'); + }); + }, 350); + } else { + menuPanelItems.forEach((item) => { + item.classList.remove('hy-desktop-menu-panel--is-active'); + item.setAttribute('aria-hidden', 'true'); + (item as HTMLElement).style.opacity = '0'; + (item as HTMLElement).style.transition = 'none'; + }); + } } handleDesktopMenuClose(event) { - this.closePanel(); + let fadeOut = true; + this.closePanel(fadeOut); + + event.stopPropagation(); + } + + // CLose the desktop menu panel if user opens the language menu. + @Listen('menuLanguageToggled', {target: 'document'}) + menuLanguageToggled() { + let fadeOut = true; + this.closePanel(fadeOut); + } + + handleDesktopMenuEnter(event, id) { + clearTimeout(this._fadeOutTimer); + + this._hoverTimer = setTimeout(() => { + const activeMenuItem = this.el.shadowRoot.querySelector(`.desktop-menu-link[link-id="${id}"]`) as HTMLElement; + + // Set focus to the button. + if (activeMenuItem !== null) activeMenuItem.focus(); + + this.currenOpenMenuId = id; + this.showPanel(id); + }, 350); event.stopPropagation(); } handleDesktopMenuLeave(event) { let leaveEvent = event as MouseEvent; + let hyHeader = this.el.closest('.hy-site-header') as HTMLElement; const headerHeight = hyHeader.offsetTop + hyHeader.offsetHeight; @@ -186,7 +242,7 @@ export class HyDesktopMenuLinks { event.stopPropagation(); } - handleDesktopMenuToggle(event, id) { + handleDesktopMenuMove(event, id) { let toggleEvent = event as MouseEvent; const activeMenuItem = this.el.shadowRoot.querySelector( @@ -194,24 +250,10 @@ export class HyDesktopMenuLinks { ) as HTMLElement; let topBorder = activeMenuItem.getClientRects()[0].top; - if (this.currenOpenMenuId != id) { - this.currenOpenMenuId = id; - - if (toggleEvent.clientY < topBorder) { - this.closePanel(); - } else { - this.isDesktopMenuOpen = true; - this.showPanel(id); - } - } else { + if (this.currenOpenMenuId == id) { // Mouse moving around the same menu link if (toggleEvent.clientY < topBorder) { this.closePanel(); - } else { - if (!this.isDesktopMenuOpen) { - this.isDesktopMenuOpen = true; - this.showPanel(id); - } } } @@ -219,7 +261,7 @@ export class HyDesktopMenuLinks { } handleDesktopMenuFocus(event, id) { - if (this.isDesktopMenuOpen && this.currenOpenMenuId != id) { + if (this.currenOpenMenuId != id) { this.currenOpenMenuId = id; this.showPanel(id); } @@ -228,12 +270,11 @@ export class HyDesktopMenuLinks { } handleDesktopMenuClick(event, id) { - this.isDesktopMenuOpen = !this.isDesktopMenuOpen; if (!this.isDesktopMenuOpen) { - this.handleDesktopMenuClose(event); - } else { this.currenOpenMenuId = id; this.showPanel(id); + } else { + this.handleDesktopMenuClose(event); } event.stopPropagation(); @@ -279,10 +320,10 @@ export class HyDesktopMenuLinks { class={classAttributes} link-id={id} onClick={(e) => this.handleDesktopMenuClick(e, id)} - //onMouseOver={(e) => this.handleDesktopMenuToggle(e, id)} - onMouseMove={(e) => this.handleDesktopMenuToggle(e, id)} - onMouseLeave={(e) => this.handleDesktopMenuLeave(e)} onFocus={(e) => this.handleDesktopMenuFocus(e, id)} + onMouseEnter={(e) => this.handleDesktopMenuEnter(e, id)} + onMouseLeave={(e) => this.handleDesktopMenuLeave(e)} + onMouseMove={(e) => this.handleDesktopMenuMove(e, id)} aria-expanded="false" > <span class={classAttributesLabel}>{label}</span> diff --git a/src/components/site-header/hy-desktop-menu-links/readme.md b/src/components/site-header/hy-desktop-menu-links/readme.md index 97289c9d478d6d75545d620dc6404a7b5125ec91..c35a3a1b94b97dfb8ade60c2e4dcc3f3f0595eb9 100644 --- a/src/components/site-header/hy-desktop-menu-links/readme.md +++ b/src/components/site-header/hy-desktop-menu-links/readme.md @@ -8,6 +8,12 @@ | ------------------ | -------------------- | ----------- | -------------------------- | ----------- | | `dataDesktopLinks` | `data-desktop-links` | | `DesktopLinks[] \| string` | `undefined` | +## Events + +| Event | Description | Type | +| -------------------- | ----------- | ------------------ | +| `menuDesktopToggled` | | `CustomEvent<any>` | + ## Dependencies ### Used by diff --git a/src/components/site-header/site-header.scss b/src/components/site-header/site-header.scss index f303e2642a6893b9d27119e9a7900449bea39e73..46f01ee176b5ff01d5448bd7bb777e36b3db7eef 100644 --- a/src/components/site-header/site-header.scss +++ b/src/components/site-header/site-header.scss @@ -11,6 +11,23 @@ place-content: center space-between; z-index: 99; + &--sticky-active { + transition: transform 200ms linear; + transform: translateY(-100%); + transition-duration: 0.2s; + transition-property: transform; + will-change: transform; + } + &--sticky-visible { + left: 0; + position: fixed; + transform: translateY(0) translateZ(0); + width: 100%; + } + &--sticky-hidden { + top: 0; + } + @include breakpoint($narrow) { align-content: center; display: flex; diff --git a/src/components/site-header/site-header.tsx b/src/components/site-header/site-header.tsx index 56de89f5e3d86b860ea3149b9c532b17d31f538d..e2f2f6b299c4e2e53fed55b4d7ed6f72b28d6980 100644 --- a/src/components/site-header/site-header.tsx +++ b/src/components/site-header/site-header.tsx @@ -46,11 +46,24 @@ export class SiteHeader { private searchLabels: ComponentLabels[]; private languageLabels: ComponentLabels[]; + @State() lastScrollTop = 0; + @State() delta = 5; + @State() navbarHeight = 0; + @State() didScroll = false; + @State() intervalId; + // Listener for toggling mobile menu panel on or off. @Listen('mobileMenuToggle') mobileMenuToggle() { this.isMenuOpen = !this.isMenuOpen; } + @Listen('scroll', {target: 'window'}) + handleScroll() { + if (this.el.getAttribute('menu-type') === 'desktop') { + this.didScroll = true; + } + } + componentDidLoad() { // Set the browser resize observer to gather information about browser width. this.ro = new ResizeObserver((entries) => { @@ -62,6 +75,8 @@ export class SiteHeader { // Pass the dataMenuLanguage prop to menu component. this.el.children[0].setAttribute('data-menu-language', this.dataMenuLanguage); + + this.navbarHeight = this.el.getClientRects()[0].height; } componentWillLoad() { @@ -92,6 +107,46 @@ export class SiteHeader { this.el.children[0].setAttribute('menu-language-label-open', this.languageLabels['open']); this.el.children[0].setAttribute('menu-language-label-close', this.languageLabels['close']); this.el.children[0].setAttribute('label-front-page', this.menuLabels['front_page']); + + this.intervalId = setInterval(() => { + this.timer(); + }, 250); + } + + timer() { + if (this.didScroll) { + this.hasScrolled(); + this.didScroll = false; + } + } + + hasScrolled() { + let st = window.pageYOffset; + if (Math.abs(this.lastScrollTop - st) <= this.delta) { + return; + } + + let hySiteHeader = this.el.shadowRoot.querySelector('.hy-site-header') as HTMLElement; + // If current position > last position AND scrolled past navbar... + if (st > this.lastScrollTop && st > this.navbarHeight) { + // Scroll Down + + hySiteHeader.classList.add('hy-site-header--sticky-active'); + hySiteHeader.classList.add('hy-site-header--sticky-hidden'); + hySiteHeader.classList.remove('hy-site-header--sticky-visible'); + } else { + // Scroll Up + if (st < this.el.offsetTop + this.navbarHeight) { + hySiteHeader.classList.remove('hy-site-header--sticky-active'); + hySiteHeader.classList.remove('hy-site-header--sticky-visible'); + hySiteHeader.classList.remove('hy-site-header--sticky-hidden'); + } else { + hySiteHeader.classList.add('hy-site-header--sticky-active'); + hySiteHeader.classList.add('hy-site-header--sticky-visible'); + hySiteHeader.classList.remove('hy-site-header--sticky-hidden'); + } + } + this.lastScrollTop = st; } componentDidUnload() { diff --git a/src/global/_colors.scss b/src/global/_colors.scss index 40d3a2fc71bfc05db99dac7cc9659b8b7ecf7560..3de5b728f2a6f3bc58b7f23be16e45f9aa6b4a89 100644 --- a/src/global/_colors.scss +++ b/src/global/_colors.scss @@ -5,6 +5,7 @@ --brand-main-dark: #003146; --brand-main-nearly-black: #000222; --link-blue: #0479a5; + --link-disabled: #767676; --grayscale-white: #fff; --grayscale-light: #f8f8f8; --grayscale-medium: #d2d2d2;