|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import React from 'react'; |
|
|
import { Text, Box } from 'ink'; |
|
|
import SelectInput, { |
|
|
type ItemProps as InkSelectItemProps, |
|
|
type IndicatorProps as InkSelectIndicatorProps, |
|
|
} from 'ink-select-input'; |
|
|
import { Colors } from '../../colors.js'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export interface RadioSelectItem<T> { |
|
|
label: string; |
|
|
value: T; |
|
|
disabled?: boolean; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export interface RadioButtonSelectProps<T> { |
|
|
|
|
|
items: Array< |
|
|
RadioSelectItem<T> & { |
|
|
themeNameDisplay?: string; |
|
|
themeTypeDisplay?: string; |
|
|
} |
|
|
>; |
|
|
|
|
|
|
|
|
initialIndex?: number; |
|
|
|
|
|
|
|
|
onSelect: (value: T) => void; |
|
|
|
|
|
|
|
|
onHighlight?: (value: T) => void; |
|
|
|
|
|
|
|
|
isFocused?: boolean; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function RadioButtonSelect<T>({ |
|
|
items, |
|
|
initialIndex, |
|
|
onSelect, |
|
|
onHighlight, |
|
|
isFocused, |
|
|
}: RadioButtonSelectProps<T>): React.JSX.Element { |
|
|
const handleSelect = (item: RadioSelectItem<T>) => { |
|
|
onSelect(item.value); |
|
|
}; |
|
|
const handleHighlight = (item: RadioSelectItem<T>) => { |
|
|
if (onHighlight) { |
|
|
onHighlight(item.value); |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function DynamicRadioIndicator({ |
|
|
isSelected = false, |
|
|
}: InkSelectIndicatorProps): React.JSX.Element { |
|
|
return ( |
|
|
<Box minWidth={2} flexShrink={0}> |
|
|
<Text color={isSelected ? Colors.AccentGreen : Colors.Foreground}> |
|
|
{isSelected ? 'β' : 'β'} |
|
|
</Text> |
|
|
</Box> |
|
|
); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function CustomThemeItemComponent( |
|
|
props: InkSelectItemProps, |
|
|
): React.JSX.Element { |
|
|
const { isSelected = false, label } = props; |
|
|
const itemWithThemeProps = props as typeof props & { |
|
|
themeNameDisplay?: string; |
|
|
themeTypeDisplay?: string; |
|
|
disabled?: boolean; |
|
|
}; |
|
|
|
|
|
let textColor = Colors.Foreground; |
|
|
if (isSelected) { |
|
|
textColor = Colors.AccentGreen; |
|
|
} else if (itemWithThemeProps.disabled === true) { |
|
|
textColor = Colors.Gray; |
|
|
} |
|
|
|
|
|
if ( |
|
|
itemWithThemeProps.themeNameDisplay && |
|
|
itemWithThemeProps.themeTypeDisplay |
|
|
) { |
|
|
return ( |
|
|
<Text color={textColor} wrap="truncate"> |
|
|
{itemWithThemeProps.themeNameDisplay}{' '} |
|
|
<Text color={Colors.Gray}>{itemWithThemeProps.themeTypeDisplay}</Text> |
|
|
</Text> |
|
|
); |
|
|
} |
|
|
|
|
|
return ( |
|
|
<Text color={textColor} wrap="truncate"> |
|
|
{label} |
|
|
</Text> |
|
|
); |
|
|
} |
|
|
|
|
|
initialIndex = initialIndex ?? 0; |
|
|
return ( |
|
|
<SelectInput |
|
|
indicatorComponent={DynamicRadioIndicator} |
|
|
itemComponent={CustomThemeItemComponent} |
|
|
items={items} |
|
|
initialIndex={initialIndex} |
|
|
onSelect={handleSelect} |
|
|
onHighlight={handleHighlight} |
|
|
isFocused={isFocused} |
|
|
/> |
|
|
); |
|
|
} |
|
|
|