{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "popover-tone",
  "title": "Tone popover",
  "description": "Styled popover: the Tone set. 5 decorative popover variations (PrimaryPopover, SuccessPopover, WarningPopover, DangerPopover, InfoPopover) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2",
    "lucide-react@^1.23.0"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/popover-tone.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { AlertTriangle, CheckCircle2, Info, Sparkles, XCircle } from \"lucide-react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Tone popover family: 5 semantically toned info panels. Each export is a complete\n   popover: internal open/closed state (uncontrolled defaultOpen or controlled\n   open/onOpenChange), NO radix or portal - a relative inline-flex wrapper + a panel\n   absolutely positioned BELOW the trigger (top-full mt-2). The trigger toggles on\n   CLICK; it closes on an outside click (window pointerdown, with inner clicks\n   ignored via the wrapper ref) and on Escape. The trigger is a real button, colored\n   with its tone. The panel sits on the -soft/-soft-foreground background of the\n   relevant tone token: an icon chip + a title + text. AnimatePresence fade+scale\n   (from above); only a fade under reduced motion. Color comes ONLY from semantic\n   tokens, via alpha color-mix - NO hex/oklch/raw alpha. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst panelSize: Record<StyledSize, string> = {\n  sm: \"w-56\",\n  md: \"w-64\",\n  lg: \"w-72\",\n  xl: \"w-80\",\n}\n\ninterface PopoverProps {\n  size?: StyledSize\n  trigger?: React.ReactNode\n  children?: React.ReactNode\n  className?: string\n  open?: boolean\n  defaultOpen?: boolean\n  onOpenChange?: (o: boolean) => void\n}\n\n/* Defines every surface of a single tone: trigger, panel, icon chip and icon. The colours come only from the relevant semantic token (directly from -soft/-soft-foreground, or through color-mix for alpha). */\ninterface ToneStyle {\n  label: string\n  trigger: string\n  panel: string\n  chip: string\n  icon: React.ReactNode\n}\n\nconst panelBase =\n  \"absolute left-0 top-full z-50 mt-2 flex origin-top gap-3 rounded-xl border p-4 text-sm shadow-lg outline-none [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n\nconst chipBase =\n  \"flex size-9 shrink-0 items-center justify-center rounded-lg [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n\nconst triggerBase =\n  \"inline-flex h-9 shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-lg border px-4 text-sm font-medium outline-none transition-colors focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n\n/* Controlled/uncontrolled acik durum yonetimi. */\nfunction usePopoverState(props: PopoverProps) {\n  const { open, defaultOpen, onOpenChange } = props\n  const isControlled = open !== undefined\n  const [internal, setInternal] = React.useState(defaultOpen ?? false)\n  const isOpen = isControlled ? open : internal\n\n  const setOpen = React.useCallback(\n    (next: boolean) => {\n      if (!isControlled) setInternal(next)\n      onOpenChange?.(next)\n    },\n    [isControlled, onOpenChange]\n  )\n\n  return { isOpen, setOpen }\n}\n\nconst bespokeMotion = {\n  initial: { opacity: 0, scale: 0.96, y: -6 },\n  animate: { opacity: 1, scale: 1, y: 0 },\n  exit: { opacity: 0, scale: 0.96, y: -6 },\n  transition: { type: \"spring\" as const, stiffness: 340, damping: 26 },\n}\n\n/* Shared toned shell: a relative wrapper plus a toned trigger plus an AnimatePresence panel. */\nfunction TonePopoverShell({\n  props,\n  tone,\n  heading,\n}: {\n  props: PopoverProps\n  tone: ToneStyle\n  heading: string\n}) {\n  const { size = \"md\", trigger, children, className } = props\n  const { isOpen, setOpen } = usePopoverState(props)\n  const reduce = useReducedMotion()\n  const wrapperRef = React.useRef<HTMLDivElement>(null)\n\n  React.useEffect(() => {\n    if (!isOpen) return\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key === \"Escape\") setOpen(false)\n    }\n    const onPointer = (e: PointerEvent) => {\n      const node = wrapperRef.current\n      if (node && e.target instanceof Node && !node.contains(e.target)) {\n        setOpen(false)\n      }\n    }\n    window.addEventListener(\"keydown\", onKey)\n    window.addEventListener(\"pointerdown\", onPointer)\n    return () => {\n      window.removeEventListener(\"keydown\", onKey)\n      window.removeEventListener(\"pointerdown\", onPointer)\n    }\n  }, [isOpen, setOpen])\n\n  const fade = { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 } }\n  const motionProps = reduce ? fade : bespokeMotion\n\n  return (\n    <div ref={wrapperRef} data-slot=\"styled-popover\" data-tone={tone.label} className=\"relative inline-flex\">\n      <span\n        data-slot=\"styled-popover-trigger\"\n        className=\"inline-flex\"\n        onClick={() => setOpen(!isOpen)}\n      >\n        {trigger ?? (\n          <button\n            type=\"button\"\n            aria-haspopup=\"dialog\"\n            aria-expanded={isOpen}\n            className={cn(triggerBase, tone.trigger)}\n          >\n            {tone.icon}\n            {tone.label}\n          </button>\n        )}\n      </span>\n\n      <AnimatePresence>\n        {isOpen ? (\n          <motion.div\n            data-slot=\"styled-popover-content\"\n            role=\"dialog\"\n            className={cn(panelBase, panelSize[size], tone.panel, className)}\n            initial={motionProps.initial}\n            animate={motionProps.animate}\n            exit={motionProps.exit}\n            transition={reduce ? { duration: 0.12 } : bespokeMotion.transition}\n          >\n            <span data-slot=\"styled-popover-icon\" className={cn(chipBase, tone.chip)}>\n              {tone.icon}\n            </span>\n            <div className=\"flex flex-col gap-1\">\n              <p className=\"font-semibold\">{heading}</p>\n              <p className=\"opacity-80\">\n                {children ?? \"A soft, tone colored panel anchored to its trigger.\"}\n              </p>\n            </div>\n          </motion.div>\n        ) : null}\n      </AnimatePresence>\n    </div>\n  )\n}\n\n/* Primary: a neutral soft surface derived from the primary token (there is no primary-soft token, hence alpha through color-mix). */\nexport function PrimaryPopover(props: PopoverProps) {\n  return (\n    <TonePopoverShell\n      props={props}\n      heading=\"Heads up\"\n      tone={{\n        label: \"Primary\",\n        trigger:\n          \"border-[color-mix(in_oklab,var(--color-primary)_22%,transparent)] bg-[color-mix(in_oklab,var(--color-primary)_10%,transparent)] text-primary hover:bg-[color-mix(in_oklab,var(--color-primary)_16%,transparent)]\",\n        panel:\n          \"border-[color-mix(in_oklab,var(--color-primary)_20%,transparent)] bg-[color-mix(in_oklab,var(--color-primary)_9%,transparent)] text-primary\",\n        chip: \"bg-[color-mix(in_oklab,var(--color-primary)_16%,transparent)] text-primary\",\n        icon: <Sparkles />,\n      }}\n    />\n  )\n}\n\n/* Success: success-soft zemin + success-soft-foreground metin. */\nexport function SuccessPopover(props: PopoverProps) {\n  return (\n    <TonePopoverShell\n      props={props}\n      heading=\"All set\"\n      tone={{\n        label: \"Success\",\n        trigger:\n          \"border-[color-mix(in_oklab,var(--color-success)_30%,transparent)] bg-success-soft text-success-soft-foreground hover:bg-[color-mix(in_oklab,var(--color-success)_16%,transparent)]\",\n        panel:\n          \"border-[color-mix(in_oklab,var(--color-success)_26%,transparent)] bg-success-soft text-success-soft-foreground\",\n        chip: \"bg-[color-mix(in_oklab,var(--color-success)_18%,transparent)] text-success-soft-foreground\",\n        icon: <CheckCircle2 />,\n      }}\n    />\n  )\n}\n\n/* Warning: warning-soft zemin + warning-soft-foreground metin. */\nexport function WarningPopover(props: PopoverProps) {\n  return (\n    <TonePopoverShell\n      props={props}\n      heading=\"Take care\"\n      tone={{\n        label: \"Warning\",\n        trigger:\n          \"border-[color-mix(in_oklab,var(--color-warning)_30%,transparent)] bg-warning-soft text-warning-soft-foreground hover:bg-[color-mix(in_oklab,var(--color-warning)_16%,transparent)]\",\n        panel:\n          \"border-[color-mix(in_oklab,var(--color-warning)_26%,transparent)] bg-warning-soft text-warning-soft-foreground\",\n        chip: \"bg-[color-mix(in_oklab,var(--color-warning)_18%,transparent)] text-warning-soft-foreground\",\n        icon: <AlertTriangle />,\n      }}\n    />\n  )\n}\n\n/* Danger: danger-soft zemin + danger-soft-foreground metin. */\nexport function DangerPopover(props: PopoverProps) {\n  return (\n    <TonePopoverShell\n      props={props}\n      heading=\"Something broke\"\n      tone={{\n        label: \"Danger\",\n        trigger:\n          \"border-[color-mix(in_oklab,var(--color-danger)_30%,transparent)] bg-danger-soft text-danger-soft-foreground hover:bg-[color-mix(in_oklab,var(--color-danger)_16%,transparent)]\",\n        panel:\n          \"border-[color-mix(in_oklab,var(--color-danger)_26%,transparent)] bg-danger-soft text-danger-soft-foreground\",\n        chip: \"bg-[color-mix(in_oklab,var(--color-danger)_18%,transparent)] text-danger-soft-foreground\",\n        icon: <XCircle />,\n      }}\n    />\n  )\n}\n\n/* Info: info-soft zemin + info-soft-foreground metin. */\nexport function InfoPopover(props: PopoverProps) {\n  return (\n    <TonePopoverShell\n      props={props}\n      heading=\"Good to know\"\n      tone={{\n        label: \"Info\",\n        trigger:\n          \"border-[color-mix(in_oklab,var(--color-info)_30%,transparent)] bg-info-soft text-info-soft-foreground hover:bg-[color-mix(in_oklab,var(--color-info)_16%,transparent)]\",\n        panel:\n          \"border-[color-mix(in_oklab,var(--color-info)_26%,transparent)] bg-info-soft text-info-soft-foreground\",\n        chip: \"bg-[color-mix(in_oklab,var(--color-info)_18%,transparent)] text-info-soft-foreground\",\n        icon: <Info />,\n      }}\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/popover-tone.tsx"
    }
  ],
  "categories": [
    "styled",
    "popover"
  ],
  "type": "registry:component"
}