mirror of
https://github.com/immich-app/immich.git
synced 2026-03-01 10:08:42 +03:00
feat: keyboard navigation to timeline (#17798)
* feat: improve focus * feat: keyboard nav * feat: improve focus * typo * test * fix test * lint * bad merge * lint * inadvertent * lint * fix: flappy e2e test * bad merge and fix tests * use modulus in loop * tests * react to modal dialog refactor * regression due to deferLayout * Review comments * Re-use change-date instead of new component * bad merge * Review comments * rework moveFocus * lint * Fix outline * use Date * Finish up removing/reducing date parsing * lint * title * strings * Rework dates, rework earlier/later algorithm * bad merge * fix tests * Fix race in scroll comp * consolidate scroll methods * Review comments * console.log * Edge cases in scroll compensation * edge case, optimizations * review comments * lint * lint * More edge cases * lint --------- Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com> Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
@@ -12,28 +12,43 @@ export const setDefaultTabbleOptions = (options: TabbableOpts) => {
|
||||
export const getTabbable = (container: Element, includeContainer: boolean = false) =>
|
||||
tabbable(container, { ...defaultOpts, includeContainer });
|
||||
|
||||
export const focusNext = (selector: (element: HTMLElement | SVGElement) => boolean, forwardDirection: boolean) => {
|
||||
const focusElements = focusable(document.body, { includeContainer: true });
|
||||
const current = document.activeElement as HTMLElement;
|
||||
const index = focusElements.indexOf(current);
|
||||
if (index === -1) {
|
||||
for (const element of focusElements) {
|
||||
if (selector(element)) {
|
||||
element.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
focusElements[0].focus();
|
||||
export const moveFocus = (
|
||||
selector: (element: HTMLElement | SVGElement) => boolean,
|
||||
direction: 'previous' | 'next',
|
||||
): void => {
|
||||
const focusableElements = focusable(document.body, { includeContainer: true });
|
||||
|
||||
if (focusableElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
const totalElements = focusElements.length;
|
||||
let i = index;
|
||||
|
||||
const currentElement = document.activeElement as HTMLElement | null;
|
||||
const currentIndex = currentElement ? focusableElements.indexOf(currentElement) : -1;
|
||||
|
||||
// If no element is focused, focus the first matching element or the first focusable element
|
||||
if (currentIndex === -1) {
|
||||
const firstMatchingElement = focusableElements.find((element) => selector(element));
|
||||
if (firstMatchingElement) {
|
||||
firstMatchingElement.focus();
|
||||
} else if (focusableElements[0]) {
|
||||
focusableElements[0].focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the step direction
|
||||
const step = direction === 'next' ? 1 : -1;
|
||||
const totalElements = focusableElements.length;
|
||||
|
||||
// Search for the next focusable element that matches the selector
|
||||
let nextIndex = currentIndex;
|
||||
do {
|
||||
i = (i + (forwardDirection ? 1 : -1) + totalElements) % totalElements;
|
||||
const next = focusElements[i];
|
||||
if (isTabbable(next) && selector(next)) {
|
||||
next.focus();
|
||||
nextIndex = (nextIndex + step + totalElements) % totalElements;
|
||||
const candidateElement = focusableElements[nextIndex];
|
||||
|
||||
if (isTabbable(candidateElement) && selector(candidateElement)) {
|
||||
candidateElement.focus();
|
||||
break;
|
||||
}
|
||||
} while (i !== index);
|
||||
} while (nextIndex !== currentIndex);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user