@dxos/react-ui-form 0.7.5-labs.a279d8c → 0.7.5-labs.a8b535d

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 (78) hide show
  1. package/dist/lib/browser/index.mjs +734 -308
  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 +793 -363
  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 +734 -308
  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 +1 -2
  11. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/ArrayField.d.ts +1 -2
  13. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/Defaults.d.ts +5 -5
  15. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/Form.d.ts +4 -3
  17. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/FormActions.d.ts +1 -2
  19. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/FormContent.d.ts +2 -3
  21. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  22. package/dist/types/src/components/Form/FormContext.d.ts +4 -1
  23. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
  24. package/dist/types/src/components/Form/Input.d.ts +2 -2
  25. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  26. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -2
  27. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +3 -0
  29. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -0
  30. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +7 -0
  31. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -0
  32. package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -2
  33. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
  34. package/dist/types/src/components/Form/custom/index.d.ts +1 -0
  35. package/dist/types/src/components/Form/custom/index.d.ts.map +1 -1
  36. package/dist/types/src/components/Form/factory.d.ts.map +1 -1
  37. package/dist/types/src/components/Form/index.d.ts +2 -0
  38. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  39. package/dist/types/src/components/ViewEditor/Test.stories.d.ts +1 -2
  40. package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
  41. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +1 -2
  42. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  43. package/dist/types/src/components/deprecated/Form.d.ts +1 -2
  44. package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
  45. package/dist/types/src/components/deprecated/Form.stories.d.ts +1 -2
  46. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
  47. package/dist/types/src/components/deprecated/FormInput.d.ts +6 -1
  48. package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
  49. package/dist/types/src/components/testing.d.ts +3 -3
  50. package/dist/types/src/components/testing.d.ts.map +1 -1
  51. package/dist/types/src/hooks/useForm.d.ts +1 -0
  52. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  53. package/dist/types/src/translations.d.ts +6 -0
  54. package/dist/types/src/translations.d.ts.map +1 -1
  55. package/dist/types/src/util/field.d.ts.map +1 -1
  56. package/dist/types/src/util/formatting.d.ts.map +1 -1
  57. package/dist/types/tsconfig.tsbuildinfo +1 -1
  58. package/package.json +26 -24
  59. package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
  60. package/src/components/FieldEditor/FieldEditor.tsx +3 -2
  61. package/src/components/Form/ArrayField.tsx +1 -1
  62. package/src/components/Form/Defaults.tsx +61 -4
  63. package/src/components/Form/Form.tsx +20 -3
  64. package/src/components/Form/FormActions.tsx +1 -1
  65. package/src/components/Form/FormContent.tsx +24 -9
  66. package/src/components/Form/FormContext.tsx +58 -10
  67. package/src/components/Form/custom/GeoPointInput.tsx +42 -12
  68. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +58 -0
  69. package/src/components/Form/custom/SelectOptionsInput.tsx +190 -0
  70. package/src/components/Form/custom/index.ts +1 -0
  71. package/src/components/Form/factory.tsx +4 -2
  72. package/src/components/Form/index.ts +3 -0
  73. package/src/components/ViewEditor/ViewEditor.tsx +7 -4
  74. package/src/components/deprecated/FormInput.tsx +20 -7
  75. package/src/hooks/useForm.ts +69 -41
  76. package/src/translations.ts +9 -1
  77. package/src/util/field.ts +19 -1
  78. package/src/util/formatting.ts +29 -1
@@ -29,12 +29,13 @@ var translations_default = [
29
29
  "format hostname": "Hostname",
30
30
  "format integer": "Integer",
31
31
  "format json": "JSON",
32
- "format latlng": "LatLng",
32
+ "format latlng": "Geopoint",
33
33
  "format markdown": "Markdown",
34
34
  "format percent": "Percent",
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,198 +78,35 @@ 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 React11, { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo5, useState as useState2 } from "react";
102
+ import React12, { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo7, useState as useState4 } from "react";
84
103
  import { FormatEnum as FormatEnum2, FormatEnums, formatToType } from "@dxos/echo-schema";
85
104
  import { log as log2 } from "@dxos/log";
86
- import { useTranslation as useTranslation4 } from "@dxos/react-ui";
105
+ import { useTranslation as useTranslation5 } from "@dxos/react-ui";
87
106
  import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
88
107
 
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 = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
115
- const { t } = useTranslation(translationKey);
116
- const { status, error } = getStatus();
117
- const [lng = 0, lat = 0] = getValue3() ?? [];
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(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(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, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
159
- const { status, error } = getStatus();
160
- const values = getValue3() ?? {};
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(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 = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
184
- const { status, error } = getStatus();
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: getValue3() ?? "",
193
- onChange: (event) => onValueChange(type, event.target.value),
194
- onBlur
195
- }), inputOnly && /* @__PURE__ */ React5.createElement(Input4.Validation, null, error));
196
- };
197
- var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
198
- const { status, error } = getStatus();
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: getValue3() ?? 0,
208
- onChange: (event) => onValueChange(type, event.target.value),
209
- onBlur
210
- }), inputOnly && /* @__PURE__ */ React5.createElement(Input4.DescriptionAndValidation, null, error));
211
- };
212
- var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
213
- const { status, error } = getStatus();
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: getValue3(),
220
- onCheckedChange: (value) => onValueChange(type, value)
221
- }), inputOnly && /* @__PURE__ */ React5.createElement(Input4.DescriptionAndValidation, null, error));
222
- };
223
- var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
224
- const { status, error } = getStatus();
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: getValue3(),
231
- onValueChange: (value) => onValueChange(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 React10, { useMemo as useMemo4 } from "react";
247
- import { mx as mx5 } from "@dxos/react-ui-theme";
248
-
249
- // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
250
- import React7 from "react";
251
- import { IconButton, useTranslation as useTranslation2 } from "@dxos/react-ui";
252
- import { mx as mx2 } from "@dxos/react-ui-theme";
253
-
254
108
  // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
255
- import React6, { createContext, useContext } from "react";
109
+ import React2, { createContext, useContext, useEffect as useEffect2, useMemo as useMemo2 } from "react";
256
110
  import { raise } from "@dxos/debug";
257
111
  import { getValue as getValue2 } from "@dxos/echo-schema";
258
112
  import { createJsonPath as createJsonPath2 } from "@dxos/effect";
@@ -268,7 +122,7 @@ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/ho
268
122
  var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
269
123
  invariant(AST.isTypeLiteral(schema.ast), void 0, {
270
124
  F: __dxlog_file,
271
- L: 91,
125
+ L: 92,
272
126
  S: void 0,
273
127
  A: [
274
128
  "AST.isTypeLiteral(schema.ast)",
@@ -311,7 +165,12 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
311
165
  values,
312
166
  schemaDirty
313
167
  ]);
314
- const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
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.
315
174
  !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
316
175
  touched,
317
176
  errors,
@@ -344,10 +203,12 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
344
203
  }, [
345
204
  errors
346
205
  ]);
