@wakastellar/ui 2.3.4 → 2.4.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.
Files changed (194) hide show
  1. package/dist/blocks/dashboard/index.d.ts +4 -1
  2. package/dist/blocks/empty-states/index.d.ts +4 -1
  3. package/dist/blocks/error-pages/index.d.ts +4 -1
  4. package/dist/blocks/index.d.ts +1 -1
  5. package/dist/blocks/landing/index.d.ts +4 -1
  6. package/dist/blocks/pricing/index.d.ts +5 -1
  7. package/dist/blocks/sidebar/index.d.ts +5 -1
  8. package/dist/index.cjs.js +130 -130
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.es.js +7905 -7856
  11. package/dist/stories/Button.d.ts +14 -0
  12. package/dist/stories/Button.stories.d.ts +8 -0
  13. package/dist/stories/Header.d.ts +11 -0
  14. package/dist/stories/Header.stories.d.ts +6 -0
  15. package/dist/stories/Page.d.ts +2 -0
  16. package/dist/stories/Page.stories.d.ts +6 -0
  17. package/dist/types/index.d.ts +1 -0
  18. package/dist/types/link.d.ts +23 -0
  19. package/package.json +11 -3
  20. package/src/blocks/activity-timeline/ActivityTimeline.stories.tsx +460 -0
  21. package/src/blocks/apm-overview/APMOverview.stories.tsx +435 -0
  22. package/src/blocks/auth-2fa/Auth2FA.stories.tsx +308 -0
  23. package/src/blocks/calendar-view/CalendarView.stories.tsx +398 -0
  24. package/src/blocks/chat/Chat.stories.tsx +466 -0
  25. package/src/blocks/chat-interface/ChatInterface.stories.tsx +412 -0
  26. package/src/blocks/checkout-flow/CheckoutFlow.stories.tsx +408 -0
  27. package/src/blocks/cicd-builder/CICDBuilder.stories.tsx +499 -0
  28. package/src/blocks/cloud-cost-dashboard/CloudCostDashboard.stories.tsx +356 -0
  29. package/src/blocks/container-orchestrator/ContainerOrchestrator.stories.tsx +461 -0
  30. package/src/blocks/dashboard/Dashboard.stories.tsx +559 -0
  31. package/src/blocks/dashboard/index.tsx +68 -27
  32. package/src/blocks/dashboard-kpi/DashboardKPI.stories.tsx +422 -0
  33. package/src/blocks/database-admin/DatabaseAdmin.stories.tsx +393 -0
  34. package/src/blocks/deployment-dashboard/DeploymentDashboard.stories.tsx +457 -0
  35. package/src/blocks/empty-states/EmptyStates.stories.tsx +467 -0
  36. package/src/blocks/empty-states/index.tsx +26 -8
  37. package/src/blocks/error-pages/ErrorPages.stories.tsx +401 -0
  38. package/src/blocks/error-pages/index.tsx +26 -8
  39. package/src/blocks/faq/FAQ.stories.tsx +416 -0
  40. package/src/blocks/file-manager/FileManager.stories.tsx +413 -0
  41. package/src/blocks/footer/Footer.stories.tsx +328 -0
  42. package/src/blocks/gitops-sync-status/GitOpsSyncStatus.stories.tsx +529 -0
  43. package/src/blocks/header/WakaHeader.stories.tsx +455 -0
  44. package/src/blocks/headtab/Headtab.stories.tsx +369 -0
  45. package/src/blocks/i18n-editor/I18nEditor.stories.tsx +451 -0
  46. package/src/blocks/incident-manager/IncidentManager.stories.tsx +464 -0
  47. package/src/blocks/index.ts +1 -1
  48. package/src/blocks/infrastructure-map/InfrastructureMap.stories.tsx +533 -0
  49. package/src/blocks/kanban-board/KanbanBoard.stories.tsx +494 -0
  50. package/src/blocks/landing/WakaLanding.stories.tsx +449 -0
  51. package/src/blocks/landing/index.tsx +125 -66
  52. package/src/blocks/language-selector/LanguageSelector.stories.tsx +345 -0
  53. package/src/blocks/layout/Layout.stories.tsx +373 -0
  54. package/src/blocks/login/Login.stories.tsx +443 -0
  55. package/src/blocks/on-call-schedule/OnCallSchedule.stories.tsx +381 -0
  56. package/src/blocks/player-profile/PlayerProfile.stories.tsx +316 -0
  57. package/src/blocks/pricing/WakaPricing.stories.tsx +530 -0
  58. package/src/blocks/pricing/index.tsx +38 -4
  59. package/src/blocks/profile/Profile.stories.tsx +371 -0
  60. package/src/blocks/release-notes/ReleaseNotes.stories.tsx +431 -0
  61. package/src/blocks/settings/Settings.stories.tsx +520 -0
  62. package/src/blocks/sidebar/WakaSidebar.stories.tsx +513 -0
  63. package/src/blocks/sidebar/index.tsx +49 -20
  64. package/src/blocks/theme-creator-block/ThemeCreatorBlock.stories.tsx +370 -0
  65. package/src/blocks/user-management/UserManagement.stories.tsx +411 -0
  66. package/src/blocks/wizard/Wizard.stories.tsx +683 -0
  67. package/src/components/accordion/Accordion.stories.tsx +93 -0
  68. package/src/components/alert/Alert.stories.tsx +95 -0
  69. package/src/components/alert-dialog/AlertDialog.stories.tsx +126 -0
  70. package/src/components/aspect-ratio/AspectRatio.stories.tsx +118 -0
  71. package/src/components/avatar/Avatar.stories.tsx +104 -0
  72. package/src/components/button/Button.stories.tsx +12 -1
  73. package/src/components/calendar/Calendar.stories.tsx +102 -0
  74. package/src/components/card/Card.stories.tsx +125 -0
  75. package/src/components/checkbox/Checkbox.stories.tsx +100 -0
  76. package/src/components/code/Code.stories.tsx +402 -0
  77. package/src/components/collapsible/Collapsible.stories.tsx +123 -0
  78. package/src/components/command/Command.stories.tsx +207 -0
  79. package/src/components/context-menu/ContextMenu.stories.tsx +220 -0
  80. package/src/components/dialog/Dialog.stories.tsx +157 -0
  81. package/src/components/dropdown-menu/DropdownMenu.stories.tsx +225 -0
  82. package/src/components/form/Form.stories.tsx +413 -0
  83. package/src/components/hover-card/HoverCard.stories.tsx +148 -0
  84. package/src/components/input-otp/InputOTP.stories.tsx +255 -0
  85. package/src/components/label/Label.stories.tsx +68 -0
  86. package/src/components/menubar/Menubar.stories.tsx +278 -0
  87. package/src/components/navigation-menu/NavigationMenu.stories.tsx +202 -0
  88. package/src/components/popover/Popover.stories.tsx +199 -0
  89. package/src/components/progress/Progress.stories.tsx +104 -0
  90. package/src/components/radio-group/RadioGroup.stories.tsx +138 -0
  91. package/src/components/scroll-area/ScrollArea.stories.tsx +153 -0
  92. package/src/components/select/Select.stories.tsx +146 -0
  93. package/src/components/separator/Separator.stories.tsx +117 -0
  94. package/src/components/sheet/Sheet.stories.tsx +195 -0
  95. package/src/components/skeleton/Skeleton.stories.tsx +114 -0
  96. package/src/components/slider/Slider.stories.tsx +157 -0
  97. package/src/components/switch/Switch.stories.tsx +114 -0
  98. package/src/components/table/Table.stories.tsx +153 -0
  99. package/src/components/tabs/Tabs.stories.tsx +155 -0
  100. package/src/components/textarea/Textarea.stories.tsx +116 -0
  101. package/src/components/toast/Toast.stories.tsx +160 -0
  102. package/src/components/toggle/Toggle.stories.tsx +125 -0
  103. package/src/components/tooltip/Tooltip.stories.tsx +133 -0
  104. package/src/components/typography/Typography.stories.tsx +207 -0
  105. package/src/components/waka-3d-pie-chart/Waka3DPieChart.stories.tsx +308 -0
  106. package/src/components/waka-achievement-unlock/WakaAchievementUnlock.stories.tsx +353 -0
  107. package/src/components/waka-artifact-list/WakaArtifactList.stories.tsx +258 -0
  108. package/src/components/waka-autocomplete/WakaAutocomplete.stories.tsx +224 -0
  109. package/src/components/waka-badge-showcase/WakaBadgeShowcase.stories.tsx +269 -0
  110. package/src/components/waka-barcode/WakaBarcode.stories.tsx +227 -0
  111. package/src/components/waka-bottom-sheet/WakaBottomSheet.stories.tsx +408 -0
  112. package/src/components/waka-breadcrumb/WakaBreadcrumb.stories.tsx +237 -0
  113. package/src/components/waka-build-matrix/WakaBuildMatrix.stories.tsx +328 -0
  114. package/src/components/waka-carousel/WakaCarousel.stories.tsx +296 -0
  115. package/src/components/waka-charts/WakaCharts.stories.tsx +519 -0
  116. package/src/components/waka-color-picker/WakaColorPicker.stories.tsx +200 -0
  117. package/src/components/waka-combobox/WakaCombobox.stories.tsx +250 -0
  118. package/src/components/waka-container-list/WakaContainerList.stories.tsx +315 -0
  119. package/src/components/waka-contribution-graph/WakaContributionGraph.stories.tsx +354 -0
  120. package/src/components/waka-cost-breakdown/WakaCostBreakdown.stories.tsx +348 -0
  121. package/src/components/waka-daily-reward/WakaDailyReward.stories.tsx +365 -0
  122. package/src/components/waka-database-card/WakaDatabaseCard.stories.tsx +306 -0
  123. package/src/components/waka-date-range-picker/WakaDateRangePicker.stories.tsx +339 -0
  124. package/src/components/waka-datetime-picker/WakaDateTimePicker.stories.tsx +317 -0
  125. package/src/components/waka-deployment-lane/WakaDeploymentLane.stories.tsx +292 -0
  126. package/src/components/waka-dock/WakaDock.stories.tsx +332 -0
  127. package/src/components/waka-drawer/WakaDrawer.stories.tsx +437 -0
  128. package/src/components/waka-env-var-editor/WakaEnvVarEditor.stories.tsx +263 -0
  129. package/src/components/waka-error-shake/WakaErrorShake.stories.tsx +410 -0
  130. package/src/components/waka-file-upload/WakaFileUpload.stories.tsx +239 -0
  131. package/src/components/waka-flow-diagram/WakaFlowDiagram.stories.tsx +365 -0
  132. package/src/components/waka-funnel-chart/WakaFunnelChart.stories.tsx +281 -0
  133. package/src/components/waka-glow-card/WakaGlowCard.stories.tsx +274 -0
  134. package/src/components/waka-haptic-button/WakaHapticButton.stories.tsx +349 -0
  135. package/src/components/waka-health-pulse/WakaHealthPulse.stories.tsx +293 -0
  136. package/src/components/waka-heatmap/WakaHeatmap.stories.tsx +376 -0
  137. package/src/components/waka-image/WakaImage.stories.tsx +255 -0
  138. package/src/components/waka-incident-timeline/WakaIncidentTimeline.stories.tsx +300 -0
  139. package/src/components/waka-kanban/WakaKanban.stories.tsx +399 -0
  140. package/src/components/waka-kubernetes-overview/WakaKubernetesOverview.stories.tsx +281 -0
  141. package/src/components/waka-leaderboard/WakaLeaderboard.stories.tsx +300 -0
  142. package/src/components/waka-level-progress/WakaLevelProgress.stories.tsx +313 -0
  143. package/src/components/waka-loading-orbit/WakaLoadingOrbit.stories.tsx +413 -0
  144. package/src/components/waka-log-viewer/WakaLogViewer.stories.tsx +312 -0
  145. package/src/components/waka-loot-box/WakaLootBox.stories.tsx +374 -0
  146. package/src/components/waka-metric-sparkline/WakaMetricSparkline.stories.tsx +312 -0
  147. package/src/components/waka-migration-list/WakaMigrationList.stories.tsx +289 -0
  148. package/src/components/waka-modal/WakaModal.stories.tsx +434 -0
  149. package/src/components/waka-morph-button/WakaMorphButton.stories.tsx +405 -0
  150. package/src/components/waka-network-topology/WakaNetworkTopology.stories.tsx +364 -0
  151. package/src/components/waka-notifications/WakaNotifications.stories.tsx +290 -0
  152. package/src/components/waka-number-input/WakaNumberInput.stories.tsx +282 -0
  153. package/src/components/waka-pagination/WakaPagination.stories.tsx +328 -0
  154. package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +318 -0
  155. package/src/components/waka-pipeline-view/WakaPipelineView.stories.tsx +386 -0
  156. package/src/components/waka-player-card/WakaPlayerCard.stories.tsx +333 -0
  157. package/src/components/waka-pod-card/WakaPodCard.stories.tsx +435 -0
  158. package/src/components/waka-qrcode/WakaQRCode.stories.tsx +232 -0
  159. package/src/components/waka-query-explain/WakaQueryExplain.stories.tsx +407 -0
  160. package/src/components/waka-quest-card/WakaQuestCard.stories.tsx +394 -0
  161. package/src/components/waka-quota-bar/WakaQuotaBar.stories.tsx +435 -0
  162. package/src/components/waka-radar-score/WakaRadarScore.stories.tsx +372 -0
  163. package/src/components/waka-resource-gauge/WakaResourceGauge.stories.tsx +366 -0
  164. package/src/components/waka-rich-text-editor/WakaRichTextEditor.stories.tsx +238 -0
  165. package/src/components/waka-sankey-diagram/WakaSankeyDiagram.stories.tsx +389 -0
  166. package/src/components/waka-scratch-card/WakaScratchCard.stories.tsx +388 -0
  167. package/src/components/waka-secret-card/WakaSecretCard.stories.tsx +314 -0
  168. package/src/components/waka-segmented-control/WakaSegmentedControl.stories.tsx +309 -0
  169. package/src/components/waka-server-rack/WakaServerRack.stories.tsx +382 -0
  170. package/src/components/waka-service-graph/WakaServiceGraph.stories.tsx +262 -0
  171. package/src/components/waka-skeleton-wave/WakaSkeletonWave.stories.tsx +321 -0
  172. package/src/components/waka-skill-tree/WakaSkillTree.stories.tsx +308 -0
  173. package/src/components/waka-spin-wheel/WakaSpinWheel.stories.tsx +368 -0
  174. package/src/components/waka-spinner/WakaSpinner.stories.tsx +156 -0
  175. package/src/components/waka-stat/WakaStat.stories.tsx +334 -0
  176. package/src/components/waka-status-matrix/WakaStatusMatrix.stories.tsx +331 -0
  177. package/src/components/waka-stepper/WakaStepper.stories.tsx +468 -0
  178. package/src/components/waka-streak-counter/WakaStreakCounter.stories.tsx +235 -0
  179. package/src/components/waka-success-explosion/WakaSuccessExplosion.stories.tsx +389 -0
  180. package/src/components/waka-tabs-morph/WakaTabsMorph.stories.tsx +471 -0
  181. package/src/components/waka-terminal-output/WakaTerminalOutput.stories.tsx +351 -0
  182. package/src/components/waka-test-report/WakaTestReport.stories.tsx +322 -0
  183. package/src/components/waka-tilt-card/WakaTiltCard.stories.tsx +300 -0
  184. package/src/components/waka-time-picker/WakaTimePicker.stories.tsx +227 -0
  185. package/src/components/waka-timeline/WakaTimeline.stories.tsx +383 -0
  186. package/src/components/waka-tournament-bracket/WakaTournamentBracket.stories.tsx +375 -0
  187. package/src/components/waka-trace-viewer/WakaTraceViewer.stories.tsx +445 -0
  188. package/src/components/waka-tree/WakaTree.stories.tsx +359 -0
  189. package/src/components/waka-treemap-chart/WakaTreemapChart.stories.tsx +378 -0
  190. package/src/components/waka-typewriter/WakaTypewriter.stories.tsx +366 -0
  191. package/src/components/waka-versus-card/WakaVersusCard.stories.tsx +530 -0
  192. package/src/components/waka-video/WakaVideo.stories.tsx +203 -0
  193. package/src/components/waka-virtual-list/WakaVirtualList.stories.tsx +273 -0
  194. package/src/components/waka-xp-bar/WakaXPBar.stories.tsx +305 -0
