Skip to content

Commit 62628b8

Browse files
committed
feat: add multi-select support to select inputs
1 parent cf64ac0 commit 62628b8

File tree

2 files changed

+85
-19
lines changed

2 files changed

+85
-19
lines changed

src/notebooks/deepnote/deepnoteInputBlockCellStatusBarProvider.ts

Lines changed: 78 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,7 @@ export class DeepnoteInputBlockCellStatusBarItemProvider
682682

683683
const selectType = metadata.deepnote_variable_select_type as string | undefined;
684684
const allowMultiple = metadata.deepnote_allow_multiple_values as boolean | undefined;
685+
const allowEmpty = metadata.deepnote_allow_empty_values as boolean | undefined;
685686
const currentValue = metadata.deepnote_variable_value;
686687

687688
// Get options based on select type
@@ -705,37 +706,96 @@ export class DeepnoteInputBlockCellStatusBarItemProvider
705706
}
706707

707708
if (allowMultiple) {
708-
// Multi-select using QuickPick
709+
// Multi-select using QuickPick with custom behavior for clear selection
709710
const currentSelection = Array.isArray(currentValue) ? currentValue : [];
710-
const selected = await window.showQuickPick(
711-
options.map((opt) => ({
712-
label: opt,
713-
picked: currentSelection.includes(opt)
714-
})),
715-
{
716-
canPickMany: true,
717-
placeHolder: l10n.t('Select one or more options')
718-
}
719-
);
711+
const clearSelectionLabel = l10n.t('$(circle-slash) Clear selection');
712+
713+
// Create quick pick items
714+
const optionItems = options.map((opt) => ({
715+
label: opt,
716+
picked: currentSelection.includes(opt)
717+
}));
718+
719+
// Add empty option if allowed
720+
if (allowEmpty) {
721+
optionItems.unshift({
722+
label: clearSelectionLabel,
723+
picked: false
724+
});
725+
}
720726

721-
if (selected === undefined) {
722-
return;
727+
// Use createQuickPick for more control
728+
const quickPick = window.createQuickPick();
729+
quickPick.items = optionItems;
730+
quickPick.selectedItems = optionItems.filter((item) => item.picked);
731+
quickPick.canSelectMany = true;
732+
quickPick.placeholder = allowEmpty
733+
? l10n.t('Select one or more options (or clear selection)')
734+
: l10n.t('Select one or more options');
735+
736+
// Listen for selection changes to handle "Clear selection" specially
737+
if (allowEmpty) {
738+
quickPick.onDidChangeSelection((selectedItems) => {
739+
const hasClearSelection = selectedItems.some((item) => item.label === clearSelectionLabel);
740+
if (hasClearSelection) {
741+
// If "Clear selection" is selected, immediately clear and close
742+
quickPick.selectedItems = [];
743+
quickPick.hide();
744+
void this.updateCellMetadata(cell, { deepnote_variable_value: [] });
745+
}
746+
});
723747
}
724748

725-
const newValue = selected.map((item) => item.label);
726-
await this.updateCellMetadata(cell, { deepnote_variable_value: newValue });
749+
quickPick.onDidAccept(() => {
750+
const selected = quickPick.selectedItems;
751+
const hasClearSelection = selected.some((item) => item.label === clearSelectionLabel);
752+
753+
if (!hasClearSelection) {
754+
const newValue = selected.map((item) => item.label);
755+
void this.updateCellMetadata(cell, { deepnote_variable_value: newValue });
756+
}
757+
quickPick.hide();
758+
});
759+
760+
quickPick.onDidHide(() => {
761+
quickPick.dispose();
762+
});
763+
764+
quickPick.show();
727765
} else {
728766
// Single select
729-
const selected = await window.showQuickPick(options, {
730-
placeHolder: l10n.t('Select an option'),
767+
const quickPickItems = options.map((opt) => ({
768+
label: opt,
769+
description: typeof currentValue === 'string' && currentValue === opt ? l10n.t('(current)') : undefined
770+
}));
771+
772+
// Add empty option if allowed
773+
if (allowEmpty) {
774+
quickPickItems.unshift({
775+
label: l10n.t('$(circle-slash) None'),
776+
description:
777+
currentValue === null || currentValue === undefined || currentValue === ''
778+
? l10n.t('(current)')
779+
: undefined
780+
});
781+
}
782+
783+
const selected = await window.showQuickPick(quickPickItems, {
784+
placeHolder: allowEmpty ? l10n.t('Select an option or none') : l10n.t('Select an option'),
731785
canPickMany: false
732786
});
733787

734788
if (selected === undefined) {
735789
return;
736790
}
737791

738-
await this.updateCellMetadata(cell, { deepnote_variable_value: selected });
792+
// Check if "None" was chosen
793+
const noneLabel = l10n.t('$(circle-slash) None');
794+
if (selected.label === noneLabel) {
795+
await this.updateCellMetadata(cell, { deepnote_variable_value: null });
796+
} else {
797+
await this.updateCellMetadata(cell, { deepnote_variable_value: selected.label });
798+
}
739799
}
740800
}
741801

src/notebooks/deepnote/inputBlockContentFormatter.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@ export function formatInputBlockCellContent(blockType: string, metadata: Record<
2121
const value = metadata.deepnote_variable_value;
2222
if (Array.isArray(value)) {
2323
// Multi-select: show as array of quoted strings
24+
if (value.length === 0) {
25+
// Empty array for multi-select
26+
return '[]';
27+
}
2428
return `[${value.map((v) => `"${v}"`).join(', ')}]`;
2529
} else if (typeof value === 'string') {
2630
// Single select: show as quoted string
2731
return `"${value}"`;
32+
} else if (value === null || value === undefined) {
33+
// Empty/null value
34+
return 'None';
2835
}
2936
return '';
3037
}
@@ -100,4 +107,3 @@ function formatDateValue(val: unknown): string {
100107
}
101108
return String(val);
102109
}
103-

0 commit comments

Comments
 (0)