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.
- package/dist/assets/BRAND_GUIDELINES.md +0 -8
- package/dist/assets/components/accordion.stories.tsx +310 -0
- package/dist/assets/components/accordion.tsx +56 -30
- package/dist/assets/components/alert.stories.tsx +199 -0
- package/dist/assets/components/autocomplete.stories.tsx +307 -0
- package/dist/assets/components/autocomplete.tsx +28 -4
- package/dist/assets/components/avatar.stories.tsx +175 -0
- package/dist/assets/components/badge.stories.tsx +258 -0
- package/dist/assets/components/breadcrumbs.stories.tsx +175 -0
- package/dist/assets/components/button.stories.tsx +362 -0
- package/dist/assets/components/button.tsx +48 -3
- package/dist/assets/components/calendar.stories.tsx +247 -0
- package/dist/assets/components/card.stories.tsx +275 -0
- package/dist/assets/components/card.tsx +26 -1
- package/dist/assets/components/checkbox-group.stories.tsx +281 -0
- package/dist/assets/components/checkbox.stories.tsx +160 -0
- package/dist/assets/components/checkbox.tsx +32 -4
- package/dist/assets/components/code.stories.tsx +265 -0
- package/dist/assets/components/date-input.stories.tsx +278 -0
- package/dist/assets/components/date-input.tsx +24 -2
- package/dist/assets/components/date-picker.stories.tsx +337 -0
- package/dist/assets/components/date-picker.tsx +28 -4
- package/dist/assets/components/date-range-picker.stories.tsx +340 -0
- package/dist/assets/components/dialog.stories.tsx +285 -0
- package/dist/assets/components/divider.stories.tsx +176 -0
- package/dist/assets/components/drawer.stories.tsx +216 -0
- package/dist/assets/components/dropdown.stories.tsx +342 -0
- package/dist/assets/components/dropdown.tsx +55 -10
- package/dist/assets/components/form.stories.tsx +372 -0
- package/dist/assets/components/image.stories.tsx +348 -0
- package/dist/assets/components/input-otp.stories.tsx +336 -0
- package/dist/assets/components/input-otp.tsx +24 -2
- package/dist/assets/components/input.stories.tsx +223 -0
- package/dist/assets/components/input.tsx +27 -2
- package/dist/assets/components/kbd.stories.tsx +272 -0
- package/dist/assets/components/link.stories.tsx +199 -0
- package/dist/assets/components/link.tsx +50 -1
- package/dist/assets/components/listbox.stories.tsx +287 -0
- package/dist/assets/components/listbox.tsx +30 -7
- package/dist/assets/components/navbar.stories.tsx +218 -0
- package/dist/assets/components/number-input.stories.tsx +295 -0
- package/dist/assets/components/number-input.tsx +30 -8
- package/dist/assets/components/pagination.stories.tsx +280 -0
- package/dist/assets/components/pagination.tsx +45 -21
- package/dist/assets/components/popover.stories.tsx +219 -0
- package/dist/assets/components/progress.stories.tsx +153 -0
- package/dist/assets/components/radio-group.stories.tsx +187 -0
- package/dist/assets/components/radio-group.tsx +30 -6
- package/dist/assets/components/range-calendar.stories.tsx +334 -0
- package/dist/assets/components/scroll-shadow.stories.tsx +335 -0
- package/dist/assets/components/select.stories.tsx +192 -0
- package/dist/assets/components/select.tsx +54 -7
- package/dist/assets/components/skeleton.stories.tsx +166 -0
- package/dist/assets/components/slider.stories.tsx +145 -0
- package/dist/assets/components/slider.tsx +43 -8
- package/dist/assets/components/spacer.stories.tsx +216 -0
- package/dist/assets/components/spinner.stories.tsx +149 -0
- package/dist/assets/components/switch.stories.tsx +170 -0
- package/dist/assets/components/switch.tsx +29 -4
- package/dist/assets/components/table.stories.tsx +322 -0
- package/dist/assets/components/tabs.stories.tsx +306 -0
- package/dist/assets/components/tabs.tsx +25 -4
- package/dist/assets/components/textarea.stories.tsx +103 -0
- package/dist/assets/components/textarea.tsx +27 -3
- package/dist/assets/components/theme-toggle.stories.tsx +248 -0
- package/dist/assets/components/time-input.stories.tsx +365 -0
- package/dist/assets/components/time-input.tsx +25 -3
- package/dist/assets/components/toast.stories.tsx +195 -0
- package/dist/assets/components/tooltip.stories.tsx +226 -0
- package/dist/assets/components/user.stories.tsx +274 -0
- package/dist/assets/logo-manifest.json +0 -18
- package/dist/index.js +2142 -85
- 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">✓</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'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
|
+
};
|