UNPKG

laif-ds

Version:

Design System di Laif con componenti React basati su principi di Atomic Design

119 lines (97 loc) 6.07 kB
# AsyncSelect ## Overview Generic async select with search input, popover list, server-side filtering, and optional multiple selection. Fully render-prop driven for option content and value extraction. --- ## Props | Prop | Type | Default | Description | | --------------------- | ------------------------------------- | ------------- | ----------------------------------------------------------------------------------- | | `fetcher` | `(query?: string) => Promise<T[]>` | **required** | Async loader for options. Called on open and on search. | | `renderOptionItem` | `(option: T) => React.ReactNode` | **required** | Custom renderer for each menu row. | | `resolveOptionValue` | `(option: T) => string` | **required** | Returns the unique ID stored in `value`. | | `renderSelectedValue` | `(option: T) => React.ReactNode` | **required** | Content shown in the trigger when selected. | | `multiple` | `boolean` | `false` | Enables multi-selection and switches `value` to `string[]`. | | `value` | `string \| string[] \| undefined` | `undefined` | Controlled value. | | `onChange` | `(value: string \| string[]) => void` | `undefined` | Emits the next value when the selection changes. | | `label` | `string \| React.ReactNode` | `undefined` | Optional label rendered above the trigger. | | `placeholder` | `string` | `"Select..."` | Placeholder when no value is selected. | | `disabled` | `boolean` | `false` | Disables trigger and input interactions. | | `className` | `string` | `undefined` | Extra classes for the trigger button. | | `wrpClassName` | `string` | `undefined` | Classes applied to the outer wrapper (use this to control width, e.g. `w-[200px]`). | | `noResultsMessage` | `string` | `undefined` | Message shown inside the default empty state. | | `clearable` | `boolean` | `true` | Shows the clear icon when a value is present. | | `size` | `"sm" \| "default" \| "lg"` | `"default"` | Trigger size variant. | | `notFound` | `React.ReactNode` | `undefined` | Custom fallback when there are no options. | | `initialOptions` | `T[]` | `undefined` | Prefills cache/selection with a known options list. | | `debounce` | `number` | `300` | Debounce delay (ms) before calling `fetcher`. | --- ## Behavior - **Search**: Debounced input (300ms) that always hits the provided `fetcher`. - **Caching**: Results cached by query; reused when re-typing. - **Multiple**: Renders selected count or first label; supports checkbox UI per item. - **Clear**: Built-in clear icon (uses DS `Icon`), respects `clearable`. - **A11y**: Trigger focus ring and keyboard navigation via `cmdk`. --- ## Examples ### Multiple with Steps ```tsx import * as React from "react"; import { AsyncSelect } from "laif-ds"; type Tag = { id: string; label: string }; const tags: Tag[] = [ { id: "react", label: "React" }, { id: "nextjs", label: "Next.js" }, { id: "tailwind", label: "Tailwind" }, ]; export function MultipleTags() { const [value, setValue] = React.useState<string[]>([]); const fetcher = async (q?: string) => !q ? tags : tags.filter((t) => t.label.toLowerCase().includes(q!.toLowerCase())); return ( <AsyncSelect<Tag> multiple fetcher={fetcher} value={value} onChange={setValue} renderOptionItem={(t) => t.label} resolveOptionValue={(t) => t.id} renderSelectedValue={(t) => t.label} placeholder="Select tags..." wrpClassName="w-[280px]" /> ); } --- ## Notes - **Trigger width**: Use `width="auto"` to match trigger width; or a fixed `number|string`. - **Filtering**: Always handled server-side via `fetcher`, with cached responses per query. - **Theming**: Uses DS token classes across trigger, list, and states. type Tag = { id: string; label: string }; const tags: Tag[] = [ { id: "react", label: "React" }, { id: "nextjs", label: "Next.js" }, { id: "tailwind", label: "Tailwind" }, ]; export function MultipleTags() { const [value, setValue] = React.useState<string[]>([]); const fetcher = async (q?: string) => !q ? tags : tags.filter((t) => t.label.toLowerCase().includes(q!.toLowerCase())); return ( <AsyncSelect<Tag> multiple fetcher={fetcher} value={value} onChange={setValue} renderOption={(t) => t.label} getOptionValue={(t) => t.id} getDisplayValue={(t) => t.label} placeholder="Select tags..." width="auto" /> ); } ``` --- ## Notes - **Trigger width**: Use `width="auto"` to match trigger width; or a fixed `number|string`. - **Filtering**: Always handled server-side via `fetcher`, with cached responses per query. - **Theming**: Uses DS token classes across trigger, list, and states.