@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,458 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: SidebarItem
|
|
3
|
+
description: Individual navigation item for customizing sidebar with icons, active states, and external links
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- GENERATED by docs-kit from docs-src/components/sidebar-item.docs.outline.md — do not edit by hand. -->
|
|
7
|
+
|
|
8
|
+
# SidebarItem
|
|
9
|
+
|
|
10
|
+
`SidebarItem` represents an individual navigation link in the sidebar. It automatically resolves titles and icons from resource definitions, supports localized title overrides, and provides active state highlighting.
|
|
11
|
+
|
|
12
|
+
## Import
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { SidebarItem } from "@tailor-platform/app-shell";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Basic Usage
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { DefaultSidebar, SidebarItem } from "@tailor-platform/app-shell";
|
|
22
|
+
|
|
23
|
+
<DefaultSidebar>
|
|
24
|
+
<SidebarItem to="/dashboard" />
|
|
25
|
+
<SidebarItem to="/products" />
|
|
26
|
+
<SidebarItem to="/orders" />
|
|
27
|
+
</DefaultSidebar>;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
When you only provide `to`, the title and icon are automatically resolved from the matching resource's `meta` property.
|
|
31
|
+
|
|
32
|
+
## Props
|
|
33
|
+
|
|
34
|
+
| Prop | Type | Default | Description |
|
|
35
|
+
| ------------- | ---------------------- | ------------- | ---------------------------------------------- |
|
|
36
|
+
| `to` | `string` | **Required** | Target URL (internal or external) |
|
|
37
|
+
| `title` | `LocalizedString` | Auto-resolved | Override the display title (i18n supported) |
|
|
38
|
+
| `icon` | `React.ReactNode` | Auto-resolved | Override the icon |
|
|
39
|
+
| `external` | `boolean` | `false` | Opens link in new tab with external icon |
|
|
40
|
+
| `activeMatch` | `"exact" \| "prefix"` | `"prefix"` | How to match the current path for active state |
|
|
41
|
+
| `render` | `(props) => ReactNode` | - | Custom render function for full control |
|
|
42
|
+
|
|
43
|
+
## Auto-Resolution
|
|
44
|
+
|
|
45
|
+
### Title Resolution
|
|
46
|
+
|
|
47
|
+
The title is automatically resolved from the resource's `meta.title`:
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
// In your module definition
|
|
51
|
+
const dashboardModule = defineModule({
|
|
52
|
+
path: "dashboard",
|
|
53
|
+
component: DashboardPage,
|
|
54
|
+
meta: {
|
|
55
|
+
title: "Dashboard", // ← Automatically used
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// In sidebar - no title needed!
|
|
60
|
+
<SidebarItem to="/dashboard" />;
|
|
61
|
+
// Renders as: Dashboard
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For the root path (`/` or `""`), when no `meta.title` is set, the title falls back to the localized string `"Home"` (English) or `"ホーム"` (Japanese).
|
|
65
|
+
|
|
66
|
+
### Icon Resolution
|
|
67
|
+
|
|
68
|
+
Icons are resolved from the module's `meta.icon`:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { Home, Package } from "lucide-react";
|
|
72
|
+
|
|
73
|
+
const modules = [
|
|
74
|
+
defineModule({
|
|
75
|
+
path: "dashboard",
|
|
76
|
+
meta: {
|
|
77
|
+
title: "Dashboard",
|
|
78
|
+
icon: <Home />, // ← Automatically used in sidebar
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
defineModule({
|
|
82
|
+
path: "products",
|
|
83
|
+
meta: {
|
|
84
|
+
title: "Products",
|
|
85
|
+
icon: <Package />,
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
<DefaultSidebar>
|
|
91
|
+
<SidebarItem to="/dashboard" /> {/* Shows Home icon */}
|
|
92
|
+
<SidebarItem to="/products" /> {/* Shows Package icon */}
|
|
93
|
+
</DefaultSidebar>;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Manual Override
|
|
97
|
+
|
|
98
|
+
### Override Title
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<SidebarItem
|
|
102
|
+
to="/dashboard"
|
|
103
|
+
title="My Dashboard" // Override auto-resolved title
|
|
104
|
+
/>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Localized Override
|
|
108
|
+
|
|
109
|
+
Pass a `LocalizedString`, such as a value from `defineI18nLabels`, to resolve the override using the current AppShell locale:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
<SidebarItem to="/dashboard" title={labels.t("dashboard")} />
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Override Icon
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { Home } from "lucide-react";
|
|
119
|
+
|
|
120
|
+
<SidebarItem
|
|
121
|
+
to="/dashboard"
|
|
122
|
+
icon={<Home />} // Override auto-resolved icon
|
|
123
|
+
/>;
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Both Title and Icon
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { Settings } from "lucide-react";
|
|
130
|
+
|
|
131
|
+
<SidebarItem to="/settings" title="Settings" icon={<Settings />} />;
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## External Links
|
|
135
|
+
|
|
136
|
+
Open links in a new tab with an external link icon:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import { ExternalLink } from "lucide-react";
|
|
140
|
+
|
|
141
|
+
<SidebarItem
|
|
142
|
+
to="https://docs.example.com"
|
|
143
|
+
title="Documentation"
|
|
144
|
+
icon={<ExternalLink />}
|
|
145
|
+
external
|
|
146
|
+
/>;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The external link icon is automatically added to the end of the item.
|
|
150
|
+
|
|
151
|
+
## Active State Matching
|
|
152
|
+
|
|
153
|
+
Control how the active state is determined:
|
|
154
|
+
|
|
155
|
+
### Prefix Matching (Default)
|
|
156
|
+
|
|
157
|
+
Highlights when the current path **starts with** the `to` path:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
<SidebarItem to="/products" activeMatch="prefix" />
|
|
161
|
+
|
|
162
|
+
// Active when:
|
|
163
|
+
// - /products
|
|
164
|
+
// - /products/123
|
|
165
|
+
// - /products/categories
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Exact Matching
|
|
169
|
+
|
|
170
|
+
Highlights only when the current path **exactly matches** the `to` path:
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
<SidebarItem to="/products" activeMatch="exact" />
|
|
174
|
+
|
|
175
|
+
// Active when:
|
|
176
|
+
// - /products (only)
|
|
177
|
+
|
|
178
|
+
// NOT active when:
|
|
179
|
+
// - /products/123
|
|
180
|
+
// - /products/categories
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Custom Rendering
|
|
184
|
+
|
|
185
|
+
For full control, use the `render` prop:
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
<SidebarItem
|
|
189
|
+
to="/notifications"
|
|
190
|
+
render={({ title, icon, isActive }) => (
|
|
191
|
+
<div className="flex items-center gap-2">
|
|
192
|
+
{icon}
|
|
193
|
+
<span>{title}</span>
|
|
194
|
+
{isActive && <Badge>New</Badge>}
|
|
195
|
+
<Badge variant="error">5</Badge>
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The render function receives:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
type SidebarItemRenderProps = {
|
|
205
|
+
title: string; // Auto-resolved or overridden
|
|
206
|
+
url: string; // The `to` prop value
|
|
207
|
+
icon?: ReactNode; // Auto-resolved or overridden
|
|
208
|
+
isActive: boolean; // Whether this item matches the current route
|
|
209
|
+
};
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Examples
|
|
213
|
+
|
|
214
|
+
### Basic Sidebar
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
<DefaultSidebar>
|
|
218
|
+
<SidebarItem to="/dashboard" />
|
|
219
|
+
<SidebarItem to="/products" />
|
|
220
|
+
<SidebarItem to="/orders" />
|
|
221
|
+
<SidebarItem to="/customers" />
|
|
222
|
+
</DefaultSidebar>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### With Manual Overrides
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
import { Home, Package, ShoppingCart, Users } from "lucide-react";
|
|
229
|
+
|
|
230
|
+
<DefaultSidebar>
|
|
231
|
+
<SidebarItem to="/" title="Home" icon={<Home />} />
|
|
232
|
+
<SidebarItem to="/products" title="Products" icon={<Package />} />
|
|
233
|
+
<SidebarItem to="/orders" title="Orders" icon={<ShoppingCart />} />
|
|
234
|
+
<SidebarItem to="/customers" title="Customers" icon={<Users />} />
|
|
235
|
+
</DefaultSidebar>;
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### With External Links
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
import { HelpCircle, FileText, ExternalLink } from "lucide-react";
|
|
242
|
+
|
|
243
|
+
<DefaultSidebar>
|
|
244
|
+
<SidebarItem to="/dashboard" />
|
|
245
|
+
<SidebarItem to="/products" />
|
|
246
|
+
|
|
247
|
+
<SidebarSeparator />
|
|
248
|
+
|
|
249
|
+
<SidebarItem to="https://docs.example.com" title="Documentation" icon={<FileText />} external />
|
|
250
|
+
<SidebarItem to="https://support.example.com" title="Support" icon={<HelpCircle />} external />
|
|
251
|
+
</DefaultSidebar>;
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### With Notification Badge
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
<SidebarItem
|
|
258
|
+
to="/notifications"
|
|
259
|
+
render={({ title, icon, isActive }) => (
|
|
260
|
+
<>
|
|
261
|
+
{icon}
|
|
262
|
+
<span>{title}</span>
|
|
263
|
+
<Badge variant="error">12</Badge>
|
|
264
|
+
</>
|
|
265
|
+
)}
|
|
266
|
+
/>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### With Active Indicator
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
<SidebarItem
|
|
273
|
+
to="/messages"
|
|
274
|
+
render={({ title, icon, isActive }) => (
|
|
275
|
+
<>
|
|
276
|
+
{icon}
|
|
277
|
+
<span className={isActive ? "font-bold" : undefined}>{title}</span>
|
|
278
|
+
{isActive && <span className="ml-auto">•</span>}
|
|
279
|
+
</>
|
|
280
|
+
)}
|
|
281
|
+
/>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Exact Match for Root
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
<DefaultSidebar>
|
|
288
|
+
<SidebarItem to="/" activeMatch="exact" />
|
|
289
|
+
<SidebarItem to="/dashboard" />
|
|
290
|
+
<SidebarItem to="/products" />
|
|
291
|
+
</DefaultSidebar>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Without `activeMatch="exact"`, the root item would be active on all pages since every path starts with `/`.
|
|
295
|
+
|
|
296
|
+
### Conditional Rendering with Guards
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
import { WithGuard } from "@tailor-platform/app-shell";
|
|
300
|
+
|
|
301
|
+
const isAdmin = ({ context }) => (context.currentUser?.role === "admin" ? pass() : hidden());
|
|
302
|
+
|
|
303
|
+
<DefaultSidebar>
|
|
304
|
+
<SidebarItem to="/dashboard" />
|
|
305
|
+
<SidebarItem to="/products" />
|
|
306
|
+
|
|
307
|
+
<WithGuard guards={[isAdmin]}>
|
|
308
|
+
<SidebarItem to="/admin" title="Admin Panel" />
|
|
309
|
+
</WithGuard>
|
|
310
|
+
</DefaultSidebar>;
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### In SidebarGroup
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
import { SidebarGroup, SidebarItem } from "@tailor-platform/app-shell";
|
|
317
|
+
import { Package } from "lucide-react";
|
|
318
|
+
|
|
319
|
+
<SidebarGroup title="Products" icon={<Package />}>
|
|
320
|
+
<SidebarItem to="/products/all" />
|
|
321
|
+
<SidebarItem to="/products/categories" />
|
|
322
|
+
<SidebarItem to="/products/brands" />
|
|
323
|
+
</SidebarGroup>;
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Styling
|
|
327
|
+
|
|
328
|
+
### Active State
|
|
329
|
+
|
|
330
|
+
Active items pick up the sidebar accent background automatically. The class carrying it is internal to the library — don't target it. Override the token instead:
|
|
331
|
+
|
|
332
|
+
```css
|
|
333
|
+
:root {
|
|
334
|
+
--sidebar-accent: oklch(0.95 0.02 260);
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Custom Styling
|
|
339
|
+
|
|
340
|
+
Add custom classes via the `render` prop:
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
<SidebarItem
|
|
344
|
+
to="/special"
|
|
345
|
+
render={({ title, icon, isActive }) => (
|
|
346
|
+
<div className={cn("flex items-center gap-2", isActive && "border-l-4 border-primary")}>
|
|
347
|
+
{icon}
|
|
348
|
+
{title}
|
|
349
|
+
</div>
|
|
350
|
+
)}
|
|
351
|
+
/>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Accessibility
|
|
355
|
+
|
|
356
|
+
- Semantic `<a>` tag for links
|
|
357
|
+
- Proper `href` attribute
|
|
358
|
+
- Active state announced to screen readers
|
|
359
|
+
- External links open in new tab with `rel="noopener noreferrer"`
|
|
360
|
+
- Keyboard navigation support (Tab, Enter)
|
|
361
|
+
|
|
362
|
+
## Best Practices
|
|
363
|
+
|
|
364
|
+
### Do:
|
|
365
|
+
|
|
366
|
+
- ✅ Let AppShell auto-resolve title/icon when possible
|
|
367
|
+
- ✅ Use `external` for links outside your app
|
|
368
|
+
- ✅ Use `exact` match for root routes
|
|
369
|
+
- ✅ Group related items with `SidebarGroup`
|
|
370
|
+
- ✅ Keep item labels short (1-2 words)
|
|
371
|
+
|
|
372
|
+
### Don't:
|
|
373
|
+
|
|
374
|
+
- ❌ Hardcode titles that exist in resource meta
|
|
375
|
+
- ❌ Forget `external` for external links
|
|
376
|
+
- ❌ Use prefix match for root route (`/`)
|
|
377
|
+
- ❌ Mix internal and external links without visual distinction
|
|
378
|
+
- ❌ Create deeply nested navigation (use breadcrumbs instead)
|
|
379
|
+
|
|
380
|
+
## Comparison: Auto vs Manual
|
|
381
|
+
|
|
382
|
+
| Aspect | Auto-Resolved | Manual Override |
|
|
383
|
+
| --------------- | -------------------------- | ---------------------------- |
|
|
384
|
+
| **Title** | From resource `meta.title` | Specify `title` prop |
|
|
385
|
+
| **Icon** | From module `meta.icon` | Specify `icon` prop |
|
|
386
|
+
| **Effort** | Less code, DRY | More flexible |
|
|
387
|
+
| **Maintenance** | Single source of truth | Must update in 2 places |
|
|
388
|
+
| **Use When** | Standard navigation | Custom items, external links |
|
|
389
|
+
|
|
390
|
+
## TypeScript
|
|
391
|
+
|
|
392
|
+
Full type safety:
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
import { type SidebarItemProps, type SidebarItemRenderProps } from "@tailor-platform/app-shell";
|
|
396
|
+
|
|
397
|
+
// Type-safe props
|
|
398
|
+
const itemProps: SidebarItemProps = {
|
|
399
|
+
to: "/dashboard",
|
|
400
|
+
title: "Dashboard",
|
|
401
|
+
activeMatch: "prefix",
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// Type-safe render function
|
|
405
|
+
const customRender = (props: SidebarItemRenderProps) => {
|
|
406
|
+
// props.title, props.icon, props.isActive are all typed
|
|
407
|
+
return <div>{props.title}</div>;
|
|
408
|
+
};
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Troubleshooting
|
|
412
|
+
|
|
413
|
+
### Title/Icon not showing
|
|
414
|
+
|
|
415
|
+
**Problem:** SidebarItem doesn't display title or icon
|
|
416
|
+
|
|
417
|
+
**Solutions:**
|
|
418
|
+
|
|
419
|
+
1. Check that the resource has `meta.title` defined
|
|
420
|
+
2. Verify the `to` prop matches the resource path exactly
|
|
421
|
+
3. Ensure the module has `meta.icon` for icon display
|
|
422
|
+
4. Try manually specifying `title` and `icon` props
|
|
423
|
+
|
|
424
|
+
### Active state not working
|
|
425
|
+
|
|
426
|
+
**Problem:** Item isn't highlighted when on that page
|
|
427
|
+
|
|
428
|
+
**Solutions:**
|
|
429
|
+
|
|
430
|
+
1. Check that the `to` prop matches the current route
|
|
431
|
+
2. Use `exact` match for root routes (`/`)
|
|
432
|
+
3. Use `prefix` match for parent routes with children
|
|
433
|
+
4. Verify you're using React Router's `<Link>` correctly
|
|
434
|
+
|
|
435
|
+
### External link not opening in new tab
|
|
436
|
+
|
|
437
|
+
**Problem:** External link opens in same tab
|
|
438
|
+
|
|
439
|
+
**Solution:** Add `external` prop:
|
|
440
|
+
|
|
441
|
+
```tsx
|
|
442
|
+
<SidebarItem to="https://..." external />
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## Related Components
|
|
446
|
+
|
|
447
|
+
- [SidebarGroup](./sidebar-group.md) - Group multiple items
|
|
448
|
+
- [DefaultSidebar](./sidebar-layout.md) - Container for sidebar items
|
|
449
|
+
- [WithGuard](./with-guard.md) - Conditional rendering
|
|
450
|
+
|
|
451
|
+
## Related Concepts
|
|
452
|
+
|
|
453
|
+
- [Sidebar Navigation](../concepts/sidebar-navigation.md) - Sidebar customization guide
|
|
454
|
+
- [Modules and Resources](../concepts/modules-and-resources.md) - Define routes and meta
|
|
455
|
+
|
|
456
|
+
## API Reference
|
|
457
|
+
|
|
458
|
+
- [usePageMeta](../api/use-page-meta.md) - Hook for resolving page metadata
|