@dxos/react-ui-form 0.7.4 → 0.7.5-labs.071a3e2

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.
Files changed (77) hide show
  1. package/dist/lib/browser/index.mjs +841 -521
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +877 -571
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +841 -521
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +2 -2
  11. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/ArrayField.d.ts +16 -0
  13. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -0
  14. package/dist/types/src/components/Form/Defaults.d.ts +5 -6
  15. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/Form.d.ts +14 -17
  17. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/Form.stories.d.ts +7 -1
  19. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/FormActions.d.ts +7 -0
  21. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -0
  22. package/dist/types/src/components/Form/FormContent.d.ts +26 -0
  23. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -0
  24. package/dist/types/src/components/Form/FormContext.d.ts +20 -0
  25. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -0
  26. package/dist/types/src/components/Form/Input.d.ts +5 -6
  27. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +4 -0
  29. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -0
  30. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +4 -0
  31. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -0
  32. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +7 -0
  33. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -0
  34. package/dist/types/src/components/Form/custom/TupleInput.d.ts +6 -0
  35. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -0
  36. package/dist/types/src/components/Form/custom/index.d.ts +4 -0
  37. package/dist/types/src/components/Form/custom/index.d.ts.map +1 -0
  38. package/dist/types/src/components/Form/factory.d.ts +2 -2
  39. package/dist/types/src/components/Form/factory.d.ts.map +1 -1
  40. package/dist/types/src/components/Form/index.d.ts +2 -2
  41. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  42. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +6 -5
  43. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  44. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  45. package/dist/types/src/components/deprecated/FormInput.d.ts +5 -0
  46. package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
  47. package/dist/types/src/hooks/useForm.d.ts +13 -15
  48. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  49. package/dist/types/src/translations.d.ts +6 -0
  50. package/dist/types/src/translations.d.ts.map +1 -1
  51. package/dist/types/tsconfig.tsbuildinfo +1 -0
  52. package/package.json +28 -27
  53. package/src/components/FieldEditor/FieldEditor.stories.tsx +3 -3
  54. package/src/components/FieldEditor/FieldEditor.tsx +77 -51
  55. package/src/components/Form/ArrayField.tsx +111 -0
  56. package/src/components/Form/Defaults.tsx +20 -33
  57. package/src/components/Form/Form.stories.tsx +48 -6
  58. package/src/components/Form/Form.tsx +49 -329
  59. package/src/components/Form/FormActions.tsx +41 -0
  60. package/src/components/Form/FormContent.tsx +173 -0
  61. package/src/components/Form/FormContext.tsx +48 -0
  62. package/src/components/Form/Input.tsx +5 -6
  63. package/src/components/Form/custom/GeoPointInput.tsx +44 -0
  64. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +58 -0
  65. package/src/components/Form/custom/SelectOptionsInput.tsx +192 -0
  66. package/src/components/Form/custom/TupleInput.tsx +49 -0
  67. package/src/components/Form/custom/index.ts +7 -0
  68. package/src/components/Form/factory.tsx +4 -7
  69. package/src/components/Form/index.ts +3 -2
  70. package/src/components/ViewEditor/ViewEditor.stories.tsx +30 -8
  71. package/src/components/ViewEditor/ViewEditor.tsx +18 -10
  72. package/src/components/deprecated/FormInput.tsx +20 -7
  73. package/src/hooks/useForm.ts +67 -79
  74. package/src/translations.ts +8 -0
  75. package/dist/types/src/components/Form/Custom.d.ts +0 -5
  76. package/dist/types/src/components/Form/Custom.d.ts.map +0 -1
  77. package/src/components/Form/Custom.tsx +0 -65
@@ -35,6 +35,7 @@ var translations_default = [
35
35
  "format ref": "Reference",
36
36
  "format regex": "RegExp",
37
37
  "format string": "String",
38
+ "format single-select": "Select",
38
39
  "format text": "Long text",
39
40
  "format time": "Time",
40
41
  "format timestamp": "Timestamp",
@@ -44,7 +45,13 @@ var translations_default = [
44
45
  "format uuid": "UUID",
45
46
  // Placeholders
46
47
  "placeholder latitude": "Latitude",
47
- "placeholder longitude": "Longitude"
48
+ "placeholder longitude": "Longitude",
49
+ // SelectOptionsInput
50
+ "select option label": "Label",
51
+ "select option label placeholder": "Option label",
52
+ "select option color": "Color",
53
+ "select option delete": "Delete",
54
+ "select option add": "Add option"
48
55
  }
49
56
  }
50
57
  }
@@ -53,6 +60,16 @@ var translations_default = [
53
60
  // packages/ui/react-ui-form/src/components/deprecated/FormInput.tsx
54
61
  import React from "react";
55
62
  import { Input } from "@dxos/react-ui";
63
+ import { mx, textBlockWidth } from "@dxos/react-ui-theme";
64
+ var DeprecatedFormContainer = ({ children, classNames }) => {
65
+ return /* @__PURE__ */ React.createElement("div", {
66
+ role: "none",
67
+ className: "w-full p-4 justify-center overflow-x-hidden overflow-y-auto"
68
+ }, /* @__PURE__ */ React.createElement("div", {
69
+ role: "form",
70
+ className: mx("flex flex-col", textBlockWidth, classNames)
71
+ }, children));
72
+ };
56
73
  var DeprecatedFormInput = ({ label, description, secondary, children }) => {
57
74
  const primary = /* @__PURE__ */ React.createElement("div", {
58
75
  role: "none",
@@ -61,46 +78,37 @@ var DeprecatedFormInput = ({ label, description, secondary, children }) => {
61
78
  role: "none",
62
79
  className: "flex flex-col w-full"
63
80
  }, /* @__PURE__ */ React.createElement(Input.Label, {
64
- classNames: "flex min-h-[40px] items-center"
81
+ classNames: "flex min-bs-[--rail-action] items-center",
82
+ style: {
83
+ fontSize: "unset"
84
+ }
65
85
  }, label), description && /* @__PURE__ */ React.createElement(Input.DescriptionAndValidation, {
66
86
  classNames: "mbs-0.5"
67
87
  }, /* @__PURE__ */ React.createElement(Input.Description, null, description))), /* @__PURE__ */ React.createElement("div", {
68
88
  role: "none"
69
89
  }, /* @__PURE__ */ React.createElement("div", {
70
90
  role: "none",
71
- className: "flex min-h-[40px] items-center"
91
+ className: "flex min-bs-[--rail-action] items-center"
72
92
  }, children))));
73
93
  if (secondary) {
74
94
  return /* @__PURE__ */ React.createElement("div", {
75
- role: "none",
76
- className: "flex flex-col w-full"
95
+ role: "none"
77
96
  }, primary, secondary);
78
97
  }
79
98
  return primary;
80
99
  };
81
100
 
82
101
  // packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
83
- import React6, { useCallback as useCallback2, useEffect as useEffect3, useState as useState2 } from "react";
102
+ import React12, { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo5, useState as useState3 } from "react";
84
103
  import { FormatEnum as FormatEnum2, FormatEnums, formatToType } from "@dxos/echo-schema";
85
- import { log as log3 } from "@dxos/log";
86
- import { useTranslation as useTranslation3 } from "@dxos/react-ui";
87
- import { getFormatSchema, getSchemaProperties as getSchemaProperties2, sortProperties } from "@dxos/schema";
88
-
89
- // packages/ui/react-ui-form/src/components/Form/Form.tsx
90
- import { pipe } from "effect";
91
- import { capitalize } from "effect/String";
92
- import React5, { useEffect as useEffect2, useMemo as useMemo2 } from "react";
93
- import { AST, S } from "@dxos/echo-schema";
94
- import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
95
104
  import { log as log2 } from "@dxos/log";
96
- import { IconButton, useTranslation as useTranslation2 } from "@dxos/react-ui";
97
- import { mx } from "@dxos/react-ui-theme";
98
- import { getSchemaProperties } from "@dxos/schema";
99
- import { isNotFalsy } from "@dxos/util";
105
+ import { useTranslation as useTranslation5 } from "@dxos/react-ui";
106
+ import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
100
107
 
101
- // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
108
+ // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
102
109
  import React3 from "react";
103
- import { Input as Input2, Select } from "@dxos/react-ui";
110
+ import { Input as Input2, useTranslation } from "@dxos/react-ui";
111
+ import { safeParseFloat } from "@dxos/util";
104
112
 
105
113
  // packages/ui/react-ui-form/src/components/Form/Input.tsx
106
114
  import React2 from "react";
@@ -121,164 +129,382 @@ var InputHeader = ({ children, error }) => {
121
129
  }, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null), error)));
122
130
  };
123
131
 
