cawdev-cli 1.0.3-beta → 1.0.5-beta

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/runner/select.mjs CHANGED
@@ -51,8 +51,14 @@ const UP = new Set([`${ESC}[A`, `${ESC}OA`, `${ESC}[Z`, '\x10', 'k']);
51
51
  * an answer, a decision or a run to watch. The widget itself does not care.
52
52
  */
53
53
  export class Select {
54
- constructor({ kind = 'choice', title = '', rows = [], at = 0, empty = null, hint = null }) {
54
+ constructor({ kind = 'choice', title = '', rows = [], at = 0, empty = null, hint = null,
55
+ many = false, picked = [] }) {
55
56
  this.kind = kind;
57
+ // MANY: space (or a digit) ticks a row, `a` ticks all or none, enter
58
+ // settles on what is ticked — or on the row under the cursor, when
59
+ // nothing is, so enter alone still means "this one".
60
+ this.many = many;
61
+ this.picked = new Set(picked);
56
62
  this.title = title;
57
63
  this.rows = rows;
58
64
  this.empty = empty;
@@ -93,8 +99,26 @@ export class Select {
93
99
  return { done: 'cancelled', row: null };
94
100
  }
95
101
  if (pressed === '\r' || pressed === '\n') {
102
+ if (this.many) {
103
+ const rows = this.rows.filter((row) => this.picked.has(row.id));
104
+ return { done: 'chosen', row: this.row, rows: rows.length ? rows : [this.row].filter(Boolean) };
105
+ }
96
106
  return { done: 'chosen', row: this.row };
97
107
  }
108
+ if (this.many && (pressed === ' ' || /^[1-9]$/.test(pressed))) {
109
+ const at = pressed === ' ' ? this.at : Number(pressed) - 1;
110
+ const row = this.rows[at];
111
+ if (!row) return null;
112
+ this.at = at;
113
+ if (this.picked.has(row.id)) this.picked.delete(row.id);
114
+ else this.picked.add(row.id);
115
+ return { done: null, moved: true };
116
+ }
117
+ if (this.many && pressed === 'a') {
118
+ const all = this.rows.every((row) => this.picked.has(row.id));
119
+ this.picked = all ? new Set() : new Set(this.rows.map((row) => row.id));
120
+ return { done: null, moved: true };
121
+ }
98
122
  if (DOWN.has(pressed)) {
99
123
  this.move(1);
100
124
  return { done: null, moved: true };
@@ -141,7 +165,8 @@ export function selectLines(select, width, ink = painter(3), room = 8) {
141
165
  const at = from + offset;
142
166
  lines.push(rowLine(row, {
143
167
  chosen: at === select.at,
144
- marker: row.marker ?? ' ',
168
+ marker: select.many ? (select.picked.has(row.id) ? ink.success('●') : ink.muted('○'))
169
+ : row.marker ?? ' ',
145
170
  // Past nine there is no digit to offer, and a number nobody can press is
146
171
  // worse than a space.
147
172
  number: at < 9 ? String(at + 1) : ' ',
@@ -172,8 +197,10 @@ function hintLine(select, width, ink) {
172
197
  if (room < 10) {
173
198
  return clip(` ${leave}`, width);
174
199
  }
175
- const keys = [ink.muted('↑↓ move'), ink.muted('enter choose')];
176
- if (select.rows.length > 1) {
200
+ const keys = select.many
201
+ ? [ink.muted('↑↓ move'), ink.muted('space tick'), ink.muted('a all'), ink.muted('enter confirm')]
202
+ : [ink.muted('↑↓ move'), ink.muted('enter choose')];
203
+ if (select.rows.length > 1 && !select.many) {
177
204
  keys.push(ink.muted('1-9 pick'));
178
205
  }
179
206
  if (select.hint) {