@@ -33,8 +33,13 @@ interface RootClickControllerHost extends ReactiveControllerHost, HTMLElement {
3333 hide ( ) : void ;
3434}
3535
36- let rootClickListenerActive = false ;
37- let pointerDownInsideHost = false ;
36+ let ROOT_CLICK_LISTENER_ACTIVE = false ;
37+
38+ /** Shared abort handler for the singleton document listeners. */
39+ const SHARED_ABORT_HANDLER = createAbortHandle ( ) ;
40+
41+ /** Tracks which hosts had a pointerdown event inside them. */
42+ const POINTER_DOWN_HOSTS = new Set < RootClickControllerHost > ( ) ;
3843
3944const HOST_CONFIGURATIONS = new WeakMap <
4045 RootClickControllerHost ,
@@ -43,45 +48,42 @@ const HOST_CONFIGURATIONS = new WeakMap<
4348
4449const ACTIVE_HOSTS = new Set < RootClickControllerHost > ( ) ;
4550
51+ /** Returns the set of elements that should be considered as "inside" the host. */
52+ function getHostTargets (
53+ host : RootClickControllerHost ,
54+ config ?: RootClickControllerConfig
55+ ) : Set < Element > {
56+ return new Set ( config ?. target ? [ host , config . target ] : [ host ] ) ;
57+ }
58+
4659function handlePointerDown ( event : PointerEvent ) : void {
47- // Check if the pointerdown occurred inside any active host or its target
60+ POINTER_DOWN_HOSTS . clear ( ) ;
61+
4862 for ( const host of ACTIVE_HOSTS ) {
4963 const config = HOST_CONFIGURATIONS . get ( host ) ;
50- const targets : Set < Element > = new Set (
51- config ?. target ? [ host , config . target ] : [ host ]
52- ) ;
64+ const targets = getHostTargets ( host , config ) ;
5365
5466 if ( findElementFromEventPath ( ( node ) => targets . has ( node ) , event ) ) {
55- pointerDownInsideHost = true ;
56- return ;
67+ POINTER_DOWN_HOSTS . add ( host ) ;
5768 }
5869 }
59-
60- pointerDownInsideHost = false ;
6170}
6271
6372function handleRootClick ( event : PointerEvent ) : void {
64- // If the interaction started inside a host, don't trigger hide
65- if ( pointerDownInsideHost ) {
66- pointerDownInsideHost = false ;
67- return ;
68- }
69-
70- for ( const host of ACTIVE_HOSTS ) {
71- const config = HOST_CONFIGURATIONS . get ( host ) ;
72-
73- if ( host . keepOpenOnOutsideClick ) {
73+ for ( const host of [ ...ACTIVE_HOSTS ] ) {
74+ if ( host . keepOpenOnOutsideClick || POINTER_DOWN_HOSTS . has ( host ) ) {
7475 continue ;
7576 }
7677
77- const targets : Set < Element > = new Set (
78- config ?. target ? [ host , config . target ] : [ host ]
79- ) ;
78+ const config = HOST_CONFIGURATIONS . get ( host ) ;
79+ const targets = getHostTargets ( host , config ) ;
8080
8181 if ( ! findElementFromEventPath ( ( node ) => targets . has ( node ) , event ) ) {
8282 config ?. onHide ? config . onHide . call ( host ) : host . hide ( ) ;
8383 }
8484 }
85+
86+ POINTER_DOWN_HOSTS . clear ( ) ;
8587}
8688
8789/* blazorSuppress */
@@ -96,7 +98,6 @@ function handleRootClick(event: PointerEvent): void {
9698 */
9799class RootClickController implements ReactiveController {
98100 private readonly _host : RootClickControllerHost ;
99- private readonly _abortHandler = createAbortHandle ( ) ;
100101 private _config ?: RootClickControllerConfig ;
101102
102103 constructor (
@@ -121,14 +122,20 @@ class RootClickController implements ReactiveController {
121122
122123 if ( this . _config ) {
123124 HOST_CONFIGURATIONS . set ( this . _host , this . _config ) ;
125+ }
124126
125- if ( ! rootClickListenerActive ) {
126- const options = { capture : true , signal : this . _abortHandler . signal } ;
127-
128- document . addEventListener ( 'pointerdown' , handlePointerDown , options ) ;
129- document . addEventListener ( 'click' , handleRootClick , options ) ;
130- rootClickListenerActive = true ;
131- }
127+ if ( ! ROOT_CLICK_LISTENER_ACTIVE ) {
128+ const options : AddEventListenerOptions = {
129+ capture : true ,
130+ signal : SHARED_ABORT_HANDLER . signal ,
131+ } ;
132+
133+ document . addEventListener ( 'pointerdown' , handlePointerDown , {
134+ ...options ,
135+ passive : true ,
136+ } ) ;
137+ document . addEventListener ( 'click' , handleRootClick , options ) ;
138+ ROOT_CLICK_LISTENER_ACTIVE = true ;
132139 }
133140 }
134141
@@ -138,10 +145,12 @@ class RootClickController implements ReactiveController {
138145 */
139146 private _removeActiveHost ( ) : void {
140147 ACTIVE_HOSTS . delete ( this . _host ) ;
148+ HOST_CONFIGURATIONS . delete ( this . _host ) ;
149+ POINTER_DOWN_HOSTS . delete ( this . _host ) ;
141150
142- if ( isEmpty ( ACTIVE_HOSTS ) && rootClickListenerActive ) {
143- this . _abortHandler . abort ( ) ;
144- rootClickListenerActive = false ;
151+ if ( isEmpty ( ACTIVE_HOSTS ) && ROOT_CLICK_LISTENER_ACTIVE ) {
152+ SHARED_ABORT_HANDLER . abort ( ) ;
153+ ROOT_CLICK_LISTENER_ACTIVE = false ;
145154 }
146155 }
147156
0 commit comments