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

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