sonance-brand-mcp 1.1.4 → 1.2.2

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 (73) hide show
  1. package/dist/assets/BRAND_GUIDELINES.md +0 -8
  2. package/dist/assets/components/accordion.stories.tsx +310 -0
  3. package/dist/assets/components/accordion.tsx +56 -30
  4. package/dist/assets/components/alert.stories.tsx +199 -0
  5. package/dist/assets/components/autocomplete.stories.tsx +307 -0
  6. package/dist/assets/components/autocomplete.tsx +28 -4
  7. package/dist/assets/components/avatar.stories.tsx +175 -0
  8. package/dist/assets/components/badge.stories.tsx +258 -0
  9. package/dist/assets/components/breadcrumbs.stories.tsx +175 -0
  10. package/dist/assets/components/button.stories.tsx +362 -0
  11. package/dist/assets/components/button.tsx +48 -3
  12. package/dist/assets/components/calendar.stories.tsx +247 -0
  13. package/dist/assets/components/card.stories.tsx +275 -0
  14. package/dist/assets/components/card.tsx +26 -1
  15. package/dist/assets/components/checkbox-group.stories.tsx +281 -0
  16. package/dist/assets/components/checkbox.stories.tsx +160 -0
  17. package/dist/assets/components/checkbox.tsx +32 -4
  18. package/dist/assets/components/code.stories.tsx +265 -0
  19. package/dist/assets/components/date-input.stories.tsx +278 -0
  20. package/dist/assets/components/date-input.tsx +24 -2
  21. package/dist/assets/components/date-picker.stories.tsx +337 -0
  22. package/dist/assets/components/date-picker.tsx +28 -4
  23. package/dist/assets/components/date-range-picker.stories.tsx +340 -0
  24. package/dist/assets/components/dialog.stories.tsx +285 -0
  25. package/dist/assets/components/divider.stories.tsx +176 -0
  26. package/dist/assets/components/drawer.stories.tsx +216 -0
  27. package/dist/assets/components/dropdown.stories.tsx +342 -0
  28. package/dist/assets/components/dropdown.tsx +55 -10
  29. package/dist/assets/components/form.stories.tsx +372 -0
  30. package/dist/assets/components/image.stories.tsx +348 -0
  31. package/dist/assets/components/input-otp.stories.tsx +336 -0
  32. package/dist/assets/components/input-otp.tsx +24 -2
  33. package/dist/assets/components/input.stories.tsx +223 -0
  34. package/dist/assets/components/input.tsx +27 -2
  35. package/dist/assets/components/kbd.stories.tsx +272 -0
  36. package/dist/assets/components/link.stories.tsx +199 -0
  37. package/dist/assets/components/link.tsx +50 -1
  38. package/dist/assets/components/listbox.stories.tsx +287 -0
  39. package/dist/assets/components/listbox.tsx +30 -7
  40. package/dist/assets/components/navbar.stories.tsx +218 -0
  41. package/dist/assets/components/number-input.stories.tsx +295 -0
  42. package/dist/assets/components/number-input.tsx +30 -8
  43. package/dist/assets/components/pagination.stories.tsx +280 -0
  44. package/dist/assets/components/pagination.tsx +45 -21
  45. package/dist/assets/components/popover.stories.tsx +219 -0
  46. package/dist/assets/components/progress.stories.tsx +153 -0
  47. package/dist/assets/components/radio-group.stories.tsx +187 -0
  48. package/dist/assets/components/radio-group.tsx +30 -6
  49. package/dist/assets/components/range-calendar.stories.tsx +334 -0
  50. package/dist/assets/components/scroll-shadow.stories.tsx +335 -0
  51. package/dist/assets/components/select.stories.tsx +192 -0
  52. package/dist/assets/components/select.tsx +54 -7
  53. package/dist/assets/components/skeleton.stories.tsx +166 -0
  54. package/dist/assets/components/slider.stories.tsx +145 -0
  55. package/dist/assets/components/slider.tsx +43 -8
  56. package/dist/assets/components/spacer.stories.tsx +216 -0
  57. package/dist/assets/components/spinner.stories.tsx +149 -0
  58. package/dist/assets/components/switch.stories.tsx +170 -0
  59. package/dist/assets/components/switch.tsx +29 -4
  60. package/dist/assets/components/table.stories.tsx +322 -0
  61. package/dist/assets/components/tabs.stories.tsx +306 -0
  62. package/dist/assets/components/tabs.tsx +25 -4
  63. package/dist/assets/components/textarea.stories.tsx +103 -0
  64. package/dist/assets/components/textarea.tsx +27 -3
  65. package/dist/assets/components/theme-toggle.stories.tsx +248 -0
  66. package/dist/assets/components/time-input.stories.tsx +365 -0
  67. package/dist/assets/components/time-input.tsx +25 -3
  68. package/dist/assets/components/toast.stories.tsx +195 -0
  69. package/dist/assets/components/tooltip.stories.tsx +226 -0
  70. package/dist/assets/components/user.stories.tsx +274 -0
  71. package/dist/assets/logo-manifest.json +0 -18
  72. package/dist/index.js +2142 -85
  73. package/package.json +1 -1
