UNPKG

@ozen-ui/kit

Version:

React component library

92 lines (91 loc) 6.09 kB
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';