@tailor-platform/app-shell 1.15.0 → 1.16.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.
Files changed (98) hide show
  1. package/dist/app-shell-core.css +1 -1
  2. package/dist/app-shell.d.ts +208 -114
  3. package/dist/app-shell.js +3449 -3446
  4. package/dist/{appshell-DLVBFo5J.js → appshell-B0wh5Lq0.js} +625 -512
  5. package/dist/style-entry.d.ts +116 -96
  6. package/dist/testing.d.ts +116 -96
  7. package/dist/testing.js +1 -1
  8. package/dist/vite-plugin.d.ts +116 -96
  9. package/package.json +8 -8
  10. package/skills/app-shell-patterns/SKILL.md +101 -14
  11. package/skills/app-shell-patterns/references/api/create-ai-gateway-client.md +115 -0
  12. package/skills/app-shell-patterns/references/api/create-typed-paths.md +117 -0
  13. package/skills/app-shell-patterns/references/api/define-i18n-labels.md +143 -0
  14. package/skills/app-shell-patterns/references/api/define-module.md +247 -0
  15. package/skills/app-shell-patterns/references/api/define-resource.md +307 -0
  16. package/skills/app-shell-patterns/references/api/guards/hidden.md +202 -0
  17. package/skills/app-shell-patterns/references/api/guards/overview.md +354 -0
  18. package/skills/app-shell-patterns/references/api/guards/pass.md +129 -0
  19. package/skills/app-shell-patterns/references/api/guards/redirect-to.md +307 -0
  20. package/skills/app-shell-patterns/references/api/router/use-location.md +240 -0
  21. package/skills/app-shell-patterns/references/api/router/use-navigate.md +138 -0
  22. package/skills/app-shell-patterns/references/api/router/use-params.md +218 -0
  23. package/skills/app-shell-patterns/references/api/use-ai-chat.md +110 -0
  24. package/skills/app-shell-patterns/references/api/use-app-shell-config.md +126 -0
  25. package/skills/app-shell-patterns/references/api/use-app-shell-data.md +146 -0
  26. package/skills/app-shell-patterns/references/api/use-app-shell.md +143 -0
  27. package/skills/app-shell-patterns/references/api/use-auth.md +145 -0
  28. package/skills/app-shell-patterns/references/api/use-override-breadcrumb.md +74 -0
  29. package/skills/app-shell-patterns/references/api/use-page-meta.md +96 -0
  30. package/skills/app-shell-patterns/references/api/use-register-command-palette-actions.md +90 -0
  31. package/skills/app-shell-patterns/references/api/use-resolved-locale.md +67 -0
  32. package/skills/app-shell-patterns/references/api/use-route-error.md +214 -0
  33. package/skills/app-shell-patterns/references/api/use-theme.md +108 -0
  34. package/skills/app-shell-patterns/references/api/use-time-zone.md +80 -0
  35. package/skills/app-shell-patterns/references/api/use-toast.md +107 -0
  36. package/skills/app-shell-patterns/references/components/action-panel.md +168 -0
  37. package/skills/app-shell-patterns/references/components/activity-card.md +142 -0
  38. package/skills/app-shell-patterns/references/components/ai-chat.md +315 -0
  39. package/skills/app-shell-patterns/references/components/alert.md +202 -0
  40. package/skills/app-shell-patterns/references/components/app-shell.md +530 -0
  41. package/skills/app-shell-patterns/references/components/appearance-switcher.md +87 -0
  42. package/skills/app-shell-patterns/references/components/attachment.md +138 -0
  43. package/skills/app-shell-patterns/references/components/autocomplete.md +278 -0
  44. package/skills/app-shell-patterns/references/components/avatar.md +80 -0
  45. package/skills/app-shell-patterns/references/components/badge.md +309 -0
  46. package/skills/app-shell-patterns/references/components/button.md +164 -0
  47. package/skills/app-shell-patterns/references/components/card.md +109 -0
  48. package/skills/app-shell-patterns/references/components/checkbox.md +169 -0
  49. package/skills/app-shell-patterns/references/components/combobox.md +318 -0
  50. package/skills/app-shell-patterns/references/components/command-palette.md +413 -0
  51. package/skills/app-shell-patterns/references/components/csv-importer.md +214 -0
  52. package/skills/app-shell-patterns/references/components/data-table.md +953 -0
  53. package/skills/app-shell-patterns/references/components/date-picker.md +248 -0
  54. package/skills/app-shell-patterns/references/components/default-header.md +75 -0
  55. package/skills/app-shell-patterns/references/components/default-sidebar.md +136 -0
  56. package/skills/app-shell-patterns/references/components/description-card.md +569 -0
  57. package/skills/app-shell-patterns/references/components/dialog.md +164 -0
  58. package/skills/app-shell-patterns/references/components/document-progress-card.md +154 -0
  59. package/skills/app-shell-patterns/references/components/form.md +378 -0
  60. package/skills/app-shell-patterns/references/components/global-header-layout.md +132 -0
  61. package/skills/app-shell-patterns/references/components/grid.md +184 -0
  62. package/skills/app-shell-patterns/references/components/input.md +83 -0
  63. package/skills/app-shell-patterns/references/components/layout.md +539 -0
  64. package/skills/app-shell-patterns/references/components/menu.md +218 -0
  65. package/skills/app-shell-patterns/references/components/metric-card.md +94 -0
  66. package/skills/app-shell-patterns/references/components/select.md +283 -0
  67. package/skills/app-shell-patterns/references/components/sheet.md +237 -0
  68. package/skills/app-shell-patterns/references/components/sidebar-group.md +466 -0
  69. package/skills/app-shell-patterns/references/components/sidebar-item.md +458 -0
  70. package/skills/app-shell-patterns/references/components/sidebar-layout.md +258 -0
  71. package/skills/app-shell-patterns/references/components/spinner.md +81 -0
  72. package/skills/app-shell-patterns/references/components/table.md +188 -0
  73. package/skills/app-shell-patterns/references/components/tabs.md +158 -0
  74. package/skills/app-shell-patterns/references/components/textarea.md +122 -0
  75. package/skills/app-shell-patterns/references/components/timeline.md +317 -0
  76. package/skills/app-shell-patterns/references/components/toolbar.md +138 -0
  77. package/skills/app-shell-patterns/references/components/tooltip.md +144 -0
  78. package/skills/app-shell-patterns/references/components/with-guard.md +445 -0
  79. package/skills/app-shell-patterns/references/concepts/authentication.md +301 -0
  80. package/skills/app-shell-patterns/references/concepts/custom-components.md +59 -0
  81. package/skills/app-shell-patterns/references/concepts/file-based-routing.md +350 -0
  82. package/skills/app-shell-patterns/references/{fundamental → concepts}/graphql.md +11 -4
  83. package/skills/app-shell-patterns/references/concepts/modules-and-resources.md +229 -0
  84. package/skills/app-shell-patterns/references/concepts/routing-navigation.md +387 -0
  85. package/skills/app-shell-patterns/references/concepts/sidebar-navigation.md +388 -0
  86. package/skills/app-shell-patterns/references/{fundamental/design-system.md → concepts/styling-theming.md} +258 -110
  87. package/skills/app-shell-patterns/references/migrations.md +34 -0
  88. package/skills/app-shell-patterns/references/pages/{detail.md → document-detail.md} +4 -27
  89. package/skills/app-shell-patterns/references/patterns/form-composer.md +43 -45
  90. package/skills/app-shell-patterns/references/patterns/form-modal.md +39 -100
  91. package/skills/app-shell-patterns/references/patterns/form-sectioned.md +12 -38
  92. package/skills/app-shell-patterns/references/patterns/form-single-page.md +11 -32
  93. package/skills/app-shell-patterns/references/patterns/form-wizard.md +12 -53
  94. package/skills/app-shell-patterns/references/patterns/interaction-confirm.md +11 -27
  95. package/skills/app-shell-patterns/references/patterns/interaction-multi-select.md +13 -27
  96. package/skills/app-shell-patterns/references/patterns/interaction-toast.md +10 -34
  97. package/skills/app-shell-patterns/references/patterns/list-dense-scan.md +11 -86
  98. package/skills/app-shell-patterns/references/fundamental/components.md +0 -1009
