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