import { useState } from 'react'; import { Text, useInput, Box, Newline } from 'ink'; import { colors } from '../../config/colors.js'; import React from 'react'; /** * Due to terminals not always being that large, we need * to cap the max amount of options to display to a low * number. */ const maxOptionsToDisplay = 3; type Option = { label: string; value: T; render?: () => JSX.Element; }; type Styles = { compact?: boolean; }; export type Props = { options: Option[]; onSelect: (value: T) => void; styles?: Styles; defaultSelectedIndex?: number; }; export function Select({ options, onSelect, styles, defaultSelectedIndex = 0 }: Props) { const [selectedIndex, setSelectedIndex] = useState(defaultSelectedIndex); const optionsToDisplay = (() => { if (selectedIndex === 0) { return options.slice(selectedIndex, maxOptionsToDisplay); } if (selectedIndex === options.length - 1) { return options.slice(-maxOptionsToDisplay); } return options.slice(selectedIndex - 1, selectedIndex - 1 + maxOptionsToDisplay); })(); const lastDisplayedIndex = options.findIndex( (option: Option) => option === optionsToDisplay[optionsToDisplay.length - 1], ); const overflowItem = lastDisplayedIndex < options.length - 1 ? options[lastDisplayedIndex + 1] : null; useInput((_, key) => { if (key.return) { onSelect(options[selectedIndex].value); return; } if (key.upArrow) { setSelectedIndex(selectedIndex <= 0 ? options.length - 1 : selectedIndex - 1); } if (key.downArrow) { setSelectedIndex(selectedIndex >= options.length - 1 ? 0 : selectedIndex + 1); } }); return ( {optionsToDisplay.map((option: Option, index: number) => { return ( {options[selectedIndex].value === option.value ? '>' : ''} {option.render ? option.render() : option.label} ); })} {overflowItem && ( {overflowItem.label} ... )} ); }