Fixed the keyboard stealing issue

This commit is contained in:
Colin Dawson 2026-02-16 16:17:48 +00:00
parent 237267ff3a
commit 9ae4b54155

View File

@ -1,4 +1,4 @@
import React, { useCallback, useRef } from "react";
import React, { useCallback, useEffect, useRef } from "react";
export interface SelectableListProps<T> {
items: T[];
@ -12,10 +12,17 @@ export const SelectableList = <T,>(
): JSX.Element => {
const { items, selectedValue, renderLabel, onSelect } = props;
// Track focus state of the list itself
const listRef = useRef<HTMLUListElement | null>(null);
const isFocusedRef = useRef(false);
// One ref per item so we can focus + scroll it
const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLUListElement>) => {
// Ignore keyboard input unless the list itself is focused
if (!isFocusedRef.current) return;
if (!items.length) return;
const currentIndex = selectedValue ? items.indexOf(selectedValue) : -1;
@ -37,11 +44,28 @@ export const SelectableList = <T,>(
[items, selectedValue, onSelect],
);
// Only focus the selected item if the list itself already has focus
useEffect(() => {
if (!isFocusedRef.current) return; // Do NOT steal focus
if (!selectedValue) return;
const index = items.indexOf(selectedValue);
if (index < 0) return;
const el = itemRefs.current[index];
if (el) {
el.focus({ preventScroll: false });
el.scrollIntoView({ block: "nearest" });
}
}, [items, selectedValue]);
return (
<ul
ref={listRef}
className="selectable-list"
tabIndex={0}
onFocus={() => (isFocusedRef.current = true)}
onBlur={() => (isFocusedRef.current = false)}
onKeyDown={handleKeyDown}
>
{items.map((item, index) => {
@ -49,7 +73,13 @@ export const SelectableList = <T,>(
const className = isSelected ? "selected" : "";
return (
<li key={index} onClick={() => onSelect(item)} className={className}>
<li
key={index}
ref={(el) => (itemRefs.current[index] = el)}
tabIndex={isSelected ? 0 : -1}
onClick={() => onSelect(item)}
className={className}
>
{renderLabel(item)}
</li>
);