{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "dependencies": [
    "react-aria-components",
    "lucide-react",
    "motion"
  ],
  "files": [
    {
      "content": "export * from \"./drawer\";\nexport * as Drawer from \"./namespace\";\nexport * from \"./styles\";\n",
      "path": "src/registry/ui/drawer/index.ts",
      "type": "registry:ui"
    },
    {
      "content": "\"use client\";\n\nimport type { VariantProps } from \"cva\";\nimport {\n  AnimatePresence,\n  motion,\n  type PanInfo,\n  type Point,\n  type TargetAndTransition,\n} from \"motion/react\";\nimport { use } from \"react\";\nimport {\n  Button,\n  composeRenderProps,\n  Dialog,\n  DialogTrigger,\n  Heading,\n  Modal,\n  ModalOverlay,\n  OverlayTriggerStateContext,\n  Text,\n} from \"react-aria-components\";\n\nimport { DrawerStyles } from \"./styles\";\n\nconst MotionModal = motion.create(Modal);\nconst MotionModalOverlay = motion.create(ModalOverlay);\n\nconst TRANSITIONS = {\n  DURATION: 0.5,\n  EASE: [0.32, 0.72, 0, 1],\n};\nconst VELOCITY_THRESHOLD = 0.4;\nconst CLOSE_THRESHOLD = 0.25;\n\ntype DrawerSide = \"top\" | \"right\" | \"bottom\" | \"left\";\n\nconst initialTransform = {\n  bottom: { y: \"100%\" },\n  left: { x: \"-100%\" },\n  right: { x: \"100%\" },\n  top: { y: \"-100%\" },\n} as const satisfies Record<DrawerSide, TargetAndTransition>;\n\nfunction getIsVertical(side: DrawerSide): boolean {\n  return side === \"top\" || side === \"bottom\";\n}\n\n// Check if drag should close the drawer\nfunction shouldClose(\n  side: DrawerSide,\n  offset: Point,\n  velocity: Point\n): boolean {\n  const isVertical = getIsVertical(side);\n  const dragDistance = isVertical ? Math.abs(offset.y) : Math.abs(offset.x);\n  const dragVelocity = isVertical ? Math.abs(velocity.y) : Math.abs(velocity.x);\n\n  // Check if dragging in close direction\n  const isClosingDirection =\n    (side === \"bottom\" && offset.y > 0) ||\n    (side === \"top\" && offset.y < 0) ||\n    (side === \"left\" && offset.x < 0) ||\n    (side === \"right\" && offset.x > 0);\n\n  if (!isClosingDirection) {\n    return false;\n  }\n\n  // Close on high velocity\n  if (dragVelocity > VELOCITY_THRESHOLD) {\n    return true;\n  }\n\n  // Close on distance threshold\n  const threshold =\n    (isVertical ? window.innerHeight : window.innerWidth) * CLOSE_THRESHOLD;\n  return dragDistance >= threshold;\n}\n\ntype DragOrientation = \"x\" | \"y\";\n\n// Get drag configuration for each side\nfunction getDragConfig(side: DrawerSide) {\n  const isVertical = getIsVertical(side);\n  const drag: DragOrientation = isVertical ? \"y\" : \"x\";\n\n  return {\n    drag,\n    dragConstraints: isVertical\n      ? side === \"top\"\n        ? { bottom: 5, top: -300 } // More restrictive for scrollable content\n        : { bottom: 300, top: -5 } // Very limited upward movement\n      : side === \"left\"\n        ? { left: -300, right: 5 }\n        : { left: -5, right: 300 },\n    dragElastic: isVertical\n      ? side === \"top\"\n        ? { bottom: 0.02, top: 0.15 } // Higher resistance near content\n        : { bottom: 0.15, top: 0.02 } // Very high resistance upward\n      : side === \"left\"\n        ? { left: 0.15, right: 0.02 }\n        : { left: 0.02, right: 0.15 },\n  };\n}\n\nexport interface DrawerRootProps\n  extends React.ComponentProps<typeof DialogTrigger> {}\n\nexport function DrawerRoot(props: DrawerRootProps) {\n  return <DialogTrigger data-slot=\"drawer-root\" {...props} />;\n}\n\nexport interface DrawerOverlayProps\n  extends Omit<React.ComponentProps<typeof MotionModalOverlay>, \"children\">,\n    VariantProps<typeof DrawerStyles.Modal> {\n  children: React.ComponentProps<typeof Dialog>[\"children\"];\n  /**\n   * Whether to close the modal when the user interacts outside it.\n   * @default true\n   */\n  isDismissable?: boolean;\n  /**\n   * Whether the drawer should float with margins.\n   * @default false\n   */\n  isFloat?: boolean;\n  /**\n   * Whether to show the drag notch indicator.\n   * @default true\n   */\n  notch?: boolean;\n}\n\nexport function DrawerOverlay({\n  className,\n  isDismissable = true,\n  side = \"bottom\",\n  isFloat = false,\n  notch = true,\n  children,\n  ...props\n}: DrawerOverlayProps) {\n  const state = use(OverlayTriggerStateContext);\n\n  if (!state) {\n    return null;\n  }\n\n  const handleDragEnd = (_: unknown, { offset, velocity }: PanInfo) => {\n    if (shouldClose(side, offset, velocity)) {\n      state.close();\n    }\n  };\n\n  const dragConfig = getDragConfig(side);\n\n  return (\n    <AnimatePresence>\n      {(props?.isOpen || state?.isOpen) && (\n        <MotionModalOverlay\n          animate={{ opacity: 1 }}\n          className={DrawerStyles.Overlay({ className })}\n          data-slot=\"drawer-overlay\"\n          exit={{ opacity: 0 }}\n          initial={{ opacity: 0 }}\n          isDismissable={isDismissable}\n          isOpen={props?.isOpen || state?.isOpen}\n          onOpenChange={props?.onOpenChange || state?.setOpen}\n          transition={TRANSITIONS}\n          {...props}\n        >\n          <MotionModal\n            animate={{ x: 0, y: 0 }}\n            className={DrawerStyles.Modal({ isFloat, side })}\n            data-slot=\"drawer-modal\"\n            drag={dragConfig.drag}\n            dragConstraints={dragConfig.dragConstraints}\n            dragElastic={dragConfig.dragElastic}\n            dragMomentum={false}\n            dragSnapToOrigin\n            exit={initialTransform[side]}\n            initial={initialTransform[side]}\n            onDragEnd={handleDragEnd}\n            transition={{\n              damping: 25,\n              stiffness: 400,\n              type: \"spring\",\n            }}\n            whileDrag={{\n              cursor: \"grabbing\",\n            }}\n          >\n            <Dialog\n              aria-label=\"Drawer\"\n              className={DrawerStyles.Content({ side })}\n              data-orientation={getIsVertical(side) ? \"vertical\" : \"horizontal\"}\n              data-side={side}\n              data-slot=\"drawer-content\"\n            >\n              {composeRenderProps(children, (children) => (\n                <>\n                  {notch && (side === \"bottom\" || side === \"top\") && (\n                    <div className={DrawerStyles.Notch()} />\n                  )}\n                  {children}\n                </>\n              ))}\n            </Dialog>\n          </MotionModal>\n        </MotionModalOverlay>\n      )}\n    </AnimatePresence>\n  );\n}\n\nexport interface DrawerHeaderProps extends React.ComponentProps<\"div\"> {}\n\nexport function DrawerHeader({ className, ...props }: DrawerHeaderProps) {\n  return (\n    <div\n      className={DrawerStyles.Header({ className })}\n      data-slot=\"drawer-header\"\n      slot=\"header\"\n      {...props}\n    />\n  );\n}\n\nexport interface DrawerTitleProps\n  extends React.ComponentProps<typeof Heading> {}\n\nexport function DrawerTitle({ className, ...props }: DrawerTitleProps) {\n  return (\n    <Heading\n      className={DrawerStyles.Title({ className })}\n      data-slot=\"drawer-title\"\n      slot=\"title\"\n      {...props}\n    />\n  );\n}\n\nexport interface DrawerDescriptionProps\n  extends React.ComponentProps<typeof Text> {}\n\nexport function DrawerDescription({\n  className,\n  ...props\n}: DrawerDescriptionProps) {\n  return (\n    <Text\n      className={DrawerStyles.Description({ className })}\n      data-slot=\"drawer-description\"\n      slot=\"description\"\n      {...props}\n    />\n  );\n}\n\nexport interface DrawerBodyProps extends React.ComponentProps<\"div\"> {}\n\nexport function DrawerBody({ className, ...props }: DrawerBodyProps) {\n  return (\n    <div\n      className={DrawerStyles.Body({ className })}\n      data-slot=\"drawer-body\"\n      slot=\"body\"\n      {...props}\n    />\n  );\n}\n\nexport interface DrawerFooterProps extends React.ComponentProps<\"div\"> {}\n\nexport function DrawerFooter({ className, ...props }: DrawerFooterProps) {\n  return (\n    <div\n      className={DrawerStyles.Footer({ className })}\n      data-slot=\"drawer-footer\"\n      slot=\"footer\"\n      {...props}\n    />\n  );\n}\n\nexport interface DrawerCloseProps extends React.ComponentProps<typeof Button> {}\n\nexport function DrawerClose({ className, ...props }: DrawerCloseProps) {\n  return (\n    <Button\n      className={className}\n      data-slot=\"drawer-close\"\n      slot=\"close\"\n      {...props}\n    />\n  );\n}\n",
      "path": "src/registry/ui/drawer/drawer.tsx",
      "type": "registry:ui"
    },
    {
      "content": "import { cva } from \"~/registry/lib/cva\";\n\nexport const DrawerStyles = {\n  Body: cva({\n    base: [\"flex-1 overflow-auto p-4\"],\n  }),\n  Content: cva({\n    base: [\n      \"group relative flex flex-col overflow-hidden outline-none\",\n      \"h-full max-h-full\",\n    ],\n    defaultVariants: {\n      side: \"bottom\",\n    },\n    variants: {\n      side: {\n        bottom: [\"mx-auto w-full max-w-lg\"],\n        left: [\"h-full\"],\n        right: [\"h-full\"],\n        top: [\"mx-auto w-full max-w-lg\"],\n      },\n    },\n  }),\n  Description: cva({\n    base: [\"text-muted-foreground text-sm\"],\n  }),\n  Footer: cva({\n    base: [\"flex flex-col-reverse p-4 sm:flex-row sm:justify-end sm:space-x-2\"],\n  }),\n  Header: cva({\n    base: [\"flex flex-col space-y-1.5 p-4 text-center sm:text-left\"],\n  }),\n  Modal: cva({\n    base: [\n      \"fixed z-50 flex touch-none flex-col gap-4 bg-background shadow-lg will-change-transform\",\n      \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n      \"data-[state=closed]:duration-300 data-[state=open]:duration-500\",\n    ],\n    compoundVariants: [\n      {\n        className: \"inset-x-2 top-2 rounded-lg border\",\n        isFloat: true,\n        side: \"top\",\n      },\n      {\n        className: \"inset-y-2 right-2 rounded-lg border\",\n        isFloat: true,\n        side: \"right\",\n      },\n      {\n        className: \"inset-x-2 bottom-2 rounded-lg border\",\n        isFloat: true,\n        side: \"bottom\",\n      },\n      {\n        className: \"inset-y-2 left-2 rounded-lg border\",\n        isFloat: true,\n        side: \"left\",\n      },\n    ],\n    defaultVariants: {\n      isFloat: false,\n      side: \"bottom\",\n    },\n    variants: {\n      isFloat: {\n        false: \"\",\n        true: \"\",\n      },\n      side: {\n        bottom: [\n          \"inset-x-0 bottom-0 rounded-t-lg border-t\",\n          \"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n        ],\n        left: [\n          \"inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm\",\n          \"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left\",\n        ],\n        right: [\n          \"inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm\",\n          \"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right\",\n        ],\n        top: [\n          \"inset-x-0 top-0 rounded-b-lg border-b\",\n          \"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n        ],\n      },\n    },\n  }),\n  Notch: cva({\n    base: [\"mx-auto h-2 w-[100px] rounded-full bg-muted\"],\n    defaultVariants: {\n      side: \"bottom\",\n    },\n    variants: {\n      side: {\n        bottom: [\"my-4\"],\n        left: [],\n        right: [],\n        top: [\"order-last mb-4\"],\n      },\n    },\n  }),\n  Overlay: cva({\n    base: [\n      \"fixed inset-0 z-50 bg-black/15 dark:bg-black/60\",\n      \"entering:motion-opacity-in motion-duration-200 motion-ease exiting:motion-opacity-out\",\n    ],\n  }),\n  Title: cva({\n    base: [\"font-semibold text-lg leading-none tracking-tight\"],\n  }),\n};\n",
      "path": "src/registry/ui/drawer/styles.ts",
      "type": "registry:ui"
    },
    {
      "content": "export {\n  DrawerBody as Body,\n  DrawerClose as Close,\n  DrawerDescription as Description,\n  DrawerFooter as Footer,\n  DrawerHeader as Header,\n  DrawerOverlay as Overlay,\n  DrawerRoot as Root,\n  DrawerTitle as Title,\n} from \"./drawer\";\n",
      "path": "src/registry/ui/drawer/namespace.ts",
      "type": "registry:ui"
    }
  ],
  "name": "drawer",
  "registryDependencies": [
    "@kanpeki/cva",
    "@kanpeki/custom-plugin"
  ],
  "type": "registry:ui"
}