2025-09-30 12:39:24 +00:00
|
|
|
import { onKeydown } from '$lib/actions/input';
|
2024-03-15 21:00:53 +01:00
|
|
|
import type { Action } from 'svelte/action';
|
|
|
|
|
|
2024-09-26 21:41:22 -04:00
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2024-11-14 08:43:25 -06:00
|
|
|
export const listNavigation: Action<HTMLElement, HTMLElement | undefined> = (
|
|
|
|
|
node: HTMLElement,
|
|
|
|
|
container?: HTMLElement,
|
|
|
|
|
) => {
|
2024-03-15 21:00:53 +01:00
|
|
|
const moveFocus = (direction: 'up' | 'down') => {
|
2024-11-14 08:43:25 -06:00
|
|
|
if (!container) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-15 21:00:53 +01:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-09-30 12:39:24 +00:00
|
|
|
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;
|
|
|
|
|
};
|
2024-03-15 21:00:53 +01:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
update(newContainer) {
|
|
|
|
|
container = newContainer;
|
|
|
|
|
},
|
|
|
|
|
destroy,
|
|
|
|
|
};
|
|
|
|
|
};
|