mirror of
https://github.com/immich-app/immich.git
synced 2025-06-02 21:24:28 -04:00
* feat(web, a11y): focus management for modals and popups * feat: hide asset options dropdown on escape key
63 lines
1.6 KiB
Svelte
63 lines
1.6 KiB
Svelte
<script lang="ts">
|
|
import { shortcuts } from '$lib/utils/shortcut';
|
|
import { onMount, onDestroy } from 'svelte';
|
|
|
|
let container: HTMLElement;
|
|
let triggerElement: HTMLElement;
|
|
|
|
onMount(() => {
|
|
triggerElement = document.activeElement as HTMLElement;
|
|
const focusableElements = getFocusableElements();
|
|
focusableElements[0]?.focus();
|
|
});
|
|
|
|
onDestroy(() => {
|
|
triggerElement?.focus();
|
|
});
|
|
|
|
const getFocusableElements = () => {
|
|
return Array.from(
|
|
container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),
|
|
) as HTMLElement[];
|
|
};
|
|
|
|
const trapFocus = (direction: 'forward' | 'backward', event: KeyboardEvent) => {
|
|
const focusableElements = getFocusableElements();
|
|
const elementCount = focusableElements.length;
|
|
const firstElement = focusableElements[0];
|
|
const lastElement = focusableElements.at(elementCount - 1);
|
|
|
|
if (document.activeElement === lastElement && direction === 'forward') {
|
|
event.preventDefault();
|
|
firstElement?.focus();
|
|
} else if (document.activeElement === firstElement && direction === 'backward') {
|
|
event.preventDefault();
|
|
lastElement?.focus();
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<div
|
|
bind:this={container}
|
|
use:shortcuts={[
|
|
{
|
|
ignoreInputFields: false,
|
|
shortcut: { key: 'Tab' },
|
|
onShortcut: (event) => {
|
|
trapFocus('forward', event);
|
|
},
|
|
preventDefault: false,
|
|
},
|
|
{
|
|
ignoreInputFields: false,
|
|
shortcut: { key: 'Tab', shift: true },
|
|
onShortcut: (event) => {
|
|
trapFocus('backward', event);
|
|
},
|
|
preventDefault: false,
|
|
},
|
|
]}
|
|
>
|
|
<slot />
|
|
</div>
|