webui/src/components/common/SelectableList.tsx

60 lines
1.6 KiB
TypeScript

import React, { useCallback, useRef } from "react";
export interface SelectableListProps<T> {
items: T[];
selectedValue?: T | null;
renderLabel: (item: T) => React.ReactNode;
onSelect: (item: T) => void;
}
export const SelectableList = <T,>(
props: SelectableListProps<T>,
): JSX.Element => {
const { items, selectedValue, renderLabel, onSelect } = props;
const listRef = useRef<HTMLUListElement | null>(null);
const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLUListElement>) => {
if (!items.length) return;
const currentIndex = selectedValue ? items.indexOf(selectedValue) : -1;
if (e.key === "ArrowDown") {
e.preventDefault();
const nextIndex =
currentIndex < items.length - 1 ? currentIndex + 1 : 0;
onSelect(items[nextIndex]);
}
if (e.key === "ArrowUp") {
e.preventDefault();
const prevIndex =
currentIndex > 0 ? currentIndex - 1 : items.length - 1;
onSelect(items[prevIndex]);
}
},
[items, selectedValue, onSelect],
);
return (
<ul
ref={listRef}
className="selectable-list"
tabIndex={0}
onKeyDown={handleKeyDown}
>
{items.map((item, index) => {
const isSelected = selectedValue === item;
const className = isSelected ? "selected" : "";
return (
<li key={index} onClick={() => onSelect(item)} className={className}>
{renderLabel(item)}
</li>
);
})}
</ul>
);
};