@@ -0,0 +1,513 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaSidebar, SidebarLayout, useSidebar } from './index'
3
+ import type { SidebarMenuItem, SidebarUserConfig, SidebarLogoConfig } from './index'
4
+ import * as React from 'react'
5
+ import {
6
+ Home,
7
+ Users,
8
+ Settings,
9
+ FileText,
10
+ BarChart3,
11
+ Mail,
12
+ Calendar,
13
+ HelpCircle,
14
+ Shield,
15
+ Database,
16
+ Bell,
17
+ Layers,
18
+ Package,
19
+ CreditCard,
20
+ Zap,
21
+ } from 'lucide-react'
22
+
23
+ const defaultMenu: SidebarMenuItem[] = [
24
+ {
25
+ id: 'dashboard',
26
+ label: 'Tableau de bord',
27
+ icon: <Home className="h-5 w-5" />,
28
+ href: '/dashboard',
29
+ active: true,
30
+ },
31
+ {
32
+ id: 'analytics',
33
+ label: 'Analytics',
34
+ icon: <BarChart3 className="h-5 w-5" />,
35
+ href: '/analytics',
36
+ badge: 'Nouveau',
37
+ },
38
+ {
39
+ id: 'users',
40
+ label: 'Utilisateurs',
41
+ icon: <Users className="h-5 w-5" />,
42
+ children: [
43
+ { id: 'users-list', label: 'Liste', href: '/users' },
44
+ { id: 'users-roles', label: 'Rôles', href: '/users/roles' },
45
+ { id: 'users-permissions', label: 'Permissions', href: '/users/permissions' },
46
+ ],
47
+ },
48
+ {
49
+ id: 'documents',
50
+ label: 'Documents',
51
+ icon: <FileText className="h-5 w-5" />,
52
+ href: '/documents',
53
+ badge: 12,
54
+ },
55
+ {
56
+ id: 'calendar',
57
+ label: 'Calendrier',
58
+ icon: <Calendar className="h-5 w-5" />,
59
+ href: '/calendar',
60
+ },
61
+ {
62
+ id: 'messages',
63
+ label: 'Messages',
64
+ icon: <Mail className="h-5 w-5" />,
65
+ href: '/messages',
66
+ badge: 5,
67
+ },
68
+ {
69
+ id: 'settings',
70
+ label: 'Paramètres',
71
+ icon: <Settings className="h-5 w-5" />,
72
+ children: [
73
+ { id: 'settings-general', label: 'Général', href: '/settings/general' },
74
+ { id: 'settings-security', label: 'Sécurité', href: '/settings/security' },
75
+ { id: 'settings-notifications', label: 'Notifications', href: '/settings/notifications' },
76
+ ],
77
+ },
78
+ {
79
+ id: 'help',
80
+ label: 'Aide',
81
+ icon: <HelpCircle className="h-5 w-5" />,
82
+ href: '/help',
83
+ },
84
+ ]
85
+
86
+ const defaultUser: SidebarUserConfig = {
87
+ name: 'Jean Dupont',
88
+ email: 'jean.dupont@example.com',
89
+ avatarUrl: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Jean',
90
+ initials: 'JD',
91
+ }
92
+
93
+ const defaultLogo: SidebarLogoConfig = {
94
+ title: 'WAKASTART',
95
+ href: '/',
96
+ }
97
+
98
+ const MockLink = ({ href, children, ...props }: any) => (
99
+ <a href={href} {...props} onClick={(e) => e.preventDefault()}>
100
+ {children}
101
+ </a>
102
+ )
103
+
104
+ const meta: Meta<typeof WakaSidebar> = {
105
+ title: 'Blocks/Sidebar',
106
+ component: WakaSidebar,
107
+ parameters: {
108
+ layout: 'fullscreen',
109
+ docs: {
110
+ description: {
111
+ component:
112
+ 'A responsive sidebar with collapsible menu, user dropdown, custom logo, and mobile sheet support.',
113
+ },
114
+ },
115
+ },
116
+ tags: ['autodocs'],
117
+ argTypes: {
118
+ userPosition: {
119
+ control: 'select',
120
+ options: ['top', 'bottom'],
121
+ },
122
+ position: {
123
+ control: 'select',
124
+ options: ['fixed', 'relative'],
125
+ },
126
+ width: {
127
+ control: { type: 'range', min: 200, max: 400, step: 10 },
128
+ },
129
+ },
130
+ }
131
+
132
+ export default meta
133
+ type Story = StoryObj<typeof WakaSidebar>
134
+
135
+ export const Default: Story = {
136
+ render: () => (
137
+ <div className="h-[600px] bg-muted/30">
138
+ <WakaSidebar
139
+ logo={defaultLogo}
140
+ menu={defaultMenu}
141
+ user={defaultUser}
142
+ position="relative"
143
+ LinkComponent={MockLink}
144
+ />
145
+ </div>
146
+ ),
147
+ }
148
+
149
+ export const WithLogoImage: Story = {
150
+ render: () => (
151
+ <div className="h-[600px] bg-muted/30">
152
+ <WakaSidebar
153
+ logo={{
154
+ src: 'https://placehold.co/120x40/3b82f6/white?text=LOGO',
155
+ alt: 'Logo',
156
+ title: 'Company',
157
+ href: '/',
158
+ }}
159
+ menu={defaultMenu}
160
+ user={defaultUser}
161
+ position="relative"
162
+ LinkComponent={MockLink}
163
+ />
164
+ </div>
165
+ ),
166
+ }
167
+
168
+ export const UserOnTop: Story = {
169
+ render: () => (
170
+ <div className="h-[600px] bg-muted/30">
171
+ <WakaSidebar
172
+ logo={defaultLogo}
173
+ menu={defaultMenu}
174
+ user={defaultUser}
175
+ userPosition="top"
176
+ position="relative"
177
+ LinkComponent={MockLink}
178
+ />
179
+ </div>
180
+ ),
181
+ }
182
+
183
+ export const NoUser: Story = {
184
+ render: () => (
185
+ <div className="h-[600px] bg-muted/30">
186
+ <WakaSidebar
187
+ logo={defaultLogo}
188
+ menu={defaultMenu}
189
+ position="relative"
190
+ LinkComponent={MockLink}
191
+ />
192
+ </div>
193
+ ),
194
+ }
195
+
196
+ export const LongMenu: Story = {
197
+ render: () => {
198
+ const extendedMenu: SidebarMenuItem[] = [
199
+ ...defaultMenu,
200
+ {
201
+ id: 'security',
202
+ label: 'Sécurité',
203
+ icon: <Shield className="h-5 w-5" />,
204
+ href: '/security',
205
+ },
206
+ {
207
+ id: 'database',
208
+ label: 'Base de données',
209
+ icon: <Database className="h-5 w-5" />,
210
+ children: [
211
+ { id: 'db-tables', label: 'Tables', href: '/database/tables' },
212
+ { id: 'db-backups', label: 'Sauvegardes', href: '/database/backups' },
213
+ { id: 'db-migrations', label: 'Migrations', href: '/database/migrations' },
214
+ ],
215
+ },
216
+ {
217
+ id: 'notifications',
218
+ label: 'Notifications',
219
+ icon: <Bell className="h-5 w-5" />,
220
+ href: '/notifications',
221
+ badge: 99,
222
+ },
223
+ {
224
+ id: 'integrations',
225
+ label: 'Intégrations',
226
+ icon: <Layers className="h-5 w-5" />,
227
+ href: '/integrations',
228
+ },
229
+ {
230
+ id: 'packages',
231
+ label: 'Packages',
232
+ icon: <Package className="h-5 w-5" />,
233
+ href: '/packages',
234
+ },
235
+ {
236
+ id: 'billing',
237
+ label: 'Facturation',
238
+ icon: <CreditCard className="h-5 w-5" />,
239
+ href: '/billing',
240
+ },
241
+ ]
242
+
243
+ return (
244
+ <div className="h-[600px] bg-muted/30">
245
+ <WakaSidebar
246
+ logo={defaultLogo}
247
+ menu={extendedMenu}
248
+ user={defaultUser}
249
+ position="relative"
250
+ LinkComponent={MockLink}
251
+ />
252
+ </div>
253
+ )
254
+ },
255
+ }
256
+
257
+ export const WithFooter: Story = {
258
+ render: () => (
259
+ <div className="h-[600px] bg-muted/30">
260
+ <WakaSidebar
261
+ logo={defaultLogo}
262
+ menu={defaultMenu}
263
+ user={defaultUser}
264
+ position="relative"
265
+ footer={
266
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
267
+ <Zap className="h-3 w-3" />
268
+ <span>Version 2.1.0</span>
269
+ </div>
270
+ }
271
+ LinkComponent={MockLink}
272
+ />
273
+ </div>
274
+ ),
275
+ }
276
+
277
+ export const WithHeader: Story = {
278
+ render: () => (
279
+ <div className="h-[600px] bg-muted/30">
280
+ <WakaSidebar
281
+ logo={defaultLogo}
282
+ menu={defaultMenu}
283
+ user={defaultUser}
284
+ position="relative"
285
+ header={
286
+ <div className="px-2 py-1 text-xs bg-primary/10 rounded text-primary">
287
+ Pro Plan
288
+ </div>
289
+ }
290
+ LinkComponent={MockLink}
291
+ />
292
+ </div>
293
+ ),
294
+ }
295
+
296
+ export const CustomWidth: Story = {
297
+ render: () => (
298
+ <div className="h-[600px] bg-muted/30 flex gap-4">
299
+ <WakaSidebar
300
+ logo={{ title: 'Narrow' }}
301
+ menu={defaultMenu.slice(0, 4)}
302
+ width={200}
303
+ position="relative"
304
+ LinkComponent={MockLink}
305
+ />
306
+ <WakaSidebar
307
+ logo={{ title: 'Wide Sidebar' }}
308
+ menu={defaultMenu.slice(0, 4)}
309
+ width={320}
310
+ position="relative"
311
+ LinkComponent={MockLink}
312
+ />
313
+ </div>
314
+ ),
315
+ }
316
+
317
+ export const DisabledItems: Story = {
318
+ render: () => {
319
+ const menuWithDisabled: SidebarMenuItem[] = [
320
+ ...defaultMenu.slice(0, 3),
321
+ {
322
+ id: 'premium',
323
+ label: 'Premium Features',
324
+ icon: <Zap className="h-5 w-5" />,
325
+ href: '/premium',
326
+ disabled: true,
327
+ badge: 'Pro',
328
+ },
329
+ ...defaultMenu.slice(3, 5),
330
+ ]
331
+
332
+ return (
333
+ <div className="h-[600px] bg-muted/30">
334
+ <WakaSidebar
335
+ logo={defaultLogo}
336
+ menu={menuWithDisabled}
337
+ user={defaultUser}
338
+ position="relative"
339
+ LinkComponent={MockLink}
340
+ />
341
+ </div>
342
+ )
343
+ },
344
+ }
345
+
346
+ export const SidebarLayoutExample: Story = {
347
+ render: () => (
348
+ <div className="h-[600px]">
349
+ <SidebarLayout
350
+ sidebar={{
351
+ logo: defaultLogo,
352
+ menu: defaultMenu,
353
+ user: defaultUser,
354
+ LinkComponent: MockLink,
355
+ }}
356
+ sidebarWidth={260}
357
+ >
358
+ <div className="p-6">
359
+ <h1 className="text-2xl font-bold mb-4">Main Content</h1>
360
+ <p className="text-muted-foreground">
361
+ This is the main content area. The sidebar is handled by the SidebarLayout component.
362
+ </p>
363
+ <div className="mt-6 grid grid-cols-3 gap-4">
364
+ {[1, 2, 3, 4, 5, 6].map((i) => (
365
+ <div key={i} className="bg-muted rounded-lg p-4 h-32 flex items-center justify-center">
366
+ Card {i}
367
+ </div>
368
+ ))}
369
+ </div>
370
+ </div>
371
+ </SidebarLayout>
372
+ </div>
373
+ ),
374
+ }
375
+
376
+ export const InteractiveMenu: Story = {
377
+ render: () => {
378
+ const [activeId, setActiveId] = React.useState('dashboard')
379
+
380
+ const menuWithActive: SidebarMenuItem[] = defaultMenu.map((item) => ({
381
+ ...item,
382
+ active: item.id === activeId,
383
+ onClick: item.children ? undefined : () => setActiveId(item.id),
384
+ children: item.children?.map((child) => ({
385
+ ...child,
386
+ active: child.id === activeId,
387
+ onClick: () => setActiveId(child.id),
388
+ })),
389
+ }))
390
+
391
+ return (
392
+ <div className="h-[600px] bg-muted/30">
393
+ <p className="absolute top-2 right-4 text-sm text-muted-foreground">
394
+ Active: {activeId}
395
+ </p>
396
+ <WakaSidebar
397
+ logo={defaultLogo}
398
+ menu={menuWithActive}
399
+ user={defaultUser}
400
+ position="relative"
401
+ LinkComponent={MockLink}
402
+ />
403
+ </div>
404
+ )
405
+ },
406
+ }
407
+
408
+ export const CustomUserActions: Story = {
409
+ render: () => (
410
+ <div className="h-[600px] bg-muted/30">
411
+ <WakaSidebar
412
+ logo={defaultLogo}
413
+ menu={defaultMenu}
414
+ user={{
415
+ ...defaultUser,
416
+ actions: [
417
+ {
418
+ id: 'profile',
419
+ label: 'Mon profil',
420
+ icon: <Users className="h-4 w-4" />,
421
+ onClick: () => console.log('Profile'),
422
+ },
423
+ {
424
+ id: 'billing',
425
+ label: 'Facturation',
426
+ icon: <CreditCard className="h-4 w-4" />,
427
+ onClick: () => console.log('Billing'),
428
+ },
429
+ {
430
+ id: 'settings',
431
+ label: 'Paramètres',
432
+ icon: <Settings className="h-4 w-4" />,
433
+ onClick: () => console.log('Settings'),
434
+ },
435
+ {
436
+ id: 'logout',
437
+ label: 'Déconnexion',
438
+ icon: <HelpCircle className="h-4 w-4" />,
439
+ variant: 'destructive',
440
+ onClick: () => console.log('Logout'),
441
+ },
442
+ ],
443
+ }}
444
+ position="relative"
445
+ LinkComponent={MockLink}
446
+ />
447
+ </div>
448
+ ),
449
+ }
450
+
451
+ export const ApplicationLayout: Story = {
452
+ render: () => (
453
+ <div className="h-screen flex">
454
+ <WakaSidebar
455
+ logo={{
456
+ title: 'MyApp',
457
+ component: (
458
+ <div className="flex items-center gap-2">
459
+ <div className="h-8 w-8 rounded-lg bg-primary flex items-center justify-center text-primary-foreground font-bold">
460
+ M
461
+ </div>
462
+ <span className="text-lg font-bold">MyApp</span>
463
+ </div>
464
+ ),
465
+ href: '/',
466
+ }}
467
+ menu={defaultMenu}
468
+ user={defaultUser}
469
+ position="relative"
470
+ width={260}
471
+ footer={
472
+ <div className="text-xs text-muted-foreground/70 text-center">
473
+ &copy; 2024 MyApp Inc.
474
+ </div>
475
+ }
476
+ LinkComponent={MockLink}
477
+ />
478
+ <main className="flex-1 bg-background p-6 overflow-auto">
479
+ <div className="max-w-4xl mx-auto">
480
+ <h1 className="text-3xl font-bold mb-2">Welcome back, Jean!</h1>
481
+ <p className="text-muted-foreground mb-8">
482
+ Here&apos;s what&apos;s happening with your projects today.
483
+ </p>
484
+
485
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4 mb-8">
486
+ {['Revenue', 'Users', 'Orders', 'Conversion'].map((label, i) => (
487
+ <div key={label} className="rounded-xl border bg-card p-4">
488
+ <p className="text-sm text-muted-foreground">{label}</p>
489
+ <p className="text-2xl font-bold">{(i + 1) * 1234}</p>
490
+ <p className="text-xs text-green-500">+{(i + 1) * 5}%</p>
491
+ </div>
492
+ ))}
493
+ </div>
494
+
495
+ <div className="rounded-xl border bg-card p-6">
496
+ <h2 className="text-xl font-semibold mb-4">Recent Activity</h2>
497
+ <div className="space-y-3">
498
+ {[1, 2, 3, 4, 5].map((i) => (
499
+ <div key={i} className="flex items-center gap-3 py-2 border-b last:border-0">
500
+ <div className="h-8 w-8 rounded-full bg-muted" />
501
+ <div className="flex-1">
502
+ <p className="text-sm font-medium">Activity item {i}</p>
503
+ <p className="text-xs text-muted-foreground">{i} hours ago</p>
504
+ </div>
505
+ </div>
506
+ ))}
507
+ </div>
508
+ </div>
509
+ </div>
510
+ </main>
511
+ </div>
512
+ ),
513
+ }
@@ -78,6 +78,10 @@ export interface SidebarLogoConfig {
78
78
  component?: React.ReactNode
79
79
  }
