@bfrs/agentic-components 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BFRS_AGENTIC_COMPONENTS.md +1236 -0
- package/README.md +132 -0
- package/dist/components/data-display/DataTable/DataTable.d.ts +2 -0
- package/dist/components/data-display/DataTable/DataTable.types.d.ts +153 -0
- package/dist/components/data-display/DataTable/TableBulkActions.d.ts +2 -0
- package/dist/components/data-display/DataTable/TableColumnVisibility.d.ts +2 -0
- package/dist/components/data-display/DataTable/TableEmptyState.d.ts +2 -0
- package/dist/components/data-display/DataTable/TableErrorState.d.ts +2 -0
- package/dist/components/data-display/DataTable/TablePagination.d.ts +2 -0
- package/dist/components/data-display/DataTable/TableRowActions.d.ts +2 -0
- package/dist/components/data-display/DataTable/TableSkeleton.d.ts +2 -0
- package/dist/components/data-display/DataTable/TableToolbar.d.ts +2 -0
- package/dist/components/data-display/DataTable/index.d.ts +10 -0
- package/dist/components/data-display/MetricCard/MetricCard.d.ts +14 -0
- package/dist/components/data-display/MetricCard/MetricCard.types.d.ts +15 -0
- package/dist/components/data-display/MetricCard/index.d.ts +2 -0
- package/dist/components/feedback/EmptyState/EmptyState.d.ts +10 -0
- package/dist/components/feedback/EmptyState/EmptyState.types.d.ts +12 -0
- package/dist/components/feedback/EmptyState/index.d.ts +2 -0
- package/dist/components/feedback/ErrorState/ErrorState.d.ts +8 -0
- package/dist/components/feedback/ErrorState/ErrorState.types.d.ts +10 -0
- package/dist/components/feedback/ErrorState/index.d.ts +2 -0
- package/dist/components/feedback/LoadingState/LoadingState.d.ts +6 -0
- package/dist/components/feedback/LoadingState/LoadingState.types.d.ts +8 -0
- package/dist/components/feedback/LoadingState/index.d.ts +2 -0
- package/dist/components/feedback/Toast/Toast.context.d.ts +3 -0
- package/dist/components/feedback/Toast/Toast.d.ts +3 -0
- package/dist/components/feedback/Toast/Toast.types.d.ts +48 -0
- package/dist/components/feedback/Toast/index.d.ts +3 -0
- package/dist/components/filters/DateRangePicker/DateRangePicker.d.ts +5 -0
- package/dist/components/filters/DateRangePicker/DateRangePicker.types.d.ts +48 -0
- package/dist/components/filters/DateRangePicker/DateRangePicker.utils.d.ts +35 -0
- package/dist/components/filters/DateRangePicker/index.d.ts +3 -0
- package/dist/components/filters/FilterBar/FilterBar.d.ts +15 -0
- package/dist/components/filters/FilterBar/FilterBar.types.d.ts +21 -0
- package/dist/components/filters/FilterBar/index.d.ts +2 -0
- package/dist/components/filters/FilterDrawer/FilterDrawer.d.ts +2 -0
- package/dist/components/filters/FilterDrawer/FilterDrawer.types.d.ts +17 -0
- package/dist/components/filters/FilterDrawer/index.d.ts +2 -0
- package/dist/components/layout/FormSection/FormSection.d.ts +14 -0
- package/dist/components/layout/FormSection/FormSection.types.d.ts +14 -0
- package/dist/components/layout/FormSection/index.d.ts +2 -0
- package/dist/components/layout/PageHeader/PageHeader.d.ts +13 -0
- package/dist/components/layout/PageHeader/PageHeader.types.d.ts +14 -0
- package/dist/components/layout/PageHeader/index.d.ts +2 -0
- package/dist/components/layout/SectionHeader/SectionHeader.d.ts +9 -0
- package/dist/components/layout/SectionHeader/SectionHeader.types.d.ts +10 -0
- package/dist/components/layout/SectionHeader/index.d.ts +2 -0
- package/dist/components/navigation/ActionMenu/ActionMenu.d.ts +2 -0
- package/dist/components/navigation/ActionMenu/ActionMenu.types.d.ts +22 -0
- package/dist/components/navigation/ActionMenu/index.d.ts +2 -0
- package/dist/components/navigation/Tabs/Tabs.d.ts +2 -0
- package/dist/components/navigation/Tabs/Tabs.types.d.ts +17 -0
- package/dist/components/navigation/Tabs/index.d.ts +2 -0
- package/dist/components/overlays/ConfirmDialog/ConfirmDialog.d.ts +2 -0
- package/dist/components/overlays/ConfirmDialog/ConfirmDialog.types.d.ts +19 -0
- package/dist/components/overlays/ConfirmDialog/index.d.ts +2 -0
- package/dist/components/overlays/Drawer/Drawer.d.ts +2 -0
- package/dist/components/overlays/Drawer/Drawer.types.d.ts +24 -0
- package/dist/components/overlays/Drawer/index.d.ts +2 -0
- package/dist/components/overlays/Modal/Modal.d.ts +2 -0
- package/dist/components/overlays/Modal/Modal.types.d.ts +22 -0
- package/dist/components/overlays/Modal/index.d.ts +2 -0
- package/dist/components/primitives/Badge/Badge.d.ts +9 -0
- package/dist/components/primitives/Badge/Badge.types.d.ts +13 -0
- package/dist/components/primitives/Badge/index.d.ts +2 -0
- package/dist/components/primitives/Button/Button.d.ts +15 -0
- package/dist/components/primitives/Button/Button.types.d.ts +17 -0
- package/dist/components/primitives/Button/index.d.ts +2 -0
- package/dist/components/primitives/Card/Card.d.ts +9 -0
- package/dist/components/primitives/Card/Card.types.d.ts +8 -0
- package/dist/components/primitives/Card/index.d.ts +2 -0
- package/dist/components/primitives/Chip/Chip.d.ts +10 -0
- package/dist/components/primitives/Chip/Chip.types.d.ts +14 -0
- package/dist/components/primitives/Chip/index.d.ts +2 -0
- package/dist/components/primitives/Input/Input.d.ts +16 -0
- package/dist/components/primitives/Input/Input.types.d.ts +18 -0
- package/dist/components/primitives/Input/index.d.ts +2 -0
- package/dist/components/ui/actions/Button/Button.d.ts +2 -0
- package/dist/components/ui/actions/Button/index.d.ts +1 -0
- package/dist/components/ui/actions/IconButton/IconButton.d.ts +2 -0
- package/dist/components/ui/actions/IconButton/index.d.ts +1 -0
- package/dist/components/ui/data-display/Avatar/Avatar.d.ts +15 -0
- package/dist/components/ui/data-display/Avatar/index.d.ts +1 -0
- package/dist/components/ui/data-display/Pagination/Pagination.d.ts +7 -0
- package/dist/components/ui/data-display/Pagination/index.d.ts +1 -0
- package/dist/components/ui/data-display/Table/Table.d.ts +28 -0
- package/dist/components/ui/data-display/Table/TableEmptyState.d.ts +2 -0
- package/dist/components/ui/data-display/Table/TableSkeleton.d.ts +5 -0
- package/dist/components/ui/data-display/Table/index.d.ts +3 -0
- package/dist/components/ui/feedback/Alert/Alert.d.ts +18 -0
- package/dist/components/ui/feedback/Alert/index.d.ts +1 -0
- package/dist/components/ui/feedback/Badge/Badge.d.ts +2 -0
- package/dist/components/ui/feedback/Badge/index.d.ts +1 -0
- package/dist/components/ui/feedback/Chip/Chip.d.ts +2 -0
- package/dist/components/ui/feedback/Chip/index.d.ts +1 -0
- package/dist/components/ui/feedback/EmptyState/EmptyState.d.ts +2 -0
- package/dist/components/ui/feedback/EmptyState/index.d.ts +1 -0
- package/dist/components/ui/feedback/Skeleton/Skeleton.d.ts +10 -0
- package/dist/components/ui/feedback/Skeleton/index.d.ts +1 -0
- package/dist/components/ui/feedback/Spinner/Spinner.d.ts +6 -0
- package/dist/components/ui/feedback/Spinner/index.d.ts +1 -0
- package/dist/components/ui/forms/Checkbox/Checkbox.d.ts +14 -0
- package/dist/components/ui/forms/Checkbox/index.d.ts +1 -0
- package/dist/components/ui/forms/FormField/FormField.d.ts +17 -0
- package/dist/components/ui/forms/FormField/index.d.ts +1 -0
- package/dist/components/ui/forms/Input/Input.d.ts +2 -0
- package/dist/components/ui/forms/Input/index.d.ts +1 -0
- package/dist/components/ui/forms/Label/Label.d.ts +9 -0
- package/dist/components/ui/forms/Label/index.d.ts +1 -0
- package/dist/components/ui/forms/Radio/Radio.d.ts +14 -0
- package/dist/components/ui/forms/Radio/index.d.ts +1 -0
- package/dist/components/ui/forms/SearchableSelect/SearchableSelect.d.ts +18 -0
- package/dist/components/ui/forms/SearchableSelect/index.d.ts +1 -0
- package/dist/components/ui/forms/Select/Select.d.ts +20 -0
- package/dist/components/ui/forms/Select/index.d.ts +1 -0
- package/dist/components/ui/forms/Switch/Switch.d.ts +7 -0
- package/dist/components/ui/forms/Switch/index.d.ts +1 -0
- package/dist/components/ui/forms/Textarea/Textarea.d.ts +9 -0
- package/dist/components/ui/forms/Textarea/index.d.ts +1 -0
- package/dist/components/ui/index.d.ts +53 -0
- package/dist/components/ui/navigation/Breadcrumbs/Breadcrumbs.d.ts +11 -0
- package/dist/components/ui/navigation/Breadcrumbs/index.d.ts +1 -0
- package/dist/components/ui/navigation/Tabs/Tabs.d.ts +2 -0
- package/dist/components/ui/navigation/Tabs/index.d.ts +1 -0
- package/dist/components/ui/overlays/Dialog/Dialog.d.ts +2 -0
- package/dist/components/ui/overlays/Dialog/index.d.ts +1 -0
- package/dist/components/ui/overlays/Drawer/Drawer.d.ts +2 -0
- package/dist/components/ui/overlays/Drawer/index.d.ts +1 -0
- package/dist/components/ui/overlays/Dropdown/Dropdown.d.ts +16 -0
- package/dist/components/ui/overlays/Dropdown/index.d.ts +1 -0
- package/dist/components/ui/overlays/Popover/Popover.d.ts +8 -0
- package/dist/components/ui/overlays/Popover/index.d.ts +1 -0
- package/dist/components/ui/overlays/Tooltip/Tooltip.d.ts +8 -0
- package/dist/components/ui/overlays/Tooltip/index.d.ts +1 -0
- package/dist/components/ui/patterns/BusinessInfoDisplayCard/BusinessInfoDisplayCard.d.ts +37 -0
- package/dist/components/ui/patterns/BusinessInfoDisplayCard/BusinessInfoDisplayCardSkeleton.d.ts +7 -0
- package/dist/components/ui/patterns/BusinessInfoDisplayCard/index.d.ts +2 -0
- package/dist/components/ui/patterns/ChatBubble/ChatBubble.d.ts +11 -0
- package/dist/components/ui/patterns/ChatBubble/index.d.ts +1 -0
- package/dist/components/ui/patterns/ChatComposer/ChatComposer.d.ts +19 -0
- package/dist/components/ui/patterns/ChatComposer/index.d.ts +1 -0
- package/dist/components/ui/patterns/FullPageLoader/FullPageLoader.d.ts +17 -0
- package/dist/components/ui/patterns/FullPageLoader/FullPageLoaderSpinner.d.ts +4 -0
- package/dist/components/ui/patterns/FullPageLoader/index.d.ts +1 -0
- package/dist/components/ui/patterns/LayoutShell/LayoutShell.d.ts +8 -0
- package/dist/components/ui/patterns/LayoutShell/index.d.ts +1 -0
- package/dist/components/ui/patterns/PageHeader/PageHeader.d.ts +2 -0
- package/dist/components/ui/patterns/PageHeader/index.d.ts +1 -0
- package/dist/components/ui/patterns/StepProgressCard/StepProgressCard.d.ts +24 -0
- package/dist/components/ui/patterns/StepProgressCard/StepProgressCardSkeleton.d.ts +7 -0
- package/dist/components/ui/patterns/StepProgressCard/index.d.ts +2 -0
- package/dist/components/ui/primitives/Box/Box.d.ts +7 -0
- package/dist/components/ui/primitives/Box/index.d.ts +1 -0
- package/dist/components/ui/primitives/Container/Container.d.ts +10 -0
- package/dist/components/ui/primitives/Container/index.d.ts +1 -0
- package/dist/components/ui/primitives/Grid/Grid.d.ts +9 -0
- package/dist/components/ui/primitives/Grid/index.d.ts +1 -0
- package/dist/components/ui/primitives/Icon/Icon.d.ts +15 -0
- package/dist/components/ui/primitives/Icon/index.d.ts +1 -0
- package/dist/components/ui/primitives/Paper/Paper.d.ts +12 -0
- package/dist/components/ui/primitives/Paper/index.d.ts +1 -0
- package/dist/components/ui/primitives/Stack/Stack.d.ts +17 -0
- package/dist/components/ui/primitives/Stack/index.d.ts +1 -0
- package/dist/components/ui/primitives/Text/Text.d.ts +16 -0
- package/dist/components/ui/primitives/Text/index.d.ts +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +12652 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cn.d.ts +2 -0
- package/dist/lib/utils.d.ts +1 -0
- package/dist/style.css +1 -0
- package/dist/theme/index.d.ts +2 -0
- package/dist/theme/theme.types.d.ts +1 -0
- package/dist/theme/tokens.d.ts +11 -0
- package/dist/tokens/colors.d.ts +35 -0
- package/dist/tokens/index.d.ts +8 -0
- package/dist/tokens/motion.d.ts +7 -0
- package/dist/tokens/radius.d.ts +8 -0
- package/dist/tokens/shadows.d.ts +6 -0
- package/dist/tokens/sizes.d.ts +75 -0
- package/dist/tokens/spacing.d.ts +12 -0
- package/dist/tokens/typography.d.ts +12 -0
- package/dist/tokens/zIndex.d.ts +9 -0
- package/package.json +94 -0
- package/scripts/postinstall.cjs +76 -0
|
@@ -0,0 +1,1236 @@
|
|
|
1
|
+
# @bfrs/agentic-components — Agent Context
|
|
2
|
+
|
|
3
|
+
This file is auto-generated by the `@bfrs/agentic-components` package on install.
|
|
4
|
+
It gives AI coding agents full knowledge of the component library so they can write
|
|
5
|
+
correct, idiomatic UI code without guessing.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Configure npm auth in the consuming app:
|
|
12
|
+
|
|
13
|
+
```ini
|
|
14
|
+
registry=https://registry.npmjs.org/
|
|
15
|
+
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @bfrs/agentic-components
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
Import the stylesheet once at your app entry point:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import "@bfrs/agentic-components/styles";
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Import components by name:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { Button, Input, Modal, DataTable, DateRangePicker, ToastProvider, useToast } from "@bfrs/agentic-components";
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Peer dependencies required:** `react >= 18.2.0`, `react-dom >= 18.2.0`
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Design System Conventions
|
|
41
|
+
|
|
42
|
+
- **Sizes** are always `"sm" | "md" | "lg"` — default is `"md"` unless noted
|
|
43
|
+
- **Tones** are always `"neutral" | "primary" | "success" | "warning" | "danger" | "info"`
|
|
44
|
+
- **Variants** control visual style (filled vs. outline vs. ghost, etc.)
|
|
45
|
+
- All interactive components forward refs and spread native HTML props
|
|
46
|
+
- Styling uses **Tailwind CSS** + **CVA** — do not add raw Tailwind classes on top of variant-controlled elements
|
|
47
|
+
- Use the `cn()` utility from the library for conditional class merging
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import { cn } from "@bfrs/agentic-components";
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Components
|
|
56
|
+
|
|
57
|
+
### Primitives
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
#### `Box`
|
|
62
|
+
|
|
63
|
+
Generic polymorphic wrapper. Use when you need a semantic HTML element with class merging.
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
<Box as="section" className="my-4">…</Box>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Prop | Type | Default |
|
|
70
|
+
|------|------|---------|
|
|
71
|
+
| `as` | `ElementType` | `"div"` |
|
|
72
|
+
| `className` | `string` | — |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
#### `Container`
|
|
77
|
+
|
|
78
|
+
Responsive max-width wrapper with horizontal padding.
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
<Container size="md">…</Container>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Prop | Type | Default |
|
|
85
|
+
|------|------|---------|
|
|
86
|
+
| `size` | `"sm" \| "md" \| "lg" \| "full"` | `"lg"` |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
#### `Paper`
|
|
91
|
+
|
|
92
|
+
Card-like surface. Use for panels, cards, and content sections.
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<Paper variant="elevated" padding="lg">…</Paper>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
| Prop | Type | Default |
|
|
99
|
+
|------|------|---------|
|
|
100
|
+
| `variant` | `"default" \| "muted" \| "elevated" \| "outlined" \| "glass"` | `"default"` |
|
|
101
|
+
| `padding` | `"none" \| "sm" \| "md" \| "lg"` | `"md"` |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
#### `Stack`
|
|
106
|
+
|
|
107
|
+
Flexbox layout. Use instead of writing flex utility classes directly.
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
<Stack direction="row" gap={4} align="center">
|
|
111
|
+
<Button>Save</Button>
|
|
112
|
+
<Button variant="ghost">Cancel</Button>
|
|
113
|
+
</Stack>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
| Prop | Type | Default |
|
|
117
|
+
|------|------|---------|
|
|
118
|
+
| `direction` | `"row" \| "column"` | `"column"` |
|
|
119
|
+
| `gap` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 8 \| 10 \| 12` | `4` |
|
|
120
|
+
| `align` | `CSSProperties["alignItems"]` | — |
|
|
121
|
+
| `justify` | `CSSProperties["justifyContent"]` | — |
|
|
122
|
+
| `wrap` | `boolean` | `false` |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
#### `Grid`
|
|
127
|
+
|
|
128
|
+
Responsive CSS grid layout.
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
<Grid columns={3} gap="md">
|
|
132
|
+
<Card />
|
|
133
|
+
<Card />
|
|
134
|
+
<Card />
|
|
135
|
+
</Grid>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
| Prop | Type | Default |
|
|
139
|
+
|------|------|---------|
|
|
140
|
+
| `columns` | `1 \| 2 \| 3 \| 4` | `3` |
|
|
141
|
+
| `gap` | `"sm" \| "md" \| "lg"` | `"md"` |
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
#### `Text`
|
|
146
|
+
|
|
147
|
+
Semantic typography. Always use this instead of raw `<p>`, `<h1>`, `<span>` for text.
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
<Text variant="h2">Welcome back</Text>
|
|
151
|
+
<Text variant="body" tone="muted">Last seen 2 hours ago</Text>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
| Prop | Type | Default |
|
|
155
|
+
|------|------|---------|
|
|
156
|
+
| `variant` | `"display" \| "h1" \| "h2" \| "h3" \| "title" \| "body" \| "body-sm" \| "caption" \| "label"` | `"body"` |
|
|
157
|
+
| `tone` | `"primary" \| "secondary" \| "muted" \| "disabled" \| "danger" \| "success" \| "accent"` | `"primary"` |
|
|
158
|
+
| `as` | `ElementType` | inferred from variant |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
#### `Icon`
|
|
163
|
+
|
|
164
|
+
Phosphor icon wrapper with consistent sizing and accessibility.
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { MagnifyingGlass } from "@phosphor-icons/react";
|
|
168
|
+
|
|
169
|
+
<Icon icon={MagnifyingGlass} size="md" weight="bold" />
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
| Prop | Type | Default |
|
|
173
|
+
|------|------|---------|
|
|
174
|
+
| `icon` | `PhosphorIconComponent` | **required** |
|
|
175
|
+
| `size` | `"sm" \| "md" \| "lg" \| number` | `"md"` |
|
|
176
|
+
| `weight` | `"thin" \| "light" \| "regular" \| "bold" \| "fill" \| "duotone"` | `"regular"` |
|
|
177
|
+
| `decorative` | `boolean` | `true` |
|
|
178
|
+
|
|
179
|
+
> When `decorative={false}`, add an `aria-label` to the wrapping element.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### Actions
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
#### `Button`
|
|
188
|
+
|
|
189
|
+
Primary interactive element. Covers all CTA patterns.
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
<Button variant="primary" size="md" onClick={handleSave}>
|
|
193
|
+
Save changes
|
|
194
|
+
</Button>
|
|
195
|
+
|
|
196
|
+
<Button variant="destructive" loading={isDeleting}>
|
|
197
|
+
Delete account
|
|
198
|
+
</Button>
|
|
199
|
+
|
|
200
|
+
<Button variant="outline" leftIcon={<Icon icon={Plus} />}>
|
|
201
|
+
Add item
|
|
202
|
+
</Button>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
| Prop | Type | Default |
|
|
206
|
+
|------|------|---------|
|
|
207
|
+
| `variant` | `"primary" \| "secondary" \| "outline" \| "ghost" \| "destructive" \| "link"` | `"primary"` |
|
|
208
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` |
|
|
209
|
+
| `fullWidth` | `boolean` | `false` |
|
|
210
|
+
| `leftIcon` | `ReactNode` | — |
|
|
211
|
+
| `rightIcon` | `ReactNode` | — |
|
|
212
|
+
| `loading` | `boolean` | `false` |
|
|
213
|
+
| `disabled` | `boolean` | `false` |
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
#### `IconButton`
|
|
218
|
+
|
|
219
|
+
Square button for icon-only actions. Always provide `aria-label`.
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
<IconButton
|
|
223
|
+
icon={<Icon icon={Trash} />}
|
|
224
|
+
variant="ghost"
|
|
225
|
+
size="sm"
|
|
226
|
+
aria-label="Delete item"
|
|
227
|
+
onClick={handleDelete}
|
|
228
|
+
/>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
| Prop | Type | Default |
|
|
232
|
+
|------|------|---------|
|
|
233
|
+
| `icon` | `ReactNode` | **required** |
|
|
234
|
+
| `aria-label` | `string` | **required** |
|
|
235
|
+
| `variant` | `"primary" \| "secondary" \| "outline" \| "ghost" \| "danger"` | `"ghost"` |
|
|
236
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` |
|
|
237
|
+
| `loading` | `boolean` | `false` |
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### Forms
|
|
242
|
+
|
|
243
|
+
> **Rule:** Always wrap form controls in `FormField` — it handles label, helper text, error text, and aria wiring automatically.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
#### `FormField`
|
|
248
|
+
|
|
249
|
+
Wraps any form control with label, helper, and error text. Connects them via `aria-describedby`.
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
<FormField
|
|
253
|
+
label="Email address"
|
|
254
|
+
required
|
|
255
|
+
helperText="We'll never share your email."
|
|
256
|
+
errorText={errors.email}
|
|
257
|
+
>
|
|
258
|
+
<Input type="email" value={email} onChange={…} />
|
|
259
|
+
</FormField>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
| Prop | Type | Default |
|
|
263
|
+
|------|------|---------|
|
|
264
|
+
| `label` | `ReactNode` | — |
|
|
265
|
+
| `required` | `boolean` | `false` |
|
|
266
|
+
| `helperText` | `ReactNode` | — |
|
|
267
|
+
| `errorText` | `ReactNode` | — |
|
|
268
|
+
| `disabled` | `boolean` | `false` |
|
|
269
|
+
| `children` | `ReactElement` | **required** |
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
#### `Label`
|
|
274
|
+
|
|
275
|
+
Standalone label. Prefer `FormField` which renders this automatically.
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
<Label required>Business name</Label>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
| Prop | Type | Default |
|
|
282
|
+
|------|------|---------|
|
|
283
|
+
| `required` | `boolean` | `false` |
|
|
284
|
+
| `disabled` | `boolean` | `false` |
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
#### `Input`
|
|
289
|
+
|
|
290
|
+
Standard text input.
|
|
291
|
+
|
|
292
|
+
```tsx
|
|
293
|
+
<Input
|
|
294
|
+
placeholder="Search…"
|
|
295
|
+
size="md"
|
|
296
|
+
leftIcon={<Icon icon={MagnifyingGlass} />}
|
|
297
|
+
/>
|
|
298
|
+
|
|
299
|
+
<Input value={val} state="error" onChange={…} />
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
| Prop | Type | Default |
|
|
303
|
+
|------|------|---------|
|
|
304
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` |
|
|
305
|
+
| `state` | `"default" \| "error"` | `"default"` |
|
|
306
|
+
| `error` | `boolean` | `false` |
|
|
307
|
+
| `leftIcon` | `ReactNode` | — |
|
|
308
|
+
| `rightIcon` | `ReactNode` | — |
|
|
309
|
+
|
|
310
|
+
Accepts all native `<input>` props.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
#### `Textarea`
|
|
315
|
+
|
|
316
|
+
Multi-line text input.
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
<Textarea
|
|
320
|
+
rows={6}
|
|
321
|
+
placeholder="Describe your issue…"
|
|
322
|
+
error={!!errors.description}
|
|
323
|
+
/>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
| Prop | Type | Default |
|
|
327
|
+
|------|------|---------|
|
|
328
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` |
|
|
329
|
+
| `error` | `boolean` | `false` |
|
|
330
|
+
| `rows` | `number` | `4` |
|
|
331
|
+
|
|
332
|
+
Accepts all native `<textarea>` props.
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
#### `Select`
|
|
337
|
+
|
|
338
|
+
Single-value dropdown. Options are `{ value: string; label: string }`.
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
<Select
|
|
342
|
+
options={[
|
|
343
|
+
{ value: "in", label: "India" },
|
|
344
|
+
{ value: "us", label: "United States" },
|
|
345
|
+
]}
|
|
346
|
+
value={country}
|
|
347
|
+
onValueChange={setCountry}
|
|
348
|
+
placeholder="Select country"
|
|
349
|
+
/>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
| Prop | Type | Default |
|
|
353
|
+
|------|------|---------|
|
|
354
|
+
| `options` | `{ value: string; label: string }[]` | **required** |
|
|
355
|
+
| `value` | `string` | — |
|
|
356
|
+
| `onValueChange` | `(value: string) => void` | — |
|
|
357
|
+
| `placeholder` | `string` | `"Select"` |
|
|
358
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` |
|
|
359
|
+
| `disabled` | `boolean` | `false` |
|
|
360
|
+
| `error` | `boolean` | `false` |
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
#### `DateRangePicker`
|
|
365
|
+
|
|
366
|
+
Preset and custom calendar date-range filter. Presets commit immediately; custom ranges use Apply/Cancel.
|
|
367
|
+
|
|
368
|
+
```tsx
|
|
369
|
+
import { DateRangePicker, presetToRange, type DateRangeValue } from "@bfrs/agentic-components";
|
|
370
|
+
|
|
371
|
+
const [range, setRange] = useState<DateRangeValue>({
|
|
372
|
+
preset: "Last 30 days",
|
|
373
|
+
...presetToRange("Last 30 days")
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
<DateRangePicker value={range} onChange={setRange} maxRangeDays={365} />;
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
| Prop | Type | Default |
|
|
380
|
+
|------|------|---------|
|
|
381
|
+
| `value` | `DateRangeValue` | — |
|
|
382
|
+
| `onChange` | `(value: DateRangeValue) => void` | — |
|
|
383
|
+
| `presets` | `DateRangePreset[]` | default presets |
|
|
384
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"sm"` |
|
|
385
|
+
| `maxRangeDays` | `number` | — |
|
|
386
|
+
| `months` | `1 \| 2` | `2` |
|
|
387
|
+
| `open` / `onOpenChange` | controlled popover state | — |
|
|
388
|
+
|
|
389
|
+
Keep API date formatting, route updates, and business limits in the consuming app.
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
#### `SearchableSelect`
|
|
394
|
+
|
|
395
|
+
Combobox with search. Use when options list is long (10+).
|
|
396
|
+
|
|
397
|
+
```tsx
|
|
398
|
+
<SearchableSelect
|
|
399
|
+
options={cities}
|
|
400
|
+
value={city}
|
|
401
|
+
onValueChange={setCity}
|
|
402
|
+
placeholder="Select city"
|
|
403
|
+
searchPlaceholder="Search cities…"
|
|
404
|
+
clearable
|
|
405
|
+
/>
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
| Prop | Type | Default |
|
|
409
|
+
|------|------|---------|
|
|
410
|
+
| `options` | `{ value: string; label: string }[]` | **required** |
|
|
411
|
+
| `value` | `string` | — |
|
|
412
|
+
| `onValueChange` | `(value: string \| undefined) => void` | — |
|
|
413
|
+
| `placeholder` | `string` | `"Select"` |
|
|
414
|
+
| `searchPlaceholder` | `string` | `"Search..."` |
|
|
415
|
+
| `emptyText` | `string` | `"No results found"` |
|
|
416
|
+
| `loading` | `boolean` | `false` |
|
|
417
|
+
| `disabled` | `boolean` | `false` |
|
|
418
|
+
| `error` | `boolean` | `false` |
|
|
419
|
+
| `clearable` | `boolean` | `true` |
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
#### `Checkbox`
|
|
424
|
+
|
|
425
|
+
Square checklist control for independent true/false decisions or multi-select choices.
|
|
426
|
+
Use `Radio` instead when exactly one option in a group can be selected.
|
|
427
|
+
|
|
428
|
+
```tsx
|
|
429
|
+
<Checkbox
|
|
430
|
+
checked={agreed}
|
|
431
|
+
onCheckedChange={setAgreed}
|
|
432
|
+
label="Accept terms"
|
|
433
|
+
description="Use checkboxes when each option can be selected independently."
|
|
434
|
+
/>
|
|
435
|
+
|
|
436
|
+
<Checkbox label="Partially selected" checked="indeterminate" />
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
| Prop | Type | Default |
|
|
440
|
+
|------|------|---------|
|
|
441
|
+
| `label` | `ReactNode` | — |
|
|
442
|
+
| `description` | `ReactNode` | — |
|
|
443
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` |
|
|
444
|
+
| `error` | `boolean` | `false` |
|
|
445
|
+
| `checked` | `boolean \| "indeterminate"` | — |
|
|
446
|
+
| `onCheckedChange` | `(checked: boolean \| "indeterminate") => void` | — |
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
#### `Radio`
|
|
451
|
+
|
|
452
|
+
Radio group. Renders a list of mutually exclusive options.
|
|
453
|
+
|
|
454
|
+
```tsx
|
|
455
|
+
<Radio
|
|
456
|
+
value={plan}
|
|
457
|
+
onValueChange={setPlan}
|
|
458
|
+
options={[
|
|
459
|
+
{ value: "free", label: "Free" },
|
|
460
|
+
{ value: "pro", label: "Pro" },
|
|
461
|
+
{ value: "enterprise", label: "Enterprise" },
|
|
462
|
+
]}
|
|
463
|
+
/>
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
| Prop | Type | Default |
|
|
467
|
+
|------|------|---------|
|
|
468
|
+
| `options` | `{ value: string; label: string }[]` | **required** |
|
|
469
|
+
| `value` | `string` | — |
|
|
470
|
+
| `onValueChange` | `(value: string) => void` | — |
|
|
471
|
+
| `error` | `boolean` | `false` |
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
#### `Switch`
|
|
476
|
+
|
|
477
|
+
Toggle switch with optional label.
|
|
478
|
+
|
|
479
|
+
```tsx
|
|
480
|
+
<Switch
|
|
481
|
+
checked={notifications}
|
|
482
|
+
onCheckedChange={setNotifications}
|
|
483
|
+
label="Email notifications"
|
|
484
|
+
/>
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
| Prop | Type | Default |
|
|
488
|
+
|------|------|---------|
|
|
489
|
+
| `label` | `string` | — |
|
|
490
|
+
| `checked` | `boolean` | — |
|
|
491
|
+
| `onCheckedChange` | `(checked: boolean) => void` | — |
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
### Feedback
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
#### `Badge`
|
|
500
|
+
|
|
501
|
+
Small inline status label.
|
|
502
|
+
|
|
503
|
+
```tsx
|
|
504
|
+
<Badge tone="success">Active</Badge>
|
|
505
|
+
<Badge tone="warning">Pending</Badge>
|
|
506
|
+
<Badge tone="danger">Blocked</Badge>
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
| Prop | Type | Default |
|
|
510
|
+
|------|------|---------|
|
|
511
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"sm"` |
|
|
512
|
+
| `tone` | `"neutral" \| "primary" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` |
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
#### `Chip`
|
|
517
|
+
|
|
518
|
+
Removable tag. Use in filter bars and multi-select UIs.
|
|
519
|
+
|
|
520
|
+
```tsx
|
|
521
|
+
<Chip
|
|
522
|
+
tone="primary"
|
|
523
|
+
variant="soft"
|
|
524
|
+
leftIcon={<Icon icon={Tag} />}
|
|
525
|
+
onRemove={() => removeFilter("status")}
|
|
526
|
+
>
|
|
527
|
+
Status: Active
|
|
528
|
+
</Chip>
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
| Prop | Type | Default |
|
|
532
|
+
|------|------|---------|
|
|
533
|
+
| `variant` | `"filled" \| "soft" \| "outline"` | `"soft"` |
|
|
534
|
+
| `tone` | `"neutral" \| "primary" \| "success" \| "warning" \| "danger"` | `"neutral"` |
|
|
535
|
+
| `leftIcon` | `ReactNode` | — |
|
|
536
|
+
| `onRemove` | `() => void` | — |
|
|
537
|
+
| `removeLabel` | `string` | `"Remove"` |
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
#### `Alert`
|
|
542
|
+
|
|
543
|
+
Contextual message banner. Auto-selects an icon based on tone.
|
|
544
|
+
|
|
545
|
+
```tsx
|
|
546
|
+
<Alert
|
|
547
|
+
tone="warning"
|
|
548
|
+
title="Your trial ends in 3 days"
|
|
549
|
+
action={<Button size="sm">Upgrade now</Button>}
|
|
550
|
+
>
|
|
551
|
+
Upgrade to keep access to all features.
|
|
552
|
+
</Alert>
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
| Prop | Type | Default |
|
|
556
|
+
|------|------|---------|
|
|
557
|
+
| `tone` | `"info" \| "success" \| "warning" \| "danger"` | `"info"` |
|
|
558
|
+
| `title` | `ReactNode` | — |
|
|
559
|
+
| `action` | `ReactNode` | — |
|
|
560
|
+
| `icon` | `ReactNode` | auto (from tone) |
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
|
|
564
|
+
#### `ToastProvider`, `useToast`, `ToastManager`
|
|
565
|
+
|
|
566
|
+
App-wide transient feedback manager. Wrap the app once, then call toast helpers from event handlers.
|
|
567
|
+
|
|
568
|
+
```tsx
|
|
569
|
+
function SaveButton() {
|
|
570
|
+
const { success, danger } = useToast();
|
|
571
|
+
|
|
572
|
+
async function save() {
|
|
573
|
+
try {
|
|
574
|
+
await saveRequest();
|
|
575
|
+
success("Changes saved");
|
|
576
|
+
} catch {
|
|
577
|
+
danger({ title: "Save failed", description: "Try again in a moment." });
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return <Button onClick={save}>Save</Button>;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
<ToastProvider placement="bottom-center">
|
|
585
|
+
<SaveButton />
|
|
586
|
+
</ToastProvider>;
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
| Prop / API | Type | Default |
|
|
590
|
+
|------|------|---------|
|
|
591
|
+
| `placement` | `"top-left" \| "top-center" \| "top-right" \| "bottom-left" \| "bottom-center" \| "bottom-right"` | `"bottom-center"` |
|
|
592
|
+
| `maxToasts` | `number` | `4` |
|
|
593
|
+
| `defaultDuration` | `number` | `2600` |
|
|
594
|
+
| `toast` | `(input: ToastInput) => string` | — |
|
|
595
|
+
| `success` / `warning` / `danger` / `info` | semantic helper functions | — |
|
|
596
|
+
| `dismiss` / `clear` | manager controls | — |
|
|
597
|
+
|
|
598
|
+
Do not put API calls, route logic, or app stores inside the library toast manager.
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
#### `Spinner`
|
|
603
|
+
|
|
604
|
+
Loading indicator. Use inside buttons, inline loaders, or page sections.
|
|
605
|
+
|
|
606
|
+
```tsx
|
|
607
|
+
<Spinner size="md" label="Fetching orders…" />
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
| Prop | Type | Default |
|
|
611
|
+
|------|------|---------|
|
|
612
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` |
|
|
613
|
+
| `label` | `string` | `"Loading"` |
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
#### `Skeleton`
|
|
618
|
+
|
|
619
|
+
Shimmer placeholder while content loads. Match the variant to the shape of the real content.
|
|
620
|
+
|
|
621
|
+
```tsx
|
|
622
|
+
<Skeleton variant="text" /> // single line
|
|
623
|
+
<Skeleton variant="card" /> // card-height block
|
|
624
|
+
<Skeleton variant="avatar" /> // circle
|
|
625
|
+
<Skeleton variant="block" /> // rectangular block
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
| Prop | Type | Default |
|
|
629
|
+
|------|------|---------|
|
|
630
|
+
| `variant` | `"text" \| "block" \| "card" \| "avatar"` | `"block"` |
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
#### `EmptyState`
|
|
635
|
+
|
|
636
|
+
Centered empty state for lists, tables, and search results.
|
|
637
|
+
|
|
638
|
+
```tsx
|
|
639
|
+
<EmptyState
|
|
640
|
+
icon={<Icon icon={Package} size="lg" />}
|
|
641
|
+
title="No orders yet"
|
|
642
|
+
description="Orders placed by your customers will appear here."
|
|
643
|
+
action={<Button>Create order</Button>}
|
|
644
|
+
/>
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
| Prop | Type | Default |
|
|
648
|
+
|------|------|---------|
|
|
649
|
+
| `title` | `ReactNode` | **required** |
|
|
650
|
+
| `icon` | `ReactNode` | — |
|
|
651
|
+
| `description` | `ReactNode` | — |
|
|
652
|
+
| `action` | `ReactNode` | — |
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
### Overlays
|
|
657
|
+
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
#### `Modal`
|
|
661
|
+
|
|
662
|
+
Centered modal. Use for confirmations, forms, and focused tasks.
|
|
663
|
+
|
|
664
|
+
```tsx
|
|
665
|
+
<Modal
|
|
666
|
+
trigger={<Button>Delete account</Button>}
|
|
667
|
+
title="Are you sure?"
|
|
668
|
+
description="This action cannot be undone."
|
|
669
|
+
footer={
|
|
670
|
+
<Stack direction="row" gap={2} justify="flex-end">
|
|
671
|
+
<Button variant="ghost">Cancel</Button>
|
|
672
|
+
<Button variant="destructive" onClick={handleDelete}>Delete</Button>
|
|
673
|
+
</Stack>
|
|
674
|
+
}
|
|
675
|
+
>
|
|
676
|
+
All your data will be permanently removed.
|
|
677
|
+
</Modal>
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
| Prop | Type | Default |
|
|
681
|
+
|------|------|---------|
|
|
682
|
+
| `title` | `ReactNode` | **required** |
|
|
683
|
+
| `children` | `ReactNode` | **required** |
|
|
684
|
+
| `trigger` | `ReactNode` | — |
|
|
685
|
+
| `description` | `ReactNode` | — |
|
|
686
|
+
| `footer` | `ReactNode` | — |
|
|
687
|
+
| `size` | `"sm" \| "md" \| "lg" \| "xl" \| "full"` | `"md"` |
|
|
688
|
+
| `open` | `boolean` | — |
|
|
689
|
+
| `onOpenChange` | `(open: boolean) => void` | — |
|
|
690
|
+
|
|
691
|
+
`Dialog` is a deprecated compatibility alias. Use `Modal` in new code.
|
|
692
|
+
|
|
693
|
+
---
|
|
694
|
+
|
|
695
|
+
#### `Drawer`
|
|
696
|
+
|
|
697
|
+
Slide-out panel. Use for detail views, settings, and secondary flows.
|
|
698
|
+
|
|
699
|
+
```tsx
|
|
700
|
+
<Drawer
|
|
701
|
+
trigger={<Button variant="outline">View details</Button>}
|
|
702
|
+
title="Order #1234"
|
|
703
|
+
placement="right"
|
|
704
|
+
size="xl"
|
|
705
|
+
>
|
|
706
|
+
<OrderDetail id="1234" />
|
|
707
|
+
</Drawer>
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
| Prop | Type | Default |
|
|
711
|
+
|------|------|---------|
|
|
712
|
+
| `title` | `ReactNode` | **required** |
|
|
713
|
+
| `children` | `ReactNode` | **required** |
|
|
714
|
+
| `placement` | `"left" \| "right" \| "bottom"` | `"right"` |
|
|
715
|
+
| `size` | `"sm" \| "md" \| "lg" \| "xl"` | `"md"` |
|
|
716
|
+
| `trigger` | `ReactNode` | — |
|
|
717
|
+
| `description` | `ReactNode` | — |
|
|
718
|
+
| `open` | `boolean` | — |
|
|
719
|
+
| `onOpenChange` | `(open: boolean) => void` | — |
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
#### `Dropdown`
|
|
724
|
+
|
|
725
|
+
Context menu attached to a trigger. Items support destructive and checked states.
|
|
726
|
+
|
|
727
|
+
```tsx
|
|
728
|
+
<Dropdown
|
|
729
|
+
trigger={<IconButton icon={<Icon icon={DotsThree} />} aria-label="More actions" />}
|
|
730
|
+
align="end"
|
|
731
|
+
items={[
|
|
732
|
+
{ label: "Edit", onSelect: handleEdit },
|
|
733
|
+
{ label: "Duplicate", onSelect: handleDuplicate },
|
|
734
|
+
{ label: "Delete", onSelect: handleDelete, destructive: true },
|
|
735
|
+
]}
|
|
736
|
+
/>
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
| Prop | Type | Default |
|
|
740
|
+
|------|------|---------|
|
|
741
|
+
| `trigger` | `ReactNode` | **required** |
|
|
742
|
+
| `items` | `{ label: string; onSelect: () => void; disabled?: boolean; destructive?: boolean; checked?: boolean }[]` | **required** |
|
|
743
|
+
| `align` | `"start" \| "center" \| "end"` | `"end"` |
|
|
744
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` |
|
|
745
|
+
|
|
746
|
+
---
|
|
747
|
+
|
|
748
|
+
#### `Tooltip`
|
|
749
|
+
|
|
750
|
+
Floating label on hover. Provide `content` as the tooltip text.
|
|
751
|
+
|
|
752
|
+
```tsx
|
|
753
|
+
<Tooltip content="Copy to clipboard" side="top">
|
|
754
|
+
<IconButton icon={<Icon icon={Copy} />} aria-label="Copy" />
|
|
755
|
+
</Tooltip>
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
| Prop | Type | Default |
|
|
759
|
+
|------|------|---------|
|
|
760
|
+
| `content` | `ReactNode` | **required** |
|
|
761
|
+
| `children` | `ReactNode` | **required** |
|
|
762
|
+
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `"top"` |
|
|
763
|
+
| `delayDuration` | `number` | `350` |
|
|
764
|
+
|
|
765
|
+
---
|
|
766
|
+
|
|
767
|
+
#### `Popover`
|
|
768
|
+
|
|
769
|
+
Floating content panel. Use for filters, pickers, and inline forms.
|
|
770
|
+
|
|
771
|
+
```tsx
|
|
772
|
+
<Popover
|
|
773
|
+
trigger={<Button variant="outline">Filter</Button>}
|
|
774
|
+
align="start"
|
|
775
|
+
>
|
|
776
|
+
<FilterForm />
|
|
777
|
+
</Popover>
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
| Prop | Type | Default |
|
|
781
|
+
|------|------|---------|
|
|
782
|
+
| `trigger` | `ReactNode` | **required** |
|
|
783
|
+
| `children` | `ReactNode` | **required** |
|
|
784
|
+
| `align` | `"start" \| "center" \| "end"` | `"center"` |
|
|
785
|
+
|
|
786
|
+
---
|
|
787
|
+
|
|
788
|
+
### Data Display
|
|
789
|
+
|
|
790
|
+
---
|
|
791
|
+
|
|
792
|
+
#### `Avatar`
|
|
793
|
+
|
|
794
|
+
User avatar with image, initials fallback, and online status.
|
|
795
|
+
|
|
796
|
+
```tsx
|
|
797
|
+
<Avatar
|
|
798
|
+
src={user.avatarUrl}
|
|
799
|
+
name={user.fullName}
|
|
800
|
+
size="md"
|
|
801
|
+
status="online"
|
|
802
|
+
/>
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
| Prop | Type | Default |
|
|
806
|
+
|------|------|---------|
|
|
807
|
+
| `src` | `string` | — |
|
|
808
|
+
| `alt` | `string` | — |
|
|
809
|
+
| `name` | `string` | — |
|
|
810
|
+
| `size` | `"sm" \| "md" \| "lg"` | `"md"` |
|
|
811
|
+
| `status` | `"online" \| "busy" \| "offline"` | — |
|
|
812
|
+
|
|
813
|
+
> When `src` is absent, renders initials derived from `name`.
|
|
814
|
+
|
|
815
|
+
---
|
|
816
|
+
|
|
817
|
+
#### `TablePagination`
|
|
818
|
+
|
|
819
|
+
Table page navigation. Handles ellipsis, edge pages, and optional page-size controls.
|
|
820
|
+
|
|
821
|
+
```tsx
|
|
822
|
+
<TablePagination
|
|
823
|
+
page={currentPage}
|
|
824
|
+
totalPages={totalPages}
|
|
825
|
+
pageSize={pageSize}
|
|
826
|
+
onPageChange={setCurrentPage}
|
|
827
|
+
onPageSizeChange={setPageSize}
|
|
828
|
+
/>
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
| Prop | Type | Default |
|
|
832
|
+
|------|------|---------|
|
|
833
|
+
| `page` | `number` | **required** |
|
|
834
|
+
| `totalPages` | `number` | **required** |
|
|
835
|
+
| `onPageChange` | `(page: number) => void` | **required** |
|
|
836
|
+
| `pageSize` | `number` | — |
|
|
837
|
+
| `onPageSizeChange` | `(pageSize: number) => void` | — |
|
|
838
|
+
|
|
839
|
+
---
|
|
840
|
+
|
|
841
|
+
#### `DataTable`
|
|
842
|
+
|
|
843
|
+
Generic data table with sorting, loading skeleton, row actions, selection, pagination, and empty/error states.
|
|
844
|
+
|
|
845
|
+
```tsx
|
|
846
|
+
<DataTable
|
|
847
|
+
columns={[
|
|
848
|
+
{ id: "name", header: "Name", accessorKey: "name", sortable: true },
|
|
849
|
+
{ id: "status", header: "Status", cell: (row) => <Badge tone={row.status === "active" ? "success" : "neutral"}>{row.status}</Badge> },
|
|
850
|
+
]}
|
|
851
|
+
data={orders}
|
|
852
|
+
loading={isLoading}
|
|
853
|
+
sorting={sorting}
|
|
854
|
+
onSortingChange={setSorting}
|
|
855
|
+
onRowClick={(row) => navigate(`/orders/${row.id}`)}
|
|
856
|
+
rowActions={(row) => (
|
|
857
|
+
<ActionMenu trigger={…} items={[…]} />
|
|
858
|
+
)}
|
|
859
|
+
emptyTitle="No orders found"
|
|
860
|
+
emptyDescription="Try adjusting your filters."
|
|
861
|
+
/>
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
| Prop | Type | Default |
|
|
865
|
+
|------|------|---------|
|
|
866
|
+
| `columns` | `DataTableColumn<T>[]` | **required** |
|
|
867
|
+
| `data` | `T[]` | **required** |
|
|
868
|
+
| `loading` | `boolean` | `false` |
|
|
869
|
+
| `enableSorting` | `boolean` | `true` |
|
|
870
|
+
| `sorting` | `{ columnId: string; direction: "asc" \| "desc" }` | — |
|
|
871
|
+
| `onSortingChange` | `(sorting) => void` | — |
|
|
872
|
+
| `enableColumnPinning` | `boolean` | `true` |
|
|
873
|
+
| `enableColumnReordering` | `boolean` | `true` |
|
|
874
|
+
| `onRowClick` | `(row: T) => void` | — |
|
|
875
|
+
| `rowActions` | `(row: T) => ReactNode` | — |
|
|
876
|
+
| `selection` | `boolean` | `false` |
|
|
877
|
+
| `density` | `"compact" \| "default" \| "comfortable"` | `"default"` |
|
|
878
|
+
| `emptyTitle` | `ReactNode` | `"No records found"` |
|
|
879
|
+
| `emptyDescription` | `ReactNode` | — |
|
|
880
|
+
|
|
881
|
+
`DataTableColumn<T>`:
|
|
882
|
+
```ts
|
|
883
|
+
{
|
|
884
|
+
id: string;
|
|
885
|
+
header: ReactNode;
|
|
886
|
+
accessorKey?: keyof T;
|
|
887
|
+
accessorFn?: (row: T) => unknown;
|
|
888
|
+
sortable?: boolean;
|
|
889
|
+
cell?: (row: T) => ReactNode;
|
|
890
|
+
}
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
`enableSorting`, `enableColumnPinning`, and `enableColumnReordering` are enabled by default. Pass `false` to disable each behavior for a simpler table.
|
|
894
|
+
|
|
895
|
+
When `loading` is true, `DataTable` renders built-in table-row skeleton placeholders rather than a spinner. The consumer app only owns the loading boolean.
|
|
896
|
+
|
|
897
|
+
---
|
|
898
|
+
|
|
899
|
+
### Navigation
|
|
900
|
+
|
|
901
|
+
---
|
|
902
|
+
|
|
903
|
+
#### `Tabs`
|
|
904
|
+
|
|
905
|
+
Tabbed content switcher.
|
|
906
|
+
|
|
907
|
+
```tsx
|
|
908
|
+
<Tabs
|
|
909
|
+
variant="line"
|
|
910
|
+
defaultValue="overview"
|
|
911
|
+
items={[
|
|
912
|
+
{ value: "overview", label: "Overview", content: <Overview /> },
|
|
913
|
+
{ value: "orders", label: "Orders", content: <Orders /> },
|
|
914
|
+
{ value: "settings", label: "Settings", content: <Settings /> },
|
|
915
|
+
]}
|
|
916
|
+
/>
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
| Prop | Type | Default |
|
|
920
|
+
|------|------|---------|
|
|
921
|
+
| `items` | `{ value: string; label: ReactNode; content: ReactNode }[]` | **required** |
|
|
922
|
+
| `variant` | `"line" \| "pill"` | `"line"` |
|
|
923
|
+
| `defaultValue` | `string` | — |
|
|
924
|
+
| `value` | `string` | — |
|
|
925
|
+
| `onValueChange` | `(value: string) => void` | — |
|
|
926
|
+
|
|
927
|
+
---
|
|
928
|
+
|
|
929
|
+
#### `Breadcrumbs`
|
|
930
|
+
|
|
931
|
+
Navigation trail. Last item is rendered as plain text (current page).
|
|
932
|
+
|
|
933
|
+
```tsx
|
|
934
|
+
<Breadcrumbs
|
|
935
|
+
items={[
|
|
936
|
+
{ label: "Home", href: "/" },
|
|
937
|
+
{ label: "Orders", href: "/orders" },
|
|
938
|
+
{ label: "Order #1234" },
|
|
939
|
+
]}
|
|
940
|
+
/>
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
| Prop | Type | Default |
|
|
944
|
+
|------|------|---------|
|
|
945
|
+
| `items` | `{ label: ReactNode; href?: string }[]` | **required** |
|
|
946
|
+
|
|
947
|
+
---
|
|
948
|
+
|
|
949
|
+
### Patterns
|
|
950
|
+
|
|
951
|
+
> Patterns are opinionated compositions for common full-page and feature-level layouts.
|
|
952
|
+
|
|
953
|
+
---
|
|
954
|
+
|
|
955
|
+
#### `PageHeader`
|
|
956
|
+
|
|
957
|
+
Standardized page title section. Use at the top of every page.
|
|
958
|
+
|
|
959
|
+
```tsx
|
|
960
|
+
<PageHeader
|
|
961
|
+
eyebrow="Orders"
|
|
962
|
+
title="All orders"
|
|
963
|
+
description="View and manage customer orders."
|
|
964
|
+
actions={<Button leftIcon={<Icon icon={Plus} />}>New order</Button>}
|
|
965
|
+
secondaryActions={<Button variant="outline">Export</Button>}
|
|
966
|
+
onBack={() => navigate(-1)}
|
|
967
|
+
/>
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
| Prop | Type | Default |
|
|
971
|
+
|------|------|---------|
|
|
972
|
+
| `title` | `ReactNode` | **required** |
|
|
973
|
+
| `eyebrow` | `ReactNode` | — |
|
|
974
|
+
| `description` | `ReactNode` | — |
|
|
975
|
+
| `onBack` | `() => void` | — |
|
|
976
|
+
| `actions` | `ReactNode` | — |
|
|
977
|
+
| `secondaryActions` | `ReactNode` | — |
|
|
978
|
+
|
|
979
|
+
---
|
|
980
|
+
|
|
981
|
+
#### `LayoutShell`
|
|
982
|
+
|
|
983
|
+
Full-page app shell with sticky header and collapsible sidebar.
|
|
984
|
+
|
|
985
|
+
```tsx
|
|
986
|
+
<LayoutShell
|
|
987
|
+
header={<AppHeader />}
|
|
988
|
+
sidebar={<SideNav />}
|
|
989
|
+
>
|
|
990
|
+
<Outlet />
|
|
991
|
+
</LayoutShell>
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
| Prop | Type | Default |
|
|
995
|
+
|------|------|---------|
|
|
996
|
+
| `children` | `ReactNode` | **required** |
|
|
997
|
+
| `header` | `ReactNode` | — |
|
|
998
|
+
| `sidebar` | `ReactNode` | — |
|
|
999
|
+
|
|
1000
|
+
---
|
|
1001
|
+
|
|
1002
|
+
#### `ChatBubble`
|
|
1003
|
+
|
|
1004
|
+
Chat message bubble. Renders differently for `user`, `assistant`, and `system` senders.
|
|
1005
|
+
|
|
1006
|
+
```tsx
|
|
1007
|
+
<ChatBubble sender="assistant" timestamp="2:34 PM" loading={isStreaming}>
|
|
1008
|
+
Here's a summary of your recent orders…
|
|
1009
|
+
</ChatBubble>
|
|
1010
|
+
|
|
1011
|
+
<ChatBubble sender="user" timestamp="2:35 PM">
|
|
1012
|
+
Can you filter by last 7 days?
|
|
1013
|
+
</ChatBubble>
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
| Prop | Type | Default |
|
|
1017
|
+
|------|------|---------|
|
|
1018
|
+
| `sender` | `"user" \| "assistant" \| "system"` | **required** |
|
|
1019
|
+
| `children` | `ReactNode` | **required** |
|
|
1020
|
+
| `timestamp` | `ReactNode` | — |
|
|
1021
|
+
| `status` | `ReactNode` | — |
|
|
1022
|
+
| `loading` | `boolean` | `false` |
|
|
1023
|
+
| `errorAction` | `ReactNode` | — |
|
|
1024
|
+
|
|
1025
|
+
---
|
|
1026
|
+
|
|
1027
|
+
#### `ChatComposer`
|
|
1028
|
+
|
|
1029
|
+
Chat input with attachment and action slots. Submits on `Ctrl+Enter`.
|
|
1030
|
+
|
|
1031
|
+
```tsx
|
|
1032
|
+
<ChatComposer
|
|
1033
|
+
value={message}
|
|
1034
|
+
onValueChange={setMessage}
|
|
1035
|
+
onSubmit={handleSend}
|
|
1036
|
+
loading={isSending}
|
|
1037
|
+
placeholder="Ask me anything"
|
|
1038
|
+
attachmentSlot={<IconButton icon={<Icon icon={Paperclip} />} aria-label="Attach" />}
|
|
1039
|
+
/>
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
| Prop | Type | Default |
|
|
1043
|
+
|------|------|---------|
|
|
1044
|
+
| `value` | `string` | **required** |
|
|
1045
|
+
| `onValueChange` | `(value: string) => void` | **required** |
|
|
1046
|
+
| `onSubmit` | `() => void` | — |
|
|
1047
|
+
| `loading` | `boolean` | `false` |
|
|
1048
|
+
| `disabled` | `boolean` | `false` |
|
|
1049
|
+
| `placeholder` | `string` | `"Ask me anything"` |
|
|
1050
|
+
| `attachmentSlot` | `ReactNode` | — |
|
|
1051
|
+
| `actionSlot` | `ReactNode` | — |
|
|
1052
|
+
|
|
1053
|
+
---
|
|
1054
|
+
|
|
1055
|
+
#### `FullPageLoader`
|
|
1056
|
+
|
|
1057
|
+
Branded full-screen loading screen. Use during auth redirects and account setup.
|
|
1058
|
+
|
|
1059
|
+
```tsx
|
|
1060
|
+
<FullPageLoader
|
|
1061
|
+
titlePrefix="Setting up your "
|
|
1062
|
+
titleHighlight="workspace"
|
|
1063
|
+
titleSuffix="…"
|
|
1064
|
+
description="This takes just a moment."
|
|
1065
|
+
statusLabel="Configuring account"
|
|
1066
|
+
/>
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
| Prop | Type | Default |
|
|
1070
|
+
|------|------|---------|
|
|
1071
|
+
| `titlePrefix` | `ReactNode` | `"Your account is ready. I'll take you to your "` |
|
|
1072
|
+
| `titleHighlight` | `ReactNode` | `"dashboard"` |
|
|
1073
|
+
| `titleSuffix` | `ReactNode` | `" now."` |
|
|
1074
|
+
| `description` | `ReactNode` | `"Your account is being configured…"` |
|
|
1075
|
+
| `fullScreen` | `boolean` | `true` |
|
|
1076
|
+
| `statusLabel` | `string` | `"Loading account dashboard"` |
|
|
1077
|
+
|
|
1078
|
+
---
|
|
1079
|
+
|
|
1080
|
+
#### `BusinessInfoDisplayCard`
|
|
1081
|
+
|
|
1082
|
+
Rich business identity card with header, logo, chips, and info sections.
|
|
1083
|
+
|
|
1084
|
+
```tsx
|
|
1085
|
+
<BusinessInfoDisplayCard
|
|
1086
|
+
businessName="Acme Corp"
|
|
1087
|
+
initials="AC"
|
|
1088
|
+
chips={[{ label: "Verified", tone: "success" }]}
|
|
1089
|
+
sections={[
|
|
1090
|
+
{
|
|
1091
|
+
title: "Contact",
|
|
1092
|
+
items: [
|
|
1093
|
+
{ label: "Email", value: "hello@acme.com" },
|
|
1094
|
+
{ label: "Phone", value: "+91 98765 43210" },
|
|
1095
|
+
],
|
|
1096
|
+
},
|
|
1097
|
+
]}
|
|
1098
|
+
/>
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
| Prop | Type | Default |
|
|
1102
|
+
|------|------|---------|
|
|
1103
|
+
| `businessName` | `ReactNode` | **required** |
|
|
1104
|
+
| `sections` | `BusinessInfoSection[]` | **required** |
|
|
1105
|
+
| `initials` | `string` | — |
|
|
1106
|
+
| `logoSrc` | `string` | — |
|
|
1107
|
+
| `logoAlt` | `string` | — |
|
|
1108
|
+
| `chips` | `{ label: string; tone?: Tone }[]` | — |
|
|
1109
|
+
| `background` | `string` | — |
|
|
1110
|
+
| `gradient` | `string` | — |
|
|
1111
|
+
| `headerBackground` | `string` | — |
|
|
1112
|
+
| `headerGradient` | `string` | — |
|
|
1113
|
+
|
|
1114
|
+
`BusinessInfoSection`:
|
|
1115
|
+
```ts
|
|
1116
|
+
{ title: string; items: { label: string; value: ReactNode }[] }
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
---
|
|
1120
|
+
|
|
1121
|
+
#### `StepProgressCard`
|
|
1122
|
+
|
|
1123
|
+
Step-by-step progress indicator. Calculates `complete` / `current` / `upcoming` state automatically from `currentStep`.
|
|
1124
|
+
|
|
1125
|
+
```tsx
|
|
1126
|
+
<StepProgressCard
|
|
1127
|
+
eyebrow="Onboarding"
|
|
1128
|
+
title="Let's get you set up"
|
|
1129
|
+
currentStep={1}
|
|
1130
|
+
animated
|
|
1131
|
+
steps={[
|
|
1132
|
+
{ label: "Create account" },
|
|
1133
|
+
{ label: "Verify email" },
|
|
1134
|
+
{ label: "Add your first product" },
|
|
1135
|
+
]}
|
|
1136
|
+
/>
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
| Prop | Type | Default |
|
|
1140
|
+
|------|------|---------|
|
|
1141
|
+
| `steps` | `{ label: ReactNode; description?: ReactNode }[]` | **required** |
|
|
1142
|
+
| `currentStep` | `number` | `0` |
|
|
1143
|
+
| `eyebrow` | `ReactNode` | `"Account Summary"` |
|
|
1144
|
+
| `title` | `ReactNode` | `"Getting your details ready…"` |
|
|
1145
|
+
| `surface` | `"card" \| "plain"` | `"card"` |
|
|
1146
|
+
| `animated` | `boolean` | `true` |
|
|
1147
|
+
|
|
1148
|
+
---
|
|
1149
|
+
|
|
1150
|
+
## Common Patterns
|
|
1151
|
+
|
|
1152
|
+
### Form with validation
|
|
1153
|
+
|
|
1154
|
+
```tsx
|
|
1155
|
+
<Stack gap={4}>
|
|
1156
|
+
<FormField label="Full name" required errorText={errors.name}>
|
|
1157
|
+
<Input value={name} onChange={(e) => setName(e.target.value)} state={errors.name ? "error" : "default"} />
|
|
1158
|
+
</FormField>
|
|
1159
|
+
|
|
1160
|
+
<FormField label="Plan" required errorText={errors.plan}>
|
|
1161
|
+
<Select options={planOptions} value={plan} onValueChange={setPlan} error={!!errors.plan} />
|
|
1162
|
+
</FormField>
|
|
1163
|
+
|
|
1164
|
+
<Stack direction="row" gap={2} justify="flex-end">
|
|
1165
|
+
<Button variant="ghost">Cancel</Button>
|
|
1166
|
+
<Button loading={isSubmitting} onClick={handleSubmit}>Save</Button>
|
|
1167
|
+
</Stack>
|
|
1168
|
+
</Stack>
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
### Table page
|
|
1172
|
+
|
|
1173
|
+
```tsx
|
|
1174
|
+
<Container>
|
|
1175
|
+
<PageHeader
|
|
1176
|
+
title="Orders"
|
|
1177
|
+
actions={<Button leftIcon={<Icon icon={Plus} />}>New order</Button>}
|
|
1178
|
+
/>
|
|
1179
|
+
<Card>
|
|
1180
|
+
<DataTable
|
|
1181
|
+
columns={columns}
|
|
1182
|
+
data={orders}
|
|
1183
|
+
loading={isLoading}
|
|
1184
|
+
sorting={sorting}
|
|
1185
|
+
onSortingChange={setSorting}
|
|
1186
|
+
pagination={{ page, totalPages, onPageChange: setPage }}
|
|
1187
|
+
/>
|
|
1188
|
+
</Card>
|
|
1189
|
+
</Container>
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
### Confirm delete dialog
|
|
1193
|
+
|
|
1194
|
+
```tsx
|
|
1195
|
+
<ConfirmDialog
|
|
1196
|
+
open={showConfirm}
|
|
1197
|
+
onOpenChange={setShowConfirm}
|
|
1198
|
+
destructive
|
|
1199
|
+
title="Delete order?"
|
|
1200
|
+
description="This cannot be undone."
|
|
1201
|
+
confirmLabel="Delete"
|
|
1202
|
+
loading={isDeleting}
|
|
1203
|
+
onConfirm={handleDelete}
|
|
1204
|
+
/>
|
|
1205
|
+
```
|
|
1206
|
+
|
|
1207
|
+
### Chat interface
|
|
1208
|
+
|
|
1209
|
+
```tsx
|
|
1210
|
+
<LayoutShell header={<AppHeader />}>
|
|
1211
|
+
<Stack gap={0}>
|
|
1212
|
+
{messages.map((msg) => (
|
|
1213
|
+
<ChatBubble key={msg.id} sender={msg.sender} timestamp={msg.time}>
|
|
1214
|
+
{msg.text}
|
|
1215
|
+
</ChatBubble>
|
|
1216
|
+
))}
|
|
1217
|
+
{isStreaming && <ChatBubble sender="assistant" loading />}
|
|
1218
|
+
</Stack>
|
|
1219
|
+
<ChatComposer
|
|
1220
|
+
value={input}
|
|
1221
|
+
onValueChange={setInput}
|
|
1222
|
+
onSubmit={sendMessage}
|
|
1223
|
+
loading={isStreaming}
|
|
1224
|
+
/>
|
|
1225
|
+
</LayoutShell>
|
|
1226
|
+
```
|
|
1227
|
+
|
|
1228
|
+
---
|
|
1229
|
+
|
|
1230
|
+
## What NOT to do
|
|
1231
|
+
|
|
1232
|
+
- Do not use raw Tailwind classes for spacing, color, or typography — use `Stack`, `Text`, and component props instead
|
|
1233
|
+
- Do not build custom modals, drawers, or dropdowns — use `Modal`, `ConfirmDialog`, `Drawer`, and `ActionMenu`
|
|
1234
|
+
- Do not render form labels manually — use `FormField` which wires aria attributes automatically
|
|
1235
|
+
- Do not use `<h1>`–`<h6>` or `<p>` directly — use `<Text variant="h1">` etc.
|
|
1236
|
+
- Do not add `loading` spinners manually — all interactive components accept a `loading` prop
|