@@ -0,0 +1,138 @@
1
+ ---
2
+ title: Attachment
3
+ description: Attachment list with upload, previews, and per-item actions; compose with Card for titles and surface styling
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/components/attachment.docs.outline.md — do not edit by hand. -->
7
+
8
+ # Attachment
9
+
10
+ `Attachment` is a reusable file/image attachment surface for ERP detail pages. It provides upload affordance (click or drag-and-drop onto the upload tile), optional helper text via `uploadHint`, image/file preview tiles, and per-item menu actions for download and delete.
11
+
12
+ ## Import
13
+
14
+ ```tsx
15
+ import { Attachment, useAttachment } from "@tailor-platform/app-shell";
16
+ import type { AttachmentItem, AttachmentOperation } from "@tailor-platform/app-shell";
17
+ ```
18
+
19
+ ## Basic usage
20
+
21
+ Use `useAttachment` to manage upload/delete state locally, then flush operations to your backend on submit via `applyChanges`. Spread the returned `props` directly onto `<Attachment />`.
22
+
23
+ ```tsx
24
+ import { Attachment, Card, useAttachment } from "@tailor-platform/app-shell";
25
+ import type { AttachmentOperation } from "@tailor-platform/app-shell";
26
+
27
+ function ProductForm() {
28
+ const { props, applyChanges } = useAttachment({
29
+ initialItems: existingAttachments,
30
+ accept: "image/*,.pdf",
31
+ });
32
+
33
+ async function handleSubmit() {
34
+ await applyChanges(async (operations) => {
35
+ for (const op of operations) {
36
+ if (op.type === "upload") await uploadToServer(op.file);
37
+ if (op.type === "delete") await deleteFromServer(op.item.id);
38
+ }
39
+ });
40
+ }
41
+
42
+ return (
43
+ <Card.Root>
44
+ <Card.Header title="Product images" description="PNG, JPG, or PDF — max 10 MB per file." />
45
+ <Card.Content>
46
+ <Attachment
47
+ {...props}
48
+ uploadLabel="Upload image"
49
+ uploadHint="PNG, JPG, or PDF — max 10 MB per file."
50
+ onDownload={(item) => console.log("download", item)}
51
+ />
52
+ </Card.Content>
53
+ </Card.Root>
54
+ );
55
+ }
56
+ ```
57
+
58
+ ## Composition with Card
59
+
60
+ - Use **`Card.Header`** for the section title and description.
61
+ - Use **`Card.Root`** (and usually **`Card.Content`**) for border, radius, shadow, and padding around the attachment block.
62
+
63
+ ## useAttachment
64
+
65
+ `useAttachment` tracks pending uploads and deletes locally until `applyChanges` is called. It handles object URL creation/revocation for image previews automatically.
66
+
67
+ ### Options
68
+
69
+ | Option | Type | Default | Description |
70
+ | -------------- | ------------------ | ------- | -------------------------------------------- |
71
+ | `initialItems` | `AttachmentItem[]` | `[]` | Existing items to populate on mount |
72
+ | `accept` | `string` | - | Accepted file types passed to the file input |
73
+ | `disabled` | `boolean` | `false` | Disable upload and item actions |
74
+
75
+ ### Return value
76
+
77
+ | Field | Type | Description |
78
+ | -------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
79
+ | `props` | `{ items, onUpload, onDelete, accept, disabled }` | Spread directly onto `<Attachment />` |
80
+ | `applyChanges` | `(fn: (ops: AttachmentOperation[]) => Promise<void>) => Promise<void>` | Flush buffered operations to your backend; clears the buffer after `fn` resolves. If `fn` throws, the buffer is preserved so the call can be retried. |
81
+ | `isApplying` | `boolean` | `true` while `applyChanges` is in progress. Use to show a loading indicator during form submission. |
82
+
83
+ > **Upload ordering:** newly uploaded files are prepended to `items`, so the most recent uploads appear first in the tile list.
84
+
85
+ ## AttachmentOperation
86
+
87
+ Represents a single pending change buffered by `useAttachment`. Passed as an array to the `applyChanges` callback.
88
+
89
+ ```ts
90
+ type AttachmentOperation =
91
+ | { type: "upload"; file: File; item: AttachmentItem }
92
+ | { type: "delete"; item: AttachmentItem };
93
+ ```
94
+
95
+ ## AttachmentItem
96
+
97
+ ```ts
98
+ interface AttachmentItem {
99
+ id: string;
100
+ fileName: string;
101
+ mimeType: string;
102
+ previewUrl?: string;
103
+ }
104
+ ```
105
+
106
+ ## Attachment props
107
+
108
+ | Prop | Type | Default | Description |
109
+ | ------------- | -------------------------------- | ------------------- | --------------------------------------------------------------------------------------------- |
110
+ | `items` | `AttachmentItem[]` | `[]` | Attachment list rendered as preview tiles |
111
+ | `onUpload` | `(files: File[]) => void` | _(required)_ | Called when files are selected or dropped on the upload tile |
112
+ | `onDelete` | `(item: AttachmentItem) => void` | _(required)_ | Called when Delete is chosen in a preview menu |
113
+ | `onDownload` | `(item: AttachmentItem) => void` | - | Called when Download is chosen in a preview menu |
114
+ | `uploadLabel` | `string` | `"Click to upload"` | Primary label on the upload tile |
115
+ | `uploadHint` | `string` | - | Supporting text below the upload label |
116
+ | `accept` | `string` | - | Accepted file types for hidden file input |
117
+ | `disabled` | `boolean` | `false` | Disables upload, drop, and hides per-item menu actions |
118
+ | `className` | `string` | - | Classes on the root (e.g. padding); component has no outer border or inset padding by default |
119
+
120
+ ## Behavior
121
+
122
+ - **Layout**: hidden file input, then the tile row—no built-in page inset.
123
+ - **Image items** (`mimeType` starts with `image/`) render as 120×120 image thumbnails when `previewUrl` loads; otherwise a fallback tile.
124
+ - **Non-image items** render as 120×120 file tiles with icon and wrapped filename.
125
+ - **Drag and drop** applies only to the **upload tile** (dashed “click to upload” control), not the full attachment block. Use the file picker from the same tile for click-to-upload.
126
+ - **Disabled state** (`disabled={true}`): the upload tile and drop target are hidden; per-item menu actions are also hidden.
127
+ - **Item actions** are available through the preview menu (`Download`, `Delete`) when not disabled.
128
+
129
+ ### DOM
130
+
131
+ - Root: `data-slot="attachment"`
132
+ - Content wrapper: `data-slot="attachment-content"`
133
+
134
+ ## Related components
135
+
136
+ - [Card](./card.md)
137
+ - [Button](./button.md)
138
+ - [Menu](./menu.md)
@@ -0,0 +1,278 @@
1
+ ---
2
+ title: Autocomplete
3
+ description: Text input with a suggestion list — value is the raw input string, not a discrete item selection
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/components/autocomplete.docs.outline.md — do not edit by hand. -->
7
+
8
+ # Autocomplete
9
+
10
+ The `Autocomplete` component provides a free-text input with a suggestion list. Unlike `Select` and `Combobox`, the component value is the raw input string — not a discrete item selection. For async suggestions use `Autocomplete.Async`. For custom compositions use `Autocomplete.Parts`.
11
+
12
+ ## Import
13
+
14
+ ```tsx
15
+ import { Autocomplete } from "@tailor-platform/app-shell";
16
+ ```
17
+
18
+ ## Basic Usage
19
+
20
+ <example-preview name="basic-usage"></example-preview>
21
+
22
+ ```tsx
23
+ function BasicUsage() {
24
+ return (
25
+ <Autocomplete
26
+ items={["Apple", "Banana", "Cherry"]}
27
+ placeholder="Type a fruit..."
28
+ onValueChange={(value) => console.log(value)}
29
+ />
30
+ );
31
+ }
32
+ ```
33
+
34
+ ## Props
35
+
36
+ ### Autocomplete Props
37
+
38
+ | Prop | Type | Default | Description |
39
+ | ----------------- | ------------------------- | --------------- | ------------------------------------------------------------------- |
40
+ | `items` | `I[]` | - | Suggestion items. May be a flat array or an array of `ItemGroup<T>` |
41
+ | `placeholder` | `string` | - | Placeholder text for the input |
42
+ | `emptyText` | `string` | `"No results."` | Text shown when no items match |
43
+ | `value` | `string` | - | Controlled value (raw input string) |
44
+ | `defaultValue` | `string` | - | Initial value (uncontrolled) |
45
+ | `onValueChange` | `(value: string) => void` | - | Called when the value changes |
46
+ | `mapItem` | `(item: T) => MappedItem` | - | Map each item to its label, key, and optional custom render |
47
+ | `className` | `string` | - | Additional CSS classes for the root container |
48
+ | `disabled` | `boolean` | `false` | Disables the autocomplete |
49
+ | `aria-label` | `string` | - | Accessible name for the input. Use when there is no visible label |
50
+ | `aria-labelledby` | `string` | - | ID of the element(s) that label the input |
51
+ | `id` | `string` | - | ID applied to the combobox input element |
52
+
53
+ ### MappedItem
54
+
55
+ ```ts
56
+ interface MappedItem {
57
+ label: string; // Display text, used for filtering and a11y
58
+ key?: string; // React key. Defaults to label
59
+ render?: React.ReactNode; // Custom JSX to render in the dropdown
60
+ }
61
+ ```
62
+
63
+ ### ItemGroup
64
+
65
+ ```ts
66
+ interface ItemGroup<T> {
67
+ label: string;
68
+ items: T[];
69
+ }
70
+ ```
71
+
72
+ ## Grouped Suggestions
73
+
74
+ ```tsx
75
+ const cities = [
76
+ { label: "Japan", items: ["Tokyo", "Osaka", "Kyoto"] },
77
+ { label: "France", items: ["Paris", "Lyon", "Marseille"] },
78
+ ];
79
+
80
+ <Autocomplete items={cities} placeholder="Search cities..." />;
81
+ ```
82
+
83
+ ## Object Items with mapItem
84
+
85
+ When items are objects, use `mapItem` to tell the component how to display them. The selected value is the item's `label` string returned by `mapItem`:
86
+
87
+ ```tsx
88
+ type City = { code: string; name: string };
89
+
90
+ const cities: City[] = [
91
+ { code: "TYO", name: "Tokyo" },
92
+ { code: "OSA", name: "Osaka" },
93
+ ];
94
+
95
+ <Autocomplete
96
+ items={cities}
97
+ mapItem={(city) => ({ label: city.name, key: city.code })}
98
+ placeholder="Search cities..."
99
+ onValueChange={(name) => console.log(name)} // receives "Tokyo", "Osaka", etc.
100
+ />;
101
+ ```
102
+
103
+ ## Async Suggestions
104
+
105
+ Use `Autocomplete.Async` to fetch suggestions as the user types. The fetcher is called on each keystroke (debounced). When the dropdown first opens or the input is cleared, the fetcher receives `null` as the query — return initial/default suggestions for `null`, or return an empty array to show nothing until the user starts typing.
106
+
107
+ ```tsx
108
+ import { type AutocompleteAsyncFetcher } from "@tailor-platform/app-shell";
109
+
110
+ const fetcher: AutocompleteAsyncFetcher<string> = async (query, { signal }) => {
111
+ const res = await fetch(`/api/suggestions?q=${query ?? ""}`, { signal });
112
+ return res.json();
113
+ };
114
+
115
+ <Autocomplete.Async
116
+ fetcher={fetcher}
117
+ placeholder="Search..."
118
+ onValueChange={(value) => console.log(value)}
119
+ />;
120
+ ```
121
+
122
+ ### Autocomplete.Async Props
123
+
124
+ Accepts all the same props as `Autocomplete` except `items`, plus:
125
+
126
+ | Prop | Type | Default | Description |
127
+ | -------------- | ----------------------------- | -------------------------- | ------------------------------------------------------------------ |
128
+ | `fetcher` | `AutocompleteAsyncFetcher<T>` | - | Fetcher called on each keystroke (debounced by default) |
129
+ | `loadingText` | `string` | `"Loading..."` | Text shown while loading |
130
+ | `errorText` | `string` | `"Couldn't load results."` | Message shown in the popover when the fetcher fails (with Retry) |
131
+ | `retryText` | `string` | `"Retry"` | Label for the retry button in the error state |
132
+ | `onFetchError` | `(error: unknown) => void` | - | Called once per outage when a fetch fails (logging/error tracking) |
133
+
134
+ ### AutocompleteAsyncFetcher
135
+
136
+ ```ts
137
+ type AutocompleteAsyncFetcher<T> =
138
+ | ((query: string | null, options: { signal: AbortSignal }) => Promise<T[]>)
139
+ | {
140
+ fn: (query: string | null, options: { signal: AbortSignal }) => Promise<T[]>;
141
+ debounceMs: number;
142
+ };
143
+ ```
144
+
145
+ `query` is `null` when the user has not typed anything (e.g. the dropdown was just opened or the input was cleared). Pass `{ fn, debounceMs }` to customize the debounce delay.
146
+
147
+ ### Error handling
148
+
149
+ If the fetcher throws or rejects, `Autocomplete.Async` renders a built-in inline error state in the popover — the `errorText` message plus a **Retry** button that re-runs the last fetch — instead of the misleading "No results." empty state. Aborted/superseded requests are ignored, and announcements are de-duped per outage. Pass `onFetchError` to run a side effect (logging, toast) — it fires once per outage and re-arms after the next successful fetch.
150
+
151
+ ## Low-level Primitives
152
+
153
+ `Autocomplete.Parts` exposes styled sub-components and hooks for fully custom compositions:
154
+
155
+ ```tsx
156
+ const {
157
+ Root,
158
+ Value,
159
+ InputGroup,
160
+ Input,
161
+ Trigger,
162
+ Content,
163
+ List,
164
+ Item,
165
+ Empty,
166
+ Clear,
167
+ Group,
168
+ GroupLabel,
169
+ Collection,
170
+ Status,
171
+ } = Autocomplete.Parts;
172
+ ```
173
+
174
+ ## Form submission
175
+
176
+ Inside a `Field.Root`, the field's `name` identifies the value and these props are unnecessary —
177
+ see [Form](./form.md). They matter for **native** submission: a plain `<form>`, `new FormData(form)`,
178
+ or a server action.
179
+
180
+ | Prop | Type | Default | Description |
181
+ | ---------- | ----------------------------- | ------- | ------------------------------------------------------------------------------------------------- |
182
+ | `name` | `string` | - | Names the input for native submission. **Ignored inside a `Field.Root`** — the field's name wins. |
183
+ | `form` | `string` | - | `id` of the owning form, when the control is rendered outside it. |
184
+ | `required` | `boolean` | `false` | Blocks submission until a value is chosen, surfacing the matching `Field.Error`. |
185
+ | `inputRef` | `React.Ref<HTMLInputElement>` | - | Ref to the underlying text input (use for React Hook Form's `field.ref`). |
186
+
187
+ Autocomplete's value is the raw input string, so no value-serialisation hook is needed.
188
+
189
+ ## Examples
190
+
191
+ ### Controlled Autocomplete
192
+
193
+ ```tsx
194
+ const [query, setQuery] = useState("");
195
+
196
+ <Autocomplete items={suggestions} value={query} onValueChange={setQuery} placeholder="Search..." />;
197
+ ```
198
+
199
+ ### Address Input with Async Suggestions
200
+
201
+ ```tsx
202
+ const fetcher: AutocompleteAsyncFetcher<string> = async (query, { signal }) => {
203
+ if (!query) return [];
204
+ const res = await fetch(`/api/address-lookup?q=${encodeURIComponent(query)}`, { signal });
205
+ return res.json();
206
+ };
207
+
208
+ <Autocomplete.Async
209
+ fetcher={fetcher}
210
+ placeholder="Start typing an address..."
211
+ onValueChange={(address) => form.setValue("address", address)}
212
+ />;
213
+ ```
214
+
215
+ ### Async with Parts (custom composition)
216
+
217
+ Combine `Autocomplete.useAsync` with `Autocomplete.Parts` for full control over layout and rendering:
218
+
219
+ `Autocomplete.useAsync` accepts the same options as `Autocomplete.Async` (including `onFetchError`) and returns:
220
+
221
+ | Property | Type | Description |
222
+ | --------- | ------------ | ------------------------------------------------------- |
223
+ | `items` | `T[]` | Currently loaded items |
224
+ | `loading` | `boolean` | Whether a fetch is in progress |
225
+ | `error` | `unknown` | The error thrown by the last fetch, if any |
226
+ | `retry` | `() => void` | Re-runs the last fetch (use to build a custom retry UI) |
227
+
228
+ ```tsx
229
+ const suggestions = Autocomplete.useAsync({
230
+ fetcher: async (query, { signal }) => {
231
+ const res = await fetch(`/api/suggestions?q=${query ?? ""}`, { signal });
232
+ return res.json();
233
+ },
234
+ onFetchError: (error) => reportError(error),
235
+ });
236
+
237
+ <Autocomplete.Parts.Root {...suggestions} filter={null}>
238
+ <Autocomplete.Parts.InputGroup>
239
+ <Autocomplete.Parts.Input placeholder="Search..." />
240
+ <Autocomplete.Parts.Clear />
241
+ </Autocomplete.Parts.InputGroup>
242
+ <Autocomplete.Parts.Content>
243
+ <Autocomplete.Parts.List>
244
+ {suggestions.items.map((item) => (
245
+ <Autocomplete.Parts.Item key={item} value={item}>
246
+ {item}
247
+ </Autocomplete.Parts.Item>
248
+ ))}
249
+ <Autocomplete.Parts.Empty>
250
+ {suggestions.error ? (
251
+ <button onClick={suggestions.retry}>Couldn't load. Retry</button>
252
+ ) : suggestions.loading ? (
253
+ "Loading..."
254
+ ) : (
255
+ "No results."
256
+ )}
257
+ </Autocomplete.Parts.Empty>
258
+ </Autocomplete.Parts.List>
259
+ </Autocomplete.Parts.Content>
260
+ </Autocomplete.Parts.Root>;
261
+ ```
262
+
263
+ ## Accessibility
264
+
265
+ - Input is keyboard accessible with arrow key navigation
266
+ - Pressing `Escape` closes the suggestion list
267
+ - Selecting a suggestion fills the input with the item's label
268
+ - When used standalone (no visible `<label>`), give the input an accessible name with `aria-label` or `aria-labelledby` — otherwise screen readers announce only the current value:
269
+
270
+ ```tsx
271
+ <Autocomplete items={items} aria-label="City search" />
272
+ ```
273
+
274
+ ## Related Components
275
+
276
+ - [Select](./select.md) - Non-searchable discrete item selection
277
+ - [Combobox](./combobox.md) - Searchable combobox that returns discrete items
278
+ - [Input](./input.md) - Plain text input without suggestions
@@ -0,0 +1,80 @@
1
+ ---
2
+ title: Avatar
3
+ description: User avatar with image and fallback (Base UI)
4
+ ---
5
+
6
+ <!-- GENERATED by docs-kit from docs-src/components/avatar.docs.outline.md — do not edit by hand. -->
7
+
8
+ # Avatar
9
+
10
+ The `Avatar` component wraps [Base UI Avatar](https://base-ui.com/react/components/avatar): a root container with an optional image and fallback content (typically initials) when the image is missing or fails to load.
11
+
12
+ ## Import
13
+
14
+ ```tsx
15
+ import { Avatar } from "@tailor-platform/app-shell";
16
+ ```
17
+
18
+ ## Basic usage
19
+
20
+ <example-preview name="basic-usage"></example-preview>
21
+
22
+ ```tsx
23
+ function BasicUsage() {
24
+ return (
25
+ <Avatar.Root>
26
+ <Avatar.Image src="/user.png" alt="Jane Doe" />
27
+ <Avatar.Fallback>JD</Avatar.Fallback>
28
+ </Avatar.Root>
29
+ );
30
+ }
31
+ ```
32
+
33
+ ## Parts
34
+
35
+ | Part | Description |
36
+ | ----------------- | ---------------------------------------------------------- |
37
+ | `Avatar.Root` | Outer container; applies size and surface styles. |
38
+ | `Avatar.Image` | `<img>` for the photo; hidden when not loaded or on error. |
39
+ | `Avatar.Fallback` | Shown when there is no image or the image failed to load. |
40
+
41
+ ## Root props
42
+
43
+ | Prop | Type | Default | Description |
44
+ | ----------- | ----------------------------------- | ----------- | -------------------------- |
45
+ | `size` | `"xs" \| "sm" \| "default" \| "lg"` | `"default"` | Circle size (28px default) |
46
+ | `className` | `string` | — | Extra classes |
47
+ | `children` | `React.ReactNode` | — | Image + fallback |
48
+
49
+ `Avatar.Root` forwards other stable props from Base UI (for example `aria-hidden`, `style`) and standard HTML attributes for the `<span>` root.
50
+
51
+ ## Sizes
52
+
53
+ The smallest size, `xs` (16px), is intended for compact/inline contexts and single-glyph or icon content; two-letter initials will be cramped.
54
+
55
+ ```tsx
56
+ <Avatar.Root size="xs">
57
+ <Avatar.Fallback>A</Avatar.Fallback>
58
+ </Avatar.Root>
59
+ <Avatar.Root size="sm">
60
+ <Avatar.Fallback>AB</Avatar.Fallback>
61
+ </Avatar.Root>
62
+ <Avatar.Root size="default">
63
+ <Avatar.Fallback>CD</Avatar.Fallback>
64
+ </Avatar.Root>
65
+ <Avatar.Root size="lg">
66
+ <Avatar.Fallback>EF</Avatar.Fallback>
67
+ </Avatar.Root>
68
+ ```
69
+
70
+ ## Types
71
+
72
+ `AvatarProps` is the props type for `Avatar.Root` (includes `size` and Base UI root props). Import when wrapping or composing:
73
+
74
+ ```tsx
75
+ import { Avatar, type AvatarProps } from "@tailor-platform/app-shell";
76
+ ```
77
+
78
+ ## `avatarVariants`
79
+
80
+ The same size styles used by `Avatar.Root` are exported as `avatarVariants` (for example if you need to mirror avatar dimensions in layout code). Prefer the `size` prop on `Avatar.Root` for normal use.