@ceed/cds 1.28.1 → 1.29.0-next.1
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/components/CurrencyInput/CurrencyInput.d.ts +1 -1
- package/dist/components/CurrencyInput/hooks/use-currency-setting.d.ts +2 -2
- package/dist/components/SearchBar/SearchBar.d.ts +21 -0
- package/dist/components/SearchBar/index.d.ts +3 -0
- package/dist/components/data-display/DataTable.md +1 -1
- package/dist/components/data-display/InfoSign.md +91 -74
- package/dist/components/data-display/Typography.md +94 -411
- package/dist/components/feedback/Dialog.md +62 -76
- package/dist/components/feedback/Modal.md +138 -430
- package/dist/components/feedback/llms.txt +0 -2
- package/dist/components/index.d.ts +2 -1
- package/dist/components/inputs/Autocomplete.md +107 -356
- package/dist/components/inputs/ButtonGroup.md +104 -115
- package/dist/components/inputs/CurrencyInput.md +5 -183
- package/dist/components/inputs/DatePicker.md +431 -108
- package/dist/components/inputs/DateRangePicker.md +492 -131
- package/dist/components/inputs/FilterableCheckboxGroup.md +19 -145
- package/dist/components/inputs/IconButton.md +88 -137
- package/dist/components/inputs/Input.md +73 -204
- package/dist/components/inputs/MonthPicker.md +422 -95
- package/dist/components/inputs/MonthRangePicker.md +466 -89
- package/dist/components/inputs/PercentageInput.md +16 -185
- package/dist/components/inputs/RadioButton.md +35 -163
- package/dist/components/inputs/SearchBar.md +44 -0
- package/dist/components/inputs/Select.md +326 -222
- package/dist/components/inputs/Switch.md +376 -143
- package/dist/components/inputs/Textarea.md +10 -213
- package/dist/components/inputs/Uploader/Uploader.md +66 -145
- package/dist/components/inputs/llms.txt +1 -4
- package/dist/components/navigation/Breadcrumbs.md +308 -57
- package/dist/components/navigation/Drawer.md +0 -180
- package/dist/components/navigation/Dropdown.md +215 -98
- package/dist/components/navigation/IconMenuButton.md +502 -40
- package/dist/components/navigation/InsetDrawer.md +650 -281
- package/dist/components/navigation/Link.md +348 -31
- package/dist/components/navigation/Menu.md +285 -92
- package/dist/components/navigation/MenuButton.md +448 -55
- package/dist/components/navigation/Pagination.md +338 -47
- package/dist/components/navigation/Stepper.md +28 -160
- package/dist/components/navigation/Tabs.md +316 -57
- package/dist/components/surfaces/Accordions.md +804 -49
- package/dist/components/surfaces/Card.md +157 -97
- package/dist/components/surfaces/Divider.md +234 -83
- package/dist/components/surfaces/Sheet.md +328 -153
- package/dist/index.cjs +411 -574
- package/dist/index.d.ts +1 -1
- package/dist/index.js +400 -507
- package/dist/llms.txt +1 -9
- package/framer/index.js +1 -1
- package/package.json +17 -22
- package/dist/chunks/rehype-accent-FZRUD7VI.js +0 -39
- package/dist/components/RadioTileGroup/RadioTileGroup.d.ts +0 -56
- package/dist/components/RadioTileGroup/index.d.ts +0 -3
- package/dist/components/feedback/CircularProgress.md +0 -257
- package/dist/components/feedback/Skeleton.md +0 -280
- package/dist/components/inputs/FormControl.md +0 -361
- package/dist/components/inputs/RadioList.md +0 -241
- package/dist/components/inputs/RadioTileGroup.md +0 -507
- package/dist/components/inputs/Slider.md +0 -334
- package/dist/guides/ThemeProvider.md +0 -89
- package/dist/guides/llms.txt +0 -9
- package/dist/index.browser.js +0 -224
- package/dist/index.browser.js.map +0 -7
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
The Breadcrumbs component displays a navigation trail that helps users understand their current location within a
|
|
6
|
-
|
|
7
|
-
Breadcrumbs are essential for sites with deep navigation hierarchies -- they provide context at a glance, reduce the number of steps needed to return to higher-level pages, and improve overall wayfinding within an application.
|
|
5
|
+
The Breadcrumbs component displays a navigation trail that helps users understand their current location within a website's hierarchy and navigate back to previous levels. It shows the path from the homepage to the current page, with each level represented as a clickable link (except for the current page). Breadcrumbs improve user experience by providing context and enabling quick navigation.
|
|
8
6
|
|
|
9
7
|
```tsx
|
|
10
8
|
<Breadcrumbs
|
|
@@ -58,18 +56,26 @@ function MyComponent() {
|
|
|
58
56
|
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
59
57
|
{ label: 'Products', type: 'link', linkHref: '/products' },
|
|
60
58
|
{ label: 'Electronics', type: 'link', linkHref: '/products/electronics' },
|
|
61
|
-
{ label: 'Laptops', type: 'text' },
|
|
59
|
+
{ label: 'Laptops', type: 'text' }, // Current page (not a link)
|
|
62
60
|
];
|
|
63
61
|
|
|
64
62
|
return <Breadcrumbs crumbs={crumbs} />;
|
|
65
63
|
}
|
|
66
64
|
```
|
|
67
65
|
|
|
68
|
-
##
|
|
66
|
+
## Examples
|
|
67
|
+
|
|
68
|
+
### Basic Breadcrumbs
|
|
69
|
+
|
|
70
|
+
A simple breadcrumb navigation with three levels.
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
<Canvas of={Breadcrumbs.BasicExample} />
|
|
74
|
+
```
|
|
69
75
|
|
|
70
76
|
### Sizes
|
|
71
77
|
|
|
72
|
-
Breadcrumbs
|
|
78
|
+
Breadcrumbs support different sizes.
|
|
73
79
|
|
|
74
80
|
```tsx
|
|
75
81
|
<div>
|
|
@@ -79,9 +85,17 @@ Breadcrumbs are available in three sizes -- `sm`, `md`, and `lg` -- allowing you
|
|
|
79
85
|
</div>
|
|
80
86
|
```
|
|
81
87
|
|
|
82
|
-
### Custom Separator
|
|
88
|
+
### With Custom Separator
|
|
89
|
+
|
|
90
|
+
Customize the separator between breadcrumb items.
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
<Canvas of={Breadcrumbs.CustomSeparator} />
|
|
94
|
+
```
|
|
83
95
|
|
|
84
|
-
|
|
96
|
+
### Collapsed Breadcrumbs
|
|
97
|
+
|
|
98
|
+
For deep hierarchies, breadcrumbs can be collapsed with an ellipsis.
|
|
85
99
|
|
|
86
100
|
```tsx
|
|
87
101
|
<Breadcrumbs
|
|
@@ -114,13 +128,34 @@ The separator character between items can be customized via the `separator` prop
|
|
|
114
128
|
type: 'text'
|
|
115
129
|
}]}
|
|
116
130
|
collapsed
|
|
117
|
-
|
|
131
|
+
/>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Collapsed Variants
|
|
135
|
+
|
|
136
|
+
Configure how many items show at the start and end when collapsed.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
<Canvas of={Breadcrumbs.CollapsedVariants} />
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Single Crumb
|
|
143
|
+
|
|
144
|
+
When there's only one item in the path.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
<Breadcrumbs
|
|
148
|
+
crumbs={[{
|
|
149
|
+
label: 'Home',
|
|
150
|
+
type: 'text'
|
|
151
|
+
}]}
|
|
152
|
+
collapsed
|
|
118
153
|
/>
|
|
119
154
|
```
|
|
120
155
|
|
|
121
156
|
### Expanded View
|
|
122
157
|
|
|
123
|
-
|
|
158
|
+
Show all breadcrumb items without collapsing.
|
|
124
159
|
|
|
125
160
|
```tsx
|
|
126
161
|
<Breadcrumbs
|
|
@@ -156,56 +191,105 @@ Setting `collapsed={false}` renders every item in the path without collapsing.
|
|
|
156
191
|
/>
|
|
157
192
|
```
|
|
158
193
|
|
|
159
|
-
|
|
194
|
+
## When to Use
|
|
160
195
|
|
|
161
|
-
|
|
196
|
+
### ✅ Good Use Cases
|
|
162
197
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
198
|
+
- **Deep hierarchies**: Sites with 3+ levels of navigation depth
|
|
199
|
+
- **E-commerce sites**: Product category → subcategory → product navigation
|
|
200
|
+
- **Documentation sites**: Section → topic → article navigation
|
|
201
|
+
- **File management**: Folder → subfolder → file navigation
|
|
202
|
+
- **Admin dashboards**: Module → submodule → detail view
|
|
203
|
+
- **Content management**: Categories and nested content structures
|
|
204
|
+
|
|
205
|
+
### ❌ When Not to Use
|
|
206
|
+
|
|
207
|
+
- **Flat sites**: Single-level navigation doesn't need breadcrumbs
|
|
208
|
+
- **Linear flows**: For sequential processes, use Stepper instead
|
|
209
|
+
- **Primary navigation**: Breadcrumbs supplement, not replace, main navigation
|
|
210
|
+
- **Mobile-first**: Consider if space allows; may need alternative patterns
|
|
211
|
+
- **Dynamic content**: When paths frequently change or are user-specific
|
|
172
212
|
|
|
173
213
|
## Common Use Cases
|
|
174
214
|
|
|
175
|
-
###
|
|
215
|
+
### E-commerce Product Page
|
|
176
216
|
|
|
177
217
|
```tsx
|
|
178
|
-
function
|
|
218
|
+
function ProductPage({ product, category, subcategory }) {
|
|
179
219
|
const crumbs = [
|
|
180
220
|
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
181
|
-
{ label: '
|
|
182
|
-
{ label: category.name, type: 'link', linkHref: `/
|
|
183
|
-
{ label:
|
|
221
|
+
{ label: 'Shop', type: 'link', linkHref: '/shop' },
|
|
222
|
+
{ label: category.name, type: 'link', linkHref: `/shop/${category.slug}` },
|
|
223
|
+
{ label: subcategory.name, type: 'link', linkHref: `/shop/${category.slug}/${subcategory.slug}` },
|
|
224
|
+
{ label: product.name, type: 'text' },
|
|
184
225
|
];
|
|
185
226
|
|
|
186
227
|
return (
|
|
187
228
|
<Box>
|
|
188
229
|
<Breadcrumbs crumbs={crumbs} />
|
|
189
|
-
<
|
|
230
|
+
<ProductDetails product={product} />
|
|
190
231
|
</Box>
|
|
191
232
|
);
|
|
192
233
|
}
|
|
193
234
|
```
|
|
194
235
|
|
|
195
|
-
###
|
|
236
|
+
### Documentation Page
|
|
196
237
|
|
|
197
238
|
```tsx
|
|
198
|
-
function
|
|
239
|
+
function DocPage({ section, topic, article }) {
|
|
199
240
|
const crumbs = [
|
|
200
|
-
{ label: '
|
|
201
|
-
{ label:
|
|
202
|
-
{ label:
|
|
241
|
+
{ label: 'Docs', type: 'link', linkHref: '/docs' },
|
|
242
|
+
{ label: section.title, type: 'link', linkHref: `/docs/${section.slug}` },
|
|
243
|
+
{ label: topic.title, type: 'link', linkHref: `/docs/${section.slug}/${topic.slug}` },
|
|
244
|
+
{ label: article.title, type: 'text' },
|
|
203
245
|
];
|
|
204
246
|
|
|
205
247
|
return (
|
|
206
|
-
<
|
|
248
|
+
<Stack spacing={2}>
|
|
207
249
|
<Breadcrumbs crumbs={crumbs} size="sm" />
|
|
208
|
-
<
|
|
250
|
+
<Typography level="h1">{article.title}</Typography>
|
|
251
|
+
<ArticleContent content={article.content} />
|
|
252
|
+
</Stack>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### File Browser
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
function FileBrowser({ path }) {
|
|
261
|
+
const crumbs = [
|
|
262
|
+
{ label: 'Root', type: 'link', linkHref: '/files' },
|
|
263
|
+
...path.map((folder, index) => ({
|
|
264
|
+
label: folder.name,
|
|
265
|
+
type: index === path.length - 1 ? 'text' : 'link',
|
|
266
|
+
linkHref: `/files/${path.slice(0, index + 1).map(f => f.id).join('/')}`,
|
|
267
|
+
})),
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
<Box>
|
|
272
|
+
<Breadcrumbs crumbs={crumbs} collapsed={path.length > 4} />
|
|
273
|
+
<FileList folderId={path[path.length - 1].id} />
|
|
274
|
+
</Box>
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Admin Dashboard
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
function UserDetailPage({ user }) {
|
|
283
|
+
const crumbs = [
|
|
284
|
+
{ label: 'Dashboard', type: 'link', linkHref: '/admin' },
|
|
285
|
+
{ label: 'Users', type: 'link', linkHref: '/admin/users' },
|
|
286
|
+
{ label: user.name, type: 'text' },
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<Box>
|
|
291
|
+
<Breadcrumbs crumbs={crumbs} />
|
|
292
|
+
<UserProfile user={user} />
|
|
209
293
|
</Box>
|
|
210
294
|
);
|
|
211
295
|
}
|
|
@@ -216,55 +300,222 @@ function AccountSettingsPage() {
|
|
|
216
300
|
```tsx
|
|
217
301
|
function DynamicBreadcrumbs() {
|
|
218
302
|
const location = useLocation();
|
|
219
|
-
const pathnames = location.pathname.split('/').filter(
|
|
303
|
+
const pathnames = location.pathname.split('/').filter((x) => x);
|
|
220
304
|
|
|
221
305
|
const crumbs = [
|
|
222
306
|
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
223
|
-
...pathnames.map((value, index) =>
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
307
|
+
...pathnames.map((value, index) => {
|
|
308
|
+
const href = `/${pathnames.slice(0, index + 1).join('/')}`;
|
|
309
|
+
const isLast = index === pathnames.length - 1;
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
label: formatLabel(value),
|
|
313
|
+
type: isLast ? 'text' : 'link',
|
|
314
|
+
linkHref: href,
|
|
315
|
+
};
|
|
316
|
+
}),
|
|
228
317
|
];
|
|
229
318
|
|
|
230
319
|
return <Breadcrumbs crumbs={crumbs} />;
|
|
231
320
|
}
|
|
232
321
|
```
|
|
233
322
|
|
|
323
|
+
## Props and Customization
|
|
324
|
+
|
|
325
|
+
### Key Props
|
|
326
|
+
|
|
327
|
+
| Prop | Type | Default | Description |
|
|
328
|
+
| ----------------- | ---------------------- | ------- | ----------------------------------------------- |
|
|
329
|
+
| `crumbs` | `Crumb[]` | `[]` | Array of breadcrumb items |
|
|
330
|
+
| `collapsed` | `boolean` | `true` | Collapse middle items with ellipsis |
|
|
331
|
+
| `startCrumbCount` | `number` | `1` | Number of items to show at start when collapsed |
|
|
332
|
+
| `endCrumbCount` | `number` | `3` | Number of items to show at end when collapsed |
|
|
333
|
+
| `separator` | `string` | `'/'` | Separator between items |
|
|
334
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the breadcrumbs |
|
|
335
|
+
|
|
336
|
+
### Crumb Type
|
|
337
|
+
|
|
338
|
+
```tsx
|
|
339
|
+
interface Crumb {
|
|
340
|
+
label: string; // Display text
|
|
341
|
+
type: 'link' | 'text'; // Link or static text
|
|
342
|
+
linkHref?: string; // URL for link type
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Collapsed Behavior
|
|
347
|
+
|
|
348
|
+
When `collapsed` is true and there are more items than `startCrumbCount + endCrumbCount`, middle items are replaced with an ellipsis:
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
// With startCrumbCount=1, endCrumbCount=2, and 6 crumbs:
|
|
352
|
+
// Home / ... / Category / Product
|
|
353
|
+
|
|
354
|
+
<Breadcrumbs
|
|
355
|
+
crumbs={crumbs}
|
|
356
|
+
collapsed={true}
|
|
357
|
+
startCrumbCount={1}
|
|
358
|
+
endCrumbCount={2}
|
|
359
|
+
/>
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Custom Styling
|
|
363
|
+
|
|
364
|
+
```tsx
|
|
365
|
+
<Breadcrumbs
|
|
366
|
+
crumbs={crumbs}
|
|
367
|
+
sx={{
|
|
368
|
+
'& .MuiBreadcrumbs-separator': {
|
|
369
|
+
mx: 1,
|
|
370
|
+
color: 'neutral.400',
|
|
371
|
+
},
|
|
372
|
+
'& a': {
|
|
373
|
+
color: 'primary.500',
|
|
374
|
+
textDecoration: 'none',
|
|
375
|
+
'&:hover': {
|
|
376
|
+
textDecoration: 'underline',
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
}}
|
|
380
|
+
/>
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Accessibility
|
|
384
|
+
|
|
385
|
+
Breadcrumbs include important accessibility features:
|
|
386
|
+
|
|
387
|
+
### Semantic HTML
|
|
388
|
+
|
|
389
|
+
- Uses `<nav>` element with `aria-label="breadcrumb"`
|
|
390
|
+
- Structured as an ordered list (`<ol>`) to indicate sequence
|
|
391
|
+
- Current page marked with `aria-current="page"`
|
|
392
|
+
|
|
393
|
+
### ARIA Attributes
|
|
394
|
+
|
|
395
|
+
```tsx
|
|
396
|
+
// Generated HTML structure
|
|
397
|
+
<nav aria-label="breadcrumb">
|
|
398
|
+
<ol>
|
|
399
|
+
<li><a href="/">Home</a></li>
|
|
400
|
+
<li><a href="/products">Products</a></li>
|
|
401
|
+
<li aria-current="page">Current Page</li>
|
|
402
|
+
</ol>
|
|
403
|
+
</nav>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Keyboard Navigation
|
|
407
|
+
|
|
408
|
+
- **Tab**: Navigate through breadcrumb links
|
|
409
|
+
- **Enter**: Activate the focused link
|
|
410
|
+
|
|
411
|
+
### Screen Reader Support
|
|
412
|
+
|
|
413
|
+
- Screen readers announce "breadcrumb navigation"
|
|
414
|
+
- Each link is announced with its position in the sequence
|
|
415
|
+
- Current page is identified as the current location
|
|
416
|
+
|
|
417
|
+
### SEO Benefits
|
|
418
|
+
|
|
419
|
+
Breadcrumbs provide:
|
|
420
|
+
|
|
421
|
+
- Clear site structure for search engines
|
|
422
|
+
- Enhanced search result display (rich snippets)
|
|
423
|
+
- Improved internal linking
|
|
424
|
+
|
|
234
425
|
## Best Practices
|
|
235
426
|
|
|
236
|
-
|
|
427
|
+
### ✅ Do
|
|
428
|
+
|
|
429
|
+
1. **Start with Home**: Always begin with a link to the homepage
|
|
237
430
|
|
|
238
431
|
```tsx
|
|
239
|
-
// Good
|
|
432
|
+
// ✅ Good: Starts with Home
|
|
240
433
|
const crumbs = [
|
|
241
434
|
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
242
|
-
{ label: '
|
|
435
|
+
{ label: 'Products', type: 'link', linkHref: '/products' },
|
|
436
|
+
{ label: 'Laptops', type: 'text' },
|
|
243
437
|
];
|
|
244
|
-
|
|
245
|
-
// Bad -- missing root item
|
|
246
|
-
const crumbs = [{ label: 'Settings', type: 'text' }];
|
|
247
438
|
```
|
|
248
439
|
|
|
249
|
-
|
|
440
|
+
2. **Make current page non-clickable**: The last item should be text, not a link
|
|
250
441
|
|
|
251
442
|
```tsx
|
|
252
|
-
// Good
|
|
443
|
+
// ✅ Good: Current page is text
|
|
253
444
|
{ label: 'Current Page', type: 'text' }
|
|
254
445
|
|
|
255
|
-
// Bad
|
|
446
|
+
// ❌ Bad: Current page is a link
|
|
256
447
|
{ label: 'Current Page', type: 'link', linkHref: '/current' }
|
|
257
448
|
```
|
|
258
449
|
|
|
259
|
-
|
|
450
|
+
3. **Use clear, concise labels**: Keep labels short but descriptive
|
|
260
451
|
|
|
261
|
-
|
|
452
|
+
4. **Maintain consistency**: Use the same labels as page titles or navigation
|
|
262
453
|
|
|
263
|
-
|
|
454
|
+
5. **Position at top**: Place breadcrumbs near the top of the page, below the header
|
|
264
455
|
|
|
265
|
-
|
|
456
|
+
### ❌ Don't
|
|
457
|
+
|
|
458
|
+
1. **Don't use as primary navigation**: Breadcrumbs supplement main navigation
|
|
459
|
+
|
|
460
|
+
2. **Don't duplicate with page title**: If the current page title is visible, breadcrumbs can end with the parent
|
|
461
|
+
|
|
462
|
+
```tsx
|
|
463
|
+
// Consider ending at parent if title is displayed
|
|
464
|
+
const crumbs = [
|
|
465
|
+
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
466
|
+
{ label: 'Products', type: 'link', linkHref: '/products' },
|
|
467
|
+
// Omit if "Laptops" is shown as page title
|
|
468
|
+
];
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
3. **Don't use for flat navigation**: Sites with only one or two levels don't need breadcrumbs
|
|
472
|
+
|
|
473
|
+
4. **Don't hide on mobile carelessly**: Either show a simplified version or omit entirely
|
|
474
|
+
|
|
475
|
+
## Performance Considerations
|
|
476
|
+
|
|
477
|
+
### Memoize Crumbs
|
|
478
|
+
|
|
479
|
+
When breadcrumbs are computed from props or state, memoize them:
|
|
480
|
+
|
|
481
|
+
```tsx
|
|
482
|
+
const crumbs = useMemo(() => [
|
|
483
|
+
{ label: 'Home', type: 'link', linkHref: '/' },
|
|
484
|
+
{ label: category.name, type: 'link', linkHref: `/category/${category.id}` },
|
|
485
|
+
{ label: product.name, type: 'text' },
|
|
486
|
+
], [category, product]);
|
|
487
|
+
|
|
488
|
+
<Breadcrumbs crumbs={crumbs} />
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Use Collapsed for Deep Hierarchies
|
|
492
|
+
|
|
493
|
+
For paths with many levels, use collapsing to reduce DOM elements:
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
<Breadcrumbs
|
|
497
|
+
crumbs={deepHierarchy}
|
|
498
|
+
collapsed={true}
|
|
499
|
+
startCrumbCount={1}
|
|
500
|
+
endCrumbCount={2}
|
|
501
|
+
/>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Lazy Load for Dynamic Breadcrumbs
|
|
505
|
+
|
|
506
|
+
If breadcrumb data requires fetching, ensure it doesn't block page render:
|
|
507
|
+
|
|
508
|
+
```tsx
|
|
509
|
+
function Page() {
|
|
510
|
+
const { data: breadcrumbData, isLoading } = useBreadcrumbPath();
|
|
511
|
+
|
|
512
|
+
return (
|
|
513
|
+
<Box>
|
|
514
|
+
{!isLoading && <Breadcrumbs crumbs={breadcrumbData} />}
|
|
515
|
+
<PageContent />
|
|
516
|
+
</Box>
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
```
|
|
266
520
|
|
|
267
|
-
|
|
268
|
-
- **Current page indicator**: The last item is automatically marked with `aria-current="page"` so screen readers announce the user's current location.
|
|
269
|
-
- **Keyboard navigation**: All link items are focusable with `Tab` and activated with `Enter`, following standard keyboard interaction patterns.
|
|
270
|
-
- **Screen readers**: The navigation landmark is announced as "breadcrumb navigation" and each link communicates its position within the path hierarchy.
|
|
521
|
+
Breadcrumbs are a simple but powerful navigation aid that helps users understand where they are and navigate efficiently. Use them consistently across your site to improve user experience and SEO.
|
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
The Drawer component is a panel that slides in from the edge of the screen, overlaying the main content to present secondary information, navigation, or actions. Built on top of Joy UI's Drawer with Framer Motion animation, it provides smooth entrance and exit transitions out of the box.
|
|
6
|
-
|
|
7
|
-
Drawer supports three size presets (`sm`, `md`, `lg`) which control the panel width on horizontal anchors and height on vertical anchors. On smaller viewports (below `md` breakpoint), the drawer automatically expands to full width for an optimized mobile experience.
|
|
8
|
-
|
|
9
5
|
```tsx
|
|
10
6
|
<Drawer
|
|
11
7
|
open
|
|
@@ -28,180 +24,4 @@ Drawer supports three size presets (`sm`, `md`, `lg`) which control the panel wi
|
|
|
28
24
|
|
|
29
25
|
```tsx
|
|
30
26
|
import { Drawer } from '@ceed/cds';
|
|
31
|
-
|
|
32
|
-
function MyComponent() {
|
|
33
|
-
const [open, setOpen] = useState(false);
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<>
|
|
37
|
-
<Button onClick={() => setOpen(true)}>Open Drawer</Button>
|
|
38
|
-
<Drawer open={open} onClose={() => setOpen(false)}>
|
|
39
|
-
<Box sx={{ p: 2, backgroundColor: 'white', height: '100%' }}>
|
|
40
|
-
<Typography level="title-lg">Drawer Content</Typography>
|
|
41
|
-
<Typography level="body-md">
|
|
42
|
-
Place your navigation, detail view, or form content here.
|
|
43
|
-
</Typography>
|
|
44
|
-
</Box>
|
|
45
|
-
</Drawer>
|
|
46
|
-
</>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Default
|
|
52
|
-
|
|
53
|
-
The default Drawer displays a panel sliding in from the left with the `md` size preset.
|
|
54
|
-
|
|
55
|
-
```tsx
|
|
56
|
-
<Drawer
|
|
57
|
-
open
|
|
58
|
-
children={<Box sx={{
|
|
59
|
-
width: '100%',
|
|
60
|
-
height: '100%',
|
|
61
|
-
backgroundColor: 'white',
|
|
62
|
-
boxShadow: 'var(--ceed-shadowRing, 0 0 #000),0px 2px 8px -2px rgba(var(--ceed-shadowChannel, 21 21 21) / var(--ceed-shadowOpacity, 0.08)),0px 6px 12px -2px rgba(var(--ceed-shadowChannel, 21 21 21) / var(--ceed-shadowOpacity, 0.08))'
|
|
63
|
-
}}>
|
|
64
|
-
asdas
|
|
65
|
-
</Box>}
|
|
66
|
-
/>
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Common Use Cases
|
|
70
|
-
|
|
71
|
-
### Navigation Drawer
|
|
72
|
-
|
|
73
|
-
```tsx
|
|
74
|
-
function NavigationDrawer({ open, onClose }) {
|
|
75
|
-
const navItems = [
|
|
76
|
-
{ label: 'Home', icon: <HomeIcon />, path: '/' },
|
|
77
|
-
{ label: 'Orders', icon: <ListIcon />, path: '/orders' },
|
|
78
|
-
{ label: 'Account', icon: <PersonIcon />, path: '/account' },
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<Drawer open={open} onClose={onClose} size="sm">
|
|
83
|
-
<Box sx={{ p: 2, backgroundColor: 'white', height: '100%' }}>
|
|
84
|
-
<Typography level="title-lg" sx={{ mb: 2 }}>Menu</Typography>
|
|
85
|
-
<List>
|
|
86
|
-
{navItems.map((item) => (
|
|
87
|
-
<ListItem key={item.path}>
|
|
88
|
-
<ListItemButton onClick={() => { navigate(item.path); onClose(); }}>
|
|
89
|
-
<ListItemDecorator>{item.icon}</ListItemDecorator>
|
|
90
|
-
{item.label}
|
|
91
|
-
</ListItemButton>
|
|
92
|
-
</ListItem>
|
|
93
|
-
))}
|
|
94
|
-
</List>
|
|
95
|
-
</Box>
|
|
96
|
-
</Drawer>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
27
|
```
|
|
100
|
-
|
|
101
|
-
### Detail View
|
|
102
|
-
|
|
103
|
-
```tsx
|
|
104
|
-
function DetailDrawer({ open, onClose, selectedItem }) {
|
|
105
|
-
return (
|
|
106
|
-
<Drawer open={open} onClose={onClose} anchor="right" size="lg">
|
|
107
|
-
<Box
|
|
108
|
-
sx={{
|
|
109
|
-
p: 3,
|
|
110
|
-
backgroundColor: 'white',
|
|
111
|
-
height: '100%',
|
|
112
|
-
display: 'flex',
|
|
113
|
-
flexDirection: 'column',
|
|
114
|
-
}}
|
|
115
|
-
>
|
|
116
|
-
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
|
117
|
-
<Typography level="title-lg">{selectedItem?.name}</Typography>
|
|
118
|
-
<IconButton onClick={onClose}><CloseIcon /></IconButton>
|
|
119
|
-
</Box>
|
|
120
|
-
<Divider />
|
|
121
|
-
<Box sx={{ flex: 1, overflow: 'auto', mt: 2 }}>
|
|
122
|
-
{/* Detail content */}
|
|
123
|
-
</Box>
|
|
124
|
-
</Box>
|
|
125
|
-
</Drawer>
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Form Drawer
|
|
131
|
-
|
|
132
|
-
```tsx
|
|
133
|
-
function FormDrawer({ open, onClose, onSubmit }) {
|
|
134
|
-
return (
|
|
135
|
-
<Drawer open={open} onClose={onClose} anchor="right" size="md">
|
|
136
|
-
<Box
|
|
137
|
-
sx={{
|
|
138
|
-
p: 3,
|
|
139
|
-
backgroundColor: 'white',
|
|
140
|
-
height: '100%',
|
|
141
|
-
display: 'flex',
|
|
142
|
-
flexDirection: 'column',
|
|
143
|
-
}}
|
|
144
|
-
>
|
|
145
|
-
<Typography level="title-lg" sx={{ mb: 2 }}>Create New Item</Typography>
|
|
146
|
-
<Divider />
|
|
147
|
-
<Box sx={{ flex: 1, overflow: 'auto', my: 2 }}>
|
|
148
|
-
<Stack gap={2}>
|
|
149
|
-
<Input label="Name" placeholder="Enter name" />
|
|
150
|
-
<Textarea label="Description" placeholder="Enter description" minRows={3} />
|
|
151
|
-
</Stack>
|
|
152
|
-
</Box>
|
|
153
|
-
<Divider />
|
|
154
|
-
<Stack direction="row" gap={1} sx={{ mt: 2, justifyContent: 'flex-end' }}>
|
|
155
|
-
<Button variant="outlined" color="neutral" onClick={onClose}>Cancel</Button>
|
|
156
|
-
<Button onClick={onSubmit}>Save</Button>
|
|
157
|
-
</Stack>
|
|
158
|
-
</Box>
|
|
159
|
-
</Drawer>
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
## Best Practices
|
|
165
|
-
|
|
166
|
-
1. **Choose the right size**: Use `sm` (360px) for simple navigation, `md` (600px) for moderate content, and `lg` (900px) for complex detail views or forms.
|
|
167
|
-
|
|
168
|
-
```tsx
|
|
169
|
-
// ✅ Good: Size matches content complexity
|
|
170
|
-
<Drawer size="sm"> {/* Simple nav list */}
|
|
171
|
-
<Drawer size="md"> {/* Filter panel or form */}
|
|
172
|
-
<Drawer size="lg"> {/* Detailed information view */}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
2. **Provide visible close affordances**: Always include a close button or cancel action inside the Drawer content, in addition to the backdrop click.
|
|
176
|
-
|
|
177
|
-
```tsx
|
|
178
|
-
// ✅ Good: Close button inside the drawer
|
|
179
|
-
<Drawer open={open} onClose={onClose}>
|
|
180
|
-
<Box>
|
|
181
|
-
<IconButton onClick={onClose}><CloseIcon /></IconButton>
|
|
182
|
-
{/* Content */}
|
|
183
|
-
</Box>
|
|
184
|
-
</Drawer>
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
3. **Apply proper background and shadow styling**: The Drawer renders children directly, so you should apply background color and shadow to your content container for visual clarity.
|
|
188
|
-
|
|
189
|
-
```tsx
|
|
190
|
-
// ✅ Good: Styled content container
|
|
191
|
-
<Drawer open={open}>
|
|
192
|
-
<Box sx={{ backgroundColor: 'white', height: '100%', boxShadow: 'md', p: 2 }}>
|
|
193
|
-
{/* Content */}
|
|
194
|
-
</Box>
|
|
195
|
-
</Drawer>
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
4. **Do not use Drawer for confirmations**: For simple yes/no prompts, use Dialog instead. Drawer is designed for richer, scrollable content.
|
|
199
|
-
|
|
200
|
-
5. **Structure content with header, body, and footer**: Use a flex column layout to keep the header and footer fixed while allowing the body to scroll.
|
|
201
|
-
|
|
202
|
-
## Accessibility
|
|
203
|
-
|
|
204
|
-
- The Drawer overlay traps focus within the panel when open, preventing keyboard users from interacting with background content.
|
|
205
|
-
- Pressing **Escape** closes the Drawer. **Tab** and **Shift+Tab** cycle through focusable elements within the Drawer content.
|
|
206
|
-
- On mobile viewports, the Drawer automatically expands to full width, ensuring touch targets remain accessible and content is not clipped.
|
|
207
|
-
- Provide an `aria-label` or visible heading within the Drawer content to describe the panel's purpose for screen reader users.
|