Files
Stirling-PDF/frontend/src/core/hooks/useFocusTrap.ts
T
James BruntonandGitHub d2b38ef4b8 Restructure frontend code to allow for extensions (#4721)
# Description of Changes
Move frontend code into `core` folder and add infrastructure for
`proprietary` folder to include premium, non-OSS features
2025-10-28 10:29:36 +00:00

77 lines
2.0 KiB
TypeScript

import { useEffect, RefObject } from 'react';
const FOCUSABLE_ELEMENTS = [
'a[href]',
'button:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"])',
].join(', ');
export function useFocusTrap(containerRef: RefObject<HTMLElement | null>, enabled: boolean = true) {
useEffect(() => {
if (!enabled || !containerRef.current) {
return;
}
const container = containerRef.current;
const getFocusableElements = () =>
Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_ELEMENTS));
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key !== 'Tab') {
return;
}
const focusableElements = getFocusableElements();
if (focusableElements.length === 0) {
return;
}
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
const activeElement = document.activeElement as HTMLElement;
// Check if focus is within the container
if (!container.contains(activeElement)) {
event.preventDefault();
firstElement.focus();
return;
}
// Shift + Tab (backwards)
if (event.shiftKey) {
if (activeElement === firstElement) {
event.preventDefault();
lastElement.focus();
}
}
// Tab (forwards)
else {
if (activeElement === lastElement) {
event.preventDefault();
firstElement.focus();
}
}
};
// Focus first element on mount
const focusableElements = getFocusableElements();
if (focusableElements.length > 0) {
// Small delay to ensure the element is fully rendered
setTimeout(() => {
focusableElements[0]?.focus();
}, 100);
}
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [containerRef, enabled]);
}