This commit is contained in:
2026-03-08 14:27:16 +03:00
parent 66c151653e
commit 11b58b68c3
22 changed files with 4652 additions and 204 deletions

63
app/hooks/useFocusTrap.ts Normal file
View File

@@ -0,0 +1,63 @@
import { useEffect, RefObject } from 'react';
/**
* Hook to trap focus within a container element
* Useful for modals, drawers, and overlays
*
* @param containerRef - Reference to the container element
* @param isActive - Whether the focus trap is active
*/
export function useFocusTrap(
containerRef: RefObject<HTMLElement>,
isActive: boolean
) {
useEffect(() => {
if (!isActive || !containerRef.current) return;
const container = containerRef.current;
// Get all focusable elements
const focusableElements = container.querySelectorAll<HTMLElement>(
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
);
if (focusableElements.length === 0) return;
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
// Store the element that had focus before the trap
const previouslyFocusedElement = document.activeElement as HTMLElement;
// Focus the first element
firstElement.focus();
const handleTabKey = (e: KeyboardEvent) => {
if (e.key !== 'Tab') return;
if (e.shiftKey) {
// Shift + Tab
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
}
} else {
// Tab
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
};
container.addEventListener('keydown', handleTabKey);
// Cleanup: restore focus to previously focused element
return () => {
container.removeEventListener('keydown', handleTabKey);
if (previouslyFocusedElement) {
previouslyFocusedElement.focus();
}
};
}, [containerRef, isActive]);
}