import { type Placement } from '@floating-ui/dom'; import { Tooltip, type TooltipController } from '../ui/tooltip'; import { defineWebComponent } from './define'; import { wireDisclosure } from './disclosure'; interface Props extends Record { /** The hint text shown on hover/focus of the slotted trigger. */ content?: string; /** Delay (ms) before the tooltip appears on hover. Defaults to 600. Focus * shows it immediately regardless. */ openDelay?: number; /** Delay (ms) before it hides after the pointer leaves. Defaults to 0 (hides * immediately). */ closeDelay?: number; /** Preferred placement: `'top' | 'bottom' | 'left' | 'right'` (+ optional * `-start`/`-end`). Defaults to `'top'`; flips to stay in view. */ placement?: string; /** Drive/observe open state (Shoelace-style: settable + reflected to the `open` * attribute, the element still self-manages on hover/focus). Set `el.open = true`, * or ``; listen for `kai-open-change`. */ open?: boolean; /** Initial open state on mount (uncontrolled seed). */ defaultOpen?: boolean; /** Turn the tooltip off while keeping the trigger mounted (hover/focus and * `show()` no longer open it). */ disabled?: boolean; } /** Events fired by ``. */ interface Events { /** The tooltip opened or closed (by hover/focus, outside-click, or a method). */ 'kai-open-change': { open: boolean }; } /** * `` — wraps a trigger and shows a hint on hover/focus. Put the * trigger as light-DOM content; set the text via `content`. Positions itself and * dismisses on Escape/outside-click; the content is portaled inside the shadow * root so it isn't clipped. * * ```html * * * * ``` */ defineWebComponent('kai-tooltip', { content: '', openDelay: undefined, closeDelay: undefined, placement: undefined, open: undefined, defaultOpen: undefined, disabled: undefined, }, (props, ctx) => { const { flag } = ctx; let api: TooltipController | undefined; // The standard overlay surface: settable+reflecting `open`, kai-open-change, // show/hide/toggle, disabled-gating. See ./disclosure. wireDisclosure(ctx, () => api, () => props.open); return ( <> {/* The shared element base sets `:host{display:block}` (styles.css). A block host wraps the inline-flex trigger in a line box, so the font-descender strut makes the host taller than the control and pins the control to the top, so it reads off-center beside non-tooltipped siblings in a flex-items-center row. inline-flex (like kai-button) drops the line box so the host hugs the trigger and their centers line up. */} (api = a)} > ); });