UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

81 lines (79 loc) 2.7 kB
/** * AuraGlass Focus Utilities * Keyboard navigation and focus management helpers */ /** * Get all focusable elements within a container */ function getFocusableElements(container) { const focusableSelectors = ['a[href]:not([disabled])', 'button:not([disabled])', 'input:not([disabled]):not([type="hidden"])', 'select:not([disabled])', 'textarea:not([disabled])', '[tabindex]:not([tabindex="-1"])', '[contenteditable="true"]', 'audio[controls]', 'video[controls]', 'details > summary'].join(', '); return Array.from(container.querySelectorAll(focusableSelectors)).filter(el => { // Check if element is visible const styles = window.getComputedStyle(el); return styles.display !== 'none' && styles.visibility !== 'hidden' && styles.opacity !== '0'; }); } /** * Trap focus within a container */ function trapFocus(container, options = {}) { const focusableElements = getFocusableElements(container); const firstFocusable = focusableElements[0]; const lastFocusable = focusableElements[focusableElements.length - 1]; // Store previously focused element const previouslyFocused = document.activeElement; // Set initial focus if (options.initialFocus?.current) { options.initialFocus.current.focus(); } else if (firstFocusable) { firstFocusable.focus(); } // Handle tab navigation const handleKeyDown = e => { if (e.key === 'Tab') { if (focusableElements.length === 0) { e.preventDefault(); return; } if (e.shiftKey) { // Shift + Tab if (document.activeElement === firstFocusable) { e.preventDefault(); lastFocusable?.focus(); } } else { // Tab if (document.activeElement === lastFocusable) { e.preventDefault(); firstFocusable?.focus(); } } } // Handle escape if (options.escapeDeactivates && e.key === 'Escape') { releaseFocus(); } }; // Handle clicks outside const handleClickOutside = e => { if (!options.allowOutsideClick && !container.contains(e.target)) { e.preventDefault(); e.stopPropagation(); firstFocusable?.focus(); } }; // Add event listeners document.addEventListener('keydown', handleKeyDown); document.addEventListener('mousedown', handleClickOutside); // Release function const releaseFocus = () => { document.removeEventListener('keydown', handleKeyDown); document.removeEventListener('mousedown', handleClickOutside); if (options.returnFocus && previouslyFocused) { previouslyFocused.focus(); } }; return releaseFocus; } export { getFocusableElements, trapFocus }; //# sourceMappingURL=focus.js.map