@gravity-ui/uikit
Version:
Gravity UI base styling and components
151 lines (150 loc) • 8.39 kB
JavaScript
'use client';
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Popup = Popup;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const React = tslib_1.__importStar(require("react"));
const react_1 = require("@floating-ui/react");
const hooks_1 = require("../../hooks/index.js");
const private_1 = require("../../hooks/private/index.js");
const Portal_1 = require("../Portal/index.js");
const cn_1 = require("../utils/cn.js");
const filterDOMProps_1 = require("../utils/filterDOMProps.js");
const layer_manager_1 = require("../utils/layer-manager/index.js");
const PopupArrow_1 = require("./PopupArrow.js");
const constants_1 = require("./constants.js");
const i18n_1 = tslib_1.__importDefault(require("./i18n/index.js"));
const utils_1 = require("./utils.js");
require("./Popup.css");
const b = (0, cn_1.block)('popup');
function PopupComponent({ keepMounted = false, hasArrow = false, open = false, onOpenChange, strategy, placement: placementProp, offset: offsetProp = 4, anchorElement, anchorRef, floatingMiddlewares, floatingContext, floatingInteractions, floatingRef, floatingStyles: floatingStylesProp, floatingClassName, modal = false, initialFocus: initialFocusProp, returnFocus = true, focusOrder, disableVisuallyHiddenDismiss = !modal, onClose, onEscapeKeyDown, onOutsideClick, disableEscapeKeyDown = false, disableOutsideClick = false, disableFocusOut = false, style, className, children, disablePortal = false, disableLayer = false, disableTransition = false, qa, role: roleProp, zIndex = 1000, onTransitionIn, onTransitionOut, onTransitionInComplete, onTransitionOutComplete, ...restProps }) {
(0, layer_manager_1.useLayer)({ open, type: 'popup', enabled: !disableLayer });
const contentRef = React.useRef(null);
const [arrowElement, setArrowElement] = React.useState(null);
const { offset } = (0, utils_1.getOffsetOptions)(offsetProp, hasArrow);
const { placement, middleware: placementMiddleware } = (0, utils_1.getPlacementOptions)(placementProp, disablePortal);
const handleOpenChange = React.useCallback((isOpen, event, reason) => {
onOpenChange?.(isOpen, event, reason);
if (isOpen || !event) {
return;
}
let closeReason;
if (reason === 'escape-key') {
closeReason = 'escapeKeyDown';
}
else if (reason === 'outside-press') {
closeReason = 'outsideClick';
}
else {
closeReason = reason;
}
if (closeReason === 'escapeKeyDown' && onEscapeKeyDown) {
onEscapeKeyDown(event);
}
if (closeReason === 'outsideClick' && onOutsideClick) {
onOutsideClick(event);
}
onClose?.(event, closeReason);
}, [onOpenChange, onClose, onEscapeKeyDown, onOutsideClick]);
const floatingNodeId = (0, react_1.useFloatingNodeId)();
const { refs, elements, floatingStyles, placement: finalPlacement, middlewareData, context, update, } = (0, react_1.useFloating)({
rootContext: floatingContext,
nodeId: floatingNodeId,
strategy,
placement: placement,
open,
onOpenChange: handleOpenChange,
middleware: floatingMiddlewares ?? [
(0, react_1.offset)(offset),
(0, react_1.shift)({
padding: constants_1.OVERFLOW_PADDING,
// Offset 22 is size of the arrow (18) + padding (4)
limiter: (0, react_1.limitShift)({ offset: 4 + (hasArrow ? 18 : 0) }),
altBoundary: disablePortal,
}),
placementMiddleware,
hasArrow && (0, react_1.arrow)({ element: arrowElement, padding: 4 }),
hasArrow && (0, utils_1.arrowStylesMiddleware)(),
],
});
React.useEffect(() => {
const element = anchorElement === undefined ? anchorRef?.current : anchorElement;
if (element !== undefined && element !== refs.reference.current) {
refs.setReference(element);
}
}, [anchorElement, anchorRef, refs]);
const role = (0, react_1.useRole)(context, {
enabled: Boolean(roleProp || modal),
role: roleProp ?? (modal ? 'dialog' : undefined),
});
const dismiss = (0, react_1.useDismiss)(context, {
enabled: !disableOutsideClick || !disableEscapeKeyDown,
outsidePress: !disableOutsideClick,
escapeKey: !disableEscapeKeyDown,
});
const { getFloatingProps } = (0, react_1.useInteractions)(floatingInteractions ?? [role, dismiss]);
const { isMounted, status } = (0, react_1.useTransitionStatus)(context, {
duration: disableTransition ? 0 : constants_1.TRANSITION_DURATION,
});
const previousStatus = (0, private_1.usePrevious)(status);
React.useEffect(() => {
if (isMounted && elements.reference && elements.floating) {
return (0, react_1.autoUpdate)(elements.reference, elements.floating, update);
}
return undefined;
}, [isMounted, elements, update]);
const handleFloatingRef = (0, hooks_1.useForkRef)(refs.setFloating, floatingRef);
const handleTransitionEnd = React.useCallback((event) => {
// There are two simultaneous transitions running at the same time
// Use specific name to only notify once
if (status === 'open' && event.propertyName === 'transform') {
onTransitionInComplete?.();
}
}, [status, onTransitionInComplete]);
// Cannot use transitionend event for these callbacks due to unmounting from the DOM
React.useEffect(() => {
if (status === 'initial' && previousStatus === 'unmounted') {
onTransitionIn?.();
}
if (status === 'close' && previousStatus === 'open') {
onTransitionOut?.();
}
if (status === 'unmounted' && previousStatus === 'close') {
onTransitionOutComplete?.();
}
}, [status, previousStatus, onTransitionIn, onTransitionOut, onTransitionOutComplete]);
let initialFocus = initialFocusProp;
if (initialFocus === undefined) {
if (modal) {
initialFocus = refs.floating;
}
else {
initialFocus = -1;
}
}
return ((0, jsx_runtime_1.jsx)(react_1.FloatingNode, { id: floatingNodeId, children: isMounted || keepMounted ? ((0, jsx_runtime_1.jsx)(Portal_1.Portal, { disablePortal: disablePortal, children: (0, jsx_runtime_1.jsx)(react_1.FloatingFocusManager, { context: context, disabled: !isMounted, modal: modal, initialFocus: initialFocus, returnFocus: returnFocus, closeOnFocusOut: !disableFocusOut, visuallyHiddenDismiss: disableVisuallyHiddenDismiss ? false : (0, i18n_1.default)('close'), guards: modal || !disablePortal, order: focusOrder, children: (0, jsx_runtime_1.jsx)("div", { ref: handleFloatingRef, className: floatingClassName, style: {
position: 'absolute',
top: 0,
left: 0,
zIndex,
width: 'max-content',
pointerEvents: isMounted ? 'auto' : 'none',
outline: 'none',
...floatingStyles,
...floatingStylesProp,
}, "data-floating-ui-placement": finalPlacement, "data-floating-ui-status": status, "aria-modal": modal && isMounted ? true : undefined, ...getFloatingProps({
onTransitionEnd: handleTransitionEnd,
}), children: (0, jsx_runtime_1.jsxs)("div", { ref: contentRef, className: b({
open: isMounted,
'disable-transition': disableTransition,
}, className), style: style, "data-qa": qa, ...(0, filterDOMProps_1.filterDOMProps)(restProps), children: [hasArrow && ((0, jsx_runtime_1.jsx)(PopupArrow_1.PopupArrow, { ref: setArrowElement, styles: middlewareData.arrowStyles })), children] }) }) }) })) : null }));
}
function Popup(props) {
const parentId = (0, react_1.useFloatingParentNodeId)();
if (parentId === null) {
return ((0, jsx_runtime_1.jsx)(react_1.FloatingTree, { children: (0, jsx_runtime_1.jsx)(PopupComponent, { ...props }) }));
}
return (0, jsx_runtime_1.jsx)(PopupComponent, { ...props });
}
//# sourceMappingURL=Popup.js.map