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