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