mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
refactor(web): list navigation with keyboard (#7987)
This commit is contained in:
parent
e21c586cc5
commit
997e9c5877
4 changed files with 76 additions and 125 deletions
32
web/src/lib/utils/list-navigation.ts
Normal file
32
web/src/lib/utils/list-navigation.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import type { Action } from 'svelte/action';
|
||||
import { shortcuts } from './shortcut';
|
||||
|
||||
export const listNavigation: Action<HTMLElement, HTMLElement> = (node, container: HTMLElement) => {
|
||||
const moveFocus = (direction: 'up' | 'down') => {
|
||||
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 { destroy } = shortcuts(node, [
|
||||
{ shortcut: { key: 'ArrowUp' }, onShortcut: () => moveFocus('up'), ignoreInputFields: false },
|
||||
{ shortcut: { key: 'ArrowDown' }, onShortcut: () => moveFocus('down'), ignoreInputFields: false },
|
||||
]);
|
||||
|
||||
return {
|
||||
update(newContainer) {
|
||||
container = newContainer;
|
||||
},
|
||||
destroy,
|
||||
};
|
||||
};
|
||||
|
|
@ -10,6 +10,7 @@ export type Shortcut = {
|
|||
|
||||
export type ShortcutOptions<T = HTMLElement> = {
|
||||
shortcut: Shortcut;
|
||||
ignoreInputFields?: boolean;
|
||||
onShortcut: (event: KeyboardEvent & { currentTarget: T }) => unknown;
|
||||
};
|
||||
|
||||
|
|
@ -50,11 +51,13 @@ export const shortcuts = <T extends HTMLElement>(
|
|||
options: ShortcutOptions<T>[],
|
||||
): ActionReturn<ShortcutOptions<T>[]> => {
|
||||
function onKeydown(event: KeyboardEvent) {
|
||||
if (shouldIgnoreShortcut(event)) {
|
||||
return;
|
||||
}
|
||||
const ignoreShortcut = shouldIgnoreShortcut(event);
|
||||
|
||||
for (const { shortcut, onShortcut, ignoreInputFields = true } of options) {
|
||||
if (ignoreInputFields && ignoreShortcut) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const { shortcut, onShortcut } of options) {
|
||||
if (matchesShortcut(event, shortcut)) {
|
||||
event.preventDefault();
|
||||
onShortcut(event as KeyboardEvent & { currentTarget: T });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue