@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.
- package/dist/index.js +3956 -3766
- package/package.json +3 -2
- package/src/components/Extension/Extension.jsx +317 -0
- package/src/components/Extension/Extension.stories.jsx +368 -0
- package/src/components/SetlistList/SetlistList.jsx +23 -0
- package/src/components/SetlistList/SetlistList.module.css +8 -3
- package/src/components/useForm/useForm.jsx +204 -0
- package/src/components/useForm/useForm.stories.jsx +459 -0
- package/src/index.jsx +3 -0
|
@@ -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'
|