@dxos/react-ui-form 0.7.5-feature-compute.4d9d99a → 0.7.5-labs.401163d

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