{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dialog-header",
  "title": "Header dialog",
  "description": "Styled dialog: the Header set. 5 decorative dialog variations (AccentHeaderDialog, IconHeaderDialog, GradientHeaderDialog, StickyHeaderDialog, DividerHeaderDialog) 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/dialog-header.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Info, X } from \"lucide-react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Header-themed dialog family: 5 modals that all carry the same plain center\n   fade+scale animation; the difference is ONLY in how the header region is\n   presented. Each export is a complete dialog: internal open/closed state\n   (uncontrolled defaultOpen or controlled open/onOpenChange), NO radix or portal -\n   a self-contained fixed backdrop + fixed panel. It closes on a backdrop click,\n   Escape or the close button. The panel takes focus on open. AnimatePresence\n   handles enter/exit; no transform under reduced motion (only a fade). Color comes\n   ONLY from tokens, via alpha color-mix. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst panelSize: Record<StyledSize, string> = {\n  sm: \"max-w-sm\",\n  md: \"max-w-md\",\n  lg: \"max-w-lg\",\n  xl: \"max-w-2xl\",\n}\n\ninterface DialogProps {\n  size?: StyledSize\n  trigger?: React.ReactNode\n  title?: React.ReactNode\n  children?: React.ReactNode\n  className?: string\n  open?: boolean\n  defaultOpen?: boolean\n  onOpenChange?: (o: boolean) => void\n}\n\nconst backdropClass =\n  \"fixed inset-0 z-50 flex items-center justify-center p-4 bg-[color-mix(in_oklab,var(--color-foreground)_50%,transparent)]\"\n\nconst panelBase =\n  \"relative w-full rounded-xl border border-border bg-popover text-popover-foreground shadow-lg outline-none\"\n\nconst closeBtn =\n  \"absolute right-3 top-3 z-20 inline-flex size-8 items-center justify-center rounded-md text-muted-foreground outline-none transition-colors hover:bg-[color-mix(in_oklab,var(--color-foreground)_8%,transparent)] hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-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\"\n\n/* Tum ailenin ortak animasyonu: sade center fade + scale. */\nconst centerMotion = {\n  initial: { opacity: 0, scale: 0.94 },\n  animate: { opacity: 1, scale: 1 },\n  exit: { opacity: 0, scale: 0.94 },\n  transition: { type: \"spring\", stiffness: 320, damping: 26 },\n} as const\n\n/* Controlled/uncontrolled acik durum yonetimi. */\nfunction useDialogState(props: DialogProps) {\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\n/* Shared shell: trigger plus an AnimatePresence backdrop plus panel. renderHeader draws the header area per variant; when scrollable is true the panel content scrolls and the header can stay stuck (in the variant that wants sticky). */\nfunction DialogShell({\n  props,\n  renderHeader,\n  scrollable,\n}: {\n  props: DialogProps\n  renderHeader: (title: React.ReactNode) => React.ReactNode\n  scrollable?: boolean\n}) {\n  const { size = \"md\", trigger, title, children, className } = props\n  const { isOpen, setOpen } = useDialogState(props)\n  const reduce = useReducedMotion()\n  const panelRef = 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    window.addEventListener(\"keydown\", onKey)\n    const id = window.requestAnimationFrame(() => panelRef.current?.focus())\n    return () => {\n      window.removeEventListener(\"keydown\", onKey)\n      window.cancelAnimationFrame(id)\n    }\n  }, [isOpen, setOpen])\n\n  const fade = { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 } }\n  const motionProps = reduce ? fade : centerMotion\n\n  const header = title ? renderHeader(title) : null\n  const body = <div className=\"px-6 py-5 text-sm text-muted-foreground\">{children}</div>\n\n  return (\n    <>\n      {/* ARIA durumu tetikleyicinin KENDISINDE. Once yoktu: ekran okuyucu\n\n          kullanicisi butonun bir dialog actigini ve acik olup olmadigini\n\n          HIC bilmiyordu. Canli AX taramasiyla bulundu (grep gostermemisti). */}\n\n      <span data-slot=\"styled-dialog-trigger\" className=\"inline-flex\">\n\n        {React.isValidElement<React.ButtonHTMLAttributes<HTMLButtonElement>>(trigger) ? (\n\n          React.cloneElement(trigger, {\n\n            \"aria-haspopup\": \"dialog\",\n\n            \"aria-expanded\": isOpen,\n\n            onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n\n              trigger.props.onClick?.(event)\n\n              setOpen(true)\n\n            },\n\n          })\n\n        ) : (\n\n          <button\n\n            type=\"button\"\n\n            aria-haspopup=\"dialog\"\n\n            aria-expanded={isOpen}\n\n            onClick={() => setOpen(true)}\n\n            className={triggerBtn}\n\n          >\n\n            {trigger ?? \"Open dialog\"}\n\n          </button>\n\n        )}\n\n      </span>\n\n      <AnimatePresence>\n        {isOpen ? (\n          <motion.div\n            data-slot=\"styled-dialog\"\n            className={backdropClass}\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            exit={{ opacity: 0 }}\n            transition={{ duration: 0.2, ease: \"easeOut\" }}\n            onClick={() => setOpen(false)}\n          >\n            <motion.div\n              ref={panelRef}\n              role=\"dialog\"\n              aria-modal=\"true\"\n              tabIndex={-1}\n              className={cn(\n                panelBase,\n                panelSize[size],\n                scrollable && \"flex max-h-[80vh] flex-col overflow-hidden\",\n                className\n              )}\n              initial={motionProps.initial}\n              animate={motionProps.animate}\n              exit={motionProps.exit}\n              transition={reduce ? { duration: 0.2, ease: \"easeOut\" } : centerMotion.transition}\n              onClick={(e) => e.stopPropagation()}\n            >\n              <button\n                type=\"button\"\n                aria-label=\"Close\"\n                className={closeBtn}\n                onClick={() => setOpen(false)}\n              >\n                <X />\n              </button>\n              {scrollable ? (\n                <div className=\"min-h-0 flex-1 overflow-y-auto\">\n                  {header}\n                  {body}\n                </div>\n              ) : (\n                <>\n                  {header}\n                  {body}\n                </>\n              )}\n            </motion.div>\n          </motion.div>\n        ) : null}\n      </AnimatePresence>\n    </>\n  )\n}\n\n/* Accent: baslik solunda dikey primary accent cubugu. */\nexport function AccentHeaderDialog(props: DialogProps) {\n  return (\n    <DialogShell\n      props={props}\n      renderHeader={(title) => (\n        <div className=\"flex items-center gap-3 border-b border-border px-6 py-4 pr-12 text-base font-semibold\">\n          <span className=\"h-5 w-1 shrink-0 rounded-full bg-primary\" aria-hidden />\n          {title}\n        </div>\n      )}\n    />\n  )\n}\n\n/* Icon: baslik solunda token soft-daire icinde lucide ikon + baslik. */\nexport function IconHeaderDialog(props: DialogProps) {\n  return (\n    <DialogShell\n      props={props}\n      renderHeader={(title) => (\n        <div className=\"flex items-center gap-3 border-b border-border px-6 py-4 pr-12 text-base font-semibold\">\n          <span\n            className=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-[color-mix(in_oklab,var(--color-primary)_14%,transparent)] text-primary [&_i]:text-base [&_i]:leading-none [&_svg]:size-4 [&_svg]:shrink-0\"\n            aria-hidden\n          >\n            <Info />\n          </span>\n          {title}\n        </div>\n      )}\n    />\n  )\n}\n\n/* Gradient: baslik bandi arka plani token'lardan gradient, foreground metin. */\nexport function GradientHeaderDialog(props: DialogProps) {\n  return (\n    <DialogShell\n      props={props}\n      renderHeader={(title) => (\n        <div className=\"rounded-t-xl border-b border-border bg-[linear-gradient(135deg,color-mix(in_oklab,var(--color-primary)_92%,transparent),color-mix(in_oklab,var(--color-primary)_55%,transparent))] px-6 py-4 pr-12 text-base font-semibold text-primary-foreground\">\n          {title}\n        </div>\n      )}\n    />\n  )\n}\n\n/* Sticky: long scrollable content; the header stays sticky top-0 and flush with the token background. */\nexport function StickyHeaderDialog(props: DialogProps) {\n  return (\n    <DialogShell\n      props={props}\n      scrollable\n      renderHeader={(title) => (\n        <div className=\"sticky top-0 z-10 border-b border-border bg-popover px-6 py-4 pr-12 text-base font-semibold\">\n          {title}\n        </div>\n      )}\n    />\n  )\n}\n\n/* Divider: baslik ustunde kucuk eyebrow etiketi, altinda kalin token ayrac. */\nexport function DividerHeaderDialog(props: DialogProps) {\n  return (\n    <DialogShell\n      props={props}\n      renderHeader={(title) => (\n        <div className=\"flex flex-col gap-1 border-b-2 border-border px-6 py-4 pr-12\">\n          <span className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n            Section\n          </span>\n          <span className=\"text-base font-semibold\">{title}</span>\n        </div>\n      )}\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/dialog-header.tsx"
    }
  ],
  "categories": [
    "styled",
    "dialog"
  ],
  "type": "registry:component"
}