diff --git a/data.json b/data.json index 81508e0..f647165 100644 --- a/data.json +++ b/data.json @@ -246,57 +246,179 @@ "url": "#", "children": [ { - "name": "Chi siamo", + "name": "Lauree triennali", "url": "#", "children": [ { - "name": "Piani, programmazione e strategie", + "name": "Architettura", "url": "#" }, { - "name": "Accordi relazioni e convenzioni", + "name": "Design della moda e arti multimediali", "url": "#" }, { - "name": "Sostenibilità", + "name": "Design del prodotto, della comunicazione visiva e degli interni", "url": "#" }, { - "name": "Didattica", + "name": "Design – Disegno industriale, sostenibilità e digitalizzazione (sede di Vicenza)", "url": "#" }, { - "name": "Parità di genere", - "url": "#" - }, - { - "name": "Immagine coordinata", + "name": "Urbanistica e pianificazione del territorio", "url": "#" } ] }, { - "name": "Amministrazione", + "name": "Lauree magistrali", "url": "#", "children": [ { - "name": "Direzione generale", + "name": "Architettura", "url": "#" }, { - "name": "Area didattica e servizi agli studenti", + "name": "Arti visive e moda", "url": "#" }, { - "name": "Area finanza e risorse umane", + "name": "Design del prodotto, della comunicazione e degli interni", "url": "#" }, { - "name": "Area ricerca, sistema bibliotecario e documentale", + "name": "MA in Architecture", "url": "#" }, { - "name": "Area tecnica", + "name": "Teatro e arti performative", + "url": "#" + }, + { + "name": "Urbanistica e pianificazione del territorio", + "url": "#" + } + ] + }, + { + "name": "Scuola di dottorato", + "url": "#", + "children": [ + { + "name": "Iscriviti", + "url": "#" + }, + { + "name": "News ed eventi", + "url": "#" + }, + { + "name": "Contatti", + "url": "#" + } + ] + }, + { + "name": "SSIBAP Scuola di specializzazione", + "url": "#", + "children": [ + { + "name": "Iscriviti", + "url": "#" + }, + { + "name": "News ed eventi", + "url": "#" + }, + { + "name": "Contatti", + "url": "#" + } + ] + }, + { + "name": "Workshop e attività", + "url": "#", + "children": [ + { + "name": "W.A.Ve.", + "url": "#" + }, + { + "name": "Workshop #1", + "url": "#" + }, + { + "name": "Workshop #2", + "url": "#" + } + ] + }, + { + "name": "Master e corsi", + "url": "#", + "children": [ + { + "name": "Master", + "url": "#" + }, + { + "name": "Corsi di perfezionamento", + "url": "#" + }, + { + "name": "Corsi singoli", + "url": "#" + }, + { + "name": "Corsi VIU", + "url": "#" + } + ] + }, + { + "name": "Ambiti", + "url": "#", + "children": [ + { + "name": "Architettura", + "url": "#" + }, + { + "name": "Arti multimediali", + "url": "#" + }, + { + "name": "Arti performative", + "url": "#" + }, + { + "name": "Arti visive", + "url": "#" + }, + { + "name": "Design degli interni", + "url": "#" + }, + { + "name": "Design del prodotto", + "url": "#" + }, + { + "name": "Design della comunicazione", + "url": "#" + }, + { + "name": "Moda", + "url": "#" + }, + { + "name": "Teatro", + "url": "#" + }, + { + "name": "Urbanistica", "url": "#" } ] @@ -305,23 +427,489 @@ }, { "name": "Orientamento", - "url": "#" + "url": "#", + "children": [ + { + "name": "Vivere e studiare a Venezia", + "url": "#", + "children": [ + { + "name": "Attività culturali", + "url": "#" + }, + { + "name": "Attività sportive", + "url": "#" + }, + { + "name": "Alloggi", + "url": "#" + }, + { + "name": "Mense", + "url": "#" + }, + { + "name": "Servizi di assistenza socio-sanitaria e a richiesta", + "url": "#" + }, + { + "name": "Corsi", + "url": "#" + }, + { + "name": "Mobilità e trasporti", + "url": "#" + } + ] + }, + { + "name": "Open Day", + "url": "#" + }, + { + "name": "Laboratori in mostra", + "url": "#", + "children": [ + { + "name": "Fashion at Iuav", + "url": "#" + }, + { + "name": "Design Open Lab", + "url": "#" + }, + { + "name": "MA Degree Show Iuav 2023", + "url": "#" + }, + { + "name": "Laboratorio in mostra #1", + "url": "#" + }, + { + "name": "Laboratorio in mostra #2", + "url": "#" + } + ] + }, + { + "name": "Attività per gli studenti in entrata", + "url": "#", + "children": [ + { + "name": "PCTO – Percorsi per le Competenze Trasversali e l’Orientamento", + "url": "#" + }, + { + "name": "Visite nelle scuole", + "url": "#" + }, + { + "name": "Lezioni d'estate", + "url": "#" + }, + { + "name": "Colloqui individuali", + "url": "#" + }, + { + "name": "Corsi di preparazione al test d'ingresso", + "url": "#" + } + ] + }, + { + "name": "Alumni", + "url": "#" + } + ] }, { "name": "Ricerca", - "url": "#" + "url": "#", + "children": [ + { + "name": "Strategia e governance", + "url": "#", + "children": [ + { + "name": "Organizzazione", + "url": "#" + }, + { + "name": "Comitato etico", + "url": "#" + }, + { + "name": "Programmi di finanziamento", + "url": "#" + }, + { + "name": "Missioni e trasferte per la ricerca", + "url": "#" + } + ] + }, + { + "name": "Tipologie di ricerca", + "url": "#", + "children": [ + { + "name": "Progetti di ricerca", + "url": "#" + }, + { + "name": "Assegni di ricerca", + "url": "#" + }, + { + "name": "Borse di ricerca", + "url": "#" + }, + { + "name": "Contratti di ricerca", + "url": "#" + }, + { + "name": "Dottorati", + "url": "#" + } + ] + }, + { + "name": "Strutture e network di ricerca", + "url": "#", + "children": [ + { + "name": "Laboratori", + "url": "#" + }, + { + "name": "Aggregazioni", + "url": "#" + }, + { + "name": "Cluster", + "url": "#" + }, + { + "name": "Unità di ricerca", + "url": "#" + }, + { + "name": "Infrastrutture", + "url": "#" + }, + { + "name": "Centro Superiore", + "url": "#" + } + ] + }, + { + "name": "Avviare una ricerca", + "url": "#", + "children": [ + { + "name": "Presentare un progetto", + "url": "#" + }, + { + "name": "Avviare un progetto", + "url": "#" + }, + { + "name": "Gestire un progetto", + "url": "#" + }, + { + "name": "Comunicare un progetto", + "url": "#" + } + ] + }, + { + "name": "Valutazione della ricerca", + "url": "#", + "children": [ + { + "name": "Valutazione dalla qualità della ricerca – VQR", + "url": "#" + }, + { + "name": "Valutatori per la ricerca", + "url": "#" + } + ] + }, + { + "name": "Divulgazione della ricerca", + "url": "#", + "children": [ + { + "name": "Archivio istituzionale della ricerca – Air", + "url": "#" + }, + { + "name": "Open access", + "url": "#" + }, + { + "name": "Open science", + "url": "#" + }, + { + "name": "Horizon Europe e Open Access", + "url": "#" + }, + { + "name": "Open Access: diritti d’autore e licenze d’uso", + "url": "#" + }, + { + "name": "APC e costi di pubblicazione: l'opportunità Wiley", + "url": "#" + }, + { + "name": "Archivi Open Access", + "url": "#" + } + ] + }, + { + "name": "Editoria e attività informativo-promozionale", + "url": "#", + "children": [ + { + "name": "Edizioni", + "url": "#" + }, + { + "name": "Dipartimento", + "url": "#" + }, + { + "name": "Scuola di dottorato", + "url": "#" + }, + { + "name": "Centro studi ClassicA", + "url": "#" + }, + { + "name": "Riviste", + "url": "#" + }, + { + "name": "Giornale Iuav", + "url": "#" + }, + { + "name": "Tutte le pubblicazioni", + "url": "#" + } + ] + }, + { + "name": "Contatti", + "url": "#" + } + ] }, { "name": "Terza missione", - "url": "#" + "url": "#", + "children": [ + { + "name": "Public engagement", + "url": "#" + }, + { + "name": "Spin-off", + "url": "#", + "children": [ + { + "name": "Regolamenti", + "url": "#" + }, + { + "name": "Elenco spin-off", + "url": "#" + } + ] + }, + { + "name": "Imprese ed enti", + "url": "#", + "children": [ + { + "name": "Avviare un accordo", + "url": "#" + }, + { + "name": "Informazioni aggiuntive", + "url": "#" + } + ] + }, + { + "name": "Progetti, attività ed eventi", + "url": "#", + "children": [ + { + "name": "Promuovere un evento", + "url": "#" + }, + { + "name": "Elenco attività", + "url": "#" + } + ] + }, + { + "name": "Divulgazione", + "url": "#" + }, + { + "name": "Contatti", + "url": "#" + } + ] }, { "name": "Internazionale", - "url": "#" + "url": "#", + "children": [ + { + "name": "Studenti in arrivo", + "url": "#", + "children": [ + { + "name": "Fare domanda", + "url": "#" + }, + { + "name": "Informazioni aggiuntive", + "url": "#" + }, + { + "name": "Contatti", + "url": "#" + } + ] + }, + { + "name": "Studenti in partenza", + "url": "#", + "children": [ + { + "name": "Erasmus+ Studio", + "url": "#" + }, + { + "name": "Erasmus+ Traineeship", + "url": "#" + }, + { + "name": "Erasmus+ ICM", + "url": "#" + }, + { + "name": "Workshop internazionali", + "url": "#" + }, + { + "name": "Progetto EUREKA", + "url": "#" + }, + { + "name": "Contatti", + "url": "#" + } + ] + }, + { + "name": "Mobilità docenti", + "url": "#", + "children": [ + { + "name": "Avviare un accordo", + "url": "#" + }, + { + "name": "Avviare un workshop internazionale", + "url": "#" + } + ] + }, + { + "name": "Mobilità personale tecnico-amministrativo", + "url": "#", + "children": [ + { + "name": "Avviare un accordo", + "url": "#" + } + ] + }, + { + "name": "Relazioni internazionali", + "url": "#", + "children": [ + { + "name": "Partenariati strategici", + "url": "#" + } + ] + }, + { + "name": "Contatti", + "url": "#" + } + ] }, { "name": "Tirocinio e lavoro", - "url": "#" + "url": "#", + "children": [ + { + "name": "Tirocinio", + "url": "#", + "children": [ + { + "name": "Avviare un tirocinio per studenti iscritti", + "url": "#" + }, + { + "name": "Avviare un tirocinio per laureati", + "url": "#" + }, + { + "name": "Avviare un tirocinio per enti e aziende", + "url": "#" + } + ] + }, + { + "name": "Lavoro", + "url": "#", + "children": [ + { + "name": "Professione e aggiornamento", + "url": "#" + }, + { + "name": "Opportunità e servizi", + "url": "#" + } + ] + }, + { + "name": "Contatti", + "url": "#" + } + ] } ] } diff --git a/src/assets/css/components/footer.css b/src/assets/css/components/footer.css index 0bcfcd2..9a5fcdb 100644 --- a/src/assets/css/components/footer.css +++ b/src/assets/css/components/footer.css @@ -33,6 +33,9 @@ @apply mt-3.75; li{ @apply border-t border-grey-500 md:border-t-0 flex items-center; + &:last-child{ + @apply border-b md:border-b-0; + } &::before{ content: ''; @screen md{ diff --git a/src/assets/css/components/header.css b/src/assets/css/components/header.css index c339fa2..30ad13c 100644 --- a/src/assets/css/components/header.css +++ b/src/assets/css/components/header.css @@ -46,6 +46,11 @@ @apply xl:w-3/4 flex justify-between items-center; nav{ @apply hidden xl:block; + ul{ + li{ + @apply select-none; + } + } > ul{ @apply flex items-center; > li{ @@ -71,15 +76,36 @@ @apply font-bold xl:w-1/4; } &-submenu{ + li{ + .menu-item{ + @apply relative overflow-hidden; + &-overlay{ + @apply absolute z-10 !important; + @apply bg-black w-full h-full block overflow-hidden top-0 left-0 pointer-events-none opacity-100; + } + > a{ + transition: color .4s ease; + @apply block text-black; + } + &:hover{ + > a{ + @apply relative text-white z-20; + } + } + } + } > li{ @apply mr-0; - > a{ - @apply inline-block w-full py-2 text-size-sm; + > .menu-item{ + > a{ + @apply inline-block w-full py-2 text-size-sm; + } } } &-0{ padding-left: var(--grid-offset); padding-right: calc(var(--grid-gutter) * 2); + orphans: 1; @apply absolute bottom-0 @@ -90,7 +116,7 @@ columns-3 pt-10 pb-12.5 - gap-x-[calc(var(--gutter-grid)*2)] + gap-x-[calc(var(--grid-gutter)*2)] opacity-0 invisible pointer-events-none ; @at-root .site-header-navbar__main ul li.is-toggled .site-header-navbar-submenu-0{ @@ -100,18 +126,22 @@ @apply hidden; } > li { - @apply inline-block w-full; - > a{ - @apply font-bold border-t-2 border-grey-400; + @apply break-inside-avoid; + > .menu-item{ + > a{ + @apply font-bold border-t-2 border-grey-400; + } } } } &:not(.site-header-navbar-submenu-0){ > li{ @apply border-t border-grey-200; - @apply grid grid-cols-4 gap-x-[var(--size-ratio-base)]; - > a{ - grid-column: 2 / 5; + .menu-item{ + @apply grid grid-cols-4 gap-x-[var(--size-ratio-base)]; + > a{ + grid-column: 2 / 5; + } } } } diff --git a/src/assets/css/components/mobilemenu.css b/src/assets/css/components/mobilemenu.css index 4c2c32a..992efaa 100644 --- a/src/assets/css/components/mobilemenu.css +++ b/src/assets/css/components/mobilemenu.css @@ -31,8 +31,8 @@ .menu-mobile-accordion-container{ @apply flex; a{ - min-height: calc(var(--spacing-size)*5); - width: calc(100% - var(--spacing-size) * 5); + min-height: 3.125rem; + width: calc(100% - 3.125rem); @apply block pt-3.5 pb-2.5 @@ -41,9 +41,9 @@ ; } button{ - @apply shrink-0 flex items-start w-xxl border-b border-grey-200; + @apply shrink-0 flex items-start w-12.5 border-b border-grey-200; svg{ - @apply pointer-events-none; + @apply w-full pointer-events-none; } } } @@ -90,7 +90,7 @@ &:first-child:last-child{ &::after{ content: ''; - @apply absolute bottom-0 right-0 block w-xxl border-b border-grey-200; + @apply absolute bottom-0 right-0 block w-12.5 border-b border-grey-200; } } } @@ -148,7 +148,7 @@ &:first-child:last-child{ &::after{ content: ''; - @apply absolute bottom-0 right-0 block w-xxl border-b border-grey-200; + @apply absolute bottom-0 right-0 block w-12.5 border-b border-grey-200; } } } @@ -159,6 +159,6 @@ } } &__secondary{ - @apply pt-sm pb-base bg-black text-white; + @apply pt-2.5 pb-5 bg-black text-white; } } \ No newline at end of file diff --git a/src/assets/js/blocks.js b/src/assets/js/blocks.js new file mode 100644 index 0000000..774455a --- /dev/null +++ b/src/assets/js/blocks.js @@ -0,0 +1,218 @@ +/** + * Scripts for custom blocks and components + */ + +window.addEventListener('load', (event) => { + + /** + * Vars + */ + // DOM elements + const marquees = document.querySelectorAll('.block-marquee-posts-row') + const cards = document.querySelectorAll('.card') + const secondaryNav = document.querySelector('.secondary-nav') + const secondaryNavTrigger = document.querySelector('.secondary-nav-trigger') + const parallaxImages = document.querySelectorAll('[data-banner-scroll-image]') + const teaseItems = document.querySelectorAll('.tease') + + /** + * Marquees + */ + let marqueesCardWidth + if (marquees.length > 0){ + marqueesCardWidth = window.getComputedStyle(marquees[0]).getPropertyValue('--marquee-card-width') + } + + marquees.forEach(marquee => { + + const marqueeSpeed = Number(marquee.getAttribute('data-marquee-row-speed')) + + let marqueePos = 0 + let marqueeIntvl + + //get vars + const items = marquee.getAttribute('data-marquee-row-items') + const wrapper = marquee.querySelector('.block-marquee-posts-row__wrapper') + + //clone cards in marquee + const cards = wrapper.querySelectorAll('.card') + cards.forEach(card => { + wrapper.appendChild(card.cloneNode(true)) + }) + + //calculate wrapper width + wrapper.style.width = `calc(${items*2}*${marqueesCardWidth})` + + marqueeIntvl = setInterval(() => { + if (marqueePos > 50) { + wrapper.style.transform = `translateX(0%)` + marqueePos = 0 + } else { + wrapper.style.transform = `translateX(-${marqueePos}%)` + } + marqueePos = marqueePos+marqueeSpeed + }, 0) + + marquee.addEventListener('mouseover', function(){ + clearInterval(marqueeIntvl) + }) + + marquee.addEventListener('mouseleave', function(){ + marqueeIntvl = setInterval(() => { + if (marqueePos > 50) { + wrapper.style.transform = `translateX(0%)` + marqueePos = 0 + } else { + wrapper.style.transform = `translateX(-${marqueePos}%)` + } + marqueePos = marqueePos+marqueeSpeed + }, 0) + }) + + }) + + /** + * Cards hover effect + */ + cards.forEach(card => { + const marqueeWrapper = card.closest('.block-marquee-posts-row') + card.addEventListener('mouseenter', function(){ + this.classList.add('is-hovered') + if (marqueeWrapper){ + marqueeWrapper.classList.add('is-hovered') + } + }) + card.addEventListener('mouseleave', function(){ + this.classList.remove('is-hovered') + if (marqueeWrapper){ + marqueeWrapper.classList.remove('is-hovered') + } + }) + }) + + /** + * Homepage Hero Banner parallax effect + */ + + document.addEventListener('scroll', function(){ + const scrollPosition = window.scrollY + parallaxImages.forEach(image => { + + const imageSpeed = image.getAttribute('data-banner-scroll-image-parallax-speed') + const imagePos = scrollPosition * imageSpeed + image.style.transform = `translateY(-${imagePos}px)` + + scrollBannerObserver.observe(image) + + }) + }) + + + let scrollBannerObserver = new IntersectionObserver((entries) => { + + entries.forEach(entry => { + + if(entry.isIntersecting && (entry.target.getBoundingClientRect().top > 0)){ + entry.target.classList.add('is-visible') + entry.target.style.opacity = entry.intersectionRatio + } + + }) + + }, + { + rootMargin: "0px 0px 0px 0px", + threshold: [0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1] + }) + + /** + * Tease height transition + */ + function updateTeaseHeight(tease){ + const teaseHeight = tease.clientHeight + const teaseImg = tease.querySelector('.tease__img') + const teaseText = tease.querySelector('.tease__main') + const teaseTextHeight = teaseText.clientHeight + teaseImg.style.height = `${teaseHeight-teaseTextHeight}px` + teaseText.style.marginTop = `${teaseHeight-teaseTextHeight}px` + } + + teaseItems.forEach(tease => { + updateTeaseHeight(tease) + }) + + /** + * Accordion + */ + const accordions = document.querySelectorAll('[data-accordion]') + accordions.forEach(accordion => { + const title = accordion.querySelector('[data-accordion-title]') + const content = accordion.querySelector('[data-accordion-content]') + title.addEventListener('click', function(){ + [...accordions].forEach(item => { item.classList.remove('is-toggled'); item.querySelector('[data-accordion-content]').style.display = 'none'}) + accordion.classList.toggle('is-toggled') + content.style.display = content.style.display == 'none' ? 'block' : 'none'; + }) + }) + + /** + * Secondary Nav + */ + // Get secondary nav height + function getSecondaryNavHeight(){ + document.documentElement.style.setProperty("--secondary-nav-height", `${secondaryNav.clientHeight}px`) + } + + if(secondaryNav !== null) getSecondaryNavHeight() + + let secondaryNavObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if(!entry.isIntersecting){ + secondaryNav.classList.add('is-visible') + } else { + secondaryNav.classList.remove('is-visible') + } + }) + }, + { + threshold: 1, + rootMargin: '-1px 0px 0px 0px' + }) + + if(secondaryNav !== null) secondaryNavObserver.observe(secondaryNavTrigger) + + function getSecondaryNavButtonWidth(){ + const secondaryNavButtonWidth = secondaryNav.querySelector('.secondary-nav__btn').clientWidth + secondaryNav.style.setProperty("--secondary-nav-button-width", `${secondaryNavButtonWidth}px`) + } + if(secondaryNav !== null) getSecondaryNavButtonWidth() + + if(secondaryNav !== null){ + window.addEventListener('scroll', () => { + currentScrollPos = window.scrollY + if (prevScrollPos > 0 && prevScrollPos < currentScrollPos ) { + secondaryNav.classList.remove('is-stacked') + } else if (prevScrollPos >= currentScrollPos) { + secondaryNav.classList.add('is-stacked') + } + }) + } + + /** + * Window resize function callbacks + */ + const resizeHandler = function(){ + + // if secondary nav exists update height and buttons width + if(secondaryNav !== null) getSecondaryNavHeight() + if(secondaryNav !== null) getSecondaryNavButtonWidth() + + //resize tease posts + teaseItems.forEach(tease => { + updateTeaseHeight(tease) + }) + } + + window.addEventListener('resize', resizeHandler) + +}) \ No newline at end of file diff --git a/src/assets/js/site.js b/src/assets/js/site.js index 7bc0552..fe6b88b 100644 --- a/src/assets/js/site.js +++ b/src/assets/js/site.js @@ -27,6 +27,7 @@ const logo = document.querySelector('.site-logo') const a11y_ariaexpanded = document.querySelectorAll('[aria-expanded="false"]') const menuItems = document.querySelectorAll('#site-header-main-nav > ul > .menu-item-has-children') + const menuSubItems = document.querySelectorAll('.site-header-navbar-submenu-0 li .menu-item') const menuBtn = document.getElementById('menu-btn') const overlay = document.getElementById('overlay') const menuMobile = document.getElementById('menu-mobile') @@ -39,7 +40,7 @@ * Check if iOS, then set --vh */ if (isIOS && window.matchMedia("(max-width: 1024px)").matches) { - $('html').addClass('is-ios') + document.documentElement.classList.add('is-ios') } function set100vh() { @@ -89,7 +90,7 @@ // const currentScrollPos = window.scrollY //if you start scrolling add class - if (currentScrollPos > 0) { + if (currentScrollPos > 0 && !menuIsOpen) { body.classList.add('is-scrolled') } else{ body.classList.remove('is-scrolled') @@ -97,11 +98,11 @@ // if scrolling down, hide header and position logo to the left // if scrolling up, show header and logo in initial position - if (prevScrollPos > 0 && prevScrollPos < currentScrollPos ) { + if (prevScrollPos > 0 && prevScrollPos < currentScrollPos && !menuIsOpen) { header.classList.add('is-hidden') logo.classList.add('is-visible') overlay.classList.remove('is-active') - } else if (prevScrollPos >= currentScrollPos) { + } else if (prevScrollPos >= currentScrollPos && !menuIsOpen) { header.classList.remove('is-hidden') logo.classList.remove('is-visible') } @@ -188,18 +189,79 @@ } } + function animateMenuItemsOverlay(e, itemOverlay, mouseenter = true){ + + // Get the bounding rectangle of target + const target = e.target + const rect = target.getBoundingClientRect() + let height = rect.height + // Mouse position + const y = e.clientY - rect.top + + const inOutFromTop = (y < height / 2) ? true : false + + let itemOverlayAnimations + let itemTransform + + if (inOutFromTop && mouseenter) { + itemOverlayAnimations = itemOverlay.animate([ + {transform: 'translate3d(0, -101%, 0)'}, + {transform: 'translate3d(0, 0, 0)'} + ], 250) + itemTransform = '0%' + } else if (inOutFromTop && !mouseenter) { + itemOverlayAnimations = itemOverlay.animate([ + {transform: 'translate3d(0, 0, 0)'}, + {transform: 'translate3d(0, -101%, 0)'} + ], 250) + itemTransform = '-101%' + } else if (!inOutFromTop && mouseenter) { + itemOverlayAnimations = itemOverlay.animate([ + {transform: 'translate3d(0, 101%, 0)'}, + {transform: 'translate3d(0, 0, 0)'} + ], 250) + itemTransform = '0%' + } else if (!inOutFromTop && !mouseenter) { + itemTransform = '101%' + itemOverlayAnimations = itemOverlay.animate([ + {transform: 'translate3d(0, 0, 0)'}, + {transform: 'translate3d(0, 101%, 0)'} + ], 250) + } + + itemOverlayAnimations.addEventListener('finish', function() { + itemOverlay.style.transform = `translate3d(0, ${itemTransform}, 0)`; + }) + } + + function openSubMenu(event){ + const item = event.target.closest('li') + event.preventDefault() + if (item.classList.contains('is-toggled')) { + toggleMenuSharedElements() + toggleDesktopMenu() + } else if (desktopMenuIsOpen){ + toggleDesktopMenu(this) + } else { + toggleMenuSharedElements() + toggleDesktopMenu(this) + } + } + menuItems.forEach(item => { - item.addEventListener('click', function(e){ - e.preventDefault() - if (item.classList.contains('is-toggled')) { - toggleMenuSharedElements() - toggleDesktopMenu() - } else if (desktopMenuIsOpen){ - toggleDesktopMenu(this) - } else { - toggleMenuSharedElements() - toggleDesktopMenu(this) - } + item.addEventListener('click', openSubMenu) + }) + + menuSubItems.forEach(subItem => { + const subItemOverlay = subItem.querySelector('.menu-item-overlay') + subItemOverlay.style.transform = 'translate3d(0, -101%, 0)' + + subItem.addEventListener('mouseenter', function (e) { + animateMenuItemsOverlay(e, subItemOverlay) + }) + + subItem.addEventListener('mouseleave', function (e) { + animateMenuItemsOverlay(e, subItemOverlay, false) }) }) @@ -283,4 +345,27 @@ } window.addEventListener('resize', resizeHandler) + /** + * Keyboard related functions and events + */ + document.onkeydown = function(evt) { + evt = evt || window.event; + var isEscape = false; + if ('key' in evt) { + isEscape = (evt.key === 'Escape' || evt.key === 'Esc'); + } else { + isEscape = (evt.keyCode === 27); + } + // if menu is open and press escape, toggle desktop or mobile menu + // based on media query + if (isEscape && menuIsOpen) { + toggleMenuSharedElements() + if (isDesktop.matches) { + toggleDesktopMenu() + } else { + toggleMobileMenu() + } + } + } + }) \ No newline at end of file diff --git a/src/layout/partials/menu.twig b/src/layout/partials/menu.twig index 85d495e..f350dcc 100644 --- a/src/layout/partials/menu.twig +++ b/src/layout/partials/menu.twig @@ -28,7 +28,10 @@