@dxos/react-ui-form 0.7.3-main.2dd075e → 0.7.3-staging.0905f03

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.
@@ -32,6 +32,7 @@ __export(node_exports, {
32
32
  DeprecatedFormInput: () => DeprecatedFormInput,
33
33
  FieldEditor: () => FieldEditor,
34
34
  Form: () => Form,
35
+ FormActions: () => FormActions,
35
36
  GeiPointInput: () => GeiPointInput,
36
37
  InputHeader: () => InputHeader,
37
38
  NumberInput: () => NumberInput,
@@ -175,11 +176,11 @@ var InputHeader = ({ children, error }) => {
175
176
  side: "bottom"
176
177
  }, /* @__PURE__ */ import_react5.default.createElement(import_react_ui5.Tooltip.Arrow, null), error)));
177
178
  };
178
- var TextInput = ({ property, type, label, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
179
+ var TextInput = ({ property, type, label, inputOnly, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
179
180
  const { status, error } = getStatus?.(property);
180
181
  return /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.Root, {
181
182
  validationValence: status
182
- }, /* @__PURE__ */ import_react4.default.createElement(InputHeader, {
183
+ }, !inputOnly && /* @__PURE__ */ import_react4.default.createElement(InputHeader, {
183
184
  error
184
185
  }, /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.Label, null, label)), /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.TextInput, {
185
186
  disabled,
@@ -187,13 +188,13 @@ var TextInput = ({ property, type, label, disabled, placeholder, getStatus, getV
187
188
  value: getValue(property) ?? "",
188
189
  onChange: (event) => onValueChange(property, type, event.target.value),
189
190
  onBlur
190
- }));
191
+ }), inputOnly && /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.Validation, null, error));
191
192
  };
192
- var NumberInput = ({ property, type, label, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
193
+ var NumberInput = ({ property, type, label, inputOnly, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
193
194
  const { status, error } = getStatus?.(property);
194
195
  return /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.Root, {
195
196
  validationValence: status
196
- }, /* @__PURE__ */ import_react4.default.createElement(InputHeader, {
197
+ }, !inputOnly && /* @__PURE__ */ import_react4.default.createElement(InputHeader, {
197
198
  error
198
199
  }, /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.Label, null, label)), /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.TextInput, {
199
200
  type: "number",
@@ -202,24 +203,24 @@ var NumberInput = ({ property, type, label, disabled, placeholder, getStatus, ge
202
203
  value: getValue(property) ?? 0,
203
204
  onChange: (event) => onValueChange(property, type, event.target.value),
204
205
  onBlur
205
- }));
206
+ }), inputOnly && /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.DescriptionAndValidation, null, error));
206
207
  };
207
- var BooleanInput = ({ property, type, label, getStatus, getValue, onValueChange }) => {
208
+ var BooleanInput = ({ property, type, label, inputOnly, getStatus, getValue, onValueChange }) => {
208
209
  const { status, error } = getStatus?.(property);
209
210
  return /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.Root, {
210
211
  validationValence: status
211
- }, /* @__PURE__ */ import_react4.default.createElement(InputHeader, {
212
+ }, !inputOnly && /* @__PURE__ */ import_react4.default.createElement(InputHeader, {
212
213
  error
213
214
  }, /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.Label, null, label)), /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.Switch, {
214
215
  checked: getValue(property),
215
216
  onCheckedChange: (value) => onValueChange(property, type, value)
216
- }));
217
+ }), inputOnly && /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.DescriptionAndValidation, null, error));
217
218
  };
218
- var SelectInput = ({ property, type, label, disabled, placeholder, options, getStatus, getValue, onValueChange }) => {
219
+ var SelectInput = ({ property, type, label, inputOnly, disabled, placeholder, options, getStatus, getValue, onValueChange }) => {
219
220
  const { status, error } = getStatus?.(property);
220
221
  return /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.Root, {
221
222
  validationValence: status
222
- }, /* @__PURE__ */ import_react4.default.createElement(InputHeader, {
223
+ }, !inputOnly && /* @__PURE__ */ import_react4.default.createElement(InputHeader, {
223
224
  error
224
225
  }, /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.Label, null, label)), /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Select.Root, {
225
226
  value: getValue(property),
@@ -234,7 +235,7 @@ var SelectInput = ({ property, type, label, disabled, placeholder, options, getS
234
235
  key: String(value),
235
236
  value: String(value)
236
237
  }, label2 ?? String(value))
237
- )))))));
238
+ )))))), inputOnly && /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Input.DescriptionAndValidation, null, error));
238
239
  };
