import { WidenType } from "./_utils.ts";import { brightBlue, underline } from "./deps.ts";import { GenericList, GenericListKeys, GenericListOption, GenericListOptionGroup, GenericListOptionGroupSettings, GenericListOptions, GenericListOptionSettings, GenericListSeparatorOption, GenericListSettings, isOption, isOptionGroup,} from "./_generic_list.ts";import { GenericPrompt } from "./_generic_prompt.ts";import { getFiguresByKeys } from "./_figures.ts";
export interface SelectOptions<TValue> extends GenericListOptions<TValue, TValue, TValue> { keys?: SelectKeys; options: Array< | Extract<TValue, string | number> | Extract<WidenType<TValue>, string | number> | SelectOption<TValue> | SelectOptionGroup<TValue> | GenericListSeparatorOption >;}
export interface SelectSettings<TValue> extends GenericListSettings< TValue, TValue, TValue, SelectOptionSettings<TValue>, SelectOptionGroupSettings<TValue> > { keys: SelectKeys;}
export type SelectOption<TValue> = GenericListOption<TValue>;
export type SelectOptionGroup<TValue> = GenericListOptionGroup< TValue, GenericListOption<TValue>>;
export type SelectOptionSettings<TValue> = GenericListOptionSettings<TValue>;
export type SelectOptionGroupSettings<TValue> = GenericListOptionGroupSettings< TValue, SelectOptionSettings<TValue>>;
export type SelectKeys = GenericListKeys;
export class Select<TValue> extends GenericList< TValue, TValue, TValue, SelectOptionSettings<TValue>, SelectOptionGroupSettings<TValue>> { protected readonly settings: SelectSettings<TValue>; protected options: Array< SelectOptionSettings<TValue> | SelectOptionGroupSettings<TValue> >; protected listIndex: number; protected listOffset: number;
public static prompt<TValue>( options: SelectOptions<TValue>, ): Promise<WidenType<TValue>> { return new this(options).prompt() as Promise<WidenType<TValue>>; }
public static inject(value: string): void { GenericPrompt.inject(value); }
constructor(options: SelectOptions<TValue>) { super(); this.settings = this.getDefaultSettings(options); this.options = this.settings.options.slice(); this.listIndex = this.getListIndex(this.settings.default); this.listOffset = this.getPageOffset(this.listIndex); }
public getDefaultSettings( options: SelectOptions<TValue>, ): SelectSettings<TValue> { return { ...super.getDefaultSettings(options), options: this.mapOptions(options, options.options), }; }
protected mapOptions( promptOptions: SelectOptions<TValue>, options: Array< | Extract<TValue, string | number> | Extract<WidenType<TValue>, string | number> | SelectOption<TValue> | SelectOptionGroup<TValue> | GenericListSeparatorOption >, ): Array<SelectOptionSettings<TValue> | SelectOptionGroupSettings<TValue>> { return options.map((option) => isSelectOptionGroup(option) ? this.mapOptionGroup(promptOptions, option) : typeof option === "string" || typeof option === "number" ? this.mapOption( promptOptions, { value: option as TValue }, ) : this.mapOption(promptOptions, option) ); }
protected input(): string { return underline(brightBlue(this.inputValue)); }
protected async submit(): Promise<void> { if ( this.isBackButton(this.selectedOption) || isOptionGroup(this.selectedOption) ) { const info = isOptionGroup(this.selectedOption) ? ` To select a group use ${ getFiguresByKeys(this.settings.keys.open ?? []).join(", ") }.` : ""; this.setErrorMessage(`No option selected.${info}`); return; }
await super.submit(); }
protected getValue(): TValue { const option = this.options[this.listIndex]; assertIsOption(option); return option.value; }
protected validate(value: TValue): boolean | string { return this.options.findIndex(( option: SelectOptionSettings<TValue> | SelectOptionGroupSettings<TValue>, ) => isOption(option) && option.value === value) !== -1; }
protected transform(value: TValue): TValue { return value; }
protected format(value: TValue): string { return this.settings.format?.(value) ?? this.getOptionByValue(value)?.name ?? String(value); }}
function assertIsOption< TValue, TOption extends GenericListOption<TValue>,>( option: TOption | GenericListOptionGroup<TValue, GenericListOption<TValue>>,): asserts option is TOption { if (!isOption(option)) { throw new Error("Expected an option but got an option group."); }}
export function isSelectOptionGroup( option: unknown, ): option is SelectOptionGroup<any> { return isOptionGroup(option);}
export type SelectValueOptions = Array< string | SelectOption<string> | SelectOptionGroup<string>>;
export type SelectValueSettings = Array< SelectOptionSettings<string> | SelectOptionGroupSettings<string>>;