@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.
- package/dist/blocks/dashboard/index.d.ts +4 -1
- package/dist/blocks/empty-states/index.d.ts +4 -1
- package/dist/blocks/error-pages/index.d.ts +4 -1
- package/dist/blocks/index.d.ts +1 -1
- package/dist/blocks/landing/index.d.ts +4 -1
- package/dist/blocks/pricing/index.d.ts +5 -1
- package/dist/blocks/sidebar/index.d.ts +5 -1
- package/dist/index.cjs.js +130 -130
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +7905 -7856
- package/dist/stories/Button.d.ts +14 -0
- package/dist/stories/Button.stories.d.ts +8 -0
- package/dist/stories/Header.d.ts +11 -0
- package/dist/stories/Header.stories.d.ts +6 -0
- package/dist/stories/Page.d.ts +2 -0
- package/dist/stories/Page.stories.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/link.d.ts +23 -0
- package/package.json +11 -3
- package/src/blocks/activity-timeline/ActivityTimeline.stories.tsx +460 -0
- package/src/blocks/apm-overview/APMOverview.stories.tsx +435 -0
- package/src/blocks/auth-2fa/Auth2FA.stories.tsx +308 -0
- package/src/blocks/calendar-view/CalendarView.stories.tsx +398 -0
- package/src/blocks/chat/Chat.stories.tsx +466 -0
- package/src/blocks/chat-interface/ChatInterface.stories.tsx +412 -0
- package/src/blocks/checkout-flow/CheckoutFlow.stories.tsx +408 -0
- package/src/blocks/cicd-builder/CICDBuilder.stories.tsx +499 -0
- package/src/blocks/cloud-cost-dashboard/CloudCostDashboard.stories.tsx +356 -0
- package/src/blocks/container-orchestrator/ContainerOrchestrator.stories.tsx +461 -0
- package/src/blocks/dashboard/Dashboard.stories.tsx +559 -0
- package/src/blocks/dashboard/index.tsx +68 -27
- package/src/blocks/dashboard-kpi/DashboardKPI.stories.tsx +422 -0
- package/src/blocks/database-admin/DatabaseAdmin.stories.tsx +393 -0
- package/src/blocks/deployment-dashboard/DeploymentDashboard.stories.tsx +457 -0
- package/src/blocks/empty-states/EmptyStates.stories.tsx +467 -0
- package/src/blocks/empty-states/index.tsx +26 -8
- package/src/blocks/error-pages/ErrorPages.stories.tsx +401 -0
- package/src/blocks/error-pages/index.tsx +26 -8
- package/src/blocks/faq/FAQ.stories.tsx +416 -0
- package/src/blocks/file-manager/FileManager.stories.tsx +413 -0
- package/src/blocks/footer/Footer.stories.tsx +328 -0
- package/src/blocks/gitops-sync-status/GitOpsSyncStatus.stories.tsx +529 -0
- package/src/blocks/header/WakaHeader.stories.tsx +455 -0
- package/src/blocks/headtab/Headtab.stories.tsx +369 -0
- package/src/blocks/i18n-editor/I18nEditor.stories.tsx +451 -0
- package/src/blocks/incident-manager/IncidentManager.stories.tsx +464 -0
- package/src/blocks/index.ts +1 -1
- package/src/blocks/infrastructure-map/InfrastructureMap.stories.tsx +533 -0
- package/src/blocks/kanban-board/KanbanBoard.stories.tsx +494 -0
- package/src/blocks/landing/WakaLanding.stories.tsx +449 -0
- package/src/blocks/landing/index.tsx +125 -66
- package/src/blocks/language-selector/LanguageSelector.stories.tsx +345 -0
- package/src/blocks/layout/Layout.stories.tsx +373 -0
- package/src/blocks/login/Login.stories.tsx +443 -0
- package/src/blocks/on-call-schedule/OnCallSchedule.stories.tsx +381 -0
- package/src/blocks/player-profile/PlayerProfile.stories.tsx +316 -0
- package/src/blocks/pricing/WakaPricing.stories.tsx +530 -0
- package/src/blocks/pricing/index.tsx +38 -4
- package/src/blocks/profile/Profile.stories.tsx +371 -0
- package/src/blocks/release-notes/ReleaseNotes.stories.tsx +431 -0
- package/src/blocks/settings/Settings.stories.tsx +520 -0
- package/src/blocks/sidebar/WakaSidebar.stories.tsx +513 -0
- package/src/blocks/sidebar/index.tsx +49 -20
- package/src/blocks/theme-creator-block/ThemeCreatorBlock.stories.tsx +370 -0
- package/src/blocks/user-management/UserManagement.stories.tsx +411 -0
- package/src/blocks/wizard/Wizard.stories.tsx +683 -0
- package/src/components/accordion/Accordion.stories.tsx +93 -0
- package/src/components/alert/Alert.stories.tsx +95 -0
- package/src/components/alert-dialog/AlertDialog.stories.tsx +126 -0
- package/src/components/aspect-ratio/AspectRatio.stories.tsx +118 -0
- package/src/components/avatar/Avatar.stories.tsx +104 -0
- package/src/components/button/Button.stories.tsx +12 -1
- package/src/components/calendar/Calendar.stories.tsx +102 -0
- package/src/components/card/Card.stories.tsx +125 -0
- package/src/components/checkbox/Checkbox.stories.tsx +100 -0
- package/src/components/code/Code.stories.tsx +402 -0
- package/src/components/collapsible/Collapsible.stories.tsx +123 -0
- package/src/components/command/Command.stories.tsx +207 -0
- package/src/components/context-menu/ContextMenu.stories.tsx +220 -0
- package/src/components/dialog/Dialog.stories.tsx +157 -0
- package/src/components/dropdown-menu/DropdownMenu.stories.tsx +225 -0
- package/src/components/form/Form.stories.tsx +413 -0
- package/src/components/hover-card/HoverCard.stories.tsx +148 -0
- package/src/components/input-otp/InputOTP.stories.tsx +255 -0
- package/src/components/label/Label.stories.tsx +68 -0
- package/src/components/menubar/Menubar.stories.tsx +278 -0
- package/src/components/navigation-menu/NavigationMenu.stories.tsx +202 -0
- package/src/components/popover/Popover.stories.tsx +199 -0
- package/src/components/progress/Progress.stories.tsx +104 -0
- package/src/components/radio-group/RadioGroup.stories.tsx +138 -0
- package/src/components/scroll-area/ScrollArea.stories.tsx +153 -0
- package/src/components/select/Select.stories.tsx +146 -0
- package/src/components/separator/Separator.stories.tsx +117 -0
- package/src/components/sheet/Sheet.stories.tsx +195 -0
- package/src/components/skeleton/Skeleton.stories.tsx +114 -0
- package/src/components/slider/Slider.stories.tsx +157 -0
- package/src/components/switch/Switch.stories.tsx +114 -0
- package/src/components/table/Table.stories.tsx +153 -0
- package/src/components/tabs/Tabs.stories.tsx +155 -0
- package/src/components/textarea/Textarea.stories.tsx +116 -0
- package/src/components/toast/Toast.stories.tsx +160 -0
- package/src/components/toggle/Toggle.stories.tsx +125 -0
- package/src/components/tooltip/Tooltip.stories.tsx +133 -0
- package/src/components/typography/Typography.stories.tsx +207 -0
- package/src/components/waka-3d-pie-chart/Waka3DPieChart.stories.tsx +308 -0
- package/src/components/waka-achievement-unlock/WakaAchievementUnlock.stories.tsx +353 -0
- package/src/components/waka-artifact-list/WakaArtifactList.stories.tsx +258 -0
- package/src/components/waka-autocomplete/WakaAutocomplete.stories.tsx +224 -0
- package/src/components/waka-badge-showcase/WakaBadgeShowcase.stories.tsx +269 -0
- package/src/components/waka-barcode/WakaBarcode.stories.tsx +227 -0
- package/src/components/waka-bottom-sheet/WakaBottomSheet.stories.tsx +408 -0
- package/src/components/waka-breadcrumb/WakaBreadcrumb.stories.tsx +237 -0
- package/src/components/waka-build-matrix/WakaBuildMatrix.stories.tsx +328 -0
- package/src/components/waka-carousel/WakaCarousel.stories.tsx +296 -0
- package/src/components/waka-charts/WakaCharts.stories.tsx +519 -0
- package/src/components/waka-color-picker/WakaColorPicker.stories.tsx +200 -0
- package/src/components/waka-combobox/WakaCombobox.stories.tsx +250 -0
- package/src/components/waka-container-list/WakaContainerList.stories.tsx +315 -0
- package/src/components/waka-contribution-graph/WakaContributionGraph.stories.tsx +354 -0
- package/src/components/waka-cost-breakdown/WakaCostBreakdown.stories.tsx +348 -0
- package/src/components/waka-daily-reward/WakaDailyReward.stories.tsx +365 -0
- package/src/components/waka-database-card/WakaDatabaseCard.stories.tsx +306 -0
- package/src/components/waka-date-range-picker/WakaDateRangePicker.stories.tsx +339 -0
- package/src/components/waka-datetime-picker/WakaDateTimePicker.stories.tsx +317 -0
- package/src/components/waka-deployment-lane/WakaDeploymentLane.stories.tsx +292 -0
- package/src/components/waka-dock/WakaDock.stories.tsx +332 -0
- package/src/components/waka-drawer/WakaDrawer.stories.tsx +437 -0
- package/src/components/waka-env-var-editor/WakaEnvVarEditor.stories.tsx +263 -0
- package/src/components/waka-error-shake/WakaErrorShake.stories.tsx +410 -0
- package/src/components/waka-file-upload/WakaFileUpload.stories.tsx +239 -0
- package/src/components/waka-flow-diagram/WakaFlowDiagram.stories.tsx +365 -0
- package/src/components/waka-funnel-chart/WakaFunnelChart.stories.tsx +281 -0
- package/src/components/waka-glow-card/WakaGlowCard.stories.tsx +274 -0
- package/src/components/waka-haptic-button/WakaHapticButton.stories.tsx +349 -0
- package/src/components/waka-health-pulse/WakaHealthPulse.stories.tsx +293 -0
- package/src/components/waka-heatmap/WakaHeatmap.stories.tsx +376 -0
- package/src/components/waka-image/WakaImage.stories.tsx +255 -0
- package/src/components/waka-incident-timeline/WakaIncidentTimeline.stories.tsx +300 -0
- package/src/components/waka-kanban/WakaKanban.stories.tsx +399 -0
- package/src/components/waka-kubernetes-overview/WakaKubernetesOverview.stories.tsx +281 -0
- package/src/components/waka-leaderboard/WakaLeaderboard.stories.tsx +300 -0
- package/src/components/waka-level-progress/WakaLevelProgress.stories.tsx +313 -0
- package/src/components/waka-loading-orbit/WakaLoadingOrbit.stories.tsx +413 -0
- package/src/components/waka-log-viewer/WakaLogViewer.stories.tsx +312 -0
- package/src/components/waka-loot-box/WakaLootBox.stories.tsx +374 -0
- package/src/components/waka-metric-sparkline/WakaMetricSparkline.stories.tsx +312 -0
- package/src/components/waka-migration-list/WakaMigrationList.stories.tsx +289 -0
- package/src/components/waka-modal/WakaModal.stories.tsx +434 -0
- package/src/components/waka-morph-button/WakaMorphButton.stories.tsx +405 -0
- package/src/components/waka-network-topology/WakaNetworkTopology.stories.tsx +364 -0
- package/src/components/waka-notifications/WakaNotifications.stories.tsx +290 -0
- package/src/components/waka-number-input/WakaNumberInput.stories.tsx +282 -0
- package/src/components/waka-pagination/WakaPagination.stories.tsx +328 -0
- package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +318 -0
- package/src/components/waka-pipeline-view/WakaPipelineView.stories.tsx +386 -0
- package/src/components/waka-player-card/WakaPlayerCard.stories.tsx +333 -0
- package/src/components/waka-pod-card/WakaPodCard.stories.tsx +435 -0
- package/src/components/waka-qrcode/WakaQRCode.stories.tsx +232 -0
- package/src/components/waka-query-explain/WakaQueryExplain.stories.tsx +407 -0
- package/src/components/waka-quest-card/WakaQuestCard.stories.tsx +394 -0
- package/src/components/waka-quota-bar/WakaQuotaBar.stories.tsx +435 -0
- package/src/components/waka-radar-score/WakaRadarScore.stories.tsx +372 -0
- package/src/components/waka-resource-gauge/WakaResourceGauge.stories.tsx +366 -0
- package/src/components/waka-rich-text-editor/WakaRichTextEditor.stories.tsx +238 -0
- package/src/components/waka-sankey-diagram/WakaSankeyDiagram.stories.tsx +389 -0
- package/src/components/waka-scratch-card/WakaScratchCard.stories.tsx +388 -0
- package/src/components/waka-secret-card/WakaSecretCard.stories.tsx +314 -0
- package/src/components/waka-segmented-control/WakaSegmentedControl.stories.tsx +309 -0
- package/src/components/waka-server-rack/WakaServerRack.stories.tsx +382 -0
- package/src/components/waka-service-graph/WakaServiceGraph.stories.tsx +262 -0
- package/src/components/waka-skeleton-wave/WakaSkeletonWave.stories.tsx +321 -0
- package/src/components/waka-skill-tree/WakaSkillTree.stories.tsx +308 -0
- package/src/components/waka-spin-wheel/WakaSpinWheel.stories.tsx +368 -0
- package/src/components/waka-spinner/WakaSpinner.stories.tsx +156 -0
- package/src/components/waka-stat/WakaStat.stories.tsx +334 -0
- package/src/components/waka-status-matrix/WakaStatusMatrix.stories.tsx +331 -0
- package/src/components/waka-stepper/WakaStepper.stories.tsx +468 -0
- package/src/components/waka-streak-counter/WakaStreakCounter.stories.tsx +235 -0
- package/src/components/waka-success-explosion/WakaSuccessExplosion.stories.tsx +389 -0
- package/src/components/waka-tabs-morph/WakaTabsMorph.stories.tsx +471 -0
- package/src/components/waka-terminal-output/WakaTerminalOutput.stories.tsx +351 -0
- package/src/components/waka-test-report/WakaTestReport.stories.tsx +322 -0
- package/src/components/waka-tilt-card/WakaTiltCard.stories.tsx +300 -0
- package/src/components/waka-time-picker/WakaTimePicker.stories.tsx +227 -0
- package/src/components/waka-timeline/WakaTimeline.stories.tsx +383 -0
- package/src/components/waka-tournament-bracket/WakaTournamentBracket.stories.tsx +375 -0
- package/src/components/waka-trace-viewer/WakaTraceViewer.stories.tsx +445 -0
- package/src/components/waka-tree/WakaTree.stories.tsx +359 -0
- package/src/components/waka-treemap-chart/WakaTreemapChart.stories.tsx +378 -0
- package/src/components/waka-typewriter/WakaTypewriter.stories.tsx +366 -0
- package/src/components/waka-versus-card/WakaVersusCard.stories.tsx +530 -0
- package/src/components/waka-video/WakaVideo.stories.tsx +203 -0
- package/src/components/waka-virtual-list/WakaVirtualList.stories.tsx +273 -0
- 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
|
+
© 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's what'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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
</
|
|
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
|
-
|
|
370
|
-
{action.
|
|
371
|
-
|
|
372
|
-
|
|
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
|