{"version":3,"file":"Tooltip.cjs","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/* eslint-disable react-hooks/refs -- the handlers below read the timer ref, and they only\n   run on user events (hover/focus), never during render */\nimport { cloneElement, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Tooltip.module.css\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport interface TooltipProps {\n    /** Tooltip content. Plain text recommended; rich content also works. */\n    content: ReactNode;\n    /** Placement relative to the trigger. Default: `\"top\"`. */\n    placement?: TooltipPlacement;\n    /** Single React element to attach hover/focus listeners to. */\n    children: ReactElement;\n    /** Disable the tooltip without changing the trigger. */\n    disabled?: boolean;\n    /** Delay (ms) before showing the tooltip. Default: 150. */\n    openDelay?: number;\n}\n\n/**\n * Lightweight tooltip. Shows on hover and on focus (keyboard-friendly). Wraps\n * a single child element via `cloneElement`, so the trigger keeps its own ref\n * and props.\n */\nexport function Tooltip({\n    content,\n    placement = \"top\",\n    children,\n    disabled = false,\n    openDelay = 150,\n}: TooltipProps) {\n    const [open, setOpen] = useState<boolean>(false);\n    const tooltipId = useId();\n    /**\n     * Handle of the pending open timer.\n     *\n     * A ref, not a `let` in the component body: that binding is recreated on every\n     * render, so any re-render between `show` and `hide` (a parent update, a state\n     * change elsewhere) dropped the handle and `clearTimeout` silently cancelled\n     * nothing — the tooltip then opened after the pointer had already left.\n     */\n    const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n    function clearTimer(): void {\n        if (timerRef.current !== null) {\n            clearTimeout(timerRef.current);\n            timerRef.current = null;\n        }\n    }\n\n    function show(): void {\n        if (disabled) return;\n        clearTimer();\n        timerRef.current = setTimeout(() => setOpen(true), openDelay);\n    }\n\n    function hide(): void {\n        clearTimer();\n        setOpen(false);\n    }\n\n    useEffect(() => {\n        return () => {\n            clearTimer();\n        };\n    }, []);\n\n    const trigger = cloneElement(children, {\n        onMouseEnter: show,\n        onMouseLeave: hide,\n        onFocus: show,\n        onBlur: hide,\n        \"aria-describedby\": open ? tooltipId : undefined,\n    } as Record<string, unknown>);\n\n    return (\n        <span className={styles.trigger}>\n            {trigger}\n            {open && (\n                <span\n                    id={tooltipId}\n                    role=\"tooltip\"\n                    className={cn(styles.bubble, styles[placement])}\n                >\n                    {content}\n                </span>\n            )}\n        </span>\n    );\n}\n"],"mappings":"8HA2BA,SAAgB,EAAQ,CACpB,UACA,YAAY,MACZ,WACA,WAAW,GACX,YAAY,KACC,CACb,GAAM,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAkB,EAAK,EACzC,GAAA,EAAY,EAAA,MAAA,CAAM,EASlB,GAAA,EAAW,EAAA,OAAA,CAA6C,IAAI,EAElE,SAAS,GAAmB,CACpB,EAAS,UAAY,OACrB,aAAa,EAAS,OAAO,EAC7B,EAAS,QAAU,KAE3B,CAEA,SAAS,GAAa,CACd,IACJ,EAAW,EACX,EAAS,QAAU,eAAiB,EAAQ,EAAI,EAAG,CAAS,EAChE,CAEA,SAAS,GAAa,CAClB,EAAW,EACX,EAAQ,EAAK,CACjB,EAEA,EAAA,EAAA,UAAA,SACiB,CACT,EAAW,CACf,EACD,CAAC,CAAC,EAEL,IAAM,GAAA,EAAU,EAAA,aAAA,CAAa,EAAU,CACnC,aAAc,EACd,aAAc,EACd,QAAS,EACT,OAAQ,EACR,mBAAoB,EAAO,EAAY,IAAA,EAC3C,CAA4B,EAE5B,OACI,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,QAAxB,SAAA,CACK,EACA,IACG,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,GAAI,EACJ,KAAK,UACL,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,EAAA,QAAO,EAAU,EAE7C,SAAA,CACC,CAAA,CAER,GAEd"}