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