From c7cb892a4aab6b5a70ff2ca2728f106b423f517c Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:55:18 -0400 Subject: [PATCH 1/6] implement inactive group headers --- packages/core/src/prompts/group-multiselect.ts | 13 +++++++++++-- packages/prompts/src/index.ts | 5 ++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/core/src/prompts/group-multiselect.ts b/packages/core/src/prompts/group-multiselect.ts index b5440539..5695f6a0 100644 --- a/packages/core/src/prompts/group-multiselect.ts +++ b/packages/core/src/prompts/group-multiselect.ts @@ -6,10 +6,12 @@ interface GroupMultiSelectOptions initialValues?: T['value'][]; required?: boolean; cursorAt?: T['value']; + selectableGroups?: boolean; } export default class GroupMultiSelectPrompt extends Prompt { options: (T & { group: string | boolean })[]; cursor: number = 0; + #selectableGroups: boolean; getGroupItems(group: string): T[] { return this.options.filter((o) => o.group === group); @@ -17,7 +19,7 @@ export default class GroupMultiSelectPrompt extends Pr isGroupSelected(group: string) { const items = this.getGroupItems(group); - return items.every((i) => this.value.includes(i.value)); + return this.#selectableGroups && items.every((i) => this.value.includes(i.value)); } private toggleValue() { @@ -53,16 +55,23 @@ export default class GroupMultiSelectPrompt extends Pr this.options.findIndex(({ value }) => value === opts.cursorAt), 0 ); + this.#selectableGroups = opts.selectableGroups ?? true; this.on('cursor', (key) => { switch (key) { case 'left': case 'up': this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1; + if (!this.#selectableGroups && this.options[this.cursor].group === true) { + this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1; + } break; case 'down': case 'right': - this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; + this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; + if (!this.#selectableGroups && this.options[this.cursor].group === true) { + this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; + } break; case 'space': this.toggleValue(); diff --git a/packages/prompts/src/index.ts b/packages/prompts/src/index.ts index b6071bb5..d858550a 100644 --- a/packages/prompts/src/index.ts +++ b/packages/prompts/src/index.ts @@ -428,8 +428,10 @@ export interface GroupMultiSelectOptions { initialValues?: Value[]; required?: boolean; cursorAt?: Value; + selectableGroups?: boolean; } export const groupMultiselect = (opts: GroupMultiSelectOptions) => { + const selectableGroups = opts.selectableGroups ?? true; const opt = ( option: Option, state: @@ -468,7 +470,7 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => } else if (state === 'submitted') { return `${color.dim(label)}`; } - return `${color.dim(prefix)}${color.dim(S_CHECKBOX_INACTIVE)} ${color.dim(label)}`; + return `${color.dim(prefix)}${isItem || selectableGroups ? color.dim(S_CHECKBOX_INACTIVE) : ""} ${color.dim(label)}`; }; return new GroupMultiSelectPrompt({ @@ -476,6 +478,7 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => initialValues: opts.initialValues, required: opts.required ?? true, cursorAt: opts.cursorAt, + selectableGroups: opts.selectableGroups, validate(selected: Value[]) { if (this.required && selected.length === 0) return `Please select at least one option.\n${color.reset( From 127b7f091d5f17604a61f5d06db14844c9eae38b Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:02:19 -0400 Subject: [PATCH 2/6] changeset --- .changeset/early-deers-cough.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/early-deers-cough.md diff --git a/.changeset/early-deers-cough.md b/.changeset/early-deers-cough.md new file mode 100644 index 00000000..9959837f --- /dev/null +++ b/.changeset/early-deers-cough.md @@ -0,0 +1,6 @@ +--- +'@clack/prompts': minor +'@clack/core': minor +--- + +feat: added `selectableGroups` option for inactive group headers for `GroupMultiSelectPrompt` From afb8eb08f12b106c1a7db824563fca62ad9095c1 Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:09:49 -0400 Subject: [PATCH 3/6] tweak --- packages/prompts/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/prompts/src/index.ts b/packages/prompts/src/index.ts index d858550a..de183603 100644 --- a/packages/prompts/src/index.ts +++ b/packages/prompts/src/index.ts @@ -478,7 +478,7 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => initialValues: opts.initialValues, required: opts.required ?? true, cursorAt: opts.cursorAt, - selectableGroups: opts.selectableGroups, + selectableGroups: selectableGroups, validate(selected: Value[]) { if (this.required && selected.length === 0) return `Please select at least one option.\n${color.reset( From 4b6b24296f698392cd7c6043f4f8a1a77ec737ce Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:13:53 -0400 Subject: [PATCH 4/6] consider initial cursor position when disabled --- packages/core/src/prompts/group-multiselect.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/prompts/group-multiselect.ts b/packages/core/src/prompts/group-multiselect.ts index 5695f6a0..2fd77e72 100644 --- a/packages/core/src/prompts/group-multiselect.ts +++ b/packages/core/src/prompts/group-multiselect.ts @@ -46,6 +46,7 @@ export default class GroupMultiSelectPrompt extends Pr constructor(opts: GroupMultiSelectOptions) { super(opts, false); const { options } = opts; + this.#selectableGroups = opts.selectableGroups ?? true; this.options = Object.entries(options).flatMap(([key, option]) => [ { value: key, group: true, label: key }, ...option.map((opt) => ({ ...opt, group: key })), @@ -53,9 +54,8 @@ export default class GroupMultiSelectPrompt extends Pr this.value = [...(opts.initialValues ?? [])]; this.cursor = Math.max( this.options.findIndex(({ value }) => value === opts.cursorAt), - 0 + this.#selectableGroups ? 0 : 1 ); - this.#selectableGroups = opts.selectableGroups ?? true; this.on('cursor', (key) => { switch (key) { From 0fca6dc71112ee12ed60400059dd54e5b93494a1 Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:27:24 -0400 Subject: [PATCH 5/6] format --- packages/core/src/prompts/group-multiselect.ts | 4 ++-- packages/prompts/src/index.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/core/src/prompts/group-multiselect.ts b/packages/core/src/prompts/group-multiselect.ts index 2fd77e72..c1adb618 100644 --- a/packages/core/src/prompts/group-multiselect.ts +++ b/packages/core/src/prompts/group-multiselect.ts @@ -68,9 +68,9 @@ export default class GroupMultiSelectPrompt extends Pr break; case 'down': case 'right': - this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; + this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; if (!this.#selectableGroups && this.options[this.cursor].group === true) { - this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; + this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; } break; case 'space': diff --git a/packages/prompts/src/index.ts b/packages/prompts/src/index.ts index de183603..8f401e25 100644 --- a/packages/prompts/src/index.ts +++ b/packages/prompts/src/index.ts @@ -470,7 +470,9 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => } else if (state === 'submitted') { return `${color.dim(label)}`; } - return `${color.dim(prefix)}${isItem || selectableGroups ? color.dim(S_CHECKBOX_INACTIVE) : ""} ${color.dim(label)}`; + return `${color.dim(prefix)}${ + isItem || selectableGroups ? color.dim(S_CHECKBOX_INACTIVE) : '' + } ${color.dim(label)}`; }; return new GroupMultiSelectPrompt({ From 320880a8fa68d3f45b7d52be8f4d9bab9ecaa59e Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Sat, 29 Jun 2024 00:44:35 -0400 Subject: [PATCH 6/6] added spacing option and removed group bars when `selectableGroups` is `false` --- packages/prompts/src/index.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/prompts/src/index.ts b/packages/prompts/src/index.ts index 8f401e25..b7d33c3c 100644 --- a/packages/prompts/src/index.ts +++ b/packages/prompts/src/index.ts @@ -429,9 +429,10 @@ export interface GroupMultiSelectOptions { required?: boolean; cursorAt?: Value; selectableGroups?: boolean; + spacedGroups?: boolean; } export const groupMultiselect = (opts: GroupMultiSelectOptions) => { - const selectableGroups = opts.selectableGroups ?? true; + const { selectableGroups = false, spacedGroups = false } = opts; const opt = ( option: Option, state: @@ -449,30 +450,33 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => const isItem = typeof (option as any).group === 'string'; const next = isItem && (options[options.indexOf(option) + 1] ?? { group: true }); const isLast = isItem && (next as any).group === true; - const prefix = isItem ? `${isLast ? S_BAR_END : S_BAR} ` : ''; + const prefix = isItem ? (selectableGroups ? `${isLast ? S_BAR_END : S_BAR} ` : ' ') : ''; + const spacingPrefix = spacedGroups && !isItem ? `\n${color.cyan(S_BAR)} ` : ''; if (state === 'active') { - return `${color.dim(prefix)}${color.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ + return `${spacingPrefix}${color.dim(prefix)}${color.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ option.hint ? color.dim(`(${option.hint})`) : '' }`; } else if (state === 'group-active') { - return `${prefix}${color.cyan(S_CHECKBOX_ACTIVE)} ${color.dim(label)}`; + return `${spacingPrefix}${prefix}${color.cyan(S_CHECKBOX_ACTIVE)} ${color.dim(label)}`; } else if (state === 'group-active-selected') { - return `${prefix}${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)}`; + return `${spacingPrefix}${prefix}${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)}`; } else if (state === 'selected') { - return `${color.dim(prefix)}${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)}`; + return `${spacingPrefix}${color.dim(prefix)}${color.green(S_CHECKBOX_SELECTED)} ${color.dim( + label + )}`; } else if (state === 'cancelled') { - return `${color.strikethrough(color.dim(label))}`; + return `${spacingPrefix}${color.strikethrough(color.dim(label))}`; } else if (state === 'active-selected') { - return `${color.dim(prefix)}${color.green(S_CHECKBOX_SELECTED)} ${label} ${ + return `${spacingPrefix}${color.dim(prefix)}${color.green(S_CHECKBOX_SELECTED)} ${label} ${ option.hint ? color.dim(`(${option.hint})`) : '' }`; } else if (state === 'submitted') { return `${color.dim(label)}`; } - return `${color.dim(prefix)}${ - isItem || selectableGroups ? color.dim(S_CHECKBOX_INACTIVE) : '' - } ${color.dim(label)}`; + return `${spacingPrefix}${color.dim(prefix)}${ + isItem || selectableGroups ? `${color.dim(S_CHECKBOX_INACTIVE)} ` : '' + }${color.dim(label)}`; }; return new GroupMultiSelectPrompt({