@dxos/react-ui-form 0.7.5-labs.e27f9b9 → 0.7.5-labs.ea4b4c2
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/lib/browser/index.mjs +283 -283
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +291 -288
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +283 -283
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/index.d.ts +2 -0
- package/dist/types/src/components/Form/index.d.ts.map +1 -1
- package/package.json +24 -24
- package/src/components/Form/Defaults.tsx +2 -1
- package/src/components/Form/Form.tsx +1 -0
- package/src/components/Form/FormActions.tsx +1 -1
- package/src/components/Form/FormContent.tsx +1 -1
- package/src/components/Form/index.ts +3 -0
|
@@ -105,28 +105,233 @@ import { log as log2 } from "@dxos/log";
|
|
|
105
105
|
import { useTranslation as useTranslation5 } from "@dxos/react-ui";
|
|
106
106
|
import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
|
|
107
107
|
|
|
108
|
+
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
109
|
+
import React2, { createContext, useContext } from "react";
|
|
110
|
+
import { raise } from "@dxos/debug";
|
|
111
|
+
import { getValue as getValue2 } from "@dxos/echo-schema";
|
|
112
|
+
import { createJsonPath as createJsonPath2 } from "@dxos/effect";
|
|
113
|
+
|
|
114
|
+
// packages/ui/react-ui-form/src/hooks/useForm.ts
|
|
115
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
116
|
+
import { getValue, setValue } from "@dxos/echo-schema";
|
|
117
|
+
import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
|
|
118
|
+
import { invariant } from "@dxos/invariant";
|
|
119
|
+
import { log } from "@dxos/log";
|
|
120
|
+
import { validateSchema } from "@dxos/schema";
|
|
121
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
|
|
122
|
+
var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
|
|
123
|
+
invariant(AST.isTypeLiteral(schema.ast), void 0, {
|
|
124
|
+
F: __dxlog_file,
|
|
125
|
+
L: 91,
|
|
126
|
+
S: void 0,
|
|
127
|
+
A: [
|
|
128
|
+
"AST.isTypeLiteral(schema.ast)",
|
|
129
|
+
""
|
|
130
|
+
]
|
|
131
|
+
});
|
|
132
|
+
const [values, setValues] = useState(initialValues);
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
setValues(initialValues);
|
|
135
|
+
}, [
|
|
136
|
+
initialValues
|
|
137
|
+
]);
|
|
138
|
+
const [touched, setTouched] = useState(createKeySet(initialValues, false));
|
|
139
|
+
const [changed, setChanged] = useState(createKeySet(initialValues, false));
|
|
140
|
+
const [errors, setErrors] = useState({});
|
|
141
|
+
const [saving, setSaving] = useState(false);
|
|
142
|
+
const validate = useCallback((values2) => {
|
|
143
|
+
let errors2 = validateSchema(schema, values2) ?? [];
|
|
144
|
+
if (errors2.length === 0 && onValidate) {
|
|
145
|
+
const validatedValues = values2;
|
|
146
|
+
errors2 = onValidate(validatedValues) ?? [];
|
|
147
|
+
}
|
|
148
|
+
setErrors(flatMap(errors2));
|
|
149
|
+
const valid = errors2.length === 0;
|
|
150
|
+
return valid;
|
|
151
|
+
}, [
|
|
152
|
+
schema,
|
|
153
|
+
onValidate
|
|
154
|
+
]);
|
|
155
|
+
const [schemaDirty, setSchemaDirty] = useState(false);
|
|
156
|
+
useEffect(() => setSchemaDirty(true), [
|
|
157
|
+
schema
|
|
158
|
+
]);
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
if (schemaDirty) {
|
|
161
|
+
validate(values);
|
|
162
|
+
setSchemaDirty(false);
|
|
163
|
+
}
|
|
164
|
+
}, [
|
|
165
|
+
values,
|
|
166
|
+
schemaDirty
|
|
167
|
+
]);
|
|
168
|
+
const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
|
|
169
|
+
!Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
|
|
170
|
+
touched,
|
|
171
|
+
errors,
|
|
172
|
+
saving
|
|
173
|
+
]);
|
|
174
|
+
const handleSave = useCallback(async () => {
|
|
175
|
+
if (validate(values)) {
|
|
176
|
+
setSaving(true);
|
|
177
|
+
try {
|
|
178
|
+
await onSave?.(values, {
|
|
179
|
+
changed
|
|
180
|
+
});
|
|
181
|
+
} finally {
|
|
182
|
+
setSaving(false);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}, [
|
|
186
|
+
values,
|
|
187
|
+
validate,
|
|
188
|
+
onSave,
|
|
189
|
+
changed
|
|
190
|
+
]);
|
|
191
|
+
const getStatus = useCallback((path) => {
|
|
192
|
+
const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
|
|
193
|
+
const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
|
|
194
|
+
return {
|
|
195
|
+
status: matchingError ? "error" : void 0,
|
|
196
|
+
error: matchingError ? matchingError[1] : void 0
|
|
197
|
+
};
|
|
198
|
+
}, [
|
|
199
|
+
errors
|
|
200
|
+
]);
|
|
201
|
+
const getFormValue = (path) => {
|
|
202
|
+
return getValue(values, createJsonPath(path));
|
|
203
|
+
};
|
|
204
|
+
const onValueChange = (path, type, value) => {
|
|
205
|
+
const jsonPath = createJsonPath(path);
|
|
206
|
+
let parsedValue = value;
|
|
207
|
+
try {
|
|
208
|
+
if (type === "number") {
|
|
209
|
+
parsedValue = parseFloat(value) || 0;
|
|
210
|
+
}
|
|
211
|
+
} catch (err) {
|
|
212
|
+
log.catch(err, void 0, {
|
|
213
|
+
F: __dxlog_file,
|
|
214
|
+
L: 193,
|
|
215
|
+
S: void 0,
|
|
216
|
+
C: (f, a) => f(...a)
|
|
217
|
+
});
|
|
218
|
+
parsedValue = void 0;
|
|
219
|
+
}
|
|
220
|
+
const newValues = {
|
|
221
|
+
...setValue(values, jsonPath, parsedValue)
|
|
222
|
+
};
|
|
223
|
+
setValues(newValues);
|
|
224
|
+
setChanged((prev) => ({
|
|
225
|
+
...prev,
|
|
226
|
+
[jsonPath]: true
|
|
227
|
+
}));
|
|
228
|
+
onValuesChanged?.(newValues);
|
|
229
|
+
const isValid = validate(newValues);
|
|
230
|
+
if (isValid && onValid) {
|
|
231
|
+
onValid(newValues, {
|
|
232
|
+
changed
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const onTouched = useCallback((path) => {
|
|
237
|
+
const jsonPath = createJsonPath(path);
|
|
238
|
+
setTouched((touched2) => ({
|
|
239
|
+
...touched2,
|
|
240
|
+
[jsonPath]: true
|
|
241
|
+
}));
|
|
242
|
+
validate(values);
|
|
243
|
+
}, [
|
|
244
|
+
validate,
|
|
245
|
+
values
|
|
246
|
+
]);
|
|
247
|
+
return {
|
|
248
|
+
// State.
|
|
249
|
+
values,
|
|
250
|
+
errors,
|
|
251
|
+
touched,
|
|
252
|
+
changed,
|
|
253
|
+
canSave,
|
|
254
|
+
handleSave,
|
|
255
|
+
// Field utils.
|
|
256
|
+
getStatus,
|
|
257
|
+
getValue: getFormValue,
|
|
258
|
+
onValueChange,
|
|
259
|
+
onTouched
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
var createKeySet = (obj, value) => {
|
|
263
|
+
invariant(obj, void 0, {
|
|
264
|
+
F: __dxlog_file,
|
|
265
|
+
L: 235,
|
|
266
|
+
S: void 0,
|
|
267
|
+
A: [
|
|
268
|
+
"obj",
|
|
269
|
+
""
|
|
270
|
+
]
|
|
271
|
+
});
|
|
272
|
+
return Object.keys(obj).reduce((acc, key) => ({
|
|
273
|
+
...acc,
|
|
274
|
+
[key]: value
|
|
275
|
+
}), {});
|
|
276
|
+
};
|
|
277
|
+
var flatMap = (errors) => {
|
|
278
|
+
return errors.reduce((result, { path, message }) => {
|
|
279
|
+
const jsonPath = fromEffectValidationPath(path);
|
|
280
|
+
if (!(jsonPath in result)) {
|
|
281
|
+
result[jsonPath] = message;
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
}, {});
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
288
|
+
var FormContext = /* @__PURE__ */ createContext(void 0);
|
|
289
|
+
var useFormContext = () => {
|
|
290
|
+
return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
|
|
291
|
+
};
|
|
292
|
+
var useFormValues = (path = []) => {
|
|
293
|
+
const { values: formValues } = useFormContext();
|
|
294
|
+
const jsonPath = createJsonPath2(path);
|
|
295
|
+
return getValue2(formValues, jsonPath);
|
|
296
|
+
};
|
|
297
|
+
var useInputProps = (path = []) => {
|
|
298
|
+
const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
|
|
299
|
+
return {
|
|
300
|
+
getStatus: () => getStatus(path),
|
|
301
|
+
getValue: () => getFormValue(path),
|
|
302
|
+
onValueChange: (type, value) => onValueChange(path, type, value),
|
|
303
|
+
onBlur: () => onTouched(path)
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
var FormProvider = ({ children, ...formOptions }) => {
|
|
307
|
+
const formHandler = useForm(formOptions);
|
|
308
|
+
return /* @__PURE__ */ React2.createElement(FormContext.Provider, {
|
|
309
|
+
value: formHandler
|
|
310
|
+
}, children);
|
|
311
|
+
};
|
|
312
|
+
|
|
108
313
|
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
109
|
-
import
|
|
314
|
+
import React4 from "react";
|
|
110
315
|
import { Input as Input2, useTranslation } from "@dxos/react-ui";
|
|
111
316
|
import { safeParseFloat } from "@dxos/util";
|
|
112
317
|
|
|
113
318
|
// packages/ui/react-ui-form/src/components/Form/Input.tsx
|
|
114
|
-
import
|
|
319
|
+
import React3 from "react";
|
|
115
320
|
import { Icon, Tooltip } from "@dxos/react-ui";
|
|
116
321
|
import { errorText } from "@dxos/react-ui-theme";
|
|
117
322
|
var InputHeader = ({ children, error }) => {
|
|
118
|
-
return /* @__PURE__ */
|
|
323
|
+
return /* @__PURE__ */ React3.createElement("div", {
|
|
119
324
|
role: "none",
|
|
120
325
|
className: "flex justify-between items-center mb-1"
|
|
121
|
-
}, children, error && /* @__PURE__ */
|
|
326
|
+
}, children, error && /* @__PURE__ */ React3.createElement(Tooltip.Root, null, /* @__PURE__ */ React3.createElement(Tooltip.Trigger, {
|
|
122
327
|
asChild: true
|
|
123
|
-
}, /* @__PURE__ */
|
|
328
|
+
}, /* @__PURE__ */ React3.createElement(Icon, {
|
|
124
329
|
icon: "ph--warning--regular",
|
|
125
330
|
size: 4,
|
|
126
331
|
classNames: errorText
|
|
127
|
-
})), /* @__PURE__ */
|
|
332
|
+
})), /* @__PURE__ */ React3.createElement(Tooltip.Content, {
|
|
128
333
|
side: "bottom"
|
|
129
|
-
}, /* @__PURE__ */
|
|
334
|
+
}, /* @__PURE__ */ React3.createElement(Tooltip.Arrow, null), error)));
|
|
130
335
|
};
|
|
131
336
|
|
|
132
337
|
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
@@ -134,13 +339,13 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
|
|
|
134
339
|
const { t } = useTranslation(translationKey);
|
|
135
340
|
const { status, error } = getStatus();
|
|
136
341
|
const [lng = 0, lat = 0] = getValue3() ?? [];
|
|
137
|
-
return /* @__PURE__ */
|
|
342
|
+
return /* @__PURE__ */ React4.createElement(Input2.Root, {
|
|
138
343
|
validationValence: status
|
|
139
|
-
}, /* @__PURE__ */
|
|
344
|
+
}, /* @__PURE__ */ React4.createElement(InputHeader, {
|
|
140
345
|
error
|
|
141
|
-
}, /* @__PURE__ */
|
|
346
|
+
}, /* @__PURE__ */ React4.createElement(Input2.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
|
|
142
347
|
className: "grid grid-cols-2 gap-2"
|
|
143
|
-
}, /* @__PURE__ */
|
|
348
|
+
}, /* @__PURE__ */ React4.createElement(Input2.TextInput, {
|
|
144
349
|
type: "number",
|
|
145
350
|
disabled,
|
|
146
351
|
placeholder: t("placeholder longitude"),
|
|
@@ -150,7 +355,7 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
|
|
|
150
355
|
safeParseFloat(event.target.value, 0)
|
|
151
356
|
]),
|
|
152
357
|
onBlur
|
|
153
|
-
}), /* @__PURE__ */
|
|
358
|
+
}), /* @__PURE__ */ React4.createElement(Input2.TextInput, {
|
|
154
359
|
type: "number",
|
|
155
360
|
disabled,
|
|
156
361
|
placeholder: t("placeholder latitude"),
|
|
@@ -164,20 +369,20 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
|
|
|
164
369
|
};
|
|
165
370
|
|
|
166
371
|
// packages/ui/react-ui-form/src/components/Form/custom/SelectOptionsInput.tsx
|
|
167
|
-
import
|
|
372
|
+
import React5, { useCallback as useCallback2, useEffect as useEffect2, useState as useState2, useRef } from "react";
|
|
168
373
|
import { PublicKey } from "@dxos/keys";
|
|
169
374
|
import { Icon as Icon2, IconButton, Input as Input3, Tag, Toolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
170
375
|
import { List } from "@dxos/react-ui-list";
|
|
171
376
|
import { HuePickerToolbarButton } from "@dxos/react-ui-pickers";
|
|
172
377
|
import { hueTokenThemes, mx as mx2 } from "@dxos/react-ui-theme";
|
|
173
378
|
var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange }) => {
|
|
174
|
-
const [selected, setSelectedId] =
|
|
175
|
-
const [isNewOption, setIsNewOption] =
|
|
379
|
+
const [selected, setSelectedId] = useState2(null);
|
|
380
|
+
const [isNewOption, setIsNewOption] = useState2(false);
|
|
176
381
|
const inputRef = useRef(null);
|
|
177
382
|
const { t } = useTranslation2(translationKey);
|
|
178
383
|
const { status, error } = getStatus();
|
|
179
384
|
const options = getValue3();
|
|
180
|
-
|
|
385
|
+
useEffect2(() => {
|
|
181
386
|
if (options === void 0) {
|
|
182
387
|
onValueChange(type, []);
|
|
183
388
|
}
|
|
@@ -197,7 +402,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
197
402
|
const availableHues = hueKeys.filter((hue) => !lastThree.has(hue));
|
|
198
403
|
return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hueKeys[Math.floor(Math.random() * hueKeys.length)];
|
|
199
404
|
};
|
|
200
|
-
const handleAdd =
|
|
405
|
+
const handleAdd = useCallback2(() => {
|
|
201
406
|
const newId = PublicKey.random().truncate();
|
|
202
407
|
const newOption = {
|
|
203
408
|
id: newId,
|
|
@@ -215,7 +420,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
215
420
|
type,
|
|
216
421
|
onValueChange
|
|
217
422
|
]);
|
|
218
|
-
const handleDelete =
|
|
423
|
+
const handleDelete = useCallback2((id) => {
|
|
219
424
|
const newOptions = options?.filter((option) => option.id !== id) ?? [];
|
|
220
425
|
onValueChange(type, newOptions);
|
|
221
426
|
setSelectedId(null);
|
|
@@ -224,7 +429,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
224
429
|
type,
|
|
225
430
|
onValueChange
|
|
226
431
|
]);
|
|
227
|
-
const handleMove =
|
|
432
|
+
const handleMove = useCallback2((from, to) => {
|
|
228
433
|
if (!options) {
|
|
229
434
|
return;
|
|
230
435
|
}
|
|
@@ -239,10 +444,10 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
239
444
|
type,
|
|
240
445
|
onValueChange
|
|
241
446
|
]);
|
|
242
|
-
const handleClick =
|
|
447
|
+
const handleClick = useCallback2((id) => {
|
|
243
448
|
setSelectedId((current) => current === id ? null : id);
|
|
244
449
|
}, []);
|
|
245
|
-
const handleTitleChange =
|
|
450
|
+
const handleTitleChange = useCallback2((id) => (e) => {
|
|
246
451
|
const newOptions = options?.map((o) => o.id === id ? {
|
|
247
452
|
...o,
|
|
248
453
|
title: e.target.value
|
|
@@ -253,12 +458,12 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
253
458
|
type,
|
|
254
459
|
onValueChange
|
|
255
460
|
]);
|
|
256
|
-
const handleKeyDown =
|
|
461
|
+
const handleKeyDown = useCallback2((e) => {
|
|
257
462
|
if (e.key === "Enter") {
|
|
258
463
|
setSelectedId(null);
|
|
259
464
|
}
|
|
260
465
|
}, []);
|
|
261
|
-
const handleColorChange =
|
|
466
|
+
const handleColorChange = useCallback2((id) => (hue) => {
|
|
262
467
|
const newOptions = options?.map((o) => o.id === id ? {
|
|
263
468
|
...o,
|
|
264
469
|
color: hue
|
|
@@ -269,7 +474,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
269
474
|
type,
|
|
270
475
|
onValueChange
|
|
271
476
|
]);
|
|
272
|
-
|
|
477
|
+
useEffect2(() => {
|
|
273
478
|
if (selected && isNewOption && inputRef.current) {
|
|
274
479
|
inputRef.current.focus();
|
|
275
480
|
setIsNewOption(false);
|
|
@@ -278,76 +483,76 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
|
|
|
278
483
|
selected,
|
|
279
484
|
isNewOption
|
|
280
485
|
]);
|
|
281
|
-
return /* @__PURE__ */
|
|
486
|
+
return /* @__PURE__ */ React5.createElement(Input3.Root, {
|
|
282
487
|
validationValence: status
|
|
283
|
-
}, /* @__PURE__ */
|
|
488
|
+
}, /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
284
489
|
error
|
|
285
|
-
}, /* @__PURE__ */
|
|
490
|
+
}, /* @__PURE__ */ React5.createElement(Input3.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
|
|
286
491
|
role: "none"
|
|
287
|
-
}, options && /* @__PURE__ */
|
|
492
|
+
}, options && /* @__PURE__ */ React5.createElement(List.Root, {
|
|
288
493
|
items: options,
|
|
289
494
|
isItem: (item) => true,
|
|
290
495
|
onMove: handleMove
|
|
291
|
-
}, ({ items }) => /* @__PURE__ */
|
|
496
|
+
}, ({ items }) => /* @__PURE__ */ React5.createElement("div", {
|
|
292
497
|
role: "list",
|
|
293
498
|
className: "w-full overflow-auto"
|
|
294
|
-
}, items?.map((item) => /* @__PURE__ */
|
|
499
|
+
}, items?.map((item) => /* @__PURE__ */ React5.createElement("div", {
|
|
295
500
|
key: item.id
|
|
296
|
-
}, /* @__PURE__ */
|
|
501
|
+
}, /* @__PURE__ */ React5.createElement(List.Item, {
|
|
297
502
|
role: "button",
|
|
298
503
|
item,
|
|
299
504
|
classNames: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
|
|
300
505
|
"aria-expanded": selected === item.id
|
|
301
|
-
}, /* @__PURE__ */
|
|
506
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
302
507
|
className: "flex items-center"
|
|
303
|
-
}, /* @__PURE__ */
|
|
508
|
+
}, /* @__PURE__ */ React5.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React5.createElement(List.ItemTitle, {
|
|
304
509
|
onClick: () => handleClick(item.id),
|
|
305
510
|
classNames: "flex-1"
|
|
306
|
-
}, /* @__PURE__ */
|
|
511
|
+
}, /* @__PURE__ */ React5.createElement(Tag, {
|
|
307
512
|
palette: item.color
|
|
308
|
-
}, item.title || "\u200B")), /* @__PURE__ */
|
|
513
|
+
}, item.title || "\u200B")), /* @__PURE__ */ React5.createElement(Icon2, {
|
|
309
514
|
icon: selected === item.id ? "ph--caret-down--regular" : "ph--caret-right--regular",
|
|
310
515
|
onClick: () => handleClick(item.id)
|
|
311
|
-
})), selected === item.id && /* @__PURE__ */
|
|
516
|
+
})), selected === item.id && /* @__PURE__ */ React5.createElement("div", {
|
|
312
517
|
className: "mis-4 mbs-2 flex flex-col gap-1"
|
|
313
|
-
}, /* @__PURE__ */
|
|
518
|
+
}, /* @__PURE__ */ React5.createElement(Input3.Label, {
|
|
314
519
|
classNames: "text-xs"
|
|
315
|
-
}, t("select option label")), /* @__PURE__ */
|
|
520
|
+
}, t("select option label")), /* @__PURE__ */ React5.createElement("div", {
|
|
316
521
|
className: "flex flex-row items-center gap-1"
|
|
317
|
-
}, /* @__PURE__ */
|
|
522
|
+
}, /* @__PURE__ */ React5.createElement(Input3.TextInput, {
|
|
318
523
|
placeholder: t("select option label placeholder"),
|
|
319
524
|
ref: selected === item.id ? inputRef : void 0,
|
|
320
525
|
value: item.title,
|
|
321
526
|
onChange: handleTitleChange(item.id),
|
|
322
527
|
onKeyDown: handleKeyDown,
|
|
323
528
|
classNames: "flex-1"
|
|
324
|
-
}), /* @__PURE__ */
|
|
529
|
+
}), /* @__PURE__ */ React5.createElement(Toolbar.Root, {
|
|
325
530
|
classNames: "p-0 m-0 !is-auto"
|
|
326
|
-
}, /* @__PURE__ */
|
|
531
|
+
}, /* @__PURE__ */ React5.createElement(HuePickerToolbarButton, {
|
|
327
532
|
hue: item.color,
|
|
328
533
|
onChangeHue: handleColorChange(item.id)
|
|
329
|
-
})), /* @__PURE__ */
|
|
534
|
+
})), /* @__PURE__ */ React5.createElement(IconButton, {
|
|
330
535
|
icon: "ph--trash--fill",
|
|
331
536
|
iconOnly: true,
|
|
332
537
|
label: t("select option delete"),
|
|
333
538
|
onClick: () => handleDelete(item.id)
|
|
334
|
-
})))))), /* @__PURE__ */
|
|
539
|
+
})))))), /* @__PURE__ */ React5.createElement("div", {
|
|
335
540
|
role: "listitem"
|
|
336
|
-
}, /* @__PURE__ */
|
|
541
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
337
542
|
role: "button",
|
|
338
543
|
className: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
|
|
339
544
|
onClick: handleAdd
|
|
340
|
-
}, /* @__PURE__ */
|
|
545
|
+
}, /* @__PURE__ */ React5.createElement("div", {
|
|
341
546
|
className: "flex items-center gap-1"
|
|
342
|
-
}, /* @__PURE__ */
|
|
547
|
+
}, /* @__PURE__ */ React5.createElement(Icon2, {
|
|
343
548
|
icon: "ph--plus--regular"
|
|
344
|
-
}), /* @__PURE__ */
|
|
549
|
+
}), /* @__PURE__ */ React5.createElement("span", {
|
|
345
550
|
className: "text-sm"
|
|
346
551
|
}, t("select option add")))))))));
|
|
347
552
|
};
|
|
348
553
|
|
|
349
554
|
// packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
|
|
350
|
-
import
|
|
555
|
+
import React6 from "react";
|
|
351
556
|
import { Input as Input4 } from "@dxos/react-ui";
|
|
352
557
|
import { mx as mx3 } from "@dxos/react-ui-theme";
|
|
353
558
|
import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
|
|
@@ -360,13 +565,13 @@ var gridCols = [
|
|
|
360
565
|
var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
361
566
|
const { status, error } = getStatus();
|
|
362
567
|
const values = getValue3() ?? {};
|
|
363
|
-
return /* @__PURE__ */
|
|
568
|
+
return /* @__PURE__ */ React6.createElement(Input4.Root, {
|
|
364
569
|
validationValence: status
|
|
365
|
-
}, /* @__PURE__ */
|
|
570
|
+
}, /* @__PURE__ */ React6.createElement(InputHeader, {
|
|
366
571
|
error
|
|
367
|
-
}, /* @__PURE__ */
|
|
572
|
+
}, /* @__PURE__ */ React6.createElement(Input4.Label, null, label)), /* @__PURE__ */ React6.createElement("div", {
|
|
368
573
|
className: mx3("grid gap-2", gridCols[binding.length - 1])
|
|
369
|
-
}, binding.map((prop) => /* @__PURE__ */
|
|
574
|
+
}, binding.map((prop) => /* @__PURE__ */ React6.createElement(Input4.TextInput, {
|
|
370
575
|
key: prop,
|
|
371
576
|
type: "number",
|
|
372
577
|
disabled,
|
|
@@ -380,68 +585,69 @@ var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValu
|
|
|
380
585
|
};
|
|
381
586
|
|
|
382
587
|
// packages/ui/react-ui-form/src/components/Form/Defaults.tsx
|
|
383
|
-
import
|
|
588
|
+
import React7 from "react";
|
|
384
589
|
import { Input as Input5, Select } from "@dxos/react-ui";
|
|
385
590
|
var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
386
591
|
const { status, error } = getStatus();
|
|
387
|
-
return /* @__PURE__ */
|
|
592
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
388
593
|
validationValence: status
|
|
389
|
-
}, !inputOnly && /* @__PURE__ */
|
|
594
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
390
595
|
error
|
|
391
|
-
}, /* @__PURE__ */
|
|
596
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
|
|
392
597
|
disabled,
|
|
393
598
|
placeholder,
|
|
394
599
|
value: getValue3() ?? "",
|
|
395
600
|
onChange: (event) => onValueChange(type, event.target.value),
|
|
396
601
|
onBlur
|
|
397
|
-
}), inputOnly && /* @__PURE__ */
|
|
602
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
|
|
398
603
|
};
|
|
399
604
|
var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
400
605
|
const { status, error } = getStatus();
|
|
401
|
-
return /* @__PURE__ */
|
|
606
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
402
607
|
validationValence: status
|
|
403
|
-
}, !inputOnly && /* @__PURE__ */
|
|
608
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
404
609
|
error
|
|
405
|
-
}, /* @__PURE__ */
|
|
610
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
|
|
406
611
|
type: "number",
|
|
407
612
|
disabled,
|
|
408
613
|
placeholder,
|
|
409
614
|
value: getValue3() ?? 0,
|
|
410
615
|
onChange: (event) => onValueChange(type, event.target.value),
|
|
411
616
|
onBlur
|
|
412
|
-
}), inputOnly && /* @__PURE__ */
|
|
617
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
413
618
|
};
|
|
414
619
|
var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
|
|
415
620
|
const { status, error } = getStatus();
|
|
416
|
-
|
|
621
|
+
const checked = Boolean(getValue3());
|
|
622
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
417
623
|
validationValence: status
|
|
418
|
-
}, !inputOnly && /* @__PURE__ */
|
|
624
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
419
625
|
error
|
|
420
|
-
}, /* @__PURE__ */
|
|
421
|
-
checked
|
|
626
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.Switch, {
|
|
627
|
+
checked,
|
|
422
628
|
onCheckedChange: (value) => onValueChange(type, value)
|
|
423
|
-
}), inputOnly && /* @__PURE__ */
|
|
629
|
+
}), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
424
630
|
};
|
|
425
631
|
var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
|
|
426
632
|
const { status, error } = getStatus();
|
|
427
|
-
return /* @__PURE__ */
|
|
633
|
+
return /* @__PURE__ */ React7.createElement(Input5.Root, {
|
|
428
634
|
validationValence: status
|
|
429
|
-
}, !inputOnly && /* @__PURE__ */
|
|
635
|
+
}, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
|
|
430
636
|
error
|
|
431
|
-
}, /* @__PURE__ */
|
|
637
|
+
}, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
|
|
432
638
|
value: getValue3(),
|
|
433
639
|
onValueChange: (value) => onValueChange(type, value)
|
|
434
|
-
}, /* @__PURE__ */
|
|
640
|
+
}, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
|
|
435
641
|
classNames: "is-full",
|
|
436
642
|
disabled,
|
|
437
643
|
placeholder
|
|
438
|
-
}), /* @__PURE__ */
|
|
644
|
+
}), /* @__PURE__ */ React7.createElement(Select.Portal, null, /* @__PURE__ */ React7.createElement(Select.Content, null, /* @__PURE__ */ React7.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
|
|
439
645
|
// NOTE: Numeric values are converted to and from strings.
|
|
440
|
-
/* @__PURE__ */
|
|
646
|
+
/* @__PURE__ */ React7.createElement(Select.Option, {
|
|
441
647
|
key: String(value),
|
|
442
648
|
value: String(value)
|
|
443
649
|
}, label2 ?? String(value))
|
|
444
|
-
)))))), inputOnly && /* @__PURE__ */
|
|
650
|
+
)))))), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
|
|
445
651
|
};
|
|
446
652
|
|
|
447
653
|
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
@@ -452,219 +658,12 @@ import { mx as mx7 } from "@dxos/react-ui-theme";
|
|
|
452
658
|
import React8 from "react";
|
|
453
659
|
import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
|
|
454
660
|
import { mx as mx4 } from "@dxos/react-ui-theme";
|
|
455
|
-
|
|
456
|
-
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
457
|
-
import React7, { createContext, useContext } from "react";
|
|
458
|
-
import { raise } from "@dxos/debug";
|
|
459
|
-
import { getValue as getValue2 } from "@dxos/echo-schema";
|
|
460
|
-
import { createJsonPath as createJsonPath2 } from "@dxos/effect";
|
|
461
|
-
|
|
462
|
-
// packages/ui/react-ui-form/src/hooks/useForm.ts
|
|
463
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useState as useState2 } from "react";
|
|
464
|
-
import { getValue, setValue } from "@dxos/echo-schema";
|
|
465
|
-
import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
|
|
466
|
-
import { invariant } from "@dxos/invariant";
|
|
467
|
-
import { log } from "@dxos/log";
|
|
468
|
-
import { validateSchema } from "@dxos/schema";
|
|
469
|
-
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
|
|
470
|
-
var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
|
|
471
|
-
invariant(AST.isTypeLiteral(schema.ast), void 0, {
|
|
472
|
-
F: __dxlog_file,
|
|
473
|
-
L: 91,
|
|
474
|
-
S: void 0,
|
|
475
|
-
A: [
|
|
476
|
-
"AST.isTypeLiteral(schema.ast)",
|
|
477
|
-
""
|
|
478
|
-
]
|
|
479
|
-
});
|
|
480
|
-
const [values, setValues] = useState2(initialValues);
|
|
481
|
-
useEffect2(() => {
|
|
482
|
-
setValues(initialValues);
|
|
483
|
-
}, [
|
|
484
|
-
initialValues
|
|
485
|
-
]);
|
|
486
|
-
const [touched, setTouched] = useState2(createKeySet(initialValues, false));
|
|
487
|
-
const [changed, setChanged] = useState2(createKeySet(initialValues, false));
|
|
488
|
-
const [errors, setErrors] = useState2({});
|
|
489
|
-
const [saving, setSaving] = useState2(false);
|
|
490
|
-
const validate = useCallback2((values2) => {
|
|
491
|
-
let errors2 = validateSchema(schema, values2) ?? [];
|
|
492
|
-
if (errors2.length === 0 && onValidate) {
|
|
493
|
-
const validatedValues = values2;
|
|
494
|
-
errors2 = onValidate(validatedValues) ?? [];
|
|
495
|
-
}
|
|
496
|
-
setErrors(flatMap(errors2));
|
|
497
|
-
const valid = errors2.length === 0;
|
|
498
|
-
return valid;
|
|
499
|
-
}, [
|
|
500
|
-
schema,
|
|
501
|
-
onValidate
|
|
502
|
-
]);
|
|
503
|
-
const [schemaDirty, setSchemaDirty] = useState2(false);
|
|
504
|
-
useEffect2(() => setSchemaDirty(true), [
|
|
505
|
-
schema
|
|
506
|
-
]);
|
|
507
|
-
useEffect2(() => {
|
|
508
|
-
if (schemaDirty) {
|
|
509
|
-
validate(values);
|
|
510
|
-
setSchemaDirty(false);
|
|
511
|
-
}
|
|
512
|
-
}, [
|
|
513
|
-
values,
|
|
514
|
-
schemaDirty
|
|
515
|
-
]);
|
|
516
|
-
const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
|
|
517
|
-
!Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
|
|
518
|
-
touched,
|
|
519
|
-
errors,
|
|
520
|
-
saving
|
|
521
|
-
]);
|
|
522
|
-
const handleSave = useCallback2(async () => {
|
|
523
|
-
if (validate(values)) {
|
|
524
|
-
setSaving(true);
|
|
525
|
-
try {
|
|
526
|
-
await onSave?.(values, {
|
|
527
|
-
changed
|
|
528
|
-
});
|
|
529
|
-
} finally {
|
|
530
|
-
setSaving(false);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}, [
|
|
534
|
-
values,
|
|
535
|
-
validate,
|
|
536
|
-
onSave,
|
|
537
|
-
changed
|
|
538
|
-
]);
|
|
539
|
-
const getStatus = useCallback2((path) => {
|
|
540
|
-
const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
|
|
541
|
-
const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
|
|
542
|
-
return {
|
|
543
|
-
status: matchingError ? "error" : void 0,
|
|
544
|
-
error: matchingError ? matchingError[1] : void 0
|
|
545
|
-
};
|
|
546
|
-
}, [
|
|
547
|
-
errors
|
|
548
|
-
]);
|
|
549
|
-
const getFormValue = (path) => {
|
|
550
|
-
return getValue(values, createJsonPath(path));
|
|
551
|
-
};
|
|
552
|
-
const onValueChange = (path, type, value) => {
|
|
553
|
-
const jsonPath = createJsonPath(path);
|
|
554
|
-
let parsedValue = value;
|
|
555
|
-
try {
|
|
556
|
-
if (type === "number") {
|
|
557
|
-
parsedValue = parseFloat(value) || 0;
|
|
558
|
-
}
|
|
559
|
-
} catch (err) {
|
|
560
|
-
log.catch(err, void 0, {
|
|
561
|
-
F: __dxlog_file,
|
|
562
|
-
L: 193,
|
|
563
|
-
S: void 0,
|
|
564
|
-
C: (f, a) => f(...a)
|
|
565
|
-
});
|
|
566
|
-
parsedValue = void 0;
|
|
567
|
-
}
|
|
568
|
-
const newValues = {
|
|
569
|
-
...setValue(values, jsonPath, parsedValue)
|
|
570
|
-
};
|
|
571
|
-
setValues(newValues);
|
|
572
|
-
setChanged((prev) => ({
|
|
573
|
-
...prev,
|
|
574
|
-
[jsonPath]: true
|
|
575
|
-
}));
|
|
576
|
-
onValuesChanged?.(newValues);
|
|
577
|
-
const isValid = validate(newValues);
|
|
578
|
-
if (isValid && onValid) {
|
|
579
|
-
onValid(newValues, {
|
|
580
|
-
changed
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
};
|
|
584
|
-
const onTouched = useCallback2((path) => {
|
|
585
|
-
const jsonPath = createJsonPath(path);
|
|
586
|
-
setTouched((touched2) => ({
|
|
587
|
-
...touched2,
|
|
588
|
-
[jsonPath]: true
|
|
589
|
-
}));
|
|
590
|
-
validate(values);
|
|
591
|
-
}, [
|
|
592
|
-
validate,
|
|
593
|
-
values
|
|
594
|
-
]);
|
|
595
|
-
return {
|
|
596
|
-
// State.
|
|
597
|
-
values,
|
|
598
|
-
errors,
|
|
599
|
-
touched,
|
|
600
|
-
changed,
|
|
601
|
-
canSave,
|
|
602
|
-
handleSave,
|
|
603
|
-
// Field utils.
|
|
604
|
-
getStatus,
|
|
605
|
-
getValue: getFormValue,
|
|
606
|
-
onValueChange,
|
|
607
|
-
onTouched
|
|
608
|
-
};
|
|
609
|
-
};
|
|
610
|
-
var createKeySet = (obj, value) => {
|
|
611
|
-
invariant(obj, void 0, {
|
|
612
|
-
F: __dxlog_file,
|
|
613
|
-
L: 235,
|
|
614
|
-
S: void 0,
|
|
615
|
-
A: [
|
|
616
|
-
"obj",
|
|
617
|
-
""
|
|
618
|
-
]
|
|
619
|
-
});
|
|
620
|
-
return Object.keys(obj).reduce((acc, key) => ({
|
|
621
|
-
...acc,
|
|
622
|
-
[key]: value
|
|
623
|
-
}), {});
|
|
624
|
-
};
|
|
625
|
-
var flatMap = (errors) => {
|
|
626
|
-
return errors.reduce((result, { path, message }) => {
|
|
627
|
-
const jsonPath = fromEffectValidationPath(path);
|
|
628
|
-
if (!(jsonPath in result)) {
|
|
629
|
-
result[jsonPath] = message;
|
|
630
|
-
}
|
|
631
|
-
return result;
|
|
632
|
-
}, {});
|
|
633
|
-
};
|
|
634
|
-
|
|
635
|
-
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
636
|
-
var FormContext = /* @__PURE__ */ createContext(void 0);
|
|
637
|
-
var useFormContext = () => {
|
|
638
|
-
return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
|
|
639
|
-
};
|
|
640
|
-
var useFormValues = (path = []) => {
|
|
641
|
-
const { values: formValues } = useFormContext();
|
|
642
|
-
const jsonPath = createJsonPath2(path);
|
|
643
|
-
return getValue2(formValues, jsonPath);
|
|
644
|
-
};
|
|
645
|
-
var useInputProps = (path = []) => {
|
|
646
|
-
const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
|
|
647
|
-
return {
|
|
648
|
-
getStatus: () => getStatus(path),
|
|
649
|
-
getValue: () => getFormValue(path),
|
|
650
|
-
onValueChange: (type, value) => onValueChange(path, type, value),
|
|
651
|
-
onBlur: () => onTouched(path)
|
|
652
|
-
};
|
|
653
|
-
};
|
|
654
|
-
var FormProvider = ({ children, ...formOptions }) => {
|
|
655
|
-
const formHandler = useForm(formOptions);
|
|
656
|
-
return /* @__PURE__ */ React7.createElement(FormContext.Provider, {
|
|
657
|
-
value: formHandler
|
|
658
|
-
}, children);
|
|
659
|
-
};
|
|
660
|
-
|
|
661
|
-
// packages/ui/react-ui-form/src/components/Form/FormActions.tsx
|
|
662
661
|
var FormActions = ({ onCancel, readonly }) => {
|
|
663
662
|
const { t } = useTranslation3(translationKey);
|
|
664
663
|
const { canSave, handleSave } = useFormContext();
|
|
665
664
|
return /* @__PURE__ */ React8.createElement("div", {
|
|
666
665
|
role: "none",
|
|
667
|
-
className: "flex justify-center"
|
|
666
|
+
className: "mt-2 flex justify-center"
|
|
668
667
|
}, /* @__PURE__ */ React8.createElement("div", {
|
|
669
668
|
role: "none",
|
|
670
669
|
className: mx4(onCancel && !readonly && "grid grid-cols-2 gap-2")
|
|
@@ -942,7 +941,7 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
|
|
|
942
941
|
]);
|
|
943
942
|
return /* @__PURE__ */ React10.createElement("div", {
|
|
944
943
|
role: "form",
|
|
945
|
-
className: mx6("flex flex-col w-full gap-2
|
|
944
|
+
className: mx6("flex flex-col w-full gap-2")
|
|
946
945
|
}, properties.map((property) => {
|
|
947
946
|
return /* @__PURE__ */ React10.createElement(FormField, {
|
|
948
947
|
key: property.name,
|
|
@@ -1519,6 +1518,7 @@ export {
|
|
|
1519
1518
|
parseValue,
|
|
1520
1519
|
pathNotUniqueError,
|
|
1521
1520
|
translations_default as translations,
|
|
1522
|
-
useForm
|
|
1521
|
+
useForm,
|
|
1522
|
+
useFormValues
|
|
1523
1523
|
};
|
|
1524
1524
|
//# sourceMappingURL=index.mjs.map
|