239
240
  var GeiPointInput = ({ property, type, label, disabled, getStatus, getValue, onValueChange, onBlur }) => {
240
241
  const { t } = (0, import_react_ui6.useTranslation)(translationKey);
@@ -300,6 +301,7 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
300
301
  const [touched, setTouched] = (0, import_react7.useState)(createKeySet(initialValues, false));
301
302
  const [changed, setChanged] = (0, import_react7.useState)(createKeySet(initialValues, false));
302
303
  const [errors, setErrors] = (0, import_react7.useState)({});
304
+ const [saving, setSaving] = (0, import_react7.useState)(false);
303
305
  const validate = (0, import_react7.useCallback)((values2) => {
304
306
  let errors2 = (0, import_schema3.validateSchema)(schema, values2) ?? [];
305
307
  if (errors2.length === 0 && onValidate) {
@@ -324,16 +326,22 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
324
326
  values,
325
327
  schemaDirty
326
328
  ]);
327
- const canSave = (0, import_react7.useMemo)(() => Object.keys(values).every((property) => touched[property] === false || !errors[property]), [
329
+ const canSave = (0, import_react7.useMemo)(() => !saving && Object.keys(values).every((property) => touched[property] === false || !errors[property]), [
328
330
  values,
329
331
  touched,
330
- errors
332
+ errors,
333
+ saving
331
334
  ]);
332
- const handleSave = (0, import_react7.useCallback)(() => {
335
+ const handleSave = (0, import_react7.useCallback)(async () => {
333
336
  if (validate(values)) {
334
- onSave?.(values, {
335
- changed
336
- });
337
+ setSaving(true);
338
+ try {
339
+ await onSave?.(values, {
340
+ changed
341
+ });
342
+ } finally {
343
+ setSaving(false);
344
+ }
337
345
  }
338
346
  }, [
339
347
  values,
@@ -348,7 +356,7 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
348
356
  errors
349
357
  ]);
350
358
  const getValue = (property) => {
351
- return values[property];
359
+ return getByPath(values, property);
352
360
  };
353
361
  const onValueChange = (property, type, value) => {
354
362
  let parsedValue = value;
@@ -359,16 +367,13 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
359
367
  } catch (err) {
360
368
  import_log3.log.catch(err, void 0, {
361
369
  F: __dxlog_file,
362
- L: 172,
370
+ L: 183,
363
371
  S: void 0,
364
372
  C: (f, a) => f(...a)
365
373
  });
366
374
  parsedValue = void 0;
367
375
  }
368
- const newValues = {
369
- ...values,
370
- [property]: parsedValue
371
- };
376
+ const newValues = setByPath(values, property, parsedValue);
372
377
  setValues(newValues);
373
378
  setChanged((prev) => ({
374
379
  ...prev,
@@ -414,7 +419,7 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
414
419
  var createKeySet = (obj, value) => {
415
420
  (0, import_invariant.invariant)(obj, void 0, {
416
421
  F: __dxlog_file,
417
- L: 223,
422
+ L: 234,
418
423
  S: void 0,
419
424
  A: [
420
425
  "obj",
@@ -434,10 +439,23 @@ var flatMap = (errors) => {
434
439
  return result;
435
440
  }, {});
436
441
  };
442
+ var getByPath = (obj, path) => {
443
+ const [key, index] = path.split(".");
444
+ return index === void 0 ? obj[key] : obj[key][parseInt(index)];
445
+ };
446
+ var setByPath = (obj, path, value) => {
447
+ const [key, index] = path.split(".");
448
+ const newValue = index === void 0 ? value : [
449
+ ...obj[key]
450
+ ].map((v, i) => i === parseInt(index) ? value : v);
451
+ return {
452
+ ...obj,
453
+ [key]: newValue
454
+ };
455
+ };
437
456
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/Form.tsx";
438
457
  var padding = "px-2";
439
458
  var Form = ({ classNames, schema, values: initialValues, path = [], readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, Custom }) => {
440
- const { t } = (0, import_react_ui3.useTranslation)(translationKey);
441
459
  const onValid = (0, import_react3.useMemo)(() => autoSave ? onSave : void 0, [
442
460
  autoSave,
443
461
  onSave
@@ -469,7 +487,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
469
487
  errors
470
488
  }, {
471
489
  F: __dxlog_file2,
472
- L: 101,
490
+ L: 100,
473
491
  S: void 0,
474
492
  C: (f, a) => f(...a)
475
493
  });
@@ -482,7 +500,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
482
500
  className: (0, import_react_ui_theme.mx)("flex flex-col w-full gap-2 py-2", classNames),
483
501
  "data-testid": testId
484
502
  }, properties.map((property) => {
485
- const { ast, name, type, format, title, description, examples, options } = property;
503
+ const { ast, name, type, format, title, description, examples, options, array } = property;
486
504
  const key = [
487
505
  ...path,
488
506
  name
@@ -511,6 +529,22 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
511
529
  }));
512
530
  }
513
531
  InputComponent = getInputComponent(type, format);
532
+ if (array) {
533
+ return /* @__PURE__ */ import_react3.default.createElement(ArrayField, {
534
+ key: name,
535
+ name,
536
+ type,
537
+ array: true,
538
+ label,
539
+ ast,
540
+ values,
541
+ format,
542
+ readonly,
543
+ placeholder,
544
+ inputProps,
545
+ Custom
546
+ });
547
+ }
514
548
  }
515
549
  if (!InputComponent) {
516
550
  if (type === "object") {
@@ -539,7 +573,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
539
573
  type
540
574
  }, {
541
575
  F: __dxlog_file2,
542
- L: 165,
576
+ L: 185,
543
577
  S: void 0,
544
578
  C: (f, a) => f(...a)
545
579
  });
@@ -558,7 +592,140 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
558
592
  placeholder,
559
593
  ...inputProps
560
594
  }));
561
- }).filter(import_util.isNotFalsy), (onCancel || onSave) && !autoSave && /* @__PURE__ */ import_react3.default.createElement("div", {
595
+ }).filter(import_util.isNotFalsy), (onCancel || onSave) && !autoSave && /* @__PURE__ */ import_react3.default.createElement(FormActions, {
596
+ onCancel,
597
+ onSubmit: handleSave,
598
+ canSubmit: canSave,
599
+ readonly
600
+ }));
601
+ };
602
+ var ArrayField = ({ name, type, label, ast, values, format, readonly, placeholder, inputProps, Custom }) => {
603
+ const { t } = (0, import_react_ui3.useTranslation)(translationKey);
604
+ const arrayValues = values[name] ?? [];
605
+ const key = [
606
+ name
607
+ ];
608
+ const tupleType = (0, import_effect2.findNode)(ast, import_echo_schema2.AST.isTupleType);
609
+ const elementType = tupleType?.rest[0]?.type;
610
+ if (type === "object" && elementType) {
611
+ const baseNode = (0, import_effect2.findNode)(elementType, import_effect2.isDiscriminatedUnion);
612
+ const typeLiteral = baseNode ? (0, import_effect2.getDiscriminatedType)(baseNode, values[name]) : (0, import_effect2.findNode)(elementType, import_echo_schema2.AST.isTypeLiteral);
613
+ if (typeLiteral) {
614
+ const schema = import_echo_schema2.S.make(typeLiteral);
615
+ return /* @__PURE__ */ import_react3.default.createElement("div", {
616
+ key: name,
617
+ role: "none",
618
+ className: (0, import_react_ui_theme.mx)(padding)
619
+ }, /* @__PURE__ */ import_react3.default.createElement(InputHeader, null, label), /* @__PURE__ */ import_react3.default.createElement("div", {
620
+ role: "none",
621
+ className: "flex flex-col gap-1"
622
+ }, arrayValues.map((value, index) => {
623
+ return /* @__PURE__ */ import_react3.default.createElement("div", {
624
+ key: index,
625
+ role: "none",
626
+ className: "flex items-center gap-1"
627
+ }, /* @__PURE__ */ import_react3.default.createElement("div", {
628
+ role: "none",
629
+ className: "flex-1"
630
+ }, /* @__PURE__ */ import_react3.default.createElement(Form, {
631
+ schema,
632
+ path: [
633
+ ...key,
634
+ index.toString()
635
+ ],
636
+ values: value,
637
+ onValuesChanged: (childValues) => {
638
+ const newValues = [
639
+ ...arrayValues
640
+ ];
641
+ newValues[index] = childValues;
642
+ inputProps.onValueChange(name, "object", newValues);
643
+ },
644
+ Custom
645
+ })), /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.IconButton, {
646
+ icon: "ph--trash--regular",
647
+ iconOnly: true,
648
+ label: t("button remove"),
649
+ onClick: () => {
650
+ const newValues = arrayValues.filter((_, i) => i !== index);
651
+ inputProps.onValueChange(name, type, newValues);
652
+ }
653
+ }));
654
+ })), /* @__PURE__ */ import_react3.default.createElement("div", {
655
+ role: "none",
656
+ className: "flex justify-between items-center plb-1"
657
+ }, /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.IconButton, {
658
+ icon: "ph--plus--regular",
659
+ iconOnly: true,
660
+ label: t("button add"),
661
+ onClick: () => {
662
+ const newValues = [
663
+ ...arrayValues,
664
+ {}
665
+ ];
666
+ inputProps.onValueChange(name, type, newValues);
667
+ }
668
+ })));
669
+ }
670
+ return /* @__PURE__ */ import_react3.default.createElement("div", {
671
+ key: name,
672
+ role: "none"
673
+ }, "Nested form not supported in arrays, yet.");
674
+ }
675
+ const InputComponent = getInputComponent(type, format);
676
+ if (!InputComponent) {
677
+ return null;
678
+ }
679
+ return /* @__PURE__ */ import_react3.default.createElement("div", {
680
+ role: "none",
681
+ key: "meta-input",
682
+ className: (0, import_react_ui_theme.mx)(padding)
683
+ }, /* @__PURE__ */ import_react3.default.createElement(InputHeader, null, label), /* @__PURE__ */ import_react3.default.createElement("div", {
684
+ role: "none",
685
+ className: "flex flex-col gap-1"
686
+ }, arrayValues.map((_, index) => /* @__PURE__ */ import_react3.default.createElement("div", {
687
+ key: index,
688
+ role: "none",
689
+ className: "flex items-start gap-1"
690
+ }, /* @__PURE__ */ import_react3.default.createElement("div", {
691
+ role: "none",
692
+ className: "flex-1"
693
+ }, /* @__PURE__ */ import_react3.default.createElement(InputComponent, {
694
+ type,
695
+ format,
696
+ label,
697
+ inputOnly: true,
698
+ property: `${name}.${index}`,
699
+ disabled: readonly,
700
+ placeholder,
701
+ ...inputProps
702
+ })), /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.IconButton, {
703
+ icon: "ph--trash--regular",
704
+ iconOnly: true,
705
+ label: t("button remove"),
706
+ onClick: () => {
707
+ const newValues = arrayValues.filter((_2, i) => i !== index);
708
+ inputProps.onValueChange(name, type, newValues);
709
+ }
710
+ })))), /* @__PURE__ */ import_react3.default.createElement("div", {
711
+ role: "none",
712
+ className: "flex justify-between items-center plb-1"
713
+ }, /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.IconButton, {
714
+ icon: "ph--plus--regular",
715
+ iconOnly: true,
716
+ label: t("button add"),
717
+ onClick: () => {
718
+ const newValues = [
719
+ ...arrayValues,
720
+ import_effect2.SimpleType.getDefaultValue(type)
721
+ ];
722
+ inputProps.onValueChange(name, type, newValues);
723
+ }
724
+ })));
725
+ };
726
+ var FormActions = ({ onCancel, onSubmit, canSubmit, readonly }) => {
727
+ const { t } = (0, import_react_ui3.useTranslation)(translationKey);
728
+ return /* @__PURE__ */ import_react3.default.createElement("div", {
562
729
  role: "none",
563
730
  className: "flex justify-center"
564
731
  }, /* @__PURE__ */ import_react3.default.createElement("div", {
@@ -569,14 +736,14 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
569
736
  icon: "ph--x--regular",
570
737
  label: t("button cancel"),
571
738
  onClick: onCancel
572
- }), onSave && /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.IconButton, {
739
+ }), onSubmit && /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.IconButton, {
573
740
  type: "submit",
574
741
  "data-testid": "save-button",
575
- disabled: !canSave,
742
+ disabled: !canSubmit,
576
743
  icon: "ph--check--regular",
577
744
  label: t("button save"),
578
- onClick: handleSave
579
- }))));
745
+ onClick: onSubmit
746
+ })));
580
747
  };
581
748
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
582
749
  var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
@@ -736,7 +903,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
736
903
  name: view.name,
737
904
  // TODO(burdon): Need to warn user of possible consequences of editing.
738
905
  // TODO(burdon): Settings should have domain name owned by user.
739
- typename: view.query.typename
906
+ typename: view.query.type
740
907
  };
741
908
  }, [
742
909
  view
@@ -744,7 +911,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
744
911
  const handleViewUpdate = (0, import_react8.useCallback)(({ name, typename }) => {
745
912
  requestAnimationFrame(() => {
746
913
  view.name = name;
747
- view.query.typename = typename;
914
+ view.query.type = typename;
748
915
  schema.updateTypename(typename);
749
916
  });
750
917
  }, [
@@ -1039,6 +1206,7 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
1039
1206
  DeprecatedFormInput,
1040
1207
  FieldEditor,
1041
1208
  Form,
1209
+ FormActions,
1042
1210
  GeiPointInput,
1043
1211
  InputHeader,
1044
1212
  NumberInput,