11import type { ReactiveController } from 'lit' ;
2- import { getElementByIdFromRoot , isString } from '../common/util.js' ;
3- import service from './tooltip-service.js' ;
2+ import {
3+ addWeakEventListener ,
4+ getElementByIdFromRoot ,
5+ isString ,
6+ } from '../common/util.js' ;
7+ import service from './service.js' ;
48import type IgcTooltipComponent from './tooltip.js' ;
59
610class TooltipController implements ReactiveController {
711 //#region Internal properties and state
812
13+ private static readonly _listeners = [
14+ 'pointerenter' ,
15+ 'pointerleave' ,
16+ ] as const ;
17+
918 private readonly _host : IgcTooltipComponent ;
1019 private readonly _options : TooltipCallbacks ;
1120
@@ -15,9 +24,10 @@ class TooltipController implements ReactiveController {
1524 private _showTriggers = new Set ( [ 'pointerenter' ] ) ;
1625 private _hideTriggers = new Set ( [ 'pointerleave' , 'click' ] ) ;
1726
18- private _anchor : TooltipAnchor ;
19- private _defaultAnchor : TooltipAnchor ;
20- private _isTransientAnchor = false ;
27+ private _anchor : WeakRef < Element > | null = null ;
28+ private _initialAnchor : WeakRef < Element > | null = null ;
29+
30+ private _isTransient = false ;
2131 private _open = false ;
2232
2333 //#endregion
@@ -37,9 +47,9 @@ class TooltipController implements ReactiveController {
3747 this . _addTooltipListeners ( ) ;
3848 service . add ( this . _host , this . _options . onEscape ) ;
3949 } else {
40- if ( this . _isTransientAnchor ) {
41- this . _isTransientAnchor = false ;
42- this . setAnchor ( this . _defaultAnchor ) ;
50+ if ( this . _isTransient ) {
51+ this . _isTransient = false ;
52+ this . setAnchor ( this . _initialAnchor ?. deref ( ) ) ;
4353 }
4454
4555 this . _removeTooltipListeners ( ) ;
@@ -51,7 +61,9 @@ class TooltipController implements ReactiveController {
5161 * Returns the current tooltip anchor target if any.
5262 */
5363 public get anchor ( ) : TooltipAnchor {
54- return this . _anchor ;
64+ return this . _isTransient
65+ ? this . _anchor ?. deref ( )
66+ : this . _initialAnchor ?. deref ( ) ;
5567 }
5668
5769 /**
@@ -105,17 +117,21 @@ class TooltipController implements ReactiveController {
105117 //#region Internal event listeners state
106118
107119 private _addAnchorListeners ( ) : void {
108- if ( ! this . _anchor ) return ;
120+ const anchor = this . anchor ;
121+
122+ if ( ! anchor ) {
123+ return ;
124+ }
109125
110126 this . _anchorAbortController = new AbortController ( ) ;
111127 const signal = this . _anchorAbortController . signal ;
112128
113129 for ( const each of this . _showTriggers ) {
114- this . _anchor . addEventListener ( each , this , { passive : true , signal } ) ;
130+ addWeakEventListener ( anchor , each , this , { passive : true , signal } ) ;
115131 }
116132
117133 for ( const each of this . _hideTriggers ) {
118- this . _anchor . addEventListener ( each , this , { passive : true , signal } ) ;
134+ addWeakEventListener ( anchor , each , this , { passive : true , signal } ) ;
119135 }
120136 }
121137
@@ -128,14 +144,9 @@ class TooltipController implements ReactiveController {
128144 this . _hostAbortController = new AbortController ( ) ;
129145 const signal = this . _hostAbortController . signal ;
130146
131- this . _host . addEventListener ( 'pointerenter' , this , {
132- passive : true ,
133- signal,
134- } ) ;
135- this . _host . addEventListener ( 'pointerleave' , this , {
136- passive : true ,
137- signal,
138- } ) ;
147+ for ( const event of TooltipController . _listeners ) {
148+ this . _host . addEventListener ( event , this , { passive : true , signal } ) ;
149+ }
139150 }
140151
141152 private _removeTooltipListeners ( ) : void {
@@ -147,33 +158,33 @@ class TooltipController implements ReactiveController {
147158
148159 //#region Event handlers
149160
150- private _handleTooltipEvent ( event : Event ) : void {
161+ private async _handleTooltipEvent ( event : Event ) : Promise < void > {
151162 switch ( event . type ) {
152163 case 'pointerenter' :
153- this . _options . onShow . call ( this . _host ) ;
164+ await this . _options . onShow . call ( this . _host ) ;
154165 break ;
155166 case 'pointerleave' :
156- this . _options . onHide . call ( this . _host ) ;
167+ await this . _options . onHide . call ( this . _host ) ;
157168 }
158169 }
159170
160- private _handleAnchorEvent ( event : Event ) : void {
171+ private async _handleAnchorEvent ( event : Event ) : Promise < void > {
161172 if ( ! this . _open && this . _showTriggers . has ( event . type ) ) {
162- this . _options . onShow . call ( this . _host ) ;
173+ await this . _options . onShow . call ( this . _host ) ;
163174 }
164175
165176 if ( this . _open && this . _hideTriggers . has ( event . type ) ) {
166- this . _options . onHide . call ( this . _host ) ;
177+ await this . _options . onHide . call ( this . _host ) ;
167178 }
168179 }
169180
170181 /** @internal */
171182 public handleEvent ( event : Event ) : void {
172183 if ( event . target === this . _host ) {
173184 this . _handleTooltipEvent ( event ) ;
174- } else if ( event . target === this . _anchor ) {
185+ } else if ( event . target === this . _anchor ?. deref ( ) ) {
175186 this . _handleAnchorEvent ( event ) ;
176- } else if ( event . target === this . _defaultAnchor ) {
187+ } else if ( event . target === this . _initialAnchor ?. deref ( ) ) {
177188 this . open = false ;
178189 this . _handleAnchorEvent ( event ) ;
179190 }
@@ -183,7 +194,10 @@ class TooltipController implements ReactiveController {
183194
184195 private _dispose ( ) : void {
185196 this . _removeAnchorListeners ( ) ;
197+ this . _removeTooltipListeners ( ) ;
198+ service . remove ( this . _host ) ;
186199 this . _anchor = null ;
200+ this . _initialAnchor = null ;
187201 }
188202
189203 //#region Public API
@@ -192,24 +206,35 @@ class TooltipController implements ReactiveController {
192206 * Removes all triggers from the previous `anchor` target and rebinds the current
193207 * sets back to the new value if it exists.
194208 */
195- public setAnchor ( value : TooltipAnchor , transient = false ) : void {
196- if ( this . _anchor === value ) return ;
209+ public setAnchor ( value : TooltipAnchor | string , transient = false ) : void {
210+ const newAnchor = isString ( value )
211+ ? getElementByIdFromRoot ( this . _host , value )
212+ : value ;
213+
214+ if ( this . _anchor ?. deref ( ) === newAnchor ) {
215+ return ;
216+ }
217+
218+ // Tooltip `show()` method called with a target. Set to hidden state.
219+ if ( transient && this . _open ) {
220+ this . open = false ;
221+ }
197222
198- if ( this . _anchor !== this . _defaultAnchor ) {
223+ if ( this . _anchor ?. deref ( ) !== this . _initialAnchor ?. deref ( ) ) {
199224 this . _removeAnchorListeners ( ) ;
200225 }
201226
202- this . _anchor = value ;
203- this . _isTransientAnchor = transient ;
227+ this . _anchor = newAnchor ? new WeakRef ( newAnchor ) : null ;
228+ this . _isTransient = transient ;
204229 this . _addAnchorListeners ( ) ;
205230 }
206231
207- public resolveAnchor ( value : Element | string | undefined ) : void {
232+ public resolveAnchor ( value : TooltipAnchor | string ) : void {
208233 const resolvedElement = isString ( value )
209234 ? getElementByIdFromRoot ( this . _host , value )
210235 : value ;
211236
212- this . _defaultAnchor = resolvedElement ;
237+ this . _initialAnchor = resolvedElement ? new WeakRef ( resolvedElement ) : null ;
213238 this . setAnchor ( resolvedElement ) ;
214239 }
215240
@@ -225,8 +250,6 @@ class TooltipController implements ReactiveController {
225250 /** @internal */
226251 public hostDisconnected ( ) : void {
227252 this . _dispose ( ) ;
228- this . _removeTooltipListeners ( ) ;
229- service . remove ( this . _host ) ;
230253 }
231254
232255 //#endregion
0 commit comments