{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dialog",
  "title": "Dialog",
  "description": "A modal dialog with title, description and actions; a click opens it with a spring, a key press opens it instantly.",
  "dependencies": [
    "@base-ui/react@^1.9.0"
  ],
  "registryDependencies": [
    "https://sproutboat.com/ikada/r/theme.json",
    "https://sproutboat.com/ikada/r/cn.json",
    "https://sproutboat.com/ikada/r/icons.json",
    "https://sproutboat.com/ikada/r/instant.json",
    "https://sproutboat.com/ikada/r/portal.json"
  ],
  "files": [
    {
      "path": "registry/ikada/ui/dialog.tsx",
      "content": "\"use client\";\n\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport type { ComponentProps } from \"react\";\nimport { cn, focusRing, pressMotion, type WithClass } from \"@/components/ikada/lib/cn\";\nimport { CloseIcon } from \"@/components/ikada/lib/icons\";\nimport { usePortalContainer } from \"@/components/ikada/lib/portal\";\nimport { InstantProvider, instantAttr, useInstant, useInstantOpen } from \"@/components/ikada/lib/instant\";\n\n/** Opens with a click or tap animate; a key press opens it with no animation. */\nexport function Dialog({ onOpenChange, ...props }: ComponentProps<typeof BaseDialog.Root>) {\n  const [instant, handle] = useInstantOpen(onOpenChange);\n  return (\n    <InstantProvider value={instant}>\n      <BaseDialog.Root onOpenChange={handle} {...props} />\n    </InstantProvider>\n  );\n}\n/** The element that opens the dialog. */\nexport const DialogTrigger = BaseDialog.Trigger;\n/** Any element that closes the dialog, such as a Cancel button. */\nexport const DialogClose = BaseDialog.Close;\n\nexport interface DialogContentProps extends Omit<ComponentProps<typeof BaseDialog.Popup>, \"className\"> {\n  className?: string;\n  /** Hide the corner close button when the footer already has Cancel. */\n  hideClose?: boolean;\n}\n\n/**\n * The modal panel with its scrim, centred. Arrives on the smooth spring from scale 0.96, leaves in\n * 150ms; a close button sits top-end unless `hideClose`.\n */\nexport function DialogContent({ className, hideClose = false, children, ref, ...props }: DialogContentProps) {\n  const instant = useInstant();\n  const container = usePortalContainer();\n  return (\n    <BaseDialog.Portal container={container}>\n      <BaseDialog.Backdrop\n        data-slot=\"dialog-overlay\"\n        {...instantAttr(instant)}\n        className={cn(\n          \"fixed inset-0 z-(--sb-z-overlay) bg-scrim\",\n          \"transition-opacity duration-(--sb-duration-dialog-out) ease-out data-open:duration-(--sb-duration-dialog-in)\",\n          \"data-starting-style:opacity-0 data-ending-style:opacity-0 data-sb-instant:transition-none\",\n        )}\n      />\n      <BaseDialog.Viewport className=\"fixed inset-0 z-(--sb-z-modal) grid place-items-center overflow-y-auto overscroll-contain p-4\">\n        <BaseDialog.Popup\n          data-slot=\"dialog-content\"\n          ref={ref}\n          {...instantAttr(instant)}\n          className={cn(\n            \"relative w-full max-w-md rounded-xl bg-raised p-6 text-fg shadow-dialog outline-hidden\",\n            // Centered: a dialog scales from its middle, never from a trigger.\n            // Arrives on the smooth spring (no overshoot), leaves in 150ms ease-out.\n            \"origin-center [transition:opacity_var(--sb-duration-dialog-out)_var(--ease-out),scale_var(--sb-duration-dialog-out)_var(--ease-out)]\",\n            \"data-open:[transition:opacity_var(--sb-duration-dialog-in)_var(--ease-out),scale_var(--sb-duration-spring-smooth)_var(--sb-spring-smooth)]\",\n            \"data-starting-style:scale-(--sb-scale-dialog) data-starting-style:opacity-0\",\n            \"data-ending-style:scale-(--sb-scale-dialog) data-ending-style:opacity-0 data-sb-instant:transition-none\",\n            className,\n          )}\n          {...props}\n        >\n          {children}\n          {hideClose ? null : (\n            <BaseDialog.Close\n              aria-label=\"Close\"\n              className={cn(\n                \"absolute end-3 top-3 inline-grid size-8 place-items-center rounded-md text-fg-muted hover:bg-hover hover:text-fg pointer-coarse:size-11\",\n                pressMotion,\n                focusRing,\n              )}\n            >\n              <CloseIcon />\n            </BaseDialog.Close>\n          )}\n        </BaseDialog.Popup>\n      </BaseDialog.Viewport>\n    </BaseDialog.Portal>\n  );\n}\n\n/** The dialog heading, wired to aria-labelledby. */\nexport function DialogTitle({ className, ...props }: WithClass<ComponentProps<typeof BaseDialog.Title>>) {\n  return (\n    <BaseDialog.Title data-slot=\"dialog-title\" className={cn(\"pe-8 text-sb-lg font-bold\", className)} {...props} />\n  );\n}\n\n/** Supporting text under the title, wired to aria-describedby. */\nexport function DialogDescription({ className, ...props }: WithClass<ComponentProps<typeof BaseDialog.Description>>) {\n  return (\n    <BaseDialog.Description\n      data-slot=\"dialog-description\"\n      className={cn(\"mt-2 text-sb-base text-fg-muted\", className)}\n      {...props}\n    />\n  );\n}\n\n/** Right-aligned row of actions; stacks on small screens. */\nexport function DialogFooter({ className, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"dialog-footer\"\n      className={cn(\"mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:ui",
      "target": "@components/ikada/dialog.tsx"
    }
  ],
  "categories": [
    "overlays"
  ],
  "type": "registry:ui"
}