{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tooltip",
  "title": "Tooltip (FRAME/12 Adapter)",
  "description": "FRAME/12 skin for accessible tooltips with hard ink shadow.",
  "registryDependencies": [
    "https://frame12.dev/r/frame12-styles.json"
  ],
  "files": [
    {
      "path": "ui/tooltip.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\n\nconst useIsoLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;\n\ninterface TooltipContextValue {\n  open: boolean;\n  setOpen: (next: boolean) => void;\n  tooltipId: string;\n  setCustomId: (id: string | undefined) => void;\n}\n\nconst TooltipContext = React.createContext<TooltipContextValue | null>(null);\n\nfunction useTooltipContext() {\n  const ctx = React.useContext(TooltipContext);\n  if (!ctx) throw new Error('Tooltip subcomponents must be used within <Tooltip>');\n  return ctx;\n}\n\nexport function TooltipProvider({ children }: { children?: React.ReactNode }) {\n  return <>{children}</>;\n}\n\nexport interface TooltipProps {\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  children?: React.ReactNode;\n}\n\nexport function Tooltip({\n  open: controlledOpen,\n  defaultOpen = false,\n  onOpenChange,\n  children,\n}: TooltipProps) {\n  const [uncontrolledOpen, setUncontrolledOpen] = React.useState<boolean>(defaultOpen);\n  const [customId, setCustomId] = React.useState<string | undefined>(undefined);\n  const isControlled = controlledOpen !== undefined;\n  const open = isControlled ? controlledOpen : uncontrolledOpen;\n  const rawId = React.useId().replace(/:/g, '');\n  const tooltipId = customId ?? `f12-tip-${rawId}`;\n\n  const setOpen = React.useCallback(\n    (next: boolean) => {\n      if (!isControlled) setUncontrolledOpen(next);\n      onOpenChange?.(next);\n    },\n    [isControlled, onOpenChange],\n  );\n\n  return (\n    <TooltipContext.Provider value={{ open, setOpen, tooltipId, setCustomId }}>\n      <span\n        className=\"f12-tooltip-wrap\"\n        onPointerEnter={() => setOpen(true)}\n        onPointerLeave={() => setOpen(false)}\n        onFocus={() => setOpen(true)}\n        onBlur={() => setOpen(false)}\n        onKeyDown={(e) => {\n          if (e.key === 'Escape') setOpen(false);\n        }}\n      >\n        {children}\n      </span>\n    </TooltipContext.Provider>\n  );\n}\n\nexport interface TooltipTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  asChild?: boolean;\n}\n\nfunction mergeRefs<T>(\n  ...refs: Array<React.Ref<T> | undefined>\n): React.RefCallback<T> {\n  let activeCleanups: Array<() => void> = [];\n  const runCleanups = () => {\n    const toRun = activeCleanups;\n    activeCleanups = [];\n    for (const cleanup of toRun) {\n      cleanup();\n    }\n  };\n  return (value: T | null) => {\n    runCleanups();\n    if (value === null) {\n      return;\n    }\n    const cleanups: Array<() => void> = [];\n    for (const ref of refs) {\n      if (typeof ref === 'function') {\n        const cleanup = ref(value);\n        if (typeof cleanup === 'function') {\n          cleanups.push(cleanup);\n        } else {\n          cleanups.push(() => {\n            ref(null);\n          });\n        }\n      } else if (ref != null) {\n        (ref as React.MutableRefObject<T | null>).current = value;\n        cleanups.push(() => {\n          (ref as React.MutableRefObject<T | null>).current = null;\n        });\n      }\n    }\n    activeCleanups = cleanups;\n  };\n}\n\nfunction getElementRef(element: React.ReactElement): React.Ref<unknown> | undefined {\n  const propsObj = (element.props || {}) as Record<string, unknown>;\n  let getter = Object.getOwnPropertyDescriptor(propsObj, 'ref')?.get;\n  let mayWarn =\n    getter && 'isReactWarning' in getter && (getter as { isReactWarning?: boolean }).isReactWarning;\n  if (mayWarn) {\n    return (element as unknown as { ref?: React.Ref<unknown> }).ref;\n  }\n  getter = Object.getOwnPropertyDescriptor(element, 'ref')?.get;\n  mayWarn =\n    getter && 'isReactWarning' in getter && (getter as { isReactWarning?: boolean }).isReactWarning;\n  if (mayWarn) {\n    return (propsObj as { ref?: React.Ref<unknown> }).ref;\n  }\n  return (\n    (propsObj as { ref?: React.Ref<unknown> }).ref ||\n    (element as unknown as { ref?: React.Ref<unknown> }).ref\n  );\n}\n\nexport const TooltipTrigger = React.forwardRef<HTMLButtonElement, TooltipTriggerProps>(\n  function TooltipTrigger(\n    {\n      asChild = false,\n      className = '',\n      'aria-describedby': triggerDescribedBy,\n      children,\n      ...rest\n    },\n    forwardedRef,\n  ) {\n    const { open, tooltipId } = useTooltipContext();\n    const childRef = React.isValidElement(children) ? getElementRef(children) : undefined;\n    const composedRef = React.useMemo(\n      () => mergeRefs(forwardedRef, childRef),\n      [forwardedRef, childRef],\n    );\n    if (asChild && React.isValidElement(children)) {\n      const child = children as React.ReactElement<Record<string, unknown>>;\n      const childProps = child.props || {};\n      const mergedClassName = [childProps.className, className].filter(Boolean).join(' ') || undefined;\n      const mergedDescribedBy =\n        [\n          triggerDescribedBy,\n          childProps['aria-describedby'] as string | undefined,\n          open ? tooltipId : undefined,\n        ]\n          .filter(Boolean)\n          .join(' ') || undefined;\n\n      const mergedProps: Record<string, unknown> = {\n        ...rest,\n        ...childProps,\n        className: mergedClassName,\n        'aria-describedby': mergedDescribedBy,\n        ref: composedRef,\n      };\n\n      for (const evt of new Set([...Object.keys(rest), ...Object.keys(childProps)])) {\n        if (!/^on[A-Z]/.test(evt)) continue;\n        const parentHandler = (rest as Record<string, unknown>)[evt];\n        const childHandler = childProps[evt];\n        if (typeof parentHandler === 'function' && typeof childHandler === 'function') {\n          mergedProps[evt] = (...args: unknown[]) => {\n            (parentHandler as (...a: unknown[]) => void)(...args);\n            (childHandler as (...a: unknown[]) => void)(...args);\n          };\n        } else if (typeof parentHandler === 'function') {\n          mergedProps[evt] = parentHandler;\n        } else if (typeof childHandler === 'function') {\n          mergedProps[evt] = childHandler;\n        }\n      }\n\n      return React.cloneElement(child, mergedProps);\n    }\n    const buttonDescribedBy =\n      [triggerDescribedBy, open ? tooltipId : undefined].filter(Boolean).join(' ') || undefined;\n    return (\n      <button\n        ref={forwardedRef}\n        type=\"button\"\n        className={`f12-btn f12-btn-secondary f12-btn-sm${className ? ` ${className}` : ''}`}\n        aria-describedby={buttonDescribedBy}\n        {...rest}\n      >\n        {children}\n      </button>\n    );\n  },\n);\n\nexport function TooltipContent({\n  id: userProvidedId,\n  className = '',\n  children,\n  ...rest\n}: React.HTMLAttributes<HTMLSpanElement>) {\n  const { open, tooltipId, setCustomId } = useTooltipContext();\n  useIsoLayoutEffect(() => {\n    setCustomId(userProvidedId);\n    return () => setCustomId(undefined);\n  }, [userProvidedId, setCustomId]);\n  if (!open) return null;\n  return (\n    <span\n      id={userProvidedId ?? tooltipId}\n      role=\"tooltip\"\n      className={`f12-tooltip${className ? ` ${className}` : ''}`}\n      {...rest}\n    >\n      {children}\n    </span>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/frame12/tooltip.tsx"
    }
  ],
  "type": "registry:ui"
}