{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "popover-compact",
  "title": "Compact popover",
  "description": "Styled popover: the Compact set. 5 decorative popover variations (MiniPopover, DensePopover, WidePopover, TallPopover, SplitPopover) 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-compact.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowRight, Check, Info, Search, Sparkles } from \"lucide-react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Compact popover family: 5 anchored panels. The shared mechanism is THE SAME as\n   popover-styled (a self-contained shell, NO radix/portal); the DIFFERENCE is the\n   content density and the panel size. Each export is a complete popover: internal\n   open/closed state (uncontrolled defaultOpen or controlled open/onOpenChange), a\n   relative inline-flex wrapper + a panel absolutely positioned BELOW the trigger\n   (top-full mt-2). The trigger toggles on CLICK; it closes on an outside click\n   (window pointerdown, with inner clicks ignored via the wrapper ref) and on\n   Escape. The trigger is a real button. AnimatePresence fade+scale (from above);\n   only a fade under reduced motion. StyledSize scales the panel width in every\n   variant; the content layout is specific to the variant. Color comes ONLY from\n   tokens, via alpha color-mix. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\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\nconst panelBase =\n  \"absolute left-0 top-full z-50 mt-2 origin-top rounded-xl border border-border bg-popover text-sm text-popover-foreground shadow-lg outline-none\"\n\nconst triggerBtn =\n  \"inline-flex h-9 shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-lg border border-border bg-secondary px-4 text-sm font-medium text-secondary-foreground outline-none transition-colors hover:bg-[color-mix(in_oklab,var(--color-foreground)_6%,transparent)] focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n\n/* Ic satirlarda ortak hover + focus davranisi (token alpha ile). */\nconst rowClass =\n  \"flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm text-popover-foreground outline-none transition-colors hover:bg-[color-mix(in_oklab,var(--color-foreground)_6%,transparent)] focus-visible:bg-[color-mix(in_oklab,var(--color-foreground)_6%,transparent)] focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground [&_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 panelMotion = {\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\", stiffness: 340, damping: 26 } as const,\n}\n\n/* Shared shell: a relative wrapper plus trigger plus an AnimatePresence panel. sizeMap carries the variant-specific width scale (the core of the Compact theme). */\nfunction PopoverShell({\n  props,\n  sizeMap,\n  panelClassName,\n  children,\n  role = \"dialog\",\n}: {\n  props: PopoverProps\n  sizeMap: Record<StyledSize, string>\n  panelClassName?: string\n  children: React.ReactNode | ((close: () => void) => React.ReactNode)\n  role?: string\n}) {\n  const { size = \"md\", trigger, 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 : panelMotion\n\n  return (\n    <div ref={wrapperRef} data-slot=\"styled-popover\" 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 type=\"button\" aria-haspopup=\"dialog\" aria-expanded={isOpen} className={triggerBtn}>\n            Open\n          </button>\n        )}\n      </span>\n\n      <AnimatePresence>\n        {isOpen ? (\n          <motion.div\n            role={role}\n            className={cn(panelBase, sizeMap[size], panelClassName, className)}\n            initial={motionProps.initial}\n            animate={motionProps.animate}\n            exit={motionProps.exit}\n            transition={reduce ? { duration: 0.18, ease: \"easeOut\" } : panelMotion.transition}\n          >\n            {typeof children === \"function\" ? children(() => setOpen(false)) : children}\n          </motion.div>\n        ) : null}\n      </AnimatePresence>\n    </div>\n  )\n}\n\n/* Mini: very small, a one-line hint. Icon plus short text, tight padding. */\nconst miniSize: Record<StyledSize, string> = {\n  sm: \"w-40\",\n  md: \"w-44\",\n  lg: \"w-48\",\n  xl: \"w-52\",\n}\n\nexport function MiniPopover(props: PopoverProps) {\n  return (\n    <PopoverShell props={props} sizeMap={miniSize} panelClassName=\"p-2\">\n      <div className=\"flex items-center gap-2 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground [&_i]:text-base [&_i]:leading-none\">\n        <Info />\n        <span className=\"truncate text-xs text-muted-foreground\">\n          {props.children ?? \"Quick hint\"}\n        </span>\n      </div>\n    </PopoverShell>\n  )\n}\n\n/* Dense: sikisik liste. Kucuk satirlar, dar dikey bosluk. */\nconst denseSize: Record<StyledSize, string> = {\n  sm: \"w-52\",\n  md: \"w-56\",\n  lg: \"w-60\",\n  xl: \"w-64\",\n}\n\nexport function DensePopover(props: PopoverProps & { items?: string[] }) {\n  const { items, ...rest } = props\n  const list = items ?? [\"Rename\", \"Duplicate\", \"Move to\", \"Archive\", \"Delete\"]\n  return (\n    <PopoverShell props={rest} sizeMap={denseSize} panelClassName=\"p-1\" role=\"menu\">\n      {(close) => (\n        <div className=\"flex flex-col gap-0.5\">\n          {list.map((item, i) => (\n            <button\n              key={`${item}-${i}`}\n              type=\"button\"\n              role=\"menuitem\"\n              className={rowClass}\n              onClick={close}\n            >\n              <ArrowRight />\n              <span className=\"truncate\">{item}</span>\n            </button>\n          ))}\n        </div>\n      )}\n    </PopoverShell>\n  )\n}\n\n/* Wide: genis yatay. Ikon blogu + metin, tek satirda yayilan duzen. */\nconst wideSize: Record<StyledSize, string> = {\n  sm: \"w-72\",\n  md: \"w-80\",\n  lg: \"w-96\",\n  xl: \"w-[28rem]\",\n}\n\nexport function WidePopover(props: PopoverProps) {\n  return (\n    <PopoverShell props={props} sizeMap={wideSize} panelClassName=\"p-4\">\n      <div className=\"flex items-center gap-4\">\n        <span className=\"flex size-10 shrink-0 items-center justify-center rounded-lg bg-[color-mix(in_oklab,var(--color-foreground)_6%,transparent)] [&_svg]:size-5 [&_svg]:text-muted-foreground\">\n          <Sparkles />\n        </span>\n        <div className=\"min-w-0 flex-1\">\n          <div className=\"truncate text-sm font-medium text-popover-foreground\">Wide layout</div>\n          <p className=\"truncate text-xs text-muted-foreground\">\n            {props.children ?? \"A horizontal panel with room to breathe.\"}\n          </p>\n        </div>\n        <button\n          type=\"button\"\n          className=\"inline-flex h-8 shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-lg border border-border bg-secondary px-3 text-xs font-medium text-secondary-foreground outline-none transition-colors hover:bg-[color-mix(in_oklab,var(--color-foreground)_6%,transparent)] focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n        >\n          Open\n        </button>\n      </div>\n    </PopoverShell>\n  )\n}\n\n/* Tall: dar + uzun, scrollable. Sabit yukseklikte dikey liste, overflow scroll. */\nconst tallSize: Record<StyledSize, string> = {\n  sm: \"w-44\",\n  md: \"w-48\",\n  lg: \"w-52\",\n  xl: \"w-56\",\n}\n\nexport function TallPopover(props: PopoverProps & { items?: string[] }) {\n  const { items, ...rest } = props\n  const list =\n    items ??\n    [\"Overview\", \"Activity\", \"Members\", \"Files\", \"Settings\", \"Billing\", \"Integrations\", \"Danger zone\"]\n  return (\n    <PopoverShell props={rest} sizeMap={tallSize} panelClassName=\"p-1\" role=\"menu\">\n      {(close) => (\n        <div className=\"flex max-h-48 flex-col gap-0.5 overflow-y-auto\">\n          {list.map((item, i) => (\n            <button\n              key={`${item}-${i}`}\n              type=\"button\"\n              role=\"menuitem\"\n              className={rowClass}\n              onClick={close}\n            >\n              <span className=\"truncate\">{item}</span>\n            </button>\n          ))}\n        </div>\n      )}\n    </PopoverShell>\n  )\n}\n\n/* Split: two columns or panes. A list on the left, a vertically separated detail pane on the right. */\nconst splitSize: Record<StyledSize, string> = {\n  sm: \"w-72\",\n  md: \"w-80\",\n  lg: \"w-96\",\n  xl: \"w-[26rem]\",\n}\n\nexport function SplitPopover(props: PopoverProps) {\n  return (\n    <PopoverShell props={props} sizeMap={splitSize} panelClassName=\"p-0\">\n      {(close) => (\n        <div className=\"flex divide-x divide-border\">\n          <div className=\"flex w-1/2 flex-col gap-0.5 p-1.5\">\n            {[\"Search\", \"Recent\", \"Shared\"].map((item) => (\n              <button key={item} type=\"button\" className={rowClass} onClick={close}>\n                <Search />\n                <span className=\"truncate\">{item}</span>\n              </button>\n            ))}\n          </div>\n          <div className=\"flex w-1/2 flex-col justify-between gap-3 p-3\">\n            <p className=\"text-xs text-muted-foreground\">\n              {props.children ?? \"A second pane with placeholder detail content.\"}\n            </p>\n            <button\n              type=\"button\"\n              className=\"inline-flex h-8 shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-lg bg-primary px-3 text-xs font-medium text-primary-foreground outline-none transition-colors hover:bg-[color-mix(in_oklab,var(--color-primary)_90%,transparent)] focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n              onClick={close}\n            >\n              <Check />\n              Apply\n            </button>\n          </div>\n        </div>\n      )}\n    </PopoverShell>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/popover-compact.tsx"
    }
  ],
  "categories": [
    "styled",
    "popover"
  ],
  "type": "registry:component"
}