1 | export function focusTrap(node: HTMLElement) { |
2 | const focusableSelectors = ['a[href]', 'button', 'input', 'textarea', 'select', '[tabindex]:not([tabindex="-1"])']; |
3 |
|
4 | function getFocusableElements() { |
5 | return Array.from(node.querySelectorAll<HTMLElement>(focusableSelectors.join(','))).filter( |
6 | (el) => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden') |
7 | ); |
8 | } |
9 |
|
10 | function handleKeyDown(event: KeyboardEvent) { |
11 | if (event.key !== 'Tab') return; |
12 |
|
13 | const focusableElements = getFocusableElements(); |
14 | const firstElement = focusableElements[0]; |
15 | const lastElement = focusableElements[focusableElements.length - 1]; |
16 |
|
17 | if (event.shiftKey && document.activeElement === firstElement) { |
18 | event.preventDefault(); |
19 | lastElement?.focus(); |
20 | } else if (!event.shiftKey && document.activeElement === lastElement) { |
21 | event.preventDefault(); |
22 | firstElement?.focus(); |
23 | } |
24 | } |
25 |
|
26 | $effect(() => { |
27 | const focusableElements = getFocusableElements(); |
28 | focusableElements[0]?.focus(); |
29 |
|
30 | node.addEventListener('keydown', handleKeyDown); |
31 |
|
32 | return () => { |
33 | node.removeEventListener('keydown', handleKeyDown); |
34 | }; |
35 | }); |
36 | } |
37 |
|