{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "drawer",
  "title": "Drawer",
  "author": "andongmin94",
  "description": "A panel that slides in from the edge of the viewport.",
  "dependencies": [
    "@base-ui/react@^1.6.0"
  ],
  "files": [
    {
      "path": "src/components/ui/drawer.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Drawer as DrawerPrimitive } from \"@base-ui/react/drawer\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype AutoFocusEventHandler = (event: Event) => void;\ntype FocusTarget = DrawerPrimitive.Popup.Props[\"initialFocus\"];\ntype PointerDownOutsideEvent = CustomEvent<{ originalEvent: PointerEvent }>;\ntype FocusOutsideEvent = CustomEvent<{ originalEvent: FocusEvent }>;\ntype DismissableLayerHandlers = {\n  onEscapeKeyDown?: (event: KeyboardEvent) => void;\n  onFocusOutside?: (event: FocusOutsideEvent) => void;\n  onInteractOutside?: (event: FocusOutsideEvent | PointerDownOutsideEvent) => void;\n  onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;\n};\ntype DrawerDirection = \"bottom\" | \"left\" | \"right\" | \"top\";\ntype DrawerSnapPoint = DrawerPrimitive.Root.SnapPoint;\ntype DrawerDragMetrics = {\n  active: boolean;\n  event: React.PointerEvent<HTMLDivElement> | null;\n  percentage: number;\n  pointerType: string;\n  releaseScheduled: boolean;\n  velocity: number;\n};\ntype DrawerProps = DrawerPrimitive.Root.Props & {\n  activeSnapPoint?: DrawerSnapPoint | null;\n  autoFocus?: boolean;\n  closeThreshold?: number;\n  container?: HTMLElement | null;\n  direction?: DrawerDirection;\n  disablePreventScroll?: boolean;\n  dismissible?: boolean;\n  fadeFromIndex?: number;\n  fixed?: boolean;\n  handleOnly?: boolean;\n  nested?: boolean;\n  noBodyStyles?: boolean;\n  onAnimationEnd?: (open: boolean) => void;\n  onClose?: () => void;\n  onDrag?: (event: React.PointerEvent<HTMLDivElement>, percentageDragged: number) => void;\n  onRelease?: (event: React.PointerEvent<HTMLDivElement>, open: boolean) => void;\n  preventScrollRestoration?: boolean;\n  repositionInputs?: boolean;\n  scrollLockTimeout?: number;\n  setActiveSnapPoint?: (snapPoint: DrawerSnapPoint | null) => void;\n  setBackgroundColorOnScale?: boolean;\n  shouldScaleBackground?: boolean;\n  showSwipeHandle?: boolean;\n  snapToSequentialPoint?: boolean;\n};\ntype DrawerPortalProps = DrawerPrimitive.Portal.Props & {\n  forceMount?: boolean;\n};\ntype DrawerOverlayProps = DrawerPrimitive.Backdrop.Props & {\n  asChild?: boolean;\n  children?: React.ReactNode;\n  forceMount?: boolean;\n};\ntype DrawerContentProps = DrawerPrimitive.Popup.Props & {\n  asChild?: boolean;\n  children?: React.ReactNode;\n  forceMount?: boolean;\n  onCloseAutoFocus?: AutoFocusEventHandler;\n  onOpenAutoFocus?: AutoFocusEventHandler;\n} & DismissableLayerHandlers;\n\ntype DrawerContextProps = {\n  actionsRef: React.RefObject<DrawerPrimitive.Root.Actions | null>;\n  activeSnapPoint: DrawerSnapPoint | null;\n  autoFocus: boolean;\n  closeThreshold: number;\n  container: HTMLElement | null | undefined;\n  direction: DrawerDirection;\n  disablePreventScroll: boolean;\n  dismissible: boolean;\n  dismissableLayerHandlersRef: React.RefObject<DismissableLayerHandlers>;\n  dragMetricsRef: React.RefObject<DrawerDragMetrics>;\n  fadeFromIndex: number | undefined;\n  fadeRange: number;\n  fadeStart: number;\n  fixed: boolean | undefined;\n  handleOnly: boolean;\n  hasSnapPoints: boolean;\n  lastScrollTimeRef: React.RefObject<number>;\n  modal: DrawerPrimitive.Root.Props[\"modal\"];\n  nested: boolean | undefined;\n  noBodyStyles: boolean;\n  onDrag: DrawerProps[\"onDrag\"];\n  open: boolean;\n  openRef: React.RefObject<boolean>;\n  popupRef: React.RefObject<HTMLDivElement | null>;\n  preventScrollRestoration: boolean;\n  repositionInputs: boolean;\n  scheduleRelease: (event: React.PointerEvent<HTMLDivElement>) => void;\n  scrollLockTimeout: number;\n  shouldFade: boolean;\n  showSwipeHandle: boolean;\n  snapPointOffset: string | undefined;\n  swipeDirection: NonNullable<DrawerPrimitive.Root.Props[\"swipeDirection\"]>;\n};\n\ntype DrawerOverlayStyle = React.CSSProperties & {\n  \"--vaul-fade-range\"?: number;\n  \"--vaul-fade-start\"?: number;\n};\n\ntype DrawerContentStyle = React.CSSProperties & {\n  \"--snap-point-height\"?: string;\n};\n\ntype PreventableBaseUIEvent = {\n  readonly baseUIHandlerPrevented?: boolean;\n  readonly defaultPrevented?: boolean;\n  preventBaseUIHandler?: () => void;\n};\ntype EventHandler = (...args: unknown[]) => unknown;\ntype RenderFunction = (props: Record<string, unknown>, state: unknown) => React.ReactElement;\n\nfunction adaptEventHandlers(props: Record<string, unknown>, beforeBaseUIHandler: boolean) {\n  const eventHandlers: Record<string, EventHandler> = {};\n\n  for (const [name, handler] of Object.entries(props)) {\n    if (!/^on[A-Z]/.test(name) || typeof handler !== \"function\") {\n      continue;\n    }\n\n    eventHandlers[name] = (...args) => {\n      const event = args[0] as PreventableBaseUIEvent | undefined;\n      if (beforeBaseUIHandler && event?.defaultPrevented) {\n        event.preventBaseUIHandler?.();\n      }\n      if (beforeBaseUIHandler && event?.baseUIHandlerPrevented) {\n        return undefined;\n      }\n\n      const result = handler(...args);\n      if (!beforeBaseUIHandler && event?.defaultPrevented) {\n        event.preventBaseUIHandler?.();\n      }\n      return result;\n    };\n  }\n\n  return eventHandlers;\n}\n\nfunction adaptRenderEventHandlers<Render>(render: Render): Render {\n  if (React.isValidElement(render)) {\n    const element = render as React.ReactElement<Record<string, unknown>>;\n    const eventHandlers = adaptEventHandlers(element.props, false);\n    return (Object.keys(eventHandlers).length === 0\n      ? render\n      : React.cloneElement(element, eventHandlers)) as unknown as Render;\n  }\n  if (typeof render === \"function\") {\n    const renderFunction = render as RenderFunction;\n    return ((props: Record<string, unknown>, state: unknown) =>\n      renderFunction({ ...props, ...adaptEventHandlers(props, true) }, state)) as unknown as Render;\n  }\n  return render;\n}\n\nfunction toSwipeDirection(direction?: DrawerDirection) {\n  if (direction === \"bottom\") {\n    return \"down\";\n  }\n\n  if (direction === \"top\") {\n    return \"up\";\n  }\n\n  return direction;\n}\n\nfunction fromSwipeDirection(\n  swipeDirection: NonNullable<DrawerPrimitive.Root.Props[\"swipeDirection\"]>,\n): DrawerDirection {\n  if (swipeDirection === \"down\") {\n    return \"bottom\";\n  }\n\n  if (swipeDirection === \"up\") {\n    return \"top\";\n  }\n\n  return swipeDirection;\n}\n\nfunction getDrawerAxisSize(direction: DrawerDirection, container?: HTMLElement | null) {\n  if (container) {\n    const rect = container.getBoundingClientRect();\n    return direction === \"bottom\" || direction === \"top\" ? rect.height : rect.width;\n  }\n\n  if (typeof window === \"undefined\") {\n    return 0;\n  }\n\n  return direction === \"bottom\" || direction === \"top\" ? window.innerHeight : window.innerWidth;\n}\n\nfunction getSnapPointValue(\n  snapPoint: DrawerSnapPoint,\n  direction: DrawerDirection,\n  container?: HTMLElement | null,\n) {\n  if (typeof snapPoint === \"number\") {\n    if (snapPoint <= 1) {\n      return snapPoint;\n    }\n\n    const axisSize = getDrawerAxisSize(direction, container);\n    return axisSize > 0 ? snapPoint / axisSize : snapPoint;\n  }\n\n  const parsed = Number.parseFloat(snapPoint);\n  if (!Number.isFinite(parsed)) {\n    return null;\n  }\n\n  const axisSize = getDrawerAxisSize(direction, container);\n  if (snapPoint.endsWith(\"rem\")) {\n    const rootFontSize =\n      typeof document === \"undefined\"\n        ? 16\n        : Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;\n    return axisSize > 0 ? (parsed * rootFontSize) / axisSize : parsed;\n  }\n\n  return axisSize > 0 ? parsed / axisSize : parsed;\n}\n\nfunction getVaulFadeRange(\n  snapPoints: DrawerSnapPoint[] | undefined,\n  fadeFromIndex: number | undefined,\n  direction: DrawerDirection,\n  container?: HTMLElement | null,\n) {\n  if (!snapPoints?.length || fadeFromIndex === undefined) {\n    return { fadeRange: 1, fadeStart: 0 };\n  }\n\n  const index = Math.min(Math.max(fadeFromIndex, 0), snapPoints.length - 1);\n  if (index === 0) {\n    return { fadeRange: 1, fadeStart: -1 };\n  }\n\n  const values = snapPoints.map((snapPoint) => getSnapPointValue(snapPoint, direction, container));\n  const first = values[0];\n  const last = values[values.length - 1];\n  const previous = values[index - 1];\n  const current = values[index];\n\n  if (first === null || last === null || previous === null || current === null || first === last) {\n    const denominator = Math.max(snapPoints.length - 1, 1);\n    return { fadeRange: 1 / denominator, fadeStart: (index - 1) / denominator };\n  }\n\n  const fadeStart = (previous - first) / (last - first);\n  const fadeEnd = (current - first) / (last - first);\n  return {\n    fadeRange: Math.max(Math.abs(fadeEnd - fadeStart), Number.EPSILON),\n    fadeStart: Math.min(fadeStart, fadeEnd),\n  };\n}\n\nfunction getVaulSnapPointOffset(\n  snapPoint: DrawerSnapPoint | undefined,\n  direction: DrawerDirection,\n  container?: HTMLElement | null,\n) {\n  if (snapPoint === undefined) {\n    return undefined;\n  }\n\n  const horizontal = direction === \"left\" || direction === \"right\";\n  const axis = container\n    ? `${getDrawerAxisSize(direction, container)}px`\n    : horizontal\n      ? \"100dvw\"\n      : \"100dvh\";\n  const visibleSize =\n    typeof snapPoint === \"number\"\n      ? snapPoint <= 1\n        ? `(${axis} * ${snapPoint})`\n        : `${snapPoint}px`\n      : snapPoint;\n  const offset = `calc(${axis} - ${visibleSize})`;\n\n  return direction === \"top\" || direction === \"left\" ? `calc(-1 * ${offset})` : offset;\n}\n\nfunction createPointerEvent(event: MouseEvent | PointerEvent | TouchEvent): PointerEvent {\n  if (\"pointerId\" in event) {\n    return event;\n  }\n\n  const touch = \"changedTouches\" in event ? event.changedTouches[0] : undefined;\n  const ownerDocument = event.target instanceof Node ? event.target.ownerDocument : null;\n  const view = ownerDocument?.defaultView ?? window;\n  const PointerEventConstructor = view?.PointerEvent;\n\n  if (!PointerEventConstructor) {\n    return event as PointerEvent;\n  }\n\n  return new PointerEventConstructor(\"pointerdown\", {\n    bubbles: event.bubbles,\n    button: \"button\" in event ? event.button : 0,\n    buttons: \"buttons\" in event ? event.buttons : 1,\n    cancelable: event.cancelable,\n    clientX: touch?.clientX ?? (\"clientX\" in event ? event.clientX : 0),\n    clientY: touch?.clientY ?? (\"clientY\" in event ? event.clientY : 0),\n    composed: event.composed,\n    ctrlKey: \"ctrlKey\" in event && event.ctrlKey,\n    metaKey: \"metaKey\" in event && event.metaKey,\n    pointerType: \"changedTouches\" in event ? \"touch\" : \"mouse\",\n    shiftKey: \"shiftKey\" in event && event.shiftKey,\n  });\n}\n\nfunction createPointerDownOutsideEvent(\n  originalEvent: MouseEvent | PointerEvent | TouchEvent,\n): PointerDownOutsideEvent {\n  const event = new CustomEvent<{ originalEvent: PointerEvent }>(\n    \"dismissableLayer.pointerDownOutside\",\n    {\n      cancelable: true,\n      detail: { originalEvent: createPointerEvent(originalEvent) },\n    },\n  );\n  Object.defineProperty(event, \"target\", { configurable: true, value: originalEvent.target });\n  return event;\n}\n\nfunction createFocusOutsideEvent(originalEvent: FocusEvent | KeyboardEvent): FocusOutsideEvent {\n  const focusEvent =\n    \"relatedTarget\" in originalEvent\n      ? originalEvent\n      : new FocusEvent(\"focusin\", { bubbles: true, cancelable: true });\n  const event = new CustomEvent<{ originalEvent: FocusEvent }>(\"dismissableLayer.focusOutside\", {\n    cancelable: true,\n    detail: { originalEvent: focusEvent },\n  });\n  Object.defineProperty(event, \"target\", { configurable: true, value: originalEvent.target });\n  return event;\n}\n\nfunction adaptDismissableLayerEvent(\n  eventDetails: DrawerPrimitive.Root.ChangeEventDetails,\n  handlers: DismissableLayerHandlers,\n) {\n  if (eventDetails.reason === \"escape-key\") {\n    handlers.onEscapeKeyDown?.(eventDetails.event);\n    if (eventDetails.event.defaultPrevented) {\n      eventDetails.cancel();\n    }\n    return;\n  }\n\n  if (eventDetails.reason === \"outside-press\") {\n    const event = createPointerDownOutsideEvent(eventDetails.event);\n    handlers.onPointerDownOutside?.(event);\n    handlers.onInteractOutside?.(event);\n    if (event.defaultPrevented) {\n      eventDetails.cancel();\n    }\n    return;\n  }\n\n  if (eventDetails.reason === \"focus-out\") {\n    const event = createFocusOutsideEvent(eventDetails.event);\n    handlers.onFocusOutside?.(event);\n    handlers.onInteractOutside?.(event);\n    if (event.defaultPrevented) {\n      eventDetails.cancel();\n    }\n  }\n}\n\nfunction adaptCloseClick(\n  handler: DrawerPrimitive.Close.Props[\"onClick\"],\n): DrawerPrimitive.Close.Props[\"onClick\"] {\n  if (!handler) {\n    return undefined;\n  }\n\n  return (event) => {\n    handler(event);\n    if (event.defaultPrevented) {\n      event.preventBaseUIHandler();\n    }\n  };\n}\n\nfunction hasForceMountedChild(children: React.ReactNode) {\n  return React.Children.toArray(children).some(\n    (child) => React.isValidElement<{ forceMount?: boolean }>(child) && child.props.forceMount,\n  );\n}\n\nfunction adaptAutoFocus(\n  handler: AutoFocusEventHandler | undefined,\n  focus: FocusTarget,\n  eventName: string,\n): FocusTarget {\n  if (!handler) {\n    return focus;\n  }\n\n  return (interactionType) => {\n    const event = new Event(eventName, { cancelable: true });\n    handler(event);\n\n    if (event.defaultPrevented) {\n      return false;\n    }\n\n    if (typeof focus === \"function\") {\n      return focus(interactionType);\n    }\n\n    if (typeof focus === \"object\" && focus !== null) {\n      return focus.current;\n    }\n\n    return typeof focus === \"boolean\" ? focus : true;\n  };\n}\n\nfunction useScaleBackground({\n  direction,\n  enabled,\n  open,\n  setBackgroundColor,\n}: {\n  direction: DrawerDirection;\n  enabled: boolean;\n  open: boolean;\n  setBackgroundColor: boolean;\n}) {\n  React.useEffect(() => {\n    if (!enabled || !open) {\n      return;\n    }\n\n    const wrapper =\n      document.querySelector<HTMLElement>(\"[data-vaul-drawer-wrapper]\") ??\n      document.querySelector<HTMLElement>(\"[vaul-drawer-wrapper]\");\n\n    if (!wrapper) {\n      return;\n    }\n\n    const previousWrapperStyles = {\n      borderRadius: wrapper.style.borderRadius,\n      overflow: wrapper.style.overflow,\n      transform: wrapper.style.transform,\n      transformOrigin: wrapper.style.transformOrigin,\n      transitionDuration: wrapper.style.transitionDuration,\n      transitionProperty: wrapper.style.transitionProperty,\n      transitionTimingFunction: wrapper.style.transitionTimingFunction,\n    };\n    const previousBodyBackground = document.body.style.background;\n    const scale = (window.innerWidth - 16) / Math.max(window.innerWidth, 1);\n    const vertical = direction === \"bottom\" || direction === \"top\";\n\n    Object.assign(wrapper.style, {\n      borderRadius: \"8px\",\n      overflow: \"hidden\",\n      transform: vertical\n        ? `scale(${scale}) translate3d(0, calc(env(safe-area-inset-top) + 14px), 0)`\n        : `scale(${scale}) translate3d(calc(env(safe-area-inset-top) + 14px), 0, 0)`,\n      transformOrigin: vertical ? \"top\" : \"left\",\n      transitionDuration: \"500ms\",\n      transitionProperty: \"transform, border-radius\",\n      transitionTimingFunction: \"cubic-bezier(0.32, 0.72, 0, 1)\",\n    });\n\n    if (setBackgroundColor) {\n      document.body.style.background = \"black\";\n    }\n\n    return () => {\n      Object.assign(wrapper.style, previousWrapperStyles);\n\n      if (setBackgroundColor) {\n        document.body.style.background = previousBodyBackground;\n      }\n    };\n  }, [direction, enabled, open, setBackgroundColor]);\n}\n\nconst DrawerContext = React.createContext<DrawerContextProps | null>(null);\n\nfunction useDrawer() {\n  const context = React.useContext(DrawerContext);\n\n  if (!context) {\n    throw new Error(\"useDrawer must be used within a Drawer.\");\n  }\n\n  return context;\n}\n\nfunction Drawer({\n  actionsRef: actionsRefProp,\n  activeSnapPoint,\n  autoFocus = true,\n  children,\n  closeThreshold = 0.25,\n  container,\n  defaultOpen = false,\n  defaultSnapPoint,\n  direction,\n  disablePreventScroll = true,\n  disablePointerDismissal,\n  dismissible = true,\n  fadeFromIndex,\n  fixed,\n  handleOnly = false,\n  modal = true,\n  nested,\n  noBodyStyles = false,\n  onAnimationEnd,\n  onClose,\n  onDrag,\n  onOpenChange,\n  onOpenChangeComplete,\n  onRelease,\n  onSnapPointChange,\n  open,\n  preventScrollRestoration = false,\n  repositionInputs = true,\n  scrollLockTimeout = 100,\n  setActiveSnapPoint,\n  setBackgroundColorOnScale = true,\n  shouldScaleBackground = true,\n  showSwipeHandle = true,\n  snapPoint,\n  snapPoints,\n  snapToSequentialPoint,\n  snapToSequentialPoints,\n  swipeDirection: swipeDirectionProp,\n  ...props\n}: DrawerProps) {\n  const swipeDirection = swipeDirectionProp ?? toSwipeDirection(direction) ?? \"down\";\n  const hasSnapPoints = snapPoints != null && snapPoints.length > 0;\n  const resolvedDirection = direction ?? fromSwipeDirection(swipeDirection);\n  const resolvedCloseThreshold = Math.min(Math.max(closeThreshold, 0), 1);\n  const controlledSnapPoint = activeSnapPoint !== undefined ? activeSnapPoint : snapPoint;\n  const resolvedDefaultSnapPoint = defaultSnapPoint ?? snapPoints?.[0] ?? null;\n  const [uncontrolledSnapPoint, setUncontrolledSnapPoint] = React.useState<DrawerSnapPoint | null>(\n    resolvedDefaultSnapPoint,\n  );\n  const resolvedSnapPoint =\n    controlledSnapPoint !== undefined ? controlledSnapPoint : uncontrolledSnapPoint;\n  const resolvedFadeFromIndex =\n    fadeFromIndex ?? (hasSnapPoints ? snapPoints.length - 1 : undefined);\n  const activeSnapPointIndex = snapPoints?.findIndex((point) =>\n    Object.is(point, resolvedSnapPoint),\n  );\n  const shouldFade =\n    !hasSnapPoints ||\n    (resolvedFadeFromIndex !== undefined && activeSnapPointIndex === resolvedFadeFromIndex);\n  const { fadeRange, fadeStart } = getVaulFadeRange(\n    snapPoints,\n    resolvedFadeFromIndex,\n    resolvedDirection,\n    container,\n  );\n  const snapPointOffset = getVaulSnapPointOffset(snapPoints?.[0], resolvedDirection, container);\n  const resolvedSnapToSequentialPoints = snapToSequentialPoint ?? snapToSequentialPoints;\n  const fallbackActionsRef = React.useRef<DrawerPrimitive.Root.Actions | null>(null);\n  const actionsRef = actionsRefProp ?? fallbackActionsRef;\n  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);\n  const resolvedOpen = open ?? uncontrolledOpen;\n  const openRef = React.useRef(resolvedOpen);\n  const popupRef = React.useRef<HTMLDivElement>(null);\n  const lastScrollTimeRef = React.useRef(0);\n  const dismissableLayerHandlersRef = React.useRef<DismissableLayerHandlers>({});\n  const dragMetricsRef = React.useRef<DrawerDragMetrics>({\n    active: false,\n    event: null,\n    percentage: 0,\n    pointerType: \"\",\n    releaseScheduled: false,\n    velocity: 0,\n  });\n  const releaseFrameRef = React.useRef<number | null>(null);\n  openRef.current = resolvedOpen;\n\n  if (hasSnapPoints && (swipeDirection === \"left\" || swipeDirection === \"right\")) {\n    throw new Error(\"Drawer snapPoints are only supported for top and bottom drawers.\");\n  }\n\n  const scheduleRelease = React.useCallback(\n    (event: React.PointerEvent<HTMLDivElement>) => {\n      if (!onRelease) {\n        return;\n      }\n\n      if (releaseFrameRef.current !== null) {\n        window.cancelAnimationFrame(releaseFrameRef.current);\n      }\n      releaseFrameRef.current = window.requestAnimationFrame(() => {\n        releaseFrameRef.current = null;\n        onRelease(event, openRef.current);\n      });\n    },\n    [onRelease],\n  );\n\n  React.useEffect(\n    () => () => {\n      if (releaseFrameRef.current !== null) {\n        window.cancelAnimationFrame(releaseFrameRef.current);\n      }\n    },\n    [],\n  );\n\n  const handleOpenChange: NonNullable<DrawerPrimitive.Root.Props[\"onOpenChange\"]> = (\n    nextOpen,\n    eventDetails,\n  ) => {\n    if (!nextOpen) {\n      adaptDismissableLayerEvent(eventDetails, dismissableLayerHandlersRef.current);\n    }\n\n    if (!nextOpen && !dismissible) {\n      eventDetails.cancel();\n    }\n\n    const dragMetrics = dragMetricsRef.current;\n    if (\n      !nextOpen &&\n      eventDetails.reason === \"swipe\" &&\n      dragMetrics.active &&\n      dragMetrics.percentage < resolvedCloseThreshold &&\n      dragMetrics.velocity <= 0.4\n    ) {\n      eventDetails.cancel();\n    }\n\n    if (!eventDetails.isCanceled) {\n      onOpenChange?.(nextOpen, eventDetails);\n    }\n\n    if (\n      !nextOpen &&\n      eventDetails.reason === \"swipe\" &&\n      dragMetrics.active &&\n      dragMetrics.event &&\n      !dragMetrics.releaseScheduled\n    ) {\n      dragMetrics.releaseScheduled = true;\n      scheduleRelease(dragMetrics.event);\n    }\n\n    if (eventDetails.isCanceled) {\n      return;\n    }\n\n    if (open === undefined) {\n      openRef.current = nextOpen;\n      setUncontrolledOpen(nextOpen);\n    }\n\n    if (!nextOpen) {\n      onClose?.();\n    }\n  };\n\n  const handleOpenChangeComplete = (nextOpen: boolean) => {\n    onOpenChangeComplete?.(nextOpen);\n    onAnimationEnd?.(nextOpen);\n  };\n\n  const handleSnapPointChange: NonNullable<DrawerPrimitive.Root.Props[\"onSnapPointChange\"]> = (\n    nextSnapPoint,\n    eventDetails,\n  ) => {\n    onSnapPointChange?.(nextSnapPoint, eventDetails);\n\n    if (!eventDetails.isCanceled) {\n      if (controlledSnapPoint === undefined) {\n        setUncontrolledSnapPoint(nextSnapPoint);\n      }\n      setActiveSnapPoint?.(nextSnapPoint);\n    }\n  };\n\n  useScaleBackground({\n    direction: resolvedDirection,\n    enabled: shouldScaleBackground,\n    open: resolvedOpen,\n    setBackgroundColor: setBackgroundColorOnScale && !noBodyStyles,\n  });\n\n  const contextValue = React.useMemo(\n    () => ({\n      actionsRef,\n      activeSnapPoint: resolvedSnapPoint,\n      autoFocus,\n      closeThreshold: resolvedCloseThreshold,\n      container,\n      direction: resolvedDirection,\n      disablePreventScroll,\n      dismissible,\n      dismissableLayerHandlersRef,\n      dragMetricsRef,\n      fadeFromIndex: resolvedFadeFromIndex,\n      fadeRange,\n      fadeStart,\n      fixed,\n      handleOnly,\n      hasSnapPoints,\n      lastScrollTimeRef,\n      modal,\n      nested,\n      noBodyStyles,\n      onDrag,\n      open: resolvedOpen,\n      openRef,\n      popupRef,\n      preventScrollRestoration,\n      repositionInputs,\n      scheduleRelease,\n      scrollLockTimeout,\n      shouldFade,\n      showSwipeHandle,\n      snapPointOffset,\n      swipeDirection,\n    }),\n    [\n      actionsRef,\n      autoFocus,\n      container,\n      disablePreventScroll,\n      dismissible,\n      fadeRange,\n      fadeStart,\n      fixed,\n      handleOnly,\n      hasSnapPoints,\n      modal,\n      nested,\n      noBodyStyles,\n      onDrag,\n      preventScrollRestoration,\n      repositionInputs,\n      resolvedDirection,\n      resolvedCloseThreshold,\n      resolvedFadeFromIndex,\n      resolvedOpen,\n      resolvedSnapPoint,\n      scheduleRelease,\n      scrollLockTimeout,\n      shouldFade,\n      showSwipeHandle,\n      snapPointOffset,\n      swipeDirection,\n    ],\n  );\n\n  return (\n    <DrawerContext.Provider value={contextValue}>\n      <DrawerPrimitive.Root\n        data-slot=\"drawer\"\n        actionsRef={actionsRef}\n        defaultSnapPoint={resolvedDefaultSnapPoint}\n        defaultOpen={defaultOpen}\n        disablePointerDismissal={disablePointerDismissal ?? !dismissible}\n        modal={modal}\n        onOpenChange={handleOpenChange}\n        onOpenChangeComplete={handleOpenChangeComplete}\n        onSnapPointChange={handleSnapPointChange}\n        open={open}\n        snapPoint={controlledSnapPoint}\n        snapPoints={snapPoints}\n        snapToSequentialPoints={resolvedSnapToSequentialPoints}\n        swipeDirection={swipeDirection}\n        {...props}\n      >\n        {repositionInputs && typeof children !== \"function\" ? (\n          <DrawerPrimitive.VirtualKeyboardProvider>\n            {children}\n          </DrawerPrimitive.VirtualKeyboardProvider>\n        ) : (\n          children\n        )}\n      </DrawerPrimitive.Root>\n    </DrawerContext.Provider>\n  );\n}\n\nfunction DrawerTrigger({\n  asChild = false,\n  children,\n  render,\n  ...props\n}: DrawerPrimitive.Trigger.Props & {\n  asChild?: boolean;\n  children?: React.ReactNode;\n}) {\n  const { open } = useDrawer();\n  const renderElement = asChild\n    ? (React.Children.toArray(children).find(React.isValidElement) as React.ReactElement)\n    : render;\n\n  return (\n    <DrawerPrimitive.Trigger\n      data-slot=\"drawer-trigger\"\n      data-state={open ? \"open\" : \"closed\"}\n      render={adaptRenderEventHandlers(renderElement)}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </DrawerPrimitive.Trigger>\n  );\n}\n\nfunction DrawerPortal({\n  container: containerProp,\n  forceMount,\n  keepMounted,\n  ...props\n}: DrawerPortalProps) {\n  const { container } = useDrawer();\n  const keepPortalMounted = forceMount ?? (hasForceMountedChild(props.children) || keepMounted);\n\n  return (\n    <DrawerPrimitive.Portal\n      data-slot=\"drawer-portal\"\n      container={containerProp ?? container ?? undefined}\n      keepMounted={keepPortalMounted}\n      {...props}\n    />\n  );\n}\n\nfunction DrawerClose({\n  asChild = false,\n  children,\n  onClick,\n  render,\n  ...props\n}: DrawerPrimitive.Close.Props & {\n  asChild?: boolean;\n  children?: React.ReactNode;\n}) {\n  const renderElement = asChild\n    ? (React.Children.toArray(children).find(React.isValidElement) as React.ReactElement)\n    : render;\n\n  return (\n    <DrawerPrimitive.Close\n      data-slot=\"drawer-close\"\n      onClick={adaptCloseClick(onClick)}\n      render={adaptRenderEventHandlers(renderElement)}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </DrawerPrimitive.Close>\n  );\n}\n\nfunction DrawerOverlay({\n  asChild = false,\n  children,\n  className,\n  forceMount: _forceMount,\n  render,\n  style,\n  ...props\n}: DrawerOverlayProps) {\n  const { fadeRange, fadeStart, hasSnapPoints, open, shouldFade } = useDrawer();\n  const renderElement = asChild\n    ? (React.Children.toArray(children).find(React.isValidElement) as React.ReactElement)\n    : render;\n  const vaulStyle: DrawerOverlayStyle = {\n    \"--vaul-fade-range\": fadeRange,\n    \"--vaul-fade-start\": fadeStart,\n  };\n  const overlayStyle: DrawerPrimitive.Backdrop.Props[\"style\"] =\n    typeof style === \"function\"\n      ? (state) => ({ ...vaulStyle, ...style(state) })\n      : { ...vaulStyle, ...style };\n\n  return (\n    <DrawerPrimitive.Backdrop\n      data-slot=\"drawer-overlay\"\n      data-state={open ? \"open\" : \"closed\"}\n      data-vaul-overlay=\"\"\n      data-vaul-snap-points={open && hasSnapPoints ? \"true\" : \"false\"}\n      data-vaul-snap-points-overlay={open && shouldFade ? \"true\" : \"false\"}\n      render={adaptRenderEventHandlers(renderElement)}\n      style={overlayStyle}\n      className={cn(\n        \"fixed inset-0 z-50 min-h-dvh bg-overlay opacity-[clamp(0,calc((1-var(--drawer-swipe-progress)-var(--vaul-fade-start,0))/var(--vaul-fade-range,1)),1)] transition-opacity duration-450 ease-[cubic-bezier(0.32,0.72,0,1)] select-none data-ending-style:pointer-events-none data-ending-style:opacity-0 data-ending-style:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-starting-style:opacity-0 data-swiping:duration-0 supports-[-webkit-touch-callout:none]:absolute\",\n        className,\n      )}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </DrawerPrimitive.Backdrop>\n  );\n}\n\nfunction DrawerSwipeHandle({ children, className, ...props }: React.ComponentProps<\"div\">) {\n  const { open } = useDrawer();\n\n  return (\n    <div\n      data-slot=\"drawer-swipe-handle\"\n      data-vaul-drawer-visible={open ? \"true\" : \"false\"}\n      data-vaul-handle=\"\"\n      aria-hidden=\"true\"\n      className={cn(\n        \"relative z-10 shrink-0 cursor-grab rounded-full bg-current transition-opacity duration-200 group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100 group-data-[swipe-axis=x]/drawer-popup:my-auto group-data-[swipe-axis=x]/drawer-popup:h-[100px] group-data-[swipe-axis=x]/drawer-popup:w-2 group-data-[swipe-axis=y]/drawer-popup:mx-auto group-data-[swipe-axis=y]/drawer-popup:h-2 group-data-[swipe-axis=y]/drawer-popup:w-[100px] group-data-[swipe-direction=down]/drawer-popup:mt-4 group-data-[swipe-direction=left]/drawer-popup:order-last group-data-[swipe-direction=left]/drawer-popup:mr-4 group-data-[swipe-direction=right]/drawer-popup:ml-4 group-data-[swipe-direction=up]/drawer-popup:order-last group-data-[swipe-direction=up]/drawer-popup:mb-4 active:cursor-grabbing\",\n        className,\n      )}\n      {...props}\n    >\n      <span data-vaul-handle-hitarea=\"\" aria-hidden=\"true\">\n        {children}\n      </span>\n    </div>\n  );\n}\n\ntype PointerDragState = {\n  active: boolean;\n  lastCoordinate: number;\n  lastTime: number;\n  pointerId: number;\n  pointerType: string;\n  size: number;\n  startCoordinate: number;\n  startCrossCoordinate: number;\n};\n\nfunction getDismissCoordinate(\n  event: React.PointerEvent<HTMLDivElement>,\n  swipeDirection: NonNullable<DrawerPrimitive.Root.Props[\"swipeDirection\"]>,\n) {\n  if (swipeDirection === \"down\") {\n    return event.clientY;\n  }\n\n  if (swipeDirection === \"up\") {\n    return -event.clientY;\n  }\n\n  if (swipeDirection === \"right\") {\n    return event.clientX;\n  }\n\n  return -event.clientX;\n}\n\nfunction getCrossCoordinate(\n  event: React.PointerEvent<HTMLDivElement>,\n  swipeDirection: NonNullable<DrawerPrimitive.Root.Props[\"swipeDirection\"]>,\n) {\n  return swipeDirection === \"down\" || swipeDirection === \"up\" ? event.clientX : event.clientY;\n}\n\nfunction isSwipeHandleTarget(target: EventTarget | null) {\n  return target instanceof Element && target.closest('[data-slot=\"drawer-swipe-handle\"]') !== null;\n}\n\nfunction shouldTrackPointerDrag(\n  event: React.PointerEvent<HTMLDivElement>,\n  handleOnly: boolean,\n  lastScrollTime: number,\n  scrollLockTimeout: number,\n) {\n  if (event.button !== 0) {\n    return false;\n  }\n\n  if (handleOnly) {\n    return isSwipeHandleTarget(event.target);\n  }\n\n  if (!(event.target instanceof Element)) {\n    return false;\n  }\n\n  if (event.target.closest(\"[data-base-ui-swipe-ignore]\")) {\n    return false;\n  }\n\n  if (event.timeStamp - lastScrollTime < scrollLockTimeout) {\n    return false;\n  }\n\n  if (\n    event.pointerType !== \"touch\" &&\n    event.target.closest(\n      '[data-drawer-content],button,a,input,select,textarea,label,[role=\"button\"]',\n    )\n  ) {\n    return false;\n  }\n\n  return true;\n}\n\nfunction getDrawerSize(\n  viewport: HTMLDivElement,\n  swipeDirection: NonNullable<DrawerPrimitive.Root.Props[\"swipeDirection\"]>,\n) {\n  const popup = viewport.querySelector<HTMLElement>('[data-slot=\"drawer-content\"]');\n\n  if (!popup) {\n    return 1;\n  }\n\n  return swipeDirection === \"down\" || swipeDirection === \"up\"\n    ? Math.max(popup.offsetHeight, 1)\n    : Math.max(popup.offsetWidth, 1);\n}\n\nfunction DrawerContent({\n  asChild = false,\n  className,\n  children,\n  finalFocus,\n  forceMount,\n  initialFocus,\n  onCloseAutoFocus,\n  onEscapeKeyDown,\n  onFocusOutside,\n  onInteractOutside,\n  onOpenAutoFocus,\n  onPointerDown,\n  onPointerDownOutside,\n  onTouchStart,\n  render,\n  style,\n  ...props\n}: DrawerContentProps) {\n  const {\n    actionsRef,\n    autoFocus,\n    closeThreshold,\n    container,\n    direction,\n    dismissible,\n    dismissableLayerHandlersRef,\n    dragMetricsRef,\n    handleOnly,\n    hasSnapPoints,\n    lastScrollTimeRef,\n    modal,\n    onDrag,\n    openRef,\n    popupRef,\n    scheduleRelease,\n    scrollLockTimeout,\n    showSwipeHandle,\n    snapPointOffset,\n    swipeDirection,\n  } = useDrawer();\n  const swipeAxis = swipeDirection === \"down\" || swipeDirection === \"up\" ? \"y\" : \"x\";\n  const dragStateRef = React.useRef<PointerDragState | null>(null);\n  const [delayedSnapPoints, setDelayedSnapPoints] = React.useState(false);\n  const dismissableLayerHandlers = React.useMemo(\n    () => ({ onEscapeKeyDown, onFocusOutside, onInteractOutside, onPointerDownOutside }),\n    [onEscapeKeyDown, onFocusOutside, onInteractOutside, onPointerDownOutside],\n  );\n  dismissableLayerHandlersRef.current = dismissableLayerHandlers;\n\n  React.useEffect(() => {\n    if (!hasSnapPoints) {\n      setDelayedSnapPoints(false);\n      return undefined;\n    }\n\n    const frame = window.requestAnimationFrame(() => setDelayedSnapPoints(true));\n    return () => window.cancelAnimationFrame(frame);\n  }, [hasSnapPoints]);\n\n  React.useEffect(\n    () => () => {\n      if (dismissableLayerHandlersRef.current === dismissableLayerHandlers) {\n        dismissableLayerHandlersRef.current = {};\n      }\n    },\n    [dismissableLayerHandlers, dismissableLayerHandlersRef],\n  );\n\n  const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n    if (!shouldTrackPointerDrag(event, handleOnly, lastScrollTimeRef.current, scrollLockTimeout)) {\n      return;\n    }\n\n    const coordinate = getDismissCoordinate(event, swipeDirection);\n    dragStateRef.current = {\n      active: false,\n      lastCoordinate: coordinate,\n      lastTime: event.timeStamp,\n      pointerId: event.pointerId,\n      pointerType: event.pointerType,\n      size: getDrawerSize(event.currentTarget, swipeDirection),\n      startCoordinate: coordinate,\n      startCrossCoordinate: getCrossCoordinate(event, swipeDirection),\n    };\n    dragMetricsRef.current = {\n      active: false,\n      event,\n      percentage: 0,\n      pointerType: event.pointerType,\n      releaseScheduled: false,\n      velocity: 0,\n    };\n  };\n\n  const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n    const dragState = dragStateRef.current;\n\n    if (!dragState || dragState.pointerId !== event.pointerId) {\n      return;\n    }\n\n    const coordinate = getDismissCoordinate(event, swipeDirection);\n    const displacement = coordinate - dragState.startCoordinate;\n    const crossDisplacement = Math.abs(\n      getCrossCoordinate(event, swipeDirection) - dragState.startCrossCoordinate,\n    );\n    const activationThreshold = event.pointerType === \"touch\" ? 10 : 2;\n\n    if (!dragState.active) {\n      if (\n        displacement < 0 ||\n        (crossDisplacement > activationThreshold && crossDisplacement > displacement)\n      ) {\n        dragStateRef.current = null;\n        return;\n      }\n\n      if (displacement < activationThreshold || displacement < crossDisplacement) {\n        return;\n      }\n\n      dragState.active = true;\n    }\n\n    const elapsed = Math.max(event.timeStamp - dragState.lastTime, 1);\n    const percentage = Math.max(0, displacement / dragState.size);\n    const velocity = Math.max(0, (coordinate - dragState.lastCoordinate) / elapsed);\n    dragState.lastCoordinate = coordinate;\n    dragState.lastTime = event.timeStamp;\n    dragMetricsRef.current = {\n      active: true,\n      event,\n      percentage,\n      pointerType: event.pointerType,\n      releaseScheduled: dragMetricsRef.current.releaseScheduled,\n      velocity,\n    };\n    onDrag?.(event, percentage);\n\n    if (event.buttons === 0) {\n      finishPointerDrag(event);\n    }\n  };\n\n  function finishPointerDrag(event: React.PointerEvent<HTMLDivElement>, canceled = false) {\n    const dragState = dragStateRef.current;\n\n    if (!dragState || dragState.pointerId !== event.pointerId) {\n      return;\n    }\n\n    if (canceled || !dragState.active) {\n      dragMetricsRef.current.active = false;\n      dragStateRef.current = null;\n      return;\n    }\n\n    const coordinate = getDismissCoordinate(event, swipeDirection);\n    const elapsed = Math.max(event.timeStamp - dragState.lastTime, 1);\n    const percentage = Math.max(0, (coordinate - dragState.startCoordinate) / dragState.size);\n    const velocity = Math.max(0, (coordinate - dragState.lastCoordinate) / elapsed);\n    dragMetricsRef.current = {\n      active: true,\n      event,\n      percentage,\n      pointerType: event.pointerType,\n      releaseScheduled: dragMetricsRef.current.releaseScheduled,\n      velocity,\n    };\n\n    if (\n      dismissible &&\n      !hasSnapPoints &&\n      !dragMetricsRef.current.releaseScheduled &&\n      openRef.current &&\n      (percentage >= closeThreshold || velocity > 0.4)\n    ) {\n      actionsRef.current?.close();\n    }\n\n    if (!dragMetricsRef.current.releaseScheduled) {\n      dragMetricsRef.current.releaseScheduled = true;\n      scheduleRelease(event);\n    }\n    dragMetricsRef.current.active = false;\n    dragStateRef.current = null;\n  }\n\n  const handlePopupPointerDown: NonNullable<DrawerPrimitive.Popup.Props[\"onPointerDown\"]> = (\n    event,\n  ) => {\n    onPointerDown?.(event);\n    if (handleOnly && !isSwipeHandleTarget(event.target)) {\n      event.stopPropagation();\n    }\n  };\n\n  const handlePopupTouchStart: NonNullable<DrawerPrimitive.Popup.Props[\"onTouchStart\"]> = (\n    event,\n  ) => {\n    onTouchStart?.(event);\n    if (handleOnly && !isSwipeHandleTarget(event.target)) {\n      event.stopPropagation();\n    }\n  };\n\n  const contentStyle: DrawerPrimitive.Popup.Props[\"style\"] =\n    typeof style === \"function\"\n      ? (state) => ({\n          \"--snap-point-height\": snapPointOffset,\n          ...style(state),\n        })\n      : ({ \"--snap-point-height\": snapPointOffset, ...style } as DrawerContentStyle);\n  const contentChild = React.Children.toArray(children).find(\n    React.isValidElement,\n  ) as React.ReactElement<{ children?: React.ReactNode }>;\n  const bodyChildren = asChild ? contentChild.props.children : children;\n  const popupChildren = (\n    <>\n      {showSwipeHandle && <DrawerSwipeHandle />}\n      <DrawerPrimitive.Content\n        data-slot=\"drawer-body\"\n        className={cn(\n          \"flex min-h-0 flex-1 flex-col overflow-hidden overscroll-contain rounded-[inherit] transition-opacity duration-300 ease-[cubic-bezier(0.45,1.005,0,1.005)] select-text group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100 group-data-swiping/drawer-popup:select-none\",\n        )}\n      >\n        {bodyChildren}\n      </DrawerPrimitive.Content>\n    </>\n  );\n  const renderElement = asChild\n    ? React.cloneElement(contentChild, undefined, popupChildren)\n    : render;\n\n  return (\n    <DrawerPortal data-slot=\"drawer-portal\" forceMount={forceMount}>\n      {modal === true && <DrawerOverlay data-snap-points={hasSnapPoints ? \"\" : undefined} />}\n      <DrawerPrimitive.Viewport\n        data-slot=\"drawer-viewport\"\n        data-modal={modal}\n        onScrollCapture={(event) => {\n          lastScrollTimeRef.current = event.timeStamp;\n        }}\n        onPointerCancel={(event) => finishPointerDrag(event, true)}\n        onPointerDown={handlePointerDown}\n        onPointerMove={handlePointerMove}\n        onPointerUp={finishPointerDrag}\n        className=\"pointer-events-none fixed inset-0 z-50 select-none data-[modal=true]:pointer-events-auto\"\n      >\n        <DrawerPrimitive.Popup\n          data-slot=\"drawer-content\"\n          data-state={openRef.current ? \"open\" : \"closed\"}\n          data-swipe-axis={swipeAxis}\n          data-snap-points={hasSnapPoints ? \"\" : undefined}\n          data-vaul-custom-container={container ? \"true\" : \"false\"}\n          data-vaul-delayed-snap-points={delayedSnapPoints ? \"true\" : \"false\"}\n          data-vaul-drawer=\"\"\n          data-vaul-drawer-direction={direction}\n          data-vaul-snap-points={openRef.current && hasSnapPoints ? \"true\" : \"false\"}\n          initialFocus={adaptAutoFocus(\n            onOpenAutoFocus,\n            initialFocus ?? autoFocus,\n            \"focusScope.autoFocusOnMount\",\n          )}\n          finalFocus={adaptAutoFocus(onCloseAutoFocus, finalFocus, \"focusScope.autoFocusOnUnmount\")}\n          onPointerDown={handlePopupPointerDown}\n          onTouchStart={handlePopupTouchStart}\n          ref={popupRef}\n          render={adaptRenderEventHandlers(renderElement)}\n          style={contentStyle}\n          className={cn(\n            // Base.\n            \"group/drawer-popup pointer-events-auto fixed z-50 m-(--drawer-inset,0px) flex h-(--drawer-content-height) max-h-(--drawer-content-max-height,none) min-h-0 w-(--drawer-content-width,auto) transform-[translate3d(var(--translate-x,0px),var(--translate-y,0px),0)_scale(var(--stack-scale))] flex-col border-2 border-border bg-background text-foreground shadow-none transition-[transform,height,opacity,filter] duration-450 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform outline-none select-none [interpolate-size:allow-keywords] data-[swipe-direction=down]:mt-24 data-[swipe-direction=down]:rounded-t-base data-[swipe-direction=left]:rounded-r-base data-[swipe-direction=right]:rounded-l-base data-[swipe-direction=up]:mb-24 data-[swipe-direction=up]:rounded-b-base\",\n            // Nested.\n            \"data-nested-drawer-open:overflow-hidden data-nested-drawer-open:brightness-95\",\n            // Bleed.\n            \"after:pointer-events-none after:absolute after:bg-(--drawer-bleed-background,var(--color-background)) data-[swipe-axis=x]:after:inset-y-0 data-[swipe-axis=x]:after:w-(--bleed) data-[swipe-axis=y]:after:inset-x-0 data-[swipe-axis=y]:after:h-(--bleed) data-[swipe-direction=down]:after:top-full data-[swipe-direction=left]:after:right-full data-[swipe-direction=right]:after:left-full data-[swipe-direction=up]:after:bottom-full\",\n            // Sizing.\n            \"[--drawer-content-height:var(--drawer-height,auto)] data-[swipe-axis=x]:[--drawer-content-width:75%] data-[swipe-axis=y]:[--drawer-content-max-height:80vh] data-[swipe-axis=y]:data-snap-points:[--drawer-content-height:100dvh] data-[swipe-axis=y]:data-snap-points:[--drawer-content-max-height:100dvh] data-[swipe-axis=x]:sm:[--drawer-content-width:24rem]\",\n            // Stack.\n            \"[--bleed:3rem] [--peek:1rem] [--stack-height:var(--drawer-frontmost-height,var(--drawer-height,0px))] [--stack-peek-offset:max(0px,calc((var(--nested-drawers)-var(--stack-progress))*var(--peek)))] [--stack-progress:clamp(0,var(--drawer-swipe-progress),1)] [--stack-scale-base:max(0,calc(1-(var(--nested-drawers)*var(--stack-step))))] [--stack-scale:clamp(0,calc(var(--stack-scale-base)+(var(--stack-step)*var(--stack-progress))),1)] [--stack-shrink:calc(1-var(--stack-scale))] [--stack-step:0.05]\",\n            // Transitions.\n            \"data-ending-style:transform-(--closed-transform) data-ending-style:opacity-[0.9999] data-ending-style:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-nested-drawer-swiping:duration-0 data-ending-style:data-nested-drawer-swiping:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-starting-style:transform-(--closed-transform) data-swiping:duration-0 data-ending-style:data-swiping:duration-[calc(var(--drawer-swipe-strength)*400ms)]\",\n            // Axis: y.\n            \"data-[swipe-axis=y]:inset-x-0 data-[swipe-axis=y]:data-nested-drawer-open:h-(--stack-height)\",\n            // Axis: x.\n            \"data-[swipe-axis=x]:inset-y-0 data-[swipe-axis=x]:flex-row\",\n            // Direction: down.\n            \"data-[swipe-direction=down]:bottom-0 data-[swipe-direction=down]:origin-bottom data-[swipe-direction=down]:[--closed-transform:translate3d(0,calc(100%+var(--drawer-inset,0px)+2px),0)] data-[swipe-direction=down]:[--translate-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y)-var(--stack-peek-offset)-(var(--stack-shrink)*var(--stack-height)))]\",\n            // Direction: up.\n            \"data-[swipe-direction=up]:top-0 data-[swipe-direction=up]:origin-top data-[swipe-direction=up]:[--closed-transform:translate3d(0,calc(-100%-var(--drawer-inset,0px)-2px),0)] data-[swipe-direction=up]:[--translate-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y)+var(--stack-peek-offset)+(var(--stack-shrink)*var(--stack-height)))]\",\n            // Direction: left.\n            \"data-[swipe-direction=left]:left-0 data-[swipe-direction=left]:origin-left data-[swipe-direction=left]:[--closed-transform:translate3d(calc(-100%-var(--drawer-inset,0px)-2px),0,0)] data-[swipe-direction=left]:[--translate-x:calc(var(--drawer-swipe-movement-x)+var(--stack-peek-offset)+(var(--stack-shrink)*100%))]\",\n            // Direction: right.\n            \"data-[swipe-direction=right]:right-0 data-[swipe-direction=right]:origin-right data-[swipe-direction=right]:[--closed-transform:translate3d(calc(100%+var(--drawer-inset,0px)+2px),0,0)] data-[swipe-direction=right]:[--translate-x:calc(var(--drawer-swipe-movement-x)-var(--stack-peek-offset)-(var(--stack-shrink)*100%))]\",\n            className,\n          )}\n          {...props}\n        >\n          {asChild ? undefined : popupChildren}\n        </DrawerPrimitive.Popup>\n      </DrawerPrimitive.Viewport>\n    </DrawerPortal>\n  );\n}\n\nfunction DrawerHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"drawer-header\"\n      className={cn(\"grid shrink-0 gap-1.5 p-4 text-center sm:text-left\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DrawerFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"drawer-footer\"\n      className={cn(\"mt-auto flex shrink-0 flex-col gap-3 p-4\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DrawerTitle({\n  asChild = false,\n  children,\n  className,\n  render,\n  ...props\n}: DrawerPrimitive.Title.Props & { asChild?: boolean; children?: React.ReactNode }) {\n  const renderElement = asChild\n    ? (React.Children.toArray(children).find(React.isValidElement) as React.ReactElement)\n    : render;\n\n  return (\n    <DrawerPrimitive.Title\n      data-slot=\"drawer-title\"\n      render={adaptRenderEventHandlers(renderElement)}\n      className={cn(\"font-heading text-lg leading-none tracking-tight\", className)}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </DrawerPrimitive.Title>\n  );\n}\n\nfunction DrawerDescription({\n  asChild = false,\n  children,\n  className,\n  render,\n  ...props\n}: DrawerPrimitive.Description.Props & { asChild?: boolean; children?: React.ReactNode }) {\n  const renderElement = asChild\n    ? (React.Children.toArray(children).find(React.isValidElement) as React.ReactElement)\n    : render;\n\n  return (\n    <DrawerPrimitive.Description\n      data-slot=\"drawer-description\"\n      render={adaptRenderEventHandlers(renderElement)}\n      className={cn(\"text-sm text-balance font-base text-foreground\", className)}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </DrawerPrimitive.Description>\n  );\n}\n\nexport {\n  Drawer,\n  DrawerPortal,\n  DrawerOverlay,\n  DrawerSwipeHandle,\n  DrawerTrigger,\n  DrawerClose,\n  DrawerContent,\n  DrawerHeader,\n  DrawerFooter,\n  DrawerTitle,\n  DrawerDescription,\n};\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "overlay",
    "dialog"
  ],
  "type": "registry:ui"
}