Skip to content

Commit

Permalink
next: add multi-select listbox doc example (#712)
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte authored Oct 2, 2024
1 parent 3ab33b1 commit e6e4afd
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 1 deletion.
26 changes: 25 additions & 1 deletion sites/docs/content/components/listbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A list of options that can be selected by the user.
---

<script>
import { APISection, ComponentPreviewV2, ListboxDemo, ListboxDemoCustomAnchor, Callout } from '$lib/components'
import { APISection, ComponentPreviewV2, ListboxDemo, ListboxDemoCustomAnchor, ListboxDemoMultiple, Callout } from '$lib/components'
export let schemas;
</script>

Expand Down Expand Up @@ -322,6 +322,30 @@ For more in-depth information on controlled components and advanced state manage

</Callout>

## Multiple Selection

The `type` prop can be set to `'multiple'` to allow multiple items to be selected at a time.

```svelte
<script lang="ts">
import { Listbox } from "bits-ui";
let value = $state<string[]>([]);
</script>
<Listbox.Root type="multiple" bind:value>
<!-- ... -->
</Listbox.Root>
```

<ComponentPreviewV2 name="listbox-demo-multiple" comp="Listbox">

{#snippet preview()}
<ListboxDemoMultiple />
{/snippet}

</ComponentPreviewV2>

## Opt-out of Floating UI

When you use the `Listbox.Content` component, Bits UI uses [Floating UI](https://floating-ui.com/) to position the content relative to the trigger, similar to other popover-like components.
Expand Down
1 change: 1 addition & 0 deletions sites/docs/src/lib/components/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export { default as LinkPreviewDemo } from "./link-preview-demo.svelte";
export { default as ListboxDemo } from "./listbox-demo.svelte";
export { default as ListboxDemoCustom } from "./listbox-demo-custom.svelte";
export { default as ListboxDemoCustomAnchor } from "./listbox-demo-custom-anchor.svelte";
export { default as ListboxDemoMultiple } from "./listbox-demo-multiple.svelte";
export { default as MenubarDemo } from "./menubar-demo.svelte";
export { default as NavigationMenuDemo } from "./navigation-menu-demo.svelte";
export { default as PaginationDemo } from "./pagination-demo.svelte";
Expand Down
83 changes: 83 additions & 0 deletions sites/docs/src/lib/components/demos/listbox-demo-multiple.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<script lang="ts">
import { Listbox } from "bits-ui";
import Check from "phosphor-svelte/lib/Check";
import Palette from "phosphor-svelte/lib/Palette";
import CaretUpDown from "phosphor-svelte/lib/CaretUpDown";
import CaretDoubleUp from "phosphor-svelte/lib/CaretDoubleUp";
import CaretDoubleDown from "phosphor-svelte/lib/CaretDoubleDown";
const themes = [
{ value: "light-monochrome", label: "Light Monochrome" },
{ value: "dark-green", label: "Dark Green" },
{ value: "svelte-orange", label: "Svelte Orange" },
{ value: "punk-pink", label: "Punk Pink" },
{ value: "ocean-blue", label: "Ocean Blue" },
{ value: "sunset-red", label: "Sunset Red" },
{ value: "forest-green", label: "Forest Green" },
{ value: "lavender-purple", label: "Lavender Purple" },
{ value: "mustard-yellow", label: "Mustard Yellow" },
{ value: "slate-gray", label: "Slate Gray" },
{ value: "neon-green", label: "Neon Green" },
{ value: "coral-reef", label: "Coral Reef" },
{ value: "midnight-blue", label: "Midnight Blue" },
{ value: "crimson-red", label: "Crimson Red" },
{ value: "mint-green", label: "Mint Green" },
{ value: "pastel-pink", label: "Pastel Pink" },
{ value: "golden-yellow", label: "Golden Yellow" },
{ value: "deep-purple", label: "Deep Purple" },
{ value: "turquoise-blue", label: "Turquoise Blue" },
{ value: "burnt-orange", label: "Burnt Orange" },
];
let value = $state<string[]>([]);
const selectedLabel = $derived(
value.length
? themes
.filter((theme) => value.includes(theme.value))
.map((theme) => theme.label)
.join(", ")
: "Select your favorite themes"
);
</script>

<Listbox.Root type="multiple" bind:value>
<Listbox.Trigger
class="inline-flex h-input w-[296px] select-none items-center rounded-9px border border-border-input bg-background px-[11px] text-sm transition-colors placeholder:text-foreground-alt/50"
aria-label="Select a theme"
>
<Palette class="mr-[9px] size-6 text-muted-foreground" />
{selectedLabel}
<CaretUpDown class="ml-auto size-6 text-muted-foreground" />
</Listbox.Trigger>
<Listbox.Portal>
<Listbox.Content
class="max-h-96 w-[var(--bits-listbox-anchor-width)] min-w-[var(--bits-listbox-anchor-width)] rounded-xl border border-muted bg-background px-1 py-3 shadow-popover outline-none"
sideOffset={10}
>
<Listbox.ScrollUpButton class="flex w-full items-center justify-center">
<CaretDoubleUp class="size-3" />
</Listbox.ScrollUpButton>
<Listbox.Viewport class="p-1">
{#each themes as theme, i (i + theme.value)}
<Listbox.Item
class="flex h-10 w-full select-none items-center rounded-button py-3 pl-5 pr-1.5 text-sm capitalize outline-none duration-75 data-[highlighted]:bg-muted"
value={theme.value}
label={theme.label}
>
{#snippet children({ selected })}
{theme.label}
{#if selected}
<div class="ml-auto">
<Check />
</div>
{/if}
{/snippet}
</Listbox.Item>
{/each}
</Listbox.Viewport>
<Listbox.ScrollDownButton class="flex w-full items-center justify-center">
<CaretDoubleDown class="size-3" />
</Listbox.ScrollDownButton>
</Listbox.Content>
</Listbox.Portal>
</Listbox.Root>

0 comments on commit e6e4afd

Please sign in to comment.