@tailor-platform/app-shell 1.15.0 → 1.17.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/dist/app-shell-core.css +1 -1
- package/dist/app-shell.d.ts +811 -131
- package/dist/app-shell.js +7155 -5797
- package/dist/{appshell-DLVBFo5J.js → appshell-CahhZy7v.js} +656 -524
- package/dist/semantic-contrast.test.ts +154 -0
- package/dist/style-entry.d.ts +147 -92
- package/dist/testing.d.ts +152 -95
- package/dist/testing.js +1 -1
- package/dist/theme-bridge.test.ts +101 -1
- package/dist/theme.bridge.css +91 -0
- package/dist/theme.css +6 -1
- package/dist/themes/_template.css +7 -1
- package/dist/themes/bloom.css +0 -6
- package/dist/themes/cream.css +0 -6
- package/dist/themes/default.css +389 -69
- package/dist/vite-plugin.d.ts +147 -92
- package/package.json +8 -8
- package/skills/app-shell-patterns/SKILL.md +108 -19
- 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 +132 -0
- package/skills/app-shell-patterns/references/api/use-app-shell-data.md +150 -0
- package/skills/app-shell-patterns/references/api/use-app-shell.md +150 -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 +104 -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 +181 -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 +1168 -0
- package/skills/app-shell-patterns/references/components/date-picker.md +259 -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/filter-rail.md +165 -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 +472 -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/sidebar-section.md +114 -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 +356 -0
- package/skills/app-shell-patterns/references/{fundamental → concepts}/graphql.md +31 -18
- package/skills/app-shell-patterns/references/concepts/modules-and-resources.md +229 -0
- package/skills/app-shell-patterns/references/concepts/routing-navigation.md +425 -0
- package/skills/app-shell-patterns/references/concepts/sidebar-navigation.md +420 -0
- package/skills/app-shell-patterns/references/concepts/styling-theming.md +785 -0
- package/skills/app-shell-patterns/references/migrations.md +55 -0
- package/skills/app-shell-patterns/references/pages/{detail.md → document-detail.md} +10 -33
- package/skills/app-shell-patterns/references/patterns/form-composer.md +43 -45
- package/skills/app-shell-patterns/references/patterns/form-modal.md +37 -92
- 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 +136 -139
- package/skills/app-shell-patterns/references/patterns/interaction-toast.md +10 -34
- package/skills/app-shell-patterns/references/patterns/list-dense-scan.md +13 -88
- package/skills/app-shell-patterns/references/fundamental/components.md +0 -1009
- package/skills/app-shell-patterns/references/fundamental/design-system.md +0 -440
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: AppShell
|
|
3
|
+
description: The root component that provides routing, navigation, and theming for your application
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- GENERATED by docs-kit from docs-src/components/app-shell.docs.outline.md — do not edit by hand. -->
|
|
7
|
+
|
|
8
|
+
# AppShell
|
|
9
|
+
|
|
10
|
+
`AppShell` is the root component that wires together routing, navigation, authentication, and theming for your AppShell application. It should wrap your entire application layout.
|
|
11
|
+
|
|
12
|
+
## Import
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { AppShell } from "@tailor-platform/app-shell";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Basic Usage
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { AppShell, SidebarLayout, defineModule } from "@tailor-platform/app-shell";
|
|
22
|
+
|
|
23
|
+
const modules = [
|
|
24
|
+
defineModule({
|
|
25
|
+
path: "dashboard",
|
|
26
|
+
component: DashboardPage,
|
|
27
|
+
meta: { title: "Dashboard" },
|
|
28
|
+
}),
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
function App() {
|
|
32
|
+
return (
|
|
33
|
+
<AppShell title="My ERP App" basePath="/app" modules={modules}>
|
|
34
|
+
<SidebarLayout />
|
|
35
|
+
</AppShell>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Props
|
|
41
|
+
|
|
42
|
+
### title
|
|
43
|
+
|
|
44
|
+
- **Type:** `string` (optional)
|
|
45
|
+
- **Description:** Application title displayed in the sidebar header. Also used as the browser tab title suffix — AppShell keeps `document.title` in sync with the active route as `"<page> · <title>"`, where `<page>` is the current breadcrumb leaf (including any [`useOverrideBreadcrumb`](../api/use-override-breadcrumb.md) override). When omitted, the tab shows just the page name.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
<AppShell title="My ERP App" modules={modules}>
|
|
49
|
+
{/* ... */}
|
|
50
|
+
</AppShell>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### icon
|
|
54
|
+
|
|
55
|
+
- **Type:** `React.ReactNode` (optional)
|
|
56
|
+
- **Description:** Application icon displayed in the sidebar header
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { Building } from "lucide-react";
|
|
60
|
+
|
|
61
|
+
<AppShell title="My App" icon={<Building />} modules={modules}>
|
|
62
|
+
{/* ... */}
|
|
63
|
+
</AppShell>;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### favicon
|
|
67
|
+
|
|
68
|
+
- **Type:** `string` (optional)
|
|
69
|
+
- **Default:** Bundled default favicon set
|
|
70
|
+
- **Description:** Browser-tab favicon href. Accepts any value valid for `<link rel="icon">` — a public-path URL (e.g. `/favicon.ico`) or a data URI. When provided, AppShell renders a single `<link rel="icon">` with this href, replacing the default favicon set.
|
|
71
|
+
|
|
72
|
+
When `favicon` is omitted and the host page declares no `<link rel="icon">`, AppShell automatically injects a small default favicon set as embedded data URIs (no asset-copy step required):
|
|
73
|
+
|
|
74
|
+
- 16×16 PNG tab icon
|
|
75
|
+
- 32×32 PNG tab icon
|
|
76
|
+
- 180×180 Apple touch icon (covers "Add to Home Screen" on iOS and Android)
|
|
77
|
+
|
|
78
|
+
If the host page already declares a `<link rel="icon">`, AppShell leaves it untouched.
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
<AppShell favicon="/favicon.ico" modules={modules}>
|
|
82
|
+
{/* ... */}
|
|
83
|
+
</AppShell>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### basePath
|
|
87
|
+
|
|
88
|
+
- **Type:** `string` (optional)
|
|
89
|
+
- **Default:** `""`
|
|
90
|
+
- **Description:** Base path prefix for all routes
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
// Routes will be /app/dashboard, /app/products, etc.
|
|
94
|
+
<AppShell basePath="/app" modules={modules}>
|
|
95
|
+
{/* ... */}
|
|
96
|
+
</AppShell>
|
|
97
|
+
|
|
98
|
+
// Routes will be /dashboard, /products, etc.
|
|
99
|
+
<AppShell basePath="" modules={modules}>
|
|
100
|
+
{/* ... */}
|
|
101
|
+
</AppShell>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### modules
|
|
105
|
+
|
|
106
|
+
- **Type:** `Module[]` (required when not using file-based routing)
|
|
107
|
+
- **Description:** Array of module definitions that define your application structure
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { defineModule, defineResource } from "@tailor-platform/app-shell";
|
|
111
|
+
|
|
112
|
+
const modules = [
|
|
113
|
+
defineModule({
|
|
114
|
+
path: "products",
|
|
115
|
+
component: ProductsListPage,
|
|
116
|
+
meta: { title: "Products" },
|
|
117
|
+
resources: [
|
|
118
|
+
defineResource({
|
|
119
|
+
path: ":id",
|
|
120
|
+
component: ProductDetailPage,
|
|
121
|
+
meta: { title: "Product Details" },
|
|
122
|
+
}),
|
|
123
|
+
],
|
|
124
|
+
}),
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
<AppShell modules={modules}>{/* ... */}</AppShell>;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
[Learn more about Modules and Resources →](../concepts/modules-and-resources.md)
|
|
131
|
+
|
|
132
|
+
### rootComponent
|
|
133
|
+
|
|
134
|
+
- **Type:** `() => React.ReactNode` (optional)
|
|
135
|
+
- **Description:** Component to render at the root path (e.g., `/app/`)
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
<AppShell basePath="/app" modules={modules} rootComponent={() => <HomePage />}>
|
|
139
|
+
{/* ... */}
|
|
140
|
+
</AppShell>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
When `rootComponent` is set, the root page is treated as a first-class navigation item: it appears in `DefaultSidebar` and `CommandPalette` just like any other module. The title defaults to the localized `"Home"` / `"ホーム"` if no explicit title is provided.
|
|
144
|
+
|
|
145
|
+
> **Tip:** For redirects from the root, use a guard with `redirectTo()` instead
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { redirectTo } from "@tailor-platform/app-shell";
|
|
149
|
+
|
|
150
|
+
<AppShell basePath="/app" modules={modules} rootComponent={() => redirectTo("/app/dashboard")}>
|
|
151
|
+
{/* ... */}
|
|
152
|
+
</AppShell>;
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
> **Note:** If a module with `path: ""` is present in `modules`, it takes precedence over `rootComponent`.
|
|
156
|
+
|
|
157
|
+
### settingsResources
|
|
158
|
+
|
|
159
|
+
- **Type:** `Resource[]` (optional)
|
|
160
|
+
- **Description:** Resources to include in the settings menu dropdown
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import { defineResource } from "@tailor-platform/app-shell";
|
|
164
|
+
|
|
165
|
+
const settingsResources = [
|
|
166
|
+
defineResource({
|
|
167
|
+
path: "profile",
|
|
168
|
+
component: ProfileSettingsPage,
|
|
169
|
+
meta: { title: "Profile" },
|
|
170
|
+
}),
|
|
171
|
+
defineResource({
|
|
172
|
+
path: "billing",
|
|
173
|
+
component: BillingSettingsPage,
|
|
174
|
+
meta: { title: "Billing" },
|
|
175
|
+
}),
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
<AppShell modules={modules} settingsResources={settingsResources}>
|
|
179
|
+
{/* ... */}
|
|
180
|
+
</AppShell>;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Settings appear in a dropdown menu in the sidebar header, accessible via the settings icon.
|
|
184
|
+
|
|
185
|
+
### defaultColorTheme
|
|
186
|
+
|
|
187
|
+
- **Type:** `"light" | "dark" | "system"` (optional)
|
|
188
|
+
- **Default:** `"system"`
|
|
189
|
+
- **Description:** Initial color mode applied before any value is loaded from `localStorage`. Does not override a stored user preference. `"system"` follows the OS light/dark setting.
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
<AppShell defaultColorTheme="light" modules={modules}>
|
|
193
|
+
{/* ... */}
|
|
194
|
+
</AppShell>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The end user's selection (via [`AppearanceSwitcher`](./appearance-switcher.md) or a custom toggle using [`useTheme`](../api/use-theme.md)) is persisted automatically and takes precedence over this prop on subsequent visits.
|
|
198
|
+
|
|
199
|
+
### locale
|
|
200
|
+
|
|
201
|
+
- **Type:** `string` (optional)
|
|
202
|
+
- **Default:** Auto-detected from browser, falls back to `"en"`
|
|
203
|
+
- **Description:** Locale code for built-in UI strings
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
<AppShell locale="ja" modules={modules}>
|
|
207
|
+
{/* ... */}
|
|
208
|
+
</AppShell>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Supported locales: `en`, `ja`
|
|
212
|
+
|
|
213
|
+
[Learn more about Internationalization →](../api/define-i18n-labels.md)
|
|
214
|
+
|
|
215
|
+
### timeZone
|
|
216
|
+
|
|
217
|
+
- **Type:** `string` (optional)
|
|
218
|
+
- **Default:** User's local timezone
|
|
219
|
+
- **Description:** IANA timezone (e.g. `"America/Los_Angeles"`) used by date/time components as the default for resolving "today" and for `ZonedDateTime` values. When omitted, date/time components fall back to the user's local timezone.
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
<AppShell timeZone="America/Los_Angeles" modules={modules}>
|
|
223
|
+
{/* ... */}
|
|
224
|
+
</AppShell>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Access the configured timezone in components using [`useTimeZone`](../api/use-time-zone.md).
|
|
228
|
+
|
|
229
|
+
### errorBoundary
|
|
230
|
+
|
|
231
|
+
- **Type:** `ErrorBoundaryComponent` (optional)
|
|
232
|
+
- **Description:** Global error boundary component applied to all routes
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
import { useRouteError } from "@tailor-platform/app-shell";
|
|
236
|
+
|
|
237
|
+
const GlobalErrorBoundary = () => {
|
|
238
|
+
const error = useRouteError() as Error;
|
|
239
|
+
return (
|
|
240
|
+
<div className="p-8">
|
|
241
|
+
<h1 className="text-xl font-bold mb-4">Something went wrong</h1>
|
|
242
|
+
<p className="text-red-600">{error.message}</p>
|
|
243
|
+
</div>
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
<AppShell modules={modules} errorBoundary={GlobalErrorBoundary}>
|
|
248
|
+
{/* ... */}
|
|
249
|
+
</AppShell>;
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
> **Note:** Module and resource-level error boundaries take precedence over the global error boundary.
|
|
253
|
+
|
|
254
|
+
### contextData
|
|
255
|
+
|
|
256
|
+
- **Type:** `ContextData` (optional)
|
|
257
|
+
- **Description:** Custom context data accessible from guards and components via `useAppShell()`
|
|
258
|
+
|
|
259
|
+
First, define your context data type using module augmentation:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// types.d.ts — `User` and `ApiClient` are your application's own types
|
|
263
|
+
declare module "@tailor-platform/app-shell" {
|
|
264
|
+
interface AppShellRegister {
|
|
265
|
+
contextData: {
|
|
266
|
+
apiClient: ApiClient;
|
|
267
|
+
currentUser: User | null;
|
|
268
|
+
tenantId: string;
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Then pass the data to AppShell:
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
// App.tsx
|
|
278
|
+
<AppShell
|
|
279
|
+
modules={modules}
|
|
280
|
+
contextData={{
|
|
281
|
+
apiClient,
|
|
282
|
+
currentUser,
|
|
283
|
+
tenantId: "tenant-123",
|
|
284
|
+
}}
|
|
285
|
+
>
|
|
286
|
+
{/* ... */}
|
|
287
|
+
</AppShell>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Access the context data in your components:
|
|
291
|
+
|
|
292
|
+
```tsx
|
|
293
|
+
import { useAppShell } from "@tailor-platform/app-shell";
|
|
294
|
+
|
|
295
|
+
function MyComponent() {
|
|
296
|
+
const { contextData } = useAppShell();
|
|
297
|
+
|
|
298
|
+
// Fully typed!
|
|
299
|
+
const user = contextData.currentUser;
|
|
300
|
+
const client = contextData.apiClient;
|
|
301
|
+
|
|
302
|
+
return <div>Welcome, {user?.name}</div>;
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Or in guards:
|
|
307
|
+
|
|
308
|
+
```tsx
|
|
309
|
+
import { pass, hidden } from "@tailor-platform/app-shell";
|
|
310
|
+
|
|
311
|
+
const requireAuth: Guard = ({ context }) => {
|
|
312
|
+
if (!context.currentUser) {
|
|
313
|
+
return redirectTo("/login");
|
|
314
|
+
}
|
|
315
|
+
return pass();
|
|
316
|
+
};
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### searchSources
|
|
320
|
+
|
|
321
|
+
- **Type:** `readonly SearchSource[]` (optional)
|
|
322
|
+
- **Description:** Async search sources wired into the built-in CommandPalette. Each source is activated by typing its `prefix` followed by `:` in the palette. When active, the Actions and Pages sections are hidden and only results from that source are shown. The empty-input state of the palette lists all registered sources in a **Search Modes** section so users can discover them with a single click.
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
import { AppShell, type SearchSource } from "@tailor-platform/app-shell";
|
|
326
|
+
|
|
327
|
+
const searchSources: readonly SearchSource[] = [
|
|
328
|
+
{
|
|
329
|
+
prefix: "ORD",
|
|
330
|
+
title: "Orders",
|
|
331
|
+
search: async (query, { signal }) => {
|
|
332
|
+
const results = await api.searchOrders(query, { signal });
|
|
333
|
+
return results.map((o) => ({
|
|
334
|
+
key: o.id,
|
|
335
|
+
label: o.number,
|
|
336
|
+
path: `/orders/${o.id}`,
|
|
337
|
+
}));
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
];
|
|
341
|
+
|
|
342
|
+
<AppShell modules={modules} searchSources={searchSources}>
|
|
343
|
+
<SidebarLayout sidebar={<DefaultSidebar />} />
|
|
344
|
+
</AppShell>;
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
See [`SearchSource`](./command-palette.md#searchsource) for the full type reference.
|
|
348
|
+
|
|
349
|
+
> **Note:** `DefaultSidebar` always renders a **Search** entry that opens the palette regardless of whether `searchSources` is configured. The `Cmd+K` / `Ctrl+K` shortcut also works globally.
|
|
350
|
+
|
|
351
|
+
### appInfo
|
|
352
|
+
|
|
353
|
+
- **Type:** `AppInfo` (optional)
|
|
354
|
+
- **Description:** Configuration for the built-in `/__appinfo` page, which exposes app metadata and the current AppShell version. The page is excluded from sidebar navigation but appears in the Command Palette as a page entry and includes a copy button for the rendered information.
|
|
355
|
+
|
|
356
|
+
`AppInfo` has the following shape:
|
|
357
|
+
|
|
358
|
+
| Property | Type | Description |
|
|
359
|
+
| ---------- | ------------------------- | ---------------------------------------------- |
|
|
360
|
+
| `metadata` | `readonly AppInfoEntry[]` | Additional rows shown on the `/__appinfo` page |
|
|
361
|
+
|
|
362
|
+
Each `AppInfoEntry` has:
|
|
363
|
+
|
|
364
|
+
| Property | Type | Description |
|
|
365
|
+
| -------- | -------------------------------------------------- | ------------------------------- |
|
|
366
|
+
| `label` | `string` | Display label shown on the page |
|
|
367
|
+
| `value` | `string \| number \| boolean \| null \| undefined` | Value rendered as text |
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
<AppShell
|
|
371
|
+
title="My App"
|
|
372
|
+
appInfo={{
|
|
373
|
+
metadata: [
|
|
374
|
+
{ label: "Environment", value: "staging" },
|
|
375
|
+
{ label: "Release", value: "2026.07.16" },
|
|
376
|
+
],
|
|
377
|
+
}}
|
|
378
|
+
modules={modules}
|
|
379
|
+
>
|
|
380
|
+
{/* ... */}
|
|
381
|
+
</AppShell>
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### children
|
|
385
|
+
|
|
386
|
+
- **Type:** `React.ReactNode` (required)
|
|
387
|
+
- **Description:** Layout component that renders your application content
|
|
388
|
+
|
|
389
|
+
Typically, you'll use `SidebarLayout`:
|
|
390
|
+
|
|
391
|
+
```tsx
|
|
392
|
+
import { SidebarLayout } from "@tailor-platform/app-shell";
|
|
393
|
+
|
|
394
|
+
<AppShell modules={modules}>
|
|
395
|
+
<SidebarLayout />
|
|
396
|
+
</AppShell>;
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Or create a custom layout:
|
|
400
|
+
|
|
401
|
+
```tsx
|
|
402
|
+
<AppShell modules={modules}>
|
|
403
|
+
<CustomLayout />
|
|
404
|
+
</AppShell>
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## File-Based Routing Mode
|
|
408
|
+
|
|
409
|
+
When using the Vite plugin for file-based routing, use `AppShell` exactly as you would without the plugin — no `modules` prop required. The plugin intercepts the `@tailor-platform/app-shell` import and wraps `AppShell` with pages pre-configured via `AppShell.WithPages()` internally.
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
// App.tsx
|
|
413
|
+
import { AppShell, SidebarLayout, DefaultSidebar } from "@tailor-platform/app-shell";
|
|
414
|
+
|
|
415
|
+
// No modules prop needed — the plugin injects pages automatically
|
|
416
|
+
<AppShell title="My App">
|
|
417
|
+
<SidebarLayout sidebar={<DefaultSidebar />} />
|
|
418
|
+
</AppShell>;
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
> **Note:** `AppShell.WithPages` is an internal method used by the Vite plugin. Do not call it directly in application code.
|
|
422
|
+
|
|
423
|
+
[Learn more about File-Based Routing →](../concepts/file-based-routing.md)
|
|
424
|
+
|
|
425
|
+
## Complete Example
|
|
426
|
+
|
|
427
|
+
Here's a complete example with all common features:
|
|
428
|
+
|
|
429
|
+
```tsx
|
|
430
|
+
import {
|
|
431
|
+
AppShell,
|
|
432
|
+
SidebarLayout,
|
|
433
|
+
defineModule,
|
|
434
|
+
defineResource,
|
|
435
|
+
useRouteError,
|
|
436
|
+
} from "@tailor-platform/app-shell";
|
|
437
|
+
import { Building } from "lucide-react";
|
|
438
|
+
|
|
439
|
+
// Define context data type
|
|
440
|
+
declare module "@tailor-platform/app-shell" {
|
|
441
|
+
interface AppShellRegister {
|
|
442
|
+
contextData: {
|
|
443
|
+
currentUser: User | null;
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Error boundary
|
|
449
|
+
const ErrorBoundary = () => {
|
|
450
|
+
const error = useRouteError() as Error;
|
|
451
|
+
return (
|
|
452
|
+
<div className="p-8">
|
|
453
|
+
<h1 className="text-xl font-bold">Error</h1>
|
|
454
|
+
<p>{error.message}</p>
|
|
455
|
+
</div>
|
|
456
|
+
);
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// Modules
|
|
460
|
+
const modules = [
|
|
461
|
+
defineModule({
|
|
462
|
+
path: "dashboard",
|
|
463
|
+
component: DashboardPage,
|
|
464
|
+
meta: { title: "Dashboard" },
|
|
465
|
+
}),
|
|
466
|
+
defineModule({
|
|
467
|
+
path: "products",
|
|
468
|
+
component: ProductsPage,
|
|
469
|
+
meta: { title: "Products" },
|
|
470
|
+
resources: [
|
|
471
|
+
defineResource({
|
|
472
|
+
path: ":id",
|
|
473
|
+
component: ProductDetailPage,
|
|
474
|
+
}),
|
|
475
|
+
],
|
|
476
|
+
}),
|
|
477
|
+
];
|
|
478
|
+
|
|
479
|
+
// Settings
|
|
480
|
+
const settingsResources = [
|
|
481
|
+
defineResource({
|
|
482
|
+
path: "profile",
|
|
483
|
+
component: ProfilePage,
|
|
484
|
+
meta: { title: "Profile" },
|
|
485
|
+
}),
|
|
486
|
+
];
|
|
487
|
+
|
|
488
|
+
// App
|
|
489
|
+
function App() {
|
|
490
|
+
const currentUser = useCurrentUser();
|
|
491
|
+
|
|
492
|
+
return (
|
|
493
|
+
<AppShell
|
|
494
|
+
title="My ERP App"
|
|
495
|
+
icon={<Building />}
|
|
496
|
+
favicon="/favicon.ico"
|
|
497
|
+
basePath="/app"
|
|
498
|
+
modules={modules}
|
|
499
|
+
settingsResources={settingsResources}
|
|
500
|
+
locale="en"
|
|
501
|
+
defaultColorTheme="system"
|
|
502
|
+
errorBoundary={ErrorBoundary}
|
|
503
|
+
contextData={{ currentUser }}
|
|
504
|
+
>
|
|
505
|
+
<SidebarLayout />
|
|
506
|
+
</AppShell>
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export default App;
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## Related Components
|
|
514
|
+
|
|
515
|
+
- [SidebarLayout](./sidebar-layout.md) - Default layout with sidebar navigation
|
|
516
|
+
- [CommandPalette](./command-palette.md) - Keyboard-driven navigation
|
|
517
|
+
|
|
518
|
+
## Related Concepts
|
|
519
|
+
|
|
520
|
+
- [Modules and Resources](../concepts/modules-and-resources.md) - Application structure
|
|
521
|
+
- [Authentication](../concepts/authentication.md) - User authentication setup
|
|
522
|
+
- [Routing and Navigation](../concepts/routing-navigation.md) - Navigation between pages
|
|
523
|
+
|
|
524
|
+
## API Reference
|
|
525
|
+
|
|
526
|
+
- [defineModule](../api/define-module.md) - Define a top-level module
|
|
527
|
+
- [defineResource](../api/define-resource.md) - Define a nested resource
|
|
528
|
+
- [useAppShell](../api/use-app-shell.md) - Access AppShell context
|
|
529
|
+
- [useResolvedLocale](../api/use-resolved-locale.md) - Access the full BCP-47 locale and language code
|
|
530
|
+
- [useTimeZone](../api/use-time-zone.md) - Access the configured IANA timezone
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: AppearanceSwitcher
|
|
3
|
+
description: Pre-built dropdown for toggling the application color theme
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- GENERATED by docs-kit from docs-src/components/appearance-switcher.docs.outline.md — do not edit by hand. -->
|
|
7
|
+
|
|
8
|
+
# AppearanceSwitcher
|
|
9
|
+
|
|
10
|
+
`AppearanceSwitcher` is a ready-made button and dropdown menu that lets end users switch between **Light**, **Dark**, and **System** color modes. It reads and writes the theme through [`useTheme`](../api/use-theme.md) and requires no configuration.
|
|
11
|
+
|
|
12
|
+
## Import
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { AppearanceSwitcher } 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 <AppearanceSwitcher />;
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Drop it anywhere inside an `<AppShell>` tree — for example in a sidebar footer or a settings toolbar.
|
|
29
|
+
|
|
30
|
+
It is also the default action in [`SidebarLayout.DefaultHeader`](./default-header.md). Because `DefaultHeader`'s `actions` prop **replaces** the entire right-hand cluster, you must include `<AppearanceSwitcher />` in your `actions` array if you customize the header and still want the switcher:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<SidebarLayout
|
|
34
|
+
header={
|
|
35
|
+
<SidebarLayout.DefaultHeader
|
|
36
|
+
actions={[<NotificationBell key="bell" />, <AppearanceSwitcher key="appearance" />]}
|
|
37
|
+
/>
|
|
38
|
+
}
|
|
39
|
+
/>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Props
|
|
43
|
+
|
|
44
|
+
`AppearanceSwitcher` accepts no props. Color theme state is managed internally via `useTheme`.
|
|
45
|
+
|
|
46
|
+
## Behavior
|
|
47
|
+
|
|
48
|
+
- Renders a palette icon button. Clicking it opens a popover menu with three radio options: **Light**, **Dark**, and **System**.
|
|
49
|
+
- The active option is highlighted.
|
|
50
|
+
- When **System** is selected, the button tooltip shows the currently resolved mode (e.g. _"Following system — currently dark"_).
|
|
51
|
+
- The selected preference is persisted to `localStorage` (key `appshell-ui-theme`) and restored on subsequent visits.
|
|
52
|
+
|
|
53
|
+
## Example: Sidebar Footer
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import {
|
|
57
|
+
AppShell,
|
|
58
|
+
SidebarLayout,
|
|
59
|
+
DefaultSidebar,
|
|
60
|
+
AppearanceSwitcher,
|
|
61
|
+
} from "@tailor-platform/app-shell";
|
|
62
|
+
|
|
63
|
+
function CustomSidebar() {
|
|
64
|
+
return (
|
|
65
|
+
<DefaultSidebar
|
|
66
|
+
footer={
|
|
67
|
+
<div className="flex items-center justify-end p-2">
|
|
68
|
+
<AppearanceSwitcher />
|
|
69
|
+
</div>
|
|
70
|
+
}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function App() {
|
|
76
|
+
return (
|
|
77
|
+
<AppShell title="My App" modules={modules}>
|
|
78
|
+
<SidebarLayout sidebar={<CustomSidebar />} />
|
|
79
|
+
</AppShell>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Related
|
|
85
|
+
|
|
86
|
+
- [useTheme](../api/use-theme.md) — hook to read and set the color theme programmatically
|
|
87
|
+
- [Styling & Theming](../concepts/styling-theming.md) — color themes and palette overview
|