@dxos/react-ui-form 0.7.3-staging.cc8dd3e → 0.7.3

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.
@@ -93,7 +93,7 @@ import { pipe } from "effect";
93
93
  import { capitalize } from "effect/String";
94
94
  import React5, { useEffect as useEffect2, useMemo as useMemo2 } from "react";
95
95
  import { AST, S } from "@dxos/echo-schema";
96
- import { findNode, getDiscriminatedType, isDiscriminatedUnion } from "@dxos/effect";
96
+ import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
97
97
  import { log as log2 } from "@dxos/log";
98
98
  import { IconButton, useTranslation as useTranslation2 } from "@dxos/react-ui";
99
99
  import { mx } from "@dxos/react-ui-theme";
@@ -124,11 +124,11 @@ var InputHeader = ({ children, error }) => {
124
124
  };
125
125
 
126
126
  // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
127
- var TextInput = ({ property, type, label, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
127
+ var TextInput = ({ property, type, label, inputOnly, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
128
128
  const { status, error } = getStatus?.(property);
129
129
  return /* @__PURE__ */ React3.createElement(Input2.Root, {
130
130
  validationValence: status
131
- }, /* @__PURE__ */ React3.createElement(InputHeader, {
131
+ }, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
132
132
  error
133
133
  }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
134
134
  disabled,
@@ -136,13 +136,13 @@ var TextInput = ({ property, type, label, disabled, placeholder, getStatus, getV
136
136
  value: getValue(property) ?? "",
137
137
  onChange: (event) => onValueChange(property, type, event.target.value),
138
138
  onBlur
139
- }));
139
+ }), inputOnly && /* @__PURE__ */ React3.createElement(Input2.Validation, null, error));
140
140
  };
141
- var NumberInput = ({ property, type, label, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
141
+ var NumberInput = ({ property, type, label, inputOnly, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
142
142
  const { status, error } = getStatus?.(property);
143
143
  return /* @__PURE__ */ React3.createElement(Input2.Root, {
144
144
  validationValence: status
145
- }, /* @__PURE__ */ React3.createElement(InputHeader, {
145
+ }, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
146
146
  error
147
147
  }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
148
148
  type: "number",
@@ -151,24 +151,24 @@ var NumberInput = ({ property, type, label, disabled, placeholder, getStatus, ge
151
151
  value: getValue(property) ?? 0,
152
152
  onChange: (event) => onValueChange(property, type, event.target.value),
153
153
  onBlur
154
- }));
154
+ }), inputOnly && /* @__PURE__ */ React3.createElement(Input2.DescriptionAndValidation, null, error));
155
155
  };
156
- var BooleanInput = ({ property, type, label, getStatus, getValue, onValueChange }) => {
156
+ var BooleanInput = ({ property, type, label, inputOnly, getStatus, getValue, onValueChange }) => {
157
157
  const { status, error } = getStatus?.(property);
158
158
  return /* @__PURE__ */ React3.createElement(Input2.Root, {
159
159
  validationValence: status
160
- }, /* @__PURE__ */ React3.createElement(InputHeader, {
160
+ }, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
161
161
  error
162
162
  }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Input2.Switch, {
163
163
  checked: getValue(property),
164
164
  onCheckedChange: (value) => onValueChange(property, type, value)
165
- }));
165
+ }), inputOnly && /* @__PURE__ */ React3.createElement(Input2.DescriptionAndValidation, null, error));
166
166
  };
167
- var SelectInput = ({ property, type, label, disabled, placeholder, options, getStatus, getValue, onValueChange }) => {
167
+ var SelectInput = ({ property, type, label, inputOnly, disabled, placeholder, options, getStatus, getValue, onValueChange }) => {
168
168
  const { status, error } = getStatus?.(property);
169
169
  return /* @__PURE__ */ React3.createElement(Input2.Root, {
170
170
  validationValence: status
171
- }, /* @__PURE__ */ React3.createElement(InputHeader, {
171
+ }, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
172
172
  error
173
173
  }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Select.Root, {
174
174
  value: getValue(property),
@@ -183,7 +183,7 @@ var SelectInput = ({ property, type, label, disabled, placeholder, options, getS
183
183
  key: String(value),
184
184
  value: String(value)
185
185
  }, label2 ?? String(value))
186
- )))))));
186
+ )))))), inputOnly && /* @__PURE__ */ React3.createElement(Input2.DescriptionAndValidation, null, error));
187
187
  };
