@dxos/react-ui-form 0.7.5-feature-compute.4d9d99a → 0.7.5-labs.a279d8c
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 +416 -409
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +461 -469
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +416 -409
- 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/ArrayField.d.ts +16 -0
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -0
- package/dist/types/src/components/Form/Defaults.d.ts +5 -6
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +10 -20
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +2 -1
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts +7 -0
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormContent.d.ts +26 -0
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -0
- package/dist/types/src/components/Form/FormContext.d.ts +20 -0
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -0
- package/dist/types/src/components/Form/Input.d.ts +5 -6
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -2
- package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/factory.d.ts +2 -2
- package/dist/types/src/components/Form/factory.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +9 -10
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/package.json +23 -23
- package/src/components/FieldEditor/FieldEditor.tsx +4 -4
- package/src/components/Form/ArrayField.tsx +111 -0
- package/src/components/Form/Defaults.tsx +18 -33
- package/src/components/Form/Form.stories.tsx +15 -5
- package/src/components/Form/Form.tsx +37 -359
- package/src/components/Form/FormActions.tsx +41 -0
- package/src/components/Form/FormContent.tsx +155 -0
- package/src/components/Form/FormContext.tsx +48 -0
- package/src/components/Form/Input.tsx +5 -6
- package/src/components/Form/custom/GeoPointInput.tsx +6 -15
- package/src/components/Form/custom/TupleInput.tsx +5 -9
- package/src/components/Form/factory.tsx +2 -5
- package/src/hooks/useForm.ts +54 -46
|
@@ -80,11 +80,11 @@ var DeprecatedFormInput = ({ label, description, secondary, children }) => {
|
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
// packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
|
|
83
|
-
import
|
|
83
|
+
import React11, { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo5, useState as useState2 } from "react";
|
|
84
84
|
import { FormatEnum as FormatEnum2, FormatEnums, formatToType } from "@dxos/echo-schema";
|
|
85
|
-
import { log as
|
|
86
|
-
import { useTranslation as
|
|
87
|
-
import { getFormatSchema, getSchemaProperties as
|
|
85
|
+
import { log as log2 } from "@dxos/log";
|
|
86
|
+
import { useTranslation as useTranslation4 } from "@dxos/react-ui";
|
|
87
|
+
import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
|
|
88
88
|
|
|
89
89
|
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
90
90
|
import React3 from "react";
|
|
@@ -111,10 +111,10 @@ var InputHeader = ({ children, error }) => {
|
|
|
111
111
|
};
|
|
112
112
|
|
|
113
113
|
// packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
|
|
114
|
-
var GeoPointInput = ({
|
|
114
|
+
var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
115
115
|
const { t } = useTranslation(translationKey);
|
|
116
|
-
const { status, error } = getStatus
|
|
117
|
-
const [lng = 0, lat = 0] =
|
|
116
|
+
const { status, error } = getStatus();
|
|
117
|
+
const [lng = 0, lat = 0] = getValue3() ?? [];
|
|
118
118
|
return /* @__PURE__ */ React3.createElement(Input2.Root, {
|
|
119
119
|
validationValence: status
|
|
120
120
|
}, /* @__PURE__ */ React3.createElement(InputHeader, {
|
|
@@ -126,7 +126,7 @@ var GeoPointInput = ({ property, type, label, disabled, getStatus, getValue: get
|
|
|
126
126
|
disabled,
|
|
127
127
|
placeholder: t("placeholder longitude"),
|
|
128
128
|
value: lng,
|
|
129
|
-
onChange: (event) => onValueChange(
|
|
129
|
+
onChange: (event) => onValueChange(type, [
|
|
130
130
|
lat,
|
|
131
131
|
safeParseFloat(event.target.value, 0)
|
|
132
132
|
]),
|
|
@@ -136,7 +136,7 @@ var GeoPointInput = ({ property, type, label, disabled, getStatus, getValue: get
|
|
|
136
136
|
disabled,
|
|
137
137
|
placeholder: t("placeholder latitude"),
|
|
138
138
|
value: lat,
|
|
139
|
-
onChange: (event) => onValueChange(
|
|
139
|
+
onChange: (event) => onValueChange(type, [
|
|
140
140
|
safeParseFloat(event.target.value, 0),
|
|
141
141
|
lng
|
|
142
142
|
]),
|
|
@@ -155,9 +155,9 @@ var gridCols = [
|
|
|
155
155
|
"grid-cols-3",
|
|
156
156
|
"grid-cols-4"
|
|
157
157
|
];
|
|
158
|
-
var TupleInput = ({ binding,
|
|
159
|
-
const { status, error } = getStatus
|
|
160
|
-
const values =
|
|
158
|
+
var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
159
|
+
const { status, error } = getStatus();
|
|
160
|
+
const values = getValue3() ?? {};
|
|
161
161
|
return /* @__PURE__ */ React4.createElement(Input3.Root, {
|
|
162
162
|
validationValence: status
|
|
163
163
|
}, /* @__PURE__ */ React4.createElement(InputHeader, {
|
|
@@ -169,7 +169,7 @@ var TupleInput = ({ binding, property, type, label, disabled, getStatus, getValu
|
|
|
169
169
|
type: "number",
|
|
170
170
|
disabled,
|
|
171
171
|
value: values[prop],
|
|
172
|
-
onChange: (event) => onValueChange(
|
|
172
|
+
onChange: (event) => onValueChange(type, {
|
|
173
173
|
...values,
|
|
174
174
|
[prop]: safeParseFloat2(event.target.value, 0)
|
|
175
175
|
}),
|
|
@@ -180,8 +180,8 @@ var TupleInput = ({ binding, property, type, label, disabled, getStatus, getValu
|
|
|
180
180
|
// packages/ui/react-ui-form/src/components/Form/Defaults.tsx
|
|
181
181
|
import React5 from "react";
|
|
182
182
|
import { Input as Input4, Select } from "@dxos/react-ui";
|
|
183
|
-
var TextInput = ({
|
|
184
|
-
const { status, error } = getStatus
|
|
183
|
+
var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
184
|
+
const { status, error } = getStatus();
|
|
185
185
|
return /* @__PURE__ */ React5.createElement(Input4.Root, {
|
|
186
186
|
validationValence: status
|
|
187
187
|
}, !inputOnly && /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
@@ -189,13 +189,13 @@ var TextInput = ({ property, type, label, inputOnly, disabled, placeholder, getS
|
|
|
189
189
|
}, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement(Input4.TextInput, {
|
|
190
190
|
disabled,
|
|
191
191
|
placeholder,
|
|
192
|
-
value:
|
|
193
|
-
onChange: (event) => onValueChange(
|
|
192
|
+
value: getValue3() ?? "",
|
|
193
|
+
onChange: (event) => onValueChange(type, event.target.value),
|
|
194
194
|
onBlur
|
|
195
195
|
}), inputOnly && /* @__PURE__ */ React5.createElement(Input4.Validation, null, error));
|
|
196
196
|
};
|
|
197
|
-
var NumberInput = ({
|
|
198
|
-
const { status, error } = getStatus
|
|
197
|
+
var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
|
|
198
|
+
const { status, error } = getStatus();
|
|
199
199
|
return /* @__PURE__ */ React5.createElement(Input4.Root, {
|
|
200
200
|
validationValence: status
|
|
201
201
|
}, !inputOnly && /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
@@ -204,31 +204,31 @@ var NumberInput = ({ property, type, label, inputOnly, disabled, placeholder, ge
|
|
|
204
204
|
type: "number",
|
|
205
205
|
disabled,
|
|
206
206
|
placeholder,
|
|
207
|
-
value:
|
|
208
|
-
onChange: (event) => onValueChange(
|
|
207
|
+
value: getValue3() ?? 0,
|
|
208
|
+
onChange: (event) => onValueChange(type, event.target.value),
|
|
209
209
|
onBlur
|
|
210
210
|
}), inputOnly && /* @__PURE__ */ React5.createElement(Input4.DescriptionAndValidation, null, error));
|
|
211
211
|
};
|
|
212
|
-
var BooleanInput = ({
|
|
213
|
-
const { status, error } = getStatus
|
|
212
|
+
var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
|
|
213
|
+
const { status, error } = getStatus();
|
|
214
214
|
return /* @__PURE__ */ React5.createElement(Input4.Root, {
|
|
215
215
|
validationValence: status
|
|
216
216
|
}, !inputOnly && /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
217
217
|
error
|
|
218
218
|
}, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement(Input4.Switch, {
|
|
219
|
-
checked:
|
|
220
|
-
onCheckedChange: (value) => onValueChange(
|
|
219
|
+
checked: getValue3(),
|
|
220
|
+
onCheckedChange: (value) => onValueChange(type, value)
|
|
221
221
|
}), inputOnly && /* @__PURE__ */ React5.createElement(Input4.DescriptionAndValidation, null, error));
|
|
222
222
|
};
|
|
223
|
-
var SelectInput = ({
|
|
224
|
-
const { status, error } = getStatus
|
|
223
|
+
var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
|
|
224
|
+
const { status, error } = getStatus();
|
|
225
225
|
return /* @__PURE__ */ React5.createElement(Input4.Root, {
|
|
226
226
|
validationValence: status
|
|
227
227
|
}, !inputOnly && /* @__PURE__ */ React5.createElement(InputHeader, {
|
|
228
228
|
error
|
|
229
229
|
}, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement(Select.Root, {
|
|
230
|
-
value:
|
|
231
|
-
onValueChange: (value) => onValueChange(
|
|
230
|
+
value: getValue3(),
|
|
231
|
+
onValueChange: (value) => onValueChange(type, value)
|
|
232
232
|
}, /* @__PURE__ */ React5.createElement(Select.TriggerButton, {
|
|
233
233
|
classNames: "is-full",
|
|
234
234
|
disabled,
|
|
@@ -243,38 +243,24 @@ var SelectInput = ({ property, type, label, inputOnly, disabled, placeholder, op
|
|
|
243
243
|
};
|
|
244
244
|
|
|
245
245
|
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
246
|
-
import {
|
|
247
|
-
import {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
import
|
|
251
|
-
import { log as log2 } from "@dxos/log";
|
|
246
|
+
import React10, { useMemo as useMemo4 } from "react";
|
|
247
|
+
import { mx as mx5 } from "@dxos/react-ui-theme";
|
|
248
|
+
|
|
249
|
+
// packages/ui/react-ui-form/src/components/Form/FormActions.tsx
|
|
250
|
+
import React7 from "react";
|
|
252
251
|
import { IconButton, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
253
252
|
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
254
|
-
import { getSchemaProperties } from "@dxos/schema";
|
|
255
|
-
import { isNotFalsy } from "@dxos/util";
|
|
256
253
|
|
|
257
|
-
// packages/ui/react-ui-form/src/components/Form/
|
|
258
|
-
import {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return GeoPointInput;
|
|
263
|
-
}
|
|
264
|
-
switch (type) {
|
|
265
|
-
case "string":
|
|
266
|
-
return TextInput;
|
|
267
|
-
case "number":
|
|
268
|
-
return NumberInput;
|
|
269
|
-
case "boolean":
|
|
270
|
-
return BooleanInput;
|
|
271
|
-
}
|
|
272
|
-
};
|
|
254
|
+
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
255
|
+
import React6, { createContext, useContext } from "react";
|
|
256
|
+
import { raise } from "@dxos/debug";
|
|
257
|
+
import { getValue as getValue2 } from "@dxos/echo-schema";
|
|
258
|
+
import { createJsonPath as createJsonPath2 } from "@dxos/effect";
|
|
273
259
|
|
|
274
260
|
// packages/ui/react-ui-form/src/hooks/useForm.ts
|
|
275
261
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
276
262
|
import { getValue, setValue } from "@dxos/echo-schema";
|
|
277
|
-
import { AST } from "@dxos/effect";
|
|
263
|
+
import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
|
|
278
264
|
import { invariant } from "@dxos/invariant";
|
|
279
265
|
import { log } from "@dxos/log";
|
|
280
266
|
import { validateSchema } from "@dxos/schema";
|
|
@@ -325,8 +311,8 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
325
311
|
values,
|
|
326
312
|
schemaDirty
|
|
327
313
|
]);
|
|
328
|
-
const canSave = useMemo(() => !saving &&
|
|
329
|
-
|
|
314
|
+
const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
|
|
315
|
+
!Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
|
|
330
316
|
touched,
|
|
331
317
|
errors,
|
|
332
318
|
saving
|
|
@@ -348,17 +334,21 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
348
334
|
onSave,
|
|
349
335
|
changed
|
|
350
336
|
]);
|
|
351
|
-
const getStatus = useCallback((
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
337
|
+
const getStatus = useCallback((path) => {
|
|
338
|
+
const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
|
|
339
|
+
const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
|
|
340
|
+
return {
|
|
341
|
+
status: matchingError ? "error" : void 0,
|
|
342
|
+
error: matchingError ? matchingError[1] : void 0
|
|
343
|
+
};
|
|
344
|
+
}, [
|
|
356
345
|
errors
|
|
357
346
|
]);
|
|
358
|
-
const getFormValue = (
|
|
359
|
-
return getValue(values,
|
|
347
|
+
const getFormValue = (path) => {
|
|
348
|
+
return getValue(values, createJsonPath(path));
|
|
360
349
|
};
|
|
361
|
-
const onValueChange = (
|
|
350
|
+
const onValueChange = (path, type, value) => {
|
|
351
|
+
const jsonPath = createJsonPath(path);
|
|
362
352
|
let parsedValue = value;
|
|
363
353
|
try {
|
|
364
354
|
if (type === "number") {
|
|
@@ -367,19 +357,19 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
367
357
|
} catch (err) {
|
|
368
358
|
log.catch(err, void 0, {
|
|
369
359
|
F: __dxlog_file,
|
|
370
|
-
L:
|
|
360
|
+
L: 194,
|
|
371
361
|
S: void 0,
|
|
372
362
|
C: (f, a) => f(...a)
|
|
373
363
|
});
|
|
374
364
|
parsedValue = void 0;
|
|
375
365
|
}
|
|
376
366
|
const newValues = {
|
|
377
|
-
...setValue(values,
|
|
367
|
+
...setValue(values, jsonPath, parsedValue)
|
|
378
368
|
};
|
|
379
369
|
setValues(newValues);
|
|
380
370
|
setChanged((prev) => ({
|
|
381
371
|
...prev,
|
|
382
|
-
[
|
|
372
|
+
[jsonPath]: true
|
|
383
373
|
}));
|
|
384
374
|
onValuesChanged?.(newValues);
|
|
385
375
|
const isValid = validate(newValues);
|
|
@@ -389,15 +379,12 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
389
379
|
});
|
|
390
380
|
}
|
|
391
381
|
};
|
|
392
|
-
const
|
|
393
|
-
const
|
|
382
|
+
const onTouched = useCallback((path) => {
|
|
383
|
+
const jsonPath = createJsonPath(path);
|
|
394
384
|
setTouched((touched2) => ({
|
|
395
385
|
...touched2,
|
|
396
|
-
[
|
|
386
|
+
[jsonPath]: true
|
|
397
387
|
}));
|
|
398
|
-
if (event.relatedTarget?.getAttribute("type") === "submit") {
|
|
399
|
-
setTouched(createKeySet(values, true));
|
|
400
|
-
}
|
|
401
388
|
validate(values);
|
|
402
389
|
}, [
|
|
403
390
|
validate,
|
|
@@ -415,13 +402,13 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
|
|
|
415
402
|
getStatus,
|
|
416
403
|
getValue: getFormValue,
|
|
417
404
|
onValueChange,
|
|
418
|
-
|
|
405
|
+
onTouched
|
|
419
406
|
};
|
|
420
407
|
};
|
|
421
408
|
var createKeySet = (obj, value) => {
|
|
422
409
|
invariant(obj, void 0, {
|
|
423
410
|
F: __dxlog_file,
|
|
424
|
-
L:
|
|
411
|
+
L: 236,
|
|
425
412
|
S: void 0,
|
|
426
413
|
A: [
|
|
427
414
|
"obj",
|
|
@@ -435,338 +422,359 @@ var createKeySet = (obj, value) => {
|
|
|
435
422
|
};
|
|
436
423
|
var flatMap = (errors) => {
|
|
437
424
|
return errors.reduce((result, { path, message }) => {
|
|
438
|
-
|
|
439
|
-
|
|
425
|
+
const jsonPath = fromEffectValidationPath(path);
|
|
426
|
+
if (!(jsonPath in result)) {
|
|
427
|
+
result[jsonPath] = message;
|
|
440
428
|
}
|
|
441
429
|
return result;
|
|
442
430
|
}, {});
|
|
443
431
|
};
|
|
444
432
|
|
|
445
|
-
// packages/ui/react-ui-form/src/components/Form/
|
|
446
|
-
var
|
|
433
|
+
// packages/ui/react-ui-form/src/components/Form/FormContext.tsx
|
|
434
|
+
var FormContext = /* @__PURE__ */ createContext(void 0);
|
|
435
|
+
var useFormContext = () => {
|
|
436
|
+
return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
|
|
437
|
+
};
|
|
438
|
+
var useFormValues = (path = []) => {
|
|
439
|
+
const { values: formValues } = useFormContext();
|
|
440
|
+
const jsonPath = createJsonPath2(path);
|
|
441
|
+
return getValue2(formValues, jsonPath);
|
|
442
|
+
};
|
|
443
|
+
var useInputProps = (path = []) => {
|
|
444
|
+
const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
|
|
445
|
+
return {
|
|
446
|
+
getStatus: () => getStatus(path),
|
|
447
|
+
getValue: () => getFormValue(path),
|
|
448
|
+
onValueChange: (type, value) => onValueChange(path, type, value),
|
|
449
|
+
onBlur: () => onTouched(path)
|
|
450
|
+
};
|
|
451
|
+
};
|
|
452
|
+
var FormProvider = ({ children, ...formOptions }) => {
|
|
453
|
+
const formHandler = useForm(formOptions);
|
|
454
|
+
return /* @__PURE__ */ React6.createElement(FormContext.Provider, {
|
|
455
|
+
value: formHandler
|
|
456
|
+
}, children);
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// packages/ui/react-ui-form/src/components/Form/FormActions.tsx
|
|
460
|
+
var FormActions = ({ onCancel, readonly }) => {
|
|
461
|
+
const { t } = useTranslation2(translationKey);
|
|
462
|
+
const { canSave, handleSave } = useFormContext();
|
|
463
|
+
return /* @__PURE__ */ React7.createElement("div", {
|
|
464
|
+
role: "none",
|
|
465
|
+
className: "flex justify-center"
|
|
466
|
+
}, /* @__PURE__ */ React7.createElement("div", {
|
|
467
|
+
role: "none",
|
|
468
|
+
className: mx2(onCancel && !readonly && "grid grid-cols-2 gap-2")
|
|
469
|
+
}, onCancel && !readonly && /* @__PURE__ */ React7.createElement(IconButton, {
|
|
470
|
+
"data-testid": "cancel-button",
|
|
471
|
+
icon: "ph--x--regular",
|
|
472
|
+
label: t("button cancel"),
|
|
473
|
+
onClick: onCancel
|
|
474
|
+
}), handleSave && /* @__PURE__ */ React7.createElement(IconButton, {
|
|
475
|
+
type: "submit",
|
|
476
|
+
"data-testid": "save-button",
|
|
477
|
+
disabled: !canSave,
|
|
478
|
+
icon: "ph--check--regular",
|
|
479
|
+
label: t("button save"),
|
|
480
|
+
onClick: handleSave
|
|
481
|
+
})));
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// packages/ui/react-ui-form/src/components/Form/FormContent.tsx
|
|
485
|
+
import { pipe as pipe2 } from "effect";
|
|
486
|
+
import { capitalize as capitalize2 } from "effect/String";
|
|
487
|
+
import React9, { useMemo as useMemo3 } from "react";
|
|
488
|
+
import { AST as AST3, S } from "@dxos/echo-schema";
|
|
489
|
+
import { createJsonPath as createJsonPath3, findNode as findNode2, getDiscriminatedType as getDiscriminatedType2, isDiscriminatedUnion as isDiscriminatedUnion2 } from "@dxos/effect";
|
|
490
|
+
import { mx as mx4 } from "@dxos/react-ui-theme";
|
|
491
|
+
import { getSchemaProperties as getSchemaProperties2 } from "@dxos/schema";
|
|
492
|
+
import { isNotFalsy } from "@dxos/util";
|
|
493
|
+
|
|
494
|
+
// packages/ui/react-ui-form/src/components/Form/ArrayField.tsx
|
|
495
|
+
import { pipe } from "effect";
|
|
496
|
+
import { capitalize } from "effect/String";
|
|
497
|
+
import React8, { useCallback as useCallback2, useMemo as useMemo2 } from "react";
|
|
498
|
+
import { AST as AST2 } from "@dxos/echo-schema";
|
|
499
|
+
import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
|
|
500
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
501
|
+
import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
|
|
502
|
+
import { mx as mx3 } from "@dxos/react-ui-theme";
|
|
503
|
+
import { getSchemaProperties } from "@dxos/schema";
|
|
504
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/ArrayField.tsx";
|
|
447
505
|
var padding = "px-2";
|
|
448
|
-
var
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
506
|
+
var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponent }) => {
|
|
507
|
+
const { t } = useTranslation3(translationKey);
|
|
508
|
+
const { ast, name, type, title } = property;
|
|
509
|
+
const values = useFormValues(path ?? []);
|
|
510
|
+
invariant2(Array.isArray(values), void 0, {
|
|
511
|
+
F: __dxlog_file2,
|
|
512
|
+
L: 37,
|
|
513
|
+
S: void 0,
|
|
514
|
+
A: [
|
|
515
|
+
"Array.isArray(values)",
|
|
516
|
+
""
|
|
517
|
+
]
|
|
460
518
|
});
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
519
|
+
const label = title ?? pipe(name, capitalize);
|
|
520
|
+
const tupleType = findNode(ast, AST2.isTupleType);
|
|
521
|
+
const elementType = tupleType?.rest[0]?.type;
|
|
522
|
+
const getDefaultObjectValue = (typeNode) => {
|
|
523
|
+
const baseNode = findNode(typeNode, isDiscriminatedUnion);
|
|
524
|
+
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, {}) : findNode(typeNode, AST2.isTypeLiteral);
|
|
525
|
+
if (!typeLiteral) {
|
|
526
|
+
return {};
|
|
527
|
+
}
|
|
528
|
+
return Object.fromEntries(getSchemaProperties(typeLiteral, {}).map((prop) => [
|
|
529
|
+
prop.name,
|
|
530
|
+
prop.defaultValue
|
|
531
|
+
]));
|
|
532
|
+
};
|
|
533
|
+
const newValue = useMemo2(() => {
|
|
534
|
+
if (type === "object" && elementType) {
|
|
535
|
+
return getDefaultObjectValue(elementType);
|
|
536
|
+
} else {
|
|
537
|
+
return SimpleType.getDefaultValue(type);
|
|
538
|
+
}
|
|
469
539
|
}, [
|
|
470
|
-
|
|
471
|
-
|
|
540
|
+
type,
|
|
541
|
+
elementType
|
|
542
|
+
]);
|
|
543
|
+
const handleAdd = useCallback2(() => {
|
|
544
|
+
inputProps.onValueChange(type, [
|
|
545
|
+
...values,
|
|
546
|
+
newValue
|
|
547
|
+
]);
|
|
548
|
+
}, [
|
|
549
|
+
type,
|
|
550
|
+
elementType,
|
|
551
|
+
inputProps,
|
|
552
|
+
newValue,
|
|
472
553
|
values
|
|
473
554
|
]);
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
errors
|
|
478
|
-
}, {
|
|
479
|
-
F: __dxlog_file2,
|
|
480
|
-
L: 106,
|
|
481
|
-
S: void 0,
|
|
482
|
-
C: (f, a) => f(...a)
|
|
483
|
-
});
|
|
484
|
-
}
|
|
555
|
+
const handleRemove = useCallback2((index) => {
|
|
556
|
+
const newValues = values.filter((_, i) => i !== index);
|
|
557
|
+
inputProps.onValueChange(type, newValues);
|
|
485
558
|
}, [
|
|
486
|
-
|
|
559
|
+
type,
|
|
560
|
+
inputProps,
|
|
561
|
+
values
|
|
487
562
|
]);
|
|
488
|
-
|
|
489
|
-
role: "form",
|
|
490
|
-
className: mx2("flex flex-col w-full gap-2 py-2", classNames),
|
|
491
|
-
"data-testid": testId
|
|
492
|
-
}, properties.map((property) => {
|
|
493
|
-
const { ast, name, type, format, title, description, examples, options, array } = property;
|
|
494
|
-
const key = [
|
|
495
|
-
...path,
|
|
496
|
-
name
|
|
497
|
-
];
|
|
498
|
-
const label = title ?? pipe(name, capitalize);
|
|
499
|
-
const placeholder = examples?.length ? `Example: "${examples[0]}"` : description;
|
|
500
|
-
const FoundComponent = lookupComponent?.({
|
|
501
|
-
prop: name,
|
|
502
|
-
schema: S.make(ast),
|
|
503
|
-
inputProps: {
|
|
504
|
-
property: name,
|
|
505
|
-
type,
|
|
506
|
-
format,
|
|
507
|
-
label,
|
|
508
|
-
disabled: readonly,
|
|
509
|
-
placeholder,
|
|
510
|
-
...inputProps
|
|
511
|
-
}
|
|
512
|
-
});
|
|
513
|
-
if (FoundComponent) {
|
|
514
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
515
|
-
key: name,
|
|
516
|
-
role: "none",
|
|
517
|
-
className: padding
|
|
518
|
-
}, FoundComponent);
|
|
519
|
-
}
|
|
520
|
-
let InputComponent = Custom?.[key.join(".")];
|
|
521
|
-
if (!InputComponent) {
|
|
522
|
-
if (options) {
|
|
523
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
524
|
-
key: name,
|
|
525
|
-
role: "none",
|
|
526
|
-
className: padding
|
|
527
|
-
}, /* @__PURE__ */ React6.createElement(SelectInput, {
|
|
528
|
-
type,
|
|
529
|
-
format,
|
|
530
|
-
property: name,
|
|
531
|
-
disabled: readonly,
|
|
532
|
-
label,
|
|
533
|
-
options: options.map((option) => ({
|
|
534
|
-
value: option,
|
|
535
|
-
label: String(option)
|
|
536
|
-
})),
|
|
537
|
-
placeholder,
|
|
538
|
-
...inputProps
|
|
539
|
-
}));
|
|
540
|
-
}
|
|
541
|
-
InputComponent = getInputComponent(type, format);
|
|
542
|
-
if (array) {
|
|
543
|
-
return /* @__PURE__ */ React6.createElement(ArrayField, {
|
|
544
|
-
key: name,
|
|
545
|
-
name,
|
|
546
|
-
type,
|
|
547
|
-
array: true,
|
|
548
|
-
label,
|
|
549
|
-
ast,
|
|
550
|
-
values,
|
|
551
|
-
format,
|
|
552
|
-
readonly,
|
|
553
|
-
placeholder,
|
|
554
|
-
inputProps,
|
|
555
|
-
Custom
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
if (!InputComponent) {
|
|
560
|
-
if (type === "object") {
|
|
561
|
-
const baseNode = findNode(ast, isDiscriminatedUnion);
|
|
562
|
-
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(ast, AST2.isTypeLiteral);
|
|
563
|
-
if (typeLiteral) {
|
|
564
|
-
const schema2 = S.make(typeLiteral);
|
|
565
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
566
|
-
key: name,
|
|
567
|
-
role: "none"
|
|
568
|
-
}, /* @__PURE__ */ React6.createElement("div", {
|
|
569
|
-
className: padding
|
|
570
|
-
}, label), /* @__PURE__ */ React6.createElement(Form, {
|
|
571
|
-
schema: schema2,
|
|
572
|
-
path: key,
|
|
573
|
-
values: values[name] ?? {},
|
|
574
|
-
onValuesChanged: (childValues) => {
|
|
575
|
-
inputProps.onValueChange(name, "object", childValues);
|
|
576
|
-
},
|
|
577
|
-
Custom
|
|
578
|
-
}));
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
log2("no renderer for property", {
|
|
582
|
-
name,
|
|
583
|
-
type
|
|
584
|
-
}, {
|
|
585
|
-
F: __dxlog_file2,
|
|
586
|
-
L: 213,
|
|
587
|
-
S: void 0,
|
|
588
|
-
C: (f, a) => f(...a)
|
|
589
|
-
});
|
|
590
|
-
return null;
|
|
591
|
-
}
|
|
592
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
593
|
-
key: name,
|
|
594
|
-
role: "none",
|
|
595
|
-
className: padding
|
|
596
|
-
}, /* @__PURE__ */ React6.createElement(InputComponent, {
|
|
597
|
-
type,
|
|
598
|
-
format,
|
|
599
|
-
property: name,
|
|
600
|
-
disabled: readonly,
|
|
601
|
-
label,
|
|
602
|
-
placeholder,
|
|
603
|
-
...inputProps
|
|
604
|
-
}));
|
|
605
|
-
}).filter(isNotFalsy), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React6.createElement(FormActions, {
|
|
606
|
-
onCancel,
|
|
607
|
-
onSubmit: handleSave,
|
|
608
|
-
canSubmit: canSave,
|
|
609
|
-
readonly
|
|
610
|
-
}));
|
|
611
|
-
};
|
|
612
|
-
var ArrayField = ({ name, type, label, ast, values, format, readonly, placeholder, inputProps, Custom }) => {
|
|
613
|
-
const { t } = useTranslation2(translationKey);
|
|
614
|
-
const arrayValues = values[name] ?? [];
|
|
615
|
-
const key = [
|
|
616
|
-
name
|
|
617
|
-
];
|
|
618
|
-
const tupleType = findNode(ast, AST2.isTupleType);
|
|
619
|
-
const elementType = tupleType?.rest[0]?.type;
|
|
620
|
-
if (type === "object" && elementType) {
|
|
621
|
-
const baseNode = findNode(elementType, isDiscriminatedUnion);
|
|
622
|
-
const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(elementType, AST2.isTypeLiteral);
|
|
623
|
-
if (typeLiteral) {
|
|
624
|
-
const schema = S.make(typeLiteral);
|
|
625
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
626
|
-
key: name,
|
|
627
|
-
role: "none",
|
|
628
|
-
className: mx2(padding)
|
|
629
|
-
}, /* @__PURE__ */ React6.createElement(InputHeader, null, label), /* @__PURE__ */ React6.createElement("div", {
|
|
630
|
-
role: "none",
|
|
631
|
-
className: "flex flex-col gap-1"
|
|
632
|
-
}, arrayValues.map((value, index) => {
|
|
633
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
634
|
-
key: index,
|
|
635
|
-
role: "none",
|
|
636
|
-
className: "flex items-center gap-1"
|
|
637
|
-
}, /* @__PURE__ */ React6.createElement("div", {
|
|
638
|
-
role: "none",
|
|
639
|
-
className: "flex-1"
|
|
640
|
-
}, /* @__PURE__ */ React6.createElement(Form, {
|
|
641
|
-
schema,
|
|
642
|
-
path: [
|
|
643
|
-
...key,
|
|
644
|
-
`[${index}]`
|
|
645
|
-
],
|
|
646
|
-
values: value,
|
|
647
|
-
onValuesChanged: (childValues) => {
|
|
648
|
-
const newValues = [
|
|
649
|
-
...arrayValues
|
|
650
|
-
];
|
|
651
|
-
newValues[index] = childValues;
|
|
652
|
-
inputProps.onValueChange(name, "object", newValues);
|
|
653
|
-
},
|
|
654
|
-
Custom
|
|
655
|
-
})), /* @__PURE__ */ React6.createElement(IconButton, {
|
|
656
|
-
icon: "ph--trash--regular",
|
|
657
|
-
iconOnly: true,
|
|
658
|
-
label: t("button remove"),
|
|
659
|
-
onClick: () => {
|
|
660
|
-
const newValues = arrayValues.filter((_, i) => i !== index);
|
|
661
|
-
inputProps.onValueChange(name, type, newValues);
|
|
662
|
-
}
|
|
663
|
-
}));
|
|
664
|
-
})), /* @__PURE__ */ React6.createElement("div", {
|
|
665
|
-
role: "none",
|
|
666
|
-
className: "flex justify-between items-center plb-1"
|
|
667
|
-
}, /* @__PURE__ */ React6.createElement(IconButton, {
|
|
668
|
-
icon: "ph--plus--regular",
|
|
669
|
-
iconOnly: true,
|
|
670
|
-
label: t("button add"),
|
|
671
|
-
onClick: () => {
|
|
672
|
-
const newValues = [
|
|
673
|
-
...arrayValues,
|
|
674
|
-
{}
|
|
675
|
-
];
|
|
676
|
-
inputProps.onValueChange(name, type, newValues);
|
|
677
|
-
}
|
|
678
|
-
})));
|
|
679
|
-
}
|
|
680
|
-
return /* @__PURE__ */ React6.createElement("div", {
|
|
681
|
-
key: name,
|
|
682
|
-
role: "none"
|
|
683
|
-
}, "Nested form not supported in arrays, yet.");
|
|
684
|
-
}
|
|
685
|
-
const InputComponent = getInputComponent(type, format);
|
|
686
|
-
if (!InputComponent) {
|
|
563
|
+
if (!elementType) {
|
|
687
564
|
return null;
|
|
688
565
|
}
|
|
689
|
-
return /* @__PURE__ */
|
|
566
|
+
return /* @__PURE__ */ React8.createElement("div", {
|
|
690
567
|
role: "none",
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
}, /* @__PURE__ */ React6.createElement(InputHeader, null, label), /* @__PURE__ */ React6.createElement("div", {
|
|
568
|
+
className: mx3(padding)
|
|
569
|
+
}, /* @__PURE__ */ React8.createElement(InputHeader, null, label), /* @__PURE__ */ React8.createElement("div", {
|
|
694
570
|
role: "none",
|
|
695
571
|
className: "flex flex-col gap-1"
|
|
696
|
-
},
|
|
572
|
+
}, values.map((_value, index) => /* @__PURE__ */ React8.createElement("div", {
|
|
697
573
|
key: index,
|
|
698
574
|
role: "none",
|
|
699
|
-
className: "flex items-
|
|
700
|
-
}, /* @__PURE__ */
|
|
575
|
+
className: "flex items-center gap-1"
|
|
576
|
+
}, /* @__PURE__ */ React8.createElement("div", {
|
|
701
577
|
role: "none",
|
|
702
578
|
className: "flex-1"
|
|
703
|
-
}, /* @__PURE__ */
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
579
|
+
}, /* @__PURE__ */ React8.createElement(FormField, {
|
|
580
|
+
property: {
|
|
581
|
+
...property,
|
|
582
|
+
array: false,
|
|
583
|
+
ast: elementType
|
|
584
|
+
},
|
|
585
|
+
path: [
|
|
586
|
+
...path ?? [],
|
|
587
|
+
index
|
|
588
|
+
],
|
|
589
|
+
readonly,
|
|
590
|
+
inline: true,
|
|
591
|
+
Custom,
|
|
592
|
+
lookupComponent
|
|
593
|
+
})), /* @__PURE__ */ React8.createElement(IconButton2, {
|
|
713
594
|
icon: "ph--trash--regular",
|
|
714
595
|
iconOnly: true,
|
|
715
596
|
label: t("button remove"),
|
|
716
|
-
onClick: () =>
|
|
717
|
-
|
|
718
|
-
inputProps.onValueChange(name, type, newValues);
|
|
719
|
-
}
|
|
720
|
-
})))), /* @__PURE__ */ React6.createElement("div", {
|
|
597
|
+
onClick: () => handleRemove(index)
|
|
598
|
+
})))), /* @__PURE__ */ React8.createElement("div", {
|
|
721
599
|
role: "none",
|
|
722
600
|
className: "flex justify-between items-center plb-1"
|
|
723
|
-
}, /* @__PURE__ */
|
|
601
|
+
}, /* @__PURE__ */ React8.createElement(IconButton2, {
|
|
724
602
|
icon: "ph--plus--regular",
|
|
725
603
|
iconOnly: true,
|
|
726
604
|
label: t("button add"),
|
|
727
|
-
onClick:
|
|
728
|
-
const newValues = [
|
|
729
|
-
...arrayValues,
|
|
730
|
-
SimpleType.getDefaultValue(type)
|
|
731
|
-
];
|
|
732
|
-
inputProps.onValueChange(name, type, newValues);
|
|
733
|
-
}
|
|
605
|
+
onClick: handleAdd
|
|
734
606
|
})));
|
|
735
607
|
};
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
608
|
+
|
|
609
|
+
// packages/ui/react-ui-form/src/components/Form/factory.tsx
|
|
610
|
+
import { FormatEnum } from "@dxos/echo-schema";
|
|
611
|
+
var getInputComponent = (type, format) => {
|
|
612
|
+
switch (format) {
|
|
613
|
+
case FormatEnum.LatLng:
|
|
614
|
+
return GeoPointInput;
|
|
615
|
+
}
|
|
616
|
+
switch (type) {
|
|
617
|
+
case "string":
|
|
618
|
+
return TextInput;
|
|
619
|
+
case "number":
|
|
620
|
+
return NumberInput;
|
|
621
|
+
case "boolean":
|
|
622
|
+
return BooleanInput;
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
// packages/ui/react-ui-form/src/components/Form/FormContent.tsx
|
|
627
|
+
var FormField = ({ property, path, readonly, inline, lookupComponent, Custom }) => {
|
|
628
|
+
const inputProps = useInputProps(path);
|
|
629
|
+
const { ast, name, type, format, title, description, options, examples, array } = property;
|
|
630
|
+
const label = useMemo3(() => title ?? pipe2(name, capitalize2), [
|
|
631
|
+
title,
|
|
632
|
+
name
|
|
633
|
+
]);
|
|
634
|
+
const placeholder = useMemo3(() => examples?.length ? `Example: "${examples[0]}"` : description, [
|
|
635
|
+
examples,
|
|
636
|
+
description
|
|
637
|
+
]);
|
|
638
|
+
if (array) {
|
|
639
|
+
return /* @__PURE__ */ React9.createElement(ArrayField, {
|
|
640
|
+
property,
|
|
641
|
+
path,
|
|
642
|
+
inputProps,
|
|
643
|
+
readonly,
|
|
644
|
+
Custom
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
const FoundComponent = lookupComponent?.({
|
|
648
|
+
prop: name,
|
|
649
|
+
schema: S.make(ast),
|
|
650
|
+
inputProps: {
|
|
651
|
+
type,
|
|
652
|
+
format,
|
|
653
|
+
label,
|
|
654
|
+
disabled: readonly,
|
|
655
|
+
placeholder,
|
|
656
|
+
...inputProps
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
if (FoundComponent) {
|
|
660
|
+
return /* @__PURE__ */ React9.createElement("div", null, FoundComponent);
|
|
661
|
+
}
|
|
662
|
+
const jsonPath = createJsonPath3(path ?? []);
|
|
663
|
+
const InputComponent = Custom?.[jsonPath] || getInputComponent(type, format);
|
|
664
|
+
if (InputComponent) {
|
|
665
|
+
return /* @__PURE__ */ React9.createElement("div", null, /* @__PURE__ */ React9.createElement(InputComponent, {
|
|
666
|
+
type,
|
|
667
|
+
format,
|
|
668
|
+
label,
|
|
669
|
+
inputOnly: inline,
|
|
670
|
+
placeholder,
|
|
671
|
+
disabled: readonly,
|
|
672
|
+
...inputProps
|
|
673
|
+
}));
|
|
674
|
+
}
|
|
675
|
+
if (options) {
|
|
676
|
+
return /* @__PURE__ */ React9.createElement("div", {
|
|
677
|
+
role: "none"
|
|
678
|
+
}, /* @__PURE__ */ React9.createElement(SelectInput, {
|
|
679
|
+
type,
|
|
680
|
+
format,
|
|
681
|
+
disabled: readonly,
|
|
682
|
+
inputOnly: inline,
|
|
683
|
+
label,
|
|
684
|
+
options: options.map((option) => ({
|
|
685
|
+
value: option,
|
|
686
|
+
label: String(option)
|
|
687
|
+
})),
|
|
688
|
+
placeholder,
|
|
689
|
+
...inputProps
|
|
690
|
+
}));
|
|
691
|
+
}
|
|
692
|
+
if (type === "object") {
|
|
693
|
+
const baseNode = findNode2(ast, isDiscriminatedUnion2);
|
|
694
|
+
const typeLiteral = baseNode ? getDiscriminatedType2(baseNode, inputProps.getValue()) : findNode2(ast, AST3.isTypeLiteral);
|
|
695
|
+
if (typeLiteral) {
|
|
696
|
+
return /* @__PURE__ */ React9.createElement("div", null, !inline && /* @__PURE__ */ React9.createElement("div", null, label), /* @__PURE__ */ React9.createElement(FormFields, {
|
|
697
|
+
schema: S.make(typeLiteral),
|
|
698
|
+
path,
|
|
699
|
+
readonly,
|
|
700
|
+
Custom
|
|
701
|
+
}));
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return null;
|
|
705
|
+
};
|
|
706
|
+
var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }) => {
|
|
707
|
+
const values = useFormValues(path);
|
|
708
|
+
const properties = useMemo3(() => {
|
|
709
|
+
const props = getSchemaProperties2(schema.ast, values);
|
|
710
|
+
const filtered = filter ? filter(props) : props;
|
|
711
|
+
return sort ? filtered.sort((a, b) => sort.indexOf(a.name) - sort.indexOf(b.name)) : filtered;
|
|
712
|
+
}, [
|
|
713
|
+
schema,
|
|
714
|
+
values,
|
|
715
|
+
filter,
|
|
716
|
+
sort
|
|
717
|
+
]);
|
|
718
|
+
return /* @__PURE__ */ React9.createElement("div", {
|
|
719
|
+
role: "form",
|
|
720
|
+
className: mx4("flex flex-col w-full gap-2 py-2")
|
|
721
|
+
}, properties.map((property) => {
|
|
722
|
+
return /* @__PURE__ */ React9.createElement(FormField, {
|
|
723
|
+
key: property.name,
|
|
724
|
+
property,
|
|
725
|
+
path: [
|
|
726
|
+
...path ?? [],
|
|
727
|
+
property.name
|
|
728
|
+
],
|
|
729
|
+
readonly,
|
|
730
|
+
lookupComponent,
|
|
731
|
+
Custom
|
|
732
|
+
});
|
|
733
|
+
}).filter(isNotFalsy));
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
// packages/ui/react-ui-form/src/components/Form/Form.tsx
|
|
737
|
+
var Form = ({ classNames, schema, values: initialValues, path = [], readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
|
|
738
|
+
const onValid = useMemo4(() => autoSave ? onSave : void 0, [
|
|
739
|
+
autoSave,
|
|
740
|
+
onSave
|
|
741
|
+
]);
|
|
742
|
+
return /* @__PURE__ */ React10.createElement(FormProvider, {
|
|
743
|
+
schema,
|
|
744
|
+
initialValues,
|
|
745
|
+
onValuesChanged,
|
|
746
|
+
onValidate,
|
|
747
|
+
onValid,
|
|
748
|
+
onSave
|
|
749
|
+
}, /* @__PURE__ */ React10.createElement("div", {
|
|
742
750
|
role: "none",
|
|
743
|
-
className:
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
751
|
+
className: mx5("p-2", classNames),
|
|
752
|
+
"data-testid": testId
|
|
753
|
+
}, /* @__PURE__ */ React10.createElement(FormFields, {
|
|
754
|
+
schema,
|
|
755
|
+
path,
|
|
756
|
+
readonly,
|
|
757
|
+
filter,
|
|
758
|
+
sort,
|
|
759
|
+
lookupComponent,
|
|
760
|
+
Custom
|
|
761
|
+
}), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React10.createElement(FormActions, {
|
|
762
|
+
onCancel,
|
|
763
|
+
readonly
|
|
756
764
|
})));
|
|
757
765
|
};
|
|
758
766
|
|
|
759
767
|
// packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
|
|
760
768
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
|
|
761
769
|
var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
762
|
-
const { t } =
|
|
770
|
+
const { t } = useTranslation4(translationKey);
|
|
763
771
|
const [props, setProps] = useState2(projection.getFieldProjection(field.id).props);
|
|
764
|
-
|
|
772
|
+
useEffect2(() => setProps(projection.getFieldProjection(field.id).props), [
|
|
765
773
|
field,
|
|
766
774
|
projection
|
|
767
775
|
]);
|
|
768
776
|
const [schemas, setSchemas] = useState2([]);
|
|
769
|
-
|
|
777
|
+
useEffect2(() => {
|
|
770
778
|
if (!registry) {
|
|
771
779
|
return;
|
|
772
780
|
}
|
|
@@ -785,7 +793,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
785
793
|
registry
|
|
786
794
|
]);
|
|
787
795
|
const [referenceSchema, setReferenceSchema] = useState2();
|
|
788
|
-
|
|
796
|
+
useEffect2(() => {
|
|
789
797
|
setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
|
|
790
798
|
}, [
|
|
791
799
|
schemas,
|
|
@@ -794,7 +802,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
794
802
|
const [{ fieldSchema }, setFieldSchema] = useState2({
|
|
795
803
|
fieldSchema: getFormatSchema(props?.format)
|
|
796
804
|
});
|
|
797
|
-
const handleValuesChanged =
|
|
805
|
+
const handleValuesChanged = useCallback3((_props) => {
|
|
798
806
|
setFieldSchema((prev) => {
|
|
799
807
|
const fieldSchema2 = getFormatSchema(_props.format);
|
|
800
808
|
if (prev.fieldSchema === fieldSchema2) {
|
|
@@ -829,7 +837,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
829
837
|
props.format,
|
|
830
838
|
props.referenceSchema
|
|
831
839
|
]);
|
|
832
|
-
const handleValidate =
|
|
840
|
+
const handleValidate = useCallback3(({ property }) => {
|
|
833
841
|
if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
|
|
834
842
|
return [
|
|
835
843
|
{
|
|
@@ -842,7 +850,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
842
850
|
view.fields,
|
|
843
851
|
field
|
|
844
852
|
]);
|
|
845
|
-
const handleSave =
|
|
853
|
+
const handleSave = useCallback3((props2) => {
|
|
846
854
|
projection.setFieldProjection({
|
|
847
855
|
field,
|
|
848
856
|
props: props2
|
|
@@ -853,29 +861,29 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
853
861
|
field,
|
|
854
862
|
onSave
|
|
855
863
|
]);
|
|
856
|
-
const handleCancel =
|
|
864
|
+
const handleCancel = useCallback3(() => {
|
|
857
865
|
requestAnimationFrame(() => onCancel?.());
|
|
858
866
|
onSave();
|
|
859
867
|
}, [
|
|
860
868
|
onSave
|
|
861
869
|
]);
|
|
862
|
-
const Custom =
|
|
863
|
-
["format"]: (props2) => /* @__PURE__ */
|
|
870
|
+
const Custom = useMemo5(() => ({
|
|
871
|
+
["format"]: (props2) => /* @__PURE__ */ React11.createElement(SelectInput, {
|
|
864
872
|
...props2,
|
|
865
873
|
options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
|
|
866
874
|
value,
|
|
867
875
|
label: t(`format ${value}`)
|
|
868
876
|
}))
|
|
869
877
|
}),
|
|
870
|
-
["referenceSchema"]: (props2) => /* @__PURE__ */
|
|
878
|
+
["referenceSchema"]: (props2) => /* @__PURE__ */ React11.createElement(SelectInput, {
|
|
871
879
|
...props2,
|
|
872
880
|
options: schemas.map((schema) => ({
|
|
873
881
|
value: schema.typename
|
|
874
882
|
}))
|
|
875
883
|
}),
|
|
876
|
-
["referencePath"]: (props2) => /* @__PURE__ */
|
|
884
|
+
["referencePath"]: (props2) => /* @__PURE__ */ React11.createElement(SelectInput, {
|
|
877
885
|
...props2,
|
|
878
|
-
options: referenceSchema ?
|
|
886
|
+
options: referenceSchema ? getSchemaProperties3(referenceSchema.ast).sort(sortProperties).map((p) => ({
|
|
879
887
|
value: p.name
|
|
880
888
|
})) : []
|
|
881
889
|
})
|
|
@@ -884,9 +892,9 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
884
892
|
schemas,
|
|
885
893
|
referenceSchema
|
|
886
894
|
]);
|
|
887
|
-
const propIsNotType =
|
|
895
|
+
const propIsNotType = useCallback3((props2) => props2.filter((p) => p.name !== "type"), []);
|
|
888
896
|
if (!fieldSchema) {
|
|
889
|
-
|
|
897
|
+
log2.warn("invalid format", {
|
|
890
898
|
props
|
|
891
899
|
}, {
|
|
892
900
|
F: __dxlog_file3,
|
|
@@ -896,7 +904,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
896
904
|
});
|
|
897
905
|
return null;
|
|
898
906
|
}
|
|
899
|
-
return /* @__PURE__ */
|
|
907
|
+
return /* @__PURE__ */ React11.createElement(Form, {
|
|
900
908
|
key: field.id,
|
|
901
909
|
autoFocus: true,
|
|
902
910
|
values: props,
|
|
@@ -915,30 +923,30 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
|
|
|
915
923
|
};
|
|
916
924
|
|
|
917
925
|
// packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
|
|
918
|
-
import
|
|
919
|
-
import { AST as
|
|
920
|
-
import { IconButton as
|
|
926
|
+
import React12, { useCallback as useCallback4, useMemo as useMemo6, useState as useState3 } from "react";
|
|
927
|
+
import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
|
|
928
|
+
import { IconButton as IconButton3, useTranslation as useTranslation5 } from "@dxos/react-ui";
|
|
921
929
|
import { List } from "@dxos/react-ui-list";
|
|
922
|
-
import { ghostHover, inputTextLabel, mx as
|
|
930
|
+
import { ghostHover, inputTextLabel, mx as mx6 } from "@dxos/react-ui-theme";
|
|
923
931
|
import { FieldSchema, ViewProjection, VIEW_FIELD_LIMIT } from "@dxos/schema";
|
|
924
932
|
import { arrayMove } from "@dxos/util";
|
|
925
933
|
var grid = "grid grid-cols-[32px_1fr_32px] min-bs-[2.5rem]";
|
|
926
934
|
var ViewMetaSchema = S2.Struct({
|
|
927
935
|
name: S2.String.annotations({
|
|
928
|
-
[
|
|
936
|
+
[AST4.TitleAnnotationId]: "View"
|
|
929
937
|
}),
|
|
930
938
|
typename: Format.DXN.annotations({
|
|
931
|
-
[
|
|
939
|
+
[AST4.TitleAnnotationId]: "Typename"
|
|
932
940
|
})
|
|
933
941
|
}).pipe(S2.mutable);
|
|
934
942
|
var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onTypenameChanged, onDelete }) => {
|
|
935
|
-
const { t } =
|
|
936
|
-
const projection =
|
|
943
|
+
const { t } = useTranslation5(translationKey);
|
|
944
|
+
const projection = useMemo6(() => new ViewProjection(schema, view), [
|
|
937
945
|
schema,
|
|
938
946
|
view
|
|
939
947
|
]);
|
|
940
948
|
const [field, setField] = useState3();
|
|
941
|
-
const viewValues =
|
|
949
|
+
const viewValues = useMemo6(() => {
|
|
942
950
|
return {
|
|
943
951
|
name: view.name,
|
|
944
952
|
// TODO(burdon): Need to warn user of possible consequences of editing.
|
|
@@ -948,7 +956,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
948
956
|
}, [
|
|
949
957
|
view
|
|
950
958
|
]);
|
|
951
|
-
const handleViewUpdate =
|
|
959
|
+
const handleViewUpdate = useCallback4(({ name, typename }) => {
|
|
952
960
|
requestAnimationFrame(() => {
|
|
953
961
|
if (view.name !== name) {
|
|
954
962
|
view.name = name;
|
|
@@ -962,67 +970,67 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
|
|
|
962
970
|
schema,
|
|
963
971
|
onTypenameChanged
|
|
964
972
|
]);
|
|
965
|
-
const handleSelect =
|
|
973
|
+
const handleSelect = useCallback4((field2) => {
|
|
966
974
|
setField((f) => f === field2 ? void 0 : field2);
|
|
967
975
|
}, []);
|
|
968
|
-
const handleAdd =
|
|
976
|
+
const handleAdd = useCallback4(() => {
|
|
969
977
|
const field2 = projection.createFieldProjection();
|
|
970
978
|
setField(field2);
|
|
971
979
|
}, [
|
|
972
980
|
view
|
|
973
981
|
]);
|
|
974
|
-
const handleMove =
|
|
982
|
+
const handleMove = useCallback4((fromIndex, toIndex) => {
|
|
975
983
|
arrayMove(view.fields, fromIndex, toIndex);
|
|
976
984
|
}, [
|
|
977
985
|
view.fields
|
|
978
986
|
]);
|
|
979
|
-
const handleClose =
|
|
980
|
-
return /* @__PURE__ */
|
|
987
|
+
const handleClose = useCallback4(() => setField(void 0), []);
|
|
988
|
+
return /* @__PURE__ */ React12.createElement("div", {
|
|
981
989
|
role: "none",
|
|
982
|
-
className:
|
|
983
|
-
}, /* @__PURE__ */
|
|
990
|
+
className: mx6("flex flex-col w-full divide-y divide-separator", classNames)
|
|
991
|
+
}, /* @__PURE__ */ React12.createElement(Form, {
|
|
984
992
|
autoFocus: true,
|
|
985
993
|
autoSave: true,
|
|
986
994
|
schema: ViewMetaSchema,
|
|
987
995
|
values: viewValues,
|
|
988
996
|
onSave: handleViewUpdate
|
|
989
|
-
}), /* @__PURE__ */
|
|
997
|
+
}), /* @__PURE__ */ React12.createElement("div", null, /* @__PURE__ */ React12.createElement("div", {
|
|
990
998
|
role: "none",
|
|
991
999
|
className: "p-2"
|
|
992
|
-
}, /* @__PURE__ */
|
|
993
|
-
className:
|
|
994
|
-
}, t("fields label"))), /* @__PURE__ */
|
|
1000
|
+
}, /* @__PURE__ */ React12.createElement("label", {
|
|
1001
|
+
className: mx6(inputTextLabel)
|
|
1002
|
+
}, t("fields label"))), /* @__PURE__ */ React12.createElement(List.Root, {
|
|
995
1003
|
items: view.fields,
|
|
996
1004
|
isItem: S2.is(FieldSchema),
|
|
997
1005
|
getId: (field2) => field2.id,
|
|
998
1006
|
onMove: handleMove
|
|
999
|
-
}, ({ items: fields }) => /* @__PURE__ */
|
|
1007
|
+
}, ({ items: fields }) => /* @__PURE__ */ React12.createElement(React12.Fragment, null, showHeading && /* @__PURE__ */ React12.createElement("div", {
|
|
1000
1008
|
role: "heading",
|
|
1001
1009
|
className: grid
|
|
1002
|
-
}, /* @__PURE__ */
|
|
1010
|
+
}, /* @__PURE__ */ React12.createElement("div", null), /* @__PURE__ */ React12.createElement("div", {
|
|
1003
1011
|
className: "flex items-center text-sm"
|
|
1004
|
-
}, t("field path label"))), /* @__PURE__ */
|
|
1012
|
+
}, t("field path label"))), /* @__PURE__ */ React12.createElement("div", {
|
|
1005
1013
|
role: "list",
|
|
1006
1014
|
className: "flex flex-col w-full"
|
|
1007
|
-
}, fields?.map((field2) => /* @__PURE__ */
|
|
1015
|
+
}, fields?.map((field2) => /* @__PURE__ */ React12.createElement(List.Item, {
|
|
1008
1016
|
key: field2.id,
|
|
1009
1017
|
item: field2,
|
|
1010
|
-
classNames:
|
|
1011
|
-
}, /* @__PURE__ */
|
|
1018
|
+
classNames: mx6(grid, ghostHover, "cursor-pointer")
|
|
1019
|
+
}, /* @__PURE__ */ React12.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React12.createElement(List.ItemTitle, {
|
|
1012
1020
|
onClick: () => handleSelect(field2)
|
|
1013
|
-
}, field2.path), /* @__PURE__ */
|
|
1021
|
+
}, field2.path), /* @__PURE__ */ React12.createElement(List.ItemDeleteButton, {
|
|
1014
1022
|
disabled: view.fields.length <= 1,
|
|
1015
1023
|
onClick: () => onDelete(field2.id)
|
|
1016
|
-
}))))))), field && /* @__PURE__ */
|
|
1024
|
+
}))))))), field && /* @__PURE__ */ React12.createElement(FieldEditor, {
|
|
1017
1025
|
key: field.id,
|
|
1018
1026
|
view,
|
|
1019
1027
|
projection,
|
|
1020
1028
|
field,
|
|
1021
1029
|
registry,
|
|
1022
1030
|
onSave: handleClose
|
|
1023
|
-
}), !readonly && !field && /* @__PURE__ */
|
|
1031
|
+
}), !readonly && !field && /* @__PURE__ */ React12.createElement("div", {
|
|
1024
1032
|
className: "flex p-2 justify-center"
|
|
1025
|
-
}, /* @__PURE__ */
|
|
1033
|
+
}, /* @__PURE__ */ React12.createElement(IconButton3, {
|
|
1026
1034
|
icon: "ph--plus--regular",
|
|
1027
1035
|
label: t("button add property"),
|
|
1028
1036
|
onClick: handleAdd,
|
|
@@ -1255,7 +1263,6 @@ export {
|
|
|
1255
1263
|
DeprecatedFormInput,
|
|
1256
1264
|
FieldEditor,
|
|
1257
1265
|
Form,
|
|
1258
|
-
FormActions,
|
|
1259
1266
|
GeoPointInput,
|
|
1260
1267
|
InputHeader,
|
|
1261
1268
|
NumberInput,
|