@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.
- package/dist/app-shell-core.css +1 -1
- package/dist/app-shell.d.ts +208 -114
- package/dist/app-shell.js +3449 -3446
- package/dist/{appshell-DLVBFo5J.js → appshell-B0wh5Lq0.js} +625 -512
- package/dist/style-entry.d.ts +116 -96
- package/dist/testing.d.ts +116 -96
- package/dist/testing.js +1 -1
- package/dist/vite-plugin.d.ts +116 -96
- package/package.json +8 -8
- package/skills/app-shell-patterns/SKILL.md +101 -14
- 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} +258 -110
- package/skills/app-shell-patterns/references/migrations.md +34 -0
- package/skills/app-shell-patterns/references/pages/{detail.md → document-detail.md} +4 -27
- package/skills/app-shell-patterns/references/patterns/form-composer.md +43 -45
- 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 +13 -27
- 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 -1009
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Sidebar Navigation
|
|
3
|
+
description: Learn how to customize sidebar navigation with auto-generation or composition mode
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- GENERATED by docs-kit from docs-src/concepts/sidebar-navigation.docs.outline.md — do not edit by hand. -->
|
|
7
|
+
|
|
8
|
+
# Sidebar Navigation
|
|
9
|
+
|
|
10
|
+
AppShell provides flexible sidebar navigation with two modes:
|
|
11
|
+
|
|
12
|
+
- **Auto-generation mode**: Automatically generates navigation from module/resource definitions
|
|
13
|
+
- **Composition mode**: Full control over sidebar structure using declarative components
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
### Auto-generation Mode (Default)
|
|
18
|
+
|
|
19
|
+
The simplest way to use the sidebar - navigation items are automatically generated from your module definitions:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { AppShell, SidebarLayout } from "@tailor-platform/app-shell";
|
|
23
|
+
|
|
24
|
+
const App = () => (
|
|
25
|
+
<AppShell modules={modules}>
|
|
26
|
+
<SidebarLayout />
|
|
27
|
+
</AppShell>
|
|
28
|
+
);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Composition Mode
|
|
32
|
+
|
|
33
|
+
For full control over sidebar structure, pass children to `DefaultSidebar` via the `sidebar` prop:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import {
|
|
37
|
+
AppShell,
|
|
38
|
+
SidebarLayout,
|
|
39
|
+
DefaultSidebar,
|
|
40
|
+
SidebarItem,
|
|
41
|
+
SidebarGroup,
|
|
42
|
+
SidebarSeparator,
|
|
43
|
+
} from "@tailor-platform/app-shell";
|
|
44
|
+
import { Package, Settings } from "lucide-react";
|
|
45
|
+
|
|
46
|
+
const App = () => (
|
|
47
|
+
<AppShell modules={modules}>
|
|
48
|
+
<SidebarLayout
|
|
49
|
+
sidebar={
|
|
50
|
+
<DefaultSidebar>
|
|
51
|
+
<SidebarItem to="/dashboard" />
|
|
52
|
+
<SidebarSeparator />
|
|
53
|
+
<SidebarGroup title="Products" icon={<Package />}>
|
|
54
|
+
<SidebarItem to="/products/all" />
|
|
55
|
+
<SidebarItem to="/products/categories" />
|
|
56
|
+
</SidebarGroup>
|
|
57
|
+
<SidebarItem to="/settings" icon={<Settings />} />
|
|
58
|
+
</DefaultSidebar>
|
|
59
|
+
}
|
|
60
|
+
/>
|
|
61
|
+
</AppShell>
|
|
62
|
+
);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Components
|
|
68
|
+
|
|
69
|
+
### SidebarItem
|
|
70
|
+
|
|
71
|
+
A navigation item that automatically resolves title and icon from resource definitions.
|
|
72
|
+
|
|
73
|
+
#### Props
|
|
74
|
+
|
|
75
|
+
| Prop | Type | Required | Description |
|
|
76
|
+
| ---------- | ----------------- | -------- | ------------------------------------------------------------------------------- |
|
|
77
|
+
| `to` | `string` | Yes | Target URL. External URLs (http://...) are rendered as external links |
|
|
78
|
+
| `title` | `LocalizedString` | No | Override title (i18n supported). When omitted, auto-resolved from resource meta |
|
|
79
|
+
| `icon` | `ReactNode` | No | Override icon. When omitted, auto-resolved from resource meta |
|
|
80
|
+
| `external` | `boolean` | No | Opens link in new tab with external link icon |
|
|
81
|
+
| `render` | `function` | No | Custom render function for full UI control |
|
|
82
|
+
|
|
83
|
+
#### Examples
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
// Auto-resolved from resource meta
|
|
87
|
+
<SidebarItem to="/dashboard" />
|
|
88
|
+
|
|
89
|
+
// Override title and icon
|
|
90
|
+
<SidebarItem to="/" title="Home" icon={<Home />} />
|
|
91
|
+
|
|
92
|
+
// External link
|
|
93
|
+
<SidebarItem to="https://docs.example.com" external />
|
|
94
|
+
|
|
95
|
+
// Custom rendering with render prop
|
|
96
|
+
<SidebarItem
|
|
97
|
+
to="/tasks"
|
|
98
|
+
render={({ title, icon, isActive }) => (
|
|
99
|
+
<div className={isActive ? "font-bold" : ""}>
|
|
100
|
+
{icon}
|
|
101
|
+
<span>{title}</span>
|
|
102
|
+
<Badge>5</Badge>
|
|
103
|
+
</div>
|
|
104
|
+
)}
|
|
105
|
+
/>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### Render Prop
|
|
109
|
+
|
|
110
|
+
When using the `render` prop, you receive `SidebarItemRenderProps`:
|
|
111
|
+
|
|
112
|
+
| Property | Type | Description |
|
|
113
|
+
| ---------- | ------------------------ | ----------------------------------------------- |
|
|
114
|
+
| `title` | `string` | Resolved title (from override or resource meta) |
|
|
115
|
+
| `url` | `string` | Target URL |
|
|
116
|
+
| `icon` | `ReactNode \| undefined` | Resolved icon |
|
|
117
|
+
| `isActive` | `boolean` | Whether this item is currently active |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### SidebarGroup
|
|
122
|
+
|
|
123
|
+
A collapsible group for organizing navigation items.
|
|
124
|
+
|
|
125
|
+
#### Props
|
|
126
|
+
|
|
127
|
+
| Prop | Type | Required | Description |
|
|
128
|
+
| ------------- | ----------------- | -------- | ---------------------------------------------- |
|
|
129
|
+
| `title` | `LocalizedString` | Yes | Group title (supports i18n) |
|
|
130
|
+
| `icon` | `ReactNode` | No | Group icon |
|
|
131
|
+
| `to` | `string` | No | When specified, title becomes a clickable link |
|
|
132
|
+
| `defaultOpen` | `boolean` | No | Initial expanded state. Default: `true` |
|
|
133
|
+
| `children` | `ReactNode` | Yes | Child items (SidebarItem, SidebarGroup, etc.) |
|
|
134
|
+
|
|
135
|
+
#### Examples
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
// Basic group
|
|
139
|
+
<SidebarGroup title="Products" icon={<Package />}>
|
|
140
|
+
<SidebarItem to="/products/all" />
|
|
141
|
+
<SidebarItem to="/products/categories" />
|
|
142
|
+
</SidebarGroup>
|
|
143
|
+
|
|
144
|
+
// Clickable group header
|
|
145
|
+
<SidebarGroup title="Settings" icon={<Settings />} to="/settings">
|
|
146
|
+
<SidebarItem to="/settings/profile" />
|
|
147
|
+
<SidebarItem to="/settings/security" />
|
|
148
|
+
</SidebarGroup>
|
|
149
|
+
|
|
150
|
+
// Initially collapsed
|
|
151
|
+
<SidebarGroup title="Archives" defaultOpen={false}>
|
|
152
|
+
<SidebarItem to="/archives/2024" />
|
|
153
|
+
<SidebarItem to="/archives/2023" />
|
|
154
|
+
</SidebarGroup>
|
|
155
|
+
|
|
156
|
+
// Nested groups
|
|
157
|
+
<SidebarGroup title="Products" icon={<Package />}>
|
|
158
|
+
<SidebarItem to="/products/all" />
|
|
159
|
+
<SidebarGroup title="Archives" defaultOpen={false}>
|
|
160
|
+
<SidebarItem to="/products/archives/2024" />
|
|
161
|
+
<SidebarItem to="/products/archives/2023" />
|
|
162
|
+
</SidebarGroup>
|
|
163
|
+
</SidebarGroup>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### i18n Support
|
|
167
|
+
|
|
168
|
+
Group titles support localized strings:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
import { defineI18nLabels } from "@tailor-platform/app-shell";
|
|
172
|
+
|
|
173
|
+
const labels = defineI18nLabels({
|
|
174
|
+
en: { products: "Products" },
|
|
175
|
+
ja: { products: "製品" },
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
<SidebarGroup title={labels.t("products")} icon={<Package />}>
|
|
179
|
+
<SidebarItem to="/products/all" />
|
|
180
|
+
</SidebarGroup>;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### SidebarSeparator
|
|
186
|
+
|
|
187
|
+
A visual divider between sidebar sections.
|
|
188
|
+
|
|
189
|
+
#### Props
|
|
190
|
+
|
|
191
|
+
This component takes no props.
|
|
192
|
+
|
|
193
|
+
#### Example
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
<DefaultSidebar>
|
|
197
|
+
<SidebarItem to="/dashboard" />
|
|
198
|
+
<SidebarSeparator />
|
|
199
|
+
<SidebarItem to="/settings" />
|
|
200
|
+
</DefaultSidebar>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### DefaultSidebar
|
|
206
|
+
|
|
207
|
+
The main sidebar container that supports both auto-generation and composition modes.
|
|
208
|
+
|
|
209
|
+
#### Props
|
|
210
|
+
|
|
211
|
+
| Prop | Type | Required | Description |
|
|
212
|
+
| ---------- | ----------- | -------- | --------------------------------------- |
|
|
213
|
+
| `header` | `ReactNode` | No | Custom header content |
|
|
214
|
+
| `footer` | `ReactNode` | No | Custom footer content |
|
|
215
|
+
| `children` | `ReactNode` | No | When provided, enables composition mode |
|
|
216
|
+
|
|
217
|
+
#### Examples
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
// Auto-generation mode
|
|
221
|
+
<DefaultSidebar />
|
|
222
|
+
|
|
223
|
+
// With custom header and footer
|
|
224
|
+
<DefaultSidebar
|
|
225
|
+
header={<Logo />}
|
|
226
|
+
footer={<UserMenu />}
|
|
227
|
+
/>
|
|
228
|
+
|
|
229
|
+
// Composition mode with custom navigation
|
|
230
|
+
<DefaultSidebar header={<Logo />}>
|
|
231
|
+
<SidebarItem to="/dashboard" />
|
|
232
|
+
<SidebarGroup title="Products">
|
|
233
|
+
<SidebarItem to="/products/all" />
|
|
234
|
+
</SidebarGroup>
|
|
235
|
+
</DefaultSidebar>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Hooks
|
|
241
|
+
|
|
242
|
+
### usePageMeta
|
|
243
|
+
|
|
244
|
+
A hook to retrieve page metadata (title, icon) for a given URL path.
|
|
245
|
+
|
|
246
|
+
#### Signature
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
const usePageMeta: (path: string) => PageMeta | null;
|
|
250
|
+
|
|
251
|
+
type PageMeta = {
|
|
252
|
+
title: string;
|
|
253
|
+
icon?: ReactNode;
|
|
254
|
+
};
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
#### Parameters
|
|
258
|
+
|
|
259
|
+
| Parameter | Type | Description |
|
|
260
|
+
| --------- | -------- | ------------------------------------------------- |
|
|
261
|
+
| `path` | `string` | URL path to find meta for (e.g., "/products/all") |
|
|
262
|
+
|
|
263
|
+
#### Returns
|
|
264
|
+
|
|
265
|
+
- `PageMeta` - Object containing `title` and optional `icon` if found
|
|
266
|
+
- `null` - For external links or when path is not found
|
|
267
|
+
|
|
268
|
+
#### Example
|
|
269
|
+
|
|
270
|
+
```tsx
|
|
271
|
+
import { usePageMeta } from "@tailor-platform/app-shell";
|
|
272
|
+
|
|
273
|
+
const MyComponent = () => {
|
|
274
|
+
const pageMeta = usePageMeta("/products/all");
|
|
275
|
+
|
|
276
|
+
if (!pageMeta) {
|
|
277
|
+
return <span>Unknown Page</span>;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<div>
|
|
282
|
+
{pageMeta.icon}
|
|
283
|
+
<span>{pageMeta.title}</span>
|
|
284
|
+
</div>
|
|
285
|
+
);
|
|
286
|
+
};
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Access Control
|
|
292
|
+
|
|
293
|
+
### Auto-generation Mode
|
|
294
|
+
|
|
295
|
+
In auto-generation mode, `guards` defined in `defineModule`/`defineResource` are automatically evaluated. Modules/resources that return `hidden()` will not appear in the sidebar.
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
import { defineModule, defineResource, pass, hidden } from "@tailor-platform/app-shell";
|
|
299
|
+
|
|
300
|
+
const adminModule = defineModule({
|
|
301
|
+
path: "admin",
|
|
302
|
+
meta: { title: "Admin" },
|
|
303
|
+
// If this guard returns hidden(), the module won't appear in sidebar
|
|
304
|
+
guards: [({ context }) => (context.currentUser.role === "admin" ? pass() : hidden())],
|
|
305
|
+
resources: [
|
|
306
|
+
defineResource({
|
|
307
|
+
path: "users",
|
|
308
|
+
meta: { title: "Users" },
|
|
309
|
+
component: () => <div>Users</div>,
|
|
310
|
+
}),
|
|
311
|
+
],
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// Used with auto-generation mode
|
|
315
|
+
<DefaultSidebar />;
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Composition Mode
|
|
319
|
+
|
|
320
|
+
In composition mode, use the `WithGuard` component to control visibility of sidebar items. This allows you to reuse the same guard logic used in route definitions.
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
import { WithGuard, pass, hidden } from "@tailor-platform/app-shell";
|
|
324
|
+
|
|
325
|
+
// Define a guard function
|
|
326
|
+
const isAdminGuard = ({ context }) => (context.currentUser.role === "admin" ? pass() : hidden());
|
|
327
|
+
|
|
328
|
+
<DefaultSidebar>
|
|
329
|
+
<SidebarItem to="/dashboard" />
|
|
330
|
+
|
|
331
|
+
{/* Wrap items with WithGuard for conditional visibility */}
|
|
332
|
+
<WithGuard guards={[isAdminGuard]}>
|
|
333
|
+
<SidebarGroup title="Admin" icon={<Shield />}>
|
|
334
|
+
<SidebarItem to="/admin/users" />
|
|
335
|
+
<SidebarItem to="/admin/settings" />
|
|
336
|
+
</SidebarGroup>
|
|
337
|
+
</WithGuard>
|
|
338
|
+
</DefaultSidebar>;
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
#### Curried Guards
|
|
342
|
+
|
|
343
|
+
For parameterized guards, use the curried pattern:
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
// Define a curried guard factory
|
|
347
|
+
const hasRole =
|
|
348
|
+
(role: string) =>
|
|
349
|
+
({ context }) =>
|
|
350
|
+
context.currentUser.role === role ? pass() : hidden();
|
|
351
|
+
|
|
352
|
+
<DefaultSidebar>
|
|
353
|
+
<WithGuard guards={[hasRole("admin")]}>
|
|
354
|
+
<SidebarItem to="/admin" />
|
|
355
|
+
</WithGuard>
|
|
356
|
+
|
|
357
|
+
<WithGuard guards={[hasRole("manager")]}>
|
|
358
|
+
<SidebarItem to="/reports" />
|
|
359
|
+
</WithGuard>
|
|
360
|
+
</DefaultSidebar>;
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
> **Note**: Route-level access control (e.g., redirects) still works via `guards` in `defineResource`, even in composition mode. `WithGuard` only controls visibility—it does not prevent navigation to protected routes.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Migration from Auto-generation
|
|
368
|
+
|
|
369
|
+
If you're currently using auto-generated navigation and want to customize it:
|
|
370
|
+
|
|
371
|
+
1. **Keep auto-generation for most items**, add custom items where needed:
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
// Before: Pure auto-generation
|
|
375
|
+
<DefaultSidebar />
|
|
376
|
+
|
|
377
|
+
// After: Composition with similar structure
|
|
378
|
+
<DefaultSidebar>
|
|
379
|
+
<SidebarItem to="/dashboard" />
|
|
380
|
+
<SidebarItem to="/products" />
|
|
381
|
+
<SidebarSeparator />
|
|
382
|
+
<SidebarItem to="https://docs.example.com" title="Documentation" external />
|
|
383
|
+
</DefaultSidebar>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
2. **Title and icon are auto-resolved** from your resource definitions, so you don't need to specify them unless you want to override.
|
|
387
|
+
|
|
388
|
+
3. **External links** can now be added directly in the sidebar.
|