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