124
- // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
125
- var TextInput = ({ property, type, label, inputOnly, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
126
- const { status, error } = getStatus?.(property);
132
+ // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
133
+ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
134
+ const { t } = useTranslation(translationKey);
135
+ const { status, error } = getStatus();
136
+ const [lng = 0, lat = 0] = getValue3() ?? [];
127
137
  return /* @__PURE__ */ React3.createElement(Input2.Root, {
128
138
  validationValence: status
129
- }, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
139
+ }, /* @__PURE__ */ React3.createElement(InputHeader, {
140
+ error
141
+ }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement("div", {
142
+ className: "grid grid-cols-2 gap-2"
143
+ }, /* @__PURE__ */ React3.createElement(Input2.TextInput, {
144
+ type: "number",
145
+ disabled,
146
+ placeholder: t("placeholder longitude"),
147
+ value: lng,
148
+ onChange: (event) => onValueChange(type, [
149
+ lat,
150
+ safeParseFloat(event.target.value, 0)
151
+ ]),
152
+ onBlur
153
+ }), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
154
+ type: "number",
155
+ disabled,
156
+ placeholder: t("placeholder latitude"),
157
+ value: lat,
158
+ onChange: (event) => onValueChange(type, [
159
+ safeParseFloat(event.target.value, 0),
160
+ lng
161
+ ]),
162
+ onBlur
163
+ })));
164
+ };
165
+
166
+ // packages/ui/react-ui-form/src/components/Form/custom/SelectOptionsInput.tsx
167
+ import React4, { useCallback, useEffect, useState, useRef } from "react";
168
+ import { PublicKey } from "@dxos/keys";
169
+ import { Icon as Icon2, IconButton, Input as Input3, Tag, Toolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
170
+ import { List } from "@dxos/react-ui-list";
171
+ import { HuePickerToolbarButton } from "@dxos/react-ui-pickers";
172
+ import { hueTokenThemes, mx as mx2 } from "@dxos/react-ui-theme";
173
+ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange }) => {
174
+ const [selected, setSelectedId] = useState(null);
175
+ const [isNewOption, setIsNewOption] = useState(false);
176
+ const inputRef = useRef(null);
177
+ const { t } = useTranslation2(translationKey);
178
+ const { status, error } = getStatus();
179
+ const options = getValue3();
180
+ useEffect(() => {
181
+ if (options === void 0) {
182
+ onValueChange(type, []);
183
+ }
184
+ }, [
185
+ options,
186
+ onValueChange,
187
+ type
188
+ ]);
189
+ const randomHue = () => {
190
+ const usedHues = new Set(options?.map((option) => option.color) ?? []);
191
+ const hueKeys = Object.keys(hueTokenThemes);
192
+ const unusedHues = hueKeys.filter((hue) => !usedHues.has(hue));
193
+ if (unusedHues.length > 0) {
194
+ return unusedHues[Math.floor(Math.random() * unusedHues.length)];
195
+ }
196
+ const lastThree = new Set(options?.slice(-3).map((option) => option.color) ?? []);
197
+ const availableHues = hueKeys.filter((hue) => !lastThree.has(hue));
198
+ return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hueKeys[Math.floor(Math.random() * hueKeys.length)];
199
+ };
200
+ const handleAdd = useCallback(() => {
201
+ const newId = PublicKey.random().truncate();
202
+ const newOption = {
203
+ id: newId,
204
+ title: "",
205
+ color: randomHue()
206
+ };
207
+ onValueChange(type, [
208
+ ...options ?? [],
209
+ newOption
210
+ ]);
211
+ setSelectedId(newId);
212
+ setIsNewOption(true);
213
+ }, [
214
+ options,
215
+ type,
216
+ onValueChange
217
+ ]);
218
+ const handleDelete = useCallback((id) => {
219
+ const newOptions = options?.filter((option) => option.id !== id) ?? [];
220
+ onValueChange(type, newOptions);
221
+ setSelectedId(null);
222
+ }, [
223
+ options,
224
+ type,
225
+ onValueChange
226
+ ]);
227
+ const handleMove = useCallback((from, to) => {
228
+ if (!options) {
229
+ return;
230
+ }
231
+ const newOptions = [
232
+ ...options
233
+ ];
234
+ const [removed] = newOptions.splice(from, 1);
235
+ newOptions.splice(to, 0, removed);
236
+ onValueChange(type, newOptions);
237
+ }, [
238
+ options,
239
+ type,
240
+ onValueChange
241
+ ]);
242
+ const handleClick = useCallback((id) => {
243
+ setSelectedId((current) => current === id ? null : id);
244
+ }, []);
245
+ const handleTitleChange = useCallback((id) => (e) => {
246
+ const newOptions = options?.map((o) => o.id === id ? {
247
+ ...o,
248
+ title: e.target.value
249
+ } : o);
250
+ onValueChange(type, newOptions ?? []);
251
+ }, [
252
+ options,
253
+ type,
254
+ onValueChange
255
+ ]);
256
+ const handleKeyDown = useCallback((e) => {
257
+ if (e.key === "Enter") {
258
+ setSelectedId(null);
259
+ }
260
+ }, []);
261
+ const handleColorChange = useCallback((id) => (hue) => {
262
+ const newOptions = options?.map((o) => o.id === id ? {
263
+ ...o,
264
+ color: hue
265
+ } : o);
266
+ onValueChange(type, newOptions ?? []);
267
+ }, [
268
+ options,
269
+ type,
270
+ onValueChange
271
+ ]);
272
+ useEffect(() => {
273
+ if (selected && isNewOption && inputRef.current) {
274
+ inputRef.current.focus();
275
+ setIsNewOption(false);
276
+ }
277
+ }, [
278
+ selected,
279
+ isNewOption
280
+ ]);
281
+ return /* @__PURE__ */ React4.createElement(Input3.Root, {
282
+ validationValence: status
283
+ }, /* @__PURE__ */ React4.createElement(InputHeader, {
284
+ error
285
+ }, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
286
+ role: "none"
287
+ }, options && /* @__PURE__ */ React4.createElement(List.Root, {
288
+ items: options,
289
+ isItem: (item) => true,
290
+ onMove: handleMove
291
+ }, ({ items }) => /* @__PURE__ */ React4.createElement("div", {
292
+ role: "list",
293
+ className: "w-full overflow-auto"
294
+ }, items?.map((item) => /* @__PURE__ */ React4.createElement("div", {
295
+ key: item.id
296
+ }, /* @__PURE__ */ React4.createElement(List.Item, {
297
+ role: "button",
298
+ item,
299
+ classNames: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
300
+ "aria-expanded": selected === item.id
301
+ }, /* @__PURE__ */ React4.createElement("div", {
302
+ className: "flex items-center"
303
+ }, /* @__PURE__ */ React4.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React4.createElement(List.ItemTitle, {
304
+ onClick: () => handleClick(item.id),
305
+ classNames: "flex-1"
306
+ }, /* @__PURE__ */ React4.createElement(Tag, {
307
+ palette: item.color
308
+ }, item.title || "\u200B")), /* @__PURE__ */ React4.createElement(Icon2, {
309
+ icon: selected === item.id ? "ph--caret-down--regular" : "ph--caret-right--regular",
310
+ onClick: () => handleClick(item.id)
311
+ })), selected === item.id && /* @__PURE__ */ React4.createElement("div", {
312
+ className: "mis-4 mbs-2 flex flex-col gap-1"
313
+ }, /* @__PURE__ */ React4.createElement(Input3.Label, {
314
+ classNames: "text-xs"
315
+ }, t("select option label")), /* @__PURE__ */ React4.createElement("div", {
316
+ className: "flex flex-row items-center gap-1"
317
+ }, /* @__PURE__ */ React4.createElement(Input3.TextInput, {
318
+ placeholder: t("select option label placeholder"),
319
+ ref: selected === item.id ? inputRef : void 0,
320
+ value: item.title,
321
+ onChange: handleTitleChange(item.id),
322
+ onKeyDown: handleKeyDown,
323
+ classNames: "flex-1"
324
+ }), /* @__PURE__ */ React4.createElement(Toolbar.Root, {
325
+ classNames: "p-0 m-0 !is-auto"
326
+ }, /* @__PURE__ */ React4.createElement(HuePickerToolbarButton, {
327
+ hue: item.color,
328
+ onChangeHue: handleColorChange(item.id)
329
+ })), /* @__PURE__ */ React4.createElement(IconButton, {
330
+ icon: "ph--trash--fill",
331
+ iconOnly: true,
332
+ label: t("select option delete"),
333
+ onClick: () => handleDelete(item.id)
334
+ })))))), /* @__PURE__ */ React4.createElement("div", {
335
+ role: "listitem"
336
+ }, /* @__PURE__ */ React4.createElement("div", {
337
+ role: "button",
338
+ className: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
339
+ onClick: handleAdd
340
+ }, /* @__PURE__ */ React4.createElement("div", {
341
+ className: "flex items-center gap-1"
342
+ }, /* @__PURE__ */ React4.createElement(Icon2, {
343
+ icon: "ph--plus--regular"
344
+ }), /* @__PURE__ */ React4.createElement("span", {
345
+ className: "text-sm"
346
+ }, t("select option add")))))))));
347
+ };
348
+
349
+ // packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
350
+ import React5 from "react";
351
+ import { Input as Input4 } from "@dxos/react-ui";
352
+ import { mx as mx3 } from "@dxos/react-ui-theme";
353
+ import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
354
+ var gridCols = [
355
+ "grid-cols-1",
356
+ "grid-cols-2",
357
+ "grid-cols-3",
358
+ "grid-cols-4"
359
+ ];
360
+ var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
361
+ const { status, error } = getStatus();
362
+ const values = getValue3() ?? {};
363
+ return /* @__PURE__ */ React5.createElement(Input4.Root, {
364
+ validationValence: status
365
+ }, /* @__PURE__ */ React5.createElement(InputHeader, {
366
+ error
367
+ }, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
368
+ className: mx3("grid gap-2", gridCols[binding.length - 1])
369
+ }, binding.map((prop) => /* @__PURE__ */ React5.createElement(Input4.TextInput, {
370
+ key: prop,
371
+ type: "number",
372
+ disabled,
373
+ value: values[prop],
374
+ onChange: (event) => onValueChange(type, {
375
+ ...values,
376
+ [prop]: safeParseFloat2(event.target.value, 0)
377
+ }),
378
+ onBlur
379
+ }))));
380
+ };
381
+
382
+ // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
383
+ import React6 from "react";
384
+ import { Input as Input5, Select } from "@dxos/react-ui";
385
+ var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
386
+ const { status, error } = getStatus();
387
+ return /* @__PURE__ */ React6.createElement(Input5.Root, {
388
+ validationValence: status
389
+ }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
130
390
  error
131
- }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
391
+ }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.TextInput, {
132
392
  disabled,
133
393
  placeholder,
134
- value: getValue(property) ?? "",
135
- onChange: (event) => onValueChange(property, type, event.target.value),
394
+ value: getValue3() ?? "",
395
+ onChange: (event) => onValueChange(type, event.target.value),
136
396
  onBlur
137
- }), inputOnly && /* @__PURE__ */ React3.createElement(Input2.Validation, null, error));
397
+ }), inputOnly && /* @__PURE__ */ React6.createElement(Input5.Validation, null, error));
138
398
  };
