From c114129a666f705880b17cccfe6bf373676c02cd Mon Sep 17 00:00:00 2001 From: Lae <30905719+mlaetitia@users.noreply.github.com> Date: Thu, 5 Nov 2026 15:32:48 +0000 Subject: [PATCH] Keep the open submenu while the pointer crosses the second menu row. Ports #252 to main. Co-Authored-By: Claude Opus 5.5 --- themes/osi/assets/js/build/theme.asset.php | 2 +- themes/osi/assets/js/build/theme.js | 2 +- themes/osi/assets/js/src/theme/mega-menu.js | 87 +++++++++++++------ .../_6_components.navigation--subnav.scss | 3 +- themes/osi/style.css | 2 +- themes/osi/style.css.map | 2 +- 6 files changed, 68 insertions(+), 30 deletions(-) diff --git a/themes/osi/assets/js/build/theme.asset.php b/themes/osi/assets/js/build/theme.asset.php index 02eeb27..ed20af8 100644 --- a/themes/osi/assets/js/build/theme.asset.php +++ b/themes/osi/assets/js/build/theme.asset.php @@ -1 +1 @@ - array(), 'version' => '1768336b7b2ec73f9a1a'); + array(), 'version' => '0e8d0c5357eb7a01b537'); diff --git a/themes/osi/assets/js/build/theme.js b/themes/osi/assets/js/build/theme.js index ce9785b..5fff76c 100644 --- a/themes/osi/assets/js/build/theme.js +++ b/themes/osi/assets/js/build/theme.js @@ -1 +1 @@ -(()=>{var e={690:()=>{jQuery},838:()=>{document.addEventListener("DOMContentLoaded",(function(){const e=document.querySelector(".wpdc-join-discussion");if(e){const t=document.createElement("h3");t.classList.add("wp-block-heading"),t.textContent="Join the discussion on Discourse!",e.insertBefore(t,e.firstChild)}}))},706:()=>{const e=document.querySelector(".header-main");if(e){const t=window.matchMedia("(max-width: 784px)");let a=!1,n=Math.max(0,window.scrollY),s=-1;const i=t=>e.classList.toggle("header-main-small",t),r=()=>{a=!1;const e=Math.max(0,window.scrollY);t.matches?e>120&&e>n?(s=-1,i(!0)):(-1===s&&(s=Math.max(n,e)),(e<=120||e40)),n=e};window.addEventListener("scroll",(()=>{a||(a=!0,window.requestAnimationFrame(r))}),{passive:!0}),t.addEventListener("change",r),r()}},7:()=>{var e;e=jQuery,(()=>{const t=e(".license-categories"),a=["ls","categories"],n=e(".license-search input"),s=e(".category-filter-cb"),i=e("#clear-search"),r=e("#clear-categories"),o=e("#search-prompt"),l=e("#toggle-license-search"),c=e("#toggle-license-categories");let d=window.location.href,u={};const m=()=>{const s={};n.length&&(""!==n.val()?(s.ls=n.val(),o.removeClass("hide"),o.find("span").html(n.val()),l.removeClass("open").siblings().removeClass("open")):(delete s.ls,o.addClass("hide"),l.addClass("open").siblings().addClass("open")));const i=t.find("input:checked");i.length?(i.each(((t,a)=>{s.categories||(s.categories=[]),s.categories.push(e(a).val())})),s.categories=s.categories.join(","),r.removeClass("hide"),c.find("span").html("("+i.length+")")):(delete s.categories,r.addClass("hide"),c.find("span").html(""));const m=new URL(d),h=new URL(d);for(const e of h.searchParams.entries())m.searchParams.delete(e[0]);for(const e in s)m.searchParams.set(e,s[e]);window.history.pushState({path:m.toString()},"",m.toString()),d=m.toString();let p=!1;a.forEach((e=>{u[e]===s[e]&&typeof u[e]==typeof s[e]||(p=!0)})),u=s,p&&g()},h=()=>{e(".license-table .term-item").on("click",(function(a){a.preventDefault();const n=e(this).attr("data-term");t.find('[value="'+n+'"]').prop("checked",!0).parent("label").addClass("active"),m()}))},g=()=>{const t=e(".license-table tbody");t.html("Loading..."),e.post(OSI_LICENSE_DB.ajaxURL,{action:OSI_LICENSE_DB.action,...u},(e=>{e.success&&e.data?.html&&e.data.licenses.length?(t.html(e.data.html),h()):t.html("No licenses found! Please try widening your search.")})).fail((()=>{t.html("Something went wrong! Please refresh and try again.")}))};(()=>{const e=new URL(d);a.forEach((t=>{e.searchParams.has(t)&&(u[t]=e.searchParams.get(t))}))})(),u.s&&n.val(u.s),u.categories&&u.categories.trim().split(",").forEach((e=>{e=e.trim();const a=t.find(`#osi-filter-cb-${e}`);a.length&&a.prop("checked",!0)})),s.each((function(){e(this).is(":checked")&&e(this).parent("label").addClass("active")})),s.is(":checked")&&r.removeClass("hide"),s.on("change",(function(){e(this).parent("label").toggleClass("active"),m()})),e(".license-search form").on("submit",(function(e){e.preventDefault(),m()})),i.on("click",(function(){n.val(""),m()})),r.on("click",(function(){s.prop("checked",!1).parent("label").removeClass("active"),m()})),h()})()},386:()=>{!function(e){const t=jQuery(".single-license .license-meta > span");let a,n;for(let e=0;e{const e=window.matchMedia("(min-width: 1200px)"),t=document.querySelector(".header-main"),a=document.querySelectorAll(".nav-main--menu > .menu-item.megamenu");if(t&&a.length){const n=e=>e.querySelector(":scope > a"),s=()=>{t.classList.toggle("is-nav-open",Boolean(document.querySelector(".nav-main--menu > .menu-item.megamenu.is-open")))},i=e=>{r(e),e.classList.add("is-dismissed"),s()},r=e=>{e.classList.remove("is-open");const t=n(e);t&&t.setAttribute("aria-expanded","false")},o=()=>{a.forEach(r),s()};a.forEach((t=>{const o=n(t);let l=null,c=!1;o&&o.setAttribute("aria-expanded","false");const d=()=>{!c&&e.matches&&(t.classList.remove("is-dismissed"),window.clearTimeout(l),a.forEach((e=>{e!==t&&r(e)})),t.classList.add("is-open"),o&&o.setAttribute("aria-expanded","true"),s())},u=e=>{window.clearTimeout(l);const a=()=>{r(t),s()};e?a():l=window.setTimeout(a,120)};o&&"#"===o.getAttribute("href")&&o.addEventListener("click",(a=>{e.matches&&(a.preventDefault(),t.classList.contains("is-open")?u(!0):d())})),t.addEventListener("mouseenter",d),t.addEventListener("mouseleave",(()=>{t.classList.remove("is-dismissed"),u(!1)})),t.addEventListener("focusin",d),t.addEventListener("focusout",(e=>{t.contains(e.relatedTarget)||u(!1)})),t.addEventListener("keydown",(e=>{"Escape"===e.key&&t.classList.contains("is-open")&&(c=!0,i(t),o&&o.focus(),c=!1)}))})),document.addEventListener("pointerdown",(e=>{!e.target.closest(".nav-main--menu > .menu-item.megamenu")&&document.querySelector(".nav-main--menu > .menu-item.megamenu.is-open")&&o()})),document.addEventListener("keydown",(e=>{if("Escape"!==e.key)return;const t=document.querySelector(".nav-main--menu > .menu-item.megamenu.is-open");t&&i(t)})),e.addEventListener("change",(e=>{e.matches||o()}))}},876:()=>{!function(e){function t(a){var n=a.closest(".menu-item-has-children"),s=a.siblings(".sub-menu").first(),i=s.hasClass("menu-collapse");i&&n.siblings(".tab-active").each((function(){t(e(this).children(".menu-toggle").first())})),s.toggleClass("menu-collapse",!i),n.toggleClass("tab-active",i),n.parent().closest(".sub-menu").toggleClass("can-overflow",i),i||(n.find(".sub-menu").addClass("menu-collapse").removeClass("can-overflow"),n.find(".tab-active").removeClass("tab-active"),n.find(".menu-toggle").removeClass("menu-toggle-active").attr("aria-expanded","false"),n.find("a[aria-expanded]").attr("aria-expanded","false")),a.toggleClass("menu-toggle-active",i).attr("aria-expanded",String(i)),a.siblings("a[aria-expanded]").first().attr("aria-expanded",String(i))}e(".sub-menu").addClass("menu-collapse"),e(".menu-item-has-children > a").each((function(a){var n=e(this),s=n.siblings(".sub-menu").first();if(s.length){var i=s.attr("id")||"osi-submenu-"+a;s.attr("id",i);var r=e('').attr({"aria-label":"Toggle submenu for "+n.text().trim(),"aria-expanded":"false","aria-controls":i}).insertAfter(n);n.closest(".nav-mobile--menu").length&&(n.attr("aria-expanded","false"),r.attr({"aria-hidden":"true",tabindex:"-1"}),n.on("click",(function(e){e.preventDefault(),t(r)})))}})),e(".menu-toggle").on("click",(function(a){a.preventDefault(),a.stopPropagation(),t(e(this))}))}(jQuery)},151:()=>{var e;(e=jQuery)("#openSearch").click((function(){e(".header-search-wrapper").slideDown(),e(this).addClass("active"),e(".header-search-wrapper .search-field").focus()})),e("#closeSearch").click((function(){e(".header-search-wrapper").slideUp(),e("#openSearch").removeClass("active")}))},823:()=>{var e=document.getElementById("openMainMenu"),t=document.getElementById("closeSidebar"),a=document.getElementById("openSidebar");function n(){var e=document.querySelectorAll("html")[0],t=e.className;-1===t.indexOf("open-the-menu")?e.className=t+" open-the-menu":e.className=t.replace(" open-the-menu","")}function s(){var e=document.querySelectorAll("html")[0],t=e.className;-1===t.indexOf("open-the-sidebar")?e.className=t+" open-the-sidebar":e.className=t.replace(" open-the-sidebar","")}null!=e&&(e.attachEvent?(e.attachEvent("onclick",n),null!==a&&a.attachEvent("onclick",s)):(e.addEventListener("click",n),null!==a&&a.addEventListener("click",s))),null!=t&&(t.attachEvent?t.attachEvent("onclick",n):t.addEventListener("click",n))},103:()=>{/(trident|msie)/i.test(navigator.userAgent)&&document.getElementById&&window.addEventListener&&window.addEventListener("hashchange",(function(){var e,t=location.hash.substring(1);/^[A-z0-9_-]+$/.test(t)&&(e=document.getElementById(t))&&(/^(?:a|select|input|button|textarea)$/i.test(e.tagName)||(e.tabIndex=-1),e.focus())}),!1)},624:()=>{"use strict";class e{constructor(e){this.tableNode=e,this.columnHeaders=e.querySelectorAll("thead th"),this.sortColumns=[];for(var t=0;tn.value?-1:1}));n.firstChild;)n.removeChild(n.lastChild);for(var d=0;d{var e;(e=jQuery)(document).ready((function(){e(".toggle-header h2").click((function(){var t=e(this).parent().attr("id");e(this).parent().hasClass("open")?(e(this).parent().removeClass("open"),e('[data-toggle="'+t+'"]').removeClass("open")):(e(this).parent().addClass("open"),e('[data-toggle="'+t+'"]').addClass("open"))}))}))}},t={};function a(n){var s=t[n];if(void 0!==s)return s.exports;var i=t[n]={exports:{}};return e[n](i,i.exports,a),i.exports}a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},a.d=(e,t)=>{for(var n in t)a.o(t,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";a(690),a(706),a(876),a(832),a(624),a(655),a(7),a(823),a(103),a(151),a(386),a(838)})()})(); \ No newline at end of file +(()=>{var e={690:()=>{jQuery},838:()=>{document.addEventListener("DOMContentLoaded",(function(){const e=document.querySelector(".wpdc-join-discussion");if(e){const t=document.createElement("h3");t.classList.add("wp-block-heading"),t.textContent="Join the discussion on Discourse!",e.insertBefore(t,e.firstChild)}}))},706:()=>{const e=document.querySelector(".header-main");if(e){const t=window.matchMedia("(max-width: 784px)");let a=!1,n=Math.max(0,window.scrollY),s=-1;const i=t=>e.classList.toggle("header-main-small",t),o=()=>{a=!1;const e=Math.max(0,window.scrollY);t.matches?e>120&&e>n?(s=-1,i(!0)):(-1===s&&(s=Math.max(n,e)),(e<=120||e40)),n=e};window.addEventListener("scroll",(()=>{a||(a=!0,window.requestAnimationFrame(o))}),{passive:!0}),t.addEventListener("change",o),o()}},7:()=>{var e;e=jQuery,(()=>{const t=e(".license-categories"),a=["ls","categories"],n=e(".license-search input"),s=e(".category-filter-cb"),i=e("#clear-search"),o=e("#clear-categories"),r=e("#search-prompt"),l=e("#toggle-license-search"),c=e("#toggle-license-categories");let d=window.location.href,u={};const h=()=>{const s={};n.length&&(""!==n.val()?(s.ls=n.val(),r.removeClass("hide"),r.find("span").html(n.val()),l.removeClass("open").siblings().removeClass("open")):(delete s.ls,r.addClass("hide"),l.addClass("open").siblings().addClass("open")));const i=t.find("input:checked");i.length?(i.each(((t,a)=>{s.categories||(s.categories=[]),s.categories.push(e(a).val())})),s.categories=s.categories.join(","),o.removeClass("hide"),c.find("span").html("("+i.length+")")):(delete s.categories,o.addClass("hide"),c.find("span").html(""));const h=new URL(d),m=new URL(d);for(const e of m.searchParams.entries())h.searchParams.delete(e[0]);for(const e in s)h.searchParams.set(e,s[e]);window.history.pushState({path:h.toString()},"",h.toString()),d=h.toString();let v=!1;a.forEach((e=>{u[e]===s[e]&&typeof u[e]==typeof s[e]||(v=!0)})),u=s,v&&p()},m=()=>{e(".license-table .term-item").on("click",(function(a){a.preventDefault();const n=e(this).attr("data-term");t.find('[value="'+n+'"]').prop("checked",!0).parent("label").addClass("active"),h()}))},p=()=>{const t=e(".license-table tbody");t.html("Loading..."),e.post(OSI_LICENSE_DB.ajaxURL,{action:OSI_LICENSE_DB.action,...u},(e=>{e.success&&e.data?.html&&e.data.licenses.length?(t.html(e.data.html),m()):t.html("No licenses found! Please try widening your search.")})).fail((()=>{t.html("Something went wrong! Please refresh and try again.")}))};(()=>{const e=new URL(d);a.forEach((t=>{e.searchParams.has(t)&&(u[t]=e.searchParams.get(t))}))})(),u.s&&n.val(u.s),u.categories&&u.categories.trim().split(",").forEach((e=>{e=e.trim();const a=t.find(`#osi-filter-cb-${e}`);a.length&&a.prop("checked",!0)})),s.each((function(){e(this).is(":checked")&&e(this).parent("label").addClass("active")})),s.is(":checked")&&o.removeClass("hide"),s.on("change",(function(){e(this).parent("label").toggleClass("active"),h()})),e(".license-search form").on("submit",(function(e){e.preventDefault(),h()})),i.on("click",(function(){n.val(""),h()})),o.on("click",(function(){s.prop("checked",!1).parent("label").removeClass("active"),h()})),m()})()},386:()=>{!function(e){const t=jQuery(".single-license .license-meta > span");let a,n;for(let e=0;e{const e=window.matchMedia("(min-width: 1200px)"),t=document.querySelector(".header-main"),a=".nav-main--menu > .menu-item.megamenu",n=document.querySelectorAll(a);if(t&&n.length){const s=e=>e.querySelector(":scope > a"),i=()=>document.querySelector(`${a}.is-open`),o=()=>{t.classList.toggle("is-nav-open",Boolean(i()))},r=e=>{v(),l(e),e.classList.add("is-dismissed"),document.querySelector(`${a}:hover`)?.classList.add("is-dismissed"),o()},l=e=>{e.classList.remove("is-open");const t=s(e);t&&t.setAttribute("aria-expanded","false")},c=()=>{n.forEach(l),o()};let d=null,u=null,h=0,m="mouse";const p=(e,t)=>{const a=Date.now(),n=()=>{window.clearTimeout(d),d=window.setTimeout((()=>{u=null,e()}),t)};u=()=>{Date.now()-a<600&&n()},n()},v=()=>{window.clearTimeout(d),u=null};t.addEventListener("pointerover",(e=>{m=e.pointerType})),t.addEventListener("mousemove",(e=>{u&&e.clientY>h&&u(),h=e.clientY})),n.forEach((t=>{const a=s(t);let d=!1;a&&a.setAttribute("aria-expanded","false");const h=()=>{!d&&e.matches&&(t.classList.remove("is-dismissed"),v(),n.forEach((e=>{e!==t&&l(e)})),t.classList.add("is-open"),a&&a.setAttribute("aria-expanded","true"),o())};a&&"#"===a.getAttribute("href")&&a.addEventListener("click",(a=>{e.matches&&(a.preventDefault(),t.classList.contains("is-open")?(v(),c()):h())})),t.addEventListener("mouseenter",(()=>{const e=i();"mouse"===m&&e&&e!==t&&t.offsetTop>e.offsetTop?p(h,150):h()})),t.addEventListener("mouseleave",(()=>{t.classList.remove("is-dismissed"),p(c,120)})),t.addEventListener("focusin",h),t.addEventListener("focusout",(e=>{t.contains(e.relatedTarget)||!t.classList.contains("is-open")||u||p(c,120)})),t.addEventListener("keydown",(e=>{"Escape"===e.key&&t.classList.contains("is-open")&&(d=!0,r(t),a&&a.focus(),d=!1)}))})),document.addEventListener("pointerdown",(e=>{!e.target.closest(a)&&i()&&c()})),document.addEventListener("keydown",(e=>{if("Escape"!==e.key)return;const t=i();t&&r(t)})),e.addEventListener("change",(e=>{e.matches||c()}))}},876:()=>{!function(e){function t(a){var n=a.closest(".menu-item-has-children"),s=a.siblings(".sub-menu").first(),i=s.hasClass("menu-collapse");i&&n.siblings(".tab-active").each((function(){t(e(this).children(".menu-toggle").first())})),s.toggleClass("menu-collapse",!i),n.toggleClass("tab-active",i),n.parent().closest(".sub-menu").toggleClass("can-overflow",i),i||(n.find(".sub-menu").addClass("menu-collapse").removeClass("can-overflow"),n.find(".tab-active").removeClass("tab-active"),n.find(".menu-toggle").removeClass("menu-toggle-active").attr("aria-expanded","false"),n.find("a[aria-expanded]").attr("aria-expanded","false")),a.toggleClass("menu-toggle-active",i).attr("aria-expanded",String(i)),a.siblings("a[aria-expanded]").first().attr("aria-expanded",String(i))}e(".sub-menu").addClass("menu-collapse"),e(".menu-item-has-children > a").each((function(a){var n=e(this),s=n.siblings(".sub-menu").first();if(s.length){var i=s.attr("id")||"osi-submenu-"+a;s.attr("id",i);var o=e('').attr({"aria-label":"Toggle submenu for "+n.text().trim(),"aria-expanded":"false","aria-controls":i}).insertAfter(n);n.closest(".nav-mobile--menu").length&&(n.attr("aria-expanded","false"),o.attr({"aria-hidden":"true",tabindex:"-1"}),n.on("click",(function(e){e.preventDefault(),t(o)})))}})),e(".menu-toggle").on("click",(function(a){a.preventDefault(),a.stopPropagation(),t(e(this))}))}(jQuery)},151:()=>{var e;(e=jQuery)("#openSearch").click((function(){e(".header-search-wrapper").slideDown(),e(this).addClass("active"),e(".header-search-wrapper .search-field").focus()})),e("#closeSearch").click((function(){e(".header-search-wrapper").slideUp(),e("#openSearch").removeClass("active")}))},823:()=>{var e=document.getElementById("openMainMenu"),t=document.getElementById("closeSidebar"),a=document.getElementById("openSidebar");function n(){var e=document.querySelectorAll("html")[0],t=e.className;-1===t.indexOf("open-the-menu")?e.className=t+" open-the-menu":e.className=t.replace(" open-the-menu","")}function s(){var e=document.querySelectorAll("html")[0],t=e.className;-1===t.indexOf("open-the-sidebar")?e.className=t+" open-the-sidebar":e.className=t.replace(" open-the-sidebar","")}null!=e&&(e.attachEvent?(e.attachEvent("onclick",n),null!==a&&a.attachEvent("onclick",s)):(e.addEventListener("click",n),null!==a&&a.addEventListener("click",s))),null!=t&&(t.attachEvent?t.attachEvent("onclick",n):t.addEventListener("click",n))},103:()=>{/(trident|msie)/i.test(navigator.userAgent)&&document.getElementById&&window.addEventListener&&window.addEventListener("hashchange",(function(){var e,t=location.hash.substring(1);/^[A-z0-9_-]+$/.test(t)&&(e=document.getElementById(t))&&(/^(?:a|select|input|button|textarea)$/i.test(e.tagName)||(e.tabIndex=-1),e.focus())}),!1)},624:()=>{"use strict";class e{constructor(e){this.tableNode=e,this.columnHeaders=e.querySelectorAll("thead th"),this.sortColumns=[];for(var t=0;tn.value?-1:1}));n.firstChild;)n.removeChild(n.lastChild);for(var d=0;d{var e;(e=jQuery)(document).ready((function(){e(".toggle-header h2").click((function(){var t=e(this).parent().attr("id");e(this).parent().hasClass("open")?(e(this).parent().removeClass("open"),e('[data-toggle="'+t+'"]').removeClass("open")):(e(this).parent().addClass("open"),e('[data-toggle="'+t+'"]').addClass("open"))}))}))}},t={};function a(n){var s=t[n];if(void 0!==s)return s.exports;var i=t[n]={exports:{}};return e[n](i,i.exports,a),i.exports}a.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return a.d(t,{a:t}),t},a.d=(e,t)=>{for(var n in t)a.o(t,n)&&!a.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";a(690),a(706),a(876),a(832),a(624),a(655),a(7),a(823),a(103),a(151),a(386),a(838)})()})(); \ No newline at end of file diff --git a/themes/osi/assets/js/src/theme/mega-menu.js b/themes/osi/assets/js/src/theme/mega-menu.js index 32c1dd4..cfffd04 100644 --- a/themes/osi/assets/js/src/theme/mega-menu.js +++ b/themes/osi/assets/js/src/theme/mega-menu.js @@ -7,7 +7,7 @@ License: GPLv2 Desktop behavior for .menu-item.megamenu items: keeps aria-expanded in sync, enforces one open panel at a time, closes on Escape or press outside, and mirrors the open state onto the header (`is-nav-open`) as the no-:has() - fallback for the CSS opaque-header rule, with a close-delay grace period. + fallback for the CSS opaque-header rule, with close and switch grace periods. Placeholder (href="#") triggers also toggle on click because taps don't reliably fire mouseenter/focusin at desktop widths (iPadOS). */ @@ -15,23 +15,26 @@ License: GPLv2 // keep in sync with $break-nav (assets/scss/_1_settings.breakpoints.scss) const desktopNav = window.matchMedia( '(min-width: 1200px)' ); const CLOSE_DELAY = 120; +const SWITCH_DELAY = 150; +const HOLD_LIMIT = 600; const header = document.querySelector( '.header-main' ); -const megaItems = document.querySelectorAll( '.nav-main--menu > .menu-item.megamenu' ); +const MEGA_ITEM = '.nav-main--menu > .menu-item.megamenu'; +const megaItems = document.querySelectorAll( MEGA_ITEM ); if ( header && megaItems.length ) { const triggerOf = ( item ) => item.querySelector( ':scope > a' ); + const findOpen = () => document.querySelector( `${ MEGA_ITEM }.is-open` ); const syncHeader = () => { - header.classList.toggle( - 'is-nav-open', - Boolean( document.querySelector( '.nav-main--menu > .menu-item.megamenu.is-open' ) ) - ); + header.classList.toggle( 'is-nav-open', Boolean( findOpen() ) ); }; const dismiss = ( item ) => { + release(); closeItem( item ); item.classList.add( 'is-dismissed' ); + document.querySelector( `${ MEGA_ITEM }:hover` )?.classList.add( 'is-dismissed' ); syncHeader(); }; @@ -48,9 +51,46 @@ if ( header && megaItems.length ) { syncHeader(); }; + let holdTimer = null; + let held = null; + let lastY = 0; + let pointerType = 'mouse'; + + const hold = ( action, delay ) => { + const start = Date.now(); + const arm = () => { + window.clearTimeout( holdTimer ); + holdTimer = window.setTimeout( () => { + held = null; + action(); + }, delay ); + }; + held = () => { + if ( Date.now() - start < HOLD_LIMIT ) { + arm(); + } + }; + arm(); + }; + + const release = () => { + window.clearTimeout( holdTimer ); + held = null; + }; + + header.addEventListener( 'pointerover', ( event ) => { + pointerType = event.pointerType; + } ); + + header.addEventListener( 'mousemove', ( event ) => { + if ( held && event.clientY > lastY ) { + held(); + } + lastY = event.clientY; + } ); + megaItems.forEach( ( item ) => { const trigger = triggerOf( item ); - let closeTimer = null; let suppressOpen = false; if ( trigger ) { @@ -62,7 +102,7 @@ if ( header && megaItems.length ) { return; } item.classList.remove( 'is-dismissed' ); - window.clearTimeout( closeTimer ); + release(); megaItems.forEach( ( other ) => { if ( other !== item ) { closeItem( other ); @@ -75,16 +115,12 @@ if ( header && megaItems.length ) { syncHeader(); }; - const close = ( immediately ) => { - window.clearTimeout( closeTimer ); - const doClose = () => { - closeItem( item ); - syncHeader(); - }; - if ( immediately ) { - doClose(); + const openOnHover = () => { + const panelOpen = findOpen(); + if ( 'mouse' === pointerType && panelOpen && panelOpen !== item && item.offsetTop > panelOpen.offsetTop ) { + hold( open, SWITCH_DELAY ); } else { - closeTimer = window.setTimeout( doClose, CLOSE_DELAY ); + open(); } }; @@ -95,22 +131,23 @@ if ( header && megaItems.length ) { } event.preventDefault(); if ( item.classList.contains( 'is-open' ) ) { - close( true ); + release(); + closeAll(); } else { open(); } } ); } - item.addEventListener( 'mouseenter', open ); + item.addEventListener( 'mouseenter', openOnHover ); item.addEventListener( 'mouseleave', () => { item.classList.remove( 'is-dismissed' ); - close( false ); + hold( closeAll, CLOSE_DELAY ); } ); item.addEventListener( 'focusin', open ); item.addEventListener( 'focusout', ( event ) => { - if ( ! item.contains( event.relatedTarget ) ) { - close( false ); + if ( ! item.contains( event.relatedTarget ) && item.classList.contains( 'is-open' ) && ! held ) { + hold( closeAll, CLOSE_DELAY ); } } ); // returning focus to the trigger fires focusin, which would reopen the panel @@ -128,8 +165,8 @@ if ( header && megaItems.length ) { document.addEventListener( 'pointerdown', ( event ) => { if ( - ! event.target.closest( '.nav-main--menu > .menu-item.megamenu' ) && - document.querySelector( '.nav-main--menu > .menu-item.megamenu.is-open' ) + ! event.target.closest( MEGA_ITEM ) && + findOpen() ) { closeAll(); } @@ -140,7 +177,7 @@ if ( header && megaItems.length ) { if ( 'Escape' !== event.key ) { return; } - const openItem = document.querySelector( '.nav-main--menu > .menu-item.megamenu.is-open' ); + const openItem = findOpen(); if ( openItem ) { dismiss( openItem ); } diff --git a/themes/osi/assets/scss/_6_components.navigation--subnav.scss b/themes/osi/assets/scss/_6_components.navigation--subnav.scss index 6d3c9bd..59efd69 100755 --- a/themes/osi/assets/scss/_6_components.navigation--subnav.scss +++ b/themes/osi/assets/scss/_6_components.navigation--subnav.scss @@ -556,7 +556,8 @@ $chevronDown: url('data:image/svg+xml;utf8,