@dxos/react-ui-form 0.8.4-main.937b3ca → 0.8.4-main.9735255

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-form",
3
- "version": "0.8.4-main.937b3ca",
3
+ "version": "0.8.4-main.9735255",
4
4
  "description": "Data components.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -37,28 +37,28 @@
37
37
  "date-fns": "^3.3.1",
38
38
  "jsonpath": "^1.1.1",
39
39
  "tabster": "^8.5.5",
40
- "@dxos/async": "0.8.4-main.937b3ca",
41
- "@dxos/debug": "0.8.4-main.937b3ca",
42
- "@dxos/echo-query": "0.8.4-main.937b3ca",
43
- "@dxos/effect": "0.8.4-main.937b3ca",
44
- "@dxos/echo": "0.8.4-main.937b3ca",
45
- "@dxos/echo-react": "0.8.4-main.937b3ca",
46
- "@dxos/invariant": "0.8.4-main.937b3ca",
47
- "@dxos/keys": "0.8.4-main.937b3ca",
48
- "@dxos/log": "0.8.4-main.937b3ca",
49
- "@dxos/lit-ui": "0.8.4-main.937b3ca",
50
- "@dxos/react-client": "0.8.4-main.937b3ca",
51
- "@dxos/react-ui-components": "0.8.4-main.937b3ca",
52
- "@dxos/react-ui-editor": "0.8.4-main.937b3ca",
53
- "@dxos/react-ui-mosaic": "0.8.4-main.937b3ca",
54
- "@dxos/react-ui-pickers": "0.8.4-main.937b3ca",
55
- "@dxos/react-ui-list": "0.8.4-main.937b3ca",
56
- "@dxos/react-ui-searchlist": "0.8.4-main.937b3ca",
57
- "@dxos/react-ui-stack": "0.8.4-main.937b3ca",
58
- "@dxos/schema": "0.8.4-main.937b3ca",
59
- "@dxos/types": "0.8.4-main.937b3ca",
60
- "@dxos/ui-types": "0.8.4-main.937b3ca",
61
- "@dxos/util": "0.8.4-main.937b3ca"
40
+ "@dxos/async": "0.8.4-main.9735255",
41
+ "@dxos/debug": "0.8.4-main.9735255",
42
+ "@dxos/echo": "0.8.4-main.9735255",
43
+ "@dxos/echo-query": "0.8.4-main.9735255",
44
+ "@dxos/echo-react": "0.8.4-main.9735255",
45
+ "@dxos/invariant": "0.8.4-main.9735255",
46
+ "@dxos/keys": "0.8.4-main.9735255",
47
+ "@dxos/lit-ui": "0.8.4-main.9735255",
48
+ "@dxos/react-client": "0.8.4-main.9735255",
49
+ "@dxos/log": "0.8.4-main.9735255",
50
+ "@dxos/react-ui-components": "0.8.4-main.9735255",
51
+ "@dxos/react-ui-editor": "0.8.4-main.9735255",
52
+ "@dxos/react-ui-mosaic": "0.8.4-main.9735255",
53
+ "@dxos/react-ui-list": "0.8.4-main.9735255",
54
+ "@dxos/react-ui-pickers": "0.8.4-main.9735255",
55
+ "@dxos/react-ui-searchlist": "0.8.4-main.9735255",
56
+ "@dxos/react-ui-stack": "0.8.4-main.9735255",
57
+ "@dxos/schema": "0.8.4-main.9735255",
58
+ "@dxos/types": "0.8.4-main.9735255",
59
+ "@dxos/ui-types": "0.8.4-main.9735255",
60
+ "@dxos/effect": "0.8.4-main.9735255",
61
+ "@dxos/util": "0.8.4-main.9735255"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@types/jsonpath": "^0.2.4",
@@ -69,19 +69,19 @@
69
69
  "react": "~19.2.3",
70
70
  "react-dom": "~19.2.3",
71
71
  "vite": "7.1.9",
72
- "@dxos/random": "0.8.4-main.937b3ca",
73
- "@dxos/react-ui": "0.8.4-main.937b3ca",
74
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.937b3ca",
75
- "@dxos/storybook-utils": "0.8.4-main.937b3ca",
76
- "@dxos/ui-theme": "0.8.4-main.937b3ca",
77
- "@dxos/react-client": "0.8.4-main.937b3ca"
72
+ "@dxos/random": "0.8.4-main.9735255",
73
+ "@dxos/react-client": "0.8.4-main.9735255",
74
+ "@dxos/react-ui": "0.8.4-main.9735255",
75
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.9735255",
76
+ "@dxos/ui-theme": "0.8.4-main.9735255",
77
+ "@dxos/storybook-utils": "0.8.4-main.9735255"
78
78
  },
79
79
  "peerDependencies": {
80
80
  "effect": "3.19.11",
81
81
  "react": "~19.2.3",
82
82
  "react-dom": "~19.2.3",
83
- "@dxos/ui-theme": "0.8.4-main.937b3ca",
84
- "@dxos/react-ui": "0.8.4-main.937b3ca"
83
+ "@dxos/react-ui": "0.8.4-main.9735255",
84
+ "@dxos/ui-theme": "0.8.4-main.9735255"
85
85
  },
