@moises.ai/design-system 3.5.7 → 3.6.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.
@@ -0,0 +1,459 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { z } from 'zod'
3
+ import {
4
+ Button,
5
+ Card,
6
+ Checkbox,
7
+ Flex,
8
+ Select,
9
+ Switch,
10
+ Text,
11
+ TextArea,
12
+ TextField,
13
+ } from '../../index'
14
+ import { useForm } from './useForm'
15
+
16
+
17
+ const sidebarDecorator = (Story) => (
18
+ <Flex direction="column" gap="3">
19
+ <Story />
20
+ </Flex>
21
+ )
22
+
23
+ export default {
24
+ title: 'Form/useForm',
25
+ decorators: [sidebarDecorator],
26
+ parameters: { layout: 'centered' },
27
+ }
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Full form (mirrors DemoForm extension)
31
+ // ---------------------------------------------------------------------------
32
+
33
+ const fullSchema = z.object({
34
+ name: z.string().min(2, 'Too short').default(''),
35
+ email: z.string().email('Invalid email').default(''),
36
+ bio: z.string().default(''),
37
+ role: z.string().min(1, 'Pick a role').default(''),
38
+ notifications: z.boolean().default(true),
39
+ terms: z.boolean().refine((v) => v, 'Required').default(false),
40
+ })
41
+
42
+ function FullFormExample() {
43
+ const { connectField, Field, getValues, reset, watchValues } = useForm({ schema: fullSchema })
44
+
45
+ useEffect(() => watchValues((data) => console.log('Changed:', data)), [])
46
+
47
+ const handleSave = () => {
48
+ const { isValid, data } = getValues()
49
+ console.log('getValues:', { isValid, data })
50
+ }
51
+
52
+ return (
53
+ <Card>
54
+ <Flex direction="column" gap="3">
55
+ <Field name="name">
56
+ <TextField placeholder="Name" {...connectField('name')} />
57
+ </Field>
58
+
59
+ <Field name="email">
60
+ <TextField placeholder="Email" {...connectField('email')} />
61
+ </Field>
62
+
63
+ <Field name="bio">
64
+ <TextArea placeholder="Bio" {...connectField('bio')} />
65
+ </Field>
66
+
67
+ <Field name="role">
68
+ <Select
69
+ placeholder="Choose role"
70
+ {...connectField('role')}
71
+ items={[
72
+ { value: 'admin', label: 'Admin' },
73
+ { value: 'editor', label: 'Editor' },
74
+ { value: 'viewer', label: 'Viewer' },
75
+ ]}
76
+ />
77
+ </Field>
78
+
79
+ <Flex align="center" gap="2">
80
+ <Switch {...connectField('notifications', 'switch')} />
81
+ <Text size="2">Email notifications</Text>
82
+ </Flex>
83
+
84
+ <Field name="terms">
85
+ <Flex align="center" gap="2">
86
+ <Checkbox {...connectField('terms', 'checkbox')} />
87
+ <Text size="2">I accept the terms</Text>
88
+ </Flex>
89
+ </Field>
90
+ </Flex>
91
+ </Card>
92
+ )
93
+ }
94
+
95
+ export const FullForm = {
96
+ name: 'Full Form (All Controls)',
97
+ render: () => <FullFormExample />,
98
+ }
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // connectField — text input
102
+ // ---------------------------------------------------------------------------
103
+
104
+ const textSchema = z.object({
105
+ username: z.string().min(3, 'At least 3 characters').default(''),
106
+ })
107
+
108
+ function ConnectFieldTextExample() {
109
+ const { connectField, Field, getValues } = useForm({ schema: textSchema })
110
+
111
+ return (
112
+ <Flex direction="column" gap="3">
113
+ <Field name="username">
114
+ <TextField placeholder="Username" {...connectField('username')} />
115
+ </Field>
116
+ <Button size="1" onClick={() => console.log(getValues())}>Validate</Button>
117
+ </Flex>
118
+ )
119
+ }
120
+
121
+ export const ConnectFieldText = {
122
+ name: 'connectField — TextField',
123
+ render: () => <ConnectFieldTextExample />,
124
+ }
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // connectField — checkbox & switch
128
+ // ---------------------------------------------------------------------------
129
+
130
+ const toggleSchema = z.object({
131
+ darkMode: z.boolean().default(false),
132
+ agree: z.boolean().refine((v) => v, 'You must agree').default(false),
133
+ })
134
+
135
+ function ConnectFieldToggleExample() {
136
+ const { connectField, Field, getValues } = useForm({ schema: toggleSchema })
137
+
138
+ return (
139
+ <Flex direction="column" gap="3">
140
+ <Flex align="center" gap="2">
141
+ <Switch {...connectField('darkMode', 'switch')} />
142
+ <Text size="2">Dark mode</Text>
143
+ </Flex>
144
+
145
+ <Field name="agree">
146
+ <Flex align="center" gap="2">
147
+ <Checkbox {...connectField('agree', 'checkbox')} />
148
+ <Text size="2">I agree to the terms</Text>
149
+ </Flex>
150
+ </Field>
151
+
152
+ <Button size="1" onClick={() => console.log(getValues())}>Validate</Button>
153
+ </Flex>
154
+ )
155
+ }
156
+
157
+ export const ConnectFieldToggle = {
158
+ name: 'connectField — Switch & Checkbox',
159
+ render: () => <ConnectFieldToggleExample />,
160
+ }
161
+
162
+ // ---------------------------------------------------------------------------
163
+ // connectField — Select
164
+ // ---------------------------------------------------------------------------
165
+
166
+ const selectSchema = z.object({
167
+ color: z.string().min(1, 'Pick a color').default(''),
168
+ })
169
+
170
+ function ConnectFieldSelectExample() {
171
+ const { connectField, Field, getValues } = useForm({ schema: selectSchema })
172
+
173
+ return (
174
+ <Flex direction="column" gap="3">
175
+ <Field name="color">
176
+ <Select
177
+ placeholder="Pick a color"
178
+ {...connectField('color')}
179
+ items={[
180
+ { value: 'red', label: 'Red' },
181
+ { value: 'green', label: 'Green' },
182
+ { value: 'blue', label: 'Blue' },
183
+ ]}
184
+ />
185
+ </Field>
186
+ <Button size="1" onClick={() => console.log(getValues())}>Validate</Button>
187
+ </Flex>
188
+ )
189
+ }
190
+
191
+ export const ConnectFieldSelect = {
192
+ name: 'connectField — Select',
193
+ render: () => <ConnectFieldSelectExample />,
194
+ }
195
+
196
+ // ---------------------------------------------------------------------------
197
+ // Field wrapper with validation errors
198
+ // ---------------------------------------------------------------------------
199
+
200
+ const validationSchema = z.object({
201
+ name: z.string().min(2, 'Name too short').default(''),
202
+ email: z.string().email('Enter a valid email').default(''),
203
+ age: z.string().regex(/^\d+$/, 'Must be a number').default(''),
204
+ })
205
+
206
+ function FieldValidationExample() {
207
+ const { connectField, Field, getValues } = useForm({ schema: validationSchema })
208
+
209
+ return (
210
+ <Flex direction="column" gap="3">
211
+ <Field name="name">
212
+ <TextField placeholder="Name (min 2)" {...connectField('name')} />
213
+ </Field>
214
+ <Field name="email">
215
+ <TextField placeholder="Email" {...connectField('email')} />
216
+ </Field>
217
+ <Field name="age">
218
+ <TextField placeholder="Age (digits only)" {...connectField('age')} />
219
+ </Field>
220
+ <Button size="1" onClick={() => console.log(getValues())}>
221
+ Submit (check console)
222
+ </Button>
223
+ </Flex>
224
+ )
225
+ }
226
+
227
+ export const FieldValidation = {
228
+ name: 'Field — Inline Errors',
229
+ render: () => <FieldValidationExample />,
230
+ }
231
+
232
+ // ---------------------------------------------------------------------------
233
+ // error() — standalone error renderer
234
+ // ---------------------------------------------------------------------------
235
+
236
+ const errorSchema = z.object({
237
+ code: z.string().length(6, 'Must be exactly 6 characters').default(''),
238
+ })
239
+
240
+ function StandaloneErrorExample() {
241
+ const { connectField, error, getValues } = useForm({ schema: errorSchema })
242
+
243
+ return (
244
+ <Flex direction="column" gap="3">
245
+ <TextField placeholder="6-char code" {...connectField('code')} />
246
+ {error('code')}
247
+ <Text size="1" color="gray">
248
+ Uses `error('code')` instead of the `Field` wrapper.
249
+ </Text>
250
+ <Button size="1" onClick={() => getValues()}>Validate</Button>
251
+ </Flex>
252
+ )
253
+ }
254
+
255
+ export const StandaloneError = {
256
+ name: 'error() — Standalone',
257
+ render: () => <StandaloneErrorExample />,
258
+ }
259
+
260
+ // ---------------------------------------------------------------------------
261
+ // values — reactive form state
262
+ // ---------------------------------------------------------------------------
263
+
264
+ const valuesSchema = z.object({
265
+ firstName: z.string().default(''),
266
+ lastName: z.string().default(''),
267
+ })
268
+
269
+ function ReactiveValuesExample() {
270
+ const { connectField, values } = useForm({ schema: valuesSchema })
271
+
272
+ return (
273
+ <Flex direction="column" gap="3">
274
+ <TextField placeholder="First name" {...connectField('firstName')} />
275
+ <TextField placeholder="Last name" {...connectField('lastName')} />
276
+ <Card>
277
+ <Text size="1" style={{ fontFamily: 'monospace', whiteSpace: 'pre' }}>
278
+ {JSON.stringify(values, null, 2)}
279
+ </Text>
280
+ </Card>
281
+ </Flex>
282
+ )
283
+ }
284
+
285
+ export const ReactiveValues = {
286
+ name: 'values — Live Preview',
287
+ render: () => <ReactiveValuesExample />,
288
+ }
289
+
290
+ // ---------------------------------------------------------------------------
291
+ // set() — imperative field setter
292
+ // ---------------------------------------------------------------------------
293
+
294
+ const setSchema = z.object({
295
+ color: z.string().default(''),
296
+ })
297
+
298
+ function ImperativeSetExample() {
299
+ const { connectField, set, values } = useForm({ schema: setSchema })
300
+
301
+ return (
302
+ <Flex direction="column" gap="3">
303
+ <TextField placeholder="Color" {...connectField('color')} />
304
+ <Flex gap="2" wrap="wrap">
305
+ {['red', 'green', 'blue', 'purple'].map((c) => (
306
+ <Button key={c} variant="soft" size="1" onClick={() => set('color', c)}>
307
+ {c}
308
+ </Button>
309
+ ))}
310
+ </Flex>
311
+ <Text size="1" color="gray">Current: {values.color || '(empty)'}</Text>
312
+ </Flex>
313
+ )
314
+ }
315
+
316
+ export const ImperativeSet = {
317
+ name: 'set() — Imperative Setter',
318
+ render: () => <ImperativeSetExample />,
319
+ }
320
+
321
+ // ---------------------------------------------------------------------------
322
+ // reset() — default and custom reset
323
+ // ---------------------------------------------------------------------------
324
+
325
+ const resetSchema = z.object({
326
+ title: z.string().default(''),
327
+ priority: z.string().default('medium'),
328
+ })
329
+
330
+ function ResetExample() {
331
+ const { connectField, reset, values } = useForm({ schema: resetSchema })
332
+
333
+ return (
334
+ <Flex direction="column" gap="3">
335
+ <TextField placeholder="Title" {...connectField('title')} />
336
+ <Select
337
+ placeholder="Priority"
338
+ {...connectField('priority')}
339
+ items={[
340
+ { value: 'low', label: 'Low' },
341
+ { value: 'medium', label: 'Medium' },
342
+ { value: 'high', label: 'High' },
343
+ ]}
344
+ />
345
+ <Card>
346
+ <Text size="1" style={{ fontFamily: 'monospace', whiteSpace: 'pre' }}>
347
+ {JSON.stringify(values, null, 2)}
348
+ </Text>
349
+ </Card>
350
+ <Flex gap="2">
351
+ <Button variant="ghost" size="1" onClick={() => reset()}>
352
+ Reset to defaults
353
+ </Button>
354
+ <Button variant="soft" size="1" onClick={() => reset({ title: 'Preset', priority: 'high' })}>
355
+ Reset to preset
356
+ </Button>
357
+ </Flex>
358
+ </Flex>
359
+ )
360
+ }
361
+
362
+ export const Reset = {
363
+ name: 'reset() — Default & Custom',
364
+ render: () => <ResetExample />,
365
+ }
366
+
367
+ // ---------------------------------------------------------------------------
368
+ // watchValues — subscribe to changes
369
+ // ---------------------------------------------------------------------------
370
+
371
+ const watchSchema = z.object({
372
+ search: z.string().default(''),
373
+ })
374
+
375
+ function WatchValuesExample() {
376
+ const { connectField, watchValues } = useForm({ schema: watchSchema })
377
+ const [log, setLog] = useState([])
378
+
379
+ useEffect(() => {
380
+ return watchValues((data) => {
381
+ setLog((prev) => [...prev.slice(-4), JSON.stringify(data)])
382
+ })
383
+ }, [])
384
+
385
+ return (
386
+ <Flex direction="column" gap="3">
387
+ <TextField placeholder="Type something…" {...connectField('search')} />
388
+ <Card>
389
+ <Flex direction="column" gap="1">
390
+ <Text size="1" weight="medium">Change log (last 5):</Text>
391
+ {log.length === 0 && <Text size="1" color="gray">No changes yet</Text>}
392
+ {log.map((entry, i) => (
393
+ <Text key={i} size="1" style={{ fontFamily: 'monospace' }}>{entry}</Text>
394
+ ))}
395
+ </Flex>
396
+ </Card>
397
+ </Flex>
398
+ )
399
+ }
400
+
401
+ export const WatchValues = {
402
+ name: 'watchValues() — Subscription',
403
+ render: () => <WatchValuesExample />,
404
+ }
405
+
406
+ // ---------------------------------------------------------------------------
407
+ // getValues() — validate and read
408
+ // ---------------------------------------------------------------------------
409
+
410
+ const getValuesSchema = z.object({
411
+ amount: z.string().regex(/^\d+(\.\d{1,2})?$/, 'Enter a valid amount').default(''),
412
+ currency: z.string().min(1, 'Required').default(''),
413
+ })
414
+
415
+ function GetValuesExample() {
416
+ const { connectField, Field, getValues } = useForm({ schema: getValuesSchema })
417
+ const [result, setResult] = useState(null)
418
+
419
+ const handleSubmit = () => {
420
+ setResult(getValues())
421
+ }
422
+
423
+ return (
424
+ <Flex direction="column" gap="3">
425
+ <Field name="amount">
426
+ <TextField placeholder="Amount (e.g. 9.99)" {...connectField('amount')} />
427
+ </Field>
428
+ <Field name="currency">
429
+ <Select
430
+ placeholder="Currency"
431
+ {...connectField('currency')}
432
+ items={[
433
+ { value: 'usd', label: 'USD' },
434
+ { value: 'eur', label: 'EUR' },
435
+ { value: 'brl', label: 'BRL' },
436
+ ]}
437
+ />
438
+ </Field>
439
+ <Button size="1" onClick={handleSubmit}>Submit</Button>
440
+ {result && (
441
+ <Card>
442
+ <Flex direction="column" gap="1">
443
+ <Text size="1" weight="medium">
444
+ {result.isValid ? '✓ Valid' : '✗ Invalid'}
445
+ </Text>
446
+ <Text size="1" style={{ fontFamily: 'monospace', whiteSpace: 'pre' }}>
447
+ {JSON.stringify(result.data, null, 2)}
448
+ </Text>
449
+ </Flex>
450
+ </Card>
451
+ )}
452
+ </Flex>
453
+ )
454
+ }
455
+
456
+ export const GetValues = {
457
+ name: 'getValues() — Validate & Read',
458
+ render: () => <GetValuesExample />,
459
+ }
package/src/index.jsx CHANGED
@@ -96,6 +96,9 @@ export { GenerationStatus } from './components/StemSeparationForm/GenerationStat
96
96
 
97
97
  export { Slider } from './components/Slider/Slider'
98
98
 
99
+ export { useForm } from './components/useForm/useForm'
100
+ export { Extension } from './components/Extension/Extension'
101
+
99
102
  export { TextArea } from './components/TextArea/TextArea'
100
103
 
101
104
  export { Button } from './components/Button/Button'