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

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