PettyUI/.firecrawl/kobalte-search.md
2026-03-30 12:08:51 +07:00

562 lines
22 KiB
Markdown

# Search
Search a searchbox text input with a menu.
Handle the case where dataset filtering needs to occur outside the combobox component.
## Import
```
Copyts
import { Search } from "@kobalte/core/search";
// or
import { Root, Label, ... } from "@kobalte/core/search";
```
```
Copyts
import { Search } from "@kobalte/core/search";
// or
import { Root, Label, ... } from "@kobalte/core/search";
```
## Features
- Inherits all the features of [combobox](https://kobalte.dev/docs/core/components/combobox), except result filtering which should be managed externally.
- Debouncing text input to rate limit search suggestions calls.
- Optional indicator to show when suggestions are loading.
## Anatomy
The search consists of:
- **Search:** The root container for a search component.
- **Search.Label:** The label that gives the user information on the search component.
- **Search.Description:** The description that gives the user more information on the component.
- **Search.Control:** Contains the search input and indicator.
- **Search.Indicator:** Wrapper for icon to indicate loading status.
- **Search.Icon:** A small icon often displayed next to the input as a visual affordance for the fact it can be open.
- **Search.Input:** The input used to search and reflects the selected suggestion values.
- **Search.Portal:** Portals its children into the `body` when the search is open.
- **Search.Content:** Contains the content to be rendered when the search is open.
- **Search.Arrow:** An optional arrow element to render alongside the search content.
- **Search.Listbox:** Contains a list of items and allows a user to search one or more of them.
- **Search.Section:** Used to render the label of an option group. It won't be focusable using arrow keys.
- **Search.Item:** An item of the search suggestion.
- **Search.ItemLabel:** An accessible label to be announced for the item.
- **Search.ItemDescription:** An optional accessible description to be announced for the item.
- **Search.NoResult:** Displayed when no suggestion options are given.
```
Copytsx
<Search>
<Search.Label />
<Search.Control>
<Search.Indicator>
<Search.Icon />
</Search.Indicator>
<Search.Input />
</Search.Control>
<Search.Description />
<Search.Portal>
<Search.Content>
<Search.Arrow />
<Search.Listbox />
<Search.NoResult>
</Search.Content>
</Search.Portal>
</Search>
```
```
Copytsx
<Search>
<Search.Label />
<Search.Control>
<Search.Indicator>
<Search.Icon />
</Search.Indicator>
<Search.Input />
</Search.Control>
<Search.Description />
<Search.Portal>
<Search.Content>
<Search.Arrow />
<Search.Listbox />
<Search.NoResult>
</Search.Content>
</Search.Portal>
</Search>
```
## Example
Magnifying Glass
Emoji selected:
index.tsxstyle.css
```
Copytsx
import { Search } from "@kobalte/core/search";
import { MagnifyingGlassIcon, ReloadIcon } from "some-icon-library";
import { createSignal } from "solid-js";
import "./style.css";
import { queryEmojiData } from "your-search-function";
function App() {
const [options, setOptions] = createSignal([]);
const [emoji, setEmoji] = createSignal();
return (
<>
<Search
triggerMode="focus"
options={options()}
onInputChange={query => setOptions(queryEmojiData(query))}
onChange={result => setEmoji(result)}
optionValue="name"
optionLabel="name"
placeholder="Search an emoji…"
itemComponent={props => (
<Search.Item item={props.item} class="search__item">
<Search.ItemLabel>{props.item.rawValue.emoji}</Search.ItemLabel>
</Search.Item>
)}
>
<Search.Control class="search__control" aria-label="Emoji">
<Search.Indicator
class="search__indicator"
loadingComponent={
<Search.Icon class="load__icon">
<ReloadIcon class="spin__icon" />
</Search.Icon>
}
>
<Search.Icon class="search__icon">
<MagnifyingGlassIcon class="center__icon" />
</Search.Icon>
</Search.Indicator>
<Search.Input class="search__input" />
</Search.Control>
<Search.Portal>
<Search.Content class="search__content" onCloseAutoFocus={(e) => e.preventDefault()}>
<Search.Listbox class="search__listbox" />
<Search.NoResult class="search__no_result">
😬 No emoji found
</Search.NoResult>
</Search.Content>
</Search.Portal>
</Search>
<div class="result__content">
Emoji selected: {emoji()?.emoji} {emoji()?.name}
</div>
</>
)
}
```
```
Copytsx
import { Search } from "@kobalte/core/search";
import { MagnifyingGlassIcon, ReloadIcon } from "some-icon-library";
import { createSignal } from "solid-js";
import "./style.css";
import { queryEmojiData } from "your-search-function";
function App() {
const [options, setOptions] = createSignal([]);
const [emoji, setEmoji] = createSignal();
return (
<>
<Search
triggerMode="focus"
options={options()}
onInputChange={query => setOptions(queryEmojiData(query))}
onChange={result => setEmoji(result)}
optionValue="name"
optionLabel="name"
placeholder="Search an emoji…"
itemComponent={props => (
<Search.Item item={props.item} class="search__item">
<Search.ItemLabel>{props.item.rawValue.emoji}</Search.ItemLabel>
</Search.Item>
)}
>
<Search.Control class="search__control" aria-label="Emoji">
<Search.Indicator
class="search__indicator"
loadingComponent={
<Search.Icon class="load__icon">
<ReloadIcon class="spin__icon" />
</Search.Icon>
}
>
<Search.Icon class="search__icon">
<MagnifyingGlassIcon class="center__icon" />
</Search.Icon>
</Search.Indicator>
<Search.Input class="search__input" />
</Search.Control>
<Search.Portal>
<Search.Content class="search__content" onCloseAutoFocus={(e) => e.preventDefault()}>
<Search.Listbox class="search__listbox" />
<Search.NoResult class="search__no_result">
😬 No emoji found
</Search.NoResult>
</Search.Content>
</Search.Portal>
</Search>
<div class="result__content">
Emoji selected: {emoji()?.emoji} {emoji()?.name}
</div>
</>
)
}
```
## Usage
### Debounce
Set `debounceOptionsMillisecond`, to prevent new search queries immediately on input change. Instead, search queries are requested once input is idle for a set time.
Show a debouncing icon by adding a `loadingComponent` to `Search.Indicator`.
Magnifying Glass
Emoji selected:
```
Copytsx
<Search
triggerMode="focus"
options={options()}
onInputChange={query => setOptions(queryEmojiData(query))}
onChange={result => setEmoji(result)}
debounceOptionsMillisecond={300}
optionValue="name"
optionLabel="name"
placeholder="Search an emoji…"
itemComponent={(props: any) => (
<Search.Item item={props.item}>
<Search.ItemLabel>{props.item.rawValue.emoji}</Search.ItemLabel>
</Search.Item>
)}
>
<Search.Control aria-label="Emoji">
<Search.Indicator
loadingComponent={
<Search.Icon>
<ReloadIcon />
</Search.Icon>
}
>
<Search.Icon>
<MagnifyingGlassIcon />
</Search.Icon>
</Search.Indicator>
<Search.Input />
</Search.Control>
<Search.Portal>
<Search.Content onCloseAutoFocus={(e) => e.preventDefault()}>
<Search.Listbox />
<Search.NoResult>
😬 No emoji found
</Search.NoResult>
</Search.Content>
</Search.Portal>
</Search>
```
```
Copytsx
<Search
triggerMode="focus"
options={options()}
onInputChange={query => setOptions(queryEmojiData(query))}
onChange={result => setEmoji(result)}
debounceOptionsMillisecond={300}
optionValue="name"
optionLabel="name"
placeholder="Search an emoji…"
itemComponent={(props: any) => (
<Search.Item item={props.item}>
<Search.ItemLabel>{props.item.rawValue.emoji}</Search.ItemLabel>
</Search.Item>
)}
>
<Search.Control aria-label="Emoji">
<Search.Indicator
loadingComponent={
<Search.Icon>
<ReloadIcon />
</Search.Icon>
}
>
<Search.Icon>
<MagnifyingGlassIcon />
</Search.Icon>
</Search.Indicator>
<Search.Input />
</Search.Control>
<Search.Portal>
<Search.Content onCloseAutoFocus={(e) => e.preventDefault()}>
<Search.Listbox />
<Search.NoResult>
😬 No emoji found
</Search.NoResult>
</Search.Content>
</Search.Portal>
</Search>
```
### Inline style
To achieve the command menu look, add the `open` prop to permanently open dropdown. Replace `Search.Portal` and `Search.Content` with a `div` to directly mount your content below the search input.
Magnifying Glass
😬 No emoji found
Emoji selected:
```
Copytsx
<Search
open
options={options()}
onInputChange={query => setOptions(queryEmojiData(query))}
onChange={result => setEmoji(result)}
debounceOptionsMillisecond={300}
optionValue="name"
optionLabel="name"
placeholder="Search an emoji…"
itemComponent={(props: any) => (
<Search.Item item={props.item}>
<Search.ItemLabel>{props.item.rawValue.emoji}</Search.ItemLabel>
</Search.Item>
)}
>
<Search.Control aria-label="Emoji">
<Search.Indicator>
<Search.Icon>
<MagnifyingGlassIcon />
</Search.Icon>
</Search.Indicator>
<Search.Input />
</Search.Control>
<div>
<Search.Listbox />
<Search.NoResult>
😬 No emoji found
</Search.NoResult>
</div>
</Search>
```
```
Copytsx
<Search
open
options={options()}
onInputChange={query => setOptions(queryEmojiData(query))}
onChange={result => setEmoji(result)}
debounceOptionsMillisecond={300}
optionValue="name"
optionLabel="name"
placeholder="Search an emoji…"
itemComponent={(props: any) => (
<Search.Item item={props.item}>
<Search.ItemLabel>{props.item.rawValue.emoji}</Search.ItemLabel>
</Search.Item>
)}
>
<Search.Control aria-label="Emoji">
<Search.Indicator>
<Search.Icon>
<MagnifyingGlassIcon />
</Search.Icon>
</Search.Indicator>
<Search.Input />
</Search.Control>
<div>
<Search.Listbox />
<Search.NoResult>
😬 No emoji found
</Search.NoResult>
</div>
</Search>
```
## API Reference
### Search
`Search` is equivalent to the `Root` import from `@kobalte/core/search`.
| Prop | Description |
| --- | --- |
| options | `Array<T | U>`<br> An array of options to display as the available options. |
| optionValue | `keyof T | ((option: T) => string | number)`<br> Property name or getter function to use as the value of an option. This is the value that will be submitted when the search component is part of a `<form>`. |
| optionTextValue | `keyof T | ((option: T) => string)`<br> Property name or getter function to use as the text value of an option for typeahead purpose. |
| optionLabel | `keyof T | ((option: T) => string)`<br> Property name or getter function to use as the label of an option. This is the string representation of the option to display in the `Search.Input`. |
| optionDisabled | `keyof T | ((option: T) => boolean)`<br> Property name or getter function to use as the disabled flag of an option. |
| optionGroupChildren | `keyof U`<br> Property name that refers to the children options of an option group. |
| itemComponent | `Component<SearchItemComponentProps<T>>`<br> When NOT virtualized, the component to render as an item in the `Search.Listbox`. |
| sectionComponent | `Component<SearchSectionComponentProps<U>>`<br> When NOT virtualized, the component to render as a section in the `Search.Listbox`. |
| multiple | `boolean`<br> Whether the search component allows multi-selection. |
| placeholder | `JSX.Element`<br> The content that will be rendered when no value or defaultValue is set. |
| value | `T | Array<T>`<br> The controlled value of the search input. |
| defaultValue | `T | Array<T>`<br> The value of the search input when initially rendered. Useful when you do not need to control the value. |
| onChange | `(value: T | Array<T>) => void`<br> Event handler called when the value changes. |
| open | `boolean`<br> The controlled open state of the search suggestion. |
| defaultOpen | `boolean`<br> The default open state when initially rendered. Useful when you do not need to control the open state. |
| onOpenChange | `(open: boolean, triggerMode?: SearchTriggerMode) => void`<br> Event handler called when the open state of the search component changes. Returns the new open state and the action that caused the opening of the menu. |
| onInputChange | `(value: string) => void`<br> Handler that is called when the search input value changes. |
| triggerMode | `SearchTriggerMode`<br> The interaction required to display search suggestion, it can be one of the following: <br> \- **input**: open search suggestion when the user is typing. <br> \- **focus**: open search suggestion when the input is focused. <br> \- **manual**: open search suggestion when pressing arrow down/up while focus is on the input or clicking on the trigger. |
| removeOnBackspace | `boolean`<br> When `multiple` is true, whether the last selected option should be removed when the user press the Backspace key and the input is empty. |
| allowDuplicateSelectionEvents | `boolean`<br> Whether `onChange` should fire even if the new value is the same as the last. |
| disallowEmptySelection | `boolean`<br> Whether the search component allows empty selection or not. |
| allowsEmptyCollection | `boolean`<br> Whether the search component allows the menu to be open when the collection is empty. |
| closeOnSelection | `boolean`<br> Whether the search component closes after selection. |
| selectionBehavior | `'toggle' | 'replace'`<br> How selection should behave in the search component. |
| virtualized | `boolean`<br> Whether the search suggestion uses virtual scrolling. |
| modal | `boolean`<br> Whether the search component should be the only visible content for screen readers, when set to `true`: <br> \- interaction with outside elements will be disabled. <br> \- scroll will be locked. <br> \- focus will be locked inside the search component content. <br> \- elements outside the search component content will not be visible for screen readers. |
| preventScroll | `boolean`<br> Whether the scroll should be locked even if the search suggestion is not modal. |
| forceMount | `boolean`<br> Used to force mounting the search suggestion (portal, positioner and content) when more control is needed. Useful when controlling animation with SolidJS animation libraries. |
| name | `string`<br> The name of the search component. Submitted with its owning form as part of a name/value pair. |
| validationState | `'valid' | 'invalid'`<br> Whether the search component should display its "valid" or "invalid" visual styling. |
| required | `boolean`<br> Whether the user must select an item before the owning form can be submitted. |
| disabled | `boolean`<br> Whether the search component is disabled. |
| readOnly | `boolean`<br> Whether the search component items can be selected but not changed by the user. |
| autoComplete | `string`<br> Describes the type of autocomplete functionality the input should provide if any. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefautocomplete) |
| translations | [`SearchIntlTranslations`](https://github.com/kobaltedev/kobalte/blob/main/packages/core/src/combobox/combobox.intl.ts)<br> Localization strings. |
`Search` also accepts the following props to customize the placement of the `Search.Content`.
| Prop | Description |
| --- | --- |
| placement | `Placement`<br> The placement of the search component content. |
| gutter | `number`<br> The distance between the search component content and the trigger element. |
| shift | `number`<br> The skidding of the search component content along the trigger element. |
| flip | `boolean | string`<br> Controls the behavior of the search component content when it overflows the viewport: <br> \- If a `boolean`, specifies whether the search component content should flip to the opposite side when it overflows.<br> \- If a `string`, indicates the preferred fallback placements when it overflows.<br>The placements must be spaced-delimited, e.g. "top left". |
| slide | `boolean`<br> Whether the search component content should slide when it overflows. |
| overlap | `boolean`<br> Whether the search component content can overlap the trigger element when it overflows. |
| sameWidth | `boolean`<br> Whether the search component content should have the same width as the trigger element. This will be exposed to CSS as `--kb-popper-anchor-width`. |
| fitViewport | `boolean`<br> Whether the search component content should fit the viewport. If this is set to true, the search component content will have `maxWidth` and `maxHeight` set to the viewport size. This will be exposed to CSS as `--kb-popper-available-width` and `--kb-popper-available-height`. |
| hideWhenDetached | `boolean`<br> Whether to hide the search component content when the trigger element becomes occluded. |
| detachedPadding | `number`<br> The minimum padding in order to consider the trigger element occluded. |
| arrowPadding | `number`<br> The minimum padding between the arrow and the search component content corner. |
| overflowPadding | `number`<br> The minimum padding between the search component content and the viewport edge. This will be exposed to CSS as `--kb-popper-overflow-padding`. |
| Data attribute | Description |
| --- | --- |
| data-valid | Present when the search component is valid according to the validation rules. |
| data-invalid | Present when the search component is invalid according to the validation rules. |
| data-required | Present when the user must select an item before the owning form can be submitted. |
| data-disabled | Present when the search component is disabled. |
| data-readonly | Present when the search component is read only. |
`Search.Label`, `Search.Control`, `Search.Input`, `Search.Trigger`, `Search.Description` and `Search.ErrorMesssage` shares the same data-attributes.
### Search.Control
| Render Prop | Description |
| --- | --- |
| selectedOptions | `Accessor<T[]>`<br> An array of selected options. |
| remove | `(option: T) => void`<br> A function to remove an option from the selection. |
| clear | `() => void`<br> A function to clear the selection. |
### Search.Indicator
| Prop | Description |
| --- | --- |
| loadingComponent | `JSX.Element`<br> The component that is displayed when suggestion options are being fetched. |
### Search.Icon
| Data attribute | Description |
| --- | --- |
| data-expanded | Present when the search component is open. |
| data-closed | Present when the search component is close. |
### Search.Content
| Data attribute | Description |
| --- | --- |
| data-expanded | Present when the search component is open. |
| data-closed | Present when the search component is close. |
### Search.Arrow
| Prop | Description |
| --- | --- |
| size | `number`<br> The size of the arrow. |
### Search.Listbox
| Prop | Description |
| --- | --- |
| scrollRef | `Accessor<HTMLElement | undefined>`<br> The ref attached to the scrollable element, used to provide automatic scrolling on item focus. If not provided, defaults to the listbox. |
| scrollToItem | `(key: string) => void`<br> When virtualized, the Virtualizer function used to scroll to the item of the given key. |
| children | `(items: Accessor<Collection<CollectionNode<T | U>>>) => JSX.Element`<br> When virtualized, a map function that receives an _items_ signal representing all items and sections. |
### Search.Item
| Prop | Description |
| --- | --- |
| item | `CollectionNode`<br> The collection node to render. |
| Data attribute | Description |
| --- | --- |
| data-disabled | Present when the item is disabled. |
| data-selected | Present when the item is selected. |
| data-highlighted | Present when the item is highlighted. |
`Search.ItemLabel` and `Search.ItemDescription` shares the same data-attributes.
## Rendered elements
| Component | Default rendered element |
| --- | --- |
| `Search` | `div` |
| `Search.Label` | `span` |
| `Search.Description` | `div` |
| `Search.Control` | `div` |
| `Search.Indicator` | `div` |
| `Search.Icon` | `span` |
| `Search.Input` | `input` |
| `Search.Portal` | `Portal` |
| `Search.Content` | `div` |
| `Search.Arrow` | `div` |
| `Search.Listbox` | `ul` |
| `Search.Section` | `li` |
| `Search.Item` | `li` |
| `Search.ItemLabel` | `div` |
| `Search.ItemDescription` | `div` |
| `Search.NoResult` | `div` |
## Accessibility
### Keyboard Interactions
| Key | Description |
| --- | --- |
| `Enter` | When focus is virtualy on an item, selects the focused item. |
| `ArrowDown` | When focus is on the input, opens the search suggestion and virtual focuses the first or selected item. <br> When focus is virtualy on an item, moves virtual focus to the next item. |
| `ArrowUp` | When focus is on the input, opens the search suggestion and virtual focuses the last or selected item. <br> When focus is virtualy on an item, moves virtual focus to the previous item. |
| `Alt` \+ `ArrowDown` | When focus is on the input, opens the search suggestion. |
| `Alt` \+ `ArrowUp` | When focus is on the input, closes the search suggestion. |
| `Home` | When focus is on the input, moves virtual focus to first item. |
| `End` | When focus is on the input, moves virtual focus to last item. |
| `Esc` | When the search suggestion is open, closes the search suggestion. <br> When the search suggestion is closed, clear the input and selection. |
Previous[←Rating Group](https://kobalte.dev/docs/core/components/rating-group)Next[Segmented Control→](https://kobalte.dev/docs/core/components/segmented-control)