{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"dialog","type":"registry:ui","title":"Dialog","description":"A modal window that focuses the user on a single task.","dependencies":["lucide-react","radix-ui"],"registryDependencies":["https://dashral.com/r/utils.json","https://dashral.com/r/lib-locale.json","https://dashral.com/r/lib-portal.json"],"files":[{"path":"registry/dashral/dialog.tsx","type":"registry:file","target":"components/dashral/dialog.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\nimport { Dialog as DialogPrimitive } from \"radix-ui\"\nimport { XIcon } from \"lucide-react\"\n\nimport { useLocale } from \"./lib/locale\"\nimport { cn } from \"./lib/utils\"\nimport { usePortalContainer } from \"./lib/portal\"\n\ntype DialogSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst sizes: Record<DialogSize, string> = {\n  sm: \"sm:max-w-sm\",\n  md: \"sm:max-w-lg\",\n  lg: \"sm:max-w-2xl\",\n  xl: \"sm:max-w-4xl\",\n}\n\ntype DialogProps = Omit<React.ComponentProps<typeof DialogPrimitive.Root>, \"children\"> & {\n  /** What opens it, e.g. a button. Leave it out to open it yourself with open and onOpenChange. */\n  trigger?: React.ReactElement\n  /** Required, so screen readers can name the dialog; hide it with classNames.title \"sr-only\" if the design has none. */\n  title: React.ReactNode\n  description?: React.ReactNode\n  /** The body. */\n  children?: React.ReactNode\n  /** Buttons at the bottom; wrap a Cancel button in DialogClose to close the dialog. */\n  footer?: React.ReactNode\n  /** Makes the body and footer one <form>, e.g. { onSubmit } or { action }: Enter submits, a submit button in the footer works. */\n  form?: React.ComponentProps<\"form\">\n  size?: DialogSize\n  /** The × in the corner. */\n  showCloseButton?: boolean\n  /** The panel's classes. */\n  className?: string\n  classNames?: { header?: string; title?: string; description?: string; footer?: string }\n}\n\n/**\n * A window over the page that takes the focus until it's closed, in one tag: a trigger, a title and description, the\n * body as children and buttons in the footer. Give it form props and the whole thing is a form.\n */\nfunction Dialog({\n  trigger,\n  title,\n  description,\n  children,\n  footer,\n  form,\n  size = \"md\",\n  showCloseButton = true,\n  className,\n  classNames,\n  ...props\n}: DialogProps) {\n  const portalContainer = usePortalContainer()\n  const { t } = useLocale()\n  const inner = (\n    <>\n      <div data-slot=\"dialog-header\" className={cn(\"flex flex-col gap-2 text-center sm:text-start\", classNames?.header)}>\n        <DialogPrimitive.Title\n          data-slot=\"dialog-title\"\n          className={cn(\"font-heading text-lg leading-none font-medium tracking-[-0.02em]\", classNames?.title)}\n        >\n          {title}\n        </DialogPrimitive.Title>\n        {description ? (\n          <DialogPrimitive.Description data-slot=\"dialog-description\" className={cn(\"text-sm text-muted-foreground\", classNames?.description)}>\n            {description}\n          </DialogPrimitive.Description>\n        ) : null}\n      </div>\n      {children}\n      {footer && (\n        <div data-slot=\"dialog-footer\" className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", classNames?.footer)}>\n          {footer}\n        </div>\n      )}\n    </>\n  )\n\n  return (\n    <DialogPrimitive.Root data-slot=\"dialog\" {...props}>\n      {trigger && <DialogPrimitive.Trigger asChild>{trigger}</DialogPrimitive.Trigger>}\n      <DialogPrimitive.Portal container={portalContainer}>\n        <DialogOverlay />\n        <DialogPrimitive.Content\n          data-slot=\"dialog-content\"\n          // Without a description, say so explicitly: otherwise Radix warns that one is missing.\n          {...(description ? {} : { \"aria-describedby\": undefined })}\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-lg border bg-background p-6 shadow-lg data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=open]:blur-in-[2px] data-[state=open]:duration-(--duration-standard) data-[state=open]:ease-spring data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:duration-(--duration-fast) data-[state=closed]:ease-exit motion-reduce:blur-in-0\",\n            sizes[size],\n            className\n          )}\n        >\n          {form ? (\n            <form {...form} className={cn(\"grid gap-4\", form.className)}>\n              {inner}\n            </form>\n          ) : (\n            inner\n          )}\n          {showCloseButton && (\n            <DialogPrimitive.Close className=\"absolute top-4 end-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:size-4\">\n              <XIcon />\n              <span className=\"sr-only\">{t(\"close\")}</span>\n            </DialogPrimitive.Close>\n          )}\n        </DialogPrimitive.Content>\n      </DialogPrimitive.Portal>\n    </DialogPrimitive.Root>\n  )\n}\n\n/** Closes the dialog it's in: wrap a button in the footer, e.g. <DialogClose asChild><Button>Cancel</Button></DialogClose>. */\nfunction DialogClose(props: React.ComponentProps<typeof DialogPrimitive.Close>) {\n  return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />\n}\n\nfunction DialogOverlay({\n  className,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n  return (\n    <DialogPrimitive.Overlay\n      data-slot=\"dialog-overlay\"\n      className={cn(\n        \"fixed inset-0 z-50 bg-black/40 backdrop-blur-[2px] duration-(--duration-standard) data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport { Dialog, DialogClose, type DialogProps, type DialogSize }\n"}],"cssVars":{"theme":{"ease-standard":"var(--ease-standard)","ease-enter":"var(--ease-enter)","ease-exit":"var(--ease-exit)","ease-spring":"var(--ease-spring)","font-heading":"var(--font-geist, \"Geist\"), var(--font-inter, \"Inter\"), var(--font-arabic, \"IBM Plex Sans Arabic\"), ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif"},"light":{"duration-fast":"160ms","duration-standard":"240ms","duration-considered":"480ms","ease-standard":"cubic-bezier(0.2, 0, 0, 1)","ease-enter":"cubic-bezier(0, 0, 0, 1)","ease-exit":"cubic-bezier(0.3, 0, 1, 1)","ease-spring":"linear(0, 0.071 4%, 0.234 8%, 0.433 12%, 0.626 17%, 0.791 21%, 0.917 25%, 1.004 29%, 1.055 33%, 1.079 38%, 1.083 42%, 1.075 46%, 1.06 50%, 1.043 54%, 1.027 58%, 1.015 62%, 1.005 67%, 0.998 71%, 0.995 75%, 0.993 79%, 0.993 83%, 0.994 88%, 0.995 92%, 0.997 96%, 1)"},"dark":{}}}