Drawer
godui/drawerFreeComponent
A draggable bottom or side sheet with rubber-band physics, flick-to-dismiss, scrim, and scroll lock.
Install it
npx shadcn@latest add https://godui.design/r/drawer.jsonSource
1"use client";23import { AnimatePresence, motion, type PanInfo } from "framer-motion";4import * as React from "react";5import { createPortal } from "react-dom";67export type DrawerSide = "bottom" | "right";89export type DrawerProps = {10 /** Controlled open state. */11 open: boolean;12 /** Called when the drawer requests to open or close. */13 onOpenChange: (open: boolean) => void;14 /** Side the drawer slides in from. */15 side?: DrawerSide;16 /** Optional accessible title rendered at the top. */17 title?: React.ReactNode;18 /** Extra classes for the panel. */19 className?: string;20 children?: React.ReactNode;21};2223const PANEL_BY_SIDE: Record<DrawerSide, string> = {24 bottom:25 "inset-x-0 bottom-0 max-h-[90vh] rounded-t-2xl border-t border-border",26 right:27 "inset-y-0 right-0 w-full max-w-md rounded-l-2xl border-l border-border",28};2930const CLOSE_OFFSET = 120;31const CLOSE_VELOCITY = 600;3233function useMounted() {34 const [mounted, setMounted] = React.useState(false);35 React.useEffect(() => setMounted(true), []);36 return mounted;37}3839const Drawer = React.forwardRef<HTMLDivElement, DrawerProps>(40 (41 { open, onOpenChange, side = "bottom", title, className, children },42 ref,43 ) => {44 const mounted = useMounted();45 const isBottom = side === "bottom";4647 React.useEffect(() => {48 if (!open) return;49 const onKey = (e: KeyboardEvent) => {50 if (e.key === "Escape") onOpenChange(false);51 };52 document.addEventListener("keydown", onKey);53 const prevOverflow = document.body.style.overflow;54 document.body.style.overflow = "hidden";55 return () => {56 document.removeEventListener("keydown", onKey);57 document.body.style.overflow = prevOverflow;58 };59 }, [open, onOpenChange]);6061 const handleDragEnd = (62 _e: MouseEvent | TouchEvent | PointerEvent,63 info: PanInfo,64 ) => {65 const offset = isBottom ? info.offset.y : info.offset.x;66 const velocity = isBottom ? info.velocity.y : info.velocity.x;67 if (offset > CLOSE_OFFSET || velocity > CLOSE_VELOCITY) {68 onOpenChange(false);69 }70 };7172 if (!mounted) return null;7374 const hidden = isBottom ? { y: "100%" } : { x: "100%" };75 const shown = isBottom ? { y: 0 } : { x: 0 };7677 return createPortal(78 <AnimatePresence>79 {open ? (80 <div className="fixed inset-0 z-modal">81 <motion.div82 aria-hidden83 initial={{ opacity: 0 }}84// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from godui
All 105 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | godui | Components | Free | 1 dep | |
| Agent Flowagent-flow | godui | Components | Free | 1 dep | |
| Agent Timelineagent-timeline | godui | Components | Free | 1 dep | |
| Animated Beamanimated-beam | godui | Components | Free | 1 dep | |
| Animated Testimonialsanimated-testimonials | godui | Components | Free | 1 dep | |
| Animated Tooltipanimated-tooltip | godui | Components | Free | 1 dep | |
| App Showcaseapp-showcase | godui | Components | Free | 1 dep | |
| Aurora Textaurora-text | godui | Components | Free | no deps |
