<nav class="navigation navigation--idle js-navigation">
<ul class="navigation__list navigation__list--parents">
<li class="navigation__item ">
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#arrow-right-long-54"></use>
</svg>
<a href="#" class="navigation__link">
Home
</a>
<span class="navigation__description">Home is the place to be #staythefuckhome</span>
</li>
<li class="navigation__item has-children is-current ">
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#arrow-right-long-54"></use>
</svg>
<a href="#" class="navigation__link">
Services
<svg class="icon navigation__chevron">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#chevron-bottom-24"></use>
</svg>
</a>
<span class="navigation__description">Everything digital under one roof.</span>
<ul class="navigation__list navigation__list--children">
<li class="navigation__item ">
<a href="#" class="navigation__link">
Business transformation
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
<li class="navigation__item ">
<a href="#" class="navigation__link">
Design & Innovation
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
<li class="navigation__item ">
<a href="#" class="navigation__link">
Development
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
<li class="navigation__item ">
<a href="#" class="navigation__link">
Maintenance
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
</ul>
</li>
<li class="navigation__item has-children ">
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#arrow-right-long-54"></use>
</svg>
<a href="#" class="navigation__link">
Work
<svg class="icon navigation__chevron">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#chevron-bottom-24"></use>
</svg>
</a>
<span class="navigation__description">Home is the place to be #staythefuckhome</span>
<ul class="navigation__list navigation__list--children">
<li class="navigation__item ">
<a href="#" class="navigation__link">
Business transformation
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
<li class="navigation__item ">
<a href="#" class="navigation__link">
Design & Innovation
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
<li class="navigation__item ">
<a href="#" class="navigation__link">
Development
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
</ul>
</li>
<li class="navigation__item ">
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#arrow-right-long-54"></use>
</svg>
<a href="#" class="navigation__link">
About
</a>
<span class="navigation__description">Home is the place to be #staythefuckhome</span>
</li>
<li class="navigation__item ">
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#arrow-right-long-54"></use>
</svg>
<a href="#" class="navigation__link">
Contact
</a>
<span class="navigation__description">Got a project you dream of realizing? Get in touch!</span>
</li>
</ul>
</nav>
{% macro navItemLink(item, icon) %}
{% set attributes %}
{% if item.target %} target="{{ item.target }}"{% endif %}
{%- if item.attr_title %} title="{{ item.attr_title }}"{% endif %}
{%- if item._menu_item_xfn and item.link %} rel="{{ item._menu_item_xfn }}"{% endif %}
{% endset %}
{% set element = item.link ? 'a' : 'p' %}
<{{element}} {% if item.link %}href="{{ item.link }}"{% endif %} class="navigation__link" {{ attributes|trim }}>
{{ item.title }}
{% if icon %} {{ icon }} {% endif %}
</{{element}}>
{% endmacro %}
{% import _self as navigation %}
<nav class="navigation {{ modifier }} {{ class }}">
{% if data.items %}
<ul class="navigation__list navigation__list--parents">
{% for item in data.items %}
<li class="navigation__item {% if item.children %}has-children{% endif %}{% if item.current or item.current_item_ancestor %} is-current{% endif %} {% if 'navigation--footer' in modifier %}navigation__item--divisible-by-{{ loop.length }}{% endif %} {{ item.classes|join(' ') }}">
{% include '@icon' with { name: 'arrow-right-long-54', class: 'navigation__arrow-icon', modifier: '' } %}
{% if item.children and 'navigation--footer' not in modifier %}
{% set icon %}
{% include '@icon' with { name: 'chevron-bottom-24', class: 'navigation__chevron', modifier: '' } %}
{% endset %}
{% else %}
{% set icon = false %}
{% endif %}
{{ navigation.navItemLink(item, icon)|trim }}
{% if item.description %}
<span class="navigation__description">{{ item.description }}</span>
{% endif %}
{% if item.children %}
<ul class="navigation__list navigation__list--children">
{% for child in item.children %}
<li class="navigation__item {% if child.current %}is-current{% endif %} {{ child.classes|join(' ') }}">
{{ navigation.navItemLink(child) }}
{% include '@icon' with { name: 'tiny-arrow-right-long-24', class: 'navigation__arrow-icon', modifier: '' } %}
</li>
{% endfor %}
{% if 'footer__navigation' in class and loop.index == 1 %}
<li class="navigation__item {% if child.current %}is-current{% endif %}">
{% include '@icon' with { name: 'tiny-arrow-right-long-24', class: 'navigation__arrow-icon', modifier: '' } %}
<a class="navigation__link" data-cc="show-preferencesModal">Cookie preferences</a>
</li>
{% endif %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</nav>
{
"language": "en-US",
"modifier": "navigation--idle",
"class": "js-navigation",
"data": {
"items": [
{
"link": "#",
"title": "Home",
"description": "Home is the place to be #staythefuckhome"
},
{
"link": "#",
"title": "Services",
"description": "Everything digital under one roof.",
"current": "true",
"children": [
{
"link": "#",
"title": "Business transformation"
},
{
"link": "#",
"title": "Design & Innovation"
},
{
"link": "#",
"title": "Development"
},
{
"link": "#",
"title": "Maintenance"
}
]
},
{
"link": "#",
"title": "Work",
"description": "Home is the place to be #staythefuckhome",
"children": [
{
"link": "#",
"title": "Business transformation"
},
{
"link": "#",
"title": "Design & Innovation"
},
{
"link": "#",
"title": "Development"
}
]
},
{
"link": "#",
"title": "About",
"description": "Home is the place to be #staythefuckhome"
},
{
"link": "#",
"title": "Contact",
"description": "Got a project you dream of realizing? Get in touch!"
}
]
}
}
.navigation {
color: $color-text-03;
@include bp(md-min) {
display: flex;
justify-content: flex-end;
}
}
.navigation--idle {
@include bp(md-min) {
justify-content: space-evenly;
}
}
.navigation--footer {
display: block;
}
.navigation__list--parents {
display: block;
@media only screen and (min-width: #{$bp-sm-min}) and (orientation: portrait) {
display: flex;
flex-direction: column;
align-items: flex-end;
}
@include bp(md-min) {
display: flex;
flex-direction: column;
align-items: flex-end;
}
&:after {
.navigation--idle & {
@include bp(md-min) {
content: '';
height: 1px;
background-color: $color-text-03;
position: absolute;
bottom: 1px;
left: 15px;
right: 15px;
width: 0;
transition-property: width;
transition-duration: $transition-duration*1.5;
transition-timing-function: $transition-easing-in;
transition-delay: $transition-duration;
}
}
.navigation--idle.is-finished-animating & {
visibility: hidden;
}
body.core-has-loaded &
.navigation--idle.is-ready & {
@include bp(md-min) {
width: calc(100% - 30px); /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
}
.navigation--idle & {
@include bp(md-min) {
display: flex;
margin: 0 -15px;
flex-direction: row;
align-items: flex-start;
width: 100%;
}
}
.navigation--footer & {
display: flex;
flex-direction: column;
align-items: stretch;
@include bp(md-min) {
flex-direction: row;
justify-content: flex-end;
margin: 0 -28px;
}
}
}
.navigation__list--children {
padding-top: 4px;
padding-bottom: 4px;
margin: 0;
position: absolute;
bottom: 0;
left: 0;
z-index: map-get($zindex, 'default');
display: block;
pointer-events: none;
@include bp(sm-min) {
right: 0;
left: auto;
display: flex;
min-width: max-content; /* stylelint-disable-line plugin/no-unsupported-browser-features */
flex-direction: column;
align-items: flex-end;
}
.navigation--idle & {
@include bp(md-min) {
width: 100%;
left: 50%;
right: auto;
transform: translateX(-50%);
padding-top: 16px;
padding-bottom: 16px;
display: block;
min-width: 0;
}
}
.navigation--footer & {
position: relative;
bottom: auto;
left: auto;
right: auto;
pointer-events: all;
display: block;
padding: 0;
margin-top: 8px;
min-width: 0;
@include bp(md-min) {
margin-top: 72px;
}
}
.navigation__item.is-active & {
pointer-events: all;
}
}
.navigation__item {
position: relative;
opacity: 0;
transition-property: opacity;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
.navigation--idle & {
@include bp(md-min) {
display: inline-block;
}
}
.navigation--footer & {
opacity: 1;
}
.navigation__list--parents > & {
transition-property: opacity;
padding: 16px 0;
@media only screen and (min-width: #{$bp-sm-min}) and (orientation: portrait) {
text-align: right;
}
@include bp(md-min) {
text-align: right;
}
.navigation--idle & {
@include bp(md-min) {
text-align: left;
padding: 0 0 1px;
flex-basis: 100%;
}
}
.navigation--footer & {
text-align: left;
display: block;
padding: 0;
@include bp(md-min) {
display: inline-block;
padding: 0 28px;
}
}
&:not(:first-child) {
.navigation--footer & {
margin-top: 24px;
@include bp(md-min) {
margin-top: 0;
}
}
}
}
&.navigation__item--divisible-by-2 {
.navigation__list--parents & {
.navigation--footer & {
@include bp(md-min) {
flex: 1 1 calc(100%/2); /* stylelint-disable-line plugin/no-unsupported-browser-features */
max-width: calc(100%/2); /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
}
}
&.navigation__item--divisible-by-3 {
.navigation__list--parents & {
.navigation--footer & {
@include bp(md-min) {
flex: 1 1 calc(100%/3); /* stylelint-disable-line plugin/no-unsupported-browser-features */
max-width: calc(100%/3); /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
}
}
&.navigation__item--divisible-by-4 {
.navigation__list--parents & {
.navigation--footer & {
@include bp(md-min) {
flex: 1 1 calc(100%/4); /* stylelint-disable-line plugin/no-unsupported-browser-features */
max-width: calc(100%/4); /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
}
}
.navigation__list--children & {
display: block;
position: relative;
padding: 8px 0;
.navigation--idle & {
@include bp(md-min) {
padding: 0;
}
}
.navigation--footer & {
padding: 0;
}
}
&.has-children {
position: relative;
}
&:nth-child(1) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing 0s both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing 0ms both;
}
}
}
&:nth-child(2) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*.1 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.2 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*.1 both;
}
}
}
&:nth-child(3) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*.4 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.4 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*.4 both;
}
}
}
&:nth-child(4) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*.7 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.6 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*.7 both;
}
}
}
&:nth-child(5) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.8 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*1 both;
}
}
}
&:nth-child(6) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.2 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*2 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.2 both;
}
}
}
&:nth-child(7) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.8 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*2.2 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.8 both;
}
}
}
&:nth-child(8) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*2.1 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*2.4 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*2.1 both;
}
}
}
}
.navigation__link {
position: relative;
display: block;
color: inherit;
text-decoration: none;
font-size: $font-size-h2-xs;
line-height: $line-height-h3-xs;
font-weight: $font-weight-medium;
@include bp(sm-min) {
transition-property: opacity;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
@media only screen and (min-width: #{$bp-sm-min}) and (orientation: portrait) {
font-size: $font-size-h2-lg;
line-height: $line-height-h2-lg;
}
@include bp(md-min) {
font-size: $font-size-h2-lg;
line-height: $line-height-h2-lg;
}
.navigation--idle & {
@include bp(md-min) {
font-size: $font-size-tiny;
line-height: $line-height-tiny;
padding: 0 15px;
display: flex;
align-items: center;
}
}
.navigation--footer & {
font-size: $font-size-h3-xs;
line-height: $line-height-h3-xs;
@include bp(md-min) {
font-size: $font-size-h3-lg;
line-height: $line-height-h3-lg;
}
}
.navigation__list--parents > .navigation__item > & {
.navigation--idle & {
@include bp(md-min) {
padding-bottom: 35px;
}
}
}
.navigation__list--children & {
font-size: $font-size-tiny;
line-height: $line-height-tiny;
padding: 4px 0;
@include bp(sm-min) {
opacity: .6;
}
.navigation--footer & {
opacity: 1;
font-size: $font-size-small;
line-height: $line-height-small;
font-weight: $font-weight-regular;
padding: 8px 0;
}
.navigation--idle & {
@include bp(md-min) {
padding: 8px 15px;
}
}
}
&:before {
.navigation__list--parents > .navigation__item > & {
.navigation--idle & {
@include bp(md-min) {
content: '';
height: 1px;
background-color: $color-text-03;
position: absolute;
bottom: 0;
left: 0;
right: 0;
opacity: 0;
transition-duration: 0ms;
transition-property: opacity;
transition-delay: $transition-duration*2.6;
}
}
body.core-has-loaded &,
.navigation--idle.is-ready & {
opacity: 1;
}
}
.navigation__item:first-child & {
.navigation__list--parents & {
left: 15px;
}
}
.navigation__item:last-child & {
.navigation__list--parents & {
right: 15px;
}
}
}
&:after {
.navigation__list--parents > .navigation__item > & {
.navigation--idle & {
@include bp(md-min) {
content: '';
height: 3px;
background-color: $color-text-03;
position: absolute;
bottom: -1px;
left: auto;
right: 15px;
width: 0;
transition-property: width, opacity;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
}
}
.navigation__item.is-current > & {
.navigation__list--parents > & {
body.core-has-loaded &,
.navigation--idle.is-ready & {
@include bp(md-min) {
opacity: 1;
transition-delay: $transition-duration*1.75;
width: calc(100% - 30px); /* stylelint-disable-line plugin/no-unsupported-browser-features */
left: 15px;
right: auto;
}
}
}
}
.navigation__item:hover > & {
.navigation__list--parents > & {
.navigation--idle & {
@include bp(md-min) {
width: calc(100% - 30px); /* stylelint-disable-line plugin/no-unsupported-browser-features */
left: 15px;
right: auto;
}
}
}
}
}
.navigation__item:not(:hover) > & {
.navigation__list--parents:hover > & {
.navigation:not(.navigation--idle):not(.navigation--footer) & {
@include bp(md-min) {
@media (hover: hover) {
opacity: .6;
transition-timing-function: $transition-easing-out;
}
}
}
}
.navigation__list--children:hover > & {
.navigation--footer & {
@include bp(sm-min) {
opacity: .6;
transition-timing-function: $transition-easing-out;
}
}
}
}
&:hover {
.navigation__list--children & {
opacity: 1;
transition-timing-function: $transition-easing-out;
}
}
&:focus {
html[data-whatintent='keyboard'] & {
opacity: 1;
transition-timing-function: $transition-easing-out;
}
}
}
.navigation__description {
display: none;
position: absolute;
top: 60px;
left: 0;
pointer-events: none;
.navigation--idle & {
@include bp(md-min) {
font-size: 14px;
color: inherit;
padding: 16px 15px 48px;
opacity: 0;
visibility: hidden;
transition-property: visibility, opacity;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
@include bp(lg-min) {
display: block;
}
}
.navigation__item:nth-child(1) & {
transition-delay: $transition-duration;
}
.navigation__item:nth-child(2) & {
transition-delay: $transition-duration*1.2;
}
.navigation__item:nth-child(3) & {
transition-delay: $transition-duration*1.4;
}
.navigation__item:nth-child(4) & {
transition-delay: $transition-duration*1.6;
}
.navigation__item:nth-child(5) & {
transition-delay: $transition-duration*1.8;
}
.navigation__item:nth-child(6) & {
transition-delay: $transition-duration*2;
}
.navigation__item:nth-child(7) & {
transition-delay: $transition-duration*2.2;
}
.navigation__item:nth-child(8) & {
transition-delay: $transition-duration*2.4;
}
body.core-has-loaded &,
.navigation--idle.is-ready & {
@include bp(md-min) {
opacity: 1;
visibility: visible;
transition-timing-function: $transition-easing-out;
}
body.is-hovering-nav & {
@include bp(md-min) {
opacity: 0;
visibility: hidden;
transition-timing-function: $transition-easing-out;
transition-delay: 0ms;
}
}
}
}
.navigation__arrow-icon {
transition-property: opacity;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
pointer-events: none;
display: none;
.navigation__item.is-current > & {
.navigation__list--parents > & {
@media only screen and (min-width: #{$bp-sm-min}) and (orientation: portrait) {
font-size: 54px;
position: absolute;
left: -70px;
top: 23px;
bottom: 0;
display: inline-block;
}
@include bp(md-min) {
font-size: 54px;
position: absolute;
left: -70px;
top: 23px;
bottom: 0;
display: inline-block;
}
.navigation--footer &,
.navigation--idle & {
@include bp(md-min) {
display: none;
}
}
}
}
.navigation__list--children & {
position: absolute;
left: -25px;
top: 12px;
bottom: 0;
font-size: 24px;
opacity: 0;
transform: translateX(-25px);
transition-property: visibility, opacity, transform;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
@include bp(sm-min) {
left: -32px;
display: inline-block;
}
.navigation--idle & {
@include bp(md-min) {
left: -25px;
}
@include bp(md-min) {
top: 8px;
}
}
.navigation--footer & {
display: none;
left: -40px;
top: 10px;
@include bp(lg-min) {
display: inline-block;
}
}
}
.navigation__item:not(:hover) > & {
.navigation__list--parents:hover > & {
.navigation:not(.navigation--idle) & {
@include bp(md-min) {
opacity: .6;
transition-timing-function: $transition-easing-out;
}
}
}
}
.navigation__item:hover > & {
.navigation__list--children & {
transform: translateX(0);
opacity: 1;
}
}
.navigation__link:focus + & {
.navigation__list--children & {
html[data-whatintent='keyboard'] & {
transform: translateX(0);
opacity: 1;
}
}
}
}
.navigation__chevron {
display: none;
font-size: 17px;
margin-left: 2px;
.navigation--idle & {
@include bp(md-min) {
display: block;
}
}
}
@keyframes setVisible {
0% {
transform: translateY(-10%);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import './navigation.scss';
interface INavigationSettings {
activeClass: string;
animationSpeed: number;
finishedClass: string;
idleModifier: string;
itemLinkClass: string;
navHiddenClass: string;
navHoverClass: string;
readyClass: string;
}
export default class Navigation extends Component {
static initSelector: string = '.js-navigation';
public dropdownItems: JQuery;
public dropdownLinks: JQuery;
public initHeight: number;
public continue: boolean;
public body: JQuery;
public animationDuration: number;
public isOpen: boolean = false;
settings: INavigationSettings;
constructor(target: HTMLElement) {
super(target);
this.body = $('body');
this.dropdownItems = this.element.find('.navigation__item.has-children');
this.dropdownLinks = this.element.find('.navigation__list--children .navigation__link');
this.initHeight = this.element.find('.navigation__item').first().outerHeight();
this.continue = false;
this.documentClickHandler = this.documentClickHandler.bind(this);
this.openDropdown = this.openDropdown.bind(this);
this.closeDropdown = this.closeDropdown.bind(this);
this.toggleHandler = this.toggleHandler.bind(this);
this.settings = {
activeClass: 'is-active',
animationSpeed: 250,
finishedClass: 'is-finished-animating',
idleModifier: 'navigation--idle',
itemLinkClass: 'navigation__link',
navHiddenClass: 'is-navigation-hidden',
navHoverClass: 'is-hovering-nav',
readyClass: 'is-ready',
};
this.init();
}
init(): void {
setTimeout(() => {
this.element.addClass(this.settings.readyClass);
}, 250);
setTimeout(() => {
this.element.addClass(this.settings.finishedClass);
}, 950);
this.setAnimationSpeed();
this.element.on('keyup.navigation', this.keyboardHandler.bind(this));
this.addTriggerListeners();
this.addFocusListener();
$(window).on('resize', this.resizeHandler.bind(this));
$(window).off('scroll', this.scrollHandler.bind(this));
$(window).on('scroll', this.scrollHandler.bind(this));
}
setAnimationSpeed(): void {
if ($(window).innerWidth() < Helpers.bp.md || this.body.hasClass(this.settings.navHiddenClass)) {
this.animationDuration = this.settings.animationSpeed;
} else {
this.animationDuration = 5;
}
}
scrollHandler(): void {
setTimeout(() => {
this.setAnimationSpeed();
}, 100);
this.addFocusListener();
}
resizeHandler(): void {
this.closeDropdown();
this.setIniHeights();
this.setAnimationSpeed();
}
addFocusListener(): void {
this.dropdownLinks.off('focus', this.sublinkFocusHandler.bind(this));
this.dropdownLinks.off('blur', this.sublinkBlurHandler.bind(this));
this.dropdownLinks.on('focus', this.sublinkFocusHandler.bind(this));
this.dropdownLinks.on('blur', this.sublinkBlurHandler.bind(this));
}
addMouseListeners(): void {
this.dropdownItems.on('mouseenter', this.openDropdown);
this.dropdownItems.on('mouseleave', this.closeDropdown);
}
removeMouseListeners(): void {
this.dropdownItems.off('mouseenter', this.openDropdown);
this.dropdownItems.off('mouseleave', this.closeDropdown);
}
addClickListeners(): void {
this.dropdownItems.children('.' + this.settings.itemLinkClass).on('click', this.toggleHandler);
}
removeClickListeners(): void {
this.dropdownItems.children('.' + this.settings.itemLinkClass).off('click', this.toggleHandler);
}
toggleHandler(): void {
event.preventDefault();
this.toggleDropdown(event);
}
addEventListeners(): void {
$(document).on('click.navigation', this.documentClickHandler);
}
addTriggerListeners(): void {
if ($(window).width() < Helpers.bp.lg && Helpers.isMobileDevice || $(window).width() <= Helpers.bp.sm) {
this.removeMouseListeners();
this.addClickListeners();
} else {
this.addMouseListeners();
this.removeClickListeners();
}
}
sublinkFocusHandler(event: JQuery.TriggeredEvent): void {
const targetElement: JQuery = $(event.currentTarget);
const parentElement: JQuery = $(targetElement.closest('.navigation__item.has-children'));
const inputDevice: string = $('html').attr('data-whatintent');
if (inputDevice === 'keyboard' && !parentElement.hasClass(this.settings.activeClass)) {
this.openDropdown();
}
}
sublinkBlurHandler(): void {
setTimeout(() => {
const isSublinkFocused: JQuery = $('.navigation__list--children .navigation__link:focus');
// avoid closing the dropdown if focus moved to another sublink
if (!isSublinkFocused.length) {
this.closeDropdown();
}
}, 1);
}
documentClickHandler(event: JQuery.TriggeredEvent): void {
if ($(event.target).closest(this.element.find('.navigation__list')).length === 0) {
this.closeDropdown();
}
}
setIniHeights(): void {
this.dropdownItems.removeAttr('style');
this.dropdownItems.each((index: number, element: HTMLElement) => {
$(element).css({
height: $(element).outerHeight(),
});
});
}
toggleDropdown(event: Event): void {
const previousOpen: JQuery = this.dropdownItems.filter('.' + this.settings.activeClass);
const parentElement: JQuery = $(event.currentTarget).closest('.navigation__item') as JQuery;
if (parentElement.hasClass(this.settings.activeClass)) {
this.animateClose(parentElement);
this.continue = true;
}
// Continue if there is nothing to close
if (this.continue) {
if (!parentElement.hasClass(this.settings.activeClass)) {
this.animateOpen(parentElement);
this.continue = false;
} else {
this.animateClose(previousOpen);
this.continue = true;
}
} else {
this.animateClose(previousOpen);
}
}
open(menuItem: JQuery): void {
menuItem.addClass(this.settings.activeClass);
menuItem.children('.' + this.settings.itemLinkClass).attr('aria-expanded', 'true');
this.addEventListeners();
}
animateOpen(parentElement: JQuery): void {
const parentHeight: number = parentElement.outerHeight();
const dropdownHeight: number = parentElement.find('.navigation__list').outerHeight();
const calculatedHeight: number = parentHeight + dropdownHeight;
if (parentElement.is(':animated')) {
return;
}
this.body.addClass(this.settings.navHoverClass);
this.initHeight = parentElement.outerHeight();
parentElement.stop().animate({
height: calculatedHeight,
}, this.animationDuration, (): void => {
this.open(parentElement);
}).css('overflow', 'visible');
}
openDropdown(): void {
if (!this.isOpen) {
const parentElement: JQuery = $(event.currentTarget).closest('.navigation__item.has-children') as JQuery;
this.animateOpen(parentElement);
this.isOpen = true;
}
}
closeDropdown(): void {
if (this.isOpen) {
this.animateClose($('.navigation__item.is-active') as JQuery);
this.isOpen = false;
}
}
close(): void {
const currentActiveItem: JQuery = this.dropdownItems.filter('.' + this.settings.activeClass);
currentActiveItem.removeClass(this.settings.activeClass);
currentActiveItem.children('.' + this.settings.itemLinkClass).attr('aria-expanded', 'false');
this.continue = true;
this.destroy();
}
animateClose(current: JQuery): void {
const currentActiveItem: JQuery = current;
this.body.removeClass(this.settings.navHoverClass);
if (currentActiveItem) {
this.close();
currentActiveItem.stop().animate({
height: this.initHeight,
}, this.animationDuration).css('overflow', 'visible');
}
}
keyboardHandler(event: KeyboardEvent): void {
if (event.key === 'Escape') {
this.close();
}
}
destroy(): void {
$(document).off('click.navigation', this.documentClickHandler);
}
}
<nav class="navigation js-navigation">
<ul class="navigation__list navigation__list--parents">
<li class="navigation__item ">
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#arrow-right-long-54"></use>
</svg>
<a href="#" class="navigation__link">
Home
</a>
<span class="navigation__description">Home is the place to be #staythefuckhome</span>
</li>
<li class="navigation__item has-children is-current ">
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#arrow-right-long-54"></use>
</svg>
<a href="#" class="navigation__link">
Services
<svg class="icon navigation__chevron">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#chevron-bottom-24"></use>
</svg>
</a>
<span class="navigation__description">Everything digital under one roof.</span>
<ul class="navigation__list navigation__list--children">
<li class="navigation__item ">
<a href="#" class="navigation__link">
Business transformation
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
<li class="navigation__item ">
<a href="#" class="navigation__link">
Design & Innovation
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
<li class="navigation__item ">
<a href="#" class="navigation__link">
Development
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
<li class="navigation__item ">
<a href="#" class="navigation__link">
Maintenance
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
</ul>
</li>
<li class="navigation__item has-children ">
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#arrow-right-long-54"></use>
</svg>
<a href="#" class="navigation__link">
Work
<svg class="icon navigation__chevron">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#chevron-bottom-24"></use>
</svg>
</a>
<span class="navigation__description">Home is the place to be #staythefuckhome</span>
<ul class="navigation__list navigation__list--children">
<li class="navigation__item ">
<a href="#" class="navigation__link">
Business transformation
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
<li class="navigation__item ">
<a href="#" class="navigation__link">
Design & Innovation
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
<li class="navigation__item ">
<a href="#" class="navigation__link">
Development
</a>
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#tiny-arrow-right-long-24"></use>
</svg>
</li>
</ul>
</li>
<li class="navigation__item ">
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#arrow-right-long-54"></use>
</svg>
<a href="#" class="navigation__link">
About
</a>
<span class="navigation__description">Home is the place to be #staythefuckhome</span>
</li>
<li class="navigation__item ">
<svg class="icon navigation__arrow-icon">
<use xlink:href="../../inc/svg/global.bc9cdd731ad718497c5d8f03d08908d4.svg#arrow-right-long-54"></use>
</svg>
<a href="#" class="navigation__link">
Contact
</a>
<span class="navigation__description">Got a project you dream of realizing? Get in touch!</span>
</li>
</ul>
</nav>
{% macro navItemLink(item, icon) %}
{% set attributes %}
{% if item.target %} target="{{ item.target }}"{% endif %}
{%- if item.attr_title %} title="{{ item.attr_title }}"{% endif %}
{%- if item._menu_item_xfn and item.link %} rel="{{ item._menu_item_xfn }}"{% endif %}
{% endset %}
{% set element = item.link ? 'a' : 'p' %}
<{{element}} {% if item.link %}href="{{ item.link }}"{% endif %} class="navigation__link" {{ attributes|trim }}>
{{ item.title }}
{% if icon %} {{ icon }} {% endif %}
</{{element}}>
{% endmacro %}
{% import _self as navigation %}
<nav class="navigation {{ modifier }} {{ class }}">
{% if data.items %}
<ul class="navigation__list navigation__list--parents">
{% for item in data.items %}
<li class="navigation__item {% if item.children %}has-children{% endif %}{% if item.current or item.current_item_ancestor %} is-current{% endif %} {% if 'navigation--footer' in modifier %}navigation__item--divisible-by-{{ loop.length }}{% endif %} {{ item.classes|join(' ') }}">
{% include '@icon' with { name: 'arrow-right-long-54', class: 'navigation__arrow-icon', modifier: '' } %}
{% if item.children and 'navigation--footer' not in modifier %}
{% set icon %}
{% include '@icon' with { name: 'chevron-bottom-24', class: 'navigation__chevron', modifier: '' } %}
{% endset %}
{% else %}
{% set icon = false %}
{% endif %}
{{ navigation.navItemLink(item, icon)|trim }}
{% if item.description %}
<span class="navigation__description">{{ item.description }}</span>
{% endif %}
{% if item.children %}
<ul class="navigation__list navigation__list--children">
{% for child in item.children %}
<li class="navigation__item {% if child.current %}is-current{% endif %} {{ child.classes|join(' ') }}">
{{ navigation.navItemLink(child) }}
{% include '@icon' with { name: 'tiny-arrow-right-long-24', class: 'navigation__arrow-icon', modifier: '' } %}
</li>
{% endfor %}
{% if 'footer__navigation' in class and loop.index == 1 %}
<li class="navigation__item {% if child.current %}is-current{% endif %}">
{% include '@icon' with { name: 'tiny-arrow-right-long-24', class: 'navigation__arrow-icon', modifier: '' } %}
<a class="navigation__link" data-cc="show-preferencesModal">Cookie preferences</a>
</li>
{% endif %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</nav>
{
"language": "en-US",
"modifier": "",
"class": "js-navigation",
"data": {
"items": [
{
"link": "#",
"title": "Home",
"description": "Home is the place to be #staythefuckhome"
},
{
"link": "#",
"title": "Services",
"description": "Everything digital under one roof.",
"current": "true",
"children": [
{
"link": "#",
"title": "Business transformation"
},
{
"link": "#",
"title": "Design & Innovation"
},
{
"link": "#",
"title": "Development"
},
{
"link": "#",
"title": "Maintenance"
}
]
},
{
"link": "#",
"title": "Work",
"description": "Home is the place to be #staythefuckhome",
"children": [
{
"link": "#",
"title": "Business transformation"
},
{
"link": "#",
"title": "Design & Innovation"
},
{
"link": "#",
"title": "Development"
}
]
},
{
"link": "#",
"title": "About",
"description": "Home is the place to be #staythefuckhome"
},
{
"link": "#",
"title": "Contact",
"description": "Got a project you dream of realizing? Get in touch!"
}
]
}
}
.navigation {
color: $color-text-03;
@include bp(md-min) {
display: flex;
justify-content: flex-end;
}
}
.navigation--idle {
@include bp(md-min) {
justify-content: space-evenly;
}
}
.navigation--footer {
display: block;
}
.navigation__list--parents {
display: block;
@media only screen and (min-width: #{$bp-sm-min}) and (orientation: portrait) {
display: flex;
flex-direction: column;
align-items: flex-end;
}
@include bp(md-min) {
display: flex;
flex-direction: column;
align-items: flex-end;
}
&:after {
.navigation--idle & {
@include bp(md-min) {
content: '';
height: 1px;
background-color: $color-text-03;
position: absolute;
bottom: 1px;
left: 15px;
right: 15px;
width: 0;
transition-property: width;
transition-duration: $transition-duration*1.5;
transition-timing-function: $transition-easing-in;
transition-delay: $transition-duration;
}
}
.navigation--idle.is-finished-animating & {
visibility: hidden;
}
body.core-has-loaded &
.navigation--idle.is-ready & {
@include bp(md-min) {
width: calc(100% - 30px); /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
}
.navigation--idle & {
@include bp(md-min) {
display: flex;
margin: 0 -15px;
flex-direction: row;
align-items: flex-start;
width: 100%;
}
}
.navigation--footer & {
display: flex;
flex-direction: column;
align-items: stretch;
@include bp(md-min) {
flex-direction: row;
justify-content: flex-end;
margin: 0 -28px;
}
}
}
.navigation__list--children {
padding-top: 4px;
padding-bottom: 4px;
margin: 0;
position: absolute;
bottom: 0;
left: 0;
z-index: map-get($zindex, 'default');
display: block;
pointer-events: none;
@include bp(sm-min) {
right: 0;
left: auto;
display: flex;
min-width: max-content; /* stylelint-disable-line plugin/no-unsupported-browser-features */
flex-direction: column;
align-items: flex-end;
}
.navigation--idle & {
@include bp(md-min) {
width: 100%;
left: 50%;
right: auto;
transform: translateX(-50%);
padding-top: 16px;
padding-bottom: 16px;
display: block;
min-width: 0;
}
}
.navigation--footer & {
position: relative;
bottom: auto;
left: auto;
right: auto;
pointer-events: all;
display: block;
padding: 0;
margin-top: 8px;
min-width: 0;
@include bp(md-min) {
margin-top: 72px;
}
}
.navigation__item.is-active & {
pointer-events: all;
}
}
.navigation__item {
position: relative;
opacity: 0;
transition-property: opacity;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
.navigation--idle & {
@include bp(md-min) {
display: inline-block;
}
}
.navigation--footer & {
opacity: 1;
}
.navigation__list--parents > & {
transition-property: opacity;
padding: 16px 0;
@media only screen and (min-width: #{$bp-sm-min}) and (orientation: portrait) {
text-align: right;
}
@include bp(md-min) {
text-align: right;
}
.navigation--idle & {
@include bp(md-min) {
text-align: left;
padding: 0 0 1px;
flex-basis: 100%;
}
}
.navigation--footer & {
text-align: left;
display: block;
padding: 0;
@include bp(md-min) {
display: inline-block;
padding: 0 28px;
}
}
&:not(:first-child) {
.navigation--footer & {
margin-top: 24px;
@include bp(md-min) {
margin-top: 0;
}
}
}
}
&.navigation__item--divisible-by-2 {
.navigation__list--parents & {
.navigation--footer & {
@include bp(md-min) {
flex: 1 1 calc(100%/2); /* stylelint-disable-line plugin/no-unsupported-browser-features */
max-width: calc(100%/2); /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
}
}
&.navigation__item--divisible-by-3 {
.navigation__list--parents & {
.navigation--footer & {
@include bp(md-min) {
flex: 1 1 calc(100%/3); /* stylelint-disable-line plugin/no-unsupported-browser-features */
max-width: calc(100%/3); /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
}
}
&.navigation__item--divisible-by-4 {
.navigation__list--parents & {
.navigation--footer & {
@include bp(md-min) {
flex: 1 1 calc(100%/4); /* stylelint-disable-line plugin/no-unsupported-browser-features */
max-width: calc(100%/4); /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
}
}
.navigation__list--children & {
display: block;
position: relative;
padding: 8px 0;
.navigation--idle & {
@include bp(md-min) {
padding: 0;
}
}
.navigation--footer & {
padding: 0;
}
}
&.has-children {
position: relative;
}
&:nth-child(1) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing 0s both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing 0ms both;
}
}
}
&:nth-child(2) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*.1 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.2 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*.1 both;
}
}
}
&:nth-child(3) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*.4 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.4 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*.4 both;
}
}
}
&:nth-child(4) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*.7 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.6 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*.7 both;
}
}
}
&:nth-child(5) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.8 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*1 both;
}
}
}
&:nth-child(6) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.2 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*2 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.2 both;
}
}
}
&:nth-child(7) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.8 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*2.2 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*1.8 both;
}
}
}
&:nth-child(8) {
.navigation__item.is-active & {
animation: setVisible $transition-duration $transition-easing $transition-duration*2.1 both;
}
body.core-has-loaded .navigation__list--parents > &,
.header--no-description .navigation__list--parents > &,
.navigation.is-ready .navigation__list--parents > & {
animation: setVisible $transition-duration $transition-easing $transition-duration*2.4 both;
@include bp(md-min) {
animation: setVisible $transition-duration $transition-easing $transition-duration*2.1 both;
}
}
}
}
.navigation__link {
position: relative;
display: block;
color: inherit;
text-decoration: none;
font-size: $font-size-h2-xs;
line-height: $line-height-h3-xs;
font-weight: $font-weight-medium;
@include bp(sm-min) {
transition-property: opacity;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
@media only screen and (min-width: #{$bp-sm-min}) and (orientation: portrait) {
font-size: $font-size-h2-lg;
line-height: $line-height-h2-lg;
}
@include bp(md-min) {
font-size: $font-size-h2-lg;
line-height: $line-height-h2-lg;
}
.navigation--idle & {
@include bp(md-min) {
font-size: $font-size-tiny;
line-height: $line-height-tiny;
padding: 0 15px;
display: flex;
align-items: center;
}
}
.navigation--footer & {
font-size: $font-size-h3-xs;
line-height: $line-height-h3-xs;
@include bp(md-min) {
font-size: $font-size-h3-lg;
line-height: $line-height-h3-lg;
}
}
.navigation__list--parents > .navigation__item > & {
.navigation--idle & {
@include bp(md-min) {
padding-bottom: 35px;
}
}
}
.navigation__list--children & {
font-size: $font-size-tiny;
line-height: $line-height-tiny;
padding: 4px 0;
@include bp(sm-min) {
opacity: .6;
}
.navigation--footer & {
opacity: 1;
font-size: $font-size-small;
line-height: $line-height-small;
font-weight: $font-weight-regular;
padding: 8px 0;
}
.navigation--idle & {
@include bp(md-min) {
padding: 8px 15px;
}
}
}
&:before {
.navigation__list--parents > .navigation__item > & {
.navigation--idle & {
@include bp(md-min) {
content: '';
height: 1px;
background-color: $color-text-03;
position: absolute;
bottom: 0;
left: 0;
right: 0;
opacity: 0;
transition-duration: 0ms;
transition-property: opacity;
transition-delay: $transition-duration*2.6;
}
}
body.core-has-loaded &,
.navigation--idle.is-ready & {
opacity: 1;
}
}
.navigation__item:first-child & {
.navigation__list--parents & {
left: 15px;
}
}
.navigation__item:last-child & {
.navigation__list--parents & {
right: 15px;
}
}
}
&:after {
.navigation__list--parents > .navigation__item > & {
.navigation--idle & {
@include bp(md-min) {
content: '';
height: 3px;
background-color: $color-text-03;
position: absolute;
bottom: -1px;
left: auto;
right: 15px;
width: 0;
transition-property: width, opacity;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
}
}
.navigation__item.is-current > & {
.navigation__list--parents > & {
body.core-has-loaded &,
.navigation--idle.is-ready & {
@include bp(md-min) {
opacity: 1;
transition-delay: $transition-duration*1.75;
width: calc(100% - 30px); /* stylelint-disable-line plugin/no-unsupported-browser-features */
left: 15px;
right: auto;
}
}
}
}
.navigation__item:hover > & {
.navigation__list--parents > & {
.navigation--idle & {
@include bp(md-min) {
width: calc(100% - 30px); /* stylelint-disable-line plugin/no-unsupported-browser-features */
left: 15px;
right: auto;
}
}
}
}
}
.navigation__item:not(:hover) > & {
.navigation__list--parents:hover > & {
.navigation:not(.navigation--idle):not(.navigation--footer) & {
@include bp(md-min) {
@media (hover: hover) {
opacity: .6;
transition-timing-function: $transition-easing-out;
}
}
}
}
.navigation__list--children:hover > & {
.navigation--footer & {
@include bp(sm-min) {
opacity: .6;
transition-timing-function: $transition-easing-out;
}
}
}
}
&:hover {
.navigation__list--children & {
opacity: 1;
transition-timing-function: $transition-easing-out;
}
}
&:focus {
html[data-whatintent='keyboard'] & {
opacity: 1;
transition-timing-function: $transition-easing-out;
}
}
}
.navigation__description {
display: none;
position: absolute;
top: 60px;
left: 0;
pointer-events: none;
.navigation--idle & {
@include bp(md-min) {
font-size: 14px;
color: inherit;
padding: 16px 15px 48px;
opacity: 0;
visibility: hidden;
transition-property: visibility, opacity;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
}
@include bp(lg-min) {
display: block;
}
}
.navigation__item:nth-child(1) & {
transition-delay: $transition-duration;
}
.navigation__item:nth-child(2) & {
transition-delay: $transition-duration*1.2;
}
.navigation__item:nth-child(3) & {
transition-delay: $transition-duration*1.4;
}
.navigation__item:nth-child(4) & {
transition-delay: $transition-duration*1.6;
}
.navigation__item:nth-child(5) & {
transition-delay: $transition-duration*1.8;
}
.navigation__item:nth-child(6) & {
transition-delay: $transition-duration*2;
}
.navigation__item:nth-child(7) & {
transition-delay: $transition-duration*2.2;
}
.navigation__item:nth-child(8) & {
transition-delay: $transition-duration*2.4;
}
body.core-has-loaded &,
.navigation--idle.is-ready & {
@include bp(md-min) {
opacity: 1;
visibility: visible;
transition-timing-function: $transition-easing-out;
}
body.is-hovering-nav & {
@include bp(md-min) {
opacity: 0;
visibility: hidden;
transition-timing-function: $transition-easing-out;
transition-delay: 0ms;
}
}
}
}
.navigation__arrow-icon {
transition-property: opacity;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
pointer-events: none;
display: none;
.navigation__item.is-current > & {
.navigation__list--parents > & {
@media only screen and (min-width: #{$bp-sm-min}) and (orientation: portrait) {
font-size: 54px;
position: absolute;
left: -70px;
top: 23px;
bottom: 0;
display: inline-block;
}
@include bp(md-min) {
font-size: 54px;
position: absolute;
left: -70px;
top: 23px;
bottom: 0;
display: inline-block;
}
.navigation--footer &,
.navigation--idle & {
@include bp(md-min) {
display: none;
}
}
}
}
.navigation__list--children & {
position: absolute;
left: -25px;
top: 12px;
bottom: 0;
font-size: 24px;
opacity: 0;
transform: translateX(-25px);
transition-property: visibility, opacity, transform;
transition-timing-function: $transition-easing-in;
transition-duration: $transition-duration;
@include bp(sm-min) {
left: -32px;
display: inline-block;
}
.navigation--idle & {
@include bp(md-min) {
left: -25px;
}
@include bp(md-min) {
top: 8px;
}
}
.navigation--footer & {
display: none;
left: -40px;
top: 10px;
@include bp(lg-min) {
display: inline-block;
}
}
}
.navigation__item:not(:hover) > & {
.navigation__list--parents:hover > & {
.navigation:not(.navigation--idle) & {
@include bp(md-min) {
opacity: .6;
transition-timing-function: $transition-easing-out;
}
}
}
}
.navigation__item:hover > & {
.navigation__list--children & {
transform: translateX(0);
opacity: 1;
}
}
.navigation__link:focus + & {
.navigation__list--children & {
html[data-whatintent='keyboard'] & {
transform: translateX(0);
opacity: 1;
}
}
}
}
.navigation__chevron {
display: none;
font-size: 17px;
margin-left: 2px;
.navigation--idle & {
@include bp(md-min) {
display: block;
}
}
}
@keyframes setVisible {
0% {
transform: translateY(-10%);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
import Component from '../component/component';
import Helpers from '../helpers/helpers';
import './navigation.scss';
interface INavigationSettings {
activeClass: string;
animationSpeed: number;
finishedClass: string;
idleModifier: string;
itemLinkClass: string;
navHiddenClass: string;
navHoverClass: string;
readyClass: string;
}
export default class Navigation extends Component {
static initSelector: string = '.js-navigation';
public dropdownItems: JQuery;
public dropdownLinks: JQuery;
public initHeight: number;
public continue: boolean;
public body: JQuery;
public animationDuration: number;
public isOpen: boolean = false;
settings: INavigationSettings;
constructor(target: HTMLElement) {
super(target);
this.body = $('body');
this.dropdownItems = this.element.find('.navigation__item.has-children');
this.dropdownLinks = this.element.find('.navigation__list--children .navigation__link');
this.initHeight = this.element.find('.navigation__item').first().outerHeight();
this.continue = false;
this.documentClickHandler = this.documentClickHandler.bind(this);
this.openDropdown = this.openDropdown.bind(this);
this.closeDropdown = this.closeDropdown.bind(this);
this.toggleHandler = this.toggleHandler.bind(this);
this.settings = {
activeClass: 'is-active',
animationSpeed: 250,
finishedClass: 'is-finished-animating',
idleModifier: 'navigation--idle',
itemLinkClass: 'navigation__link',
navHiddenClass: 'is-navigation-hidden',
navHoverClass: 'is-hovering-nav',
readyClass: 'is-ready',
};
this.init();
}
init(): void {
setTimeout(() => {
this.element.addClass(this.settings.readyClass);
}, 250);
setTimeout(() => {
this.element.addClass(this.settings.finishedClass);
}, 950);
this.setAnimationSpeed();
this.element.on('keyup.navigation', this.keyboardHandler.bind(this));
this.addTriggerListeners();
this.addFocusListener();
$(window).on('resize', this.resizeHandler.bind(this));
$(window).off('scroll', this.scrollHandler.bind(this));
$(window).on('scroll', this.scrollHandler.bind(this));
}
setAnimationSpeed(): void {
if ($(window).innerWidth() < Helpers.bp.md || this.body.hasClass(this.settings.navHiddenClass)) {
this.animationDuration = this.settings.animationSpeed;
} else {
this.animationDuration = 5;
}
}
scrollHandler(): void {
setTimeout(() => {
this.setAnimationSpeed();
}, 100);
this.addFocusListener();
}
resizeHandler(): void {
this.closeDropdown();
this.setIniHeights();
this.setAnimationSpeed();
}
addFocusListener(): void {
this.dropdownLinks.off('focus', this.sublinkFocusHandler.bind(this));
this.dropdownLinks.off('blur', this.sublinkBlurHandler.bind(this));
this.dropdownLinks.on('focus', this.sublinkFocusHandler.bind(this));
this.dropdownLinks.on('blur', this.sublinkBlurHandler.bind(this));
}
addMouseListeners(): void {
this.dropdownItems.on('mouseenter', this.openDropdown);
this.dropdownItems.on('mouseleave', this.closeDropdown);
}
removeMouseListeners(): void {
this.dropdownItems.off('mouseenter', this.openDropdown);
this.dropdownItems.off('mouseleave', this.closeDropdown);
}
addClickListeners(): void {
this.dropdownItems.children('.' + this.settings.itemLinkClass).on('click', this.toggleHandler);
}
removeClickListeners(): void {
this.dropdownItems.children('.' + this.settings.itemLinkClass).off('click', this.toggleHandler);
}
toggleHandler(): void {
event.preventDefault();
this.toggleDropdown(event);
}
addEventListeners(): void {
$(document).on('click.navigation', this.documentClickHandler);
}
addTriggerListeners(): void {
if ($(window).width() < Helpers.bp.lg && Helpers.isMobileDevice || $(window).width() <= Helpers.bp.sm) {
this.removeMouseListeners();
this.addClickListeners();
} else {
this.addMouseListeners();
this.removeClickListeners();
}
}
sublinkFocusHandler(event: JQuery.TriggeredEvent): void {
const targetElement: JQuery = $(event.currentTarget);
const parentElement: JQuery = $(targetElement.closest('.navigation__item.has-children'));
const inputDevice: string = $('html').attr('data-whatintent');
if (inputDevice === 'keyboard' && !parentElement.hasClass(this.settings.activeClass)) {
this.openDropdown();
}
}
sublinkBlurHandler(): void {
setTimeout(() => {
const isSublinkFocused: JQuery = $('.navigation__list--children .navigation__link:focus');
// avoid closing the dropdown if focus moved to another sublink
if (!isSublinkFocused.length) {
this.closeDropdown();
}
}, 1);
}
documentClickHandler(event: JQuery.TriggeredEvent): void {
if ($(event.target).closest(this.element.find('.navigation__list')).length === 0) {
this.closeDropdown();
}
}
setIniHeights(): void {
this.dropdownItems.removeAttr('style');
this.dropdownItems.each((index: number, element: HTMLElement) => {
$(element).css({
height: $(element).outerHeight(),
});
});
}
toggleDropdown(event: Event): void {
const previousOpen: JQuery = this.dropdownItems.filter('.' + this.settings.activeClass);
const parentElement: JQuery = $(event.currentTarget).closest('.navigation__item') as JQuery;
if (parentElement.hasClass(this.settings.activeClass)) {
this.animateClose(parentElement);
this.continue = true;
}
// Continue if there is nothing to close
if (this.continue) {
if (!parentElement.hasClass(this.settings.activeClass)) {
this.animateOpen(parentElement);
this.continue = false;
} else {
this.animateClose(previousOpen);
this.continue = true;
}
} else {
this.animateClose(previousOpen);
}
}
open(menuItem: JQuery): void {
menuItem.addClass(this.settings.activeClass);
menuItem.children('.' + this.settings.itemLinkClass).attr('aria-expanded', 'true');
this.addEventListeners();
}
animateOpen(parentElement: JQuery): void {
const parentHeight: number = parentElement.outerHeight();
const dropdownHeight: number = parentElement.find('.navigation__list').outerHeight();
const calculatedHeight: number = parentHeight + dropdownHeight;
if (parentElement.is(':animated')) {
return;
}
this.body.addClass(this.settings.navHoverClass);
this.initHeight = parentElement.outerHeight();
parentElement.stop().animate({
height: calculatedHeight,
}, this.animationDuration, (): void => {
this.open(parentElement);
}).css('overflow', 'visible');
}
openDropdown(): void {
if (!this.isOpen) {
const parentElement: JQuery = $(event.currentTarget).closest('.navigation__item.has-children') as JQuery;
this.animateOpen(parentElement);
this.isOpen = true;
}
}
closeDropdown(): void {
if (this.isOpen) {
this.animateClose($('.navigation__item.is-active') as JQuery);
this.isOpen = false;
}
}
close(): void {
const currentActiveItem: JQuery = this.dropdownItems.filter('.' + this.settings.activeClass);
currentActiveItem.removeClass(this.settings.activeClass);
currentActiveItem.children('.' + this.settings.itemLinkClass).attr('aria-expanded', 'false');
this.continue = true;
this.destroy();
}
animateClose(current: JQuery): void {
const currentActiveItem: JQuery = current;
this.body.removeClass(this.settings.navHoverClass);
if (currentActiveItem) {
this.close();
currentActiveItem.stop().animate({
height: this.initHeight,
}, this.animationDuration).css('overflow', 'visible');
}
}
keyboardHandler(event: KeyboardEvent): void {
if (event.key === 'Escape') {
this.close();
}
}
destroy(): void {
$(document).off('click.navigation', this.documentClickHandler);
}
}