From 6283794727315935613338b8427fef5dc2579a71 Mon Sep 17 00:00:00 2001 From: Chase Weaver Date: Sat, 7 May 2022 13:07:14 -0400 Subject: [PATCH 1/4] feat: added 'multiple' as an option to Listbox --- src/lib/components/listbox/Listbox.svelte | 33 ++++++++++++++++++- .../components/listbox/ListboxOption.svelte | 31 +++++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/lib/components/listbox/Listbox.svelte b/src/lib/components/listbox/Listbox.svelte index c0f323d9..98a3e757 100644 --- a/src/lib/components/listbox/Listbox.svelte +++ b/src/lib/components/listbox/Listbox.svelte @@ -3,6 +3,10 @@ Open, Closed, } + export enum ValueMode { + Single, + Multi, + } export type ListboxOptionDataRef = { textValue: string; disabled: boolean; @@ -14,6 +18,7 @@ listboxState: ListboxStates; value: unknown; orientation: "vertical" | "horizontal"; + mode: ValueMode; labelRef: Writable; buttonRef: Writable; @@ -61,6 +66,8 @@ horizontal?: boolean; /** The selected value */ value?: StateDefinition["value"]; + /** Wether the `Listbox` should allow mutliple selections */ + multiple?: boolean; }; @@ -89,6 +96,7 @@ export let use: HTMLActionArray = []; export let disabled = false; export let horizontal = false; + export let multiple = false; export let value: StateDefinition["value"]; /***** Events *****/ @@ -112,6 +120,9 @@ let options: StateDefinition["options"] = []; let searchQuery: StateDefinition["searchQuery"] = ""; let activeOptionIndex: StateDefinition["activeOptionIndex"] = null; + let mode: StateDefinition["mode"] = multiple + ? ValueMode.Multi + : ValueMode.Single; let api = writable({ listboxState, @@ -124,6 +135,7 @@ activeOptionIndex, disabled, orientation, + mode, closeListbox() { if (disabled) return; if (listboxState === ListboxStates.Closed) return; @@ -231,7 +243,25 @@ }, select(value: unknown) { if (disabled) return; - dispatch("change", value); + dispatch( + "change", + match(mode, { + [ValueMode.Single]: () => value, + [ValueMode.Multi]: () => { + let copy = ($api.value as unknown[]).slice(); + let raw = value; + + let idx = copy.indexOf(raw); + if (idx === -1) { + copy.push(raw); + } else { + copy.splice(idx, 1); + } + + return copy; + }, + }) + ); }, }); setContext(LISTBOX_CONTEXT_NAME, api); @@ -256,6 +286,7 @@ activeOptionIndex, disabled, orientation, + mode, }; }); diff --git a/src/lib/components/listbox/ListboxOption.svelte b/src/lib/components/listbox/ListboxOption.svelte index 8b80033b..115737ed 100644 --- a/src/lib/components/listbox/ListboxOption.svelte +++ b/src/lib/components/listbox/ListboxOption.svelte @@ -12,7 +12,11 @@ diff --git a/src/routes/docs/listbox.svx b/src/routes/docs/listbox.svx index 8167bcc5..3609d4d9 100644 --- a/src/routes/docs/listbox.svx +++ b/src/routes/docs/listbox.svx @@ -99,6 +99,44 @@ You can use these states to conditionally apply whatever active/focus styles you ``` +## Multiple Values + +To allow selecting multiple values in your listbox, use the `multiple` prop and pass an array to `value` instead of a single option. + +```svelte + + + (selectedPeople = e.detail)} multiple> + Assignee: + {selectedPeople.map((person) => person.name).join(', ')} + + {#each people as person (person.id)} + + {person.name} + + {/each} + + +``` + ## Using a custom label By default, the `Listbox` will use the `` contents as the label for screenreaders. If you'd like more control over what is announced to assistive technologies, use the `ListboxLabel` component: From ab965668546d9696519b71c0ebe7dcf456433118 Mon Sep 17 00:00:00 2001 From: Chase Weaver Date: Sun, 6 Nov 2022 09:50:31 -0500 Subject: [PATCH 3/4] fix: prevent dropdown from closing on value change --- src/lib/components/listbox/ListboxOption.svelte | 2 +- src/lib/components/listbox/ListboxOptions.svelte | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/components/listbox/ListboxOption.svelte b/src/lib/components/listbox/ListboxOption.svelte index 115737ed..561578a7 100644 --- a/src/lib/components/listbox/ListboxOption.svelte +++ b/src/lib/components/listbox/ListboxOption.svelte @@ -118,7 +118,7 @@ let event = e as any as MouseEvent; if (disabled) return event.preventDefault(); $api.select(value); - $api.closeListbox(); + if ($api.mode === ValueMode.Single) $api.closeListbox(); await tick(); $buttonRef?.focus({ preventScroll: true }); } diff --git a/src/lib/components/listbox/ListboxOptions.svelte b/src/lib/components/listbox/ListboxOptions.svelte index ee733e35..c87a1108 100644 --- a/src/lib/components/listbox/ListboxOptions.svelte +++ b/src/lib/components/listbox/ListboxOptions.svelte @@ -12,7 +12,7 @@