torch-glare 1.2.8 → 1.3.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/apps/lib/components/TableDnDWrapper.ts +495 -0
- package/apps/lib/components/TextEditor.tsx +53 -1
- package/dist/bin/index.js +5 -0
- package/dist/bin/index.js.map +1 -1
- package/dist/src/commands/mcp.d.ts +2 -0
- package/dist/src/commands/mcp.d.ts.map +1 -0
- package/dist/src/commands/mcp.js +91 -0
- package/dist/src/commands/mcp.js.map +1 -0
- package/dist/src/shared/configureFonts.d.ts +6 -0
- package/dist/src/shared/configureFonts.d.ts.map +1 -0
- package/dist/src/shared/configureFonts.js +106 -0
- package/dist/src/shared/configureFonts.js.map +1 -0
- package/dist/src/shared/configureGlobalCss.d.ts +6 -0
- package/dist/src/shared/configureGlobalCss.d.ts.map +1 -0
- package/dist/src/shared/configureGlobalCss.js +154 -0
- package/dist/src/shared/configureGlobalCss.js.map +1 -0
- package/dist/src/shared/configureTailwind.d.ts +7 -0
- package/dist/src/shared/configureTailwind.d.ts.map +1 -0
- package/dist/src/shared/configureTailwind.js +163 -0
- package/dist/src/shared/configureTailwind.js.map +1 -0
- package/dist/src/shared/detectFramework.d.ts +23 -0
- package/dist/src/shared/detectFramework.d.ts.map +1 -0
- package/dist/src/shared/detectFramework.js +119 -0
- package/dist/src/shared/detectFramework.js.map +1 -0
- package/dist/src/shared/getDependenciesAndInstallNestedComponents.d.ts.map +1 -1
- package/dist/src/shared/getDependenciesAndInstallNestedComponents.js +18 -2
- package/dist/src/shared/getDependenciesAndInstallNestedComponents.js.map +1 -1
- package/dist/src/shared/installBaseUtils.d.ts +5 -0
- package/dist/src/shared/installBaseUtils.d.ts.map +1 -0
- package/dist/src/shared/installBaseUtils.js +79 -0
- package/dist/src/shared/installBaseUtils.js.map +1 -0
- package/dist/src/shared/resolveAliases.d.ts +24 -0
- package/dist/src/shared/resolveAliases.d.ts.map +1 -0
- package/dist/src/shared/resolveAliases.js +98 -0
- package/dist/src/shared/resolveAliases.js.map +1 -0
- package/docs/components/breadcrumb.md +955 -0
- package/docs/components/button-group.md +647 -0
- package/docs/components/text-editor.md +711 -0
- package/docs/components/toggle-button.md +640 -0
- package/docs/tutorials/getting-started.md +123 -431
- package/package.json +1 -1
|
@@ -0,0 +1,955 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Breadcrumb
|
|
3
|
+
version: 1.1.15
|
|
4
|
+
status: stable
|
|
5
|
+
category: components/navigation
|
|
6
|
+
tags: [navigation, breadcrumb, wayfinding, compound, accessible, rtl]
|
|
7
|
+
last-reviewed: 2024-11-05
|
|
8
|
+
bundle-size: 2.2kb
|
|
9
|
+
dependencies:
|
|
10
|
+
- "@radix-ui/react-slot": "^1.0.0"
|
|
11
|
+
- "class-variance-authority": "^0.7.0"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Breadcrumb
|
|
15
|
+
|
|
16
|
+
> A compound breadcrumb navigation component with seven sub-components for building flexible, accessible breadcrumb trails. Supports multiple sizes, two style variants, the asChild pattern for custom link components, and collapsible ellipsis for long paths.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install torch-glare
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Import
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import {
|
|
28
|
+
Breadcrumb,
|
|
29
|
+
BreadcrumbList,
|
|
30
|
+
BreadcrumbItem,
|
|
31
|
+
BreadcrumbLink,
|
|
32
|
+
BreadcrumbPage,
|
|
33
|
+
BreadcrumbSeparator,
|
|
34
|
+
BreadcrumbEllipsis,
|
|
35
|
+
} from 'torch-glare/lib/components/Breadcrumb'
|
|
36
|
+
// or
|
|
37
|
+
import {
|
|
38
|
+
Breadcrumb,
|
|
39
|
+
BreadcrumbList,
|
|
40
|
+
BreadcrumbItem,
|
|
41
|
+
BreadcrumbLink,
|
|
42
|
+
BreadcrumbPage,
|
|
43
|
+
BreadcrumbSeparator,
|
|
44
|
+
BreadcrumbEllipsis,
|
|
45
|
+
} from 'torch-glare/lib/components'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick Examples
|
|
49
|
+
|
|
50
|
+
### Basic Usage
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import {
|
|
54
|
+
Breadcrumb,
|
|
55
|
+
BreadcrumbList,
|
|
56
|
+
BreadcrumbItem,
|
|
57
|
+
BreadcrumbLink,
|
|
58
|
+
BreadcrumbPage,
|
|
59
|
+
BreadcrumbSeparator,
|
|
60
|
+
} from 'torch-glare/lib/components/Breadcrumb'
|
|
61
|
+
|
|
62
|
+
function Example() {
|
|
63
|
+
return (
|
|
64
|
+
<Breadcrumb>
|
|
65
|
+
<BreadcrumbList>
|
|
66
|
+
<BreadcrumbItem>
|
|
67
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
68
|
+
</BreadcrumbItem>
|
|
69
|
+
<BreadcrumbSeparator />
|
|
70
|
+
<BreadcrumbItem>
|
|
71
|
+
<BreadcrumbLink href="/products">Products</BreadcrumbLink>
|
|
72
|
+
</BreadcrumbItem>
|
|
73
|
+
<BreadcrumbSeparator />
|
|
74
|
+
<BreadcrumbItem>
|
|
75
|
+
<BreadcrumbPage>Current Product</BreadcrumbPage>
|
|
76
|
+
</BreadcrumbItem>
|
|
77
|
+
</BreadcrumbList>
|
|
78
|
+
</Breadcrumb>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### With Different Sizes
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Small
|
|
87
|
+
<Breadcrumb>
|
|
88
|
+
<BreadcrumbList size="S">
|
|
89
|
+
<BreadcrumbItem>
|
|
90
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
91
|
+
</BreadcrumbItem>
|
|
92
|
+
<BreadcrumbSeparator />
|
|
93
|
+
<BreadcrumbItem>
|
|
94
|
+
<BreadcrumbPage>Page</BreadcrumbPage>
|
|
95
|
+
</BreadcrumbItem>
|
|
96
|
+
</BreadcrumbList>
|
|
97
|
+
</Breadcrumb>
|
|
98
|
+
|
|
99
|
+
// Medium (default)
|
|
100
|
+
<Breadcrumb>
|
|
101
|
+
<BreadcrumbList size="M">
|
|
102
|
+
<BreadcrumbItem>
|
|
103
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
104
|
+
</BreadcrumbItem>
|
|
105
|
+
<BreadcrumbSeparator />
|
|
106
|
+
<BreadcrumbItem>
|
|
107
|
+
<BreadcrumbPage>Page</BreadcrumbPage>
|
|
108
|
+
</BreadcrumbItem>
|
|
109
|
+
</BreadcrumbList>
|
|
110
|
+
</Breadcrumb>
|
|
111
|
+
|
|
112
|
+
// Large
|
|
113
|
+
<Breadcrumb>
|
|
114
|
+
<BreadcrumbList size="L">
|
|
115
|
+
<BreadcrumbItem>
|
|
116
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
117
|
+
</BreadcrumbItem>
|
|
118
|
+
<BreadcrumbSeparator />
|
|
119
|
+
<BreadcrumbItem>
|
|
120
|
+
<BreadcrumbPage>Page</BreadcrumbPage>
|
|
121
|
+
</BreadcrumbItem>
|
|
122
|
+
</BreadcrumbList>
|
|
123
|
+
</Breadcrumb>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### With SystemStyle Variant
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
<Breadcrumb>
|
|
130
|
+
<BreadcrumbList>
|
|
131
|
+
<BreadcrumbItem>
|
|
132
|
+
<BreadcrumbLink variant="SystemStyle" href="/">Home</BreadcrumbLink>
|
|
133
|
+
</BreadcrumbItem>
|
|
134
|
+
<BreadcrumbSeparator variant="SystemStyle" />
|
|
135
|
+
<BreadcrumbItem>
|
|
136
|
+
<BreadcrumbLink variant="SystemStyle" href="/docs">Docs</BreadcrumbLink>
|
|
137
|
+
</BreadcrumbItem>
|
|
138
|
+
<BreadcrumbSeparator variant="SystemStyle" />
|
|
139
|
+
<BreadcrumbItem>
|
|
140
|
+
<BreadcrumbPage variant="SystemStyle">API</BreadcrumbPage>
|
|
141
|
+
</BreadcrumbItem>
|
|
142
|
+
</BreadcrumbList>
|
|
143
|
+
</Breadcrumb>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### With Ellipsis (Collapsed Items)
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
<Breadcrumb>
|
|
150
|
+
<BreadcrumbList>
|
|
151
|
+
<BreadcrumbItem>
|
|
152
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
153
|
+
</BreadcrumbItem>
|
|
154
|
+
<BreadcrumbSeparator />
|
|
155
|
+
<BreadcrumbItem>
|
|
156
|
+
<BreadcrumbEllipsis />
|
|
157
|
+
</BreadcrumbItem>
|
|
158
|
+
<BreadcrumbSeparator />
|
|
159
|
+
<BreadcrumbItem>
|
|
160
|
+
<BreadcrumbLink href="/category/subcategory">Subcategory</BreadcrumbLink>
|
|
161
|
+
</BreadcrumbItem>
|
|
162
|
+
<BreadcrumbSeparator />
|
|
163
|
+
<BreadcrumbItem>
|
|
164
|
+
<BreadcrumbPage>Current Page</BreadcrumbPage>
|
|
165
|
+
</BreadcrumbItem>
|
|
166
|
+
</BreadcrumbList>
|
|
167
|
+
</Breadcrumb>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### With Custom Separator
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
<Breadcrumb>
|
|
174
|
+
<BreadcrumbList>
|
|
175
|
+
<BreadcrumbItem>
|
|
176
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
177
|
+
</BreadcrumbItem>
|
|
178
|
+
<BreadcrumbSeparator>
|
|
179
|
+
<i className="ri-arrow-right-s-line" />
|
|
180
|
+
</BreadcrumbSeparator>
|
|
181
|
+
<BreadcrumbItem>
|
|
182
|
+
<BreadcrumbLink href="/docs">Docs</BreadcrumbLink>
|
|
183
|
+
</BreadcrumbItem>
|
|
184
|
+
<BreadcrumbSeparator>
|
|
185
|
+
/
|
|
186
|
+
</BreadcrumbSeparator>
|
|
187
|
+
<BreadcrumbItem>
|
|
188
|
+
<BreadcrumbPage>Guide</BreadcrumbPage>
|
|
189
|
+
</BreadcrumbItem>
|
|
190
|
+
</BreadcrumbList>
|
|
191
|
+
</Breadcrumb>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### With asChild (Custom Link Component)
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import Link from 'next/link'
|
|
198
|
+
|
|
199
|
+
<Breadcrumb>
|
|
200
|
+
<BreadcrumbList>
|
|
201
|
+
<BreadcrumbItem>
|
|
202
|
+
<BreadcrumbLink asChild>
|
|
203
|
+
<Link href="/">Home</Link>
|
|
204
|
+
</BreadcrumbLink>
|
|
205
|
+
</BreadcrumbItem>
|
|
206
|
+
<BreadcrumbSeparator />
|
|
207
|
+
<BreadcrumbItem>
|
|
208
|
+
<BreadcrumbLink asChild>
|
|
209
|
+
<Link href="/products">Products</Link>
|
|
210
|
+
</BreadcrumbLink>
|
|
211
|
+
</BreadcrumbItem>
|
|
212
|
+
<BreadcrumbSeparator />
|
|
213
|
+
<BreadcrumbItem>
|
|
214
|
+
<BreadcrumbPage>Details</BreadcrumbPage>
|
|
215
|
+
</BreadcrumbItem>
|
|
216
|
+
</BreadcrumbList>
|
|
217
|
+
</Breadcrumb>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### With Theme Override
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
<Breadcrumb theme="dark">
|
|
224
|
+
<BreadcrumbList>
|
|
225
|
+
<BreadcrumbItem>
|
|
226
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
227
|
+
</BreadcrumbItem>
|
|
228
|
+
<BreadcrumbSeparator />
|
|
229
|
+
<BreadcrumbItem>
|
|
230
|
+
<BreadcrumbPage>Dark Page</BreadcrumbPage>
|
|
231
|
+
</BreadcrumbItem>
|
|
232
|
+
</BreadcrumbList>
|
|
233
|
+
</Breadcrumb>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### With Icons
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
<Breadcrumb>
|
|
240
|
+
<BreadcrumbList>
|
|
241
|
+
<BreadcrumbItem>
|
|
242
|
+
<BreadcrumbLink href="/">
|
|
243
|
+
<i className="ri-home-line mr-1" />
|
|
244
|
+
Home
|
|
245
|
+
</BreadcrumbLink>
|
|
246
|
+
</BreadcrumbItem>
|
|
247
|
+
<BreadcrumbSeparator />
|
|
248
|
+
<BreadcrumbItem>
|
|
249
|
+
<BreadcrumbLink href="/settings">
|
|
250
|
+
<i className="ri-settings-line mr-1" />
|
|
251
|
+
Settings
|
|
252
|
+
</BreadcrumbLink>
|
|
253
|
+
</BreadcrumbItem>
|
|
254
|
+
<BreadcrumbSeparator />
|
|
255
|
+
<BreadcrumbItem>
|
|
256
|
+
<BreadcrumbPage>
|
|
257
|
+
<i className="ri-user-line mr-1" />
|
|
258
|
+
Profile
|
|
259
|
+
</BreadcrumbPage>
|
|
260
|
+
</BreadcrumbItem>
|
|
261
|
+
</BreadcrumbList>
|
|
262
|
+
</Breadcrumb>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## API Reference
|
|
266
|
+
|
|
267
|
+
### Breadcrumb (Root)
|
|
268
|
+
|
|
269
|
+
| Prop | Type | Default | Description |
|
|
270
|
+
|------|------|---------|-------------|
|
|
271
|
+
| `theme` | `'light' \| 'dark' \| 'default'` | - | Override theme for the breadcrumb |
|
|
272
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
273
|
+
|
|
274
|
+
Renders a `<nav>` element with `aria-label="breadcrumb"`.
|
|
275
|
+
|
|
276
|
+
### BreadcrumbList
|
|
277
|
+
|
|
278
|
+
| Prop | Type | Default | Description |
|
|
279
|
+
|------|------|---------|-------------|
|
|
280
|
+
| `size` | `'S' \| 'M' \| 'L'` | `'M'` | Text size and gap spacing |
|
|
281
|
+
| `theme` | `'light' \| 'dark' \| 'default'` | - | Override theme |
|
|
282
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
283
|
+
|
|
284
|
+
Renders an `<ol>` element with flex-wrap layout.
|
|
285
|
+
|
|
286
|
+
### BreadcrumbItem
|
|
287
|
+
|
|
288
|
+
| Prop | Type | Default | Description |
|
|
289
|
+
|------|------|---------|-------------|
|
|
290
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
291
|
+
|
|
292
|
+
Renders an `<li>` element with inline-flex alignment.
|
|
293
|
+
|
|
294
|
+
### BreadcrumbLink
|
|
295
|
+
|
|
296
|
+
| Prop | Type | Default | Description |
|
|
297
|
+
|------|------|---------|-------------|
|
|
298
|
+
| `variant` | `'PresentationStyle' \| 'SystemStyle'` | `'PresentationStyle'` | Visual style variant |
|
|
299
|
+
| `asChild` | `boolean` | `false` | Merge props onto child element (Slot pattern) |
|
|
300
|
+
| `href` | `string` | - | Link destination URL |
|
|
301
|
+
| `theme` | `'light' \| 'dark' \| 'default'` | - | Override theme |
|
|
302
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
303
|
+
|
|
304
|
+
Renders an `<a>` element (or child via Slot when `asChild` is true).
|
|
305
|
+
|
|
306
|
+
### BreadcrumbPage
|
|
307
|
+
|
|
308
|
+
| Prop | Type | Default | Description |
|
|
309
|
+
|------|------|---------|-------------|
|
|
310
|
+
| `variant` | `'PresentationStyle' \| 'SystemStyle'` | `'PresentationStyle'` | Visual style variant |
|
|
311
|
+
| `theme` | `'light' \| 'dark' \| 'default'` | - | Override theme |
|
|
312
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
313
|
+
|
|
314
|
+
Renders a `<span>` with `aria-current="page"` and `aria-disabled="true"`.
|
|
315
|
+
|
|
316
|
+
### BreadcrumbSeparator
|
|
317
|
+
|
|
318
|
+
| Prop | Type | Default | Description |
|
|
319
|
+
|------|------|---------|-------------|
|
|
320
|
+
| `variant` | `'PresentationStyle' \| 'SystemStyle'` | `'PresentationStyle'` | Visual style variant |
|
|
321
|
+
| `theme` | `'light' \| 'dark' \| 'default'` | - | Override theme |
|
|
322
|
+
| `children` | `React.ReactNode` | `<i className="ri-arrow-right-s-line" />` | Custom separator content |
|
|
323
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
324
|
+
|
|
325
|
+
Renders an `<li>` with `role="presentation"` and `aria-hidden="true"`. Defaults to a right-arrow icon.
|
|
326
|
+
|
|
327
|
+
### BreadcrumbEllipsis
|
|
328
|
+
|
|
329
|
+
| Prop | Type | Default | Description |
|
|
330
|
+
|------|------|---------|-------------|
|
|
331
|
+
| `variant` | `'PresentationStyle' \| 'SystemStyle'` | `'PresentationStyle'` | Visual style variant |
|
|
332
|
+
| `theme` | `'light' \| 'dark' \| 'default'` | - | Override theme |
|
|
333
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
334
|
+
|
|
335
|
+
Renders a `<span>` with `role="presentation"`, `aria-hidden="true"`, and a "More" screen-reader label. Displays a `ri-more-line` icon.
|
|
336
|
+
|
|
337
|
+
### Size Variants (BreadcrumbList)
|
|
338
|
+
|
|
339
|
+
| Size | Text Size | Gap |
|
|
340
|
+
|------|-----------|-----|
|
|
341
|
+
| S | 12px | 4px (gap-1) |
|
|
342
|
+
| M | 14px | 6px (gap-1.5) |
|
|
343
|
+
| L | 16px | 8px (gap-2) |
|
|
344
|
+
|
|
345
|
+
### TypeScript
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { ComponentPropsWithoutRef } from 'react'
|
|
349
|
+
import { VariantProps } from 'class-variance-authority'
|
|
350
|
+
|
|
351
|
+
type Themes = 'light' | 'dark' | 'default'
|
|
352
|
+
|
|
353
|
+
interface BreadcrumbProps extends ComponentPropsWithoutRef<'nav'> {
|
|
354
|
+
theme?: Themes
|
|
355
|
+
separator?: React.ReactNode
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
interface BreadcrumbListProps
|
|
359
|
+
extends ComponentPropsWithoutRef<'ol'>,
|
|
360
|
+
VariantProps<typeof breadcrumbListStyles> {
|
|
361
|
+
theme?: Themes
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
interface BreadcrumbLinkProps
|
|
365
|
+
extends ComponentPropsWithoutRef<'a'>,
|
|
366
|
+
VariantProps<typeof breadcrumbLinkStyles> {
|
|
367
|
+
asChild?: boolean
|
|
368
|
+
theme?: Themes
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
interface BreadcrumbPageProps
|
|
372
|
+
extends ComponentPropsWithoutRef<'span'>,
|
|
373
|
+
VariantProps<typeof breadcrumbPageStyles> {
|
|
374
|
+
theme?: Themes
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
interface BreadcrumbSeparatorProps
|
|
378
|
+
extends ComponentPropsWithoutRef<'li'>,
|
|
379
|
+
VariantProps<typeof breadcrumbSeparatorStyles> {
|
|
380
|
+
theme?: Themes
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
interface BreadcrumbEllipsisProps
|
|
384
|
+
extends ComponentPropsWithoutRef<'span'>,
|
|
385
|
+
VariantProps<typeof breadcrumbEllipsisStyles> {
|
|
386
|
+
theme?: Themes
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export const Breadcrumb: React.ForwardRefExoticComponent<
|
|
390
|
+
BreadcrumbProps & React.RefAttributes<HTMLElement>
|
|
391
|
+
>
|
|
392
|
+
export const BreadcrumbList: React.ForwardRefExoticComponent<
|
|
393
|
+
BreadcrumbListProps & React.RefAttributes<HTMLOListElement>
|
|
394
|
+
>
|
|
395
|
+
export const BreadcrumbItem: React.ForwardRefExoticComponent<
|
|
396
|
+
ComponentPropsWithoutRef<'li'> & React.RefAttributes<HTMLLIElement>
|
|
397
|
+
>
|
|
398
|
+
export const BreadcrumbLink: React.ForwardRefExoticComponent<
|
|
399
|
+
BreadcrumbLinkProps & React.RefAttributes<HTMLAnchorElement>
|
|
400
|
+
>
|
|
401
|
+
export const BreadcrumbPage: React.ForwardRefExoticComponent<
|
|
402
|
+
BreadcrumbPageProps & React.RefAttributes<HTMLSpanElement>
|
|
403
|
+
>
|
|
404
|
+
export const BreadcrumbSeparator: React.ForwardRefExoticComponent<
|
|
405
|
+
BreadcrumbSeparatorProps & React.RefAttributes<HTMLLIElement>
|
|
406
|
+
>
|
|
407
|
+
export const BreadcrumbEllipsis: React.ForwardRefExoticComponent<
|
|
408
|
+
BreadcrumbEllipsisProps & React.RefAttributes<HTMLSpanElement>
|
|
409
|
+
>
|
|
410
|
+
|
|
411
|
+
export {
|
|
412
|
+
breadcrumbListStyles,
|
|
413
|
+
breadcrumbLinkStyles,
|
|
414
|
+
breadcrumbPageStyles,
|
|
415
|
+
breadcrumbSeparatorStyles,
|
|
416
|
+
breadcrumbEllipsisStyles,
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Common Patterns
|
|
421
|
+
|
|
422
|
+
### Dynamic Breadcrumb from Route
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
function DynamicBreadcrumb({ segments }: { segments: { label: string; href: string }[] }) {
|
|
426
|
+
return (
|
|
427
|
+
<Breadcrumb>
|
|
428
|
+
<BreadcrumbList>
|
|
429
|
+
{segments.map((segment, index) => {
|
|
430
|
+
const isLast = index === segments.length - 1
|
|
431
|
+
|
|
432
|
+
return (
|
|
433
|
+
<React.Fragment key={segment.href}>
|
|
434
|
+
<BreadcrumbItem>
|
|
435
|
+
{isLast ? (
|
|
436
|
+
<BreadcrumbPage>{segment.label}</BreadcrumbPage>
|
|
437
|
+
) : (
|
|
438
|
+
<BreadcrumbLink href={segment.href}>
|
|
439
|
+
{segment.label}
|
|
440
|
+
</BreadcrumbLink>
|
|
441
|
+
)}
|
|
442
|
+
</BreadcrumbItem>
|
|
443
|
+
{!isLast && <BreadcrumbSeparator />}
|
|
444
|
+
</React.Fragment>
|
|
445
|
+
)
|
|
446
|
+
})}
|
|
447
|
+
</BreadcrumbList>
|
|
448
|
+
</Breadcrumb>
|
|
449
|
+
)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Usage
|
|
453
|
+
<DynamicBreadcrumb
|
|
454
|
+
segments={[
|
|
455
|
+
{ label: 'Home', href: '/' },
|
|
456
|
+
{ label: 'Products', href: '/products' },
|
|
457
|
+
{ label: 'Electronics', href: '/products/electronics' },
|
|
458
|
+
{ label: 'Laptops', href: '/products/electronics/laptops' },
|
|
459
|
+
]}
|
|
460
|
+
/>
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Collapsible Breadcrumb with Dropdown
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
import { DropdownMenu } from 'torch-glare/lib/components/DropdownMenu'
|
|
467
|
+
|
|
468
|
+
function CollapsibleBreadcrumb({
|
|
469
|
+
items,
|
|
470
|
+
maxVisible = 3
|
|
471
|
+
}: {
|
|
472
|
+
items: { label: string; href: string }[]
|
|
473
|
+
maxVisible?: number
|
|
474
|
+
}) {
|
|
475
|
+
const shouldCollapse = items.length > maxVisible
|
|
476
|
+
const visibleStart = items.slice(0, 1)
|
|
477
|
+
const collapsed = shouldCollapse ? items.slice(1, items.length - (maxVisible - 1)) : []
|
|
478
|
+
const visibleEnd = shouldCollapse ? items.slice(items.length - (maxVisible - 1)) : items.slice(1)
|
|
479
|
+
|
|
480
|
+
return (
|
|
481
|
+
<Breadcrumb>
|
|
482
|
+
<BreadcrumbList>
|
|
483
|
+
{/* First item always visible */}
|
|
484
|
+
{visibleStart.map((item) => (
|
|
485
|
+
<React.Fragment key={item.href}>
|
|
486
|
+
<BreadcrumbItem>
|
|
487
|
+
<BreadcrumbLink href={item.href}>{item.label}</BreadcrumbLink>
|
|
488
|
+
</BreadcrumbItem>
|
|
489
|
+
<BreadcrumbSeparator />
|
|
490
|
+
</React.Fragment>
|
|
491
|
+
))}
|
|
492
|
+
|
|
493
|
+
{/* Collapsed items shown as ellipsis */}
|
|
494
|
+
{collapsed.length > 0 && (
|
|
495
|
+
<>
|
|
496
|
+
<BreadcrumbItem>
|
|
497
|
+
<DropdownMenu>
|
|
498
|
+
<DropdownMenu.Trigger asChild>
|
|
499
|
+
<BreadcrumbEllipsis />
|
|
500
|
+
</DropdownMenu.Trigger>
|
|
501
|
+
<DropdownMenu.Content>
|
|
502
|
+
{collapsed.map((item) => (
|
|
503
|
+
<DropdownMenu.Item key={item.href} asChild>
|
|
504
|
+
<a href={item.href}>{item.label}</a>
|
|
505
|
+
</DropdownMenu.Item>
|
|
506
|
+
))}
|
|
507
|
+
</DropdownMenu.Content>
|
|
508
|
+
</DropdownMenu>
|
|
509
|
+
</BreadcrumbItem>
|
|
510
|
+
<BreadcrumbSeparator />
|
|
511
|
+
</>
|
|
512
|
+
)}
|
|
513
|
+
|
|
514
|
+
{/* Remaining visible items */}
|
|
515
|
+
{visibleEnd.map((item, index) => {
|
|
516
|
+
const isLast = index === visibleEnd.length - 1
|
|
517
|
+
return (
|
|
518
|
+
<React.Fragment key={item.href}>
|
|
519
|
+
<BreadcrumbItem>
|
|
520
|
+
{isLast ? (
|
|
521
|
+
<BreadcrumbPage>{item.label}</BreadcrumbPage>
|
|
522
|
+
) : (
|
|
523
|
+
<BreadcrumbLink href={item.href}>{item.label}</BreadcrumbLink>
|
|
524
|
+
)}
|
|
525
|
+
</BreadcrumbItem>
|
|
526
|
+
{!isLast && <BreadcrumbSeparator />}
|
|
527
|
+
</React.Fragment>
|
|
528
|
+
)
|
|
529
|
+
})}
|
|
530
|
+
</BreadcrumbList>
|
|
531
|
+
</Breadcrumb>
|
|
532
|
+
)
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Next.js App Router Integration
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
'use client'
|
|
540
|
+
|
|
541
|
+
import { usePathname } from 'next/navigation'
|
|
542
|
+
import Link from 'next/link'
|
|
543
|
+
|
|
544
|
+
function AppBreadcrumb() {
|
|
545
|
+
const pathname = usePathname()
|
|
546
|
+
const segments = pathname.split('/').filter(Boolean)
|
|
547
|
+
|
|
548
|
+
return (
|
|
549
|
+
<Breadcrumb>
|
|
550
|
+
<BreadcrumbList>
|
|
551
|
+
<BreadcrumbItem>
|
|
552
|
+
<BreadcrumbLink asChild>
|
|
553
|
+
<Link href="/">Home</Link>
|
|
554
|
+
</BreadcrumbLink>
|
|
555
|
+
</BreadcrumbItem>
|
|
556
|
+
|
|
557
|
+
{segments.map((segment, index) => {
|
|
558
|
+
const href = '/' + segments.slice(0, index + 1).join('/')
|
|
559
|
+
const isLast = index === segments.length - 1
|
|
560
|
+
const label = segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ')
|
|
561
|
+
|
|
562
|
+
return (
|
|
563
|
+
<React.Fragment key={href}>
|
|
564
|
+
<BreadcrumbSeparator />
|
|
565
|
+
<BreadcrumbItem>
|
|
566
|
+
{isLast ? (
|
|
567
|
+
<BreadcrumbPage>{label}</BreadcrumbPage>
|
|
568
|
+
) : (
|
|
569
|
+
<BreadcrumbLink asChild>
|
|
570
|
+
<Link href={href}>{label}</Link>
|
|
571
|
+
</BreadcrumbLink>
|
|
572
|
+
)}
|
|
573
|
+
</BreadcrumbItem>
|
|
574
|
+
</React.Fragment>
|
|
575
|
+
)
|
|
576
|
+
})}
|
|
577
|
+
</BreadcrumbList>
|
|
578
|
+
</Breadcrumb>
|
|
579
|
+
)
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### File Explorer Breadcrumb
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
function FileExplorerBreadcrumb({
|
|
587
|
+
path,
|
|
588
|
+
onNavigate
|
|
589
|
+
}: {
|
|
590
|
+
path: string[]
|
|
591
|
+
onNavigate: (index: number) => void
|
|
592
|
+
}) {
|
|
593
|
+
return (
|
|
594
|
+
<Breadcrumb>
|
|
595
|
+
<BreadcrumbList size="S">
|
|
596
|
+
<BreadcrumbItem>
|
|
597
|
+
<BreadcrumbLink
|
|
598
|
+
href="#"
|
|
599
|
+
onClick={(e) => { e.preventDefault(); onNavigate(-1) }}
|
|
600
|
+
>
|
|
601
|
+
<i className="ri-folder-line mr-1" />
|
|
602
|
+
Root
|
|
603
|
+
</BreadcrumbLink>
|
|
604
|
+
</BreadcrumbItem>
|
|
605
|
+
|
|
606
|
+
{path.map((folder, index) => {
|
|
607
|
+
const isLast = index === path.length - 1
|
|
608
|
+
return (
|
|
609
|
+
<React.Fragment key={index}>
|
|
610
|
+
<BreadcrumbSeparator />
|
|
611
|
+
<BreadcrumbItem>
|
|
612
|
+
{isLast ? (
|
|
613
|
+
<BreadcrumbPage>
|
|
614
|
+
<i className="ri-folder-open-line mr-1" />
|
|
615
|
+
{folder}
|
|
616
|
+
</BreadcrumbPage>
|
|
617
|
+
) : (
|
|
618
|
+
<BreadcrumbLink
|
|
619
|
+
href="#"
|
|
620
|
+
onClick={(e) => { e.preventDefault(); onNavigate(index) }}
|
|
621
|
+
>
|
|
622
|
+
<i className="ri-folder-line mr-1" />
|
|
623
|
+
{folder}
|
|
624
|
+
</BreadcrumbLink>
|
|
625
|
+
)}
|
|
626
|
+
</BreadcrumbItem>
|
|
627
|
+
</React.Fragment>
|
|
628
|
+
)
|
|
629
|
+
})}
|
|
630
|
+
</BreadcrumbList>
|
|
631
|
+
</Breadcrumb>
|
|
632
|
+
)
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
## Testing
|
|
637
|
+
|
|
638
|
+
### Unit Test Example
|
|
639
|
+
|
|
640
|
+
```typescript
|
|
641
|
+
import { render, screen } from '@testing-library/react'
|
|
642
|
+
import {
|
|
643
|
+
Breadcrumb,
|
|
644
|
+
BreadcrumbList,
|
|
645
|
+
BreadcrumbItem,
|
|
646
|
+
BreadcrumbLink,
|
|
647
|
+
BreadcrumbPage,
|
|
648
|
+
BreadcrumbSeparator,
|
|
649
|
+
BreadcrumbEllipsis,
|
|
650
|
+
} from 'torch-glare/lib/components/Breadcrumb'
|
|
651
|
+
|
|
652
|
+
describe('Breadcrumb', () => {
|
|
653
|
+
it('renders navigation landmark', () => {
|
|
654
|
+
render(
|
|
655
|
+
<Breadcrumb>
|
|
656
|
+
<BreadcrumbList>
|
|
657
|
+
<BreadcrumbItem>
|
|
658
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
659
|
+
</BreadcrumbItem>
|
|
660
|
+
</BreadcrumbList>
|
|
661
|
+
</Breadcrumb>
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
expect(screen.getByRole('navigation')).toHaveAttribute('aria-label', 'breadcrumb')
|
|
665
|
+
})
|
|
666
|
+
|
|
667
|
+
it('renders links as anchors', () => {
|
|
668
|
+
render(
|
|
669
|
+
<Breadcrumb>
|
|
670
|
+
<BreadcrumbList>
|
|
671
|
+
<BreadcrumbItem>
|
|
672
|
+
<BreadcrumbLink href="/test">Test</BreadcrumbLink>
|
|
673
|
+
</BreadcrumbItem>
|
|
674
|
+
</BreadcrumbList>
|
|
675
|
+
</Breadcrumb>
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
const link = screen.getByText('Test')
|
|
679
|
+
expect(link.tagName).toBe('A')
|
|
680
|
+
expect(link).toHaveAttribute('href', '/test')
|
|
681
|
+
})
|
|
682
|
+
|
|
683
|
+
it('marks current page with aria-current', () => {
|
|
684
|
+
render(
|
|
685
|
+
<Breadcrumb>
|
|
686
|
+
<BreadcrumbList>
|
|
687
|
+
<BreadcrumbItem>
|
|
688
|
+
<BreadcrumbPage>Current</BreadcrumbPage>
|
|
689
|
+
</BreadcrumbItem>
|
|
690
|
+
</BreadcrumbList>
|
|
691
|
+
</Breadcrumb>
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
const page = screen.getByText('Current')
|
|
695
|
+
expect(page).toHaveAttribute('aria-current', 'page')
|
|
696
|
+
expect(page).toHaveAttribute('aria-disabled', 'true')
|
|
697
|
+
})
|
|
698
|
+
|
|
699
|
+
it('hides separators from accessibility tree', () => {
|
|
700
|
+
const { container } = render(
|
|
701
|
+
<Breadcrumb>
|
|
702
|
+
<BreadcrumbList>
|
|
703
|
+
<BreadcrumbItem>
|
|
704
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
705
|
+
</BreadcrumbItem>
|
|
706
|
+
<BreadcrumbSeparator />
|
|
707
|
+
<BreadcrumbItem>
|
|
708
|
+
<BreadcrumbPage>Page</BreadcrumbPage>
|
|
709
|
+
</BreadcrumbItem>
|
|
710
|
+
</BreadcrumbList>
|
|
711
|
+
</Breadcrumb>
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
const separator = container.querySelector('[role="presentation"]')
|
|
715
|
+
expect(separator).toHaveAttribute('aria-hidden', 'true')
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
it('renders ellipsis with screen reader text', () => {
|
|
719
|
+
render(
|
|
720
|
+
<Breadcrumb>
|
|
721
|
+
<BreadcrumbList>
|
|
722
|
+
<BreadcrumbItem>
|
|
723
|
+
<BreadcrumbEllipsis />
|
|
724
|
+
</BreadcrumbItem>
|
|
725
|
+
</BreadcrumbList>
|
|
726
|
+
</Breadcrumb>
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
expect(screen.getByText('More')).toHaveClass('sr-only')
|
|
730
|
+
})
|
|
731
|
+
|
|
732
|
+
it('applies size variant to list', () => {
|
|
733
|
+
const { container } = render(
|
|
734
|
+
<Breadcrumb>
|
|
735
|
+
<BreadcrumbList size="L">
|
|
736
|
+
<BreadcrumbItem>
|
|
737
|
+
<BreadcrumbPage>Page</BreadcrumbPage>
|
|
738
|
+
</BreadcrumbItem>
|
|
739
|
+
</BreadcrumbList>
|
|
740
|
+
</Breadcrumb>
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
const list = container.querySelector('ol')
|
|
744
|
+
expect(list).toHaveClass('text-[16px]', 'gap-2')
|
|
745
|
+
})
|
|
746
|
+
})
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
### Accessibility Test
|
|
750
|
+
|
|
751
|
+
```typescript
|
|
752
|
+
import { axe } from '@axe-core/react'
|
|
753
|
+
|
|
754
|
+
test('Breadcrumb meets WCAG standards', async () => {
|
|
755
|
+
const { container } = render(
|
|
756
|
+
<Breadcrumb>
|
|
757
|
+
<BreadcrumbList>
|
|
758
|
+
<BreadcrumbItem>
|
|
759
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
760
|
+
</BreadcrumbItem>
|
|
761
|
+
<BreadcrumbSeparator />
|
|
762
|
+
<BreadcrumbItem>
|
|
763
|
+
<BreadcrumbPage>Current Page</BreadcrumbPage>
|
|
764
|
+
</BreadcrumbItem>
|
|
765
|
+
</BreadcrumbList>
|
|
766
|
+
</Breadcrumb>
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
const results = await axe(container)
|
|
770
|
+
expect(results).toHaveNoViolations()
|
|
771
|
+
})
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
## Accessibility
|
|
775
|
+
|
|
776
|
+
### Semantic HTML
|
|
777
|
+
|
|
778
|
+
The Breadcrumb component renders proper semantic structure:
|
|
779
|
+
|
|
780
|
+
```html
|
|
781
|
+
<nav aria-label="breadcrumb">
|
|
782
|
+
<ol>
|
|
783
|
+
<li>
|
|
784
|
+
<a href="/">Home</a>
|
|
785
|
+
</li>
|
|
786
|
+
<li role="presentation" aria-hidden="true">
|
|
787
|
+
<!-- separator icon -->
|
|
788
|
+
</li>
|
|
789
|
+
<li>
|
|
790
|
+
<span role="link" aria-disabled="true" aria-current="page">
|
|
791
|
+
Current Page
|
|
792
|
+
</span>
|
|
793
|
+
</li>
|
|
794
|
+
</ol>
|
|
795
|
+
</nav>
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
### ARIA Attributes
|
|
799
|
+
|
|
800
|
+
- `<nav aria-label="breadcrumb">`: Identifies the navigation landmark
|
|
801
|
+
- `<span aria-current="page">`: Marks the current page in the trail
|
|
802
|
+
- `<span aria-disabled="true">`: Indicates the current page is not interactive
|
|
803
|
+
- `<li role="presentation" aria-hidden="true">`: Hides decorative separators
|
|
804
|
+
- `<span role="presentation" aria-hidden="true">`: Hides decorative ellipsis
|
|
805
|
+
|
|
806
|
+
### Keyboard Support
|
|
807
|
+
|
|
808
|
+
- **Tab**: Navigate between breadcrumb links
|
|
809
|
+
- **Enter**: Activate focused link
|
|
810
|
+
- **Space**: Activate focused link
|
|
811
|
+
|
|
812
|
+
### Screen Reader Support
|
|
813
|
+
|
|
814
|
+
- Navigation landmark announced as "breadcrumb"
|
|
815
|
+
- Links are announced with their text content
|
|
816
|
+
- Current page announced with "current page" semantics
|
|
817
|
+
- Separators and ellipsis hidden from screen readers
|
|
818
|
+
- "More" label provided for ellipsis via `sr-only` text
|
|
819
|
+
|
|
820
|
+
### Best Practices
|
|
821
|
+
|
|
822
|
+
```typescript
|
|
823
|
+
// Use BreadcrumbPage for the current/last item (not a link)
|
|
824
|
+
<BreadcrumbPage>Current Page</BreadcrumbPage>
|
|
825
|
+
|
|
826
|
+
// Use BreadcrumbLink for navigable items
|
|
827
|
+
<BreadcrumbLink href="/path">Navigable</BreadcrumbLink>
|
|
828
|
+
|
|
829
|
+
// Use asChild with framework routing components
|
|
830
|
+
<BreadcrumbLink asChild>
|
|
831
|
+
<Link href="/path">Next.js Link</Link>
|
|
832
|
+
</BreadcrumbLink>
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
## Styling
|
|
836
|
+
|
|
837
|
+
### Variant Styles
|
|
838
|
+
|
|
839
|
+
#### PresentationStyle (Default)
|
|
840
|
+
|
|
841
|
+
- **Links**: `text-content-presentation-global-secondary`, hover to `text-content-presentation-global-primary`
|
|
842
|
+
- **Current Page**: `text-content-presentation-global-primary`
|
|
843
|
+
- **Separator**: `text-content-presentation-global-secondary`
|
|
844
|
+
- **Ellipsis**: hover `bg-background-presentation-action-hover`
|
|
845
|
+
|
|
846
|
+
#### SystemStyle
|
|
847
|
+
|
|
848
|
+
- **Links**: `text-content-system-global-secondary`, hover to `text-content-system-global-primary`
|
|
849
|
+
- **Current Page**: `text-content-system-global-primary`
|
|
850
|
+
- **Separator**: `text-content-system-global-secondary`
|
|
851
|
+
- **Ellipsis**: hover `bg-background-system-action-secondary-hover`
|
|
852
|
+
|
|
853
|
+
### Link Interactions
|
|
854
|
+
|
|
855
|
+
```css
|
|
856
|
+
/* Links have hover underline and focus ring */
|
|
857
|
+
hover:underline
|
|
858
|
+
focus-visible:ring-2
|
|
859
|
+
focus-visible:ring-border-presentation-state-focus
|
|
860
|
+
focus-visible:rounded-[2px]
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
### Ellipsis Dimensions
|
|
864
|
+
|
|
865
|
+
```css
|
|
866
|
+
/* Fixed 24x24px clickable area with rounded corners */
|
|
867
|
+
w-6 h-6
|
|
868
|
+
rounded-[4px]
|
|
869
|
+
cursor-pointer
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
### Custom Styles
|
|
873
|
+
|
|
874
|
+
```typescript
|
|
875
|
+
<Breadcrumb className="bg-gray-50 px-4 py-2 rounded-lg">
|
|
876
|
+
<BreadcrumbList>
|
|
877
|
+
<BreadcrumbItem>
|
|
878
|
+
<BreadcrumbLink href="/" className="text-blue-600">Home</BreadcrumbLink>
|
|
879
|
+
</BreadcrumbItem>
|
|
880
|
+
<BreadcrumbSeparator className="text-gray-400" />
|
|
881
|
+
<BreadcrumbItem>
|
|
882
|
+
<BreadcrumbPage className="font-bold">Page</BreadcrumbPage>
|
|
883
|
+
</BreadcrumbItem>
|
|
884
|
+
</BreadcrumbList>
|
|
885
|
+
</Breadcrumb>
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
## Performance
|
|
889
|
+
|
|
890
|
+
| Metric | Value |
|
|
891
|
+
|--------|-------|
|
|
892
|
+
| Bundle size (gzip) | 2.2kb |
|
|
893
|
+
| First render | <5ms |
|
|
894
|
+
| Re-render | <2ms |
|
|
895
|
+
| Tree-shakeable | Yes |
|
|
896
|
+
|
|
897
|
+
### Optimization Tips
|
|
898
|
+
|
|
899
|
+
1. Use `React.Fragment` with keys for dynamic breadcrumb generation
|
|
900
|
+
2. Memoize breadcrumb segments if derived from expensive computations
|
|
901
|
+
3. Use `asChild` pattern to avoid extra DOM nodes when integrating with routers
|
|
902
|
+
|
|
903
|
+
## Troubleshooting
|
|
904
|
+
|
|
905
|
+
### Links not navigating with Next.js
|
|
906
|
+
|
|
907
|
+
**Solution:** Use the `asChild` prop to pass props to your framework's Link component:
|
|
908
|
+
|
|
909
|
+
```typescript
|
|
910
|
+
<BreadcrumbLink asChild>
|
|
911
|
+
<Link href="/path">Page</Link>
|
|
912
|
+
</BreadcrumbLink>
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
### Separator icon not showing
|
|
916
|
+
|
|
917
|
+
**Solution:** Ensure Remix Icon CSS is loaded. The default separator uses `ri-arrow-right-s-line`. Alternatively, provide custom separator content:
|
|
918
|
+
|
|
919
|
+
```typescript
|
|
920
|
+
<BreadcrumbSeparator>/</BreadcrumbSeparator>
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
### Breadcrumb wrapping on small screens
|
|
924
|
+
|
|
925
|
+
**Solution:** The BreadcrumbList uses `flex-wrap` by default. To prevent wrapping, add `flex-nowrap` and `overflow-hidden`:
|
|
926
|
+
|
|
927
|
+
```typescript
|
|
928
|
+
<BreadcrumbList className="flex-nowrap overflow-hidden">
|
|
929
|
+
{/* items */}
|
|
930
|
+
</BreadcrumbList>
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
## Related Components
|
|
934
|
+
|
|
935
|
+
- [DropdownMenu](/docs/components/dropdown-menu.md) - Combine with BreadcrumbEllipsis for collapsed items
|
|
936
|
+
- [LinkButton](/docs/components/link-button.md) - Styled link actions
|
|
937
|
+
- [Button](/docs/components/button.md) - Action buttons
|
|
938
|
+
|
|
939
|
+
## Browser Support
|
|
940
|
+
|
|
941
|
+
- Chrome 90+
|
|
942
|
+
- Firefox 88+
|
|
943
|
+
- Safari 14+
|
|
944
|
+
- Edge 90+
|
|
945
|
+
- Mobile browsers
|
|
946
|
+
|
|
947
|
+
## Changelog
|
|
948
|
+
|
|
949
|
+
### v1.1.15
|
|
950
|
+
- Initial stable release
|
|
951
|
+
- 7 sub-components (Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis)
|
|
952
|
+
- 2 style variants (PresentationStyle, SystemStyle)
|
|
953
|
+
- 3 list size variants (S, M, L)
|
|
954
|
+
- asChild pattern for custom link components
|
|
955
|
+
- Full accessibility with ARIA attributes and semantic HTML
|