347
- const getFormValue = (path) => {
206
+ const getFormValue = useCallback((path) => {
348
207
  return getValue(values, createJsonPath(path));
349
- };
350
- const onValueChange = (path, type, value) => {
208
+ }, [
209
+ values
210
+ ]);
211
+ const onValueChange = useCallback((path, type, value) => {
351
212
  const jsonPath = createJsonPath(path);
352
213
  let parsedValue = value;
353
214
  try {
@@ -357,7 +218,7 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
357
218
  } catch (err) {
358
219
  log.catch(err, void 0, {
359
220
  F: __dxlog_file,
360
- L: 194,
221
+ L: 202,
361
222
  S: void 0,
362
223
  C: (f, a) => f(...a)
363
224
  });
@@ -378,7 +239,13 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
378
239
  changed
379
240
  });
380
241
  }
381
- };
242
+ }, [
243
+ values,
244
+ onValuesChanged,
245
+ validate,
246
+ onValid,
247
+ changed
248
+ ]);
382
249
  const onTouched = useCallback((path) => {
383
250
  const jsonPath = createJsonPath(path);
384
251
  setTouched((touched2) => ({
@@ -390,25 +257,39 @@ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, on
390
257
  validate,
391
258
  values
392
259
  ]);
393
- return {
260
+ return useMemo(() => ({
394
261
  // State.
395
262
  values,
396
263
  errors,
397
264
  touched,
398
265
  changed,
399
266
  canSave,
267
+ formIsValid,
268
+ // Actions.
400
269
  handleSave,
401
270
  // Field utils.
402
271
  getStatus,
403
272
  getValue: getFormValue,
404
273
  onValueChange,
405
274
  onTouched
406
- };
275
+ }), [
276
+ values,
277
+ errors,
278
+ touched,
279
+ changed,
280
+ canSave,
281
+ formIsValid,
282
+ handleSave,
283
+ getStatus,
284
+ getFormValue,
285
+ onValueChange,
286
+ onTouched
287
+ ]);
407
288
  };
408
289
  var createKeySet = (obj, value) => {
409
290
  invariant(obj, void 0, {
410
291
  F: __dxlog_file,
411
- L: 236,
292
+ L: 264,
412
293
  S: void 0,
413
294
  A: [
414
295
  "obj",
@@ -442,36 +323,499 @@ var useFormValues = (path = []) => {
442
323
  };
443
324
  var useInputProps = (path = []) => {
444
325
  const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
445
- return {
446
- getStatus: () => getStatus(path),
447
- getValue: () => getFormValue(path),
448
- onValueChange: (type, value) => onValueChange(path, type, value),
449
- onBlur: () => onTouched(path)
450
- };
326
+ const stablePath = useMemo2(() => path, [
327
+ Array.isArray(path) ? path.join(".") : path
328
+ ]);
329
+ return useMemo2(() => ({
330
+ getStatus: () => getStatus(stablePath),
331
+ getValue: () => getFormValue(stablePath),
332
+ onValueChange: (type, value) => onValueChange(stablePath, type, value),
333
+ onBlur: () => onTouched(stablePath)
334
+ }), [
335
+ getStatus,
336
+ getFormValue,
337
+ onValueChange,
338
+ onTouched,
339
+ stablePath
340
+ ]);
451
341
  };
452
- var FormProvider = ({ children, ...formOptions }) => {
453
- const formHandler = useForm(formOptions);
454
- return /* @__PURE__ */ React6.createElement(FormContext.Provider, {
455
- value: formHandler
342
+ var FormProvider = ({ children, formRef, autoSave, ...formOptions }) => {
343
+ const form = useForm(formOptions);
344
+ useEffect2(() => {
345
+ if (!formRef?.current) {
346
+ return;
347
+ }
348
+ const handleKeyDown = (event) => {
349
+ const keyIsEnter = event.key === "Enter";
350
+ const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
351
+ const inputIsTextarea = event.target.tagName.toLowerCase() === "textarea";
352
+ const inputOptOut = event.target.hasAttribute("data-no-submit");
353
+ const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
354
+ const shouldSubmitTextarea = inputIsTextarea && keyIsEnter && event.metaKey;
355
+ if ((shouldSubmitRegularInput || shouldSubmitTextarea) && !inputOptOut) {
356
+ if (!autoSave && form.canSave) {
357
+ form.handleSave();
358
+ }
359
+ if (autoSave && form.formIsValid) {
360
+ event.target.blur();
361
+ }
362
+ }
363
+ };
364
+ const formElement = formRef.current;
365
+ formElement.addEventListener("keydown", handleKeyDown);
366
+ return () => formElement.removeEventListener("keydown", handleKeyDown);
367
+ }, [
368
+ formRef,
369
+ form.canSave,
370
+ form.formIsValid,
371
+ form.handleSave,
372
+ autoSave
373
+ ]);
374
+ return /* @__PURE__ */ React2.createElement(FormContext.Provider, {
375
+ value: form
456
376
  }, children);
457
377
  };
458
378
 
379
+ // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
380
+ import React4, { useCallback as useCallback2, useState as useState2, useEffect as useEffect3, useMemo as useMemo3 } from "react";
381
+ import { GeoLocation } from "@dxos/echo-schema";
382
+ import { Input as Input2, useTranslation } from "@dxos/react-ui";
383
+ import { safeParseFloat } from "@dxos/util";
384
+
385
+ // packages/ui/react-ui-form/src/components/Form/Input.tsx
386
+ import React3 from "react";
387
+ import { Icon, Tooltip } from "@dxos/react-ui";
388
+ import { errorText } from "@dxos/react-ui-theme";
389
+ var InputHeader = ({ children, error }) => {
390
+ return /* @__PURE__ */ React3.createElement("div", {
391
+ role: "none",
392
+ className: "flex justify-between items-center mb-1"
393
+ }, children, error && /* @__PURE__ */ React3.createElement(Tooltip.Root, null, /* @__PURE__ */ React3.createElement(Tooltip.Trigger, {
394
+ asChild: true
395
+ }, /* @__PURE__ */ React3.createElement(Icon, {
396
+ icon: "ph--warning--regular",
397
+ size: 4,
398
+ classNames: errorText
399
+ })), /* @__PURE__ */ React3.createElement(Tooltip.Content, {
400
+ side: "bottom"
401
+ }, /* @__PURE__ */ React3.createElement(Tooltip.Arrow, null), error)));
402
+ };
403
+
404
+ // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
405
+ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
406
+ const { t } = useTranslation(translationKey);
407
+ const { status, error } = getStatus();
408
+ const geoPoint = useMemo3(() => getValue3() ?? [
409
+ 0,
410
+ 0
411
+ ], [
412
+ getValue3
413
+ ]);
414
+ const geoLocation = useMemo3(() => GeoLocation.fromGeoPoint(geoPoint), [
415
+ geoPoint
416
+ ]);
417
+ const [longitudeText, setLongitudeText] = useState2(geoLocation.longitude?.toString());
418
+ const [latitudeText, setLatitudeText] = useState2(geoLocation.latitude?.toString());
419
+ useEffect3(() => {
420
+ const location = GeoLocation.fromGeoPoint(geoPoint);
421
+ setLongitudeText(location.longitude.toString());
422
+ setLatitudeText(location.latitude.toString());
423
+ }, [
424
+ geoPoint
425
+ ]);
426
+ const handleCoordinateChange = useCallback2((coordinateType, setText) => (event) => {
427
+ const inputText = event.target.value;
428
+ setText(inputText);
429
+ if (inputText !== "" && inputText !== "-") {
430
+ const coord = safeParseFloat(inputText);
431
+ if (coord !== void 0 && !isNaN(coord)) {
432
+ const currentLocation = GeoLocation.fromGeoPoint(getValue3() ?? [
433
+ 0,
434
+ 0
435
+ ]);
436
+ const newLocation = {
437
+ ...currentLocation,
438
+ [coordinateType]: coord
439
+ };
440
+ const newValue = GeoLocation.toGeoPoint(newLocation);
441
+ onValueChange(type, newValue);
442
+ }
443
+ }
444
+ }, [
445
+ getValue3,
446
+ onValueChange,
447
+ type
448
+ ]);
449
+ return /* @__PURE__ */ React4.createElement(Input2.Root, {
450
+ validationValence: status
451
+ }, /* @__PURE__ */ React4.createElement(InputHeader, {
452
+ error
453
+ }, /* @__PURE__ */ React4.createElement(Input2.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
454
+ role: "none",
455
+ className: "grid grid-cols-2 gap-2"
456
+ }, /* @__PURE__ */ React4.createElement(Input2.TextInput, {
457
+ type: "text",
458
+ pattern: "^-?[0-9]*\\.?[0-9]*$",
459
+ disabled,
460
+ placeholder: t("placeholder latitude"),
461
+ value: latitudeText,
462
+ onChange: handleCoordinateChange("latitude", setLatitudeText),
463
+ onBlur
464
+ }), /* @__PURE__ */ React4.createElement(Input2.TextInput, {
465
+ type: "text",
466
+ pattern: "^-?[0-9]*\\.?[0-9]*$",
467
+ disabled,
468
+ placeholder: t("placeholder longitude"),
469
+ value: longitudeText,
470
+ onChange: handleCoordinateChange("longitude", setLongitudeText),
471
+ onBlur
472
+ })));
473
+ };
474
+
475
+ // packages/ui/react-ui-form/src/components/Form/custom/SelectOptionsInput.tsx
476
+ import React5, { useCallback as useCallback3, useEffect as useEffect4, useState as useState3, useRef } from "react";
477
+ import { PublicKey } from "@dxos/keys";
478
+ import { Icon as Icon2, IconButton, Input as Input3, Tag, Toolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
479
+ import { List } from "@dxos/react-ui-list";
480
+ import { HuePicker } from "@dxos/react-ui-pickers";
481
+ import { hues, mx as mx2 } from "@dxos/react-ui-theme";
482
+ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange }) => {
483
+ const [selected, setSelectedId] = useState3(null);
484
+ const [isNewOption, setIsNewOption] = useState3(false);
485
+ const inputRef = useRef(null);
486
+ const { t } = useTranslation2(translationKey);
487
+ const { status, error } = getStatus();
488
+ const options = getValue3();
489
+ useEffect4(() => {
490
+ if (options === void 0) {
491
+ onValueChange(type, []);
492
+ }
493
+ }, [
494
+ options,
495
+ onValueChange,
496
+ type
497
+ ]);
498
+ const randomHue = () => {
499
+ const usedHues = new Set(options?.map((option) => option.color) ?? []);
500
+ const unusedHues = hues.filter((hue) => !usedHues.has(hue));
501
+ if (unusedHues.length > 0) {
502
+ return unusedHues[Math.floor(Math.random() * unusedHues.length)];
503
+ }
504
+ const lastThree = new Set(options?.slice(-3).map((option) => option.color) ?? []);
505
+ const availableHues = hues.filter((hue) => !lastThree.has(hue));
506
+ return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hues[Math.floor(Math.random() * hues.length)];
507
+ };
508
+ const handleAdd = useCallback3(() => {
509
+ const newId = PublicKey.random().truncate();
510
+ const newOption = {
511
+ id: newId,
512
+ title: "",
513
+ color: randomHue()
514
+ };
515
+ onValueChange(type, [
516
+ ...options ?? [],
517
+ newOption
518
+ ]);
519
+ setSelectedId(newId);
520
+ setIsNewOption(true);
521
+ }, [
522
+ options,
523
+ type,
524
+ onValueChange
525
+ ]);
526
+ const handleDelete = useCallback3((id) => {
527
+ const newOptions = options?.filter((option) => option.id !== id) ?? [];
528
+ onValueChange(type, newOptions);
529
+ setSelectedId(null);
530
+ }, [
531
+ options,
532
+ type,
533
+ onValueChange
534
+ ]);
535
+ const handleMove = useCallback3((from, to) => {
536
+ if (!options) {
537
+ return;
538
+ }
539
+ const newOptions = [
540
+ ...options
541
+ ];
542
+ const [removed] = newOptions.splice(from, 1);
543
+ newOptions.splice(to, 0, removed);
544
+ onValueChange(type, newOptions);
545
+ }, [
546
+ options,
547
+ type,
548
+ onValueChange
549
+ ]);
550
+ const handleClick = useCallback3((id) => {
551
+ setSelectedId((current) => current === id ? null : id);
552
+ }, []);
553
+ const handleTitleChange = useCallback3((id) => (e) => {
554
+ const newOptions = options?.map((o) => o.id === id ? {
555
+ ...o,
556
+ title: e.target.value
557
+ } : o);
558
+ onValueChange(type, newOptions ?? []);
559
+ }, [
560
+ options,
561
+ type,
562
+ onValueChange
563
+ ]);
564
+ const handleKeyDown = useCallback3((e) => {
565
+ if (e.key === "Enter") {
566
+ setSelectedId(null);
567
+ }
568
+ }, []);
569
+ const handleColorChange = useCallback3((id) => (hue) => {
570
+ const newOptions = options?.map((o) => o.id === id ? {
571
+ ...o,
572
+ color: hue
573
+ } : o);
574
+ onValueChange(type, newOptions ?? []);
575
+ }, [
576
+ options,
577
+ type,
578
+ onValueChange
579
+ ]);
580
+ useEffect4(() => {
581
+ if (selected && isNewOption && inputRef.current) {
582
+ inputRef.current.focus();
583
+ setIsNewOption(false);
584
+ }
585
+ }, [
586
+ selected,
587
+ isNewOption
588
+ ]);
589
+ return /* @__PURE__ */ React5.createElement(Input3.Root, {
590
+ validationValence: status
591
+ }, /* @__PURE__ */ React5.createElement(InputHeader, {
592
+ error
593
+ }, /* @__PURE__ */ React5.createElement(Input3.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
594
+ role: "none"
595
+ }, options && /* @__PURE__ */ React5.createElement(List.Root, {
596
+ items: options,
597
+ isItem: (item) => true,
598
+ onMove: handleMove
599
+ }, ({ items }) => /* @__PURE__ */ React5.createElement("div", {
600
+ role: "list",
601
+ className: "w-full overflow-auto"
602
+ }, items?.map((item) => /* @__PURE__ */ React5.createElement("div", {
603
+ key: item.id
604
+ }, /* @__PURE__ */ React5.createElement(List.Item, {
605
+ role: "button",
606
+ item,
607
+ classNames: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
608
+ "aria-expanded": selected === item.id
609
+ }, /* @__PURE__ */ React5.createElement("div", {
610
+ className: "flex items-center"
611
+ }, /* @__PURE__ */ React5.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React5.createElement(List.ItemTitle, {
612
+ onClick: () => handleClick(item.id),
613
+ classNames: "flex-1"
614
+ }, /* @__PURE__ */ React5.createElement(Tag, {
615
+ palette: item.color
616
+ }, item.title || "\u200B")), /* @__PURE__ */ React5.createElement(Icon2, {
617
+ icon: selected === item.id ? "ph--caret-down--regular" : "ph--caret-right--regular",
618
+ onClick: () => handleClick(item.id)
619
+ })), selected === item.id && /* @__PURE__ */ React5.createElement("div", {
620
+ className: "mis-4 mbs-2 flex flex-col gap-1"
621
+ }, /* @__PURE__ */ React5.createElement(Input3.Label, {
622
+ classNames: "text-xs"
623
+ }, t("select option label")), /* @__PURE__ */ React5.createElement("div", {
624
+ className: "flex flex-row items-center gap-1"
625
+ }, /* @__PURE__ */ React5.createElement(Input3.TextInput, {
626
+ placeholder: t("select option label placeholder"),
627
+ ref: selected === item.id ? inputRef : void 0,
628
+ value: item.title,
629
+ onChange: handleTitleChange(item.id),
630
+ onKeyDown: handleKeyDown,
631
+ classNames: "flex-1",
632
+ "data-no-submit": true
633
+ }), /* @__PURE__ */ React5.createElement(Toolbar.Root, {
634
+ classNames: "p-0 m-0 !is-auto"
635
+ }, /* @__PURE__ */ React5.createElement(HuePicker, {
636
+ value: item.color,
637
+ onChange: handleColorChange(item.id)
638
+ })), /* @__PURE__ */ React5.createElement(IconButton, {
639
+ icon: "ph--trash--fill",
640
+ iconOnly: true,
641
+ label: t("select option delete"),
642
+ onClick: () => handleDelete(item.id)
643
+ })))))), /* @__PURE__ */ React5.createElement("div", {
644
+ role: "listitem"
645
+ }, /* @__PURE__ */ React5.createElement("div", {
646
+ role: "button",
647
+ className: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
648
+ onClick: handleAdd
649
+ }, /* @__PURE__ */ React5.createElement("div", {
650
+ className: "flex items-center gap-1"
651
+ }, /* @__PURE__ */ React5.createElement(Icon2, {
652
+ icon: "ph--plus--regular"
653
+ }), /* @__PURE__ */ React5.createElement("span", {
654
+ className: "text-sm"
655
+ }, t("select option add")))))))));
656
+ };
657
+
658
+ // packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
659
+ import React6 from "react";
660
+ import { Input as Input4 } from "@dxos/react-ui";
661
+ import { mx as mx3 } from "@dxos/react-ui-theme";
662
+ import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
663
+ var gridCols = [
664
+ "grid-cols-1",
665
+ "grid-cols-2",
666
+ "grid-cols-3",
667
+ "grid-cols-4"
668
+ ];
669
+ var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
670
+ const { status, error } = getStatus();
671
+ const values = getValue3() ?? {};
672
+ return /* @__PURE__ */ React6.createElement(Input4.Root, {
673
+ validationValence: status
674
+ }, /* @__PURE__ */ React6.createElement(InputHeader, {
675
+ error
676
+ }, /* @__PURE__ */ React6.createElement(Input4.Label, null, label)), /* @__PURE__ */ React6.createElement("div", {
677
+ className: mx3("grid gap-2", gridCols[binding.length - 1])
678
+ }, binding.map((prop) => /* @__PURE__ */ React6.createElement(Input4.TextInput, {
679
+ key: prop,
680
+ type: "number",
681
+ disabled,
682
+ value: values[prop],
683
+ onChange: (event) => onValueChange(type, {
684
+ ...values,
685
+ [prop]: safeParseFloat2(event.target.value, 0)
686
+ }),
687
+ onBlur
688
+ }))));
689
+ };
690
+
691
+ // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
692
+ import React7, { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef2 } from "react";
693
+ import { Input as Input5, Select } from "@dxos/react-ui";
694
+ var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
695
+ const { status, error } = getStatus();
696
+ return /* @__PURE__ */ React7.createElement(Input5.Root, {
697
+ validationValence: status
698
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
699
+ error
700
+ }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
701
+ disabled,
702
+ placeholder,
703
+ value: getValue3() ?? "",
704
+ onChange: (event) => onValueChange(type, event.target.value),
705
+ onBlur
706
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
707
+ };
708
+ var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
709
+ const { status, error } = getStatus();
710
+ return /* @__PURE__ */ React7.createElement(Input5.Root, {
711
+ validationValence: status
712
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
713
+ error
714
+ }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
715
+ type: "number",
716
+ disabled,
717
+ placeholder,
718
+ value: getValue3() ?? 0,
719
+ onChange: (event) => onValueChange(type, event.target.value),
720
+ onBlur
721
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
722
+ };
723
+ var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
724
+ const { status, error } = getStatus();
725
+ const checked = Boolean(getValue3());
726
+ return /* @__PURE__ */ React7.createElement(Input5.Root, {
727
+ validationValence: status
728
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
729
+ error
730
+ }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.Switch, {
731
+ checked,
732
+ onCheckedChange: (value) => onValueChange(type, value)
733
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
734
+ };
735
+ var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
736
+ const { status, error } = getStatus();
737
+ return /* @__PURE__ */ React7.createElement(Input5.Root, {
738
+ validationValence: status
739
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
740
+ error
741
+ }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
742
+ value: getValue3(),
743
+ onValueChange: (value) => onValueChange(type, value)
744
+ }, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
745
+ classNames: "is-full",
746
+ disabled,
747
+ placeholder
748
+ }), /* @__PURE__ */ React7.createElement(Select.Portal, null, /* @__PURE__ */ React7.createElement(Select.Content, null, /* @__PURE__ */ React7.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
749
+ // NOTE: Numeric values are converted to and from strings.
750
+ /* @__PURE__ */ React7.createElement(Select.Option, {
751
+ key: String(value),
752
+ value: String(value)
753
+ }, label2 ?? String(value))
754
+ )))))), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
755
+ };
756
+ var MarkdownInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
757
+ const { status, error } = getStatus();
758
+ const textareaRef = useRef2(null);
759
+ const adjustHeight = useCallback4(() => {
760
+ const textarea = textareaRef.current;
761
+ if (textarea) {
762
+ textarea.style.blockSize = "auto";
763
+ textarea.style.blockSize = `${textarea.scrollHeight + 2}px`;
764
+ }
765
+ }, []);
766
+ useEffect5(() => {
767
+ adjustHeight();
768
+ }, [
769
+ adjustHeight
770
+ ]);
771
+ useEffect5(() => {
772
+ adjustHeight();
773
+ }, [
774
+ getValue3(),
775
+ adjustHeight
776
+ ]);
777
+ return /* @__PURE__ */ React7.createElement(Input5.Root, {
778
+ validationValence: status
779
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
780
+ error
781
+ }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextArea, {
782
+ ref: textareaRef,
783
+ disabled,
784
+ placeholder,
785
+ value: getValue3() ?? "",
786
+ classNames: "min-bs-auto max-h-40 overflow-auto",
787
+ onChange: (event) => onValueChange(type, event.target.value),
788
+ onBlur,
789
+ style: {
790
+ resize: "none"
791
+ },
792
+ spellCheck: false
793
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
794
+ };
795
+
796
+ // packages/ui/react-ui-form/src/components/Form/Form.tsx
797
+ import React11, { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef3 } from "react";
798
+ import { mx as mx7 } from "@dxos/react-ui-theme";
799
+
459
800
  // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
801
+ import React8 from "react";
802
+ import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
803
+ import { mx as mx4 } from "@dxos/react-ui-theme";
460
804
  var FormActions = ({ onCancel, readonly }) => {
461
- const { t } = useTranslation2(translationKey);
805
+ const { t } = useTranslation3(translationKey);
462
806
  const { canSave, handleSave } = useFormContext();
463
- return /* @__PURE__ */ React7.createElement("div", {
807
+ return /* @__PURE__ */ React8.createElement("div", {
464
808
  role: "none",
465
- className: "flex justify-center"
466
- }, /* @__PURE__ */ React7.createElement("div", {
809
+ className: "mt-2 flex justify-center"
810
+ }, /* @__PURE__ */ React8.createElement("div", {
467
811
  role: "none",
468
- className: mx2(onCancel && !readonly && "grid grid-cols-2 gap-2")
469
- }, onCancel && !readonly && /* @__PURE__ */ React7.createElement(IconButton, {
812
+ className: mx4(onCancel && !readonly && "grid grid-cols-2 gap-2")
813
+ }, onCancel && !readonly && /* @__PURE__ */ React8.createElement(IconButton2, {
470
814
  "data-testid": "cancel-button",
471
815
  icon: "ph--x--regular",
472
816
  label: t("button cancel"),
473
817
  onClick: onCancel
474
- }), handleSave && /* @__PURE__ */ React7.createElement(IconButton, {
818
+ }), handleSave && /* @__PURE__ */ React8.createElement(IconButton2, {
475
819
  type: "submit",
476
820
  "data-testid": "save-button",
477
821
  disabled: !canSave,
@@ -484,36 +828,36 @@ var FormActions = ({ onCancel, readonly }) => {
484
828
  // packages/ui/react-ui-form/src/components/Form/FormContent.tsx
485
829
  import { pipe as pipe2 } from "effect";
486
830
  import { capitalize as capitalize2 } from "effect/String";
487
- import React9, { useMemo as useMemo3 } from "react";
831
+ import React10, { useMemo as useMemo5 } from "react";
488
832
  import { AST as AST3, S } from "@dxos/echo-schema";
489
833
  import { createJsonPath as createJsonPath3, findNode as findNode2, getDiscriminatedType as getDiscriminatedType2, isDiscriminatedUnion as isDiscriminatedUnion2 } from "@dxos/effect";
490
- import { mx as mx4 } from "@dxos/react-ui-theme";
834
+ import { mx as mx6 } from "@dxos/react-ui-theme";
491
835
  import { getSchemaProperties as getSchemaProperties2 } from "@dxos/schema";
492
836
  import { isNotFalsy } from "@dxos/util";
493
837
 
494
838
  // packages/ui/react-ui-form/src/components/Form/ArrayField.tsx
495
839
  import { pipe } from "effect";
496
840
  import { capitalize } from "effect/String";
497
- import React8, { useCallback as useCallback2, useMemo as useMemo2 } from "react";
841
+ import React9, { useCallback as useCallback5, useMemo as useMemo4 } from "react";
498
842
  import { AST as AST2 } from "@dxos/echo-schema";
499
843
  import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
500
844
  import { invariant as invariant2 } from "@dxos/invariant";
501
- import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
502
- import { mx as mx3 } from "@dxos/react-ui-theme";
845
+ import { IconButton as IconButton3, useTranslation as useTranslation4 } from "@dxos/react-ui";
846
+ import { mx as mx5 } from "@dxos/react-ui-theme";
503
847
  import { getSchemaProperties } from "@dxos/schema";
504
848
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/ArrayField.tsx";
505
849
  var padding = "px-2";
506
850
  var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponent }) => {
507
- const { t } = useTranslation3(translationKey);
851
+ const { t } = useTranslation4(translationKey);
508
852
  const { ast, name, type, title } = property;
509
853
  const values = useFormValues(path ?? []);
510
- invariant2(Array.isArray(values), void 0, {
854
+ invariant2(Array.isArray(values), `Values at path ${path?.join(".")} must be an array.`, {
511
855
  F: __dxlog_file2,
512
856
  L: 37,
513
857
  S: void 0,
514
858
  A: [
515
859
  "Array.isArray(values)",
516
- ""
860
+ "`Values at path ${path?.join('.')} must be an array.`"
517
861
  ]
518
862
  });
519
863
  const label = title ?? pipe(name, capitalize);
@@ -530,7 +874,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
530
874
  prop.defaultValue
531
875
  ]));
532
876
  };
533
- const newValue = useMemo2(() => {
877
+ const newValue = useMemo4(() => {
534
878
  if (type === "object" && elementType) {
535
879
  return getDefaultObjectValue(elementType);
536
880
  } else {
@@ -540,7 +884,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
540
884
  type,
541
885
  elementType
542
886
  ]);
543
- const handleAdd = useCallback2(() => {
887
+ const handleAdd = useCallback5(() => {
544
888
  inputProps.onValueChange(type, [
545
889
  ...values,
546
890
  newValue
@@ -552,7 +896,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
552
896
  newValue,
553
897
  values
554
898
  ]);
555
- const handleRemove = useCallback2((index) => {
899
+ const handleRemove = useCallback5((index) => {
556
900
  const newValues = values.filter((_, i) => i !== index);
557
901
  inputProps.onValueChange(type, newValues);
558
902
  }, [
@@ -563,20 +907,20 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
563
907
  if (!elementType) {
564
908
  return null;
565
909
  }
566
- return /* @__PURE__ */ React8.createElement("div", {
910
+ return /* @__PURE__ */ React9.createElement("div", {
567
911
  role: "none",
568
- className: mx3(padding)
569
- }, /* @__PURE__ */ React8.createElement(InputHeader, null, label), /* @__PURE__ */ React8.createElement("div", {
912
+ className: mx5(padding)
913
+ }, /* @__PURE__ */ React9.createElement(InputHeader, null, label), /* @__PURE__ */ React9.createElement("div", {
570
914
  role: "none",
571
915
  className: "flex flex-col gap-1"
572
- }, values.map((_value, index) => /* @__PURE__ */ React8.createElement("div", {
916
+ }, values.map((_value, index) => /* @__PURE__ */ React9.createElement("div", {
573
917
  key: index,
574
918
  role: "none",
575
919
  className: "flex items-center gap-1"
576
- }, /* @__PURE__ */ React8.createElement("div", {
920
+ }, /* @__PURE__ */ React9.createElement("div", {
577
921
  role: "none",
578
922
  className: "flex-1"
579
- }, /* @__PURE__ */ React8.createElement(FormField, {
923
+ }, /* @__PURE__ */ React9.createElement(FormField, {
580
924
  property: {
581
925
  ...property,
582
926
  array: false,
@@ -590,15 +934,15 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
590
934
  inline: true,
591
935
  Custom,
592
936
  lookupComponent
593
- })), /* @__PURE__ */ React8.createElement(IconButton2, {
937
+ })), /* @__PURE__ */ React9.createElement(IconButton3, {
594
938
  icon: "ph--trash--regular",
595
939
  iconOnly: true,
596
940
  label: t("button remove"),
597
941
  onClick: () => handleRemove(index)
598
- })))), /* @__PURE__ */ React8.createElement("div", {
942
+ })))), /* @__PURE__ */ React9.createElement("div", {
599
943
  role: "none",
600
944
  className: "flex justify-between items-center plb-1"
601
- }, /* @__PURE__ */ React8.createElement(IconButton2, {
945
+ }, /* @__PURE__ */ React9.createElement(IconButton3, {
602
946
  icon: "ph--plus--regular",
603
947
  iconOnly: true,
604
948
  label: t("button add"),
@@ -610,8 +954,10 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
610
954
  import { FormatEnum } from "@dxos/echo-schema";
611
955
  var getInputComponent = (type, format) => {
612
956
  switch (format) {
613
- case FormatEnum.LatLng:
957
+ case FormatEnum.GeoPoint:
614
958
  return GeoPointInput;
959
+ case FormatEnum.Markdown:
960
+ return MarkdownInput;
615
961
  }
616
962
  switch (type) {
617
963
  case "string":
@@ -627,23 +973,14 @@ var getInputComponent = (type, format) => {
627
973
  var FormField = ({ property, path, readonly, inline, lookupComponent, Custom }) => {
628
974
  const inputProps = useInputProps(path);
629
975
  const { ast, name, type, format, title, description, options, examples, array } = property;
630
- const label = useMemo3(() => title ?? pipe2(name, capitalize2), [
976
+ const label = useMemo5(() => title ?? pipe2(name, capitalize2), [
631
977
  title,
632
978
  name
633
979
  ]);
634
- const placeholder = useMemo3(() => examples?.length ? `Example: "${examples[0]}"` : description, [
980
+ const placeholder = useMemo5(() => examples?.length ? `Example: "${examples[0]}"` : description, [
635
981
  examples,
636
982
  description
637
983
  ]);
638
- if (array) {
639
- return /* @__PURE__ */ React9.createElement(ArrayField, {
640
- property,
641
- path,
642
- inputProps,
643
- readonly,
644
- Custom
645
- });
646
- }
647
984
  const FoundComponent = lookupComponent?.({
648
985
  prop: name,
649
986
  schema: S.make(ast),
@@ -657,12 +994,37 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
657
994
  }
658
995
  });
659
996
  if (FoundComponent) {
660
- return /* @__PURE__ */ React9.createElement("div", null, FoundComponent);
997
+ return /* @__PURE__ */ React10.createElement("div", {
998
+ role: "none"
999
+ }, FoundComponent);
661
1000
  }
662
1001
  const jsonPath = createJsonPath3(path ?? []);
663
- const InputComponent = Custom?.[jsonPath] || getInputComponent(type, format);
1002
+ const CustomComponent = Custom?.[jsonPath];
1003
+ if (CustomComponent) {
1004
+ return /* @__PURE__ */ React10.createElement(CustomComponent, {
1005
+ type,
1006
+ format,
1007
+ label,
1008
+ inputOnly: inline,
1009
+ placeholder,
1010
+ disabled: readonly,
1011
+ ...inputProps
1012
+ });
1013
+ }
1014
+ if (array) {
1015
+ return /* @__PURE__ */ React10.createElement(ArrayField, {
1016
+ property,
1017
+ path,
1018
+ inputProps,
1019
+ readonly,
1020
+ Custom
1021
+ });
1022
+ }
1023
+ const InputComponent = getInputComponent(type, format);
664
1024
  if (InputComponent) {
665
- return /* @__PURE__ */ React9.createElement("div", null, /* @__PURE__ */ React9.createElement(InputComponent, {
1025
+ return /* @__PURE__ */ React10.createElement("div", {
1026
+ role: "none"
1027
+ }, /* @__PURE__ */ React10.createElement(InputComponent, {
666
1028
  type,
667
1029
  format,
668
1030
  label,
@@ -673,9 +1035,9 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
673
1035
  }));
674
1036
  }
675
1037
  if (options) {
676
- return /* @__PURE__ */ React9.createElement("div", {
1038
+ return /* @__PURE__ */ React10.createElement("div", {
677
1039
  role: "none"
678
- }, /* @__PURE__ */ React9.createElement(SelectInput, {
1040
+ }, /* @__PURE__ */ React10.createElement(SelectInput, {
679
1041
  type,
680
1042
  format,
681
1043
  disabled: readonly,
@@ -693,7 +1055,9 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
693
1055
  const baseNode = findNode2(ast, isDiscriminatedUnion2);
694
1056
  const typeLiteral = baseNode ? getDiscriminatedType2(baseNode, inputProps.getValue()) : findNode2(ast, AST3.isTypeLiteral);
695
1057
  if (typeLiteral) {
696
- return /* @__PURE__ */ React9.createElement("div", null, !inline && /* @__PURE__ */ React9.createElement("div", null, label), /* @__PURE__ */ React9.createElement(FormFields, {
1058
+ return /* @__PURE__ */ React10.createElement("div", {
1059
+ role: "none"
1060
+ }, !inline && /* @__PURE__ */ React10.createElement("div", null, label), /* @__PURE__ */ React10.createElement(FormFields, {
697
1061
  schema: S.make(typeLiteral),
698
1062
  path,
699
1063
  readonly,
@@ -705,7 +1069,7 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
705
1069
  };
706
1070
  var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }) => {
707
1071
  const values = useFormValues(path);
708
- const properties = useMemo3(() => {
1072
+ const properties = useMemo5(() => {
709
1073
  const props = getSchemaProperties2(schema.ast, values);
710
1074
  const filtered = filter ? filter(props) : props;
711
1075
  return sort ? filtered.sort((a, b) => sort.indexOf(a.name) - sort.indexOf(b.name)) : filtered;
@@ -715,11 +1079,11 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
715
1079
  filter,
716
1080
  sort
717
1081
  ]);
718
- return /* @__PURE__ */ React9.createElement("div", {
1082
+ return /* @__PURE__ */ React10.createElement("div", {
719
1083
  role: "form",
720
- className: mx4("flex flex-col w-full gap-2 py-2")
1084
+ className: mx6("flex flex-col w-full gap-2")
721
1085
  }, properties.map((property) => {
722
- return /* @__PURE__ */ React9.createElement(FormField, {
1086
+ return /* @__PURE__ */ React10.createElement(FormField, {
723
1087
  key: property.name,
724
1088
  property,
725
1089
  path: [
@@ -734,23 +1098,37 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
734
1098
  };
735
1099
 
736
1100
  // packages/ui/react-ui-form/src/components/Form/Form.tsx
737
- var Form = ({ classNames, schema, values: initialValues, path = [], readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
738
- const onValid = useMemo4(() => autoSave ? onSave : void 0, [
1101
+ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
1102
+ const formRef = useRef3(null);
1103
+ const onValid = useMemo6(() => autoSave ? onSave : void 0, [
739
1104
  autoSave,
740
1105
  onSave
741
1106
  ]);
742
- return /* @__PURE__ */ React10.createElement(FormProvider, {
1107
+ useEffect6(() => {
1108
+ if (autoFocus && formRef.current) {
1109
+ const input = formRef.current.querySelector("input");
1110
+ if (input) {
1111
+ input.focus();
1112
+ }
1113
+ }
1114
+ }, [
1115
+ autoFocus
1116
+ ]);
1117
+ return /* @__PURE__ */ React11.createElement(FormProvider, {
1118
+ formRef,
743
1119
  schema,
1120
+ autoSave,
744
1121
  initialValues,
745
1122
  onValuesChanged,
746
1123
  onValidate,
747
1124
  onValid,
748
1125
  onSave
749
- }, /* @__PURE__ */ React10.createElement("div", {
1126
+ }, /* @__PURE__ */ React11.createElement("div", {
1127
+ ref: formRef,
750
1128
  role: "none",
751
- className: mx5("p-2", classNames),
1129
+ className: mx7("p-2", classNames),
752
1130
  "data-testid": testId
753
- }, /* @__PURE__ */ React10.createElement(FormFields, {
1131
+ }, /* @__PURE__ */ React11.createElement(FormFields, {
754
1132
  schema,
755
1133
  path,
756
1134
  readonly,
@@ -758,7 +1136,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
758
1136
  sort,
759
1137
  lookupComponent,
760
1138
  Custom
761
- }), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React10.createElement(FormActions, {
1139
+ }), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React11.createElement(FormActions, {
762
1140
  onCancel,
763
1141
  readonly
764
1142
  })));
@@ -767,14 +1145,14 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
767
1145
  // packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
768
1146
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
769
1147
  var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
770
- const { t } = useTranslation4(translationKey);
771
- const [props, setProps] = useState2(projection.getFieldProjection(field.id).props);
772
- useEffect2(() => setProps(projection.getFieldProjection(field.id).props), [
1148
+ const { t } = useTranslation5(translationKey);
1149
+ const [props, setProps] = useState4(projection.getFieldProjection(field.id).props);
1150
+ useEffect7(() => setProps(projection.getFieldProjection(field.id).props), [
773
1151
  field,
774
1152
  projection
775
1153
  ]);
776
- const [schemas, setSchemas] = useState2([]);
777
- useEffect2(() => {
1154
+ const [schemas, setSchemas] = useState4([]);
1155
+ useEffect7(() => {
778
1156
  if (!registry) {
779
1157
  return;
780
1158
  }
@@ -792,17 +1170,17 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
792
1170
  }, [
793
1171
  registry
794
1172
  ]);
795
- const [referenceSchema, setReferenceSchema] = useState2();
796
- useEffect2(() => {
1173
+ const [referenceSchema, setReferenceSchema] = useState4();
1174
+ useEffect7(() => {
797
1175
  setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
798
1176
  }, [
799
1177
  schemas,
800
1178
  props?.referenceSchema
801
1179
  ]);
802
- const [{ fieldSchema }, setFieldSchema] = useState2({
1180
+ const [{ fieldSchema }, setFieldSchema] = useState4({
803
1181
  fieldSchema: getFormatSchema(props?.format)
804
1182
  });
805
- const handleValuesChanged = useCallback3((_props) => {
1183
+ const handleValuesChanged = useCallback6((_props) => {
806
1184
  setFieldSchema((prev) => {
807
1185
  const fieldSchema2 = getFormatSchema(_props.format);
808
1186
  if (prev.fieldSchema === fieldSchema2) {
@@ -830,14 +1208,14 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
830
1208
  type
831
1209
  };
832
1210
  }
833
- return props2;
1211
+ return _props;
834
1212
  });
835
1213
  }, [
836
1214
  schemas,
837
1215
  props.format,
838
1216
  props.referenceSchema
839
1217
  ]);
840
- const handleValidate = useCallback3(({ property }) => {
1218
+ const handleValidate = useCallback6(({ property }) => {
841
1219
  if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
842
1220
  return [
843
1221
  {
@@ -850,7 +1228,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
850
1228
  view.fields,
851
1229
  field
852
1230
  ]);
853
- const handleSave = useCallback3((props2) => {
1231
+ const handleSave = useCallback6((props2) => {
854
1232
  projection.setFieldProjection({
855
1233
  field,
856
1234
  props: props2
@@ -861,50 +1239,51 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
861
1239
  field,
862
1240
  onSave
863
1241
  ]);
864
- const handleCancel = useCallback3(() => {
1242
+ const handleCancel = useCallback6(() => {
865
1243
  requestAnimationFrame(() => onCancel?.());
866
1244
  onSave();
867
1245
  }, [
868
1246
  onSave
869
1247
  ]);
870
- const Custom = useMemo5(() => ({
871
- ["format"]: (props2) => /* @__PURE__ */ React11.createElement(SelectInput, {
1248
+ const Custom = useMemo7(() => ({
1249
+ ["format"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
872
1250
  ...props2,
873
1251
  options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
874
1252
  value,
875
1253
  label: t(`format ${value}`)
876
1254
  }))
877
1255
  }),
878
- ["referenceSchema"]: (props2) => /* @__PURE__ */ React11.createElement(SelectInput, {
1256
+ ["referenceSchema"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
879
1257
  ...props2,
880
1258
  options: schemas.map((schema) => ({
881
1259
  value: schema.typename
882
1260
  }))
883
1261
  }),
884
- ["referencePath"]: (props2) => /* @__PURE__ */ React11.createElement(SelectInput, {
1262
+ ["referencePath"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
885
1263
  ...props2,
886
1264
  options: referenceSchema ? getSchemaProperties3(referenceSchema.ast).sort(sortProperties).map((p) => ({
887
1265
  value: p.name
888
1266
  })) : []
889
- })
1267
+ }),
1268
+ ["options"]: (props2) => /* @__PURE__ */ React12.createElement(SelectOptionInput, props2)
890
1269
  }), [
891
1270
  t,
892
1271
  schemas,
893
1272
  referenceSchema
894
1273
  ]);
895
- const propIsNotType = useCallback3((props2) => props2.filter((p) => p.name !== "type"), []);
1274
+ const propIsNotType = useCallback6((props2) => props2.filter((p) => p.name !== "type"), []);
896
1275
  if (!fieldSchema) {
897
1276
  log2.warn("invalid format", {
898
1277
  props
899
1278
  }, {
900
1279
  F: __dxlog_file3,
901
- L: 173,
1280
+ L: 174,
902
1281
  S: void 0,
903
1282
  C: (f, a) => f(...a)
904
1283
  });
905
1284
  return null;
906
1285
  }
907
- return /* @__PURE__ */ React11.createElement(Form, {
1286
+ return /* @__PURE__ */ React12.createElement(Form, {
908
1287
  key: field.id,
909
1288
  autoFocus: true,
910
1289
  values: props,
@@ -923,30 +1302,29 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
923
1302
  };
924
1303
 
925
1304
  // packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
926
- import React12, { useCallback as useCallback4, useMemo as useMemo6, useState as useState3 } from "react";
1305
+ import React13, { useCallback as useCallback7, useMemo as useMemo8, useState as useState5 } from "react";
927
1306
  import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
928
- import { IconButton as IconButton3, useTranslation as useTranslation5 } from "@dxos/react-ui";
929
- import { List } from "@dxos/react-ui-list";
930
- import { ghostHover, inputTextLabel, mx as mx6 } from "@dxos/react-ui-theme";
1307
+ import { IconButton as IconButton4, useTranslation as useTranslation6 } from "@dxos/react-ui";
1308
+ import { List as List2 } from "@dxos/react-ui-list";
1309
+ import { ghostHover, inputTextLabel, mx as mx8 } from "@dxos/react-ui-theme";
931
1310
  import { FieldSchema, ViewProjection, VIEW_FIELD_LIMIT } from "@dxos/schema";
932
- import { arrayMove } from "@dxos/util";
933
1311
  var grid = "grid grid-cols-[32px_1fr_32px] min-bs-[2.5rem]";
934
1312
  var ViewMetaSchema = S2.Struct({
935
1313
  name: S2.String.annotations({
936
1314
  [AST4.TitleAnnotationId]: "View"
937
1315
  }),
938
- typename: Format.DXN.annotations({
1316
+ typename: Format.URL.annotations({
939
1317
  [AST4.TitleAnnotationId]: "Typename"
940
1318
  })
941
1319
  }).pipe(S2.mutable);
942
1320
  var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onTypenameChanged, onDelete }) => {
943
- const { t } = useTranslation5(translationKey);
944
- const projection = useMemo6(() => new ViewProjection(schema, view), [
1321
+ const { t } = useTranslation6(translationKey);
1322
+ const projection = useMemo8(() => new ViewProjection(schema, view), [
945
1323
  schema,
946
1324
  view
947
1325
  ]);
948
- const [field, setField] = useState3();
949
- const viewValues = useMemo6(() => {
1326
+ const [field, setField] = useState5();
1327
+ const viewValues = useMemo8(() => {
950
1328
  return {
951
1329
  name: view.name,
952
1330
  // TODO(burdon): Need to warn user of possible consequences of editing.
@@ -956,7 +1334,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
956
1334
  }, [
957
1335
  view
958
1336
  ]);
959
- const handleViewUpdate = useCallback4(({ name, typename }) => {
1337
+ const handleViewUpdate = useCallback7(({ name, typename }) => {
960
1338
  requestAnimationFrame(() => {
961
1339
  if (view.name !== name) {
962
1340
  view.name = name;
@@ -970,67 +1348,71 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
970
1348
  schema,
971
1349
  onTypenameChanged
972
1350
  ]);
973
- const handleSelect = useCallback4((field2) => {
1351
+ const handleSelect = useCallback7((field2) => {
974
1352
  setField((f) => f === field2 ? void 0 : field2);
975
1353
  }, []);
976
- const handleAdd = useCallback4(() => {
1354
+ const handleAdd = useCallback7(() => {
977
1355
  const field2 = projection.createFieldProjection();
978
1356
  setField(field2);
979
1357
  }, [
980
1358
  view
981
1359
  ]);
982
- const handleMove = useCallback4((fromIndex, toIndex) => {
983
- arrayMove(view.fields, fromIndex, toIndex);
1360
+ const handleMove = useCallback7((fromIndex, toIndex) => {
1361
+ const fields = [
1362
+ ...view.fields
1363
+ ];
1364
+ const [moved] = fields.splice(fromIndex, 1);
1365
+ fields.splice(toIndex, 0, moved);
1366
+ view.fields = fields;
984
1367
  }, [
985
1368
  view.fields
986
1369
  ]);
987
- const handleClose = useCallback4(() => setField(void 0), []);
988
- return /* @__PURE__ */ React12.createElement("div", {
1370
+ const handleClose = useCallback7(() => setField(void 0), []);
1371
+ return /* @__PURE__ */ React13.createElement("div", {
989
1372
  role: "none",
990
- className: mx6("flex flex-col w-full divide-y divide-separator", classNames)
991
- }, /* @__PURE__ */ React12.createElement(Form, {
992
- autoFocus: true,
1373
+ className: mx8("flex flex-col w-full divide-y divide-separator", classNames)
1374
+ }, /* @__PURE__ */ React13.createElement(Form, {
993
1375
  autoSave: true,
994
1376
  schema: ViewMetaSchema,
995
1377
  values: viewValues,
996
1378
  onSave: handleViewUpdate
997
- }), /* @__PURE__ */ React12.createElement("div", null, /* @__PURE__ */ React12.createElement("div", {
1379
+ }), /* @__PURE__ */ React13.createElement("div", null, /* @__PURE__ */ React13.createElement("div", {
998
1380
  role: "none",
999
1381
  className: "p-2"
1000
- }, /* @__PURE__ */ React12.createElement("label", {
1001
- className: mx6(inputTextLabel)
1002
- }, t("fields label"))), /* @__PURE__ */ React12.createElement(List.Root, {
1382
+ }, /* @__PURE__ */ React13.createElement("label", {
1383
+ className: mx8(inputTextLabel)
1384
+ }, t("fields label"))), /* @__PURE__ */ React13.createElement(List2.Root, {
1003
1385
  items: view.fields,
1004
1386
  isItem: S2.is(FieldSchema),
1005
1387
  getId: (field2) => field2.id,
1006
1388
  onMove: handleMove
1007
- }, ({ items: fields }) => /* @__PURE__ */ React12.createElement(React12.Fragment, null, showHeading && /* @__PURE__ */ React12.createElement("div", {
1389
+ }, ({ items: fields }) => /* @__PURE__ */ React13.createElement(React13.Fragment, null, showHeading && /* @__PURE__ */ React13.createElement("div", {
1008
1390
  role: "heading",
1009
1391
  className: grid
1010
- }, /* @__PURE__ */ React12.createElement("div", null), /* @__PURE__ */ React12.createElement("div", {
1392
+ }, /* @__PURE__ */ React13.createElement("div", null), /* @__PURE__ */ React13.createElement("div", {
1011
1393
  className: "flex items-center text-sm"
1012
- }, t("field path label"))), /* @__PURE__ */ React12.createElement("div", {
1394
+ }, t("field path label"))), /* @__PURE__ */ React13.createElement("div", {
1013
1395
  role: "list",
1014
1396
  className: "flex flex-col w-full"
1015
- }, fields?.map((field2) => /* @__PURE__ */ React12.createElement(List.Item, {
1397
+ }, fields?.map((field2) => /* @__PURE__ */ React13.createElement(List2.Item, {
1016
1398
  key: field2.id,
1017
1399
  item: field2,
1018
- classNames: mx6(grid, ghostHover, "cursor-pointer")
1019
- }, /* @__PURE__ */ React12.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React12.createElement(List.ItemTitle, {
1400
+ classNames: mx8(grid, ghostHover, "cursor-pointer")
1401
+ }, /* @__PURE__ */ React13.createElement(List2.ItemDragHandle, null), /* @__PURE__ */ React13.createElement(List2.ItemTitle, {
1020
1402
  onClick: () => handleSelect(field2)
1021
- }, field2.path), /* @__PURE__ */ React12.createElement(List.ItemDeleteButton, {
1403
+ }, field2.path), /* @__PURE__ */ React13.createElement(List2.ItemDeleteButton, {
1022
1404
  disabled: view.fields.length <= 1,
1023
1405
  onClick: () => onDelete(field2.id)
1024
- }))))))), field && /* @__PURE__ */ React12.createElement(FieldEditor, {
1406
+ }))))))), field && /* @__PURE__ */ React13.createElement(FieldEditor, {
1025
1407
  key: field.id,
1026
1408
  view,
1027
1409
  projection,
1028
1410
  field,
1029
1411
  registry,
1030
1412
  onSave: handleClose
1031
- }), !readonly && !field && /* @__PURE__ */ React12.createElement("div", {
1413
+ }), !readonly && !field && /* @__PURE__ */ React13.createElement("div", {
1032
1414
  className: "flex p-2 justify-center"
1033
- }, /* @__PURE__ */ React12.createElement(IconButton3, {
1415
+ }, /* @__PURE__ */ React13.createElement(IconButton4, {
1034
1416
  icon: "ph--plus--regular",
1035
1417
  label: t("button add property"),
1036
1418
  onClick: handleAdd,
@@ -1040,7 +1422,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1040
1422
  };
1041
1423
 
1042
1424
  // packages/ui/react-ui-form/src/util/field.ts
1043
- import { FormatEnum as FormatEnum3, TypeEnum } from "@dxos/echo-schema";
1425
+ import { FormatEnum as FormatEnum3, GeoLocation as GeoLocation2, TypeEnum } from "@dxos/echo-schema";
1044
1426
  var parseValue = ({ type, format, value }) => {
1045
1427
  if (value === void 0 || value === null || value === "") {
1046
1428
  return void 0;
@@ -1094,6 +1476,26 @@ var parseValue = ({ type, format, value }) => {
1094
1476
  const date = new Date(value);
1095
1477
  return isNaN(date.getTime()) ? null : date;
1096
1478
  }
1479
+ case FormatEnum3.GeoPoint: {
1480
+ if (typeof value === "string") {
1481
+ const cleaned = value.replace(/[^0-9.,-]+/g, "");
1482
+ const parts = cleaned.split(",");
1483
+ if (parts.length === 2) {
1484
+ const lat = parseFloat(parts[0]);
1485
+ const long = parseFloat(parts[1]);
1486
+ if (!isNaN(lat) && !isNaN(long)) {
1487
+ return GeoLocation2.toGeoPoint({
1488
+ latitude: lat,
1489
+ longitude: long
1490
+ });
1491
+ }
1492
+ }
1493
+ }
1494
+ return [
1495
+ 0,
1496
+ 0
1497
+ ];
1498
+ }
1097
1499
  default: {
1098
1500
  return value;
1099
1501
  }
@@ -1152,7 +1554,7 @@ var pathNotUniqueError = (path) => ({
1152
1554
  });
1153
1555
 
1154
1556
  // packages/ui/react-ui-form/src/util/formatting.ts
1155
- import { FormatEnum as FormatEnum4, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
1557
+ import { FormatEnum as FormatEnum4, GeoLocation as GeoLocation3, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
1156
1558
  var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
1157
1559
  const formatScalar = (type2) => {
1158
1560
  switch (type2) {
@@ -1205,6 +1607,16 @@ var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
1205
1607
  return date.toLocaleTimeString(locale);
1206
1608
  }
1207
1609
  }
1610
+ case FormatEnum4.GeoPoint: {
1611
+ if (value === null || value === void 0) {
1612
+ return "";
1613
+ }
1614
+ if (Array.isArray(value) && value.length >= 2 && value.every(Number.isFinite)) {
1615
+ const { latitude, longitude } = GeoLocation3.fromGeoPoint(value);
1616
+ return `${latitude},${longitude}`;
1617
+ }
1618
+ return String(value);
1619
+ }
1208
1620
  default: {
1209
1621
  if (value === null || value === "undefined") {
1210
1622
  return "";
@@ -1253,6 +1665,16 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
1253
1665
  const date = new Date(value);
1254
1666
  return date.toISOString().split("T")[1].split(".")[0];
1255
1667
  }
1668
+ case FormatEnum4.GeoPoint: {
1669
+ if (value === null || value === void 0) {
1670
+ return "";
1671
+ }
1672
+ if (Array.isArray(value) && value.length >= 2 && value.every(Number.isFinite)) {
1673
+ const { latitude, longitude } = GeoLocation3.fromGeoPoint(value);
1674
+ return `${latitude},${longitude}`;
1675
+ }
1676
+ return String(value);
1677
+ }
1256
1678
  default: {
1257
1679
  return String(value);
1258
1680
  }
@@ -1260,13 +1682,16 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
1260
1682
  };
1261
1683
  export {
1262
1684
  BooleanInput,
1685
+ DeprecatedFormContainer,
1263
1686
  DeprecatedFormInput,
1264
1687
  FieldEditor,
1265
1688
  Form,
1266
1689
  GeoPointInput,
1267
1690
  InputHeader,
1691
+ MarkdownInput,
1268
1692
  NumberInput,
1269
1693
  SelectInput,
1694
+ SelectOptionInput,
1270
1695
  TextInput,
1271
1696
  TupleInput,
1272
1697
  ViewEditor,
@@ -1277,6 +1702,7 @@ export {
1277
1702
  parseValue,
1278
1703
  pathNotUniqueError,
1279
1704
  translations_default as translations,
1280
- useForm
1705
+ useForm,
1706
+ useFormValues
1281
1707
  };
1282
1708
  //# sourceMappingURL=index.mjs.map