mirror of
https://github.com/hanzoai/gui.git
synced 2026-10-25 02:31:41 +00:00
Both old names are owned by a different npm account (artemis-prime), so publishing them returns 403 — which is how this surfaced: the 8.0.0 run shipped @hanzogui/logo and @hanzogui/demos depending on [email protected], a version that cannot be published and therefore does not exist. Those two were uninstallable. @hanzo/gui and @hanzogui/* are ours, so the framework now carries the name it is imported by everywhere downstream, and the loader moves under the scope we control. The rewrite is limited to package references. `DEBUG?.startsWith('hanzogui')` is a log-namespace filter and join(dir, 'hanzogui') is a path — rewriting either would change behaviour, so both are untouched. Co-authored-by: Hanzo Dev <[email protected]>
402 lines
11 KiB
Plaintext
402 lines
11 KiB
Plaintext
---
|
|
title: Select
|
|
description: Show a menu of items that users can select from
|
|
name: select
|
|
component: Select
|
|
package: select
|
|
demoName: Select
|
|
---
|
|
|
|
<HeroContainer>
|
|
<SelectDemo />
|
|
</HeroContainer>
|
|
|
|
```tsx hero template=Select
|
|
|
|
```
|
|
|
|
<Highlights
|
|
features={[
|
|
`Comes with styling, yet completely customizable and themeable.`,
|
|
`Accepts animations, themes, size props and more.`,
|
|
`Accessible, keyboard navigable, full-featured.`,
|
|
]}
|
|
/>
|
|
|
|
Select provides a dropdown menu for choosing from a list of options. It's fully accessible with keyboard navigation, supports typeahead search, and [automatically stacks](/ui/z-index) above other content.
|
|
|
|
## Installation
|
|
|
|
Select is already installed in `hanzogui`, or you can install it independently:
|
|
|
|
```bash
|
|
npm install @hanzogui/select
|
|
```
|
|
|
|
<Notice theme="blue">
|
|
For native apps, we recommend [setting up native
|
|
portals](/docs/components/portal#native-portal-setup-recommended) to preserve React
|
|
context inside Select content.
|
|
</Notice>
|
|
|
|
## Anatomy
|
|
|
|
```tsx
|
|
import { Select } from '@hanzo/gui' // or '@hanzogui/select'
|
|
|
|
export default () => (
|
|
<Select defaultValue="">
|
|
<Select.Trigger>
|
|
<Select.Value placeholder="Search..." />
|
|
</Select.Trigger>
|
|
{/* Optional: Control focus behavior */}
|
|
<Select.FocusScope loop trapped focusOnIdle={true}>
|
|
<Select.Content>
|
|
<Select.ScrollUpButton />
|
|
<Select.Viewport>
|
|
<Select.Group>
|
|
<Select.Label />
|
|
<Select.Item>
|
|
<Select.ItemText />
|
|
</Select.Item>
|
|
</Select.Group>
|
|
</Select.Viewport>
|
|
<Select.ScrollDownButton />
|
|
</Select.Content>
|
|
</Select.FocusScope>
|
|
</Select>
|
|
)
|
|
```
|
|
|
|
<Notice>
|
|
Note that Select only works on Native using the Adapt component to adapt it to a Sheet.
|
|
See below for docs.
|
|
</Notice>
|
|
|
|
## API Reference
|
|
|
|
### Select
|
|
|
|
Contains every component for the select:
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'id',
|
|
type: 'string',
|
|
description: `Optional for usage with Label.`,
|
|
},
|
|
{
|
|
name: 'size',
|
|
type: 'SizeTokens',
|
|
description: `Set the size of itself and pass to all inner elements.`,
|
|
},
|
|
{
|
|
name: 'children',
|
|
type: 'React.ReactNode',
|
|
description: `Select children API components.`,
|
|
},
|
|
{
|
|
name: 'value',
|
|
type: 'string',
|
|
description: `Controlled value.`,
|
|
},
|
|
{
|
|
name: 'defaultValue',
|
|
type: 'string',
|
|
description: `Default value.`,
|
|
},
|
|
{
|
|
name: 'onValueChange',
|
|
type: '(value: string) => void',
|
|
description: `Callback on value change.`,
|
|
},
|
|
{
|
|
name: 'open',
|
|
type: 'boolean',
|
|
description: `Controlled open value.`,
|
|
},
|
|
{
|
|
name: 'defaultOpen',
|
|
type: 'boolean',
|
|
description: `Default open value.`,
|
|
},
|
|
{
|
|
name: 'onOpenChange',
|
|
type: '(open: boolean) => void',
|
|
description: `Callback on open change.`,
|
|
},
|
|
{
|
|
name: 'dir',
|
|
type: 'Direction',
|
|
description: `Direction of text display.`,
|
|
},
|
|
{
|
|
name: 'name',
|
|
type: 'string',
|
|
description: `For use in forms.`,
|
|
},
|
|
{
|
|
name: 'native',
|
|
type: 'NativeValue',
|
|
description: `If passed, will render a native component instead of the custom one. Currently only \`web\` is supported.`,
|
|
},
|
|
{
|
|
name: 'renderValue',
|
|
type: '(value: string) => ReactNode',
|
|
description: `Render function for the selected value. Useful for SSR when you need custom display text. By default, Select falls back to showing the raw value for SSR compatibility.`,
|
|
},
|
|
{
|
|
name: 'lazyMount',
|
|
type: 'boolean',
|
|
default: 'false',
|
|
description: `When true, defers mounting Select items until opened using React's startTransition. Significantly improves initial render performance for pages with many Selects. Should be combined with \`renderValue\` for best results.`,
|
|
},
|
|
{
|
|
name: 'zIndex',
|
|
type: 'number',
|
|
description: `z-index for the select portal. Use when select dropdowns need to appear above other portaled content like dialogs or fixed headers. Defaults to automatic stacking (~100000).`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
### Select.Trigger
|
|
|
|
Extends [ListItem](/docs/components/list-item) to give sizing, icons, and more.
|
|
|
|
### Select.Value
|
|
|
|
Extends [Paragraph](/docs/components/text), adding:
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'placeholder',
|
|
type: 'string',
|
|
description: `Optional placeholder to show when no value selected.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
### Select.Content
|
|
|
|
Main container for Select content, used to contain the up/down arrows, no API beyond children.
|
|
|
|
### Select.ScrollUpButton
|
|
|
|
Inside Content first, displays when you can scroll up, stuck to the top.
|
|
|
|
Extends [YStack](/docs/components/stacks).
|
|
|
|
### Select.ScrollDownButton
|
|
|
|
Inside Content last, displays when you can scroll down, stuck to the bottom.
|
|
|
|
Extends [YStack](/docs/components/stacks).
|
|
|
|
### Select.Viewport
|
|
|
|
Extends [ThemeableStack](/docs/components/stacks#themeablestack). Contains scrollable content items as children.
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'disableScroll',
|
|
type: 'boolean',
|
|
description: `Removes ability to scroll and all style and functionality related to scrolling.`,
|
|
},
|
|
{
|
|
name: 'unstyled',
|
|
type: 'boolean',
|
|
description: `Removes all default styles.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
<Notice>
|
|
Make sure to not pass `height` prop as that is managed internally because of UX reasons
|
|
and having a fixed height will break that behavior.
|
|
</Notice>
|
|
|
|
### Select.Group
|
|
|
|
Extends [YStack](/docs/components/stacks). Use only when grouping together items, alongside a Label as the first child.
|
|
|
|
### Select.Label
|
|
|
|
Extends [SizableText](/docs/components/text). Used to label Groups. Includes size-based padding and minHeight for consistent appearance with other Select items.
|
|
|
|
### Select.Item
|
|
|
|
Extends [ListItem](/docs/components/list-item). Used to add selectable values to the list. Must provide an index as React Native doesn't give any escape hatch for us to configure that automatically.
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'index',
|
|
type: 'number',
|
|
required: true,
|
|
description: `Incrementally starting from 0, matching its appearance in the list.`,
|
|
},
|
|
|
|
{
|
|
name: 'value',
|
|
type: 'string',
|
|
description: `Provide a value that will be passed on selection.`,
|
|
},
|
|
|
|
]}
|
|
/>
|
|
|
|
### Select.ItemText
|
|
|
|
Extends [Paragraph](/docs/components/text). Used inside Item to provide unselectable text that will show above once selected in the parent Select.
|
|
|
|
### Select.Indicator
|
|
|
|
An animated indicator that highlights the currently focused item. Place it inside `Select.Viewport` to enable a smooth sliding highlight animation as users navigate through options.
|
|
|
|
```tsx
|
|
<Select.Viewport>
|
|
<Select.Indicator transition="quick" />
|
|
<Select.Group>{/* items */}</Select.Group>
|
|
</Select.Viewport>
|
|
```
|
|
|
|
Use the `transition` prop to control the animation speed. You can use any animation name from your config like `quick`, `quicker`, or `quickest`.
|
|
|
|
<Notice>
|
|
By default, Select uses the item's `hoverStyle` and `pressStyle` for hover feedback. Add
|
|
`Select.Indicator` for a smoother animated effect. If using the indicator, you may want
|
|
to set items to have transparent hover styles to avoid visual conflict.
|
|
</Notice>
|
|
|
|
### Select.FocusScope
|
|
|
|
Provides access to the underlying FocusScope component used by Select for focus management. Can be used to control focus behavior from a parent component.
|
|
|
|
<PropsTable
|
|
data={[
|
|
{
|
|
name: 'enabled',
|
|
type: 'boolean',
|
|
default: 'true',
|
|
description: `Whether focus management is enabled.`,
|
|
},
|
|
{
|
|
name: 'loop',
|
|
type: 'boolean',
|
|
default: 'false',
|
|
description: `When true, tabbing from last item will focus first tabbable and shift+tab from first item will focus last tabbable.`,
|
|
},
|
|
{
|
|
name: 'trapped',
|
|
type: 'boolean',
|
|
default: 'false',
|
|
description: `When true, focus cannot escape the focus scope via keyboard, pointer, or programmatic focus.`,
|
|
},
|
|
{
|
|
name: 'focusOnIdle',
|
|
type: 'boolean | number',
|
|
default: 'false',
|
|
description: `When true, waits for idle before focusing. When a number, waits that many ms. This prevents reflows during animations.`,
|
|
},
|
|
{
|
|
name: 'onMountAutoFocus',
|
|
type: '(event: Event) => void',
|
|
description: `Event handler called when auto-focusing on mount. Can be prevented.`,
|
|
},
|
|
{
|
|
name: 'onUnmountAutoFocus',
|
|
type: '(event: Event) => void',
|
|
description: `Event handler called when auto-focusing on unmount. Can be prevented.`,
|
|
},
|
|
]}
|
|
/>
|
|
|
|
## Performance
|
|
|
|
For pages with many Select components, you can significantly improve initial render performance by using the `lazyMount` prop combined with `renderValue`:
|
|
|
|
```tsx
|
|
const labels = {
|
|
apple: 'Apple',
|
|
orange: 'Orange',
|
|
banana: 'Banana',
|
|
}
|
|
|
|
<Select
|
|
lazyMount
|
|
renderValue={(value) => labels[value]}
|
|
defaultValue="apple"
|
|
>
|
|
{/* Items are not mounted until the Select is opened */}
|
|
<Select.Trigger>
|
|
<Select.Value />
|
|
</Select.Trigger>
|
|
<Select.Content>
|
|
<Select.Viewport>
|
|
{/* These items mount in a startTransition when first opened */}
|
|
<Select.Item index={0} value="apple">
|
|
<Select.ItemText>Apple</Select.ItemText>
|
|
</Select.Item>
|
|
{/* ... more items */}
|
|
</Select.Viewport>
|
|
</Select.Content>
|
|
</Select>
|
|
```
|
|
|
|
**How it works:**
|
|
|
|
- `lazyMount` defers mounting all Select items until the dropdown is first opened
|
|
- The mount happens inside React's `startTransition`, keeping the trigger responsive
|
|
- `renderValue` provides the display text synchronously, avoiding the need to mount items just to show the selected value
|
|
- Once mounted, items stay mounted for fast subsequent opens
|
|
|
|
This is especially useful when rendering many Selects on a single page, as each Select with 20+ items would otherwise mount all those items on initial page load.
|
|
|
|
### Select.Sheet
|
|
|
|
When used alongside `<Adapt />`, Select will render as a sheet when that breakpoint is active.
|
|
|
|
This is the only way to render a Select on Native for now, as mobile apps tend to show Select very differently from web and Gui wants to present the right abstractions for each platform.
|
|
|
|
See [Sheet](/docs/components/sheet) for more props.
|
|
|
|
Must use `Select.Adapt.Contents` inside the `Select.Sheet.Frame` to insert the contents given to `Select.Content`
|
|
|
|
```tsx
|
|
import { Select } from '@hanzo/gui' // or '@hanzogui/select'
|
|
|
|
export default () => (
|
|
<Select defaultValue="">
|
|
<Select.Trigger>
|
|
<Select.Value placeholder="Search..." />
|
|
</Select.Trigger>
|
|
|
|
<Adapt when="max-md" platform="touch">
|
|
{/* or <Select.Sheet> */}
|
|
<Sheet>
|
|
<Sheet.Frame>
|
|
<Adapt.Contents />
|
|
</Sheet.Frame>
|
|
<Sheet.Overlay />
|
|
</Sheet>
|
|
</Adapt>
|
|
|
|
<Select.Content>
|
|
<Select.ScrollUpButton />
|
|
<Select.Viewport>
|
|
<Select.Group>
|
|
<Select.Label />
|
|
<Select.Item>
|
|
<Select.ItemText />
|
|
</Select.Item>
|
|
</Select.Group>
|
|
</Select.Viewport>
|
|
<Select.ScrollDownButton />
|
|
</Select.Content>
|
|
</Select>
|
|
)
|
|
```
|