@@ -0,0 +1,348 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2
+ import { Image, ZoomImage, ImageGallery } from './image';
3
+
4
+ const meta: Meta<typeof Image> = {
5
+ title: 'Components/Data Display/Image',
6
+ component: Image,
7
+ tags: ['autodocs'],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component: 'An enhanced image component with loading states, fallbacks, and various display options.',
12
+ },
13
+ },
14
+ },
15
+ argTypes: {
16
+ aspectRatio: {
17
+ control: 'select',
18
+ options: ['auto', 'square', 'video', 'wide'],
19
+ },
20
+ objectFit: {
21
+ control: 'select',
22
+ options: ['contain', 'cover', 'fill', 'none', 'scale-down'],
23
+ },
24
+ radius: {
25
+ control: 'select',
26
+ options: ['none', 'sm', 'md', 'lg', 'full'],
27
+ },
28
+ },
29
+ };
30
+
31
+ export default meta;
32
+ type Story = StoryObj<typeof meta>;
33
+
34
+ export const Default: Story = {
35
+ render: () => (
36
+ <div className="w-64">
37
+ <Image
38
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop"
39
+ alt="Speaker system"
40
+ />
41
+ </div>
42
+ ),
43
+ };
44
+
45
+ export const AspectRatios: Story = {
46
+ render: () => (
47
+ <div className="grid grid-cols-2 gap-4 max-w-2xl">
48
+ <div>
49
+ <p className="text-xs text-foreground-muted mb-2">Auto</p>
50
+ <Image
51
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop"
52
+ alt="Auto aspect"
53
+ aspectRatio="auto"
54
+ className="w-full"
55
+ />
56
+ </div>
57
+ <div>
58
+ <p className="text-xs text-foreground-muted mb-2">Square</p>
59
+ <Image
60
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=400&fit=crop"
61
+ alt="Square aspect"
62
+ aspectRatio="square"
63
+ className="w-full"
64
+ />
65
+ </div>
66
+ <div>
67
+ <p className="text-xs text-foreground-muted mb-2">Video (16:9)</p>
68
+ <Image
69
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=800&h=450&fit=crop"
70
+ alt="Video aspect"
71
+ aspectRatio="video"
72
+ className="w-full"
73
+ />
74
+ </div>
75
+ <div>
76
+ <p className="text-xs text-foreground-muted mb-2">Wide (21:9)</p>
77
+ <Image
78
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=800&h=343&fit=crop"
79
+ alt="Wide aspect"
80
+ aspectRatio="wide"
81
+ className="w-full"
82
+ />
83
+ </div>
84
+ </div>
85
+ ),
86
+ };
87
+
88
+ export const ObjectFit: Story = {
89
+ render: () => (
90
+ <div className="grid grid-cols-3 gap-4 max-w-3xl">
91
+ <div>
92
+ <p className="text-xs text-foreground-muted mb-2">Cover</p>
93
+ <Image
94
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=200&fit=crop"
95
+ alt="Cover fit"
96
+ aspectRatio="square"
97
+ objectFit="cover"
98
+ className="w-full"
99
+ />
100
+ </div>
101
+ <div>
102
+ <p className="text-xs text-foreground-muted mb-2">Contain</p>
103
+ <Image
104
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=200&fit=crop"
105
+ alt="Contain fit"
106
+ aspectRatio="square"
107
+ objectFit="contain"
108
+ className="w-full"
109
+ />
110
+ </div>
111
+ <div>
112
+ <p className="text-xs text-foreground-muted mb-2">Fill</p>
113
+ <Image
114
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=200&fit=crop"
115
+ alt="Fill fit"
116
+ aspectRatio="square"
117
+ objectFit="fill"
118
+ className="w-full"
119
+ />
120
+ </div>
121
+ </div>
122
+ ),
123
+ };
124
+
125
+ export const BorderRadius: Story = {
126
+ render: () => (
127
+ <div className="flex gap-4 flex-wrap">
128
+ <div>
129
+ <p className="text-xs text-foreground-muted mb-2">None</p>
130
+ <Image
131
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=150&h=150&fit=crop"
132
+ alt="No radius"
133
+ aspectRatio="square"
134
+ radius="none"
135
+ className="w-24"
136
+ />
137
+ </div>
138
+ <div>
139
+ <p className="text-xs text-foreground-muted mb-2">Small</p>
140
+ <Image
141
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=150&h=150&fit=crop"
142
+ alt="Small radius"
143
+ aspectRatio="square"
144
+ radius="sm"
145
+ className="w-24"
146
+ />
147
+ </div>
148
+ <div>
149
+ <p className="text-xs text-foreground-muted mb-2">Medium</p>
150
+ <Image
151
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=150&h=150&fit=crop"
152
+ alt="Medium radius"
153
+ aspectRatio="square"
154
+ radius="md"
155
+ className="w-24"
156
+ />
157
+ </div>
158
+ <div>
159
+ <p className="text-xs text-foreground-muted mb-2">Large</p>
160
+ <Image
161
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=150&h=150&fit=crop"
162
+ alt="Large radius"
163
+ aspectRatio="square"
164
+ radius="lg"
165
+ className="w-24"
166
+ />
167
+ </div>
168
+ <div>
169
+ <p className="text-xs text-foreground-muted mb-2">Full</p>
170
+ <Image
171
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=150&h=150&fit=crop"
172
+ alt="Full radius"
173
+ aspectRatio="square"
174
+ radius="full"
175
+ className="w-24"
176
+ />
177
+ </div>
178
+ </div>
179
+ ),
180
+ };
181
+
182
+ export const ErrorState: Story = {
183
+ render: () => (
184
+ <div className="w-64">
185
+ <Image
186
+ src="https://invalid-url-that-will-fail.com/image.jpg"
187
+ alt="Failed image"
188
+ aspectRatio="video"
189
+ />
190
+ </div>
191
+ ),
192
+ };
193
+
194
+ export const CustomFallback: Story = {
195
+ render: () => (
196
+ <div className="w-64">
197
+ <Image
198
+ src="https://invalid-url-that-will-fail.com/image.jpg"
199
+ alt="Failed image"
200
+ aspectRatio="video"
201
+ fallback={
202
+ <div className="flex flex-col items-center gap-2 text-foreground-muted">
203
+ <span className="text-3xl">📷</span>
204
+ <span className="text-sm">Image not available</span>
205
+ </div>
206
+ }
207
+ />
208
+ </div>
209
+ ),
210
+ };
211
+
212
+ export const WithoutSkeleton: Story = {
213
+ render: () => (
214
+ <div className="w-64">
215
+ <Image
216
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop"
217
+ alt="No skeleton"
218
+ showSkeleton={false}
219
+ />
220
+ </div>
221
+ ),
222
+ };
223
+
224
+ export const ZoomOnHover: Story = {
225
+ render: () => (
226
+ <div className="w-64">
227
+ <ZoomImage
228
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=400&fit=crop"
229
+ alt="Zoom on hover"
230
+ aspectRatio="square"
231
+ zoomScale={1.15}
232
+ />
233
+ </div>
234
+ ),
235
+ };
236
+
237
+ export const Gallery: Story = {
238
+ render: () => (
239
+ <div className="max-w-2xl">
240
+ <ImageGallery
241
+ images={[
242
+ { src: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=400&fit=crop', alt: 'Product 1' },
243
+ { src: 'https://images.unsplash.com/photo-1545454675-3531b543be5d?w=400&h=400&fit=crop', alt: 'Product 2' },
244
+ { src: 'https://images.unsplash.com/photo-1558089687-f282ffcbc126?w=400&h=400&fit=crop', alt: 'Product 3' },
245
+ { src: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400&h=400&fit=crop', alt: 'Product 4' },
246
+ { src: 'https://images.unsplash.com/photo-1484704849700-f032a568e944?w=400&h=400&fit=crop', alt: 'Product 5' },
247
+ { src: 'https://images.unsplash.com/photo-1524678606370-a47ad25cb82a?w=400&h=400&fit=crop', alt: 'Product 6' },
248
+ ]}
249
+ columns={3}
250
+ gap="md"
251
+ />
252
+ </div>
253
+ ),
254
+ };
255
+
256
+ export const GalleryColumns: Story = {
257
+ render: () => {
258
+ const images = [
259
+ { src: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=400&fit=crop', alt: 'Image 1' },
260
+ { src: 'https://images.unsplash.com/photo-1545454675-3531b543be5d?w=400&h=400&fit=crop', alt: 'Image 2' },
261
+ { src: 'https://images.unsplash.com/photo-1558089687-f282ffcbc126?w=400&h=400&fit=crop', alt: 'Image 3' },
262
+ { src: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400&h=400&fit=crop', alt: 'Image 4' },
263
+ ];
264
+
265
+ return (
266
+ <div className="space-y-8">
267
+ <div>
268
+ <p className="text-xs text-foreground-muted mb-2">2 Columns</p>
269
+ <ImageGallery images={images} columns={2} />
270
+ </div>
271
+ <div>
272
+ <p className="text-xs text-foreground-muted mb-2">4 Columns</p>
273
+ <ImageGallery images={images} columns={4} />
274
+ </div>
275
+ </div>
276
+ );
277
+ },
278
+ };
279
+
280
+ export const ProductImage: Story = {
281
+ render: () => (
282
+ <div className="w-80 border border-border">
283
+ <Image
284
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=600&h=400&fit=crop"
285
+ alt="VP66 TL In-Wall Speaker"
286
+ aspectRatio="video"
287
+ />
288
+ <div className="p-4">
289
+ <h3 className="font-medium text-foreground">VP66 TL</h3>
290
+ <p className="text-sm text-foreground-muted">In-Wall Speaker</p>
291
+ <p className="text-lg font-medium text-foreground mt-2">$1,299.00</p>
292
+ </div>
293
+ </div>
294
+ ),
295
+ };
296
+
297
+ // Responsive Matrix - Mobile, Tablet, Desktop
298
+ export const ResponsiveMatrix: Story = {
299
+ render: () => (
300
+ <div className="space-y-8">
301
+ {/* Mobile */}
302
+ <div>
303
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
304
+ <div className="w-[375px] border border-dashed border-border p-4">
305
+ <Image
306
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop"
307
+ alt="Speaker"
308
+ aspectRatio="video"
309
+ />
310
+ </div>
311
+ </div>
312
+ {/* Tablet */}
313
+ <div>
314
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
315
+ <div className="w-[768px] border border-dashed border-border p-4">
316
+ <div className="grid grid-cols-2 gap-4">
317
+ <Image
318
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=400&fit=crop"
319
+ alt="Square"
320
+ aspectRatio="square"
321
+ />
322
+ <Image
323
+ src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=600&h=400&fit=crop"
324
+ alt="Video"
325
+ aspectRatio="video"
326
+ />
327
+ </div>
328
+ </div>
329
+ </div>
330
+ {/* Desktop */}
331
+ <div>
332
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
333
+ <div className="w-[1280px] border border-dashed border-border p-4">
334
+ <ImageGallery
335
+ images={[
336
+ { src: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=400&fit=crop', alt: 'Product 1' },
337
+ { src: 'https://images.unsplash.com/photo-1545454675-3531b543be5d?w=400&h=400&fit=crop', alt: 'Product 2' },
338
+ { src: 'https://images.unsplash.com/photo-1558089687-f282ffcbc126?w=400&h=400&fit=crop', alt: 'Product 3' },
339
+ { src: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400&h=400&fit=crop', alt: 'Product 4' },
340
+ ]}
341
+ columns={4}
342
+ gap="md"
343
+ />
344
+ </div>
345
+ </div>
346
+ </div>
347
+ ),
348
+ };
@@ -0,0 +1,336 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2
+ import { useState } from 'react';
3
+ import { InputOTP, InputOTPGrouped, type InputOTPState } from './input-otp';
4
+
5
+ const meta: Meta<typeof InputOTP> = {
6
+ title: 'Components/Forms/InputOTP',
7
+ component: InputOTP,
8
+ tags: ['autodocs'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component: 'A one-time password (OTP) input component for verification codes. Supports paste, arrow key navigation, and auto-focus on next input.',
13
+ },
14
+ },
15
+ },
16
+ argTypes: {
17
+ length: {
18
+ control: { type: 'number', min: 4, max: 8 },
19
+ description: 'Number of digits',
20
+ table: {
21
+ defaultValue: { summary: '6' },
22
+ },
23
+ },
24
+ label: {
25
+ control: 'text',
26
+ description: 'Input label',
27
+ },
28
+ error: {
29
+ control: 'text',
30
+ description: 'Error message',
31
+ },
32
+ disabled: {
33
+ control: 'boolean',
34
+ description: 'Disabled state',
35
+ },
36
+ autoFocus: {
37
+ control: 'boolean',
38
+ description: 'Auto-focus first input on mount',
39
+ },
40
+ state: {
41
+ control: 'select',
42
+ options: ['default', 'hover', 'focus', 'error', 'disabled'],
43
+ description: 'Visual state for documentation',
44
+ table: {
45
+ defaultValue: { summary: 'default' },
46
+ },
47
+ },
48
+ },
49
+ };
50
+
51
+ export default meta;
52
+ type Story = StoryObj<typeof meta>;
53
+
54
+ // Default
55
+ export const Default: Story = {
56
+ render: () => {
57
+ const [value, setValue] = useState('');
58
+ return (
59
+ <div className="space-y-4">
60
+ <InputOTP
61
+ value={value}
62
+ onValueChange={setValue}
63
+ onComplete={(code) => console.log('Complete:', code)}
64
+ />
65
+ <p className="text-sm text-foreground-muted">
66
+ Value: {value || 'empty'}
67
+ </p>
68
+ </div>
69
+ );
70
+ },
71
+ };
72
+
73
+ // With Label
74
+ export const WithLabel: Story = {
75
+ render: () => {
76
+ const [value, setValue] = useState('');
77
+ return (
78
+ <InputOTP
79
+ label="Verification Code"
80
+ value={value}
81
+ onValueChange={setValue}
82
+ />
83
+ );
84
+ },
85
+ };
86
+
87
+ // Four Digits
88
+ export const FourDigits: Story = {
89
+ render: () => {
90
+ const [value, setValue] = useState('');
91
+ return (
92
+ <InputOTP
93
+ label="PIN"
94
+ length={4}
95
+ value={value}
96
+ onValueChange={setValue}
97
+ />
98
+ );
99
+ },
100
+ };
101
+
102
+ // Eight Digits
103
+ export const EightDigits: Story = {
104
+ render: () => {
105
+ const [value, setValue] = useState('');
106
+ return (
107
+ <InputOTP
108
+ label="Recovery Code"
109
+ length={8}
110
+ value={value}
111
+ onValueChange={setValue}
112
+ />
113
+ );
114
+ },
115
+ };
116
+
117
+ // With Error
118
+ export const WithError: Story = {
119
+ render: () => {
120
+ const [value, setValue] = useState('123456');
121
+ return (
122
+ <InputOTP
123
+ label="Enter Code"
124
+ error="Invalid verification code"
125
+ value={value}
126
+ onValueChange={setValue}
127
+ />
128
+ );
129
+ },
130
+ };
131
+
132
+ // Disabled
133
+ export const Disabled: Story = {
134
+ render: () => {
135
+ return (
136
+ <InputOTP
137
+ label="Disabled Input"
138
+ defaultValue="123456"
139
+ disabled
140
+ />
141
+ );
142
+ },
143
+ };
144
+
145
+ // Auto Focus
146
+ export const AutoFocus: Story = {
147
+ render: () => {
148
+ const [value, setValue] = useState('');
149
+ return (
150
+ <div className="space-y-2">
151
+ <InputOTP
152
+ label="Auto-focused"
153
+ autoFocus
154
+ value={value}
155
+ onValueChange={setValue}
156
+ />
157
+ <p className="text-sm text-foreground-muted">
158
+ The first input is automatically focused
159
+ </p>
160
+ </div>
161
+ );
162
+ },
163
+ };
164
+
165
+ // With Completion Handler
166
+ export const WithCompletionHandler: Story = {
167
+ render: () => {
168
+ const [value, setValue] = useState('');
169
+ const [status, setStatus] = useState<'idle' | 'verifying' | 'success' | 'error'>('idle');
170
+
171
+ const handleComplete = async (code: string) => {
172
+ setStatus('verifying');
173
+ // Simulate API verification
174
+ await new Promise(resolve => setTimeout(resolve, 1500));
175
+ if (code === '123456') {
176
+ setStatus('success');
177
+ } else {
178
+ setStatus('error');
179
+ }
180
+ };
181
+
182
+ return (
183
+ <div className="space-y-4">
184
+ <InputOTP
185
+ label="Enter Verification Code"
186
+ value={value}
187
+ onValueChange={(v) => {
188
+ setValue(v);
189
+ if (status !== 'idle') setStatus('idle');
190
+ }}
191
+ onComplete={handleComplete}
192
+ error={status === 'error' ? 'Invalid code. Try 123456' : undefined}
193
+ />
194
+ {status === 'verifying' && (
195
+ <p className="text-sm text-foreground-muted">Verifying...</p>
196
+ )}
197
+ {status === 'success' && (
198
+ <p className="text-sm text-success">Code verified successfully!</p>
199
+ )}
200
+ </div>
201
+ );
202
+ },
203
+ };
204
+
205
+ // Verification Flow Example
206
+ export const VerificationFlowExample: Story = {
207
+ render: () => {
208
+ const [value, setValue] = useState('');
209
+ const [verified, setVerified] = useState(false);
210
+
211
+ const handleComplete = async (code: string) => {
212
+ // Simulate verification
213
+ await new Promise(resolve => setTimeout(resolve, 1000));
214
+ if (code.length === 6) {
215
+ setVerified(true);
216
+ }
217
+ };
218
+
219
+ if (verified) {
220
+ return (
221
+ <div className="w-80 p-6 border border-success rounded-sm text-center space-y-2">
222
+ <div className="text-success text-2xl">&#10003;</div>
223
+ <h3 className="font-medium">Verified!</h3>
224
+ <p className="text-sm text-foreground-muted">
225
+ Your phone number has been verified.
226
+ </p>
227
+ <button
228
+ onClick={() => {
229
+ setValue('');
230
+ setVerified(false);
231
+ }}
232
+ className="text-sm text-primary hover:underline"
233
+ >
234
+ Reset demo
235
+ </button>
236
+ </div>
237
+ );
238
+ }
239
+
240
+ return (
241
+ <div className="w-80 space-y-4">
242
+ <div className="text-center space-y-1">
243
+ <h3 className="font-medium">Verify your phone</h3>
244
+ <p className="text-sm text-foreground-muted">
245
+ We sent a code to +1 (555) ***-1234
246
+ </p>
247
+ </div>
248
+ <InputOTP
249
+ value={value}
250
+ onValueChange={setValue}
251
+ onComplete={handleComplete}
252
+ autoFocus
253
+ />
254
+ <p className="text-center text-sm text-foreground-muted">
255
+ Didn&apos;t receive a code?{' '}
256
+ <button className="text-primary hover:underline">Resend</button>
257
+ </p>
258
+ </div>
259
+ );
260
+ },
261
+ };
262
+
263
+ // All States
264
+ export const AllStates: Story = {
265
+ render: () => (
266
+ <div className="space-y-6">
267
+ <InputOTP label="Default" />
268
+ <InputOTP label="With Value" defaultValue="123456" />
269
+ <InputOTP label="With Error" error="Invalid code" defaultValue="000000" />
270
+ <InputOTP label="Disabled" disabled defaultValue="123456" />
271
+ <InputOTP label="4 Digits" length={4} />
272
+ </div>
273
+ ),
274
+ };
275
+
276
+ // State Matrix - Visual documentation of all states
277
+ export const StateMatrix: Story = {
278
+ render: () => {
279
+ const states: InputOTPState[] = ['default', 'hover', 'focus', 'error', 'disabled'];
280
+ return (
281
+ <div className="space-y-6">
282
+ <h3 className="text-sm font-medium text-foreground-muted">InputOTP States</h3>
283
+ <div className="space-y-4">
284
+ {states.map((state) => (
285
+ <div key={state}>
286
+ <span className="text-xs font-medium text-foreground-muted uppercase">{state}</span>
287
+ <InputOTP
288
+ state={state}
289
+ length={6}
290
+ defaultValue={state === 'error' ? '000000' : ''}
291
+ error={state === 'error' ? 'Invalid code' : undefined}
292
+ />
293
+ </div>
294
+ ))}
295
+ </div>
296
+ </div>
297
+ );
298
+ },
299
+ };
300
+
301
+ // Responsive Matrix - Mobile, Tablet, Desktop
302
+ export const ResponsiveMatrix: Story = {
303
+ render: () => (
304
+ <div className="space-y-8">
305
+ {/* Mobile */}
306
+ <div>
307
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
308
+ <div className="w-[375px] border border-dashed border-border p-4">
309
+ <InputOTP label="Verification Code" length={6} />
310
+ </div>
311
+ </div>
312
+ {/* Tablet */}
313
+ <div>
314
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
315
+ <div className="w-[768px] border border-dashed border-border p-4">
316
+ <div className="flex gap-8">
317
+ <InputOTP label="4-Digit PIN" length={4} />
318
+ <InputOTP label="6-Digit Code" length={6} />
319
+ </div>
320
+ </div>
321
+ </div>
322
+ {/* Desktop */}
323
+ <div>
324
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
325
+ <div className="w-[1280px] border border-dashed border-border p-4">
326
+ <div className="flex gap-8">
327
+ <InputOTP label="Default" />
328
+ <InputOTP label="With Value" defaultValue="123456" />
329
+ <InputOTP label="With Error" error="Invalid" defaultValue="000000" />
330
+ <InputOTP label="Disabled" disabled defaultValue="123456" />
331
+ </div>
332
+ </div>
333
+ </div>
334
+ </div>
335
+ ),
336
+ };