Files
immich/web/src/lib/actions/focus-trap.ts
Michel Heusschen fa64277476 fix(web): focus trap inside portal (#11797)
* fix(web): focus trap inside portal

* fix tests
2024-08-15 04:36:29 -04:00

59 lines
1.7 KiB
TypeScript

import { shortcuts } from '$lib/actions/shortcut';
import { tick } from 'svelte';
const selectors =
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
export function focusTrap(container: HTMLElement) {
const triggerElement = document.activeElement;
const focusableElement = container.querySelector<HTMLElement>(selectors);
// Use tick() to ensure focus trap works correctly inside <Portal />
void tick().then(() => focusableElement?.focus());
const getFocusableElements = (): [HTMLElement | null, HTMLElement | null] => {
const focusableElements = container.querySelectorAll<HTMLElement>(selectors);
return [
focusableElements.item(0), //
focusableElements.item(focusableElements.length - 1),
];
};
const { destroy: destroyShortcuts } = shortcuts(container, [
{
ignoreInputFields: false,
preventDefault: false,
shortcut: { key: 'Tab' },
onShortcut: (event) => {
const [firstElement, lastElement] = getFocusableElements();
if (document.activeElement === lastElement) {
event.preventDefault();
firstElement?.focus();
}
},
},
{
ignoreInputFields: false,
preventDefault: false,
shortcut: { key: 'Tab', shift: true },
onShortcut: (event) => {
const [firstElement, lastElement] = getFocusableElements();
if (document.activeElement === firstElement) {
event.preventDefault();
lastElement?.focus();
}
},
},
]);
return {
destroy() {
destroyShortcuts?.();
if (triggerElement instanceof HTMLElement) {
triggerElement.focus();
}
},
};
}