{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "info-tooltip",
  "title": "Info tooltip",
  "description": "An (i) icon that springs open an inverted tooltip with an optional title and footer; opens on tap on a phone.",
  "dependencies": [
    "radix-ui",
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/items/info-tooltip/info-tooltip.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Tooltip as TooltipPrimitive } from \"radix-ui\";\nimport { AnimatePresence, motion, useReducedMotionConfig } from \"motion/react\";\nimport { Info } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface InfoTooltipProps {\n  /** The body of the tooltip. */\n  content?: React.ReactNode;\n  /** Optional bold title at the top. */\n  title?: React.ReactNode;\n  /** Optional footer, under a hairline (a source, a caveat). */\n  footer?: React.ReactNode;\n  /** Custom trigger. When omitted, an accessible (i) icon button is rendered. */\n  children?: React.ReactNode;\n  /** Side of the trigger to open on. Defaults to \"top\". */\n  side?: \"top\" | \"bottom\" | \"left\" | \"right\";\n  /** Alignment against the trigger. Defaults to \"center\". */\n  align?: \"start\" | \"center\" | \"end\";\n  /** Gap from the trigger in pixels. Defaults to 6. */\n  sideOffset?: number;\n  /** Extra classes for the tooltip box. */\n  contentClassName?: string;\n  /** Extra classes for the default (i) trigger. */\n  triggerClassName?: string;\n  /** Hover delay before opening, in ms. Defaults to 150. */\n  delayDuration?: number;\n  /** Accessible label for the default trigger. Falls back to the title or content when they are strings. */\n  label?: string;\n  /** Show a small arrow pointing at the trigger. Defaults to false. */\n  showArrow?: boolean;\n}\n\n/**\n * A small explanation behind an (i). An inverted box (`bg-ink` on\n * `text-on-solid`, so it flips with the mode) that springs out of its\n * trigger, with an optional title and footer.\n *\n * On a phone, Radix never opens a tooltip on touch, which would leave the\n * explanation unreachable. The default (i) trigger explains and does nothing\n * else, so a tap opens it; a custom trigger might be a link or a control,\n * where a tap has to keep doing that instead.\n */\nexport function InfoTooltip({\n  content,\n  title,\n  footer,\n  children,\n  side = \"top\",\n  align,\n  sideOffset = 6,\n  contentClassName,\n  triggerClassName,\n  delayDuration = 150,\n  label,\n  showArrow = false,\n}: InfoTooltipProps) {\n  const reduceMotion = useReducedMotionConfig();\n  const [open, setOpen] = React.useState(false);\n  // The box is portalled to <body>, out of any `[data-theme]` scope the\n  // trigger sits in. Carry the scope across so the tooltip wears the same\n  // design system as the thing it explains.\n  const [theme, setTheme] = React.useState<string | undefined>();\n  const triggerRef = React.useRef<HTMLButtonElement>(null);\n  const touchRef = React.useRef(false);\n  const openOnTap = !children;\n\n  function changeOpen(next: boolean) {\n    if (next) {\n      setTheme(\n        triggerRef.current?.closest(\"[data-theme]\")?.getAttribute(\"data-theme\") ??\n          undefined,\n      );\n    }\n    setOpen(next);\n  }\n\n  function handlePointerDown(event: React.PointerEvent<HTMLButtonElement>) {\n    touchRef.current = openOnTap && event.pointerType === \"touch\";\n    // Radix closes an open tooltip on pointer-down; for a tap that would undo\n    // the open the click below is about to make.\n    if (touchRef.current) event.preventDefault();\n  }\n\n  function handleClick(event: React.MouseEvent<HTMLButtonElement>) {\n    if (!touchRef.current) return;\n    touchRef.current = false;\n    // Radix's own click handler closes the tooltip; a prevented event skips it.\n    event.preventDefault();\n    changeOpen(true);\n  }\n\n  if (!content && !title) {\n    return <>{children}</>;\n  }\n\n  const trigger = children ?? (\n    <button\n      type=\"button\"\n      aria-label={\n        label ??\n        (typeof title === \"string\"\n          ? title\n          : typeof content === \"string\"\n            ? content\n            : \"More information\")\n      }\n      className={cn(\n        \"inline-flex cursor-help items-center justify-center rounded-full text-faint transition-colors hover:text-dim\",\n        \"focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-lead\",\n        // A 44px hit area on a touch screen; the icon itself stays small.\n        \"pointer-coarse:relative pointer-coarse:after:absolute pointer-coarse:after:top-1/2 pointer-coarse:after:left-1/2 pointer-coarse:after:size-[max(100%,44px)] pointer-coarse:after:-translate-x-1/2 pointer-coarse:after:-translate-y-1/2 pointer-coarse:after:content-['']\",\n        triggerClassName,\n      )}\n    >\n      <Info className=\"size-3.5\" />\n    </button>\n  );\n\n  return (\n    <TooltipPrimitive.Provider delayDuration={delayDuration}>\n      <TooltipPrimitive.Root open={open} onOpenChange={changeOpen}>\n        <TooltipPrimitive.Trigger\n          ref={triggerRef}\n          {...{ asChild: true }}\n          onPointerDown={handlePointerDown}\n          onClick={handleClick}\n        >\n          {trigger}\n        </TooltipPrimitive.Trigger>\n\n        <AnimatePresence>\n          {open && (\n            <TooltipPrimitive.Portal forceMount>\n              <TooltipPrimitive.Content\n                {...{ asChild: true }}\n                forceMount\n                side={side}\n                align={align}\n                sideOffset={sideOffset}\n                // Never flush against a phone's edge.\n                collisionPadding={12}\n              >\n                <motion.div\n                  data-slot=\"info-tooltip\"\n                  data-theme={theme}\n                  initial={reduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.5 }}\n                  animate={{ opacity: 1, scale: 1 }}\n                  exit={reduceMotion ? { opacity: 0 } : { opacity: 0, scale: 0.5 }}\n                  transition={\n                    reduceMotion\n                      ? { duration: 0.1 }\n                      : { type: \"spring\", stiffness: 300, damping: 25 }\n                  }\n                  className={cn(\n                    \"z-50 w-max max-w-[18rem] origin-(--radix-tooltip-content-transform-origin) max-sm:max-w-[calc(100vw-1.5rem)]\",\n                    \"rounded-inset border border-on-solid/10 bg-ink px-3.5 py-2.5 text-left font-body text-[12px] leading-[1.5] text-balance text-on-solid shadow-lg\",\n                    contentClassName,\n                  )}\n                >\n                  {title && (\n                    <span className=\"block font-semibold text-on-solid\">{title}</span>\n                  )}\n                  {content && (\n                    <span className={cn(\"block text-on-solid/80\", title && \"mt-1\")}>\n                      {content}\n                    </span>\n                  )}\n                  {footer && (\n                    <span className=\"mt-2 block border-t border-on-solid/10 pt-1.5 text-[11px] text-on-solid/70\">\n                      {footer}\n                    </span>\n                  )}\n                  {showArrow && (\n                    <TooltipPrimitive.Arrow className=\"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-ink fill-ink\" />\n                  )}\n                </motion.div>\n              </TooltipPrimitive.Content>\n            </TooltipPrimitive.Portal>\n          )}\n        </AnimatePresence>\n      </TooltipPrimitive.Root>\n    </TooltipPrimitive.Provider>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "meta": {
    "kind": "primitive",
    "whenToUse": "Use to explain a label, metric or setting in a sentence or two without cluttering the screen, or wrap any element to give it a hover hint. Do not put actions or anything the reader must see inside it; that belongs in a popover or on the page.",
    "origin": {
      "project": "180Connect",
      "path": "src/components/ui/info-tooltip.tsx"
    },
    "credits": [
      {
        "name": "Animate UI (Radix Tooltip)",
        "url": "https://animate-ui.com",
        "license": "MIT"
      }
    ]
  },
  "type": "registry:ui"
}
