import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; import { CommandItem } from '../extensions/suggestions/commands'; interface CommandsListProps { items: CommandItem[]; command: (props: { content: string }) => void; } export const CommandsList = forwardRef((props: CommandsListProps, ref) => { const [selectedIndex, setSelectedIndex] = useState(0); const selectItem = (index: number) => { const item = props.items[index]; if (item) { props.command({ content: item.content }); } }; const upHandler = () => { setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length); }; const downHandler = () => { setSelectedIndex((selectedIndex + 1) % props.items.length); }; const enterHandler = () => { selectItem(selectedIndex); }; useEffect(() => setSelectedIndex(0), [props.items]); useImperativeHandle(ref, () => ({ onKeyDown: ({ event }: { event: KeyboardEvent }) => { if (event.key === 'ArrowUp') { upHandler(); return true; } if (event.key === 'ArrowDown') { downHandler(); return true; } if (event.key === 'Enter') { enterHandler(); return true; } return false; }, })); // Scroll to selected item when it changes useEffect(() => { const element = document.getElementById(`command-item-${selectedIndex}`); if (element) { element.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); } }, [selectedIndex]); return (
Commands ({props.items.length})
Type to filter commands
{props.items.length ? ( props.items.map((item, index) => ( )) ) : (
No results
)}
to navigate Enter to select
); }); CommandsList.displayName = 'CommandsList';