Skip to content

Commit abdb997

Browse files
natemoo-reulken
authored andcommitted
adds validate state, triggered after 300ms
1 parent 46163fa commit abdb997

File tree

3 files changed

+20
-3
lines changed

3 files changed

+20
-3
lines changed

examples/basic/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ async function main() {
1515
p.text({
1616
message: 'Where should we create your project?',
1717
placeholder: './sparkling-solid',
18-
validate: (value) => {
18+
validate: async (value) => {
19+
await setTimeout(299);
1920
if (!value) return 'Please enter a path.';
2021
if (value[0] !== '.') return 'Please enter a relative path.';
2122
},

packages/core/src/prompts/prompt.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { stdin, stdout } from 'node:process';
44
import readline from 'node:readline';
55
import { Readable, Writable } from 'node:stream';
66
import { WriteStream } from 'node:tty';
7+
import { setTimeout } from 'node:timers/promises';
78
import { cursor, erase } from 'sisteransi';
89
import wrap from 'wrap-ansi';
910

@@ -52,7 +53,7 @@ export interface PromptOptions<Self extends Prompt> {
5253
debug?: boolean;
5354
}
5455

55-
export type State = 'initial' | 'active' | 'cancel' | 'submit' | 'error';
56+
export type State = 'initial' | 'active' | 'cancel' | 'validate' | 'submit' | 'error';
5657

5758
export default class Prompt {
5859
protected input: Readable;
@@ -176,7 +177,17 @@ export default class Prompt {
176177

177178
if (key?.name === 'return') {
178179
if (this.opts.validate) {
179-
const problem = await this.opts.validate(this.value);
180+
this.state = 'validate';
181+
let problem = this.opts.validate(this.value);
182+
// Only trigger validation state after 300ms.
183+
// If problem resolves first, render will be cancelled.
184+
await Promise.race([
185+
problem,
186+
setTimeout(300).then(() => {
187+
this.render();
188+
}),
189+
]);
190+
problem = await problem;
180191
if (problem) {
181192
this.error = problem;
182193
this.state = 'error';

packages/prompts/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export { isCancel } from '@clack/core';
2020
const unicode = isUnicodeSupported();
2121
const s = (c: string, fallback: string) => (unicode ? c : fallback);
2222
const S_STEP_ACTIVE = s('◆', '*');
23+
const S_STEP_VALIDATE = S_STEP_ACTIVE;
2324
const S_STEP_CANCEL = s('■', 'x');
2425
const S_STEP_ERROR = s('▲', 'x');
2526
const S_STEP_SUBMIT = s('◇', 'o');
@@ -50,6 +51,8 @@ const symbol = (state: State) => {
5051
case 'initial':
5152
case 'active':
5253
return color.cyan(S_STEP_ACTIVE);
54+
case 'validate':
55+
return color.cyan(S_STEP_VALIDATE);
5356
case 'cancel':
5457
return color.red(S_STEP_CANCEL);
5558
case 'error':
@@ -80,6 +83,8 @@ export const text = (opts: TextOptions) => {
8083
const value = !this.value ? placeholder : this.valueWithCursor;
8184

8285
switch (this.state) {
86+
case 'validate':
87+
return `${title}${color.cyan(S_BAR)} ${value}\n${color.cyan(S_BAR_END)} ${color.dim('Validating...')}\n`;
8388
case 'error':
8489
return `${title.trim()}\n${color.yellow(S_BAR)} ${value}\n${color.yellow(
8590
S_BAR_END

0 commit comments

Comments
 (0)