@dxos/react-ui-form 0.8.1-main.ae460ac → 0.8.1-staging.31c3ee1

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 (71) hide show
  1. package/dist/lib/browser/index.mjs +672 -499
  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 +744 -565
  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 +672 -499
  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/ControlSection.d.ts +20 -0
  11. package/dist/types/src/components/ControlSection.d.ts.map +1 -0
  12. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +2 -1
  13. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/ArrayField.d.ts +2 -1
  15. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/Defaults.d.ts +6 -5
  17. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/Form.d.ts +2 -2
  19. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/Form.stories.d.ts +9 -9
  21. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  22. package/dist/types/src/components/Form/FormActions.d.ts +2 -1
  23. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
  24. package/dist/types/src/components/Form/FormContent.d.ts +3 -2
  25. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  26. package/dist/types/src/components/Form/FormContext.d.ts +1 -1
  27. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/Input.d.ts +3 -3
  29. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  30. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +2 -1
  31. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
  32. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +2 -1
  33. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
  34. package/dist/types/src/components/Form/custom/TupleInput.d.ts +2 -1
  35. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
  36. package/dist/types/src/components/Form/index.d.ts +1 -2
  37. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  38. package/dist/types/src/components/ViewEditor/Test.stories.d.ts +2 -1
  39. package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
  40. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +6 -5
  41. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  42. package/dist/types/src/components/deprecated/Form.d.ts +2 -1
  43. package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
  44. package/dist/types/src/components/deprecated/Form.stories.d.ts +2 -1
  45. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
  46. package/dist/types/src/components/deprecated/FormInput.d.ts +2 -2
  47. package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
  48. package/dist/types/src/components/index.d.ts +1 -0
  49. package/dist/types/src/components/index.d.ts.map +1 -1
  50. package/dist/types/src/components/testing.d.ts +3 -3
  51. package/dist/types/src/components/testing.d.ts.map +1 -1
  52. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  53. package/dist/types/src/translations.d.ts +1 -0
  54. package/dist/types/src/translations.d.ts.map +1 -1
  55. package/dist/types/src/util/field.d.ts +0 -1
  56. package/dist/types/src/util/field.d.ts.map +1 -1
  57. package/dist/types/src/util/formatting.d.ts.map +1 -1
  58. package/package.json +26 -26
  59. package/src/components/ControlSection.tsx +113 -0
  60. package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
  61. package/src/components/Form/Form.stories.tsx +2 -2
  62. package/src/components/Form/Input.tsx +1 -1
  63. package/src/components/Form/custom/SelectOptionsInput.tsx +5 -1
  64. package/src/components/Form/index.ts +1 -3
  65. package/src/components/ViewEditor/ViewEditor.stories.tsx +1 -1
  66. package/src/components/ViewEditor/ViewEditor.tsx +105 -20
  67. package/src/components/index.ts +1 -1
  68. package/src/hooks/useForm.ts +10 -1
  69. package/src/translations.ts +1 -0
  70. package/src/util/field.ts +4 -4
  71. package/src/util/formatting.ts +9 -10
