{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "modal",
  "title": "Modal",
  "dependencies": [
    "@base-ui/react",
    "@phosphor-icons/react"
  ],
  "registryDependencies": [
    "https://ui.hotfix.jobs/r/button.json",
    "https://ui.hotfix.jobs/r/recipes.json",
    "https://ui.hotfix.jobs/r/tokens.json",
    "https://ui.hotfix.jobs/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/components/ui/modal.tsx",
      "content": "\"use client\";\n\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\";\nimport { X } from \"@phosphor-icons/react/dist/ssr\";\nimport {\n  Children,\n  isValidElement,\n  useCallback,\n} from \"react\";\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { selectionFocus } from \"@/lib/recipes\";\nimport { Button, type ButtonProps } from \"@/components/ui/button\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\nconst SIZE_CLASSES: Record<ModalSize, string> = {\n  sm: \"sm:max-w-sm\",\n  md: \"sm:max-w-md\",\n  lg: \"sm:max-w-lg\",\n  xl: \"sm:max-w-xl\",\n  full: \"sm:max-w-none\",\n};\n\nexport interface ModalProps {\n  active: boolean;\n  onClickOutside?: () => void;\n  initialFocusRef?: React.RefObject<HTMLElement | null>;\n  /** Max-width on tablet+; mobile uses narrow viewport gutters. Default `md`. */\n  size?: ModalSize;\n  /** Auto-render a close X in the top-right corner. Defaults to true when\n   *  there are no `<ModalActions>` in the tree, false when there are. */\n  showClose?: boolean;\n  className?: string;\n  children: React.ReactNode;\n}\n\nfunction hasChildOfType(\n  children: React.ReactNode,\n  Component: React.ComponentType,\n): boolean {\n  let found = false;\n  Children.forEach(children, (child) => {\n    if (!isValidElement(child)) return;\n    if (child.type === Component) found = true;\n  });\n  return found;\n}\n\nexport function Modal({\n  active,\n  onClickOutside,\n  initialFocusRef,\n  size = \"md\",\n  showClose,\n  className,\n  children,\n}: ModalProps): React.ReactElement {\n  const resolvedShowClose =\n    showClose ?? !hasChildOfType(children, ModalActions);\n\n  const onOpenChange = useCallback(\n    (open: boolean) => {\n      if (!open) onClickOutside?.();\n    },\n    [onClickOutside],\n  );\n\n  return (\n    <DialogPrimitive.Root open={active} onOpenChange={onOpenChange}>\n      <DialogPrimitive.Portal>\n        <DialogPrimitive.Backdrop\n          data-slot=\"modal-backdrop\"\n          className={cn(\n            \"fixed inset-0 z-50 bg-white/60 dark:bg-black/60\",\n            \"transition-opacity duration-[var(--duration-overlay)] ease-[var(--ease-standard)]\",\n            \"data-starting-style:opacity-0 data-ending-style:opacity-0\",\n          )}\n        />\n        <DialogPrimitive.Popup\n          data-slot=\"modal-popup\"\n          initialFocus={initialFocusRef}\n          className={cn(\n            \"fixed left-1/2 top-1/2 z-50 flex w-[calc(100vw-1rem)] min-w-0 min-h-0 -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-[var(--radius-12)] bg-layer-1 text-ink border border-hairline shadow-modal max-h-[calc(100dvh-2rem)] sm:w-full\",\n            SIZE_CLASSES[size],\n            \"transition-[opacity,transform,scale] duration-[var(--duration-overlay)] ease-[var(--ease-standard)]\",\n            \"data-starting-style:scale-97 data-starting-style:opacity-0 data-ending-style:scale-97 data-ending-style:opacity-0\",\n            className,\n          )}\n        >\n          {resolvedShowClose && (\n            <ModalClose className=\"absolute top-2 end-2 z-10\" />\n          )}\n          {children}\n        </DialogPrimitive.Popup>\n      </DialogPrimitive.Portal>\n    </DialogPrimitive.Root>\n  );\n}\n\nexport interface ModalHeaderProps extends React.ComponentProps<\"div\"> {\n  /** Content pinned to the leading edge of the header row. When set,\n   *  switches the header to a three-slot row layout: leading /\n   *  children (center) / trailing. */\n  leading?: React.ReactNode;\n  /** Content pinned to the trailing edge. */\n  trailing?: React.ReactNode;\n}\n\nexport function ModalHeader({\n  className,\n  leading,\n  trailing,\n  children,\n  ...props\n}: ModalHeaderProps): React.ReactElement {\n  const isRow = leading != null || trailing != null;\n  if (isRow) {\n    return (\n      <div\n        data-slot=\"modal-header\"\n        className={cn(\n          \"flex h-11 shrink-0 items-center justify-between gap-3 px-4\",\n          className,\n        )}\n        {...props}\n      >\n        <div className=\"flex min-w-0 flex-1 basis-0 items-center gap-2 text-mini text-ink-muted\">\n          {leading}\n        </div>\n        <div className=\"flex min-w-0 items-center gap-2 text-small font-medium text-ink\">\n          {children}\n        </div>\n        <div className=\"flex min-w-0 flex-1 basis-0 items-center justify-end gap-2\">\n          {trailing}\n        </div>\n      </div>\n    );\n  }\n  return (\n    <div\n      data-slot=\"modal-header\"\n      className={cn(\n        \"flex flex-col gap-1 px-5 pt-5 pb-2\",\n        className,\n      )}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport function ModalTitle({\n  className,\n  ...props\n}: React.ComponentProps<\"h2\">): React.ReactElement {\n  return (\n    <DialogPrimitive.Title\n      data-slot=\"modal-title\"\n      className={cn(\"text-regular font-medium text-ink\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function ModalSubtitle({\n  className,\n  ...props\n}: React.ComponentProps<\"p\">): React.ReactElement {\n  return (\n    <DialogPrimitive.Description\n      data-slot=\"modal-subtitle\"\n      className={cn(\"text-small text-ink-muted\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function ModalClose({\n  className,\n  ...props\n}: React.ComponentProps<\"button\">): React.ReactElement {\n  return (\n    <DialogPrimitive.Close\n      aria-label=\"Close\"\n      data-slot=\"modal-close\"\n      className={cn(\n        \"inline-flex size-8 shrink-0 items-center justify-center rounded-[var(--radius-8)] text-ink-muted hover:bg-layer-hover hover:text-ink active:bg-layer-hover transition-colors duration-[var(--duration-state)] ease-[var(--ease-standard)]\",\n        selectionFocus,\n        className,\n      )}\n      {...props}\n    >\n      <X aria-hidden className=\"size-4\" />\n    </DialogPrimitive.Close>\n  );\n}\n\nexport function ModalBody({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">): React.ReactElement {\n  return (\n    <div\n      data-slot=\"modal-body\"\n      className={cn(\n        \"flex flex-col gap-4 p-5 flex-1 min-h-0 overflow-y-auto\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport function ModalInset({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">): React.ReactElement {\n  return (\n    <div\n      data-slot=\"modal-inset\"\n      className={cn(\n        \"rounded-[var(--radius-8)] bg-fill-1 p-4\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport interface ModalActionsProps extends React.ComponentProps<\"div\"> {\n  /** Stack actions vertically full-width. */\n  stacked?: boolean;\n}\n\nexport function ModalActions({\n  className,\n  stacked = false,\n  ...props\n}: ModalActionsProps): React.ReactElement {\n  return (\n    <div\n      data-slot=\"modal-actions\"\n      className={cn(\n        \"flex gap-2 px-5 pt-2 pb-5\",\n        stacked ? \"flex-col\" : \"flex-row justify-between\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport interface ModalActionProps extends Omit<ButtonProps, \"variant\"> {\n  /** Default `secondary`. */\n  variant?: ButtonProps[\"variant\"];\n  fullWidth?: boolean;\n}\n\nexport function ModalAction({\n  variant = \"secondary\",\n  fullWidth,\n  className,\n  ...props\n}: ModalActionProps): React.ReactElement {\n  return (\n    <Button\n      variant={variant}\n      data-slot=\"modal-action\"\n      className={cn(fullWidth && \"w-full\", className)}\n      {...props}\n    />\n  );\n}\n\nexport const ModalPrimitive = DialogPrimitive;\n",
      "type": "registry:ui",
      "target": "components/ui/modal.tsx"
    }
  ],
  "type": "registry:ui"
}