airi/docs/ai/context/ui-components.md
Ryan Lei 37807b84d6
Some checks are pending
CI / Lint (push) Waiting to run
CI / Build Test (stage-tamagotchi) (push) Waiting to run
CI / Build Test (stage-tamagotchi-godot) (push) Waiting to run
CI / Build Test (stage-web) (push) Waiting to run
CI / Build Test (ui-loading-screens) (push) Waiting to run
CI / Build Test (ui-transitions) (push) Waiting to run
CI / Type Check (push) Waiting to run
CI / Check Provenance (push) Waiting to run
Cloudflare Workers / Deploy - stage-web (push) Waiting to run
fix(ui): add openOnClick prop to Combobox for reliable dropdown opening (#1795)
---------

Co-authored-by: leiyutian <leiyutian@echo.tech>
Co-authored-by-agent: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 22:44:27 +08:00

578 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# `@proj-airi/ui` Component Reference
> **Auto-maintained**: When adding or updating components in `packages/ui`, update this document accordingly.
Standardized primitives built on [reka-ui](https://reka-ui.com/). Minimal business logic — use these instead of raw DOM elements.
Source: `packages/ui/src/components/`
---
## Animations
### TransitionBidirectional
Bidirectional Vue `<Transition>` wrapper with customizable CSS classes.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `fromClass` | `string?` | — | CSS class for initial state |
| `activeClass` | `string?` | — | CSS class during transition |
| `toClass` | `string?` | — | CSS class for final state |
**Slots**: `default`
### TransitionHorizontal
Horizontal slide/fade transition (0.5s hardcoded).
**Props**: None | **Slots**: `default`
### TransitionVertical
Smooth vertical expand/collapse with height animation and opacity control.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `duration` | `number?` | `250` | Animation duration (ms) |
| `easingEnter` | `string?` | `'ease-in-out'` | Enter easing function |
| `easingLeave` | `string?` | `'ease-in-out'` | Leave easing function |
| `opacityClosed` | `number?` | `0` | Opacity when closed |
| `opacityOpened` | `number?` | `1` | Opacity when opened |
**Slots**: `default`
---
## Layout
### Collapsible
Expandable/collapsible container with trigger button and vertical animation.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `default` | `boolean?` | — | Initial visibility |
| `label` | `string?` | — | Trigger button label |
**v-model**: `visible: boolean`
**Slots**: `trigger({ visible, setVisible })`, `default({ visible, setVisible })`
### Screen
Responsive screen component that calculates canvas dimensions based on breakpoints.
**Props**: None | **Slots**: `default({ width, height })`
### Skeleton
Loading placeholder with animation.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `animation` | `'pulse' \| 'wave' \| 'none'` | `'pulse'` | Animation style |
**Slots**: `default`
### Truncatable
Line-clamped content container that expands and collapses when the overflowing content area is clicked.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `lineClamp` | `number?` | `3` | Maximum visible lines while collapsed |
**Slots**: `default`
---
## Misc
### Button
Versatile button with variants, sizes, and states.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `toggled` | `boolean?` | `false` | Toggle state |
| `icon` | `string?` | — | UnoCSS/Iconify icon class |
| `label` | `string?` | — | Button text |
| `disabled` | `boolean?` | `false` | Disabled state |
| `loading` | `boolean?` | `false` | Loading state |
| `variant` | `'primary' \| 'secondary' \| 'secondary-muted' \| 'danger' \| 'caution' \| 'pure' \| 'ghost'` | `'primary'` | Visual variant |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size |
| `shape` | `'rounded' \| 'pill' \| 'square'` | `'pill'` | Shape |
| `block` | `boolean?` | `false` | Full width |
**Slots**: `default` (fallback when no `label`)
### Callout
Alert/callout box with themed accent bar.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `theme` | `'primary' \| 'violet' \| 'lime' \| 'orange'` | `'primary'` | Color theme |
| `label` | `string?` | — | Title |
**Slots**: `label`, `default`
### ContainerError
Error display with copy/feedback buttons and scrollable stack trace.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `error` | `unknown?` | — | Error object |
| `message` | `string?` | — | Custom message |
| `stack` | `string?` | — | Stack trace |
| `includeStack` | `boolean?` | `true` | Show stack |
| `showCopyButton` | `boolean?` | `true` | Show copy button |
| `showFeedbackButton` | `boolean?` | `true` | Show feedback button |
| `copyButtonLabel` | `string?` | `'Copy'` | Copy button text |
| `copiedButtonLabel` | `string?` | `'Copied'` | Copied state text |
| `feedbackButtonLabel` | `string?` | `'Feedback'` | Feedback button text |
| `heightPreset` | `'sm' \| 'md' \| 'lg' \| 'xl' \| 'auto'` | `'md'` | Container height |
**Emits**: `copy(content: string)`, `feedback()`
### ErrorBoundary
Catches synchronous render/setup errors in descendants via `onErrorCaptured` and renders a fallback (built-in `ContainerError` + retry button) instead of letting the error propagate. Use to wrap `<RouterView>` or any subtree where partial failure should not blank the host layout. Async/unhandled rejections are NOT captured — use `app.config.errorHandler` for those.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `title` | `string?` | — | Optional title shown above error details |
| `retryable` | `boolean?` | `true` | Show built-in retry button |
| `retryLabel` | `string?` | `'Try again'` | Retry button label |
**Slots**: `default`, `fallback({ error, info, retry })`
**Emits**: `error(err, instance, info)`, `retry()`
**Exposed**: `retry()`, `hasError()`
### DoubleCheckButton
Two-stage confirmation button — click once to reveal confirm/cancel.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `ButtonVariant` | `'danger'` | Confirm button variant |
| `cancelVariant` | `ButtonVariant` | `'secondary'` | Cancel button variant |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size |
| `block` | `boolean?` | `false` | Full width |
| `disabled` | `boolean?` | `false` | Disabled |
| `loading` | `boolean?` | `false` | Loading |
**Emits**: `confirm()`, `cancel()`
**Slots**: `default` (initial text), `confirm` (confirm text), `cancel` (cancel text)
### Progress
Linear progress bar with animated shine.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `progress` | `number` | *(required)* | Percentage 0100 |
| `barClass` | `string?` | — | Custom bar color class |
---
## Form — Input
### Input
Basic text/number input.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `type` | `InputType?` | — | HTML input type |
| `variant` | `'primary' \| 'secondary' \| 'primary-dimmed'` | `'primary'` | Visual variant |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size |
**v-model**: `modelValue: string | number`
### BasicInputFile
Low-level file input with drag-drop support.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `accept` | `string?` | — | Accepted MIME types |
| `multiple` | `boolean?` | — | Allow multiple files |
| `isDraggingClasses` | `string \| string[]?` | — | Classes when dragging |
| `isNotDraggingClasses` | `string \| string[]?` | — | Classes when not dragging |
**v-model**: `modelValue: File[]`
**Slots**: `default({ isDragging, firstFile, files })`
### InputFile
File input with preview and drag-drop UI.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `accept` | `string?` | — | Accepted file types |
| `multiple` | `boolean?` | — | Allow multiple |
| `placeholder` | `string?` | `'Choose file'` | Placeholder text |
**v-model**: `modelValue: File[] | undefined`
### InputFileCard
Styled file upload card with drag-and-drop zone.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `accept` | `string?` | — | Accepted file types |
| `multiple` | `boolean?` | — | Allow multiple |
**v-model**: inherits from `BasicInputFile`
**Slots**: `default` (custom upload UI)
### InputKeyValue
Two-column input for key-value pairs.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `name` | `string?` | — | Input name attribute |
| `keyPlaceholder` | `string?` | — | Key placeholder |
| `valuePlaceholder` | `string?` | — | Value placeholder |
**v-model**: `propertyKey: string`, `propertyValue: string`
---
## Form — Textarea
### BasicTextarea
Auto-resizing textarea with submit and paste-file events.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `defaultHeight` | `string?` | — | Initial height when empty |
| `submitOnEnter` | `boolean?` | `true` | Submit on Enter (Shift+Enter for newline) |
**v-model**: `input: string`
**Emits**: `submit(message: string)`, `pasteFile(files: File[])`
### Textarea
Styled textarea wrapping `BasicTextarea`.
**v-model**: `modelValue: string`
---
## Form — Checkbox / Radio
### Checkbox
Toggle switch using reka-ui `SwitchRoot`.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `disabled` | `boolean?` | — | Disabled state |
**v-model**: `modelValue: boolean`
### Radio
Single radio button for radio groups.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `string` | *(required)* | Unique ID |
| `name` | `string` | *(required)* | Radio group name |
| `value` | `string` | *(required)* | Option value |
| `title` | `string` | *(required)* | Display label |
| `deprecated` | `boolean?` | `false` | Deprecation indicator |
**v-model**: `modelValue: string`
---
## Form — Range
### Range
Horizontal slider with progress visualization.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `min` | `number?` | `0` | Minimum value |
| `max` | `number?` | `100` | Maximum value |
| `step` | `number?` | `1` | Step increment |
| `disabled` | `boolean?` | `false` | Disabled |
| `thumbColor` | `string?` | `'#9090906e'` | Thumb color |
| `trackColor` | `string?` | `'gray'` | Track color |
| `trackValueColor` | `string?` | `'red'` | Filled track color |
**v-model**: `modelValue: number`
### ColorHueRange
HSL hue selector (0360) with rainbow gradient.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `disabled` | `boolean?` | — | Disabled |
**v-model**: `modelValue: number`
### RoundRange
Rounded-style slider.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `min` | `number?` | `0` | Minimum |
| `max` | `number?` | `100` | Maximum |
| `step` | `number?` | `1` | Step |
| `disabled` | `boolean?` | `false` | Disabled |
**v-model**: `modelValue: number`
---
## Form — Select
### Select
Dropdown select using reka-ui with grouping and custom rendering.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `options` | `SelectOptionItem<T>[] \| SelectOptionGroupItem<T>[]` | *(required)* | Options |
| `placeholder` | `string?` | `'Select an option'` | Placeholder |
| `disabled` | `boolean?` | `false` | Disabled |
| `by` | `string \| ((a: T, b: T) => boolean)?` | — | Custom comparison |
| `contentMinWidth` | `string \| number?` | `160` | Dropdown min width |
| `contentWidth` | `string \| number?` | — | Dropdown width |
| `shape` | `'rounded' \| 'default'` | `'default'` | Shape |
| `variant` | `'blurry' \| 'default'` | `'default'` | Variant |
**v-model**: `modelValue: T`
**Slots**: `value({ option, value, placeholder })`, `option({ option })`
### SelectOption
Individual option item within `Select`.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `option` | `SelectOptionItem<T>` | *(required)* | Option data |
**Slots**: `default`
---
## Form — Combobox
### Combobox
Searchable dropdown/autocomplete using reka-ui with grouping.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `options` | `ComboboxOptionItem<T>[] \| ComboboxOptionGroupItem<T>[]` | *(required)* | Options |
| `placeholder` | `string?` | — | Placeholder |
| `disabled` | `boolean?` | `false` | Disabled |
| `openOnClick` | `boolean?` | `true` | Auto-open dropdown on click |
| `contentMinWidth` | `string \| number?` | — | Dropdown min width |
| `contentWidth` | `string \| number?` | — | Dropdown width |
**v-model**: `modelValue: T`
**Slots**: `option({ option })`, `empty`
### ComboboxSelect
Simplified Combobox wrapper for string/number options.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `options` | `{ label, value, description?, disabled?, icon? }[]?` | — | Options |
| `placeholder` | `string?` | — | Placeholder |
| `disabled` | `boolean?` | `false` | Disabled |
| `openOnClick` | `boolean?` | `true` | Auto-open dropdown on click |
| `title` | `string?` | — | Title |
| `layout` | `'horizontal' \| 'vertical'?` | — | Layout direction |
| `contentMinWidth` | `string \| number?` | — | Dropdown min width |
| `contentWidth` | `string \| number?` | — | Dropdown width |
**v-model**: `modelValue: string | number`
**Slots**: `option({ option })`, `empty`
### ComboboxOption
Option item within combobox (uses provide/inject).
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `string \| number` | *(required)* | Value |
| `label` | `string?` | — | Display text |
| `active` | `boolean?` | — | Active state |
**Slots**: `default`
---
## Form — SelectTab
### SelectTab
Tab-like selection using radio buttons with animated indicator.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `options` | `SelectTabOption[]` | *(required)* | Tab options `{ label, value, description?, icon? }` |
| `disabled` | `boolean?` | `false` | Disabled |
| `readonly` | `boolean?` | `false` | Read-only |
| `size` | `'sm' \| 'md'` | `'md'` | Size |
**v-model**: `modelValue: T`
---
## Form — Field (Labeled wrappers)
All Field components wrap a base input with `label`, `description`, and consistent layout. Common slots: `label`, `description`.
### FieldInput
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string?` | — | Label |
| `description` | `string?` | — | Helper text |
| `placeholder` | `string?` | — | Placeholder |
| `required` | `boolean?` | — | Required indicator |
| `type` | `InputType?` | — | Input type |
| `inputClass` | `string?` | — | Custom input class |
| `singleLine` | `boolean?` | `true` | `true` = input, `false` = textarea |
**v-model**: `modelValue: T`
### FieldCheckbox
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string?` | — | Label |
| `description` | `string?` | — | Helper text |
| `disabled` | `boolean?` | — | Disabled |
| `placement` | `'left' \| 'right'` | `'right'` | Switch position |
**v-model**: `modelValue: boolean`
### FieldTextArea
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string?` | — | Label |
| `description` | `string?` | — | Helper text |
| `placeholder` | `string?` | — | Placeholder |
| `required` | `boolean?` | — | Required indicator |
| `textareaClass` | `string?` | — | Custom textarea class |
| `rows` | `number?` | `6` | Rows |
**v-model**: `modelValue: string`
### FieldRange
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `min` | `number?` | — | Min |
| `max` | `number?` | — | Max |
| `step` | `number?` | — | Step |
| `label` | `string?` | — | Label |
| `description` | `string?` | — | Helper text |
| `formatValue` | `(value: number) => string?` | — | Value formatter |
| `as` | `'label' \| 'div'` | `'label'` | Wrapper element |
**v-model**: `modelValue: number`
### FieldInputFile
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string?` | — | Label |
| `description` | `string?` | — | Helper text |
| `accept` | `string?` | — | Accepted types |
| `multiple` | `boolean?` | — | Multiple |
| `placeholder` | `string?` | — | Placeholder |
**v-model**: `modelValue: File[] | undefined`
### FieldSelect
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string` | *(required)* | Label |
| `description` | `string?` | — | Helper text |
| `options` | `SelectOptionItem<T>[] \| SelectOptionGroupItem<T>[]?` | — | Options |
| `placeholder` | `string?` | — | Placeholder |
| `disabled` | `boolean?` | — | Disabled |
| `layout` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout |
| `by` | `string \| ((a, b) => boolean)?` | — | Comparison |
| `shape` | `'rounded' \| 'default'?` | — | Shape |
| `variant` | `'blurry' \| 'default'?` | — | Variant |
**v-model**: `modelValue: T`
**Slots**: `label`, `description`, `value`, `option`
### FieldCombobox
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string` | *(required)* | Label |
| `description` | `string?` | — | Helper text |
| `options` | `{ label, value, description?, disabled?, icon? }[]?` | — | Options |
| `placeholder` | `string?` | — | Placeholder |
| `disabled` | `boolean?` | `false` | Disabled |
| `openOnClick` | `boolean?` | `true` | Auto-open dropdown on click |
| `layout` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout |
**v-model**: `modelValue: string`
**Slots**: `label`, `description`, `option`, `empty`
### FieldKeyValues
Dynamic key-value pair list with add/remove.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string?` | — | Label |
| `description` | `string?` | — | Helper text |
| `name` | `string?` | — | Input name |
| `keyPlaceholder` | `string?` | — | Key placeholder |
| `valuePlaceholder` | `string?` | — | Value placeholder |
| `required` | `boolean?` | — | Required |
| `inputClass` | `string?` | — | Custom input class |
**v-model**: `keyValues: { key: string, value: string }[]`
**Emits**: `remove(index: number)`, `add(key: string, value: string)`
### FieldValues
Dynamic string list with add/remove.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `label` | `string?` | — | Label |
| `description` | `string?` | — | Helper text |
| `name` | `string?` | — | Input name |
| `valuePlaceholder` | `string?` | — | Value placeholder |
| `required` | `boolean?` | — | Required |
| `inputClass` | `string?` | — | Custom input class |
**v-model**: `items: string[]`
**Emits**: `remove(index: number)`, `add()`
---
## Composables
Exported from `packages/ui/src/composables/`:
- **`useDeferredMount()`** — Defers component mounting (useful for heavy components).
- **`useTheme()`** — Theme management composable.