mirror of
https://github.com/robonen/tools.git
synced 2026-03-20 02:44:45 +00:00
64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
import {
|
|
AUTOFOCUS_ON_MOUNT,
|
|
AUTOFOCUS_ON_UNMOUNT,
|
|
EVENT_OPTIONS,
|
|
focus,
|
|
focusFirst,
|
|
getActiveElement,
|
|
getTabbableCandidates,
|
|
} from '@robonen/platform/browsers';
|
|
import type { FocusScopeAPI } from './stack';
|
|
import type { ShallowRef } from 'vue';
|
|
import { createFocusScopesStack } from './stack';
|
|
import { watchPostEffect } from 'vue';
|
|
|
|
function dispatchCancelableEvent(
|
|
container: HTMLElement,
|
|
eventName: string,
|
|
handler: (ev: Event) => void,
|
|
): CustomEvent {
|
|
const event = new CustomEvent(eventName, EVENT_OPTIONS);
|
|
container.addEventListener(eventName, handler);
|
|
container.dispatchEvent(event);
|
|
container.removeEventListener(eventName, handler);
|
|
return event;
|
|
}
|
|
|
|
export function useAutoFocus(
|
|
container: Readonly<ShallowRef<HTMLElement | null>>,
|
|
focusScope: FocusScopeAPI,
|
|
onMountAutoFocus: (ev: Event) => void,
|
|
onUnmountAutoFocus: (ev: Event) => void,
|
|
) {
|
|
const stack = createFocusScopesStack();
|
|
|
|
watchPostEffect((onCleanup) => {
|
|
const el = container.value;
|
|
if (!el) return;
|
|
|
|
stack.add(focusScope);
|
|
const previouslyFocusedElement = getActiveElement();
|
|
|
|
if (!el.contains(previouslyFocusedElement)) {
|
|
const event = dispatchCancelableEvent(el, AUTOFOCUS_ON_MOUNT, onMountAutoFocus);
|
|
|
|
if (!event.defaultPrevented) {
|
|
focusFirst(getTabbableCandidates(el), { select: true });
|
|
|
|
if (getActiveElement() === previouslyFocusedElement)
|
|
focus(el);
|
|
}
|
|
}
|
|
|
|
onCleanup(() => {
|
|
const event = dispatchCancelableEvent(el, AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);
|
|
|
|
if (!event.defaultPrevented) {
|
|
focus(previouslyFocusedElement ?? document.body, { select: true });
|
|
}
|
|
|
|
stack.remove(focusScope);
|
|
});
|
|
});
|
|
}
|