139
- var NumberInput = ({ property, type, label, inputOnly, disabled, placeholder, getStatus, getValue, onValueChange, onBlur }) => {
140
- const { status, error } = getStatus?.(property);
141
- return /* @__PURE__ */ React3.createElement(Input2.Root, {
399
+ var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
400
+ const { status, error } = getStatus();
401
+ return /* @__PURE__ */ React6.createElement(Input5.Root, {
142
402
  validationValence: status
143
- }, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
403
+ }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
144
404
  error
145
- }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
405
+ }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.TextInput, {
146
406
  type: "number",
147
407
  disabled,
148
408
  placeholder,
149
- value: getValue(property) ?? 0,
150
- onChange: (event) => onValueChange(property, type, event.target.value),
409
+ value: getValue3() ?? 0,
410
+ onChange: (event) => onValueChange(type, event.target.value),
151
411
  onBlur
152
- }), inputOnly && /* @__PURE__ */ React3.createElement(Input2.DescriptionAndValidation, null, error));
412
+ }), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
153
413
  };
154
- var BooleanInput = ({ property, type, label, inputOnly, getStatus, getValue, onValueChange }) => {
155
- const { status, error } = getStatus?.(property);
156
- return /* @__PURE__ */ React3.createElement(Input2.Root, {
414
+ var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
415
+ const { status, error } = getStatus();
416
+ return /* @__PURE__ */ React6.createElement(Input5.Root, {
157
417
  validationValence: status
158
- }, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
418
+ }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
159
419
  error
160
- }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Input2.Switch, {
161
- checked: getValue(property),
162
- onCheckedChange: (value) => onValueChange(property, type, value)
163
- }), inputOnly && /* @__PURE__ */ React3.createElement(Input2.DescriptionAndValidation, null, error));
420
+ }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.Switch, {
421
+ checked: getValue3(),
422
+ onCheckedChange: (value) => onValueChange(type, value)
423
+ }), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
164
424
  };
165
- var SelectInput = ({ property, type, label, inputOnly, disabled, placeholder, options, getStatus, getValue, onValueChange }) => {
166
- const { status, error } = getStatus?.(property);
167
- return /* @__PURE__ */ React3.createElement(Input2.Root, {
425
+ var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
426
+ const { status, error } = getStatus();
427
+ return /* @__PURE__ */ React6.createElement(Input5.Root, {
168
428
  validationValence: status
169
- }, !inputOnly && /* @__PURE__ */ React3.createElement(InputHeader, {
429
+ }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
170
430
  error
171
- }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement(Select.Root, {
172
- value: getValue(property),
173
- onValueChange: (value) => onValueChange(property, type, value)
174
- }, /* @__PURE__ */ React3.createElement(Select.TriggerButton, {
431
+ }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Select.Root, {
432
+ value: getValue3(),
433
+ onValueChange: (value) => onValueChange(type, value)
434
+ }, /* @__PURE__ */ React6.createElement(Select.TriggerButton, {
175
435
  classNames: "is-full",
176
436
  disabled,
177
437
  placeholder
178
- }), /* @__PURE__ */ React3.createElement(Select.Portal, null, /* @__PURE__ */ React3.createElement(Select.Content, null, /* @__PURE__ */ React3.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
438
+ }), /* @__PURE__ */ React6.createElement(Select.Portal, null, /* @__PURE__ */ React6.createElement(Select.Content, null, /* @__PURE__ */ React6.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
179
439
  // NOTE: Numeric values are converted to and from strings.
180
- /* @__PURE__ */ React3.createElement(Select.Option, {
440
+ /* @__PURE__ */ React6.createElement(Select.Option, {
181
441
  key: String(value),
182
442
  value: String(value)
183
443
  }, label2 ?? String(value))
184
- )))))), inputOnly && /* @__PURE__ */ React3.createElement(Input2.DescriptionAndValidation, null, error));
444
+ )))))), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
185
445
  };
186
446
 
187
- // packages/ui/react-ui-form/src/components/Form/factory.tsx
188
- import { FormatEnum } from "@dxos/echo-schema";
447
+ // packages/ui/react-ui-form/src/components/Form/Form.tsx
448
+ import React11, { useEffect as useEffect3, useMemo as useMemo4 } from "react";
449
+ import { mx as mx7 } from "@dxos/react-ui-theme";
189
450
 
190
- // packages/ui/react-ui-form/src/components/Form/Custom.tsx
191
- import React4 from "react";
192
- import { Input as Input3, useTranslation } from "@dxos/react-ui";
193
- var GeiPointInput = ({ property, type, label, disabled, getStatus, getValue, onValueChange, onBlur }) => {
194
- const { t } = useTranslation(translationKey);
195
- const { status, error } = getStatus?.(property);
196
- const [lng = 0, lat = 0] = getValue(property) ?? [];
197
- return /* @__PURE__ */ React4.createElement(Input3.Root, {
198
- validationValence: status
199
- }, /* @__PURE__ */ React4.createElement(InputHeader, {
200
- error
201
- }, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
202
- className: "grid grid-cols-2 gap-2"
203
- }, /* @__PURE__ */ React4.createElement(Input3.TextInput, {
204
- type: "number",
205
- disabled,
206
- placeholder: t("placeholder longitude"),
207
- value: lng,
208
- onChange: (event) => onValueChange(property, type, [
209
- lat,
210
- safeParseFloat(event.target.value, 0)
211
- ]),
212
- onBlur
213
- }), /* @__PURE__ */ React4.createElement(Input3.TextInput, {
214
- type: "number",
215
- disabled,
216
- placeholder: t("placeholder latitude"),
217
- value: lat,
218
- onChange: (event) => onValueChange(property, type, [
219
- safeParseFloat(event.target.value, 0),
220
- lng
221
- ]),
222
- onBlur
223
- })));
224
- };
225
- var safeParseFloat = (str, defaultValue) => {
226
- try {
227
- return parseFloat(str);
228
- } catch {
229
- return defaultValue ?? 0;
230
- }
231
- };
451
+ // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
452
+ import React8 from "react";
453
+ import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
454
+ import { mx as mx4 } from "@dxos/react-ui-theme";
232
455
 
233
- // packages/ui/react-ui-form/src/components/Form/factory.tsx
234
- var getInputComponent = (type, format) => {
235
- switch (format) {
236
- case FormatEnum.LatLng:
237
- return GeiPointInput;
238
- }
239
- switch (type) {
240
- case "string":
241
- return TextInput;
242
- case "number":
243
- return NumberInput;
244
- case "boolean":
245
- return BooleanInput;
246
- }
247
- };
456
+ // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
457
+ import React7, { createContext, useContext } from "react";
458
+ import { raise } from "@dxos/debug";
459
+ import { getValue as getValue2 } from "@dxos/echo-schema";
460
+ import { createJsonPath as createJsonPath2 } from "@dxos/effect";
248
461
 
249
462
  // packages/ui/react-ui-form/src/hooks/useForm.ts
250
- import { useCallback, useEffect, useMemo, useState } from "react";
463
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useState as useState2 } from "react";
464
+ import { getValue, setValue } from "@dxos/echo-schema";
465
+ import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
251
466
  import { invariant } from "@dxos/invariant";
252
467
  import { log } from "@dxos/log";
253
468
  import { validateSchema } from "@dxos/schema";
254
469
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
255
470
  var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
