@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.
@@ -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
  }
@@ -246,7 +246,7 @@
246
246
  }
247
247
 
248
248
  .headerCollapsed {
249
- padding-left: 17px;
249
+ padding-left: 16px;
250
250
  }
251
251
 
252
252
  .separator {
@@ -16,5 +16,5 @@
16
16
 
17
17
  .separator {
18
18
  background: var(--neutral-alpha-4) !important;
19
- margin: 0 4px 23px 4px;
19
+ margin: 0 3px 23px 3px;
20
20
  }
@@ -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
+ }