{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dialog",
  "type": "registry:ui",
  "title": "Dialog and AlertDialog",
  "description": "React Aria backed modal dialog and alertdialog primitives with provider-aware portals, token-backed overlay/content styling, controlled open state, dismissal controls, reduced-motion transitions, explicit confirmation actions, and accessible title/description wiring.",
  "dependencies": [
    "react-aria@^3.52.1",
    "react-aria-components@^1.21.1"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://components.dethink.co.uk/r/dethink-base.json",
    "https://components.dethink.co.uk/r/button.json"
  ],
  "files": [
    {
      "path": "packages/components/src/components/dialog/dialog.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/components/dialog/dialog.tsx",
      "content": "import {\n  Button as AriaButton,\n  Dialog as AriaDialog,\n  DialogTrigger as AriaDialogTrigger,\n  Heading as AriaHeading,\n  Modal,\n  ModalOverlay,\n  type ButtonProps as AriaButtonProps,\n  type DialogRenderProps as AriaDialogRenderProps,\n  type DialogTriggerProps as AriaDialogTriggerProps,\n  type HeadingProps as AriaHeadingProps,\n  type ModalOverlayProps as AriaModalOverlayProps,\n} from \"react-aria-components\";\nimport {\n  createContext,\n  forwardRef,\n  type HTMLAttributes,\n  type ReactNode,\n  type Ref,\n  useContext,\n  useEffect,\n  useId,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport {\n  buttonClassNames,\n  type ButtonSize,\n  type ButtonVariant,\n} from \"../button\";\nimport {\n  DethinkPortalProvider,\n  useProviderPortalRoot,\n  useScopedOverlayState,\n} from \"../../utils/provider-portal\";\nimport { cn } from \"../../utils/cn\";\nimport {\n  canOpenWithoutTrigger,\n  TriggerlessPressRegistration,\n} from \"./triggerless-press-registration\";\n\nexport type DialogSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\nexport type DialogScrollBehavior = \"inside\" | \"outside\";\n\nexport interface DialogProps extends Omit<\n  AriaDialogTriggerProps,\n  \"children\" | \"isOpen\" | \"onOpenChange\"\n> {\n  \"data-slot\"?: string;\n  children?: ReactNode;\n  className?: string;\n  onOpenChange?: (open: boolean) => void;\n  open?: boolean;\n}\n\nexport interface DialogTriggerProps extends Omit<\n  AriaButtonProps,\n  \"children\" | \"className\"\n> {\n  \"data-slot\"?: string;\n  children?: ReactNode;\n  className?: string;\n  size?: ButtonSize;\n  variant?: ButtonVariant;\n}\n\nexport interface DialogContentProps extends Omit<\n  AriaModalOverlayProps,\n  | \"children\"\n  | \"className\"\n  | \"defaultOpen\"\n  | \"isDismissable\"\n  | \"isKeyboardDismissDisabled\"\n  | \"isOpen\"\n  | \"onOpenChange\"\n  | \"UNSTABLE_portalContainer\"\n> {\n  \"aria-describedby\"?: string;\n  \"aria-label\"?: string;\n  \"aria-labelledby\"?: string;\n  children?: ReactNode | ((opts: AriaDialogRenderProps) => ReactNode);\n  className?: string;\n  closeButtonClassName?: string;\n  closeButtonLabel?: string;\n  dismissible?: boolean;\n  keyboardDismissDisabled?: boolean;\n  overlayClassName?: string;\n  scrollBehavior?: DialogScrollBehavior;\n  showCloseButton?: boolean;\n  size?: DialogSize;\n}\n\nexport interface DialogHeaderProps extends HTMLAttributes<HTMLDivElement> {}\n\nexport interface DialogFooterProps extends HTMLAttributes<HTMLDivElement> {}\n\nexport interface DialogTitleProps extends Omit<AriaHeadingProps, \"className\"> {\n  className?: string;\n  visuallyHidden?: boolean;\n}\n\nexport interface DialogDescriptionProps extends HTMLAttributes<HTMLParagraphElement> {}\n\nexport interface DialogCloseProps extends DialogTriggerProps {}\n\ninterface DialogContentContextValue {\n  defaultTitleId: string;\n  hasCloseButton: boolean;\n  setDescriptionId: (id: string | null) => void;\n  setTitleId: (id: string | null) => void;\n}\n\nconst DialogContentContext = createContext<DialogContentContextValue | null>(\n  null,\n);\n\ninterface DialogRootContextValue {\n  setTriggerElement: (element: HTMLButtonElement | null) => void;\n}\n\nconst DialogRootContext = createContext<DialogRootContextValue | null>(null);\n\nconst useIsomorphicLayoutEffect =\n  typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\nconst dialogRootClasses = \"contents\";\n\nconst dialogOverlayBaseClasses =\n  \"fixed inset-0 z-50 grid min-h-dvh place-items-center overflow-y-auto overscroll-contain bg-[var(--dt-overlay-scrim)] p-[var(--dt-space-4)] text-foreground backdrop-blur-xs outline-none motion-safe:data-[entering]:animate-scrim-in motion-safe:data-[exiting]:animate-scrim-out motion-reduce:animate-none sm:p-[var(--dt-space-6)] forced-colors:backdrop-blur-none\";\n\nconst dialogContentBaseClasses =\n  \"relative grid w-full overflow-hidden rounded-lg border border-border bg-background text-foreground shadow-xl outline-none motion-safe:data-[entering]:animate-dialog-in motion-safe:data-[exiting]:animate-dialog-out motion-reduce:animate-none\";\n\nconst dialogContentSizeClasses: Record<DialogSize, string> = {\n  sm: \"max-w-sm\",\n  md: \"max-w-lg\",\n  lg: \"max-w-2xl\",\n  xl: \"max-w-4xl\",\n  full: \"min-h-[calc(100dvh-(env(safe-area-inset-top)+env(safe-area-inset-bottom)+var(--dt-space-8)))] max-w-[calc(100vw-(env(safe-area-inset-left)+env(safe-area-inset-right)+var(--dt-space-8)))]\",\n};\n\nconst dialogContentScrollBehaviorClasses: Record<DialogScrollBehavior, string> =\n  {\n    inside:\n      \"max-h-[calc(100dvh-(env(safe-area-inset-top)+env(safe-area-inset-bottom)+var(--dt-space-8)))] overflow-y-auto overscroll-contain\",\n    outside: \"my-[var(--dt-space-4)] overflow-visible\",\n  };\n\n// The dialog itself receives initial focus from React Aria. display: contents\n// removes its focusable box in browsers, leaving Escape on the page body.\nconst dialogPanelClasses = \"grid min-w-0 outline-none\";\n\nconst dialogHeaderClasses =\n  \"grid gap-[var(--dt-space-1-5)] p-[var(--dt-space-6)] pb-[var(--dt-space-3)] text-start\";\n\nconst dialogHeaderWithCloseButtonClasses =\n  \"pe-[calc(var(--dt-space-6)+var(--dt-space-8))]\";\n\nconst dialogFooterClasses =\n  \"flex flex-col-reverse gap-density-gap p-[var(--dt-space-6)] pt-[var(--dt-space-3)] sm:flex-row sm:justify-end\";\n\nconst alertDialogFooterClasses =\n  \"flex flex-row flex-wrap items-center justify-end gap-density-gap p-[var(--dt-space-6)] pt-[var(--dt-space-3)]\";\n\nconst dialogTitleClasses =\n  \"text-lg font-semibold leading-7 tracking-normal text-foreground\";\n\nconst dialogDescriptionClasses = \"text-sm leading-6 text-muted-foreground\";\n\nconst visuallyHiddenClasses = \"sr-only\";\n\nconst dialogCloseIconClasses = \"pointer-events-none size-4 shrink-0\";\n\nconst dialogCloseButtonClasses =\n  \"absolute end-[var(--dt-space-3)] top-[var(--dt-space-3)] z-10\";\n\nfunction joinIds(...ids: Array<string | undefined>) {\n  return ids.filter(Boolean).join(\" \") || undefined;\n}\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n  if (typeof ref === \"function\") {\n    ref(value);\n  } else if (ref) {\n    (ref as { current: T | null }).current = value;\n  }\n}\n\nfunction composeRefs<T>(...refs: Array<Ref<T> | undefined>) {\n  return (node: T | null) => {\n    for (const ref of refs) {\n      assignRef(ref, node);\n    }\n  };\n}\n\nfunction renderDialogChildren(\n  children: DialogContentProps[\"children\"],\n  opts: AriaDialogRenderProps,\n) {\n  const renderedChildren =\n    typeof children === \"function\" ? children(opts) : children;\n\n  return renderedChildren;\n}\n\nexport function dialogClassNames({\n  className,\n}: Pick<DialogProps, \"className\"> = {}) {\n  return cn(dialogRootClasses, className);\n}\n\nexport function dialogTriggerClassNames({\n  className,\n  size = \"md\",\n  variant = \"solid\",\n}: Pick<DialogTriggerProps, \"className\" | \"size\" | \"variant\"> = {}) {\n  return buttonClassNames({ className, size, variant });\n}\n\nexport function dialogOverlayClassNames({\n  className,\n}: {\n  className?: string;\n} = {}) {\n  return cn(dialogOverlayBaseClasses, className);\n}\n\nexport function dialogContentClassNames({\n  className,\n  scrollBehavior = \"inside\",\n  size = \"md\",\n}: Pick<DialogContentProps, \"className\" | \"scrollBehavior\" | \"size\"> = {}) {\n  return cn(\n    dialogContentBaseClasses,\n    dialogContentSizeClasses[size],\n    dialogContentScrollBehaviorClasses[scrollBehavior],\n    className,\n  );\n}\n\nexport function dialogHeaderClassNames({\n  className,\n  hasCloseButton = false,\n}: Pick<DialogHeaderProps, \"className\"> & {\n  hasCloseButton?: boolean;\n} = {}) {\n  return cn(\n    dialogHeaderClasses,\n    hasCloseButton && dialogHeaderWithCloseButtonClasses,\n    className,\n  );\n}\n\nexport function dialogFooterClassNames({\n  className,\n}: Pick<DialogFooterProps, \"className\"> = {}) {\n  return cn(dialogFooterClasses, className);\n}\n\nexport function dialogTitleClassNames({\n  className,\n  visuallyHidden = false,\n}: Pick<DialogTitleProps, \"className\" | \"visuallyHidden\"> = {}) {\n  return cn(\n    dialogTitleClasses,\n    visuallyHidden && visuallyHiddenClasses,\n    className,\n  );\n}\n\nexport function dialogDescriptionClassNames({\n  className,\n}: Pick<DialogDescriptionProps, \"className\"> = {}) {\n  return cn(dialogDescriptionClasses, className);\n}\n\nexport function dialogCloseButtonClassNames({\n  className,\n}: {\n  className?: string;\n} = {}) {\n  return cn(dialogCloseButtonClasses, className);\n}\n\nfunction CloseIcon() {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={dialogCloseIconClasses}\n      fill=\"none\"\n      viewBox=\"0 0 16 16\"\n    >\n      <path\n        d=\"m4.25 4.25 7.5 7.5m0-7.5-7.5 7.5\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeWidth=\"1.75\"\n      />\n    </svg>\n  );\n}\n\nexport const Dialog = forwardRef<HTMLDivElement, DialogProps>(\n  (\n    {\n      \"data-slot\": dataSlot,\n      children,\n      className,\n      defaultOpen,\n      onOpenChange,\n      open,\n      ...props\n    },\n    ref,\n  ) => {\n    const [resolvedOpen, handleOpenChange] = useScopedOverlayState({\n      open,\n      defaultOpen,\n      onOpenChange,\n    });\n    const previousOpenRef = useRef(resolvedOpen);\n    const triggerElementRef = useRef<HTMLButtonElement | null>(null);\n    const { portalContainer, rootRef } = useProviderPortalRoot<HTMLDivElement>({\n      forwardedRef: ref,\n      portalSlot: \"dialog-portal-container\",\n    });\n    const rootContextValue = useMemo<DialogRootContextValue>(\n      () => ({\n        setTriggerElement: (element) => {\n          triggerElementRef.current = element;\n        },\n      }),\n      [],\n    );\n\n    useEffect(() => {\n      const wasOpen = previousOpenRef.current;\n\n      previousOpenRef.current = resolvedOpen;\n\n      if (wasOpen && !resolvedOpen && typeof window !== \"undefined\") {\n        const restoreFocus = window.setTimeout(() => {\n          triggerElementRef.current?.focus();\n        }, 0);\n\n        return () => {\n          window.clearTimeout(restoreFocus);\n        };\n      }\n\n      return undefined;\n    }, [resolvedOpen]);\n\n    return (\n      <div\n        ref={rootRef}\n        data-slot={dataSlot ?? \"dialog\"}\n        className={dialogClassNames({ className })}\n      >\n        <DethinkPortalProvider container={portalContainer}>\n          <DialogRootContext.Provider value={rootContextValue}>\n            <AriaDialogTrigger\n              {...props}\n              isOpen={resolvedOpen}\n              onOpenChange={handleOpenChange}\n            >\n              {canOpenWithoutTrigger(open, defaultOpen) ? (\n                <TriggerlessPressRegistration />\n              ) : null}\n              {children}\n            </AriaDialogTrigger>\n          </DialogRootContext.Provider>\n        </DethinkPortalProvider>\n      </div>\n    );\n  },\n);\n\nDialog.displayName = \"Dialog\";\n\nexport const DialogTrigger = forwardRef<HTMLButtonElement, DialogTriggerProps>(\n  (\n    {\n      \"data-slot\": dataSlot,\n      children,\n      className,\n      size = \"md\",\n      variant = \"solid\",\n      ...props\n    },\n    ref,\n  ) => {\n    const rootContext = useContext(DialogRootContext);\n    const setTriggerRef = (node: HTMLButtonElement | null) => {\n      rootContext?.setTriggerElement(node);\n    };\n\n    return (\n      <AriaButton\n        {...props}\n        ref={composeRefs(ref, setTriggerRef)}\n        data-slot={dataSlot ?? \"dialog-trigger\"}\n        className={dialogTriggerClassNames({ className, size, variant })}\n      >\n        {children}\n      </AriaButton>\n    );\n  },\n);\n\nDialogTrigger.displayName = \"DialogTrigger\";\n\nexport const DialogContent = forwardRef<HTMLDivElement, DialogContentProps>(\n  (\n    {\n      \"aria-describedby\": ariaDescribedBy,\n      \"aria-label\": ariaLabel,\n      \"aria-labelledby\": ariaLabelledBy,\n      children,\n      className,\n      closeButtonClassName,\n      closeButtonLabel = \"Close dialog\",\n      dismissible = false,\n      keyboardDismissDisabled = false,\n      overlayClassName,\n      scrollBehavior = \"inside\",\n      shouldCloseOnInteractOutside,\n      showCloseButton = false,\n      size = \"md\",\n      ...props\n    },\n    ref,\n  ) => {\n    const defaultTitleId = useId();\n    const [titleId, setTitleId] = useState<string | null>(null);\n    const [descriptionId, setDescriptionId] = useState<string | null>(null);\n    const labelledBy =\n      ariaLabelledBy ?? (ariaLabel ? undefined : (titleId ?? defaultTitleId));\n    const contextValue = useMemo(\n      () => ({\n        defaultTitleId,\n        hasCloseButton: showCloseButton,\n        setDescriptionId,\n        setTitleId,\n      }),\n      [defaultTitleId, showCloseButton],\n    );\n\n    return (\n      <ModalOverlay\n        {...props}\n        data-slot=\"dialog-overlay\"\n        isDismissable={dismissible}\n        isKeyboardDismissDisabled={keyboardDismissDisabled}\n        shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}\n        className={dialogOverlayClassNames({ className: overlayClassName })}\n      >\n        <Modal\n          ref={ref}\n          data-slot=\"dialog-content\"\n          data-size={size}\n          data-scroll-behavior={scrollBehavior}\n          className={dialogContentClassNames({\n            className,\n            scrollBehavior,\n            size,\n          })}\n        >\n          <AriaDialog\n            aria-describedby={joinIds(\n              ariaDescribedBy,\n              descriptionId ?? undefined,\n            )}\n            aria-label={ariaLabel}\n            aria-labelledby={labelledBy}\n            className={dialogPanelClasses}\n            data-slot=\"dialog-panel\"\n            role=\"dialog\"\n          >\n            {(opts) => (\n              <DialogContentContext.Provider value={contextValue}>\n                {showCloseButton ? (\n                  <DialogClose\n                    aria-label={closeButtonLabel}\n                    className={dialogCloseButtonClassNames({\n                      className: closeButtonClassName,\n                    })}\n                  />\n                ) : null}\n                {renderDialogChildren(children, opts)}\n              </DialogContentContext.Provider>\n            )}\n          </AriaDialog>\n        </Modal>\n      </ModalOverlay>\n    );\n  },\n);\n\nDialogContent.displayName = \"DialogContent\";\n\nexport const DialogHeader = forwardRef<HTMLDivElement, DialogHeaderProps>(\n  ({ className, ...props }, ref) => {\n    const context = useContext(DialogContentContext);\n\n    return (\n      <div\n        {...props}\n        ref={ref}\n        data-slot=\"dialog-header\"\n        className={dialogHeaderClassNames({\n          className,\n          hasCloseButton: context?.hasCloseButton,\n        })}\n      />\n    );\n  },\n);\n\nDialogHeader.displayName = \"DialogHeader\";\n\nexport const DialogFooter = forwardRef<HTMLDivElement, DialogFooterProps>(\n  ({ className, ...props }, ref) => (\n    <div\n      {...props}\n      ref={ref}\n      data-slot=\"dialog-footer\"\n      className={dialogFooterClassNames({ className })}\n    />\n  ),\n);\n\nDialogFooter.displayName = \"DialogFooter\";\n\nexport const DialogTitle = forwardRef<HTMLHeadingElement, DialogTitleProps>(\n  ({ className, id, level = 2, visuallyHidden = false, ...props }, ref) => {\n    const context = useContext(DialogContentContext);\n    const generatedId = useId();\n    const resolvedId = id ?? context?.defaultTitleId ?? generatedId;\n\n    useIsomorphicLayoutEffect(() => {\n      context?.setTitleId(resolvedId);\n\n      return () => {\n        context?.setTitleId(null);\n      };\n    }, [context, resolvedId]);\n\n    return (\n      <AriaHeading\n        {...props}\n        ref={ref}\n        id={resolvedId}\n        slot=\"title\"\n        level={level}\n        data-slot=\"dialog-title\"\n        className={dialogTitleClassNames({ className, visuallyHidden })}\n      />\n    );\n  },\n);\n\nDialogTitle.displayName = \"DialogTitle\";\n\nexport const DialogDescription = forwardRef<\n  HTMLParagraphElement,\n  DialogDescriptionProps\n>(({ className, id, ...props }, ref) => {\n  const context = useContext(DialogContentContext);\n  const generatedId = useId();\n  const resolvedId = id ?? generatedId;\n\n  useEffect(() => {\n    context?.setDescriptionId(resolvedId);\n\n    return () => {\n      context?.setDescriptionId(null);\n    };\n  }, [context, resolvedId]);\n\n  return (\n    <p\n      {...props}\n      ref={ref}\n      id={resolvedId}\n      data-slot=\"dialog-description\"\n      className={dialogDescriptionClassNames({ className })}\n    />\n  );\n});\n\nDialogDescription.displayName = \"DialogDescription\";\n\nexport const DialogClose = forwardRef<HTMLButtonElement, DialogCloseProps>(\n  (\n    {\n      \"aria-label\": ariaLabel,\n      children,\n      className,\n      size,\n      variant = \"ghost\",\n      ...props\n    },\n    ref,\n  ) => {\n    const hasVisibleChildren = children != null;\n    const resolvedSize = size ?? (hasVisibleChildren ? \"md\" : \"icon\");\n\n    return (\n      <AriaButton\n        {...props}\n        ref={ref}\n        aria-label={\n          ariaLabel ?? (hasVisibleChildren ? undefined : \"Close dialog\")\n        }\n        slot=\"close\"\n        data-slot=\"dialog-close\"\n        className={dialogTriggerClassNames({\n          className,\n          size: resolvedSize,\n          variant,\n        })}\n      >\n        {hasVisibleChildren ? children : <CloseIcon />}\n      </AriaButton>\n    );\n  },\n);\n\nDialogClose.displayName = \"DialogClose\";\n\nexport interface AlertDialogProps extends DialogProps {}\n\nexport interface AlertDialogTriggerProps extends DialogTriggerProps {}\n\nexport interface AlertDialogContentProps extends DialogContentProps {}\n\nexport interface AlertDialogHeaderProps extends DialogHeaderProps {}\n\nexport interface AlertDialogFooterProps extends DialogFooterProps {}\n\nexport interface AlertDialogTitleProps extends DialogTitleProps {}\n\nexport interface AlertDialogDescriptionProps extends DialogDescriptionProps {}\n\nexport interface AlertDialogCloseProps extends DialogCloseProps {}\n\nexport interface AlertDialogCancelProps extends DialogCloseProps {}\n\nexport interface AlertDialogActionProps extends DialogCloseProps {}\n\ninterface AlertDialogContentContextValue {\n  defaultTitleId: string;\n  hasCloseButton: boolean;\n  setDescriptionId: (id: string | null) => void;\n  setTitleId: (id: string | null) => void;\n}\n\nconst AlertDialogContentContext =\n  createContext<AlertDialogContentContextValue | null>(null);\n\nexport function alertDialogClassNames({\n  className,\n}: Pick<AlertDialogProps, \"className\"> = {}) {\n  return cn(dialogRootClasses, className);\n}\n\nexport function alertDialogTriggerClassNames({\n  className,\n  size = \"md\",\n  variant = \"solid\",\n}: Pick<AlertDialogTriggerProps, \"className\" | \"size\" | \"variant\"> = {}) {\n  return buttonClassNames({ className, size, variant });\n}\n\nexport function alertDialogOverlayClassNames({\n  className,\n}: {\n  className?: string;\n} = {}) {\n  return cn(dialogOverlayBaseClasses, className);\n}\n\nexport function alertDialogContentClassNames({\n  className,\n  scrollBehavior = \"inside\",\n  size = \"md\",\n}: Pick<\n  AlertDialogContentProps,\n  \"className\" | \"scrollBehavior\" | \"size\"\n> = {}) {\n  return cn(\n    dialogContentBaseClasses,\n    dialogContentSizeClasses[size],\n    dialogContentScrollBehaviorClasses[scrollBehavior],\n    className,\n  );\n}\n\nexport function alertDialogHeaderClassNames({\n  className,\n  hasCloseButton = false,\n}: Pick<AlertDialogHeaderProps, \"className\"> & {\n  hasCloseButton?: boolean;\n} = {}) {\n  return cn(\n    dialogHeaderClasses,\n    hasCloseButton && dialogHeaderWithCloseButtonClasses,\n    className,\n  );\n}\n\nexport function alertDialogFooterClassNames({\n  className,\n}: Pick<AlertDialogFooterProps, \"className\"> = {}) {\n  return cn(alertDialogFooterClasses, className);\n}\n\nexport function alertDialogTitleClassNames({\n  className,\n  visuallyHidden = false,\n}: Pick<AlertDialogTitleProps, \"className\" | \"visuallyHidden\"> = {}) {\n  return cn(\n    dialogTitleClasses,\n    visuallyHidden && visuallyHiddenClasses,\n    className,\n  );\n}\n\nexport function alertDialogDescriptionClassNames({\n  className,\n}: Pick<AlertDialogDescriptionProps, \"className\"> = {}) {\n  return cn(dialogDescriptionClasses, className);\n}\n\nexport function alertDialogCloseButtonClassNames({\n  className,\n}: {\n  className?: string;\n} = {}) {\n  return cn(dialogCloseButtonClasses, className);\n}\n\nfunction alertDialogButtonClassNames({\n  className,\n  size = \"md\",\n  variant = \"solid\",\n}: Pick<AlertDialogCloseProps, \"className\" | \"size\" | \"variant\"> = {}) {\n  return buttonClassNames({ className, size, variant });\n}\n\nexport function alertDialogCloseClassNames({\n  className,\n  size = \"icon\",\n  variant = \"ghost\",\n}: Pick<AlertDialogCloseProps, \"className\" | \"size\" | \"variant\"> = {}) {\n  return alertDialogButtonClassNames({ className, size, variant });\n}\n\nexport function alertDialogCancelClassNames({\n  className,\n  size = \"md\",\n  variant = \"outline\",\n}: Pick<AlertDialogCancelProps, \"className\" | \"size\" | \"variant\"> = {}) {\n  return alertDialogButtonClassNames({ className, size, variant });\n}\n\nexport function alertDialogActionClassNames({\n  className,\n  size = \"md\",\n  variant = \"solid\",\n}: Pick<AlertDialogActionProps, \"className\" | \"size\" | \"variant\"> = {}) {\n  return alertDialogButtonClassNames({ className, size, variant });\n}\n\nexport const AlertDialog = forwardRef<HTMLDivElement, AlertDialogProps>(\n  (\n    {\n      \"data-slot\": dataSlot,\n      children,\n      className,\n      defaultOpen,\n      onOpenChange,\n      open,\n      ...props\n    },\n    ref,\n  ) => {\n    const [resolvedOpen, handleOpenChange] = useScopedOverlayState({\n      open,\n      defaultOpen,\n      onOpenChange,\n    });\n    const { portalContainer, rootRef } = useProviderPortalRoot<HTMLDivElement>({\n      forwardedRef: ref,\n      portalSlot: \"alert-dialog-portal-container\",\n    });\n\n    return (\n      <div\n        ref={rootRef}\n        data-slot={dataSlot ?? \"alert-dialog\"}\n        className={alertDialogClassNames({ className })}\n      >\n        <DethinkPortalProvider container={portalContainer}>\n          <AriaDialogTrigger\n            {...props}\n            isOpen={resolvedOpen}\n            onOpenChange={handleOpenChange}\n          >\n            {canOpenWithoutTrigger(open, defaultOpen) ? (\n              <TriggerlessPressRegistration />\n            ) : null}\n            {children}\n          </AriaDialogTrigger>\n        </DethinkPortalProvider>\n      </div>\n    );\n  },\n);\n\nAlertDialog.displayName = \"AlertDialog\";\n\nexport const AlertDialogTrigger = forwardRef<\n  HTMLButtonElement,\n  AlertDialogTriggerProps\n>(({ children, className, size = \"md\", variant = \"solid\", ...props }, ref) => {\n  return (\n    <AriaButton\n      {...props}\n      ref={ref}\n      data-slot=\"alert-dialog-trigger\"\n      className={alertDialogTriggerClassNames({ className, size, variant })}\n    >\n      {children}\n    </AriaButton>\n  );\n});\n\nAlertDialogTrigger.displayName = \"AlertDialogTrigger\";\n\nexport const AlertDialogContent = forwardRef<\n  HTMLDivElement,\n  AlertDialogContentProps\n>(\n  (\n    {\n      \"aria-describedby\": ariaDescribedBy,\n      \"aria-label\": ariaLabel,\n      \"aria-labelledby\": ariaLabelledBy,\n      children,\n      className,\n      closeButtonClassName,\n      closeButtonLabel = \"Close alert dialog\",\n      dismissible = false,\n      keyboardDismissDisabled = false,\n      overlayClassName,\n      scrollBehavior = \"inside\",\n      shouldCloseOnInteractOutside,\n      showCloseButton = false,\n      size = \"md\",\n      ...props\n    },\n    ref,\n  ) => {\n    const defaultTitleId = useId();\n    const [titleId, setTitleId] = useState<string | null>(null);\n    const [descriptionId, setDescriptionId] = useState<string | null>(null);\n    const labelledBy =\n      ariaLabelledBy ?? (ariaLabel ? undefined : (titleId ?? defaultTitleId));\n    const contextValue = useMemo(\n      () => ({\n        defaultTitleId,\n        hasCloseButton: showCloseButton,\n        setDescriptionId,\n        setTitleId,\n      }),\n      [defaultTitleId, showCloseButton],\n    );\n\n    return (\n      <ModalOverlay\n        {...props}\n        data-slot=\"alert-dialog-overlay\"\n        isDismissable={dismissible}\n        isKeyboardDismissDisabled={keyboardDismissDisabled}\n        shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}\n        className={alertDialogOverlayClassNames({\n          className: overlayClassName,\n        })}\n      >\n        <Modal\n          ref={ref}\n          data-slot=\"alert-dialog-content\"\n          data-size={size}\n          data-scroll-behavior={scrollBehavior}\n          className={alertDialogContentClassNames({\n            className,\n            scrollBehavior,\n            size,\n          })}\n        >\n          <AriaDialog\n            aria-describedby={joinIds(\n              ariaDescribedBy,\n              descriptionId ?? undefined,\n            )}\n            aria-label={ariaLabel}\n            aria-labelledby={labelledBy}\n            className={dialogPanelClasses}\n            data-slot=\"alert-dialog-panel\"\n            role=\"alertdialog\"\n          >\n            {(opts) => (\n              <AlertDialogContentContext.Provider value={contextValue}>\n                {showCloseButton ? (\n                  <AlertDialogClose\n                    aria-label={closeButtonLabel}\n                    className={alertDialogCloseButtonClassNames({\n                      className: closeButtonClassName,\n                    })}\n                  />\n                ) : null}\n                {renderDialogChildren(children, opts)}\n              </AlertDialogContentContext.Provider>\n            )}\n          </AriaDialog>\n        </Modal>\n      </ModalOverlay>\n    );\n  },\n);\n\nAlertDialogContent.displayName = \"AlertDialogContent\";\n\nexport const AlertDialogHeader = forwardRef<\n  HTMLDivElement,\n  AlertDialogHeaderProps\n>(({ className, ...props }, ref) => {\n  const context = useContext(AlertDialogContentContext);\n\n  return (\n    <div\n      {...props}\n      ref={ref}\n      data-slot=\"alert-dialog-header\"\n      className={alertDialogHeaderClassNames({\n        className,\n        hasCloseButton: context?.hasCloseButton,\n      })}\n    />\n  );\n});\n\nAlertDialogHeader.displayName = \"AlertDialogHeader\";\n\nexport const AlertDialogFooter = forwardRef<\n  HTMLDivElement,\n  AlertDialogFooterProps\n>(({ className, ...props }, ref) => (\n  <div\n    {...props}\n    ref={ref}\n    data-slot=\"alert-dialog-footer\"\n    className={alertDialogFooterClassNames({ className })}\n  />\n));\n\nAlertDialogFooter.displayName = \"AlertDialogFooter\";\n\nexport const AlertDialogTitle = forwardRef<\n  HTMLHeadingElement,\n  AlertDialogTitleProps\n>(({ className, id, level = 2, visuallyHidden = false, ...props }, ref) => {\n  const context = useContext(AlertDialogContentContext);\n  const generatedId = useId();\n  const resolvedId = id ?? context?.defaultTitleId ?? generatedId;\n\n  useIsomorphicLayoutEffect(() => {\n    context?.setTitleId(resolvedId);\n\n    return () => {\n      context?.setTitleId(null);\n    };\n  }, [context, resolvedId]);\n\n  return (\n    <AriaHeading\n      {...props}\n      ref={ref}\n      id={resolvedId}\n      slot=\"title\"\n      level={level}\n      data-slot=\"alert-dialog-title\"\n      className={alertDialogTitleClassNames({ className, visuallyHidden })}\n    />\n  );\n});\n\nAlertDialogTitle.displayName = \"AlertDialogTitle\";\n\nexport const AlertDialogDescription = forwardRef<\n  HTMLParagraphElement,\n  AlertDialogDescriptionProps\n>(({ className, id, ...props }, ref) => {\n  const context = useContext(AlertDialogContentContext);\n  const generatedId = useId();\n  const resolvedId = id ?? generatedId;\n\n  useEffect(() => {\n    context?.setDescriptionId(resolvedId);\n\n    return () => {\n      context?.setDescriptionId(null);\n    };\n  }, [context, resolvedId]);\n\n  return (\n    <p\n      {...props}\n      ref={ref}\n      id={resolvedId}\n      data-slot=\"alert-dialog-description\"\n      className={alertDialogDescriptionClassNames({ className })}\n    />\n  );\n});\n\nAlertDialogDescription.displayName = \"AlertDialogDescription\";\n\nfunction renderAlertDialogButton({\n  ariaLabel,\n  children,\n  className,\n  dataSlot,\n  defaultAriaLabel,\n  defaultVariant,\n  ref,\n  size,\n  variant,\n  ...props\n}: AlertDialogCloseProps & {\n  ariaLabel?: string;\n  dataSlot: string;\n  defaultAriaLabel: string;\n  defaultVariant: ButtonVariant;\n  ref: Ref<HTMLButtonElement>;\n}) {\n  const hasVisibleChildren = children != null;\n  const resolvedSize = size ?? (hasVisibleChildren ? \"md\" : \"icon\");\n\n  return (\n    <AriaButton\n      {...props}\n      ref={ref}\n      aria-label={\n        ariaLabel ?? (hasVisibleChildren ? undefined : defaultAriaLabel)\n      }\n      slot=\"close\"\n      data-slot={dataSlot}\n      className={alertDialogButtonClassNames({\n        className,\n        size: resolvedSize,\n        variant: variant ?? defaultVariant,\n      })}\n    >\n      {hasVisibleChildren ? children : <CloseIcon />}\n    </AriaButton>\n  );\n}\n\nexport const AlertDialogClose = forwardRef<\n  HTMLButtonElement,\n  AlertDialogCloseProps\n>(\n  (\n    { \"aria-label\": ariaLabel, children, className, size, variant, ...props },\n    ref,\n  ) =>\n    renderAlertDialogButton({\n      ...props,\n      ariaLabel,\n      children,\n      className,\n      dataSlot: \"alert-dialog-close\",\n      defaultAriaLabel: \"Close alert dialog\",\n      defaultVariant: \"ghost\",\n      ref,\n      size,\n      variant,\n    }),\n);\n\nAlertDialogClose.displayName = \"AlertDialogClose\";\n\nexport const AlertDialogCancel = forwardRef<\n  HTMLButtonElement,\n  AlertDialogCancelProps\n>(\n  (\n    { \"aria-label\": ariaLabel, children, className, size, variant, ...props },\n    ref,\n  ) =>\n    renderAlertDialogButton({\n      ...props,\n      ariaLabel,\n      children,\n      className,\n      dataSlot: \"alert-dialog-cancel\",\n      defaultAriaLabel: \"Cancel alert dialog\",\n      defaultVariant: \"outline\",\n      ref,\n      size,\n      variant,\n    }),\n);\n\nAlertDialogCancel.displayName = \"AlertDialogCancel\";\n\nexport const AlertDialogAction = forwardRef<\n  HTMLButtonElement,\n  AlertDialogActionProps\n>(\n  (\n    { \"aria-label\": ariaLabel, children, className, size, variant, ...props },\n    ref,\n  ) =>\n    renderAlertDialogButton({\n      ...props,\n      ariaLabel,\n      children,\n      className,\n      dataSlot: \"alert-dialog-action\",\n      defaultAriaLabel: \"Confirm alert dialog\",\n      defaultVariant: \"solid\",\n      ref,\n      size,\n      variant,\n    }),\n);\n\nAlertDialogAction.displayName = \"AlertDialogAction\";\n"
    },
    {
      "path": "packages/components/src/components/dialog/index.ts",
      "type": "registry:file",
      "target": "~/components/dethink/components/dialog/index.ts",
      "content": "export {\n  AlertDialog,\n  AlertDialogAction,\n  AlertDialogCancel,\n  AlertDialogClose,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n  AlertDialogTrigger,\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n  dialogClassNames,\n  dialogCloseButtonClassNames,\n  dialogContentClassNames,\n  dialogDescriptionClassNames,\n  dialogFooterClassNames,\n  dialogHeaderClassNames,\n  dialogOverlayClassNames,\n  dialogTitleClassNames,\n  dialogTriggerClassNames,\n  alertDialogActionClassNames,\n  alertDialogCancelClassNames,\n  alertDialogClassNames,\n  alertDialogCloseClassNames,\n  alertDialogCloseButtonClassNames,\n  alertDialogContentClassNames,\n  alertDialogDescriptionClassNames,\n  alertDialogFooterClassNames,\n  alertDialogHeaderClassNames,\n  alertDialogOverlayClassNames,\n  alertDialogTitleClassNames,\n  alertDialogTriggerClassNames,\n  type AlertDialogActionProps,\n  type AlertDialogCancelProps,\n  type AlertDialogCloseProps,\n  type AlertDialogContentProps,\n  type AlertDialogDescriptionProps,\n  type AlertDialogFooterProps,\n  type AlertDialogHeaderProps,\n  type AlertDialogProps,\n  type AlertDialogTitleProps,\n  type AlertDialogTriggerProps,\n  type DialogCloseProps,\n  type DialogContentProps,\n  type DialogDescriptionProps,\n  type DialogFooterProps,\n  type DialogHeaderProps,\n  type DialogProps,\n  type DialogScrollBehavior,\n  type DialogSize,\n  type DialogTitleProps,\n  type DialogTriggerProps,\n} from \"./dialog\";\n"
    },
    {
      "path": "packages/components/src/components/dialog/triggerless-press-registration.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/components/dialog/triggerless-press-registration.tsx",
      "content": "import { usePress } from \"react-aria\";\n\n/**\n * React Aria's `DialogTrigger` wraps its children in a press responder and\n * warns (\"A PressResponder was rendered without a pressable child\") when no\n * pressable registers with it. A dialog that can open without a trigger\n * (controlled, or `defaultOpen`) legitimately has none, so the root renders\n * this to register on its behalf. It renders nothing and passes no ref, so a\n * real trigger alongside it still gets the trigger props, its `aria-expanded`\n * and focus restoration.\n */\nexport function TriggerlessPressRegistration() {\n  usePress({});\n  return null;\n}\n\n/** Whether a dialog root can open without a trigger, and so registers one itself. */\nexport function canOpenWithoutTrigger(\n  open: boolean | undefined,\n  defaultOpen: boolean | undefined,\n) {\n  return open !== undefined || defaultOpen === true;\n}\n"
    },
    {
      "path": "packages/components/src/utils/provider-portal.tsx",
      "type": "registry:file",
      "target": "~/components/dethink/utils/provider-portal.tsx",
      "content": "import {\n  createContext,\n  type ForwardedRef,\n  type ReactNode,\n  type RefCallback,\n  useCallback,\n  useContext,\n  useEffect,\n  useRef,\n  useState,\n} from \"react\";\nimport { UNSAFE_PortalProvider } from \"react-aria/PortalProvider\";\nimport { setDethinkPortalOwner } from \"./portal-owner\";\n\nconst providerPortalDefaultClasses = \"bg-background font-sans text-foreground\";\nconst providerPortalMirroredAttributes = [\n  \"data-theme\",\n  \"data-density\",\n  \"dir\",\n] as const;\n\nexport type ProviderPortalMirroredAttribute =\n  (typeof providerPortalMirroredAttributes)[number];\n\nexport interface ProviderPortalRootOptions<T extends HTMLElement> {\n  forwardedRef?: ForwardedRef<T>;\n  portalSlot: string;\n}\n\nexport interface ProviderPortalRootState<T extends HTMLElement> {\n  portalContainer: HTMLElement | null;\n  rootRef: RefCallback<T>;\n}\n\nexport interface DethinkPortalProviderProps {\n  children?: ReactNode;\n  container: HTMLElement | null;\n}\n\nconst DethinkPortalContext = createContext(false);\nconst OverlayVisibilityContext = createContext(true);\n\n/** Persistent surfaces can dismiss overlays without unmounting their content. */\nexport function DethinkOverlayScope({\n  visible,\n  children,\n}: {\n  visible: boolean;\n  children?: ReactNode;\n}) {\n  const parentVisible = useContext(OverlayVisibilityContext);\n  return (\n    <OverlayVisibilityContext.Provider value={visible && parentVisible}>\n      {children}\n    </OverlayVisibilityContext.Provider>\n  );\n}\n\nexport function useScopedOverlayState({\n  open,\n  defaultOpen = false,\n  onOpenChange,\n}: {\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n}): readonly [boolean, (open: boolean) => void] {\n  const visible = useContext(OverlayVisibilityContext);\n  const [localOpen, setLocalOpen] = useState(defaultOpen);\n  const requestedOpen = open ?? localOpen;\n  const closeRequested = useRef(false);\n  const setOpen = useCallback(\n    (next: boolean) => {\n      if (next && !visible) return;\n      if (open === undefined) setLocalOpen(next);\n      onOpenChange?.(next);\n    },\n    [open, onOpenChange, visible],\n  );\n  useEffect(() => {\n    if (visible) closeRequested.current = false;\n    else if (requestedOpen && !closeRequested.current) {\n      closeRequested.current = true;\n      // Synchronize overlay state with the containing surface's lifecycle.\n      setOpen(false);\n    }\n  }, [visible, requestedOpen, setOpen]);\n  return [visible && requestedOpen, setOpen];\n}\n\nfunction assignForwardedRef<T>(\n  ref: ForwardedRef<T> | undefined,\n  value: T | null,\n) {\n  if (typeof ref === \"function\") {\n    ref(value);\n  } else if (ref) {\n    ref.current = value;\n  }\n}\n\nfunction fallbackProviderAttribute(name: ProviderPortalMirroredAttribute) {\n  if (typeof document === \"undefined\") {\n    return null;\n  }\n\n  if (name === \"data-density\") {\n    return \"default\";\n  }\n\n  if (name === \"data-theme\") {\n    return document.documentElement.getAttribute(name) ?? \"system\";\n  }\n\n  return document.documentElement.getAttribute(\"dir\") ?? document.dir ?? \"ltr\";\n}\n\nexport function syncDethinkPortalContainer({\n  container,\n  portalSlot,\n  provider,\n}: {\n  container: HTMLElement;\n  portalSlot: string;\n  provider: HTMLElement | null;\n}) {\n  const source =\n    provider ??\n    (typeof document === \"undefined\" ? null : document.documentElement);\n\n  container.setAttribute(\"data-slot\", portalSlot);\n  container.setAttribute(\"data-dethink-provider\", \"\");\n  container.className = provider?.className || providerPortalDefaultClasses;\n  container.style.cssText = provider?.getAttribute(\"style\") ?? \"\";\n  container.style.display = \"contents\";\n\n  for (const attribute of providerPortalMirroredAttributes) {\n    const value =\n      source?.getAttribute(attribute) ?? fallbackProviderAttribute(attribute);\n\n    if (value) {\n      container.setAttribute(attribute, value);\n    } else {\n      container.removeAttribute(attribute);\n    }\n  }\n}\n\nexport function useProviderPortalRoot<T extends HTMLElement>({\n  forwardedRef,\n  portalSlot,\n}: ProviderPortalRootOptions<T>): ProviderPortalRootState<T> {\n  const [portalContainer] = useState<HTMLElement | null>(() => {\n    if (typeof document === \"undefined\") {\n      return null;\n    }\n\n    return document.createElement(\"div\");\n  });\n  const [rootElement, setRootElement] = useState<T | null>(null);\n\n  const rootRef = useCallback(\n    (node: T | null) => {\n      setRootElement(node);\n      assignForwardedRef(forwardedRef, node);\n\n      if (node && portalContainer && typeof document !== \"undefined\") {\n        setDethinkPortalOwner(portalContainer, node);\n        const provider =\n          node.closest<HTMLElement>(\"[data-dethink-provider]\") ?? null;\n\n        syncDethinkPortalContainer({\n          container: portalContainer,\n          portalSlot,\n          provider,\n        });\n\n        if (!portalContainer.isConnected) {\n          document.body.appendChild(portalContainer);\n        }\n      }\n    },\n    [forwardedRef, portalContainer, portalSlot],\n  );\n\n  useEffect(() => {\n    if (!portalContainer || typeof document === \"undefined\") {\n      return undefined;\n    }\n\n    const provider =\n      rootElement?.closest<HTMLElement>(\"[data-dethink-provider]\") ?? null;\n    const syncContainer = () => {\n      syncDethinkPortalContainer({\n        container: portalContainer,\n        portalSlot,\n        provider,\n      });\n    };\n\n    syncContainer();\n    document.body.appendChild(portalContainer);\n\n    const observerTarget = provider ?? document.documentElement;\n    const observer = new MutationObserver(syncContainer);\n    observer.observe(observerTarget, {\n      attributeFilter: [\"class\", \"data-density\", \"data-theme\", \"dir\", \"style\"],\n      attributes: true,\n    });\n\n    return () => {\n      observer.disconnect();\n      portalContainer.remove();\n    };\n  }, [portalContainer, portalSlot, rootElement]);\n\n  return {\n    portalContainer,\n    rootRef,\n  };\n}\n\nexport function DethinkPortalProvider({\n  children,\n  container,\n}: DethinkPortalProviderProps) {\n  const hasDethinkPortalParent = useContext(DethinkPortalContext);\n  const content = hasDethinkPortalParent ? (\n    children\n  ) : (\n    <UNSAFE_PortalProvider getContainer={() => container}>\n      {children}\n    </UNSAFE_PortalProvider>\n  );\n\n  return (\n    <DethinkPortalContext.Provider value>\n      {content}\n    </DethinkPortalContext.Provider>\n  );\n}\n"
    },
    {
      "path": "packages/components/src/utils/portal-owner.ts",
      "type": "registry:file",
      "target": "~/components/dethink/utils/portal-owner.ts",
      "content": "/**\n * Links a Dethink portal container on <body> to the component root that owns\n * it, so a modal surface (the mobile sidebar) can keep overlays opened from\n * inside it usable while it makes the rest of the page inert. A global symbol\n * key, so components copied from the registry agree without sharing imports.\n */\nconst PORTAL_OWNER: unique symbol = Symbol.for(\"dethink.portal-owner\");\n\ntype OwnedElement = Element & { [PORTAL_OWNER]?: Element };\n\nexport function setDethinkPortalOwner(container: Element, owner: Element) {\n  (container as OwnedElement)[PORTAL_OWNER] = owner;\n}\n\n/** The element owning a Dethink portal container, or null for any other element. */\nexport function getDethinkPortalOwner(element: Element): Element | null {\n  return (element as OwnedElement)[PORTAL_OWNER] ?? null;\n}\n"
    },
    {
      "path": "packages/components/src/utils/cn.ts",
      "type": "registry:file",
      "target": "~/components/dethink/utils/cn.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n"
    }
  ]
}