188
188
 
189
189
  // packages/ui/react-ui-form/src/components/Form/factory.tsx
@@ -319,7 +319,7 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
319
319
  errors
320
320
  ]);
321
321
  const getValue = (property) => {
322
- return values[property];
322
+ return getByPath(values, property);
323
323
  };
324
324
  const onValueChange = (property, type, value) => {
325
325
  let parsedValue = value;
@@ -330,16 +330,13 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
330
330
  } catch (err) {
331
331
  log.catch(err, void 0, {
332
332
  F: __dxlog_file,
333
- L: 180,
333
+ L: 183,
334
334
  S: void 0,
335
335
  C: (f, a) => f(...a)
336
336
  });
337
337
  parsedValue = void 0;
338
338
  }
339
- const newValues = {
340
- ...values,
341
- [property]: parsedValue
342
- };
339
+ const newValues = setByPath(values, property, parsedValue);
343
340
  setValues(newValues);
344
341
  setChanged((prev) => ({
345
342
  ...prev,
@@ -385,7 +382,7 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
385
382
  var createKeySet = (obj, value) => {
386
383
  invariant(obj, void 0, {
387
384
  F: __dxlog_file,
388
- L: 231,
385
+ L: 234,
389
386
  S: void 0,
390
387
  A: [
391
388
  "obj",
@@ -405,12 +402,25 @@ var flatMap = (errors) => {
405
402
  return result;
406
403
  }, {});
407
404
  };
405
+ var getByPath = (obj, path) => {
406
+ const [key, index] = path.split(".");
407
+ return index === void 0 ? obj[key] : obj[key][parseInt(index)];
408
+ };
409
+ var setByPath = (obj, path, value) => {
410
+ const [key, index] = path.split(".");
411
+ const newValue = index === void 0 ? value : [
412
+ ...obj[key]
413
+ ].map((v, i) => i === parseInt(index) ? value : v);
414
+ return {
415
+ ...obj,
416
+ [key]: newValue
417
+ };
418
+ };
408
419
 
409
420
  // packages/ui/react-ui-form/src/components/Form/Form.tsx
410
421
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/Form.tsx";
411
422
  var padding = "px-2";
412
423
  var Form = ({ classNames, schema, values: initialValues, path = [], readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, Custom }) => {
413
- const { t } = useTranslation2(translationKey);
414
424
  const onValid = useMemo2(() => autoSave ? onSave : void 0, [
415
425
  autoSave,
416
426
  onSave
@@ -442,7 +452,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
442
452
  errors
443
453
  }, {
444
454
  F: __dxlog_file2,
445
- L: 101,
455
+ L: 100,
446
456
  S: void 0,
447
457
  C: (f, a) => f(...a)
448
458
  });
@@ -455,7 +465,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
455
465
  className: mx("flex flex-col w-full gap-2 py-2", classNames),
456
466
  "data-testid": testId
457
467
  }, properties.map((property) => {
458
- const { ast, name, type, format, title, description, examples, options } = property;
468
+ const { ast, name, type, format, title, description, examples, options, array } = property;
459
469
  const key = [
460
470
  ...path,
461
471
  name
@@ -484,6 +494,22 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
484
494
  }));
485
495
  }
486
496
  InputComponent = getInputComponent(type, format);
497
+ if (array) {
498
+ return /* @__PURE__ */ React5.createElement(ArrayField, {
499
+ key: name,
500
+ name,
501
+ type,
502
+ array: true,
503
+ label,
504
+ ast,
505
+ values,
506
+ format,
507
+ readonly,
508
+ placeholder,
509
+ inputProps,
510
+ Custom
511
+ });
512
+ }
487
513
  }
488
514
  if (!InputComponent) {
489
515
  if (type === "object") {
@@ -512,7 +538,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
512
538
  type
513
539
  }, {
514
540
  F: __dxlog_file2,
515
- L: 165,
541
+ L: 185,
516
542
  S: void 0,
517
543
  C: (f, a) => f(...a)
518
544
  });
@@ -531,7 +557,140 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
531
557
  placeholder,
532
558
  ...inputProps
533
559
  }));
