{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sheet",
  "type": "registry:ui",
  "title": "Sheet",
  "description": "Slide-out sheet from the left, right, top or bottom with a backdrop, a titled header, a close button and a scrollable body, rendered as a modal dialog in React.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://ui.minidev.pro/r/button.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/sheet.tsx",
      "type": "registry:ui",
      "target": "components/ui/sheet.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { cn } from \"@/lib/utils\"\nimport { XIcon } from \"lucide-react\"\nimport { Button } from \"@/components/ui/button\"\n\n/** A side panel that slides over the page from any edge, with a scrim, a title bar and a close button. */\ntype SheetProps = {\n  /** Controlled open state. Leave it out and the sheet renders its own \"Open sheet\" button. */\n  open?: boolean\n  onClose?: () => void\n  side?: \"left\" | \"right\" | \"top\" | \"bottom\"\n  title?: string\n  /** Panel content. Defaults to a Lumen invoice summary. */\n  children?: React.ReactNode\n  className?: string\n}\n\nconst SAMPLE_TITLE = \"Invoice INV-2041\"\n\nconst SAMPLE_CONTENT = (\n  <div className=\"space-y-4 text-sm\">\n    <dl className=\"space-y-3\">\n      {[\n        [\"Customer\", \"Northwind Traders\"],\n        [\"Amount\", \"$12,400.00\"],\n        [\"Issued\", \"Mar 1, 2026\"],\n        [\"Due\", \"Mar 14, 2026\"],\n        [\"Status\", \"Sent\"],\n        [\"Owner\", \"Maya Chen\"],\n      ].map(([k, v]) => (\n        <div key={k} className=\"flex items-baseline justify-between gap-4\">\n          <dt className=\"text-fg-muted\">{k}</dt>\n          <dd className=\"font-medium tabular-nums text-fg\">{v}</dd>\n        </div>\n      ))}\n    </dl>\n    <p className=\"text-xs leading-[1.6] text-fg-muted\">\n      Sent to billing@northwind.com. A reminder goes out on Mar 10 if the invoice is still unpaid.\n    </p>\n  </div>\n)\n\nfunction Sheet({ open, onClose, side = \"right\", title, children, className }: SheetProps) {\n  const controlled = open !== undefined\n  const [internal, setInternal] = React.useState(false)\n  const isOpen = controlled ? open : internal\n  const panelRef = React.useRef<HTMLElement>(null)\n  const returnRef = React.useRef<HTMLElement | null>(null)\n\n  const close = React.useCallback(() => {\n    if (!controlled) setInternal(false)\n    onClose?.()\n  }, [controlled, onClose])\n\n  // Move focus into the panel on open and hand it back on close.\n  React.useEffect(() => {\n    if (!isOpen) return\n    returnRef.current = document.activeElement as HTMLElement | null\n    panelRef.current?.focus()\n    return () => returnRef.current?.focus?.()\n  }, [isOpen])\n\n  React.useEffect(() => {\n    if (!isOpen) return\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key === \"Escape\") close()\n    }\n    document.addEventListener(\"keydown\", onKey)\n    return () => document.removeEventListener(\"keydown\", onKey)\n  }, [isOpen, close])\n\n  const heading = title ?? (children === undefined ? SAMPLE_TITLE : undefined)\n  const trigger = controlled ? null : (\n    <Button variant=\"outline\" onClick={() => setInternal(true)} aria-haspopup=\"dialog\" aria-expanded={isOpen}>\n      Open sheet\n    </Button>\n  )\n  if (!isOpen) return trigger\n  const pos =\n    side === \"right\"\n      ? \"inset-y-0 right-0 w-full max-w-md border-l\"\n      : side === \"left\"\n        ? \"inset-y-0 left-0 w-full max-w-md border-r\"\n        : side === \"top\"\n          ? \"inset-x-0 top-0 max-h-[80vh] border-b\"\n          : \"inset-x-0 bottom-0 max-h-[80vh] border-t\"\n  return (\n    <>\n      {trigger}\n      <div data-slot=\"sheet\" className=\"fixed inset-0 z-50\">\n        <button type=\"button\" aria-label=\"Close\" className=\"absolute inset-0 bg-fg/40\" onClick={close} />\n        <aside\n          ref={panelRef}\n          role=\"dialog\"\n          aria-modal\n          aria-label={heading ?? \"Sheet\"}\n          tabIndex={-1}\n          className={cn(\n            \"absolute flex flex-col border-border bg-surface shadow-lg outline-none\",\n            pos,\n            className\n          )}\n        >\n          <div className=\"flex h-12 items-center justify-between border-b border-border px-4\">\n            <h2 className=\"text-sm font-medium text-fg\">{heading}</h2>\n            <button\n              type=\"button\"\n              aria-label=\"Close sheet\"\n              className=\"rounded-md text-fg-muted outline-none hover:text-fg focus-visible:ring-2 focus-visible:ring-accent\"\n              onClick={close}\n            >\n              <XIcon className=\"size-4\" />\n            </button>\n          </div>\n          <div className=\"min-h-0 flex-1 overflow-auto p-4\">{children ?? SAMPLE_CONTENT}</div>\n        </aside>\n      </div>\n    </>\n  )\n}\nexport { Sheet }\nexport type { SheetProps }\n"
    }
  ]
}
