@@ -21,13 +21,16 @@ export interface TooltipProperties {
2121/**
2222 * @attr id - Id of the tooltip. Used for searching an anchor element with aria-describedby.
2323 * @attr hover-delay - Hover length in ms before the tooltip is shown and hidden.
24- * @attr variant - Variant of the tooltip, `"simple"` for strings only, inverted background,
25- * `"rich"` for interactive content, background according to theme's surface.
24+ * @attr variant - Variant of the tooltip, `"simple"` for strings only, inverted background, `"rich"` for interactive
25+ * content, background according to theme's surface.
2626 * @attr use-click - If present, the tooltip will be shown on click instead of on hover.
27+ * @attr use-hotkey - If present, the tooltip will be shown on hover but not when receiving focus. Requires a hotkey to
28+ * open when fosed (Alt-down). When `"use-click"` is present as well, use-click takes precedence.
2729 * @prop {String } id - reflects the `"id"` attribute.
2830 * @prop {Number } hoverDelay - reflects the `"hover-delay"` attribute.
2931 * @prop {String } variant - reflects the `"variant"` attribute.
3032 * @prop {Boolean } useClick - reflects the `"click"` attribute.
33+ * @prop {Boolean } useHotkey - reflects the `"use-hotkey"` attribute.
3134 */
3235export class Tooltip extends HTMLElement {
3336 static readonly observedAttributes = [ 'id' , 'variant' , 'jslogcontext' ] ;
@@ -42,6 +45,17 @@ export class Tooltip extends HTMLElement {
4245 return this . matches ( ':popover-open' ) ;
4346 }
4447
48+ get useHotkey ( ) : boolean {
49+ return this . hasAttribute ( 'use-hotkey' ) ?? false ;
50+ }
51+ set useHotkey ( useHotkey : boolean ) {
52+ if ( useHotkey ) {
53+ this . setAttribute ( 'use-hotkey' , '' ) ;
54+ } else {
55+ this . removeAttribute ( 'use-hotkey' ) ;
56+ }
57+ }
58+
4559 get useClick ( ) : boolean {
4660 return this . hasAttribute ( 'use-click' ) ?? false ;
4761 }
@@ -143,7 +157,8 @@ export class Tooltip extends HTMLElement {
143157 window . clearTimeout ( this . #timeout) ;
144158 }
145159 // Don't hide a rich tooltip when hovering over the tooltip itself.
146- if ( this . variant === 'rich' && event . relatedTarget === this ) {
160+ if ( this . variant === 'rich' &&
161+ ( event . relatedTarget === this || ( event . relatedTarget as Element ) ?. parentElement === this ) ) {
147162 return ;
148163 }
149164 this . #timeout = window . setTimeout ( ( ) => {
@@ -192,13 +207,24 @@ export class Tooltip extends HTMLElement {
192207 }
193208 } ;
194209
210+ #keyDown = ( event : KeyboardEvent ) : void => {
211+ if ( ( event . altKey && event . key === 'ArrowDown' ) || ( event . key === 'Escape' && this . open ) ) {
212+ this . toggle ( ) ;
213+ event . stopPropagation ( ) ;
214+ }
215+ } ;
216+
195217 #registerEventListeners( ) : void {
196218 if ( this . #anchor) {
197219 if ( this . useClick ) {
198220 this . #anchor. addEventListener ( 'click' , this . toggle ) ;
199221 } else {
200222 this . #anchor. addEventListener ( 'mouseenter' , this . showTooltip ) ;
201- this . #anchor. addEventListener ( 'focus' , this . showTooltip ) ;
223+ if ( this . useHotkey ) {
224+ this . #anchor. addEventListener ( 'keydown' , this . #keyDown) ;
225+ } else {
226+ this . #anchor. addEventListener ( 'focus' , this . showTooltip ) ;
227+ }
202228 this . #anchor. addEventListener ( 'blur' , this . hideTooltip ) ;
203229 this . #anchor. addEventListener ( 'mouseleave' , this . hideTooltip ) ;
204230 this . addEventListener ( 'mouseleave' , this . hideTooltip ) ;
@@ -220,6 +246,7 @@ export class Tooltip extends HTMLElement {
220246 this . #anchor. removeEventListener ( 'mouseenter' , this . showTooltip ) ;
221247 this . #anchor. removeEventListener ( 'focus' , this . showTooltip ) ;
222248 this . #anchor. removeEventListener ( 'blur' , this . hideTooltip ) ;
249+ this . #anchor. removeEventListener ( 'keydown' , this . #keyDown) ;
223250 this . #anchor. removeEventListener ( 'mouseleave' , this . hideTooltip ) ;
224251 }
225252 this . removeEventListener ( 'mouseleave' , this . hideTooltip ) ;
0 commit comments