{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sheet",
  "title": "Sheet",
  "dependencies": [
    "@base-ui/react",
    "@phosphor-icons/react"
  ],
  "registryDependencies": [
    "https://ui.hotfix.jobs/r/recipes.json",
    "https://ui.hotfix.jobs/r/tokens.json",
    "https://ui.hotfix.jobs/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/components/ui/sheet.tsx",
      "content": "\"use client\";\n\nimport { Drawer as DrawerPrimitive } from \"@base-ui/react/drawer\";\nimport { X } from \"@phosphor-icons/react/dist/ssr\";\nimport {\n  Children,\n  cloneElement,\n  createContext,\n  isValidElement,\n  useContext,\n} from \"react\";\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { selectionFocus } from \"@/lib/recipes\";\n\nexport type SheetSide = \"top\" | \"right\" | \"bottom\" | \"left\";\n\nconst SIDE_TO_SWIPE: Record<SheetSide, \"up\" | \"down\" | \"left\" | \"right\"> = {\n  top: \"up\",\n  right: \"right\",\n  bottom: \"down\",\n  left: \"left\",\n};\n\n/* --------------------------------- Root -------------------------------- */\n\nexport interface SheetProps {\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  defaultOpen?: boolean;\n  /** Edge the drawer slides from. Default `right`. */\n  side?: SheetSide;\n  children: React.ReactNode;\n}\n\nexport function Sheet({\n  open,\n  onOpenChange,\n  defaultOpen = false,\n  side = \"right\",\n  children,\n}: SheetProps): React.ReactElement {\n  return (\n    <DrawerPrimitive.Root\n      open={open}\n      onOpenChange={onOpenChange ? (next) => onOpenChange(next) : undefined}\n      defaultOpen={defaultOpen}\n      swipeDirection={SIDE_TO_SWIPE[side]}\n    >\n      <SheetSideContext.Provider value={side}>\n        {children}\n      </SheetSideContext.Provider>\n    </DrawerPrimitive.Root>\n  );\n}\n\nconst SheetSideContext = createContext<SheetSide>(\"right\");\n\n/* ------------------------------- Trigger ------------------------------- */\n\nexport type SheetTriggerProps = React.ComponentProps<\n  typeof DrawerPrimitive.Trigger\n>;\n\nexport function SheetTrigger(props: SheetTriggerProps): React.ReactElement {\n  return <DrawerPrimitive.Trigger data-slot=\"sheet-trigger\" {...props} />;\n}\n\n/* ------------------------------- Content ------------------------------- */\n\nconst SIDE_LAYOUT: Record<SheetSide, string> = {\n  right:\n    \"top-0 bottom-0 right-0 w-[85vw] max-w-md border-l border-hairline rounded-l-[var(--radius-12)]\",\n  left:\n    \"top-0 bottom-0 left-0 w-[85vw] max-w-md border-r border-hairline rounded-r-[var(--radius-12)]\",\n  top: \"top-0 left-0 right-0 border-b border-hairline rounded-b-[var(--radius-12)]\",\n  bottom:\n    \"bottom-0 left-0 right-0 border-t border-hairline rounded-t-[var(--radius-12)]\",\n};\n\nconst SIDE_ENTER: Record<SheetSide, string> = {\n  right:\n    \"data-starting-style:translate-x-full data-ending-style:translate-x-full\",\n  left:\n    \"data-starting-style:-translate-x-full data-ending-style:-translate-x-full\",\n  top: \"data-starting-style:-translate-y-full data-ending-style:-translate-y-full\",\n  bottom:\n    \"data-starting-style:translate-y-full data-ending-style:translate-y-full\",\n};\n\nexport interface SheetContentProps\n  extends Omit<\n    React.ComponentProps<typeof DrawerPrimitive.Popup>,\n    \"children\"\n  > {\n  /** @deprecated Pass `side` on the parent `<Sheet>` instead. */\n  side?: SheetSide;\n  /** Auto-render a close X in the top-right corner. Defaults to true\n   *  when there's no `<SheetHeader>` in the tree (the header would host\n   *  its own X in the trailing slot), false when there is. */\n  showClose?: boolean;\n  children?: React.ReactNode;\n}\n\nfunction hasChildOfType(\n  children: React.ReactNode,\n  Component: React.ComponentType,\n): boolean {\n  // Match on function identity OR displayName. Identity alone breaks\n  // under HMR — Fast Refresh mints a new function reference for the\n  // parent's captured `Component` on hot-reload, so a SheetHeader in\n  // the tree (with a fresh identity) can slip past === and the auto\n  // corner X double-renders. displayName is stable across reloads.\n  const targetName = Component.displayName ?? Component.name;\n  let found = false;\n  Children.forEach(children, (child) => {\n    if (!isValidElement(child)) return;\n    if (child.type === Component) {\n      found = true;\n      return;\n    }\n    if (typeof child.type === \"function\") {\n      const fn = child.type as { displayName?: string; name?: string };\n      const childName = fn.displayName ?? fn.name;\n      if (childName && childName === targetName) found = true;\n    }\n  });\n  return found;\n}\n\nexport function SheetContent({\n  side: sideProp,\n  showClose,\n  className,\n  children,\n  ...props\n}: SheetContentProps): React.ReactElement {\n  const contextSide = useContext(SheetSideContext);\n  const side = sideProp ?? contextSide;\n  const resolvedShowClose =\n    showClose ?? !hasChildOfType(children, SheetHeader);\n\n  return (\n    <DrawerPrimitive.Portal>\n      <DrawerPrimitive.Backdrop\n        data-slot=\"sheet-backdrop\"\n        className={cn(\n          \"fixed inset-0 z-70 bg-white/60 dark:bg-black/60\",\n          \"transition-opacity duration-[var(--duration-overlay)] ease-[var(--ease-standard)]\",\n          \"data-starting-style:opacity-0 data-ending-style:opacity-0\",\n        )}\n      />\n      <DrawerPrimitive.Viewport className=\"fixed inset-0 z-70\">\n        <DrawerPrimitive.Popup\n          data-slot=\"sheet-popup\"\n          data-side={side}\n          className={cn(\n            \"fixed flex flex-col overflow-hidden bg-layer-1 text-ink shadow-modal\",\n            SIDE_LAYOUT[side],\n            \"transition-transform duration-[var(--duration-overlay)] ease-[var(--ease-standard)]\",\n            SIDE_ENTER[side],\n            className,\n          )}\n          {...props}\n        >\n          {resolvedShowClose && (\n            <SheetClose className=\"absolute top-2 end-2 z-10\" />\n          )}\n          {children}\n        </DrawerPrimitive.Popup>\n      </DrawerPrimitive.Viewport>\n    </DrawerPrimitive.Portal>\n  );\n}\n\n/* -------------------------------- Close -------------------------------- */\n\nexport interface SheetCloseProps {\n  render?: React.ReactElement;\n  children?: React.ReactNode;\n}\n\nexport function SheetClose({\n  render,\n  children,\n  className,\n  ...rest\n}: SheetCloseProps &\n  React.ButtonHTMLAttributes<HTMLButtonElement>): React.ReactElement {\n  if (render && isValidElement(render)) {\n    return (\n      <DrawerPrimitive.Close\n        data-slot=\"sheet-close\"\n        render={cloneElement(render, {\n          children:\n            (render.props as { children?: React.ReactNode }).children ??\n            children,\n        } as React.HTMLAttributes<HTMLElement>)}\n        {...rest}\n      />\n    );\n  }\n\n  if (children != null) {\n    return (\n      <DrawerPrimitive.Close\n        data-slot=\"sheet-close\"\n        className={className}\n        {...rest}\n      >\n        {children}\n      </DrawerPrimitive.Close>\n    );\n  }\n\n  return (\n    <DrawerPrimitive.Close\n      aria-label=\"Close\"\n      data-slot=\"sheet-close\"\n      className={cn(\n        \"inline-flex size-8 shrink-0 items-center justify-center rounded-[var(--radius-8)] text-ink-muted hover:bg-layer-hover hover:text-ink active:bg-layer-hover transition-colors duration-[var(--duration-state)] ease-[var(--ease-standard)]\",\n        selectionFocus,\n        className,\n      )}\n      {...rest}\n    >\n      <X aria-hidden className=\"size-4\" />\n    </DrawerPrimitive.Close>\n  );\n}\n\n/* -------------------------- Header / Title / Desc -------------------- */\n\nexport interface SheetHeaderProps extends React.ComponentProps<\"div\"> {\n  /** Content pinned to the leading edge. When set, switches the header\n   *  to a three-slot row layout: leading / children / trailing. */\n  leading?: React.ReactNode;\n  /** Content pinned to the trailing edge. Defaults to `<SheetClose />`\n   *  so a SheetHeader-hosting sheet always has a properly-anchored X.\n   *  Pass `null` to opt out, or override with your own actions. */\n  trailing?: React.ReactNode;\n  /** Suppress the default trailing SheetClose. Same as `trailing={null}`\n   *  but reads clearer at the call site. */\n  hideClose?: boolean;\n}\n\nexport function SheetHeader({\n  className,\n  leading,\n  trailing,\n  hideClose = false,\n  children,\n  ...props\n}: SheetHeaderProps): React.ReactElement {\n  // Row layout is reserved for breadcrumb-y headers where the caller\n  // passes leading content (nav crumb, back button, section label).\n  // Everything else uses the stacked title + description layout, and\n  // the auto SheetClose sits absolute in the top-right so it doesn't\n  // force the header into row mode.\n  const isRow = leading != null || trailing !== undefined;\n  if (isRow) {\n    const resolvedTrailing =\n      trailing !== undefined ? trailing : hideClose ? null : <SheetClose />;\n    return (\n      <div\n        data-slot=\"sheet-header\"\n        className={cn(\n          \"flex h-11 shrink-0 items-center justify-between gap-3 px-4\",\n          className,\n        )}\n        {...props}\n      >\n        <div className=\"flex min-w-0 items-center gap-2 text-mini text-ink-muted\">\n          {leading}\n        </div>\n        <div className=\"flex min-w-0 flex-1 items-center gap-2 text-small font-medium text-ink\">\n          {children}\n        </div>\n        <div className=\"flex min-w-0 items-center justify-end gap-2\">\n          {resolvedTrailing}\n        </div>\n      </div>\n    );\n  }\n  return (\n    <div\n      data-slot=\"sheet-header\"\n      className={cn(\n        \"relative flex flex-col gap-1 px-5 pt-5 pb-2\",\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      {!hideClose && <SheetClose className=\"absolute top-2 end-2\" />}\n    </div>\n  );\n}\n\nexport function SheetTitle({\n  className,\n  ...props\n}: React.ComponentProps<\"h2\">): React.ReactElement {\n  return (\n    <DrawerPrimitive.Title\n      data-slot=\"sheet-title\"\n      className={cn(\"min-w-0 text-regular font-medium text-ink\", className)}\n      {...props}\n    />\n  );\n}\n\nexport function SheetDescription({\n  className,\n  ...props\n}: React.ComponentProps<\"p\">): React.ReactElement {\n  return (\n    <DrawerPrimitive.Description\n      data-slot=\"sheet-description\"\n      className={cn(\"text-small text-ink-muted\", className)}\n      {...props}\n    />\n  );\n}\n\n/* --------------------------------- Body -------------------------------- */\n\nexport function SheetBody({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">): React.ReactElement {\n  return (\n    <div\n      data-slot=\"sheet-body\"\n      // `data-base-ui-swipe-ignore` tells base-ui's drawer to skip\n      // pointer drags starting inside this region, so mouse-drag text\n      // selection doesn't kick off the sheet's swipe-to-dismiss.\n      // Header / footer / edges remain swipeable on touch.\n      data-base-ui-swipe-ignore=\"\"\n      className={cn(\n        \"flex flex-col gap-4 p-5 flex-1 min-h-0 overflow-y-auto\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/* -------------------------------- Footer ------------------------------ */\n\nexport interface SheetFooterProps extends React.ComponentProps<\"div\"> {\n  /** Stack actions vertically full-width. */\n  stacked?: boolean;\n}\n\nexport function SheetFooter({\n  className,\n  stacked = false,\n  ...props\n}: SheetFooterProps): React.ReactElement {\n  return (\n    <div\n      data-slot=\"sheet-footer\"\n      className={cn(\n        \"flex gap-2 px-5 pt-2 pb-5\",\n        stacked ? \"flex-col\" : \"flex-row justify-between\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n// Explicit displayName so `hasChildOfType` can fall back on it when\n// Fast Refresh has torn the function identities apart.\nSheetHeader.displayName = \"SheetHeader\";\nSheetFooter.displayName = \"SheetFooter\";\n\nexport { DrawerPrimitive as SheetPrimitive };\n",
      "type": "registry:ui",
      "target": "components/ui/sheet.tsx"
    }
  ],
  "type": "registry:ui"
}