@@ -21,16 +21,34 @@ export interface TooltipProperties {
2121 * @attr hover-delay - Hover length in ms before the tooltip is shown and hidden.
2222 * @attr variant - Variant of the tooltip, `"simple"` for strings only, inverted background,
2323 * `"rich"` for interactive content, background according to theme's surface.
24+ * @attr use-click - If present, the tooltip will be shown on click instead of on hover.
2425 * @prop {String } id - reflects the `"id"` attribute.
2526 * @prop {Number } hoverDelay - reflects the `"hover-delay"` attribute.
2627 * @prop {String } variant - reflects the `"variant"` attribute.
28+ * @prop {Boolean } useClick - reflects the `"click"` attribute.
2729 */
2830export class Tooltip extends HTMLElement {
2931 static readonly observedAttributes = [ 'id' , 'variant' ] ;
3032
3133 readonly #shadow = this . attachShadow ( { mode : 'open' } ) ;
3234 #anchor: HTMLElement | null = null ;
3335 #timeout: number | null = null ;
36+ #closing = false ;
37+
38+ get open ( ) : boolean {
39+ return this . matches ( ':popover-open' ) ;
40+ }
41+
42+ get useClick ( ) : boolean {
43+ return this . hasAttribute ( 'use-click' ) ?? false ;
44+ }
45+ set useClick ( useClick : boolean ) {
46+ if ( useClick ) {
47+ this . setAttribute ( 'use-click' , '' ) ;
48+ } else {
49+ this . removeAttribute ( 'use-click' ) ;
50+ }
51+ }
3452
3553 get hoverDelay ( ) : number {
3654 return this . hasAttribute ( 'hover-delay' ) ? Number ( this . getAttribute ( 'hover-delay' ) ) : 200 ;
@@ -110,42 +128,65 @@ export class Tooltip extends HTMLElement {
110128 } , this . hoverDelay ) ;
111129 } ;
112130
131+ toggle = ( ) : void => {
132+ // We need this check because clicking on the anchor while the tooltip is open will trigger both
133+ // the click event on the anchor and the toggle event from the backdrop of the tooltip.
134+ if ( ! this . #closing) {
135+ this . togglePopover ( ) ;
136+ }
137+ } ;
138+
113139 #setAttributes( ) : void {
114140 if ( ! this . hasAttribute ( 'role' ) ) {
115141 this . setAttribute ( 'role' , 'tooltip' ) ;
116142 }
117- this . setAttribute ( 'popover' , 'manual' ) ;
118- }
119-
120- #preventDefault( event : Event ) : void {
121- event . preventDefault ( ) ;
143+ this . setAttribute ( 'popover' , this . useClick ? 'auto' : 'manual' ) ;
122144 }
123145
124146 #stopPropagation( event : Event ) : void {
125147 event . stopPropagation ( ) ;
126148 }
127149
150+ #setClosing = ( event : Event ) : void => {
151+ if ( ( event as ToggleEvent ) . newState === 'closed' ) {
152+ this . #closing = true ;
153+ }
154+ } ;
155+
156+ #resetClosing = ( event : Event ) : void => {
157+ if ( ( event as ToggleEvent ) . newState === 'closed' ) {
158+ this . #closing = false ;
159+ }
160+ } ;
161+
128162 #registerEventListeners( ) : void {
129163 if ( this . #anchor) {
130- this . #anchor. addEventListener ( 'mouseenter' , this . showTooltip ) ;
131- this . #anchor. addEventListener ( 'mouseleave' , this . hideTooltip ) ;
132- // By default the anchor with a popovertarget would toggle the popover on click.
133- this . #anchor. addEventListener ( 'click' , this . #preventDefault) ;
164+ if ( this . useClick ) {
165+ this . #anchor. addEventListener ( 'click' , this . toggle ) ;
166+ } else {
167+ this . #anchor. addEventListener ( 'mouseenter' , this . showTooltip ) ;
168+ this . #anchor. addEventListener ( 'mouseleave' , this . hideTooltip ) ;
169+ this . addEventListener ( 'mouseleave' , this . hideTooltip ) ;
170+ }
134171 }
135- this . addEventListener ( 'mouseleave' , this . hideTooltip ) ;
136172 // Prevent interaction with the parent element.
137173 this . addEventListener ( 'click' , this . #stopPropagation) ;
138174 this . addEventListener ( 'mouseup' , this . #stopPropagation) ;
175+ this . addEventListener ( 'beforetoggle' , this . #setClosing) ;
176+ this . addEventListener ( 'toggle' , this . #resetClosing) ;
139177 }
140178
141179 #removeEventListeners( ) : void {
142180 if ( this . #anchor) {
181+ this . #anchor. removeEventListener ( 'click' , this . toggle ) ;
143182 this . #anchor. removeEventListener ( 'mouseenter' , this . showTooltip ) ;
144183 this . #anchor. removeEventListener ( 'mouseleave' , this . hideTooltip ) ;
145184 }
146185 this . removeEventListener ( 'mouseleave' , this . hideTooltip ) ;
147186 this . removeEventListener ( 'click' , this . #stopPropagation) ;
148187 this . removeEventListener ( 'mouseup' , this . #stopPropagation) ;
188+ this . removeEventListener ( 'beforetoggle' , this . #setClosing) ;
189+ this . removeEventListener ( 'toggle' , this . #resetClosing) ;
149190 }
150191
151192 #attachToAnchor( ) : void {
0 commit comments