| 
1 | 1 | <script lang="ts">  | 
2 |  | -    import { tick } from 'svelte';  | 
 | 2 | +    import { tick, hasContext } from 'svelte';  | 
3 | 3 |     import type { Placement } from '@floating-ui/dom';  | 
4 | 4 |     import { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';  | 
5 | 5 | 
  | 
 | 6 | +    export let portal: boolean = false;  | 
6 | 7 |     export let placement: Placement | undefined = undefined;  | 
7 | 8 |     export let padding: 'none' | 'm' = 'm';  | 
8 | 9 |     export let offsetAmount: number = 6;  | 
9 | 10 |     export let disabled = false;  | 
10 | 11 |     export let maxWidth = '11.25rem';  | 
 | 12 | +    export let delay: number = 0;  | 
11 | 13 | 
  | 
12 | 14 |     let show = false;  | 
13 | 15 |     let showing = false;  | 
14 |  | -    const id = 'tooltip-' + Math.random().toString(36).substring(2, 9);  | 
15 |  | -    let referenceElement: HTMLSpanElement;  | 
 | 16 | +    let delayTimeout: ReturnType<typeof setTimeout>;  | 
 | 17 | +
  | 
16 | 18 |     let tooltipElement: HTMLDivElement;  | 
 | 19 | +    let referenceElement: HTMLSpanElement;  | 
 | 20 | +    const id = 'tooltip-' + Math.random().toString(36).substring(2, 9);  | 
 | 21 | +
  | 
 | 22 | +    const inDialogGroup = hasContext('dialog-group');  | 
17 | 23 | 
  | 
18 | 24 |     async function showTooltip() {  | 
19 |  | -        await update();  | 
20 |  | -        showing = show = !disabled;  | 
 | 25 | +        if (disabled) return;  | 
 | 26 | +
  | 
 | 27 | +        if (delayTimeout) {  | 
 | 28 | +            clearTimeout(delayTimeout);  | 
 | 29 | +        }  | 
 | 30 | +
  | 
 | 31 | +        if (delay > 0) {  | 
 | 32 | +            delayTimeout = setTimeout(async () => {  | 
 | 33 | +                await update();  | 
 | 34 | +                showing = show = true;  | 
 | 35 | +            }, delay);  | 
 | 36 | +        } else {  | 
 | 37 | +            await update();  | 
 | 38 | +            showing = show = true;  | 
 | 39 | +        }  | 
21 | 40 |     }  | 
22 | 41 | 
  | 
23 | 42 |     function hideTooltip() {  | 
 | 43 | +        if (delayTimeout) {  | 
 | 44 | +            clearTimeout(delayTimeout);  | 
 | 45 | +        }  | 
24 | 46 |         show = false;  | 
25 | 47 |     }  | 
26 | 48 | 
  | 
 | 
43 | 65 |         });  | 
44 | 66 |     }  | 
45 | 67 | 
  | 
 | 68 | +    function portalPopover(node: HTMLElement) {  | 
 | 69 | +        if (!portal && !inDialogGroup) return;  | 
 | 70 | +
  | 
 | 71 | +        const target = !inDialogGroup  | 
 | 72 | +            ? document.body  | 
 | 73 | +            : // can be inside a modal/dialog  | 
 | 74 | +              document.body.querySelector<HTMLDialogElement>('dialog[open]');  | 
 | 75 | +
  | 
 | 76 | +        if (target) {  | 
 | 77 | +            target.appendChild(node);  | 
 | 78 | +        }  | 
 | 79 | +
  | 
 | 80 | +        return {  | 
 | 81 | +            destroy() {  | 
 | 82 | +                if (target && node.parentNode === target) {  | 
 | 83 | +                    target.removeChild(node);  | 
 | 84 | +                }  | 
 | 85 | +            }  | 
 | 86 | +        };  | 
 | 87 | +    }  | 
 | 88 | +
  | 
46 | 89 |     function fadeSlide(_: Node, { y = 8, duration = 200 } = {}) {  | 
47 | 90 |         return {  | 
48 | 91 |             duration,  | 
 | 
83 | 126 |         {id}  | 
84 | 127 |         transition:fadeSlide  | 
85 | 128 |         use:autoUpdateAction  | 
 | 129 | +        use:portalPopover  | 
86 | 130 |         on:transitionend={() => (showing = false)}  | 
87 | 131 |         bind:this={tooltipElement}  | 
88 | 132 |         aria-hidden={!show}  | 
 | 
0 commit comments