Skip to content

Commit 44df9af

Browse files
43081jnatemoo-re
andauthored
feat: add groupSpacing option to group multi select (#256)
Co-authored-by: Nate Moore <[email protected]>
1 parent 5968272 commit 44df9af

File tree

4 files changed

+235
-7
lines changed

4 files changed

+235
-7
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@clack/prompts": minor
3+
---
4+
5+
Adds a new `groupSpacing` option to grouped multi-select prompts. If set to an integer greater than 0, it will add that number of new lines between each group.

packages/prompts/src/__snapshots__/index.test.ts.snap

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,94 @@ exports[`prompts (isCI = false) > groupMultiselect > cursorAt sets initial selec
380380
]
381381
`;
382382

383+
exports[`prompts (isCI = false) > groupMultiselect > groupSpacing > negative spacing is ignored 1`] = `
384+
[
385+
"[?25l",
386+
"│
387+
◆ foo
388+
│ ◻ group1
389+
│ └ ◻ group1value0
390+
│ ◻ group2
391+
│ └ ◻ group2value0
392+
└
393+
",
394+
"",
395+
"",
396+
"",
397+
"│ ◻ group1
398+
│ └ ◻ group1value0
399+
│ ◻ group2
400+
│ └ ◻ group2value0
401+
└
402+
",
403+
"",
404+
"",
405+
"",
406+
"│ ◼ group1
407+
│ └ ◼ group1value0
408+
│ ◻ group2
409+
│ └ ◻ group2value0
410+
└
411+
",
412+
"",
413+
"",
414+
"",
415+
"◇ foo
416+
│ group1value0",
417+
"
418+
",
419+
"[?25h",
420+
]
421+
`;
422+
423+
exports[`prompts (isCI = false) > groupMultiselect > groupSpacing > renders spaced groups 1`] = `
424+
[
425+
"[?25l",
426+
"│
427+
◆ foo
428+
│
429+
│
430+
│ ◻ group1
431+
│ └ ◻ group1value0
432+
│
433+
│
434+
│ ◻ group2
435+
│ └ ◻ group2value0
436+
└
437+
",
438+
"",
439+
"",
440+
"",
441+
"│ ◻ group1
442+
│ └ ◻ group1value0
443+
│
444+
│
445+
│ ◻ group2
446+
│ └ ◻ group2value0
447+
└
448+
",
449+
"",
450+
"",
451+
"",
452+
"│ ◼ group1
453+
│ └ ◼ group1value0
454+
│
455+
│
456+
│ ◻ group2
457+
│ └ ◻ group2value0
458+
└
459+
",
460+
"",
461+
"",
462+
"",
463+
"◇ foo
464+
│ group1value0",
465+
"
466+
",
467+
"[?25h",
468+
]
469+
`;
470+
383471
exports[`prompts (isCI = false) > groupMultiselect > initial values can be set 1`] = `
384472
[
385473
"[?25l",
@@ -2154,6 +2242,94 @@ exports[`prompts (isCI = true) > groupMultiselect > cursorAt sets initial select
21542242
]
21552243
`;
21562244

2245+
exports[`prompts (isCI = true) > groupMultiselect > groupSpacing > negative spacing is ignored 1`] = `
2246+
[
2247+
"[?25l",
2248+
"│
2249+
◆ foo
2250+
│ ◻ group1
2251+
│ └ ◻ group1value0
2252+
│ ◻ group2
2253+
│ └ ◻ group2value0
2254+
└
2255+
",
2256+
"",
2257+
"",
2258+
"",
2259+
"│ ◻ group1
2260+
│ └ ◻ group1value0
2261+
│ ◻ group2
2262+
│ └ ◻ group2value0
2263+
└
2264+
",
2265+
"",
2266+
"",
2267+
"",
2268+
"│ ◼ group1
2269+
│ └ ◼ group1value0
2270+
│ ◻ group2
2271+
│ └ ◻ group2value0
2272+
└
2273+
",
2274+
"",
2275+
"",
2276+
"",
2277+
"◇ foo
2278+
│ group1value0",
2279+
"
2280+
",
2281+
"[?25h",
2282+
]
2283+
`;
2284+
2285+
exports[`prompts (isCI = true) > groupMultiselect > groupSpacing > renders spaced groups 1`] = `
2286+
[
2287+
"[?25l",
2288+
"│
2289+
◆ foo
2290+
│
2291+
│
2292+
│ ◻ group1
2293+
│ └ ◻ group1value0
2294+
│
2295+
│
2296+
│ ◻ group2
2297+
│ └ ◻ group2value0
2298+
└
2299+
",
2300+
"",
2301+
"",
2302+
"",
2303+
"│ ◻ group1
2304+
│ └ ◻ group1value0
2305+
│
2306+
│
2307+
│ ◻ group2
2308+
│ └ ◻ group2value0
2309+
└
2310+
",
2311+
"",
2312+
"",
2313+
"",
2314+
"│ ◼ group1
2315+
│ └ ◼ group1value0
2316+
│
2317+
│
2318+
│ ◻ group2
2319+
│ └ ◻ group2value0
2320+
└
2321+
",
2322+
"",
2323+
"",
2324+
"",
2325+
"◇ foo
2326+
│ group1value0",
2327+
"
2328+
",
2329+
"[?25h",
2330+
]
2331+
`;
2332+
21572333
exports[`prompts (isCI = true) > groupMultiselect > initial values can be set 1`] = `
21582334
[
21592335
"[?25l",

packages/prompts/src/index.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,5 +1204,49 @@ describe.each(['true', 'false'])('prompts (isCI = %s)', (isCI) => {
12041204
expect(value).toEqual([value0]);
12051205
expect(output.buffer).toMatchSnapshot();
12061206
});
1207+
1208+
describe('groupSpacing', () => {
1209+
test('renders spaced groups', async () => {
1210+
const result = prompts.groupMultiselect({
1211+
message: 'foo',
1212+
input,
1213+
output,
1214+
options: {
1215+
group1: [{ value: 'group1value0' }],
1216+
group2: [{ value: 'group2value0' }],
1217+
},
1218+
groupSpacing: 2,
1219+
});
1220+
1221+
input.emit('keypress', '', { name: 'down' });
1222+
input.emit('keypress', '', { name: 'space' });
1223+
input.emit('keypress', '', { name: 'return' });
1224+
1225+
await result;
1226+
1227+
expect(output.buffer).toMatchSnapshot();
1228+
});
1229+
1230+
test('negative spacing is ignored', async () => {
1231+
const result = prompts.groupMultiselect({
1232+
message: 'foo',
1233+
input,
1234+
output,
1235+
options: {
1236+
group1: [{ value: 'group1value0' }],
1237+
group2: [{ value: 'group2value0' }],
1238+
},
1239+
groupSpacing: -2,
1240+
});
1241+
1242+
input.emit('keypress', '', { name: 'down' });
1243+
input.emit('keypress', '', { name: 'space' });
1244+
input.emit('keypress', '', { name: 'return' });
1245+
1246+
await result;
1247+
1248+
expect(output.buffer).toMatchSnapshot();
1249+
});
1250+
});
12071251
});
12081252
});