86
86
  "publishConfig": {
87
87
  "access": "public"
@@ -8,7 +8,7 @@ import type * as SchemaAST from 'effect/SchemaAST';
8
8
  import React, { type PropsWithChildren, useEffect, useMemo, useRef } from 'react';
9
9
 
10
10
  import { type AnyProperties } from '@dxos/echo/internal';
11
- import { createJsonPath, getValue as getValue$, setValue as setValue$ } from '@dxos/effect';
11
+ import { createJsonPath, getValue as getValue$ } from '@dxos/effect';
12
12
  import { IconButton, type IconButtonProps, ScrollArea, type ThemedClassName, useTranslation } from '@dxos/react-ui';
13
13
  import { mx } from '@dxos/ui-theme';
14
14
 
@@ -20,6 +20,7 @@ import {
20
20
  useKeyHandler,
21
21
  } from '../../hooks';
22
22
  import { translationKey } from '../../translations';
23
+ import { setValueEchoAware } from '../../util';
23
24
 
24
25
  import { FormFieldLabel, type FormFieldLabelProps, type FormFieldStateProps } from './FormFieldComponent';
25
26
  import {
@@ -93,7 +94,7 @@ const useFormValues: {
93
94
 
94
95
  useEffect(() => {
95
96
  if (!value && defaultValue) {
96
- setValue$(values, jsonPath, defaultValue());
97
+ setValueEchoAware(values, jsonPath, defaultValue());
97
98
  }
98
99
  }, [value, defaultValue]);
99
100
 
@@ -12,6 +12,7 @@ import React, { forwardRef, useCallback, useContext, useImperativeHandle, useMem
12
12
  import { Filter, Format, Obj, Query, QueryAST, type SchemaRegistry } from '@dxos/echo';
13
13
  import { EchoSchema, type JsonProp, isMutable, toJsonSchema } from '@dxos/echo/internal';
14
14
  import { invariant } from '@dxos/invariant';
15
+ import { useObject } from '@dxos/react-client/echo';
15
16
  import { Callout, IconButton, Input, type ThemedClassName, useTranslation } from '@dxos/react-ui';
16
17
  import { QueryForm, type QueryFormProps } from '@dxos/react-ui-components';
17
18
  import { List } from '@dxos/react-ui-list';
@@ -21,7 +22,6 @@ import {
21
22
  ProjectionModel,
22
23
  VIEW_FIELD_LIMIT,
23
24
  type View,
24
- createDirectChangeCallback,
25
25
  createEchoChangeCallback,
26
26
  getTypenameFromQuery,
27
27
  } from '@dxos/schema';
@@ -81,11 +81,9 @@ export const ViewEditor = forwardRef<ProjectionModel, ViewEditorProps>(
81
81
  // Use reactive and mutable version of json schema when schema is mutable.
82
82
  const jsonSchema = schema instanceof EchoSchema ? schema.jsonSchema : toJsonSchema(schema);
83
83
 
84
- // Use createEchoChangeCallback for mutable schemas (EchoSchema), otherwise use direct mutation.
85
- const change =
86
- schema instanceof EchoSchema
87
- ? createEchoChangeCallback(view, schema)
88
- : createDirectChangeCallback(view.projection, jsonSchema);
84
+ // Always use createEchoChangeCallback since the view is ECHO-backed.
85
+ // Pass schema only when mutable to allow schema mutations.
86
+ const change = createEchoChangeCallback(view, schema instanceof EchoSchema ? schema : undefined);
89
87
 
90
88
  const model = new ProjectionModel({
91
89
  registry: atomRegistry,
@@ -220,15 +218,16 @@ const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDe
220
218
  const schemaReadonly = !isMutable(schema);
221
219
  const { t } = useTranslation(translationKey);
222
220
 
221
+ // Subscribe to view changes for reactivity.
222
+ const [viewSnapshot] = useObject(view);
223
+
223
224
  const projectionModel = useMemo(() => {
224
225
  // Use reactive and mutable version of json schema when schema is mutable.
225
226
  const jsonSchema = schema instanceof EchoSchema ? schema.jsonSchema : toJsonSchema(schema);
226
227
 
227
- // Use createEchoChangeCallback for mutable schemas (EchoSchema), otherwise use direct mutation.
228
- const change =
229
- schema instanceof EchoSchema
230
- ? createEchoChangeCallback(view, schema)
231
- : createDirectChangeCallback(view.projection, jsonSchema);
228
+ // Always use createEchoChangeCallback since the view is ECHO-backed.
229
+ // Pass schema only when mutable to allow schema mutations.
230
+ const change = createEchoChangeCallback(view, schema instanceof EchoSchema ? schema : undefined);
232
231
 
233
232
  const model = new ProjectionModel({
234
233
  registry: atomRegistry,
@@ -264,15 +263,15 @@ const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDe
264
263
  const handleMove = useCallback(
265
264
  (fromIndex: number, toIndex: number) => {
266
265
  invariant(!readonly);
267
- // NOTE(ZaymonFC): Using arrayMove here causes a race condition with the kanban model.
268
- const fields = [...view.projection.fields];
269
- const [moved] = fields.splice(fromIndex, 1);
270
- fields.splice(toIndex, 0, moved);
271
266
  Obj.change(view, (v) => {
267
+ // NOTE(ZaymonFC): Using arrayMove here causes a race condition with the kanban model.
268
+ const fields = [...v.projection.fields];
269
+ const [moved] = fields.splice(fromIndex, 1);
270
+ fields.splice(toIndex, 0, moved);
272
271
  v.projection.fields = fields;
273
272
  });
274
273
  },
275
- [view.projection.fields, readonly],
274
+ [view, readonly],
276
275
  );
277
276
 
278
277
  const handleClose = useCallback(() => setExpandedField(undefined), []);
@@ -293,9 +292,13 @@ const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDe
293
292
  [projectionModel],
294
293
  );
295
294
 
295
+ if (!viewSnapshot) {
296
+ return null;
297
+ }
298
+
296
299
  return (
297
300
  <List.Root<FieldType>
298
- items={view.projection.fields as FieldType[]}
301
+ items={viewSnapshot.projection.fields as FieldType[]}
299
302
  isItem={Schema.is(FieldSchema)}
300
303
  getId={(field) => field.id}
301
304
  onMove={readonly ? undefined : handleMove}
@@ -332,7 +335,7 @@ const FieldList = ({ schema, view, registry, readonly, showHeading = false, onDe
332
335
  <List.ItemDeleteButton
333
336
  label={t('delete field label')}
334
337
  autoHide={false}
335
- disabled={readonly || schemaReadonly || view.projection.fields.length <= 1}
338
+ disabled={readonly || schemaReadonly || viewSnapshot.projection.fields.length <= 1}
336
339
  onClick={() => handleDelete(field.id)}
337
340
  data-testid='field.delete'
338
341
  />
@@ -7,6 +7,7 @@ import type * as Schema from 'effect/Schema';
7
7
  import type * as SchemaAST from 'effect/SchemaAST';
8
8
  import { useCallback, useEffect, useMemo, useState } from 'react';
9
9
 
10
+ import { Obj } from '@dxos/echo';
10
11
  import { type AnyProperties } from '@dxos/echo/internal';
11
12
  import {
12
13
  type JsonPath,
@@ -20,6 +21,8 @@ import { useDefaultValue } from '@dxos/react-ui';
20
21
  import { type ValidationError, validateSchema } from '@dxos/schema';
21
22
  import { type MaybePromise } from '@dxos/util';
22
23
 
24
+ import { setValueEchoAware } from '../util';
25
+
23
26
  export type FormFieldStatus = {
24
27
  status?: 'error';
25
28
  error?: string;
@@ -267,7 +270,10 @@ export const useFormHandler = <T extends AnyProperties>({
267
270
  }
268
271
 
269
272
  // Update.
270
- const newValues = { ...setValue$(values, jsonPath, parsedValue) };
273
+ // For ECHO objects, mutate in place via Obj.change(); for plain objects, spread to create new reference.
274
+ const newValues = Obj.isObject(values)
275
+ ? (setValueEchoAware(values, jsonPath, parsedValue), values)
276
+ : { ...setValue$(values, jsonPath, parsedValue) };
271
277
  setValues(newValues);
272
278
 
273
279
  // TODO(burdon): Check value has changed from original.
@@ -0,0 +1,25 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Obj } from '@dxos/echo';
6
+ import { type JsonPath, setValue as setValuePlain, splitJsonPath } from '@dxos/effect';
7
+
8
+ /**
9
+ * Sets a value on an object, using Obj.change() if it's an ECHO object.
10
+ */
11
+ // TODO(wittjosiah): Consider refactoring Form to not know about ECHO at all.
12
+ // Instead of directly mutating the values object, Form could work with a plain
13
+ // object copy and communicate all mutations through callbacks (e.g., onValuesChanged).
14
+ // The caller would then be responsible for applying changes to ECHO objects via
15
+ // Obj.change(). This would keep Form agnostic to the underlying data layer and
16
+ // avoid coupling react-ui-form to @dxos/echo.
17
+ export const setValueEchoAware = <T extends object>(obj: T, path: JsonPath, value: any): T => {
18
+ if (Obj.isObject(obj)) {
19
+ Obj.change(obj, (mutableObj) => {
20
+ Obj.setValue(mutableObj, splitJsonPath(path), value);
21
+ });
22
+ return obj;
23
+ }
24
+ return setValuePlain(obj, path, value);
25
+ };
package/src/util/index.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ export * from './echo';
5
6
  export * from './field';
6
7
  export * from './formatting';
7
8
  export * from './properties';