{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dialog",
  "title": "Dialog",
  "author": "andongmin94",
  "description": "A modal overlay for focused content, forms, and actions.",
  "dependencies": [
    "@base-ui/react@^1.6.0"
  ],
  "registryDependencies": [
    "https://neobrutal-ui.andongmin.com/r/button.json"
  ],
  "files": [
    {
      "path": "src/components/ui/dialog.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { XIcon } from \"lucide-react\";\n\ntype AutoFocusEventHandler = (event: Event) => void;\ntype FocusTarget = DialogPrimitive.Popup.Props[\"initialFocus\"];\ntype PointerDownOutsideEvent = CustomEvent<{ originalEvent: PointerEvent }>;\ntype FocusOutsideEvent = CustomEvent<{ originalEvent: FocusEvent }>;\ntype DismissableLayerHandlers = {\n  onEscapeKeyDown?: (event: KeyboardEvent) => void;\n  onFocusOutside?: (event: FocusOutsideEvent) => void;\n  onInteractOutside?: (event: FocusOutsideEvent | PointerDownOutsideEvent) => void;\n  onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;\n};\ntype DialogPortalProps = DialogPrimitive.Portal.Props & {\n  forceMount?: boolean;\n};\ntype DialogOverlayProps = DialogPrimitive.Backdrop.Props & {\n  asChild?: boolean;\n  children?: React.ReactNode;\n  forceMount?: boolean;\n};\ntype DialogContentProps = DialogPrimitive.Popup.Props & {\n  asChild?: boolean;\n  children?: React.ReactNode;\n  forceMount?: boolean;\n  onCloseAutoFocus?: AutoFocusEventHandler;\n  onOpenAutoFocus?: AutoFocusEventHandler;\n  showCloseButton?: boolean;\n} & DismissableLayerHandlers;\ntype DialogContextProps = {\n  dismissableLayerHandlersRef: React.RefObject<DismissableLayerHandlers>;\n  open: boolean;\n};\n\ntype PreventableBaseUIEvent = {\n  readonly baseUIHandlerPrevented?: boolean;\n  readonly defaultPrevented?: boolean;\n  preventBaseUIHandler?: () => void;\n};\ntype EventHandler = (...args: unknown[]) => unknown;\ntype RenderFunction = (props: Record<string, unknown>, state: unknown) => React.ReactElement;\n\nfunction adaptEventHandlers(props: Record<string, unknown>, beforeBaseUIHandler: boolean) {\n  const eventHandlers: Record<string, EventHandler> = {};\n\n  for (const [name, handler] of Object.entries(props)) {\n    if (!/^on[A-Z]/.test(name) || typeof handler !== \"function\") {\n      continue;\n    }\n\n    eventHandlers[name] = (...args) => {\n      const event = args[0] as PreventableBaseUIEvent | undefined;\n      if (beforeBaseUIHandler && event?.defaultPrevented) {\n        event.preventBaseUIHandler?.();\n      }\n      if (beforeBaseUIHandler && event?.baseUIHandlerPrevented) {\n        return undefined;\n      }\n\n      const result = handler(...args);\n      if (!beforeBaseUIHandler && event?.defaultPrevented) {\n        event.preventBaseUIHandler?.();\n      }\n      return result;\n    };\n  }\n\n  return eventHandlers;\n}\n\nfunction adaptRenderEventHandlers<Render>(render: Render): Render {\n  if (React.isValidElement(render)) {\n    const element = render as React.ReactElement<Record<string, unknown>>;\n    const eventHandlers = adaptEventHandlers(element.props, false);\n    return (Object.keys(eventHandlers).length === 0\n      ? render\n      : React.cloneElement(element, eventHandlers)) as unknown as Render;\n  }\n  if (typeof render === \"function\") {\n    const renderFunction = render as RenderFunction;\n    return ((props: Record<string, unknown>, state: unknown) =>\n      renderFunction({ ...props, ...adaptEventHandlers(props, true) }, state)) as unknown as Render;\n  }\n  return render;\n}\n\nconst DialogContext = React.createContext<DialogContextProps | null>(null);\n\nfunction useDialogContext() {\n  const context = React.useContext(DialogContext);\n\n  if (!context) {\n    throw new Error(\"Dialog components must be used within Dialog.\");\n  }\n\n  return context;\n}\n\nfunction createPointerEvent(event: MouseEvent | PointerEvent | TouchEvent): PointerEvent {\n  if (\"pointerId\" in event) {\n    return event;\n  }\n\n  const touch = \"changedTouches\" in event ? event.changedTouches[0] : undefined;\n  const ownerDocument = event.target instanceof Node ? event.target.ownerDocument : null;\n  const view = ownerDocument?.defaultView ?? window;\n  const PointerEventConstructor = view?.PointerEvent;\n\n  if (!PointerEventConstructor) {\n    return event as PointerEvent;\n  }\n\n  return new PointerEventConstructor(\"pointerdown\", {\n    bubbles: event.bubbles,\n    button: \"button\" in event ? event.button : 0,\n    buttons: \"buttons\" in event ? event.buttons : 1,\n    cancelable: event.cancelable,\n    clientX: touch?.clientX ?? (\"clientX\" in event ? event.clientX : 0),\n    clientY: touch?.clientY ?? (\"clientY\" in event ? event.clientY : 0),\n    composed: event.composed,\n    ctrlKey: \"ctrlKey\" in event && event.ctrlKey,\n    metaKey: \"metaKey\" in event && event.metaKey,\n    pointerType: \"changedTouches\" in event ? \"touch\" : \"mouse\",\n    shiftKey: \"shiftKey\" in event && event.shiftKey,\n  });\n}\n\nfunction createPointerDownOutsideEvent(\n  originalEvent: MouseEvent | PointerEvent | TouchEvent,\n): PointerDownOutsideEvent {\n  const event = new CustomEvent<{ originalEvent: PointerEvent }>(\n    \"dismissableLayer.pointerDownOutside\",\n    {\n      cancelable: true,\n      detail: { originalEvent: createPointerEvent(originalEvent) },\n    },\n  );\n  Object.defineProperty(event, \"target\", { configurable: true, value: originalEvent.target });\n  return event;\n}\n\nfunction createFocusOutsideEvent(originalEvent: FocusEvent | KeyboardEvent): FocusOutsideEvent {\n  const focusEvent =\n    \"relatedTarget\" in originalEvent\n      ? originalEvent\n      : new FocusEvent(\"focusin\", { bubbles: true, cancelable: true });\n  const event = new CustomEvent<{ originalEvent: FocusEvent }>(\"dismissableLayer.focusOutside\", {\n    cancelable: true,\n    detail: { originalEvent: focusEvent },\n  });\n  Object.defineProperty(event, \"target\", { configurable: true, value: originalEvent.target });\n  return event;\n}\n\nfunction adaptDismissableLayerEvent(\n  eventDetails: DialogPrimitive.Root.ChangeEventDetails,\n  handlers: DismissableLayerHandlers,\n) {\n  if (eventDetails.reason === \"escape-key\") {\n    handlers.onEscapeKeyDown?.(eventDetails.event);\n    if (eventDetails.event.defaultPrevented) {\n      eventDetails.cancel();\n    }\n    return;\n  }\n\n  if (eventDetails.reason === \"outside-press\") {\n    const event = createPointerDownOutsideEvent(eventDetails.event);\n    handlers.onPointerDownOutside?.(event);\n    handlers.onInteractOutside?.(event);\n    if (event.defaultPrevented) {\n      eventDetails.cancel();\n    }\n    return;\n  }\n\n  if (eventDetails.reason === \"focus-out\") {\n    const event = createFocusOutsideEvent(eventDetails.event);\n    handlers.onFocusOutside?.(event);\n    handlers.onInteractOutside?.(event);\n    if (event.defaultPrevented) {\n      eventDetails.cancel();\n    }\n  }\n}\n\nfunction adaptCloseClick(\n  handler: DialogPrimitive.Close.Props[\"onClick\"],\n): DialogPrimitive.Close.Props[\"onClick\"] {\n  if (!handler) {\n    return undefined;\n  }\n\n  return (event) => {\n    handler(event);\n    if (event.defaultPrevented) {\n      event.preventBaseUIHandler();\n    }\n  };\n}\n\nfunction hasForceMountedChild(children: React.ReactNode) {\n  return React.Children.toArray(children).some(\n    (child) => React.isValidElement<{ forceMount?: boolean }>(child) && child.props.forceMount,\n  );\n}\n\nfunction adaptAutoFocus(\n  handler: AutoFocusEventHandler | undefined,\n  focus: FocusTarget,\n  eventName: string,\n): FocusTarget {\n  if (!handler) {\n    return focus;\n  }\n\n  return (interactionType) => {\n    const event = new Event(eventName, { cancelable: true });\n    handler(event);\n\n    if (event.defaultPrevented) {\n      return false;\n    }\n\n    if (typeof focus === \"function\") {\n      return focus(interactionType);\n    }\n\n    if (typeof focus === \"object\" && focus !== null) {\n      return focus.current;\n    }\n\n    return typeof focus === \"boolean\" ? focus : true;\n  };\n}\n\nfunction Dialog({ defaultOpen = false, onOpenChange, open, ...props }: DialogPrimitive.Root.Props) {\n  const dismissableLayerHandlersRef = React.useRef<DismissableLayerHandlers>({});\n  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);\n  const resolvedOpen = open ?? uncontrolledOpen;\n  const contextValue = React.useMemo(\n    () => ({ dismissableLayerHandlersRef, open: resolvedOpen }),\n    [resolvedOpen],\n  );\n\n  const handleOpenChange: NonNullable<DialogPrimitive.Root.Props[\"onOpenChange\"]> = (\n    nextOpen,\n    eventDetails,\n  ) => {\n    if (!nextOpen) {\n      adaptDismissableLayerEvent(eventDetails, dismissableLayerHandlersRef.current);\n    }\n    if (eventDetails.isCanceled) {\n      return;\n    }\n    if (open === undefined) {\n      setUncontrolledOpen(nextOpen);\n    }\n    onOpenChange?.(nextOpen, eventDetails);\n  };\n\n  return (\n    <DialogContext.Provider value={contextValue}>\n      <DialogPrimitive.Root\n        data-slot=\"dialog\"\n        defaultOpen={defaultOpen}\n        open={open}\n        {...props}\n        onOpenChange={handleOpenChange}\n      />\n    </DialogContext.Provider>\n  );\n}\n\nfunction DialogTrigger({\n  asChild = false,\n  children,\n  render,\n  ...props\n}: DialogPrimitive.Trigger.Props & {\n  asChild?: boolean;\n  children?: React.ReactNode;\n}) {\n  const { open } = useDialogContext();\n  const renderElement = asChild\n    ? (React.Children.toArray(children).find(React.isValidElement) as React.ReactElement)\n    : render;\n\n  return (\n    <DialogPrimitive.Trigger\n      data-slot=\"dialog-trigger\"\n      data-state={open ? \"open\" : \"closed\"}\n      render={adaptRenderEventHandlers(renderElement)}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </DialogPrimitive.Trigger>\n  );\n}\n\nfunction DialogPortal({ forceMount, keepMounted, ...props }: DialogPortalProps) {\n  const keepPortalMounted = forceMount ?? (hasForceMountedChild(props.children) || keepMounted);\n\n  return (\n    <DialogPrimitive.Portal data-slot=\"dialog-portal\" keepMounted={keepPortalMounted} {...props} />\n  );\n}\n\nfunction DialogClose({\n  asChild = false,\n  children,\n  onClick,\n  render,\n  ...props\n}: DialogPrimitive.Close.Props & {\n  asChild?: boolean;\n  children?: React.ReactNode;\n}) {\n  const renderElement = asChild\n    ? (React.Children.toArray(children).find(React.isValidElement) as React.ReactElement)\n    : render;\n\n  return (\n    <DialogPrimitive.Close\n      data-slot=\"dialog-close\"\n      onClick={adaptCloseClick(onClick)}\n      render={adaptRenderEventHandlers(renderElement)}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </DialogPrimitive.Close>\n  );\n}\n\nfunction DialogOverlay({\n  asChild = false,\n  children,\n  className,\n  forceMount: _forceMount,\n  render,\n  ...props\n}: DialogOverlayProps) {\n  const { open } = useDialogContext();\n  const renderElement = asChild\n    ? (React.Children.toArray(children).find(React.isValidElement) as React.ReactElement)\n    : render;\n\n  return (\n    <DialogPrimitive.Backdrop\n      data-slot=\"dialog-overlay\"\n      data-state={open ? \"open\" : \"closed\"}\n      render={adaptRenderEventHandlers(renderElement)}\n      className={cn(\n        \"fixed inset-0 isolate z-50 bg-overlay duration-200 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0\",\n        className,\n      )}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </DialogPrimitive.Backdrop>\n  );\n}\n\nfunction DialogContent({\n  asChild = false,\n  className,\n  children,\n  finalFocus,\n  forceMount,\n  initialFocus,\n  onCloseAutoFocus,\n  onEscapeKeyDown,\n  onFocusOutside,\n  onInteractOutside,\n  onOpenAutoFocus,\n  onPointerDownOutside,\n  render,\n  showCloseButton = true,\n  ...props\n}: DialogContentProps) {\n  const { dismissableLayerHandlersRef, open } = useDialogContext();\n  const dismissableLayerHandlers = React.useMemo(\n    () => ({ onEscapeKeyDown, onFocusOutside, onInteractOutside, onPointerDownOutside }),\n    [onEscapeKeyDown, onFocusOutside, onInteractOutside, onPointerDownOutside],\n  );\n  dismissableLayerHandlersRef.current = dismissableLayerHandlers;\n\n  const closeButton = showCloseButton ? (\n    <DialogPrimitive.Close\n      data-slot=\"dialog-close\"\n      render={<Button variant=\"ghost\" className=\"absolute top-4 right-4\" size=\"icon-sm\" />}\n    >\n      <XIcon />\n      <span className=\"sr-only\">Close</span>\n    </DialogPrimitive.Close>\n  ) : null;\n  const contentChild = React.Children.toArray(children).find(\n    React.isValidElement,\n  ) as React.ReactElement<{ children?: React.ReactNode }>;\n  const renderElement = asChild\n    ? React.cloneElement(contentChild, undefined, contentChild.props.children, closeButton)\n    : render;\n\n  React.useEffect(\n    () => () => {\n      if (dismissableLayerHandlersRef.current === dismissableLayerHandlers) {\n        dismissableLayerHandlersRef.current = {};\n      }\n    },\n    [dismissableLayerHandlers, dismissableLayerHandlersRef],\n  );\n\n  return (\n    <DialogPortal forceMount={forceMount}>\n      <DialogOverlay />\n      <DialogPrimitive.Popup\n        data-slot=\"dialog-content\"\n        data-state={open ? \"open\" : \"closed\"}\n        render={adaptRenderEventHandlers(renderElement)}\n        initialFocus={adaptAutoFocus(onOpenAutoFocus, initialFocus, \"focusScope.autoFocusOnMount\")}\n        finalFocus={adaptAutoFocus(onCloseAutoFocus, finalFocus, \"focusScope.autoFocusOnUnmount\")}\n        className={cn(\n          \"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-base border-2 border-border bg-background p-6 text-foreground shadow-shadow duration-200 outline-none sm:max-w-lg data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95\",\n          className,\n        )}\n        {...props}\n      >\n        {asChild ? undefined : children}\n        {asChild ? undefined : closeButton}\n      </DialogPrimitive.Popup>\n    </DialogPortal>\n  );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"dialog-header\"\n      className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DialogFooter({\n  className,\n  showCloseButton = false,\n  children,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  showCloseButton?: boolean;\n}) {\n  return (\n    <div\n      data-slot=\"dialog-footer\"\n      className={cn(\"flex flex-col-reverse gap-3 sm:flex-row sm:justify-end\", className)}\n      {...props}\n    >\n      {children}\n      {showCloseButton && (\n        <DialogPrimitive.Close render={<Button variant=\"outline\" />}>Close</DialogPrimitive.Close>\n      )}\n    </div>\n  );\n}\n\nfunction DialogTitle({\n  asChild = false,\n  children,\n  className,\n  render,\n  ...props\n}: DialogPrimitive.Title.Props & { asChild?: boolean; children?: React.ReactNode }) {\n  const renderElement = asChild\n    ? (React.Children.toArray(children).find(React.isValidElement) as React.ReactElement)\n    : render;\n\n  return (\n    <DialogPrimitive.Title\n      data-slot=\"dialog-title\"\n      render={adaptRenderEventHandlers(renderElement)}\n      className={cn(\"font-heading text-lg leading-none tracking-tight\", className)}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </DialogPrimitive.Title>\n  );\n}\n\nfunction DialogDescription({\n  asChild = false,\n  children,\n  className,\n  render,\n  ...props\n}: DialogPrimitive.Description.Props & { asChild?: boolean; children?: React.ReactNode }) {\n  const renderElement = asChild\n    ? (React.Children.toArray(children).find(React.isValidElement) as React.ReactElement)\n    : render;\n\n  return (\n    <DialogPrimitive.Description\n      data-slot=\"dialog-description\"\n      render={adaptRenderEventHandlers(renderElement)}\n      className={cn(\"text-sm font-base text-foreground\", className)}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </DialogPrimitive.Description>\n  );\n}\n\nexport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogOverlay,\n  DialogPortal,\n  DialogTitle,\n  DialogTrigger,\n};\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "overlay",
    "dialog"
  ],
  "type": "registry:ui"
}