{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "b-tooltip",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "b-tooltip.tsx",
      "content": "\"use client\";\n\nimport { Tooltip as TooltipPrimitive } from \"@base-ui/react/tooltip\";\nimport { motion } from \"motion/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst controlCornerClassName =\n  \"rounded-lg supports-[corner-shape:squircle]:corner-squircle supports-[corner-shape:squircle]:rounded-[11px]\";\n\nconst tooltipThemeClassName =\n  \"[--tt-surface:#111111] [--tt-foreground:#ffffff] dark:[--tt-surface:#f6f3ec] dark:[--tt-foreground:#111111]\";\n\nconst tooltipContentClassName = cn(\n  controlCornerClassName,\n  \"group/tooltip pointer-events-none relative z-50 max-w-60 whitespace-normal bg-[color:var(--tt-surface)] px-3 py-1.5 font-medium text-[color:var(--tt-foreground)] text-xs leading-snug shadow-[0_4px_24px_-4px_rgba(0,0,0,0.25)]\"\n);\n\nconst tooltipArrowClassName =\n  \"absolute h-2 w-2 rotate-45 bg-[color:var(--tt-surface)] group-data-[side=bottom]/tooltip:-top-1 group-data-[side=left]/tooltip:top-1/2 group-data-[side=right]/tooltip:top-1/2 group-data-[side=left]/tooltip:-right-1 group-data-[side=top]/tooltip:-bottom-1 group-data-[side=bottom]/tooltip:left-1/2 group-data-[side=right]/tooltip:-left-1 group-data-[side=top]/tooltip:left-1/2 group-data-[side=bottom]/tooltip:-translate-x-1/2 group-data-[side=top]/tooltip:-translate-x-1/2 group-data-[side=left]/tooltip:-translate-y-1/2 group-data-[side=right]/tooltip:-translate-y-1/2\";\n\ntype Side = \"top\" | \"bottom\" | \"left\" | \"right\";\ntype TooltipTriggerElement = React.ReactElement<{\n  \"aria-describedby\"?: string;\n}>;\n\ntype PopupRenderProps = React.HTMLAttributes<HTMLDivElement> & {\n  children?: React.ReactNode;\n  className?: string;\n  ref?: React.Ref<HTMLDivElement>;\n  style?: React.CSSProperties;\n};\n\nexport interface TooltipProps {\n  children: TooltipTriggerElement;\n  content: string;\n  side?: Side;\n  delay?: number;\n  className?: string;\n}\n\nconst MAX_TOOLTIP_CHARACTERS = 80;\n\nfunction isTooltipTriggerElement(\n  node: React.ReactNode\n): node is TooltipTriggerElement {\n  return React.isValidElement(node) && node.type !== React.Fragment;\n}\n\nfunction mergeDescribedBy(...ids: Array<string | undefined>) {\n  const merged = ids.filter(Boolean).join(\" \");\n\n  return merged.length > 0 ? merged : undefined;\n}\n\nfunction setRef<T>(ref: React.Ref<T> | undefined, value: T) {\n  if (typeof ref === \"function\") {\n    ref(value);\n    return;\n  }\n\n  if (ref) {\n    (ref as React.MutableRefObject<T>).current = value;\n  }\n}\n\nfunction resolvePopupProps(popupProps: PopupRenderProps) {\n  const {\n    children: _popupChildren,\n    className: popupClassName,\n    onAnimationEnd: _onAnimationEnd,\n    onAnimationIteration: _onAnimationIteration,\n    onAnimationStart: _onAnimationStart,\n    onDrag: _onDrag,\n    onDragEnd: _onDragEnd,\n    onDragEnter: _onDragEnter,\n    onDragExit: _onDragExit,\n    onDragLeave: _onDragLeave,\n    onDragOver: _onDragOver,\n    onDragStart: _onDragStart,\n    onDrop: _onDrop,\n    ref: popupRef,\n    style: popupStyle,\n    ...resolvedPopupProps\n  } = popupProps;\n\n  return {\n    popupClassName,\n    popupRef,\n    popupStyle,\n    resolvedPopupProps,\n  };\n}\n\nexport function Tooltip({\n  children,\n  content,\n  side = \"top\",\n  delay = 0.15,\n  className,\n}: TooltipProps) {\n  const [open, setOpen] = React.useState(false);\n  const [present, setPresent] = React.useState(false);\n  const actionsRef = React.useRef<TooltipPrimitive.Root.Actions | null>(null);\n  const timeoutRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);\n  const tooltipId = React.useId();\n  const triggerId = React.useId();\n  const normalizedContent = content.trim();\n\n  if (!isTooltipTriggerElement(children)) {\n    throw new Error(\n      \"Tooltip expects a single element child so it can forward hover, focus, and accessibility props.\"\n    );\n  }\n\n  React.useEffect(() => {\n    if (\n      process.env.NODE_ENV !== \"production\" &&\n      (normalizedContent.length > MAX_TOOLTIP_CHARACTERS ||\n        normalizedContent.includes(\"\\n\"))\n    ) {\n      console.warn(\n        \"Tooltip content should stay short, single-line, and non-interactive. Use Popover for longer or multiline content.\"\n      );\n    }\n  }, [normalizedContent]);\n\n  const childAriaDescribedBy = children.props[\"aria-describedby\"];\n  const triggerDescription = open\n    ? mergeDescribedBy(childAriaDescribedBy, tooltipId)\n    : childAriaDescribedBy;\n\n  const handleOpenChange = React.useCallback(\n    (nextOpen: boolean) => {\n      clearTimeout(timeoutRef.current);\n\n      if (nextOpen) {\n        if (open) {\n          return;\n        }\n\n        timeoutRef.current = setTimeout(() => {\n          setPresent(true);\n          setOpen(true);\n        }, delay * 1000);\n        return;\n      }\n\n      setOpen(false);\n    },\n    [delay, open]\n  );\n\n  React.useEffect(() => () => clearTimeout(timeoutRef.current), []);\n\n  if (normalizedContent.length === 0) {\n    return children;\n  }\n\n  return (\n    <TooltipPrimitive.Root\n      actionsRef={actionsRef}\n      disableHoverablePopup\n      onOpenChange={(nextOpen) => {\n        handleOpenChange(nextOpen);\n      }}\n      open={open}\n      triggerId={triggerId}\n    >\n      <TooltipPrimitive.Trigger\n        closeDelay={0}\n        delay={0}\n        id={triggerId}\n        render={React.cloneElement(children, {\n          \"aria-describedby\": triggerDescription,\n        })}\n      />\n\n      {present ? (\n        <TooltipPrimitive.Portal>\n          <TooltipPrimitive.Positioner\n            align=\"center\"\n            className=\"z-50 outline-none\"\n            collisionPadding={12}\n            side={side}\n            sideOffset={10}\n          >\n            <TooltipPrimitive.Popup\n              render={(popupProps, popupState) => {\n                const {\n                  popupClassName,\n                  popupRef,\n                  popupStyle,\n                  resolvedPopupProps,\n                } = resolvePopupProps(popupProps as PopupRenderProps);\n                const resolvedSide = (popupState.side as Side) ?? side;\n\n                return (\n                  <motion.div\n                    {...resolvedPopupProps}\n                    animate={\n                      open\n                        ? {\n                            opacity: 1,\n                            scale: 1,\n                            filter: \"blur(0px)\",\n                          }\n                        : {\n                            opacity: 0,\n                            scale: 0.92,\n                            filter: \"blur(4px)\",\n                          }\n                    }\n                    className={cn(\n                      tooltipThemeClassName,\n                      tooltipContentClassName,\n                      popupClassName,\n                      className\n                    )}\n                    data-side={resolvedSide}\n                    id={tooltipId}\n                    initial={{\n                      opacity: 0,\n                      scale: 0.92,\n                      filter: \"blur(4px)\",\n                    }}\n                    onAnimationComplete={() => {\n                      if (!open) {\n                        actionsRef.current?.unmount();\n                        setPresent(false);\n                      }\n                    }}\n                    ref={(node: HTMLDivElement | null) => {\n                      setRef(popupRef, node);\n                    }}\n                    role=\"tooltip\"\n                    style={\n                      {\n                        transformOrigin: \"var(--transform-origin)\",\n                        ...popupStyle,\n                      } as React.CSSProperties\n                    }\n                    transition={{\n                      type: \"spring\",\n                      stiffness: 400,\n                      damping: 24,\n                      mass: 0.6,\n                    }}\n                  >\n                    <motion.span\n                      animate={{ scale: 1 }}\n                      className={tooltipArrowClassName}\n                      exit={{ scale: 0.95, opacity: 0 }}\n                      initial={{ scale: 0.95, opacity: 0 }}\n                      transition={{\n                        type: \"spring\",\n                        stiffness: 500,\n                        damping: 28,\n                        delay: 0.03,\n                      }}\n                    />\n                    {normalizedContent}\n                  </motion.div>\n                );\n              }}\n            />\n          </TooltipPrimitive.Positioner>\n        </TooltipPrimitive.Portal>\n      ) : null}\n    </TooltipPrimitive.Root>\n  );\n}\n\nexport { Tooltip as tooltip };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Tooltip (Base UI)",
  "description": "Tooltip with the same Iconiq API layered over Base UI primitives, preserving the original controlled delay timing, bubble shell, rotated-square arrow, and spring entrance motion."
}
