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