@dxos/react-ui-form 0.8.1-staging.391c573 → 0.8.1-staging.9eaf14f

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