@tailor-platform/app-shell 1.14.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.
- package/LICENSE.md +21 -0
- package/dist/app-shell-core.css +1 -1
- package/dist/app-shell.d.ts +1203 -316
- package/dist/app-shell.js +4562 -3307
- package/dist/{appshell-CtS8Y3LW.js → appshell-B0wh5Lq0.js} +850 -549
- package/dist/style-entry.d.ts +118 -98
- package/dist/testing.d.ts +118 -98
- package/dist/testing.js +1 -1
- package/dist/themes/_template.css +1 -1
- package/dist/vite-plugin.d.ts +118 -98
- package/package.json +13 -13
- package/skills/app-shell-patterns/SKILL.md +108 -24
- package/skills/app-shell-patterns/references/api/create-ai-gateway-client.md +115 -0
- package/skills/app-shell-patterns/references/api/create-typed-paths.md +117 -0
- package/skills/app-shell-patterns/references/api/define-i18n-labels.md +143 -0
- package/skills/app-shell-patterns/references/api/define-module.md +247 -0
- package/skills/app-shell-patterns/references/api/define-resource.md +307 -0
- package/skills/app-shell-patterns/references/api/guards/hidden.md +202 -0
- package/skills/app-shell-patterns/references/api/guards/overview.md +354 -0
- package/skills/app-shell-patterns/references/api/guards/pass.md +129 -0
- package/skills/app-shell-patterns/references/api/guards/redirect-to.md +307 -0
- package/skills/app-shell-patterns/references/api/router/use-location.md +240 -0
- package/skills/app-shell-patterns/references/api/router/use-navigate.md +138 -0
- package/skills/app-shell-patterns/references/api/router/use-params.md +218 -0
- package/skills/app-shell-patterns/references/api/use-ai-chat.md +110 -0
- package/skills/app-shell-patterns/references/api/use-app-shell-config.md +126 -0
- package/skills/app-shell-patterns/references/api/use-app-shell-data.md +146 -0
- package/skills/app-shell-patterns/references/api/use-app-shell.md +143 -0
- package/skills/app-shell-patterns/references/api/use-auth.md +145 -0
- package/skills/app-shell-patterns/references/api/use-override-breadcrumb.md +74 -0
- package/skills/app-shell-patterns/references/api/use-page-meta.md +96 -0
- package/skills/app-shell-patterns/references/api/use-register-command-palette-actions.md +90 -0
- package/skills/app-shell-patterns/references/api/use-resolved-locale.md +67 -0
- package/skills/app-shell-patterns/references/api/use-route-error.md +214 -0
- package/skills/app-shell-patterns/references/api/use-theme.md +108 -0
- package/skills/app-shell-patterns/references/api/use-time-zone.md +80 -0
- package/skills/app-shell-patterns/references/api/use-toast.md +107 -0
- package/skills/app-shell-patterns/references/components/action-panel.md +168 -0
- package/skills/app-shell-patterns/references/components/activity-card.md +142 -0
- package/skills/app-shell-patterns/references/components/ai-chat.md +315 -0
- package/skills/app-shell-patterns/references/components/alert.md +202 -0
- package/skills/app-shell-patterns/references/components/app-shell.md +530 -0
- package/skills/app-shell-patterns/references/components/appearance-switcher.md +87 -0
- package/skills/app-shell-patterns/references/components/attachment.md +138 -0
- package/skills/app-shell-patterns/references/components/autocomplete.md +278 -0
- package/skills/app-shell-patterns/references/components/avatar.md +80 -0
- package/skills/app-shell-patterns/references/components/badge.md +309 -0
- package/skills/app-shell-patterns/references/components/button.md +164 -0
- package/skills/app-shell-patterns/references/components/card.md +109 -0
- package/skills/app-shell-patterns/references/components/checkbox.md +169 -0
- package/skills/app-shell-patterns/references/components/combobox.md +318 -0
- package/skills/app-shell-patterns/references/components/command-palette.md +413 -0
- package/skills/app-shell-patterns/references/components/csv-importer.md +214 -0
- package/skills/app-shell-patterns/references/components/data-table.md +953 -0
- package/skills/app-shell-patterns/references/components/date-picker.md +248 -0
- package/skills/app-shell-patterns/references/components/default-header.md +75 -0
- package/skills/app-shell-patterns/references/components/default-sidebar.md +136 -0
- package/skills/app-shell-patterns/references/components/description-card.md +569 -0
- package/skills/app-shell-patterns/references/components/dialog.md +164 -0
- package/skills/app-shell-patterns/references/components/document-progress-card.md +154 -0
- package/skills/app-shell-patterns/references/components/form.md +378 -0
- package/skills/app-shell-patterns/references/components/global-header-layout.md +132 -0
- package/skills/app-shell-patterns/references/components/grid.md +184 -0
- package/skills/app-shell-patterns/references/components/input.md +83 -0
- package/skills/app-shell-patterns/references/components/layout.md +539 -0
- package/skills/app-shell-patterns/references/components/menu.md +218 -0
- package/skills/app-shell-patterns/references/components/metric-card.md +94 -0
- package/skills/app-shell-patterns/references/components/select.md +283 -0
- package/skills/app-shell-patterns/references/components/sheet.md +237 -0
- package/skills/app-shell-patterns/references/components/sidebar-group.md +466 -0
- package/skills/app-shell-patterns/references/components/sidebar-item.md +458 -0
- package/skills/app-shell-patterns/references/components/sidebar-layout.md +258 -0
- package/skills/app-shell-patterns/references/components/spinner.md +81 -0
- package/skills/app-shell-patterns/references/components/table.md +188 -0
- package/skills/app-shell-patterns/references/components/tabs.md +158 -0
- package/skills/app-shell-patterns/references/components/textarea.md +122 -0
- package/skills/app-shell-patterns/references/components/timeline.md +317 -0
- package/skills/app-shell-patterns/references/components/toolbar.md +138 -0
- package/skills/app-shell-patterns/references/components/tooltip.md +144 -0
- package/skills/app-shell-patterns/references/components/with-guard.md +445 -0
- package/skills/app-shell-patterns/references/concepts/authentication.md +301 -0
- package/skills/app-shell-patterns/references/concepts/custom-components.md +59 -0
- package/skills/app-shell-patterns/references/concepts/file-based-routing.md +350 -0
- package/skills/app-shell-patterns/references/{fundamental → concepts}/graphql.md +11 -4
- package/skills/app-shell-patterns/references/concepts/modules-and-resources.md +229 -0
- package/skills/app-shell-patterns/references/concepts/routing-navigation.md +387 -0
- package/skills/app-shell-patterns/references/concepts/sidebar-navigation.md +388 -0
- package/skills/app-shell-patterns/references/{fundamental/design-system.md → concepts/styling-theming.md} +260 -105
- package/skills/app-shell-patterns/references/migrations.md +34 -0
- package/skills/app-shell-patterns/references/pages/document-detail.md +1125 -0
- package/skills/app-shell-patterns/references/patterns/form-composer.md +44 -46
- package/skills/app-shell-patterns/references/patterns/form-modal.md +39 -100
- package/skills/app-shell-patterns/references/patterns/form-sectioned.md +12 -38
- package/skills/app-shell-patterns/references/patterns/form-single-page.md +11 -32
- package/skills/app-shell-patterns/references/patterns/form-wizard.md +12 -53
- package/skills/app-shell-patterns/references/patterns/interaction-confirm.md +11 -27
- package/skills/app-shell-patterns/references/patterns/interaction-multi-select.md +14 -28
- package/skills/app-shell-patterns/references/patterns/interaction-toast.md +10 -34
- package/skills/app-shell-patterns/references/patterns/list-dense-scan.md +11 -86
- package/skills/app-shell-patterns/references/fundamental/components.md +0 -981
- package/skills/app-shell-patterns/references/patterns/detail-hero-with-actions.md +0 -201
|
@@ -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.
|