80
80
 
81
+ // Re-export du type partagé
82
+ export type { LinkComponentProps } from "../../types/link"
83
+ import type { LinkComponentProps } from "../../types/link"
84
+
81
85
  export interface WakaSidebarProps {
82
86
  /** Configuration du logo */
83
87
  logo?: SidebarLogoConfig
@@ -119,6 +123,8 @@ export interface WakaSidebarProps {
119
123
  header?: React.ReactNode
120
124
  /** Mode de positionnement : "fixed" pour app layout, "relative" pour preview/demo */
121
125
  position?: "fixed" | "relative"
126
+ /** Composant de lien personnalisé (ex: next/link) pour navigation SPA */
127
+ LinkComponent?: React.ComponentType<LinkComponentProps>
122
128
  }
123
129
 
124
130
  // ============ CONTEXT ============
@@ -131,6 +137,7 @@ interface SidebarContextValue {
131
137
  isMobile: boolean
132
138
  closeOnNavigate: boolean
133
139
  onClose: () => void
140
+ LinkComponent?: React.ComponentType<LinkComponentProps>
134
141
  }
135
142
 
136
143
  const SidebarContext = React.createContext<SidebarContextValue | null>(null)
@@ -149,6 +156,8 @@ function useSidebarContext() {
149
156
  * Logo de la sidebar
150
157
  */
151
158
  function SidebarLogo({ config }: { config: SidebarLogoConfig }) {
159
+ const { LinkComponent } = useSidebarContext()
160
+
152
161
  const content = config.component ? (
153
162
  config.component
154
163
  ) : (
@@ -167,6 +176,13 @@ function SidebarLogo({ config }: { config: SidebarLogoConfig }) {
167
176
  )
168
177
 
169
178
  if (config.href) {
179
+ if (LinkComponent) {
180
+ return (
181
+ <LinkComponent href={config.href} className="block" onClick={config.onClick}>
182
+ {content}
183
+ </LinkComponent>
184
+ )
185
+ }
170
186
  return (
171
187
  <a href={config.href} className="block" onClick={config.onClick}>
172
188
  {content}
@@ -197,7 +213,7 @@ function SidebarMenuItemComponent({
197
213
  isChild?: boolean
198
214
  renderItem?: (item: SidebarMenuItem, isChild: boolean) => React.ReactNode
199
215
  }) {
200
- const { activeId, setActiveId, expandedIds, toggleExpanded, isMobile, closeOnNavigate, onClose } =
216
+ const { activeId, setActiveId, expandedIds, toggleExpanded, isMobile, closeOnNavigate, onClose, LinkComponent } =
201
217
  useSidebarContext()
202
218
 
203
219
  const hasChildren = item.children && item.children.length > 0
@@ -284,21 +300,23 @@ function SidebarMenuItemComponent({
284
300
 
285
301
  // Lien ou bouton simple
286
302
  if (item.href) {
287
- return (
288
- <a
289
- href={item.href}
290
- onClick={(e) => {
291
- if (item.disabled) {
292
- e.preventDefault()
293
- return
294
- }
295
- handleClick()
296
- }}
297
- className="block"
298
- >
299
- {itemContent}
300
- </a>
301
- )
303
+ const linkProps = {
304
+ href: item.href,
305
+ className: "block",
306
+ onClick: (e: React.MouseEvent) => {
307
+ if (item.disabled) {
308
+ e.preventDefault()
309
+ return
310
+ }
311
+ handleClick()
312
+ },
313
+ }
314
+
315
+ if (LinkComponent) {
316
+ return <LinkComponent {...linkProps}>{itemContent}</LinkComponent>
317
+ }
318
+
319
+ return <a {...linkProps}>{itemContent}</a>
302
320
  }
303
321
 
304
322
  return (
@@ -317,6 +335,8 @@ function SidebarMenuItemComponent({
317
335
  * Menu utilisateur
318
336
  */
319
337
  function SidebarUser({ config }: { config: SidebarUserConfig }) {
338
+ const { LinkComponent } = useSidebarContext()
339
+
320
340
  const initials =
321
341
  config.initials ||
322
342
  config.name
@@ -366,10 +386,17 @@ function SidebarUser({ config }: { config: SidebarUserConfig }) {
366
386
  asChild={!!action.href}
367
387
  >
368
388
  {action.href ? (
369
- <a href={action.href} className="flex items-center gap-2">
370
- {action.icon}
371
- {action.label}
372
- </a>
389
+ LinkComponent ? (
390
+ <LinkComponent href={action.href} className="flex items-center gap-2">
391
+ {action.icon}
392
+ {action.label}
393
+ </LinkComponent>
394
+ ) : (
395
+ <a href={action.href} className="flex items-center gap-2">
396
+ {action.icon}
397
+ {action.label}
398
+ </a>
399
+ )
373
400
  ) : (
374
401
  <span className="flex items-center gap-2">
375
402
  {action.icon}
@@ -487,6 +514,7 @@ export function WakaSidebar({
487
514
  footer,
488
515
  header,
489
516
  position = "fixed",
517
+ LinkComponent,
490
518
  }: WakaSidebarProps) {
491
519
  // État interne pour mobile si non contrôlé
492
520
  const [internalOpen, setInternalOpen] = React.useState(false)
@@ -552,6 +580,7 @@ export function WakaSidebar({
552
580
  isMobile,
553
581
  closeOnNavigate: true,
554
582
  onClose: () => setIsOpen(false),
583
+ LinkComponent,
555
584
  }
556
585
 
557
586
  // Styles personnalisés - utilise les variables CSS du thème par défaut