256
- const [values, setValues] = useState(initialValues);
257
- useEffect(() => {
471
+ invariant(AST.isTypeLiteral(schema.ast), void 0, {
472
+ F: __dxlog_file,
473
+ L: 91,
474
+ S: void 0,
475
+ A: [
476
+ "AST.isTypeLiteral(schema.ast)",
477
+ ""
478
+ ]
479
+ });
480
+ const [values, setValues] = useState2(initialValues);
481
+ useEffect2(() => {
258
482
  setValues(initialValues);
259
483
  }, [
260
484
  initialValues
261
485
  ]);
262
- const [touched, setTouched] = useState(createKeySet(initialValues, false));
263
- const [changed, setChanged] = useState(createKeySet(initialValues, false));
264
- const [errors, setErrors] = useState({});
265
- const [saving, setSaving] = useState(false);
266
- const validate = useCallback((values2) => {
486
+ const [touched, setTouched] = useState2(createKeySet(initialValues, false));
487
+ const [changed, setChanged] = useState2(createKeySet(initialValues, false));
488
+ const [errors, setErrors] = useState2({});
489
+ const [saving, setSaving] = useState2(false);
490
+ const validate = useCallback2((values2) => {
267
491
  let errors2 = validateSchema(schema, values2) ?? [];
268
492
  if (errors2.length === 0 && onValidate) {
269
- errors2 = onValidate(values2) ?? [];
493
+ const validatedValues = values2;
494
+ errors2 = onValidate(validatedValues) ?? [];
270
495
  }
271
496
  setErrors(flatMap(errors2));
272
- return errors2.length === 0;
497
+ const valid = errors2.length === 0;
498
+ return valid;
273
499
  }, [
274
500
  schema,
275
501
  onValidate
276
502
  ]);
277
- const [schemaDirty, setSchemaDirty] = useState(false);
278
- useEffect(() => setSchemaDirty(true), [
503
+ const [schemaDirty, setSchemaDirty] = useState2(false);
504
+ useEffect2(() => setSchemaDirty(true), [
279
505
  schema
280
506
  ]);
281
- useEffect(() => {
507
+ useEffect2(() => {
282
508
  if (schemaDirty) {
283
509
  validate(values);
284
510
  setSchemaDirty(false);
@@ -287,13 +513,13 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
287
513
  values,
288
514
  schemaDirty
289
515
  ]);
290
- const canSave = useMemo(() => !saving && Object.keys(values).every((property) => touched[property] === false || !errors[property]), [
291
- values,
516
+ const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
517
+ !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
292
518
  touched,
293
519
  errors,
294
520
  saving
295
521
  ]);
296
- const handleSave = useCallback(async () => {
522
+ const handleSave = useCallback2(async () => {
297
523
  if (validate(values)) {
298
524
  setSaving(true);
299
525
  try {
@@ -307,19 +533,24 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
307
533
  }, [
308
534
  values,
309
535
  validate,
310
- onSave
536
+ onSave,
537
+ changed
311
538
  ]);
312
- const getStatus = useCallback((property) => ({
313
- status: errors[property] ? "error" : void 0,
314
- error: errors[property] ? errors[property] : void 0
315
- }), [
316
- touched,
539
+ const getStatus = useCallback2((path) => {
540
+ const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
541
+ const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
542
+ return {
543
+ status: matchingError ? "error" : void 0,
544
+ error: matchingError ? matchingError[1] : void 0
545
+ };
546
+ }, [
317
547
  errors
318
548
  ]);
319
- const getValue = (property) => {
320
- return getByPath(values, property);
549
+ const getFormValue = (path) => {
550
+ return getValue(values, createJsonPath(path));
321
551
  };
322
- const onValueChange = (property, type, value) => {
552
+ const onValueChange = (path, type, value) => {
553
+ const jsonPath = createJsonPath(path);
323
554
  let parsedValue = value;
324
555
  try {
325
556
  if (type === "number") {
@@ -328,17 +559,19 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
328
559
  } catch (err) {
329
560
  log.catch(err, void 0, {
330
561
  F: __dxlog_file,
331
- L: 183,
562
+ L: 193,
332
563
  S: void 0,
333
564
  C: (f, a) => f(...a)
334
565
  });
335
566
  parsedValue = void 0;
336
567
  }
337
- const newValues = setByPath(values, property, parsedValue);
568
+ const newValues = {
569
+ ...setValue(values, jsonPath, parsedValue)
570
+ };
338
571
  setValues(newValues);
339
572
  setChanged((prev) => ({
340
573
  ...prev,
341
- [property]: true
574
+ [jsonPath]: true
342
575
  }));
343
576
  onValuesChanged?.(newValues);
344
577
  const isValid = validate(newValues);
@@ -348,15 +581,12 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
348
581
  });
349
582
  }
350
583
  };
351
- const onBlur = useCallback((event) => {
352
- const { name } = event.target;
584
+ const onTouched = useCallback2((path) => {
585
+ const jsonPath = createJsonPath(path);
353
586
  setTouched((touched2) => ({
354
587
  ...touched2,
355
- [name]: true
588
+ [jsonPath]: true
356
589
  }));
357
- if (event.relatedTarget?.getAttribute("type") === "submit") {
358
- setTouched(createKeySet(values, true));
359
- }
360
590
  validate(values);
361
591
  }, [
362
592
  validate,
@@ -372,15 +602,15 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
372
602
  handleSave,
373
603
  // Field utils.
374
604
  getStatus,
375
- getValue,
605
+ getValue: getFormValue,
376
606
  onValueChange,
377
- onBlur
607
+ onTouched
378
608
  };
379
609
  };
380
610
  var createKeySet = (obj, value) => {
381
611
  invariant(obj, void 0, {
382
612
  F: __dxlog_file,
383
- L: 234,
613
+ L: 235,
384
614
  S: void 0,
385
615
  A: [
386
616
  "obj",
@@ -394,338 +624,400 @@ var createKeySet = (obj, value) => {
394
624
  };
395
625
  var flatMap = (errors) => {
396
626
  return errors.reduce((result, { path, message }) => {
397
- if (!(path in result)) {
398
- result[path] = message;
627
+ const jsonPath = fromEffectValidationPath(path);
628
+ if (!(jsonPath in result)) {
629
+ result[jsonPath] = message;
399
630
  }
400
631
  return result;
401
632
  }, {});
402
633
  };
403
- var getByPath = (obj, path) => {
404
- const [key, index] = path.split(".");
405
- return index === void 0 ? obj[key] : obj[key][parseInt(index)];
634
+
635
+ // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
636
+ var FormContext = /* @__PURE__ */ createContext(void 0);
637
+ var useFormContext = () => {
638
+ return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
639
+ };
640
+ var useFormValues = (path = []) => {
641
+ const { values: formValues } = useFormContext();
642
+ const jsonPath = createJsonPath2(path);
643
+ return getValue2(formValues, jsonPath);
406
644
  };
407
- var setByPath = (obj, path, value) => {
408
- const [key, index] = path.split(".");
409
- const newValue = index === void 0 ? value : [
410
- ...obj[key]
411
- ].map((v, i) => i === parseInt(index) ? value : v);
645
+ var useInputProps = (path = []) => {
646
+ const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
412
647
  return {
413
- ...obj,
414
- [key]: newValue
648
+ getStatus: () => getStatus(path),
649
+ getValue: () => getFormValue(path),
650
+ onValueChange: (type, value) => onValueChange(path, type, value),
651
+ onBlur: () => onTouched(path)
415
652
  };
416
653
  };
654
+ var FormProvider = ({ children, ...formOptions }) => {
655
+ const formHandler = useForm(formOptions);
656
+ return /* @__PURE__ */ React7.createElement(FormContext.Provider, {
657
+ value: formHandler
658
+ }, children);
659
+ };
417
660
 
418
- // packages/ui/react-ui-form/src/components/Form/Form.tsx
419
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/Form.tsx";
661
+ // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
662
+ var FormActions = ({ onCancel, readonly }) => {
663
+ const { t } = useTranslation3(translationKey);
664
+ const { canSave, handleSave } = useFormContext();
665
+ return /* @__PURE__ */ React8.createElement("div", {
666
+ role: "none",
667
+ className: "flex justify-center"
668
+ }, /* @__PURE__ */ React8.createElement("div", {
669
+ role: "none",
670
+ className: mx4(onCancel && !readonly && "grid grid-cols-2 gap-2")
671
+ }, onCancel && !readonly && /* @__PURE__ */ React8.createElement(IconButton2, {
672
+ "data-testid": "cancel-button",
673
+ icon: "ph--x--regular",
674
+ label: t("button cancel"),
675
+ onClick: onCancel
676
+ }), handleSave && /* @__PURE__ */ React8.createElement(IconButton2, {
677
+ type: "submit",
678
+ "data-testid": "save-button",
679
+ disabled: !canSave,
680
+ icon: "ph--check--regular",
681
+ label: t("button save"),
682
+ onClick: handleSave
683
+ })));
684
+ };
685
+
686
+ // packages/ui/react-ui-form/src/components/Form/FormContent.tsx
687
+ import { pipe as pipe2 } from "effect";
688
+ import { capitalize as capitalize2 } from "effect/String";
689
+ import React10, { useMemo as useMemo3 } from "react";
690
+ import { AST as AST3, S } from "@dxos/echo-schema";
691
+ import { createJsonPath as createJsonPath3, findNode as findNode2, getDiscriminatedType as getDiscriminatedType2, isDiscriminatedUnion as isDiscriminatedUnion2 } from "@dxos/effect";
692
+ import { mx as mx6 } from "@dxos/react-ui-theme";
693
+ import { getSchemaProperties as getSchemaProperties2 } from "@dxos/schema";
694
+ import { isNotFalsy } from "@dxos/util";
695
+
696
+ // packages/ui/react-ui-form/src/components/Form/ArrayField.tsx
697
+ import { pipe } from "effect";
698
+ import { capitalize } from "effect/String";
699
+ import React9, { useCallback as useCallback3, useMemo as useMemo2 } from "react";
700
+ import { AST as AST2 } from "@dxos/echo-schema";
701
+ import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
702
+ import { invariant as invariant2 } from "@dxos/invariant";
703
+ import { IconButton as IconButton3, useTranslation as useTranslation4 } from "@dxos/react-ui";
704
+ import { mx as mx5 } from "@dxos/react-ui-theme";
705
+ import { getSchemaProperties } from "@dxos/schema";
706
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/ArrayField.tsx";
420
707
  var padding = "px-2";
421
- var Form = ({ classNames, schema, values: initialValues, path = [], readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, Custom }) => {
422
- const onValid = useMemo2(() => autoSave ? onSave : void 0, [
423
- autoSave,
424
- onSave
425
- ]);
426
- const { canSave, values, errors, handleSave, ...inputProps } = useForm({
427
- schema,
428
- initialValues,
429
- onValuesChanged,
430
- onValidate,
431
- onValid,
432
- onSave
708
+ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponent }) => {
709
+ const { t } = useTranslation4(translationKey);
710
+ const { ast, name, type, title } = property;
711
+ const values = useFormValues(path ?? []);
712
+ invariant2(Array.isArray(values), `Values at path ${path?.join(".")} must be an array.`, {
713
+ F: __dxlog_file2,
714
+ L: 37,
715
+ S: void 0,
716
+ A: [
717
+ "Array.isArray(values)",
718
+ "`Values at path ${path?.join('.')} must be an array.`"
719
+ ]
433
720
  });
434
- const properties = useMemo2(() => {
435
- const props = getSchemaProperties(schema.ast, values);
436
- const filtered = filter ? filter(props) : props;
437
- const findIndex = (props2, prop) => {
438
- const idx = props2.findIndex((p) => p === prop);
439
- return idx === -1 ? Infinity : idx;
440
- };
441
- return sort ? filtered.sort(({ name: a }, { name: b }) => findIndex(sort, a) - findIndex(sort, b)) : filtered;
721
+ const label = title ?? pipe(name, capitalize);
722
+ const tupleType = findNode(ast, AST2.isTupleType);
723
+ const elementType = tupleType?.rest[0]?.type;
724
+ const getDefaultObjectValue = (typeNode) => {
725
+ const baseNode = findNode(typeNode, isDiscriminatedUnion);
726
+ const typeLiteral = baseNode ? getDiscriminatedType(baseNode, {}) : findNode(typeNode, AST2.isTypeLiteral);
727
+ if (!typeLiteral) {
728
+ return {};
729
+ }
730
+ return Object.fromEntries(getSchemaProperties(typeLiteral, {}).map((prop) => [
731
+ prop.name,
732
+ prop.defaultValue
733
+ ]));
734
+ };
735
+ const newValue = useMemo2(() => {
736
+ if (type === "object" && elementType) {
737
+ return getDefaultObjectValue(elementType);
738
+ } else {
739
+ return SimpleType.getDefaultValue(type);
740
+ }
442
741
  }, [
443
- schema,
444
- filter,
742
+ type,
743
+ elementType
744
+ ]);
745
+ const handleAdd = useCallback3(() => {
746
+ inputProps.onValueChange(type, [
747
+ ...values,
748
+ newValue
749
+ ]);
750
+ }, [
751
+ type,
752
+ elementType,
753
+ inputProps,
754
+ newValue,
445
755
  values
446
756
  ]);
447
- useEffect2(() => {
448
- if (errors && Object.keys(errors).length) {
449
- log2.warn("validation", {
450
- errors
451
- }, {
452
- F: __dxlog_file2,
453
- L: 100,
454
- S: void 0,
455
- C: (f, a) => f(...a)
456
- });
457
- }
757
+ const handleRemove = useCallback3((index) => {
758
+ const newValues = values.filter((_, i) => i !== index);
759
+ inputProps.onValueChange(type, newValues);
458
760
  }, [
459
- errors
761
+ type,
762
+ inputProps,
763
+ values
460
764
  ]);
461
- return /* @__PURE__ */ React5.createElement("div", {
462
- role: "form",
463
- className: mx("flex flex-col w-full gap-2 py-2", classNames),
464
- "data-testid": testId
465
- }, properties.map((property) => {
466
- const { ast, name, type, format, title, description, examples, options, array } = property;
467
- const key = [
468
- ...path,
469
- name
470
- ];
471
- const label = title ?? pipe(name, capitalize);
472
- const placeholder = examples?.length ? `Example: "${examples[0]}"` : description;
473
- let InputComponent = Custom?.[key.join(".")];
474
- if (!InputComponent) {
475
- if (options) {
476
- return /* @__PURE__ */ React5.createElement("div", {
477
- key: name,
478
- role: "none",
479
- className: padding
480
- }, /* @__PURE__ */ React5.createElement(SelectInput, {
481
- type,
482
- format,
483
- property: name,
484
- disabled: readonly,
485
- label,
486
- options: options.map((option) => ({
487
- value: option,
488
- label: String(option)
489
- })),
490
- placeholder,
491
- ...inputProps
492
- }));
493
- }
494
- InputComponent = getInputComponent(type, format);
495
- if (array) {
496
- return /* @__PURE__ */ React5.createElement(ArrayField, {
497
- key: name,
498
- name,
499
- type,
500
- array: true,
501
- label,
502
- ast,
503
- values,
504
- format,
505
- readonly,
506
- placeholder,
507
- inputProps,
508
- Custom
509
- });
510
- }
511
- }
512
- if (!InputComponent) {
513
- if (type === "object") {
514
- const baseNode = findNode(ast, isDiscriminatedUnion);
515
- const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(ast, AST.isTypeLiteral);
516
- if (typeLiteral) {
517
- const schema2 = S.make(typeLiteral);
518
- return /* @__PURE__ */ React5.createElement("div", {
519
- key: name,
520
- role: "none"
521
- }, /* @__PURE__ */ React5.createElement("div", {
522
- className: padding
523
- }, label), /* @__PURE__ */ React5.createElement(Form, {
524
- schema: schema2,
525
- path: key,
526
- values: values[name] ?? {},
527
- onValuesChanged: (childValues) => {
528
- inputProps.onValueChange(name, "object", childValues);
529
- },
530
- Custom
531
- }));
532
- }
533
- }
534
- log2("no renderer for property", {
535
- name,
536
- type
537
- }, {
538
- F: __dxlog_file2,
539
- L: 185,
540
- S: void 0,
541
- C: (f, a) => f(...a)
542
- });
543
- return null;
544
- }
545
- return /* @__PURE__ */ React5.createElement("div", {
546
- key: name,
547
- role: "none",
548
- className: padding
549
- }, /* @__PURE__ */ React5.createElement(InputComponent, {
550
- type,
551
- format,
552
- property: name,
553
- disabled: readonly,
554
- label,
555
- placeholder,
556
- ...inputProps
557
- }));
558
- }).filter(isNotFalsy), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React5.createElement(FormActions, {
559
- onCancel,
560
- onSubmit: handleSave,
561
- canSubmit: canSave,
562
- readonly
563
- }));
564
- };
565
- var ArrayField = ({ name, type, label, ast, values, format, readonly, placeholder, inputProps, Custom }) => {
566
- const { t } = useTranslation2(translationKey);
567
- const arrayValues = values[name] ?? [];
568
- const key = [
569
- name
570
- ];
571
- const tupleType = findNode(ast, AST.isTupleType);
572
- const elementType = tupleType?.rest[0]?.type;
573
- if (type === "object" && elementType) {
574
- const baseNode = findNode(elementType, isDiscriminatedUnion);
575
- const typeLiteral = baseNode ? getDiscriminatedType(baseNode, values[name]) : findNode(elementType, AST.isTypeLiteral);
576
- if (typeLiteral) {
577
- const schema = S.make(typeLiteral);
578
- return /* @__PURE__ */ React5.createElement("div", {
579
- key: name,
580
- role: "none",
581
- className: mx(padding)
582
- }, /* @__PURE__ */ React5.createElement(InputHeader, null, label), /* @__PURE__ */ React5.createElement("div", {
583
- role: "none",
584
- className: "flex flex-col gap-1"
585
- }, arrayValues.map((value, index) => {
586
- return /* @__PURE__ */ React5.createElement("div", {
587
- key: index,
588
- role: "none",
589
- className: "flex items-center gap-1"
590
- }, /* @__PURE__ */ React5.createElement("div", {
591
- role: "none",
592
- className: "flex-1"
593
- }, /* @__PURE__ */ React5.createElement(Form, {
594
- schema,
595
- path: [
596
- ...key,
597
- index.toString()
598
- ],
599
- values: value,
600
- onValuesChanged: (childValues) => {
601
- const newValues = [
602
- ...arrayValues
603
- ];
604
- newValues[index] = childValues;
605
- inputProps.onValueChange(name, "object", newValues);
606
- },
607
- Custom
608
- })), /* @__PURE__ */ React5.createElement(IconButton, {
609
- icon: "ph--trash--regular",
610
- iconOnly: true,
611
- label: t("button remove"),
612
- onClick: () => {
613
- const newValues = arrayValues.filter((_, i) => i !== index);
614
- inputProps.onValueChange(name, type, newValues);
615
- }
616
- }));
617
- })), /* @__PURE__ */ React5.createElement("div", {
618
- role: "none",
619
- className: "flex justify-between items-center plb-1"
620
- }, /* @__PURE__ */ React5.createElement(IconButton, {
621
- icon: "ph--plus--regular",
622
- iconOnly: true,
623
- label: t("button add"),
624
- onClick: () => {
625
- const newValues = [
626
- ...arrayValues,
627
- {}
628
- ];
629
- inputProps.onValueChange(name, type, newValues);
630
- }
631
- })));
632
- }
633
- return /* @__PURE__ */ React5.createElement("div", {
634
- key: name,
635
- role: "none"
636
- }, "Nested form not supported in arrays, yet.");
637
- }
638
- const InputComponent = getInputComponent(type, format);
639
- if (!InputComponent) {
765
+ if (!elementType) {
640
766
  return null;
641
767
  }
642
- return /* @__PURE__ */ React5.createElement("div", {
768
+ return /* @__PURE__ */ React9.createElement("div", {
643
769
  role: "none",
644
- key: "meta-input",
645
- className: mx(padding)
646
- }, /* @__PURE__ */ React5.createElement(InputHeader, null, label), /* @__PURE__ */ React5.createElement("div", {
770
+ className: mx5(padding)
771
+ }, /* @__PURE__ */ React9.createElement(InputHeader, null, label), /* @__PURE__ */ React9.createElement("div", {
647
772
  role: "none",
648
773
  className: "flex flex-col gap-1"
649
- }, arrayValues.map((_, index) => /* @__PURE__ */ React5.createElement("div", {
774
+ }, values.map((_value, index) => /* @__PURE__ */ React9.createElement("div", {
650
775
  key: index,
651
776
  role: "none",
652
- className: "flex items-start gap-1"
653
- }, /* @__PURE__ */ React5.createElement("div", {
777
+ className: "flex items-center gap-1"
778
+ }, /* @__PURE__ */ React9.createElement("div", {
654
779
  role: "none",
655
780
  className: "flex-1"
656
- }, /* @__PURE__ */ React5.createElement(InputComponent, {
657
- type,
658
- format,
659
- label,
660
- inputOnly: true,
661
- property: `${name}.${index}`,
662
- disabled: readonly,
663
- placeholder,
664
- ...inputProps
665
- })), /* @__PURE__ */ React5.createElement(IconButton, {
781
+ }, /* @__PURE__ */ React9.createElement(FormField, {
782
+ property: {
783
+ ...property,
784
+ array: false,
785
+ ast: elementType
786
+ },
787
+ path: [
788
+ ...path ?? [],
789
+ index
790
+ ],
791
+ readonly,
792
+ inline: true,
793
+ Custom,
794
+ lookupComponent
795
+ })), /* @__PURE__ */ React9.createElement(IconButton3, {
666
796
  icon: "ph--trash--regular",
667
797
  iconOnly: true,
668
798
  label: t("button remove"),
669
- onClick: () => {
670
- const newValues = arrayValues.filter((_2, i) => i !== index);
671
- inputProps.onValueChange(name, type, newValues);
672
- }
673
- })))), /* @__PURE__ */ React5.createElement("div", {
799
+ onClick: () => handleRemove(index)
800
+ })))), /* @__PURE__ */ React9.createElement("div", {
674
801
  role: "none",
675
802
  className: "flex justify-between items-center plb-1"
676
- }, /* @__PURE__ */ React5.createElement(IconButton, {
803
+ }, /* @__PURE__ */ React9.createElement(IconButton3, {
677
804
  icon: "ph--plus--regular",
678
805
  iconOnly: true,
679
806
  label: t("button add"),
680
- onClick: () => {
681
- const newValues = [
682
- ...arrayValues,
683
- SimpleType.getDefaultValue(type)
684
- ];
685
- inputProps.onValueChange(name, type, newValues);
686
- }
807
+ onClick: handleAdd
687
808
  })));
688
809
  };
689
- var FormActions = ({ onCancel, onSubmit, canSubmit, readonly }) => {
690
- const { t } = useTranslation2(translationKey);
691
- return /* @__PURE__ */ React5.createElement("div", {
692
- role: "none",
693
- className: "flex justify-center"
694
- }, /* @__PURE__ */ React5.createElement("div", {
810
+
811
+ // packages/ui/react-ui-form/src/components/Form/factory.tsx
812
+ import { FormatEnum } from "@dxos/echo-schema";
813
+ var getInputComponent = (type, format) => {
814
+ switch (format) {
815
+ case FormatEnum.LatLng:
816
+ return GeoPointInput;
817
+ }
818
+ switch (type) {
819
+ case "string":
820
+ return TextInput;
821
+ case "number":
822
+ return NumberInput;
823
+ case "boolean":
824
+ return BooleanInput;
825
+ }
826
+ };
827
+
828
+ // packages/ui/react-ui-form/src/components/Form/FormContent.tsx
829
+ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom }) => {
830
+ const inputProps = useInputProps(path);
831
+ const { ast, name, type, format, title, description, options, examples, array } = property;
832
+ const label = useMemo3(() => title ?? pipe2(name, capitalize2), [
833
+ title,
834
+ name
835
+ ]);
836
+ const placeholder = useMemo3(() => examples?.length ? `Example: "${examples[0]}"` : description, [
837
+ examples,
838
+ description
839
+ ]);
840
+ const FoundComponent = lookupComponent?.({
841
+ prop: name,
842
+ schema: S.make(ast),
843
+ inputProps: {
844
+ type,
845
+ format,
846
+ label,
847
+ disabled: readonly,
848
+ placeholder,
849
+ ...inputProps
850
+ }
851
+ });
852
+ if (FoundComponent) {
853
+ return /* @__PURE__ */ React10.createElement("div", {
854
+ role: "none"
855
+ }, FoundComponent);
856
+ }
857
+ const jsonPath = createJsonPath3(path ?? []);
858
+ const CustomComponent = Custom?.[jsonPath];
859
+ if (CustomComponent) {
860
+ return (
861
+ // TODO(ZaymonFC): We should try to remove this div wrapper.
862
+ /* @__PURE__ */ React10.createElement("div", {
863
+ role: "none"
864
+ }, /* @__PURE__ */ React10.createElement(CustomComponent, {
865
+ type,
866
+ format,
867
+ label,
868
+ inputOnly: inline,
869
+ placeholder,
870
+ disabled: readonly,
871
+ ...inputProps
872
+ }))
873
+ );
874
+ }
875
+ if (array) {
876
+ return /* @__PURE__ */ React10.createElement(ArrayField, {
877
+ property,
878
+ path,
879
+ inputProps,
880
+ readonly,
881
+ Custom
882
+ });
883
+ }
884
+ const InputComponent = getInputComponent(type, format);
885
+ if (InputComponent) {
886
+ return /* @__PURE__ */ React10.createElement("div", {
887
+ role: "none"
888
+ }, /* @__PURE__ */ React10.createElement(InputComponent, {
889
+ type,
890
+ format,
891
+ label,
892
+ inputOnly: inline,
893
+ placeholder,
894
+ disabled: readonly,
895
+ ...inputProps
896
+ }));
897
+ }
898
+ if (options) {
899
+ return /* @__PURE__ */ React10.createElement("div", {
900
+ role: "none"
901
+ }, /* @__PURE__ */ React10.createElement(SelectInput, {
902
+ type,
903
+ format,
904
+ disabled: readonly,
905
+ inputOnly: inline,
906
+ label,
907
+ options: options.map((option) => ({
908
+ value: option,
909
+ label: String(option)
910
+ })),
911
+ placeholder,
912
+ ...inputProps
913
+ }));
914
+ }
915
+ if (type === "object") {
916
+ const baseNode = findNode2(ast, isDiscriminatedUnion2);
917
+ const typeLiteral = baseNode ? getDiscriminatedType2(baseNode, inputProps.getValue()) : findNode2(ast, AST3.isTypeLiteral);
918
+ if (typeLiteral) {
919
+ return /* @__PURE__ */ React10.createElement("div", {
920
+ role: "none"
921
+ }, !inline && /* @__PURE__ */ React10.createElement("div", null, label), /* @__PURE__ */ React10.createElement(FormFields, {
922
+ schema: S.make(typeLiteral),
923
+ path,
924
+ readonly,
925
+ Custom
926
+ }));
927
+ }
928
+ }
929
+ return null;
930
+ };
931
+ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }) => {
932
+ const values = useFormValues(path);
933
+ const properties = useMemo3(() => {
934
+ const props = getSchemaProperties2(schema.ast, values);
935
+ const filtered = filter ? filter(props) : props;
936
+ return sort ? filtered.sort((a, b) => sort.indexOf(a.name) - sort.indexOf(b.name)) : filtered;
937
+ }, [
938
+ schema,
939
+ values,
940
+ filter,
941
+ sort
942
+ ]);
943
+ return /* @__PURE__ */ React10.createElement("div", {
944
+ role: "form",
945
+ className: mx6("flex flex-col w-full gap-2 py-2")
946
+ }, properties.map((property) => {
947
+ return /* @__PURE__ */ React10.createElement(FormField, {
948
+ key: property.name,
949
+ property,
950
+ path: [
951
+ ...path ?? [],
952
+ property.name
953
+ ],
954
+ readonly,
955
+ lookupComponent,
956
+ Custom
957
+ });
958
+ }).filter(isNotFalsy));
959
+ };
960
+
961
+ // packages/ui/react-ui-form/src/components/Form/Form.tsx
962
+ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
963
+ const onValid = useMemo4(() => autoSave ? onSave : void 0, [
964
+ autoSave,
965
+ onSave
966
+ ]);
967
+ useEffect3(() => {
968
+ if (autoFocus) {
969
+ const input = document.querySelector("input");
970
+ if (input) {
971
+ input.focus();
972
+ }
973
+ }
974
+ }, [
975
+ autoFocus
976
+ ]);
977
+ return /* @__PURE__ */ React11.createElement(FormProvider, {
978
+ schema,
979
+ initialValues,
980
+ onValuesChanged,
981
+ onValidate,
982
+ onValid,
983
+ onSave
984
+ }, /* @__PURE__ */ React11.createElement("div", {
695
985
  role: "none",
696
- className: mx(onCancel && !readonly && "grid grid-cols-2 gap-2")
697
- }, onCancel && !readonly && /* @__PURE__ */ React5.createElement(IconButton, {
698
- "data-testid": "cancel-button",
699
- icon: "ph--x--regular",
700
- label: t("button cancel"),
701
- onClick: onCancel
702
- }), onSubmit && /* @__PURE__ */ React5.createElement(IconButton, {
703
- type: "submit",
704
- "data-testid": "save-button",
705
- disabled: !canSubmit,
706
- icon: "ph--check--regular",
707
- label: t("button save"),
708
- onClick: onSubmit
986
+ className: mx7("p-2", classNames),
987
+ "data-testid": testId
988
+ }, /* @__PURE__ */ React11.createElement(FormFields, {
989
+ schema,
990
+ path,
991
+ readonly,
992
+ filter,
993
+ sort,
994
+ lookupComponent,
995
+ Custom
996
+ }), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React11.createElement(FormActions, {
997
+ onCancel,
998
+ readonly
709
999
  })));
710
1000
  };
711
1001
 
712
1002
  // packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
713
1003
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
714
1004
  var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
715
- const { t } = useTranslation3(translationKey);
716
- const [props, setProps] = useState2(projection.getFieldProjection(field.id).props);
717
- useEffect3(() => setProps(projection.getFieldProjection(field.id).props), [
1005
+ const { t } = useTranslation5(translationKey);
1006
+ const [props, setProps] = useState3(projection.getFieldProjection(field.id).props);
1007
+ useEffect4(() => setProps(projection.getFieldProjection(field.id).props), [
718
1008
  field,
719
1009
  projection
720
1010
  ]);
721
- const [schemas, setSchemas] = useState2([]);
722
- useEffect3(() => {
1011
+ const [schemas, setSchemas] = useState3([]);
1012
+ useEffect4(() => {
723
1013
  if (!registry) {
724
1014
  return;
725
1015
  }
726
- const subscription = registry.subscribe(setSchemas);
1016
+ const subscription = registry.query().subscribe((query) => setSchemas(query.results), {
1017
+ fire: true
1018
+ });
727
1019
  const t2 = setTimeout(async () => {
728
- const schemas2 = await registry.query();
1020
+ const schemas2 = await registry.query().run();
729
1021
  setSchemas(schemas2);
730
1022
  });
731
1023
  return () => {
@@ -735,25 +1027,35 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
735
1027
  }, [
736
1028
  registry
737
1029
  ]);
738
- const [schema, setSchema] = useState2();
739
- useEffect3(() => {
740
- setSchema(schemas.find((schema2) => schema2.typename === props?.referenceSchema));
1030
+ const [referenceSchema, setReferenceSchema] = useState3();
1031
+ useEffect4(() => {
1032
+ setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
741
1033
  }, [
742
1034
  schemas,
743
1035
  props?.referenceSchema
744
1036
  ]);
745
- const [{ fieldSchema }, setFieldSchema] = useState2({
1037
+ const [{ fieldSchema }, setFieldSchema] = useState3({
746
1038
  fieldSchema: getFormatSchema(props?.format)
747
1039
  });
748
- const handleValuesChanged = useCallback2((_props) => {
749
- if (_props.format !== props.format) {
750
- setFieldSchema({
751
- fieldSchema: getFormatSchema(_props.format)
752
- });
753
- }
754
- if (_props.referenceSchema !== props.referenceSchema) {
755
- setSchema(schemas.find((schema2) => schema2.typename === _props.referenceSchema));
756
- }
1040
+ const handleValuesChanged = useCallback4((_props) => {
1041
+ setFieldSchema((prev) => {
1042
+ const fieldSchema2 = getFormatSchema(_props.format);
1043
+ if (prev.fieldSchema === fieldSchema2) {
1044
+ return prev;
1045
+ }
1046
+ return {
1047
+ fieldSchema: fieldSchema2
1048
+ };
1049
+ });
1050
+ setReferenceSchema((prev) => {
1051
+ if (_props.referenceSchema !== prev?.typename) {
1052
+ const newSchema = schemas.find((schema) => schema.typename === _props.referenceSchema);
1053
+ if (newSchema) {
1054
+ return newSchema;
1055
+ }
1056
+ }
1057
+ return prev;
1058
+ });
757
1059
  setProps((props2) => {
758
1060
  const type = formatToType[_props.format];
759
1061
  if (props2.type !== type) {
@@ -763,13 +1065,14 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
763
1065
  type
764
1066
  };
765
1067
  }
766
- return props2;
1068
+ return _props;
767
1069
  });
768
1070
  }, [
769
1071
  schemas,
770
- props
1072
+ props.format,
1073
+ props.referenceSchema
771
1074
  ]);
772
- const handleValidate = useCallback2(({ property }) => {
1075
+ const handleValidate = useCallback4(({ property }) => {
773
1076
  if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
774
1077
  return [
775
1078
  {
@@ -782,7 +1085,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
782
1085
  view.fields,
783
1086
  field
784
1087
  ]);
785
- const handleSave = useCallback2((props2) => {
1088
+ const handleSave = useCallback4((props2) => {
786
1089
  projection.setFieldProjection({
787
1090
  field,
788
1091
  props: props2
@@ -793,29 +1096,56 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
793
1096
  field,
794
1097
  onSave
795
1098
  ]);
796
- const handleCancel = useCallback2(() => {
1099
+ const handleCancel = useCallback4(() => {
797
1100
  requestAnimationFrame(() => onCancel?.());
798
1101
  onSave();
799
1102
  }, [
800
1103
  onSave
801
1104
  ]);
1105
+ const Custom = useMemo5(() => ({
1106
+ ["format"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
1107
+ ...props2,
1108
+ options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
1109
+ value,
1110
+ label: t(`format ${value}`)
1111
+ }))
1112
+ }),
1113
+ ["referenceSchema"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
1114
+ ...props2,
1115
+ options: schemas.map((schema) => ({
1116
+ value: schema.typename
1117
+ }))
1118
+ }),
1119
+ ["referencePath"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
1120
+ ...props2,
1121
+ options: referenceSchema ? getSchemaProperties3(referenceSchema.ast).sort(sortProperties).map((p) => ({
1122
+ value: p.name
1123
+ })) : []
1124
+ }),
1125
+ ["options"]: (props2) => /* @__PURE__ */ React12.createElement(SelectOptionInput, props2)
1126
+ }), [
1127
+ t,
1128
+ schemas,
1129
+ referenceSchema
1130
+ ]);
1131
+ const propIsNotType = useCallback4((props2) => props2.filter((p) => p.name !== "type"), []);
802
1132
  if (!fieldSchema) {
803
- log3.warn("invalid format", {
1133
+ log2.warn("invalid format", {
804
1134
  props
805
1135
  }, {
806
1136
  F: __dxlog_file3,
807
- L: 118,
1137
+ L: 174,
808
1138
  S: void 0,
809
1139
  C: (f, a) => f(...a)
810
1140
  });
811
1141
  return null;
812
1142
  }
813
- return /* @__PURE__ */ React6.createElement(Form, {
1143
+ return /* @__PURE__ */ React12.createElement(Form, {
814
1144
  key: field.id,
815
1145
  autoFocus: true,
816
1146
  values: props,
817
1147
  schema: fieldSchema,
818
- filter: (props2) => props2.filter((p) => p.name !== "type"),
1148
+ filter: propIsNotType,
819
1149
  sort: [
820
1150
  "property",
821
1151
  "format"
@@ -824,55 +1154,34 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
824
1154
  onValidate: handleValidate,
825
1155
  onSave: handleSave,
826
1156
  onCancel: handleCancel,
827
- Custom: {
828
- ["format"]: (props2) => /* @__PURE__ */ React6.createElement(SelectInput, {
829
- ...props2,
830
- options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
831
- value,
832
- label: t(`format ${value}`)
833
- }))
834
- }),
835
- ["referenceSchema"]: (props2) => /* @__PURE__ */ React6.createElement(SelectInput, {
836
- ...props2,
837
- options: schemas.map((schema2) => ({
838
- value: schema2.typename
839
- }))
840
- }),
841
- ["referencePath"]: (props2) => /* @__PURE__ */ React6.createElement(SelectInput, {
842
- ...props2,
843
- options: schema ? getSchemaProperties2(schema.schema.ast).sort(sortProperties).map((p) => ({
844
- value: p.name
845
- })) : []
846
- })
847
- }
1157
+ Custom
848
1158
  });
849
1159
  };
850
1160
 
851
1161
  // packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
852
- import React7, { useCallback as useCallback3, useMemo as useMemo3, useState as useState3 } from "react";
853
- import { AST as AST2, Format, S as S2 } from "@dxos/echo-schema";
854
- import { IconButton as IconButton2, useTranslation as useTranslation4 } from "@dxos/react-ui";
855
- import { List } from "@dxos/react-ui-list";
856
- import { ghostHover, inputTextLabel, mx as mx2 } from "@dxos/react-ui-theme";
1162
+ import React13, { useCallback as useCallback5, useMemo as useMemo6, useState as useState4 } from "react";
1163
+ import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
1164
+ import { IconButton as IconButton4, useTranslation as useTranslation6 } from "@dxos/react-ui";
1165
+ import { List as List2 } from "@dxos/react-ui-list";
1166
+ import { ghostHover, inputTextLabel, mx as mx8 } from "@dxos/react-ui-theme";
857
1167
  import { FieldSchema, ViewProjection, VIEW_FIELD_LIMIT } from "@dxos/schema";
858
- import { arrayMove } from "@dxos/util";
859
1168
  var grid = "grid grid-cols-[32px_1fr_32px] min-bs-[2.5rem]";
860
1169
  var ViewMetaSchema = S2.Struct({
861
1170
  name: S2.String.annotations({
862
- [AST2.TitleAnnotationId]: "View"
1171
+ [AST4.TitleAnnotationId]: "View"
863
1172
  }),
864
1173
  typename: Format.DXN.annotations({
865
- [AST2.TitleAnnotationId]: "Typename"
1174
+ [AST4.TitleAnnotationId]: "Typename"
866
1175
  })
867
1176
  }).pipe(S2.mutable);
868
- var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onDelete }) => {
869
- const { t } = useTranslation4(translationKey);
870
- const projection = useMemo3(() => new ViewProjection(schema, view), [
1177
+ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onTypenameChanged, onDelete }) => {
1178
+ const { t } = useTranslation6(translationKey);
1179
+ const projection = useMemo6(() => new ViewProjection(schema, view), [
871
1180
  schema,
872
1181
  view
873
1182
  ]);
874
- const [field, setField] = useState3();
875
- const viewValues = useMemo3(() => {
1183
+ const [field, setField] = useState4();
1184
+ const viewValues = useMemo6(() => {
876
1185
  return {
877
1186
  name: view.name,
878
1187
  // TODO(burdon): Need to warn user of possible consequences of editing.
@@ -882,77 +1191,86 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
882
1191
  }, [
883
1192
  view
884
1193
  ]);
885
- const handleViewUpdate = useCallback3(({ name, typename }) => {
1194
+ const handleViewUpdate = useCallback5(({ name, typename }) => {
886
1195
  requestAnimationFrame(() => {
887
- view.name = name;
888
- view.query.type = typename;
889
- schema.updateTypename(typename);
1196
+ if (view.name !== name) {
1197
+ view.name = name;
1198
+ }
1199
+ if (view.query.type !== typename) {
1200
+ onTypenameChanged(typename);
1201
+ }
890
1202
  });
891
1203
  }, [
892
1204
  view,
893
- schema
1205
+ schema,
1206
+ onTypenameChanged
894
1207
  ]);
895
- const handleSelect = useCallback3((field2) => {
1208
+ const handleSelect = useCallback5((field2) => {
896
1209
  setField((f) => f === field2 ? void 0 : field2);
897
1210
  }, []);
898
- const handleAdd = useCallback3(() => {
1211
+ const handleAdd = useCallback5(() => {
899
1212
  const field2 = projection.createFieldProjection();
900
1213
  setField(field2);
901
1214
  }, [
902
1215
  view
903
1216
  ]);
904
- const handleMove = useCallback3((fromIndex, toIndex) => {
905
- arrayMove(view.fields, fromIndex, toIndex);
1217
+ const handleMove = useCallback5((fromIndex, toIndex) => {
1218
+ const fields = [
1219
+ ...view.fields
1220
+ ];
1221
+ const [moved] = fields.splice(fromIndex, 1);
1222
+ fields.splice(toIndex, 0, moved);
1223
+ view.fields = fields;
906
1224
  }, [
907
1225
  view.fields
908
1226
  ]);
909
- const handleClose = useCallback3(() => setField(void 0), []);
910
- return /* @__PURE__ */ React7.createElement("div", {
1227
+ const handleClose = useCallback5(() => setField(void 0), []);
1228
+ return /* @__PURE__ */ React13.createElement("div", {
911
1229
  role: "none",
912
- className: mx2("flex flex-col w-full divide-y divide-separator", classNames)
913
- }, /* @__PURE__ */ React7.createElement(Form, {
1230
+ className: mx8("flex flex-col w-full divide-y divide-separator", classNames)
1231
+ }, /* @__PURE__ */ React13.createElement(Form, {
914
1232
  autoFocus: true,
915
1233
  autoSave: true,
916
1234
  schema: ViewMetaSchema,
917
1235
  values: viewValues,
918
1236
  onSave: handleViewUpdate
919
- }), /* @__PURE__ */ React7.createElement("div", null, /* @__PURE__ */ React7.createElement("div", {
1237
+ }), /* @__PURE__ */ React13.createElement("div", null, /* @__PURE__ */ React13.createElement("div", {
920
1238
  role: "none",
921
1239
  className: "p-2"
922
- }, /* @__PURE__ */ React7.createElement("label", {
923
- className: mx2(inputTextLabel)
924
- }, t("fields label"))), /* @__PURE__ */ React7.createElement(List.Root, {
1240
+ }, /* @__PURE__ */ React13.createElement("label", {
1241
+ className: mx8(inputTextLabel)
1242
+ }, t("fields label"))), /* @__PURE__ */ React13.createElement(List2.Root, {
925
1243
  items: view.fields,
926
1244
  isItem: S2.is(FieldSchema),
927
1245
  getId: (field2) => field2.id,
928
1246
  onMove: handleMove
929
- }, ({ items: fields }) => /* @__PURE__ */ React7.createElement(React7.Fragment, null, showHeading && /* @__PURE__ */ React7.createElement("div", {
1247
+ }, ({ items: fields }) => /* @__PURE__ */ React13.createElement(React13.Fragment, null, showHeading && /* @__PURE__ */ React13.createElement("div", {
930
1248
  role: "heading",
931
1249
  className: grid
932
- }, /* @__PURE__ */ React7.createElement("div", null), /* @__PURE__ */ React7.createElement("div", {
1250
+ }, /* @__PURE__ */ React13.createElement("div", null), /* @__PURE__ */ React13.createElement("div", {
933
1251
  className: "flex items-center text-sm"
934
- }, t("field path label"))), /* @__PURE__ */ React7.createElement("div", {
1252
+ }, t("field path label"))), /* @__PURE__ */ React13.createElement("div", {
935
1253
  role: "list",
936
1254
  className: "flex flex-col w-full"
937
- }, fields?.map((field2) => /* @__PURE__ */ React7.createElement(List.Item, {
1255
+ }, fields?.map((field2) => /* @__PURE__ */ React13.createElement(List2.Item, {
938
1256
  key: field2.id,
939
1257
  item: field2,
940
- classNames: mx2(grid, ghostHover, "cursor-pointer")
941
- }, /* @__PURE__ */ React7.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React7.createElement(List.ItemTitle, {
1258
+ classNames: mx8(grid, ghostHover, "cursor-pointer")
1259
+ }, /* @__PURE__ */ React13.createElement(List2.ItemDragHandle, null), /* @__PURE__ */ React13.createElement(List2.ItemTitle, {
942
1260
  onClick: () => handleSelect(field2)
943
- }, field2.path), /* @__PURE__ */ React7.createElement(List.ItemDeleteButton, {
1261
+ }, field2.path), /* @__PURE__ */ React13.createElement(List2.ItemDeleteButton, {
944
1262
  disabled: view.fields.length <= 1,
945
1263
  onClick: () => onDelete(field2.id)
946
- }))))))), field && /* @__PURE__ */ React7.createElement(FieldEditor, {
1264
+ }))))))), field && /* @__PURE__ */ React13.createElement(FieldEditor, {
947
1265
  key: field.id,
948
1266
  view,
949
1267
  projection,
950
1268
  field,
951
1269
  registry,
952
1270
  onSave: handleClose
953
- }), !readonly && !field && /* @__PURE__ */ React7.createElement("div", {
1271
+ }), !readonly && !field && /* @__PURE__ */ React13.createElement("div", {
954
1272
  className: "flex p-2 justify-center"
955
- }, /* @__PURE__ */ React7.createElement(IconButton2, {
1273
+ }, /* @__PURE__ */ React13.createElement(IconButton4, {
956
1274
  icon: "ph--plus--regular",
957
1275
  label: t("button add property"),
958
1276
  onClick: handleAdd,
@@ -1182,15 +1500,17 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
1182
1500
  };
1183
1501
  export {
1184
1502
  BooleanInput,
1503
+ DeprecatedFormContainer,
1185
1504
  DeprecatedFormInput,
1186
1505
  FieldEditor,
1187
1506
  Form,
1188
- FormActions,
1189
- GeiPointInput,
1507
+ GeoPointInput,
1190
1508
  InputHeader,
1191
1509
  NumberInput,
1192
1510
  SelectInput,
1511
+ SelectOptionInput,
1193
1512
  TextInput,
1513
+ TupleInput,
1194
1514
  ViewEditor,
1195
1515
  cellClassesForFieldType,
1196
1516
  cellClassesForRowSelection,