diff --git a/web/src/lib/utils/focus-util.ts b/web/src/lib/utils/focus-util.ts index 2136b81001..501effd549 100644 --- a/web/src/lib/utils/focus-util.ts +++ b/web/src/lib/utils/focus-util.ts @@ -12,28 +12,43 @@ export const setDefaultTabbleOptions = (options: TabbableOpts) => { export const getTabbable = (container: Element, includeContainer: boolean = false) => tabbable(container, { ...defaultOpts, includeContainer }); -export const moveFocus = (selector: (element: HTMLElement | SVGElement) => boolean, direction: 'previous' | 'next') => { - 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(selector); + 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 + (direction === 'next' ? 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); };