inquirer-plugins 1.0.0-alpha.2 → 1.0.0-alpha.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 cilly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1 +1,40 @@
1
+ <!-- MD_HOPPER: CONFIG:
2
+ skip-hidden: true
3
+ filenames: README.md
4
+ output: README.md
5
+ -->
6
+
1
7
  # inquirer-plugins
8
+
9
+ _This repository manages custom plugins for the iInquirer package. (Currently, it only includes table.)_
10
+
11
+ This is mostly a collection of custom plugins for my personal use, but feel free to use any that you find useful.
12
+
13
+ ## Plugins
14
+
15
+ <!-- MD_HOPPER: LINK_NEXT_LINE:
16
+ id: src-_internal-plugins-table-rmd
17
+ inline: true
18
+ -->
19
+
20
+ ### [Table][md_hopper:src-_internal-plugins-table-rmd]
21
+
22
+ `Table Plugin` is based on the original [inquirer-table-input](https://github.com/edelciomolina/inquirer-table-input).
23
+ This plugin has been customized to support TypeScript and symbol input.
24
+
25
+ ## PLUGIN DOCS (same as above plugin title links)
26
+
27
+ <!-- MD_HOPPER: BEGIN_LINKS:
28
+ all: true
29
+ linked: true
30
+ -->
31
+
32
+ [TABLE PLUGIN][md_hopper:src-_internal-plugins-table-rmd]
33
+
34
+ <!-- MD_HOPPER: END_LINKS: -->
35
+
36
+ <!-- MD_HOPPER: BEGIN_DEFINE_LINKS: -->
37
+
38
+ [md_hopper:src-_internal-plugins-table-rmd]: ./src/_internal/plugins/table/README.md 'TABLE PLUGIN'
39
+
40
+ <!-- MD_HOPPER: END_DEFINE_LINKS: -->
@@ -1,11 +1,15 @@
1
- import { Column, Cell } from './types.js';
1
+ import { Answers } from 'inquirer';
2
+ import { Column, Cell, RawInputs } from './types.js';
2
3
  export declare class TableClass {
3
4
  columns: Column[];
4
5
  rows: string[][];
5
6
  constructor(columns: Column[], rows: Cell[][]);
6
7
  get header(): string[];
7
8
  getColumnType(columnIndex: number): import("../../types/parse-types.js").ParseType;
8
- updateRow(rowIndex: number, columnIndex: number, value: string): void;
9
+ getParser(columnIndex: number): Exclude<Column['parser'], undefined>;
10
+ getValidator(columnIndex: number): Exclude<Column['validate'], undefined>;
11
+ updateRow(rowIndex: number, columnIndex: number, value: string, answers: Answers): string;
9
12
  getValue(rowIndex: number, columnIndex: number): string;
10
- getResult(): Record<string, Cell>[];
13
+ getRaw(): RawInputs;
14
+ getResult(answers: Answers): Record<string, Cell>[];
11
15
  }
@@ -1,5 +1,5 @@
1
1
  import { PARSE_TYPES } from '../../types/parse-types.js';
2
- import { trim } from '../../utils/chalk.js';
2
+ import { trim, error } from '../../utils/chalk.js';
3
3
  import { parse } from '../../utils/parse-types.js';
4
4
  export class TableClass {
5
5
  columns = [];
@@ -17,21 +17,29 @@ export class TableClass {
17
17
  }
18
18
  return this.columns[columnIndex].type;
19
19
  }
20
- updateRow(rowIndex, columnIndex, value) {
20
+ getParser(columnIndex) {
21
+ return this.columns[columnIndex].parser || ((txt, _inputs, _answers) => txt);
22
+ }
23
+ getValidator(columnIndex) {
24
+ return (this.columns[columnIndex].validate ||
25
+ ((_txt, _inputs, _answers) => ({ isValid: true, message: '' })));
26
+ }
27
+ updateRow(rowIndex, columnIndex, value, answers) {
21
28
  if (!(rowIndex in this.rows)) {
22
- return;
29
+ return error(`row index (${rowIndex}) not found`);
23
30
  }
24
31
  const columns = this.rows[rowIndex];
25
32
  if (!(columnIndex in columns)) {
26
- return;
33
+ return error(`column index (${columnIndex}) not found`);
27
34
  }
28
- const validate = this.columns[columnIndex].validate || (() => true);
29
- if (!validate(value)) {
30
- return;
35
+ const validateResult = this.getValidator(columnIndex)(value, this.getRaw(), answers);
36
+ if (!validateResult.isValid) {
37
+ return validateResult.message;
31
38
  }
32
- const parser = this.columns[columnIndex].parser || ((txt) => txt);
33
- const parsed = parser(value);
39
+ const parser = this.getParser(columnIndex);
40
+ const parsed = parser(value, this.getRaw(), answers);
34
41
  columns[columnIndex] = `${parsed}`;
42
+ return '';
35
43
  }
36
44
  getValue(rowIndex, columnIndex) {
37
45
  if (!(rowIndex in this.rows)) {
@@ -43,14 +51,25 @@ export class TableClass {
43
51
  }
44
52
  return columns[columnIndex];
45
53
  }
46
- getResult() {
54
+ getRaw() {
55
+ const result = [];
56
+ for (let i = 0; i < this.rows.length; i++) {
57
+ result.push({});
58
+ for (let ii = 0; ii < (this.rows[i] || []).length; ii++) {
59
+ const { value: key } = this.columns[ii];
60
+ result[i][key] = trim(this.rows[i][ii]);
61
+ }
62
+ }
63
+ return result;
64
+ }
65
+ getResult(answers) {
47
66
  const result = [];
48
67
  for (let i = 0; i < this.rows.length; i++) {
49
68
  result.push({});
50
69
  for (let ii = 0; ii < (this.rows[i] || []).length; ii++) {
51
70
  const { value: key, type, parser } = this.columns[ii];
52
71
  const value = trim(this.rows[i][ii]);
53
- result[i][key] = parser ? parser(value) : parse(value, type);
72
+ result[i][key] = parser ? parser(value, this.getRaw(), answers) : parse(value, type);
54
73
  }
55
74
  }
56
75
  return result;
@@ -1,4 +1,5 @@
1
1
  import { ChalkInstance } from 'chalk';
2
+ import { Answers } from 'inquirer';
2
3
  import { ParseType } from '../../types/parse-types.js';
3
4
  export declare const DEFAULT_PAGE_SIZE = 5;
4
5
  export declare const INDEX_ERRORS: Readonly<{
@@ -39,7 +40,8 @@ export interface Column {
39
40
  name: string | ChalkInstance;
40
41
  value: string;
41
42
  type: ParseType;
42
- validate?: (input: string) => boolean;
43
- parser?: (input: any) => any;
43
+ validate?: (input: string, raws: RawInputs, answers: Answers) => ValidateResult;
44
+ parser?: (input: any, raws: RawInputs, answers: Answers) => any;
44
45
  }
45
46
  export type Cell = number | string | boolean;
47
+ export type RawInputs = Record<string, string>[];
@@ -18,6 +18,7 @@ export declare class TablePlugin extends Base {
18
18
  };
19
19
  editing: boolean;
20
20
  temporaryBackup: string | null;
21
+ purTyping: string;
21
22
  answers: Answers;
22
23
  constructor(question: Question<TableQuestion>, readLine: ReadLineInterface, answers: Answers);
23
24
  getCurrentValues(): Record<string, import("./table/types.js").Cell>[];
@@ -35,8 +36,7 @@ export declare class TablePlugin extends Base {
35
36
  onEnd(state: any): void;
36
37
  validate(): import("./table/types.js").ValidateResult;
37
38
  onError(_state: any): void;
38
- validateValue(): void;
39
- updateChoice(value: string): void;
39
+ updateChoice(value: string): string;
40
40
  get currentValue(): string;
41
41
  onEditPress(key: Key): void;
42
42
  getShowRowIndex(): {
@@ -45,5 +45,5 @@ export declare class TablePlugin extends Base {
45
45
  };
46
46
  isSelected(rowIndex: number, columnIndex: number): boolean;
47
47
  getColor(_value: string, isSelected: boolean, isEditable: boolean): string;
48
- render(isDisconnect?: boolean): void;
48
+ render(isDisconnect?: boolean, message?: string): void;
49
49
  }
@@ -4,7 +4,7 @@ import Base from 'inquirer/lib/prompts/base.js';
4
4
  import observe from 'inquirer/lib/utils/events.js';
5
5
  import { map } from 'rxjs/operators';
6
6
  import { dim, error } from '../utils/chalk.js';
7
- import { getKeypressValue } from '../utils/keypress.js';
7
+ import { getKeypressValue, getBackspaceText } from '../utils/keypress.js';
8
8
  import { TableClass } from './table/table.js';
9
9
  import { DEFAULT_OPTIONS } from './table/types.js';
10
10
  import { isNoChoiceOfIndex, gateValidateColumnIndexError, getNextIndex } from './table/utils.js';
@@ -21,6 +21,7 @@ export class TablePlugin extends Base {
21
21
  };
22
22
  editing = false;
23
23
  temporaryBackup = null;
24
+ purTyping = '';
24
25
  answers = [];
25
26
  constructor(question, readLine, answers) {
26
27
  super({ ...question, validate: () => false }, readLine, answers);
@@ -38,7 +39,7 @@ export class TablePlugin extends Base {
38
39
  this.updateColumnIndex(1);
39
40
  }
40
41
  getCurrentValues() {
41
- return this.table.getResult();
42
+ return this.table.getResult(this.answers);
42
43
  }
43
44
  isEditable(index) {
44
45
  return !this.options.disableColumnIndexes.includes(index);
@@ -75,6 +76,7 @@ export class TablePlugin extends Base {
75
76
  default:
76
77
  if (this.isEditable(this.pointerIndexes.column)) {
77
78
  this.editing = true;
79
+ this.clear();
78
80
  this.onEditPress(key);
79
81
  }
80
82
  }
@@ -129,10 +131,10 @@ export class TablePlugin extends Base {
129
131
  }
130
132
  clear() {
131
133
  this.temporaryBackup = null;
134
+ this.purTyping = '';
132
135
  }
133
136
  onEnd(state) {
134
137
  this.render(true);
135
- this.clear();
136
138
  this.status = 'answered';
137
139
  this.screen.done();
138
140
  cliCursor.show();
@@ -152,7 +154,6 @@ export class TablePlugin extends Base {
152
154
  return this.options.validate(this.getCurrentValues(), this.answers);
153
155
  }
154
156
  onError(_state) {
155
- this.clear();
156
157
  if (this.editing) {
157
158
  this.editing = false;
158
159
  this.render();
@@ -166,38 +167,39 @@ export class TablePlugin extends Base {
166
167
  }
167
168
  }
168
169
  }
169
- validateValue() { }
170
170
  updateChoice(value) {
171
- this.table.updateRow(this.pointerIndexes.row, this.pointerIndexes.column, value);
171
+ return this.table.updateRow(this.pointerIndexes.row, this.pointerIndexes.column, value, this.answers);
172
172
  }
173
173
  get currentValue() {
174
174
  return this.table.getValue(this.pointerIndexes.row, this.pointerIndexes.column);
175
175
  }
176
176
  onEditPress(key) {
177
- let value = this.currentValue;
178
177
  if (this.temporaryBackup === null) {
179
- this.temporaryBackup = value;
180
- value = '';
178
+ this.temporaryBackup = this.currentValue;
181
179
  }
180
+ let value = this.purTyping;
182
181
  switch (key.name) {
183
182
  case 'escape':
184
- this.updateChoice(this.temporaryBackup);
183
+ this.purTyping = '';
184
+ value = this.temporaryBackup;
185
185
  this.editing = false;
186
186
  break;
187
187
  case 'delete':
188
- this.updateChoice('');
188
+ this.purTyping = '';
189
+ value = this.purTyping;
189
190
  break;
190
191
  case 'backspace': {
191
- let currentValue = value;
192
- currentValue = currentValue.slice(0, -1);
193
- this.updateChoice(currentValue);
192
+ this.purTyping = getBackspaceText(this.purTyping);
193
+ value = this.purTyping;
194
194
  break;
195
195
  }
196
196
  default:
197
- this.updateChoice(`${value}${getKeypressValue(key)}`);
197
+ this.purTyping = `${this.purTyping}${getKeypressValue(key)}`;
198
+ value = this.purTyping;
198
199
  break;
199
200
  }
200
- this.render();
201
+ const message = this.updateChoice(value);
202
+ this.render(false, message);
201
203
  }
202
204
  getShowRowIndex() {
203
205
  const middleOfPage = Math.floor(this.options.pageSize / 2);
@@ -219,7 +221,7 @@ export class TablePlugin extends Base {
219
221
  }
220
222
  return isEditable ? this.options.colors.editable(value) : this.options.colors.selected(value);
221
223
  }
222
- render(isDisconnect = false) {
224
+ render(isDisconnect = false, message = '') {
223
225
  let content = this.getQuestion();
224
226
  let bottomContent = '';
225
227
  content += this.options.description ? `\n${this.options.description}` : '';
@@ -235,7 +237,8 @@ export class TablePlugin extends Base {
235
237
  for (let ii = 0; ii < (this.table.rows[i] || []).length; ii++) {
236
238
  const isEditable = this.isEditable(ii);
237
239
  const value = this.table.rows[i][ii];
238
- columnValues.push(this.getColor(value, this.isSelected(i, ii), isEditable));
240
+ const parser = this.table.getParser(ii);
241
+ columnValues.push(this.getColor(parser(value, this.table.getRaw(), this.answers), this.isSelected(i, ii), isEditable));
239
242
  }
240
243
  table.push(columnValues);
241
244
  }
@@ -252,6 +255,9 @@ export class TablePlugin extends Base {
252
255
  if (this.isNextCloses.escape) {
253
256
  bottomContent = this.options.escapeMessage;
254
257
  }
258
+ if (message) {
259
+ bottomContent = message;
260
+ }
255
261
  this.screen.render(content, bottomContent);
256
262
  }
257
263
  }
@@ -1,15 +1,14 @@
1
1
  import chalk from 'chalk';
2
2
  import inquirer from 'inquirer';
3
- import { success, warn, hop, cyan } from '../utils/chalk.js';
3
+ import { success, warn, hop, cyan, error } from '../utils/chalk.js';
4
4
  import { TablePlugin } from './table.js';
5
5
  const run = async () => {
6
6
  inquirer.registerPrompt('table-input', TablePlugin);
7
- const rows = [...Array(33).keys()].map(i => [cyan(`--path ${i}--`), '--id--', true, i + 100, '--lock--']);
8
7
  const question = {
9
8
  type: 'table-input',
10
- name: 'links',
11
- message: chalk.red('GENERATE LINKS PATH SETTINGS'),
12
- description: 'Edit setting.',
9
+ name: 'settings',
10
+ message: 'SETTINGS',
11
+ description: `You can edit data.`,
13
12
  disableColumnIndexes: [0],
14
13
  colors: {
15
14
  selected: warn,
@@ -17,18 +16,22 @@ const run = async () => {
17
16
  editing: hop,
18
17
  },
19
18
  columns: [
20
- { name: cyan.bold('filepath'), value: 'path', type: 'input' },
21
- { name: cyan.bold('id'), value: 'id', type: 'input', parser: () => 'aaa' },
22
- { name: cyan.bold('title'), value: 'title', type: 'confirm' },
23
- {
24
- name: cyan.bold('num'),
25
- value: 'output',
26
- type: 'number',
27
- validate: (value) => /^\d*$/.test(value) || value === '0' || !value,
19
+ { name: cyan.bold('ID'), value: 'id', type: 'input' },
20
+ { name: cyan.bold('title'), value: 'title', type: 'input', parser: (input) => /\.md$/.test(input) ? input : `${input}.md` },
21
+ { name: cyan.bold('count'), value: 'cnt', type: 'number', validate: (input) => {
22
+ const isValid = input !== '0';
23
+ return {
24
+ isValid,
25
+ message: isValid ? '' : error('should be more than 0')
26
+ };
27
+ }
28
28
  },
29
- { name: cyan.bold('lock'), value: 'lock', type: 'input' },
29
+ { name: cyan.bold('IS LOCK'), value: 'lock', type: 'confirm' },
30
+ ],
31
+ rows: [
32
+ [chalk.bold('ABCD'), 'hoge', 0, true],
33
+ [chalk.bold('EFG'), 'foo', 1, false]
30
34
  ],
31
- rows: rows,
32
35
  validate: (inputs, _answers) => {
33
36
  const isValid = inputs.every((i) => Object.values(i).every(v => `${v}`.length));
34
37
  return {
@@ -1,3 +1,4 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  import { Key } from 'readline';
3
3
  export declare const getKeypressValue: (key: Key) => string | undefined;
4
+ export declare const getBackspaceText: (str: string) => string;
@@ -12,3 +12,9 @@ export const getKeypressValue = (key) => {
12
12
  return key.sequence;
13
13
  }
14
14
  };
15
+ export const getBackspaceText = (str) => {
16
+ if (!str) {
17
+ return '';
18
+ }
19
+ return str.slice(0, -1);
20
+ };
@@ -1,9 +1,10 @@
1
+ import { isNumber } from 'my-gadgetry/type-check';
1
2
  import { PARSE_TYPES } from '../types/parse-types.js';
2
3
  const BOOL_VALUES = Object.freeze(['Y', 'y', 'yes', 'YES', 'x', 'v', 'o', 'true']);
3
4
  export const parse = (value, type) => {
4
5
  switch (type) {
5
6
  case PARSE_TYPES.number:
6
- return value ? Number(value) : 0;
7
+ return isNumber(value, true) ? Number(value) : 0;
7
8
  case PARSE_TYPES.confirm:
8
9
  return BOOL_VALUES.includes(value);
9
10
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inquirer-plugins",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.3",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -16,16 +16,19 @@
16
16
  "generate": "tsx --tsconfig ./tsconfig.tsx.json ./tools/generate-alias.zx.ts",
17
17
  "ci:update-version": "tsx --tsconfig ./tsconfig.tsx.json ./tools/update-version.zx.ts",
18
18
  "md:generate": "tsx --tsconfig ./tsconfig.tsx.json ./tools/generate-md.ts",
19
+
19
20
  "//--- test ---//": "",
20
21
  "test": "run-s test:*",
21
22
  "test:lint": "npm run lint",
22
23
  "test:prettier": "npm run prettier:check",
24
+
23
25
  "//--- build ---//": "",
24
26
  "build": "run-s build:*",
25
27
  "build:test": "npm run test",
26
28
  "build:clean": "npm run clean",
27
29
  "build:tsc": "npm run tsc",
28
30
  "build:generate": "npm run generate",
31
+
29
32
  "//--- publish ---//": "",
30
33
  "publish:build": "run-s build:{clean,tsc,generate}",
31
34
  "dry-run": "tsx --tsconfig ./tsconfig.tsx.json ./src/_internal/plugins/test.ts"
@@ -36,6 +39,20 @@
36
39
  "engines": {
37
40
  "node": ">=19.0.0"
38
41
  },
42
+ "keywords": [
43
+ "answer",
44
+ "answers",
45
+ "ask",
46
+ "confirm",
47
+ "generator",
48
+ "input",
49
+ "inquire",
50
+ "inquirer",
51
+ "prompt",
52
+ "prompts",
53
+ "table",
54
+ "question"
55
+ ],
39
56
  "exports": {
40
57
  ".": "./dist/**/*.js",
41
58
  "./table": "./dist/modules/table.js",
@@ -51,6 +68,7 @@
51
68
  "cli-cursor": "^4.0.0",
52
69
  "cli-table3": "^0.6.5",
53
70
  "inquirer": "^9.2.23",
71
+ "my-gadgetry": "^1.2.5",
54
72
  "zx": "^8.1.2"
55
73
  },
56
74
  "devDependencies": {
@@ -66,7 +84,6 @@
66
84
  "eslint-plugin-unused-imports": "^3.2.0",
67
85
  "husky": "^9.0.11",
68
86
  "lint-staged": "^15.2.5",
69
- "my-gadgetry": "^1.2.5",
70
87
  "npm-run-all2": "^6.2.0",
71
88
  "prettier": "^3.3.1",
72
89
  "tsc-alias": "^1.8.10",
@@ -0,0 +1,117 @@
1
+ <!-- MD_HOPPER: ID: src-_internal-plugins-table-rmd -->
2
+ <!-- MD_HOPPER: TITLE: TABLE PLUGIN -->
3
+ <!-- MD_HOPPER: OUTPUT: README.md -->
4
+
5
+ `Table Plugin` is based on the original [inquirer-table-input](https://github.com/edelciomolina/inquirer-table-input).
6
+ This plugin has been customized to support TypeScript and symbol input.
7
+
8
+ ```ts
9
+ import chalk from 'chalk'
10
+ import inquirer from 'inquirer'
11
+ import { TablePlugin, TableQuestion } from 'inquirer-plugins/table'
12
+
13
+ inquirer.registerPrompt('table', TablePlugin)
14
+
15
+ const question: TableQuestion = {
16
+ type: 'table',
17
+ name: 'settings',
18
+ message: 'SETTINGS',
19
+ description: `You can edit data.`,
20
+ disableColumnIndexes: [0],
21
+ colors: {
22
+ selected: chalk.hex('#FFA500'),
23
+ editable: chalk.green,
24
+ editing: chalk.hex('#F577F5'),
25
+ },
26
+ columns: [
27
+ { name: cyan.bold('ID'), value: 'id', type: 'input' },
28
+ {
29
+ name: cyan.bold('filename'),
30
+ value: 'filename',
31
+ type: 'input',
32
+ parser: (input: string, _inputs, _answers) => (/\.md$/.test(input) ? input : `${input}.md`),
33
+ },
34
+ {
35
+ name: cyan.bold('count'),
36
+ value: 'cnt',
37
+ type: 'number',
38
+ validate: (input: string, _inputs, _answers) => {
39
+ const isValid = Number(input) > 0
40
+ return {
41
+ isValid,
42
+ message: isValid ? '' : error('should be more than 0'),
43
+ }
44
+ },
45
+ },
46
+ { name: cyan.bold('IS LOCK'), value: 'lock', type: 'confirm' },
47
+ ],
48
+ rows: [
49
+ [chalk.bold('ABCD'), 'hoge', 0, true],
50
+ [chalk.bold('EFG'), 'foo', 1, false],
51
+ ],
52
+ validate: (inputs, _answers) => {
53
+ const isValid = inputs.every((i: any) => Object.values(i).every(v => `${v}`.length))
54
+ return {
55
+ isValid,
56
+ message: isValid ? '' : 'not allow empty input',
57
+ }
58
+ },
59
+ }
60
+ inquirer.prompt([question])
61
+ ```
62
+
63
+ > In the [inquirer-table-input](https://github.com/edelciomolina/inquirer-table-input#usage), it was necessary to set validation to false, but this plugin makes it configurable by separating the validation and processing within Inquirer. Instead of using a boolean, changes have been made to pass isValid and message.
64
+
65
+ ```json
66
+ {
67
+ "settings": {
68
+ "state": true,
69
+ "result": [
70
+ {
71
+ "id": "ABCD",
72
+ "filename": "hoge.md",
73
+ "cnt": 0,
74
+ "lock": true
75
+ },
76
+ {
77
+ "id": "EFG",
78
+ "filename": "foo.md",
79
+ "cnt": 1,
80
+ "lock": false
81
+ }
82
+ ]
83
+ }
84
+ }
85
+ ```
86
+
87
+ ## Params
88
+
89
+ - **type** [*required*]
90
+ - **name** [*required*]
91
+ - **message** [*optional*] (String | ChalkInstance)
92
+ - **description** [*optional*] (String | ChalkInstance)
93
+ - **escapeMessage** [*optional*] (String | ChalkInstance)
94
+ - **confirmMessage** [*optional*] (String | ChalkInstance)
95
+ - **disableColumnIndexes** [*optional*] (number[])
96
+ - **colors** [*optional*]
97
+ - _selected_ [*optional*] (ChalkInstance)
98
+ - _editable_ [*optional*] (ChalkInstance)
99
+ - _editing_ [*optional*] (ChalkInstance)
100
+ - **columns** [*required*]
101
+ - **rows** [*required*]
102
+ - **validate** [*optional*] (() => { isValid: boolean, message: string })
103
+ - **pageSize** [*optional*] number
104
+
105
+ ## Column Params
106
+
107
+ - **name**: [*required*] (String | ChalkInstance) To show table header colum name.
108
+ - **value**: [*required*] (String) result param property name.
109
+ - **type**: [*required*] (String) `input`, `number`, `confirm`
110
+ - **parser**: [*optional*] (() => any) table view & result parser
111
+ - **validate**: [*optional*] (() => { isValid: boolean, message: string }) validate cell
112
+
113
+ ## Raws (number | string | boolean)
114
+
115
+ <!-- MD_HOPPER: BEGIN_DEFINE_LINKS: -->
116
+
117
+ <!-- MD_HOPPER: END_DEFINE_LINKS: -->
@@ -1,8 +1,10 @@
1
+ import { Answers } from 'inquirer'
2
+
1
3
  import { PARSE_TYPES } from '~types/parse-types.js'
2
- import { trim } from '~utils/chalk.js'
4
+ import { trim, error } from '~utils/chalk.js'
3
5
  import { parse } from '~utils/parse-types.js'
4
6
 
5
- import { Column, Cell } from './types.js'
7
+ import { Column, Cell, RawInputs } from './types.js'
6
8
 
7
9
  export class TableClass {
8
10
  columns: Column[] = []
@@ -24,21 +26,33 @@ export class TableClass {
24
26
  return this.columns[columnIndex].type
25
27
  }
26
28
 
27
- updateRow(rowIndex: number, columnIndex: number, value: string) {
29
+ getParser(columnIndex: number): Exclude<Column['parser'], undefined> {
30
+ return this.columns[columnIndex].parser || ((txt: string, _inputs: RawInputs, _answers: Answers) => txt)
31
+ }
32
+
33
+ getValidator(columnIndex: number): Exclude<Column['validate'], undefined> {
34
+ return (
35
+ this.columns[columnIndex].validate ||
36
+ ((_txt: string, _inputs: RawInputs, _answers: Answers) => ({ isValid: true, message: '' }))
37
+ )
38
+ }
39
+
40
+ updateRow(rowIndex: number, columnIndex: number, value: string, answers: Answers) {
28
41
  if (!(rowIndex in this.rows)) {
29
- return
42
+ return error(`row index (${rowIndex}) not found`)
30
43
  }
31
44
  const columns = this.rows[rowIndex]
32
45
  if (!(columnIndex in columns)) {
33
- return
46
+ return error(`column index (${columnIndex}) not found`)
34
47
  }
35
- const validate = this.columns[columnIndex].validate || (() => true)
36
- if (!validate(value)) {
37
- return
48
+ const validateResult = this.getValidator(columnIndex)(value, this.getRaw(), answers)
49
+ if (!validateResult.isValid) {
50
+ return validateResult.message
38
51
  }
39
- const parser = this.columns[columnIndex].parser || ((txt: string) => txt)
40
- const parsed = parser(value)
52
+ const parser = this.getParser(columnIndex)
53
+ const parsed = parser(value, this.getRaw(), answers)
41
54
  columns[columnIndex] = `${parsed}`
55
+ return ''
42
56
  }
43
57
 
44
58
  getValue(rowIndex: number, columnIndex: number) {
@@ -52,14 +66,26 @@ export class TableClass {
52
66
  return columns[columnIndex]
53
67
  }
54
68
 
55
- getResult() {
69
+ getRaw(): RawInputs {
70
+ const result: RawInputs = []
71
+ for (let i = 0; i < this.rows.length; i++) {
72
+ result.push({})
73
+ for (let ii = 0; ii < (this.rows[i] || []).length; ii++) {
74
+ const { value: key } = this.columns[ii]
75
+ result[i][key] = trim(this.rows[i][ii])
76
+ }
77
+ }
78
+ return result
79
+ }
80
+
81
+ getResult(answers: Answers) {
56
82
  const result: Record<string, Cell>[] = []
57
83
  for (let i = 0; i < this.rows.length; i++) {
58
84
  result.push({})
59
85
  for (let ii = 0; ii < (this.rows[i] || []).length; ii++) {
60
86
  const { value: key, type, parser } = this.columns[ii]
61
87
  const value = trim(this.rows[i][ii])
62
- result[i][key] = parser ? parser(value) : parse(value, type)
88
+ result[i][key] = parser ? parser(value, this.getRaw(), answers) : parse(value, type)
63
89
  }
64
90
  }
65
91
  return result
@@ -1,4 +1,5 @@
1
1
  import chalk, { ChalkInstance } from 'chalk'
2
+ import { Answers } from 'inquirer'
2
3
 
3
4
  import { ParseType } from '~types/parse-types.js'
4
5
  import { hop, success, warn } from '~utils/chalk.js'
@@ -64,8 +65,10 @@ export interface Column {
64
65
  name: string | ChalkInstance
65
66
  value: string
66
67
  type: ParseType
67
- validate?: (input: string) => boolean
68
- parser?: (input: any) => any
68
+ validate?: (input: string, raws: RawInputs, answers: Answers) => ValidateResult
69
+ parser?: (input: any, raws: RawInputs, answers: Answers) => any
69
70
  }
70
71
 
71
72
  export type Cell = number | string | boolean
73
+
74
+ export type RawInputs = Record<string, string>[]
@@ -10,7 +10,7 @@ import { map } from 'rxjs/operators'
10
10
 
11
11
  import { Done } from '~types/inquirer.js'
12
12
  import { dim, error } from '~utils/chalk.js'
13
- import { getKeypressValue } from '~utils/keypress.js'
13
+ import { getKeypressValue, getBackspaceText } from '~utils/keypress.js'
14
14
 
15
15
  import { TableClass } from './table/table.js'
16
16
  import { DEFAULT_OPTIONS, RequiredOptions, TableQuestion } from './table/types.js'
@@ -31,6 +31,7 @@ export class TablePlugin extends Base {
31
31
  }
32
32
  editing = false
33
33
  temporaryBackup: string | null = null
34
+ purTyping = ''
34
35
  answers: Answers = []
35
36
 
36
37
  constructor(question: Question<TableQuestion>, readLine: ReadLineInterface, answers: Answers) {
@@ -51,7 +52,7 @@ export class TablePlugin extends Base {
51
52
  }
52
53
 
53
54
  getCurrentValues() {
54
- return this.table.getResult()
55
+ return this.table.getResult(this.answers)
55
56
  }
56
57
 
57
58
  isEditable(index: number) {
@@ -97,6 +98,7 @@ export class TablePlugin extends Base {
97
98
  default:
98
99
  if (this.isEditable(this.pointerIndexes.column)) {
99
100
  this.editing = true
101
+ this.clear()
100
102
  this.onEditPress(key)
101
103
  }
102
104
  }
@@ -159,11 +161,11 @@ export class TablePlugin extends Base {
159
161
 
160
162
  clear() {
161
163
  this.temporaryBackup = null
164
+ this.purTyping = ''
162
165
  }
163
166
 
164
167
  onEnd(state: any) {
165
168
  this.render(true)
166
- this.clear()
167
169
  this.status = 'answered'
168
170
  this.screen.done()
169
171
  cliCursor.show()
@@ -184,7 +186,6 @@ export class TablePlugin extends Base {
184
186
  }
185
187
 
186
188
  onError(_state: any) {
187
- this.clear()
188
189
  if (this.editing) {
189
190
  this.editing = false
190
191
  this.render()
@@ -197,10 +198,8 @@ export class TablePlugin extends Base {
197
198
  }
198
199
  }
199
200
 
200
- validateValue() {}
201
-
202
201
  updateChoice(value: string) {
203
- this.table.updateRow(this.pointerIndexes.row, this.pointerIndexes.column, value)
202
+ return this.table.updateRow(this.pointerIndexes.row, this.pointerIndexes.column, value, this.answers)
204
203
  }
205
204
 
206
205
  get currentValue() {
@@ -208,30 +207,32 @@ export class TablePlugin extends Base {
208
207
  }
209
208
 
210
209
  onEditPress(key: Key) {
211
- let value = this.currentValue
212
210
  if (this.temporaryBackup === null) {
213
- this.temporaryBackup = value
214
- value = ''
211
+ this.temporaryBackup = this.currentValue
215
212
  }
213
+ let value = this.purTyping
216
214
  switch (key.name) {
217
215
  case 'escape':
218
- this.updateChoice(this.temporaryBackup)
216
+ this.purTyping = ''
217
+ value = this.temporaryBackup
219
218
  this.editing = false
220
219
  break
221
220
  case 'delete':
222
- this.updateChoice('')
221
+ this.purTyping = ''
222
+ value = this.purTyping
223
223
  break
224
224
  case 'backspace': {
225
- let currentValue = value
226
- currentValue = currentValue.slice(0, -1)
227
- this.updateChoice(currentValue)
225
+ this.purTyping = getBackspaceText(this.purTyping)
226
+ value = this.purTyping
228
227
  break
229
228
  }
230
229
  default:
231
- this.updateChoice(`${value}${getKeypressValue(key)}`)
230
+ this.purTyping = `${this.purTyping}${getKeypressValue(key)}`
231
+ value = this.purTyping
232
232
  break
233
233
  }
234
- this.render()
234
+ const message = this.updateChoice(value)
235
+ this.render(false, message)
235
236
  }
236
237
 
237
238
  getShowRowIndex() {
@@ -256,7 +257,7 @@ export class TablePlugin extends Base {
256
257
  return isEditable ? this.options.colors.editable(value) : this.options.colors.selected(value)
257
258
  }
258
259
 
259
- render(isDisconnect = false) {
260
+ render(isDisconnect = false, message = '') {
260
261
  let content = this.getQuestion()
261
262
  let bottomContent: string | ChalkInstance = ''
262
263
 
@@ -274,7 +275,10 @@ export class TablePlugin extends Base {
274
275
  for (let ii = 0; ii < (this.table.rows[i] || []).length; ii++) {
275
276
  const isEditable = this.isEditable(ii)
276
277
  const value = this.table.rows[i][ii]
277
- columnValues.push(this.getColor(value, this.isSelected(i, ii), isEditable))
278
+ const parser = this.table.getParser(ii)
279
+ columnValues.push(
280
+ this.getColor(parser(value, this.table.getRaw(), this.answers), this.isSelected(i, ii), isEditable)
281
+ )
278
282
  }
279
283
  table.push(columnValues)
280
284
  }
@@ -293,7 +297,9 @@ export class TablePlugin extends Base {
293
297
  if (this.isNextCloses.escape) {
294
298
  bottomContent = this.options.escapeMessage
295
299
  }
296
-
300
+ if (message) {
301
+ bottomContent = message
302
+ }
297
303
  this.screen.render(content, bottomContent as string)
298
304
  }
299
305
  }
@@ -1,38 +1,56 @@
1
1
  import chalk from 'chalk'
2
2
  import inquirer from 'inquirer'
3
3
 
4
- import { success, warn, hop, cyan } from '../utils/chalk.js'
4
+ import { success, warn, hop, cyan, error } from '../utils/chalk.js'
5
5
 
6
6
  import { TableQuestion } from './table/types.js'
7
7
  import { TablePlugin } from './table.js'
8
8
 
9
9
  const run = async () => {
10
10
  inquirer.registerPrompt('table-input', TablePlugin)
11
- const rows = [...Array(33).keys()].map(i => [cyan(`--path ${i}--`), '--id--', true, i + 100, '--lock--'])
11
+ // const rows = [...Array(33).keys()].map(i => [cyan(`--path ${i}--`), '--id--', true, i + 100, '--lock--'])
12
12
  const question: TableQuestion = {
13
13
  type: 'table-input',
14
- name: 'links',
15
- message: chalk.red('GENERATE LINKS PATH SETTINGS'),
16
- description: 'Edit setting.',
14
+ name: 'settings',
15
+ message: 'SETTINGS',
16
+ description: `You can edit data.`,
17
17
  disableColumnIndexes: [0],
18
18
  colors: {
19
19
  selected: warn,
20
20
  editable: success,
21
21
  editing: hop,
22
22
  },
23
+ // columns: [
24
+ // { name: cyan.bold('filepath'), value: 'path', type: 'input' },
25
+ // { name: cyan.bold('id'), value: 'id', type: 'input', parser: () => 'aaa' },
26
+ // { name: cyan.bold('title'), value: 'title', type: 'confirm' },
27
+ // {
28
+ // name: cyan.bold('num'),
29
+ // value: 'output',
30
+ // type: 'number',
31
+ // validate: (value: string) => /^\d*$/.test(value) || value === '0' || !value,
32
+ // },
33
+ // { name: cyan.bold('lock'), value: 'lock', type: 'input' },
34
+ // ],
35
+ // rows: rows,
23
36
  columns: [
24
- { name: cyan.bold('filepath'), value: 'path', type: 'input' },
25
- { name: cyan.bold('id'), value: 'id', type: 'input', parser: () => 'aaa' },
26
- { name: cyan.bold('title'), value: 'title', type: 'confirm' },
27
- {
28
- name: cyan.bold('num'),
29
- value: 'output',
30
- type: 'number',
31
- validate: (value: string) => /^\d*$/.test(value) || value === '0' || !value,
37
+ { name: cyan.bold('ID'), value: 'id', type: 'input' },
38
+ // { name: cyan.bold('title'), value: 'title', type: 'input', parser: (input: string) => `${input}.md` },
39
+ { name: cyan.bold('title'), value: 'title', type: 'input', parser: (input: string) => /\.md$/.test(input) ? input : `${input}.md` },
40
+ { name: cyan.bold('count'), value: 'cnt', type: 'number', validate: (input) => {
41
+ const isValid = input !== '0'
42
+ return {
43
+ isValid,
44
+ message: isValid ? '' : error('should be more than 0')
45
+ }
46
+ }
32
47
  },
33
- { name: cyan.bold('lock'), value: 'lock', type: 'input' },
48
+ { name: cyan.bold('IS LOCK'), value: 'lock', type: 'confirm' },
49
+ ],
50
+ rows: [
51
+ [chalk.bold('ABCD'), 'hoge', 0, true],
52
+ [chalk.bold('EFG'), 'foo', 1, false]
34
53
  ],
35
- rows: rows,
36
54
  validate: (inputs, _answers) => {
37
55
  const isValid = inputs.every((i: any) => Object.values(i).every(v => `${v}`.length))
38
56
  return {
@@ -14,3 +14,10 @@ export const getKeypressValue = (key: Key) => {
14
14
  return key.sequence
15
15
  }
16
16
  }
17
+
18
+ export const getBackspaceText = (str: string) => {
19
+ if (!str) {
20
+ return ''
21
+ }
22
+ return str.slice(0, -1)
23
+ }
@@ -1,3 +1,5 @@
1
+ import { isNumber } from 'my-gadgetry/type-check'
2
+
1
3
  import { PARSE_TYPES, ParseType } from '~types/parse-types.js'
2
4
 
3
5
  const BOOL_VALUES = Object.freeze(['Y', 'y', 'yes', 'YES', 'x', 'v', 'o', 'true'])
@@ -5,7 +7,7 @@ const BOOL_VALUES = Object.freeze(['Y', 'y', 'yes', 'YES', 'x', 'v', 'o', 'true'
5
7
  export const parse = (value: string, type: ParseType) => {
6
8
  switch (type) {
7
9
  case PARSE_TYPES.number:
8
- return value ? Number(value) : 0
10
+ return isNumber(value, true) ? Number(value) : 0
9
11
  case PARSE_TYPES.confirm:
10
12
  return BOOL_VALUES.includes(value)
11
13
  default: