{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"sheet","type":"registry:ui","title":"Sheet","description":"A panel that slides in from the edge of the screen.","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/sheet.tsx","type":"registry:file","target":"components/dashral/sheet.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\nimport { Dialog as SheetPrimitive } 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\n/** start and end follow the reading direction (end is the right in English, the left in Arabic); left and right don't. */\ntype SheetSide = \"top\" | \"right\" | \"bottom\" | \"left\" | \"start\" | \"end\"\n\nconst sides: Record<SheetSide, string> = {\n  right: \"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm\",\n  left: \"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm\",\n  start: \"inset-y-0 start-0 h-full w-3/4 border-e data-[state=closed]:slide-out-to-start data-[state=open]:slide-in-from-start sm:max-w-sm\",\n  end: \"inset-y-0 end-0 h-full w-3/4 border-s data-[state=closed]:slide-out-to-end data-[state=open]:slide-in-from-end sm:max-w-sm\",\n  top: \"inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n  bottom: \"inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n}\n\ntype SheetProps = Omit<React.ComponentProps<typeof SheetPrimitive.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 panel; hide it with classNames.title \"sr-only\" if the design has none. */\n  title: React.ReactNode\n  description?: React.ReactNode\n  /** The body; it scrolls when it's taller than the screen. */\n  children?: React.ReactNode\n  /** Pinned to the bottom, e.g. a Save button; wrap a Cancel button in SheetClose to close the sheet. */\n  footer?: React.ReactNode\n  /** Makes the body and footer one <form>, e.g. { onSubmit } or { action }. */\n  form?: React.ComponentProps<\"form\">\n  /** Which edge it slides in from. */\n  side?: SheetSide\n  /** none drops the padding around the body, for a layout of your own (a navigation list). */\n  padding?: \"default\" | \"none\"\n  showCloseButton?: boolean\n  /**\n   * Whether opening it focuses the first control inside (the default). false focuses the panel instead: for a menu\n   * whose first control is a text field, where a phone would otherwise open its keyboard over the menu.\n   */\n  autoFocus?: boolean\n  /** The panel's classes, e.g. a width. */\n  className?: string\n  classNames?: { header?: string; title?: string; description?: string; body?: string; footer?: string }\n}\n\n/**\n * A panel that slides in from an edge, in one tag: a trigger, a title and description, the body as children and a\n * footer pinned to the bottom. Give it form props and the whole thing is a form.\n */\nfunction Sheet({\n  trigger,\n  title,\n  description,\n  children,\n  footer,\n  form,\n  side = \"end\",\n  padding = \"default\",\n  showCloseButton = true,\n  autoFocus = true,\n  className,\n  classNames,\n  ...props\n}: SheetProps) {\n  const portalContainer = usePortalContainer()\n  const { t } = useLocale()\n  const padded = padding === \"default\"\n  const inner = (\n    <>\n      <div data-slot=\"sheet-header\" className={cn(\"flex flex-col gap-1.5 p-4\", !padded && \"sr-only\", classNames?.header)}>\n        <SheetPrimitive.Title data-slot=\"sheet-title\" className={cn(\"font-heading font-medium text-foreground\", classNames?.title)}>\n          {title}\n        </SheetPrimitive.Title>\n        {description ? (\n          <SheetPrimitive.Description data-slot=\"sheet-description\" className={cn(\"text-sm text-muted-foreground\", classNames?.description)}>\n            {description}\n          </SheetPrimitive.Description>\n        ) : null}\n      </div>\n      <div data-slot=\"sheet-body\" className={cn(\"min-h-0 flex-1 overflow-y-auto\", padded && \"px-4\", classNames?.body)}>\n        {children}\n      </div>\n      {footer && (\n        <div data-slot=\"sheet-footer\" className={cn(\"mt-auto flex flex-col gap-2 p-4\", classNames?.footer)}>\n          {footer}\n        </div>\n      )}\n    </>\n  )\n\n  return (\n    <SheetPrimitive.Root data-slot=\"sheet\" {...props}>\n      {trigger && <SheetPrimitive.Trigger asChild>{trigger}</SheetPrimitive.Trigger>}\n      <SheetPrimitive.Portal container={portalContainer}>\n        <SheetPrimitive.Overlay\n          data-slot=\"sheet-overlay\"\n          className=\"fixed inset-0 z-50 bg-black/40 backdrop-blur-[2px] duration-(--duration-standard) data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\"\n        />\n        <SheetPrimitive.Content\n          data-slot=\"sheet-content\"\n          {...(description ? {} : { \"aria-describedby\": undefined })}\n          onOpenAutoFocus={\n            autoFocus\n              ? undefined\n              : (event) => {\n                  // The panel takes focus (it's focusable, tabIndex -1), so Tab still starts inside it.\n                  event.preventDefault()\n                  ;(event.currentTarget as HTMLElement).focus()\n                }\n          }\n          className={cn(\n            \"fixed z-50 flex flex-col bg-background shadow-lg transition data-[state=closed]:animate-out data-[state=closed]:duration-(--duration-standard) data-[state=closed]:ease-exit data-[state=open]:animate-in data-[state=open]:duration-(--duration-considered) data-[state=open]:ease-enter\",\n            sides[side],\n            className\n          )}\n        >\n          {form ? (\n            <form {...form} className={cn(\"flex min-h-0 flex-1 flex-col\", form.className)}>\n              {inner}\n            </form>\n          ) : (\n            inner\n          )}\n          {showCloseButton && (\n            <SheetPrimitive.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\">\n              <XIcon className=\"size-4\" />\n              <span className=\"sr-only\">{t(\"close\")}</span>\n            </SheetPrimitive.Close>\n          )}\n        </SheetPrimitive.Content>\n      </SheetPrimitive.Portal>\n    </SheetPrimitive.Root>\n  )\n}\n\n/** Closes the sheet it's in: wrap a button, e.g. <SheetClose asChild><Button>Done</Button></SheetClose>. */\nfunction SheetClose(props: React.ComponentProps<typeof SheetPrimitive.Close>) {\n  return <SheetPrimitive.Close data-slot=\"sheet-close\" {...props} />\n}\n\nexport { Sheet, SheetClose, type SheetProps, type SheetSide }\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":{}}}