cli-menu-kit 0.1.26 → 0.2.1
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/dist/api.d.ts +23 -5
- package/dist/api.js +16 -4
- package/dist/component-factories.d.ts +59 -0
- package/dist/component-factories.js +141 -0
- package/dist/components/display/header-v2.d.ts +13 -0
- package/dist/components/display/header-v2.js +43 -0
- package/dist/components/display/header.d.ts +40 -0
- package/dist/components/display/header.js +331 -18
- package/dist/components/display/headers.d.ts +1 -0
- package/dist/components/display/headers.js +15 -5
- package/dist/components/display/hints-v2.d.ts +10 -0
- package/dist/components/display/hints-v2.js +34 -0
- package/dist/components/display/hints.d.ts +56 -0
- package/dist/components/display/hints.js +81 -0
- package/dist/components/display/index.d.ts +4 -1
- package/dist/components/display/index.js +17 -1
- package/dist/components/display/input-prompt.d.ts +35 -0
- package/dist/components/display/input-prompt.js +36 -0
- package/dist/components/display/list.d.ts +49 -0
- package/dist/components/display/list.js +86 -0
- package/dist/components/display/messages.js +5 -5
- package/dist/components/display/progress.d.ts +17 -0
- package/dist/components/display/progress.js +18 -0
- package/dist/components/display/summary.js +72 -10
- package/dist/components/display/table.d.ts +44 -0
- package/dist/components/display/table.js +108 -0
- package/dist/components/inputs/language-input.js +8 -5
- package/dist/components/inputs/number-input.js +19 -14
- package/dist/components/inputs/text-input.js +50 -13
- package/dist/components/menus/boolean-menu.js +34 -20
- package/dist/components/menus/checkbox-menu.d.ts +2 -1
- package/dist/components/menus/checkbox-menu.js +35 -61
- package/dist/components/menus/checkbox-table-menu.d.ts +12 -0
- package/dist/components/menus/checkbox-table-menu.js +398 -0
- package/dist/components/menus/index.d.ts +1 -0
- package/dist/components/menus/index.js +3 -1
- package/dist/components/menus/radio-menu-split.d.ts +34 -0
- package/dist/components/menus/radio-menu-split.js +258 -0
- package/dist/components/menus/radio-menu-v2.d.ts +11 -0
- package/dist/components/menus/radio-menu-v2.js +150 -0
- package/dist/components/menus/radio-menu.d.ts +2 -1
- package/dist/components/menus/radio-menu.js +100 -134
- package/dist/components.js +3 -3
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.js +21 -0
- package/dist/config/language-config.d.ts +73 -0
- package/dist/config/language-config.js +157 -0
- package/dist/config/user-config.d.ts +83 -0
- package/dist/config/user-config.js +185 -0
- package/dist/core/colors.d.ts +24 -18
- package/dist/core/colors.js +74 -7
- package/dist/core/hint-manager.d.ts +29 -0
- package/dist/core/hint-manager.js +65 -0
- package/dist/core/renderer.d.ts +2 -1
- package/dist/core/renderer.js +46 -22
- package/dist/core/screen-manager.d.ts +54 -0
- package/dist/core/screen-manager.js +119 -0
- package/dist/core/state-manager.d.ts +27 -0
- package/dist/core/state-manager.js +56 -0
- package/dist/core/terminal.d.ts +17 -1
- package/dist/core/terminal.js +124 -4
- package/dist/core/virtual-scroll.d.ts +65 -0
- package/dist/core/virtual-scroll.js +120 -0
- package/dist/features/commands.js +23 -22
- package/dist/i18n/languages/en.js +4 -1
- package/dist/i18n/languages/zh.js +4 -1
- package/dist/i18n/registry.d.ts +4 -3
- package/dist/i18n/registry.js +12 -4
- package/dist/i18n/types.d.ts +3 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +49 -4
- package/dist/layout.d.ts +67 -0
- package/dist/layout.js +86 -0
- package/dist/page-layout.d.ts +123 -0
- package/dist/page-layout.js +195 -0
- package/dist/types/input.types.d.ts +8 -0
- package/dist/types/menu.types.d.ts +61 -5
- package/package.json +4 -1
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CheckboxTableMenu - Multi-select menu with table display
|
|
4
|
+
* Combines checkbox selection with formatted table layout
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.showCheckboxTableMenu = showCheckboxTableMenu;
|
|
8
|
+
const terminal_js_1 = require("../../core/terminal.js");
|
|
9
|
+
const keyboard_js_1 = require("../../core/keyboard.js");
|
|
10
|
+
const renderer_js_1 = require("../../core/renderer.js");
|
|
11
|
+
const colors_js_1 = require("../../core/colors.js");
|
|
12
|
+
const registry_js_1 = require("../../i18n/registry.js");
|
|
13
|
+
const virtual_scroll_js_1 = require("../../core/virtual-scroll.js");
|
|
14
|
+
/**
|
|
15
|
+
* Calculate column widths based on content
|
|
16
|
+
*/
|
|
17
|
+
function calculateColumnWidths(columns, data, mode) {
|
|
18
|
+
return columns.map((col) => {
|
|
19
|
+
// Use specified width if provided
|
|
20
|
+
if (col.width)
|
|
21
|
+
return col.width;
|
|
22
|
+
if (mode === 'fixed') {
|
|
23
|
+
// Use default width for fixed mode
|
|
24
|
+
return 20;
|
|
25
|
+
}
|
|
26
|
+
// Auto-calculate based on header and data
|
|
27
|
+
const headerWidth = col.header.length;
|
|
28
|
+
const dataWidth = Math.max(...data.map(row => String(row[col.key] || '').length), 0);
|
|
29
|
+
// Add padding and set reasonable limits
|
|
30
|
+
return Math.min(Math.max(headerWidth, dataWidth) + 2, 50);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build options array with separators inserted
|
|
35
|
+
*/
|
|
36
|
+
function buildOptionsWithSeparators(data, separators) {
|
|
37
|
+
const result = [];
|
|
38
|
+
const sortedSeparators = (separators || []).sort((a, b) => a.beforeIndex - b.beforeIndex);
|
|
39
|
+
let sepIndex = 0;
|
|
40
|
+
for (let i = 0; i < data.length; i++) {
|
|
41
|
+
// Insert separator if needed
|
|
42
|
+
while (sepIndex < sortedSeparators.length && sortedSeparators[sepIndex].beforeIndex === i) {
|
|
43
|
+
result.push({
|
|
44
|
+
type: 'separator',
|
|
45
|
+
label: sortedSeparators[sepIndex].label,
|
|
46
|
+
description: sortedSeparators[sepIndex].description
|
|
47
|
+
});
|
|
48
|
+
sepIndex++;
|
|
49
|
+
}
|
|
50
|
+
// Add data row
|
|
51
|
+
result.push({
|
|
52
|
+
type: 'data',
|
|
53
|
+
data: data[i],
|
|
54
|
+
originalIndex: i
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Render table header
|
|
61
|
+
*/
|
|
62
|
+
function renderTableHeader(columns, columnWidths, checkboxWidth, showHeaderSeparator) {
|
|
63
|
+
let lineCount = 0;
|
|
64
|
+
// Header row
|
|
65
|
+
let headerLine = ''.padEnd(checkboxWidth); // Space for checkbox column
|
|
66
|
+
columns.forEach((col, index) => {
|
|
67
|
+
const width = columnWidths[index];
|
|
68
|
+
const align = col.align || 'left';
|
|
69
|
+
const paddedHeader = (0, renderer_js_1.padText)(col.header, width, align);
|
|
70
|
+
headerLine += `${colors_js_1.uiColors.primary}${colors_js_1.colors.bold}${paddedHeader}${colors_js_1.colors.reset}`;
|
|
71
|
+
});
|
|
72
|
+
process.stdout.write(` ${headerLine}\n`);
|
|
73
|
+
lineCount++;
|
|
74
|
+
// Separator line
|
|
75
|
+
if (showHeaderSeparator) {
|
|
76
|
+
const totalWidth = checkboxWidth + columnWidths.reduce((sum, w) => sum + w, 0);
|
|
77
|
+
process.stdout.write(` ${colors_js_1.uiColors.separator}${'─'.repeat(totalWidth)}${colors_js_1.colors.reset}\n`);
|
|
78
|
+
lineCount++;
|
|
79
|
+
}
|
|
80
|
+
// Blank line after header
|
|
81
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
82
|
+
lineCount++;
|
|
83
|
+
return lineCount;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Render a table row with checkbox
|
|
87
|
+
*/
|
|
88
|
+
function renderTableRow(rowData, columns, columnWidths, isSelected, isHighlighted, checkboxWidth) {
|
|
89
|
+
let line = '';
|
|
90
|
+
// Cursor indicator with background for highlighted row
|
|
91
|
+
if (isHighlighted) {
|
|
92
|
+
line += `${colors_js_1.uiColors.cursor}${colors_js_1.colors.bold}❯ ${colors_js_1.colors.reset}`;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
line += ' ';
|
|
96
|
+
}
|
|
97
|
+
// Checkbox
|
|
98
|
+
line += isSelected
|
|
99
|
+
? `${colors_js_1.uiColors.selected}◉${colors_js_1.colors.reset} `
|
|
100
|
+
: `${colors_js_1.uiColors.disabled}○${colors_js_1.colors.reset} `;
|
|
101
|
+
// Table cells with background for highlighted row
|
|
102
|
+
columns.forEach((col, colIndex) => {
|
|
103
|
+
const value = String(rowData[col.key] || '');
|
|
104
|
+
const width = columnWidths[colIndex];
|
|
105
|
+
const align = col.align || 'left';
|
|
106
|
+
// Truncate if too long
|
|
107
|
+
const truncated = value.length > width ? value.substring(0, width - 3) + '...' : value;
|
|
108
|
+
const paddedValue = (0, renderer_js_1.padText)(truncated, width, align);
|
|
109
|
+
// Apply color and background based on state
|
|
110
|
+
if (isHighlighted) {
|
|
111
|
+
// Highlighted row: cyan text with reverse video (background)
|
|
112
|
+
line += `${colors_js_1.uiColors.primary}${colors_js_1.colors.bold}\x1b[7m${paddedValue}\x1b[27m${colors_js_1.colors.reset}`;
|
|
113
|
+
}
|
|
114
|
+
else if (isSelected) {
|
|
115
|
+
// Selected but not highlighted: normal text
|
|
116
|
+
line += `${colors_js_1.uiColors.textPrimary}${paddedValue}${colors_js_1.colors.reset}`;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Not selected: dim text
|
|
120
|
+
line += `${colors_js_1.uiColors.textSecondary}${paddedValue}${colors_js_1.colors.reset}`;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
process.stdout.write(line + '\n');
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Show a checkbox table menu (multi-select with table display)
|
|
127
|
+
* @param config - Menu configuration
|
|
128
|
+
* @param hints - Optional hints to display at the bottom (for Page Layout use)
|
|
129
|
+
* @returns Promise resolving to selected rows
|
|
130
|
+
*/
|
|
131
|
+
async function showCheckboxTableMenu(config, hints) {
|
|
132
|
+
const { columns, data, idKey, defaultSelected = [], minSelections = 0, maxSelections, allowSelectAll = true, allowInvert = true, showBorders = false, showHeaderSeparator = true, separators, separatorAlign = 'center', widthMode = 'fixed', checkboxWidth = 4, title, prompt, separatorWidth = 30, onExit, preserveOnSelect = false } = config;
|
|
133
|
+
const preserveOnExit = config.preserveOnExit ?? preserveOnSelect;
|
|
134
|
+
// Validate data
|
|
135
|
+
if (!data || data.length === 0) {
|
|
136
|
+
throw new Error('CheckboxTableMenu requires at least one data row');
|
|
137
|
+
}
|
|
138
|
+
if (!columns || columns.length === 0) {
|
|
139
|
+
throw new Error('CheckboxTableMenu requires at least one column');
|
|
140
|
+
}
|
|
141
|
+
// Calculate column widths
|
|
142
|
+
const columnWidths = calculateColumnWidths(columns, data, widthMode);
|
|
143
|
+
// Calculate total table width for separators
|
|
144
|
+
const totalTableWidth = checkboxWidth + columnWidths.reduce((sum, w) => sum + w, 0);
|
|
145
|
+
// Build options with separators
|
|
146
|
+
const optionsWithSeparators = buildOptionsWithSeparators(data, separators);
|
|
147
|
+
// Initialize state
|
|
148
|
+
let cursorIndex = 0;
|
|
149
|
+
const selected = new Set();
|
|
150
|
+
// Map default selected (can be indices or IDs)
|
|
151
|
+
defaultSelected.forEach(item => {
|
|
152
|
+
if (typeof item === 'number') {
|
|
153
|
+
selected.add(item);
|
|
154
|
+
}
|
|
155
|
+
else if (idKey) {
|
|
156
|
+
const index = data.findIndex(row => row[idKey] === item);
|
|
157
|
+
if (index >= 0)
|
|
158
|
+
selected.add(index);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
const state = (0, terminal_js_1.initTerminal)(); // Use normal mode (inline rendering)
|
|
162
|
+
// Get selectable indices (skip separators)
|
|
163
|
+
const selectableIndices = [];
|
|
164
|
+
optionsWithSeparators.forEach((item, index) => {
|
|
165
|
+
if (item.type === 'data') {
|
|
166
|
+
selectableIndices.push(index);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
// Ensure cursorIndex points to a selectable option
|
|
170
|
+
if (!selectableIndices.includes(cursorIndex)) {
|
|
171
|
+
cursorIndex = selectableIndices[0] || 0;
|
|
172
|
+
}
|
|
173
|
+
// Helper function to get next/previous selectable index
|
|
174
|
+
const getNextSelectableIndex = (currentIndex, direction) => {
|
|
175
|
+
let nextIndex = currentIndex;
|
|
176
|
+
const maxAttempts = optionsWithSeparators.length;
|
|
177
|
+
let attempts = 0;
|
|
178
|
+
do {
|
|
179
|
+
if (direction === 'up') {
|
|
180
|
+
nextIndex = nextIndex > 0 ? nextIndex - 1 : optionsWithSeparators.length - 1;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
nextIndex = nextIndex < optionsWithSeparators.length - 1 ? nextIndex + 1 : 0;
|
|
184
|
+
}
|
|
185
|
+
attempts++;
|
|
186
|
+
} while (!selectableIndices.includes(nextIndex) && attempts < maxAttempts);
|
|
187
|
+
return selectableIndices.includes(nextIndex) ? nextIndex : currentIndex;
|
|
188
|
+
};
|
|
189
|
+
// Render function with virtual scrolling
|
|
190
|
+
const render = () => {
|
|
191
|
+
// Clear previous render
|
|
192
|
+
if (state.renderedLines > 0) {
|
|
193
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
194
|
+
}
|
|
195
|
+
let lineCount = 0;
|
|
196
|
+
// Render title if provided
|
|
197
|
+
if (title) {
|
|
198
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
199
|
+
lineCount++;
|
|
200
|
+
}
|
|
201
|
+
// Render prompt if provided
|
|
202
|
+
if (prompt) {
|
|
203
|
+
process.stdout.write(` ${colors_js_1.colors.dim}${prompt}${colors_js_1.colors.reset}\n`);
|
|
204
|
+
lineCount++;
|
|
205
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
206
|
+
lineCount++;
|
|
207
|
+
}
|
|
208
|
+
// Render table header
|
|
209
|
+
lineCount += renderTableHeader(columns, columnWidths, checkboxWidth, showHeaderSeparator);
|
|
210
|
+
// Virtual scrolling: calculate visible range using utility
|
|
211
|
+
const scrollResult = (0, virtual_scroll_js_1.calculateVirtualScroll)({
|
|
212
|
+
items: optionsWithSeparators,
|
|
213
|
+
cursorIndex,
|
|
214
|
+
targetLines: 30,
|
|
215
|
+
getItemLineCount: (item, index) => {
|
|
216
|
+
if (item.type === 'separator') {
|
|
217
|
+
let lines = 1; // title line
|
|
218
|
+
if (index > 0)
|
|
219
|
+
lines++; // blank line before (except very first item)
|
|
220
|
+
if (item.description)
|
|
221
|
+
lines++; // description line
|
|
222
|
+
return lines;
|
|
223
|
+
}
|
|
224
|
+
return 1; // data row
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
const { visibleStart, visibleEnd, isScrolled } = scrollResult;
|
|
228
|
+
// Render visible options
|
|
229
|
+
for (let index = visibleStart; index < visibleEnd; index++) {
|
|
230
|
+
const item = optionsWithSeparators[index];
|
|
231
|
+
if (item.type === 'separator') {
|
|
232
|
+
// Add blank line before separator (except for the first visible one)
|
|
233
|
+
if (index > visibleStart) {
|
|
234
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
235
|
+
lineCount++;
|
|
236
|
+
}
|
|
237
|
+
// Render separator with configured alignment
|
|
238
|
+
(0, renderer_js_1.renderSectionLabel)(item.label || '', totalTableWidth, separatorAlign);
|
|
239
|
+
lineCount++;
|
|
240
|
+
// Render description if provided (with same alignment)
|
|
241
|
+
if (item.description) {
|
|
242
|
+
const descLength = item.description.length;
|
|
243
|
+
let padding = 0;
|
|
244
|
+
switch (separatorAlign) {
|
|
245
|
+
case 'left':
|
|
246
|
+
padding = 2; // Just left margin
|
|
247
|
+
break;
|
|
248
|
+
case 'right':
|
|
249
|
+
padding = Math.max(0, totalTableWidth - descLength);
|
|
250
|
+
break;
|
|
251
|
+
case 'center':
|
|
252
|
+
default:
|
|
253
|
+
padding = Math.max(0, Math.floor((totalTableWidth - descLength) / 2)) + 2;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
const alignedDesc = ' '.repeat(padding) + item.description;
|
|
257
|
+
process.stdout.write(`${colors_js_1.colors.dim}${alignedDesc}${colors_js_1.colors.reset}\n`);
|
|
258
|
+
lineCount++;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
// Render data row
|
|
263
|
+
const originalIndex = item.originalIndex;
|
|
264
|
+
const isSelected = selected.has(originalIndex);
|
|
265
|
+
const isHighlighted = index === cursorIndex;
|
|
266
|
+
renderTableRow(item.data, columns, columnWidths, isSelected, isHighlighted, checkboxWidth);
|
|
267
|
+
lineCount++;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Show scroll indicator if content is scrolled
|
|
271
|
+
if (isScrolled) {
|
|
272
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
273
|
+
lineCount++;
|
|
274
|
+
// Calculate current position among selectable items
|
|
275
|
+
const selectableBeforeCursor = selectableIndices.filter(i => i <= cursorIndex).length;
|
|
276
|
+
const totalSelectable = selectableIndices.length;
|
|
277
|
+
const scrollText = (0, registry_js_1.t)('menus.scrollIndicator', {
|
|
278
|
+
current: String(selectableBeforeCursor),
|
|
279
|
+
total: String(totalSelectable)
|
|
280
|
+
});
|
|
281
|
+
const scrollInfo = ` ${colors_js_1.colors.dim}[${scrollText}]${colors_js_1.colors.reset}`;
|
|
282
|
+
process.stdout.write(scrollInfo + '\n');
|
|
283
|
+
lineCount++;
|
|
284
|
+
}
|
|
285
|
+
// Render hints if provided
|
|
286
|
+
if (hints && hints.length > 0) {
|
|
287
|
+
(0, renderer_js_1.renderBlankLines)(1);
|
|
288
|
+
lineCount++;
|
|
289
|
+
(0, renderer_js_1.renderHints)(hints);
|
|
290
|
+
lineCount += 1;
|
|
291
|
+
}
|
|
292
|
+
state.renderedLines = lineCount;
|
|
293
|
+
};
|
|
294
|
+
// Initial render
|
|
295
|
+
render();
|
|
296
|
+
// Keyboard input handling
|
|
297
|
+
return new Promise((resolve) => {
|
|
298
|
+
const onData = (key) => {
|
|
299
|
+
if ((0, keyboard_js_1.isCtrlC)(key)) {
|
|
300
|
+
state.stdin.removeListener('data', onData);
|
|
301
|
+
if (!preserveOnExit) {
|
|
302
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
303
|
+
}
|
|
304
|
+
(0, terminal_js_1.restoreTerminal)(state);
|
|
305
|
+
if (onExit)
|
|
306
|
+
onExit();
|
|
307
|
+
process.exit(0);
|
|
308
|
+
}
|
|
309
|
+
if ((0, keyboard_js_1.isEnter)(key)) {
|
|
310
|
+
// Validate minimum selections
|
|
311
|
+
if (selected.size < minSelections) {
|
|
312
|
+
// TODO: Show error message
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
// Clean up
|
|
316
|
+
state.stdin.removeListener('data', onData);
|
|
317
|
+
if (!preserveOnSelect) {
|
|
318
|
+
(0, terminal_js_1.clearMenu)(state);
|
|
319
|
+
}
|
|
320
|
+
(0, terminal_js_1.restoreTerminal)(state);
|
|
321
|
+
// Build result
|
|
322
|
+
const selectedIndices = Array.from(selected).sort((a, b) => a - b);
|
|
323
|
+
const selectedRows = selectedIndices.map(i => data[i]);
|
|
324
|
+
const result = {
|
|
325
|
+
indices: selectedIndices,
|
|
326
|
+
rows: selectedRows
|
|
327
|
+
};
|
|
328
|
+
if (idKey) {
|
|
329
|
+
result.ids = selectedRows.map(row => row[idKey]);
|
|
330
|
+
}
|
|
331
|
+
resolve(result);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if ((0, keyboard_js_1.isSpace)(key)) {
|
|
335
|
+
// Toggle selection for current row
|
|
336
|
+
const currentItem = optionsWithSeparators[cursorIndex];
|
|
337
|
+
if (currentItem.type === 'data') {
|
|
338
|
+
const originalIndex = currentItem.originalIndex;
|
|
339
|
+
if (selected.has(originalIndex)) {
|
|
340
|
+
selected.delete(originalIndex);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
// Check max selections
|
|
344
|
+
if (!maxSelections || selected.size < maxSelections) {
|
|
345
|
+
selected.add(originalIndex);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
render();
|
|
349
|
+
}
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
// Arrow keys
|
|
353
|
+
if (key === keyboard_js_1.KEY_CODES.UP) {
|
|
354
|
+
cursorIndex = getNextSelectableIndex(cursorIndex, 'up');
|
|
355
|
+
render();
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (key === keyboard_js_1.KEY_CODES.DOWN) {
|
|
359
|
+
cursorIndex = getNextSelectableIndex(cursorIndex, 'down');
|
|
360
|
+
render();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
// Select all (A key)
|
|
364
|
+
if (allowSelectAll && (key === 'a' || key === 'A')) {
|
|
365
|
+
if (selected.size === data.length) {
|
|
366
|
+
// Deselect all
|
|
367
|
+
selected.clear();
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
// Select all
|
|
371
|
+
data.forEach((_, index) => {
|
|
372
|
+
if (!maxSelections || selected.size < maxSelections) {
|
|
373
|
+
selected.add(index);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
render();
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
// Invert selection (I key)
|
|
381
|
+
if (allowInvert && (key === 'i' || key === 'I')) {
|
|
382
|
+
const newSelected = new Set();
|
|
383
|
+
data.forEach((_, index) => {
|
|
384
|
+
if (!selected.has(index)) {
|
|
385
|
+
if (!maxSelections || newSelected.size < maxSelections) {
|
|
386
|
+
newSelected.add(index);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
selected.clear();
|
|
391
|
+
newSelected.forEach(i => selected.add(i));
|
|
392
|
+
render();
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
state.stdin.on('data', onData);
|
|
397
|
+
});
|
|
398
|
+
}
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* Exports all menu component functions
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.showBooleanMenu = exports.showCheckboxMenu = exports.showRadioMenu = void 0;
|
|
7
|
+
exports.showBooleanMenu = exports.showCheckboxTableMenu = exports.showCheckboxMenu = exports.showRadioMenu = void 0;
|
|
8
8
|
var radio_menu_js_1 = require("./radio-menu.js");
|
|
9
9
|
Object.defineProperty(exports, "showRadioMenu", { enumerable: true, get: function () { return radio_menu_js_1.showRadioMenu; } });
|
|
10
10
|
var checkbox_menu_js_1 = require("./checkbox-menu.js");
|
|
11
11
|
Object.defineProperty(exports, "showCheckboxMenu", { enumerable: true, get: function () { return checkbox_menu_js_1.showCheckboxMenu; } });
|
|
12
|
+
var checkbox_table_menu_js_1 = require("./checkbox-table-menu.js");
|
|
13
|
+
Object.defineProperty(exports, "showCheckboxTableMenu", { enumerable: true, get: function () { return checkbox_table_menu_js_1.showCheckboxTableMenu; } });
|
|
12
14
|
var boolean_menu_js_1 = require("./boolean-menu.js");
|
|
13
15
|
Object.defineProperty(exports, "showBooleanMenu", { enumerable: true, get: function () { return boolean_menu_js_1.showBooleanMenu; } });
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RadioMenu - Split rendering and interaction
|
|
3
|
+
* Supports Page Layout V2 architecture with separate render/interact phases
|
|
4
|
+
* Uses ScreenManager for independent region updates
|
|
5
|
+
*/
|
|
6
|
+
import { RadioMenuConfig, RadioMenuResult } from '../../types/menu.types.js';
|
|
7
|
+
import { TerminalState } from '../../core/terminal.js';
|
|
8
|
+
/**
|
|
9
|
+
* Menu state for split rendering
|
|
10
|
+
*/
|
|
11
|
+
export interface RadioMenuState {
|
|
12
|
+
config: RadioMenuConfig;
|
|
13
|
+
selectedIndex: number;
|
|
14
|
+
selectableIndices: number[];
|
|
15
|
+
optionData: Array<{
|
|
16
|
+
display: string;
|
|
17
|
+
value: string;
|
|
18
|
+
isSeparator: boolean;
|
|
19
|
+
label?: string;
|
|
20
|
+
}>;
|
|
21
|
+
terminalState: TerminalState;
|
|
22
|
+
displayPrompt: string;
|
|
23
|
+
initialLineCount?: number;
|
|
24
|
+
regionId: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Render radio menu UI (non-blocking)
|
|
28
|
+
* Returns state for later interaction
|
|
29
|
+
*/
|
|
30
|
+
export declare function renderRadioMenuUI(config: RadioMenuConfig): RadioMenuState;
|
|
31
|
+
/**
|
|
32
|
+
* Wait for user input and return result (blocking)
|
|
33
|
+
*/
|
|
34
|
+
export declare function waitForRadioMenuInput(menuState: RadioMenuState): Promise<RadioMenuResult>;
|