@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
JavaScript
;
'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";