mepcli 0.3.0 → 0.5.0
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 +59 -4
- package/dist/ansi.d.ts +3 -0
- package/dist/ansi.js +4 -0
- package/dist/base.d.ts +6 -0
- package/dist/base.js +18 -0
- package/dist/core.d.ts +8 -3
- package/dist/core.js +20 -24
- package/dist/input.d.ts +1 -0
- package/dist/input.js +62 -8
- package/dist/prompts/autocomplete.d.ts +22 -0
- package/dist/prompts/autocomplete.js +175 -0
- package/dist/prompts/checkbox.d.ts +2 -1
- package/dist/prompts/checkbox.js +14 -4
- package/dist/prompts/confirm.d.ts +2 -1
- package/dist/prompts/confirm.js +6 -0
- package/dist/prompts/date.d.ts +3 -1
- package/dist/prompts/date.js +62 -31
- package/dist/prompts/file.js +0 -6
- package/dist/prompts/multi-select.d.ts +2 -1
- package/dist/prompts/multi-select.js +15 -0
- package/dist/prompts/number.d.ts +2 -1
- package/dist/prompts/number.js +22 -0
- package/dist/prompts/rating.d.ts +8 -0
- package/dist/prompts/rating.js +78 -0
- package/dist/prompts/select.d.ts +2 -1
- package/dist/prompts/select.js +15 -0
- package/dist/prompts/slider.d.ts +2 -1
- package/dist/prompts/slider.js +14 -0
- package/dist/prompts/sort.d.ts +14 -0
- package/dist/prompts/sort.js +160 -0
- package/dist/prompts/table.d.ts +14 -0
- package/dist/prompts/table.js +106 -0
- package/dist/prompts/text.js +0 -50
- package/dist/prompts/toggle.d.ts +2 -1
- package/dist/prompts/toggle.js +6 -0
- package/dist/spinner.d.ts +33 -0
- package/dist/spinner.js +89 -0
- package/dist/symbols.d.ts +4 -0
- package/dist/symbols.js +6 -2
- package/dist/types.d.ts +32 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +4 -1
- package/example.ts +60 -11
- package/package.json +1 -1
package/dist/prompts/date.js
CHANGED
|
@@ -37,6 +37,18 @@ class DatePrompt extends base_1.Prompt {
|
|
|
37
37
|
}
|
|
38
38
|
handleInput(char) {
|
|
39
39
|
if (char === '\r' || char === '\n') {
|
|
40
|
+
// Min constraint check
|
|
41
|
+
if (this.options.min && this.value < this.options.min) {
|
|
42
|
+
this.errorMsg = 'Date cannot be before minimum allowed.';
|
|
43
|
+
this.render(false);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Max constraint check
|
|
47
|
+
if (this.options.max && this.value > this.options.max) {
|
|
48
|
+
this.errorMsg = 'Date cannot be after maximum allowed.';
|
|
49
|
+
this.render(false);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
40
52
|
this.submit(this.value);
|
|
41
53
|
return;
|
|
42
54
|
}
|
|
@@ -89,7 +101,16 @@ class DatePrompt extends base_1.Prompt {
|
|
|
89
101
|
else if (this.selectedField === 4)
|
|
90
102
|
d.setMinutes(Math.max(0, Math.min(59, finalVal)));
|
|
91
103
|
this.value = d;
|
|
92
|
-
|
|
104
|
+
// Check immediately after updating the value to display an error message (but still allow further input)
|
|
105
|
+
if (this.options.min && this.value < this.options.min) {
|
|
106
|
+
this.errorMsg = 'Warning: Date is before minimum.';
|
|
107
|
+
}
|
|
108
|
+
else if (this.options.max && this.value > this.options.max) {
|
|
109
|
+
this.errorMsg = 'Warning: Date is after maximum.';
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.errorMsg = '';
|
|
113
|
+
}
|
|
93
114
|
this.render(false);
|
|
94
115
|
return;
|
|
95
116
|
}
|
|
@@ -99,39 +120,49 @@ class DatePrompt extends base_1.Prompt {
|
|
|
99
120
|
if (isUp || isDown) {
|
|
100
121
|
this.inputBuffer = ''; // Reset buffer on arrow move
|
|
101
122
|
const dir = isUp ? 1 : -1;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
break;
|
|
110
|
-
case 2:
|
|
111
|
-
d.setDate(d.getDate() + dir);
|
|
112
|
-
break;
|
|
113
|
-
case 3:
|
|
114
|
-
d.setHours(d.getHours() + dir);
|
|
115
|
-
break;
|
|
116
|
-
case 4:
|
|
117
|
-
d.setMinutes(d.getMinutes() + dir);
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
let valid = true;
|
|
121
|
-
if (this.options.min && d < this.options.min) {
|
|
122
|
-
this.errorMsg = 'Date cannot be before minimum allowed.';
|
|
123
|
-
valid = false;
|
|
124
|
-
}
|
|
125
|
-
if (this.options.max && d > this.options.max) {
|
|
126
|
-
this.errorMsg = 'Date cannot be after maximum allowed.';
|
|
127
|
-
valid = false;
|
|
123
|
+
this.adjustDate(dir);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
handleMouse(event) {
|
|
127
|
+
if (event.action === 'scroll') {
|
|
128
|
+
if (event.scroll === 'up') {
|
|
129
|
+
this.adjustDate(1);
|
|
128
130
|
}
|
|
129
|
-
if (
|
|
130
|
-
this.
|
|
131
|
-
this.errorMsg = '';
|
|
131
|
+
else if (event.scroll === 'down') {
|
|
132
|
+
this.adjustDate(-1);
|
|
132
133
|
}
|
|
133
|
-
this.render(false);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
adjustDate(dir) {
|
|
137
|
+
const d = new Date(this.value);
|
|
138
|
+
switch (this.selectedField) {
|
|
139
|
+
case 0:
|
|
140
|
+
d.setFullYear(d.getFullYear() + dir);
|
|
141
|
+
break;
|
|
142
|
+
case 1:
|
|
143
|
+
d.setMonth(d.getMonth() + dir);
|
|
144
|
+
break;
|
|
145
|
+
case 2:
|
|
146
|
+
d.setDate(d.getDate() + dir);
|
|
147
|
+
break;
|
|
148
|
+
case 3:
|
|
149
|
+
d.setHours(d.getHours() + dir);
|
|
150
|
+
break;
|
|
151
|
+
case 4:
|
|
152
|
+
d.setMinutes(d.getMinutes() + dir);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
this.value = d;
|
|
156
|
+
if (this.options.min && this.value < this.options.min) {
|
|
157
|
+
this.errorMsg = 'Date cannot be before minimum allowed.';
|
|
158
|
+
}
|
|
159
|
+
else if (this.options.max && this.value > this.options.max) {
|
|
160
|
+
this.errorMsg = 'Date cannot be after maximum allowed.';
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
this.errorMsg = '';
|
|
164
|
+
}
|
|
165
|
+
this.render(false);
|
|
166
|
+
}
|
|
136
167
|
}
|
|
137
168
|
exports.DatePrompt = DatePrompt;
|
package/dist/prompts/file.js
CHANGED
|
@@ -131,12 +131,6 @@ class FilePrompt extends base_1.Prompt {
|
|
|
131
131
|
// Move right
|
|
132
132
|
const prefix = `${icon} ${theme_1.theme.title}${this.options.message} `;
|
|
133
133
|
const prefixLen = this.stripAnsi(prefix).length;
|
|
134
|
-
// Cursor is usually at the end of input unless we add backspace support etc.
|
|
135
|
-
// The cursor property tracks it, but my handleInput simplified it.
|
|
136
|
-
// Let's rely on this.input.length for now since handleInput appends.
|
|
137
|
-
// Ah, handleInput logic below supports cursor pos theoretically but I only see appending?
|
|
138
|
-
// Actually handleInput doesn't support left/right in the original code, it supports down/up for suggestions.
|
|
139
|
-
// So cursor is always at end.
|
|
140
134
|
const targetCol = prefixLen + this.input.length;
|
|
141
135
|
this.print(ansi_1.ANSI.CURSOR_LEFT);
|
|
142
136
|
if (targetCol > 0)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { MultiSelectOptions } from '../types';
|
|
2
|
+
import { MultiSelectOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class MultiSelectPrompt<V> extends Prompt<any[], MultiSelectOptions<V>> {
|
|
4
4
|
private selectedIndex;
|
|
5
5
|
private checkedState;
|
|
@@ -11,4 +11,5 @@ export declare class MultiSelectPrompt<V> extends Prompt<any[], MultiSelectOptio
|
|
|
11
11
|
private getFilteredChoices;
|
|
12
12
|
protected render(firstRender: boolean): void;
|
|
13
13
|
protected handleInput(char: string): void;
|
|
14
|
+
protected handleMouse(event: MouseEvent): void;
|
|
14
15
|
}
|
|
@@ -120,5 +120,20 @@ class MultiSelectPrompt extends base_1.Prompt {
|
|
|
120
120
|
this.render(false);
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
handleMouse(event) {
|
|
124
|
+
const choices = this.getFilteredChoices();
|
|
125
|
+
if (choices.length === 0)
|
|
126
|
+
return;
|
|
127
|
+
if (event.action === 'scroll') {
|
|
128
|
+
if (event.scroll === 'up') {
|
|
129
|
+
this.selectedIndex = (this.selectedIndex - 1 + choices.length) % choices.length;
|
|
130
|
+
this.render(false);
|
|
131
|
+
}
|
|
132
|
+
else if (event.scroll === 'down') {
|
|
133
|
+
this.selectedIndex = (this.selectedIndex + 1) % choices.length;
|
|
134
|
+
this.render(false);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
123
138
|
}
|
|
124
139
|
exports.MultiSelectPrompt = MultiSelectPrompt;
|
package/dist/prompts/number.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { NumberOptions } from '../types';
|
|
2
|
+
import { NumberOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class NumberPrompt extends Prompt<number, NumberOptions> {
|
|
4
4
|
private stringValue;
|
|
5
5
|
private cursor;
|
|
@@ -7,4 +7,5 @@ export declare class NumberPrompt extends Prompt<number, NumberOptions> {
|
|
|
7
7
|
constructor(options: NumberOptions);
|
|
8
8
|
protected render(firstRender: boolean): void;
|
|
9
9
|
protected handleInput(char: string): void;
|
|
10
|
+
protected handleMouse(event: MouseEvent): void;
|
|
10
11
|
}
|
package/dist/prompts/number.js
CHANGED
|
@@ -130,5 +130,27 @@ class NumberPrompt extends base_1.Prompt {
|
|
|
130
130
|
this.render(false);
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
+
handleMouse(event) {
|
|
134
|
+
if (event.action === 'scroll') {
|
|
135
|
+
let num = parseFloat(this.stringValue) || 0;
|
|
136
|
+
const step = this.options.step ?? 1;
|
|
137
|
+
if (event.scroll === 'up') {
|
|
138
|
+
num += step;
|
|
139
|
+
if (this.options.max !== undefined && num > this.options.max)
|
|
140
|
+
num = this.options.max;
|
|
141
|
+
}
|
|
142
|
+
else if (event.scroll === 'down') {
|
|
143
|
+
num -= step;
|
|
144
|
+
if (this.options.min !== undefined && num < this.options.min)
|
|
145
|
+
num = this.options.min;
|
|
146
|
+
}
|
|
147
|
+
// Round to avoid float errors
|
|
148
|
+
num = Math.round(num * 10000) / 10000;
|
|
149
|
+
this.stringValue = num.toString();
|
|
150
|
+
this.cursor = this.stringValue.length;
|
|
151
|
+
this.errorMsg = '';
|
|
152
|
+
this.render(false);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
133
155
|
}
|
|
134
156
|
exports.NumberPrompt = NumberPrompt;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { RatingOptions, MouseEvent } from '../types';
|
|
3
|
+
export declare class RatingPrompt extends Prompt<number, RatingOptions> {
|
|
4
|
+
constructor(options: RatingOptions);
|
|
5
|
+
protected render(firstRender: boolean): void;
|
|
6
|
+
protected handleInput(char: string): void;
|
|
7
|
+
protected handleMouse(event: MouseEvent): void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RatingPrompt = void 0;
|
|
4
|
+
const ansi_1 = require("../ansi");
|
|
5
|
+
const base_1 = require("../base");
|
|
6
|
+
const theme_1 = require("../theme");
|
|
7
|
+
const symbols_1 = require("../symbols");
|
|
8
|
+
class RatingPrompt extends base_1.Prompt {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
super(options);
|
|
11
|
+
// Default to min if initial is not provided
|
|
12
|
+
this.value = options.initial ?? (options.min || 1);
|
|
13
|
+
}
|
|
14
|
+
render(firstRender) {
|
|
15
|
+
const min = this.options.min || 1;
|
|
16
|
+
const max = this.options.max || 5;
|
|
17
|
+
// Render stars
|
|
18
|
+
let stars = '';
|
|
19
|
+
for (let i = min; i <= max; i++) {
|
|
20
|
+
if (i <= this.value) {
|
|
21
|
+
stars += `${theme_1.theme.success}${symbols_1.symbols.star}${ansi_1.ANSI.RESET} `;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
stars += `${theme_1.theme.muted}${symbols_1.symbols.starEmpty}${ansi_1.ANSI.RESET} `;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const output = `${theme_1.theme.success}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} ${stars}`;
|
|
28
|
+
this.renderFrame(output);
|
|
29
|
+
}
|
|
30
|
+
handleInput(char) {
|
|
31
|
+
const min = this.options.min || 1;
|
|
32
|
+
const max = this.options.max || 5;
|
|
33
|
+
if (char === '\r' || char === '\n') {
|
|
34
|
+
this.submit(this.value);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Arrow keys
|
|
38
|
+
if (this.isLeft(char) || this.isDown(char)) { // Left/Down decreases
|
|
39
|
+
if (this.value > min) {
|
|
40
|
+
this.value--;
|
|
41
|
+
this.render(false);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (this.isRight(char) || this.isUp(char)) { // Right/Up increases
|
|
45
|
+
if (this.value < max) {
|
|
46
|
+
this.value++;
|
|
47
|
+
this.render(false);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Number keys (1-9)
|
|
51
|
+
const num = parseInt(char);
|
|
52
|
+
if (!isNaN(num)) {
|
|
53
|
+
if (num >= min && num <= max) {
|
|
54
|
+
this.value = num;
|
|
55
|
+
this.render(false);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
handleMouse(event) {
|
|
60
|
+
const min = this.options.min || 1;
|
|
61
|
+
const max = this.options.max || 5;
|
|
62
|
+
if (event.action === 'scroll') {
|
|
63
|
+
if (event.scroll === 'up') {
|
|
64
|
+
if (this.value < max) {
|
|
65
|
+
this.value++;
|
|
66
|
+
this.render(false);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (event.scroll === 'down') {
|
|
70
|
+
if (this.value > min) {
|
|
71
|
+
this.value--;
|
|
72
|
+
this.render(false);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.RatingPrompt = RatingPrompt;
|
package/dist/prompts/select.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { SelectOptions } from '../types';
|
|
2
|
+
import { SelectOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class SelectPrompt<V> extends Prompt<any, SelectOptions<V>> {
|
|
4
4
|
private selectedIndex;
|
|
5
5
|
private searchBuffer;
|
|
@@ -11,4 +11,5 @@ export declare class SelectPrompt<V> extends Prompt<any, SelectOptions<V>> {
|
|
|
11
11
|
private getFilteredChoices;
|
|
12
12
|
protected render(firstRender: boolean): void;
|
|
13
13
|
protected handleInput(char: string): void;
|
|
14
|
+
protected handleMouse(event: MouseEvent): void;
|
|
14
15
|
}
|
package/dist/prompts/select.js
CHANGED
|
@@ -145,5 +145,20 @@ class SelectPrompt extends base_1.Prompt {
|
|
|
145
145
|
this.render(false);
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
+
handleMouse(event) {
|
|
149
|
+
const choices = this.getFilteredChoices();
|
|
150
|
+
if (choices.length === 0)
|
|
151
|
+
return;
|
|
152
|
+
if (event.action === 'scroll') {
|
|
153
|
+
if (event.scroll === 'up') {
|
|
154
|
+
this.selectedIndex = this.findNextSelectableIndex(this.selectedIndex, -1);
|
|
155
|
+
this.render(false);
|
|
156
|
+
}
|
|
157
|
+
else if (event.scroll === 'down') {
|
|
158
|
+
this.selectedIndex = this.findNextSelectableIndex(this.selectedIndex, 1);
|
|
159
|
+
this.render(false);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
148
163
|
}
|
|
149
164
|
exports.SelectPrompt = SelectPrompt;
|
package/dist/prompts/slider.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Prompt } from '../base';
|
|
2
|
-
import { SliderOptions } from '../types';
|
|
2
|
+
import { SliderOptions, MouseEvent } from '../types';
|
|
3
3
|
export declare class SliderPrompt extends Prompt<number, SliderOptions> {
|
|
4
4
|
constructor(options: SliderOptions);
|
|
5
5
|
protected render(firstRender: boolean): void;
|
|
6
6
|
protected handleInput(char: string): void;
|
|
7
|
+
protected handleMouse(event: MouseEvent): void;
|
|
7
8
|
}
|
package/dist/prompts/slider.js
CHANGED
|
@@ -46,5 +46,19 @@ class SliderPrompt extends base_1.Prompt {
|
|
|
46
46
|
this.render(false);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
handleMouse(event) {
|
|
50
|
+
if (event.action === 'scroll') {
|
|
51
|
+
const step = this.options.step || 1;
|
|
52
|
+
if (event.scroll === 'up') { // Scroll Up -> Increase
|
|
53
|
+
this.value = Math.min(this.options.max, this.value + step);
|
|
54
|
+
}
|
|
55
|
+
if (event.scroll === 'down') { // Scroll Down -> Decrease
|
|
56
|
+
this.value = Math.max(this.options.min, this.value - step);
|
|
57
|
+
}
|
|
58
|
+
// Round to avoid float errors
|
|
59
|
+
this.value = Math.round(this.value * 10000) / 10000;
|
|
60
|
+
this.render(false);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
49
63
|
}
|
|
50
64
|
exports.SliderPrompt = SliderPrompt;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { SortOptions, MouseEvent } from '../types';
|
|
3
|
+
export declare class SortPrompt extends Prompt<string[], SortOptions> {
|
|
4
|
+
private items;
|
|
5
|
+
private selectedIndex;
|
|
6
|
+
private grabbedIndex;
|
|
7
|
+
private scrollTop;
|
|
8
|
+
private readonly pageSize;
|
|
9
|
+
constructor(options: SortOptions);
|
|
10
|
+
protected render(firstRender: boolean): void;
|
|
11
|
+
protected handleInput(char: string): void;
|
|
12
|
+
private swap;
|
|
13
|
+
protected handleMouse(event: MouseEvent): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SortPrompt = void 0;
|
|
4
|
+
const ansi_1 = require("../ansi");
|
|
5
|
+
const base_1 = require("../base");
|
|
6
|
+
const theme_1 = require("../theme");
|
|
7
|
+
const symbols_1 = require("../symbols");
|
|
8
|
+
class SortPrompt extends base_1.Prompt {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
super(options);
|
|
11
|
+
this.selectedIndex = 0;
|
|
12
|
+
this.grabbedIndex = null;
|
|
13
|
+
this.scrollTop = 0;
|
|
14
|
+
this.pageSize = 10;
|
|
15
|
+
this.items = [...options.items];
|
|
16
|
+
}
|
|
17
|
+
render(firstRender) {
|
|
18
|
+
// Adjust Scroll Top
|
|
19
|
+
if (this.selectedIndex < this.scrollTop) {
|
|
20
|
+
this.scrollTop = this.selectedIndex;
|
|
21
|
+
}
|
|
22
|
+
else if (this.selectedIndex >= this.scrollTop + this.pageSize) {
|
|
23
|
+
this.scrollTop = this.selectedIndex - this.pageSize + 1;
|
|
24
|
+
}
|
|
25
|
+
// Ensure valid scroll (handle list shrinking?) - list doesn't shrink here but good practice
|
|
26
|
+
this.scrollTop = Math.max(0, Math.min(this.scrollTop, this.items.length - this.pageSize));
|
|
27
|
+
if (this.scrollTop < 0)
|
|
28
|
+
this.scrollTop = 0;
|
|
29
|
+
let output = '';
|
|
30
|
+
// Header
|
|
31
|
+
output += `${theme_1.theme.success}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET} ${theme_1.theme.muted}(Press <space> to grab, arrows to move, <enter> to confirm)${ansi_1.ANSI.RESET}\n`;
|
|
32
|
+
const visibleItems = this.items.slice(this.scrollTop, this.scrollTop + this.pageSize);
|
|
33
|
+
visibleItems.forEach((item, index) => {
|
|
34
|
+
const actualIndex = this.scrollTop + index;
|
|
35
|
+
if (index > 0)
|
|
36
|
+
output += '\n';
|
|
37
|
+
const isSelected = actualIndex === this.selectedIndex;
|
|
38
|
+
const isGrabbed = actualIndex === this.grabbedIndex;
|
|
39
|
+
// Pointer
|
|
40
|
+
let prefix = ' ';
|
|
41
|
+
if (isSelected) {
|
|
42
|
+
if (isGrabbed) {
|
|
43
|
+
prefix = `${theme_1.theme.main}${symbols_1.symbols.pointer}${symbols_1.symbols.pointer} `; // Double pointer for grabbed?
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
prefix = `${theme_1.theme.main}${symbols_1.symbols.pointer} `;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else if (isGrabbed) {
|
|
50
|
+
// Should not happen if we move grabbed item with cursor
|
|
51
|
+
// But if logic differs, maybe.
|
|
52
|
+
}
|
|
53
|
+
// Item Content
|
|
54
|
+
let content = item;
|
|
55
|
+
if (isGrabbed) {
|
|
56
|
+
content = `${ansi_1.ANSI.BOLD}${theme_1.theme.main}${content}${ansi_1.ANSI.RESET}`;
|
|
57
|
+
}
|
|
58
|
+
else if (isSelected) {
|
|
59
|
+
content = `${theme_1.theme.main}${content}${ansi_1.ANSI.RESET}`;
|
|
60
|
+
}
|
|
61
|
+
// Index indicator? Maybe not needed, minimalist.
|
|
62
|
+
output += `${prefix}${content}`;
|
|
63
|
+
});
|
|
64
|
+
this.renderFrame(output);
|
|
65
|
+
}
|
|
66
|
+
handleInput(char) {
|
|
67
|
+
// Enter
|
|
68
|
+
if (char === '\r' || char === '\n') {
|
|
69
|
+
this.submit(this.items);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Space (Grab/Drop)
|
|
73
|
+
if (char === ' ') {
|
|
74
|
+
if (this.grabbedIndex === null) {
|
|
75
|
+
this.grabbedIndex = this.selectedIndex;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
this.grabbedIndex = null;
|
|
79
|
+
}
|
|
80
|
+
this.render(false);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Up
|
|
84
|
+
if (this.isUp(char)) {
|
|
85
|
+
if (this.grabbedIndex !== null) {
|
|
86
|
+
// Move item up
|
|
87
|
+
if (this.selectedIndex > 0) {
|
|
88
|
+
const newIndex = this.selectedIndex - 1;
|
|
89
|
+
this.swap(this.selectedIndex, newIndex);
|
|
90
|
+
this.selectedIndex = newIndex;
|
|
91
|
+
this.grabbedIndex = newIndex;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// Move cursor up
|
|
96
|
+
this.selectedIndex = this.selectedIndex > 0 ? this.selectedIndex - 1 : this.items.length - 1;
|
|
97
|
+
}
|
|
98
|
+
this.render(false);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Down
|
|
102
|
+
if (this.isDown(char)) {
|
|
103
|
+
if (this.grabbedIndex !== null) {
|
|
104
|
+
// Move item down
|
|
105
|
+
if (this.selectedIndex < this.items.length - 1) {
|
|
106
|
+
const newIndex = this.selectedIndex + 1;
|
|
107
|
+
this.swap(this.selectedIndex, newIndex);
|
|
108
|
+
this.selectedIndex = newIndex;
|
|
109
|
+
this.grabbedIndex = newIndex;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Move cursor down
|
|
114
|
+
this.selectedIndex = this.selectedIndex < this.items.length - 1 ? this.selectedIndex + 1 : 0;
|
|
115
|
+
}
|
|
116
|
+
this.render(false);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
swap(i, j) {
|
|
121
|
+
[this.items[i], this.items[j]] = [this.items[j], this.items[i]];
|
|
122
|
+
}
|
|
123
|
+
// Mouse support?
|
|
124
|
+
// Drag and drop is hard with just clicks/scroll.
|
|
125
|
+
// Maybe click to grab, scroll to move?
|
|
126
|
+
handleMouse(event) {
|
|
127
|
+
// Simple scroll support for navigation
|
|
128
|
+
if (event.action === 'scroll') {
|
|
129
|
+
if (event.scroll === 'up') {
|
|
130
|
+
if (this.grabbedIndex !== null) {
|
|
131
|
+
if (this.selectedIndex > 0) {
|
|
132
|
+
const newIndex = this.selectedIndex - 1;
|
|
133
|
+
this.swap(this.selectedIndex, newIndex);
|
|
134
|
+
this.selectedIndex = newIndex;
|
|
135
|
+
this.grabbedIndex = newIndex;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
this.selectedIndex = this.selectedIndex > 0 ? this.selectedIndex - 1 : this.items.length - 1;
|
|
140
|
+
}
|
|
141
|
+
this.render(false);
|
|
142
|
+
}
|
|
143
|
+
else if (event.scroll === 'down') {
|
|
144
|
+
if (this.grabbedIndex !== null) {
|
|
145
|
+
if (this.selectedIndex < this.items.length - 1) {
|
|
146
|
+
const newIndex = this.selectedIndex + 1;
|
|
147
|
+
this.swap(this.selectedIndex, newIndex);
|
|
148
|
+
this.selectedIndex = newIndex;
|
|
149
|
+
this.grabbedIndex = newIndex;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
this.selectedIndex = this.selectedIndex < this.items.length - 1 ? this.selectedIndex + 1 : 0;
|
|
154
|
+
}
|
|
155
|
+
this.render(false);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
exports.SortPrompt = SortPrompt;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Prompt } from '../base';
|
|
2
|
+
import { TableOptions, MouseEvent } from '../types';
|
|
3
|
+
export declare class TablePrompt<V> extends Prompt<V, TableOptions<V>> {
|
|
4
|
+
private selectedIndex;
|
|
5
|
+
private scrollTop;
|
|
6
|
+
private readonly pageSize;
|
|
7
|
+
private colWidths;
|
|
8
|
+
constructor(options: TableOptions<V>);
|
|
9
|
+
private calculateColWidths;
|
|
10
|
+
protected render(firstRender: boolean): void;
|
|
11
|
+
private pad;
|
|
12
|
+
protected handleInput(char: string): void;
|
|
13
|
+
protected handleMouse(event: MouseEvent): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TablePrompt = void 0;
|
|
4
|
+
const ansi_1 = require("../ansi");
|
|
5
|
+
const base_1 = require("../base");
|
|
6
|
+
const theme_1 = require("../theme");
|
|
7
|
+
const symbols_1 = require("../symbols");
|
|
8
|
+
const utils_1 = require("../utils");
|
|
9
|
+
class TablePrompt extends base_1.Prompt {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
super(options);
|
|
12
|
+
this.selectedIndex = 0;
|
|
13
|
+
this.scrollTop = 0;
|
|
14
|
+
this.colWidths = [];
|
|
15
|
+
this.pageSize = options.rows || 7;
|
|
16
|
+
this.calculateColWidths();
|
|
17
|
+
}
|
|
18
|
+
calculateColWidths() {
|
|
19
|
+
const { columns, data } = this.options;
|
|
20
|
+
this.colWidths = columns.map(c => (0, utils_1.stringWidth)(c));
|
|
21
|
+
data.forEach(row => {
|
|
22
|
+
row.row.forEach((cell, idx) => {
|
|
23
|
+
if (idx < this.colWidths.length) {
|
|
24
|
+
this.colWidths[idx] = Math.max(this.colWidths[idx], (0, utils_1.stringWidth)(cell));
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
// Add padding
|
|
29
|
+
this.colWidths = this.colWidths.map(w => w + 2);
|
|
30
|
+
}
|
|
31
|
+
render(firstRender) {
|
|
32
|
+
// Scroll Logic
|
|
33
|
+
if (this.selectedIndex < this.scrollTop) {
|
|
34
|
+
this.scrollTop = this.selectedIndex;
|
|
35
|
+
}
|
|
36
|
+
else if (this.selectedIndex >= this.scrollTop + this.pageSize) {
|
|
37
|
+
this.scrollTop = this.selectedIndex - this.pageSize + 1;
|
|
38
|
+
}
|
|
39
|
+
const maxScroll = Math.max(0, this.options.data.length - this.pageSize);
|
|
40
|
+
this.scrollTop = Math.min(this.scrollTop, maxScroll);
|
|
41
|
+
let output = '';
|
|
42
|
+
// Title
|
|
43
|
+
output += `${theme_1.theme.success}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${theme_1.theme.title}${this.options.message}${ansi_1.ANSI.RESET}\n`;
|
|
44
|
+
// Table Header
|
|
45
|
+
let headerStr = ' '; // Indent for pointer space
|
|
46
|
+
this.options.columns.forEach((col, i) => {
|
|
47
|
+
headerStr += this.pad(col, this.colWidths[i]);
|
|
48
|
+
});
|
|
49
|
+
output += `${ansi_1.ANSI.BOLD}${headerStr}${ansi_1.ANSI.RESET}\n`;
|
|
50
|
+
// Table Body
|
|
51
|
+
const visibleRows = this.options.data.slice(this.scrollTop, this.scrollTop + this.pageSize);
|
|
52
|
+
visibleRows.forEach((item, index) => {
|
|
53
|
+
const actualIndex = this.scrollTop + index;
|
|
54
|
+
if (index > 0)
|
|
55
|
+
output += '\n';
|
|
56
|
+
const isSelected = actualIndex === this.selectedIndex;
|
|
57
|
+
const pointer = isSelected ? `${theme_1.theme.main}${symbols_1.symbols.pointer}${ansi_1.ANSI.RESET} ` : ' ';
|
|
58
|
+
let rowStr = '';
|
|
59
|
+
item.row.forEach((cell, colIdx) => {
|
|
60
|
+
const width = this.colWidths[colIdx];
|
|
61
|
+
let cellStr = this.pad(cell, width);
|
|
62
|
+
if (isSelected) {
|
|
63
|
+
cellStr = `${theme_1.theme.main}${cellStr}${ansi_1.ANSI.RESET}`;
|
|
64
|
+
}
|
|
65
|
+
rowStr += cellStr;
|
|
66
|
+
});
|
|
67
|
+
output += `${pointer}${rowStr}`;
|
|
68
|
+
});
|
|
69
|
+
this.renderFrame(output);
|
|
70
|
+
}
|
|
71
|
+
pad(str, width) {
|
|
72
|
+
const len = (0, utils_1.stringWidth)(str);
|
|
73
|
+
if (len >= width)
|
|
74
|
+
return str;
|
|
75
|
+
return str + ' '.repeat(width - len);
|
|
76
|
+
}
|
|
77
|
+
handleInput(char) {
|
|
78
|
+
if (char === '\r' || char === '\n') {
|
|
79
|
+
this.submit(this.options.data[this.selectedIndex].value);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (this.isUp(char)) {
|
|
83
|
+
this.selectedIndex = this.selectedIndex > 0 ? this.selectedIndex - 1 : this.options.data.length - 1;
|
|
84
|
+
this.render(false);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (this.isDown(char)) {
|
|
88
|
+
this.selectedIndex = this.selectedIndex < this.options.data.length - 1 ? this.selectedIndex + 1 : 0;
|
|
89
|
+
this.render(false);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
handleMouse(event) {
|
|
94
|
+
if (event.action === 'scroll') {
|
|
95
|
+
if (event.scroll === 'up') {
|
|
96
|
+
this.selectedIndex = this.selectedIndex > 0 ? this.selectedIndex - 1 : this.options.data.length - 1;
|
|
97
|
+
this.render(false);
|
|
98
|
+
}
|
|
99
|
+
else if (event.scroll === 'down') {
|
|
100
|
+
this.selectedIndex = this.selectedIndex < this.options.data.length - 1 ? this.selectedIndex + 1 : 0;
|
|
101
|
+
this.render(false);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.TablePrompt = TablePrompt;
|