UNPKG

@clayui/shared

Version:
149 lines (145 loc) 6.64 kB
"use strict"; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } Object.defineProperty(exports, "__esModule", { value: true }); exports.Overlay = Overlay; var _ariaHidden = require("aria-hidden"); var _react = _interopRequireWildcard(require("react")); var _Keys = require("./Keys"); var _Portal = require("./Portal"); var _useInteractOutside = require("./useInteractOutside"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) { if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } } return n.default = e, t && t.set(e, n), n; } /** * SPDX-FileCopyrightText: © 2022 Liferay, Inc. <https://liferay.com> * SPDX-License-Identifier: BSD-3-Clause */ var overlayStack = []; /** * Overlay component is used for components like dialog and modal. * For example, Autocomplete, DatePicker, ColorPicker, DropDown are components * that can use and have the same consistent behavior. */ function Overlay(_ref) { var children = _ref.children, inert = _ref.inert, _ref$isCloseOnInterac = _ref.isCloseOnInteractOutside, isCloseOnInteractOutside = _ref$isCloseOnInterac === void 0 ? false : _ref$isCloseOnInterac, _ref$isKeyboardDismis = _ref.isKeyboardDismiss, isKeyboardDismiss = _ref$isKeyboardDismis === void 0 ? false : _ref$isKeyboardDismis, _ref$isModal = _ref.isModal, isModal = _ref$isModal === void 0 ? false : _ref$isModal, _ref$isOpen = _ref.isOpen, isOpen = _ref$isOpen === void 0 ? false : _ref$isOpen, menuClassName = _ref.menuClassName, menuRef = _ref.menuRef, onClose = _ref.onClose, portalRef = _ref.portalRef, suppress = _ref.suppress, triggerRef = _ref.triggerRef; var unsuppressCallbackRef = (0, _react.useRef)(null); var onHide = (0, _react.useCallback)(function (action) { if (overlayStack[overlayStack.length - 1] === menuRef) { onClose(action); } }, [onClose]); useEvent('focus', (0, _react.useCallback)(function (event) { var _portalRef$current; if (portalRef && !((_portalRef$current = portalRef.current) !== null && _portalRef$current !== void 0 && _portalRef$current.contains(event.target)) && triggerRef.current && !triggerRef.current.contains(event.target)) { onHide('blur'); } }, [onHide]), isOpen, true, [isOpen, onHide]); useEvent('keydown', (0, _react.useCallback)(function (event) { if (event.key === _Keys.Keys.Esc && overlayStack[overlayStack.length - 1] === menuRef) { event.stopImmediatePropagation(); event.preventDefault(); if (triggerRef.current) { // When inert is used to suppress user interaction with the rest of // the document, to retrieve the focus in the trigger we need to // first undo and then move the focus. if (unsuppressCallbackRef.current) { unsuppressCallbackRef.current(); unsuppressCallbackRef.current = null; } triggerRef.current.focus(); } onClose('escape'); } }, [onClose]), isOpen && isKeyboardDismiss, true, [isOpen, onClose]); (0, _useInteractOutside.useInteractOutside)({ isDisabled: isOpen ? !isCloseOnInteractOutside : true, onInteract: function onInteract() { onHide('blur'); }, onInteractStart: function onInteractStart(event) { if (overlayStack[overlayStack.length - 1] === menuRef) { if (unsuppressCallbackRef.current) { unsuppressCallbackRef.current(); unsuppressCallbackRef.current = null; } if (isModal) { event.stopPropagation(); event.preventDefault(); } } }, ref: portalRef !== null && portalRef !== void 0 ? portalRef : menuRef, triggerRef: triggerRef }); (0, _react.useEffect)(function () { if (isOpen) { overlayStack.push(menuRef); } return function () { var index = overlayStack.indexOf(menuRef); if (index >= 0) { overlayStack.splice(index, 1); } }; }, [isOpen, menuRef]); (0, _react.useEffect)(function () { if (menuRef.current && isOpen) { var elements = suppress ? suppress.map(function (ref) { return ref.current; }) : menuRef.current; // Inert is a new native feature to better handle DOM arias that are not // assertive to a SR or that should ignore any user interaction. // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert if ((isModal || inert) && (0, _ariaHidden.supportsInert)()) { unsuppressCallbackRef.current = (0, _ariaHidden.suppressOthers)(elements); } else { unsuppressCallbackRef.current = (0, _ariaHidden.hideOthers)(elements); } return function () { if (unsuppressCallbackRef.current) { unsuppressCallbackRef.current(); } unsuppressCallbackRef.current = null; }; } }, [isModal, inert, isOpen]); return /*#__PURE__*/_react.default.createElement(_Portal.ClayPortal, { className: menuClassName, subPortalRef: portalRef }, isModal && /*#__PURE__*/_react.default.createElement("span", { "data-focus-scope-start": "true", tabIndex: 0 }), children, isModal && /*#__PURE__*/_react.default.createElement("span", { "data-focus-scope-end": "true", tabIndex: 0 })); } function useEvent(name, onEvent, conditional, capture) { var deps = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : []; (0, _react.useEffect)(function () { // This check should go away when the Overlay is shown using conditional // instead of class CSS. if (conditional) { document.addEventListener(name, onEvent, capture); return function () { document.removeEventListener(name, onEvent, capture); }; } }, deps); }