UNPKG

@clayui/shared

Version:
236 lines (235 loc) 8.31 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var useFocusManagement_exports = {}; __export(useFocusManagement_exports, { FOCUSABLE_ELEMENTS: () => FOCUSABLE_ELEMENTS, isFocusable: () => isFocusable, useFocusManagement: () => useFocusManagement }); module.exports = __toCommonJS(useFocusManagement_exports); var import_react = __toESM(require("react")); const HostComponent = 5; let minimalTabIndex = 0; function isFocusable({ contentEditable, disabled, href, offsetParent, rel, tabIndex, tagName, type }) { tagName = tagName?.toLowerCase(); if (!offsetParent) { return false; } if (disabled) { return false; } if (tabIndex !== null && tabIndex !== void 0 && tabIndex < minimalTabIndex) { return false; } if (tabIndex !== null && tabIndex !== void 0 && tabIndex >= minimalTabIndex || contentEditable === true || contentEditable === "true") { return true; } if (tagName === "a" || tagName === "area") { return !!href && rel !== "ignore"; } if (tagName === "input") { return type !== "file" && type !== "hidden"; } return tagName === "button" || tagName === "embed" || tagName === "iframe" || tagName === "object" || tagName === "select" || tagName === "textarea"; } const FOCUS_SCOPE_MARKERS = [ 'span[data-focus-scope-end="true"]', 'span[data-focus-scope-start="true"]' ]; const FOCUSABLE_ELEMENTS = [ "a[href]", "[contenteditable]", '[tabindex]:not([tabindex^="-"])', "area[href]", "button:not([disabled])", "embed", "iframe", 'input:not([disabled]):not([type="hidden"])', "object", "select:not([disabled]):not([aria-hidden])", "textarea:not([disabled]):not([aria-hidden])" ]; let hasSibling = false; function collectDocumentFocusTargets() { const focusTargets = [...FOCUSABLE_ELEMENTS, ...FOCUS_SCOPE_MARKERS]; return Array.from( document.querySelectorAll(focusTargets.join(",")) ).filter((element) => { const isFocusScopeMarker = element.dataset["focusScopeEnd"] || element.dataset["focusScopeStart"]; if (isFocusable(element) || isFocusScopeMarker) { return window.getComputedStyle(element).visibility !== "hidden"; } return false; }); } function isFiberFocusable(fiber) { const { memoizedProps, stateNode, type } = fiber; if (memoizedProps === null) { return false; } return isFocusable({ contentEditable: memoizedProps.contentEditable, disabled: memoizedProps.disabled, href: memoizedProps.href, offsetParent: stateNode.offsetParent, rel: memoizedProps.rel, tabIndex: memoizedProps.tabIndex, tagName: type, type: memoizedProps.type }); } function isFiberFocusScopeMarker(fiber) { return fiber.stateNode.dataset["focusScopeEnd"] || fiber.stateNode.dataset["focusScopeStart"]; } function collectFocusTargets(node, focusTargets) { const isFiberFocusTarget = node.tag === HostComponent && (isFiberFocusable(node) || isFiberFocusScopeMarker(node)); if (isFiberFocusTarget) { focusTargets.push(node.stateNode); } const child = node.child; if (child !== null) { collectFocusTargets(child, focusTargets); } const sibling = node.sibling; if (sibling) { hasSibling = true; collectFocusTargets(sibling, focusTargets); } } function getFiber(scope) { if (!scope.current) { return null; } const internalKey = Object.keys(scope.current).find( (key) => key.indexOf("__reactInternalInstance") === 0 || key.indexOf("__reactFiber") === 0 ); if (internalKey) { return scope.current[internalKey]; } return null; } function getFocusTargetsInScope(fiberNode) { const focusTargets = []; const { child } = fiberNode; if (child !== null) { collectFocusTargets(child, focusTargets); } return focusTargets; } function useFocusManagement(scope) { const nextFocusInDocRef = import_react.default.useRef(null); const prevFocusInDocRef = import_react.default.useRef(null); const moveFocusInScope = (scope2, backwards = false, persistOnScope = false) => { let fiberFocusTargets = getFocusTargetsInScope( scope2.alternate ?? scope2 ); if (!hasSibling) { fiberFocusTargets = getFocusTargetsInScope(scope2); } else { hasSibling = false; } if (!fiberFocusTargets.length) { return null; } const activeElement = document.activeElement; if (!activeElement) { return; } const docFocusTargets = collectDocumentFocusTargets(); const docPosition = docFocusTargets.indexOf(activeElement); const reactFiberPosition = fiberFocusTargets.indexOf(activeElement); const startFocusTrap = fiberFocusTargets.find( (element) => element.getAttribute("data-focus-scope-start") === "true" ); const endFocusTrap = fiberFocusTargets.find( (element) => element.getAttribute("data-focus-scope-end") === "true" ); const nextFocusInDoc = docFocusTargets[docPosition + 1]; const prevFocusInDoc = docFocusTargets[docPosition - 1]; if (reactFiberPosition < 0 && !prevFocusInDocRef.current && !nextFocusInDocRef.current && nextFocusInDoc !== endFocusTrap && prevFocusInDoc !== startFocusTrap) { return null; } let nextFocusInFiber = fiberFocusTargets[reactFiberPosition + 1]; let prevFocusInFiber = fiberFocusTargets[reactFiberPosition - 1]; if (startFocusTrap && endFocusTrap && startFocusTrap !== prevFocusInDoc && endFocusTrap !== nextFocusInDoc) { return null; } if (endFocusTrap && endFocusTrap === nextFocusInDoc) { nextFocusInFiber = docFocusTargets.find( (_, index, array) => array[index - 1] === startFocusTrap ); } if (startFocusTrap && startFocusTrap === prevFocusInDoc) { prevFocusInFiber = docFocusTargets.find( (_, index, array) => array[index + 1] === endFocusTrap ); } if (persistOnScope && (!nextFocusInFiber || backwards && !prevFocusInFiber)) { return null; } if (nextFocusInFiber !== nextFocusInDoc) { nextFocusInDocRef.current = nextFocusInDoc; } if (prevFocusInFiber !== prevFocusInDoc) { prevFocusInDocRef.current = prevFocusInDoc; } let nextActive = backwards ? prevFocusInFiber : nextFocusInFiber; if (!nextActive) { nextActive = backwards ? prevFocusInDocRef.current : nextFocusInDocRef.current; } if (nextActive) { nextActive.focus(); if (nextActive === prevFocusInDocRef.current || nextActive === nextFocusInDocRef.current) { nextFocusInDocRef.current = null; prevFocusInDocRef.current = null; } return nextActive; } return null; }; return { focusFirst: () => { minimalTabIndex = -1; const next = moveFocusInScope(getFiber(scope), false, true); minimalTabIndex = 0; return next; }, focusNext: (persistOnScope) => moveFocusInScope(getFiber(scope), false, persistOnScope), focusPrevious: (persistOnScope) => moveFocusInScope(getFiber(scope), true, persistOnScope) }; }