@base-ui-components/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.
209 lines (207 loc) • 7.11 kB
JavaScript
;
'use client';
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.PopoverRoot = PopoverRoot;
var React = _interopRequireWildcard(require("react"));
var ReactDOM = _interopRequireWildcard(require("react-dom"));
var _useTimeout = require("@base-ui-components/utils/useTimeout");
var _useControlled = require("@base-ui-components/utils/useControlled");
var _useEventCallback = require("@base-ui-components/utils/useEventCallback");
var _floatingUiReact = require("../../floating-ui-react");
var _useTransitionStatus = require("../../utils/useTransitionStatus");
var _constants = require("../utils/constants");
var _useOpenInteractionType = require("../../utils/useOpenInteractionType");
var _translateOpenChangeReason = require("../../utils/translateOpenChangeReason");
var _useOpenChangeComplete = require("../../utils/useOpenChangeComplete");
var _constants2 = require("../../utils/constants");
var _useScrollLock = require("../../utils/useScrollLock");
var _PopoverRootContext = require("./PopoverRootContext");
var _mergeProps = require("../../merge-props");
var _jsxRuntime = require("react/jsx-runtime");
function PopoverRootComponent({
props
}) {
const {
open: externalOpen,
onOpenChange,
defaultOpen = false,
delay = _constants.OPEN_DELAY,
closeDelay = 0,
openOnHover = false,
onOpenChangeComplete,
modal = false
} = props;
const [instantType, setInstantType] = React.useState();
const [titleId, setTitleId] = React.useState();
const [descriptionId, setDescriptionId] = React.useState();
const [triggerElement, setTriggerElement] = React.useState(null);
const [positionerElement, setPositionerElement] = React.useState(null);
const [openReason, setOpenReason] = React.useState(null);
const [stickIfOpen, setStickIfOpen] = React.useState(true);
const popupRef = React.useRef(null);
const stickIfOpenTimeout = (0, _useTimeout.useTimeout)();
const [open, setOpenUnwrapped] = (0, _useControlled.useControlled)({
controlled: externalOpen,
default: defaultOpen,
name: 'Popover',
state: 'open'
});
const {
mounted,
setMounted,
transitionStatus
} = (0, _useTransitionStatus.useTransitionStatus)(open);
const {
openMethod,
triggerProps,
reset: resetOpenInteractionType
} = (0, _useOpenInteractionType.useOpenInteractionType)(open);
const handleUnmount = (0, _useEventCallback.useEventCallback)(() => {
setMounted(false);
setStickIfOpen(true);
setOpenReason(null);
onOpenChangeComplete?.(false);
resetOpenInteractionType();
});
(0, _useOpenChangeComplete.useOpenChangeComplete)({
enabled: !props.actionsRef,
open,
ref: popupRef,
onComplete() {
if (!open) {
handleUnmount();
}
}
});
React.useImperativeHandle(props.actionsRef, () => ({
unmount: handleUnmount
}), [handleUnmount]);
React.useEffect(() => {
if (!open) {
stickIfOpenTimeout.clear();
}
}, [stickIfOpenTimeout, open]);
const setOpen = (0, _useEventCallback.useEventCallback)((nextOpen, event, reason) => {
const isHover = reason === 'trigger-hover';
const isKeyboardClick = reason === 'trigger-press' && event.detail === 0;
const isDismissClose = !nextOpen && (reason === 'escape-key' || reason == null);
function changeState() {
onOpenChange?.(nextOpen, event, reason);
setOpenUnwrapped(nextOpen);
if (nextOpen) {
setOpenReason(reason ?? null);
}
}
if (isHover) {
// Only allow "patient" clicks to close the popover if it's open.
// If they clicked within 500ms of the popover opening, keep it open.
setStickIfOpen(true);
stickIfOpenTimeout.start(_constants2.PATIENT_CLICK_THRESHOLD, () => {
setStickIfOpen(false);
});
ReactDOM.flushSync(changeState);
} else {
changeState();
}
if (isKeyboardClick || isDismissClose) {
setInstantType(isKeyboardClick ? 'click' : 'dismiss');
} else {
setInstantType(undefined);
}
});
const floatingContext = (0, _floatingUiReact.useFloatingRootContext)({
elements: {
reference: triggerElement,
floating: positionerElement
},
open,
onOpenChange(openValue, eventValue, reasonValue) {
setOpen(openValue, eventValue, (0, _translateOpenChangeReason.translateOpenChangeReason)(reasonValue));
}
});
(0, _useScrollLock.useScrollLock)({
enabled: open && modal === true && openReason !== 'trigger-hover' && openMethod !== 'touch',
mounted,
open,
referenceElement: positionerElement
});
const computedRestMs = delay;
const hover = (0, _floatingUiReact.useHover)(floatingContext, {
enabled: openOnHover && (openMethod !== 'touch' || openReason !== 'trigger-press'),
mouseOnly: true,
move: false,
handleClose: (0, _floatingUiReact.safePolygon)({
blockPointerEvents: true
}),
restMs: computedRestMs,
delay: {
close: closeDelay
}
});
const click = (0, _floatingUiReact.useClick)(floatingContext, {
stickIfOpen
});
const dismiss = (0, _floatingUiReact.useDismiss)(floatingContext, {
outsidePressEvent: {
mouse: 'intentional',
touch: 'sloppy'
}
});
const role = (0, _floatingUiReact.useRole)(floatingContext);
const {
getReferenceProps,
getFloatingProps
} = (0, _floatingUiReact.useInteractions)([hover, click, dismiss, role]);
const popoverContext = React.useMemo(() => ({
open,
setOpen,
mounted,
setMounted,
transitionStatus,
triggerElement,
setTriggerElement,
positionerElement,
setPositionerElement,
popupRef,
titleId,
setTitleId,
descriptionId,
setDescriptionId,
triggerProps: (0, _mergeProps.mergeProps)(getReferenceProps(), triggerProps),
popupProps: getFloatingProps(),
floatingRootContext: floatingContext,
instantType,
openMethod,
openReason,
onOpenChangeComplete,
openOnHover,
delay,
closeDelay,
modal
}), [open, setOpen, mounted, setMounted, transitionStatus, positionerElement, titleId, descriptionId, getReferenceProps, triggerElement, triggerProps, getFloatingProps, floatingContext, instantType, openMethod, openReason, onOpenChangeComplete, openOnHover, delay, closeDelay, modal]);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_PopoverRootContext.PopoverRootContext.Provider, {
value: popoverContext,
children: props.children
});
}
/**
* Groups all parts of the popover.
* Doesn’t render its own HTML element.
*
* Documentation: [Base UI Popover](https://base-ui.com/react/components/popover)
*/
function PopoverRoot(props) {
if ((0, _PopoverRootContext.usePopoverRootContext)(true)) {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(PopoverRootComponent, {
props: props
});
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingUiReact.FloatingTree, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(PopoverRootComponent, {
props: props
})
});
}