import type { KeyCode } from "../keycode/key_code.ts";import { GenericPrompt } from "./_generic_prompt.ts";import { GenericSuggestions, GenericSuggestionsKeys, GenericSuggestionsOptions, GenericSuggestionsSettings,} from "./_generic_suggestions.ts";import { parseNumber } from "./_utils.ts";import { blue, yellow } from "./deps.ts";import { Figures } from "./figures.ts";
export interface NumberKeys extends GenericSuggestionsKeys { increaseValue?: string[]; decreaseValue?: string[];}
type UnsupportedOptions = "files";
export interface NumberOptions extends Omit<GenericSuggestionsOptions<number, string>, UnsupportedOptions> { min?: number; max?: number; float?: boolean; round?: number; keys?: NumberKeys;}
interface NumberSettings extends GenericSuggestionsSettings<number, string> { min: number; max: number; float: boolean; round: number; keys?: NumberKeys;}
export class Number extends GenericSuggestions<number, string, NumberSettings> { public static prompt(options: string | NumberOptions): Promise<number> { if (typeof options === "string") { options = { message: options }; }
return new this({ pointer: blue(Figures.POINTER_SMALL), prefix: yellow("? "), indent: " ", listPointer: blue(Figures.POINTER), maxRows: 8, min: -Infinity, max: Infinity, float: false, round: 2, ...options, files: false, keys: { increaseValue: ["up", "u", "+"], decreaseValue: ["down", "d", "-"], ...(options.keys ?? {}), }, }).prompt(); }
public static inject(value: string): void { GenericPrompt.inject(value); }
protected success(value: number): string | undefined { this.saveSuggestions(value); return super.success(value); }
protected async handleEvent(event: KeyCode): Promise<void> { switch (true) { case this.settings.suggestions && this.isKey(this.settings.keys, "next", event): if (this.settings.list) { this.selectPreviousSuggestion(); } else { this.selectNextSuggestion(); } break; case this.settings.suggestions && this.isKey(this.settings.keys, "previous", event): if (this.settings.list) { this.selectNextSuggestion(); } else { this.selectPreviousSuggestion(); } break; case this.isKey(this.settings.keys, "increaseValue", event): this.increaseValue(); break; case this.isKey(this.settings.keys, "decreaseValue", event): this.decreaseValue(); break; default: await super.handleEvent(event); } }
public increaseValue() { this.manipulateIndex(false); }
public decreaseValue() { this.manipulateIndex(true); }
protected manipulateIndex(decrease?: boolean) { if (this.inputValue[this.inputIndex] === "-") { this.inputIndex++; }
if ( this.inputValue.length && (this.inputIndex > this.inputValue.length - 1) ) { this.inputIndex--; }
const decimalIndex: number = this.inputValue.indexOf("."); const [abs, dec] = this.inputValue.split(".");
if (dec && this.inputIndex === decimalIndex) { this.inputIndex--; }
const inDecimal: boolean = decimalIndex !== -1 && this.inputIndex > decimalIndex; let value: string = (inDecimal ? dec : abs) || "0"; const oldLength: number = this.inputValue.length; const index: number = inDecimal ? this.inputIndex - decimalIndex - 1 : this.inputIndex; const increaseValue = Math.pow(10, value.length - index - 1);
value = (parseInt(value) + (decrease ? -increaseValue : increaseValue)) .toString();
this.inputValue = !dec ? value : (this.inputIndex > decimalIndex ? abs + "." + value : value + "." + dec);
if (this.inputValue.length > oldLength) { this.inputIndex++; } else if ( this.inputValue.length < oldLength && this.inputValue[this.inputIndex - 1] !== "-" ) { this.inputIndex--; }
this.inputIndex = Math.max( 0, Math.min(this.inputIndex, this.inputValue.length - 1), ); }
protected addChar(char: string): void { if (isNumeric(char)) { super.addChar(char); } else if ( this.settings.float && char === "." && this.inputValue.indexOf(".") === -1 && (this.inputValue[0] === "-" ? this.inputIndex > 1 : this.inputIndex > 0) ) { super.addChar(char); } }
protected validate(value: string): boolean | string { if (!isNumeric(value)) { return false; }
const val: number = parseFloat(value);
if (val > this.settings.max) { return `Value must be lower or equal than ${this.settings.max}`; }
if (val < this.settings.min) { return `Value must be greater or equal than ${this.settings.min}`; }
return true; }
protected transform(value: string): number | undefined { const val: number = parseFloat(value);
if (this.settings.float) { return parseFloat(val.toFixed(this.settings.round)); }
return val; }
protected format(value: number): string { return value.toString(); }
protected getValue(): string { return this.inputValue; }}
function isNumeric(value: string | number): value is number | string { return typeof value === "number" || (!!value && !isNaN(parseNumber(value)));}