@dxos/react-ui-form 0.7.5-main.ff8607b → 0.7.5-staging.b81e783

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 (65) hide show
  1. package/dist/lib/browser/index.mjs +532 -360
  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 +567 -391
  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 +532 -360
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +1 -2
  11. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/ArrayField.d.ts +1 -2
  13. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/Defaults.d.ts +5 -5
  15. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/Form.d.ts +2 -2
  17. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/FormActions.d.ts +1 -2
  19. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/FormContent.d.ts +2 -3
  21. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  22. package/dist/types/src/components/Form/FormContext.d.ts +4 -1
  23. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
  24. package/dist/types/src/components/Form/Input.d.ts +2 -2
  25. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  26. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -2
  27. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +1 -2
  29. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
  30. package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -2
  31. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
  32. package/dist/types/src/components/Form/factory.d.ts.map +1 -1
  33. package/dist/types/src/components/Form/index.d.ts +2 -0
  34. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  35. package/dist/types/src/components/ViewEditor/Test.stories.d.ts +1 -2
  36. package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +1 -2
  38. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  39. package/dist/types/src/components/deprecated/Form.d.ts +1 -2
  40. package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
  41. package/dist/types/src/components/deprecated/Form.stories.d.ts +1 -2
  42. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
  43. package/dist/types/src/components/deprecated/FormInput.d.ts +2 -2
  44. package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
  45. package/dist/types/src/components/testing.d.ts +3 -3
  46. package/dist/types/src/components/testing.d.ts.map +1 -1
  47. package/dist/types/src/hooks/useForm.d.ts +3 -2
  48. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  49. package/dist/types/src/util/field.d.ts.map +1 -1
  50. package/dist/types/src/util/formatting.d.ts.map +1 -1
  51. package/package.json +28 -27
  52. package/src/components/Form/Defaults.tsx +61 -2
  53. package/src/components/Form/Form.tsx +17 -2
  54. package/src/components/Form/FormActions.tsx +1 -1
  55. package/src/components/Form/FormContent.tsx +10 -13
  56. package/src/components/Form/FormContext.tsx +48 -4
  57. package/src/components/Form/custom/GeoPointInput.tsx +42 -12
  58. package/src/components/Form/custom/SelectOptionsInput.tsx +7 -9
  59. package/src/components/Form/factory.tsx +4 -2
  60. package/src/components/Form/index.ts +3 -0
  61. package/src/components/ViewEditor/ViewEditor.tsx +2 -2
  62. package/src/components/deprecated/FormInput.tsx +1 -4
  63. package/src/hooks/useForm.ts +10 -3
  64. package/src/util/field.ts +19 -1
  65. package/src/util/formatting.ts +29 -1
@@ -92,93 +92,371 @@ var DeprecatedFormInput = ({ label, description, secondary, children }) => {
92
92
  }, children))));
93
93
  if (secondary) {
94
94
  return /* @__PURE__ */ React.createElement("div", {
95
- role: "none",
96
- className: "flex flex-col w-full"
95
+ role: "none"
97
96
  }, primary, secondary);
98
97
  }
99
98
  return primary;
100
99
  };
101
100
 
102
101
  // packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
103
- import React12, { useCallback as useCallback4, useEffect as useEffect3, useMemo as useMemo5, useState as useState3 } from "react";
102
+ import React12, { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo6, useState as useState4 } from "react";
104
103
  import { FormatEnum as FormatEnum2, FormatEnums, formatToType } from "@dxos/echo-schema";
105
104
  import { log as log2 } from "@dxos/log";
106
105
  import { useTranslation as useTranslation5 } from "@dxos/react-ui";
107
106
  import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
108
107
 
