"use strict"; "use client"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // packages/react/tooltip/src/index.ts var src_exports = {}; __export(src_exports, { Arrow: () => Arrow2, Content: () => Content2, Portal: () => Portal, Provider: () => Provider, Root: () => Root3, Tooltip: () => Tooltip, TooltipArrow: () => TooltipArrow, TooltipContent: () => TooltipContent, TooltipPortal: () => TooltipPortal, TooltipProvider: () => TooltipProvider, TooltipTrigger: () => TooltipTrigger, Trigger: () => Trigger, createTooltipScope: () => createTooltipScope }); module.exports = __toCommonJS(src_exports); // packages/react/tooltip/src/Tooltip.tsx var React = __toESM(require("react")); var import_primitive = require("@radix-ui/primitive"); var import_react_compose_refs = require("@radix-ui/react-compose-refs"); var import_react_context = require("@radix-ui/react-context"); var import_react_dismissable_layer = require("@radix-ui/react-dismissable-layer"); var import_react_id = require("@radix-ui/react-id"); var PopperPrimitive = __toESM(require("@radix-ui/react-popper")); var import_react_popper = require("@radix-ui/react-popper"); var import_react_portal = require("@radix-ui/react-portal"); var import_react_presence = require("@radix-ui/react-presence"); var import_react_primitive = require("@radix-ui/react-primitive"); var import_react_slot = require("@radix-ui/react-slot"); var import_react_use_controllable_state = require("@radix-ui/react-use-controllable-state"); var VisuallyHiddenPrimitive = __toESM(require("@radix-ui/react-visually-hidden")); var import_jsx_runtime = require("react/jsx-runtime"); var [createTooltipContext, createTooltipScope] = (0, import_react_context.createContextScope)("Tooltip", [ import_react_popper.createPopperScope ]); var usePopperScope = (0, import_react_popper.createPopperScope)(); var PROVIDER_NAME = "TooltipProvider"; var DEFAULT_DELAY_DURATION = 700; var TOOLTIP_OPEN = "tooltip.open"; var [TooltipProviderContextProvider, useTooltipProviderContext] = createTooltipContext(PROVIDER_NAME); var TooltipProvider = (props) => { const { __scopeTooltip, delayDuration = DEFAULT_DELAY_DURATION, skipDelayDuration = 300, disableHoverableContent = false, children } = props; const [isOpenDelayed, setIsOpenDelayed] = React.useState(true); const isPointerInTransitRef = React.useRef(false); const skipDelayTimerRef = React.useRef(0); React.useEffect(() => { const skipDelayTimer = skipDelayTimerRef.current; return () => window.clearTimeout(skipDelayTimer); }, []); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( TooltipProviderContextProvider, { scope: __scopeTooltip, isOpenDelayed, delayDuration, onOpen: React.useCallback(() => { window.clearTimeout(skipDelayTimerRef.current); setIsOpenDelayed(false); }, []), onClose: React.useCallback(() => { window.clearTimeout(skipDelayTimerRef.current); skipDelayTimerRef.current = window.setTimeout( () => setIsOpenDelayed(true), skipDelayDuration ); }, [skipDelayDuration]), isPointerInTransitRef, onPointerInTransitChange: React.useCallback((inTransit) => { isPointerInTransitRef.current = inTransit; }, []), disableHoverableContent, children } ); }; TooltipProvider.displayName = PROVIDER_NAME; var TOOLTIP_NAME = "Tooltip"; var [TooltipContextProvider, useTooltipContext] = createTooltipContext(TOOLTIP_NAME); var Tooltip = (props) => { const { __scopeTooltip, children, open: openProp, defaultOpen = false, onOpenChange, disableHoverableContent: disableHoverableContentProp, delayDuration: delayDurationProp } = props; const providerContext = useTooltipProviderContext(TOOLTIP_NAME, props.__scopeTooltip); const popperScope = usePopperScope(__scopeTooltip); const [trigger, setTrigger] = React.useState(null); const contentId = (0, import_react_id.useId)(); const openTimerRef = React.useRef(0); const disableHoverableContent = disableHoverableContentProp ?? providerContext.disableHoverableContent; const delayDuration = delayDurationProp ?? providerContext.delayDuration; const wasOpenDelayedRef = React.useRef(false); const [open = false, setOpen] = (0, import_react_use_controllable_state.useControllableState)({ prop: openProp, defaultProp: defaultOpen, onChange: (open2) => { if (open2) { providerContext.onOpen(); document.dispatchEvent(new CustomEvent(TOOLTIP_OPEN)); } else { providerContext.onClose(); } onOpenChange?.(open2); } }); const stateAttribute = React.useMemo(() => { return open ? wasOpenDelayedRef.current ? "delayed-open" : "instant-open" : "closed"; }, [open]); const handleOpen = React.useCallback(() => { window.clearTimeout(openTimerRef.current); wasOpenDelayedRef.current = false; setOpen(true); }, [setOpen]); const handleClose = React.useCallback(() => { window.clearTimeout(openTimerRef.current); setOpen(false); }, [setOpen]); const handleDelayedOpen = React.useCallback(() => { window.clearTimeout(openTimerRef.current); openTimerRef.current = window.setTimeout(() => { wasOpenDelayedRef.current = true; setOpen(true); }, delayDuration); }, [delayDuration, setOpen]); React.useEffect(() => { return () => window.clearTimeout(openTimerRef.current); }, []); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopperPrimitive.Root, { ...popperScope, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( TooltipContextProvider, { scope: __scopeTooltip, contentId, open, stateAttribute, trigger, onTriggerChange: setTrigger, onTriggerEnter: React.useCallback(() => { if (providerContext.isOpenDelayed) handleDelayedOpen(); else handleOpen(); }, [providerContext.isOpenDelayed, handleDelayedOpen, handleOpen]), onTriggerLeave: React.useCallback(() => { if (disableHoverableContent) { handleClose(); } else { window.clearTimeout(openTimerRef.current); } }, [handleClose, disableHoverableContent]), onOpen: handleOpen, onClose: handleClose, disableHoverableContent, children } ) }); }; Tooltip.displayName = TOOLTIP_NAME; var TRIGGER_NAME = "TooltipTrigger"; var TooltipTrigger = React.forwardRef( (props, forwardedRef) => { const { __scopeTooltip, ...triggerProps } = props; const context = useTooltipContext(TRIGGER_NAME, __scopeTooltip); const providerContext = useTooltipProviderContext(TRIGGER_NAME, __scopeTooltip); const popperScope = usePopperScope(__scopeTooltip); const ref = React.useRef(null); const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, ref, context.onTriggerChange); const isPointerDownRef = React.useRef(false); const hasPointerMoveOpenedRef = React.useRef(false); const handlePointerUp = React.useCallback(() => isPointerDownRef.current = false, []); React.useEffect(() => { return () => document.removeEventListener("pointerup", handlePointerUp); }, [handlePointerUp]); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopperPrimitive.Anchor, { asChild: true, ...popperScope, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react_primitive.Primitive.button, { "aria-describedby": context.open ? context.contentId : void 0, "data-state": context.stateAttribute, ...triggerProps, ref: composedRefs, onPointerMove: (0, import_primitive.composeEventHandlers)(props.onPointerMove, (event) => { if (event.pointerType === "touch") return; if (!hasPointerMoveOpenedRef.current && !providerContext.isPointerInTransitRef.current) { context.onTriggerEnter(); hasPointerMoveOpenedRef.current = true; } }), onPointerLeave: (0, import_primitive.composeEventHandlers)(props.onPointerLeave, () => { context.onTriggerLeave(); hasPointerMoveOpenedRef.current = false; }), onPointerDown: (0, import_primitive.composeEventHandlers)(props.onPointerDown, () => { isPointerDownRef.current = true; document.addEventListener("pointerup", handlePointerUp, { once: true }); }), onFocus: (0, import_primitive.composeEventHandlers)(props.onFocus, () => { if (!isPointerDownRef.current) context.onOpen(); }), onBlur: (0, import_primitive.composeEventHandlers)(props.onBlur, context.onClose), onClick: (0, import_primitive.composeEventHandlers)(props.onClick, context.onClose) } ) }); } ); TooltipTrigger.displayName = TRIGGER_NAME; var PORTAL_NAME = "TooltipPortal"; var [PortalProvider, usePortalContext] = createTooltipContext(PORTAL_NAME, { forceMount: void 0 }); var TooltipPortal = (props) => { const { __scopeTooltip, forceMount, children, container } = props; const context = useTooltipContext(PORTAL_NAME, __scopeTooltip); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PortalProvider, { scope: __scopeTooltip, forceMount, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_presence.Presence, { present: forceMount || context.open, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_portal.Portal, { asChild: true, container, children }) }) }); }; TooltipPortal.displayName = PORTAL_NAME; var CONTENT_NAME = "TooltipContent"; var TooltipContent = React.forwardRef( (props, forwardedRef) => { const portalContext = usePortalContext(CONTENT_NAME, props.__scopeTooltip); const { forceMount = portalContext.forceMount, side = "top", ...contentProps } = props; const context = useTooltipContext(CONTENT_NAME, props.__scopeTooltip); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_presence.Presence, { present: forceMount || context.open, children: context.disableHoverableContent ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContentImpl, { side, ...contentProps, ref: forwardedRef }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContentHoverable, { side, ...contentProps, ref: forwardedRef }) }); } ); var TooltipContentHoverable = React.forwardRef((props, forwardedRef) => { const context = useTooltipContext(CONTENT_NAME, props.__scopeTooltip); const providerContext = useTooltipProviderContext(CONTENT_NAME, props.__scopeTooltip); const ref = React.useRef(null); const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, ref); const [pointerGraceArea, setPointerGraceArea] = React.useState(null); const { trigger, onClose } = context; const content = ref.current; const { onPointerInTransitChange } = providerContext; const handleRemoveGraceArea = React.useCallback(() => { setPointerGraceArea(null); onPointerInTransitChange(false); }, [onPointerInTransitChange]); const handleCreateGraceArea = React.useCallback( (event, hoverTarget) => { const currentTarget = event.currentTarget; const exitPoint = { x: event.clientX, y: event.clientY }; const exitSide = getExitSideFromRect(exitPoint, currentTarget.getBoundingClientRect()); const paddedExitPoints = getPaddedExitPoints(exitPoint, exitSide); const hoverTargetPoints = getPointsFromRect(hoverTarget.getBoundingClientRect()); const graceArea = getHull([...paddedExitPoints, ...hoverTargetPoints]); setPointerGraceArea(graceArea); onPointerInTransitChange(true); }, [onPointerInTransitChange] ); React.useEffect(() => { return () => handleRemoveGraceArea(); }, [handleRemoveGraceArea]); React.useEffect(() => { if (trigger && content) { const handleTriggerLeave = (event) => handleCreateGraceArea(event, content); const handleContentLeave = (event) => handleCreateGraceArea(event, trigger); trigger.addEventListener("pointerleave", handleTriggerLeave); content.addEventListener("pointerleave", handleContentLeave); return () => { trigger.removeEventListener("pointerleave", handleTriggerLeave); content.removeEventListener("pointerleave", handleContentLeave); }; } }, [trigger, content, handleCreateGraceArea, handleRemoveGraceArea]); React.useEffect(() => { if (pointerGraceArea) { const handleTrackPointerGrace = (event) => { const target = event.target; const pointerPosition = { x: event.clientX, y: event.clientY }; const hasEnteredTarget = trigger?.contains(target) || content?.contains(target); const isPointerOutsideGraceArea = !isPointInPolygon(pointerPosition, pointerGraceArea); if (hasEnteredTarget) { handleRemoveGraceArea(); } else if (isPointerOutsideGraceArea) { handleRemoveGraceArea(); onClose(); } }; document.addEventListener("pointermove", handleTrackPointerGrace); return () => document.removeEventListener("pointermove", handleTrackPointerGrace); } }, [trigger, content, pointerGraceArea, onClose, handleRemoveGraceArea]); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContentImpl, { ...props, ref: composedRefs }); }); var [VisuallyHiddenContentContextProvider, useVisuallyHiddenContentContext] = createTooltipContext(TOOLTIP_NAME, { isInside: false }); var TooltipContentImpl = React.forwardRef( (props, forwardedRef) => { const { __scopeTooltip, children, "aria-label": ariaLabel, onEscapeKeyDown, onPointerDownOutside, ...contentProps } = props; const context = useTooltipContext(CONTENT_NAME, __scopeTooltip); const popperScope = usePopperScope(__scopeTooltip); const { onClose } = context; React.useEffect(() => { document.addEventListener(TOOLTIP_OPEN, onClose); return () => document.removeEventListener(TOOLTIP_OPEN, onClose); }, [onClose]); React.useEffect(() => { if (context.trigger) { const handleScroll = (event) => { const target = event.target; if (target?.contains(context.trigger)) onClose(); }; window.addEventListener("scroll", handleScroll, { capture: true }); return () => window.removeEventListener("scroll", handleScroll, { capture: true }); } }, [context.trigger, onClose]); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_react_dismissable_layer.DismissableLayer, { asChild: true, disableOutsidePointerEvents: false, onEscapeKeyDown, onPointerDownOutside, onFocusOutside: (event) => event.preventDefault(), onDismiss: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( PopperPrimitive.Content, { "data-state": context.stateAttribute, ...popperScope, ...contentProps, ref: forwardedRef, style: { ...contentProps.style, // re-namespace exposed content custom properties ...{ "--radix-tooltip-content-transform-origin": "var(--radix-popper-transform-origin)", "--radix-tooltip-content-available-width": "var(--radix-popper-available-width)", "--radix-tooltip-content-available-height": "var(--radix-popper-available-height)", "--radix-tooltip-trigger-width": "var(--radix-popper-anchor-width)", "--radix-tooltip-trigger-height": "var(--radix-popper-anchor-height)" } }, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_slot.Slottable, { children }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VisuallyHiddenContentContextProvider, { scope: __scopeTooltip, isInside: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VisuallyHiddenPrimitive.Root, { id: context.contentId, role: "tooltip", children: ariaLabel || children }) }) ] } ) } ); } ); TooltipContent.displayName = CONTENT_NAME; var ARROW_NAME = "TooltipArrow"; var TooltipArrow = React.forwardRef( (props, forwardedRef) => { const { __scopeTooltip, ...arrowProps } = props; const popperScope = usePopperScope(__scopeTooltip); const visuallyHiddenContentContext = useVisuallyHiddenContentContext( ARROW_NAME, __scopeTooltip ); return visuallyHiddenContentContext.isInside ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PopperPrimitive.Arrow, { ...popperScope, ...arrowProps, ref: forwardedRef }); } ); TooltipArrow.displayName = ARROW_NAME; function getExitSideFromRect(point, rect) { const top = Math.abs(rect.top - point.y); const bottom = Math.abs(rect.bottom - point.y); const right = Math.abs(rect.right - point.x); const left = Math.abs(rect.left - point.x); switch (Math.min(top, bottom, right, left)) { case left: return "left"; case right: return "right"; case top: return "top"; case bottom: return "bottom"; default: throw new Error("unreachable"); } } function getPaddedExitPoints(exitPoint, exitSide, padding = 5) { const paddedExitPoints = []; switch (exitSide) { case "top": paddedExitPoints.push( { x: exitPoint.x - padding, y: exitPoint.y + padding }, { x: exitPoint.x + padding, y: exitPoint.y + padding } ); break; case "bottom": paddedExitPoints.push( { x: exitPoint.x - padding, y: exitPoint.y - padding }, { x: exitPoint.x + padding, y: exitPoint.y - padding } ); break; case "left": paddedExitPoints.push( { x: exitPoint.x + padding, y: exitPoint.y - padding }, { x: exitPoint.x + padding, y: exitPoint.y + padding } ); break; case "right": paddedExitPoints.push( { x: exitPoint.x - padding, y: exitPoint.y - padding }, { x: exitPoint.x - padding, y: exitPoint.y + padding } ); break; } return paddedExitPoints; } function getPointsFromRect(rect) { const { top, right, bottom, left } = rect; return [ { x: left, y: top }, { x: right, y: top }, { x: right, y: bottom }, { x: left, y: bottom } ]; } function isPointInPolygon(point, polygon) { const { x, y } = point; let inside = false; for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { const xi = polygon[i].x; const yi = polygon[i].y; const xj = polygon[j].x; const yj = polygon[j].y; const intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi; if (intersect) inside = !inside; } return inside; } function getHull(points) { const newPoints = points.slice(); newPoints.sort((a, b) => { if (a.x < b.x) return -1; else if (a.x > b.x) return 1; else if (a.y < b.y) return -1; else if (a.y > b.y) return 1; else return 0; }); return getHullPresorted(newPoints); } function getHullPresorted(points) { if (points.length <= 1) return points.slice(); const upperHull = []; for (let i = 0; i < points.length; i++) { const p = points[i]; while (upperHull.length >= 2) { const q = upperHull[upperHull.length - 1]; const r = upperHull[upperHull.length - 2]; if ((q.x - r.x) * (p.y - r.y) >= (q.y - r.y) * (p.x - r.x)) upperHull.pop(); else break; } upperHull.push(p); } upperHull.pop(); const lowerHull = []; for (let i = points.length - 1; i >= 0; i--) { const p = points[i]; while (lowerHull.length >= 2) { const q = lowerHull[lowerHull.length - 1]; const r = lowerHull[lowerHull.length - 2]; if ((q.x - r.x) * (p.y - r.y) >= (q.y - r.y) * (p.x - r.x)) lowerHull.pop(); else break; } lowerHull.push(p); } lowerHull.pop(); if (upperHull.length === 1 && lowerHull.length === 1 && upperHull[0].x === lowerHull[0].x && upperHull[0].y === lowerHull[0].y) { return upperHull; } else { return upperHull.concat(lowerHull); } } var Provider = TooltipProvider; var Root3 = Tooltip; var Trigger = TooltipTrigger; var Portal = TooltipPortal; var Content2 = TooltipContent; var Arrow2 = TooltipArrow; //# sourceMappingURL=index.js.map