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.

291 lines (288 loc) 11.3 kB
"use strict"; 'use client'; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.DrawerPopup = void 0; var React = _interopRequireWildcard(require("react")); var _useIsoLayoutEffect = require("@base-ui/utils/useIsoLayoutEffect"); var _useStableCallback = require("@base-ui/utils/useStableCallback"); var _floatingUiReact = require("../../floating-ui-react"); var _DialogRootContext = require("../../dialog/root/DialogRootContext"); var _useRenderElement = require("../../utils/useRenderElement"); var _popupStateMapping = require("../../utils/popupStateMapping"); var _stateAttributesMapping = require("../../utils/stateAttributesMapping"); var _DrawerBackdropCssVars = require("../backdrop/DrawerBackdropCssVars"); var _DrawerPopupCssVars = require("./DrawerPopupCssVars"); var _DrawerPopupDataAttributes = require("./DrawerPopupDataAttributes"); var _DialogPortalContext = require("../../dialog/portal/DialogPortalContext"); var _useOpenChangeComplete = require("../../utils/useOpenChangeComplete"); var _composite = require("../../composite/composite"); var _DrawerRootContext = require("../root/DrawerRootContext"); var _useDrawerSnapPoints = require("../root/useDrawerSnapPoints"); var _DrawerViewportContext = require("../viewport/DrawerViewportContext"); var _constants = require("../../utils/constants"); var _jsxRuntime = require("react/jsx-runtime"); const stateAttributesMapping = { ..._popupStateMapping.popupStateMapping, ..._stateAttributesMapping.transitionStatusMapping, expanded(value) { return value ? { [_DrawerPopupDataAttributes.DrawerPopupDataAttributes.expanded]: '' } : null; }, nestedDrawerOpen(value) { return value ? { [_DrawerPopupDataAttributes.DrawerPopupDataAttributes.nestedDrawerOpen]: '' } : null; }, nestedDrawerSwiping(value) { return value ? { [_DrawerPopupDataAttributes.DrawerPopupDataAttributes.nestedDrawerSwiping]: '' } : null; }, swipeDirection(value) { return value ? { [_DrawerPopupDataAttributes.DrawerPopupDataAttributes.swipeDirection]: value } : null; }, swiping(value) { return value ? { [_DrawerPopupDataAttributes.DrawerPopupDataAttributes.swiping]: '' } : null; } }; /** * A container for the drawer contents. * Renders a `<div>` element. * * Documentation: [Base UI Drawer](https://base-ui.com/react/components/drawer) */ const DrawerPopup = exports.DrawerPopup = /*#__PURE__*/React.forwardRef(function DrawerPopup(componentProps, forwardedRef) { const { className, finalFocus, initialFocus, render, ...elementProps } = componentProps; const { store } = (0, _DialogRootContext.useDialogRootContext)(); const { swipeDirection, frontmostHeight, hasNestedDrawer, nestedSwiping, nestedSwipeProgressStore, onPopupHeightChange, notifyParentFrontmostHeight, notifyParentHasNestedDrawer } = (0, _DrawerRootContext.useDrawerRootContext)(); const descriptionElementId = store.useState('descriptionElementId'); const disablePointerDismissal = store.useState('disablePointerDismissal'); const floatingRootContext = store.useState('floatingRootContext'); const rootPopupProps = store.useState('popupProps'); const modal = store.useState('modal'); const mounted = store.useState('mounted'); const nested = store.useState('nested'); const nestedOpenDialogCount = store.useState('nestedOpenDialogCount'); const transitionStatus = store.useState('transitionStatus'); const open = store.useState('open'); const openMethod = store.useState('openMethod'); const titleElementId = store.useState('titleElementId'); const role = store.useState('role'); const nestedDrawerOpen = nestedOpenDialogCount > 0; const swipe = (0, _DrawerViewportContext.useDrawerViewportContext)(true); const swiping = swipe?.swiping ?? false; const swipeStrength = swipe?.swipeStrength ?? null; const { snapPoints, activeSnapPoint, activeSnapPointOffset } = (0, _useDrawerSnapPoints.useDrawerSnapPoints)(); (0, _DialogPortalContext.useDialogPortalContext)(); const [popupHeight, setPopupHeight] = React.useState(0); const popupHeightRef = React.useRef(0); const measureHeight = (0, _useStableCallback.useStableCallback)(() => { const popupElement = store.context.popupRef.current; if (!popupElement) { return; } const offsetHeight = popupElement.offsetHeight; // Only skip while the element is still actually stretched beyond its last measured height. if (popupHeightRef.current > 0 && frontmostHeight > popupHeightRef.current && offsetHeight > popupHeightRef.current) { return; } const keepHeightWhileNested = popupHeightRef.current > 0 && hasNestedDrawer; if (keepHeightWhileNested) { const oldHeight = popupHeightRef.current; setPopupHeight(oldHeight); onPopupHeightChange(oldHeight); return; } const scrollHeight = popupElement.scrollHeight; const nextHeight = scrollHeight > 0 ? Math.min(offsetHeight, scrollHeight) : offsetHeight; if (nextHeight === popupHeightRef.current) { return; } popupHeightRef.current = nextHeight; setPopupHeight(nextHeight); onPopupHeightChange(nextHeight); }); (0, _useIsoLayoutEffect.useIsoLayoutEffect)(() => { if (!mounted) { popupHeightRef.current = 0; setPopupHeight(0); onPopupHeightChange(0); return undefined; } const popupElement = store.context.popupRef.current; if (!popupElement) { return undefined; } measureHeight(); if (typeof ResizeObserver !== 'function') { return undefined; } const resizeObserver = new ResizeObserver(measureHeight); resizeObserver.observe(popupElement); return () => { resizeObserver.disconnect(); }; }, [measureHeight, mounted, nestedDrawerOpen, onPopupHeightChange, store.context.popupRef]); (0, _useIsoLayoutEffect.useIsoLayoutEffect)(() => { const popupRef = store.context.popupRef; const syncNestedSwipeProgress = () => { const popupElement = popupRef.current; if (!popupElement) { return; } const progress = nestedSwipeProgressStore.getSnapshot(); if (progress > 0) { popupElement.style.setProperty(_DrawerBackdropCssVars.DrawerBackdropCssVars.swipeProgress, `${progress}`); } else { popupElement.style.setProperty(_DrawerBackdropCssVars.DrawerBackdropCssVars.swipeProgress, '0'); } }; syncNestedSwipeProgress(); const unsubscribe = nestedSwipeProgressStore.subscribe(syncNestedSwipeProgress); return () => { unsubscribe(); const popupElement = popupRef.current; if (popupElement) { popupElement.style.setProperty(_DrawerBackdropCssVars.DrawerBackdropCssVars.swipeProgress, '0'); } }; }, [nestedSwipeProgressStore, store.context.popupRef]); React.useEffect(() => { if (!open) { return undefined; } notifyParentFrontmostHeight?.(frontmostHeight); return () => { notifyParentFrontmostHeight?.(0); }; }, [frontmostHeight, open, notifyParentFrontmostHeight]); React.useEffect(() => { if (!notifyParentHasNestedDrawer) { return undefined; } const present = open || transitionStatus === 'ending'; notifyParentHasNestedDrawer(present); return () => { notifyParentHasNestedDrawer(false); }; }, [notifyParentHasNestedDrawer, open, transitionStatus]); (0, _useOpenChangeComplete.useOpenChangeComplete)({ open, ref: store.context.popupRef, onComplete() { if (open) { store.context.onOpenChangeComplete?.(true); } } }); const resolvedInitialFocus = initialFocus === undefined ? store.context.popupRef : initialFocus; const state = { open, nested, transitionStatus, expanded: activeSnapPoint === 1, nestedDrawerOpen, nestedDrawerSwiping: nestedSwiping, swipeDirection, swiping }; let popupHeightCssVarValue; const shouldUseAutoHeight = !hasNestedDrawer && transitionStatus !== 'ending'; if (popupHeight && !shouldUseAutoHeight) { popupHeightCssVarValue = `${popupHeight}px`; } const shouldApplySnapPoints = snapPoints && snapPoints.length > 0 && (swipeDirection === 'down' || swipeDirection === 'up'); let snapPointOffsetValue = null; if (shouldApplySnapPoints && activeSnapPointOffset !== null) { snapPointOffsetValue = swipeDirection === 'up' ? -activeSnapPointOffset : activeSnapPointOffset; } let dragStyles = swipe ? swipe.getDragStyles() : _constants.EMPTY_OBJECT; if (shouldApplySnapPoints && swipeDirection === 'down') { const baseOffset = activeSnapPointOffset ?? 0; const movementValue = Number.parseFloat(String(dragStyles[_DrawerPopupCssVars.DrawerPopupCssVars.swipeMovementY] ?? 0)); const nextOffset = Number.isFinite(movementValue) ? baseOffset + movementValue : baseOffset; const shouldDamp = nextOffset < 0; if (swiping && shouldDamp && Number.isFinite(movementValue)) { const overshoot = Math.abs(nextOffset); const dampedOffset = -Math.sqrt(overshoot); const dampedMovement = dampedOffset - baseOffset; dragStyles = { ...dragStyles, transform: undefined, [_DrawerPopupCssVars.DrawerPopupCssVars.swipeMovementY]: `${dampedMovement}px` }; } else { dragStyles = { ...dragStyles, transform: undefined }; } } const element = (0, _useRenderElement.useRenderElement)('div', componentProps, { state, props: [rootPopupProps, { 'aria-labelledby': titleElementId, 'aria-describedby': descriptionElementId, role, tabIndex: -1, hidden: !mounted, onKeyDown(event) { if (_composite.COMPOSITE_KEYS.has(event.key)) { event.stopPropagation(); } }, style: { ...dragStyles, [_DrawerBackdropCssVars.DrawerBackdropCssVars.swipeProgress]: '0', [_DrawerPopupCssVars.DrawerPopupCssVars.nestedDrawers]: nestedOpenDialogCount, [_DrawerPopupCssVars.DrawerPopupCssVars.height]: popupHeightCssVarValue, [_DrawerPopupCssVars.DrawerPopupCssVars.snapPointOffset]: typeof snapPointOffsetValue === 'number' ? `${snapPointOffsetValue}px` : '0px', [_DrawerPopupCssVars.DrawerPopupCssVars.frontmostHeight]: frontmostHeight ? `${frontmostHeight}px` : undefined, [_DrawerPopupCssVars.DrawerPopupCssVars.swipeStrength]: typeof swipeStrength === 'number' && Number.isFinite(swipeStrength) && swipeStrength > 0 ? `${swipeStrength}` : '1' } }, elementProps], ref: [forwardedRef, store.context.popupRef, store.useStateSetter('popupElement')], stateAttributesMapping }); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingUiReact.FloatingFocusManager, { context: floatingRootContext, openInteractionType: openMethod, disabled: !mounted, closeOnFocusOut: !disablePointerDismissal, initialFocus: resolvedInitialFocus, returnFocus: finalFocus, modal: modal !== false, restoreFocus: "popup", children: element }); }); if (process.env.NODE_ENV !== "production") DrawerPopup.displayName = "DrawerPopup";