11import type { ReactiveController , ReactiveControllerHost } from 'lit' ;
2+ import { createAbortHandle } from '../abort-handler.js' ;
23import { findElementFromEventPath , isEmpty } from '../util.js' ;
34
45/** Configuration options for the RootClickController */
@@ -33,25 +34,49 @@ interface RootClickControllerHost extends ReactiveControllerHost, HTMLElement {
3334}
3435
3536let rootClickListenerActive = false ;
37+ let pointerDownInsideHost = false ;
3638
37- const HostConfigs = new WeakMap <
39+ const HOST_CONFIGURATIONS = new WeakMap <
3840 RootClickControllerHost ,
3941 RootClickControllerConfig
4042> ( ) ;
4143
42- const ActiveHosts = new Set < RootClickControllerHost > ( ) ;
44+ const ACTIVE_HOSTS = new Set < RootClickControllerHost > ( ) ;
4345
44- function handleRootClick ( event : MouseEvent ) : void {
45- for ( const host of ActiveHosts ) {
46- const config = HostConfigs . get ( host ) ;
46+ function handlePointerDown ( event : PointerEvent ) : void {
47+ // Check if the pointerdown occurred inside any active host or its target
48+ for ( const host of ACTIVE_HOSTS ) {
49+ const config = HOST_CONFIGURATIONS . get ( host ) ;
50+ const targets : Set < Element > = new Set (
51+ config ?. target ? [ host , config . target ] : [ host ]
52+ ) ;
53+
54+ if ( findElementFromEventPath ( ( node ) => targets . has ( node ) , event ) ) {
55+ pointerDownInsideHost = true ;
56+ return ;
57+ }
58+ }
59+
60+ pointerDownInsideHost = false ;
61+ }
62+
63+ function 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 ) ;
4772
4873 if ( host . keepOpenOnOutsideClick ) {
4974 continue ;
5075 }
5176
52- const targets : Set < Element > = config ?. target
53- ? new Set ( [ host , config . target ] )
54- : new Set ( [ host ] ) ;
77+ const targets : Set < Element > = new Set (
78+ config ?. target ? [ host , config . target ] : [ host ]
79+ ) ;
5580
5681 if ( ! findElementFromEventPath ( ( node ) => targets . has ( node ) , event ) ) {
5782 config ?. onHide ? config . onHide . call ( host ) : host . hide ( ) ;
@@ -71,6 +96,7 @@ function handleRootClick(event: MouseEvent): void {
7196 */
7297class RootClickController implements ReactiveController {
7398 private readonly _host : RootClickControllerHost ;
99+ private readonly _abortHandler = createAbortHandle ( ) ;
74100 private _config ?: RootClickControllerConfig ;
75101
76102 constructor (
@@ -82,7 +108,7 @@ class RootClickController implements ReactiveController {
82108 this . _host . addController ( this ) ;
83109
84110 if ( this . _config ) {
85- HostConfigs . set ( this . _host , this . _config ) ;
111+ HOST_CONFIGURATIONS . set ( this . _host , this . _config ) ;
86112 }
87113 }
88114
@@ -91,13 +117,16 @@ class RootClickController implements ReactiveController {
91117 * document click listener is active if needed.
92118 */
93119 private _addActiveHost ( ) : void {
94- ActiveHosts . add ( this . _host ) ;
120+ ACTIVE_HOSTS . add ( this . _host ) ;
95121
96122 if ( this . _config ) {
97- HostConfigs . set ( this . _host , this . _config ) ;
123+ HOST_CONFIGURATIONS . set ( this . _host , this . _config ) ;
98124
99125 if ( ! rootClickListenerActive ) {
100- document . addEventListener ( 'click' , handleRootClick , { capture : true } ) ;
126+ const options = { capture : true , signal : this . _abortHandler . signal } ;
127+
128+ document . addEventListener ( 'pointerdown' , handlePointerDown , options ) ;
129+ document . addEventListener ( 'click' , handleRootClick , options ) ;
101130 rootClickListenerActive = true ;
102131 }
103132 }
@@ -108,10 +137,10 @@ class RootClickController implements ReactiveController {
108137 * document click listener if no other hosts are active.
109138 */
110139 private _removeActiveHost ( ) : void {
111- ActiveHosts . delete ( this . _host ) ;
140+ ACTIVE_HOSTS . delete ( this . _host ) ;
112141
113- if ( isEmpty ( ActiveHosts ) && rootClickListenerActive ) {
114- document . removeEventListener ( 'click' , handleRootClick , { capture : true } ) ;
142+ if ( isEmpty ( ACTIVE_HOSTS ) && rootClickListenerActive ) {
143+ this . _abortHandler . abort ( ) ;
115144 rootClickListenerActive = false ;
116145 }
117146 }
@@ -130,7 +159,7 @@ class RootClickController implements ReactiveController {
130159 public update ( config ?: RootClickControllerConfig ) : void {
131160 if ( config ) {
132161 this . _config = { ...this . _config , ...config } ;
133- HostConfigs . set ( this . _host , this . _config ) ;
162+ HOST_CONFIGURATIONS . set ( this . _host , this . _config ) ;
134163 }
135164
136165 this . _configureListeners ( ) ;
0 commit comments