108
+ // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
109
+ import React2, { createContext, useContext, useEffect as useEffect2 } from "react";
110
+ import { raise } from "@dxos/debug";
111
+ import { getValue as getValue2 } from "@dxos/echo-schema";
112
+ import { createJsonPath as createJsonPath2 } from "@dxos/effect";
113
+
114
+ // packages/ui/react-ui-form/src/hooks/useForm.ts
115
+ import { useCallback, useEffect, useMemo, useState } from "react";
116
+ import { getValue, setValue } from "@dxos/echo-schema";
117
+ import { AST, createJsonPath, fromEffectValidationPath } from "@dxos/effect";
118
+ import { invariant } from "@dxos/invariant";
119
+ import { log } from "@dxos/log";
120
+ import { validateSchema } from "@dxos/schema";
121
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
122
+ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
123
+ invariant(AST.isTypeLiteral(schema.ast), void 0, {
124
+ F: __dxlog_file,
125
+ L: 92,
126
+ S: void 0,
127
+ A: [
128
+ "AST.isTypeLiteral(schema.ast)",
129
+ ""
130
+ ]
131
+ });
132
+ const [values, setValues] = useState(initialValues);
133
+ useEffect(() => {
134
+ setValues(initialValues);
135
+ }, [
136
+ initialValues
137
+ ]);
138
+ const [touched, setTouched] = useState(createKeySet(initialValues, false));
139
+ const [changed, setChanged] = useState(createKeySet(initialValues, false));
140
+ const [errors, setErrors] = useState({});
141
+ const [saving, setSaving] = useState(false);
142
+ const validate = useCallback((values2) => {
143
+ let errors2 = validateSchema(schema, values2) ?? [];
144
+ if (errors2.length === 0 && onValidate) {
145
+ const validatedValues = values2;
146
+ errors2 = onValidate(validatedValues) ?? [];
147
+ }
148
+ setErrors(flatMap(errors2));
149
+ const valid = errors2.length === 0;
150
+ return valid;
151
+ }, [
152
+ schema,
153
+ onValidate
154
+ ]);
155
+ const [schemaDirty, setSchemaDirty] = useState(false);
156
+ useEffect(() => setSchemaDirty(true), [
157
+ schema
158
+ ]);
159
+ useEffect(() => {
160
+ if (schemaDirty) {
161
+ validate(values);
162
+ setSchemaDirty(false);
163
+ }
164
+ }, [
165
+ values,
166
+ schemaDirty
167
+ ]);
168
+ const formIsValid = useMemo(() => {
169
+ return Object.keys(errors).length === 0;
170
+ }, [
171
+ errors
172
+ ]);
173
+ const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
174
+ !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
175
+ touched,
176
+ errors,
177
+ saving
178
+ ]);
179
+ const handleSave = useCallback(async () => {
180
+ if (validate(values)) {
181
+ setSaving(true);
182
+ try {
183
+ await onSave?.(values, {
184
+ changed
185
+ });
186
+ } finally {
187
+ setSaving(false);
188
+ }
189
+ }
190
+ }, [
191
+ values,
192
+ validate,
193
+ onSave,
194
+ changed
195
+ ]);
196
+ const getStatus = useCallback((path) => {
197
+ const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
198
+ const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
199
+ return {
200
+ status: matchingError ? "error" : void 0,
201
+ error: matchingError ? matchingError[1] : void 0
202
+ };
203
+ }, [
204
+ errors
205
+ ]);
206
+ const getFormValue = (path) => {
207
+ return getValue(values, createJsonPath(path));
208
+ };
209
+ const onValueChange = (path, type, value) => {
210
+ const jsonPath = createJsonPath(path);
211
+ let parsedValue = value;
212
+ try {
213
+ if (type === "number") {
214
+ parsedValue = parseFloat(value) || 0;
215
+ }
216
+ } catch (err) {
217
+ log.catch(err, void 0, {
218
+ F: __dxlog_file,
219
+ L: 198,
220
+ S: void 0,
221
+ C: (f, a) => f(...a)
222
+ });
223
+ parsedValue = void 0;
224
+ }
225
+ const newValues = {
226
+ ...setValue(values, jsonPath, parsedValue)
227
+ };
228
+ setValues(newValues);
229
+ setChanged((prev) => ({
230
+ ...prev,
231
+ [jsonPath]: true
232
+ }));
233
+ onValuesChanged?.(newValues);
234
+ const isValid = validate(newValues);
235
+ if (isValid && onValid) {
236
+ onValid(newValues, {
237
+ changed
238
+ });
239
+ }
240
+ };
241
+ const onTouched = useCallback((path) => {
242
+ const jsonPath = createJsonPath(path);
243
+ setTouched((touched2) => ({
244
+ ...touched2,
245
+ [jsonPath]: true
246
+ }));
247
+ validate(values);
248
+ }, [
249
+ validate,
250
+ values
251
+ ]);
252
+ return {
253
+ // State.
254
+ values,
255
+ errors,
256
+ touched,
257
+ changed,
258
+ canSave,
259
+ handleSave,
260
+ formIsValid,
261
+ // Field utils.
262
+ getStatus,
263
+ getValue: getFormValue,
264
+ onValueChange,
265
+ onTouched
266
+ };
267
+ };
268
+ var createKeySet = (obj, value) => {
269
+ invariant(obj, void 0, {
270
+ F: __dxlog_file,
271
+ L: 241,
272
+ S: void 0,
273
+ A: [
274
+ "obj",
275
+ ""
276
+ ]
277
+ });
278
+ return Object.keys(obj).reduce((acc, key) => ({
279
+ ...acc,
280
+ [key]: value
281
+ }), {});
282
+ };
283
+ var flatMap = (errors) => {
284
+ return errors.reduce((result, { path, message }) => {
285
+ const jsonPath = fromEffectValidationPath(path);
286
+ if (!(jsonPath in result)) {
287
+ result[jsonPath] = message;
288
+ }
289
+ return result;
290
+ }, {});
291
+ };
292
+
293
+ // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
294
+ var FormContext = /* @__PURE__ */ createContext(void 0);
295
+ var useFormContext = () => {
296
+ return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
297
+ };
298
+ var useFormValues = (path = []) => {
299
+ const { values: formValues } = useFormContext();
300
+ const jsonPath = createJsonPath2(path);
301
+ return getValue2(formValues, jsonPath);
302
+ };
303
+ var useInputProps = (path = []) => {
304
+ const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
305
+ return {
306
+ getStatus: () => getStatus(path),
307
+ getValue: () => getFormValue(path),
308
+ onValueChange: (type, value) => onValueChange(path, type, value),
309
+ onBlur: () => onTouched(path)
310
+ };
311
+ };
312
+ var FormProvider = ({ children, formRef, autoSave, ...formOptions }) => {
313
+ const form = useForm(formOptions);
314
+ useEffect2(() => {
315
+ if (!formRef?.current) {
316
+ return;
317
+ }
318
+ const handleKeyDown = (event) => {
319
+ const keyIsEnter = event.key === "Enter";
320
+ const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
321
+ const inputIsTextarea = event.target.tagName.toLowerCase() === "textarea";
322
+ const inputOptOut = event.target.hasAttribute("data-no-submit");
323
+ const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
324
+ const shouldSubmitTextarea = inputIsTextarea && keyIsEnter && event.metaKey;
325
+ if ((shouldSubmitRegularInput || shouldSubmitTextarea) && !inputOptOut) {
326
+ if (!autoSave && form.canSave) {
327
+ form.handleSave();
328
+ }
329
+ if (autoSave && form.formIsValid) {
330
+ event.target.blur();
331
+ }
332
+ }
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
346
+ }, children);
347
+ };
348
+
109
349
  // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
110
- import React3 from "react";
350
+ import React4, { useCallback as useCallback2, useState as useState2, useEffect as useEffect3, useMemo as useMemo2 } from "react";
351
+ import { GeoLocation } from "@dxos/echo-schema";
111
352
  import { Input as Input2, useTranslation } from "@dxos/react-ui";
112
353
  import { safeParseFloat } from "@dxos/util";
113
354
 
114
355
  // packages/ui/react-ui-form/src/components/Form/Input.tsx
115
- import React2 from "react";
356
+ import React3 from "react";
116
357
  import { Icon, Tooltip } from "@dxos/react-ui";
117
358
  import { errorText } from "@dxos/react-ui-theme";
118
359
  var InputHeader = ({ children, error }) => {
119
- return /* @__PURE__ */ React2.createElement("div", {
360
+ return /* @__PURE__ */ React3.createElement("div", {
120
361
  role: "none",
121
362
  className: "flex justify-between items-center mb-1"
122
- }, children, error && /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
363
+ }, children, error && /* @__PURE__ */ React3.createElement(Tooltip.Root, null, /* @__PURE__ */ React3.createElement(Tooltip.Trigger, {
123
364
  asChild: true
124
- }, /* @__PURE__ */ React2.createElement(Icon, {
365
+ }, /* @__PURE__ */ React3.createElement(Icon, {
125
366
  icon: "ph--warning--regular",
126
367
  size: 4,
127
368
  classNames: errorText
128
- })), /* @__PURE__ */ React2.createElement(Tooltip.Content, {
369
+ })), /* @__PURE__ */ React3.createElement(Tooltip.Content, {
129
370
  side: "bottom"
130
- }, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null), error)));
371
+ }, /* @__PURE__ */ React3.createElement(Tooltip.Arrow, null), error)));
131
372
  };
132
373
 
133
374
  // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
134
375
  var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
135
376
  const { t } = useTranslation(translationKey);
136
377
  const { status, error } = getStatus();
