{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dialog",
  "title": "Dialog",
  "description": "A dialog component.",
  "dependencies": [
    "@hugeicons/react",
    "@hugeicons/core-free-icons"
  ],
  "registryDependencies": [
    "@cubby-ui/button",
    "@cubby-ui/scroll-area",
    "@cubby-ui/elevated"
  ],
  "files": [
    {
      "path": "registry/default/dialog/dialog.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/registry/default/button/button\";\nimport {\n  elevatedSurface,\n  type SurfaceLevel,\n} from \"@/registry/default/lib/elevated\";\nimport {\n  ScrollArea,\n  type ScrollAreaProps,\n} from \"@/registry/default/scroll-area/scroll-area\";\n\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Cancel01Icon } from \"@hugeicons/core-free-icons\";\ninterface DialogConfigContextValue {\n  modal: boolean | \"trap-focus\";\n}\n\nconst DialogConfigContext = React.createContext<DialogConfigContextValue>({\n  modal: true,\n});\n\nfunction Dialog<Payload>({\n  modal = true,\n  disablePointerDismissal,\n  ...props\n}: BaseDialog.Root.Props<Payload>) {\n  const configValue = React.useMemo(() => ({ modal }), [modal]);\n  return (\n    <DialogConfigContext.Provider value={configValue}>\n      <BaseDialog.Root\n        modal={modal}\n        disablePointerDismissal={disablePointerDismissal ?? modal !== true}\n        {...props}\n      />\n    </DialogConfigContext.Provider>\n  );\n}\n\nconst createDialogHandle = BaseDialog.createHandle;\n\nfunction DialogPortal({ ...props }: BaseDialog.Portal.Props) {\n  return <BaseDialog.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogTrigger({ ...props }: BaseDialog.Trigger.Props) {\n  return <BaseDialog.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogClose({ ...props }: BaseDialog.Close.Props) {\n  return <BaseDialog.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogBackdrop({ className, ...props }: BaseDialog.Backdrop.Props) {\n  return (\n    <BaseDialog.Backdrop\n      className={cn(\n        \"ease-out-expo fixed inset-0 min-h-dvh bg-black/40 transition-all duration-200 supports-[-webkit-touch-callout:none]:absolute\",\n        \"backdrop-blur-sm data-ending-style:opacity-0 data-starting-style:opacity-0\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DialogViewport({ className, ...props }: BaseDialog.Viewport.Props) {\n  return (\n    <BaseDialog.Viewport\n      data-slot=\"dialog-viewport\"\n      className={cn(\n        \"fixed inset-0 flex flex-col items-center justify-center overflow-hidden px-4 py-6\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DialogContent({\n  className,\n  children,\n  showCloseButton = true,\n  variant = \"default\",\n  level = 5,\n  shadowLevel = 5,\n  ...props\n}: BaseDialog.Popup.Props & {\n  showCloseButton?: boolean;\n  variant?: \"default\" | \"inset\";\n  /** Surface elevation level for the dialog bg (1-8). Defaults to 5 — the standard \"dialog tier\" above page/cards/popovers. Bump to 7 for a hero/critical modal. */\n  level?: SurfaceLevel;\n  /** Shadow weight (1-8). Pinned to 5 by default — matches the dialog tier, dramatic enough to anchor the modal without overpowering. Bump to 7 for hero modals. */\n  shadowLevel?: SurfaceLevel;\n}) {\n  const { modal } = React.useContext(DialogConfigContext);\n  const isModal = modal === true;\n  return (\n    <DialogPortal>\n      {isModal && <DialogBackdrop />}\n      <DialogViewport className={cn(!isModal && \"pointer-events-none\")}>\n        <BaseDialog.Popup\n          data-slot=\"dialog-content\"\n          data-variant={variant}\n          data-level={level}\n          className={cn(\n            \"text-popover-foreground relative z-50 flex max-h-full min-h-0 w-full max-w-full min-w-0 flex-col overflow-hidden\",\n            \"rounded-2xl sm:max-w-lg\",\n            // Surface elevation — bg + shadow + rim overlay (rim uses ::after at z-[2])\n            elevatedSurface(level, shadowLevel),\n            // Mobile: bottom sheet style\n            // \"right-0 bottom-0 left-0 rounded-t-lg\",\n            // Desktop: centered modal\n            \"-translate-y-[calc(1.25rem*var(--nested-dialogs))]\",\n\n            // Scale effect for nested dialogs on desktop\n            \"scale-[calc(1-0.1*var(--nested-dialogs))]\",\n            // Animation duration\n            \"ease-out-expo transition-all duration-200\",\n            // Desktop animations: scale and fade\n            \"data-starting-style:translate-y-[calc(1.25rem)] data-starting-style:scale-95 data-starting-style:opacity-0\",\n            \"data-ending-style:translate-y-[calc(1.25rem)] data-ending-style:scale-95 data-ending-style:opacity-0\",\n            // Nested dialog overlay — uses ::before (not ::after, that's the rim) at z-[3] so it paints above content AND above the rim\n            \"before:pointer-events-none before:absolute before:inset-0 before:z-3 before:hidden before:rounded-[inherit] before:bg-black/5 before:opacity-0 before:transition-[opacity,display] before:transition-discrete before:duration-200\",\n            \"data-nested-dialog-open:before:block data-nested-dialog-open:before:opacity-100\",\n            \"starting:data-nested-dialog-open:before:opacity-0\",\n            !isModal && \"pointer-events-auto\",\n            className,\n          )}\n          {...props}\n        >\n          {children}\n          {showCloseButton && (\n            <DialogClose\n              aria-label=\"Close\"\n              className=\"absolute end-2 top-2\"\n              render={<Button size=\"icon_sm\" variant=\"ghost\" />}\n            >\n              <HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} />\n            </DialogClose>\n          )}\n        </BaseDialog.Popup>\n      </DialogViewport>\n    </DialogPortal>\n  );\n}\n\n// Slot-presence padding uses ancestor `:has()` on `[data-slot=dialog-content]`\n// so header/body/footer can be wrapped in forms, ErrorBoundaries, or fragments\n// without breaking spacing — adjacent-sibling selectors wouldn't reach through.\nfunction DialogHeader({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n  return (\n    <div\n      data-slot=\"dialog-header\"\n      className={cn(\n        \"flex flex-col gap-2 px-6 pt-6 pb-3\",\n        // Header alone with footer (no body anywhere in content)\n        \"in-[[data-slot=dialog-content]:not(:has([data-slot=dialog-body])):has([data-slot=dialog-footer])]:pb-6\",\n        // Header alone (no body, no footer)\n        \"in-[[data-slot=dialog-content]:not(:has([data-slot=dialog-body])):not(:has([data-slot=dialog-footer]))]:pb-6\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DialogBody({\n  className,\n  nativeScroll = false,\n  fadeEdges = true,\n  scrollbarGutter = false,\n  persistScrollbar,\n  hideScrollbar,\n  children,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement> & {\n  nativeScroll?: boolean;\n} & Pick<\n    ScrollAreaProps,\n    \"fadeEdges\" | \"scrollbarGutter\" | \"persistScrollbar\" | \"hideScrollbar\"\n  >) {\n  return (\n    <div\n      data-slot=\"dialog-body\"\n      className={cn(\n        \"flex min-h-0 flex-1 flex-col\",\n        // No header anywhere in content → body needs its own top padding\n        \"in-[[data-slot=dialog-content]:not(:has([data-slot=dialog-header]))]:pt-5\",\n        // No footer anywhere in content → body needs its own bottom padding\n        \"in-[[data-slot=dialog-content]:not(:has([data-slot=dialog-footer]))]:pb-5\",\n        // Inset variant: still need bottom padding when a footer follows so the\n        // body's content doesn't crowd the footer's top border\n        \"in-data-[variant=inset]:in-[[data-slot=dialog-content]:has([data-slot=dialog-footer])]:pb-5\",\n      )}\n    >\n      <ScrollArea\n        className=\"flex-1\"\n        fadeEdges={fadeEdges}\n        scrollbarGutter={scrollbarGutter}\n        persistScrollbar={persistScrollbar}\n        hideScrollbar={hideScrollbar}\n        nativeScroll={nativeScroll}\n      >\n        <div className={cn(\"px-6 py-1\", className)} {...props}>\n          {children}\n        </div>\n      </ScrollArea>\n    </div>\n  );\n}\n\nfunction DialogFooter({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n  return (\n    <div\n      data-slot=\"dialog-footer\"\n      className={cn(\n        \"flex flex-col-reverse gap-2 px-6 pt-4 pb-6 sm:flex-row sm:justify-end\",\n        // No header AND no body → footer alone, needs its own top padding\n        \"in-[[data-slot=dialog-content]:not(:has([data-slot=dialog-header])):not(:has([data-slot=dialog-body]))]:pt-6\",\n        // Reduce top padding when body is present\n        \"not-in-data-[variant=inset]:in-[[data-slot=dialog-content]:has([data-slot=dialog-body])]:pt-3\",\n        // Inset variant: muted background with top border for separation\n        \"in-data-[variant=inset]:border-border in-data-[variant=inset]:bg-muted in-data-[variant=inset]:rounded-b-2xl in-data-[variant=inset]:border-t in-data-[variant=inset]:pt-4 in-data-[variant=inset]:pb-4\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DialogTitle({ className, ...props }: BaseDialog.Title.Props) {\n  return (\n    <BaseDialog.Title\n      className={cn(\n        \"text-foreground text-lg leading-none font-semibold tracking-tight text-balance\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DialogDescription({\n  className,\n  ...props\n}: BaseDialog.Description.Props) {\n  return (\n    <BaseDialog.Description\n      className={cn(\"text-muted-foreground text-sm text-pretty\", className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Dialog,\n  DialogTrigger,\n  DialogContent,\n  DialogHeader,\n  DialogBody,\n  DialogFooter,\n  DialogTitle,\n  DialogDescription,\n  DialogClose,\n  DialogPortal,\n  DialogBackdrop,\n  DialogViewport,\n  createDialogHandle,\n};\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/dialog.tsx"
    }
  ],
  "type": "registry:ui"
}