@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,437 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaDrawer, useDrawer, DrawerHeader, DrawerBody, DrawerFooter } from './index'
3
+ import * as React from 'react'
4
+ import { Button } from '../button'
5
+ import { Settings, User, Bell, Shield, CreditCard, LogOut, Menu, Filter, X } from 'lucide-react'
6
+
7
+ const meta: Meta<typeof WakaDrawer> = {
8
+ title: 'Components/Display/WakaDrawer',
9
+ component: WakaDrawer,
10
+ parameters: {
11
+ layout: 'centered',
12
+ docs: {
13
+ description: {
14
+ component: 'A slide-out drawer component with multiple positions, sizes, and customization options.',
15
+ },
16
+ },
17
+ },
18
+ tags: ['autodocs'],
19
+ argTypes: {
20
+ position: {
21
+ control: 'select',
22
+ options: ['left', 'right', 'top', 'bottom'],
23
+ description: 'Position of the drawer',
24
+ },
25
+ size: {
26
+ control: 'select',
27
+ options: ['sm', 'md', 'lg', 'xl', 'full'],
28
+ description: 'Size of the drawer',
29
+ },
30
+ showCloseButton: {
31
+ control: 'boolean',
32
+ description: 'Show close button',
33
+ },
34
+ closeOnOverlayClick: {
35
+ control: 'boolean',
36
+ description: 'Close when clicking overlay',
37
+ },
38
+ showOverlay: {
39
+ control: 'boolean',
40
+ description: 'Show overlay backdrop',
41
+ },
42
+ },
43
+ }
44
+
45
+ export default meta
46
+ type Story = StoryObj<typeof WakaDrawer>
47
+
48
+ export const Default: Story = {
49
+ args: {
50
+ title: 'Drawer Title',
51
+ description: 'This is a drawer description.',
52
+ },
53
+ render: (args) => {
54
+ const [open, setOpen] = React.useState(false)
55
+ return (
56
+ <>
57
+ <Button onClick={() => setOpen(true)}>Open Drawer</Button>
58
+ <WakaDrawer
59
+ {...args}
60
+ open={open}
61
+ onOpenChange={setOpen}
62
+ >
63
+ <p>Drawer content goes here.</p>
64
+ </WakaDrawer>
65
+ </>
66
+ )
67
+ },
68
+ }
69
+
70
+ export const Positions: Story = {
71
+ render: () => {
72
+ const [position, setPosition] = React.useState<'left' | 'right' | 'top' | 'bottom'>('right')
73
+ const [open, setOpen] = React.useState(false)
74
+
75
+ return (
76
+ <div className="space-y-4">
77
+ <div className="flex gap-2">
78
+ {(['left', 'right', 'top', 'bottom'] as const).map((pos) => (
79
+ <Button
80
+ key={pos}
81
+ variant={position === pos ? 'default' : 'outline'}
82
+ onClick={() => {
83
+ setPosition(pos)
84
+ setOpen(true)
85
+ }}
86
+ >
87
+ {pos.charAt(0).toUpperCase() + pos.slice(1)}
88
+ </Button>
89
+ ))}
90
+ </div>
91
+ <WakaDrawer
92
+ open={open}
93
+ onOpenChange={setOpen}
94
+ position={position}
95
+ title={`${position.charAt(0).toUpperCase() + position.slice(1)} Drawer`}
96
+ description="This drawer slides in from the selected position."
97
+ >
98
+ <p>Drawer content for {position} position.</p>
99
+ </WakaDrawer>
100
+ </div>
101
+ )
102
+ },
103
+ }
104
+
105
+ export const Sizes: Story = {
106
+ render: () => {
107
+ const [size, setSize] = React.useState<'sm' | 'md' | 'lg' | 'xl' | 'full'>('md')
108
+ const [open, setOpen] = React.useState(false)
109
+
110
+ return (
111
+ <div className="space-y-4">
112
+ <div className="flex gap-2 flex-wrap">
113
+ {(['sm', 'md', 'lg', 'xl', 'full'] as const).map((s) => (
114
+ <Button
115
+ key={s}
116
+ variant={size === s ? 'default' : 'outline'}
117
+ onClick={() => {
118
+ setSize(s)
119
+ setOpen(true)
120
+ }}
121
+ >
122
+ {s.toUpperCase()}
123
+ </Button>
124
+ ))}
125
+ </div>
126
+ <WakaDrawer
127
+ open={open}
128
+ onOpenChange={setOpen}
129
+ size={size}
130
+ title={`Size: ${size.toUpperCase()}`}
131
+ description="Demonstrating different drawer sizes."
132
+ >
133
+ <p>This drawer has size "{size}".</p>
134
+ </WakaDrawer>
135
+ </div>
136
+ )
137
+ },
138
+ }
139
+
140
+ export const WithFooter: Story = {
141
+ render: () => {
142
+ const [open, setOpen] = React.useState(false)
143
+ return (
144
+ <>
145
+ <Button onClick={() => setOpen(true)}>Open with Footer</Button>
146
+ <WakaDrawer
147
+ open={open}
148
+ onOpenChange={setOpen}
149
+ title="Edit Profile"
150
+ description="Make changes to your profile information."
151
+ footer={
152
+ <>
153
+ <Button variant="outline" onClick={() => setOpen(false)}>
154
+ Cancel
155
+ </Button>
156
+ <Button onClick={() => setOpen(false)}>
157
+ Save Changes
158
+ </Button>
159
+ </>
160
+ }
161
+ >
162
+ <div className="space-y-4">
163
+ <div>
164
+ <label className="text-sm font-medium">Name</label>
165
+ <input
166
+ type="text"
167
+ className="w-full mt-1 px-3 py-2 border rounded-md"
168
+ defaultValue="John Doe"
169
+ />
170
+ </div>
171
+ <div>
172
+ <label className="text-sm font-medium">Email</label>
173
+ <input
174
+ type="email"
175
+ className="w-full mt-1 px-3 py-2 border rounded-md"
176
+ defaultValue="john@example.com"
177
+ />
178
+ </div>
179
+ <div>
180
+ <label className="text-sm font-medium">Bio</label>
181
+ <textarea
182
+ className="w-full mt-1 px-3 py-2 border rounded-md"
183
+ rows={3}
184
+ defaultValue="Software developer passionate about React."
185
+ />
186
+ </div>
187
+ </div>
188
+ </WakaDrawer>
189
+ </>
190
+ )
191
+ },
192
+ }
193
+
194
+ export const NavigationMenu: Story = {
195
+ render: () => {
196
+ const [open, setOpen] = React.useState(false)
197
+
198
+ const menuItems = [
199
+ { icon: <User className="h-5 w-5" />, label: 'Profile', href: '#' },
200
+ { icon: <Settings className="h-5 w-5" />, label: 'Settings', href: '#' },
201
+ { icon: <Bell className="h-5 w-5" />, label: 'Notifications', href: '#' },
202
+ { icon: <Shield className="h-5 w-5" />, label: 'Security', href: '#' },
203
+ { icon: <CreditCard className="h-5 w-5" />, label: 'Billing', href: '#' },
204
+ ]
205
+
206
+ return (
207
+ <>
208
+ <Button variant="ghost" size="icon" onClick={() => setOpen(true)}>
209
+ <Menu className="h-5 w-5" />
210
+ </Button>
211
+ <WakaDrawer
212
+ open={open}
213
+ onOpenChange={setOpen}
214
+ position="left"
215
+ size="sm"
216
+ title="Menu"
217
+ >
218
+ <nav className="space-y-1">
219
+ {menuItems.map((item) => (
220
+ <a
221
+ key={item.label}
222
+ href={item.href}
223
+ className="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-muted transition-colors"
224
+ onClick={() => setOpen(false)}
225
+ >
226
+ {item.icon}
227
+ <span>{item.label}</span>
228
+ </a>
229
+ ))}
230
+ </nav>
231
+ <div className="mt-auto pt-4 border-t">
232
+ <button className="flex items-center gap-3 px-3 py-2 w-full text-destructive hover:bg-destructive/10 rounded-md transition-colors">
233
+ <LogOut className="h-5 w-5" />
234
+ <span>Sign out</span>
235
+ </button>
236
+ </div>
237
+ </WakaDrawer>
238
+ </>
239
+ )
240
+ },
241
+ }
242
+
243
+ export const FilterPanel: Story = {
244
+ render: () => {
245
+ const [open, setOpen] = React.useState(false)
246
+ const [filters, setFilters] = React.useState({
247
+ category: '',
248
+ priceRange: [0, 100],
249
+ inStock: false,
250
+ })
251
+
252
+ return (
253
+ <>
254
+ <Button variant="outline" onClick={() => setOpen(true)}>
255
+ <Filter className="h-4 w-4 mr-2" />
256
+ Filters
257
+ </Button>
258
+ <WakaDrawer
259
+ open={open}
260
+ onOpenChange={setOpen}
261
+ position="right"
262
+ title="Filters"
263
+ description="Refine your search results"
264
+ footer={
265
+ <>
266
+ <Button
267
+ variant="outline"
268
+ onClick={() =>
269
+ setFilters({ category: '', priceRange: [0, 100], inStock: false })
270
+ }
271
+ >
272
+ Reset
273
+ </Button>
274
+ <Button onClick={() => setOpen(false)}>Apply Filters</Button>
275
+ </>
276
+ }
277
+ >
278
+ <div className="space-y-6">
279
+ <div>
280
+ <label className="text-sm font-medium">Category</label>
281
+ <select
282
+ className="w-full mt-1 px-3 py-2 border rounded-md"
283
+ value={filters.category}
284
+ onChange={(e) =>
285
+ setFilters({ ...filters, category: e.target.value })
286
+ }
287
+ >
288
+ <option value="">All Categories</option>
289
+ <option value="electronics">Electronics</option>
290
+ <option value="clothing">Clothing</option>
291
+ <option value="books">Books</option>
292
+ <option value="home">Home & Garden</option>
293
+ </select>
294
+ </div>
295
+ <div>
296
+ <label className="text-sm font-medium">Price Range</label>
297
+ <div className="flex items-center gap-2 mt-1">
298
+ <input
299
+ type="number"
300
+ className="w-full px-3 py-2 border rounded-md"
301
+ placeholder="Min"
302
+ value={filters.priceRange[0]}
303
+ onChange={(e) =>
304
+ setFilters({
305
+ ...filters,
306
+ priceRange: [Number(e.target.value), filters.priceRange[1]],
307
+ })
308
+ }
309
+ />
310
+ <span>-</span>
311
+ <input
312
+ type="number"
313
+ className="w-full px-3 py-2 border rounded-md"
314
+ placeholder="Max"
315
+ value={filters.priceRange[1]}
316
+ onChange={(e) =>
317
+ setFilters({
318
+ ...filters,
319
+ priceRange: [filters.priceRange[0], Number(e.target.value)],
320
+ })
321
+ }
322
+ />
323
+ </div>
324
+ </div>
325
+ <div className="flex items-center gap-2">
326
+ <input
327
+ type="checkbox"
328
+ id="inStock"
329
+ checked={filters.inStock}
330
+ onChange={(e) =>
331
+ setFilters({ ...filters, inStock: e.target.checked })
332
+ }
333
+ />
334
+ <label htmlFor="inStock" className="text-sm">
335
+ Only show in-stock items
336
+ </label>
337
+ </div>
338
+ </div>
339
+ </WakaDrawer>
340
+ </>
341
+ )
342
+ },
343
+ }
344
+
345
+ export const WithHook: Story = {
346
+ render: () => {
347
+ const drawer = useDrawer({
348
+ onOpen: () => console.log('Drawer opened'),
349
+ onClose: () => console.log('Drawer closed'),
350
+ })
351
+
352
+ return (
353
+ <div className="space-y-4">
354
+ <div className="flex gap-2">
355
+ <Button onClick={drawer.openDrawer}>Open</Button>
356
+ <Button variant="outline" onClick={drawer.toggleDrawer}>Toggle</Button>
357
+ </div>
358
+ <WakaDrawer
359
+ {...drawer}
360
+ title="Using useDrawer Hook"
361
+ description="This drawer is controlled by the useDrawer hook."
362
+ >
363
+ <p>The drawer state is managed by the hook.</p>
364
+ <Button
365
+ variant="outline"
366
+ className="mt-4"
367
+ onClick={drawer.closeDrawer}
368
+ >
369
+ Close Drawer
370
+ </Button>
371
+ </WakaDrawer>
372
+ </div>
373
+ )
374
+ },
375
+ }
376
+
377
+ export const NoOverlay: Story = {
378
+ render: () => {
379
+ const [open, setOpen] = React.useState(false)
380
+ return (
381
+ <>
382
+ <Button onClick={() => setOpen(true)}>Open Without Overlay</Button>
383
+ <WakaDrawer
384
+ open={open}
385
+ onOpenChange={setOpen}
386
+ showOverlay={false}
387
+ title="No Overlay"
388
+ description="This drawer doesn't have a backdrop overlay."
389
+ >
390
+ <p>You can still interact with the page content behind.</p>
391
+ </WakaDrawer>
392
+ </>
393
+ )
394
+ },
395
+ }
396
+
397
+ export const CustomContent: Story = {
398
+ render: () => {
399
+ const [open, setOpen] = React.useState(false)
400
+ return (
401
+ <>
402
+ <Button onClick={() => setOpen(true)}>Open Custom Drawer</Button>
403
+ <WakaDrawer
404
+ open={open}
405
+ onOpenChange={setOpen}
406
+ size="lg"
407
+ >
408
+ <div className="h-full flex flex-col">
409
+ <div className="p-4 border-b flex items-center justify-between">
410
+ <h2 className="font-semibold text-lg">Custom Layout</h2>
411
+ <Button variant="ghost" size="icon" onClick={() => setOpen(false)}>
412
+ <X className="h-4 w-4" />
413
+ </Button>
414
+ </div>
415
+ <div className="flex-1 overflow-auto p-4">
416
+ <p className="text-muted-foreground mb-4">
417
+ This drawer uses custom layout without the built-in header/footer.
418
+ </p>
419
+ <div className="grid grid-cols-2 gap-4">
420
+ {Array.from({ length: 6 }).map((_, i) => (
421
+ <div key={i} className="border rounded-lg p-4 bg-muted/30">
422
+ Card {i + 1}
423
+ </div>
424
+ ))}
425
+ </div>
426
+ </div>
427
+ <div className="p-4 border-t bg-muted/30">
428
+ <Button className="w-full" onClick={() => setOpen(false)}>
429
+ Done
430
+ </Button>
431
+ </div>
432
+ </div>
433
+ </WakaDrawer>
434
+ </>
435
+ )
436
+ },
437
+ }
@@ -0,0 +1,263 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { WakaEnvVarEditor, defaultEnvVariables } from './index'
3
+ import type { EnvVariable } from './index'
4
+ import * as React from 'react'
5
+
6
+ const simpleVariables: EnvVariable[] = [
7
+ { key: 'NODE_ENV', value: 'development' },
8
+ { key: 'PORT', value: '3000' },
9
+ { key: 'DEBUG', value: 'true' },
10
+ ]
11
+
12
+ const productionVariables: EnvVariable[] = [
13
+ { key: 'NODE_ENV', value: 'production', isRequired: true },
14
+ { key: 'DATABASE_URL', value: 'postgresql://prod-user:secure-pass@db.example.com:5432/production', isSecret: true, isRequired: true },
15
+ { key: 'REDIS_URL', value: 'redis://redis.example.com:6379', isSecret: true },
16
+ { key: 'API_KEY', value: 'pk_live_1234567890abcdefghijklmnopqrstuvwxyz', isSecret: true, isRequired: true },
17
+ { key: 'SECRET_KEY', value: 'sk_live_9876543210zyxwvutsrqponmlkjihgfedcba', isSecret: true, isRequired: true },
18
+ { key: 'LOG_LEVEL', value: 'warn' },
19
+ { key: 'SENTRY_DSN', value: 'https://abc123@sentry.io/12345', isSecret: true },
20
+ { key: 'ENABLE_METRICS', value: 'true' },
21
+ ]
22
+
23
+ const variablesWithSources: EnvVariable[] = [
24
+ { key: 'DATABASE_URL', value: 'postgresql://localhost:5432/dev', source: 'local', isSecret: true },
25
+ { key: 'DATABASE_URL', value: 'postgresql://staging-db:5432/staging', source: 'staging', isSecret: true },
26
+ { key: 'DATABASE_URL', value: 'postgresql://prod-db:5432/production', source: 'production', isSecret: true },
27
+ { key: 'API_KEY', value: 'dev_key_123', source: 'local', isSecret: true },
28
+ { key: 'API_KEY', value: 'staging_key_456', source: 'staging', isSecret: true },
29
+ { key: 'API_KEY', value: 'prod_key_789', source: 'production', isSecret: true },
30
+ ]
31
+
32
+ const incompleteVariables: EnvVariable[] = [
33
+ { key: 'NODE_ENV', value: 'production', isRequired: true },
34
+ { key: 'DATABASE_URL', value: '', isSecret: true, isRequired: true },
35
+ { key: 'API_KEY', value: '', isSecret: true, isRequired: true },
36
+ { key: 'OPTIONAL_VAR', value: '' },
37
+ ]
38
+
39
+ const meta: Meta<typeof WakaEnvVarEditor> = {
40
+ title: 'Components/DevOps/WakaEnvVarEditor',
41
+ component: WakaEnvVarEditor,
42
+ parameters: {
43
+ layout: 'centered',
44
+ docs: {
45
+ description: {
46
+ component: 'An environment variable editor with secret masking, import/export, search, and validation for required variables.',
47
+ },
48
+ },
49
+ },
50
+ tags: ['autodocs'],
51
+ argTypes: {
52
+ readOnly: {
53
+ control: 'boolean',
54
+ description: 'Read-only mode',
55
+ },
56
+ showSource: {
57
+ control: 'boolean',
58
+ description: 'Show source column',
59
+ },
60
+ },
61
+ }
62
+
63
+ export default meta
64
+ type Story = StoryObj<typeof WakaEnvVarEditor>
65
+
66
+ export const Default: Story = {
67
+ args: {
68
+ variables: defaultEnvVariables,
69
+ },
70
+ render: (args) => (
71
+ <div className="w-[700px] h-[500px]">
72
+ <WakaEnvVarEditor
73
+ {...args}
74
+ onChange={(vars) => console.log('Change:', vars)}
75
+ onSave={(vars) => console.log('Save:', vars)}
76
+ onExport={() => console.log('Export')}
77
+ />
78
+ </div>
79
+ ),
80
+ }
81
+
82
+ export const Simple: Story = {
83
+ render: () => (
84
+ <div className="w-[700px] h-[400px]">
85
+ <WakaEnvVarEditor
86
+ variables={simpleVariables}
87
+ title="Development Variables"
88
+ onChange={(vars) => console.log('Change:', vars)}
89
+ onSave={(vars) => console.log('Save:', vars)}
90
+ />
91
+ </div>
92
+ ),
93
+ }
94
+
95
+ export const Production: Story = {
96
+ render: () => (
97
+ <div className="w-[700px] h-[500px]">
98
+ <WakaEnvVarEditor
99
+ variables={productionVariables}
100
+ title="Production Environment"
101
+ onChange={(vars) => console.log('Change:', vars)}
102
+ onSave={(vars) => console.log('Save:', vars)}
103
+ onExport={() => console.log('Export')}
104
+ />
105
+ </div>
106
+ ),
107
+ }
108
+
109
+ export const WithSources: Story = {
110
+ render: () => (
111
+ <div className="w-[750px] h-[500px]">
112
+ <p className="text-sm text-muted-foreground mb-4">
113
+ Variables from different environments
114
+ </p>
115
+ <WakaEnvVarEditor
116
+ variables={variablesWithSources}
117
+ showSource
118
+ title="Multi-Environment Variables"
119
+ onChange={(vars) => console.log('Change:', vars)}
120
+ />
121
+ </div>
122
+ ),
123
+ }
124
+
125
+ export const MissingRequired: Story = {
126
+ render: () => (
127
+ <div className="w-[700px] h-[400px]">
128
+ <p className="text-sm text-muted-foreground mb-4">
129
+ Shows warning indicators for empty required variables
130
+ </p>
131
+ <WakaEnvVarEditor
132
+ variables={incompleteVariables}
133
+ title="Incomplete Configuration"
134
+ onChange={(vars) => console.log('Change:', vars)}
135
+ onSave={(vars) => console.log('Save:', vars)}
136
+ />
137
+ </div>
138
+ ),
139
+ }
140
+
141
+ export const ReadOnly: Story = {
142
+ render: () => (
143
+ <div className="w-[700px] h-[500px]">
144
+ <WakaEnvVarEditor
145
+ variables={productionVariables}
146
+ readOnly
147
+ title="Production Variables (Read Only)"
148
+ onExport={() => console.log('Export')}
149
+ />
150
+ </div>
151
+ ),
152
+ }
153
+
154
+ export const Interactive: Story = {
155
+ render: () => {
156
+ const [variables, setVariables] = React.useState<EnvVariable[]>(simpleVariables)
157
+ const [saved, setSaved] = React.useState(false)
158
+
159
+ const handleSave = (vars: EnvVariable[]) => {
160
+ console.log('Saving:', vars)
161
+ setSaved(true)
162
+ setTimeout(() => setSaved(false), 2000)
163
+ }
164
+
165
+ return (
166
+ <div className="w-[700px] h-[500px]">
167
+ {saved && (
168
+ <div className="mb-4 p-2 bg-green-500/10 border border-green-500/30 rounded text-sm text-green-500">
169
+ Variables saved successfully!
170
+ </div>
171
+ )}
172
+ <WakaEnvVarEditor
173
+ variables={variables}
174
+ onChange={setVariables}
175
+ onSave={handleSave}
176
+ onExport={() => {
177
+ const content = variables
178
+ .map((v) => `${v.key}=${v.value}`)
179
+ .join('\n')
180
+ console.log('Export content:\n', content)
181
+ alert('Check console for exported content')
182
+ }}
183
+ title="Interactive Editor"
184
+ />
185
+ </div>
186
+ )
187
+ },
188
+ }
189
+
190
+ export const Empty: Story = {
191
+ render: () => (
192
+ <div className="w-[700px] h-[400px]">
193
+ <WakaEnvVarEditor
194
+ variables={[]}
195
+ title="No Variables"
196
+ onChange={(vars) => console.log('Change:', vars)}
197
+ onSave={(vars) => console.log('Save:', vars)}
198
+ />
199
+ </div>
200
+ ),
201
+ }
202
+
203
+ export const ManyVariables: Story = {
204
+ render: () => {
205
+ const manyVars: EnvVariable[] = Array.from({ length: 30 }, (_, i) => ({
206
+ key: `VARIABLE_${i + 1}`,
207
+ value: `value_${i + 1}`,
208
+ isSecret: i % 3 === 0,
209
+ isRequired: i % 5 === 0,
210
+ }))
211
+
212
+ return (
213
+ <div className="w-[700px] h-[600px]">
214
+ <WakaEnvVarEditor
215
+ variables={manyVars}
216
+ title="Large Configuration"
217
+ onChange={(vars) => console.log('Change:', vars)}
218
+ onSave={(vars) => console.log('Save:', vars)}
219
+ onExport={() => console.log('Export')}
220
+ />
221
+ </div>
222
+ )
223
+ },
224
+ }
225
+
226
+ export const ConfigurationPanel: Story = {
227
+ render: () => {
228
+ const [variables, setVariables] = React.useState<EnvVariable[]>(defaultEnvVariables)
229
+
230
+ return (
231
+ <div className="p-6 rounded-xl border bg-card">
232
+ <div className="flex items-center justify-between mb-6">
233
+ <div>
234
+ <h2 className="text-xl font-bold">Application Configuration</h2>
235
+ <p className="text-sm text-muted-foreground">Environment: Production</p>
236
+ </div>
237
+ <div className="flex items-center gap-2 text-sm">
238
+ <span className="text-muted-foreground">{variables.length} variables</span>
239
+ <span className="text-muted-foreground">•</span>
240
+ <span className="text-yellow-500">{variables.filter((v) => v.isSecret).length} secrets</span>
241
+ </div>
242
+ </div>
243
+
244
+ <div className="w-[650px]">
245
+ <WakaEnvVarEditor
246
+ variables={variables}
247
+ onChange={setVariables}
248
+ onSave={(vars) => {
249
+ console.log('Saving configuration:', vars)
250
+ alert(`Saved ${vars.length} variables!`)
251
+ }}
252
+ onExport={() => {
253
+ const content = variables
254
+ .map((v) => `${v.key}=${v.isSecret ? '***' : v.value}`)
255
+ .join('\n')
256
+ console.log('Export (secrets masked):\n', content)
257
+ }}
258
+ />
259
+ </div>
260
+ </div>
261
+ )
262
+ },
263
+ }