immich/web/src/lib/actions/list-navigation.ts

52 lines
1.5 KiB
TypeScript
Raw Normal View History

import { onKeydown } from '$lib/actions/input';
import type { Action } from 'svelte/action';
/**
* Enables keyboard navigation (up and down arrows) for a list of elements.
* @param node Element which listens for keyboard events
* @param container Element containing the list of elements
*/
export const listNavigation: Action<HTMLElement, HTMLElement | undefined> = (
node: HTMLElement,
container?: HTMLElement,
) => {
const moveFocus = (direction: 'up' | 'down') => {
if (!container) {
return;
}
const children = Array.from(container?.children);
if (children.length === 0) {
return;
}
const currentIndex = document.activeElement === null ? -1 : children.indexOf(document.activeElement);
const directionFactor = (direction === 'up' ? -1 : 1) + (direction === 'up' && currentIndex === -1 ? 1 : 0);
const newIndex = (currentIndex + directionFactor + children.length) % children.length;
const element = children.at(newIndex);
if (element instanceof HTMLElement) {
element.focus();
}
};
const unregisterUp = onKeydown('ArrowUp', (event) => (event.preventDefault(), moveFocus('up')), {
ignoreInputFields: false,
})(node);
const unregisterDown = onKeydown('ArrowDown', (event) => (event.preventDefault(), moveFocus('down')), {
ignoreInputFields: false,
})(node);
let destroy = () => {
unregisterUp();
unregisterDown();
destroy = () => void 0;
};
return {
update(newContainer) {
container = newContainer;
},
destroy,
};
};