terkelg / prompts

❯ Lightweight, beautiful and user-friendly interactive prompts
MIT License
8.89k stars 305 forks source link

allow functions for initial and provide access to response object to use previous answers #256

Open sramam opened 4 years ago

sramam commented 4 years ago

Problem

Often, information is requested from the user in pieces and collated as we go along. In my experience, this could be to

1. break things down to provide a better/clearer UX (a description for each field),

export const questions: PromptObject[] = [{
  type: "text",
  name: "scope",
  message: "npm module scope",
  format: (val) => !!val && val[0] !== '@' ? `@${val}` : val,
  initial: scope
}, {
  type: "text",
  name: "name",
  message: "npm module name",
  validate: (val) => !!val ? val : "name is required",
  initial: (response) => response.scope ? `${response.scope}/${name}` : name,
}];

2. conditional determination of the set of possible values for the next prompt.

export const questions: PromptObject[] = [{
  type: "select",
  name: "vegOrFruit",
  message: "Would you like to pick a vegetable or fruit?",
  choices: [{ title: "fruit", value: "fruit"}, { title: "vegetable", value: "vegetable"}],
}, {
  type: "select",
  name: "pick",
  message: (response) => response.vegOrFruit === "vegetable" 
     ? "Fresh vegetables available today:"
    : "Juicy fruits for you to pick:",
  choices: (response) => 
    response.vegOrFruit === "vegetable"
   ? [{ title: "carrots", value: "carrots"}, {title: "peas", value: "peas"}]
  : [{title: "apples", value: "apples"}, {title: "bananas", value: "bananas"}]
}];

Describe the solution you'd like

While only demonstrated for message and choices, it'd be useful to optionally provide a function for each property of a prompt definition. As shown, the function should provide as an argument the response object collected thus far.

meilel commented 4 years ago

I also have a usecase where I have a confirm-question, if its true I would need 3 more text-questions. If its false none of these 3 questions should display

alexilyaev commented 4 years ago

This is possible right now:

const responses = await prompts([
  {
    type: 'text',
    name: 'scope',
    message: 'npm module scope',
    format: val => (Boolean(val) && val[0] !== '@' ? `@${val}` : val),
    initial: '@my-scope',
  },
  {
    type: 'text',
    name: 'name',
    message: 'npm module name',
    validate: val => Boolean(val) || 'name is required',
    initial(prev, values, prompt) {
      console.log(prev);
      // e.g. "@my-scope"
      console.log(values);
      // e.g. { scope: '@my-scope' }

      return `${values.scope}/my-module`;
    },
  },
]);

It's kind of mentioned in the README in 2 places:

But not in the Options table for the text type: https://github.com/terkelg/prompts#textmessage-initial-style And the types definition doesn't have it as well: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/prompts/index.d.ts#L81

I think that in general, the options tables should show the full spectrum of types and not just the basic one. Otherwise it's hard to discover the more advanced features.

For examples: The date type accepts a format function but it doesn't show in the Options table. So I constantly find myself jumping up and down the README to see what else is possible.

Side Notes

@sramam The specific example you mentioned now has a weird UX, since you can't really press Enter for an empty value, so you can't skip the scope question. Tracked in https://github.com/terkelg/prompts/issues/219

@meilel That already possible as well, see: https://github.com/terkelg/prompts#type