@fragments-sdk/ui 0.2.2 → 0.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.
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { defineSegment } from '@fragments/core';
|
|
3
|
+
import { Sidebar, SidebarProvider, useSidebar } from './index.js';
|
|
4
|
+
import { Button } from '../Button/index.js';
|
|
5
|
+
|
|
6
|
+
// Example icons (inline SVGs for demo)
|
|
7
|
+
const HomeIcon = () => (
|
|
8
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 256 256" fill="currentColor">
|
|
9
|
+
<path d="M219.31,108.68l-80-80a16,16,0,0,0-22.62,0l-80,80A15.87,15.87,0,0,0,32,120v96a8,8,0,0,0,8,8H96a8,8,0,0,0,8-8V160h48v56a8,8,0,0,0,8,8h56a8,8,0,0,0,8-8V120A15.87,15.87,0,0,0,219.31,108.68ZM208,208H168V152a8,8,0,0,0-8-8H96a8,8,0,0,0-8,8v56H48V120l80-80,80,80Z" />
|
|
10
|
+
</svg>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const UsersIcon = () => (
|
|
14
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 256 256" fill="currentColor">
|
|
15
|
+
<path d="M117.25,157.92a60,60,0,1,0-66.5,0A95.83,95.83,0,0,0,3.53,195.63a8,8,0,1,0,13.4,8.74,80,80,0,0,1,134.14,0,8,8,0,0,0,13.4-8.74A95.83,95.83,0,0,0,117.25,157.92ZM40,108a44,44,0,1,1,44,44A44.05,44.05,0,0,1,40,108Zm210.14,98.7a8,8,0,0,1-11.07-2.33A79.83,79.83,0,0,0,172,168a8,8,0,0,1,0-16,44,44,0,1,0-16.34-84.87,8,8,0,1,1-5.94-14.85,60,60,0,0,1,55.53,105.64,95.83,95.83,0,0,1,47.22,37.71A8,8,0,0,1,250.14,206.7Z" />
|
|
16
|
+
</svg>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const FolderIcon = () => (
|
|
20
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 256 256" fill="currentColor">
|
|
21
|
+
<path d="M216,72H130.67L102.93,51.2a16.12,16.12,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V200a16,16,0,0,0,16,16H216.89A15.13,15.13,0,0,0,232,200.89V88A16,16,0,0,0,216,72Zm0,128H40V64H93.33l27.74,20.8a16.12,16.12,0,0,0,9.6,3.2H216Z" />
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const ChartIcon = () => (
|
|
26
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 256 256" fill="currentColor">
|
|
27
|
+
<path d="M224,200h-8V40a8,8,0,0,0-16,0V200H168V96a8,8,0,0,0-16,0V200H120V136a8,8,0,0,0-16,0v64H72V168a8,8,0,0,0-16,0v32H40a8,8,0,0,0,0,16H224a8,8,0,0,0,0-16Z" />
|
|
28
|
+
</svg>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const GearIcon = () => (
|
|
32
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 256 256" fill="currentColor">
|
|
33
|
+
<path d="M128,80a48,48,0,1,0,48,48A48.05,48.05,0,0,0,128,80Zm0,80a32,32,0,1,1,32-32A32,32,0,0,1,128,160Zm88-29.84q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.21,107.21,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.71,107.71,0,0,0-26.25-10.87,8,8,0,0,0-7.06,1.49L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.21,107.21,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06Zm-16.1-6.5a73.93,73.93,0,0,1,0,8.68,8,8,0,0,0,1.74,5.48l14.19,17.73a91.57,91.57,0,0,1-6.23,15L187,173.11a8,8,0,0,0-5.1,2.64,74.11,74.11,0,0,1-6.14,6.14,8,8,0,0,0-2.64,5.1l-2.51,22.58a91.32,91.32,0,0,1-15,6.23l-17.74-14.19a8,8,0,0,0-5-1.75h-.48a73.93,73.93,0,0,1-8.68,0,8,8,0,0,0-5.48,1.74L100.45,215.8a91.57,91.57,0,0,1-15-6.23L82.89,187a8,8,0,0,0-2.64-5.1,74.11,74.11,0,0,1-6.14-6.14,8,8,0,0,0-5.1-2.64L46.43,170.6a91.32,91.32,0,0,1-6.23-15l14.19-17.74a8,8,0,0,0,1.74-5.48,73.93,73.93,0,0,1,0-8.68,8,8,0,0,0-1.74-5.48L40.2,100.45a91.57,91.57,0,0,1,6.23-15L69,82.89a8,8,0,0,0,5.1-2.64,74.11,74.11,0,0,1,6.14-6.14A8,8,0,0,0,82.89,69L85.4,46.43a91.32,91.32,0,0,1,15-6.23l17.74,14.19a8,8,0,0,0,5.48,1.74,73.93,73.93,0,0,1,8.68,0,8,8,0,0,0,5.48-1.74L155.55,40.2a91.57,91.57,0,0,1,15,6.23L173.11,69a8,8,0,0,0,2.64,5.1,74.11,74.11,0,0,1,6.14,6.14,8,8,0,0,0,5.1,2.64l22.58,2.51a91.32,91.32,0,0,1,6.23,15l-14.19,17.74A8,8,0,0,0,199.87,123.66Z" />
|
|
34
|
+
</svg>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const HelpIcon = () => (
|
|
38
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 256 256" fill="currentColor">
|
|
39
|
+
<path d="M140,180a12,12,0,1,1-12-12A12,12,0,0,1,140,180ZM128,72c-22.06,0-40,16.15-40,36v4a8,8,0,0,0,16,0v-4c0-11,10.77-20,24-20s24,9,24,20-10.77,20-24,20a8,8,0,0,0-8,8v8a8,8,0,0,0,16,0v-.72c18.24-3.35,32-17.9,32-35.28C168,88.15,150.06,72,128,72Zm104,56A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z" />
|
|
40
|
+
</svg>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const PlusIcon = () => (
|
|
44
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256" fill="currentColor">
|
|
45
|
+
<path d="M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z" />
|
|
46
|
+
</svg>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const SidebarToggleIcon = () => (
|
|
50
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 256 256" fill="currentColor">
|
|
51
|
+
<path d="M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40ZM40,56H80V200H40ZM216,200H96V56H216V200Z" />
|
|
52
|
+
</svg>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Mock Link component for demonstrating asChild pattern
|
|
56
|
+
const MockLink = React.forwardRef<HTMLAnchorElement, React.AnchorHTMLAttributes<HTMLAnchorElement>>(
|
|
57
|
+
({ children, ...props }, ref) => (
|
|
58
|
+
<a ref={ref} {...props} onClick={(e) => { e.preventDefault(); props.onClick?.(e); }}>
|
|
59
|
+
{children}
|
|
60
|
+
</a>
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const LogoIcon = ({ size = 32 }: { size?: number }) => (
|
|
65
|
+
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 256 256" fill="currentColor">
|
|
66
|
+
<path d="M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32Zm0,176H48V48H208V208Z" />
|
|
67
|
+
</svg>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Shared styles for demo content area
|
|
71
|
+
const mainContentStyle: React.CSSProperties = {
|
|
72
|
+
flex: 1,
|
|
73
|
+
minWidth: 0, // Prevents flex item from overflowing
|
|
74
|
+
padding: '24px',
|
|
75
|
+
background: 'var(--fui-bg-secondary)',
|
|
76
|
+
display: 'flex',
|
|
77
|
+
alignItems: 'center',
|
|
78
|
+
justifyContent: 'center',
|
|
79
|
+
color: 'var(--fui-text-secondary)',
|
|
80
|
+
fontSize: 'var(--fui-font-size-sm)',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const demoContainerStyle: React.CSSProperties = {
|
|
84
|
+
height: '400px',
|
|
85
|
+
display: 'flex',
|
|
86
|
+
width: '100%',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Stateful demo for collapsed state
|
|
90
|
+
function CollapsedDemo() {
|
|
91
|
+
const [collapsed, setCollapsed] = useState(true);
|
|
92
|
+
return (
|
|
93
|
+
<div style={demoContainerStyle}>
|
|
94
|
+
<Sidebar collapsed={collapsed} onCollapsedChange={setCollapsed}>
|
|
95
|
+
<Sidebar.Header collapsedContent={<LogoIcon size={32} />}>
|
|
96
|
+
<LogoIcon size={32} />
|
|
97
|
+
<span style={{ fontWeight: 600, fontSize: '16px' }}>Acme App</span>
|
|
98
|
+
</Sidebar.Header>
|
|
99
|
+
<Sidebar.Nav>
|
|
100
|
+
<Sidebar.Section>
|
|
101
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
102
|
+
<Sidebar.Item icon={<ChartIcon />}>Analytics</Sidebar.Item>
|
|
103
|
+
<Sidebar.Item icon={<UsersIcon />}>Team</Sidebar.Item>
|
|
104
|
+
<Sidebar.Item icon={<FolderIcon />}>Projects</Sidebar.Item>
|
|
105
|
+
</Sidebar.Section>
|
|
106
|
+
<Sidebar.Section label="Settings">
|
|
107
|
+
<Sidebar.Item icon={<GearIcon />}>Preferences</Sidebar.Item>
|
|
108
|
+
<Sidebar.Item icon={<HelpIcon />}>Help</Sidebar.Item>
|
|
109
|
+
</Sidebar.Section>
|
|
110
|
+
</Sidebar.Nav>
|
|
111
|
+
<Sidebar.Footer>
|
|
112
|
+
<Sidebar.CollapseToggle />
|
|
113
|
+
</Sidebar.Footer>
|
|
114
|
+
</Sidebar>
|
|
115
|
+
<main style={mainContentStyle}>
|
|
116
|
+
Hover over icons to see tooltips. Click toggle to expand.
|
|
117
|
+
</main>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Demo for submenu expansion using uncontrolled defaultExpanded
|
|
123
|
+
function SubmenuDemo() {
|
|
124
|
+
return (
|
|
125
|
+
<div style={demoContainerStyle}>
|
|
126
|
+
<Sidebar>
|
|
127
|
+
<Sidebar.Header collapsedContent={<LogoIcon size={32} />}>
|
|
128
|
+
<LogoIcon size={32} />
|
|
129
|
+
<span style={{ fontWeight: 600, fontSize: '16px' }}>Acme App</span>
|
|
130
|
+
</Sidebar.Header>
|
|
131
|
+
<Sidebar.Nav>
|
|
132
|
+
<Sidebar.Section>
|
|
133
|
+
<Sidebar.Item icon={<HomeIcon />}>Dashboard</Sidebar.Item>
|
|
134
|
+
<Sidebar.Item icon={<FolderIcon />} hasSubmenu defaultExpanded>
|
|
135
|
+
Projects
|
|
136
|
+
</Sidebar.Item>
|
|
137
|
+
<Sidebar.Submenu>
|
|
138
|
+
<Sidebar.SubItem active>Website Redesign</Sidebar.SubItem>
|
|
139
|
+
<Sidebar.SubItem>Mobile App</Sidebar.SubItem>
|
|
140
|
+
<Sidebar.SubItem>API Integration</Sidebar.SubItem>
|
|
141
|
+
</Sidebar.Submenu>
|
|
142
|
+
<Sidebar.Item icon={<UsersIcon />} hasSubmenu>
|
|
143
|
+
Team
|
|
144
|
+
</Sidebar.Item>
|
|
145
|
+
<Sidebar.Submenu>
|
|
146
|
+
<Sidebar.SubItem>Engineering</Sidebar.SubItem>
|
|
147
|
+
<Sidebar.SubItem>Design</Sidebar.SubItem>
|
|
148
|
+
</Sidebar.Submenu>
|
|
149
|
+
</Sidebar.Section>
|
|
150
|
+
</Sidebar.Nav>
|
|
151
|
+
<Sidebar.Footer>
|
|
152
|
+
<Sidebar.CollapseToggle />
|
|
153
|
+
</Sidebar.Footer>
|
|
154
|
+
</Sidebar>
|
|
155
|
+
<main style={mainContentStyle}>
|
|
156
|
+
Click on "Projects" or "Team" to toggle their submenus
|
|
157
|
+
</main>
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Demo for SidebarProvider with external trigger
|
|
163
|
+
function ExternalTriggerContent() {
|
|
164
|
+
const { toggleSidebar, state } = useSidebar();
|
|
165
|
+
return (
|
|
166
|
+
<main style={mainContentStyle}>
|
|
167
|
+
<div style={{ textAlign: 'center' }}>
|
|
168
|
+
<p style={{ marginBottom: '16px' }}>
|
|
169
|
+
Sidebar state: <strong>{state}</strong>
|
|
170
|
+
</p>
|
|
171
|
+
<Button onClick={toggleSidebar} variant="outline" size="sm">
|
|
172
|
+
<SidebarToggleIcon /> Toggle Sidebar
|
|
173
|
+
</Button>
|
|
174
|
+
<p style={{ marginTop: '16px', fontSize: '12px', opacity: 0.7 }}>
|
|
175
|
+
Also try pressing Cmd/Ctrl+B
|
|
176
|
+
</p>
|
|
177
|
+
</div>
|
|
178
|
+
</main>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function ProviderDemo() {
|
|
183
|
+
return (
|
|
184
|
+
<div style={demoContainerStyle}>
|
|
185
|
+
<SidebarProvider defaultCollapsed={false}>
|
|
186
|
+
<Sidebar>
|
|
187
|
+
<Sidebar.Header collapsedContent={<LogoIcon size={32} />}>
|
|
188
|
+
<LogoIcon size={32} />
|
|
189
|
+
<span style={{ fontWeight: 600, fontSize: '16px' }}>Acme App</span>
|
|
190
|
+
</Sidebar.Header>
|
|
191
|
+
<Sidebar.Nav>
|
|
192
|
+
<Sidebar.Section>
|
|
193
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
194
|
+
<Sidebar.Item icon={<ChartIcon />}>Analytics</Sidebar.Item>
|
|
195
|
+
<Sidebar.Item icon={<UsersIcon />}>Team</Sidebar.Item>
|
|
196
|
+
</Sidebar.Section>
|
|
197
|
+
</Sidebar.Nav>
|
|
198
|
+
<Sidebar.Footer>
|
|
199
|
+
<Sidebar.CollapseToggle />
|
|
200
|
+
</Sidebar.Footer>
|
|
201
|
+
</Sidebar>
|
|
202
|
+
<ExternalTriggerContent />
|
|
203
|
+
</SidebarProvider>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Demo for asChild pattern with mock Link
|
|
209
|
+
function AsChildDemo() {
|
|
210
|
+
return (
|
|
211
|
+
<div style={demoContainerStyle}>
|
|
212
|
+
<Sidebar>
|
|
213
|
+
<Sidebar.Header collapsedContent={<LogoIcon size={32} />}>
|
|
214
|
+
<LogoIcon size={32} />
|
|
215
|
+
<span style={{ fontWeight: 600, fontSize: '16px' }}>Acme App</span>
|
|
216
|
+
</Sidebar.Header>
|
|
217
|
+
<Sidebar.Nav>
|
|
218
|
+
<Sidebar.Section>
|
|
219
|
+
<Sidebar.Item icon={<HomeIcon />} active asChild>
|
|
220
|
+
<MockLink href="/dashboard">Dashboard</MockLink>
|
|
221
|
+
</Sidebar.Item>
|
|
222
|
+
<Sidebar.Item icon={<ChartIcon />} asChild>
|
|
223
|
+
<MockLink href="/analytics">Analytics</MockLink>
|
|
224
|
+
</Sidebar.Item>
|
|
225
|
+
<Sidebar.Item icon={<UsersIcon />} asChild>
|
|
226
|
+
<MockLink href="/team">Team</MockLink>
|
|
227
|
+
</Sidebar.Item>
|
|
228
|
+
<Sidebar.Item icon={<FolderIcon />} asChild>
|
|
229
|
+
<MockLink href="/projects">Projects</MockLink>
|
|
230
|
+
</Sidebar.Item>
|
|
231
|
+
</Sidebar.Section>
|
|
232
|
+
</Sidebar.Nav>
|
|
233
|
+
<Sidebar.Footer>
|
|
234
|
+
<Sidebar.CollapseToggle />
|
|
235
|
+
</Sidebar.Footer>
|
|
236
|
+
</Sidebar>
|
|
237
|
+
<main style={mainContentStyle}>
|
|
238
|
+
Items rendered as Link components using asChild pattern
|
|
239
|
+
</main>
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Demo for section with action button
|
|
245
|
+
function SectionActionDemo() {
|
|
246
|
+
return (
|
|
247
|
+
<div style={demoContainerStyle}>
|
|
248
|
+
<Sidebar>
|
|
249
|
+
<Sidebar.Header collapsedContent={<LogoIcon size={32} />}>
|
|
250
|
+
<LogoIcon size={32} />
|
|
251
|
+
<span style={{ fontWeight: 600, fontSize: '16px' }}>Acme App</span>
|
|
252
|
+
</Sidebar.Header>
|
|
253
|
+
<Sidebar.Nav>
|
|
254
|
+
<Sidebar.Section>
|
|
255
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
256
|
+
<Sidebar.Item icon={<ChartIcon />}>Analytics</Sidebar.Item>
|
|
257
|
+
</Sidebar.Section>
|
|
258
|
+
<Sidebar.Section
|
|
259
|
+
label="Projects"
|
|
260
|
+
action={
|
|
261
|
+
<Sidebar.SectionAction aria-label="Add project" onClick={() => alert('Add project clicked')}>
|
|
262
|
+
<PlusIcon />
|
|
263
|
+
</Sidebar.SectionAction>
|
|
264
|
+
}
|
|
265
|
+
>
|
|
266
|
+
<Sidebar.Item icon={<FolderIcon />}>Website Redesign</Sidebar.Item>
|
|
267
|
+
<Sidebar.Item icon={<FolderIcon />}>Mobile App</Sidebar.Item>
|
|
268
|
+
<Sidebar.Item icon={<FolderIcon />}>API Integration</Sidebar.Item>
|
|
269
|
+
</Sidebar.Section>
|
|
270
|
+
</Sidebar.Nav>
|
|
271
|
+
<Sidebar.Footer>
|
|
272
|
+
<Sidebar.CollapseToggle />
|
|
273
|
+
</Sidebar.Footer>
|
|
274
|
+
</Sidebar>
|
|
275
|
+
<main style={mainContentStyle}>
|
|
276
|
+
Section header has an "Add" action button
|
|
277
|
+
</main>
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Demo for skeleton loading state
|
|
283
|
+
function SkeletonDemo() {
|
|
284
|
+
const [loading, setLoading] = useState(true);
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
<div style={demoContainerStyle}>
|
|
288
|
+
<Sidebar>
|
|
289
|
+
<Sidebar.Header collapsedContent={<LogoIcon size={32} />}>
|
|
290
|
+
<LogoIcon size={32} />
|
|
291
|
+
<span style={{ fontWeight: 600, fontSize: '16px' }}>Acme App</span>
|
|
292
|
+
</Sidebar.Header>
|
|
293
|
+
<Sidebar.Nav>
|
|
294
|
+
{loading ? (
|
|
295
|
+
<Sidebar.MenuSkeleton count={6} />
|
|
296
|
+
) : (
|
|
297
|
+
<Sidebar.Section>
|
|
298
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
299
|
+
<Sidebar.Item icon={<ChartIcon />}>Analytics</Sidebar.Item>
|
|
300
|
+
<Sidebar.Item icon={<UsersIcon />}>Team</Sidebar.Item>
|
|
301
|
+
<Sidebar.Item icon={<FolderIcon />}>Projects</Sidebar.Item>
|
|
302
|
+
<Sidebar.Item icon={<GearIcon />}>Settings</Sidebar.Item>
|
|
303
|
+
<Sidebar.Item icon={<HelpIcon />}>Help</Sidebar.Item>
|
|
304
|
+
</Sidebar.Section>
|
|
305
|
+
)}
|
|
306
|
+
</Sidebar.Nav>
|
|
307
|
+
<Sidebar.Footer>
|
|
308
|
+
<Sidebar.CollapseToggle />
|
|
309
|
+
</Sidebar.Footer>
|
|
310
|
+
</Sidebar>
|
|
311
|
+
<main style={mainContentStyle}>
|
|
312
|
+
<div style={{ textAlign: 'center' }}>
|
|
313
|
+
<p style={{ marginBottom: '16px' }}>
|
|
314
|
+
{loading ? 'Loading navigation...' : 'Navigation loaded!'}
|
|
315
|
+
</p>
|
|
316
|
+
<Button onClick={() => setLoading(!loading)} variant="outline" size="sm">
|
|
317
|
+
{loading ? 'Show Content' : 'Show Skeleton'}
|
|
318
|
+
</Button>
|
|
319
|
+
</div>
|
|
320
|
+
</main>
|
|
321
|
+
</div>
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Demo for rail toggle
|
|
326
|
+
function RailDemo() {
|
|
327
|
+
const [collapsed, setCollapsed] = useState(false);
|
|
328
|
+
|
|
329
|
+
return (
|
|
330
|
+
<div style={{ ...demoContainerStyle, position: 'relative' }}>
|
|
331
|
+
<Sidebar collapsed={collapsed} onCollapsedChange={setCollapsed}>
|
|
332
|
+
<Sidebar.Header collapsedContent={<LogoIcon size={32} />}>
|
|
333
|
+
<LogoIcon size={32} />
|
|
334
|
+
<span style={{ fontWeight: 600, fontSize: '16px' }}>Acme App</span>
|
|
335
|
+
</Sidebar.Header>
|
|
336
|
+
<Sidebar.Nav>
|
|
337
|
+
<Sidebar.Section>
|
|
338
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
339
|
+
<Sidebar.Item icon={<ChartIcon />}>Analytics</Sidebar.Item>
|
|
340
|
+
<Sidebar.Item icon={<UsersIcon />}>Team</Sidebar.Item>
|
|
341
|
+
<Sidebar.Item icon={<FolderIcon />}>Projects</Sidebar.Item>
|
|
342
|
+
</Sidebar.Section>
|
|
343
|
+
</Sidebar.Nav>
|
|
344
|
+
<Sidebar.Footer>
|
|
345
|
+
<Sidebar.CollapseToggle />
|
|
346
|
+
</Sidebar.Footer>
|
|
347
|
+
<Sidebar.Rail />
|
|
348
|
+
</Sidebar>
|
|
349
|
+
<main style={mainContentStyle}>
|
|
350
|
+
<div style={{ textAlign: 'center' }}>
|
|
351
|
+
<p>Hover over the right edge of the sidebar to see the rail indicator.</p>
|
|
352
|
+
<p style={{ marginTop: '8px', opacity: 0.7 }}>Click the rail to toggle collapse/expand.</p>
|
|
353
|
+
</div>
|
|
354
|
+
</main>
|
|
355
|
+
</div>
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export default defineSegment({
|
|
360
|
+
component: Sidebar,
|
|
361
|
+
|
|
362
|
+
meta: {
|
|
363
|
+
name: 'Sidebar',
|
|
364
|
+
description: 'Responsive navigation sidebar with collapsible desktop mode and mobile drawer behavior.',
|
|
365
|
+
category: 'navigation',
|
|
366
|
+
status: 'stable',
|
|
367
|
+
tags: ['sidebar', 'navigation', 'drawer', 'menu', 'layout'],
|
|
368
|
+
since: '0.3.0',
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
usage: {
|
|
372
|
+
when: [
|
|
373
|
+
'Primary app navigation with multiple sections',
|
|
374
|
+
'Dashboard layouts requiring persistent navigation',
|
|
375
|
+
'Admin interfaces with hierarchical menu structure',
|
|
376
|
+
'Apps that need both mobile drawer and desktop sidebar',
|
|
377
|
+
],
|
|
378
|
+
whenNot: [
|
|
379
|
+
'Simple websites with few pages (use header nav)',
|
|
380
|
+
'Content-focused sites where navigation is secondary',
|
|
381
|
+
'Single-page applications with no navigation needs',
|
|
382
|
+
'Mobile-only apps where bottom navigation is preferred',
|
|
383
|
+
],
|
|
384
|
+
guidelines: [
|
|
385
|
+
'Group related items into sections with clear labels',
|
|
386
|
+
'Use icons for all items to support collapsed mode',
|
|
387
|
+
'Limit nesting to 2 levels maximum',
|
|
388
|
+
'Place most frequently used items at the top',
|
|
389
|
+
'Use badges sparingly for notifications or counts',
|
|
390
|
+
'The `active` prop on items should be controlled by your app based on current route',
|
|
391
|
+
'Use `collapsedContent` on Header to show just a logo icon when collapsed',
|
|
392
|
+
'Submenus are hidden when collapsed - use tooltips for navigation hints instead',
|
|
393
|
+
'Use SidebarProvider to enable external triggers and keyboard shortcuts',
|
|
394
|
+
'Use asChild with routing libraries (Next.js Link, React Router NavLink)',
|
|
395
|
+
'Use Sidebar.MenuSkeleton while loading navigation data',
|
|
396
|
+
],
|
|
397
|
+
accessibility: [
|
|
398
|
+
'Uses semantic <nav> element with aria-label',
|
|
399
|
+
'aria-current="page" on active items',
|
|
400
|
+
'aria-expanded on items with submenus',
|
|
401
|
+
'Escape key closes mobile drawer',
|
|
402
|
+
'Cmd/Ctrl+B keyboard shortcut toggles sidebar (when using SidebarProvider)',
|
|
403
|
+
'Focus trap in mobile drawer mode',
|
|
404
|
+
'Minimum 44px touch targets',
|
|
405
|
+
],
|
|
406
|
+
},
|
|
407
|
+
|
|
408
|
+
props: {
|
|
409
|
+
children: {
|
|
410
|
+
type: 'node',
|
|
411
|
+
description: 'Sidebar content (use Sidebar.Header, Sidebar.Nav, Sidebar.Section, etc.)',
|
|
412
|
+
required: true,
|
|
413
|
+
},
|
|
414
|
+
collapsed: {
|
|
415
|
+
type: 'boolean',
|
|
416
|
+
description: 'Icon-only mode for desktop (controlled)',
|
|
417
|
+
},
|
|
418
|
+
defaultCollapsed: {
|
|
419
|
+
type: 'boolean',
|
|
420
|
+
description: 'Initial collapsed state (uncontrolled)',
|
|
421
|
+
default: false,
|
|
422
|
+
},
|
|
423
|
+
onCollapsedChange: {
|
|
424
|
+
type: 'function',
|
|
425
|
+
description: 'Called when collapsed state changes',
|
|
426
|
+
},
|
|
427
|
+
open: {
|
|
428
|
+
type: 'boolean',
|
|
429
|
+
description: 'Mobile drawer open state (controlled)',
|
|
430
|
+
},
|
|
431
|
+
defaultOpen: {
|
|
432
|
+
type: 'boolean',
|
|
433
|
+
description: 'Initial open state (uncontrolled)',
|
|
434
|
+
default: false,
|
|
435
|
+
},
|
|
436
|
+
onOpenChange: {
|
|
437
|
+
type: 'function',
|
|
438
|
+
description: 'Called when open state changes',
|
|
439
|
+
},
|
|
440
|
+
width: {
|
|
441
|
+
type: 'string',
|
|
442
|
+
description: 'Width of expanded sidebar',
|
|
443
|
+
default: '240px',
|
|
444
|
+
},
|
|
445
|
+
collapsedWidth: {
|
|
446
|
+
type: 'string',
|
|
447
|
+
description: 'Width when collapsed',
|
|
448
|
+
default: '64px',
|
|
449
|
+
},
|
|
450
|
+
position: {
|
|
451
|
+
type: 'enum',
|
|
452
|
+
description: 'Sidebar position',
|
|
453
|
+
values: ['left', 'right'],
|
|
454
|
+
default: 'left',
|
|
455
|
+
},
|
|
456
|
+
collapsible: {
|
|
457
|
+
type: 'enum',
|
|
458
|
+
description: 'Collapse behavior mode',
|
|
459
|
+
values: ['icon', 'offcanvas', 'none'],
|
|
460
|
+
default: 'icon',
|
|
461
|
+
},
|
|
462
|
+
asChild: {
|
|
463
|
+
type: 'boolean',
|
|
464
|
+
description: '(Sidebar.Item) Render as child element for polymorphic composition',
|
|
465
|
+
default: false,
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
relations: [
|
|
470
|
+
{ component: 'Tabs', relationship: 'alternative', note: 'Use Tabs for in-page section navigation' },
|
|
471
|
+
{ component: 'Menu', relationship: 'composition', note: 'Use Menu for contextual actions within sidebar' },
|
|
472
|
+
],
|
|
473
|
+
|
|
474
|
+
contract: {
|
|
475
|
+
propsSummary: [
|
|
476
|
+
'collapsed: boolean - icon-only desktop mode',
|
|
477
|
+
'open: boolean - mobile drawer state',
|
|
478
|
+
'width: string - expanded width (default: 240px)',
|
|
479
|
+
'position: left|right - sidebar position',
|
|
480
|
+
'active: boolean - set on Sidebar.Item to mark current page (app-controlled)',
|
|
481
|
+
],
|
|
482
|
+
scenarioTags: [
|
|
483
|
+
'navigation.sidebar',
|
|
484
|
+
'layout.sidebar',
|
|
485
|
+
'navigation.drawer',
|
|
486
|
+
],
|
|
487
|
+
a11yRules: ['A11Y_NAV_LANDMARK', 'A11Y_FOCUS_TRAP', 'A11Y_ESCAPE_CLOSE'],
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
variants: [
|
|
491
|
+
{
|
|
492
|
+
name: 'Default',
|
|
493
|
+
description: 'Standard sidebar with navigation sections. The `active` prop highlights the current page.',
|
|
494
|
+
code: `<Sidebar>
|
|
495
|
+
<Sidebar.Header>
|
|
496
|
+
<Logo />
|
|
497
|
+
<span>Acme App</span>
|
|
498
|
+
</Sidebar.Header>
|
|
499
|
+
<Sidebar.Nav>
|
|
500
|
+
<Sidebar.Section>
|
|
501
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
502
|
+
<Sidebar.Item icon={<ChartIcon />}>Analytics</Sidebar.Item>
|
|
503
|
+
<Sidebar.Item icon={<UsersIcon />}>Team</Sidebar.Item>
|
|
504
|
+
<Sidebar.Item icon={<FolderIcon />}>Projects</Sidebar.Item>
|
|
505
|
+
</Sidebar.Section>
|
|
506
|
+
<Sidebar.Section label="Settings">
|
|
507
|
+
<Sidebar.Item icon={<GearIcon />}>Preferences</Sidebar.Item>
|
|
508
|
+
<Sidebar.Item icon={<HelpIcon />}>Help</Sidebar.Item>
|
|
509
|
+
</Sidebar.Section>
|
|
510
|
+
</Sidebar.Nav>
|
|
511
|
+
<Sidebar.Footer>
|
|
512
|
+
<Sidebar.CollapseToggle />
|
|
513
|
+
</Sidebar.Footer>
|
|
514
|
+
</Sidebar>`,
|
|
515
|
+
render: () => (
|
|
516
|
+
<div style={demoContainerStyle}>
|
|
517
|
+
<Sidebar>
|
|
518
|
+
<Sidebar.Header collapsedContent={<LogoIcon size={32} />}>
|
|
519
|
+
<LogoIcon size={32} />
|
|
520
|
+
<span style={{ fontWeight: 600, fontSize: '16px' }}>Acme App</span>
|
|
521
|
+
</Sidebar.Header>
|
|
522
|
+
<Sidebar.Nav>
|
|
523
|
+
<Sidebar.Section>
|
|
524
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
525
|
+
<Sidebar.Item icon={<ChartIcon />}>Analytics</Sidebar.Item>
|
|
526
|
+
<Sidebar.Item icon={<UsersIcon />}>Team</Sidebar.Item>
|
|
527
|
+
<Sidebar.Item icon={<FolderIcon />}>Projects</Sidebar.Item>
|
|
528
|
+
</Sidebar.Section>
|
|
529
|
+
<Sidebar.Section label="Settings">
|
|
530
|
+
<Sidebar.Item icon={<GearIcon />}>Preferences</Sidebar.Item>
|
|
531
|
+
<Sidebar.Item icon={<HelpIcon />}>Help</Sidebar.Item>
|
|
532
|
+
</Sidebar.Section>
|
|
533
|
+
</Sidebar.Nav>
|
|
534
|
+
<Sidebar.Footer>
|
|
535
|
+
<Sidebar.CollapseToggle />
|
|
536
|
+
</Sidebar.Footer>
|
|
537
|
+
</Sidebar>
|
|
538
|
+
<main style={mainContentStyle}>
|
|
539
|
+
Dashboard content goes here
|
|
540
|
+
</main>
|
|
541
|
+
</div>
|
|
542
|
+
),
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
name: 'Collapsed',
|
|
546
|
+
description: 'Icon-only collapsed state. Header shows only logo, tooltips appear on hover.',
|
|
547
|
+
code: `function App() {
|
|
548
|
+
const [collapsed, setCollapsed] = useState(true);
|
|
549
|
+
|
|
550
|
+
return (
|
|
551
|
+
<Sidebar collapsed={collapsed} onCollapsedChange={setCollapsed}>
|
|
552
|
+
<Sidebar.Header collapsedContent={<Logo />}>
|
|
553
|
+
<Logo />
|
|
554
|
+
<span>Acme App</span>
|
|
555
|
+
</Sidebar.Header>
|
|
556
|
+
<Sidebar.Nav>
|
|
557
|
+
<Sidebar.Section>
|
|
558
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
559
|
+
<Sidebar.Item icon={<ChartIcon />}>Analytics</Sidebar.Item>
|
|
560
|
+
</Sidebar.Section>
|
|
561
|
+
</Sidebar.Nav>
|
|
562
|
+
<Sidebar.Footer>
|
|
563
|
+
<Sidebar.CollapseToggle />
|
|
564
|
+
</Sidebar.Footer>
|
|
565
|
+
</Sidebar>
|
|
566
|
+
);
|
|
567
|
+
}`,
|
|
568
|
+
render: () => <CollapsedDemo />,
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
name: 'With Badges',
|
|
572
|
+
description: 'Navigation items with notification badges for counts or alerts.',
|
|
573
|
+
code: `<Sidebar>
|
|
574
|
+
<Sidebar.Nav>
|
|
575
|
+
<Sidebar.Section>
|
|
576
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
577
|
+
<Sidebar.Item icon={<ChartIcon />} badge="3">Analytics</Sidebar.Item>
|
|
578
|
+
<Sidebar.Item icon={<UsersIcon />} badge="12">Team</Sidebar.Item>
|
|
579
|
+
<Sidebar.Item icon={<FolderIcon />}>Projects</Sidebar.Item>
|
|
580
|
+
</Sidebar.Section>
|
|
581
|
+
</Sidebar.Nav>
|
|
582
|
+
</Sidebar>`,
|
|
583
|
+
render: () => (
|
|
584
|
+
<div style={demoContainerStyle}>
|
|
585
|
+
<Sidebar>
|
|
586
|
+
<Sidebar.Header collapsedContent={<LogoIcon size={32} />}>
|
|
587
|
+
<LogoIcon size={32} />
|
|
588
|
+
<span style={{ fontWeight: 600, fontSize: '16px' }}>Acme App</span>
|
|
589
|
+
</Sidebar.Header>
|
|
590
|
+
<Sidebar.Nav>
|
|
591
|
+
<Sidebar.Section>
|
|
592
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
593
|
+
<Sidebar.Item icon={<ChartIcon />} badge="3">Analytics</Sidebar.Item>
|
|
594
|
+
<Sidebar.Item icon={<UsersIcon />} badge="12">Team</Sidebar.Item>
|
|
595
|
+
<Sidebar.Item icon={<FolderIcon />}>Projects</Sidebar.Item>
|
|
596
|
+
</Sidebar.Section>
|
|
597
|
+
</Sidebar.Nav>
|
|
598
|
+
<Sidebar.Footer>
|
|
599
|
+
<Sidebar.CollapseToggle />
|
|
600
|
+
</Sidebar.Footer>
|
|
601
|
+
</Sidebar>
|
|
602
|
+
<main style={mainContentStyle}>
|
|
603
|
+
Badges indicate unread items or notifications
|
|
604
|
+
</main>
|
|
605
|
+
</div>
|
|
606
|
+
),
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: 'With Submenu',
|
|
610
|
+
description: 'Nested navigation with expandable sections. Use defaultExpanded for initial state without manual state tracking.',
|
|
611
|
+
code: `<Sidebar>
|
|
612
|
+
<Sidebar.Nav>
|
|
613
|
+
<Sidebar.Section>
|
|
614
|
+
<Sidebar.Item icon={<HomeIcon />}>Dashboard</Sidebar.Item>
|
|
615
|
+
{/* Use defaultExpanded for uncontrolled mode - no state needed! */}
|
|
616
|
+
<Sidebar.Item icon={<FolderIcon />} hasSubmenu defaultExpanded>
|
|
617
|
+
Projects
|
|
618
|
+
</Sidebar.Item>
|
|
619
|
+
<Sidebar.Submenu>
|
|
620
|
+
<Sidebar.SubItem active>Website Redesign</Sidebar.SubItem>
|
|
621
|
+
<Sidebar.SubItem>Mobile App</Sidebar.SubItem>
|
|
622
|
+
<Sidebar.SubItem>API Integration</Sidebar.SubItem>
|
|
623
|
+
</Sidebar.Submenu>
|
|
624
|
+
{/* Multiple submenus work without tracking separate state */}
|
|
625
|
+
<Sidebar.Item icon={<UsersIcon />} hasSubmenu>
|
|
626
|
+
Team
|
|
627
|
+
</Sidebar.Item>
|
|
628
|
+
<Sidebar.Submenu>
|
|
629
|
+
<Sidebar.SubItem>Engineering</Sidebar.SubItem>
|
|
630
|
+
<Sidebar.SubItem>Design</Sidebar.SubItem>
|
|
631
|
+
</Sidebar.Submenu>
|
|
632
|
+
</Sidebar.Section>
|
|
633
|
+
</Sidebar.Nav>
|
|
634
|
+
</Sidebar>`,
|
|
635
|
+
render: () => <SubmenuDemo />,
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
name: 'With Disabled Items',
|
|
639
|
+
description: 'Some navigation items are disabled for permissions or feature flags.',
|
|
640
|
+
code: `<Sidebar>
|
|
641
|
+
<Sidebar.Nav>
|
|
642
|
+
<Sidebar.Section>
|
|
643
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
644
|
+
<Sidebar.Item icon={<ChartIcon />}>Analytics</Sidebar.Item>
|
|
645
|
+
<Sidebar.Item icon={<UsersIcon />} disabled>
|
|
646
|
+
Team (Coming Soon)
|
|
647
|
+
</Sidebar.Item>
|
|
648
|
+
<Sidebar.Item icon={<FolderIcon />} disabled>
|
|
649
|
+
Projects (Upgrade)
|
|
650
|
+
</Sidebar.Item>
|
|
651
|
+
</Sidebar.Section>
|
|
652
|
+
</Sidebar.Nav>
|
|
653
|
+
</Sidebar>`,
|
|
654
|
+
render: () => (
|
|
655
|
+
<div style={demoContainerStyle}>
|
|
656
|
+
<Sidebar>
|
|
657
|
+
<Sidebar.Header collapsedContent={<LogoIcon size={32} />}>
|
|
658
|
+
<LogoIcon size={32} />
|
|
659
|
+
<span style={{ fontWeight: 600, fontSize: '16px' }}>Acme App</span>
|
|
660
|
+
</Sidebar.Header>
|
|
661
|
+
<Sidebar.Nav>
|
|
662
|
+
<Sidebar.Section>
|
|
663
|
+
<Sidebar.Item icon={<HomeIcon />} active>Dashboard</Sidebar.Item>
|
|
664
|
+
<Sidebar.Item icon={<ChartIcon />}>Analytics</Sidebar.Item>
|
|
665
|
+
<Sidebar.Item icon={<UsersIcon />} disabled>Team (Coming Soon)</Sidebar.Item>
|
|
666
|
+
<Sidebar.Item icon={<FolderIcon />} disabled>Projects (Upgrade)</Sidebar.Item>
|
|
667
|
+
</Sidebar.Section>
|
|
668
|
+
</Sidebar.Nav>
|
|
669
|
+
<Sidebar.Footer>
|
|
670
|
+
<Sidebar.CollapseToggle />
|
|
671
|
+
</Sidebar.Footer>
|
|
672
|
+
</Sidebar>
|
|
673
|
+
<main style={mainContentStyle}>
|
|
674
|
+
Disabled items cannot be clicked
|
|
675
|
+
</main>
|
|
676
|
+
</div>
|
|
677
|
+
),
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
name: 'With Provider & External Trigger',
|
|
681
|
+
description: 'SidebarProvider enables external triggers and keyboard shortcuts (Cmd/Ctrl+B).',
|
|
682
|
+
code: `function App() {
|
|
683
|
+
return (
|
|
684
|
+
<SidebarProvider defaultCollapsed={false}>
|
|
685
|
+
<Sidebar>
|
|
686
|
+
{/* sidebar content */}
|
|
687
|
+
</Sidebar>
|
|
688
|
+
<MainContent />
|
|
689
|
+
</SidebarProvider>
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function MainContent() {
|
|
694
|
+
const { toggleSidebar, state } = useSidebar();
|
|
695
|
+
return (
|
|
696
|
+
<main>
|
|
697
|
+
<p>State: {state}</p>
|
|
698
|
+
<button onClick={toggleSidebar}>Toggle</button>
|
|
699
|
+
</main>
|
|
700
|
+
);
|
|
701
|
+
}`,
|
|
702
|
+
render: () => <ProviderDemo />,
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
name: 'With asChild (Polymorphic)',
|
|
706
|
+
description: 'Use asChild to render items as custom elements like Next.js Link or React Router NavLink.',
|
|
707
|
+
code: `import { Link } from 'next/link';
|
|
708
|
+
|
|
709
|
+
<Sidebar>
|
|
710
|
+
<Sidebar.Nav>
|
|
711
|
+
<Sidebar.Section>
|
|
712
|
+
<Sidebar.Item icon={<HomeIcon />} active asChild>
|
|
713
|
+
<Link href="/dashboard">Dashboard</Link>
|
|
714
|
+
</Sidebar.Item>
|
|
715
|
+
<Sidebar.Item icon={<ChartIcon />} asChild>
|
|
716
|
+
<Link href="/analytics">Analytics</Link>
|
|
717
|
+
</Sidebar.Item>
|
|
718
|
+
</Sidebar.Section>
|
|
719
|
+
</Sidebar.Nav>
|
|
720
|
+
</Sidebar>`,
|
|
721
|
+
render: () => <AsChildDemo />,
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
name: 'With Section Action',
|
|
725
|
+
description: 'Section headers can include action buttons for quick actions like "Add Project".',
|
|
726
|
+
code: `<Sidebar>
|
|
727
|
+
<Sidebar.Nav>
|
|
728
|
+
<Sidebar.Section
|
|
729
|
+
label="Projects"
|
|
730
|
+
action={
|
|
731
|
+
<Sidebar.SectionAction aria-label="Add project" onClick={handleAdd}>
|
|
732
|
+
<PlusIcon />
|
|
733
|
+
</Sidebar.SectionAction>
|
|
734
|
+
}
|
|
735
|
+
>
|
|
736
|
+
<Sidebar.Item icon={<FolderIcon />}>Website Redesign</Sidebar.Item>
|
|
737
|
+
<Sidebar.Item icon={<FolderIcon />}>Mobile App</Sidebar.Item>
|
|
738
|
+
</Sidebar.Section>
|
|
739
|
+
</Sidebar.Nav>
|
|
740
|
+
</Sidebar>`,
|
|
741
|
+
render: () => <SectionActionDemo />,
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
name: 'With Loading Skeleton',
|
|
745
|
+
description: 'Show skeleton placeholders while navigation data is loading.',
|
|
746
|
+
code: `<Sidebar>
|
|
747
|
+
<Sidebar.Nav>
|
|
748
|
+
{loading ? (
|
|
749
|
+
<Sidebar.MenuSkeleton count={6} />
|
|
750
|
+
) : (
|
|
751
|
+
<Sidebar.Section>
|
|
752
|
+
<Sidebar.Item icon={<HomeIcon />}>Dashboard</Sidebar.Item>
|
|
753
|
+
{/* ... */}
|
|
754
|
+
</Sidebar.Section>
|
|
755
|
+
)}
|
|
756
|
+
</Sidebar.Nav>
|
|
757
|
+
</Sidebar>`,
|
|
758
|
+
render: () => <SkeletonDemo />,
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
name: 'With Rail Toggle',
|
|
762
|
+
description: 'Add a Rail component for a subtle drag-handle style toggle at the sidebar edge. Hover to reveal, click to toggle.',
|
|
763
|
+
code: `<Sidebar collapsed={collapsed} onCollapsedChange={setCollapsed}>
|
|
764
|
+
<Sidebar.Header>{/* ... */}</Sidebar.Header>
|
|
765
|
+
<Sidebar.Nav>{/* ... */}</Sidebar.Nav>
|
|
766
|
+
<Sidebar.Footer>
|
|
767
|
+
<Sidebar.CollapseToggle />
|
|
768
|
+
</Sidebar.Footer>
|
|
769
|
+
<Sidebar.Rail />
|
|
770
|
+
</Sidebar>`,
|
|
771
|
+
render: () => <RailDemo />,
|
|
772
|
+
},
|
|
773
|
+
],
|
|
774
|
+
});
|