@inquirer/select 1.0.3 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,7 @@ yarn add @inquirer/select
15
15
  # Usage
16
16
 
17
17
  ```js
18
- import select from '@inquirer/select';
18
+ import select, { Separator } from '@inquirer/select';
19
19
 
20
20
  const answer = await select({
21
21
  message: 'Select a package manager',
@@ -30,6 +30,7 @@ const answer = await select({
30
30
  value: 'yarn',
31
31
  description: 'yarn is an awesome package manager',
32
32
  },
33
+ new Separator(),
33
34
  {
34
35
  name: 'jspm',
35
36
  value: 'jspm',
@@ -46,11 +47,13 @@ const answer = await select({
46
47
 
47
48
  ## Options
48
49
 
49
- | Property | Type | Required | Description |
50
- | -------- | --------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
51
- | message | `string` | yes | The question to ask |
52
- | choices | `Array<{ value: string, name?: string, description?: string, disabled?: boolean \| string }>` | yes | List of the available choices. The `value` will be returned as the answer, and used as display if no `name` is defined. Choices who're `disabled` will be displayed, but not selectable. The `description` will be displayed under the prompt when the cursor land over the choice. |
53
- | pageSize | `number` | no | By default, lists of choice longer than 7 will be paginated. Use this option to control how many choices will appear on the screen at once. |
50
+ | Property | Type | Required | Description |
51
+ | -------- | ---------------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
52
+ | message | `string` | yes | The question to ask |
53
+ | choices | `Array<{ value: string, name?: string, description?: string, disabled?: boolean \| string } \| Separator>` | yes | List of the available choices. The `value` will be returned as the answer, and used as display if no `name` is defined. Choices who're `disabled` will be displayed, but not selectable. The `description` will be displayed under the prompt when the cursor land over the choice. |
54
+ | pageSize | `number` | no | By default, lists of choice longer than 7 will be paginated. Use this option to control how many choices will appear on the screen at once. |
55
+
56
+ The `Separator` object can be used to render non-selectable lines in the choice list. By default it'll render a line, but you can provide the text as argument (`new Separator('-- Dependencies --')`). This option is often used to add labels to groups within long list of options.
54
57
 
55
58
  # License
56
59
 
package/dist/cjs/index.js CHANGED
@@ -3,28 +3,40 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Separator = void 0;
6
7
  const core_1 = require("@inquirer/core");
8
+ Object.defineProperty(exports, "Separator", { enumerable: true, get: function () { return core_1.Separator; } });
7
9
  const chalk_1 = __importDefault(require("chalk"));
8
10
  const figures_1 = __importDefault(require("figures"));
9
11
  const ansi_escapes_1 = __importDefault(require("ansi-escapes"));
12
+ function isSelectableChoice(choice) {
13
+ return choice != null && choice.type !== 'separator' && !choice.disabled;
14
+ }
10
15
  exports.default = (0, core_1.createPrompt)((config, done) => {
11
16
  const { choices } = config;
12
- const startIndex = Math.max(choices.findIndex(({ disabled }) => !disabled), 0);
13
17
  const paginator = (0, core_1.useRef)(new core_1.Paginator()).current;
14
18
  const firstRender = (0, core_1.useRef)(true);
15
19
  const prefix = (0, core_1.usePrefix)();
16
20
  const [status, setStatus] = (0, core_1.useState)('pending');
17
- const [cursorPosition, setCursorPos] = (0, core_1.useState)(startIndex);
21
+ const [cursorPosition, setCursorPos] = (0, core_1.useState)(() => {
22
+ const startIndex = choices.findIndex(isSelectableChoice);
23
+ if (startIndex < 0) {
24
+ throw new Error('[select prompt] No selectable choices. All choices are disabled.');
25
+ }
26
+ return startIndex;
27
+ });
28
+ // Safe to assume the cursor position always point to a Choice.
29
+ const choice = choices[cursorPosition];
18
30
  (0, core_1.useKeypress)((key) => {
19
31
  if ((0, core_1.isEnterKey)(key)) {
20
32
  setStatus('done');
21
- done(choices[cursorPosition].value);
33
+ done(choice.value);
22
34
  }
23
35
  else if ((0, core_1.isUpKey)(key) || (0, core_1.isDownKey)(key)) {
24
36
  let newCursorPosition = cursorPosition;
25
37
  const offset = (0, core_1.isUpKey)(key) ? -1 : 1;
26
38
  let selectedOption;
27
- while (!selectedOption || selectedOption.disabled) {
39
+ while (!isSelectableChoice(selectedOption)) {
28
40
  newCursorPosition =
29
41
  (newCursorPosition + offset + choices.length) % choices.length;
30
42
  selectedOption = choices[newCursorPosition];
@@ -35,7 +47,7 @@ exports.default = (0, core_1.createPrompt)((config, done) => {
35
47
  // Adjust index to start at 1
36
48
  const newCursorPosition = Number(key.name) - 1;
37
49
  // Abort if the choice doesn't exists or if disabled
38
- if (!choices[newCursorPosition] || choices[newCursorPosition].disabled) {
50
+ if (!isSelectableChoice(choices[newCursorPosition])) {
39
51
  return;
40
52
  }
41
53
  setCursorPos(newCursorPosition);
@@ -47,14 +59,17 @@ exports.default = (0, core_1.createPrompt)((config, done) => {
47
59
  firstRender.current = false;
48
60
  }
49
61
  if (status === 'done') {
50
- const choice = choices[cursorPosition];
51
62
  return `${prefix} ${message} ${chalk_1.default.cyan(choice.name || choice.value)}`;
52
63
  }
53
64
  const allChoices = choices
54
- .map(({ name, value, disabled }, index) => {
55
- const line = name || value;
56
- if (disabled) {
57
- return chalk_1.default.dim(`- ${line} ${typeof disabled === 'string' ? disabled : '(disabled)'}`);
65
+ .map((choice, index) => {
66
+ if (choice.type === 'separator') {
67
+ return ` ${choice.separator}`;
68
+ }
69
+ const line = choice.name || choice.value;
70
+ if (choice.disabled) {
71
+ const disabledLabel = typeof choice.disabled === 'string' ? choice.disabled : '(disabled)';
72
+ return chalk_1.default.dim(`- ${line} ${disabledLabel}`);
58
73
  }
59
74
  if (index === cursorPosition) {
60
75
  return chalk_1.default.cyan(`${figures_1.default.pointer} ${line}`);
@@ -63,7 +78,6 @@ exports.default = (0, core_1.createPrompt)((config, done) => {
63
78
  })
64
79
  .join('\n');
65
80
  const windowedChoices = paginator.paginate(allChoices, cursorPosition, config.pageSize);
66
- const choice = choices[cursorPosition];
67
- const choiceDescription = choice && choice.description ? `\n${choice.description}` : ``;
81
+ const choiceDescription = choice.description ? `\n${choice.description}` : ``;
68
82
  return `${prefix} ${message}\n${windowedChoices}${choiceDescription}${ansi_escapes_1.default.cursorHide}`;
69
83
  });
@@ -1,12 +1,15 @@
1
- import { AsyncPromptConfig } from '@inquirer/core';
1
+ import { Separator, AsyncPromptConfig, SeparatorType } from '@inquirer/core';
2
+ type Choice = {
3
+ type?: undefined;
4
+ value: string;
5
+ name?: string;
6
+ description?: string;
7
+ disabled?: boolean | string;
8
+ };
2
9
  type SelectConfig = AsyncPromptConfig & {
3
- choices: {
4
- value: string;
5
- name?: string;
6
- description?: string;
7
- disabled?: boolean | string;
8
- }[];
10
+ choices: Array<SeparatorType | Choice>;
9
11
  pageSize?: number;
10
12
  };
11
13
  declare const _default: import("@inquirer/type").Prompt<string, SelectConfig>;
12
14
  export default _default;
15
+ export { Separator };
@@ -1,25 +1,35 @@
1
- import { createPrompt, useState, useKeypress, useRef, usePrefix, isEnterKey, isUpKey, isDownKey, isNumberKey, Paginator, } from '@inquirer/core';
1
+ import { createPrompt, useState, useKeypress, useRef, usePrefix, isEnterKey, isUpKey, isDownKey, isNumberKey, Paginator, Separator, } from '@inquirer/core';
2
2
  import chalk from 'chalk';
3
3
  import figures from 'figures';
4
4
  import ansiEscapes from 'ansi-escapes';
5
+ function isSelectableChoice(choice) {
6
+ return choice != null && choice.type !== 'separator' && !choice.disabled;
7
+ }
5
8
  export default createPrompt((config, done) => {
6
9
  const { choices } = config;
7
- const startIndex = Math.max(choices.findIndex(({ disabled }) => !disabled), 0);
8
10
  const paginator = useRef(new Paginator()).current;
9
11
  const firstRender = useRef(true);
10
12
  const prefix = usePrefix();
11
13
  const [status, setStatus] = useState('pending');
12
- const [cursorPosition, setCursorPos] = useState(startIndex);
14
+ const [cursorPosition, setCursorPos] = useState(() => {
15
+ const startIndex = choices.findIndex(isSelectableChoice);
16
+ if (startIndex < 0) {
17
+ throw new Error('[select prompt] No selectable choices. All choices are disabled.');
18
+ }
19
+ return startIndex;
20
+ });
21
+ // Safe to assume the cursor position always point to a Choice.
22
+ const choice = choices[cursorPosition];
13
23
  useKeypress((key) => {
14
24
  if (isEnterKey(key)) {
15
25
  setStatus('done');
16
- done(choices[cursorPosition].value);
26
+ done(choice.value);
17
27
  }
18
28
  else if (isUpKey(key) || isDownKey(key)) {
19
29
  let newCursorPosition = cursorPosition;
20
30
  const offset = isUpKey(key) ? -1 : 1;
21
31
  let selectedOption;
22
- while (!selectedOption || selectedOption.disabled) {
32
+ while (!isSelectableChoice(selectedOption)) {
23
33
  newCursorPosition =
24
34
  (newCursorPosition + offset + choices.length) % choices.length;
25
35
  selectedOption = choices[newCursorPosition];
@@ -30,7 +40,7 @@ export default createPrompt((config, done) => {
30
40
  // Adjust index to start at 1
31
41
  const newCursorPosition = Number(key.name) - 1;
32
42
  // Abort if the choice doesn't exists or if disabled
33
- if (!choices[newCursorPosition] || choices[newCursorPosition].disabled) {
43
+ if (!isSelectableChoice(choices[newCursorPosition])) {
34
44
  return;
35
45
  }
36
46
  setCursorPos(newCursorPosition);
@@ -42,14 +52,17 @@ export default createPrompt((config, done) => {
42
52
  firstRender.current = false;
43
53
  }
44
54
  if (status === 'done') {
45
- const choice = choices[cursorPosition];
46
55
  return `${prefix} ${message} ${chalk.cyan(choice.name || choice.value)}`;
47
56
  }
48
57
  const allChoices = choices
49
- .map(({ name, value, disabled }, index) => {
50
- const line = name || value;
51
- if (disabled) {
52
- return chalk.dim(`- ${line} ${typeof disabled === 'string' ? disabled : '(disabled)'}`);
58
+ .map((choice, index) => {
59
+ if (choice.type === 'separator') {
60
+ return ` ${choice.separator}`;
61
+ }
62
+ const line = choice.name || choice.value;
63
+ if (choice.disabled) {
64
+ const disabledLabel = typeof choice.disabled === 'string' ? choice.disabled : '(disabled)';
65
+ return chalk.dim(`- ${line} ${disabledLabel}`);
53
66
  }
54
67
  if (index === cursorPosition) {
55
68
  return chalk.cyan(`${figures.pointer} ${line}`);
@@ -58,7 +71,7 @@ export default createPrompt((config, done) => {
58
71
  })
59
72
  .join('\n');
60
73
  const windowedChoices = paginator.paginate(allChoices, cursorPosition, config.pageSize);
61
- const choice = choices[cursorPosition];
62
- const choiceDescription = choice && choice.description ? `\n${choice.description}` : ``;
74
+ const choiceDescription = choice.description ? `\n${choice.description}` : ``;
63
75
  return `${prefix} ${message}\n${windowedChoices}${choiceDescription}${ansiEscapes.cursorHide}`;
64
76
  });
77
+ export { Separator };
@@ -1,12 +1,15 @@
1
- import { AsyncPromptConfig } from '@inquirer/core';
1
+ import { Separator, AsyncPromptConfig, SeparatorType } from '@inquirer/core';
2
+ type Choice = {
3
+ type?: undefined;
4
+ value: string;
5
+ name?: string;
6
+ description?: string;
7
+ disabled?: boolean | string;
8
+ };
2
9
  type SelectConfig = AsyncPromptConfig & {
3
- choices: {
4
- value: string;
5
- name?: string;
6
- description?: string;
7
- disabled?: boolean | string;
8
- }[];
10
+ choices: Array<SeparatorType | Choice>;
9
11
  pageSize?: number;
10
12
  };
11
13
  declare const _default: import("@inquirer/type").Prompt<string, SelectConfig>;
12
14
  export default _default;
15
+ export { Separator };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inquirer/select",
3
- "version": "1.0.3",
3
+ "version": "1.1.2",
4
4
  "description": "Inquirer select/list prompt",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "typings": "./dist/cjs/types/index.d.mts",
@@ -54,7 +54,7 @@
54
54
  "license": "MIT",
55
55
  "homepage": "https://github.com/SBoudrias/Inquirer.js/blob/master/packages/select/README.md",
56
56
  "dependencies": {
57
- "@inquirer/core": "^1.0.4",
57
+ "@inquirer/core": "^1.1.2",
58
58
  "@inquirer/type": "^1.0.3",
59
59
  "ansi-escapes": "^4.3.2",
60
60
  "chalk": "^4.1.2",
@@ -85,5 +85,5 @@
85
85
  }
86
86
  }
87
87
  },
88
- "gitHead": "12fecef5292da253564c845ef5be67b37441e381"
88
+ "gitHead": "fbeb06c92f1e64a88bb77028c7a038768c4291b2"
89
89
  }