Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions apps/www/src/app/examples/combobox/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client';
import { Combobox, Flex } from '@raystack/apsara';

const Page = () => {
return (
<Flex
style={{
height: '100vh',
width: '100%',
backgroundColor: 'var(--rs-color-background-base-primary)',
padding: '32px'
}}
direction='column'
gap={8}
>
<Combobox>
<Combobox.Input />
<Combobox.Content>
<Combobox.Item value='item-1'>Item 1</Combobox.Item>
<Combobox.Item value='item-2'>Item 2</Combobox.Item>
<Combobox.Item value='item-3'>Item 3</Combobox.Item>
</Combobox.Content>
</Combobox>
</Flex>
);
};

export default Page;
149 changes: 149 additions & 0 deletions apps/www/src/content/docs/components/combobox/demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
'use client';

import { getPropsString } from '@/lib/utils';

export const getCode = (props: Record<string, unknown>) => {
const { multiple, ...rest } = props;
return `
<Combobox${getPropsString({ ...(multiple ? { multiple } : {}) })}>
<Combobox.Input placeholder="Enter a fruit"${getPropsString(rest)} />
<Combobox.Content>
<Combobox.Item>Apple</Combobox.Item>
<Combobox.Item>Banana</Combobox.Item>
<Combobox.Item>Blueberry</Combobox.Item>
<Combobox.Item>Grapes</Combobox.Item>
<Combobox.Item>Pineapple</Combobox.Item>
</Combobox.Content>
</Combobox>`;
};

export const playground = {
type: 'playground',
controls: {
label: { type: 'text', initialValue: 'Fruits' },
size: {
type: 'select',
options: ['small', 'large'],
defaultValue: 'large'
},
multiple: {
type: 'checkbox',
defaultValue: false
}
},
getCode
};

export const basicDemo = {
type: 'code',
code: `
<Combobox>
<Combobox.Input placeholder="Enter a fruit" />
<Combobox.Content>
<Combobox.Item>Apple</Combobox.Item>
<Combobox.Item>Banana</Combobox.Item>
<Combobox.Item>Grape</Combobox.Item>
<Combobox.Item>Orange</Combobox.Item>
</Combobox.Content>
</Combobox>`
};

export const iconDemo = {
type: 'code',
code: `
<Combobox>
<Combobox.Input placeholder="Enter a fruit" />
<Combobox.Content>
<Combobox.Item leadingIcon={<Info size={16} />}>Apple</Combobox.Item>
<Combobox.Item leadingIcon={<X size={16} />}>Banana</Combobox.Item>
<Combobox.Item leadingIcon={<Home size={16} />}>Grape</Combobox.Item>
<Combobox.Item leadingIcon={<Laugh size={16} />}>Orange</Combobox.Item>
</Combobox.Content>
</Combobox>`
};

export const sizeDemo = {
type: 'code',
code: `
<Flex align="center" gap="large">
<Combobox>
<Combobox.Input size="small" placeholder="Small combobox" />
<Combobox.Content>
<Combobox.Item>Option 1</Combobox.Item>
<Combobox.Item>Option 2</Combobox.Item>
</Combobox.Content>
</Combobox>
<Combobox>
<Combobox.Input placeholder="Large combobox" />
<Combobox.Content>
<Combobox.Item>Option 1</Combobox.Item>
<Combobox.Item>Option 2</Combobox.Item>
</Combobox.Content>
</Combobox>
</Flex>`
};

export const multipleDemo = {
type: 'code',
code: `
<Combobox multiple>
<Combobox.Input placeholder="Select fruits..." />
<Combobox.Content>
<Combobox.Item>Apple</Combobox.Item>
<Combobox.Item>Banana</Combobox.Item>
<Combobox.Item>Grape</Combobox.Item>
<Combobox.Item>Orange</Combobox.Item>
<Combobox.Item>Pineapple</Combobox.Item>
<Combobox.Item>Mango</Combobox.Item>
</Combobox.Content>
</Combobox>`
};