137
- const [lng = 0, lat = 0] = getValue3() ?? [];
138
- return /* @__PURE__ */ React3.createElement(Input2.Root, {
378
+ const geoPoint = useMemo2(() => getValue3() ?? [
379
+ 0,
380
+ 0
381
+ ], [
382
+ getValue3
383
+ ]);
384
+ const geoLocation = useMemo2(() => GeoLocation.fromGeoPoint(geoPoint), [
385
+ geoPoint
386
+ ]);
387
+ const [longitudeText, setLongitudeText] = useState2(geoLocation.longitude?.toString());
388
+ const [latitudeText, setLatitudeText] = useState2(geoLocation.latitude?.toString());
389
+ useEffect3(() => {
390
+ const location = GeoLocation.fromGeoPoint(geoPoint);
391
+ setLongitudeText(location.longitude.toString());
392
+ setLatitudeText(location.latitude.toString());
393
+ }, [
394
+ geoPoint
395
+ ]);
396
+ const handleCoordinateChange = useCallback2((coordinateType, setText) => (event) => {
397
+ const inputText = event.target.value;
398
+ setText(inputText);
399
+ if (inputText !== "" && inputText !== "-") {
400
+ const coord = safeParseFloat(inputText);
401
+ if (coord !== void 0 && !isNaN(coord)) {
402
+ const currentLocation = GeoLocation.fromGeoPoint(getValue3() ?? [
403
+ 0,
404
+ 0
405
+ ]);
406
+ const newLocation = {
407
+ ...currentLocation,
408
+ [coordinateType]: coord
409
+ };
410
+ const newValue = GeoLocation.toGeoPoint(newLocation);
411
+ onValueChange(type, newValue);
412
+ }
413
+ }
414
+ }, [
415
+ getValue3,
416
+ onValueChange,
417
+ type
418
+ ]);
419
+ return /* @__PURE__ */ React4.createElement(Input2.Root, {
139
420
  validationValence: status
140
- }, /* @__PURE__ */ React3.createElement(InputHeader, {
421
+ }, /* @__PURE__ */ React4.createElement(InputHeader, {
141
422
  error
142
- }, /* @__PURE__ */ React3.createElement(Input2.Label, null, label)), /* @__PURE__ */ React3.createElement("div", {
423
+ }, /* @__PURE__ */ React4.createElement(Input2.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
424
+ role: "none",
143
425
  className: "grid grid-cols-2 gap-2"
144
- }, /* @__PURE__ */ React3.createElement(Input2.TextInput, {
145
- type: "number",
426
+ }, /* @__PURE__ */ React4.createElement(Input2.TextInput, {
427
+ type: "text",
428
+ pattern: "^-?[0-9]*\\.?[0-9]*$",
146
429
  disabled,
147
- placeholder: t("placeholder longitude"),
148
- value: lng,
149
- onChange: (event) => onValueChange(type, [
150
- lat,
151
- safeParseFloat(event.target.value, 0)
152
- ]),
430
+ placeholder: t("placeholder latitude"),
431
+ value: latitudeText,
432
+ onChange: handleCoordinateChange("latitude", setLatitudeText),
153
433
  onBlur
154
- }), /* @__PURE__ */ React3.createElement(Input2.TextInput, {
155
- type: "number",
434
+ }), /* @__PURE__ */ React4.createElement(Input2.TextInput, {
435
+ type: "text",
436
+ pattern: "^-?[0-9]*\\.?[0-9]*$",
156
437
  disabled,
157
- placeholder: t("placeholder latitude"),
158
- value: lat,
159
- onChange: (event) => onValueChange(type, [
160
- safeParseFloat(event.target.value, 0),
161
- lng
162
- ]),
438
+ placeholder: t("placeholder longitude"),
439
+ value: longitudeText,
440
+ onChange: handleCoordinateChange("longitude", setLongitudeText),
163
441
  onBlur
164
442
  })));
165
443
  };
166
444
 
167
445
  // packages/ui/react-ui-form/src/components/Form/custom/SelectOptionsInput.tsx
168
- import React4, { useCallback, useEffect, useState, useRef } from "react";
446
+ import React5, { useCallback as useCallback3, useEffect as useEffect4, useState as useState3, useRef } from "react";
169
447
  import { PublicKey } from "@dxos/keys";
170
448
  import { Icon as Icon2, IconButton, Input as Input3, Tag, Toolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
171
449
  import { List } from "@dxos/react-ui-list";
172
- import { HuePickerToolbarButton } from "@dxos/react-ui-pickers";
173
- import { hueTokenThemes, mx as mx2 } from "@dxos/react-ui-theme";
450
+ import { HuePicker } from "@dxos/react-ui-pickers";
451
+ import { hues, mx as mx2 } from "@dxos/react-ui-theme";
174
452
  var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange }) => {
175
- const [selected, setSelectedId] = useState(null);
176
- const [isNewOption, setIsNewOption] = useState(false);
453
+ const [selected, setSelectedId] = useState3(null);
454
+ const [isNewOption, setIsNewOption] = useState3(false);
177
455
  const inputRef = useRef(null);
178
456
  const { t } = useTranslation2(translationKey);
179
457
  const { status, error } = getStatus();
180
458
  const options = getValue3();
181
- useEffect(() => {
459
+ useEffect4(() => {
182
460
  if (options === void 0) {
183
461
  onValueChange(type, []);
184
462
  }
@@ -189,16 +467,15 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
189
467
  ]);
190
468
  const randomHue = () => {
191
469
  const usedHues = new Set(options?.map((option) => option.color) ?? []);
192
- const hueKeys = Object.keys(hueTokenThemes);
193
- const unusedHues = hueKeys.filter((hue) => !usedHues.has(hue));
470
+ const unusedHues = hues.filter((hue) => !usedHues.has(hue));
194
471
  if (unusedHues.length > 0) {
195
472
  return unusedHues[Math.floor(Math.random() * unusedHues.length)];
196
473
  }
197
474
  const lastThree = new Set(options?.slice(-3).map((option) => option.color) ?? []);
198
- const availableHues = hueKeys.filter((hue) => !lastThree.has(hue));
199
- return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hueKeys[Math.floor(Math.random() * hueKeys.length)];
475
+ const availableHues = hues.filter((hue) => !lastThree.has(hue));
476
+ return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hues[Math.floor(Math.random() * hues.length)];
200
477
  };
201
- const handleAdd = useCallback(() => {
478
+ const handleAdd = useCallback3(() => {
202
479
  const newId = PublicKey.random().truncate();
203
480
  const newOption = {
204
481
  id: newId,
@@ -216,7 +493,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
216
493
  type,
217
494
  onValueChange
218
495
  ]);
219
- const handleDelete = useCallback((id) => {
496
+ const handleDelete = useCallback3((id) => {
220
497
  const newOptions = options?.filter((option) => option.id !== id) ?? [];
221
498
  onValueChange(type, newOptions);
222
499
  setSelectedId(null);
@@ -225,7 +502,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
225
502
  type,
226
503
  onValueChange
227
504
  ]);
228
- const handleMove = useCallback((from, to) => {
505
+ const handleMove = useCallback3((from, to) => {
229
506
  if (!options) {
230
507
  return;
231
508
  }
@@ -240,10 +517,10 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
240
517
  type,
241
518
  onValueChange
242
519
  ]);
243
- const handleClick = useCallback((id) => {
520
+ const handleClick = useCallback3((id) => {
244
521
  setSelectedId((current) => current === id ? null : id);
245
522
  }, []);
246
- const handleTitleChange = useCallback((id) => (e) => {
523
+ const handleTitleChange = useCallback3((id) => (e) => {
247
524
  const newOptions = options?.map((o) => o.id === id ? {
248
525
  ...o,
249
526
  title: e.target.value
@@ -254,12 +531,12 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
254
531
  type,
255
532
  onValueChange
256
533
  ]);
257
- const handleKeyDown = useCallback((e) => {
534
+ const handleKeyDown = useCallback3((e) => {
258
535
  if (e.key === "Enter") {
259
536
  setSelectedId(null);
260
537
  }
261
538
  }, []);
262
- const handleColorChange = useCallback((id) => (hue) => {
539
+ const handleColorChange = useCallback3((id) => (hue) => {
263
540
  const newOptions = options?.map((o) => o.id === id ? {
264
541
  ...o,
265
542
  color: hue
@@ -270,7 +547,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
270
547
  type,
271
548
  onValueChange
272
549
  ]);
273
- useEffect(() => {
550
+ useEffect4(() => {
274
551
  if (selected && isNewOption && inputRef.current) {
275
552
  inputRef.current.focus();
276
553
  setIsNewOption(false);
@@ -279,76 +556,77 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
279
556
  selected,
280
557
  isNewOption
281
558
  ]);
282
- return /* @__PURE__ */ React4.createElement(Input3.Root, {
559
+ return /* @__PURE__ */ React5.createElement(Input3.Root, {
283
560
  validationValence: status
284
- }, /* @__PURE__ */ React4.createElement(InputHeader, {
561
+ }, /* @__PURE__ */ React5.createElement(InputHeader, {
285
562
  error
286
- }, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
563
+ }, /* @__PURE__ */ React5.createElement(Input3.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
287
564
  role: "none"
288
- }, options && /* @__PURE__ */ React4.createElement(List.Root, {
565
+ }, options && /* @__PURE__ */ React5.createElement(List.Root, {
289
566
  items: options,
290
567
  isItem: (item) => true,
291
568
  onMove: handleMove
292
- }, ({ items }) => /* @__PURE__ */ React4.createElement("div", {
569
+ }, ({ items }) => /* @__PURE__ */ React5.createElement("div", {
293
570
  role: "list",
294
571
  className: "w-full overflow-auto"
295
- }, items?.map((item) => /* @__PURE__ */ React4.createElement("div", {
572
+ }, items?.map((item) => /* @__PURE__ */ React5.createElement("div", {
296
573
  key: item.id
297
- }, /* @__PURE__ */ React4.createElement(List.Item, {
574
+ }, /* @__PURE__ */ React5.createElement(List.Item, {
298
575
  role: "button",
299
576
  item,
300
577
  classNames: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
301
578
  "aria-expanded": selected === item.id
302
- }, /* @__PURE__ */ React4.createElement("div", {
579
+ }, /* @__PURE__ */ React5.createElement("div", {
303
580
  className: "flex items-center"
304
- }, /* @__PURE__ */ React4.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React4.createElement(List.ItemTitle, {
581
+ }, /* @__PURE__ */ React5.createElement(List.ItemDragHandle, null), /* @__PURE__ */ React5.createElement(List.ItemTitle, {
305
582
  onClick: () => handleClick(item.id),
306
583
  classNames: "flex-1"
307
- }, /* @__PURE__ */ React4.createElement(Tag, {
584
+ }, /* @__PURE__ */ React5.createElement(Tag, {
308
585
  palette: item.color
309
- }, item.title || "\u200B")), /* @__PURE__ */ React4.createElement(Icon2, {
586
+ }, item.title || "\u200B")), /* @__PURE__ */ React5.createElement(Icon2, {
310
587
  icon: selected === item.id ? "ph--caret-down--regular" : "ph--caret-right--regular",
311
588
  onClick: () => handleClick(item.id)
312
- })), selected === item.id && /* @__PURE__ */ React4.createElement("div", {
589
+ })), selected === item.id && /* @__PURE__ */ React5.createElement("div", {
313
590
  className: "mis-4 mbs-2 flex flex-col gap-1"
314
- }, /* @__PURE__ */ React4.createElement(Input3.Label, {
591
+ }, /* @__PURE__ */ React5.createElement(Input3.Label, {
315
592
  classNames: "text-xs"
316
- }, t("select option label")), /* @__PURE__ */ React4.createElement("div", {
593
+ }, t("select option label")), /* @__PURE__ */ React5.createElement("div", {
317
594
  className: "flex flex-row items-center gap-1"
318
- }, /* @__PURE__ */ React4.createElement(Input3.TextInput, {
595
+ }, /* @__PURE__ */ React5.createElement(Input3.TextInput, {
319
596
  placeholder: t("select option label placeholder"),
320
597
  ref: selected === item.id ? inputRef : void 0,
321
598
  value: item.title,
322
599
  onChange: handleTitleChange(item.id),
323
600
  onKeyDown: handleKeyDown,
324
- classNames: "flex-1"
325
- }), /* @__PURE__ */ React4.createElement(Toolbar.Root, {
601
+ classNames: "flex-1",
602
+ "data-no-submit": true
603
+ }), /* @__PURE__ */ React5.createElement(Toolbar.Root, {
326
604
  classNames: "p-0 m-0 !is-auto"
327
- }, /* @__PURE__ */ React4.createElement(HuePickerToolbarButton, {
328
- hue: item.color,
329
- onChangeHue: handleColorChange(item.id)
330
- })), /* @__PURE__ */ React4.createElement(IconButton, {
605
+ }, /* @__PURE__ */ React5.createElement(HuePicker, {
606
+ value: item.color,
607
+ onChange: handleColorChange(item.id)
608
+ })), /* @__PURE__ */ React5.createElement(IconButton, {
331
609
  icon: "ph--trash--fill",
332
610
  iconOnly: true,
333
611
  label: t("select option delete"),
334
612
  onClick: () => handleDelete(item.id)
335
- })))))), /* @__PURE__ */ React4.createElement("div", {
613
+ })))))), /* @__PURE__ */ React5.createElement("div", {
336
614
  role: "listitem"
337
- }, /* @__PURE__ */ React4.createElement("div", {
615
+ }, /* @__PURE__ */ React5.createElement("div", {
338
616
  role: "button",
339
617
  className: mx2("p-1 plb-2", "flex flex-col", "cursor-pointer", "hover:bg-hoverOverlay"),
340
618
  onClick: handleAdd
341
- }, /* @__PURE__ */ React4.createElement("div", {
619
+ }, /* @__PURE__ */ React5.createElement("div", {
342
620
  className: "flex items-center gap-1"
343
- }, /* @__PURE__ */ React4.createElement(Icon2, {
621
+ }, /* @__PURE__ */ React5.createElement(Icon2, {
344
622
  icon: "ph--plus--regular"
345
- }), /* @__PURE__ */ React4.createElement("span", {
623
+ }), /* @__PURE__ */ React5.createElement("span", {
346
624
  className: "text-sm"
347
625
  }, t("select option add")))))))));
348
626
  };
349
627
 
350
628
  // packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
351
- import React5 from "react";
629
+ import React6 from "react";
352
630
  import { Input as Input4 } from "@dxos/react-ui";
353
631
  import { mx as mx3 } from "@dxos/react-ui-theme";
354
632
  import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
@@ -361,13 +639,13 @@ var gridCols = [
361
639
  var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
362
640
  const { status, error } = getStatus();
363
641
  const values = getValue3() ?? {};
364
- return /* @__PURE__ */ React5.createElement(Input4.Root, {
642
+ return /* @__PURE__ */ React6.createElement(Input4.Root, {
365
643
  validationValence: status
366
- }, /* @__PURE__ */ React5.createElement(InputHeader, {
644
+ }, /* @__PURE__ */ React6.createElement(InputHeader, {
367
645
  error
368
- }, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
646
+ }, /* @__PURE__ */ React6.createElement(Input4.Label, null, label)), /* @__PURE__ */ React6.createElement("div", {
369
647
  className: mx3("grid gap-2", gridCols[binding.length - 1])
370
- }, binding.map((prop) => /* @__PURE__ */ React5.createElement(Input4.TextInput, {
648
+ }, binding.map((prop) => /* @__PURE__ */ React6.createElement(Input4.TextInput, {
371
649
  key: prop,
372
650
  type: "number",
373
651
  disabled,
@@ -381,282 +659,124 @@ var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValu
381
659
  };
382
660
 
383
661
  // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
384
- import React6 from "react";
662
+ import React7, { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef2 } from "react";
385
663
  import { Input as Input5, Select } from "@dxos/react-ui";
386
664
  var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
387
665
  const { status, error } = getStatus();
388
- return /* @__PURE__ */ React6.createElement(Input5.Root, {
666
+ return /* @__PURE__ */ React7.createElement(Input5.Root, {
389
667
  validationValence: status
390
- }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
668
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
391
669
  error
392
- }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.TextInput, {
670
+ }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
393
671
  disabled,
394
672
  placeholder,
395
673
  value: getValue3() ?? "",
396
674
  onChange: (event) => onValueChange(type, event.target.value),
397
675
  onBlur
398
- }), inputOnly && /* @__PURE__ */ React6.createElement(Input5.Validation, null, error));
676
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
399
677
  };
400
678
  var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
401
679
  const { status, error } = getStatus();
402
- return /* @__PURE__ */ React6.createElement(Input5.Root, {
680
+ return /* @__PURE__ */ React7.createElement(Input5.Root, {
403
681
  validationValence: status
404
- }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
682
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
405
683
  error
406
- }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.TextInput, {
684
+ }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
407
685
  type: "number",
408
686
  disabled,
409
687
  placeholder,
410
688
  value: getValue3() ?? 0,
411
689
  onChange: (event) => onValueChange(type, event.target.value),
412
690
  onBlur
413
- }), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
691
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
414
692
  };
415
693
  var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
416
694
  const { status, error } = getStatus();
417
- return /* @__PURE__ */ React6.createElement(Input5.Root, {
695
+ const checked = Boolean(getValue3());
696
+ return /* @__PURE__ */ React7.createElement(Input5.Root, {
418
697
  validationValence: status
419
- }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
698
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
420
699
  error
421
- }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Input5.Switch, {
422
- checked: getValue3(),
700
+ }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.Switch, {
701
+ checked,
423
702
  onCheckedChange: (value) => onValueChange(type, value)
424
- }), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
703
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
425
704
  };
426
705
  var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
427
706
  const { status, error } = getStatus();
428
- return /* @__PURE__ */ React6.createElement(Input5.Root, {
707
+ return /* @__PURE__ */ React7.createElement(Input5.Root, {
429
708
  validationValence: status
430
- }, !inputOnly && /* @__PURE__ */ React6.createElement(InputHeader, {
709
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
431
710
  error
432
- }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement(Select.Root, {
711
+ }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
433
712
  value: getValue3(),
434
713
  onValueChange: (value) => onValueChange(type, value)
435
- }, /* @__PURE__ */ React6.createElement(Select.TriggerButton, {
714
+ }, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
436
715
  classNames: "is-full",
437
716
  disabled,
438
717
  placeholder
439
- }), /* @__PURE__ */ React6.createElement(Select.Portal, null, /* @__PURE__ */ React6.createElement(Select.Content, null, /* @__PURE__ */ React6.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
718
+ }), /* @__PURE__ */ React7.createElement(Select.Portal, null, /* @__PURE__ */ React7.createElement(Select.Content, null, /* @__PURE__ */ React7.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
440
719
  // NOTE: Numeric values are converted to and from strings.
441
- /* @__PURE__ */ React6.createElement(Select.Option, {
720
+ /* @__PURE__ */ React7.createElement(Select.Option, {
442
721
  key: String(value),
443
722
  value: String(value)
444
723
  }, label2 ?? String(value))
445
- )))))), inputOnly && /* @__PURE__ */ React6.createElement(Input5.DescriptionAndValidation, null, error));
724
+ )))))), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
446
725
  };
447
-
448
- // packages/ui/react-ui-form/src/components/Form/Form.tsx
449
- import React11, { useMemo as useMemo4 } from "react";
450
- import { mx as mx7 } from "@dxos/react-ui-theme";
451
-
452
- // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
453
- import React8 from "react";
454
- import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
455
- import { mx as mx4 } from "@dxos/react-ui-theme";
456
-
457
- // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
458
- import React7, { createContext, useContext } from "react";
459
- import { raise } from "@dxos/debug";
460
- import { getValue as getValue2 } from "@dxos/echo-schema";
461
- import { createJsonPath as createJsonPath2 } from "@dxos/effect";
462
-
463
- // packages/ui/react-ui-form/src/hooks/useForm.ts
464
- import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useState as useState2 } from "react";
465
- import { getValue, setValue } from "@dxos/echo-schema";
466
- import { createJsonPath, fromEffectValidationPath } from "@dxos/effect";
467
- import { invariant } from "@dxos/invariant";
468
- import { log } from "@dxos/log";
469
- import { validateSchema } from "@dxos/schema";
470
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
471
- var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
472
- const [values, setValues] = useState2(initialValues);
473
- useEffect2(() => {
474
- setValues(initialValues);
475
- }, [
476
- initialValues
477
- ]);
478
- const [touched, setTouched] = useState2(createKeySet(initialValues, false));
479
- const [changed, setChanged] = useState2(createKeySet(initialValues, false));
480
- const [errors, setErrors] = useState2({});
481
- const [saving, setSaving] = useState2(false);
482
- const validate = useCallback2((values2) => {
483
- let errors2 = validateSchema(schema, values2) ?? [];
484
- if (errors2.length === 0 && onValidate) {
485
- const validatedValues = values2;
486
- errors2 = onValidate(validatedValues) ?? [];
487
- }
488
- setErrors(flatMap(errors2));
489
- const valid = errors2.length === 0;
490
- return valid;
491
- }, [
492
- schema,
493
- onValidate
494
- ]);
495
- const [schemaDirty, setSchemaDirty] = useState2(false);
496
- useEffect2(() => setSchemaDirty(true), [
497
- schema
498
- ]);
499
- useEffect2(() => {
500
- if (schemaDirty) {
501
- validate(values);
502
- setSchemaDirty(false);
503
- }
504
- }, [
505
- values,
506
- schemaDirty
507
- ]);
508
- const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors
509
- !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
510
- touched,
511
- errors,
512
- saving
513
- ]);
514
- const handleSave = useCallback2(async () => {
515
- if (validate(values)) {
516
- setSaving(true);
517
- try {
518
- await onSave?.(values, {
519
- changed
520
- });
521
- } finally {
522
- setSaving(false);
523
- }
726
+ var MarkdownInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
727
+ const { status, error } = getStatus();
728
+ const textareaRef = useRef2(null);
729
+ const adjustHeight = useCallback4(() => {
730
+ const textarea = textareaRef.current;
731
+ if (textarea) {
732
+ textarea.style.blockSize = "auto";
733
+ textarea.style.blockSize = `${textarea.scrollHeight + 2}px`;
524
734
  }
735
+ }, []);
736
+ useEffect5(() => {
737
+ adjustHeight();
525
738
  }, [
526
- values,
527
- validate,
528
- onSave,
529
- changed
739
+ adjustHeight
530
740
  ]);
531
- const getStatus = useCallback2((path) => {
532
- const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
533
- const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
534
- return {
535
- status: matchingError ? "error" : void 0,
536
- error: matchingError ? matchingError[1] : void 0
537
- };
741
+ useEffect5(() => {
742
+ adjustHeight();
538
743
  }, [
539
- errors
744
+ getValue3(),
745
+ adjustHeight
540
746
  ]);
541
- const getFormValue = (path) => {
542
- return getValue(values, createJsonPath(path));
543
- };
544
- const onValueChange = (path, type, value) => {
545
- const jsonPath = createJsonPath(path);
546
- let parsedValue = value;
547
- try {
548
- if (type === "number") {
549
- parsedValue = parseFloat(value) || 0;
550
- }
551
- } catch (err) {
552
- log.catch(err, void 0, {
553
- F: __dxlog_file,
554
- L: 192,
555
- S: void 0,
556
- C: (f, a) => f(...a)
557
- });
558
- parsedValue = void 0;
559
- }
560
- const newValues = {
561
- ...setValue(values, jsonPath, parsedValue)
562
- };
563
- setValues(newValues);
564
- setChanged((prev) => ({
565
- ...prev,
566
- [jsonPath]: true
567
- }));
568
- onValuesChanged?.(newValues);
569
- const isValid = validate(newValues);
570
- if (isValid && onValid) {
571
- onValid(newValues, {
572
- changed
573
- });
574
- }
575
- };
576
- const onTouched = useCallback2((path) => {
577
- const jsonPath = createJsonPath(path);
578
- setTouched((touched2) => ({
579
- ...touched2,
580
- [jsonPath]: true
581
- }));
582
- validate(values);
583
- }, [
584
- validate,
585
- values
586
- ]);
587
- return {
588
- // State.
589
- values,
590
- errors,
591
- touched,
592
- changed,
593
- canSave,
594
- handleSave,
595
- // Field utils.
596
- getStatus,
597
- getValue: getFormValue,
598
- onValueChange,
599
- onTouched
600
- };
601
- };
602
- var createKeySet = (obj, value) => {
603
- invariant(obj, void 0, {
604
- F: __dxlog_file,
605
- L: 234,
606
- S: void 0,
607
- A: [
608
- "obj",
609
- ""
610
- ]
611
- });
612
- return Object.keys(obj).reduce((acc, key) => ({
613
- ...acc,
614
- [key]: value
615
- }), {});
616
- };
617
- var flatMap = (errors) => {
618
- return errors.reduce((result, { path, message }) => {
619
- const jsonPath = fromEffectValidationPath(path);
620
- if (!(jsonPath in result)) {
621
- result[jsonPath] = message;
622
- }
623
- return result;
624
- }, {});
747
+ return /* @__PURE__ */ React7.createElement(Input5.Root, {
748
+ validationValence: status
749
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
750
+ error
751
+ }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextArea, {
752
+ ref: textareaRef,
753
+ disabled,
754
+ placeholder,
755
+ value: getValue3() ?? "",
756
+ classNames: "min-bs-auto max-h-40 overflow-auto",
757
+ onChange: (event) => onValueChange(type, event.target.value),
758
+ onBlur,
759
+ style: {
760
+ resize: "none"
761
+ },
762
+ spellCheck: false
763
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
625
764
  };
626
765
 
627
- // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
628
- var FormContext = /* @__PURE__ */ createContext(void 0);
629
- var useFormContext = () => {
630
- return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
631
- };
632
- var useFormValues = (path = []) => {
633
- const { values: formValues } = useFormContext();
634
- const jsonPath = createJsonPath2(path);
635
- return getValue2(formValues, jsonPath);
636
- };
637
- var useInputProps = (path = []) => {
638
- const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
639
- return {
640
- getStatus: () => getStatus(path),
641
- getValue: () => getFormValue(path),
642
- onValueChange: (type, value) => onValueChange(path, type, value),
643
- onBlur: () => onTouched(path)
644
- };
645
- };
646
- var FormProvider = ({ children, ...formOptions }) => {
647
- const formHandler = useForm(formOptions);
648
- return /* @__PURE__ */ React7.createElement(FormContext.Provider, {
649
- value: formHandler
650
- }, children);
651
- };
766
+ // packages/ui/react-ui-form/src/components/Form/Form.tsx
767
+ import React11, { useEffect as useEffect6, useMemo as useMemo5, useRef as useRef3 } from "react";
768
+ import { mx as mx7 } from "@dxos/react-ui-theme";
652
769
 
653
770
  // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
771
+ import React8 from "react";
772
+ import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
773
+ import { mx as mx4 } from "@dxos/react-ui-theme";
654
774
  var FormActions = ({ onCancel, readonly }) => {
655
775
  const { t } = useTranslation3(translationKey);
656
776
  const { canSave, handleSave } = useFormContext();
657
777
  return /* @__PURE__ */ React8.createElement("div", {
658
778
  role: "none",
659
- className: "flex justify-center"
779
+ className: "mt-2 flex justify-center"
660
780
  }, /* @__PURE__ */ React8.createElement("div", {
661
781
  role: "none",
662
782
  className: mx4(onCancel && !readonly && "grid grid-cols-2 gap-2")
@@ -678,8 +798,8 @@ var FormActions = ({ onCancel, readonly }) => {
678
798
  // packages/ui/react-ui-form/src/components/Form/FormContent.tsx
679
799
  import { pipe as pipe2 } from "effect";
680
800
  import { capitalize as capitalize2 } from "effect/String";
681
- import React10, { useMemo as useMemo3 } from "react";
682
- import { AST as AST2, S } from "@dxos/echo-schema";
801
+ import React10, { useMemo as useMemo4 } from "react";
802
+ import { AST as AST3, S } from "@dxos/echo-schema";
683
803
  import { createJsonPath as createJsonPath3, findNode as findNode2, getDiscriminatedType as getDiscriminatedType2, isDiscriminatedUnion as isDiscriminatedUnion2 } from "@dxos/effect";
684
804
  import { mx as mx6 } from "@dxos/react-ui-theme";
685
805
  import { getSchemaProperties as getSchemaProperties2 } from "@dxos/schema";
@@ -688,8 +808,8 @@ import { isNotFalsy } from "@dxos/util";
688
808
  // packages/ui/react-ui-form/src/components/Form/ArrayField.tsx
689
809
  import { pipe } from "effect";
690
810
  import { capitalize } from "effect/String";
691
- import React9, { useCallback as useCallback3, useMemo as useMemo2 } from "react";
692
- import { AST } from "@dxos/echo-schema";
811
+ import React9, { useCallback as useCallback5, useMemo as useMemo3 } from "react";
812
+ import { AST as AST2 } from "@dxos/echo-schema";
693
813
  import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
694
814
  import { invariant as invariant2 } from "@dxos/invariant";
695
815
  import { IconButton as IconButton3, useTranslation as useTranslation4 } from "@dxos/react-ui";
@@ -711,11 +831,11 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
711
831
  ]
712
832
  });
713
833
  const label = title ?? pipe(name, capitalize);
714
- const tupleType = findNode(ast, AST.isTupleType);
834
+ const tupleType = findNode(ast, AST2.isTupleType);
715
835
  const elementType = tupleType?.rest[0]?.type;
716
836
  const getDefaultObjectValue = (typeNode) => {
717
837
  const baseNode = findNode(typeNode, isDiscriminatedUnion);
718
- const typeLiteral = baseNode ? getDiscriminatedType(baseNode, {}) : findNode(typeNode, AST.isTypeLiteral);
838
+ const typeLiteral = baseNode ? getDiscriminatedType(baseNode, {}) : findNode(typeNode, AST2.isTypeLiteral);
719
839
  if (!typeLiteral) {
720
840
  return {};
721
841
  }
@@ -724,7 +844,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
724
844
  prop.defaultValue
725
845
  ]));
726
846
  };
727
- const newValue = useMemo2(() => {
847
+ const newValue = useMemo3(() => {
728
848
  if (type === "object" && elementType) {
729
849
  return getDefaultObjectValue(elementType);
730
850
  } else {
@@ -734,7 +854,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
734
854
  type,
735
855
  elementType
736
856
  ]);
737
- const handleAdd = useCallback3(() => {
857
+ const handleAdd = useCallback5(() => {
738
858
  inputProps.onValueChange(type, [
739
859
  ...values,
740
860
  newValue
@@ -746,7 +866,7 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
746
866
  newValue,
747
867
  values
748
868
  ]);
749
- const handleRemove = useCallback3((index) => {
869
+ const handleRemove = useCallback5((index) => {
750
870
  const newValues = values.filter((_, i) => i !== index);
751
871
  inputProps.onValueChange(type, newValues);
752
872
  }, [
@@ -804,8 +924,10 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
804
924
  import { FormatEnum } from "@dxos/echo-schema";
805
925
  var getInputComponent = (type, format) => {
806
926
  switch (format) {
807
- case FormatEnum.LatLng:
927
+ case FormatEnum.LatLong:
808
928
  return GeoPointInput;
929
+ case FormatEnum.Markdown:
930
+ return MarkdownInput;
809
931
  }
810
932
  switch (type) {
811
933
  case "string":
@@ -821,11 +943,11 @@ var getInputComponent = (type, format) => {
821
943
  var FormField = ({ property, path, readonly, inline, lookupComponent, Custom }) => {
822
944
  const inputProps = useInputProps(path);
823
945
  const { ast, name, type, format, title, description, options, examples, array } = property;
824
- const label = useMemo3(() => title ?? pipe2(name, capitalize2), [
946
+ const label = useMemo4(() => title ?? pipe2(name, capitalize2), [
825
947
  title,
826
948
  name
827
949
  ]);
828
- const placeholder = useMemo3(() => examples?.length ? `Example: "${examples[0]}"` : description, [
950
+ const placeholder = useMemo4(() => examples?.length ? `Example: "${examples[0]}"` : description, [
829
951
  examples,
830
952
  description
831
953
  ]);
@@ -849,20 +971,15 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
849
971
  const jsonPath = createJsonPath3(path ?? []);
850
972
  const CustomComponent = Custom?.[jsonPath];
851
973
  if (CustomComponent) {
852
- return (
853
- // TODO(ZaymonFC): We should try to remove this div wrapper.
854
- /* @__PURE__ */ React10.createElement("div", {
855
- role: "none"
856
- }, /* @__PURE__ */ React10.createElement(CustomComponent, {
857
- type,
858
- format,
859
- label,
860
- inputOnly: inline,
861
- placeholder,
862
- disabled: readonly,
863
- ...inputProps
864
- }))
865
- );
974
+ return /* @__PURE__ */ React10.createElement(CustomComponent, {
975
+ type,
976
+ format,
977
+ label,
978
+ inputOnly: inline,
979
+ placeholder,
980
+ disabled: readonly,
981
+ ...inputProps
982
+ });
866
983
  }
867
984
  if (array) {
868
985
  return /* @__PURE__ */ React10.createElement(ArrayField, {
@@ -906,7 +1023,7 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
906
1023
  }
907
1024
  if (type === "object") {
908
1025
  const baseNode = findNode2(ast, isDiscriminatedUnion2);
909
- const typeLiteral = baseNode ? getDiscriminatedType2(baseNode, inputProps.getValue()) : findNode2(ast, AST2.isTypeLiteral);
1026
+ const typeLiteral = baseNode ? getDiscriminatedType2(baseNode, inputProps.getValue()) : findNode2(ast, AST3.isTypeLiteral);
910
1027
  if (typeLiteral) {
911
1028
  return /* @__PURE__ */ React10.createElement("div", {
912
1029
  role: "none"
@@ -922,7 +1039,7 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
922
1039
  };
923
1040
  var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custom }) => {
924
1041
  const values = useFormValues(path);
925
- const properties = useMemo3(() => {
1042
+ const properties = useMemo4(() => {
926
1043
  const props = getSchemaProperties2(schema.ast, values);
927
1044
  const filtered = filter ? filter(props) : props;
928
1045
  return sort ? filtered.sort((a, b) => sort.indexOf(a.name) - sort.indexOf(b.name)) : filtered;
@@ -934,7 +1051,7 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
934
1051
  ]);
935
1052
  return /* @__PURE__ */ React10.createElement("div", {
936
1053
  role: "form",
937
- className: mx6("flex flex-col w-full gap-2 py-2")
1054
+ className: mx6("flex flex-col w-full gap-2")
938
1055
  }, properties.map((property) => {
939
1056
  return /* @__PURE__ */ React10.createElement(FormField, {
940
1057
  key: property.name,
@@ -951,19 +1068,33 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
951
1068
  };
952
1069
 
953
1070
  // packages/ui/react-ui-form/src/components/Form/Form.tsx
954
- var Form = ({ classNames, schema, values: initialValues, path = [], readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
955
- const onValid = useMemo4(() => autoSave ? onSave : void 0, [
1071
+ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, readonly, filter, sort, autoSave, testId, onValuesChanged, onValidate, onSave, onCancel, lookupComponent, Custom }) => {
1072
+ const formRef = useRef3(null);
1073
+ const onValid = useMemo5(() => autoSave ? onSave : void 0, [
956
1074
  autoSave,
957
1075
  onSave
958
1076
  ]);
1077
+ useEffect6(() => {
1078
+ if (autoFocus && formRef.current) {
1079
+ const input = formRef.current.querySelector("input");
1080
+ if (input) {
1081
+ input.focus();
1082
+ }
1083
+ }
1084
+ }, [
1085
+ autoFocus
1086
+ ]);
959
1087
  return /* @__PURE__ */ React11.createElement(FormProvider, {
1088
+ formRef,
960
1089
  schema,
1090
+ autoSave,
961
1091
  initialValues,
962
1092
  onValuesChanged,
963
1093
  onValidate,
964
1094
  onValid,
965
1095
  onSave
966
1096
  }, /* @__PURE__ */ React11.createElement("div", {
1097
+ ref: formRef,
967
1098
  role: "none",
968
1099
  className: mx7("p-2", classNames),
969
1100
  "data-testid": testId
@@ -985,13 +1116,13 @@ var Form = ({ classNames, schema, values: initialValues, path = [], readonly, fi
985
1116
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
986
1117
  var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
987
1118
  const { t } = useTranslation5(translationKey);
988
- const [props, setProps] = useState3(projection.getFieldProjection(field.id).props);
989
- useEffect3(() => setProps(projection.getFieldProjection(field.id).props), [
1119
+ const [props, setProps] = useState4(projection.getFieldProjection(field.id).props);
1120
+ useEffect7(() => setProps(projection.getFieldProjection(field.id).props), [
990
1121
  field,
991
1122
  projection
992
1123
  ]);
993
- const [schemas, setSchemas] = useState3([]);
994
- useEffect3(() => {
1124
+ const [schemas, setSchemas] = useState4([]);
1125
+ useEffect7(() => {
995
1126
  if (!registry) {
996
1127
  return;
997
1128
  }
@@ -1009,17 +1140,17 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1009
1140
  }, [
1010
1141
  registry
1011
1142
  ]);
1012
- const [referenceSchema, setReferenceSchema] = useState3();
1013
- useEffect3(() => {
1143
+ const [referenceSchema, setReferenceSchema] = useState4();
1144
+ useEffect7(() => {
1014
1145
  setReferenceSchema(schemas.find((schema) => schema.typename === props?.referenceSchema));
1015
1146
  }, [
1016
1147
  schemas,
1017
1148
  props?.referenceSchema
1018
1149
  ]);
1019
- const [{ fieldSchema }, setFieldSchema] = useState3({
1150
+ const [{ fieldSchema }, setFieldSchema] = useState4({
1020
1151
  fieldSchema: getFormatSchema(props?.format)
1021
1152
  });
1022
- const handleValuesChanged = useCallback4((_props) => {
1153
+ const handleValuesChanged = useCallback6((_props) => {
1023
1154
  setFieldSchema((prev) => {
1024
1155
  const fieldSchema2 = getFormatSchema(_props.format);
1025
1156
  if (prev.fieldSchema === fieldSchema2) {
@@ -1054,7 +1185,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1054
1185
  props.format,
1055
1186
  props.referenceSchema
1056
1187
  ]);
1057
- const handleValidate = useCallback4(({ property }) => {
1188
+ const handleValidate = useCallback6(({ property }) => {
1058
1189
  if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
1059
1190
  return [
1060
1191
  {
@@ -1067,7 +1198,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1067
1198
  view.fields,
1068
1199
  field
1069
1200
  ]);
1070
- const handleSave = useCallback4((props2) => {
1201
+ const handleSave = useCallback6((props2) => {
1071
1202
  projection.setFieldProjection({
1072
1203
  field,
1073
1204
  props: props2
@@ -1078,13 +1209,13 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1078
1209
  field,
1079
1210
  onSave
1080
1211
  ]);
1081
- const handleCancel = useCallback4(() => {
1212
+ const handleCancel = useCallback6(() => {
1082
1213
  requestAnimationFrame(() => onCancel?.());
1083
1214
  onSave();
1084
1215
  }, [
1085
1216
  onSave
1086
1217
  ]);
1087
- const Custom = useMemo5(() => ({
1218
+ const Custom = useMemo6(() => ({
1088
1219
  ["format"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
1089
1220
  ...props2,
1090
1221
  options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
@@ -1110,7 +1241,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1110
1241
  schemas,
1111
1242
  referenceSchema
1112
1243
  ]);
1113
- const propIsNotType = useCallback4((props2) => props2.filter((p) => p.name !== "type"), []);
1244
+ const propIsNotType = useCallback6((props2) => props2.filter((p) => p.name !== "type"), []);
1114
1245
  if (!fieldSchema) {
1115
1246
  log2.warn("invalid format", {
1116
1247
  props
@@ -1141,8 +1272,8 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1141
1272
  };
1142
1273
 
1143
1274
  // packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
1144
- import React13, { useCallback as useCallback5, useMemo as useMemo6, useState as useState4 } from "react";
1145
- import { AST as AST3, Format, S as S2 } from "@dxos/echo-schema";
1275
+ import React13, { useCallback as useCallback7, useMemo as useMemo7, useState as useState5 } from "react";
1276
+ import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
1146
1277
  import { IconButton as IconButton4, useTranslation as useTranslation6 } from "@dxos/react-ui";
1147
1278
  import { List as List2 } from "@dxos/react-ui-list";
1148
1279
  import { ghostHover, inputTextLabel, mx as mx8 } from "@dxos/react-ui-theme";
@@ -1150,20 +1281,20 @@ import { FieldSchema, ViewProjection, VIEW_FIELD_LIMIT } from "@dxos/schema";
1150
1281
  var grid = "grid grid-cols-[32px_1fr_32px] min-bs-[2.5rem]";
1151
1282
  var ViewMetaSchema = S2.Struct({
1152
1283
  name: S2.String.annotations({
1153
- [AST3.TitleAnnotationId]: "View"
1284
+ [AST4.TitleAnnotationId]: "View"
1154
1285
  }),
1155
- typename: Format.DXN.annotations({
1156
- [AST3.TitleAnnotationId]: "Typename"
1286
+ typename: Format.URL.annotations({
1287
+ [AST4.TitleAnnotationId]: "Typename"
1157
1288
  })
1158
1289
  }).pipe(S2.mutable);
1159
1290
  var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onTypenameChanged, onDelete }) => {
1160
1291
  const { t } = useTranslation6(translationKey);
1161
- const projection = useMemo6(() => new ViewProjection(schema, view), [
1292
+ const projection = useMemo7(() => new ViewProjection(schema, view), [
1162
1293
  schema,
1163
1294
  view
1164
1295
  ]);
1165
- const [field, setField] = useState4();
1166
- const viewValues = useMemo6(() => {
1296
+ const [field, setField] = useState5();
1297
+ const viewValues = useMemo7(() => {
1167
1298
  return {
1168
1299
  name: view.name,
1169
1300
  // TODO(burdon): Need to warn user of possible consequences of editing.
@@ -1173,7 +1304,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1173
1304
  }, [
1174
1305
  view
1175
1306
  ]);
1176
- const handleViewUpdate = useCallback5(({ name, typename }) => {
1307
+ const handleViewUpdate = useCallback7(({ name, typename }) => {
1177
1308
  requestAnimationFrame(() => {
1178
1309
  if (view.name !== name) {
1179
1310
  view.name = name;
@@ -1187,16 +1318,16 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1187
1318
  schema,
1188
1319
  onTypenameChanged
1189
1320
  ]);
1190
- const handleSelect = useCallback5((field2) => {
1321
+ const handleSelect = useCallback7((field2) => {
1191
1322
  setField((f) => f === field2 ? void 0 : field2);
1192
1323
  }, []);
1193
- const handleAdd = useCallback5(() => {
1324
+ const handleAdd = useCallback7(() => {
1194
1325
  const field2 = projection.createFieldProjection();
1195
1326
  setField(field2);
1196
1327
  }, [
1197
1328
  view
1198
1329
  ]);
1199
- const handleMove = useCallback5((fromIndex, toIndex) => {
1330
+ const handleMove = useCallback7((fromIndex, toIndex) => {
1200
1331
  const fields = [
1201
1332
  ...view.fields
1202
1333
  ];
@@ -1206,12 +1337,11 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1206
1337
  }, [
1207
1338
  view.fields
1208
1339
  ]);
1209
- const handleClose = useCallback5(() => setField(void 0), []);
1340
+ const handleClose = useCallback7(() => setField(void 0), []);
1210
1341
  return /* @__PURE__ */ React13.createElement("div", {
1211
1342
  role: "none",
1212
1343
  className: mx8("flex flex-col w-full divide-y divide-separator", classNames)
1213
1344
  }, /* @__PURE__ */ React13.createElement(Form, {
1214
- autoFocus: true,
1215
1345
  autoSave: true,
1216
1346
  schema: ViewMetaSchema,
1217
1347
  values: viewValues,
@@ -1262,7 +1392,7 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1262
1392
  };
1263
1393
 
1264
1394
  // packages/ui/react-ui-form/src/util/field.ts
1265
- import { FormatEnum as FormatEnum3, TypeEnum } from "@dxos/echo-schema";
1395
+ import { FormatEnum as FormatEnum3, GeoLocation as GeoLocation2, TypeEnum } from "@dxos/echo-schema";
1266
1396
  var parseValue = ({ type, format, value }) => {
1267
1397
  if (value === void 0 || value === null || value === "") {
1268
1398
  return void 0;
@@ -1316,6 +1446,26 @@ var parseValue = ({ type, format, value }) => {
1316
1446
  const date = new Date(value);
1317
1447
  return isNaN(date.getTime()) ? null : date;
1318
1448
  }
1449
+ case FormatEnum3.LatLong: {
1450
+ if (typeof value === "string") {
1451
+ const cleaned = value.replace(/[^0-9.,-]+/g, "");
1452
+ const parts = cleaned.split(",");
1453
+ if (parts.length === 2) {
1454
+ const lat = parseFloat(parts[0]);
1455
+ const long = parseFloat(parts[1]);
1456
+ if (!isNaN(lat) && !isNaN(long)) {
1457
+ return GeoLocation2.toGeoPoint({
1458
+ latitude: lat,
1459
+ longitude: long
1460
+ });
1461
+ }
1462
+ }
1463
+ }
1464
+ return [
1465
+ 0,
1466
+ 0
1467
+ ];
1468
+ }
1319
1469
  default: {
1320
1470
  return value;
1321
1471
  }
@@ -1374,7 +1524,7 @@ var pathNotUniqueError = (path) => ({
1374
1524
  });
1375
1525
 
1376
1526
  // packages/ui/react-ui-form/src/util/formatting.ts
1377
- import { FormatEnum as FormatEnum4, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
1527
+ import { FormatEnum as FormatEnum4, GeoLocation as GeoLocation3, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
1378
1528
  var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
1379
1529
  const formatScalar = (type2) => {
1380
1530
  switch (type2) {
@@ -1427,6 +1577,16 @@ var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
1427
1577
  return date.toLocaleTimeString(locale);
1428
1578
  }
1429
1579
  }
1580
+ case FormatEnum4.LatLong: {
1581
+ if (value === null || value === void 0) {
1582
+ return "";
1583
+ }
1584
+ if (Array.isArray(value) && value.length >= 2 && value.every(Number.isFinite)) {
1585
+ const { latitude, longitude } = GeoLocation3.fromGeoPoint(value);
1586
+ return `${latitude},${longitude}`;
1587
+ }
1588
+ return String(value);
1589
+ }
1430
1590
  default: {
1431
1591
  if (value === null || value === "undefined") {
1432
1592
  return "";
@@ -1475,6 +1635,16 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
1475
1635
  const date = new Date(value);
1476
1636
  return date.toISOString().split("T")[1].split(".")[0];
1477
1637
  }
1638
+ case FormatEnum4.LatLong: {
1639
+ if (value === null || value === void 0) {
1640
+ return "";
1641
+ }
1642
+ if (Array.isArray(value) && value.length >= 2 && value.every(Number.isFinite)) {
1643
+ const { latitude, longitude } = GeoLocation3.fromGeoPoint(value);
1644
+ return `${latitude},${longitude}`;
1645
+ }
1646
+ return String(value);
1647
+ }
1478
1648
  default: {
1479
1649
  return String(value);
1480
1650
  }
@@ -1488,6 +1658,7 @@ export {
1488
1658
  Form,
1489
1659
  GeoPointInput,
1490
1660
  InputHeader,
1661
+ MarkdownInput,
1491
1662
  NumberInput,
1492
1663
  SelectInput,
1493
1664
  SelectOptionInput,
@@ -1501,6 +1672,7 @@ export {
1501
1672
  parseValue,
1502
1673
  pathNotUniqueError,
1503
1674
  translations_default as translations,
1504
- useForm
1675
+ useForm,
1676
+ useFormValues
1505
1677
  };
1506
1678
  //# sourceMappingURL=index.mjs.map