@blueprintjs/core
Version:
Core styles & components
78 lines • 3.94 kB
JavaScript
;
/* !
* (c) Copyright 2026 Palantir Technologies Inc. All rights reserved.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePopover = usePopover;
const react_1 = require("@floating-ui/react");
const react_2 = require("react");
function usePopover({ autoUpdateOptions, disabled = false, isControlled = false, isOpen = false, middleware, placement, positioningStrategy = "absolute", onOpenChange, } = {}) {
const [isOpenState, setIsOpenState] = (0, react_2.useState)(isOpen);
(0, react_2.useEffect)(() => {
setIsOpenState(isOpen);
}, [isOpen]);
const handleOpenChange = (0, react_2.useCallback)((nextOpen, event) => {
// Only update internal state for uncontrolled components
if (!isControlled) {
setIsOpenState(nextOpen);
}
// Always call the external callback if provided
if (onOpenChange) {
onOpenChange(nextOpen, event);
}
}, [onOpenChange, isControlled]);
// Store options in a ref so the memoized callback always reads the latest
// values without changing its identity on every render.
const autoUpdateOptionsRef = (0, react_2.useRef)(autoUpdateOptions);
autoUpdateOptionsRef.current = autoUpdateOptions;
const whileElementsMounted = (0, react_2.useMemo)(() => autoUpdateOptions != null
? (reference, floating, update) => (0, react_1.autoUpdate)(reference, floating, update, autoUpdateOptionsRef.current)
: react_1.autoUpdate,
// Only change identity when toggling between with/without options;
// actual option values are read from the ref at call time.
// eslint-disable-next-line react-hooks/exhaustive-deps
[autoUpdateOptions != null]);
const data = (0, react_1.useFloating)({
middleware,
onOpenChange: handleOpenChange,
open: isOpenState,
placement,
strategy: positioningStrategy,
whileElementsMounted,
});
const { context } = data;
const click = (0, react_1.useClick)(context, {
enabled: !disabled,
// Disable Floating UI's built-in Space/Enter keyboard handlers because they
// call `preventDefault()` on the Space keydown event to prevent page scrolling.
// This also prevents space characters from being typed in <input>/<textarea>
// elements that are children of the target wrapper element.
// See: https://github.com/palantir/blueprint/pull/7997
//
// PopoverTarget provides its own keyboard click handling that avoids this issue
// by skipping typeable elements while still maintaining keyboard accessibility
// for non-typeable targets.
keyboardHandlers: false,
});
const dismiss = (0, react_1.useDismiss)(context, {
// Escape handling lives on Overlay2 (`canEscapeKeyClose`), which is stack-aware via
// OverlayContext so only the topmost overlay closes per keystroke. Floating UI's
// `useDismiss` attaches a non-stack-aware document keydown listener — enabling both
// means every stacked popover/tooltip closes on a single Escape.
escapeKey: false,
// Outside-press handling lives on Overlay2 (`canOutsideClickClose`), which is
// stack-aware via OverlayContext so clicks inside portaled child overlays (e.g. a
// Dialog rendered in popover content) don't incorrectly close the popover. Floating
// UI's `useDismiss` treats those clicks as "outside" the popover because the child
// overlay is rendered outside the popover's DOM subtree.
outsidePress: false,
});
const interactions = (0, react_1.useInteractions)([click, dismiss]);
return (0, react_2.useMemo)(() => ({
isOpen: isOpenState,
setIsOpen: setIsOpenState,
...interactions,
...data,
}), [data, interactions, isOpenState]);
}
//# sourceMappingURL=usePopover.js.map