@ozen-ui/kit
Version:
React component library
92 lines (91 loc) • 6.09 kB
JavaScript
import { __assign, __read, __rest, __spreadArray } from "tslib";
import './Modal.css';
import React, { useRef, useEffect, useState, useCallback, forwardRef, } from 'react';
import { CSSTransition } from 'react-transition-group';
import { isServer } from '../../constants/environment';
import { useClickOutside } from '../../hooks/useClickOutside';
import { useEventListener } from '../../hooks/useEventListener';
import { useFocusTrap } from '../../hooks/useFocusTrap';
import { useLockBodyScroll } from '../../hooks/useLockBodyScroll';
import { useMultiRef } from '../../hooks/useMultiRef';
import { usePortalContainer } from '../../hooks/usePortalContainer';
import { cn } from '../../utils/classname';
import { isKey } from '../../utils/isKey';
import { Backdrop } from '../Backdrop';
import { PortalBase } from '../PortalBase';
import { ModalConsumer } from './components';
import { MODAL_DEFAULT_DISABLE_CLICK_OUTSIDE, MODAL_DEFAULT_DISABLE_SCROLL_LOCK, MODAL_DEFAULT_ESCAPE_KEY_DOWN, MODAL_DEFAULT_HIDE_BACKDROP, MODAL_DEFAULT_KEEP_MOUNTED, MODAL_DEFAULT_OPEN, MODAL_DEFAULT_PORTAL_CONTAINER, } from './constants';
import { useModalManager } from './index';
export var cnModal = cn('Modal');
export var Modal = forwardRef(function (_a, ref) {
var _b = _a.open, open = _b === void 0 ? MODAL_DEFAULT_OPEN : _b, _c = _a.keepMounted, keepMounted = _c === void 0 ? MODAL_DEFAULT_KEEP_MOUNTED : _c, _d = _a.hideBackdrop, hideBackdrop = _d === void 0 ? MODAL_DEFAULT_HIDE_BACKDROP : _d, _e = _a.disableScrollLock, disableScrollLock = _e === void 0 ? MODAL_DEFAULT_DISABLE_SCROLL_LOCK : _e, _f = _a.disableClickOutside, disableClickOutside = _f === void 0 ? MODAL_DEFAULT_DISABLE_CLICK_OUTSIDE : _f, _g = _a.disableEscapeKeyDown, disableEscapeKeyDown = _g === void 0 ? MODAL_DEFAULT_ESCAPE_KEY_DOWN : _g, children = _a.children, onEnter = _a.onEnter, onEntered = _a.onEntered, onExit = _a.onExit, onExitedProp = _a.onExited, onClose = _a.onClose, backdropProps = _a.backdropProps, windowProps = _a.windowProps, transitionProps = _a.transitionProps, className = _a.className, ignoreClickOutsideRefs = _a.ignoreClickOutsideRefs, _h = _a.container, containerProp = _h === void 0 ? MODAL_DEFAULT_PORTAL_CONTAINER : _h, other = __rest(_a, ["open", "keepMounted", "hideBackdrop", "disableScrollLock", "disableClickOutside", "disableEscapeKeyDown", "children", "onEnter", "onEntered", "onExit", "onExited", "onClose", "backdropProps", "windowProps", "transitionProps", "className", "ignoreClickOutsideRefs", "container"]);
var hasBackdrop = !hideBackdrop;
var rootRef = useRef(null);
var windowInnerRef = useRef(null);
var focusedElement = useRef(null);
var _j = __read(useState(false), 2), openState = _j[0], setOpenState = _j[1];
var _k = __read(useState(false), 2), opened = _k[0], setOpened = _k[1];
var container = usePortalContainer(containerProp);
var _l = useModalManager(windowInnerRef, 1000, openState), isTop = _l.isTop, refsClickOutside = _l.refsClickOutside;
var portalMultiRef = useMultiRef([rootRef, ref]);
var trapRef = useFocusTrap({
active: isTop,
focusinTrap: false,
});
var modalConsumerMultiRef = useMultiRef([
windowInnerRef,
trapRef,
windowProps === null || windowProps === void 0 ? void 0 : windowProps.ref,
]);
useClickOutside({
refs: __spreadArray(__spreadArray([
windowInnerRef
], __read(refsClickOutside), false), __read((ignoreClickOutsideRefs || [])), false),
handler: onClose,
active: open && !disableClickOutside,
});
// Блокирует прокрутку основного окна приложения
useLockBodyScroll(opened && !disableScrollLock);
useEffect(function () {
var _a;
// Сохраняем фокус активного элемента до момента открытия всплывающего окна
if (open) {
setOpened(true);
focusedElement.current =
document.activeElement;
(_a = focusedElement.current) === null || _a === void 0 ? void 0 : _a.blur();
}
setOpenState(open);
}, [open]);
var onExited = useCallback(function () {
var _a, _b;
if (isServer) {
return;
}
// Возвращаем фокус активного элемента
if (((_a = windowInnerRef.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 });
}
setOpened(false);
onExitedProp === null || onExitedProp === void 0 ? void 0 : onExitedProp();
}, [onExitedProp]);
// Нажатие клавиши {ESC}
useEventListener({
eventName: 'keydown',
handler: function (event) {
if (!isKey(event, 'Escape'))
return;
onClose === null || onClose === void 0 ? void 0 : onClose();
},
active: isTop && !disableEscapeKeyDown,
});
if (!container) {
return null;
}
return (React.createElement(CSSTransition, __assign({ classNames: cnModal({ animation: true }), nodeRef: rootRef, timeout: 300 }, transitionProps, { in: openState, onEnter: onEnter, onEntered: onEntered, onExit: onExit, onExited: onExited, unmountOnExit: !keepMounted, appear: true }),
React.createElement(PortalBase, __assign({}, other, { container: container, className: cnModal({ hidden: keepMounted && !openState, hasBackdrop: hasBackdrop }, [className]), ref: portalMultiRef }),
!hideBackdrop && (React.createElement(Backdrop, __assign({ zIndex: -1, open: openState }, backdropProps, { className: backdropProps === null || backdropProps === void 0 ? void 0 : backdropProps.className }))),
React.createElement(ModalConsumer, __assign({}, windowProps, { ref: modalConsumerMultiRef }), children))));
});
Modal.displayName = 'Modal';