import type { KeyCode } from "../keycode/mod.ts";import { blue, dim, green, red, yellow } from "./deps.ts";import { Figures } from "./figures.ts";import { GenericList, GenericListKeys, GenericListOption, GenericListOptions, GenericListOptionSettings, GenericListSettings,} from "./_generic_list.ts";import { GenericPrompt } from "./_generic_prompt.ts";
export interface CheckboxKeys extends GenericListKeys { check?: string[];}
export interface CheckboxOption extends GenericListOption { checked?: boolean; icon?: boolean;}
export interface CheckboxOptionSettings extends GenericListOptionSettings { checked: boolean; icon: boolean;}
export type CheckboxValueOptions = (string | CheckboxOption)[];export type CheckboxValueSettings = CheckboxOptionSettings[];
export interface CheckboxOptions extends GenericListOptions<string[], string[]> { options: CheckboxValueOptions; check?: string; uncheck?: string; minOptions?: number; maxOptions?: number; keys?: CheckboxKeys;}
interface CheckboxSettings extends GenericListSettings<string[], string[]> { options: CheckboxValueSettings; check: string; uncheck: string; minOptions: number; maxOptions: number; keys?: CheckboxKeys;}
export class Checkbox extends GenericList<string[], string[], CheckboxSettings> { public static inject(value: string[]): void { GenericPrompt.inject(value); }
public static prompt(options: CheckboxOptions): Promise<string[]> { return new this({ pointer: blue(Figures.POINTER_SMALL), prefix: yellow("? "), indent: " ", listPointer: blue(Figures.POINTER), maxRows: 10, searchLabel: blue(Figures.SEARCH), minOptions: 0, maxOptions: Infinity, check: green(Figures.TICK), uncheck: red(Figures.CROSS), ...options, keys: { check: ["space"], ...(options.keys ?? {}), }, options: Checkbox.mapOptions(options), }).prompt(); }
public static separator(label?: string): CheckboxOption { return { ...super.separator(label), icon: false, }; }
protected static mapOptions(options: CheckboxOptions): CheckboxValueSettings { return options.options .map((item: string | CheckboxOption) => typeof item === "string" ? { value: item } : item ) .map((item) => ({ ...this.mapOption(item), checked: typeof item.checked === "undefined" && options.default && options.default.indexOf(item.value) !== -1 ? true : !!item.checked, icon: typeof item.icon === "undefined" ? true : item.icon, })); }
protected getListItem( item: CheckboxOptionSettings, isSelected?: boolean, ): string { let line = this.settings.indent;
line += isSelected ? this.settings.listPointer + " " : " ";
if (item.icon) { let check = item.checked ? this.settings.check + " " : this.settings.uncheck + " "; if (item.disabled) { check = dim(check); } line += check; } else { line += " "; }
line += `${ isSelected && !item.disabled ? this.highlight(item.name, (val) => val) : this.highlight(item.name) }`;
return line; }
protected getValue(): string[] { return this.settings.options .filter((item) => item.checked) .map((item) => item.value); }
protected async handleEvent(event: KeyCode): Promise<void> { switch (true) { case this.isKey(this.settings.keys, "check", event): this.checkValue(); break; default: await super.handleEvent(event); } }
protected checkValue(): void { const item = this.options[this.listIndex]; if (item.disabled) { this.setErrorMessage("This option is disabled and cannot be changed."); } else { item.checked = !item.checked; } }
protected validate(value: string[]): boolean | string { const isValidValue = Array.isArray(value) && value.every((val) => typeof val === "string" && val.length > 0 && this.settings.options.findIndex((option: CheckboxOptionSettings) => option.value === val ) !== -1 );
if (!isValidValue) { return false; }
if (value.length < this.settings.minOptions) { return `The minimum number of options is ${this.settings.minOptions} but got ${value.length}.`; } if (value.length > this.settings.maxOptions) { return `The maximum number of options is ${this.settings.maxOptions} but got ${value.length}.`; }
return true; }
protected transform(value: string[]): string[] { return value.map((val) => val.trim()); }
protected format(value: string[]): string { return value.map((val) => this.getOptionByValue(val)?.name ?? val) .join(", "); }}