@ozen-ui/kit
Version:
React component library
168 lines (167 loc) • 11.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Popover = exports.cnPopover = void 0;
var tslib_1 = require("tslib");
var react_1 = tslib_1.__importStar(require("react"));
var react_popper_1 = require("react-popper");
var react_transition_group_1 = require("react-transition-group");
require("./Popover.css");
var environment_1 = require("../../constants/environment");
var useClickOutside_1 = require("../../hooks/useClickOutside");
var useEventListener_1 = require("../../hooks/useEventListener");
var useFocusTrap_1 = require("../../hooks/useFocusTrap");
var useMultiRef_1 = require("../../hooks/useMultiRef");
var usePortalContainer_1 = require("../../hooks/usePortalContainer");
var useThemeProps_1 = require("../../hooks/useThemeProps");
var classname_1 = require("../../utils/classname");
var isKey_1 = require("../../utils/isKey");
var polymorphicComponentWithRef_1 = require("../../utils/polymorphicComponentWithRef");
var Paper_1 = require("../Paper");
var PortalBase_1 = require("../PortalBase");
var components_1 = require("./components");
var constants_1 = require("./constants");
var index_1 = require("./index");
var PopoverContext_1 = require("./PopoverContext");
exports.cnPopover = (0, classname_1.cn)('Popover');
exports.Popover = (0, polymorphicComponentWithRef_1.polymorphicComponentWithRef)(function (inProps, ref) {
var props = (0, useThemeProps_1.useThemeProps)({ props: inProps, name: 'Popover' });
var _a = props.arrow, arrow = _a === void 0 ? constants_1.POPOVER_DEFAULT_ARROW : _a, _b = props.open, open = _b === void 0 ? constants_1.POPOVER_DEFAULT_OPEN : _b, _c = props.disableInteractive, disableInteractive = _c === void 0 ? constants_1.POPOVER_DEFAULT_DISABLE_INTERACTIVE : _c, _d = props.disableEnforceFocus, disableEnforceFocus = _d === void 0 ? constants_1.POPOVER_DEFAULT_DISABLE_ENFORCE_FOCUS : _d, _e = props.disableReturnFocus, disableReturnFocus = _e === void 0 ? constants_1.POPOVER_DEFAULT_DISABLE_RETURN_FOCUS : _e, _f = props.disableClickOutside, disableClickOutside = _f === void 0 ? constants_1.POPOVER_DEFAULT_DISABLE_CLICK_OUTSIDE : _f, _g = props.placement, placement = _g === void 0 ? constants_1.POPOVER_DEFAULT_PLACEMENT : _g, _h = props.strategy, strategy = _h === void 0 ? constants_1.POPOVER_DEFAULT_STRATEGY : _h, _j = props.disablePreventOverflow, disablePreventOverflow = _j === void 0 ? constants_1.POPOVER_DEFAULT_DISABLE_PREVENT_OVERFLOW : _j, _k = props.container, containerProp = _k === void 0 ? constants_1.POPOVER_DEFAULT_PORTAL_CONTAINER : _k, ignoreClickOutsideRefs = props.ignoreClickOutsideRefs, disableEscapeKeyDown = props.disableEscapeKeyDown, arrowProps = props.arrowProps, anchorRef = props.anchorRef, equalAnchorWidth = props.equalAnchorWidth, offset = props.offset, children = props.children, transitionProps = props.transitionProps, onClose = props.onClose, onEnter = props.onEnter, onEntered = props.onEntered, onExit = props.onExit, onExitedProp = props.onExited, className = props.className, position = props.position, style = props.style, setUpdate = props.setUpdate, modifiersProp = props.modifiers, _l = props.as, as = _l === void 0 ? constants_1.POPOVER_DEFAULT_TAG : _l, other = tslib_1.__rest(props, ["arrow", "open", "disableInteractive", "disableEnforceFocus", "disableReturnFocus", "disableClickOutside", "placement", "strategy", "disablePreventOverflow", "container", "ignoreClickOutsideRefs", "disableEscapeKeyDown", "arrowProps", "anchorRef", "equalAnchorWidth", "offset", "children", "transitionProps", "onClose", "onEnter", "onEntered", "onExit", "onExited", "className", "position", "style", "setUpdate", "modifiers", "as"]);
var Tag = (0, react_1.useMemo)(function () {
// eslint-disable-next-line react/display-name
return (0, react_1.forwardRef)(function (props, ref) { return (react_1.default.createElement(Paper_1.Paper, tslib_1.__assign({ radius: "l", shadow: "m", background: "main" }, props, { as: as, ref: ref }))); });
}, [as]);
var _m = tslib_1.__read((0, react_1.useState)(false), 2), openState = _m[0], setOpenState = _m[1];
var focusedElement = (0, react_1.useRef)(null);
var popoverRef = (0, react_1.useRef)(null);
var _o = (0, index_1.usePopoverManager)(popoverRef, 1000, openState), refsClickOutside = _o.refsClickOutside, isTop = _o.isTop;
// Ловушка фокуса
var trapRef = (0, useFocusTrap_1.useFocusTrap)({
active: !disableEnforceFocus && isTop,
focusinTrap: false,
});
var _p = tslib_1.__read((0, react_1.useState)(null), 2), popperElement = _p[0], setPopperElement = _p[1];
var portalRef = (0, useMultiRef_1.useMultiRef)([ref, popoverRef, trapRef, setPopperElement]);
// Нажатие клавиши {ESC}
(0, useEventListener_1.useEventListener)({
eventName: 'keydown',
handler: function (event) {
if (!(0, isKey_1.isKey)(event, 'Escape'))
return;
onClose === null || onClose === void 0 ? void 0 : onClose();
},
active: isTop && !disableEscapeKeyDown,
});
(0, useClickOutside_1.useClickOutside)({
refs: tslib_1.__spreadArray(tslib_1.__spreadArray(tslib_1.__spreadArray([
popoverRef
], tslib_1.__read(refsClickOutside), false), tslib_1.__read((anchorRef ? [anchorRef] : [])), false), tslib_1.__read((ignoreClickOutsideRefs || [])), false),
handler: onClose,
active: openState && !disableClickOutside,
});
var onExited = (0, react_1.useCallback)(function () {
var _a, _b;
if (environment_1.isServer) {
return;
}
// Возвращаем фокус активного элемента
if (!disableReturnFocus &&
(((_a = popoverRef.current) === null || _a === void 0 ? void 0 : _a.contains(document.activeElement)) ||
document.activeElement === document.body)) {
(_b = focusedElement.current) === null || _b === void 0 ? void 0 : _b.focus({ preventScroll: true });
}
onExitedProp === null || onExitedProp === void 0 ? void 0 : onExitedProp();
}, [onExitedProp]);
var modifiers = (0, react_1.useMemo)(function () { return tslib_1.__spreadArray([
// Вычисление стилей позиционирования
{
name: 'computeStyles',
options: {
// Позиционирование через position: absolute
// https://popper.js.org/docs/v2/modifiers/compute-styles/#gpuacceleration
gpuAcceleration: false,
},
},
{ name: 'preventOverflow', enabled: disablePreventOverflow },
// Ширина всплывающего элемента
{
name: 'sameWidth',
enabled: !!equalAnchorWidth,
fn: function (_a) {
var state = _a.state;
if ('popper' in state.styles) {
// eslint-disable-next-line no-param-reassign
state.styles.popper.width = "".concat(state.rects.reference.width, "px");
}
},
effect: function (_a) {
var _b;
var state = _a.state;
// eslint-disable-next-line no-param-reassign
state.elements.popper.style.width = "".concat((_b = state.elements.reference) === null || _b === void 0 ? void 0 : _b.offsetWidth, "px");
},
phase: 'beforeWrite',
requires: ['computeStyles'],
},
{
name: 'arrow',
options: {
padding: 12,
},
},
// Отступы от якорного элемента
{
name: 'offset',
options: {
offset: offset,
},
}
], tslib_1.__read((modifiersProp || [])), false); }, [offset, equalAnchorWidth, modifiersProp]);
// Якорный элемент или координаты
var resolveReferenceElement = (0, react_1.useMemo)(function () {
if (anchorRef === null || anchorRef === void 0 ? void 0 : anchorRef.current)
return anchorRef === null || anchorRef === void 0 ? void 0 : anchorRef.current;
if (position)
return {
getBoundingClientRect: function () {
return {
top: (position === null || position === void 0 ? void 0 : position.y) || 0,
left: (position === null || position === void 0 ? void 0 : position.x) || 0,
bottom: (position === null || position === void 0 ? void 0 : position.y) || 0,
right: (position === null || position === void 0 ? void 0 : position.x) || 0,
width: 0,
height: 0,
};
},
};
return undefined;
}, [anchorRef === null || anchorRef === void 0 ? void 0 : anchorRef.current, position]);
var _q = (0, react_popper_1.usePopper)(resolveReferenceElement, popperElement, {
placement: placement,
strategy: strategy,
modifiers: modifiers,
}), styles = _q.styles, attributes = _q.attributes, update = _q.update;
// Передача метода по перерасчету расположения компонента Popover в родительский компонент
(0, react_1.useEffect)(function () {
setUpdate === null || setUpdate === void 0 ? void 0 : setUpdate(update);
}, [update]);
(0, react_1.useEffect)(function () {
var _a;
// Сохраняем фокус активного элемента до момента открытия всплывающего окна
if (open && !disableReturnFocus) {
focusedElement.current =
document.activeElement;
(_a = focusedElement.current) === null || _a === void 0 ? void 0 : _a.blur();
}
setOpenState(open);
}, [open]);
var container = (0, usePortalContainer_1.usePortalContainer)(containerProp);
if (!container) {
return null;
}
return (react_1.default.createElement(PopoverContext_1.PopoverContext.Provider, { value: { isTop: isTop } },
react_1.default.createElement(react_transition_group_1.CSSTransition, tslib_1.__assign({ nodeRef: popoverRef, classNames: (0, exports.cnPopover)({ animation: true }), timeout: 120 }, transitionProps, { in: openState, onEnter: onEnter, onEntered: onEntered, onExit: onExit, onExited: onExited, unmountOnExit: true }),
react_1.default.createElement(PortalBase_1.PortalBase, tslib_1.__assign({ as: Tag }, other, { style: tslib_1.__assign(tslib_1.__assign({}, style), styles.popper), ref: portalRef, container: container, className: (0, exports.cnPopover)({ disableInteractive: disableInteractive }, [className]) }, attributes.popper),
children,
arrow && (react_1.default.createElement(components_1.PopoverArrow, tslib_1.__assign({}, arrowProps, { style: tslib_1.__assign(tslib_1.__assign({}, arrowProps === null || arrowProps === void 0 ? void 0 : arrowProps.style), styles.arrow), "data-popper-arrow": true })))))));
});
exports.Popover.displayName = 'Popover';