@@ -7,6 +7,7 @@ var translations_default = [
7
7
  "en-US": {
8
8
  [translationKey]: {
9
9
  "fields label": "Fields",
10
+ "hidden fields label": "Hidden Fields",
10
11
  // TODO(burdon): Standardize field/property.
11
12
  "button add property": "Add property",
12
13
  "field property label": "Property",
@@ -100,289 +101,81 @@ var DeprecatedFormInput = ({ label, description, secondary, children }) => {
100
101
  return primary;
101
102
  };
102
103
 
103
- // packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
104
- import React12, { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo7, useState as useState4 } from "react";
105
- import { FormatEnum as FormatEnum2, FormatEnums, formatToType } from "@dxos/echo-schema";
106
- import { log as log2 } from "@dxos/log";
107
- import { useTranslation as useTranslation5 } from "@dxos/react-ui";
108
- import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
109
-
110
- // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
111
- import React2, { createContext, useContext, useEffect as useEffect2, useMemo as useMemo2 } 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 { SchemaAST as AST } from "effect";
118
- import { useCallback, useEffect, useMemo, useState } from "react";
119
- import { getValue, setValue } from "@dxos/echo-schema";
120
- import { createJsonPath, fromEffectValidationPath } from "@dxos/effect";
121
- import { invariant } from "@dxos/invariant";
122
- import { log } from "@dxos/log";
123
- import { validateSchema } from "@dxos/schema";
124
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
125
- var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
126
- invariant(AST.isTypeLiteral(schema.ast), void 0, {
127
- F: __dxlog_file,
128
- L: 93,
129
- S: void 0,
130
- A: [
131
- "AST.isTypeLiteral(schema.ast)",
132
- ""
133
- ]
134
- });
135
- const [values, setValues] = useState(initialValues);
136
- useEffect(() => {
137
- setValues(initialValues);
138
- }, [
139
- initialValues
140
- ]);
141
- const [touched, setTouched] = useState(createKeySet(initialValues, false));
142
- const [changed, setChanged] = useState(createKeySet(initialValues, false));
143
- const [errors, setErrors] = useState({});
144
- const [saving, setSaving] = useState(false);
145
- const validate = useCallback((values2) => {
146
- let errors2 = validateSchema(schema, values2) ?? [];
147
- if (errors2.length === 0 && onValidate) {
148
- const validatedValues = values2;
149
- errors2 = onValidate(validatedValues) ?? [];
150
- }
151
- setErrors(flatMap(errors2));
152
- const valid = errors2.length === 0;
153
- return valid;
154
- }, [
155
- schema,
156
- onValidate
157
- ]);
158
- const [schemaDirty, setSchemaDirty] = useState(false);
159
- useEffect(() => setSchemaDirty(true), [
160
- schema
161
- ]);
162
- useEffect(() => {
163
- if (schemaDirty) {
164
- validate(values);
165
- setSchemaDirty(false);
166
- }
167
- }, [
168
- values,
169
- schemaDirty
170
- ]);
171
- const formIsValid = useMemo(() => {
172
- return Object.keys(errors).length === 0;
173
- }, [
174
- errors
175
- ]);
176
- const canSave = useMemo(() => !saving && // Check if any error paths that are touched have errors.
177
- !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
178
- touched,
179
- errors,
180
- saving
181
- ]);
182
- const handleSave = useCallback(async () => {
183
- if (validate(values)) {
184
- setSaving(true);
185
- try {
186
- await onSave?.(values, {
187
- changed
188
- });
189
- } finally {
190
- setSaving(false);
191
- }
192
- }
193
- }, [
194
- values,
195
- validate,
196
- onSave,
197
- changed
198
- ]);
199
- const getStatus = useCallback((path) => {
200
- const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
201
- const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
202
- return {
203
- status: matchingError ? "error" : void 0,
204
- error: matchingError ? matchingError[1] : void 0
205
- };
206
- }, [
207
- errors
208
- ]);
209
- const getFormValue = useCallback((path) => {
210
- return getValue(values, createJsonPath(path));
211
- }, [
212
- values
213
- ]);
214
- const onValueChange = useCallback((path, type, value) => {
215
- const jsonPath = createJsonPath(path);
216
- let parsedValue = value;
217
- try {
218
- if (type === "number") {
219
- parsedValue = parseFloat(value) || 0;
220
- }
221
- } catch (err) {
222
- log.catch(err, void 0, {
223
- F: __dxlog_file,
224
- L: 203,
225
- S: void 0,
226
- C: (f, a) => f(...a)
227
- });
228
- parsedValue = void 0;
229
- }
230
- const newValues = {
231
- ...setValue(values, jsonPath, parsedValue)
232
- };
233
- setValues(newValues);
234
- setChanged((prev) => ({
235
- ...prev,
236
- [jsonPath]: true
237
- }));
238
- onValuesChanged?.(newValues);
239
- const isValid = validate(newValues);
240
- if (isValid && onValid) {
241
- onValid(newValues, {
242
- changed
243
- });
244
- }
245
- }, [
246
- values,
247
- onValuesChanged,
248
- validate,
249
- onValid,
250
- changed
251
- ]);
252
- const onTouched = useCallback((path) => {
253
- const jsonPath = createJsonPath(path);
254
- setTouched((touched2) => ({
255
- ...touched2,
256
- [jsonPath]: true
257
- }));
258
- validate(values);
259
- }, [
260
- validate,
261
- values
262
- ]);
263
- return useMemo(() => ({
264
- // State.
265
- values,
266
- errors,
267
- touched,
268
- changed,
269
- canSave,
270
- formIsValid,
271
- // Actions.
272
- handleSave,
273
- // Field utils.
274
- getStatus,
275
- getValue: getFormValue,
276
- onValueChange,
277
- onTouched
278
- }), [
279
- values,
280
- errors,
281
- touched,
282
- changed,
283
- canSave,
284
- formIsValid,
285
- handleSave,
286
- getStatus,
287
- getFormValue,
288
- onValueChange,
289
- onTouched
290
- ]);
291
- };
292
- var createKeySet = (obj, value) => {
293
- invariant(obj, void 0, {
294
- F: __dxlog_file,
295
- L: 265,
296
- S: void 0,
297
- A: [
298
- "obj",
299
- ""
300
- ]
301
- });
302
- return Object.keys(obj).reduce((acc, key) => ({
303
- ...acc,
304
- [key]: value
305
- }), {});
306
- };
307
- var flatMap = (errors) => {
308
- return errors.reduce((result, { path, message }) => {
309
- const jsonPath = fromEffectValidationPath(path);
310
- if (!(jsonPath in result)) {
311
- result[jsonPath] = message;
312
- }
313
- return result;
314
- }, {});
104
+ // packages/ui/react-ui-form/src/components/ControlSection.tsx
105
+ import React2 from "react";
106
+ import { toLocalizedString, useTranslation, Input as Input2 } from "@dxos/react-ui";
107
+ var ControlSectionHeading = ({ title, description }) => {
108
+ const { t } = useTranslation(translationKey);
109
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("h2", {
110
+ className: "pli-4 container-max-width text-xl mbs-6 mbe-4"
111
+ }, toLocalizedString(title, t)), description && /* @__PURE__ */ React2.createElement("p", {
112
+ className: "pli-4 mlb-4 container-max-width text-description"
113
+ }, toLocalizedString(description, t)));
315
114
  };
316
-
317
- // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
318
- var FormContext = /* @__PURE__ */ createContext(void 0);
319
- var useFormContext = () => {
320
- return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
115
+ var ControlSection = ({ title, description, children }) => {
116
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(ControlSectionHeading, {
117
+ title,
118
+ description
119
+ }), children);
321
120
  };
322
- var useFormValues = (path = []) => {
323
- const { values: formValues } = useFormContext();
324
- const jsonPath = createJsonPath2(path);
325
- return getValue2(formValues, jsonPath);
121
+ var ControlGroup = ({ children }) => /* @__PURE__ */ React2.createElement("div", {
122
+ role: "none",
123
+ className: "group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-4"
124
+ }, children);
125
+ var ControlFrame = ({ children }) => /* @__PURE__ */ React2.createElement("div", {
126
+ role: "none",
127
+ className: "p-4 border border-separator rounded-lg container-max-width grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-2 md:gap-8"
128
+ }, children);
129
+ var controlItemClasses = "p-4 border border-separator rounded-lg container-max-width grid md:col-span-2 grid-cols-subgrid items-center";
130
+ var controlItemTitleClasses = "text-lg font-normal mbe-2";
131
+ var controlItemDescriptionClasses = "text-base mlb-2 md:mbe-0 text-description";
132
+ var ControlItem = ({ title, description, children }) => {
133
+ const { t } = useTranslation(translationKey);
134
+ return /* @__PURE__ */ React2.createElement("div", {
135
+ className: controlItemClasses
136
+ }, /* @__PURE__ */ React2.createElement("div", {
137
+ role: "none"
138
+ }, /* @__PURE__ */ React2.createElement("h3", {
139
+ className: controlItemTitleClasses
140
+ }, toLocalizedString(title, t)), description && /* @__PURE__ */ React2.createElement("p", {
141
+ className: controlItemDescriptionClasses
142
+ }, toLocalizedString(description, t))), children);
326
143
  };
327
- var useInputProps = (path = []) => {
328
- const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
329
- const stablePath = useMemo2(() => path, [
330
- Array.isArray(path) ? path.join(".") : path
331
- ]);
332
- return useMemo2(() => ({
333
- getStatus: () => getStatus(stablePath),
334
- getValue: () => getFormValue(stablePath),
335
- onValueChange: (type, value) => onValueChange(stablePath, type, value),
336
- onBlur: () => onTouched(stablePath)
337
- }), [
338
- getStatus,
339
- getFormValue,
340
- onValueChange,
341
- onTouched,
342
- stablePath
343
- ]);
144
+ var ControlItemInput = ({ title, description, children }) => {
145
+ const { t } = useTranslation(translationKey);
146
+ return /* @__PURE__ */ React2.createElement(Input2.Root, null, /* @__PURE__ */ React2.createElement("div", {
147
+ className: controlItemClasses
148
+ }, /* @__PURE__ */ React2.createElement("div", {
149
+ role: "none"
150
+ }, /* @__PURE__ */ React2.createElement(Input2.Label, {
151
+ classNames: controlItemTitleClasses
152
+ }, toLocalizedString(title, t)), description && /* @__PURE__ */ React2.createElement(Input2.DescriptionAndValidation, null, /* @__PURE__ */ React2.createElement(Input2.Description, {
153
+ classNames: controlItemDescriptionClasses
154
+ }, toLocalizedString(description, t)))), children));
344
155
  };
345
- var FormProvider = ({ children, formRef, autoSave, ...formOptions }) => {
346
- const form = useForm(formOptions);
347
- useEffect2(() => {
348
- if (!formRef?.current) {
349
- return;
350
- }
351
- const handleKeyDown = (event) => {
352
- const keyIsEnter = event.key === "Enter";
353
- const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
354
- const inputIsTextarea = event.target.tagName.toLowerCase() === "textarea";
355
- const inputOptOut = event.target.hasAttribute("data-no-submit");
356
- const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
357
- const shouldSubmitTextarea = inputIsTextarea && keyIsEnter && event.metaKey;
358
- if ((shouldSubmitRegularInput || shouldSubmitTextarea) && !inputOptOut) {
359
- if (!autoSave && form.canSave) {
360
- form.handleSave();
361
- }
362
- if (autoSave && form.formIsValid) {
363
- event.target.blur();
364
- }
365
- }
366
- };
367
- const formElement = formRef.current;
368
- formElement.addEventListener("keydown", handleKeyDown);
369
- return () => formElement.removeEventListener("keydown", handleKeyDown);
370
- }, [
371
- formRef,
372
- form.canSave,
373
- form.formIsValid,
374
- form.handleSave,
375
- autoSave
376
- ]);
377
- return /* @__PURE__ */ React2.createElement(FormContext.Provider, {
378
- value: form
379
- }, children);
156
+ var ControlFrameItem = ({ title, description, children }) => {
157
+ const { t } = useTranslation(translationKey);
158
+ return /* @__PURE__ */ React2.createElement("div", {
159
+ role: "group",
160
+ className: "min-is-0"
161
+ }, /* @__PURE__ */ React2.createElement("h3", {
162
+ className: "text-lg mbe-2"
163
+ }, toLocalizedString(title, t)), description && /* @__PURE__ */ React2.createElement("p", {
164
+ className: "mlb-2 md:mbe-0 text-description"
165
+ }, toLocalizedString(description, t)), children);
380
166
  };
381
167
 
168
+ // packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
169
+ import React13, { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo7, useState as useState4 } from "react";
170
+ import { FormatEnum as FormatEnum2, FormatEnums, formatToType } from "@dxos/echo-schema";
171
+ import { log as log2 } from "@dxos/log";
172
+ import { useTranslation as useTranslation6 } from "@dxos/react-ui";
173
+ import { getFormatSchema, getSchemaProperties as getSchemaProperties3, sortProperties } from "@dxos/schema";
174
+
382
175
  // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
383
- import React4, { useCallback as useCallback2, useState as useState2, useEffect as useEffect3, useMemo as useMemo3 } from "react";
176
+ import React4, { useCallback, useState, useEffect, useMemo } from "react";
384
177
  import { GeoLocation } from "@dxos/echo-schema";
385
- import { Input as Input2, useTranslation } from "@dxos/react-ui";
178
+ import { Input as Input3, useTranslation as useTranslation2 } from "@dxos/react-ui";
386
179
  import { safeParseFloat } from "@dxos/util";
387
180
 
388
181
  // packages/ui/react-ui-form/src/components/Form/Input.tsx
@@ -406,27 +199,27 @@ var InputHeader = ({ children, error }) => {
406
199
 
407
200
  // packages/ui/react-ui-form/src/components/Form/custom/GeoPointInput.tsx
408
201
  var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
409
- const { t } = useTranslation(translationKey);
202
+ const { t } = useTranslation2(translationKey);
410
203
  const { status, error } = getStatus();
411
- const geoPoint = useMemo3(() => getValue3() ?? [
204
+ const geoPoint = useMemo(() => getValue3() ?? [
412
205
  0,
413
206
  0
414
207
  ], [
415
208
  getValue3
416
209
  ]);
417
- const geoLocation = useMemo3(() => GeoLocation.fromGeoPoint(geoPoint), [
210
+ const geoLocation = useMemo(() => GeoLocation.fromGeoPoint(geoPoint), [
418
211
  geoPoint
419
212
  ]);
420
- const [longitudeText, setLongitudeText] = useState2(geoLocation.longitude?.toString());
421
- const [latitudeText, setLatitudeText] = useState2(geoLocation.latitude?.toString());
422
- useEffect3(() => {
213
+ const [longitudeText, setLongitudeText] = useState(geoLocation.longitude?.toString());
214
+ const [latitudeText, setLatitudeText] = useState(geoLocation.latitude?.toString());
215
+ useEffect(() => {
423
216
  const location = GeoLocation.fromGeoPoint(geoPoint);
424
217
  setLongitudeText(location.longitude.toString());
425
218
  setLatitudeText(location.latitude.toString());
426
219
  }, [
427
220
  geoPoint
428
221
  ]);
429
- const handleCoordinateChange = useCallback2((coordinateType, setText) => (event) => {
222
+ const handleCoordinateChange = useCallback((coordinateType, setText) => (event) => {
430
223
  const inputText = event.target.value;
431
224
  setText(inputText);
432
225
  if (inputText !== "" && inputText !== "-") {
@@ -449,14 +242,14 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
449
242
  onValueChange,
450
243
  type
451
244
  ]);
452
- return /* @__PURE__ */ React4.createElement(Input2.Root, {
245
+ return /* @__PURE__ */ React4.createElement(Input3.Root, {
453
246
  validationValence: status
454
247
  }, /* @__PURE__ */ React4.createElement(InputHeader, {
455
248
  error
456
- }, /* @__PURE__ */ React4.createElement(Input2.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
249
+ }, /* @__PURE__ */ React4.createElement(Input3.Label, null, label)), /* @__PURE__ */ React4.createElement("div", {
457
250
  role: "none",
458
251
  className: "grid grid-cols-2 gap-2"
459
- }, /* @__PURE__ */ React4.createElement(Input2.TextInput, {
252
+ }, /* @__PURE__ */ React4.createElement(Input3.TextInput, {
460
253
  type: "text",
461
254
  pattern: "^-?[0-9]*\\.?[0-9]*$",
462
255
  disabled,
@@ -464,7 +257,7 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
464
257
  value: latitudeText,
465
258
  onChange: handleCoordinateChange("latitude", setLatitudeText),
466
259
  onBlur
467
- }), /* @__PURE__ */ React4.createElement(Input2.TextInput, {
260
+ }), /* @__PURE__ */ React4.createElement(Input3.TextInput, {
468
261
  type: "text",
469
262
  pattern: "^-?[0-9]*\\.?[0-9]*$",
470
263
  disabled,
@@ -476,20 +269,20 @@ var GeoPointInput = ({ type, label, disabled, getStatus, getValue: getValue3, on
476
269
  };
477
270
 
478
271
  // packages/ui/react-ui-form/src/components/Form/custom/SelectOptionsInput.tsx
479
- import React5, { useCallback as useCallback3, useEffect as useEffect4, useState as useState3, useRef } from "react";
272
+ import React5, { useCallback as useCallback2, useEffect as useEffect2, useState as useState2, useRef } from "react";
480
273
  import { PublicKey } from "@dxos/keys";
481
- import { Icon as Icon2, IconButton, Input as Input3, Tag, Toolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
274
+ import { Icon as Icon2, IconButton, Input as Input4, Tag, Toolbar, useTranslation as useTranslation3 } from "@dxos/react-ui";
482
275
  import { List } from "@dxos/react-ui-list";
483
276
  import { HuePicker } from "@dxos/react-ui-pickers";
484
277
  import { hues, mx as mx2 } from "@dxos/react-ui-theme";
485
278
  var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3, onValueChange }) => {
486
- const [selected, setSelectedId] = useState3(null);
487
- const [isNewOption, setIsNewOption] = useState3(false);
279
+ const [selected, setSelectedId] = useState2(null);
280
+ const [isNewOption, setIsNewOption] = useState2(false);
488
281
  const inputRef = useRef(null);
489
- const { t } = useTranslation2(translationKey);
282
+ const { t } = useTranslation3(translationKey);
490
283
  const { status, error } = getStatus();
491
284
  const options = getValue3();
492
- useEffect4(() => {
285
+ useEffect2(() => {
493
286
  if (options === void 0) {
494
287
  onValueChange(type, []);
495
288
  }
@@ -508,7 +301,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
508
301
  const availableHues = hues.filter((hue) => !lastThree.has(hue));
509
302
  return availableHues[Math.floor(Math.random() * availableHues.length)] ?? hues[Math.floor(Math.random() * hues.length)];
510
303
  };
511
- const handleAdd = useCallback3(() => {
304
+ const handleAdd = useCallback2(() => {
512
305
  const newId = PublicKey.random().truncate();
513
306
  const newOption = {
514
307
  id: newId,
@@ -526,7 +319,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
526
319
  type,
527
320
  onValueChange
528
321
  ]);
529
- const handleDelete = useCallback3((id) => {
322
+ const handleDelete = useCallback2((id) => {
530
323
  const newOptions = options?.filter((option) => option.id !== id) ?? [];
531
324
  onValueChange(type, newOptions);
532
325
  setSelectedId(null);
@@ -535,7 +328,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
535
328
  type,
536
329
  onValueChange
537
330
  ]);
538
- const handleMove = useCallback3((from, to) => {
331
+ const handleMove = useCallback2((from, to) => {
539
332
  if (!options) {
540
333
  return;
541
334
  }
@@ -550,10 +343,10 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
550
343
  type,
551
344
  onValueChange
552
345
  ]);
553
- const handleClick = useCallback3((id) => {
346
+ const handleClick = useCallback2((id) => {
554
347
  setSelectedId((current) => current === id ? null : id);
555
348
  }, []);
556
- const handleTitleChange = useCallback3((id) => (e) => {
349
+ const handleTitleChange = useCallback2((id) => (e) => {
557
350
  const newOptions = options?.map((o) => o.id === id ? {
558
351
  ...o,
559
352
  title: e.target.value
@@ -564,12 +357,12 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
564
357
  type,
565
358
  onValueChange
566
359
  ]);
567
- const handleKeyDown = useCallback3((e) => {
360
+ const handleKeyDown = useCallback2((e) => {
568
361
  if (e.key === "Enter") {
569
362
  setSelectedId(null);
570
363
  }
571
364
  }, []);
572
- const handleColorChange = useCallback3((id) => (hue) => {
365
+ const handleColorChange = useCallback2((id) => (hue) => {
573
366
  const newOptions = options?.map((o) => o.id === id ? {
574
367
  ...o,
575
368
  color: hue
@@ -580,7 +373,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
580
373
  type,
581
374
  onValueChange
582
375
  ]);
583
- useEffect4(() => {
376
+ useEffect2(() => {
584
377
  if (selected && isNewOption && inputRef.current) {
585
378
  inputRef.current.focus();
586
379
  setIsNewOption(false);
@@ -589,11 +382,11 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
589
382
  selected,
590
383
  isNewOption
591
384
  ]);
592
- return /* @__PURE__ */ React5.createElement(Input3.Root, {
385
+ return /* @__PURE__ */ React5.createElement(Input4.Root, {
593
386
  validationValence: status
594
387
  }, /* @__PURE__ */ React5.createElement(InputHeader, {
595
388
  error
596
- }, /* @__PURE__ */ React5.createElement(Input3.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
389
+ }, /* @__PURE__ */ React5.createElement(Input4.Label, null, label)), /* @__PURE__ */ React5.createElement("div", {
597
390
  role: "none"
598
391
  }, options && /* @__PURE__ */ React5.createElement(List.Root, {
599
392
  items: options,
@@ -621,11 +414,11 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
621
414
  onClick: () => handleClick(item.id)
622
415
  })), selected === item.id && /* @__PURE__ */ React5.createElement("div", {
623
416
  className: "mis-4 mbs-2 flex flex-col gap-1"
624
- }, /* @__PURE__ */ React5.createElement(Input3.Label, {
417
+ }, /* @__PURE__ */ React5.createElement(Input4.Label, {
625
418
  classNames: "text-xs"
626
419
  }, t("select option label")), /* @__PURE__ */ React5.createElement("div", {
627
420
  className: "flex flex-row items-center gap-1"
628
- }, /* @__PURE__ */ React5.createElement(Input3.TextInput, {
421
+ }, /* @__PURE__ */ React5.createElement(Input4.TextInput, {
629
422
  placeholder: t("select option label placeholder"),
630
423
  ref: selected === item.id ? inputRef : void 0,
631
424
  value: item.title,
@@ -637,7 +430,8 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
637
430
  classNames: "p-0 m-0 !is-auto"
638
431
  }, /* @__PURE__ */ React5.createElement(HuePicker, {
639
432
  value: item.color,
640
- onChange: handleColorChange(item.id)
433
+ onChange: handleColorChange(item.id),
434
+ rootVariant: "toolbar-button"
641
435
  })), /* @__PURE__ */ React5.createElement(IconButton, {
642
436
  icon: "ph--trash--fill",
643
437
  iconOnly: true,
@@ -660,7 +454,7 @@ var SelectOptionInput = ({ type, label, disabled, getStatus, getValue: getValue3
660
454
 
661
455
  // packages/ui/react-ui-form/src/components/Form/custom/TupleInput.tsx
662
456
  import React6 from "react";
663
- import { Input as Input4 } from "@dxos/react-ui";
457
+ import { Input as Input5 } from "@dxos/react-ui";
664
458
  import { mx as mx3 } from "@dxos/react-ui-theme";
665
459
  import { safeParseFloat as safeParseFloat2 } from "@dxos/util";
666
460
  var gridCols = [
@@ -672,13 +466,13 @@ var gridCols = [
672
466
  var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
673
467
  const { status, error } = getStatus();
674
468
  const values = getValue3() ?? {};
675
- return /* @__PURE__ */ React6.createElement(Input4.Root, {
469
+ return /* @__PURE__ */ React6.createElement(Input5.Root, {
676
470
  validationValence: status
677
471
  }, /* @__PURE__ */ React6.createElement(InputHeader, {
678
472
  error
679
- }, /* @__PURE__ */ React6.createElement(Input4.Label, null, label)), /* @__PURE__ */ React6.createElement("div", {
473
+ }, /* @__PURE__ */ React6.createElement(Input5.Label, null, label)), /* @__PURE__ */ React6.createElement("div", {
680
474
  className: mx3("grid gap-2", gridCols[binding.length - 1])
681
- }, binding.map((prop) => /* @__PURE__ */ React6.createElement(Input4.TextInput, {
475
+ }, binding.map((prop) => /* @__PURE__ */ React6.createElement(Input5.TextInput, {
682
476
  key: prop,
683
477
  type: "number",
684
478
  disabled,
@@ -690,135 +484,417 @@ var TupleInput = ({ binding, type, label, disabled, getStatus, getValue: getValu
690
484
  onBlur
691
485
  }))));
692
486
  };
693
-
694
- // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
695
- import React7, { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef2 } from "react";
696
- import { Input as Input5, Select } from "@dxos/react-ui";
697
- var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
698
- const { status, error } = getStatus();
699
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
700
- validationValence: status
701
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
702
- error
703
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
704
- disabled,
705
- placeholder,
706
- value: getValue3() ?? "",
707
- onChange: (event) => onValueChange(type, event.target.value),
708
- onBlur
709
- }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
487
+
488
+ // packages/ui/react-ui-form/src/components/Form/Defaults.tsx
489
+ import React7, { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef2 } from "react";
490
+ import { Input as Input6, Select } from "@dxos/react-ui";
491
+ var TextInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
492
+ const { status, error } = getStatus();
493
+ return /* @__PURE__ */ React7.createElement(Input6.Root, {
494
+ validationValence: status
495
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
496
+ error
497
+ }, /* @__PURE__ */ React7.createElement(Input6.Label, null, label)), /* @__PURE__ */ React7.createElement(Input6.TextInput, {
498
+ disabled,
499
+ placeholder,
500
+ value: getValue3() ?? "",
501
+ onChange: (event) => onValueChange(type, event.target.value),
502
+ onBlur
503
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input6.Validation, null, error));
504
+ };
505
+ var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
506
+ const { status, error } = getStatus();
507
+ return /* @__PURE__ */ React7.createElement(Input6.Root, {
508
+ validationValence: status
509
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
510
+ error
511
+ }, /* @__PURE__ */ React7.createElement(Input6.Label, null, label)), /* @__PURE__ */ React7.createElement(Input6.TextInput, {
512
+ type: "number",
513
+ disabled,
514
+ placeholder,
515
+ value: getValue3() ?? 0,
516
+ onChange: (event) => onValueChange(type, event.target.value),
517
+ onBlur
518
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input6.DescriptionAndValidation, null, error));
519
+ };
520
+ var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
521
+ const { status, error } = getStatus();
522
+ const checked = Boolean(getValue3());
523
+ return /* @__PURE__ */ React7.createElement(Input6.Root, {
524
+ validationValence: status
525
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
526
+ error
527
+ }, /* @__PURE__ */ React7.createElement(Input6.Label, null, label)), /* @__PURE__ */ React7.createElement(Input6.Switch, {
528
+ checked,
529
+ onCheckedChange: (value) => onValueChange(type, value)
530
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input6.DescriptionAndValidation, null, error));
531
+ };
532
+ var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
533
+ const { status, error } = getStatus();
534
+ return /* @__PURE__ */ React7.createElement(Input6.Root, {
535
+ validationValence: status
536
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
537
+ error
538
+ }, /* @__PURE__ */ React7.createElement(Input6.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
539
+ value: getValue3(),
540
+ onValueChange: (value) => onValueChange(type, value)
541
+ }, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
542
+ classNames: "is-full",
543
+ disabled,
544
+ placeholder
545
+ }), /* @__PURE__ */ React7.createElement(Select.Portal, null, /* @__PURE__ */ React7.createElement(Select.Content, null, /* @__PURE__ */ React7.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
546
+ // NOTE: Numeric values are converted to and from strings.
547
+ /* @__PURE__ */ React7.createElement(Select.Option, {
548
+ key: String(value),
549
+ value: String(value)
550
+ }, label2 ?? String(value))
551
+ )))))), inputOnly && /* @__PURE__ */ React7.createElement(Input6.DescriptionAndValidation, null, error));
552
+ };
553
+ var MarkdownInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
554
+ const { status, error } = getStatus();
555
+ const textareaRef = useRef2(null);
556
+ const adjustHeight = useCallback3(() => {
557
+ const textarea = textareaRef.current;
558
+ if (textarea) {
559
+ textarea.style.blockSize = "auto";
560
+ textarea.style.blockSize = `${textarea.scrollHeight + 2}px`;
561
+ }
562
+ }, []);
563
+ useEffect3(() => {
564
+ adjustHeight();
565
+ }, [
566
+ adjustHeight
567
+ ]);
568
+ useEffect3(() => {
569
+ adjustHeight();
570
+ }, [
571
+ getValue3(),
572
+ adjustHeight
573
+ ]);
574
+ return /* @__PURE__ */ React7.createElement(Input6.Root, {
575
+ validationValence: status
576
+ }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
577
+ error
578
+ }, /* @__PURE__ */ React7.createElement(Input6.Label, null, label)), /* @__PURE__ */ React7.createElement(Input6.TextArea, {
579
+ ref: textareaRef,
580
+ disabled,
581
+ placeholder,
582
+ value: getValue3() ?? "",
583
+ classNames: "min-bs-auto max-h-40 overflow-auto",
584
+ onChange: (event) => onValueChange(type, event.target.value),
585
+ onBlur,
586
+ style: {
587
+ resize: "none"
588
+ },
589
+ spellCheck: false
590
+ }), inputOnly && /* @__PURE__ */ React7.createElement(Input6.Validation, null, error));
591
+ };
592
+
593
+ // packages/ui/react-ui-form/src/components/Form/Form.tsx
594
+ import React12, { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef3 } from "react";
595
+ import { mx as mx7 } from "@dxos/react-ui-theme";
596
+
597
+ // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
598
+ import React9 from "react";
599
+ import { IconButton as IconButton2, useTranslation as useTranslation4 } from "@dxos/react-ui";
600
+ import { mx as mx4 } from "@dxos/react-ui-theme";
601
+
602
+ // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
603
+ import React8, { createContext, useContext, useEffect as useEffect5, useMemo as useMemo3 } from "react";
604
+ import { raise } from "@dxos/debug";
605
+ import { getValue as getValue2 } from "@dxos/echo-schema";
606
+ import { createJsonPath as createJsonPath2 } from "@dxos/effect";
607
+
608
+ // packages/ui/react-ui-form/src/hooks/useForm.ts
609
+ import { SchemaAST as AST } from "effect";
610
+ import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo2, useState as useState3 } from "react";
611
+ import { getValue, setValue } from "@dxos/echo-schema";
612
+ import { createJsonPath, fromEffectValidationPath } from "@dxos/effect";
613
+ import { invariant } from "@dxos/invariant";
614
+ import { log } from "@dxos/log";
615
+ import { validateSchema } from "@dxos/schema";
616
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/hooks/useForm.ts";
617
+ var useForm = ({ schema, initialValues, onValuesChanged, onValidate, onValid, onSave }) => {
618
+ invariant(AST.isTypeLiteral(schema.ast), void 0, {
619
+ F: __dxlog_file,
620
+ L: 93,
621
+ S: void 0,
622
+ A: [
623
+ "AST.isTypeLiteral(schema.ast)",
624
+ ""
625
+ ]
626
+ });
627
+ const [values, setValues] = useState3(initialValues);
628
+ useEffect4(() => {
629
+ setValues(initialValues);
630
+ }, [
631
+ initialValues
632
+ ]);
633
+ const [touched, setTouched] = useState3(createKeySet(initialValues, false));
634
+ const [changed, setChanged] = useState3(createKeySet(initialValues, false));
635
+ const [errors, setErrors] = useState3({});
636
+ const [saving, setSaving] = useState3(false);
637
+ const validate = useCallback4((values2) => {
638
+ let errors2 = validateSchema(schema, values2) ?? [];
639
+ if (errors2.length === 0 && onValidate) {
640
+ const validatedValues = values2;
641
+ errors2 = onValidate(validatedValues) ?? [];
642
+ }
643
+ setErrors(flatMap(errors2));
644
+ const valid = errors2.length === 0;
645
+ return valid;
646
+ }, [
647
+ schema,
648
+ onValidate
649
+ ]);
650
+ const [schemaDirty, setSchemaDirty] = useState3(false);
651
+ useEffect4(() => setSchemaDirty(true), [
652
+ schema
653
+ ]);
654
+ useEffect4(() => {
655
+ if (schemaDirty) {
656
+ validate(values);
657
+ setSchemaDirty(false);
658
+ }
659
+ }, [
660
+ values,
661
+ schemaDirty
662
+ ]);
663
+ const formIsValid = useMemo2(() => {
664
+ return Object.keys(errors).length === 0;
665
+ }, [
666
+ errors
667
+ ]);
668
+ const canSave = useMemo2(() => !saving && // Check if any error paths that are touched have errors.
669
+ !Object.entries(touched).some(([path, isTouched]) => isTouched && Object.keys(errors).some((errorPath) => errorPath === path || errorPath.startsWith(`${path}.`) || errorPath.startsWith(`${path}[`))), [
670
+ touched,
671
+ errors,
672
+ saving
673
+ ]);
674
+ const handleSave = useCallback4(async () => {
675
+ if (validate(values)) {
676
+ setSaving(true);
677
+ try {
678
+ await onSave?.(values, {
679
+ changed
680
+ });
681
+ } finally {
682
+ setSaving(false);
683
+ }
684
+ }
685
+ }, [
686
+ values,
687
+ validate,
688
+ onSave,
689
+ changed
690
+ ]);
691
+ const getStatus = useCallback4((path) => {
692
+ const jsonPath = Array.isArray(path) ? createJsonPath(path) : path;
693
+ const matchingError = Object.entries(errors).find(([errorPath]) => errorPath === jsonPath || errorPath.startsWith(`${jsonPath}.`) || errorPath.startsWith(`${jsonPath}[`));
694
+ const isTouched = touched[jsonPath];
695
+ if (!isTouched) {
696
+ return {
697
+ status: void 0,
698
+ error: void 0
699
+ };
700
+ }
701
+ return {
702
+ status: matchingError ? "error" : void 0,
703
+ error: matchingError ? matchingError[1] : void 0
704
+ };
705
+ }, [
706
+ errors,
707
+ touched
708
+ ]);
709
+ const getFormValue = useCallback4((path) => {
710
+ return getValue(values, createJsonPath(path));
711
+ }, [
712
+ values
713
+ ]);
714
+ const onValueChange = useCallback4((path, type, value) => {
715
+ const jsonPath = createJsonPath(path);
716
+ let parsedValue = value;
717
+ try {
718
+ if (type === "number") {
719
+ parsedValue = parseFloat(value) || 0;
720
+ }
721
+ } catch (err) {
722
+ log.catch(err, void 0, {
723
+ F: __dxlog_file,
724
+ L: 212,
725
+ S: void 0,
726
+ C: (f, a) => f(...a)
727
+ });
728
+ parsedValue = void 0;
729
+ }
730
+ const newValues = {
731
+ ...setValue(values, jsonPath, parsedValue)
732
+ };
733
+ setValues(newValues);
734
+ setChanged((prev) => ({
735
+ ...prev,
736
+ [jsonPath]: true
737
+ }));
738
+ onValuesChanged?.(newValues);
739
+ const isValid = validate(newValues);
740
+ if (isValid && onValid) {
741
+ onValid(newValues, {
742
+ changed
743
+ });
744
+ }
745
+ }, [
746
+ values,
747
+ onValuesChanged,
748
+ validate,
749
+ onValid,
750
+ changed
751
+ ]);
752
+ const onTouched = useCallback4((path) => {
753
+ const jsonPath = createJsonPath(path);
754
+ setTouched((touched2) => ({
755
+ ...touched2,
756
+ [jsonPath]: true
757
+ }));
758
+ validate(values);
759
+ }, [
760
+ validate,
761
+ values
762
+ ]);
763
+ return useMemo2(() => ({
764
+ // State.
765
+ values,
766
+ errors,
767
+ touched,
768
+ changed,
769
+ canSave,
770
+ formIsValid,
771
+ // Actions.
772
+ handleSave,
773
+ // Field utils.
774
+ getStatus,
775
+ getValue: getFormValue,
776
+ onValueChange,
777
+ onTouched
778
+ }), [
779
+ values,
780
+ errors,
781
+ touched,
782
+ changed,
783
+ canSave,
784
+ formIsValid,
785
+ handleSave,
786
+ getStatus,
787
+ getFormValue,
788
+ onValueChange,
789
+ onTouched
790
+ ]);
791
+ };
792
+ var createKeySet = (obj, value) => {
793
+ invariant(obj, void 0, {
794
+ F: __dxlog_file,
795
+ L: 274,
796
+ S: void 0,
797
+ A: [
798
+ "obj",
799
+ ""
800
+ ]
801
+ });
802
+ return Object.keys(obj).reduce((acc, key) => ({
803
+ ...acc,
804
+ [key]: value
805
+ }), {});
710
806
  };
711
- var NumberInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
712
- const { status, error } = getStatus();
713
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
714
- validationValence: status
715
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
716
- error
717
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextInput, {
718
- type: "number",
719
- disabled,
720
- placeholder,
721
- value: getValue3() ?? 0,
722
- onChange: (event) => onValueChange(type, event.target.value),
723
- onBlur
724
- }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
807
+ var flatMap = (errors) => {
808
+ return errors.reduce((result, { path, message }) => {
809
+ const jsonPath = fromEffectValidationPath(path);
810
+ if (!(jsonPath in result)) {
811
+ result[jsonPath] = message;
812
+ }
813
+ return result;
814
+ }, {});
725
815
  };
726
- var BooleanInput = ({ type, label, inputOnly, getStatus, getValue: getValue3, onValueChange }) => {
727
- const { status, error } = getStatus();
728
- const checked = Boolean(getValue3());
729
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
730
- validationValence: status
731
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
732
- error
733
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.Switch, {
734
- checked,
735
- onCheckedChange: (value) => onValueChange(type, value)
736
- }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
816
+
817
+ // packages/ui/react-ui-form/src/components/Form/FormContext.tsx
818
+ var FormContext = /* @__PURE__ */ createContext(void 0);
819
+ var useFormContext = () => {
820
+ return useContext(FormContext) ?? raise(new Error("Missing FormContext"));
737
821
  };
738
- var SelectInput = ({ type, label, inputOnly, disabled, placeholder, options, getStatus, getValue: getValue3, onValueChange }) => {
739
- const { status, error } = getStatus();
740
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
741
- validationValence: status
742
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
743
- error
744
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Select.Root, {
745
- value: getValue3(),
746
- onValueChange: (value) => onValueChange(type, value)
747
- }, /* @__PURE__ */ React7.createElement(Select.TriggerButton, {
748
- classNames: "is-full",
749
- disabled,
750
- placeholder
751
- }), /* @__PURE__ */ React7.createElement(Select.Portal, null, /* @__PURE__ */ React7.createElement(Select.Content, null, /* @__PURE__ */ React7.createElement(Select.Viewport, null, options?.map(({ value, label: label2 }) => (
752
- // NOTE: Numeric values are converted to and from strings.
753
- /* @__PURE__ */ React7.createElement(Select.Option, {
754
- key: String(value),
755
- value: String(value)
756
- }, label2 ?? String(value))
757
- )))))), inputOnly && /* @__PURE__ */ React7.createElement(Input5.DescriptionAndValidation, null, error));
822
+ var useFormValues = (path = []) => {
823
+ const { values: formValues } = useFormContext();
824
+ const jsonPath = createJsonPath2(path);
825
+ return getValue2(formValues, jsonPath);
758
826
  };
759
- var MarkdownInput = ({ type, label, inputOnly, disabled, placeholder, getStatus, getValue: getValue3, onValueChange, onBlur }) => {
760
- const { status, error } = getStatus();
761
- const textareaRef = useRef2(null);
762
- const adjustHeight = useCallback4(() => {
763
- const textarea = textareaRef.current;
764
- if (textarea) {
765
- textarea.style.blockSize = "auto";
766
- textarea.style.blockSize = `${textarea.scrollHeight + 2}px`;
767
- }
768
- }, []);
769
- useEffect5(() => {
770
- adjustHeight();
771
- }, [
772
- adjustHeight
827
+ var useInputProps = (path = []) => {
828
+ const { getStatus, getValue: getFormValue, onValueChange, onTouched } = useFormContext();
829
+ const stablePath = useMemo3(() => path, [
830
+ Array.isArray(path) ? path.join(".") : path
831
+ ]);
832
+ return useMemo3(() => ({
833
+ getStatus: () => getStatus(stablePath),
834
+ getValue: () => getFormValue(stablePath),
835
+ onValueChange: (type, value) => onValueChange(stablePath, type, value),
836
+ onBlur: () => onTouched(stablePath)
837
+ }), [
838
+ getStatus,
839
+ getFormValue,
840
+ onValueChange,
841
+ onTouched,
842
+ stablePath
773
843
  ]);
844
+ };
845
+ var FormProvider = ({ children, formRef, autoSave, ...formOptions }) => {
846
+ const form = useForm(formOptions);
774
847
  useEffect5(() => {
775
- adjustHeight();
848
+ if (!formRef?.current) {
849
+ return;
850
+ }
851
+ const handleKeyDown = (event) => {
852
+ const keyIsEnter = event.key === "Enter";
853
+ const modifierUsed = event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;
854
+ const inputIsTextarea = event.target.tagName.toLowerCase() === "textarea";
855
+ const inputOptOut = event.target.hasAttribute("data-no-submit");
856
+ const shouldSubmitRegularInput = !inputIsTextarea && keyIsEnter && !modifierUsed;
857
+ const shouldSubmitTextarea = inputIsTextarea && keyIsEnter && event.metaKey;
858
+ if ((shouldSubmitRegularInput || shouldSubmitTextarea) && !inputOptOut) {
859
+ if (!autoSave && form.canSave) {
860
+ form.handleSave();
861
+ }
862
+ if (autoSave && form.formIsValid) {
863
+ event.target.blur();
864
+ }
865
+ }
866
+ };
867
+ const formElement = formRef.current;
868
+ formElement.addEventListener("keydown", handleKeyDown);
869
+ return () => formElement.removeEventListener("keydown", handleKeyDown);
776
870
  }, [
777
- getValue3(),
778
- adjustHeight
871
+ formRef,
872
+ form.canSave,
873
+ form.formIsValid,
874
+ form.handleSave,
875
+ autoSave
779
876
  ]);
780
- return /* @__PURE__ */ React7.createElement(Input5.Root, {
781
- validationValence: status
782
- }, !inputOnly && /* @__PURE__ */ React7.createElement(InputHeader, {
783
- error
784
- }, /* @__PURE__ */ React7.createElement(Input5.Label, null, label)), /* @__PURE__ */ React7.createElement(Input5.TextArea, {
785
- ref: textareaRef,
786
- disabled,
787
- placeholder,
788
- value: getValue3() ?? "",
789
- classNames: "min-bs-auto max-h-40 overflow-auto",
790
- onChange: (event) => onValueChange(type, event.target.value),
791
- onBlur,
792
- style: {
793
- resize: "none"
794
- },
795
- spellCheck: false
796
- }), inputOnly && /* @__PURE__ */ React7.createElement(Input5.Validation, null, error));
877
+ return /* @__PURE__ */ React8.createElement(FormContext.Provider, {
878
+ value: form
879
+ }, children);
797
880
  };
798
881
 
799
- // packages/ui/react-ui-form/src/components/Form/Form.tsx
800
- import React11, { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef3 } from "react";
801
- import { mx as mx7 } from "@dxos/react-ui-theme";
802
-
803
882
  // packages/ui/react-ui-form/src/components/Form/FormActions.tsx
804
- import React8 from "react";
805
- import { IconButton as IconButton2, useTranslation as useTranslation3 } from "@dxos/react-ui";
806
- import { mx as mx4 } from "@dxos/react-ui-theme";
807
883
  var FormActions = ({ onCancel, readonly }) => {
808
- const { t } = useTranslation3(translationKey);
884
+ const { t } = useTranslation4(translationKey);
809
885
  const { canSave, handleSave } = useFormContext();
810
- return /* @__PURE__ */ React8.createElement("div", {
886
+ return /* @__PURE__ */ React9.createElement("div", {
811
887
  role: "none",
812
888
  className: "mt-2 flex justify-center"
813
- }, /* @__PURE__ */ React8.createElement("div", {
889
+ }, /* @__PURE__ */ React9.createElement("div", {
814
890
  role: "none",
815
891
  className: mx4(onCancel && !readonly && "grid grid-cols-2 gap-2")
816
- }, onCancel && !readonly && /* @__PURE__ */ React8.createElement(IconButton2, {
892
+ }, onCancel && !readonly && /* @__PURE__ */ React9.createElement(IconButton2, {
817
893
  "data-testid": "cancel-button",
818
894
  icon: "ph--x--regular",
819
895
  label: t("button cancel"),
820
896
  onClick: onCancel
821
- }), handleSave && /* @__PURE__ */ React8.createElement(IconButton2, {
897
+ }), handleSave && /* @__PURE__ */ React9.createElement(IconButton2, {
822
898
  type: "submit",
823
899
  "data-testid": "save-button",
824
900
  disabled: !canSave,
@@ -831,7 +907,7 @@ var FormActions = ({ onCancel, readonly }) => {
831
907
  // packages/ui/react-ui-form/src/components/Form/FormContent.tsx
832
908
  import { pipe as pipe2 } from "effect";
833
909
  import { capitalize as capitalize2 } from "effect/String";
834
- import React10, { useMemo as useMemo5 } from "react";
910
+ import React11, { useMemo as useMemo5 } from "react";
835
911
  import { AST as AST3, S } from "@dxos/echo-schema";
836
912
  import { createJsonPath as createJsonPath3, findNode as findNode2, getDiscriminatedType as getDiscriminatedType2, isDiscriminatedUnion as isDiscriminatedUnion2 } from "@dxos/effect";
837
913
  import { mx as mx6 } from "@dxos/react-ui-theme";
@@ -841,17 +917,17 @@ import { isNotFalsy } from "@dxos/util";
841
917
  // packages/ui/react-ui-form/src/components/Form/ArrayField.tsx
842
918
  import { pipe } from "effect";
843
919
  import { capitalize } from "effect/String";
844
- import React9, { useCallback as useCallback5, useMemo as useMemo4 } from "react";
920
+ import React10, { useCallback as useCallback5, useMemo as useMemo4 } from "react";
845
921
  import { AST as AST2 } from "@dxos/echo-schema";
846
922
  import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from "@dxos/effect";
847
923
  import { invariant as invariant2 } from "@dxos/invariant";
848
- import { IconButton as IconButton3, useTranslation as useTranslation4 } from "@dxos/react-ui";
924
+ import { IconButton as IconButton3, useTranslation as useTranslation5 } from "@dxos/react-ui";
849
925
  import { mx as mx5 } from "@dxos/react-ui-theme";
850
926
  import { getSchemaProperties } from "@dxos/schema";
851
927
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/Form/ArrayField.tsx";
852
928
  var padding = "px-2";
853
929
  var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponent }) => {
854
- const { t } = useTranslation4(translationKey);
930
+ const { t } = useTranslation5(translationKey);
855
931
  const { ast, name, type, title } = property;
856
932
  const values = useFormValues(path ?? []);
857
933
  invariant2(Array.isArray(values), `Values at path ${path?.join(".")} must be an array.`, {
@@ -910,20 +986,20 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
910
986
  if (!elementType) {
911
987
  return null;
912
988
  }
913
- return /* @__PURE__ */ React9.createElement("div", {
989
+ return /* @__PURE__ */ React10.createElement("div", {
914
990
  role: "none",
915
991
  className: mx5(padding)
916
- }, /* @__PURE__ */ React9.createElement(InputHeader, null, label), /* @__PURE__ */ React9.createElement("div", {
992
+ }, /* @__PURE__ */ React10.createElement(InputHeader, null, label), /* @__PURE__ */ React10.createElement("div", {
917
993
  role: "none",
918
994
  className: "flex flex-col gap-1"
919
- }, values.map((_value, index) => /* @__PURE__ */ React9.createElement("div", {
995
+ }, values.map((_value, index) => /* @__PURE__ */ React10.createElement("div", {
920
996
  key: index,
921
997
  role: "none",
922
998
  className: "flex items-center gap-1"
923
- }, /* @__PURE__ */ React9.createElement("div", {
999
+ }, /* @__PURE__ */ React10.createElement("div", {
924
1000
  role: "none",
925
1001
  className: "flex-1"
926
- }, /* @__PURE__ */ React9.createElement(FormField, {
1002
+ }, /* @__PURE__ */ React10.createElement(FormField, {
927
1003
  property: {
928
1004
  ...property,
929
1005
  array: false,
@@ -937,15 +1013,15 @@ var ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponen
937
1013
  inline: true,
938
1014
  Custom,
939
1015
  lookupComponent
940
- })), /* @__PURE__ */ React9.createElement(IconButton3, {
1016
+ })), /* @__PURE__ */ React10.createElement(IconButton3, {
941
1017
  icon: "ph--trash--regular",
942
1018
  iconOnly: true,
943
1019
  label: t("button remove"),
944
1020
  onClick: () => handleRemove(index)
945
- })))), /* @__PURE__ */ React9.createElement("div", {
1021
+ })))), /* @__PURE__ */ React10.createElement("div", {
946
1022
  role: "none",
947
1023
  className: "flex justify-between items-center plb-1"
948
- }, /* @__PURE__ */ React9.createElement(IconButton3, {
1024
+ }, /* @__PURE__ */ React10.createElement(IconButton3, {
949
1025
  icon: "ph--plus--regular",
950
1026
  iconOnly: true,
951
1027
  label: t("button add"),
@@ -997,14 +1073,14 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
997
1073
  }
998
1074
  });
999
1075
  if (FoundComponent) {
1000
- return /* @__PURE__ */ React10.createElement("div", {
1076
+ return /* @__PURE__ */ React11.createElement("div", {
1001
1077
  role: "none"
1002
1078
  }, FoundComponent);
1003
1079
  }
1004
1080
  const jsonPath = createJsonPath3(path ?? []);
1005
1081
  const CustomComponent = Custom?.[jsonPath];
1006
1082
  if (CustomComponent) {
1007
- return /* @__PURE__ */ React10.createElement(CustomComponent, {
1083
+ return /* @__PURE__ */ React11.createElement(CustomComponent, {
1008
1084
  type,
1009
1085
  format,
1010
1086
  label,
@@ -1015,7 +1091,7 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
1015
1091
  });
1016
1092
  }
1017
1093
  if (array) {
1018
- return /* @__PURE__ */ React10.createElement(ArrayField, {
1094
+ return /* @__PURE__ */ React11.createElement(ArrayField, {
1019
1095
  property,
1020
1096
  path,
1021
1097
  inputProps,
@@ -1025,9 +1101,9 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
1025
1101
  }
1026
1102
  const InputComponent = getInputComponent(type, format);
1027
1103
  if (InputComponent) {
1028
- return /* @__PURE__ */ React10.createElement("div", {
1104
+ return /* @__PURE__ */ React11.createElement("div", {
1029
1105
  role: "none"
1030
- }, /* @__PURE__ */ React10.createElement(InputComponent, {
1106
+ }, /* @__PURE__ */ React11.createElement(InputComponent, {
1031
1107
  type,
1032
1108
  format,
1033
1109
  label,
@@ -1038,9 +1114,9 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
1038
1114
  }));
1039
1115
  }
1040
1116
  if (options) {
1041
- return /* @__PURE__ */ React10.createElement("div", {
1117
+ return /* @__PURE__ */ React11.createElement("div", {
1042
1118
  role: "none"
1043
- }, /* @__PURE__ */ React10.createElement(SelectInput, {
1119
+ }, /* @__PURE__ */ React11.createElement(SelectInput, {
1044
1120
  type,
1045
1121
  format,
1046
1122
  disabled: readonly,
@@ -1058,9 +1134,9 @@ var FormField = ({ property, path, readonly, inline, lookupComponent, Custom })
1058
1134
  const baseNode = findNode2(ast, isDiscriminatedUnion2);
1059
1135
  const typeLiteral = baseNode ? getDiscriminatedType2(baseNode, inputProps.getValue()) : findNode2(ast, AST3.isTypeLiteral);
1060
1136
  if (typeLiteral) {
1061
- return /* @__PURE__ */ React10.createElement("div", {
1137
+ return /* @__PURE__ */ React11.createElement("div", {
1062
1138
  role: "none"
1063
- }, !inline && /* @__PURE__ */ React10.createElement("div", null, label), /* @__PURE__ */ React10.createElement(FormFields, {
1139
+ }, !inline && /* @__PURE__ */ React11.createElement("div", null, label), /* @__PURE__ */ React11.createElement(FormFields, {
1064
1140
  schema: S.make(typeLiteral),
1065
1141
  path,
1066
1142
  readonly,
@@ -1082,11 +1158,11 @@ var FormFields = ({ schema, path, filter, sort, readonly, lookupComponent, Custo
1082
1158
  filter,
1083
1159
  sort
1084
1160
  ]);
1085
- return /* @__PURE__ */ React10.createElement("div", {
1161
+ return /* @__PURE__ */ React11.createElement("div", {
1086
1162
  role: "form",
1087
1163
  className: mx6("flex flex-col w-full gap-2")
1088
1164
  }, properties.map((property) => {
1089
- return /* @__PURE__ */ React10.createElement(FormField, {
1165
+ return /* @__PURE__ */ React11.createElement(FormField, {
1090
1166
  key: property.name,
1091
1167
  property,
1092
1168
  path: [
@@ -1117,7 +1193,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, r
1117
1193
  }, [
1118
1194
  autoFocus
1119
1195
  ]);
1120
- return /* @__PURE__ */ React11.createElement(FormProvider, {
1196
+ return /* @__PURE__ */ React12.createElement(FormProvider, {
1121
1197
  formRef,
1122
1198
  schema,
1123
1199
  autoSave,
@@ -1126,12 +1202,12 @@ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, r
1126
1202
  onValidate,
1127
1203
  onValid,
1128
1204
  onSave
1129
- }, /* @__PURE__ */ React11.createElement("div", {
1205
+ }, /* @__PURE__ */ React12.createElement("div", {
1130
1206
  ref: formRef,
1131
1207
  role: "none",
1132
1208
  className: mx7("p-2", classNames),
1133
1209
  "data-testid": testId
1134
- }, /* @__PURE__ */ React11.createElement(FormFields, {
1210
+ }, /* @__PURE__ */ React12.createElement(FormFields, {
1135
1211
  schema,
1136
1212
  path,
1137
1213
  readonly,
@@ -1139,7 +1215,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, r
1139
1215
  sort,
1140
1216
  lookupComponent,
1141
1217
  Custom
1142
- }), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React11.createElement(FormActions, {
1218
+ }), (onCancel || onSave) && !autoSave && /* @__PURE__ */ React12.createElement(FormActions, {
1143
1219
  onCancel,
1144
1220
  readonly
1145
1221
  })));
@@ -1148,7 +1224,7 @@ var Form = ({ classNames, schema, values: initialValues, path = [], autoFocus, r
1148
1224
  // packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx
1149
1225
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/FieldEditor/FieldEditor.tsx";
1150
1226
  var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1151
- const { t } = useTranslation5(translationKey);
1227
+ const { t } = useTranslation6(translationKey);
1152
1228
  const [props, setProps] = useState4(projection.getFieldProjection(field.id).props);
1153
1229
  useEffect7(() => setProps(projection.getFieldProjection(field.id).props), [
1154
1230
  field,
@@ -1249,26 +1325,26 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1249
1325
  onSave
1250
1326
  ]);
1251
1327
  const Custom = useMemo7(() => ({
1252
- ["format"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
1328
+ ["format"]: (props2) => /* @__PURE__ */ React13.createElement(SelectInput, {
1253
1329
  ...props2,
1254
1330
  options: FormatEnums.filter((value) => value !== FormatEnum2.None).map((value) => ({
1255
1331
  value,
1256
1332
  label: t(`format ${value}`)
1257
1333
  }))
1258
1334
  }),
1259
- ["referenceSchema"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
1335
+ ["referenceSchema"]: (props2) => /* @__PURE__ */ React13.createElement(SelectInput, {
1260
1336
  ...props2,
1261
1337
  options: schemas.map((schema) => ({
1262
1338
  value: schema.typename
1263
1339
  }))
1264
1340
  }),
1265
- ["referencePath"]: (props2) => /* @__PURE__ */ React12.createElement(SelectInput, {
1341
+ ["referencePath"]: (props2) => /* @__PURE__ */ React13.createElement(SelectInput, {
1266
1342
  ...props2,
1267
1343
  options: referenceSchema ? getSchemaProperties3(referenceSchema.ast).sort(sortProperties).map((p) => ({
1268
1344
  value: p.name
1269
1345
  })) : []
1270
1346
  }),
1271
- ["options"]: (props2) => /* @__PURE__ */ React12.createElement(SelectOptionInput, props2)
1347
+ ["options"]: (props2) => /* @__PURE__ */ React13.createElement(SelectOptionInput, props2)
1272
1348
  }), [
1273
1349
  t,
1274
1350
  schemas,
@@ -1286,7 +1362,7 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1286
1362
  });
1287
1363
  return null;
1288
1364
  }
1289
- return /* @__PURE__ */ React12.createElement(Form, {
1365
+ return /* @__PURE__ */ React13.createElement(Form, {
1290
1366
  key: field.id,
1291
1367
  autoFocus: true,
1292
1368
  values: props,
@@ -1305,12 +1381,14 @@ var FieldEditor = ({ view, projection, field, registry, onSave, onCancel }) => {
1305
1381
  };
1306
1382
 
1307
1383
  // packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx
1308
- import React13, { useCallback as useCallback7, useMemo as useMemo8, useState as useState5 } from "react";
1384
+ import React14, { useCallback as useCallback7, useMemo as useMemo8, useState as useState5 } from "react";
1309
1385
  import { AST as AST4, Format, S as S2 } from "@dxos/echo-schema";
1310
- import { IconButton as IconButton4, useTranslation as useTranslation6 } from "@dxos/react-ui";
1386
+ import { invariant as invariant3 } from "@dxos/invariant";
1387
+ import { IconButton as IconButton4, useTranslation as useTranslation7 } from "@dxos/react-ui";
1311
1388
  import { List as List2 } from "@dxos/react-ui-list";
1312
1389
  import { ghostHover, inputTextLabel, mx as mx8 } from "@dxos/react-ui-theme";
1313
1390
  import { FieldSchema, ViewProjection, VIEW_FIELD_LIMIT } from "@dxos/schema";
1391
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-form/src/components/ViewEditor/ViewEditor.tsx";
1314
1392
  var grid = "grid grid-cols-[32px_1fr_32px] min-bs-[2.5rem]";
1315
1393
  var ViewMetaSchema = S2.Struct({
1316
1394
  name: S2.String.annotations({
@@ -1321,8 +1399,8 @@ var ViewMetaSchema = S2.Struct({
1321
1399
  })
1322
1400
  }).pipe(S2.mutable);
1323
1401
  var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading = false, onTypenameChanged, onDelete }) => {
1324
- const { t } = useTranslation6(translationKey);
1325
- const projection = useMemo8(() => new ViewProjection(schema, view), [
1402
+ const { t } = useTranslation7(translationKey);
1403
+ const projection = useMemo8(() => new ViewProjection(schema.jsonSchema, view), [
1326
1404
  schema,
1327
1405
  view
1328
1406
  ]);
@@ -1337,30 +1415,77 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1337
1415
  }, [
1338
1416
  view
1339
1417
  ]);
1340
- const handleViewUpdate = useCallback7(({ name, typename }) => {
1418
+ const handleSelect = useCallback7((field2) => {
1419
+ setField((f) => f === field2 ? void 0 : field2);
1420
+ }, []);
1421
+ const handleAdd = useCallback7(() => {
1422
+ invariant3(!schema.readonly, void 0, {
1423
+ F: __dxlog_file4,
1424
+ L: 76,
1425
+ S: void 0,
1426
+ A: [
1427
+ "!schema.readonly",
1428
+ ""
1429
+ ]
1430
+ });
1431
+ const field2 = projection.createFieldProjection();
1432
+ setField(field2);
1433
+ }, [
1434
+ schema,
1435
+ view
1436
+ ]);
1437
+ const handleUpdate = useCallback7(({ name, typename }) => {
1438
+ invariant3(!schema.readonly, void 0, {
1439
+ F: __dxlog_file4,
1440
+ L: 83,
1441
+ S: void 0,
1442
+ A: [
1443
+ "!schema.readonly",
1444
+ ""
1445
+ ]
1446
+ });
1341
1447
  requestAnimationFrame(() => {
1342
1448
  if (view.name !== name) {
1343
1449
  view.name = name;
1344
1450
  }
1345
1451
  if (view.query.typename !== typename) {
1346
- onTypenameChanged(typename);
1452
+ onTypenameChanged?.(typename);
1347
1453
  }
1348
1454
  });
1349
1455
  }, [
1350
- view,
1351
1456
  schema,
1457
+ view,
1352
1458
  onTypenameChanged
1353
1459
  ]);
1354
- const handleSelect = useCallback7((field2) => {
1355
- setField((f) => f === field2 ? void 0 : field2);
1356
- }, []);
1357
- const handleAdd = useCallback7(() => {
1358
- const field2 = projection.createFieldProjection();
1359
- setField(field2);
1460
+ const handleDelete = useCallback7((fieldId) => {
1461
+ invariant3(!schema.readonly, void 0, {
1462
+ F: __dxlog_file4,
1463
+ L: 99,
1464
+ S: void 0,
1465
+ A: [
1466
+ "!schema.readonly",
1467
+ ""
1468
+ ]
1469
+ });
1470
+ if (fieldId === field?.id) {
1471
+ setField(void 0);
1472
+ }
1473
+ onDelete?.(fieldId);
1360
1474
  }, [
1361
- view
1475
+ schema,
1476
+ field,
1477
+ onDelete
1362
1478
  ]);
1363
1479
  const handleMove = useCallback7((fromIndex, toIndex) => {
1480
+ invariant3(!schema.readonly, void 0, {
1481
+ F: __dxlog_file4,
1482
+ L: 111,
1483
+ S: void 0,
1484
+ A: [
1485
+ "!schema.readonly",
1486
+ ""
1487
+ ]
1488
+ });
1364
1489
  const fields = [
1365
1490
  ...view.fields
1366
1491
  ];
@@ -1368,57 +1493,97 @@ var ViewEditor = ({ classNames, schema, view, registry, readonly, showHeading =
1368
1493
  fields.splice(toIndex, 0, moved);
1369
1494
  view.fields = fields;
1370
1495
  }, [
1496
+ schema,
1371
1497
  view.fields
1372
1498
  ]);
1373
1499
  const handleClose = useCallback7(() => setField(void 0), []);
1374
- return /* @__PURE__ */ React13.createElement("div", {
1500
+ const hiddenProperties = projection.getHiddenProperties();
1501
+ const handleHide = useCallback7((fieldId) => {
1502
+ setField(void 0);
1503
+ projection.hideFieldProjection(fieldId);
1504
+ }, [
1505
+ projection
1506
+ ]);
1507
+ const handleShow = useCallback7((property) => {
1508
+ setField(void 0);
1509
+ projection.showFieldProjection(property);
1510
+ }, [
1511
+ projection
1512
+ ]);
1513
+ return /* @__PURE__ */ React14.createElement("div", {
1375
1514
  role: "none",
1376
1515
  className: mx8("flex flex-col w-full divide-y divide-separator", classNames)
1377
- }, /* @__PURE__ */ React13.createElement(Form, {
1516
+ }, /* @__PURE__ */ React14.createElement(Form, {
1378
1517
  autoSave: true,
1379
1518
  schema: ViewMetaSchema,
1380
1519
  values: viewValues,
1381
- onSave: handleViewUpdate
1382
- }), /* @__PURE__ */ React13.createElement("div", null, /* @__PURE__ */ React13.createElement("div", {
1520
+ onSave: schema.readonly ? void 0 : handleUpdate
1521
+ }), /* @__PURE__ */ React14.createElement("div", null, /* @__PURE__ */ React14.createElement("div", {
1383
1522
  role: "none",
1384
1523
  className: "p-2"
1385
- }, /* @__PURE__ */ React13.createElement("label", {
1524
+ }, /* @__PURE__ */ React14.createElement("label", {
1386
1525
  className: mx8(inputTextLabel)
1387
- }, t("fields label"))), /* @__PURE__ */ React13.createElement(List2.Root, {
1526
+ }, t("fields label"))), /* @__PURE__ */ React14.createElement(List2.Root, {
1388
1527
  items: view.fields,
1389
1528
  isItem: S2.is(FieldSchema),
1390
1529
  getId: (field2) => field2.id,
1391
- onMove: handleMove
1392
- }, ({ items: fields }) => /* @__PURE__ */ React13.createElement(React13.Fragment, null, showHeading && /* @__PURE__ */ React13.createElement("div", {
1530
+ onMove: schema.readonly ? void 0 : handleMove
1531
+ }, ({ items: fields }) => /* @__PURE__ */ React14.createElement(React14.Fragment, null, showHeading && /* @__PURE__ */ React14.createElement("div", {
1393
1532
  role: "heading",
1394
1533
  className: grid
1395
- }, /* @__PURE__ */ React13.createElement("div", null), /* @__PURE__ */ React13.createElement("div", {
1534
+ }, /* @__PURE__ */ React14.createElement("div", null), /* @__PURE__ */ React14.createElement("div", {
1396
1535
  className: "flex items-center text-sm"
1397
- }, t("field path label"))), /* @__PURE__ */ React13.createElement("div", {
1536
+ }, t("field path label"))), /* @__PURE__ */ React14.createElement("div", {
1398
1537
  role: "list",
1399
1538
  className: "flex flex-col w-full"
1400
- }, fields?.map((field2) => /* @__PURE__ */ React13.createElement(List2.Item, {
1539
+ }, fields?.map((field2) => /* @__PURE__ */ React14.createElement(List2.Item, {
1401
1540
  key: field2.id,
1402
1541
  item: field2,
1403
1542
  classNames: mx8(grid, ghostHover, "cursor-pointer")
1404
- }, /* @__PURE__ */ React13.createElement(List2.ItemDragHandle, null), /* @__PURE__ */ React13.createElement(List2.ItemTitle, {
1543
+ }, /* @__PURE__ */ React14.createElement(List2.ItemDragHandle, null), /* @__PURE__ */ React14.createElement(List2.ItemTitle, {
1405
1544
  onClick: () => handleSelect(field2)
1406
- }, field2.path), /* @__PURE__ */ React13.createElement(List2.ItemDeleteButton, {
1545
+ }, field2.path), /* @__PURE__ */ React14.createElement("div", {
1546
+ className: "flex items-center gap-2 -ml-4"
1547
+ }, /* @__PURE__ */ React14.createElement(List2.ItemButton, {
1548
+ icon: "ph--eye-slash--regular",
1549
+ disabled: view.fields.length <= 1,
1550
+ onClick: () => handleHide(field2.id)
1551
+ }), !schema.readonly && /* @__PURE__ */ React14.createElement(List2.ItemDeleteButton, {
1552
+ icon: "ph--trash--regular",
1407
1553
  disabled: view.fields.length <= 1,
1408
- onClick: () => onDelete(field2.id)
1409
- }))))))), field && /* @__PURE__ */ React13.createElement(FieldEditor, {
1554
+ onClick: () => handleDelete(field2.id)
1555
+ }))))))), hiddenProperties.length > 0 && /* @__PURE__ */ React14.createElement("div", null, /* @__PURE__ */ React14.createElement("div", {
1556
+ role: "none",
1557
+ className: "p-2"
1558
+ }, /* @__PURE__ */ React14.createElement("label", {
1559
+ className: mx8(inputTextLabel)
1560
+ }, t("hidden fields label"))), /* @__PURE__ */ React14.createElement(List2.Root, {
1561
+ items: hiddenProperties,
1562
+ isItem: (item) => typeof item === "string",
1563
+ getId: (property) => property
1564
+ }, ({ items: properties }) => /* @__PURE__ */ React14.createElement("div", {
1565
+ role: "list",
1566
+ className: "flex flex-col w-full"
1567
+ }, properties?.map((property) => /* @__PURE__ */ React14.createElement(List2.Item, {
1568
+ key: property,
1569
+ item: property,
1570
+ classNames: mx8(grid, ghostHover, "cursor-pointer")
1571
+ }, /* @__PURE__ */ React14.createElement("div", null), /* @__PURE__ */ React14.createElement(List2.ItemTitle, null, property), /* @__PURE__ */ React14.createElement(List2.ItemButton, {
1572
+ icon: "ph--eye--regular",
1573
+ onClick: () => handleShow(property)
1574
+ }))))))), field && /* @__PURE__ */ React14.createElement(FieldEditor, {
1410
1575
  key: field.id,
1411
1576
  view,
1412
1577
  projection,
1413
1578
  field,
1414
1579
  registry,
1415
1580
  onSave: handleClose
1416
- }), !readonly && !field && /* @__PURE__ */ React13.createElement("div", {
1581
+ }), !readonly && !field && /* @__PURE__ */ React14.createElement("div", {
1417
1582
  className: "flex p-2 justify-center"
1418
- }, /* @__PURE__ */ React13.createElement(IconButton4, {
1583
+ }, /* @__PURE__ */ React14.createElement(IconButton4, {
1419
1584
  icon: "ph--plus--regular",
1420
1585
  label: t("button add property"),
1421
- onClick: handleAdd,
1586
+ onClick: schema.readonly ? void 0 : handleAdd,
1422
1587
  // TODO(burdon): Show field limit in ux (not tooltip).
1423
1588
  disabled: view.fields.length >= VIEW_FIELD_LIMIT
1424
1589
  })));
@@ -1529,18 +1694,21 @@ var cellClassesForFieldType = ({ type, format }) => {
1529
1694
  switch (format) {
1530
1695
  case FormatEnum3.Markdown:
1531
1696
  return void 0;
1697
+ case FormatEnum3.Time:
1532
1698
  case FormatEnum3.Timestamp:
1533
1699
  case FormatEnum3.DateTime:
1534
1700
  case FormatEnum3.Date:
1535
- case FormatEnum3.Time:
1701
+ case FormatEnum3.Duration:
1536
1702
  return [
1537
- "font-mono"
1703
+ "font-mono",
1704
+ "text-right"
1538
1705
  ];
1539
1706
  case FormatEnum3.Currency:
1540
1707
  return [
1541
1708
  "text-right"
1542
1709
  ];
1543
1710
  case FormatEnum3.JSON:
1711
+ case FormatEnum3.DID:
1544
1712
  return [
1545
1713
  "font-mono"
1546
1714
  ];
@@ -1548,15 +1716,13 @@ var cellClassesForFieldType = ({ type, format }) => {
1548
1716
  return void 0;
1549
1717
  }
1550
1718
  };
1551
- var cellClassesForRowSelection = (selected) => selected ? [
1552
- "!bg-gridCellSelected"
1553
- ] : void 0;
1554
1719
  var pathNotUniqueError = (path) => ({
1555
1720
  path: "path",
1556
1721
  message: `'${path}' is already present`
1557
1722
  });
1558
1723
 
1559
1724
  // packages/ui/react-ui-form/src/util/formatting.ts
1725
+ import { format as formatDate } from "date-fns/format";
1560
1726
  import { FormatEnum as FormatEnum4, GeoLocation as GeoLocation3, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
1561
1727
  var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
1562
1728
  const formatScalar = (type2) => {
@@ -1598,17 +1764,14 @@ var formatForDisplay = ({ type, format, value, locale = void 0 }) => {
1598
1764
  maximumFractionDigits: 2
1599
1765
  });
1600
1766
  }
1601
- case FormatEnum4.DateTime:
1602
- case FormatEnum4.Date:
1767
+ case FormatEnum4.Date: {
1768
+ return formatDate(new Date(value), "yyyy-MM-dd");
1769
+ }
1603
1770
  case FormatEnum4.Time: {
1604
- const date = new Date(value);
1605
- if (format === FormatEnum4.DateTime) {
1606
- return date.toLocaleString(locale);
1607
- } else if (format === FormatEnum4.Date) {
1608
- return date.toLocaleDateString(locale);
1609
- } else {
1610
- return date.toLocaleTimeString(locale);
1611
- }
1771
+ return formatDate(new Date(value), "HH:mm:ss");
1772
+ }
1773
+ case FormatEnum4.DateTime: {
1774
+ return formatDate(new Date(value), "yyyy-MM-dd HH:mm:ss");
1612
1775
  }
1613
1776
  case FormatEnum4.GeoPoint: {
1614
1777
  if (value === null || value === void 0) {
@@ -1685,10 +1848,18 @@ var formatForEditing = ({ type, format, value, locale = void 0 }) => {
1685
1848
  };
1686
1849
  export {
1687
1850
  BooleanInput,
1851
+ ControlFrame,
1852
+ ControlFrameItem,
1853
+ ControlGroup,
1854
+ ControlItem,
1855
+ ControlItemInput,
1856
+ ControlSection,
1857
+ ControlSectionHeading,
1688
1858
  DeprecatedFormContainer,
1689
1859
  DeprecatedFormInput,
1690
1860
  FieldEditor,
1691
1861
  Form,
1862
+ FormProvider,
1692
1863
  GeoPointInput,
1693
1864
  InputHeader,
1694
1865
  MarkdownInput,
@@ -1699,13 +1870,15 @@ export {
1699
1870
  TupleInput,
1700
1871
  ViewEditor,
1701
1872
  cellClassesForFieldType,
1702
- cellClassesForRowSelection,
1873
+ controlItemClasses,
1703
1874
  formatForDisplay,
1704
1875
  formatForEditing,
1705
1876
  parseValue,
1706
1877
  pathNotUniqueError,
1707
1878
  translations_default as translations,
1708
1879
  useForm,
1709
- useFormValues
1880
+ useFormContext,
1881
+ useFormValues,
1882
+ useInputProps
1710
1883
  };
1711
1884
  //# sourceMappingURL=index.mjs.map