/** * Context set by `` that carries drawer-specific state on top * of the `` context the drawer composes over. * * Mirrors Base UI React's `DrawerRootContext` * (`packages/react/src/drawer/root/DrawerRootContext.ts`). The Solid port * switches every field to an `Accessor` so consumers can subscribe * without wrapping, and follows the null-sentinel convention for * the optional "is this a drawer subtree?" detection path. */ import { type Accessor, createContext, useContext } from "solid-js"; import type { BaseUIChangeEventDetails, REASONS, } from "../../internals/change-event-details"; /** Swipe-direction union. `'down'` by default. */ export type DrawerSwipeDirection = "up" | "down" | "left" | "right"; /** A single snap point — fraction (0..1), px count, or a CSS length string. */ export type DrawerSnapPoint = number | string; /** * Nested-drawer swipe-progress store. Implemented without Solid's reactive * graph so parent drawers can observe progress without re-running effects. * Mirrors React's same-named shape. */ export interface DrawerNestedSwipeProgressStore { getSnapshot: () => number; subscribe: (listener: () => void) => () => void; } export interface DrawerRootContextValue { /** Dismissal direction. */ swipeDirection: Accessor; /** Whether snap points can be skipped based on swipe velocity. */ snapToSequentialPoints: Accessor; /** Configured snap-point list. */ snapPoints: Accessor; /** Active snap point. */ activeSnapPoint: Accessor; /** Request an open-state change from a drawer-owned interaction. */ setOpen: ( next: boolean, details: BaseUIChangeEventDetails< DrawerRootChangeEventReason, { preventUnmountOnClose: () => void } >, ) => void; /** Write a new active snap point. */ setActiveSnapPoint: ( snapPoint: DrawerSnapPoint | null, details?: DrawerRootSnapPointChangeEventDetails, ) => void; /** Measured frontmost popup height in the current nested stack. */ frontmostHeight: Accessor; /** Measured height of this drawer's popup. */ popupHeight: Accessor; /** Whether a nested drawer is mounted (open or transitioning out). */ hasNestedDrawer: Accessor; /** Whether a nested drawer is currently being swiped. */ nestedSwiping: Accessor; /** Nested swipe progress store (non-reactive). */ nestedSwipeProgressStore: DrawerNestedSwipeProgressStore; /** Popup → root: popup height changed. */ onPopupHeightChange: (height: number) => void; /** Nested → root: frontmost-height changed. */ onNestedFrontmostHeightChange: (height: number) => void; /** Nested → root: nested drawer presence changed. */ onNestedDrawerPresenceChange: (present: boolean) => void; /** Nested → root: nested swiping changed. */ onNestedSwipingChange: (swiping: boolean) => void; /** Nested → root: nested swipe progress changed. */ onNestedSwipeProgressChange: (progress: number) => void; /** Proxy of this root's `onNestedFrontmostHeightChange` for children. */ notifyParentFrontmostHeight?: ((height: number) => void) | undefined; /** Proxy of this root's `onNestedSwipingChange`. */ notifyParentSwipingChange?: ((swiping: boolean) => void) | undefined; /** Proxy of this root's `onNestedSwipeProgressChange`. */ notifyParentSwipeProgressChange?: ((progress: number) => void) | undefined; /** Proxy of this root's `onNestedDrawerPresenceChange`. */ notifyParentHasNestedDrawer?: ((present: boolean) => void) | undefined; } export type DrawerRootChangeEventReason = | typeof REASONS.triggerPress | typeof REASONS.outsidePress | typeof REASONS.escapeKey | typeof REASONS.closeWatcher | typeof REASONS.closePress | typeof REASONS.focusOut | typeof REASONS.imperativeAction | typeof REASONS.swipe | typeof REASONS.none; export type DrawerRootSnapPointChangeEventReason = DrawerRootChangeEventReason; /** Change-event details for `onSnapPointChange`. Matches React. */ export type DrawerRootSnapPointChangeEventDetails = BaseUIChangeEventDetails; /** * Null-sentinel context. `` is optional around * a `` when a detached handle is supplied. */ export const DrawerRootContext = createContext( null, ); export function useDrawerRootContext(): DrawerRootContextValue; export function useDrawerRootContext( optional: true, ): DrawerRootContextValue | null; export function useDrawerRootContext( optional?: boolean, ): DrawerRootContextValue | null { const context = useContext(DrawerRootContext); if (context == null && !optional) { throw new Error( "Base UI: DrawerRootContext is missing. Drawer parts must be placed within .", ); } return context; }