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