UNPKG

@yandex/ui

Version:

Yandex UI components

107 lines (106 loc) 5.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LayerManager = void 0; var tslib_1 = require("tslib"); var react_1 = tslib_1.__importStar(require("react")); var usePreviousValue_1 = require("../usePreviousValue"); var useUniqId_1 = require("../useUniqId"); /** * Компонент реализующий закрытие всплывающих компонентов, * таких как `Popup`, `Modal`, `Tooltip` и `MessageBox` в нужном порядке, * по умолчанию используется внутри `Popup`. * * @param {LayerManagerProps} */ var LayerManager = function (_a) { var visible = _a.visible, onClose = _a.onClose, children = _a.children, essentialRefs = _a.essentialRefs; var id = useUniqId_1.useUniqId('layer'); var prevVisible = usePreviousValue_1.usePreviousValue(visible); var prevOnClose = usePreviousValue_1.usePreviousValue(onClose); var mouseDownRef = react_1.useRef(null); var onDocumentKeyUp = react_1.useCallback(function (event) { var key = event.key; // @fixme: ISL-9529: keyboard.ts: использовать библиотеку для клавиатурных событий if (key === 'Escape' || key === 'Esc') { var _a = tslib_1.__read(exports.LayerManager.stack[exports.LayerManager.stack.length - 1] || [], 2), layerId = _a[0], layerOnClose = _a[1]; // Дополнительно проверяем id слоя, чтобы не вызывать layerOnClose n-раз. if (layerId === id && layerOnClose !== undefined) { layerOnClose(event, 'esc'); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); var onDocumentMouseDown = react_1.useCallback(function (event) { mouseDownRef.current = event.target; }, []); var onDocumentClick = react_1.useCallback(function (event) { var _a = tslib_1.__read(exports.LayerManager.stack[exports.LayerManager.stack.length - 1] || [], 3), layerId = _a[0], layerOnClose = _a[1], refs = _a[2]; // Убеждаемся, что элемент, который был нажат, совпадает с последним // при срабатывании события mousedown. Это предотвращает закрытие диалогового окна // перетаскиванием курсора (например, выделением текста внутри диалогового окна // и отпусканием мыши за его пределами). if (mouseDownRef.current !== event.target) { return; } // Дополнительно проверяем id слоя, чтобы не вызывать layerOnClose n-раз. if (layerId === id && layerOnClose !== undefined && refs !== undefined) { var isEssentionalClick = refs .filter(function (ref) { return ref.current !== null; }) .some(function (ref) { return ref.current.contains(event.target); }); if (!isEssentionalClick) { layerOnClose(event, 'click'); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); react_1.useEffect(function () { if (onClose !== prevOnClose && onClose !== undefined) { exports.LayerManager.stack.forEach(function (_a, i) { var _b = tslib_1.__read(_a, 2), layerOnClose = _b[1]; if (layerOnClose === prevOnClose) { exports.LayerManager.stack[i][1] = onClose; } }); } if (visible === prevVisible || onClose === undefined) { return; } if (visible) { exports.LayerManager.stack.push([id, onClose, essentialRefs]); document.addEventListener('keyup', onDocumentKeyUp); document.addEventListener('mousedown', onDocumentMouseDown, true); document.addEventListener('click', onDocumentClick, true); } else { // Т.к. onCloseHandlers у нас не является стейтом компонента, // то удаление обработчика может произойти раньше, чем его вызов, // поэтому используем raf для удаления в следующем тике. requestAnimationFrame(function () { return removeLayerById(id); }); document.removeEventListener('keyup', onDocumentKeyUp); document.removeEventListener('mousedown', onDocumentMouseDown, true); document.removeEventListener('click', onDocumentClick, true); } // Не добавляем essentialRefs в зависимости, // т.к. они нужны единожды при добавлении в стек. // eslint-disable-next-line react-hooks/exhaustive-deps }, [prevVisible, visible, onClose, prevOnClose]); react_1.useEffect(function () { return function () { requestAnimationFrame(function () { return removeLayerById(id); }); document.removeEventListener('keyup', onDocumentKeyUp); document.removeEventListener('mousedown', onDocumentMouseDown, true); document.removeEventListener('click', onDocumentClick, true); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return react_1.default.createElement(react_1.default.Fragment, null, children); }; exports.LayerManager = LayerManager; exports.LayerManager.stack = []; exports.LayerManager.displayName = 'LayerManager'; function removeLayerById(id) { exports.LayerManager.stack = exports.LayerManager.stack.filter(function (_a) { var _b = tslib_1.__read(_a, 1), layerId = _b[0]; return layerId !== id; }); }