UNPKG

@gravity-ui/uikit

Version:

Gravity UI base styling and components

151 lines (150 loc) 8.39 kB
'use client'; "use strict"; 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