@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,410 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { WakaErrorShake, WakaFormFieldShake, useErrorShake } from './index'
|
|
3
|
+
import type { WakaErrorShakeRef } from './index'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
import { Button } from '../button'
|
|
6
|
+
import { AlertCircle } from 'lucide-react'
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof WakaErrorShake> = {
|
|
9
|
+
title: 'Components/Effects/WakaErrorShake',
|
|
10
|
+
component: WakaErrorShake,
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: 'centered',
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component: 'An error shake animation component for visual feedback on errors or invalid input.',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
tags: ['autodocs'],
|
|
20
|
+
argTypes: {
|
|
21
|
+
intensity: {
|
|
22
|
+
control: 'select',
|
|
23
|
+
options: ['light', 'medium', 'strong'],
|
|
24
|
+
description: 'Shake intensity',
|
|
25
|
+
},
|
|
26
|
+
duration: {
|
|
27
|
+
control: { type: 'range', min: 200, max: 1000, step: 50 },
|
|
28
|
+
description: 'Animation duration in ms',
|
|
29
|
+
},
|
|
30
|
+
glitchEffect: {
|
|
31
|
+
control: 'boolean',
|
|
32
|
+
description: 'Enable glitch/distortion effect',
|
|
33
|
+
},
|
|
34
|
+
flashEffect: {
|
|
35
|
+
control: 'boolean',
|
|
36
|
+
description: 'Enable red flash effect',
|
|
37
|
+
},
|
|
38
|
+
flashColor: {
|
|
39
|
+
control: 'color',
|
|
40
|
+
description: 'Flash color',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default meta
|
|
46
|
+
type Story = StoryObj<typeof WakaErrorShake>
|
|
47
|
+
|
|
48
|
+
export const Default: Story = {
|
|
49
|
+
args: {},
|
|
50
|
+
render: (args) => {
|
|
51
|
+
const [trigger, setTrigger] = React.useState(false)
|
|
52
|
+
|
|
53
|
+
const handleShake = () => {
|
|
54
|
+
setTrigger(true)
|
|
55
|
+
setTimeout(() => setTrigger(false), 100)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className="space-y-6">
|
|
60
|
+
<WakaErrorShake trigger={trigger}>
|
|
61
|
+
<div className="p-6 border rounded-lg bg-card w-[300px]">
|
|
62
|
+
<h3 className="font-semibold mb-2">Error Content</h3>
|
|
63
|
+
<p className="text-sm text-muted-foreground">
|
|
64
|
+
This content will shake when triggered.
|
|
65
|
+
</p>
|
|
66
|
+
</div>
|
|
67
|
+
</WakaErrorShake>
|
|
68
|
+
<Button variant="destructive" onClick={handleShake}>
|
|
69
|
+
Trigger Shake
|
|
70
|
+
</Button>
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const Intensities: Story = {
|
|
77
|
+
render: () => {
|
|
78
|
+
const [triggers, setTriggers] = React.useState({
|
|
79
|
+
light: false,
|
|
80
|
+
medium: false,
|
|
81
|
+
strong: false,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const handleTrigger = (intensity: keyof typeof triggers) => {
|
|
85
|
+
setTriggers((prev) => ({ ...prev, [intensity]: true }))
|
|
86
|
+
setTimeout(() => setTriggers((prev) => ({ ...prev, [intensity]: false })), 100)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className="flex gap-6">
|
|
91
|
+
{(['light', 'medium', 'strong'] as const).map((intensity) => (
|
|
92
|
+
<div key={intensity} className="flex flex-col items-center gap-4">
|
|
93
|
+
<WakaErrorShake trigger={triggers[intensity]} intensity={intensity}>
|
|
94
|
+
<div className="p-4 border rounded-lg bg-card w-[150px] text-center">
|
|
95
|
+
<span className="capitalize">{intensity}</span>
|
|
96
|
+
</div>
|
|
97
|
+
</WakaErrorShake>
|
|
98
|
+
<Button size="sm" variant="outline" onClick={() => handleTrigger(intensity)}>
|
|
99
|
+
Shake
|
|
100
|
+
</Button>
|
|
101
|
+
</div>
|
|
102
|
+
))}
|
|
103
|
+
</div>
|
|
104
|
+
)
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const WithGlitchEffect: Story = {
|
|
109
|
+
render: () => {
|
|
110
|
+
const [trigger, setTrigger] = React.useState(false)
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div className="space-y-6">
|
|
114
|
+
<WakaErrorShake trigger={trigger} glitchEffect intensity="strong">
|
|
115
|
+
<div className="p-6 border rounded-lg bg-card w-[300px]">
|
|
116
|
+
<h3 className="font-semibold mb-2 text-destructive">Critical Error</h3>
|
|
117
|
+
<p className="text-sm text-muted-foreground">
|
|
118
|
+
This includes a glitch/distortion effect.
|
|
119
|
+
</p>
|
|
120
|
+
</div>
|
|
121
|
+
</WakaErrorShake>
|
|
122
|
+
<Button
|
|
123
|
+
variant="destructive"
|
|
124
|
+
onClick={() => {
|
|
125
|
+
setTrigger(true)
|
|
126
|
+
setTimeout(() => setTrigger(false), 100)
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
Trigger Glitch
|
|
130
|
+
</Button>
|
|
131
|
+
</div>
|
|
132
|
+
)
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const NoFlashEffect: Story = {
|
|
137
|
+
render: () => {
|
|
138
|
+
const [trigger, setTrigger] = React.useState(false)
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<div className="space-y-6">
|
|
142
|
+
<WakaErrorShake trigger={trigger} flashEffect={false}>
|
|
143
|
+
<div className="p-6 border rounded-lg bg-card w-[300px]">
|
|
144
|
+
<h3 className="font-semibold mb-2">No Flash</h3>
|
|
145
|
+
<p className="text-sm text-muted-foreground">
|
|
146
|
+
Only shake animation, no red flash.
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
</WakaErrorShake>
|
|
150
|
+
<Button
|
|
151
|
+
variant="outline"
|
|
152
|
+
onClick={() => {
|
|
153
|
+
setTrigger(true)
|
|
154
|
+
setTimeout(() => setTrigger(false), 100)
|
|
155
|
+
}}
|
|
156
|
+
>
|
|
157
|
+
Shake Only
|
|
158
|
+
</Button>
|
|
159
|
+
</div>
|
|
160
|
+
)
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const CustomFlashColor: Story = {
|
|
165
|
+
render: () => {
|
|
166
|
+
const [trigger, setTrigger] = React.useState(false)
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<div className="space-y-6">
|
|
170
|
+
<WakaErrorShake trigger={trigger} flashColor="rgb(251, 146, 60)">
|
|
171
|
+
<div className="p-6 border rounded-lg bg-card w-[300px]">
|
|
172
|
+
<h3 className="font-semibold mb-2">Warning Flash</h3>
|
|
173
|
+
<p className="text-sm text-muted-foreground">
|
|
174
|
+
Orange flash color for warnings.
|
|
175
|
+
</p>
|
|
176
|
+
</div>
|
|
177
|
+
</WakaErrorShake>
|
|
178
|
+
<Button
|
|
179
|
+
className="bg-orange-500 hover:bg-orange-600"
|
|
180
|
+
onClick={() => {
|
|
181
|
+
setTrigger(true)
|
|
182
|
+
setTimeout(() => setTrigger(false), 100)
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
Trigger Warning
|
|
186
|
+
</Button>
|
|
187
|
+
</div>
|
|
188
|
+
)
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export const FormFieldShake: Story = {
|
|
193
|
+
render: () => {
|
|
194
|
+
const [error, setError] = React.useState(false)
|
|
195
|
+
const [value, setValue] = React.useState('')
|
|
196
|
+
|
|
197
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
198
|
+
e.preventDefault()
|
|
199
|
+
if (!value.includes('@')) {
|
|
200
|
+
setError(true)
|
|
201
|
+
} else {
|
|
202
|
+
setError(false)
|
|
203
|
+
alert('Submitted!')
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<form onSubmit={handleSubmit} className="w-[300px] space-y-4">
|
|
209
|
+
<div>
|
|
210
|
+
<label className="text-sm font-medium">Email</label>
|
|
211
|
+
<WakaFormFieldShake hasError={error}>
|
|
212
|
+
<input
|
|
213
|
+
type="text"
|
|
214
|
+
value={value}
|
|
215
|
+
onChange={(e) => {
|
|
216
|
+
setValue(e.target.value)
|
|
217
|
+
if (error) setError(false)
|
|
218
|
+
}}
|
|
219
|
+
className={`w-full mt-1 px-3 py-2 border rounded-md ${
|
|
220
|
+
error ? 'border-destructive' : ''
|
|
221
|
+
}`}
|
|
222
|
+
placeholder="Enter your email"
|
|
223
|
+
/>
|
|
224
|
+
</WakaFormFieldShake>
|
|
225
|
+
{error && (
|
|
226
|
+
<p className="text-sm text-destructive mt-1 flex items-center gap-1">
|
|
227
|
+
<AlertCircle className="h-3 w-3" />
|
|
228
|
+
Please enter a valid email
|
|
229
|
+
</p>
|
|
230
|
+
)}
|
|
231
|
+
</div>
|
|
232
|
+
<Button type="submit" className="w-full">
|
|
233
|
+
Submit
|
|
234
|
+
</Button>
|
|
235
|
+
</form>
|
|
236
|
+
)
|
|
237
|
+
},
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export const WithRef: Story = {
|
|
241
|
+
render: () => {
|
|
242
|
+
const ref = React.useRef<WakaErrorShakeRef>(null)
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<div className="space-y-6">
|
|
246
|
+
<WakaErrorShake ref={ref}>
|
|
247
|
+
<div className="p-6 border rounded-lg bg-card w-[300px]">
|
|
248
|
+
<h3 className="font-semibold mb-2">Controlled via Ref</h3>
|
|
249
|
+
<p className="text-sm text-muted-foreground">
|
|
250
|
+
Use the ref to trigger shake programmatically.
|
|
251
|
+
</p>
|
|
252
|
+
</div>
|
|
253
|
+
</WakaErrorShake>
|
|
254
|
+
<div className="flex gap-2">
|
|
255
|
+
<Button variant="destructive" onClick={() => ref.current?.shake()}>
|
|
256
|
+
Shake
|
|
257
|
+
</Button>
|
|
258
|
+
<Button variant="outline" onClick={() => ref.current?.reset()}>
|
|
259
|
+
Reset
|
|
260
|
+
</Button>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
)
|
|
264
|
+
},
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const WithHook: Story = {
|
|
268
|
+
render: () => {
|
|
269
|
+
const { shake, ShakeWrapper } = useErrorShake({
|
|
270
|
+
intensity: 'medium',
|
|
271
|
+
onComplete: () => console.log('Shake complete!'),
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<div className="space-y-6">
|
|
276
|
+
<ShakeWrapper>
|
|
277
|
+
<div className="p-6 border rounded-lg bg-card w-[300px]">
|
|
278
|
+
<h3 className="font-semibold mb-2">Using Hook</h3>
|
|
279
|
+
<p className="text-sm text-muted-foreground">
|
|
280
|
+
Controlled via useErrorShake hook.
|
|
281
|
+
</p>
|
|
282
|
+
</div>
|
|
283
|
+
</ShakeWrapper>
|
|
284
|
+
<Button variant="destructive" onClick={shake}>
|
|
285
|
+
Trigger via Hook
|
|
286
|
+
</Button>
|
|
287
|
+
</div>
|
|
288
|
+
)
|
|
289
|
+
},
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export const LoginFormExample: Story = {
|
|
293
|
+
render: () => {
|
|
294
|
+
const [username, setUsername] = React.useState('')
|
|
295
|
+
const [password, setPassword] = React.useState('')
|
|
296
|
+
const [errors, setErrors] = React.useState({ username: false, password: false })
|
|
297
|
+
const [loginError, setLoginError] = React.useState(false)
|
|
298
|
+
|
|
299
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
300
|
+
e.preventDefault()
|
|
301
|
+
|
|
302
|
+
const newErrors = {
|
|
303
|
+
username: !username,
|
|
304
|
+
password: !password,
|
|
305
|
+
}
|
|
306
|
+
setErrors(newErrors)
|
|
307
|
+
|
|
308
|
+
if (!newErrors.username && !newErrors.password) {
|
|
309
|
+
if (username !== 'admin' || password !== 'password') {
|
|
310
|
+
setLoginError(true)
|
|
311
|
+
setTimeout(() => setLoginError(false), 100)
|
|
312
|
+
} else {
|
|
313
|
+
alert('Login successful!')
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return (
|
|
319
|
+
<WakaErrorShake trigger={loginError} intensity="medium">
|
|
320
|
+
<form onSubmit={handleSubmit} className="w-[320px] p-6 border rounded-lg bg-card">
|
|
321
|
+
<h2 className="text-xl font-bold mb-6 text-center">Login</h2>
|
|
322
|
+
<div className="space-y-4">
|
|
323
|
+
<div>
|
|
324
|
+
<label className="text-sm font-medium">Username</label>
|
|
325
|
+
<WakaFormFieldShake hasError={errors.username}>
|
|
326
|
+
<input
|
|
327
|
+
type="text"
|
|
328
|
+
value={username}
|
|
329
|
+
onChange={(e) => {
|
|
330
|
+
setUsername(e.target.value)
|
|
331
|
+
setErrors((p) => ({ ...p, username: false }))
|
|
332
|
+
}}
|
|
333
|
+
className={`w-full mt-1 px-3 py-2 border rounded-md ${
|
|
334
|
+
errors.username ? 'border-destructive' : ''
|
|
335
|
+
}`}
|
|
336
|
+
placeholder="Enter username"
|
|
337
|
+
/>
|
|
338
|
+
</WakaFormFieldShake>
|
|
339
|
+
{errors.username && (
|
|
340
|
+
<p className="text-xs text-destructive mt-1">Username is required</p>
|
|
341
|
+
)}
|
|
342
|
+
</div>
|
|
343
|
+
<div>
|
|
344
|
+
<label className="text-sm font-medium">Password</label>
|
|
345
|
+
<WakaFormFieldShake hasError={errors.password}>
|
|
346
|
+
<input
|
|
347
|
+
type="password"
|
|
348
|
+
value={password}
|
|
349
|
+
onChange={(e) => {
|
|
350
|
+
setPassword(e.target.value)
|
|
351
|
+
setErrors((p) => ({ ...p, password: false }))
|
|
352
|
+
}}
|
|
353
|
+
className={`w-full mt-1 px-3 py-2 border rounded-md ${
|
|
354
|
+
errors.password ? 'border-destructive' : ''
|
|
355
|
+
}`}
|
|
356
|
+
placeholder="Enter password"
|
|
357
|
+
/>
|
|
358
|
+
</WakaFormFieldShake>
|
|
359
|
+
{errors.password && (
|
|
360
|
+
<p className="text-xs text-destructive mt-1">Password is required</p>
|
|
361
|
+
)}
|
|
362
|
+
</div>
|
|
363
|
+
<Button type="submit" className="w-full">
|
|
364
|
+
Sign In
|
|
365
|
+
</Button>
|
|
366
|
+
</div>
|
|
367
|
+
<p className="text-xs text-muted-foreground mt-4 text-center">
|
|
368
|
+
Try: admin / password
|
|
369
|
+
</p>
|
|
370
|
+
</form>
|
|
371
|
+
</WakaErrorShake>
|
|
372
|
+
)
|
|
373
|
+
},
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export const WithCallback: Story = {
|
|
377
|
+
render: () => {
|
|
378
|
+
const [trigger, setTrigger] = React.useState(false)
|
|
379
|
+
const [message, setMessage] = React.useState('')
|
|
380
|
+
|
|
381
|
+
return (
|
|
382
|
+
<div className="space-y-6">
|
|
383
|
+
<WakaErrorShake
|
|
384
|
+
trigger={trigger}
|
|
385
|
+
onComplete={() => setMessage('Shake animation completed!')}
|
|
386
|
+
>
|
|
387
|
+
<div className="p-6 border rounded-lg bg-card w-[300px]">
|
|
388
|
+
<h3 className="font-semibold mb-2">With Callback</h3>
|
|
389
|
+
<p className="text-sm text-muted-foreground">
|
|
390
|
+
Callback fires when animation ends.
|
|
391
|
+
</p>
|
|
392
|
+
</div>
|
|
393
|
+
</WakaErrorShake>
|
|
394
|
+
<Button
|
|
395
|
+
variant="destructive"
|
|
396
|
+
onClick={() => {
|
|
397
|
+
setMessage('')
|
|
398
|
+
setTrigger(true)
|
|
399
|
+
setTimeout(() => setTrigger(false), 100)
|
|
400
|
+
}}
|
|
401
|
+
>
|
|
402
|
+
Trigger
|
|
403
|
+
</Button>
|
|
404
|
+
{message && (
|
|
405
|
+
<p className="text-sm text-muted-foreground">{message}</p>
|
|
406
|
+
)}
|
|
407
|
+
</div>
|
|
408
|
+
)
|
|
409
|
+
},
|
|
410
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { WakaFileUpload } from './index'
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof WakaFileUpload> = {
|
|
6
|
+
title: 'Components/Forms/WakaFileUpload',
|
|
7
|
+
component: WakaFileUpload,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: 'A file upload component with drag & drop, preview, and progress tracking.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
tags: ['autodocs'],
|
|
17
|
+
argTypes: {
|
|
18
|
+
variant: {
|
|
19
|
+
control: 'select',
|
|
20
|
+
options: ['dropzone', 'button', 'inline'],
|
|
21
|
+
description: 'Display variant',
|
|
22
|
+
},
|
|
23
|
+
multiple: {
|
|
24
|
+
control: 'boolean',
|
|
25
|
+
description: 'Allow multiple files',
|
|
26
|
+
},
|
|
27
|
+
showPreview: {
|
|
28
|
+
control: 'boolean',
|
|
29
|
+
description: 'Show file preview',
|
|
30
|
+
},
|
|
31
|
+
showProgress: {
|
|
32
|
+
control: 'boolean',
|
|
33
|
+
description: 'Show upload progress',
|
|
34
|
+
},
|
|
35
|
+
disabled: {
|
|
36
|
+
control: 'boolean',
|
|
37
|
+
description: 'Disable upload',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default meta
|
|
43
|
+
type Story = StoryObj<typeof WakaFileUpload>
|
|
44
|
+
|
|
45
|
+
// Mock upload function
|
|
46
|
+
const mockUpload = (file: File, onProgress: (progress: number) => void): Promise<string> => {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
let progress = 0
|
|
49
|
+
const interval = setInterval(() => {
|
|
50
|
+
progress += 10
|
|
51
|
+
onProgress(progress)
|
|
52
|
+
if (progress >= 100) {
|
|
53
|
+
clearInterval(interval)
|
|
54
|
+
resolve(`https://example.com/uploads/${file.name}`)
|
|
55
|
+
}
|
|
56
|
+
}, 200)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const Default: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
label: 'Upload Files',
|
|
63
|
+
description: 'Drag and drop files here or click to browse',
|
|
64
|
+
},
|
|
65
|
+
render: (args) => (
|
|
66
|
+
<div className="w-[400px]">
|
|
67
|
+
<WakaFileUpload
|
|
68
|
+
{...args}
|
|
69
|
+
onUpload={mockUpload}
|
|
70
|
+
onComplete={(files) => console.log('Uploaded:', files)}
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const Dropzone: Story = {
|
|
77
|
+
render: () => (
|
|
78
|
+
<div className="w-[400px]">
|
|
79
|
+
<WakaFileUpload
|
|
80
|
+
variant="dropzone"
|
|
81
|
+
label="Documents"
|
|
82
|
+
placeholder="Drop your documents here"
|
|
83
|
+
onUpload={mockUpload}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const ButtonVariant: Story = {
|
|
90
|
+
render: () => (
|
|
91
|
+
<WakaFileUpload
|
|
92
|
+
variant="button"
|
|
93
|
+
placeholder="Upload File"
|
|
94
|
+
onUpload={mockUpload}
|
|
95
|
+
/>
|
|
96
|
+
),
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const InlineVariant: Story = {
|
|
100
|
+
render: () => (
|
|
101
|
+
<div className="w-[300px]">
|
|
102
|
+
<WakaFileUpload
|
|
103
|
+
variant="inline"
|
|
104
|
+
placeholder="Add attachment"
|
|
105
|
+
onUpload={mockUpload}
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const ImagesOnly: Story = {
|
|
112
|
+
render: () => (
|
|
113
|
+
<div className="w-[400px]">
|
|
114
|
+
<WakaFileUpload
|
|
115
|
+
label="Upload Images"
|
|
116
|
+
accept="image/*"
|
|
117
|
+
placeholder="Drop images here (PNG, JPG, GIF)"
|
|
118
|
+
onUpload={mockUpload}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const PDFOnly: Story = {
|
|
125
|
+
render: () => (
|
|
126
|
+
<div className="w-[400px]">
|
|
127
|
+
<WakaFileUpload
|
|
128
|
+
label="Upload PDF"
|
|
129
|
+
accept=".pdf"
|
|
130
|
+
placeholder="Drop PDF files here"
|
|
131
|
+
onUpload={mockUpload}
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
),
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export const MultipleFiles: Story = {
|
|
138
|
+
render: () => (
|
|
139
|
+
<div className="w-[400px]">
|
|
140
|
+
<WakaFileUpload
|
|
141
|
+
label="Upload Multiple Files"
|
|
142
|
+
multiple
|
|
143
|
+
maxFiles={5}
|
|
144
|
+
placeholder="Drop up to 5 files here"
|
|
145
|
+
onUpload={mockUpload}
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
),
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const WithSizeLimit: Story = {
|
|
152
|
+
render: () => (
|
|
153
|
+
<div className="w-[400px]">
|
|
154
|
+
<WakaFileUpload
|
|
155
|
+
label="Upload (Max 2MB)"
|
|
156
|
+
maxSize={2 * 1024 * 1024}
|
|
157
|
+
onUpload={mockUpload}
|
|
158
|
+
onError={(error) => console.error('Error:', error)}
|
|
159
|
+
/>
|
|
160
|
+
</div>
|
|
161
|
+
),
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const NoPreview: Story = {
|
|
165
|
+
render: () => (
|
|
166
|
+
<div className="w-[400px]">
|
|
167
|
+
<WakaFileUpload
|
|
168
|
+
label="Upload"
|
|
169
|
+
showPreview={false}
|
|
170
|
+
onUpload={mockUpload}
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
),
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export const NoProgress: Story = {
|
|
177
|
+
render: () => (
|
|
178
|
+
<div className="w-[400px]">
|
|
179
|
+
<WakaFileUpload
|
|
180
|
+
label="Quick Upload"
|
|
181
|
+
showProgress={false}
|
|
182
|
+
onUpload={mockUpload}
|
|
183
|
+
/>
|
|
184
|
+
</div>
|
|
185
|
+
),
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export const Disabled: Story = {
|
|
189
|
+
render: () => (
|
|
190
|
+
<div className="w-[400px]">
|
|
191
|
+
<WakaFileUpload
|
|
192
|
+
label="Upload (Disabled)"
|
|
193
|
+
disabled
|
|
194
|
+
/>
|
|
195
|
+
</div>
|
|
196
|
+
),
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const ProfilePicture: Story = {
|
|
200
|
+
render: () => (
|
|
201
|
+
<div className="w-[400px] space-y-4">
|
|
202
|
+
<h3 className="font-semibold">Profile Picture</h3>
|
|
203
|
+
<WakaFileUpload
|
|
204
|
+
accept="image/*"
|
|
205
|
+
maxSize={5 * 1024 * 1024}
|
|
206
|
+
maxFiles={1}
|
|
207
|
+
placeholder="Upload a profile picture (max 5MB)"
|
|
208
|
+
onUpload={mockUpload}
|
|
209
|
+
/>
|
|
210
|
+
</div>
|
|
211
|
+
),
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export const DocumentUpload: Story = {
|
|
215
|
+
render: () => (
|
|
216
|
+
<div className="w-[400px] space-y-4 p-4 border rounded-lg">
|
|
217
|
+
<div>
|
|
218
|
+
<h3 className="font-semibold">Document Submission</h3>
|
|
219
|
+
<p className="text-sm text-muted-foreground">
|
|
220
|
+
Upload your required documents
|
|
221
|
+
</p>
|
|
222
|
+
</div>
|
|
223
|
+
<WakaFileUpload
|
|
224
|
+
label="ID Document"
|
|
225
|
+
accept=".pdf,.jpg,.png"
|
|
226
|
+
maxSize={10 * 1024 * 1024}
|
|
227
|
+
placeholder="Upload ID (PDF, JPG, PNG)"
|
|
228
|
+
onUpload={mockUpload}
|
|
229
|
+
/>
|
|
230
|
+
<WakaFileUpload
|
|
231
|
+
label="Proof of Address"
|
|
232
|
+
accept=".pdf,.jpg,.png"
|
|
233
|
+
maxSize={10 * 1024 * 1024}
|
|
234
|
+
placeholder="Upload proof of address"
|
|
235
|
+
onUpload={mockUpload}
|
|
236
|
+
/>
|
|
237
|
+
</div>
|
|
238
|
+
),
|
|
239
|
+
}
|