@svnrnns/react-bottom-sheets 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../lib/store/store.ts","../lib/utils/snap.ts","../lib/utils/gestures.ts","../lib/context/scrollContext.tsx","../lib/hooks/useFocusTrap.ts","../lib/components/BottomSheet.tsx","../lib/context/context.tsx","../lib/api.ts","../lib/components/BottomSheetScrollable.tsx"],"sourcesContent":["import type { SheetDescriptor } from '../types';\n\nexport type SheetController = {\n snapToIndex: (index: number) => void;\n openFully: () => void;\n close: () => void;\n};\n\nlet idCounter = 0;\nfunction nextId(): symbol {\n return Symbol(`bottom-sheet-${++idCounter}`);\n}\n\nlet sheets: Array<SheetDescriptor & { id: symbol }> = [];\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n listeners.forEach((l) => l());\n}\n\nexport function getSheets(): ReadonlyArray<SheetDescriptor & { id: symbol }> {\n return sheets;\n}\n\nexport function subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function addSheet(descriptor: Omit<SheetDescriptor, 'id'>): symbol {\n const id = nextId();\n const entry = { ...descriptor, id } as SheetDescriptor & { id: symbol };\n sheets = [...sheets, entry];\n emit();\n return id;\n}\n\nexport function removeSheet(id: string | symbol): void {\n sheets = sheets.filter((s) => s.id !== id);\n emit();\n}\n\nexport function removeAllSheets(): void {\n sheets = [];\n emit();\n}\n\nconst sheetControllers = new Map<symbol | string, SheetController>();\nconst pendingSnapIndex = new Map<symbol | string, number>();\nconst pendingOpenFully = new Set<symbol | string>();\n\nexport function registerSheetController(id: symbol | string, controller: SheetController): void {\n sheetControllers.set(id, controller);\n const snap = pendingSnapIndex.get(id);\n if (snap !== undefined) {\n pendingSnapIndex.delete(id);\n controller.snapToIndex(snap);\n }\n if (pendingOpenFully.has(id)) {\n pendingOpenFully.delete(id);\n controller.openFully();\n }\n}\n\nexport function unregisterSheetController(id: symbol | string): void {\n sheetControllers.delete(id);\n}\n\nexport function getSheetController(id: symbol | string): SheetController | undefined {\n return sheetControllers.get(id);\n}\n\nexport function invokeSnapToIndex(id: symbol | string, index: number): void {\n const ctrl = sheetControllers.get(id);\n if (ctrl) ctrl.snapToIndex(index);\n else pendingSnapIndex.set(id, index);\n}\n\nexport function invokeOpenFully(id: symbol | string): void {\n const ctrl = sheetControllers.get(id);\n if (ctrl) ctrl.openFully();\n else pendingOpenFully.add(id);\n}\n","/**\n * Parse a single snap point string to pixels relative to viewport height.\n * Valid: \"50%\", \"100px\", \"20rem\", \"50\" (treated as px).\n * Invalid entries return null.\n */\nfunction parseSnapValue(value: string, viewportHeightPx: number, remPx: number): number | null {\n const trimmed = value.trim();\n if (!trimmed) return null;\n\n if (trimmed.endsWith('%')) {\n const num = parseFloat(trimmed.slice(0, -1));\n if (Number.isNaN(num) || num < 0 || num > 100) return null;\n return (num / 100) * viewportHeightPx;\n }\n\n if (trimmed.endsWith('px')) {\n const num = parseFloat(trimmed.slice(0, -2));\n if (Number.isNaN(num)) return null;\n return num;\n }\n\n if (trimmed.endsWith('rem')) {\n const num = parseFloat(trimmed.slice(0, -3));\n if (Number.isNaN(num)) return null;\n return num * remPx;\n }\n\n const asNumber = parseFloat(trimmed);\n if (!Number.isNaN(asNumber)) return asNumber;\n return null;\n}\n\nfunction getRemPx(): number {\n if (typeof document === 'undefined') return 16;\n const root = document.documentElement;\n const fontSize = getComputedStyle(root).fontSize;\n const rem = parseFloat(fontSize);\n return Number.isNaN(rem) ? 16 : rem;\n}\n\n/**\n * Parse snap point strings to an array of heights in pixels (from bottom of viewport).\n * Sorted ascending (smallest = most closed, largest = most open).\n * viewportHeightPx: window.innerHeight or equivalent.\n */\nexport function parseSnapPoints(\n snapPoint: string[] | undefined,\n viewportHeightPx: number\n): number[] {\n if (!snapPoint?.length) return [];\n const remPx = getRemPx();\n const parsed = snapPoint\n .map((s) => parseSnapValue(s, viewportHeightPx, remPx))\n .filter((v): v is number => v !== null && v >= 0);\n const unique = [...new Set(parsed)];\n unique.sort((a, b) => a - b);\n return unique;\n}\n\n/**\n * Clamp value between min and max. For rubberband, we allow overscroll with resistance.\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nconst RUBBERBAND_SCALE_PX = 80;\nconst RUBBERBAND_FACTOR_MAX = 0.35;\nconst RUBBERBAND_FACTOR_MIN = 0.08;\n\n/**\n * Resistance factor that decreases as overscroll (over) increases, approaching min but never 0.\n */\nfunction rubberbandFactor(over: number): number {\n return (\n RUBBERBAND_FACTOR_MIN +\n (RUBBERBAND_FACTOR_MAX - RUBBERBAND_FACTOR_MIN) / (1 + over / RUBBERBAND_SCALE_PX)\n );\n}\n\n/**\n * Apply rubberband when beyond bounds. Resistance is incremental: the more you overscroll,\n * the stronger the resistance (smaller movement per pixel), approaching a minimum factor but never zero.\n * Used when swiping past the last (highest) snap point, or past max height when there are no snap points.\n */\nexport function rubberband(value: number, min: number, max: number): number {\n if (value >= min && value <= max) return value;\n if (value < min) {\n const over = min - value;\n const factor = rubberbandFactor(over);\n return min - over * factor;\n }\n const over = value - max;\n const factor = rubberbandFactor(over);\n return max + over * factor;\n}\n","/**\n * Velocity threshold (px/ms) above which we consider it a \"fast swipe\" for closing or snapping.\n */\nexport const VELOCITY_THRESHOLD = 0.5;\n\n/**\n * Fraction of travel (0..1) toward closed position. If past this and velocity is down, can close.\n */\nexport const CLOSE_THRESHOLD = 0.6;\n\n/**\n * Fraction of travel (0..1) between two snap points. If past 50%, go to next snap.\n */\nexport const SNAP_PROGRESS_THRESHOLD = 0.5;\n\nexport interface ReleaseResult {\n action: 'close' | 'snap';\n targetIndex?: number;\n targetY?: number;\n}\n\n/**\n * Given current position (translateY, 0 = top), sheet height, snap points in px (heights from bottom),\n * and velocity (positive = downward), decide whether to close or snap to an index.\n * Snap points are ordered ascending (smallest = most closed). Position is \"how much the sheet is down\"\n * so 0 = fully open (sheet top at viewport top), sheetHeight = fully closed.\n */\nexport function getReleaseTarget(\n currentTranslateY: number,\n sheetHeight: number,\n snapPointsPx: number[],\n velocityY: number,\n closeOffset: number = 0\n): ReleaseResult {\n const closedPosition = sheetHeight + closeOffset;\n\n if (snapPointsPx.length === 0) {\n if (currentTranslateY >= closedPosition * CLOSE_THRESHOLD) {\n return { action: 'close' };\n }\n return { action: 'snap', targetY: 0 };\n }\n\n const sortedSnaps = [...snapPointsPx].sort((a, b) => a - b);\n const firstSnap = sortedSnaps[0];\n const firstSnapTranslateY = sheetHeight - firstSnap;\n const closedPos = sheetHeight + closeOffset;\n const closeZoneStart = firstSnapTranslateY;\n const closeZoneLength = closedPos - closeZoneStart;\n const closeThresholdY = closeZoneStart + CLOSE_THRESHOLD * closeZoneLength;\n\n if (velocityY > VELOCITY_THRESHOLD) {\n if (currentTranslateY >= closeThresholdY) {\n return { action: 'close' };\n }\n const nextSnapDown = findNextSnapDown(currentTranslateY, sortedSnaps, sheetHeight);\n if (nextSnapDown !== undefined) {\n const targetY = sheetHeight - nextSnapDown;\n return { action: 'snap', targetY, targetIndex: snapPointsPx.indexOf(nextSnapDown) };\n }\n }\n\n if (velocityY < -VELOCITY_THRESHOLD) {\n const nextSnapUp = findNextSnapUp(currentTranslateY, sortedSnaps, sheetHeight);\n if (nextSnapUp !== undefined) {\n const targetY = sheetHeight - nextSnapUp;\n const idx = sortedSnaps.indexOf(nextSnapUp);\n return { action: 'snap', targetY, targetIndex: idx };\n }\n }\n\n const currentSnapIndex = findCurrentSnapIndex(currentTranslateY, sortedSnaps, sheetHeight);\n const progress = getProgressToNextSnap(currentTranslateY, currentSnapIndex, sortedSnaps, sheetHeight);\n\n if (progress >= SNAP_PROGRESS_THRESHOLD && currentSnapIndex < sortedSnaps.length - 1) {\n const nextSnap = sortedSnaps[currentSnapIndex + 1];\n return { action: 'snap', targetY: sheetHeight - nextSnap, targetIndex: currentSnapIndex + 1 };\n }\n\n if (currentSnapIndex > 0 || currentTranslateY < closeZoneStart) {\n const snap = sortedSnaps[currentSnapIndex];\n return { action: 'snap', targetY: sheetHeight - snap, targetIndex: currentSnapIndex };\n }\n\n if (currentTranslateY >= closeThresholdY) {\n return { action: 'close' };\n }\n\n return { action: 'snap', targetY: firstSnapTranslateY, targetIndex: 0 };\n}\n\nfunction findNextSnapDown(\n currentY: number,\n sortedSnaps: number[],\n sheetHeight: number\n): number | undefined {\n const currentVisible = sheetHeight - currentY;\n for (let i = sortedSnaps.length - 1; i >= 0; i--) {\n if (sortedSnaps[i] < currentVisible) return sortedSnaps[i];\n }\n return undefined;\n}\n\nfunction findNextSnapUp(\n currentY: number,\n sortedSnaps: number[],\n sheetHeight: number\n): number | undefined {\n const currentVisible = sheetHeight - currentY;\n for (let i = 0; i < sortedSnaps.length; i++) {\n if (sortedSnaps[i] > currentVisible) return sortedSnaps[i];\n }\n return undefined;\n}\n\nfunction findCurrentSnapIndex(\n currentY: number,\n sortedSnaps: number[],\n sheetHeight: number\n): number {\n const currentVisible = sheetHeight - currentY;\n let best = 0;\n let bestDist = Infinity;\n for (let i = 0; i < sortedSnaps.length; i++) {\n const d = Math.abs(sortedSnaps[i] - currentVisible);\n if (d < bestDist) {\n bestDist = d;\n best = i;\n }\n }\n return best;\n}\n\nfunction getProgressToNextSnap(\n currentY: number,\n currentSnapIndex: number,\n sortedSnaps: number[],\n sheetHeight: number\n): number {\n if (currentSnapIndex >= sortedSnaps.length - 1) return 0;\n const currentVisible = sheetHeight - currentY;\n const fromSnap = sortedSnaps[currentSnapIndex];\n const toSnap = sortedSnaps[currentSnapIndex + 1];\n const segment = toSnap - fromSnap;\n if (segment <= 0) return 0;\n const progress = (currentVisible - fromSnap) / segment;\n return Math.max(0, Math.min(1, progress));\n}\n","import { createContext, useCallback, useRef, useContext } from 'react';\n\nexport interface ScrollContainerContextValue {\n /** Register a scroll container. Returns unregister function. */\n registerScrollContainer: (el: HTMLElement, getCanDrag: () => boolean) => () => void;\n /** True if target is inside any registered scroll container. */\n isInScrollContainer: (target: Node) => boolean;\n /**\n * Returns true if sheet gestures should be blocked for this target.\n * (e.g. when inside a scrollable that is not at top)\n */\n shouldBlockGestures: (target: Node) => boolean;\n /**\n * For targets inside a scroll container at top: can we capture for a downward swipe?\n * (scrollTop === 0)\n */\n canCaptureForDownSwipe: (target: Node) => boolean;\n}\n\nexport const ScrollContainerContext = createContext<ScrollContainerContextValue | null>(null);\n\nexport function useScrollContainerContext(): ScrollContainerContextValue | null {\n return useContext(ScrollContainerContext);\n}\n\nfunction findScrollContainer(\n target: Node,\n containers: Map<HTMLElement, () => boolean>\n): { el: HTMLElement; getCanDrag: () => boolean } | null {\n let node: Node | null = target;\n while (node && node !== document.body) {\n if (node instanceof HTMLElement && containers.has(node)) {\n return { el: node, getCanDrag: containers.get(node)! };\n }\n node = node.parentNode;\n }\n return null;\n}\n\nexport function useScrollContainerContextValue(): ScrollContainerContextValue {\n const containersRef = useRef<Map<HTMLElement, () => boolean>>(new Map());\n\n const registerScrollContainer = useCallback((el: HTMLElement, getCanDrag: () => boolean) => {\n containersRef.current.set(el, getCanDrag);\n return () => {\n containersRef.current.delete(el);\n };\n }, []);\n\n const isInScrollContainer = useCallback((target: Node): boolean => {\n return findScrollContainer(target, containersRef.current) !== null;\n }, []);\n\n const shouldBlockGestures = useCallback((target: Node): boolean => {\n const entry = findScrollContainer(target, containersRef.current);\n if (!entry) return false;\n return !entry.getCanDrag();\n }, []);\n\n const canCaptureForDownSwipe = useCallback((target: Node): boolean => {\n const entry = findScrollContainer(target, containersRef.current);\n if (!entry) return false;\n return entry.getCanDrag();\n }, []);\n\n return {\n registerScrollContainer,\n isInScrollContainer,\n shouldBlockGestures,\n canCaptureForDownSwipe,\n };\n}\n","import { useEffect, type RefObject } from \"react\";\n\nconst FOCUSABLE_SELECTOR =\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])';\n\nconst CLOSE_BUTTON_CLASS = \"modals-close\";\n\nfunction getFocusableElements(container: HTMLElement): HTMLElement[] {\n return Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));\n}\n\nfunction getFocusableExcludingClose(container: HTMLElement): HTMLElement[] {\n return getFocusableElements(container).filter(\n (el) => !el.classList.contains(CLOSE_BUTTON_CLASS)\n );\n}\n\nexport function useFocusTrap(\n containerRef: RefObject<HTMLElement | null>,\n enabled: boolean,\n disableAutoFocus = false\n): void {\n useEffect(() => {\n if (!enabled || !containerRef.current) return;\n const container = containerRef.current;\n const previouslyFocused = document.activeElement as HTMLElement | null;\n\n if (!disableAutoFocus) {\n const focusableExcludingClose = getFocusableExcludingClose(container);\n const focusable = getFocusableElements(container);\n const first =\n focusableExcludingClose[0] ?? focusable[0];\n\n if (first) {\n first.focus();\n } else {\n container.setAttribute(\"tabindex\", \"-1\");\n container.focus();\n }\n }\n\n function handleKeyDown(e: KeyboardEvent) {\n if (e.key !== \"Tab\") return;\n const focusableNow = getFocusableElements(container);\n const firstNow = focusableNow[0];\n const lastNow = focusableNow[focusableNow.length - 1];\n if (e.shiftKey) {\n if (document.activeElement === firstNow && lastNow) {\n e.preventDefault();\n lastNow.focus();\n }\n } else {\n if (document.activeElement === lastNow && firstNow) {\n e.preventDefault();\n firstNow.focus();\n }\n }\n }\n\n container.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n container.removeEventListener(\"keydown\", handleKeyDown);\n if (previouslyFocused && typeof previouslyFocused.focus === \"function\") {\n previouslyFocused.focus();\n }\n };\n // containerRef is intentionally omitted - refs are mutable and should not be in deps\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, disableAutoFocus]);\n}\n","import {\n useRef,\n useState,\n useEffect,\n useCallback,\n useLayoutEffect,\n useContext,\n} from 'react';\nimport { flushSync } from 'react-dom';\nimport type { SheetDescriptor } from '../types';\nimport { parseSnapPoints, rubberband } from '../utils/snap';\nimport { getReleaseTarget } from '../utils/gestures';\nimport { removeSheet } from '../store/store';\nimport { BottomSheetContext } from '../context/context';\nimport { ScrollContainerContext, useScrollContainerContextValue } from '../context/scrollContext';\nimport { useFocusTrap } from '../hooks/useFocusTrap';\n\nconst VIEWPORT_MAX = typeof window !== 'undefined' ? () => window.innerHeight : () => 800;\nconst DRAG_THRESHOLD = 5;\n\nfunction getCssVar(name: string, fallback: string): string {\n if (typeof document === 'undefined') return fallback;\n const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();\n return value || fallback;\n}\n\nfunction getDurationMs(): number {\n const s = getCssVar('--bottom-sheet-duration', '0.5s');\n const match = s.match(/^([\\d.]+)s$/);\n if (match) return parseFloat(match[1]) * 1000;\n const matchMs = s.match(/^([\\d.]+)ms$/);\n if (matchMs) return parseFloat(matchMs[1]);\n return 500;\n}\n\nfunction getEaseCubicBezierY(t: number, y1: number, y2: number): number {\n const t2 = 1 - t;\n return 3 * t2 * t2 * t * y1 + 3 * t2 * t * t * y2 + t * t * t;\n}\n\nfunction getEasedProgress(progress: number, easingVar: string): number {\n const match = easingVar.match(/cubic-bezier\\s*\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*\\)/);\n if (!match) return progress;\n const values = match.map(Number);\n const y1 = values[2];\n const y2 = values[4];\n return getEaseCubicBezierY(progress, y1, y2);\n}\n\nfunction scaleForStackDepth(d: number): number {\n return d === 0 ? 1 : Math.max(0.5, 0.9 - (d - 1) * 0.05);\n}\n\nfunction offsetForStackDepth(d: number): number {\n return d === 0 ? 0 : 12 + (d - 1) * 8;\n}\n\nexport interface BottomSheetProps {\n descriptor: SheetDescriptor & { id: symbol };\n index: number;\n isTop: boolean;\n /** 0 = top sheet, 1 = first behind, 2 = second behind, etc. */\n stackDepth: number;\n}\n\nexport function BottomSheet({ descriptor, index, isTop, stackDepth }: BottomSheetProps) {\n const ctx = useContext(BottomSheetContext);\n const {\n id,\n component: Component,\n props: componentProps,\n height: heightProp,\n width: widthProp,\n snapPoint,\n className,\n onClose,\n enableBackdrop,\n enableClickBackdropToClose,\n disableEsc,\n gestureOnlyOnHandler,\n disableSwipeDownToClose,\n } = descriptor;\n\n const effectiveWidth = widthProp ?? ctx?.defaultWidth;\n const widthCss =\n effectiveWidth == null ? undefined : typeof effectiveWidth === 'number' ? `${effectiveWidth}px` : effectiveWidth;\n\n const viewportHeight = VIEWPORT_MAX();\n const closeOffset =\n typeof document !== 'undefined'\n ? parseFloat(getCssVar('--bottom-sheet-close-extra-offset', '0')) || 0\n : 0;\n\n const hasSnapPoints = Boolean(snapPoint?.length);\n const contentDrivenHeight = heightProp == null && !hasSnapPoints;\n\n const sheetRef = useRef<HTMLDivElement>(null);\n const handlerRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const scrollCtxValue = useScrollContainerContextValue();\n const [sheetHeight, setSheetHeight] = useState(() => {\n if (heightProp != null) return parseHeight(heightProp);\n if (contentDrivenHeight) return 0;\n return viewportHeight;\n });\n const [contentHeightMeasured, setContentHeightMeasured] = useState(!contentDrivenHeight);\n const closedY = sheetHeight + closeOffset;\n const snapPointsPx = parseSnapPoints(snapPoint, viewportHeight);\n const effectiveSnaps = snapPointsPx\n .filter((s) => s <= sheetHeight)\n .sort((a, b) => a - b);\n const firstSnapY = effectiveSnaps.length > 0 ? sheetHeight - effectiveSnaps[0] : 0;\n const [translateY, setTranslateY] = useState(() =>\n contentDrivenHeight ? viewportHeight : effectiveSnaps.length > 0 ? closedY : 0\n );\n const [isDragging, setIsDragging] = useState(false);\n const [isAnimating, setIsAnimating] = useState(false);\n const [hasOpened, setHasOpened] = useState(false);\n const [backdropOpacity, setBackdropOpacity] = useState(0);\n const dragStartRef = useRef<{ y: number; sheetY: number; time: number } | null>(null);\n const lastMoveRef = useRef<{ y: number; time: number } | null>(null);\n const hasCapturedRef = useRef(false);\n const heightAnimationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useLayoutEffect(() => {\n if (effectiveSnaps.length > 0) setTranslateY(closedY);\n }, []);\n\n useEffect(() => {\n if (heightProp != null) {\n setSheetHeight(parseHeight(heightProp));\n setContentHeightMeasured(true);\n return;\n }\n if (!contentDrivenHeight) {\n const el = contentRef.current;\n if (!el) return;\n const ro = new ResizeObserver(() => {\n const h = el.getBoundingClientRect().height;\n setSheetHeight((prev) => Math.min(Math.max(prev, h), viewportHeight));\n });\n ro.observe(el);\n const h = el.getBoundingClientRect().height;\n setSheetHeight((prev) => Math.min(Math.max(prev, h), viewportHeight));\n return () => ro.disconnect();\n }\n const el = sheetRef.current;\n if (!el) return;\n const updateHeight = () => {\n const sheet = sheetRef.current;\n const handler = handlerRef.current;\n const content = contentRef.current;\n if (!sheet || !handler || !content) return;\n if (sheetHeight === 0) {\n const h = Math.min(sheet.getBoundingClientRect().height, viewportHeight);\n if (h > 0) {\n setSheetHeight(h);\n setContentHeightMeasured(true);\n }\n return;\n }\n const style = getComputedStyle(sheet);\n const paddingTop = parseFloat(style.paddingTop) || 0;\n const paddingBottom = parseFloat(style.paddingBottom) || 0;\n const gapStr = style.gap || '0.5rem';\n const gapPx = gapStr.endsWith('rem')\n ? parseFloat(gapStr) * (parseFloat(getComputedStyle(document.documentElement).fontSize) || 16)\n : parseFloat(gapStr) || 8;\n const contentEl = content.firstElementChild;\n const contentHeight = contentEl\n ? contentEl.getBoundingClientRect().height\n : content.scrollHeight || content.getBoundingClientRect().height;\n const h = Math.min(\n paddingTop + paddingBottom + handler.offsetHeight + gapPx + contentHeight,\n viewportHeight\n );\n if (h > 0) {\n if (heightAnimationTimeoutRef.current) {\n clearTimeout(heightAnimationTimeoutRef.current);\n heightAnimationTimeoutRef.current = null;\n }\n setSheetHeight(h);\n setIsAnimating(true);\n heightAnimationTimeoutRef.current = setTimeout(() => {\n heightAnimationTimeoutRef.current = null;\n setIsAnimating(false);\n }, getDurationMs());\n }\n };\n const ro = new ResizeObserver(updateHeight);\n if (contentRef.current) ro.observe(contentRef.current);\n const firstChild = contentRef.current?.firstElementChild;\n if (firstChild) ro.observe(firstChild);\n if (handlerRef.current) ro.observe(handlerRef.current);\n ro.observe(sheetRef.current);\n requestAnimationFrame(updateHeight);\n return () => {\n ro.disconnect();\n if (heightAnimationTimeoutRef.current) {\n clearTimeout(heightAnimationTimeoutRef.current);\n heightAnimationTimeoutRef.current = null;\n }\n };\n }, [heightProp, viewportHeight, contentDrivenHeight, sheetHeight]);\n\n useEffect(() => {\n if (hasOpened) return;\n if (contentDrivenHeight && !contentHeightMeasured) return;\n setHasOpened(true);\n setTranslateY(closedY);\n requestAnimationFrame(() => {\n setBackdropOpacity(1);\n setIsAnimating(true);\n setTranslateY(firstSnapY);\n const duration = getDurationMs();\n setTimeout(() => setIsAnimating(false), duration);\n });\n }, [hasOpened, contentDrivenHeight, contentHeightMeasured, firstSnapY, closedY, sheetHeight]);\n\n const closeDrawer = useCallback(() => {\n if (isAnimating) return;\n setBackdropOpacity(0);\n setIsAnimating(true);\n const duration = getDurationMs();\n setTranslateY(sheetHeight + closeOffset);\n if (isTop && ctx) {\n ctx.setTopSheetClosingProgress(0);\n const startTime = Date.now();\n const tick = () => {\n const elapsed = Date.now() - startTime;\n const progress = Math.min(1, elapsed / duration);\n ctx?.setTopSheetClosingProgress(progress);\n if (progress < 1) {\n requestAnimationFrame(tick);\n } else {\n onClose?.();\n removeSheet(id);\n ctx?.setTopSheetClosingProgress(null);\n setIsAnimating(false);\n }\n };\n requestAnimationFrame(tick);\n } else {\n const t = setTimeout(() => {\n onClose?.();\n removeSheet(id);\n setIsAnimating(false);\n }, duration);\n return () => clearTimeout(t);\n }\n }, [id, sheetHeight, closeOffset, onClose, isAnimating, isTop, ctx]);\n\n const snapToIndex = useCallback(\n (i: number) => {\n if (isAnimating) return;\n if (effectiveSnaps.length === 0) {\n setTranslateY(0);\n return;\n }\n const idx = Math.max(0, Math.min(i, effectiveSnaps.length - 1));\n const targetY = sheetHeight - effectiveSnaps[idx];\n setIsAnimating(true);\n setTranslateY(targetY);\n const duration = getDurationMs();\n const t = setTimeout(() => setIsAnimating(false), duration);\n return () => clearTimeout(t);\n },\n [effectiveSnaps, sheetHeight, isAnimating]\n );\n\n const openFully = useCallback(() => {\n if (effectiveSnaps.length > 0) {\n snapToIndex(effectiveSnaps.length - 1);\n } else {\n setIsAnimating(true);\n setTranslateY(0);\n const t = setTimeout(() => setIsAnimating(false), getDurationMs());\n return () => clearTimeout(t);\n }\n }, [effectiveSnaps.length, snapToIndex]);\n\n useLayoutEffect(() => {\n if (!ctx) return;\n ctx.registerController(id, { snapToIndex, openFully, close: closeDrawer });\n return () => ctx.unregisterController(id);\n }, [id, ctx, snapToIndex, openFully, closeDrawer]);\n\n useEffect(() => {\n if (!isTop) return;\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && !disableEsc) {\n e.preventDefault();\n flushSync(() => {\n setIsDragging(false);\n setIsAnimating(false);\n });\n dragStartRef.current = null;\n lastMoveRef.current = null;\n closeDrawer();\n }\n };\n window.addEventListener('keydown', onKeyDown);\n return () => window.removeEventListener('keydown', onKeyDown);\n }, [isTop, disableEsc, closeDrawer]);\n\n useFocusTrap(sheetRef, isTop && hasOpened);\n\n const cleanupDragState = useCallback((pointerId?: number) => {\n if (hasCapturedRef.current && sheetRef.current && pointerId != null) {\n try {\n sheetRef.current.releasePointerCapture(pointerId);\n } catch {\n /* ignore if no capture */\n }\n setIsDragging(false);\n }\n dragStartRef.current = null;\n lastMoveRef.current = null;\n hasCapturedRef.current = false;\n }, []);\n\n const handlePointerDownCapture = useCallback(\n (e: React.PointerEvent) => {\n if (e.button !== 0) return;\n const isTouch = e.pointerType === 'touch';\n const isInContent = contentRef.current?.contains(e.target as Node) ?? false;\n if (isTouch && isInContent) {\n // When inside a scroll container, defer capture until we know swipe direction (see handlePointerMove)\n if (scrollCtxValue.isInScrollContainer(e.target as Node)) {\n return;\n }\n e.currentTarget.setPointerCapture(e.pointerId);\n hasCapturedRef.current = true;\n setIsDragging(true);\n }\n },\n [scrollCtxValue]\n );\n\n const handlePointerDown = useCallback(\n (e: React.PointerEvent) => {\n if (e.button !== 0) return;\n const pointerId = e.pointerId;\n dragStartRef.current = { y: e.clientY, sheetY: translateY, time: Date.now() };\n lastMoveRef.current = { y: e.clientY, time: Date.now() };\n const onPointerUpGlobal = (ev: PointerEvent) => {\n if (ev.pointerId !== pointerId) return;\n window.removeEventListener('pointerup', onPointerUpGlobal);\n window.removeEventListener('pointercancel', onPointerUpGlobal);\n cleanupDragState(pointerId);\n };\n window.addEventListener('pointerup', onPointerUpGlobal);\n window.addEventListener('pointercancel', onPointerUpGlobal);\n },\n [translateY, cleanupDragState]\n );\n\n const handlePointerMove = useCallback(\n (e: React.PointerEvent) => {\n if (!dragStartRef.current) return;\n if (e.buttons === 0) {\n if (hasCapturedRef.current) {\n e.currentTarget.releasePointerCapture(e.pointerId);\n setIsDragging(false);\n }\n dragStartRef.current = null;\n lastMoveRef.current = null;\n hasCapturedRef.current = false;\n return;\n }\n lastMoveRef.current = { y: e.clientY, time: Date.now() };\n const delta = e.clientY - dragStartRef.current.y;\n const threshold = e.pointerType === 'touch' ? 1 : DRAG_THRESHOLD;\n if (!hasCapturedRef.current && Math.abs(delta) > threshold) {\n const target = e.target as Node;\n if (scrollCtxValue.isInScrollContainer(target)) {\n if (scrollCtxValue.shouldBlockGestures(target)) return;\n if (!scrollCtxValue.canCaptureForDownSwipe(target) || delta <= 0) return;\n e.preventDefault();\n }\n e.currentTarget.setPointerCapture(e.pointerId);\n hasCapturedRef.current = true;\n setIsDragging(true);\n }\n if (!hasCapturedRef.current) return;\n let next = dragStartRef.current.sheetY + delta;\n const min = 0;\n const max = sheetHeight + closeOffset;\n if (effectiveSnaps.length > 0) {\n const maxSnap = Math.max(...effectiveSnaps);\n const minY = sheetHeight - maxSnap;\n if (next > max) next = rubberband(next, minY, max);\n else if (next < minY) next = rubberband(next, minY, max);\n else next = Math.max(minY, Math.min(max, next));\n } else {\n if (next > max || next < min) next = rubberband(next, min, max);\n else next = Math.max(min, Math.min(max, next));\n }\n setTranslateY(next);\n },\n [sheetHeight, closeOffset, effectiveSnaps, scrollCtxValue]\n );\n\n const handlePointerUp = useCallback(\n (e: React.PointerEvent) => {\n if (!dragStartRef.current) return;\n const didCapture = hasCapturedRef.current;\n if (didCapture) {\n e.currentTarget.releasePointerCapture(e.pointerId);\n setIsDragging(false);\n }\n const last = lastMoveRef.current;\n const velocityY =\n last && last.time !== dragStartRef.current.time\n ? (e.clientY - last.y) / (Date.now() - last.time)\n : 0;\n dragStartRef.current = null;\n lastMoveRef.current = null;\n hasCapturedRef.current = false;\n\n if (!didCapture) return;\n\n const result = getReleaseTarget(\n translateY,\n sheetHeight,\n effectiveSnaps,\n velocityY,\n closeOffset\n );\n\n if (result.action === 'close') {\n if (disableSwipeDownToClose) {\n const targetY = effectiveSnaps.length > 0 ? sheetHeight - effectiveSnaps[0] : 0;\n setIsAnimating(true);\n setTranslateY(targetY);\n const duration = getDurationMs();\n setTimeout(() => setIsAnimating(false), duration);\n return;\n }\n setBackdropOpacity(0);\n setIsAnimating(true);\n setTranslateY(sheetHeight + closeOffset);\n const duration = getDurationMs();\n if (isTop && ctx) {\n ctx.setTopSheetClosingProgress(0);\n const startTime = Date.now();\n const tick = () => {\n const elapsed = Date.now() - startTime;\n const progress = Math.min(1, elapsed / duration);\n ctx?.setTopSheetClosingProgress(progress);\n if (progress < 1) {\n requestAnimationFrame(tick);\n } else {\n onClose?.();\n removeSheet(id);\n ctx?.setTopSheetClosingProgress(null);\n setIsAnimating(false);\n }\n };\n requestAnimationFrame(tick);\n } else {\n setTimeout(() => {\n onClose?.();\n removeSheet(id);\n setIsAnimating(false);\n }, duration);\n }\n return;\n }\n\n const targetY = result.targetY ?? 0;\n setIsAnimating(true);\n setTranslateY(targetY);\n const duration = getDurationMs();\n setTimeout(() => setIsAnimating(false), duration);\n },\n [\n translateY,\n sheetHeight,\n effectiveSnaps,\n closeOffset,\n id,\n onClose,\n disableSwipeDownToClose,\n isTop,\n ctx,\n ]\n );\n\n const handlePointerCancel = useCallback((e: React.PointerEvent) => {\n if (hasCapturedRef.current) {\n e.currentTarget.releasePointerCapture(e.pointerId);\n setIsDragging(false);\n }\n dragStartRef.current = null;\n lastMoveRef.current = null;\n hasCapturedRef.current = false;\n }, []);\n\n useEffect(() => {\n if (!isDragging) return;\n const prev = document.body.style.userSelect;\n document.body.style.userSelect = 'none';\n return () => {\n document.body.style.userSelect = prev;\n };\n }, [isDragging]);\n\n const gestureProps = {\n onPointerDownCapture: handlePointerDownCapture,\n onPointerDown: handlePointerDown,\n onPointerMove: handlePointerMove,\n onPointerUp: handlePointerUp,\n onPointerCancel: handlePointerCancel,\n };\n\n const duration = getCssVar('--bottom-sheet-duration', '0.5s');\n const easing = getCssVar('--bottom-sheet-easing', 'cubic-bezier(0.32, 0.72, 0, 1)');\n const closingProgress = ctx?.topSheetClosingProgress ?? null;\n const isRevealingDuringClose = stackDepth >= 1 && closingProgress != null;\n const easedProgress = isRevealingDuringClose ? getEasedProgress(closingProgress, easing) : 0;\n const transition =\n isDragging || isRevealingDuringClose\n ? 'none'\n : `transform ${duration} ${easing}, height ${duration} ${easing}, transform-origin ${duration} ${easing}`;\n\n const fromScale = scaleForStackDepth(stackDepth);\n const toScale = scaleForStackDepth(stackDepth - 1);\n const fromOffset = offsetForStackDepth(stackDepth);\n const toOffset = offsetForStackDepth(stackDepth - 1);\n const stackScale = isRevealingDuringClose\n ? fromScale + (toScale - fromScale) * easedProgress\n : fromScale;\n const stackOffsetY = isRevealingDuringClose\n ? fromOffset + (toOffset - fromOffset) * easedProgress\n : fromOffset;\n\n const closedYNum = sheetHeight + closeOffset;\n const dragProgress =\n closedYNum > firstSnapY\n ? Math.max(0, Math.min(1, (translateY - firstSnapY) / (closedYNum - firstSnapY)))\n : 0;\n const overlayOpacity = isDragging ? 1 - dragProgress : backdropOpacity;\n const overlayTransition = isDragging ? 'none' : `opacity ${duration} ${easing}`;\n\n const lastOverlayStyleRef = useRef<{ opacity: number; transition: string; pointerEvents: 'auto' | 'none' } | null>(null);\n\n useEffect(() => {\n if (!isTop || !ctx) return;\n const opacity = enableBackdrop ? overlayOpacity : 0;\n const transition = overlayTransition;\n const pointerEvents = enableBackdrop && enableClickBackdropToClose ? 'auto' : 'none';\n const last = lastOverlayStyleRef.current;\n if (last && last.opacity === opacity && last.transition === transition && last.pointerEvents === pointerEvents) return;\n lastOverlayStyleRef.current = { opacity, transition, pointerEvents };\n ctx.setOverlayStyle({ opacity, transition, pointerEvents });\n }, [isTop, ctx, enableBackdrop, enableClickBackdropToClose, overlayOpacity, overlayTransition]);\n\n const content = (\n <>\n <div\n ref={sheetRef}\n role={isTop ? 'dialog' : undefined}\n aria-modal={isTop ? true : undefined}\n className={`bottom-sheet${isDragging ? ' dragging' : ''}${stackDepth > 0 ? ' stacked' : ''} ${className ?? ''}`.trim()}\n {...(stackDepth === 0 && !gestureOnlyOnHandler ? gestureProps : {})}\n style={{\n position: 'fixed',\n ...(widthCss\n ? {\n width: widthCss,\n maxWidth: '100%',\n left: '50%',\n transform: `translateX(-50%) translateY(${translateY}px) scale(${stackScale}) translateY(-${stackOffsetY}%)`,\n transformOrigin: 'bottom center',\n }\n : {\n left: 0,\n right: 0,\n transform: `translateY(${translateY}px) scale(${stackScale}) translateY(-${stackOffsetY}%)`,\n transformOrigin: 'bottom center',\n }),\n bottom: 0,\n ...(contentDrivenHeight\n ? { height: sheetHeight === 0 ? 'auto' : sheetHeight, maxHeight: '100vh' }\n : { height: sheetHeight, maxHeight: '100vh' }),\n ['--bottom-sheet-height' as string]: `${sheetHeight}px`,\n transition,\n zIndex: index + 1,\n display: 'flex',\n flexDirection: 'column',\n background: 'var(--bottom-sheet-bg)',\n borderRadius: 'var(--bottom-sheet-border-radius)',\n boxShadow: 'var(--bottom-sheet-shadow)',\n padding: 'var(--bottom-sheet-padding)',\n }}\n >\n <div\n ref={handlerRef}\n className=\"bottom-sheet-handler\"\n {...(stackDepth === 0 && gestureOnlyOnHandler ? gestureProps : {})}\n style={{\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 'var(--bottom-sheet-handler-padding)',\n gap: 'var(--bottom-sheet-gap)',\n }}\n >\n <div\n style={{\n width: 'var(--bottom-sheet-handler-width)',\n height: 'var(--bottom-sheet-handler-height)',\n borderRadius: 'var(--bottom-sheet-handler-border-radius)',\n background: 'var(--bottom-sheet-handler-bg)',\n }}\n />\n </div>\n <div\n ref={contentRef}\n className=\"bottom-sheet-content\"\n style={{\n ...(contentDrivenHeight && sheetHeight === 0\n ? { flex: '0 0 auto', overflow: 'visible', minHeight: 0 }\n : { flex: 1, overflow: 'auto', minHeight: 0 }),\n }}\n >\n <ScrollContainerContext.Provider value={scrollCtxValue}>\n <Component\n {...(componentProps as object)}\n closeDrawer={closeDrawer}\n snapToIndex={snapToIndex}\n />\n </ScrollContainerContext.Provider>\n </div>\n <div\n className=\"bottom-sheet-stack-overlay\"\n aria-hidden\n style={{\n position: 'absolute',\n inset: 0,\n background: 'var(--bottom-sheet-stack-overlay-bg)',\n borderRadius: 'inherit',\n pointerEvents: stackDepth === 0 && !isRevealingDuringClose ? 'none' : 'auto',\n opacity: isRevealingDuringClose && stackDepth === 1\n ? 1 - easedProgress\n : (stackDepth === 0 ? 0 : 1),\n transition: isRevealingDuringClose ? 'none' : `opacity ${duration} ${easing}`,\n ...((stackDepth > 0 || isRevealingDuringClose) ? { cursor: 'default' as const } : {}),\n }}\n />\n </div>\n </>\n );\n\n return content;\n}\n\nfunction parseHeight(h: string | number): number {\n if (typeof h === 'number') return h;\n if (typeof document === 'undefined') return 400;\n const v = h.trim();\n if (v.endsWith('%')) {\n return (window.innerHeight * parseFloat(v.slice(0, -1))) / 100;\n }\n if (v.endsWith('px')) return parseFloat(v.slice(0, -2)) || 400;\n if (v.endsWith('rem')) {\n const rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;\n return parseFloat(v.slice(0, -3)) * rem || 400;\n }\n return parseFloat(v) || 400;\n}\n","import { createContext, useSyncExternalStore, useCallback, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n getSheets,\n subscribe,\n registerSheetController,\n unregisterSheetController,\n getSheetController,\n} from '../store/store';\nimport type { SheetDescriptor } from '../types';\nimport { BottomSheet } from '../components/BottomSheet';\n\nexport const BottomSheetContext = createContext<{\n registerController: (id: symbol | string, ctrl: { snapToIndex: (i: number) => void; openFully: () => void; close: () => void }) => void;\n unregisterController: (id: symbol | string) => void;\n defaultWidth?: string | number;\n setOverlayStyle: (style: { opacity: number; transition: string; pointerEvents: 'auto' | 'none' }) => void;\n topSheetClosingProgress: number | null;\n setTopSheetClosingProgress: (progress: number | null) => void;\n} | null>(null);\n\nfunction useSheets(): ReadonlyArray<SheetDescriptor & { id: symbol }> {\n return useSyncExternalStore(subscribe, getSheets, getSheets);\n}\n\nexport interface BottomSheetRootProps {\n /** Default width for all bottom sheets (e.g. '50%', '20rem', 400). Number = px. When set, sheets are centered. */\n width?: string | number;\n}\n\nexport function BottomSheetRoot({ width }: BottomSheetRootProps = {}) {\n const sheets = useSheets();\n const [topSheetClosingProgress, setTopSheetClosingProgress] = useState<number | null>(null);\n const [overlayStyle, setOverlayStyle] = useState<{\n opacity: number;\n transition: string;\n pointerEvents: 'auto' | 'none';\n }>({ opacity: 0, transition: 'opacity 0.5s cubic-bezier(0.32, 0.72, 0, 1)', pointerEvents: 'none' });\n\n const registerController = useCallback(\n (id: symbol | string, ctrl: { snapToIndex: (i: number) => void; openFully: () => void; close: () => void }) => {\n registerSheetController(id, ctrl);\n },\n []\n );\n\n const unregisterController = useCallback((id: symbol | string) => {\n unregisterSheetController(id);\n }, []);\n\n const container = typeof document !== 'undefined' ? document.body : null;\n const hasContent = sheets.length > 0 && container;\n\n if (!hasContent) return null;\n\n return (\n <BottomSheetContext.Provider value={{ registerController, unregisterController, defaultWidth: width, setOverlayStyle, topSheetClosingProgress, setTopSheetClosingProgress }}>\n {createPortal(\n <div className=\"bottom-sheets-portal\" aria-hidden=\"false\">\n {sheets.length > 0 && (\n <div\n className=\"bottom-sheet-overlay\"\n style={{\n position: 'fixed',\n inset: 0,\n background: 'var(--bottom-sheet-overlay-bg)',\n backdropFilter: 'var(--bottom-sheet-overlay-blur-filter)',\n zIndex: 0,\n pointerEvents: overlayStyle.pointerEvents ?? 'none',\n opacity: overlayStyle.opacity,\n transition: overlayStyle.transition,\n }}\n onClick={() => {\n const top = sheets[sheets.length - 1];\n if (top?.enableClickBackdropToClose) {\n getSheetController(top.id)?.close();\n }\n }}\n aria-hidden\n />\n )}\n {sheets.map((descriptor, index) => (\n <BottomSheet\n key={descriptor.id.toString()}\n descriptor={descriptor}\n index={index}\n isTop={index === sheets.length - 1}\n stackDepth={sheets.length - 1 - index}\n />\n ))}\n </div>,\n container\n )}\n </BottomSheetContext.Provider>\n );\n}\n","import {\n addSheet,\n removeSheet,\n removeAllSheets,\n getSheets,\n invokeSnapToIndex,\n invokeOpenFully,\n} from './store/store';\nimport type { PushOptions, BottomSheetInstance, SheetDescriptor } from './types';\nimport { BottomSheetRoot } from './context/context';\n\nexport { BottomSheetRoot };\nexport type { BottomSheetRootProps } from './context/context';\n\nexport function pushBottomSheet<T = Record<string, unknown>>(\n options: PushOptions<T>\n): BottomSheetInstance {\n const descriptor: Omit<SheetDescriptor<T>, 'id'> = {\n component: options.component,\n props: (options.props ?? {}) as T,\n height: options.height,\n width: options.width,\n snapPoint: options.snapPoint ?? options.snapPoints,\n className: options.className,\n onClose: options.onClose,\n enableClickBackdropToClose: options.enableClickBackdropToClose,\n enableBackdrop: options.enableBackdrop,\n disableEsc: options.disableEsc,\n gestureOnlyOnHandler: options.gestureOnlyOnHandler,\n disableSwipeDownToClose: options.disableSwipeDownToClose,\n };\n const id = addSheet(descriptor as Omit<SheetDescriptor, 'id'>);\n return {\n id,\n close: () => removeSheet(id),\n snapToIndex: (index: number) => invokeSnapToIndex(id, index),\n openFully: () => invokeOpenFully(id),\n };\n}\n\nexport function popBottomSheet(): void {\n const sheets = getSheets();\n if (sheets.length > 0) {\n const last = sheets[sheets.length - 1];\n removeSheet(last.id);\n }\n}\n\nexport function closeBottomSheet(id: string | symbol): void {\n removeSheet(id);\n}\n\nexport function closeAllBottomSheets(): void {\n removeAllSheets();\n}\n","import { useRef, useEffect, type CSSProperties } from 'react';\nimport { useScrollContainerContext } from '../context/scrollContext';\n\nexport interface BottomSheetScrollableProps {\n children: React.ReactNode;\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * A scrollable container that coordinates with BottomSheet gestures.\n * - When scrollTop > 0: vertical drags scroll the content (sheet gestures disabled)\n * - When scrollTop === 0: swipe down activates sheet gestures (close/pan), swipe up scrolls\n */\nexport function BottomSheetScrollable({ children, className, style }: BottomSheetScrollableProps) {\n const scrollRef = useRef<HTMLDivElement>(null);\n const touchStartY = useRef<number>(0);\n const ctx = useScrollContainerContext();\n\n useEffect(() => {\n if (!ctx || !scrollRef.current) return;\n const el = scrollRef.current;\n const getCanDrag = () => el.scrollTop === 0;\n return ctx.registerScrollContainer(el, getCanDrag);\n }, [ctx]);\n\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n const onTouchStart = (e: TouchEvent) => {\n touchStartY.current = e.touches[0]?.clientY ?? 0;\n };\n const onTouchMove = (e: TouchEvent) => {\n if (el.scrollTop > 0 || !e.cancelable) return;\n const touch = e.touches[0];\n if (!touch) return;\n const deltaY = touch.clientY - touchStartY.current;\n if (deltaY > 8) {\n e.preventDefault();\n }\n };\n el.addEventListener('touchstart', onTouchStart, { passive: true });\n el.addEventListener('touchmove', onTouchMove, { passive: false, capture: true });\n return () => {\n el.removeEventListener('touchstart', onTouchStart);\n el.removeEventListener('touchmove', onTouchMove, { capture: true });\n };\n }, []);\n\n return (\n <div\n ref={scrollRef}\n className={className}\n style={{\n flex: 1,\n minHeight: 0,\n overflow: 'auto',\n WebkitOverflowScrolling: 'touch',\n overscrollBehaviorY: 'contain',\n ...style,\n }}\n >\n {children}\n </div>\n );\n}\n"],"names":["idCounter","nextId","sheets","listeners","emit","l","getSheets","subscribe","listener","addSheet","descriptor","id","entry","removeSheet","s","removeAllSheets","sheetControllers","pendingSnapIndex","pendingOpenFully","registerSheetController","controller","snap","unregisterSheetController","getSheetController","invokeSnapToIndex","index","ctrl","invokeOpenFully","parseSnapValue","value","viewportHeightPx","remPx","trimmed","num","asNumber","getRemPx","root","fontSize","rem","parseSnapPoints","snapPoint","parsed","v","unique","a","b","RUBBERBAND_SCALE_PX","RUBBERBAND_FACTOR_MAX","RUBBERBAND_FACTOR_MIN","rubberbandFactor","over","rubberband","min","max","factor","VELOCITY_THRESHOLD","CLOSE_THRESHOLD","SNAP_PROGRESS_THRESHOLD","getReleaseTarget","currentTranslateY","sheetHeight","snapPointsPx","velocityY","closeOffset","closedPosition","sortedSnaps","firstSnap","firstSnapTranslateY","closedPos","closeZoneStart","closeZoneLength","closeThresholdY","nextSnapDown","findNextSnapDown","nextSnapUp","findNextSnapUp","targetY","idx","currentSnapIndex","findCurrentSnapIndex","getProgressToNextSnap","nextSnap","currentY","currentVisible","i","best","bestDist","d","fromSnap","segment","progress","ScrollContainerContext","createContext","useScrollContainerContext","useContext","findScrollContainer","target","containers","node","useScrollContainerContextValue","containersRef","useRef","registerScrollContainer","useCallback","el","getCanDrag","isInScrollContainer","shouldBlockGestures","canCaptureForDownSwipe","FOCUSABLE_SELECTOR","CLOSE_BUTTON_CLASS","getFocusableElements","container","getFocusableExcludingClose","useFocusTrap","containerRef","enabled","disableAutoFocus","useEffect","previouslyFocused","focusableExcludingClose","focusable","first","handleKeyDown","e","focusableNow","firstNow","lastNow","VIEWPORT_MAX","DRAG_THRESHOLD","getCssVar","name","fallback","getDurationMs","match","matchMs","getEaseCubicBezierY","y1","y2","t2","getEasedProgress","easingVar","values","scaleForStackDepth","offsetForStackDepth","BottomSheet","isTop","stackDepth","ctx","BottomSheetContext","Component","componentProps","heightProp","widthProp","className","onClose","enableBackdrop","enableClickBackdropToClose","disableEsc","gestureOnlyOnHandler","disableSwipeDownToClose","effectiveWidth","widthCss","viewportHeight","hasSnapPoints","contentDrivenHeight","sheetRef","handlerRef","contentRef","scrollCtxValue","setSheetHeight","useState","parseHeight","contentHeightMeasured","setContentHeightMeasured","closedY","effectiveSnaps","firstSnapY","translateY","setTranslateY","isDragging","setIsDragging","isAnimating","setIsAnimating","hasOpened","setHasOpened","backdropOpacity","setBackdropOpacity","dragStartRef","lastMoveRef","hasCapturedRef","heightAnimationTimeoutRef","useLayoutEffect","ro","h","prev","updateHeight","sheet","handler","content","style","paddingTop","paddingBottom","gapStr","gapPx","contentEl","contentHeight","firstChild","_a","duration","closeDrawer","startTime","tick","elapsed","t","snapToIndex","openFully","onKeyDown","flushSync","cleanupDragState","pointerId","handlePointerDownCapture","isTouch","isInContent","handlePointerDown","onPointerUpGlobal","ev","handlePointerMove","delta","threshold","next","maxSnap","minY","handlePointerUp","didCapture","last","result","handlePointerCancel","gestureProps","easing","closingProgress","isRevealingDuringClose","easedProgress","transition","fromScale","toScale","fromOffset","toOffset","stackScale","stackOffsetY","closedYNum","dragProgress","overlayOpacity","overlayTransition","lastOverlayStyleRef","opacity","pointerEvents","jsx","Fragment","jsxs","useSheets","useSyncExternalStore","BottomSheetRoot","width","topSheetClosingProgress","setTopSheetClosingProgress","overlayStyle","setOverlayStyle","registerController","unregisterController","createPortal","top","pushBottomSheet","options","popBottomSheet","closeBottomSheet","closeAllBottomSheets","BottomSheetScrollable","children","scrollRef","touchStartY","onTouchStart","onTouchMove","touch"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { SheetDescriptor } from '../types';
2
+
3
+ export type SheetController = {
4
+ snapToIndex: (index: number) => void;
5
+ openFully: () => void;
6
+ close: () => void;
7
+ };
8
+ export declare function getSheets(): ReadonlyArray<SheetDescriptor & {
9
+ id: symbol;
10
+ }>;
11
+ export declare function subscribe(listener: () => void): () => void;
12
+ export declare function addSheet(descriptor: Omit<SheetDescriptor, 'id'>): symbol;
13
+ export declare function removeSheet(id: string | symbol): void;
14
+ export declare function removeAllSheets(): void;
15
+ export declare function registerSheetController(id: symbol | string, controller: SheetController): void;
16
+ export declare function unregisterSheetController(id: symbol | string): void;
17
+ export declare function getSheetController(id: symbol | string): SheetController | undefined;
18
+ export declare function invokeSnapToIndex(id: symbol | string, index: number): void;
19
+ export declare function invokeOpenFully(id: symbol | string): void;
@@ -0,0 +1 @@
1
+ :root{--bottom-sheet-bg: #fff;--bottom-sheet-padding: 1rem;--bottom-sheet-border-radius: 12px 12px 0 0;--bottom-sheet-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--bottom-sheet-shadow: none;--bottom-sheet-overlay-bg: rgba(0, 0, 0, .3);--bottom-sheet-overlay-blur-filter: blur(8px);--bottom-sheet-duration: .5s;--bottom-sheet-easing: cubic-bezier(.32, .72, 0, 1);--bottom-sheet-handler-bg: #cbd5e1;--bottom-sheet-handler-width: 40px;--bottom-sheet-handler-height: 4px;--bottom-sheet-handler-border-radius: 2px;--bottom-sheet-handler-padding: 0rem 0rem .5rem 0rem;--bottom-sheet-gap: .5rem;--bottom-sheet-close-extra-offset: 0;--bottom-sheet-stack-overlay-bg: rgba(0, 0, 0, .5);--bottom-sheet-drag-hitbox-extension: 8px;--bottom-sheet-content-touch-action: none}.bottom-sheets-portal{position:fixed;top:0;right:0;bottom:0;left:0;pointer-events:none;z-index:9999}.bottom-sheets-portal>*{pointer-events:auto}.bottom-sheet-overlay{pointer-events:auto}.bottom-sheet{touch-action:none;-webkit-user-select:none;user-select:none;cursor:grab}.bottom-sheet.dragging{cursor:grabbing}.bottom-sheet.stacked{cursor:default}.bottom-sheet:before{content:"";position:absolute;left:0;right:0;bottom:100%;height:var(--bottom-sheet-drag-hitbox-extension);pointer-events:auto}.bottom-sheet:after{content:"";position:absolute;left:0;right:0;top:99%;width:100%;height:calc(2 * var(--bottom-sheet-height, 0px));background:var(--bottom-sheet-bg);pointer-events:none}.bottom-sheet-handler{touch-action:none}.bottom-sheet-handler:active{cursor:inherit}.bottom-sheet-content{touch-action:var(--bottom-sheet-content-touch-action)}
@@ -0,0 +1,47 @@
1
+ import { ComponentType } from 'react';
2
+
3
+ export interface BottomSheetInjectedProps {
4
+ closeDrawer: () => void;
5
+ snapToIndex: (index: number) => void;
6
+ }
7
+ export interface BottomSheetInstance {
8
+ id: string | symbol;
9
+ close: () => void;
10
+ snapToIndex: (index: number) => void;
11
+ openFully: () => void;
12
+ }
13
+ export interface PushOptions<T = Record<string, unknown>> {
14
+ component: ComponentType<T & BottomSheetInjectedProps>;
15
+ props?: T;
16
+ height?: string | number;
17
+ /** Width in any CSS unit (%, rem, px, vw, etc.), or number (treated as px). When set, the sheet is centered. */
18
+ width?: string | number;
19
+ /** Snap points (e.g. ['10%', '25%']). Also accepts `snapPoints` as alias. */
20
+ snapPoint?: string[];
21
+ /** Alias for snapPoint. Prefer snapPoint. */
22
+ snapPoints?: string[];
23
+ className?: string;
24
+ onClose?: () => void;
25
+ enableClickBackdropToClose?: boolean;
26
+ enableBackdrop?: boolean;
27
+ disableEsc?: boolean;
28
+ gestureOnlyOnHandler?: boolean;
29
+ /** When true, swipe down never closes the sheet; it always returns to the first snap point or open position. */
30
+ disableSwipeDownToClose?: boolean;
31
+ }
32
+ export interface SheetDescriptor<T = Record<string, unknown>> {
33
+ id: string | symbol;
34
+ component: ComponentType<T & BottomSheetInjectedProps>;
35
+ props: T;
36
+ height?: string | number;
37
+ /** Width in any CSS unit (%, rem, px, vw, etc.), or number (treated as px). When set, the sheet is centered. */
38
+ width?: string | number;
39
+ snapPoint?: string[];
40
+ className?: string;
41
+ onClose?: () => void;
42
+ enableClickBackdropToClose?: boolean;
43
+ enableBackdrop?: boolean;
44
+ disableEsc?: boolean;
45
+ gestureOnlyOnHandler?: boolean;
46
+ disableSwipeDownToClose?: boolean;
47
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Velocity threshold (px/ms) above which we consider it a "fast swipe" for closing or snapping.
3
+ */
4
+ export declare const VELOCITY_THRESHOLD = 0.5;
5
+ /**
6
+ * Fraction of travel (0..1) toward closed position. If past this and velocity is down, can close.
7
+ */
8
+ export declare const CLOSE_THRESHOLD = 0.6;
9
+ /**
10
+ * Fraction of travel (0..1) between two snap points. If past 50%, go to next snap.
11
+ */
12
+ export declare const SNAP_PROGRESS_THRESHOLD = 0.5;
13
+ export interface ReleaseResult {
14
+ action: 'close' | 'snap';
15
+ targetIndex?: number;
16
+ targetY?: number;
17
+ }
18
+ /**
19
+ * Given current position (translateY, 0 = top), sheet height, snap points in px (heights from bottom),
20
+ * and velocity (positive = downward), decide whether to close or snap to an index.
21
+ * Snap points are ordered ascending (smallest = most closed). Position is "how much the sheet is down"
22
+ * so 0 = fully open (sheet top at viewport top), sheetHeight = fully closed.
23
+ */
24
+ export declare function getReleaseTarget(currentTranslateY: number, sheetHeight: number, snapPointsPx: number[], velocityY: number, closeOffset?: number): ReleaseResult;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Parse snap point strings to an array of heights in pixels (from bottom of viewport).
3
+ * Sorted ascending (smallest = most closed, largest = most open).
4
+ * viewportHeightPx: window.innerHeight or equivalent.
5
+ */
6
+ export declare function parseSnapPoints(snapPoint: string[] | undefined, viewportHeightPx: number): number[];
7
+ /**
8
+ * Clamp value between min and max. For rubberband, we allow overscroll with resistance.
9
+ */
10
+ export declare function clamp(value: number, min: number, max: number): number;
11
+ /**
12
+ * Apply rubberband when beyond bounds. Resistance is incremental: the more you overscroll,
13
+ * the stronger the resistance (smaller movement per pixel), approaching a minimum factor but never zero.
14
+ * Used when swiping past the last (highest) snap point, or past max height when there are no snap points.
15
+ */
16
+ export declare function rubberband(value: number, min: number, max: number): number;
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@svnrnns/react-bottom-sheets",
3
+ "version": "1.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "Imperative bottom sheets for React with gestures, snap points and stacking",
8
+ "type": "module",
9
+ "main": "./dist/index.cjs",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs",
16
+ "types": "./dist/index.d.ts"
17
+ },
18
+ "./styles.css": "./dist/styles.css"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "prepublishOnly": "npm run clean && npm run build",
25
+ "clean": "rm -rf dist",
26
+ "build": "vite build",
27
+ "dev": "vite build --watch"
28
+ },
29
+ "peerDependencies": {
30
+ "react": ">=18.0.0",
31
+ "react-dom": ">=18.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/react": "^18.2.0",
35
+ "@types/react-dom": "^18.2.0",
36
+ "@vitejs/plugin-react": "^4.2.0",
37
+ "vite-plugin-dts": "^3.7.0",
38
+ "react": "^18.2.0",
39
+ "react-dom": "^18.2.0",
40
+ "typescript": "^5.3.0",
41
+ "vite": "^5.0.0"
42
+ }
43
+ }