{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "drawer",
  "title": "Drawer",
  "description": "A swipeable panel that slides in from an edge of the screen, built on Base UI.",
  "dependencies": [
    "@base-ui/react",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.persian-labs.ir/r/scroll-area.json"
  ],
  "files": [
    {
      "path": "registry/base/ui/drawer.tsx",
      "content": "\"use client\"\n\nimport { Checkbox as CheckboxPrimitive } from \"@base-ui/react/checkbox\"\nimport { Drawer as DrawerPrimitive } from \"@base-ui/react/drawer\"\nimport { mergeProps } from \"@base-ui/react/merge-props\"\nimport { Radio as RadioPrimitive } from \"@base-ui/react/radio\"\nimport { RadioGroup as RadioGroupPrimitive } from \"@base-ui/react/radio-group\"\nimport { useRender } from \"@base-ui/react/use-render\"\nimport { CheckIcon, ChevronRightIcon, XIcon } from \"lucide-react\"\nimport * as React from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { ScrollArea } from \"@/components/ui/scroll-area\"\nimport { cn } from \"@/lib/utils\"\n\nconst DrawerCreateHandle: typeof DrawerPrimitive.createHandle =\n  DrawerPrimitive.createHandle\n\ntype DrawerPosition = \"right\" | \"left\" | \"top\" | \"bottom\" | \"start\" | \"end\"\ntype PhysicalDrawerPosition = \"right\" | \"left\" | \"top\" | \"bottom\"\n\n/**\n * DrawerPopup renders through a Portal, so it doesn't inherit `dir` from a\n * nearby wrapper (only from document.documentElement). DrawerTrigger\n * measures the ambient direction where it's actually rendered and pushes it\n * here so a logical position=\"start\"/\"end\" resolves to the correct physical\n * edge, and so the portaled content picks up the right text direction.\n */\nconst DrawerDirContext = React.createContext<{\n  dir: \"ltr\" | \"rtl\"\n  setDir: (dir: \"ltr\" | \"rtl\") => void\n}>({ dir: \"ltr\", setDir: () => {} })\n\nconst DrawerPositionContext = React.createContext<{ position: DrawerPosition }>(\n  { position: \"bottom\" }\n)\nconst DrawerPopupVariantContext = React.createContext<\n  \"default\" | \"straight\" | \"inset\"\n>(\"default\")\n\nfunction resolvePosition(\n  position: DrawerPosition,\n  dir: \"ltr\" | \"rtl\"\n): PhysicalDrawerPosition {\n  if (position === \"start\") return dir === \"rtl\" ? \"right\" : \"left\"\n  if (position === \"end\") return dir === \"rtl\" ? \"left\" : \"right\"\n  return position\n}\n\nfunction useResolvedDrawerPosition(\n  positionProp?: DrawerPosition\n): PhysicalDrawerPosition {\n  const { position: contextPosition } = React.useContext(DrawerPositionContext)\n  const { dir } = React.useContext(DrawerDirContext)\n  return resolvePosition(positionProp ?? contextPosition, dir)\n}\n\nconst directionMap: Record<\n  PhysicalDrawerPosition,\n  DrawerPrimitive.Root.Props[\"swipeDirection\"]\n> = {\n  bottom: \"down\",\n  left: \"left\",\n  right: \"right\",\n  top: \"up\",\n}\n\nfunction now(): number {\n  return typeof performance !== \"undefined\" ? performance.now() : Date.now()\n}\n\nfunction Drawer({\n  swipeDirection,\n  position = \"bottom\",\n  onOpenChange,\n  open,\n  ...props\n}: DrawerPrimitive.Root.Props & {\n  position?: DrawerPosition\n}) {\n  const [dir, setDir] = React.useState<\"ltr\" | \"rtl\">(\"ltr\")\n  const dirContextValue = React.useMemo(() => ({ dir, setDir }), [dir])\n  const positionContextValue = React.useMemo(() => ({ position }), [position])\n  const resolvedPosition = resolvePosition(position, dir)\n\n  // Guard against a spurious \"open then instantly dismiss\" that appears as the\n  // drawer flashing with no animation. In re-render-heavy apps, the\n  // interaction that opens the drawer can produce a follow-up click that Base\n  // UI catches as an outside-press and closes it in the same tick. Detected\n  // by timing: a real outside-press happens well after opening, so any\n  // outside-press within a short window of opening is treated as the bogus\n  // self-close and cancelled. Later outside-presses still dismiss normally.\n  const openedAtRef = React.useRef(0)\n  React.useEffect(() => {\n    if (open) openedAtRef.current = now()\n  }, [open])\n\n  return (\n    <DrawerDirContext.Provider value={dirContextValue}>\n      <DrawerPositionContext.Provider value={positionContextValue}>\n        <DrawerPrimitive.Root\n          open={open}\n          swipeDirection={swipeDirection ?? directionMap[resolvedPosition]}\n          onOpenChange={(open, details) => {\n            if (open) {\n              openedAtRef.current = now()\n            } else if (details?.reason === \"outside-press\") {\n              if (now() - openedAtRef.current < 200) {\n                details.cancel?.()\n                return\n              }\n            }\n            onOpenChange?.(open, details)\n          }}\n          {...props}\n        />\n      </DrawerPositionContext.Provider>\n    </DrawerDirContext.Provider>\n  )\n}\n\nconst DrawerPortal = DrawerPrimitive.Portal\n\nfunction DrawerTrigger({ className, ...props }: DrawerPrimitive.Trigger.Props) {\n  const ref = React.useRef<HTMLButtonElement>(null)\n  const { setDir } = React.useContext(DrawerDirContext)\n\n  React.useEffect(() => {\n    function update() {\n      if (!ref.current) return\n      setDir(getComputedStyle(ref.current).direction === \"rtl\" ? \"rtl\" : \"ltr\")\n    }\n\n    update()\n\n    const observer = new MutationObserver(update)\n    observer.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: [\"dir\"],\n      subtree: true,\n    })\n\n    return () => observer.disconnect()\n  }, [setDir])\n\n  return (\n    <DrawerPrimitive.Trigger\n      ref={ref}\n      data-slot=\"drawer-trigger\"\n      className={className}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerClose(props: DrawerPrimitive.Close.Props) {\n  return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />\n}\n\nfunction DrawerSwipeArea({\n  className,\n  position: positionProp,\n  ...props\n}: DrawerPrimitive.SwipeArea.Props & {\n  position?: DrawerPosition\n}) {\n  const position = useResolvedDrawerPosition(positionProp)\n\n  return (\n    <DrawerPrimitive.SwipeArea\n      data-slot=\"drawer-swipe-area\"\n      className={cn(\n        \"fixed z-50 touch-none\",\n        position === \"bottom\" && \"inset-x-0 bottom-0 h-8\",\n        position === \"top\" && \"inset-x-0 top-0 h-8\",\n        position === \"left\" && \"inset-y-0 left-0 w-8\",\n        position === \"right\" && \"inset-y-0 right-0 w-8\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerBackdrop({\n  className,\n  ...props\n}: DrawerPrimitive.Backdrop.Props) {\n  return (\n    <DrawerPrimitive.Backdrop\n      data-slot=\"drawer-backdrop\"\n      className={cn(\n        \"fixed inset-0 z-50 bg-black/45 opacity-[calc(1-var(--drawer-swipe-progress))] backdrop-blur-md transition-opacity duration-450 ease-[cubic-bezier(0.32,0.72,0,1)] data-swiping:duration-0 data-[ending-style]:opacity-0 data-[ending-style]:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-[starting-style]:opacity-0 supports-[-webkit-touch-callout:none]:absolute\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerViewport({\n  className,\n  position: positionProp,\n  variant = \"default\",\n  ...props\n}: DrawerPrimitive.Viewport.Props & {\n  position?: DrawerPosition\n  variant?: \"default\" | \"straight\" | \"inset\"\n}) {\n  const { dir } = React.useContext(DrawerDirContext)\n  const position = useResolvedDrawerPosition(positionProp)\n\n  return (\n    <DrawerPrimitive.Viewport\n      dir={dir}\n      data-slot=\"drawer-viewport\"\n      className={cn(\n        // `--inset` feeds the popup's enter/exit transform\n        // `translateY(calc(100% + env(...) + var(--inset)))`. It MUST carry a\n        // length unit: Tailwind ≥4.3.1 compiles `--spacing(0)` to a unitless\n        // `0`, which makes that calc invalid (`length + 0`) so the transform\n        // resolves to `none` and the drawer stops animating. Use `0px`.\n        \"fixed inset-0 z-50 [--bleed:--spacing(12)] [--inset:0px]\",\n        \"touch-none\",\n        // flex + align to an edge, not CSS grid: with snapPoints, the popup's\n        // own max-height (see DrawerPopup) clamps its border-box, and\n        // padding-bottom eats into that clamped box to leave only the\n        // snapped-to amount of content visible before translateY pushes the\n        // rest off-screen. That box-model interaction needs the popup to\n        // just be a normal flex item pinned to an edge — a grid \"auto\" row\n        // sizes itself from the popup's un-padded content instead, which\n        // left snap points with nothing to shrink against.\n        position === \"bottom\" && \"flex items-end justify-center pt-12\",\n        position === \"top\" && \"flex items-start justify-center pb-12\",\n        // justify-start/justify-end (flex-start/flex-end) are direction-\n        // sensitive and reverse once `dir=\"rtl\"` is set below, undoing the\n        // physical position this branch already resolved to. The CSS\n        // `justify-content: left`/`right` keywords stay physical regardless\n        // of writing direction — but they aren't part of Tailwind's\n        // justify-content value scale, so the value-only `justify-[right]`\n        // shorthand silently fails to generate a rule. The full arbitrary\n        // property syntax below works.\n        position === \"left\" && \"flex [justify-content:left]\",\n        position === \"right\" && \"flex [justify-content:right]\",\n        variant === \"inset\" && \"px-(--inset) sm:[--inset:--spacing(4)]\",\n        variant === \"inset\" && position !== \"bottom\" && \"pt-(--inset)\",\n        variant === \"inset\" && position !== \"top\" && \"pb-(--inset)\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerPopup({\n  className,\n  children,\n  position: positionProp,\n  variant = \"default\",\n  showCloseButton = false,\n  showBar = false,\n  portalProps,\n  ...props\n}: DrawerPrimitive.Popup.Props & {\n  position?: DrawerPosition\n  variant?: \"default\" | \"straight\" | \"inset\"\n  showCloseButton?: boolean\n  showBar?: boolean\n  portalProps?: DrawerPrimitive.Portal.Props\n}) {\n  const position = useResolvedDrawerPosition(positionProp)\n\n  return (\n    <DrawerPortal {...portalProps}>\n      <DrawerBackdrop />\n      <DrawerViewport position={position} variant={variant}>\n        <DrawerPopupVariantContext.Provider value={variant}>\n          <DrawerPrimitive.Popup\n            data-slot=\"drawer-popup\"\n            className={cn(\n              \"relative flex max-h-full min-h-0 w-full min-w-0 flex-col bg-popover text-popover-foreground shadow-lg/5 transition-[transform,box-shadow,height,background-color] duration-450 ease-[cubic-bezier(0.32,0.72,0,1)] will-change-transform outline-none [--peek:calc(--spacing(6)-1px)] [--scale-base:calc(max(0,1-(var(--nested-drawers)*var(--stack-step))))] [--scale:clamp(0,calc(var(--scale-base)+(var(--stack-step)*var(--stack-progress))),1)] [--shrink:calc(1-var(--scale))] [--stack-peek-offset:max(0px,calc((var(--nested-drawers)-var(--stack-progress))*var(--peek)))] [--stack-progress:clamp(0,var(--drawer-swipe-progress),1)] [--stack-step:0.05] not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:shadow-[0_1px_--theme(--color-black/4%)] after:pointer-events-none after:absolute after:bg-popover data-nested-drawer-open:overflow-hidden data-nested-drawer-open:bg-[color-mix(in_srgb,var(--popover),var(--color-black)_calc(2%*(var(--nested-drawers)-var(--stack-progress))))] data-swiping:select-none data-[ending-style]:shadow-transparent data-[ending-style]:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-[starting-style]:shadow-transparent dark:before:shadow-[0_-1px_--theme(--color-white/6%)] dark:data-nested-drawer-open:bg-[color-mix(in_srgb,var(--popover),var(--color-black)_calc(6%*(var(--nested-drawers)-var(--stack-progress))))]\",\n              \"touch-none\",\n              position === \"bottom\" &&\n                \"max-w-[400px] transform-[translateY(calc(var(--drawer-snap-point-offset)+var(--drawer-swipe-movement-y)))] border-t border-border pb-[max(0px,calc(env(safe-area-inset-bottom,0px)+var(--drawer-snap-point-offset,0px)+clamp(0,1,var(--drawer-snap-point-offset,0px)/1px)*var(--drawer-swipe-movement-y,0px)))] not-data-[starting-style]:not-data-[ending-style]:transition-[transform,box-shadow,height,background-color,padding] after:inset-x-0 after:top-full after:h-(--bleed) has-data-[slot=drawer-bar]:pt-2 data-[ending-style]:transform-[translateY(calc(100%+env(safe-area-inset-bottom,0px)+var(--inset)))] data-[ending-style]:pb-0 data-[starting-style]:transform-[translateY(calc(100%+env(safe-area-inset-bottom,0px)+var(--inset)))] data-[starting-style]:pb-0\",\n              position === \"top\" &&\n                \"transform-[translateY(var(--drawer-swipe-movement-y))] border-b border-border after:inset-x-0 after:bottom-full after:h-(--bleed) has-data-[slot=drawer-bar]:pb-2 data-[ending-style]:transform-[translateY(calc(-100%-var(--inset)))] data-[starting-style]:transform-[translateY(calc(-100%-var(--inset)))]\",\n              position === \"left\" &&\n                \"w-[calc(100%-(--spacing(12)))] max-w-md transform-[translateX(var(--drawer-swipe-movement-x))] border-r border-border after:inset-y-0 after:right-full after:w-(--bleed) has-data-[slot=drawer-bar]:pr-2 data-[ending-style]:transform-[translateX(calc(-100%-var(--inset)))] data-[starting-style]:transform-[translateX(calc(-100%-var(--inset)))]\",\n              position === \"right\" &&\n                \"w-[calc(100%-(--spacing(12)))] max-w-md transform-[translateX(var(--drawer-swipe-movement-x))] border-l border-border after:inset-y-0 after:left-full after:w-(--bleed) has-data-[slot=drawer-bar]:pl-2 data-[ending-style]:transform-[translateX(calc(100%+var(--inset)))] data-[starting-style]:transform-[translateX(calc(100%+var(--inset)))]\",\n              variant !== \"straight\" &&\n                cn(\n                  position === \"bottom\" && \"rounded-t-2xl\",\n                  position === \"top\" &&\n                    \"rounded-b-2xl **:data-[slot=drawer-footer]:rounded-b-[calc(var(--radius-2xl)-1px)]\",\n                  position === \"left\" &&\n                    \"rounded-r-2xl **:data-[slot=drawer-footer]:rounded-br-[calc(var(--radius-2xl)-1px)]\",\n                  position === \"right\" &&\n                    \"rounded-l-2xl **:data-[slot=drawer-footer]:rounded-bl-[calc(var(--radius-2xl)-1px)]\"\n                ),\n              variant === \"default\" &&\n                cn(\n                  position === \"bottom\" &&\n                    \"before:rounded-t-[calc(var(--radius-2xl)-1px)]\",\n                  position === \"top\" &&\n                    \"before:rounded-b-[calc(var(--radius-2xl)-1px)]\",\n                  position === \"left\" &&\n                    \"before:rounded-r-[calc(var(--radius-2xl)-1px)]\",\n                  position === \"right\" &&\n                    \"before:rounded-l-[calc(var(--radius-2xl)-1px)]\"\n                ),\n              variant === \"inset\" &&\n                // The footer paints its own bleed rectangle below itself (see\n                // DrawerFooter) to cover DrawerPopup's own bleed color during\n                // drag/overscroll. The inset variant floats as a fully\n                // rounded, bordered card instead — nothing sits behind it to\n                // cover, so that rectangle just pokes out past the rounded\n                // bottom corner as a stray patch of color. Kill it here the\n                // same way the popup's own bleed is killed below.\n                \"before:hidden sm:rounded-2xl sm:border sm:border-border sm:before:rounded-[calc(var(--radius-2xl)-1px)] sm:after:bg-transparent sm:**:data-[slot=drawer-footer]:rounded-b-[calc(var(--radius-2xl)-1px)] sm:**:data-[slot=drawer-footer]:after:border-transparent sm:**:data-[slot=drawer-footer]:after:bg-transparent\",\n              variant === \"straight\" && \"[--stack-step:0]\",\n              (position === \"bottom\" || position === \"top\") &&\n                // max-h-full is a no-op here: the viewport lays these two\n                // positions out with CSS grid (row 2 is auto-sized to fit the\n                // popup itself), so percentage heights have nothing definite\n                // to resolve against and the popup can grow past the viewport\n                // with tall content. Cap it against the viewport directly —\n                // this is also what lets snapPoints do anything: Base UI\n                // derives --drawer-snap-point-offset from how much taller the\n                // popup's *actual* rendered height is than each snap target,\n                // so a popup that's never allowed to grow past its content\n                // always measures the same height as every snap point and the\n                // offset comes out to 0.\n                \"h-(--drawer-height,auto) max-h-[calc(100dvh-(--spacing(12)))] [--height:max(0px,calc(var(--drawer-frontmost-height,var(--drawer-height))))] data-nested-drawer-open:h-(--height)\",\n              position === \"bottom\" &&\n                \"origin-[50%_calc(100%-var(--inset))] data-nested-drawer-open:transform-[translateY(calc(var(--drawer-swipe-movement-y)-var(--stack-peek-offset)-(var(--shrink)*var(--height))))_scale(var(--scale))]\",\n              position === \"top\" &&\n                \"origin-[50%_var(--inset)] data-nested-drawer-open:transform-[translateY(calc(var(--drawer-swipe-movement-y)+var(--stack-peek-offset)+(var(--shrink)*var(--height))))_scale(var(--scale))]\",\n              position === \"left\" &&\n                \"origin-right data-nested-drawer-open:transform-[translateX(calc(var(--drawer-swipe-movement-x)+var(--stack-peek-offset)))_scale(var(--scale))]\",\n              position === \"right\" &&\n                \"origin-left data-nested-drawer-open:transform-[translateX(calc(var(--drawer-swipe-movement-x)-var(--stack-peek-offset)))_scale(var(--scale))]\",\n              className\n            )}\n            {...props}\n          >\n            {children}\n            {showCloseButton && (\n              <DrawerPrimitive.Close\n                aria-label=\"Close\"\n                className=\"absolute end-2 top-2\"\n                render={<Button size=\"icon-sm\" variant=\"ghost\" />}\n              >\n                <XIcon />\n              </DrawerPrimitive.Close>\n            )}\n            {showBar && <DrawerBar position={position} />}\n          </DrawerPrimitive.Popup>\n        </DrawerPopupVariantContext.Provider>\n      </DrawerViewport>\n    </DrawerPortal>\n  )\n}\n\nfunction DrawerHeader({\n  className,\n  allowSelection = false,\n  render,\n  ...props\n}: useRender.ComponentProps<\"div\"> & {\n  allowSelection?: boolean\n}) {\n  const defaultProps = {\n    className: cn(\n      \"flex flex-col gap-2 p-6 in-[[data-slot=drawer-popup]:has([data-slot=drawer-panel])]:pb-3 max-sm:pb-4\",\n      !allowSelection && \"cursor-default\",\n      className\n    ),\n    \"data-slot\": \"drawer-header\",\n  }\n\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(defaultProps, props),\n    render: allowSelection ? <DrawerContent render={render} /> : render,\n  })\n}\n\nfunction DrawerFooter({\n  className,\n  variant = \"default\",\n  allowSelection = true,\n  position: positionProp,\n  render,\n  ...props\n}: useRender.ComponentProps<\"div\"> & {\n  variant?: \"default\" | \"bare\"\n  allowSelection?: boolean\n  position?: DrawerPosition\n}) {\n  const position = useResolvedDrawerPosition(positionProp)\n  const popupVariant = React.useContext(DrawerPopupVariantContext)\n\n  const defaultProps = {\n    className: cn(\n      \"flex flex-col-reverse gap-2 px-6 pb-(--safe-area-inset-bottom,0px) sm:flex-row sm:justify-end\",\n      !allowSelection && \"cursor-default\",\n      variant === \"default\" &&\n        // The footer is always the last item in the popup's column, so\n        // dragging/overscrolling exposes the area past its bottom edge.\n        // That area is otherwise filled by DrawerPopup's own bleed\n        // (bg-popover), which doesn't match the footer's muted background —\n        // paint a matching bleed here so the footer's color and border\n        // appear to continue instead of cutting to a different shade.\n        //\n        // after:z-10 is load-bearing: DrawerPopup has its own after: bleed\n        // (bg-popover) covering the same region, and since that bleed is\n        // *generated content of the popup itself* — which has a transform\n        // and so establishes its own stacking context — it paints as if it\n        // were the popup's last child, i.e. after and on top of this\n        // footer (an earlier child) and this pseudo-element, even though\n        // this is declared later in the DOM. Without an explicit z-index\n        // this fix is invisible: the popup's own solid bleed just paints\n        // over it.\n        \"relative border-t border-border bg-muted/72 pt-4 pb-[calc(env(safe-area-inset-bottom,0px)+--spacing(4))] after:pointer-events-none after:absolute after:z-10 after:bg-muted/72\",\n      // Bottom and top drawers leave space below the footer, so their bleed\n      // continues downward.\n      variant === \"default\" &&\n        position !== \"right\" &&\n        position !== \"left\" &&\n        \"after:inset-x-0 after:top-full after:h-[200px]\",\n      // Side drawers stretch sideways. Continue the footer into the gap on\n      // the outer edge, including its top border, so the muted footer color\n      // does not cut back to the popup color while dragging. Offset by the\n      // footer border so the bleed's border-box matches its rendered height\n      // as the footer content changes.\n      variant === \"default\" &&\n        position === \"right\" &&\n        popupVariant === \"default\" &&\n        \"after:-top-px after:bottom-0 after:left-full after:w-[200px] after:border-t after:border-border\",\n      variant === \"default\" &&\n        position === \"left\" &&\n        popupVariant === \"default\" &&\n        \"after:-top-px after:right-full after:bottom-0 after:w-[200px] after:border-t after:border-border\",\n      variant === \"bare\" &&\n        \"pt-4 pb-[calc(env(safe-area-inset-bottom,0px)+--spacing(6))] in-[[data-slot=drawer-popup]:has([data-slot=drawer-panel])]:pt-3\",\n      // DrawerPopup reserves space for the drag bar via padding on itself\n      // (e.g. pr-2 for a left drawer), which insets every child equally —\n      // including the footer's own background. Cancel that inset here with\n      // a matching negative margin, only when a bar sibling is actually\n      // present, so the footer's background still reaches the handle edge.\n      position === \"left\" && \"has-[~[data-slot=drawer-bar]]:-mr-2\",\n      position === \"right\" && \"has-[~[data-slot=drawer-bar]]:-ml-2\",\n      position === \"top\" && \"has-[~[data-slot=drawer-bar]]:-mb-2\",\n      className\n    ),\n    \"data-slot\": \"drawer-footer\",\n  }\n\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(defaultProps, props),\n    render: allowSelection ? <DrawerContent render={render} /> : render,\n  })\n}\n\nfunction DrawerTitle({ className, ...props }: DrawerPrimitive.Title.Props) {\n  return (\n    <DrawerPrimitive.Title\n      data-slot=\"drawer-title\"\n      className={cn(\n        \"font-heading text-xl leading-none font-semibold\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerDescription({\n  className,\n  ...props\n}: DrawerPrimitive.Description.Props) {\n  return (\n    <DrawerPrimitive.Description\n      data-slot=\"drawer-description\"\n      className={cn(\"text-sm text-muted-foreground\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerPanel({\n  className,\n  scrollable = true,\n  allowSelection = true,\n  render,\n  ...props\n}: useRender.ComponentProps<\"div\"> & {\n  scrollable?: boolean\n  allowSelection?: boolean\n}) {\n  const defaultProps = {\n    className: cn(\n      \"p-6 in-[[data-slot=drawer-popup]:has([data-slot=drawer-footer]:not(.border-t))]:pb-1 in-[[data-slot=drawer-popup]:has([data-slot=drawer-header])]:pt-1\",\n      !allowSelection && \"cursor-default\",\n      className\n    ),\n    \"data-slot\": \"drawer-panel\",\n  }\n\n  const content = useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(defaultProps, props),\n    render: allowSelection ? <DrawerContent render={render} /> : render,\n  })\n\n  if (scrollable) {\n    // ScrollArea's own root has no intrinsic height — as a flex item inside\n    // DrawerPopup's column it needs min-h-0 too, not just flex-1, or it\n    // refuses to shrink below its content's natural height (the default\n    // `min-height: auto` on flex items) and the popup grows to fit\n    // everything instead of scrolling internally.\n    return (\n      <ScrollArea className=\"min-h-0 flex-1 touch-auto\">{content}</ScrollArea>\n    )\n  }\n\n  return content\n}\n\nfunction DrawerBar({\n  className,\n  position: positionProp,\n  render,\n  ...props\n}: useRender.ComponentProps<\"div\"> & {\n  position?: DrawerPosition\n}) {\n  const position = useResolvedDrawerPosition(positionProp)\n  const horizontal = position === \"left\" || position === \"right\"\n  const defaultProps = {\n    \"aria-hidden\": true as const,\n    className: cn(\n      \"absolute flex touch-none items-center justify-center p-3 before:rounded-full before:bg-input\",\n      horizontal\n        ? \"inset-y-0 before:h-12 before:w-1\"\n        : \"inset-x-0 before:h-1 before:w-12\",\n      position === \"top\" && \"bottom-0\",\n      position === \"bottom\" && \"top-0\",\n      position === \"left\" && \"right-0\",\n      position === \"right\" && \"left-0\",\n      className\n    ),\n    \"data-slot\": \"drawer-bar\",\n  }\n\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(defaultProps, props),\n    render,\n  })\n}\n\nconst DrawerContent = DrawerPrimitive.Content\n\nfunction DrawerMenu({\n  className,\n  render,\n  ...props\n}: useRender.ComponentProps<\"nav\">) {\n  const defaultProps = {\n    className: cn(\"-m-2 flex flex-col\", className),\n    \"data-slot\": \"drawer-menu\",\n  }\n\n  return useRender({\n    defaultTagName: \"nav\",\n    props: mergeProps<\"nav\">(defaultProps, props),\n    render,\n  })\n}\n\nfunction DrawerMenuItem({\n  className,\n  variant = \"default\",\n  render,\n  disabled,\n  ...props\n}: useRender.ComponentProps<\"button\"> & {\n  variant?: \"default\" | \"destructive\"\n}) {\n  const defaultProps = {\n    className: cn(\n      \"flex min-h-9 w-full cursor-default items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none select-none hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[variant=destructive]:text-destructive sm:min-h-8 sm:text-sm [&>svg]:pointer-events-none [&>svg]:-mx-0.5 [&>svg]:shrink-0 [&>svg:not([class*='size-'])]:size-4.5 sm:[&>svg:not([class*='size-'])]:size-4\",\n      className\n    ),\n    \"data-slot\": \"drawer-menu-item\",\n    \"data-variant\": variant,\n    disabled,\n    type: \"button\" as const,\n  }\n\n  return useRender({\n    defaultTagName: \"button\",\n    props: mergeProps<\"button\">(defaultProps, props),\n    render,\n  })\n}\n\nfunction DrawerMenuSeparator({\n  className,\n  render,\n  ...props\n}: useRender.ComponentProps<\"div\">) {\n  const defaultProps = {\n    className: cn(\"mx-2 my-1 h-px bg-border\", className),\n    \"data-slot\": \"drawer-menu-separator\",\n  }\n\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(defaultProps, props),\n    render,\n  })\n}\n\nfunction DrawerMenuGroup({\n  className,\n  render,\n  ...props\n}: useRender.ComponentProps<\"div\">) {\n  const defaultProps = {\n    className: cn(\"flex flex-col\", className),\n    \"data-slot\": \"drawer-menu-group\",\n  }\n\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(defaultProps, props),\n    render,\n  })\n}\n\nfunction DrawerMenuGroupLabel({\n  className,\n  render,\n  ...props\n}: useRender.ComponentProps<\"div\">) {\n  const defaultProps = {\n    className: cn(\n      \"px-2 py-1.5 text-xs font-medium text-muted-foreground\",\n      className\n    ),\n    \"data-slot\": \"drawer-menu-group-label\",\n  }\n\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(defaultProps, props),\n    render,\n  })\n}\n\nfunction DrawerMenuTrigger({\n  className,\n  children,\n  ...props\n}: DrawerPrimitive.Trigger.Props) {\n  return (\n    <DrawerTrigger\n      data-slot=\"drawer-menu-trigger\"\n      className={cn(\n        \"flex min-h-9 w-full cursor-default items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none select-none hover:bg-accent hover:text-accent-foreground sm:min-h-8 sm:text-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n      <ChevronRightIcon className=\"ms-auto opacity-80 rtl:-scale-x-100\" />\n    </DrawerTrigger>\n  )\n}\n\nfunction DrawerMenuCheckboxItem({\n  className,\n  children,\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  variant = \"default\",\n  disabled,\n  render,\n  ...props\n}: CheckboxPrimitive.Root.Props & {\n  variant?: \"default\" | \"switch\"\n  render?: React.ReactElement\n}) {\n  return (\n    <CheckboxPrimitive.Root\n      checked={checked}\n      className={cn(\n        \"group grid min-h-9 w-full cursor-default items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none select-none hover:bg-accent hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 sm:min-h-8 sm:text-sm [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4\",\n        variant === \"switch\"\n          ? \"grid-cols-[1fr_auto] gap-4 pe-1.5\"\n          : \"grid-cols-[1rem_1fr] pe-4\",\n        className\n      )}\n      data-slot=\"drawer-menu-checkbox-item\"\n      defaultChecked={defaultChecked}\n      disabled={disabled}\n      onCheckedChange={onCheckedChange}\n      render={render}\n      {...props}\n    >\n      {variant === \"switch\" ? (\n        <>\n          <span className=\"col-start-1\">{children}</span>\n          <CheckboxPrimitive.Indicator\n            className=\"col-start-2 inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 items-center rounded-full p-px transition-[background-color,box-shadow] duration-200 outline-none [--thumb-size:--spacing(4)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-[checked]:bg-primary data-[disabled]:opacity-50 data-[unchecked]:bg-input sm:[--thumb-size:--spacing(3)]\"\n            keepMounted\n          >\n            <span className=\"pointer-events-none block aspect-square h-full origin-left rounded-(--thumb-size) bg-background shadow-sm transition-transform group-data-[checked]:translate-x-[calc(var(--thumb-size)-4px)] rtl:group-data-[checked]:-translate-x-[calc(var(--thumb-size)-4px)]\" />\n          </CheckboxPrimitive.Indicator>\n        </>\n      ) : (\n        <>\n          <CheckboxPrimitive.Indicator className=\"col-start-1\">\n            <CheckIcon />\n          </CheckboxPrimitive.Indicator>\n          <span className=\"col-start-2\">{children}</span>\n        </>\n      )}\n    </CheckboxPrimitive.Root>\n  )\n}\n\nfunction DrawerMenuRadioGroup({\n  className,\n  ...props\n}: RadioGroupPrimitive.Props) {\n  return (\n    <RadioGroupPrimitive\n      data-slot=\"drawer-menu-radio-group\"\n      className={cn(\"flex flex-col\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction DrawerMenuRadioItem({\n  className,\n  children,\n  value,\n  disabled,\n  render,\n  ...props\n}: RadioPrimitive.Root.Props & {\n  value: string\n  render?: React.ReactElement\n}) {\n  return (\n    <RadioPrimitive.Root\n      data-slot=\"drawer-menu-radio-item\"\n      className={cn(\n        \"grid min-h-9 w-full cursor-default items-center gap-2 rounded-sm px-2 py-1 text-base text-foreground outline-none select-none hover:bg-accent hover:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 sm:min-h-8 sm:text-sm [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4\",\n        \"grid-cols-[1rem_1fr] items-center pe-4\",\n        className\n      )}\n      disabled={disabled}\n      render={render}\n      value={value}\n      {...props}\n    >\n      <RadioPrimitive.Indicator className=\"col-start-1\">\n        <CheckIcon />\n      </RadioPrimitive.Indicator>\n      <span className=\"col-start-2\">{children}</span>\n    </RadioPrimitive.Root>\n  )\n}\n\nexport {\n  Drawer,\n  DrawerBackdrop,\n  DrawerBar,\n  DrawerClose,\n  DrawerContent,\n  DrawerCreateHandle,\n  DrawerDescription,\n  DrawerFooter,\n  DrawerHeader,\n  DrawerMenu,\n  DrawerMenuCheckboxItem,\n  DrawerMenuGroup,\n  DrawerMenuGroupLabel,\n  DrawerMenuItem,\n  DrawerMenuRadioGroup,\n  DrawerMenuRadioItem,\n  DrawerMenuSeparator,\n  DrawerMenuTrigger,\n  DrawerPanel,\n  DrawerPopup,\n  DrawerPortal,\n  DrawerSwipeArea,\n  DrawerTitle,\n  DrawerTrigger,\n  DrawerViewport,\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}