export const groupDemo = {
type: 'code',
code: `
<Combobox>
<Combobox.Input placeholder="Enter a fruit" />
<Combobox.Content>
<Combobox.Group>
<Combobox.Label>Fruits</Combobox.Label>
<Combobox.Item>Apple</Combobox.Item>
<Combobox.Item>Banana</Combobox.Item>
</Combobox.Group>
<Combobox.Separator />
<Combobox.Group>
<Combobox.Label>Vegetables</Combobox.Label>
<Combobox.Item>Carrot</Combobox.Item>
<Combobox.Item>Broccoli</Combobox.Item>
</Combobox.Group>
</Combobox.Content>
</Combobox>`
};

export const controlledDemo = {
type: 'code',
code: `
function ControlledDemo() {
const [value, setValue] = React.useState("");
const [inputValue, setInputValue] = React.useState("");

return (
<Flex direction="column" gap="medium">
<Text>Selected: {value || "None"}</Text>
<Combobox
value={value}
onValueChange={setValue}
inputValue={inputValue}
onInputValueChange={setInputValue}
>
<Combobox.Input placeholder="Enter a fruit" />
<Combobox.Content>
<Combobox.Item>Apple</Combobox.Item>
<Combobox.Item>Banana</Combobox.Item>
<Combobox.Item>Grape</Combobox.Item>
</Combobox.Content>
</Combobox>
</Flex>
);
}`
};
113 changes: 113 additions & 0 deletions apps/www/src/content/docs/components/combobox/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
title: Combobox
description: An input field with an attached dropdown that allows users to search and select from a list of options.
tag: new
---

import {
playground,
basicDemo,
sizeDemo,
iconDemo,
multipleDemo,
groupDemo,
controlledDemo
} from "./demo.ts";

<Demo data={playground} />

## Usage

```tsx
import { Combobox } from "@raystack/apsara";
```

## Combobox Props

The Combobox component is composed of several parts, each with their own props.

The root element is the parent component that manages the combobox state including open/close, input value, and selection. It is built using [Ariakit ComboboxProvider](https://ariakit.org/reference/combobox-provider) and [Radix Popover](https://www.radix-ui.com/primitives/docs/components/popover).

<auto-type-table path="./props.ts" name="ComboboxRootProps" />

### Combobox.Input Props

The input field that triggers the combobox dropdown and allows users to type and filter options.

<auto-type-table path="./props.ts" name="ComboboxInputProps" />

### Combobox.Content Props

The dropdown container that holds the combobox items.

<auto-type-table path="./props.ts" name="ComboboxContentProps" />

### Combobox.Item Props

Individual selectable options within the combobox.

<auto-type-table path="./props.ts" name="ComboboxItemProps" />

### Combobox.Group Props

A way to group related combobox items together.

<auto-type-table path="./props.ts" name="ComboboxGroupProps" />

### Combobox.Label Props

Renders a label in a combobox group. This component should be used inside Combobox.Group.

<auto-type-table path="./props.ts" name="ComboboxLabelProps" />

### Combobox.Separator Props

Visual divider between combobox items or groups.

<auto-type-table path="./props.ts" name="ComboboxSeparatorProps" />

## Examples

### Basic Combobox

A simple combobox with search functionality.

<Demo data={basicDemo} />

### Size

The combobox input supports different sizes.

<Demo data={sizeDemo} />

### Multiple Selection

To enable multiple selection, pass the `multiple` prop to the Combobox root element.

When multiple selection is enabled, the value, onValueChange, and defaultValue will be an array of strings. Selected items are displayed as chips in the input field.

<Demo data={multipleDemo} />

### Groups and Separators

Use Combobox.Group, Combobox.Label, and Combobox.Separator to organize items into logical groups.

<Demo data={groupDemo} />

### Controlled

You can control the combobox value and input value using the `value`, `onValueChange`, `inputValue`, and `onInputValueChange` props.

<Demo data={controlledDemo} />

## Accessibility

The Combobox component follows WAI-ARIA guidelines:

- Input has role `combobox`
- Content has role `listbox`
- Items have role `option`
- Supports keyboard navigation (Arrow keys, Enter, Escape)
- ARIA labels and descriptions for screen readers
- Focus management between input and listbox

Loading