534
- }).filter(isNotFalsy), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React5.createElement("div", {
560
+ }).filter(isNotFalsy), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React5.createElement(FormActions, {
561
+ onCancel,
562
+ onSubmit: handleSave,
563
+ canSubmit: canSave,
564
+ readonly
565
+ }));
566
+ };
567
+ var ArrayField = ({ name, type, label, ast, values, format, readonly, placeholder, inputProps, Custom }) => {
568
+ const { t } = useTranslation2(translationKey);
569
+ const arrayValues = values[name] ?? [];
570
+ const key = [
571
+ name
572
+ ];
573
+ const tupleType = findNode(ast, AST.isTupleType);
574
+ const elementType = tupleType?.rest[0]?.type;
575
+ if (type === "object" && elementType) {
576
+ const baseNode = findNode(elementType, isDiscriminatedUnion);
577
+ const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(elementType, AST.isTypeLiteral);
578
+ if (typeLiteral) {
579
+ const schema = S.make(typeLiteral);
580
+ return /* @__PURE__ */ React5.createElement("div", {
581
+ key: name,
582
+ role: "none",
583
+ className: mx(padding)
584
+ }, /* @__PURE__ */ React5.createElement(InputHeader, null, label), /* @__PURE__ */ React5.createElement("div", {
585
+ role: "none",
586
+ className: "flex flex-col gap-1"
587
+ }, arrayValues.map((value, index) => {
588
+ return /* @__PURE__ */ React5.createElement("div", {
589
+ key: index,
590
+ role: "none",
591
+ className: "flex items-center gap-1"
592
+ }, /* @__PURE__ */ React5.createElement("div", {
593
+ role: "none",
594
+ className: "flex-1"
595
+ }, /* @__PURE__ */ React5.createElement(Form, {
596
+ schema,
597
+ path: [
598
+ ...key,
599
+ index.toString()
600
+ ],
601
+ values: value,
602
+ onValuesChanged: (childValues) => {
603
+ const newValues = [
604
+ ...arrayValues
605
+ ];
606
+ newValues[index] = childValues;
607
+ inputProps.onValueChange(name, "object", newValues);
608
+ },
609
+ Custom
610
+ })), /* @__PURE__ */ React5.createElement(IconButton, {
611
+ icon: "ph--trash--regular",
612
+ iconOnly: true,
613
+ label: t("button remove"),
614
+ onClick: () => {
615
+ const newValues = arrayValues.filter((_, i) => i !== index);
616
+ inputProps.onValueChange(name, type, newValues);
617
+ }
618
+ }));
619
+ })), /* @__PURE__ */ React5.createElement("div", {
620
+ role: "none",
621
+ className: "flex justify-between items-center plb-1"
622
+ }, /* @__PURE__ */ React5.createElement(IconButton, {
623
+ icon: "ph--plus--regular",
624
+ iconOnly: true,
625
+ label: t("button add"),
626
+ onClick: () => {
627
+ const newValues = [
628
+ ...arrayValues,
629
+ {}
630
+ ];
631
+ inputProps.onValueChange(name, type, newValues);
632
+ }
633
+ })));
634
+ }
635
+ return /* @__PURE__ */ React5.createElement("div", {
636
+ key: name,
637
+ role: "none"
638
+ }, "Nested form not supported in arrays, yet.");
639
+ }
640
+ const InputComponent = getInputComponent(type, format);
641
+ if (!InputComponent) {
642
+ return null;
643
+ }
644
+ return /* @__PURE__ */ React5.createElement("div", {
645
+ role: "none",
646
+ key: "meta-input",
647
+ className: mx(padding)
648
+ }, /* @__PURE__ */ React5.createElement(InputHeader, null, label), /* @__PURE__ */ React5.createElement("div", {
649
+ role: "none",
650
+ className: "flex flex-col gap-1"
651
+ }, arrayValues.map((_, index) => /* @__PURE__ */ React5.createElement("div", {
652
+ key: index,
653
+ role: "none",
654
+ className: "flex items-start gap-1"
655
+ }, /* @__PURE__ */ React5.createElement("div", {
656
+ role: "none",
657
+ className: "flex-1"
658
+ }, /* @__PURE__ */ React5.createElement(InputComponent, {
659
+ type,
660
+ format,
661
+ label,
662
+ inputOnly: true,
663
+ property: `${name}.${index}`,
664
+ disabled: readonly,
665
+ placeholder,
666
+ ...inputProps
667
+ })), /* @__PURE__ */ React5.createElement(IconButton, {
668
+ icon: "ph--trash--regular",
669
+ iconOnly: true,
670
+ label: t("button remove"),
671
+ onClick: () => {
672
+ const newValues = arrayValues.filter((_2, i) => i !== index);
673
+ inputProps.onValueChange(name, type, newValues);
674
+ }
675
+ })))), /* @__PURE__ */ React5.createElement("div", {
676
+ role: "none",
677
+ className: "flex justify-between items-center plb-1"
678
+ }, /* @__PURE__ */ React5.createElement(IconButton, {
679
+ icon: "ph--plus--regular",
680
+ iconOnly: true,
681
+ label: t("button add"),
682
+ onClick: () => {
683
+ const newValues = [
684
+ ...arrayValues,
685
+ SimpleType.getDefaultValue(type)
686
+ ];
687
+ inputProps.onValueChange(name, type, newValues);
688
+ }
689
+ })));
690
+ };
691
+ var FormActions = ({ onCancel, onSubmit, canSubmit, readonly }) => {
692
+ const { t } = useTranslation2(translationKey);
693
+ return /* @__PURE__ */ React5.createElement("div", {
535
694
  role: "none",
536
695
  className: "flex justify-center"
537
696
  }, /* @__PURE__ */ React5.createElement("div", {
@@ -542,14 +701,14 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
542
701
  icon: "ph--x--regular",
543
702
  label: t("button cancel"),
544
703
  onClick: onCancel
545
- }), onSave && /* @__PURE__ */ React5.createElement(IconButton, {
704
+ }), onSubmit && /* @__PURE__ */ React5.createElement(IconButton, {
546
705
  type: "submit",
547
706
  "data-testid": "save-button",
548
- disabled: !canSave,
707
+ disabled: !canSubmit,
549
708
  icon: "ph--check--regular",
550
709
  label: t("button save"),
551
- onClick: handleSave
552
- }))));
710
+ onClick: onSubmit
711
+ })));
553
712
  };
554
713
 
555
714
  // packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
@@ -720,7 +879,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
720
879
  name: view.name,
721
880
  // TODO(burdon): Need to warn user of possible consequences of editing.
722
881
  // TODO(burdon): Settings should have domain name owned by user.
723
- typename: view.query.typename
882
+ typename: view.query.type
724
883
  };
725
884
  }, [
726
885
  view
@@ -728,7 +887,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
728
887
  const handleViewUpdate = useCallback3(({ name, typename }) => {
729
888
  requestAnimationFrame(() => {
730
889
  view.name = name;
731
- view.query.typename = typename;
890
+ view.query.type = typename;
732
891
  schema.updateTypename(typename);
733
892
  });
734
893
  }, [
@@ -1028,6 +1187,7 @@ export {
1028
1187
  DeprecatedFormInput,
1029
1188
  FieldEditor,
1030
1189
  Form,
1190
+ FormActions,
1031
1191
  GeiPointInput,
1032
1192
  InputHeader,
1033
1193
  NumberInput,