@dxos/react-ui-form 0.7.5-main.9d26e3a → 0.7.5-main.e9bb01b

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