UNPKG

@i-novus/ui-core

Version:

Базовые UI компоненты

84 lines (83 loc) 3.16 kB
import { jsx as _jsx } from "react/jsx-runtime"; import { useEffect, forwardRef, useRef, } from 'react'; import { getActiveElement } from '../../../utils/get-active-dom-node'; import { useForwardedRef } from '../../hooks'; const ALL_TABBABLE_SELECTORS = [ 'a[href]', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]:not([tabindex="-1"])', ].toString(); export const FocusTrap = forwardRef(({ children, initialFocusableSelector, }, ref) => { const firstItemRef = useRef(); const lastItemRef = useRef(); const trapRef = useForwardedRef(ref); // eslint-disable-next-line sonarjs/cognitive-complexity useEffect(() => { const element = trapRef.current; if (!element) { return undefined; } const findFocusableElements = () => { const allFocusableElements = [...element.querySelectorAll(ALL_TABBABLE_SELECTORS)].filter(el => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden')); lastItemRef.current = allFocusableElements[allFocusableElements.length - 1]; // eslint-disable-next-line prefer-destructuring firstItemRef.current = allFocusableElements[0]; }; const observer = new MutationObserver(findFocusableElements); observer.observe(element, { childList: true, subtree: true, }); findFocusableElements(); const firstEnter = (event) => { const focusableElement = initialFocusableSelector ? element.querySelector(initialFocusableSelector) : firstItemRef.current; if (event.shiftKey) { lastItemRef.current?.focus(); event.preventDefault(); } else { focusableElement?.focus(); event.preventDefault(); } }; const loop = (event) => { const activeElement = getActiveElement(); if (event.shiftKey) { if (activeElement === firstItemRef.current) { lastItemRef.current?.focus(); event.preventDefault(); } } else if (activeElement === lastItemRef.current) { firstItemRef.current?.focus(); event.preventDefault(); } }; const handleKeyDown = (event) => { const isTabPressed = (event.code === 'Tab'); if (!isTabPressed) { return; } const activeElement = getActiveElement(); const isContainerActiveElement = element.contains(activeElement); if (isContainerActiveElement) { loop(event); } else { firstEnter(event); } }; document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('keydown', handleKeyDown); observer.disconnect(); }; }, [initialFocusableSelector, trapRef]); return (_jsx("div", { ref: trapRef, children: children })); });