@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,309 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaSegmentedControl, useSegmentedControl } from './index'
3
+ import type { SegmentOption } from './index'
4
+ import * as React from 'react'
5
+ import { List, Grid, LayoutGrid, Sun, Moon, Monitor, AlignLeft, AlignCenter, AlignRight, AlignJustify } from 'lucide-react'
6
+
7
+ const meta: Meta<typeof WakaSegmentedControl> = {
8
+ title: 'Components/Display/WakaSegmentedControl',
9
+ component: WakaSegmentedControl,
10
+ parameters: {
11
+ layout: 'centered',
12
+ docs: {
13
+ description: {
14
+ component: 'A segmented control component for switching between related options with an animated indicator.',
15
+ },
16
+ },
17
+ },
18
+ tags: ['autodocs'],
19
+ argTypes: {
20
+ size: {
21
+ control: 'select',
22
+ options: ['sm', 'md', 'lg'],
23
+ description: 'Size of the control',
24
+ },
25
+ variant: {
26
+ control: 'select',
27
+ options: ['default', 'pills'],
28
+ description: 'Visual variant',
29
+ },
30
+ fullWidth: {
31
+ control: 'boolean',
32
+ description: 'Take full width of container',
33
+ },
34
+ disabled: {
35
+ control: 'boolean',
36
+ description: 'Disable the control',
37
+ },
38
+ },
39
+ }
40
+
41
+ export default meta
42
+ type Story = StoryObj<typeof WakaSegmentedControl>
43
+
44
+ const viewOptions: SegmentOption[] = [
45
+ { value: 'list', label: 'List' },
46
+ { value: 'grid', label: 'Grid' },
47
+ { value: 'gallery', label: 'Gallery' },
48
+ ]
49
+
50
+ export const Default: Story = {
51
+ args: {
52
+ options: viewOptions,
53
+ },
54
+ render: (args) => {
55
+ const [value, setValue] = React.useState('list')
56
+ return (
57
+ <WakaSegmentedControl
58
+ {...args}
59
+ value={value}
60
+ onChange={setValue}
61
+ />
62
+ )
63
+ },
64
+ }
65
+
66
+ export const Sizes: Story = {
67
+ render: () => {
68
+ const [value, setValue] = React.useState('list')
69
+ return (
70
+ <div className="space-y-6">
71
+ <div>
72
+ <p className="text-sm text-muted-foreground mb-2">Small</p>
73
+ <WakaSegmentedControl
74
+ options={viewOptions}
75
+ value={value}
76
+ onChange={setValue}
77
+ size="sm"
78
+ />
79
+ </div>
80
+ <div>
81
+ <p className="text-sm text-muted-foreground mb-2">Medium (Default)</p>
82
+ <WakaSegmentedControl
83
+ options={viewOptions}
84
+ value={value}
85
+ onChange={setValue}
86
+ size="md"
87
+ />
88
+ </div>
89
+ <div>
90
+ <p className="text-sm text-muted-foreground mb-2">Large</p>
91
+ <WakaSegmentedControl
92
+ options={viewOptions}
93
+ value={value}
94
+ onChange={setValue}
95
+ size="lg"
96
+ />
97
+ </div>
98
+ </div>
99
+ )
100
+ },
101
+ }
102
+
103
+ export const Variants: Story = {
104
+ render: () => {
105
+ const [value, setValue] = React.useState('list')
106
+ return (
107
+ <div className="space-y-6">
108
+ <div>
109
+ <p className="text-sm text-muted-foreground mb-2">Default (Animated Indicator)</p>
110
+ <WakaSegmentedControl
111
+ options={viewOptions}
112
+ value={value}
113
+ onChange={setValue}
114
+ variant="default"
115
+ />
116
+ </div>
117
+ <div>
118
+ <p className="text-sm text-muted-foreground mb-2">Pills</p>
119
+ <WakaSegmentedControl
120
+ options={viewOptions}
121
+ value={value}
122
+ onChange={setValue}
123
+ variant="pills"
124
+ />
125
+ </div>
126
+ </div>
127
+ )
128
+ },
129
+ }
130
+
131
+ export const WithIcons: Story = {
132
+ render: () => {
133
+ const optionsWithIcons: SegmentOption[] = [
134
+ { value: 'list', label: 'List', icon: <List className="h-4 w-4" /> },
135
+ { value: 'grid', label: 'Grid', icon: <Grid className="h-4 w-4" /> },
136
+ { value: 'gallery', label: 'Gallery', icon: <LayoutGrid className="h-4 w-4" /> },
137
+ ]
138
+
139
+ const [value, setValue] = React.useState('list')
140
+ return (
141
+ <WakaSegmentedControl
142
+ options={optionsWithIcons}
143
+ value={value}
144
+ onChange={setValue}
145
+ />
146
+ )
147
+ },
148
+ }
149
+
150
+ export const IconOnly: Story = {
151
+ render: () => {
152
+ const iconOnlyOptions: SegmentOption[] = [
153
+ { value: 'left', label: '', icon: <AlignLeft className="h-4 w-4" /> },
154
+ { value: 'center', label: '', icon: <AlignCenter className="h-4 w-4" /> },
155
+ { value: 'right', label: '', icon: <AlignRight className="h-4 w-4" /> },
156
+ { value: 'justify', label: '', icon: <AlignJustify className="h-4 w-4" /> },
157
+ ]
158
+
159
+ const [value, setValue] = React.useState('left')
160
+ return (
161
+ <WakaSegmentedControl
162
+ options={iconOnlyOptions}
163
+ value={value}
164
+ onChange={setValue}
165
+ />
166
+ )
167
+ },
168
+ }
169
+
170
+ export const ThemeSwitcher: Story = {
171
+ render: () => {
172
+ const themeOptions: SegmentOption[] = [
173
+ { value: 'light', label: 'Light', icon: <Sun className="h-4 w-4" /> },
174
+ { value: 'dark', label: 'Dark', icon: <Moon className="h-4 w-4" /> },
175
+ { value: 'system', label: 'System', icon: <Monitor className="h-4 w-4" /> },
176
+ ]
177
+
178
+ const [value, setValue] = React.useState('system')
179
+ return (
180
+ <div className="border rounded-lg p-6 w-[400px]">
181
+ <h3 className="font-semibold mb-4">Appearance</h3>
182
+ <WakaSegmentedControl
183
+ options={themeOptions}
184
+ value={value}
185
+ onChange={setValue}
186
+ fullWidth
187
+ />
188
+ <p className="text-sm text-muted-foreground mt-4">
189
+ Current theme: <span className="font-medium capitalize">{value}</span>
190
+ </p>
191
+ </div>
192
+ )
193
+ },
194
+ }
195
+
196
+ export const FullWidth: Story = {
197
+ render: () => {
198
+ const [value, setValue] = React.useState('list')
199
+ return (
200
+ <div className="w-[400px]">
201
+ <WakaSegmentedControl
202
+ options={viewOptions}
203
+ value={value}
204
+ onChange={setValue}
205
+ fullWidth
206
+ />
207
+ </div>
208
+ )
209
+ },
210
+ }
211
+
212
+ export const WithDisabledOptions: Story = {
213
+ render: () => {
214
+ const optionsWithDisabled: SegmentOption[] = [
215
+ { value: 'free', label: 'Free' },
216
+ { value: 'pro', label: 'Pro' },
217
+ { value: 'enterprise', label: 'Enterprise', disabled: true },
218
+ ]
219
+
220
+ const [value, setValue] = React.useState('free')
221
+ return (
222
+ <WakaSegmentedControl
223
+ options={optionsWithDisabled}
224
+ value={value}
225
+ onChange={setValue}
226
+ />
227
+ )
228
+ },
229
+ }
230
+
231
+ export const Disabled: Story = {
232
+ render: () => {
233
+ const [value, setValue] = React.useState('list')
234
+ return (
235
+ <WakaSegmentedControl
236
+ options={viewOptions}
237
+ value={value}
238
+ onChange={setValue}
239
+ disabled
240
+ />
241
+ )
242
+ },
243
+ }
244
+
245
+ export const WithHook: Story = {
246
+ render: () => {
247
+ const { value, onChange, selectNext, selectPrevious } = useSegmentedControl(viewOptions, 'list')
248
+
249
+ return (
250
+ <div className="space-y-4 w-[400px]">
251
+ <WakaSegmentedControl
252
+ options={viewOptions}
253
+ value={value}
254
+ onChange={onChange}
255
+ />
256
+ <div className="flex gap-2">
257
+ <button
258
+ onClick={selectPrevious}
259
+ className="px-3 py-1 text-sm border rounded hover:bg-muted"
260
+ >
261
+ Previous
262
+ </button>
263
+ <button
264
+ onClick={selectNext}
265
+ className="px-3 py-1 text-sm border rounded hover:bg-muted"
266
+ >
267
+ Next
268
+ </button>
269
+ </div>
270
+ <p className="text-sm text-muted-foreground">
271
+ Selected: <span className="font-medium capitalize">{value}</span>
272
+ </p>
273
+ </div>
274
+ )
275
+ },
276
+ }
277
+
278
+ export const PricingToggle: Story = {
279
+ render: () => {
280
+ const billingOptions: SegmentOption[] = [
281
+ { value: 'monthly', label: 'Monthly' },
282
+ { value: 'yearly', label: 'Yearly (Save 20%)' },
283
+ ]
284
+
285
+ const [billing, setBilling] = React.useState('monthly')
286
+ const price = billing === 'monthly' ? 29 : 23
287
+
288
+ return (
289
+ <div className="text-center space-y-6 w-[400px]">
290
+ <WakaSegmentedControl
291
+ options={billingOptions}
292
+ value={billing}
293
+ onChange={setBilling}
294
+ variant="pills"
295
+ />
296
+ <div className="border rounded-lg p-6">
297
+ <h3 className="font-semibold text-lg">Pro Plan</h3>
298
+ <div className="mt-4">
299
+ <span className="text-4xl font-bold">${price}</span>
300
+ <span className="text-muted-foreground">/month</span>
301
+ </div>
302
+ {billing === 'yearly' && (
303
+ <p className="text-sm text-green-600 mt-2">You save $72/year!</p>
304
+ )}
305
+ </div>
306
+ </div>
307
+ )
308
+ },
309
+ }
@@ -0,0 +1,382 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaServerRack, WakaServerRackDemo } from './index'
3
+ import type { ServerUnit } from './index'
4
+ import * as React from 'react'
5
+
6
+ const sampleServers: ServerUnit[] = [
7
+ {
8
+ id: 'srv-001',
9
+ name: 'Web Server 01',
10
+ unit: 40,
11
+ height: 2,
12
+ status: 'online',
13
+ cpu: 45,
14
+ ram: 62,
15
+ disk: 34,
16
+ network: { in: 850, out: 420 },
17
+ },
18
+ {
19
+ id: 'srv-002',
20
+ name: 'Database Primary',
21
+ unit: 37,
22
+ height: 3,
23
+ status: 'online',
24
+ cpu: 78,
25
+ ram: 85,
26
+ disk: 67,
27
+ network: { in: 1200, out: 980 },
28
+ },
29
+ {
30
+ id: 'srv-003',
31
+ name: 'Cache Server',
32
+ unit: 35,
33
+ height: 2,
34
+ status: 'warning',
35
+ cpu: 92,
36
+ ram: 88,
37
+ disk: 45,
38
+ network: { in: 2100, out: 1850 },
39
+ },
40
+ {
41
+ id: 'srv-004',
42
+ name: 'API Gateway',
43
+ unit: 33,
44
+ height: 1,
45
+ status: 'online',
46
+ cpu: 34,
47
+ ram: 41,
48
+ disk: 22,
49
+ network: { in: 3500, out: 3200 },
50
+ },
51
+ {
52
+ id: 'srv-005',
53
+ name: 'Worker Node 01',
54
+ unit: 31,
55
+ height: 2,
56
+ status: 'online',
57
+ cpu: 56,
58
+ ram: 67,
59
+ disk: 41,
60
+ },
61
+ {
62
+ id: 'srv-006',
63
+ name: 'Worker Node 02',
64
+ unit: 29,
65
+ height: 2,
66
+ status: 'offline',
67
+ cpu: 0,
68
+ ram: 0,
69
+ disk: 41,
70
+ },
71
+ {
72
+ id: 'srv-007',
73
+ name: 'Storage Array',
74
+ unit: 25,
75
+ height: 4,
76
+ status: 'online',
77
+ cpu: 12,
78
+ ram: 24,
79
+ disk: 89,
80
+ network: { in: 500, out: 4500 },
81
+ },
82
+ {
83
+ id: 'srv-008',
84
+ name: 'Backup Server',
85
+ unit: 22,
86
+ height: 3,
87
+ status: 'maintenance',
88
+ cpu: 5,
89
+ ram: 15,
90
+ disk: 72,
91
+ },
92
+ ]
93
+
94
+ const meta: Meta<typeof WakaServerRack> = {
95
+ title: 'Components/DevOps/WakaServerRack',
96
+ component: WakaServerRack,
97
+ parameters: {
98
+ layout: 'centered',
99
+ docs: {
100
+ description: {
101
+ component: 'A 3D server rack visualization with status LEDs, CPU/RAM/Disk metrics, hover tooltips, and selection state.',
102
+ },
103
+ },
104
+ backgrounds: { default: 'dark' },
105
+ },
106
+ tags: ['autodocs'],
107
+ argTypes: {
108
+ totalUnits: {
109
+ control: { type: 'number', min: 10, max: 48, step: 1 },
110
+ description: 'Total rack units available',
111
+ },
112
+ showMetrics: {
113
+ control: 'boolean',
114
+ description: 'Show CPU/RAM/Disk metrics bars',
115
+ },
116
+ animated: {
117
+ control: 'boolean',
118
+ description: 'Enable animations',
119
+ },
120
+ },
121
+ }
122
+
123
+ export default meta
124
+ type Story = StoryObj<typeof WakaServerRack>
125
+
126
+ export const Default: Story = {
127
+ args: {
128
+ servers: sampleServers,
129
+ totalUnits: 42,
130
+ showMetrics: true,
131
+ animated: true,
132
+ },
133
+ render: (args) => (
134
+ <div className="p-8 bg-zinc-950 rounded-2xl">
135
+ <WakaServerRack {...args} />
136
+ </div>
137
+ ),
138
+ }
139
+
140
+ export const Demo: Story = {
141
+ render: () => <WakaServerRackDemo />,
142
+ }
143
+
144
+ export const WithSelection: Story = {
145
+ render: () => {
146
+ const [selected, setSelected] = React.useState<string | null>('srv-002')
147
+
148
+ return (
149
+ <div className="p-8 bg-zinc-950 rounded-2xl">
150
+ <div className="mb-4 text-zinc-300 text-sm">
151
+ Selected: {selected || 'None'}
152
+ </div>
153
+ <WakaServerRack
154
+ servers={sampleServers}
155
+ totalUnits={42}
156
+ selectedServer={selected || undefined}
157
+ onServerClick={(id) => setSelected(prev => prev === id ? null : id)}
158
+ showMetrics
159
+ animated
160
+ />
161
+ </div>
162
+ )
163
+ },
164
+ }
165
+
166
+ export const StatusStates: Story = {
167
+ render: () => {
168
+ const statusServers: ServerUnit[] = [
169
+ { id: 's1', name: 'Online Server', unit: 10, height: 2, status: 'online', cpu: 45, ram: 60, disk: 30 },
170
+ { id: 's2', name: 'Warning Server', unit: 8, height: 2, status: 'warning', cpu: 92, ram: 88, disk: 75 },
171
+ { id: 's3', name: 'Offline Server', unit: 6, height: 2, status: 'offline', cpu: 0, ram: 0, disk: 0 },
172
+ { id: 's4', name: 'Maintenance', unit: 4, height: 2, status: 'maintenance', cpu: 5, ram: 10, disk: 20 },
173
+ ]
174
+
175
+ return (
176
+ <div className="p-8 bg-zinc-950 rounded-2xl">
177
+ <h3 className="text-zinc-100 font-semibold mb-4">Server Status States</h3>
178
+ <WakaServerRack
179
+ servers={statusServers}
180
+ totalUnits={12}
181
+ showMetrics
182
+ animated
183
+ />
184
+ </div>
185
+ )
186
+ },
187
+ }
188
+
189
+ export const HighDensity: Story = {
190
+ render: () => {
191
+ const denseServers: ServerUnit[] = Array.from({ length: 20 }, (_, i) => ({
192
+ id: `srv-${i + 1}`,
193
+ name: `Server ${String(i + 1).padStart(2, '0')}`,
194
+ unit: i * 2 + 1,
195
+ height: 1,
196
+ status: ['online', 'online', 'online', 'warning', 'online'][Math.floor(Math.random() * 5)] as ServerUnit['status'],
197
+ cpu: Math.floor(Math.random() * 80) + 10,
198
+ ram: Math.floor(Math.random() * 70) + 20,
199
+ disk: Math.floor(Math.random() * 60) + 10,
200
+ }))
201
+
202
+ return (
203
+ <div className="p-8 bg-zinc-950 rounded-2xl">
204
+ <h3 className="text-zinc-100 font-semibold mb-4">High Density 1U Servers</h3>
205
+ <WakaServerRack
206
+ servers={denseServers}
207
+ totalUnits={42}
208
+ showMetrics={false}
209
+ animated
210
+ />
211
+ </div>
212
+ )
213
+ },
214
+ }
215
+
216
+ export const SmallRack: Story = {
217
+ render: () => {
218
+ const smallServers: ServerUnit[] = [
219
+ { id: 's1', name: 'App Server', unit: 8, height: 2, status: 'online', cpu: 55, ram: 72, disk: 45 },
220
+ { id: 's2', name: 'DB Server', unit: 5, height: 3, status: 'online', cpu: 68, ram: 81, disk: 62 },
221
+ { id: 's3', name: 'NAS', unit: 1, height: 4, status: 'online', cpu: 15, ram: 30, disk: 88 },
222
+ ]
223
+
224
+ return (
225
+ <div className="p-8 bg-zinc-950 rounded-2xl">
226
+ <h3 className="text-zinc-100 font-semibold mb-4">12U Cabinet</h3>
227
+ <WakaServerRack
228
+ servers={smallServers}
229
+ totalUnits={12}
230
+ showMetrics
231
+ animated
232
+ />
233
+ </div>
234
+ )
235
+ },
236
+ }
237
+
238
+ export const NoMetrics: Story = {
239
+ render: () => (
240
+ <div className="p-8 bg-zinc-950 rounded-2xl">
241
+ <WakaServerRack
242
+ servers={sampleServers}
243
+ totalUnits={42}
244
+ showMetrics={false}
245
+ animated
246
+ />
247
+ </div>
248
+ ),
249
+ }
250
+
251
+ export const NoAnimations: Story = {
252
+ render: () => (
253
+ <div className="p-8 bg-zinc-950 rounded-2xl">
254
+ <WakaServerRack
255
+ servers={sampleServers}
256
+ totalUnits={42}
257
+ showMetrics
258
+ animated={false}
259
+ />
260
+ </div>
261
+ ),
262
+ }
263
+
264
+ export const HighLoad: Story = {
265
+ render: () => {
266
+ const highLoadServers: ServerUnit[] = sampleServers.map(server => ({
267
+ ...server,
268
+ cpu: Math.min(100, server.cpu + 40),
269
+ ram: Math.min(100, server.ram + 30),
270
+ disk: Math.min(100, server.disk + 20),
271
+ status: server.status === 'online' && server.cpu > 70 ? 'warning' : server.status,
272
+ }))
273
+
274
+ return (
275
+ <div className="p-8 bg-zinc-950 rounded-2xl">
276
+ <div className="mb-4">
277
+ <span className="text-red-400 text-sm font-medium">⚠️ High load detected</span>
278
+ </div>
279
+ <WakaServerRack
280
+ servers={highLoadServers}
281
+ totalUnits={42}
282
+ showMetrics
283
+ animated
284
+ />
285
+ </div>
286
+ )
287
+ },
288
+ }
289
+
290
+ export const DataCenterDashboard: Story = {
291
+ render: () => {
292
+ const [selected, setSelected] = React.useState<string | null>(null)
293
+ const selectedServer = sampleServers.find(s => s.id === selected)
294
+
295
+ return (
296
+ <div className="flex gap-6 p-6 bg-zinc-950 rounded-2xl">
297
+ <div>
298
+ <h3 className="text-zinc-100 font-semibold mb-2">Rack A-01</h3>
299
+ <p className="text-zinc-500 text-sm mb-4">
300
+ {sampleServers.filter(s => s.status === 'online').length} online,{' '}
301
+ {sampleServers.filter(s => s.status === 'warning').length} warning,{' '}
302
+ {sampleServers.filter(s => s.status === 'offline').length} offline
303
+ </p>
304
+ <WakaServerRack
305
+ servers={sampleServers}
306
+ totalUnits={42}
307
+ selectedServer={selected || undefined}
308
+ onServerClick={(id) => setSelected(prev => prev === id ? null : id)}
309
+ showMetrics
310
+ animated
311
+ />
312
+ </div>
313
+
314
+ {selectedServer && (
315
+ <div className="w-64 p-4 bg-zinc-900 rounded-lg border border-zinc-700">
316
+ <h4 className="font-semibold text-zinc-100 mb-4">{selectedServer.name}</h4>
317
+ <div className="space-y-3 text-sm">
318
+ <div className="flex justify-between">
319
+ <span className="text-zinc-400">Status</span>
320
+ <span className={
321
+ selectedServer.status === 'online' ? 'text-green-400' :
322
+ selectedServer.status === 'warning' ? 'text-yellow-400' :
323
+ selectedServer.status === 'offline' ? 'text-red-400' :
324
+ 'text-blue-400'
325
+ }>
326
+ {selectedServer.status.charAt(0).toUpperCase() + selectedServer.status.slice(1)}
327
+ </span>
328
+ </div>
329
+ <div className="flex justify-between">
330
+ <span className="text-zinc-400">Position</span>
331
+ <span className="text-zinc-200">U{selectedServer.unit}-U{selectedServer.unit + selectedServer.height - 1}</span>
332
+ </div>
333
+ <div className="flex justify-between">
334
+ <span className="text-zinc-400">Size</span>
335
+ <span className="text-zinc-200">{selectedServer.height}U</span>
336
+ </div>
337
+ <div className="border-t border-zinc-700 my-3" />
338
+ <div className="space-y-2">
339
+ <div className="flex justify-between items-center">
340
+ <span className="text-zinc-400">CPU</span>
341
+ <div className="flex items-center gap-2">
342
+ <div className="w-20 h-1.5 rounded-full bg-zinc-800">
343
+ <div
344
+ className="h-full rounded-full bg-blue-500"
345
+ style={{ width: `${selectedServer.cpu}%` }}
346
+ />
347
+ </div>
348
+ <span className="text-zinc-200 w-8 text-right">{selectedServer.cpu}%</span>
349
+ </div>
350
+ </div>
351
+ <div className="flex justify-between items-center">
352
+ <span className="text-zinc-400">RAM</span>
353
+ <div className="flex items-center gap-2">
354
+ <div className="w-20 h-1.5 rounded-full bg-zinc-800">
355
+ <div
356
+ className="h-full rounded-full bg-purple-500"
357
+ style={{ width: `${selectedServer.ram}%` }}
358
+ />
359
+ </div>
360
+ <span className="text-zinc-200 w-8 text-right">{selectedServer.ram}%</span>
361
+ </div>
362
+ </div>
363
+ <div className="flex justify-between items-center">
364
+ <span className="text-zinc-400">Disk</span>
365
+ <div className="flex items-center gap-2">
366
+ <div className="w-20 h-1.5 rounded-full bg-zinc-800">
367
+ <div
368
+ className="h-full rounded-full bg-cyan-500"
369
+ style={{ width: `${selectedServer.disk}%` }}
370
+ />
371
+ </div>
372
+ <span className="text-zinc-200 w-8 text-right">{selectedServer.disk}%</span>
373
+ </div>
374
+ </div>
375
+ </div>
376
+ </div>
377
+ </div>
378
+ )}
379
+ </div>
380
+ )
381
+ },
382
+ }