-
-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): add Combobox component with demo and documentation
- Loading branch information
1 parent
7f4807c
commit cb1125f
Showing
6 changed files
with
287 additions
and
0 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 @@ | ||
--- | ||
'@tszhong0411/ui': patch | ||
--- | ||
|
||
Add Combobox component |
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,48 @@ | ||
--- | ||
title: Combobox | ||
description: Autocomplete input and command palette with a list of suggestions. | ||
--- | ||
|
||
<ComponentPreview name='combobox/combobox' /> | ||
|
||
## Usage | ||
|
||
```tsx | ||
import { | ||
Combobox, | ||
ComboboxContent, | ||
ComboboxControl, | ||
ComboboxInput, | ||
ComboboxItem, | ||
ComboboxItemGroup, | ||
ComboboxItemGroupLabel, | ||
ComboboxItemIndicator, | ||
ComboboxItemText, | ||
ComboboxTrigger | ||
} from '@tszhong0411/ui' | ||
``` | ||
|
||
```tsx | ||
<Combobox items={['React', 'Vue']}> | ||
<ComboboxLabel>Framework</ComboboxLabel> | ||
<ComboboxControl> | ||
<ComboboxInput placeholder='Select a framework' /> | ||
<ComboboxTrigger> | ||
<ChevronsUpDownIcon /> | ||
</ComboboxTrigger> | ||
</ComboboxControl> | ||
<ComboboxContent> | ||
<ComboboxItemGroup> | ||
<ComboboxItemGroupLabel>Frameworks</ComboboxItemGroupLabel> | ||
{['React', 'Vue'].map((item) => ( | ||
<ComboboxItem key={item} item={item}> | ||
<ComboboxItemText>{item}</ComboboxItemText> | ||
<ComboboxItemIndicator> | ||
<CheckIcon /> | ||
</ComboboxItemIndicator> | ||
</ComboboxItem> | ||
))} | ||
</ComboboxItemGroup> | ||
</ComboboxContent> | ||
</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,93 @@ | ||
'use client' | ||
|
||
import { | ||
type ComboboxInputValueChangeDetails, | ||
ComboboxLabel, | ||
createListCollection | ||
} from '@tszhong0411/ui' | ||
import { | ||
Combobox, | ||
ComboboxContent, | ||
ComboboxControl, | ||
ComboboxInput, | ||
ComboboxItem, | ||
ComboboxItemGroup, | ||
ComboboxItemGroupLabel, | ||
ComboboxItemIndicator, | ||
ComboboxItemText, | ||
ComboboxTrigger | ||
} from '@tszhong0411/ui' | ||
import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react' | ||
import { useState } from 'react' | ||
|
||
const frameworks = [ | ||
{ | ||
value: 'next.js', | ||
label: 'Next.js' | ||
}, | ||
{ | ||
value: 'sveltekit', | ||
label: 'SvelteKit', | ||
disabled: true | ||
}, | ||
{ | ||
value: 'nuxt.js', | ||
label: 'Nuxt.js' | ||
}, | ||
{ | ||
value: 'remix', | ||
label: 'Remix' | ||
}, | ||
{ | ||
value: 'astro', | ||
label: 'Astro' | ||
} | ||
] | ||
|
||
const initialCollection = createListCollection({ items: frameworks }) | ||
|
||
const ComboboxDemo = () => { | ||
const [collection, setCollection] = useState(initialCollection) | ||
const handleInputChange = (details: ComboboxInputValueChangeDetails) => { | ||
const filtered = frameworks.filter((item) => | ||
item.label.toLowerCase().includes(details.inputValue.toLowerCase()) | ||
) | ||
if (filtered.length > 0) setCollection(createListCollection({ items: filtered })) | ||
} | ||
|
||
const handleOpenChange = () => { | ||
setCollection(initialCollection) | ||
} | ||
|
||
return ( | ||
<Combobox | ||
className='w-64' | ||
collection={collection} | ||
onInputValueChange={handleInputChange} | ||
onOpenChange={handleOpenChange} | ||
> | ||
<ComboboxLabel>Framework</ComboboxLabel> | ||
<ComboboxControl className='relative'> | ||
<ComboboxInput placeholder='Select a framework' className='pr-6' /> | ||
<ComboboxTrigger className='absolute right-2 top-0 h-full'> | ||
<ChevronsUpDownIcon className='size-4 shrink-0 opacity-50' /> | ||
</ComboboxTrigger> | ||
</ComboboxControl> | ||
<ComboboxContent> | ||
<ComboboxItemGroup> | ||
<ComboboxItemGroupLabel>Frameworks</ComboboxItemGroupLabel> | ||
{collection.items.map((item) => ( | ||
<ComboboxItem key={item.value} item={item}> | ||
<ComboboxItemText>{item.label}</ComboboxItemText> | ||
<ComboboxItemIndicator className='ml-auto'> | ||
<CheckIcon className='size-4' /> | ||
</ComboboxItemIndicator> | ||
</ComboboxItem> | ||
))} | ||
</ComboboxItemGroup> | ||
</ComboboxContent> | ||
</Combobox> | ||
) | ||
} | ||
|
||
export default ComboboxDemo |
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,136 @@ | ||
'use client' | ||
|
||
import { Portal } from '@ark-ui/react' | ||
import { Combobox as ComboboxPrimitive } from '@ark-ui/react/combobox' | ||
import { cn } from '@tszhong0411/utils' | ||
|
||
const ComboboxContext = ComboboxPrimitive.Context | ||
const ComboboxItemContext = ComboboxPrimitive.ItemContext | ||
const ComboboxControl = ComboboxPrimitive.Control | ||
const ComboboxItemText = ComboboxPrimitive.ItemText | ||
const ComboboxItemIndicator = ComboboxPrimitive.ItemIndicator | ||
const ComboboxTrigger = ComboboxPrimitive.Trigger | ||
const ComboboxClearTrigger = ComboboxPrimitive.ClearTrigger | ||
const ComboboxList = ComboboxPrimitive.List | ||
const ComboboxItemGroup = ComboboxPrimitive.ItemGroup | ||
|
||
type ComboboxProps = React.ComponentProps<typeof ComboboxPrimitive.Root> | ||
|
||
const Combobox = (props: ComboboxProps) => { | ||
const { openOnClick = true, ...rest } = props | ||
|
||
return <ComboboxPrimitive.Root openOnClick={openOnClick} {...rest} /> | ||
} | ||
|
||
type ComboboxInputProps = React.ComponentProps<typeof ComboboxPrimitive.Input> | ||
|
||
const ComboboxInput = (props: ComboboxInputProps) => { | ||
const { className, ...rest } = props | ||
|
||
return ( | ||
<ComboboxPrimitive.Input | ||
className={cn( | ||
'border-input bg-background ring-offset-background flex h-10 w-full items-center justify-between rounded-lg border px-3 py-2 text-sm', | ||
'placeholder:text-muted-foreground', | ||
'focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2', | ||
'disabled:cursor-not-allowed disabled:opacity-50', | ||
className | ||
)} | ||
{...rest} | ||
/> | ||
) | ||
} | ||
|
||
type ComboboxLabelProps = React.ComponentProps<typeof ComboboxPrimitive.Label> | ||
|
||
const ComboboxLabel = (props: ComboboxLabelProps) => { | ||
const { className, ...rest } = props | ||
|
||
return ( | ||
<ComboboxPrimitive.Label | ||
className={cn('text-sm font-medium leading-none', className)} | ||
{...rest} | ||
/> | ||
) | ||
} | ||
|
||
type ComboboxContentProps = React.ComponentProps<typeof ComboboxPrimitive.Content> | ||
|
||
const ComboboxContent = (props: ComboboxContentProps) => { | ||
const { className, ...rest } = props | ||
|
||
return ( | ||
<Portal> | ||
<ComboboxPrimitive.Positioner> | ||
<ComboboxPrimitive.Content | ||
className={cn( | ||
'bg-popover text-popover-foreground z-50 min-w-32 overflow-hidden rounded-lg border p-1 shadow-lg', | ||
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95', | ||
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95', | ||
className | ||
)} | ||
{...rest} | ||
/> | ||
</ComboboxPrimitive.Positioner> | ||
</Portal> | ||
) | ||
} | ||
|
||
type ComboboxItemGroupLabelProps = React.ComponentProps<typeof ComboboxPrimitive.ItemGroupLabel> | ||
|
||
const ComboboxItemGroupLabel = (props: ComboboxItemGroupLabelProps) => { | ||
const { className, ...rest } = props | ||
|
||
return ( | ||
<ComboboxPrimitive.ItemGroupLabel | ||
className={cn('px-2 py-1.5 text-sm font-semibold', className)} | ||
{...rest} | ||
/> | ||
) | ||
} | ||
|
||
type ComboboxItemProps = React.ComponentProps<typeof ComboboxPrimitive.Item> | ||
|
||
const ComboboxItem = (props: ComboboxItemProps) => { | ||
const { className, ...rest } = props | ||
|
||
return ( | ||
<ComboboxPrimitive.Item | ||
className={cn( | ||
'relative flex cursor-default select-none items-center rounded-md px-2 py-1.5 text-sm outline-none transition-colors', | ||
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground', | ||
'data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50', | ||
className | ||
)} | ||
{...rest} | ||
/> | ||
) | ||
} | ||
|
||
export { | ||
Combobox, | ||
ComboboxClearTrigger, | ||
ComboboxContent, | ||
ComboboxContext, | ||
ComboboxControl, | ||
ComboboxInput, | ||
ComboboxItem, | ||
ComboboxItemContext, | ||
ComboboxItemGroup, | ||
ComboboxItemGroupLabel, | ||
ComboboxItemIndicator, | ||
ComboboxItemText, | ||
ComboboxLabel, | ||
ComboboxList, | ||
ComboboxTrigger | ||
} | ||
|
||
export type { | ||
CollectionItem, | ||
ComboboxHighlightChangeDetails, | ||
ComboboxInputValueChangeDetails, | ||
ComboboxOpenChangeDetails, | ||
ComboboxValueChangeDetails, | ||
ListCollection | ||
} from '@ark-ui/react/combobox' | ||
export { createListCollection } from '@ark-ui/react/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