sonance-brand-mcp 1.1.3 → 1.1.7
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/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/index.js +1732 -13
- package/package.json +1 -1
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
import { Form, FormField, FormLabel, FormDescription, FormMessage, FormSubmit } from './form';
|
|
3
|
+
import { Input } from './input';
|
|
4
|
+
import { Textarea } from './textarea';
|
|
5
|
+
import { Select } from './select';
|
|
6
|
+
import { Checkbox } from './checkbox';
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Form> = {
|
|
9
|
+
title: 'Components/Forms/Form',
|
|
10
|
+
component: Form,
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
parameters: {
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component: 'A form component with built-in validation, error handling, and submission state management. Provides context for FormField components.',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<typeof meta>;
|
|
23
|
+
|
|
24
|
+
// Default Simple Form
|
|
25
|
+
export const Default: Story = {
|
|
26
|
+
render: () => {
|
|
27
|
+
const handleSubmit = (data: FormData) => {
|
|
28
|
+
alert(`Submitted: ${data.get('email')}`);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Form onSubmit={handleSubmit} className="w-80 space-y-4">
|
|
33
|
+
<FormField name="email">
|
|
34
|
+
<FormLabel>Email</FormLabel>
|
|
35
|
+
<Input name="email" type="email" placeholder="Enter your email" />
|
|
36
|
+
</FormField>
|
|
37
|
+
<FormSubmit>Subscribe</FormSubmit>
|
|
38
|
+
</Form>
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// With Validation
|
|
44
|
+
export const WithValidation: Story = {
|
|
45
|
+
render: () => {
|
|
46
|
+
const handleValidate = (data: FormData) => {
|
|
47
|
+
const errors: Record<string, string> = {};
|
|
48
|
+
const email = data.get('email') as string;
|
|
49
|
+
const password = data.get('password') as string;
|
|
50
|
+
|
|
51
|
+
if (!email) {
|
|
52
|
+
errors.email = 'Email is required';
|
|
53
|
+
} else if (!email.includes('@')) {
|
|
54
|
+
errors.email = 'Please enter a valid email';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!password) {
|
|
58
|
+
errors.password = 'Password is required';
|
|
59
|
+
} else if (password.length < 8) {
|
|
60
|
+
errors.password = 'Password must be at least 8 characters';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return errors;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const handleSubmit = (data: FormData) => {
|
|
67
|
+
alert('Form submitted successfully!');
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Form
|
|
72
|
+
onSubmit={handleSubmit}
|
|
73
|
+
onValidate={handleValidate}
|
|
74
|
+
className="w-80 space-y-4"
|
|
75
|
+
>
|
|
76
|
+
<FormField name="email">
|
|
77
|
+
<FormLabel required>Email</FormLabel>
|
|
78
|
+
<Input name="email" type="email" placeholder="you@example.com" />
|
|
79
|
+
<FormMessage />
|
|
80
|
+
</FormField>
|
|
81
|
+
|
|
82
|
+
<FormField name="password">
|
|
83
|
+
<FormLabel required>Password</FormLabel>
|
|
84
|
+
<Input name="password" type="password" placeholder="Enter password" />
|
|
85
|
+
<FormDescription>Must be at least 8 characters</FormDescription>
|
|
86
|
+
<FormMessage />
|
|
87
|
+
</FormField>
|
|
88
|
+
|
|
89
|
+
<FormSubmit>Sign In</FormSubmit>
|
|
90
|
+
</Form>
|
|
91
|
+
);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Contact Form
|
|
96
|
+
export const ContactForm: Story = {
|
|
97
|
+
render: () => {
|
|
98
|
+
const handleValidate = (data: FormData) => {
|
|
99
|
+
const errors: Record<string, string> = {};
|
|
100
|
+
|
|
101
|
+
if (!data.get('name')) errors.name = 'Name is required';
|
|
102
|
+
if (!data.get('email')) errors.email = 'Email is required';
|
|
103
|
+
if (!data.get('subject')) errors.subject = 'Please select a subject';
|
|
104
|
+
if (!data.get('message')) errors.message = 'Message is required';
|
|
105
|
+
|
|
106
|
+
return errors;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const handleSubmit = async (data: FormData) => {
|
|
110
|
+
// Simulate API call
|
|
111
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
112
|
+
alert('Message sent!');
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Form
|
|
117
|
+
onSubmit={handleSubmit}
|
|
118
|
+
onValidate={handleValidate}
|
|
119
|
+
className="w-96 space-y-4"
|
|
120
|
+
>
|
|
121
|
+
<div className="grid grid-cols-2 gap-4">
|
|
122
|
+
<FormField name="name">
|
|
123
|
+
<FormLabel required>Name</FormLabel>
|
|
124
|
+
<Input name="name" placeholder="Your name" />
|
|
125
|
+
<FormMessage />
|
|
126
|
+
</FormField>
|
|
127
|
+
|
|
128
|
+
<FormField name="email">
|
|
129
|
+
<FormLabel required>Email</FormLabel>
|
|
130
|
+
<Input name="email" type="email" placeholder="you@example.com" />
|
|
131
|
+
<FormMessage />
|
|
132
|
+
</FormField>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<FormField name="subject">
|
|
136
|
+
<FormLabel required>Subject</FormLabel>
|
|
137
|
+
<Select
|
|
138
|
+
placeholder="Select a topic"
|
|
139
|
+
options={[
|
|
140
|
+
{ value: 'sales', label: 'Sales Inquiry' },
|
|
141
|
+
{ value: 'support', label: 'Technical Support' },
|
|
142
|
+
{ value: 'partnership', label: 'Partnership' },
|
|
143
|
+
{ value: 'other', label: 'Other' },
|
|
144
|
+
]}
|
|
145
|
+
/>
|
|
146
|
+
<FormMessage />
|
|
147
|
+
</FormField>
|
|
148
|
+
|
|
149
|
+
<FormField name="message">
|
|
150
|
+
<FormLabel required>Message</FormLabel>
|
|
151
|
+
<Textarea name="message" placeholder="How can we help?" rows={4} />
|
|
152
|
+
<FormMessage />
|
|
153
|
+
</FormField>
|
|
154
|
+
|
|
155
|
+
<FormSubmit className="w-full">Send Message</FormSubmit>
|
|
156
|
+
</Form>
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Registration Form
|
|
162
|
+
export const RegistrationForm: Story = {
|
|
163
|
+
render: () => {
|
|
164
|
+
const handleValidate = (data: FormData) => {
|
|
165
|
+
const errors: Record<string, string> = {};
|
|
166
|
+
const password = data.get('password') as string;
|
|
167
|
+
const confirmPassword = data.get('confirmPassword') as string;
|
|
168
|
+
|
|
169
|
+
if (!data.get('firstName')) errors.firstName = 'First name is required';
|
|
170
|
+
if (!data.get('lastName')) errors.lastName = 'Last name is required';
|
|
171
|
+
if (!data.get('email')) errors.email = 'Email is required';
|
|
172
|
+
|
|
173
|
+
if (!password) {
|
|
174
|
+
errors.password = 'Password is required';
|
|
175
|
+
} else if (password.length < 8) {
|
|
176
|
+
errors.password = 'Password must be at least 8 characters';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (password !== confirmPassword) {
|
|
180
|
+
errors.confirmPassword = 'Passwords do not match';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!data.get('terms')) {
|
|
184
|
+
errors.terms = 'You must accept the terms';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return errors;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const handleSubmit = async (data: FormData) => {
|
|
191
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
192
|
+
alert('Account created!');
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<Form
|
|
197
|
+
onSubmit={handleSubmit}
|
|
198
|
+
onValidate={handleValidate}
|
|
199
|
+
className="w-96 space-y-4"
|
|
200
|
+
>
|
|
201
|
+
<div className="grid grid-cols-2 gap-4">
|
|
202
|
+
<FormField name="firstName">
|
|
203
|
+
<FormLabel required>First Name</FormLabel>
|
|
204
|
+
<Input name="firstName" placeholder="John" />
|
|
205
|
+
<FormMessage />
|
|
206
|
+
</FormField>
|
|
207
|
+
|
|
208
|
+
<FormField name="lastName">
|
|
209
|
+
<FormLabel required>Last Name</FormLabel>
|
|
210
|
+
<Input name="lastName" placeholder="Doe" />
|
|
211
|
+
<FormMessage />
|
|
212
|
+
</FormField>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<FormField name="email">
|
|
216
|
+
<FormLabel required>Email</FormLabel>
|
|
217
|
+
<Input name="email" type="email" placeholder="john@example.com" />
|
|
218
|
+
<FormMessage />
|
|
219
|
+
</FormField>
|
|
220
|
+
|
|
221
|
+
<FormField name="password">
|
|
222
|
+
<FormLabel required>Password</FormLabel>
|
|
223
|
+
<Input name="password" type="password" />
|
|
224
|
+
<FormDescription>Minimum 8 characters</FormDescription>
|
|
225
|
+
<FormMessage />
|
|
226
|
+
</FormField>
|
|
227
|
+
|
|
228
|
+
<FormField name="confirmPassword">
|
|
229
|
+
<FormLabel required>Confirm Password</FormLabel>
|
|
230
|
+
<Input name="confirmPassword" type="password" />
|
|
231
|
+
<FormMessage />
|
|
232
|
+
</FormField>
|
|
233
|
+
|
|
234
|
+
<FormField name="terms">
|
|
235
|
+
<Checkbox name="terms" value="accepted" label="I accept the terms and conditions" />
|
|
236
|
+
<FormMessage />
|
|
237
|
+
</FormField>
|
|
238
|
+
|
|
239
|
+
<FormSubmit className="w-full">Create Account</FormSubmit>
|
|
240
|
+
</Form>
|
|
241
|
+
);
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Form Components Showcase
|
|
246
|
+
export const ComponentsShowcase: Story = {
|
|
247
|
+
render: () => (
|
|
248
|
+
<div className="w-80 space-y-6">
|
|
249
|
+
<div>
|
|
250
|
+
<h4 className="text-sm font-medium mb-2">FormLabel</h4>
|
|
251
|
+
<FormLabel>Regular Label</FormLabel>
|
|
252
|
+
<FormLabel required>Required Label</FormLabel>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<div>
|
|
256
|
+
<h4 className="text-sm font-medium mb-2">FormDescription</h4>
|
|
257
|
+
<FormDescription>
|
|
258
|
+
This is helper text that provides additional context for the field.
|
|
259
|
+
</FormDescription>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<div>
|
|
263
|
+
<h4 className="text-sm font-medium mb-2">FormMessage (Error)</h4>
|
|
264
|
+
<FormMessage>This field is required</FormMessage>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div>
|
|
268
|
+
<h4 className="text-sm font-medium mb-2">FormSubmit</h4>
|
|
269
|
+
<FormSubmit>Submit Button</FormSubmit>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
),
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// Async Submission
|
|
276
|
+
export const AsyncSubmission: Story = {
|
|
277
|
+
render: () => {
|
|
278
|
+
const handleSubmit = async (data: FormData) => {
|
|
279
|
+
// Simulate slow API call
|
|
280
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
281
|
+
alert('Submitted!');
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<Form onSubmit={handleSubmit} className="w-80 space-y-4">
|
|
286
|
+
<FormField name="data">
|
|
287
|
+
<FormLabel>Data</FormLabel>
|
|
288
|
+
<Input name="data" placeholder="Enter something" />
|
|
289
|
+
<FormDescription>
|
|
290
|
+
The submit button shows loading state during submission
|
|
291
|
+
</FormDescription>
|
|
292
|
+
</FormField>
|
|
293
|
+
<FormSubmit>Submit (3s delay)</FormSubmit>
|
|
294
|
+
</Form>
|
|
295
|
+
);
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// Responsive Matrix - Mobile, Tablet, Desktop
|
|
300
|
+
export const ResponsiveMatrix: Story = {
|
|
301
|
+
render: () => (
|
|
302
|
+
<div className="space-y-8">
|
|
303
|
+
{/* Mobile */}
|
|
304
|
+
<div>
|
|
305
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
|
|
306
|
+
<div className="w-[375px] border border-dashed border-border p-4">
|
|
307
|
+
<Form className="space-y-4">
|
|
308
|
+
<FormField name="email">
|
|
309
|
+
<FormLabel>Email</FormLabel>
|
|
310
|
+
<Input name="email" type="email" placeholder="you@example.com" />
|
|
311
|
+
</FormField>
|
|
312
|
+
<FormSubmit className="w-full">Subscribe</FormSubmit>
|
|
313
|
+
</Form>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
{/* Tablet */}
|
|
317
|
+
<div>
|
|
318
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
|
|
319
|
+
<div className="w-[768px] border border-dashed border-border p-4">
|
|
320
|
+
<Form className="space-y-4">
|
|
321
|
+
<div className="grid grid-cols-2 gap-4">
|
|
322
|
+
<FormField name="firstName">
|
|
323
|
+
<FormLabel>First Name</FormLabel>
|
|
324
|
+
<Input name="firstName" placeholder="John" />
|
|
325
|
+
</FormField>
|
|
326
|
+
<FormField name="lastName">
|
|
327
|
+
<FormLabel>Last Name</FormLabel>
|
|
328
|
+
<Input name="lastName" placeholder="Doe" />
|
|
329
|
+
</FormField>
|
|
330
|
+
</div>
|
|
331
|
+
<FormField name="email">
|
|
332
|
+
<FormLabel>Email</FormLabel>
|
|
333
|
+
<Input name="email" type="email" placeholder="john@example.com" />
|
|
334
|
+
</FormField>
|
|
335
|
+
<FormSubmit>Submit</FormSubmit>
|
|
336
|
+
</Form>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
{/* Desktop */}
|
|
340
|
+
<div>
|
|
341
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
|
|
342
|
+
<div className="w-[1280px] border border-dashed border-border p-4">
|
|
343
|
+
<Form className="space-y-4 max-w-2xl">
|
|
344
|
+
<div className="grid grid-cols-3 gap-4">
|
|
345
|
+
<FormField name="firstName">
|
|
346
|
+
<FormLabel required>First Name</FormLabel>
|
|
347
|
+
<Input name="firstName" placeholder="John" />
|
|
348
|
+
<FormMessage />
|
|
349
|
+
</FormField>
|
|
350
|
+
<FormField name="lastName">
|
|
351
|
+
<FormLabel required>Last Name</FormLabel>
|
|
352
|
+
<Input name="lastName" placeholder="Doe" />
|
|
353
|
+
<FormMessage />
|
|
354
|
+
</FormField>
|
|
355
|
+
<FormField name="email">
|
|
356
|
+
<FormLabel required>Email</FormLabel>
|
|
357
|
+
<Input name="email" type="email" placeholder="john@example.com" />
|
|
358
|
+
<FormMessage />
|
|
359
|
+
</FormField>
|
|
360
|
+
</div>
|
|
361
|
+
<FormField name="message">
|
|
362
|
+
<FormLabel>Message</FormLabel>
|
|
363
|
+
<Textarea name="message" placeholder="How can we help?" rows={3} />
|
|
364
|
+
<FormDescription>Optional message</FormDescription>
|
|
365
|
+
</FormField>
|
|
366
|
+
<FormSubmit>Send Message</FormSubmit>
|
|
367
|
+
</Form>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
),
|
|
372
|
+
};
|