packages/prompts/src/index.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -489,9 +489,10 @@ export interface GroupMultiSelectOptions<Value> extends CommonOptions {
489489
required?: boolean;
490490
cursorAt?: Value;
491491
selectableGroups?: boolean;
492+
groupSpacing?: number;
492493
}
493494
export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) => {
494-
const { selectableGroups = true } = opts;
495+
const { selectableGroups = true, groupSpacing = 0 } = opts;
495496
const opt = (
496497
option: Option<Value>,
497498
state:
@@ -510,37 +511,39 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
510511
const next = isItem && (options[options.indexOf(option) + 1] ?? { group: true });
511512
const isLast = isItem && (next as any).group === true;
512513
const prefix = isItem ? (selectableGroups ? `${isLast ? S_BAR_END : S_BAR} ` : ' ') : '';
514+
const spacingPrefix =
515+
groupSpacing > 0 && !isItem ? `\n${color.cyan(S_BAR)} `.repeat(groupSpacing) : '';
513516

514517
if (state === 'active') {
515-
return `${color.dim(prefix)}${color.cyan(S_CHECKBOX_ACTIVE)} ${label} ${
518+
return `${spacingPrefix}${color.dim(prefix)}${color.cyan(S_CHECKBOX_ACTIVE)} ${label} ${
516519
option.hint ? color.dim(`(${option.hint})`) : ''
517520
}`;
518521
}
519522
if (state === 'group-active') {
520-
return `${prefix}${color.cyan(S_CHECKBOX_ACTIVE)} ${color.dim(label)}`;
523+
return `${spacingPrefix}${prefix}${color.cyan(S_CHECKBOX_ACTIVE)} ${color.dim(label)}`;
521524
}
522525
if (state === 'group-active-selected') {
523-
return `${prefix}${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)}`;
526+
return `${spacingPrefix}${prefix}${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)}`;
524527
}
525528
if (state === 'selected') {
526529
const selectedCheckbox = isItem || selectableGroups ? color.green(S_CHECKBOX_SELECTED) : '';
527-
return `${color.dim(prefix)}${selectedCheckbox} ${color.dim(label)} ${
530+
return `${spacingPrefix}${color.dim(prefix)}${selectedCheckbox} ${color.dim(label)} ${
528531
option.hint ? color.dim(`(${option.hint})`) : ''
529532
}`;
530533
}
531534
if (state === 'cancelled') {
532535
return `${color.strikethrough(color.dim(label))}`;
533536
}
534537
if (state === 'active-selected') {
535-
return `${color.dim(prefix)}${color.green(S_CHECKBOX_SELECTED)} ${label} ${
538+
return `${spacingPrefix}${color.dim(prefix)}${color.green(S_CHECKBOX_SELECTED)} ${label} ${
536539
option.hint ? color.dim(`(${option.hint})`) : ''
537540
}`;
538541
}
539542
if (state === 'submitted') {
540543
return `${color.dim(label)}`;
541544
}
542545
const unselectedCheckbox = isItem || selectableGroups ? color.dim(S_CHECKBOX_INACTIVE) : '';
543-
return `${color.dim(prefix)}${unselectedCheckbox} ${color.dim(label)}`;
546+
return `${spacingPrefix}${color.dim(prefix)}${unselectedCheckbox} ${color.dim(label)}`;
544547
};
545548

546549
return new GroupMultiSelectPrompt({

0 commit comments

Comments
 (0)