@@ -37,47 +37,51 @@ class DetailsMenuElement extends HTMLElement {
3737 if ( ! summary . hasAttribute ( 'role' ) ) summary . setAttribute ( 'role' , 'button' )
3838 }
3939
40- details . addEventListener ( 'click' , shouldCommit )
41- details . addEventListener ( 'change' , shouldCommit )
42- details . addEventListener ( 'keydown' , keydown )
43- details . addEventListener ( 'toggle' , loadFragment , { once : true } )
44- details . addEventListener ( 'toggle' , closeCurrentMenu )
45- if ( this . preload ) {
46- details . addEventListener ( 'mouseover' , loadFragment , { once : true } )
47- }
48-
49- const subscriptions = [ focusOnOpen ( details ) ]
50- states . set ( this , { details, subscriptions, loaded : false } )
40+ const subscriptions = [
41+ fromEvent ( details , 'click' , e => shouldCommit ( details , this , e ) ) ,
42+ fromEvent ( details , 'change' , e => shouldCommit ( details , this , e ) ) ,
43+ fromEvent ( details , 'keydown' , e => keydown ( details , this , e ) ) ,
44+ fromEvent ( details , 'toggle' , ( ) => loadFragment ( details , this ) , { once : true } ) ,
45+ fromEvent ( details , 'toggle' , ( ) => closeCurrentMenu ( details ) ) ,
46+ this . preload
47+ ? fromEvent ( details , 'mouseover' , ( ) => loadFragment ( details , this ) , { once : true } )
48+ : NullSubscription ,
49+ ...focusOnOpen ( details )
50+ ]
51+
52+ states . set ( this , { subscriptions, loaded : false } )
5153 }
5254
5355 disconnectedCallback ( ) {
5456 const state = states . get ( this )
5557 if ( ! state ) return
56-
5758 states . delete ( this )
58-
59- const { details, subscriptions} = state
60- for ( const sub of subscriptions ) {
59+ for ( const sub of state . subscriptions ) {
6160 sub . unsubscribe ( )
6261 }
63- details . removeEventListener ( 'click' , shouldCommit )
64- details . removeEventListener ( 'change' , shouldCommit )
65- details . removeEventListener ( 'keydown' , keydown )
66- details . removeEventListener ( 'toggle' , loadFragment , { once : true } )
67- details . removeEventListener ( 'toggle' , closeCurrentMenu )
68- details . removeEventListener ( 'mouseover' , loadFragment , { once : true } )
6962 }
7063}
7164
7265const states = new WeakMap ( )
7366
74- function loadFragment ( event : Event ) {
75- const details = event . currentTarget
76- if ( ! ( details instanceof Element ) ) return
67+ type Subscription = { unsubscribe ( ) : void }
68+ const NullSubscription = { unsubscribe ( ) { } }
7769
78- const menu = details . querySelector ( 'details-menu' )
79- if ( ! menu ) return
70+ function fromEvent (
71+ target : EventTarget ,
72+ eventName : string ,
73+ onNext : EventHandler ,
74+ options : EventListenerOptionsOrUseCapture = false
75+ ) : Subscription {
76+ target . addEventListener ( eventName , onNext , options )
77+ return {
78+ unsubscribe : ( ) => {
79+ target . removeEventListener ( eventName , onNext , options )
80+ }
81+ }
82+ }
8083
84+ function loadFragment ( details : Element , menu : DetailsMenuElement ) {
8185 const src = menu . getAttribute ( 'src' )
8286 if ( ! src ) return
8387
@@ -94,7 +98,7 @@ function loadFragment(event: Event) {
9498 }
9599}
96100
97- function focusOnOpen ( details : Element ) {
101+ function focusOnOpen ( details : Element ) : Array < Subscription > {
98102 let isMouse = false
99103 const onmousedown = ( ) => ( isMouse = true )
100104 const onkeydown = ( ) => ( isMouse = false )
@@ -104,27 +108,19 @@ function focusOnOpen(details: Element) {
104108 if ( ! isMouse ) focusFirstItem ( details )
105109 }
106110
107- details . addEventListener ( 'mousedown' , onmousedown )
108- details . addEventListener ( 'keydown' , onkeydown )
109- details . addEventListener ( 'toggle' , ontoggle )
110-
111- return {
112- unsubscribe : ( ) => {
113- details . removeEventListener ( 'mousedown' , onmousedown )
114- details . removeEventListener ( 'keydown' , onkeydown )
115- details . removeEventListener ( 'toggle' , ontoggle )
116- }
117- }
111+ return [
112+ fromEvent ( details , 'mousedown' , onmousedown ) ,
113+ fromEvent ( details , 'keydown' , onkeydown ) ,
114+ fromEvent ( details , 'toggle' , ontoggle )
115+ ]
118116}
119117
120- function closeCurrentMenu ( event : Event ) {
121- const el = event . currentTarget
122- if ( ! ( el instanceof Element ) ) return
123- if ( ! el . hasAttribute ( 'open' ) ) return
118+ function closeCurrentMenu ( details : Element ) {
119+ if ( ! details . hasAttribute ( 'open' ) ) return
124120
125121 for ( const menu of document . querySelectorAll ( 'details[open] > details-menu' ) ) {
126122 const opened = menu . closest ( 'details' )
127- if ( opened && opened !== el && ! opened . contains ( el ) ) {
123+ if ( opened && opened !== details && ! opened . contains ( details ) ) {
128124 opened . removeAttribute ( 'open' )
129125 }
130126 }
@@ -163,13 +159,10 @@ function sibling(details: Element, next: boolean): ?HTMLElement {
163159
164160const ctrlBindings = navigator . userAgent . match ( / M a c i n t o s h / )
165161
166- function shouldCommit ( event : Event ) {
162+ function shouldCommit ( details : Element , menu : DetailsMenuElement , event : Event ) {
167163 const target = event . target
168164 if ( ! ( target instanceof Element ) ) return
169165
170- const details = event . currentTarget
171- if ( ! ( details instanceof Element ) ) return
172-
173166 // Ignore clicks from nested details.
174167 if ( target . closest ( 'details' ) !== details ) return
175168
@@ -219,9 +212,8 @@ function commit(selected: Element, details: Element) {
219212 )
220213}
221214
222- function keydown ( event : KeyboardEvent ) {
223- const details = event . currentTarget
224- if ( ! ( details instanceof Element ) ) return
215+ function keydown ( details : Element , menu : DetailsMenuElement , event : Event ) {
216+ if ( ! ( event instanceof KeyboardEvent ) ) return
225217 const isSummaryFocused = event . target instanceof Element && event . target . tagName === 'SUMMARY'
226218
227219 // Ignore key presses from nested details.
0 commit comments