@@ -13,35 +13,24 @@ const MOBILE_MQ = '(max-width: 1115px)';
1313// ---------------------------------------------------------------------------
1414
1515/**
16- * Open a dropdown by toggling its state attributes .
16+ * Set a dropdown's open/closed state.
1717 * @param {Element } dropdown - A .nav_dropdown_component element
18+ * @param {boolean } open
1819 */
19- function openDropdown ( dropdown ) {
20+ function setDropdownOpen ( dropdown , open ) {
2021 const toggle = dropdown . querySelector ( '.w-dropdown-toggle' ) ;
2122 if ( ! toggle ) return ;
22- toggle . setAttribute ( 'aria-expanded' , 'true' ) ;
23- toggle . classList . add ( 'w--open' ) ;
24- dropdown . querySelector ( '.w-dropdown-list' ) ?. classList . add ( 'w--open' ) ;
25- }
26-
27- /**
28- * Close a dropdown by toggling its state attributes.
29- * @param {Element } dropdown - A .nav_dropdown_component element
30- */
31- function closeDropdown ( dropdown ) {
32- const toggle = dropdown . querySelector ( '.w-dropdown-toggle' ) ;
33- if ( ! toggle ) return ;
34- toggle . setAttribute ( 'aria-expanded' , 'false' ) ;
35- toggle . classList . remove ( 'w--open' ) ;
36- dropdown . querySelector ( '.w-dropdown-list' ) ?. classList . remove ( 'w--open' ) ;
23+ toggle . setAttribute ( 'aria-expanded' , String ( open ) ) ;
24+ toggle . classList . toggle ( 'w--open' , open ) ;
25+ dropdown . querySelector ( '.w-dropdown-list' ) ?. classList . toggle ( 'w--open' , open ) ;
3726}
3827
3928/**
4029 * Close every open dropdown in the shadow root.
4130 * @param {ShadowRoot } root
4231 */
4332function closeAllDropdowns ( root ) {
44- root . querySelectorAll ( '.nav_dropdown_component.w-dropdown' ) . forEach ( closeDropdown ) ;
33+ root . querySelectorAll ( '.nav_dropdown_component.w-dropdown' ) . forEach ( ( d ) => setDropdownOpen ( d , false ) ) ;
4534}
4635
4736/**
@@ -61,28 +50,36 @@ function bindDropdowns(root) {
6150 if ( ! toggle ) return ;
6251
6352 // Desktop: hover
64- dropdown . addEventListener ( 'mouseenter' , ( ) => {
65- if ( mql . matches ) return ;
66- closeAllDropdowns ( root ) ;
67- openDropdown ( dropdown ) ;
68- } , { signal : ac . signal } ) ;
69-
70- dropdown . addEventListener ( 'mouseleave' , ( ) => {
71- if ( mql . matches ) return ;
72- closeDropdown ( dropdown ) ;
73- } , { signal : ac . signal } ) ;
53+ dropdown . addEventListener (
54+ 'mouseenter' ,
55+ ( ) => {
56+ if ( mql . matches ) return ;
57+ closeAllDropdowns ( root ) ;
58+ setDropdownOpen ( dropdown , true ) ;
59+ } ,
60+ { signal : ac . signal } ,
61+ ) ;
62+
63+ dropdown . addEventListener (
64+ 'mouseleave' ,
65+ ( ) => {
66+ if ( mql . matches ) return ;
67+ setDropdownOpen ( dropdown , false ) ;
68+ } ,
69+ { signal : ac . signal } ,
70+ ) ;
7471
7572 // Mobile: click (only when mobile menu is open)
76- toggle . addEventListener ( 'click' , ( e ) => {
77- if ( ! mql . matches ) return ;
78- e . preventDefault ( ) ;
79- const isOpen = toggle . getAttribute ( 'aria-expanded' ) === 'true' ;
80- if ( isOpen ) {
81- closeDropdown ( dropdown ) ;
82- } else {
83- openDropdown ( dropdown ) ;
84- }
85- } , { signal : ac . signal } ) ;
73+ toggle . addEventListener (
74+ 'click' ,
75+ ( e ) => {
76+ if ( ! mql . matches ) return ;
77+ e . preventDefault ( ) ;
78+ const isOpen = toggle . getAttribute ( 'aria-expanded' ) === 'true' ;
79+ setDropdownOpen ( dropdown , ! isOpen ) ;
80+ } ,
81+ { signal : ac . signal } ,
82+ ) ;
8683 } ) ;
8784
8885 return ( ) => controllers . forEach ( ( ac ) => ac . abort ( ) ) ;
@@ -125,71 +122,74 @@ function bindMobileMenu(root) {
125122 const midLine = lines [ 1 ] ;
126123 const botLine = lines [ 2 ] ;
127124
128- function setHamburgerOpen ( ) {
125+ /**
126+ * Apply transforms to hamburger lines to match the open/closed state.
127+ * @param {boolean } open - Whether the menu is open, which determines the transform applied.
128+ */
129+ function setHamburgerState ( open ) {
129130 if ( topLine ) {
130- topLine . style . transform = 'translate3d(0px, 0.425rem, 0px) scale3d(1, 1, 1) rotateX(0deg) rotateY(0deg) rotateZ(45deg) skew(0deg, 0deg)' ;
131- topLine . style . transformStyle = 'preserve-3d' ;
131+ topLine . style . transform = open
132+ ? 'translate3d(0px, 0.425rem, 0px) scale3d(1, 1, 1) rotateX(0deg) rotateY(0deg) rotateZ(45deg) skew(0deg, 0deg)'
133+ : '' ;
134+ topLine . style . transformStyle = open ? 'preserve-3d' : '' ;
132135 }
133136 if ( midLine ) {
134- midLine . style . opacity = '0 ';
137+ midLine . style . opacity = open ? '0' : ' ';
135138 }
136139 if ( botLine ) {
137- botLine . style . transform = 'translate3d(0px, -0.425rem, 0px) scale3d(1, 1, 1) rotateX(0deg) rotateY(0deg) rotateZ(-45deg) skew(0deg, 0deg)' ;
138- botLine . style . transformStyle = 'preserve-3d' ;
140+ botLine . style . transform = open
141+ ? 'translate3d(0px, -0.425rem, 0px) scale3d(1, 1, 1) rotateX(0deg) rotateY(0deg) rotateZ(-45deg) skew(0deg, 0deg)'
142+ : '' ;
143+ botLine . style . transformStyle = open ? 'preserve-3d' : '' ;
139144 }
140145 }
141146
142- function setHamburgerClosed ( ) {
143- if ( topLine ) { topLine . style . transform = '' ; topLine . style . transformStyle = '' ; }
144- if ( midLine ) { midLine . style . opacity = '' ; }
145- if ( botLine ) { botLine . style . transform = '' ; botLine . style . transformStyle = '' ; }
146- }
147-
148- function openMenu ( ) {
149- // Move menu into overlay (like original Webflow JS).
150- if ( overlay ) {
151- overlay . style . display = 'block' ;
152- overlay . style . height = `${ document . documentElement . scrollHeight } px` ;
153- overlay . appendChild ( menu ) ;
154- menu . setAttribute ( 'data-nav-menu-open' , '' ) ;
155- }
156- toggle . classList . add ( 'w--open' ) ;
157- toggle . setAttribute ( 'aria-expanded' , 'true' ) ;
158- setHamburgerOpen ( ) ;
159- document . body . style . overflow = 'hidden' ;
160- }
161-
162- function closeMenu ( ) {
163- toggle . classList . remove ( 'w--open' ) ;
164- toggle . setAttribute ( 'aria-expanded' , 'false' ) ;
165- setHamburgerClosed ( ) ;
166- closeAllDropdowns ( root ) ;
167- // Move menu back to original position.
168- if ( overlay ) {
169- menu . removeAttribute ( 'data-nav-menu-open' ) ;
170- if ( menuNextSibling ) {
171- menuParent . insertBefore ( menu , menuNextSibling ) ;
172- } else {
173- menuParent . appendChild ( menu ) ;
147+ /**
148+ * Set the menu open or closed.
149+ * @param {boolean } open - Whether the menu should be open.
150+ */
151+ function setMenuOpen ( open ) {
152+ toggle . classList . toggle ( 'w--open' , open ) ;
153+ toggle . setAttribute ( 'aria-expanded' , String ( open ) ) ;
154+ setHamburgerState ( open ) ;
155+
156+ if ( open ) {
157+ // Move menu into overlay (like original Webflow JS).
158+ if ( overlay ) {
159+ overlay . style . display = 'block' ;
160+ overlay . style . height = `${ document . documentElement . scrollHeight } px` ;
161+ overlay . appendChild ( menu ) ;
162+ menu . setAttribute ( 'data-nav-menu-open' , '' ) ;
174163 }
175- overlay . style . display = '' ;
176- overlay . style . height = '' ;
164+ document . body . style . overflow = 'hidden' ;
165+ } else {
166+ closeAllDropdowns ( root ) ;
167+ // Move menu back to original position.
168+ if ( overlay ) {
169+ menu . removeAttribute ( 'data-nav-menu-open' ) ;
170+ if ( menuNextSibling ) {
171+ menuParent . insertBefore ( menu , menuNextSibling ) ;
172+ } else {
173+ menuParent . appendChild ( menu ) ;
174+ }
175+ overlay . style . display = '' ;
176+ overlay . style . height = '' ;
177+ }
178+ document . body . style . overflow = '' ;
177179 }
178- document . body . style . overflow = '' ;
179180 }
180181
181- toggle . addEventListener ( 'click' , ( ) => {
182- const opening = ! toggle . classList . contains ( 'w--open' ) ;
183- if ( opening ) {
184- openMenu ( ) ;
185- } else {
186- closeMenu ( ) ;
187- }
188- } , { signal : ac . signal } ) ;
182+ toggle . addEventListener (
183+ 'click' ,
184+ ( ) => {
185+ setMenuOpen ( ! toggle . classList . contains ( 'w--open' ) ) ;
186+ } ,
187+ { signal : ac . signal } ,
188+ ) ;
189189
190190 return ( ) => {
191191 ac . abort ( ) ;
192- closeMenu ( ) ;
192+ setMenuOpen ( false ) ;
193193 } ;
194194}
195195
0 commit comments