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