-
-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Hunter Johnston <johnstonhuntera@gmail.com> Co-authored-by: Hunter Johnston <64506580+huntabyte@users.noreply.github.com>
- Loading branch information
1 parent
f58e9fb
commit d1b0fc9
Showing
26 changed files
with
1,514 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"bits-ui": minor | ||
--- | ||
|
||
New Component: [Combobox](https://bits-ui.com/docs/components/combobox) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
--- | ||
title: Combobox | ||
description: Enables users to pick from a list of options displayed in a dropdown. | ||
--- | ||
|
||
<script> | ||
import { APISection, ComponentPreview, ComboboxDemo } from '@/components' | ||
export let schemas; | ||
</script> | ||
|
||
<ComponentPreview name="combobox-demo" comp="combobox"> | ||
|
||
<ComboboxDemo slot="preview" /> | ||
|
||
</ComponentPreview> | ||
|
||
## Structure | ||
|
||
```svelte | ||
<script lang="ts"> | ||
import { Combobox } from "bits-ui"; | ||
</script> | ||
<Combobox.Root> | ||
<Combobox.Input /> | ||
<Combobox.Label /> | ||
<Combobox.Content> | ||
<Combobox.Item> | ||
<Combobox.ItemIndicator /> | ||
</Combobox.Item> | ||
<Combobox.Separator> | ||
</Combobox.Content> | ||
<Combobox.Arrow /> | ||
<Combobox.HiddenInput /> | ||
</Combobox.Root> | ||
``` | ||
|
||
<APISection {schemas} /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<script lang="ts"> | ||
import { Combobox } from "$lib"; | ||
import { flyAndScale } from "@/utils"; | ||
import { Check, OrangeSlice, CaretUpDown } from "$icons/index.js"; | ||
const fruits = [ | ||
{ value: "mango", label: "Mango" }, | ||
{ value: "watermelon", label: "Watermelon" }, | ||
{ value: "apple", label: "Apple" }, | ||
{ value: "pineapple", label: "Pineapple" }, | ||
{ value: "orange", label: "Orange" }, | ||
]; | ||
let inputValue = ""; | ||
$: filteredFruits = inputValue | ||
? fruits.filter((fruit) => fruit.value.includes(inputValue.toLowerCase())) | ||
: fruits; | ||
</script> | ||
|
||
<Combobox.Root items={filteredFruits} bind:inputValue> | ||
<div class="relative"> | ||
<OrangeSlice class="absolute start-3 top-1/2 size-6 -translate-y-1/2 text-muted-foreground" /> | ||
<Combobox.Input | ||
class="inline-flex h-input w-[296px] truncate rounded-9px border border-border-input bg-background px-11 text-sm transition-colors placeholder:text-foreground-alt/50 focus:outline-none focus:ring-2 focus:ring-foreground focus:ring-offset-2 focus:ring-offset-background" | ||
placeholder="Select a fruit" | ||
aria-label="Select a fruit" | ||
/> | ||
<CaretUpDown class="absolute end-3 top-1/2 size-6 -translate-y-1/2 text-muted-foreground" /> | ||
</div> | ||
|
||
<Combobox.Content | ||
class="w-full rounded-xl border border-muted bg-background px-1 py-3 shadow-popover outline-none" | ||
transition={flyAndScale} | ||
sideOffset={8} | ||
> | ||
{#each filteredFruits as fruit (fruit.value)} | ||
<Combobox.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 transition-all duration-75 data-[highlighted]:bg-muted" | ||
value={fruit.value} | ||
label={fruit.label} | ||
> | ||
{fruit.label} | ||
<Combobox.ItemIndicator class="ml-auto" asChild={false}> | ||
<Check /> | ||
</Combobox.ItemIndicator> | ||
</Combobox.Item> | ||
{:else} | ||
<span class="block px-5 py-2 text-sm text-muted-foreground"> No results found </span> | ||
{/each} | ||
</Combobox.Content> | ||
<Combobox.HiddenInput name="favoriteFruit" /> | ||
</Combobox.Root> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
import type { APISchema } from "@/types/index.js"; | ||
import { | ||
arrowProps, | ||
asChild, | ||
attrsSlotProp, | ||
domElProps, | ||
enums, | ||
idsSlotProp, | ||
portalProp, | ||
transitionProps, | ||
builderAndAttrsSlotProps, | ||
onOutsideClickProp, | ||
} from "@/content/api-reference/helpers.js"; | ||
import { floatingPositioning } from "./floating.js"; | ||
import * as C from "@/content/constants.js"; | ||
import type * as Combobox from "$lib/bits/combobox/_types.js"; | ||
|
||
export const root: APISchema<Combobox.Props> = { | ||
title: "Root", | ||
description: "The root combobox component which manages & scopes the state of the select.", | ||
props: { | ||
disabled: { | ||
default: C.FALSE, | ||
type: C.BOOLEAN, | ||
description: "Whether or not the combobox component is disabled.", | ||
}, | ||
multiple: { | ||
default: C.FALSE, | ||
type: C.BOOLEAN, | ||
description: "Whether or not the combobox menu allows multiple selections.", | ||
}, | ||
preventScroll: { | ||
default: C.TRUE, | ||
type: C.BOOLEAN, | ||
description: "Whether or not to prevent scrolling the body when the menu is open.", | ||
}, | ||
closeOnEscape: { | ||
default: C.TRUE, | ||
type: C.BOOLEAN, | ||
description: "Whether to close the combobox menu when the escape key is pressed.", | ||
}, | ||
closeOnOutsideClick: { | ||
type: C.BOOLEAN, | ||
default: C.TRUE, | ||
description: "Whether to close the combobox menu when a click occurs outside of it.", | ||
}, | ||
loop: { | ||
type: C.BOOLEAN, | ||
default: C.FALSE, | ||
description: | ||
"Whether or not to loop through the menu items when navigating with the keyboard.", | ||
}, | ||
open: { | ||
type: C.BOOLEAN, | ||
default: C.FALSE, | ||
description: "The open state of the combobox menu.", | ||
}, | ||
onOpenChange: { | ||
type: { | ||
type: C.FUNCTION, | ||
definition: "(open: boolean) => void", | ||
}, | ||
description: "A callback that is fired when the combobox menu's open state changes.", | ||
}, | ||
selected: { | ||
type: { | ||
type: C.OBJECT, | ||
definition: "{ value: unknown; label?: string }", | ||
}, | ||
description: "The value of the currently selected item.", | ||
}, | ||
onSelectedChange: { | ||
type: { | ||
type: C.FUNCTION, | ||
definition: "(value: unknown | undefined) => void", | ||
}, | ||
description: "A callback that is fired when the combobox menu's value changes.", | ||
}, | ||
portal: { ...portalProp("combobox menu") }, | ||
highlightOnHover: { | ||
type: C.BOOLEAN, | ||
default: C.TRUE, | ||
description: "Whether or not to highlight the currently hovered item.", | ||
}, | ||
name: { | ||
type: C.STRING, | ||
description: "The name to apply to the hidden input element for form submission.", | ||
}, | ||
required: { | ||
default: C.FALSE, | ||
type: C.BOOLEAN, | ||
description: "Whether or not the combobox menu is required.", | ||
}, | ||
scrollAlignment: { | ||
default: "'nearest'", | ||
type: { | ||
type: C.ENUM, | ||
definition: enums("nearest", "center"), | ||
}, | ||
description: "The alignment of the highlighted item when scrolling.", | ||
}, | ||
inputValue: { | ||
default: "", | ||
type: C.STRING, | ||
description: "The value of the combobox input element.", | ||
}, | ||
items: { | ||
type: { | ||
type: "Selected[]", | ||
definition: "Array<{ value: T; label?: string }>", | ||
}, | ||
description: "An array of items to add type-safety to the `onSelectedChange` callback.", | ||
}, | ||
onOutsideClick: onOutsideClickProp, | ||
}, | ||
slotProps: { ids: idsSlotProp }, | ||
}; | ||
|
||
export const content: APISchema<Combobox.ContentProps> = { | ||
title: "Content", | ||
description: "The element which contains the combobox menu's items.", | ||
props: { ...transitionProps, ...floatingPositioning, ...domElProps("HTMLDivElement") }, | ||
slotProps: { ...builderAndAttrsSlotProps }, | ||
dataAttributes: [ | ||
{ | ||
name: "combobox-content", | ||
description: "Present on the content element.", | ||
}, | ||
], | ||
}; | ||
|
||
export const item: APISchema<Combobox.ItemProps> = { | ||
title: "Item", | ||
description: "A combobox item, which must be a child of the `Combobox.Content` component.", | ||
props: { | ||
label: { | ||
type: C.STRING, | ||
description: "The label of the select item, which is displayed in the menu.", | ||
}, | ||
value: { | ||
type: C.UNKNOWN, | ||
description: "The value of the select item.", | ||
}, | ||
disabled: { | ||
type: C.BOOLEAN, | ||
default: C.FALSE, | ||
description: | ||
"Whether or not the combobox item is disabled. This will prevent interaction/selection.", | ||
}, | ||
...domElProps("HTMLDivElement"), | ||
}, | ||
slotProps: { ...builderAndAttrsSlotProps }, | ||
dataAttributes: [ | ||
{ | ||
name: "state", | ||
description: "The state of the item.", | ||
value: enums("selected", "hovered"), | ||
isEnum: true, | ||
}, | ||
{ | ||
name: "disabled", | ||
description: "Present when the item is disabled.", | ||
}, | ||
{ | ||
name: "combobox-item", | ||
description: "Present on the item element.", | ||
}, | ||
], | ||
}; | ||
|
||
export const input: APISchema = { | ||
title: "Input", | ||
description: | ||
"A representation of the combobox input element, which is typically displayed in the content.", | ||
props: { | ||
placeholder: { | ||
type: C.STRING, | ||
description: "A placeholder value to display when no value is selected.", | ||
}, | ||
asChild, | ||
}, | ||
slotProps: { | ||
attrs: attrsSlotProp, | ||
label: { | ||
type: C.STRING, | ||
description: "The label of the currently selected item.", | ||
}, | ||
}, | ||
dataAttributes: [ | ||
{ | ||
name: "select-input", | ||
description: "Present on the input element.", | ||
}, | ||
], | ||
}; | ||
|
||
export const hiddenInput: APISchema<Combobox.InputProps> = { | ||
title: "hidden-input", | ||
description: | ||
"A hidden input element which is used to store the combobox menu's value, used for form submission. It receives the same value as the `Select.Value` component and can receive any props that a normal input element can receive.", | ||
props: domElProps("HTMLInputElement"), | ||
slotProps: { ...builderAndAttrsSlotProps }, | ||
}; | ||
|
||
export const label: APISchema<Combobox.LabelProps> = { | ||
title: "Label", | ||
description: | ||
"A label for the combobox input element, which is typically displayed in the content.", | ||
props: domElProps("HTMLLabelElement"), | ||
slotProps: { ...builderAndAttrsSlotProps }, | ||
dataAttributes: [ | ||
{ | ||
name: "combobox-label", | ||
description: "Present on the label element.", | ||
}, | ||
], | ||
}; | ||
|
||
export const indicator: APISchema<Combobox.IndicatorProps> = { | ||
title: "Indicator", | ||
description: "A visual indicator for use between combobox items or groups.", | ||
props: domElProps("HTMLDivElement"), | ||
slotProps: { | ||
attrs: attrsSlotProp, | ||
isSelected: { | ||
type: C.BOOLEAN, | ||
description: "Whether or not the item is selected.", | ||
}, | ||
}, | ||
dataAttributes: [ | ||
{ | ||
name: "combobox-indicator", | ||
description: "Present on the indicator element.", | ||
}, | ||
], | ||
}; | ||
|
||
export const arrow: APISchema<Combobox.ArrowProps> = { | ||
title: "Arrow", | ||
description: "An optional arrow element which points to the selected item when menu open.", | ||
props: arrowProps, | ||
slotProps: { ...builderAndAttrsSlotProps }, | ||
dataAttributes: [ | ||
{ | ||
name: "arrow", | ||
description: "Present on the arrow element.", | ||
}, | ||
], | ||
}; | ||
|
||
export const combobox = [root, content, item, input, label, hiddenInput, arrow]; |
Oops, something went wrong.