@moises.ai/design-system 3.5.6 → 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 +4261 -4030
- package/package.json +3 -2
- package/src/components/AdditionalItems/AdditionalItems.jsx +8 -0
- package/src/components/AdditionalItems/AdditionalItems.module.css +5 -0
- package/src/components/DropdownButton/DropdownButton.module.css +64 -0
- package/src/components/Extension/Extension.jsx +317 -0
- package/src/components/Extension/Extension.stories.jsx +368 -0
- package/src/components/ProductsList/ProductsList.jsx +8 -0
- package/src/components/ProductsList/ProductsList.module.css +5 -0
- package/src/components/ProfileMenu/MenuTrigger.jsx +2 -0
- package/src/components/ProfileMenu/ProfileMenu.module.css +5 -0
- package/src/components/Select/Select.jsx +5 -1
- package/src/components/Select/Select.module.css +67 -0
- package/src/components/SetlistList/SetlistList.jsx +57 -5
- package/src/components/SetlistList/SetlistList.module.css +34 -0
- package/src/components/Sidebar/Sidebar.module.css +1 -1
- package/src/components/Sidebar/SidebarSection/SidebarSection.module.css +1 -1
- package/src/components/useForm/useForm.jsx +204 -0
- package/src/components/useForm/useForm.stories.jsx +459 -0
- package/src/index.jsx +3 -0
- package/src/lib/menu/Menu.module.css +69 -4
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
align-items: center;
|
|
10
10
|
width: calc(100% - 24px);
|
|
11
11
|
|
|
12
|
+
&:focus-visible {
|
|
13
|
+
outline: 2px solid var(--neutral-alpha-8);
|
|
14
|
+
outline-offset: -1px;
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
&:hover,
|
|
13
18
|
&.menuOpen {
|
|
14
19
|
background: var(--neutral-alpha-2);
|
|
@@ -117,6 +122,23 @@
|
|
|
117
122
|
border-radius: var(--Radius-3-max, 6px);
|
|
118
123
|
background: var(--neutral-alpha-3);
|
|
119
124
|
|
|
125
|
+
&.collapsed {
|
|
126
|
+
background: transparent;
|
|
127
|
+
|
|
128
|
+
.avatarSetlist {
|
|
129
|
+
background: var(--neutral-2);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
&:hover,
|
|
133
|
+
&.menuOpen {
|
|
134
|
+
background: transparent !important;
|
|
135
|
+
|
|
136
|
+
.avatarSetlist {
|
|
137
|
+
background: var(--neutral-2) !important;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
120
142
|
&:hover,
|
|
121
143
|
&.menuOpen {
|
|
122
144
|
border-radius: var(--Radius-3-max, 6px);
|
|
@@ -168,20 +190,32 @@
|
|
|
168
190
|
|
|
169
191
|
.setlistsContent {
|
|
170
192
|
/* gap: 4px; */
|
|
193
|
+
margin: 1px 0;
|
|
171
194
|
}
|
|
172
195
|
.collapsedStack {
|
|
173
196
|
gap: 0;
|
|
174
197
|
}
|
|
198
|
+
|
|
199
|
+
.collapsedStackCentered {
|
|
200
|
+
align-items: center;
|
|
201
|
+
}
|
|
175
202
|
.collapsedStackItem {
|
|
176
203
|
position: relative;
|
|
204
|
+
/* justify-content: flex-start; */
|
|
177
205
|
}
|
|
178
206
|
|
|
179
207
|
.collapsedMask {
|
|
180
208
|
position: relative;
|
|
181
209
|
overflow: hidden;
|
|
182
210
|
padding-top: 2px;
|
|
211
|
+
display: flex;
|
|
212
|
+
|
|
183
213
|
}
|
|
184
214
|
|
|
215
|
+
/* .collapsedStack.collapsedMask {
|
|
216
|
+
padding-top: 12px;
|
|
217
|
+
} */
|
|
218
|
+
|
|
185
219
|
.collapsedTransition {
|
|
186
220
|
transition: margin-top 360ms ease-in-out;
|
|
187
221
|
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { useState, useCallback, useRef, useEffect } from 'react'
|
|
2
|
+
import { Text, Flex } from '../../index'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('zod').ZodObject} ZodSchema
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {'checkbox' | 'switch'} ToggleFieldType
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} TextFieldProps
|
|
14
|
+
* @property {string} value - Current field value.
|
|
15
|
+
* @property {(e: Event | string) => void} onChange - Change handler that accepts
|
|
16
|
+
* an input event or a raw value (for Select-like components).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {Object} ToggleFieldProps
|
|
21
|
+
* @property {boolean} checked - Current checked state.
|
|
22
|
+
* @property {(checked: boolean) => void} onCheckedChange - Callback fired when the checked state changes.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {Object} FormResult
|
|
27
|
+
* @property {boolean} isValid - Whether the current values pass schema validation.
|
|
28
|
+
* @property {Object} data - Parsed data when valid, raw values when invalid.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} FieldProps
|
|
33
|
+
* @property {string} name - Field name matching a key in the schema.
|
|
34
|
+
* @property {React.ReactNode} children - The form control to render.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {Object} UseFormReturn
|
|
39
|
+
* @property {((name: string, type?: undefined) => TextFieldProps) & ((name: string, type: ToggleFieldType) => ToggleFieldProps)} connectField
|
|
40
|
+
* Returns spread-ready props for a form control. Pass `'checkbox'` or `'switch'`
|
|
41
|
+
* as the second argument for toggle controls.
|
|
42
|
+
* @property {React.FC<FieldProps>} Field - Wrapper that renders its children with
|
|
43
|
+
* an inline validation error message below.
|
|
44
|
+
* @property {(name: string) => React.ReactNode | null} error - Returns the error
|
|
45
|
+
* message element for a field, or `null` if the field is valid.
|
|
46
|
+
* @property {Object} values - Current form values object (reactive).
|
|
47
|
+
* @property {(name: string, value: *) => void} set - Imperatively set a single field value.
|
|
48
|
+
* @property {(vals?: Object) => void} reset - Reset all values and errors.
|
|
49
|
+
* Pass an object to reset to specific values, or omit for schema defaults.
|
|
50
|
+
* @property {() => FormResult} getValues - Validate the form and return
|
|
51
|
+
* `{ isValid, data }`. Sets field errors on failure.
|
|
52
|
+
* @property {(cb: (values: Object) => void) => () => void} watchValues
|
|
53
|
+
* Subscribe to value changes. Returns an unsubscribe function.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/** @param {{ name: string, errorsRef: React.MutableRefObject<Object>, children: React.ReactNode }} props */
|
|
57
|
+
function FieldView({ name, errorsRef, children }) {
|
|
58
|
+
const msg = errorsRef.current[name]
|
|
59
|
+
return (
|
|
60
|
+
<Flex direction="column" gap="1">
|
|
61
|
+
{children}
|
|
62
|
+
{msg && <Text size="1" color="red">{msg}</Text>}
|
|
63
|
+
</Flex>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Lightweight form hook driven by a Zod schema.
|
|
69
|
+
*
|
|
70
|
+
* @param {Object} options
|
|
71
|
+
* @param {ZodSchema} options.schema - Zod object schema used for default values and validation.
|
|
72
|
+
* @returns {UseFormReturn}
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* const schema = z.object({
|
|
76
|
+
* name: z.string().min(2, 'Too short').default(''),
|
|
77
|
+
* agree: z.boolean().refine(v => v, 'Required').default(false),
|
|
78
|
+
* })
|
|
79
|
+
*
|
|
80
|
+
* function MyForm() {
|
|
81
|
+
* const { connectField, Field, getValues } = useForm({ schema })
|
|
82
|
+
*
|
|
83
|
+
* return (
|
|
84
|
+
* <>
|
|
85
|
+
* <Field name="name">
|
|
86
|
+
* <TextField placeholder="Name" {...connectField('name')} />
|
|
87
|
+
* </Field>
|
|
88
|
+
* <Field name="agree">
|
|
89
|
+
* <Checkbox {...connectField('agree', 'checkbox')} />
|
|
90
|
+
* </Field>
|
|
91
|
+
* <Button onClick={() => {
|
|
92
|
+
* const { isValid, data } = getValues()
|
|
93
|
+
* if (isValid) console.log(data)
|
|
94
|
+
* }}>Save</Button>
|
|
95
|
+
* </>
|
|
96
|
+
* )
|
|
97
|
+
* }
|
|
98
|
+
*/
|
|
99
|
+
export function useForm({ schema }) {
|
|
100
|
+
const [values, setValues] = useState(() => schema.parse({}))
|
|
101
|
+
const [errors, setErrors] = useState({})
|
|
102
|
+
const errorsRef = useRef(errors)
|
|
103
|
+
errorsRef.current = errors
|
|
104
|
+
const listenersRef = useRef([])
|
|
105
|
+
|
|
106
|
+
const mountedRef = useRef(false)
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!mountedRef.current) {
|
|
109
|
+
mountedRef.current = true
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
for (const cb of listenersRef.current) cb(values)
|
|
113
|
+
}, [values])
|
|
114
|
+
|
|
115
|
+
/** @type {(name: string, value: *) => void} */
|
|
116
|
+
const set = useCallback((name, value) => {
|
|
117
|
+
setValues((prev) => ({ ...prev, [name]: value }))
|
|
118
|
+
setErrors((prev) => ({ ...prev, [name]: null }))
|
|
119
|
+
}, [])
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Returns spread-ready props for a form control.
|
|
123
|
+
*
|
|
124
|
+
* - Default (no type): returns `{ value, onChange }` for text inputs and selects.
|
|
125
|
+
* - `'checkbox'` / `'switch'`: returns `{ checked, onCheckedChange }`.
|
|
126
|
+
*
|
|
127
|
+
* @param {string} name - Field name matching a key in the schema.
|
|
128
|
+
* @param {ToggleFieldType} [type] - Control type hint.
|
|
129
|
+
* @returns {TextFieldProps | ToggleFieldProps}
|
|
130
|
+
*/
|
|
131
|
+
const connectField = (name, type) => {
|
|
132
|
+
const val = values[name]
|
|
133
|
+
const update = (v) => set(name, v)
|
|
134
|
+
if (type === 'checkbox' || type === 'switch') {
|
|
135
|
+
return { checked: !!val, onCheckedChange: update }
|
|
136
|
+
}
|
|
137
|
+
return { value: val ?? '', onChange: (e) => update(e?.target?.value ?? e) }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Validate the current values against the schema.
|
|
142
|
+
*
|
|
143
|
+
* @returns {FormResult} `{ isValid: true, data }` on success, or
|
|
144
|
+
* `{ isValid: false, data }` on failure (with field errors set).
|
|
145
|
+
*/
|
|
146
|
+
const getValues = () => {
|
|
147
|
+
const result = schema.safeParse(values)
|
|
148
|
+
if (result.success) {
|
|
149
|
+
setErrors({})
|
|
150
|
+
return { isValid: true, data: result.data }
|
|
151
|
+
}
|
|
152
|
+
const fieldErrors = {}
|
|
153
|
+
for (const issue of result.error.issues) {
|
|
154
|
+
const name = issue.path.join('.')
|
|
155
|
+
if (!fieldErrors[name]) fieldErrors[name] = issue.message
|
|
156
|
+
}
|
|
157
|
+
setErrors(fieldErrors)
|
|
158
|
+
return { isValid: false, data: values }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Subscribe to value changes. The callback fires after every state
|
|
163
|
+
* update (skipping the initial mount).
|
|
164
|
+
*
|
|
165
|
+
* @param {(values: Object) => void} cb - Listener called with the latest values.
|
|
166
|
+
* @returns {() => void} Unsubscribe function (safe to return from `useEffect`).
|
|
167
|
+
*/
|
|
168
|
+
const watchValues = useCallback((cb) => {
|
|
169
|
+
listenersRef.current.push(cb)
|
|
170
|
+
return () => {
|
|
171
|
+
listenersRef.current = listenersRef.current.filter((l) => l !== cb)
|
|
172
|
+
}
|
|
173
|
+
}, [])
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Returns the error message element for a field, or `null` if valid.
|
|
177
|
+
*
|
|
178
|
+
* @param {string} name - Field name.
|
|
179
|
+
* @returns {React.ReactNode | null}
|
|
180
|
+
*/
|
|
181
|
+
const error = (name) => {
|
|
182
|
+
const msg = errors[name]
|
|
183
|
+
if (!msg) return null
|
|
184
|
+
return <Text size="1" color="red">{msg}</Text>
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Reset all values and clear errors.
|
|
189
|
+
*
|
|
190
|
+
* @param {Object} [vals] - Optional values to reset to. Defaults to schema defaults.
|
|
191
|
+
*/
|
|
192
|
+
const reset = (vals) => {
|
|
193
|
+
setValues(vals ?? schema.parse({}))
|
|
194
|
+
setErrors({})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** @type {React.FC<FieldProps>} */
|
|
198
|
+
const Field = useCallback(
|
|
199
|
+
(props) => <FieldView {...props} errorsRef={errorsRef} />,
|
|
200
|
+
[]
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
return { connectField, Field, error, values, set, reset, getValues, watchValues }
|
|
204
|
+
}
|