UNPKG

@base-ui/react

Version:

Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.

235 lines (234 loc) 9.85 kB
"use strict"; 'use client'; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.DrawerRoot = DrawerRoot; var React = _interopRequireWildcard(require("react")); var _useControlled = require("@base-ui/utils/useControlled"); var _useStableCallback = require("@base-ui/utils/useStableCallback"); var _owner = require("@base-ui/utils/owner"); var _detectBrowser = require("@base-ui/utils/detectBrowser"); var _DrawerRootContext = require("./DrawerRootContext"); var _dialog = require("../../dialog"); var _createBaseUIEventDetails = require("../../utils/createBaseUIEventDetails"); var _reasons = require("../../utils/reasons"); var _DialogRootContext = require("../../dialog/root/DialogRootContext"); var _DrawerProviderContext = require("../provider/DrawerProviderContext"); var _jsxRuntime = require("react/jsx-runtime"); var _DrawerProviderReport, _DrawerProviderReport2; /** * Groups all parts of the drawer. * Doesn't render its own HTML element. * * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer) */ function DrawerRoot(props) { const { children, open: openProp, defaultOpen = false, onOpenChange, onOpenChangeComplete, disablePointerDismissal = false, modal = true, actionsRef, handle, triggerId: triggerIdProp, defaultTriggerId: defaultTriggerIdProp = null, swipeDirection = 'down', snapToSequentialPoints = false, snapPoints, snapPoint: snapPointProp, defaultSnapPoint, onSnapPointChange: onSnapPointChangeProp } = props; const onSnapPointChange = (0, _useStableCallback.useStableCallback)(onSnapPointChangeProp); const parentDrawerRootContext = (0, _DrawerRootContext.useDrawerRootContext)(true); const notifyParentSwipeProgressChange = parentDrawerRootContext?.onNestedSwipeProgressChange; const notifyParentFrontmostHeight = parentDrawerRootContext?.onNestedFrontmostHeightChange; const notifyParentSwipingChange = parentDrawerRootContext?.onNestedSwipingChange; const notifyParentHasNestedDrawer = parentDrawerRootContext?.onNestedDrawerPresenceChange; const [popupHeight, setPopupHeight] = React.useState(0); const [frontmostHeight, setFrontmostHeight] = React.useState(0); const [hasNestedDrawer, setHasNestedDrawer] = React.useState(false); const [nestedSwiping, setNestedSwiping] = React.useState(false); const [nestedSwipeProgressStore] = React.useState(createNestedSwipeProgressStore); const resolvedDefaultSnapPoint = defaultSnapPoint !== undefined ? defaultSnapPoint : snapPoints?.[0] ?? null; const isSnapPointControlled = snapPointProp !== undefined; const [activeSnapPoint, setActiveSnapPointUnwrapped] = (0, _useControlled.useControlled)({ controlled: snapPointProp, default: resolvedDefaultSnapPoint, name: 'Drawer', state: 'snapPoint' }); const isNestedDrawerOpenRef = React.useRef(false); const setActiveSnapPoint = (0, _useStableCallback.useStableCallback)((nextSnapPoint, eventDetails) => { const resolvedEventDetails = eventDetails ?? (0, _createBaseUIEventDetails.createChangeEventDetails)(_reasons.REASONS.none); onSnapPointChange?.(nextSnapPoint, resolvedEventDetails); if (resolvedEventDetails.isCanceled) { return; } setActiveSnapPointUnwrapped(nextSnapPoint); }); const resolvedActiveSnapPoint = React.useMemo(() => { if (isSnapPointControlled) { return activeSnapPoint; } if (!snapPoints || snapPoints.length === 0) { return activeSnapPoint; } if (activeSnapPoint === null || !snapPoints.some(snapPoint => Object.is(snapPoint, activeSnapPoint))) { return resolvedDefaultSnapPoint; } return activeSnapPoint; }, [activeSnapPoint, isSnapPointControlled, resolvedDefaultSnapPoint, snapPoints]); const onPopupHeightChange = (0, _useStableCallback.useStableCallback)(height => { setPopupHeight(height); if (!isNestedDrawerOpenRef.current && height > 0) { setFrontmostHeight(height); } }); const onNestedFrontmostHeightChange = (0, _useStableCallback.useStableCallback)(height => { if (height > 0) { isNestedDrawerOpenRef.current = true; setFrontmostHeight(height); return; } isNestedDrawerOpenRef.current = false; if (popupHeight > 0) { setFrontmostHeight(popupHeight); } }); const onNestedDrawerPresenceChange = (0, _useStableCallback.useStableCallback)(present => { setHasNestedDrawer(present); }); const onNestedSwipeProgressChange = (0, _useStableCallback.useStableCallback)(progress => { nestedSwipeProgressStore.set(progress); notifyParentSwipeProgressChange?.(progress); }); const onNestedSwipingChange = (0, _useStableCallback.useStableCallback)(swiping => { setNestedSwiping(swiping); notifyParentSwipingChange?.(swiping); }); const handleOpenChange = (0, _useStableCallback.useStableCallback)((nextOpen, eventDetails) => { onOpenChange?.(nextOpen, eventDetails); if (eventDetails.isCanceled) { return; } if (!nextOpen && snapPoints && snapPoints.length > 0) { setActiveSnapPoint(resolvedDefaultSnapPoint, (0, _createBaseUIEventDetails.createChangeEventDetails)(eventDetails.reason, eventDetails.event, eventDetails.trigger)); } }); const contextValue = React.useMemo(() => ({ swipeDirection, snapToSequentialPoints, snapPoints, activeSnapPoint: resolvedActiveSnapPoint, setActiveSnapPoint, frontmostHeight, popupHeight, hasNestedDrawer, nestedSwiping, nestedSwipeProgressStore, onNestedDrawerPresenceChange, onPopupHeightChange, onNestedFrontmostHeightChange, onNestedSwipingChange, onNestedSwipeProgressChange, notifyParentFrontmostHeight, notifyParentSwipingChange, notifyParentSwipeProgressChange, notifyParentHasNestedDrawer }), [resolvedActiveSnapPoint, frontmostHeight, hasNestedDrawer, nestedSwiping, nestedSwipeProgressStore, notifyParentHasNestedDrawer, notifyParentSwipeProgressChange, notifyParentSwipingChange, notifyParentFrontmostHeight, onNestedDrawerPresenceChange, onNestedFrontmostHeightChange, onNestedSwipeProgressChange, onNestedSwipingChange, onPopupHeightChange, popupHeight, setActiveSnapPoint, snapPoints, snapToSequentialPoints, swipeDirection]); const resolvedChildren = typeof children === 'function' ? payload => /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, { children: [_DrawerProviderReport || (_DrawerProviderReport = /*#__PURE__*/(0, _jsxRuntime.jsx)(DrawerProviderReporter, {})), children(payload)] }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, { children: [_DrawerProviderReport2 || (_DrawerProviderReport2 = /*#__PURE__*/(0, _jsxRuntime.jsx)(DrawerProviderReporter, {})), children] }); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_DrawerRootContext.DrawerRootContext.Provider, { value: contextValue, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dialog.Dialog.Root, { open: openProp, defaultOpen: defaultOpen, onOpenChange: handleOpenChange, onOpenChangeComplete: onOpenChangeComplete, disablePointerDismissal: disablePointerDismissal, modal: modal, actionsRef: actionsRef, handle: handle, triggerId: triggerIdProp, defaultTriggerId: defaultTriggerIdProp, children: resolvedChildren }) }); } function createNestedSwipeProgressStore() { let progress = 0; const listeners = new Set(); return { getSnapshot: () => progress, set(nextProgress) { const resolved = Number.isFinite(nextProgress) ? nextProgress : 0; if (resolved === progress) { return; } progress = resolved; listeners.forEach(listener => { listener(); }); }, subscribe(listener) { listeners.add(listener); return () => { listeners.delete(listener); }; } }; } function DrawerProviderReporter() { const drawerId = React.useId(); const providerContext = (0, _DrawerProviderContext.useDrawerProviderContext)(true); const dialogRootContext = (0, _DialogRootContext.useDialogRootContext)(false); const open = dialogRootContext.store.useState('open'); const nestedOpenDialogCount = dialogRootContext.store.useState('nestedOpenDialogCount'); const popupElement = dialogRootContext.store.useState('popupElement'); const isTopmost = nestedOpenDialogCount === 0; React.useEffect(() => { if (!providerContext) { return undefined; } return () => { providerContext.removeDrawer(drawerId); }; }, [drawerId, providerContext]); React.useEffect(() => { providerContext?.setDrawerOpen(drawerId, open); }, [drawerId, open, providerContext]); React.useEffect(() => { // CloseWatcher enables the Android back gesture (Chromium-only). // Keep this Android-only for now to avoid interfering with Escape/nesting semantics on desktop due to `useDismiss`. if (!open || !isTopmost || !_detectBrowser.isAndroid) { return undefined; } const win = (0, _owner.ownerWindow)(popupElement); const CloseWatcherCtor = win.CloseWatcher; if (!CloseWatcherCtor) { return undefined; } function handleCloseWatcher(event) { if (!dialogRootContext.store.select('open')) { return; } dialogRootContext.store.setOpen(false, (0, _createBaseUIEventDetails.createChangeEventDetails)(_reasons.REASONS.closeWatcher, event)); } const closeWatcher = new CloseWatcherCtor(); closeWatcher.addEventListener('close', handleCloseWatcher); return () => { closeWatcher.removeEventListener('close', handleCloseWatcher); closeWatcher.destroy(); }; }, [dialogRootContext.store, isTopmost, open, popupElement]); return null; }