@dxos/react-ui-form 0.8.4-main.84f28bd → 0.8.4-main.c1de068

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 (28) hide show
  1. package/dist/lib/browser/index.mjs +29 -35
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +29 -35
  5. package/dist/lib/node-esm/index.mjs.map +3 -3
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +3 -4
  8. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  9. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +2 -2
  10. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -2
  12. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  13. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +3 -3
  14. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/deprecated/Form.d.ts +3 -3
  16. package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
  17. package/dist/types/tsconfig.tsbuildinfo +1 -1
  18. package/package.json +29 -29
  19. package/src/components/FieldEditor/FieldEditor.stories.tsx +12 -10
  20. package/src/components/FieldEditor/FieldEditor.test.tsx +11 -11
  21. package/src/components/FieldEditor/FieldEditor.tsx +5 -15
  22. package/src/components/Form/ArrayField.tsx +1 -1
  23. package/src/components/Form/custom/SelectOptionsInput.tsx +1 -1
  24. package/src/components/ViewEditor/ViewEditor.stories.tsx +14 -8
  25. package/src/components/ViewEditor/ViewEditor.test.tsx +14 -12
  26. package/src/components/ViewEditor/ViewEditor.tsx +17 -32
  27. package/src/components/deprecated/Form.stories.tsx +3 -3
  28. package/src/components/deprecated/Form.tsx +4 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-form",
3
- "version": "0.8.4-main.84f28bd",
3
+ "version": "0.8.4-main.c1de068",
4
4
  "description": "Data components.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -29,48 +29,48 @@
29
29
  "@radix-ui/react-context": "1.1.1",
30
30
  "date-fns": "^3.3.1",
31
31
  "jsonpath": "^1.1.1",
32
- "@dxos/async": "0.8.4-main.84f28bd",
33
- "@dxos/debug": "0.8.4-main.84f28bd",
34
- "@dxos/echo": "0.8.4-main.84f28bd",
35
- "@dxos/echo-db": "0.8.4-main.84f28bd",
36
- "@dxos/echo-schema": "0.8.4-main.84f28bd",
37
- "@dxos/effect": "0.8.4-main.84f28bd",
38
- "@dxos/invariant": "0.8.4-main.84f28bd",
39
- "@dxos/keys": "0.8.4-main.84f28bd",
40
- "@dxos/lit-ui": "0.8.4-main.84f28bd",
41
- "@dxos/live-object": "0.8.4-main.84f28bd",
42
- "@dxos/log": "0.8.4-main.84f28bd",
43
- "@dxos/react-client": "0.8.4-main.84f28bd",
44
- "@dxos/react-ui-list": "0.8.4-main.84f28bd",
45
- "@dxos/react-ui-pickers": "0.8.4-main.84f28bd",
46
- "@dxos/react-ui-stack": "0.8.4-main.84f28bd",
47
- "@dxos/react-ui-tag-picker": "0.8.4-main.84f28bd",
48
- "@dxos/react-ui-types": "0.8.4-main.84f28bd",
49
- "@dxos/schema": "0.8.4-main.84f28bd",
50
- "@dxos/util": "0.8.4-main.84f28bd"
32
+ "@dxos/async": "0.8.4-main.c1de068",
33
+ "@dxos/debug": "0.8.4-main.c1de068",
34
+ "@dxos/echo": "0.8.4-main.c1de068",
35
+ "@dxos/echo-db": "0.8.4-main.c1de068",
36
+ "@dxos/echo-schema": "0.8.4-main.c1de068",
37
+ "@dxos/effect": "0.8.4-main.c1de068",
38
+ "@dxos/invariant": "0.8.4-main.c1de068",
39
+ "@dxos/keys": "0.8.4-main.c1de068",
40
+ "@dxos/live-object": "0.8.4-main.c1de068",
41
+ "@dxos/lit-ui": "0.8.4-main.c1de068",
42
+ "@dxos/log": "0.8.4-main.c1de068",
43
+ "@dxos/react-client": "0.8.4-main.c1de068",
44
+ "@dxos/react-ui-list": "0.8.4-main.c1de068",
45
+ "@dxos/react-ui-pickers": "0.8.4-main.c1de068",
46
+ "@dxos/react-ui-tag-picker": "0.8.4-main.c1de068",
47
+ "@dxos/react-ui-stack": "0.8.4-main.c1de068",
48
+ "@dxos/react-ui-types": "0.8.4-main.c1de068",
49
+ "@dxos/schema": "0.8.4-main.c1de068",
50
+ "@dxos/util": "0.8.4-main.c1de068"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/jsonpath": "^0.2.4",
54
54
  "@types/react": "~18.2.0",
55
55
  "@types/react-dom": "~18.2.0",
56
- "effect": "3.16.13",
56
+ "effect": "3.17.0",
57
57
  "fast-check": "^3.19.0",
58
58
  "react": "~18.2.0",
59
59
  "react-dom": "~18.2.0",
60
60
  "vite": "5.4.7",
61
- "@dxos/echo-signals": "0.8.4-main.84f28bd",
62
- "@dxos/random": "0.8.4-main.84f28bd",
63
- "@dxos/react-ui": "0.8.4-main.84f28bd",
64
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.84f28bd",
65
- "@dxos/react-ui-theme": "0.8.4-main.84f28bd",
66
- "@dxos/storybook-utils": "0.8.4-main.84f28bd"
61
+ "@dxos/echo-signals": "0.8.4-main.c1de068",
62
+ "@dxos/react-ui": "0.8.4-main.c1de068",
63
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.c1de068",
64
+ "@dxos/random": "0.8.4-main.c1de068",
65
+ "@dxos/react-ui-theme": "0.8.4-main.c1de068",
66
+ "@dxos/storybook-utils": "0.8.4-main.c1de068"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "effect": "^3.13.3",
70
70
  "react": "~18.2.0",
71
71
  "react-dom": "~18.2.0",
72
- "@dxos/react-ui": "0.8.4-main.84f28bd",
73
- "@dxos/react-ui-theme": "0.8.4-main.84f28bd"
72
+ "@dxos/react-ui": "0.8.4-main.c1de068",
73
+ "@dxos/react-ui-theme": "0.8.4-main.c1de068"
74
74
  },
75
75
  "publishConfig": {
76
76
  "access": "public"
@@ -9,7 +9,7 @@ import React, { useMemo, useEffect } from 'react';
9
9
 
10
10
  import { createEchoSchema } from '@dxos/live-object/testing';
11
11
  import { log } from '@dxos/log';
12
- import { ViewProjection } from '@dxos/schema';
12
+ import { ProjectionModel } from '@dxos/schema';
13
13
  import { TestSchema, testView } from '@dxos/schema/testing';
14
14
  import { withLayout, withTheme } from '@dxos/storybook-utils';
15
15
 
@@ -20,13 +20,12 @@ import { TestLayout, TestPanel, FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
20
20
  // Type definition for debug objects exposed to tests.
21
21
  export type FieldEditorDebugObjects = {
22
22
  props: FieldEditorProps;
23
- projection: ViewProjection;
23
+ projection: ProjectionModel;
24
24
  };
25
25
 
26
26
  type StoryProps = FieldEditorProps;
27
27
 
28
28
  const DefaultStory = (props: FieldEditorProps) => {
29
- const projection = useMemo(() => new ViewProjection(createEchoSchema(TestSchema).jsonSchema, testView), []);
30
29
  const handleComplete: FieldEditorProps['onSave'] = () => {
31
30
  log.info('onClose', { props });
32
31
  };
@@ -34,20 +33,23 @@ const DefaultStory = (props: FieldEditorProps) => {
34
33
  // Expose objects on window for test access.
35
34
  useEffect(() => {
36
35
  if (typeof window !== 'undefined') {
37
- (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = { props, projection } satisfies FieldEditorDebugObjects;
36
+ (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = {
37
+ props,
38
+ projection: props.projection,
39
+ } satisfies FieldEditorDebugObjects;
38
40
  }
39
- }, [props, projection]);
41
+ }, [props, props.projection]);
40
42
 
41
43
  // NOTE(ZaymonFC): This looks awkward but it resolves an infinite parsing issue with sb.
42
44
  const json = useMemo(
43
- () => JSON.parse(JSON.stringify({ props, projection })),
44
- [JSON.stringify(props), JSON.stringify(projection)],
45
+ () => JSON.parse(JSON.stringify({ props, projection: props.projection })),
46
+ [JSON.stringify(props), JSON.stringify(props.projection)],
45
47
  );
46
48
 
47
49
  return (
48
50
  <TestLayout json={json}>
49
51
  <TestPanel>
50
- <FieldEditor {...props} projection={projection} onSave={handleComplete} />
52
+ <FieldEditor {...props} onSave={handleComplete} />
51
53
  </TestPanel>
52
54
  </TestLayout>
53
55
  );
@@ -69,8 +71,8 @@ type Story = StoryObj<StoryProps>;
69
71
 
70
72
  export const Default: Story = {
71
73
  args: {
72
- view: testView,
73
- field: testView.fields[0],
74
+ projection: new ProjectionModel(createEchoSchema(TestSchema).jsonSchema, testView.projection),
75
+ field: testView.projection.fields[0],
74
76
  },
75
77
  parameters: { controls: { disabled: true } },
76
78
  };
@@ -6,7 +6,7 @@ import { composeStories } from '@storybook/react';
6
6
  import { screen, cleanup, fireEvent, act } from '@testing-library/react';
7
7
  import { afterEach, describe, expect, test } from 'vitest';
8
8
 
9
- import { ViewProjection } from '@dxos/schema';
9
+ import { ProjectionModel } from '@dxos/schema';
10
10
 
11
11
  import * as stories from './FieldEditor.stories';
12
12
  import { type FieldEditorDebugObjects } from './FieldEditor.stories';
@@ -14,14 +14,6 @@ import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
14
14
 
15
15
  const { Default } = composeStories(stories);
16
16
 
17
- const getFieldEditorDebugObjects = (): FieldEditorDebugObjects => {
18
- const debugObjects = (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] as FieldEditorDebugObjects;
19
- expect(debugObjects).toBeDefined();
20
- expect(debugObjects.props).toBeInstanceOf(Object); // Props object
21
- expect(debugObjects.projection).toBeInstanceOf(ViewProjection);
22
- return debugObjects;
23
- };
24
-
25
17
  describe('FieldEditor', () => {
26
18
  afterEach(() => {
27
19
  cleanup();
@@ -33,7 +25,7 @@ describe('FieldEditor', () => {
33
25
  expect(screen.getByText('Type format')).toBeInTheDocument();
34
26
  expect(screen.getByText('String')).toBeInTheDocument();
35
27
 
36
- // Verify the initial field shows as String type
28
+ // Verify the initial field shows as String type.
37
29
  expect(screen.getByText('String')).toBeInTheDocument();
38
30
 
39
31
  fireEvent.click(screen.getByRole('combobox'));
@@ -49,9 +41,17 @@ describe('FieldEditor', () => {
49
41
  // Access the live objects directly from window using symbol.
50
42
  const debugObjects = getFieldEditorDebugObjects();
51
43
 
52
- const name = debugObjects.projection.schema.properties!.name;
44
+ const name = debugObjects.projection.baseSchema.properties!.name;
53
45
  expect(name.type).toBe('number');
54
46
  expect(name.format).toBe('number');
55
47
  expect(name.description).toBe('Full name.');
56
48
  });
57
49
  });
50
+
51
+ const getFieldEditorDebugObjects = (): FieldEditorDebugObjects => {
52
+ const debugObjects = (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] as FieldEditorDebugObjects;
53
+ expect(debugObjects).toBeDefined();
54
+ expect(debugObjects.props).toBeInstanceOf(Object); // Props object
55
+ expect(debugObjects.projection).toBeInstanceOf(ProjectionModel);
56
+ return debugObjects;
57
+ };
@@ -14,8 +14,7 @@ import {
14
14
  sortProperties,
15
15
  type FieldType,
16
16
  type PropertyType,
17
- type ViewType,
18
- type ViewProjection,
17
+ type ProjectionModel,
19
18
  type SchemaProperty,
20
19
  } from '@dxos/schema';
21
20
 
@@ -23,8 +22,7 @@ import { translationKey } from '../../translations';
23
22
  import { Form, type FormProps, type InputComponent, SelectInput, SelectOptionInput } from '../Form';
24
23
 
25
24
  export type FieldEditorProps = {
26
- view: ViewType;
27
- projection: ViewProjection;
25
+ projection: ProjectionModel;
28
26
  field: FieldType;
29
27
  registry?: SchemaRegistry;
30
28
  onSave: () => void;
@@ -34,15 +32,7 @@ export type FieldEditorProps = {
34
32
  /**
35
33
  * Displays a Form representing the metadata for a given `Field` and `View`.
36
34
  */
37
- export const FieldEditor = ({
38
- view,
39
- projection,
40
- field,
41
- registry,
42
- onSave,
43
- onCancel,
44
- outerSpacing,
45
- }: FieldEditorProps) => {
35
+ export const FieldEditor = ({ projection, field, registry, onSave, onCancel, outerSpacing }: FieldEditorProps) => {
46
36
  const { t } = useTranslation(translationKey);
47
37
  const [props, setProps] = useState<PropertyType>(projection.getFieldProjection(field.id).props);
48
38
  useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
@@ -112,7 +102,7 @@ export const FieldEditor = ({
112
102
 
113
103
  const handleValidate = useCallback<NonNullable<FormProps<PropertyType>['onValidate']>>(
114
104
  ({ property }) => {
115
- if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
105
+ if (property && projection.fields.find((f) => f.path === property && f.path !== field.path)) {
116
106
  return [
117
107
  {
118
108
  path: 'property',
@@ -121,7 +111,7 @@ export const FieldEditor = ({
121
111
  ];
122
112
  }
123
113
  },
124
- [view.fields, field],
114
+ [projection.fields, field],
125
115
  );
126
116
 
127
117
  const handleSave = useCallback<NonNullable<FormProps<PropertyType>['onSave']>>(
@@ -113,7 +113,7 @@ export const ArrayField = ({ property, readonly, path, inputProps, Custom, looku
113
113
  </div>
114
114
  {!readonly && (
115
115
  <IconButton
116
- classNames='is-full mlb-1 flex'
116
+ classNames='is-full mlb-cardSpacingBlock flex'
117
117
  icon='ph--plus--regular'
118
118
  label={t('add field')}
119
119
  onClick={handleAdd}
@@ -123,7 +123,7 @@ export const SelectOptionInput = ({ type, label, disabled, getStatus, getValue,
123
123
  <List.Item
124
124
  role='button'
125
125
  item={item}
126
- classNames={mx('p-1 plb-2', 'flex flex-col', 'cursor-pointer', 'hover:bg-hoverOverlay')}
126
+ classNames={['p-1 plb-2', 'flex flex-col', 'cursor-pointer', 'hover:bg-hoverOverlay']}
127
127
  aria-expanded={selected === item.id}
128
128
  >
129
129
  <div className='flex items-center'>
@@ -8,11 +8,12 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import { Schema } from 'effect';
9
9
  import React, { useCallback, useMemo, useState, useEffect } from 'react';
10
10
 
11
+ import { Obj, Type } from '@dxos/echo';
11
12
  import { Format, type EchoSchema, toJsonSchema, TypedObject } from '@dxos/echo-schema';
12
13
  import { Filter, useQuery, useSpace } from '@dxos/react-client/echo';
13
14
  import { withClientProvider } from '@dxos/react-client/testing';
14
15
  import { useAsyncEffect } from '@dxos/react-ui';
15
- import { ViewProjection, ViewType, createView } from '@dxos/schema';
16
+ import { DataType, ProjectionModel, createView } from '@dxos/schema';
16
17
  import { withTheme, withLayout } from '@dxos/storybook-utils';
17
18
 
18
19
  import { ViewEditor, type ViewEditorProps } from './ViewEditor';
@@ -22,8 +23,8 @@ import { TestLayout, TestPanel, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
22
23
  // Type definition for debug objects exposed to tests.
23
24
  export type ViewEditorDebugObjects = {
24
25
  schema: EchoSchema;
25
- view: ViewType;
26
- projection: ViewProjection;
26
+ view: DataType.View;
27
+ projection: ProjectionModel;
27
28
  };
28
29
 
29
30
  type StoryProps = Pick<ViewEditorProps, 'readonly'>;
@@ -31,8 +32,8 @@ type StoryProps = Pick<ViewEditorProps, 'readonly'>;
31
32
  const DefaultStory = (props: StoryProps) => {
32
33
  const space = useSpace();
33
34
  const [schema, setSchema] = useState<EchoSchema>();
34
- const [view, setView] = useState<ViewType>();
35
- const [projection, setProjection] = useState<ViewProjection>();
35
+ const [view, setView] = useState<DataType.View>();
36
+ const [projection, setProjection] = useState<ProjectionModel>();
36
37
  useAsyncEffect(async () => {
37
38
  if (space) {
38
39
  class TestSchema extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
@@ -42,8 +43,13 @@ const DefaultStory = (props: StoryProps) => {
42
43
  }) {}
43
44
 
44
45
  const [schema] = await space.db.schemaRegistry.register([TestSchema]);
45
- const view = createView({ name: 'Test', typename: schema.typename, jsonSchema: toJsonSchema(TestSchema) });
46
- const projection = new ViewProjection(schema.jsonSchema, view);
46
+ const view = createView({
47
+ name: 'Test',
48
+ typename: schema.typename,
49
+ jsonSchema: toJsonSchema(TestSchema),
50
+ presentation: Obj.make(Type.Expando, {}),
51
+ });
52
+ const projection = new ProjectionModel(schema.jsonSchema, view.projection);
47
53
 
48
54
  setSchema(schema);
49
55
  setView(view);
@@ -51,7 +57,7 @@ const DefaultStory = (props: StoryProps) => {
51
57
  }
52
58
  }, [space]);
53
59
 
54
- const views = useQuery(space, Filter.type(ViewType));
60
+ const views = useQuery(space, Filter.type(DataType.View));
55
61
  const currentTypename = useMemo(() => view?.query?.typename, [view]);
56
62
  const updateViewTypename = useCallback(
57
63
  (newTypename: string) => {
@@ -6,8 +6,8 @@ import { composeStories } from '@storybook/react';
6
6
  import { screen, cleanup, fireEvent, waitFor } from '@testing-library/react';
7
7
  import { afterEach, describe, expect, test } from 'vitest';
8
8
 
9
- import { EchoSchema } from '@dxos/echo-schema';
10
- import { ViewProjection, ViewType } from '@dxos/schema';
9
+ import { EchoSchema, isInstanceOf } from '@dxos/echo-schema';
10
+ import { DataType, ProjectionModel } from '@dxos/schema';
11
11
 
12
12
  import * as stories from './ViewEditor.stories';
13
13
  import { type ViewEditorDebugObjects } from './ViewEditor.stories';
@@ -19,8 +19,8 @@ const getViewEditorDebugObjects = (): ViewEditorDebugObjects => {
19
19
  const debugObjects = (window as any)[VIEW_EDITOR_DEBUG_SYMBOL] as ViewEditorDebugObjects;
20
20
  expect(debugObjects).toBeDefined();
21
21
  expect(debugObjects.schema).toBeInstanceOf(EchoSchema);
22
- expect(debugObjects.view).toBeInstanceOf(ViewType);
23
- expect(debugObjects.projection).toBeInstanceOf(ViewProjection);
22
+ expect(isInstanceOf(DataType.View, debugObjects.view)).toBeTruthy();
23
+ expect(debugObjects.projection).toBeInstanceOf(ProjectionModel);
24
24
  return debugObjects;
25
25
  };
26
26
 
@@ -41,7 +41,7 @@ describe('ViewEditor', () => {
41
41
  test('renders view editor', async () => {
42
42
  await Default.run();
43
43
  await waitForViewEditor();
44
- expect(screen.getByText('View')).toBeInTheDocument();
44
+ expect(screen.getByText('Typename')).toBeInTheDocument();
45
45
  });
46
46
 
47
47
  test('change property name', async () => {
@@ -73,7 +73,7 @@ describe('ViewEditor', () => {
73
73
  expect(schemaProperties!.name).toBeUndefined();
74
74
 
75
75
  // Check view contains new_property field.
76
- const newPropertyField = debugObjects.view.fields.find((field: any) => field.path === 'new_property');
76
+ const newPropertyField = debugObjects.view.projection.fields.find((field: any) => field.path === 'new_property');
77
77
  expect(newPropertyField).toBeDefined();
78
78
  expect(newPropertyField!.path).toBe('new_property');
79
79
 
@@ -120,7 +120,9 @@ describe('ViewEditor', () => {
120
120
  expect(schemaProperties!.added_property).toBeDefined();
121
121
 
122
122
  // Check view contains added_property field.
123
- const addedPropertyField = debugObjects.view.fields.find((field: any) => field.path === 'added_property');
123
+ const addedPropertyField = debugObjects.view.projection.fields.find(
124
+ (field: any) => field.path === 'added_property',
125
+ );
124
126
  expect(addedPropertyField).toBeDefined();
125
127
  expect(addedPropertyField!.path).toBe('added_property');
126
128
 
@@ -161,7 +163,7 @@ describe('ViewEditor', () => {
161
163
  expect(schemaProperties!.name).toBeUndefined();
162
164
 
163
165
  // Check view no longer contains the name field.
164
- const nameField = debugObjects.view.fields.find((field: any) => field.path === 'name');
166
+ const nameField = debugObjects.view.projection.fields.find((field: any) => field.path === 'name');
165
167
  expect(nameField).toBeUndefined();
166
168
 
167
169
  // Check projection no longer contains the name property.
@@ -187,18 +189,18 @@ describe('ViewEditor', () => {
187
189
  const debugObjects = getViewEditorDebugObjects();
188
190
 
189
191
  // Check that hiddenFields is non-empty.
190
- expect(debugObjects.view.hiddenFields).toBeDefined();
191
- expect(debugObjects.view.hiddenFields!.length).toBeGreaterThan(0);
192
+ expect(debugObjects.view.projection.hiddenFields).toBeDefined();
193
+ expect(debugObjects.view.projection.hiddenFields!.length).toBeGreaterThan(0);
192
194
 
193
195
  // Click the show button
194
196
  fireEvent.click(screen.getByTestId('show-field-button'));
195
197
 
196
198
  // Wait for the data to update and check that hiddenFields is empty.
197
199
  await waitFor(() => {
198
- expect(debugObjects.view.hiddenFields!.length).toBe(0);
200
+ expect(debugObjects.view.projection.hiddenFields!.length).toBe(0);
199
201
  });
200
202
 
201
203
  // Also verify that the field is back in the visible fields list.
202
- expect(debugObjects.view.fields.length).toBeGreaterThan(0);
204
+ expect(debugObjects.view.projection.fields.length).toBeGreaterThan(0);
203
205
  });
204
206
  });
@@ -12,7 +12,7 @@ import { Callout, IconButton, type ThemedClassName, useTranslation } from '@dxos
12
12
  import { List } from '@dxos/react-ui-list';
13
13
  import { cardSpacing } from '@dxos/react-ui-stack';
14
14
  import { ghostHover, inputTextLabel, mx } from '@dxos/react-ui-theme';
15
- import { FieldSchema, type FieldType, type ViewType, ViewProjection, VIEW_FIELD_LIMIT } from '@dxos/schema';
15
+ import { FieldSchema, type FieldType, VIEW_FIELD_LIMIT, type DataType, ProjectionModel } from '@dxos/schema';
16
16
 
17
17
  import { translationKey } from '../../translations';
18
18
  import { FieldEditor } from '../FieldEditor';
@@ -21,9 +21,6 @@ import { Form, type FormProps } from '../Form';
21
21
  const grid = 'grid grid-cols-[32px_1fr_32px_32px] min-bs-[2.5rem]';
22
22
 
23
23
  const ViewMetaSchema = Schema.Struct({
24
- name: Schema.String.annotations({
25
- title: 'View',
26
- }),
27
24
  typename: Format.URL.annotations({
28
25
  title: 'Typename',
29
26
  }),
@@ -34,7 +31,7 @@ type ViewMetaType = Schema.Schema.Type<typeof ViewMetaSchema>;
34
31
  export type ViewEditorProps = ThemedClassName<
35
32
  {
36
33
  schema: Schema.Schema.AnyNoContext;
37
- view: ViewType;
34
+ view: DataType.View;
38
35
  registry?: SchemaRegistry;
39
36
  readonly?: boolean;
40
37
  showHeading?: boolean;
@@ -61,20 +58,19 @@ export const ViewEditor = ({
61
58
  const projection = useMemo(() => {
62
59
  // Use reactive and mutable version of json schema when schema is mutable.
63
60
  const jsonSchema = schema instanceof EchoSchema ? schema.jsonSchema : toJsonSchema(schema);
64
- return new ViewProjection(jsonSchema, view);
65
- }, [schema, view]);
61
+ return new ProjectionModel(jsonSchema, view.projection);
62
+ }, [schema, view.projection]);
66
63
  const [field, setField] = useState<FieldType>();
67
64
  const readonly = _readonly || !isMutable(schema);
68
65
 
69
66
  // TODO(burdon): Should be reactive.
70
67
  const viewValues = useMemo(() => {
71
68
  return {
72
- name: view.name,
73
69
  // TODO(burdon): Need to warn user of possible consequences of editing.
74
70
  // TODO(burdon): Settings should have domain name owned by user.
75
71
  typename: view.query.typename,
76
72
  };
77
- }, [view]);
73
+ }, [view.query.typename]);
78
74
 
79
75
  const handleSelect = useCallback(
80
76
  (field: FieldType) => {
@@ -92,22 +88,18 @@ export const ViewEditor = ({
92
88
  invariant(!readonly);
93
89
  const field = projection.createFieldProjection();
94
90
  setField(field);
95
- }, [schema, view, readonly]);
91
+ }, [schema, projection, readonly]);
96
92
 
97
93
  const handleUpdate = useCallback(
98
- ({ name, typename }: ViewMetaType) => {
94
+ ({ typename }: ViewMetaType) => {
99
95
  invariant(!readonly);
100
96
  requestAnimationFrame(() => {
101
- if (view.name !== name) {
102
- view.name = name;
103
- }
104
-
105
97
  if (view.query.typename !== typename && !readonly) {
106
98
  onTypenameChanged?.(typename);
107
99
  }
108
100
  });
109
101
  },
110
- [schema, view, onTypenameChanged, readonly],
102
+ [view.query.typename, onTypenameChanged, readonly],
111
103
  );
112
104
 
113
105
  const handleDelete = useCallback(
@@ -119,19 +111,19 @@ export const ViewEditor = ({
119
111
 
120
112
  onDelete?.(fieldId);
121
113
  },
122
- [schema, field, onDelete, readonly],
114
+ [field, onDelete, readonly],
123
115
  );
124
116
 
125
117
  const handleMove = useCallback(
126
118
  (fromIndex: number, toIndex: number) => {
127
119
  invariant(!readonly);
128
120
  // NOTE(ZaymonFC): Using arrayMove here causes a race condition with the kanban model.
129
- const fields = [...view.fields];
121
+ const fields = [...view.projection.fields];
130
122
  const [moved] = fields.splice(fromIndex, 1);
131
123
  fields.splice(toIndex, 0, moved);
132
- view.fields = fields;
124
+ view.projection.fields = fields;
133
125
  },
134
- [schema, view.fields, readonly],
126
+ [view.projection.fields, readonly],
135
127
  );
136
128
 
137
129
  const handleClose = useCallback(() => setField(undefined), []);
@@ -179,7 +171,7 @@ export const ViewEditor = ({
179
171
  <label className={mx(inputTextLabel)}>{t('fields label')}</label>
180
172
 
181
173
  <List.Root<FieldType>
182
- items={view.fields}
174
+ items={view.projection.fields}
183
175
  isItem={Schema.is(FieldSchema)}
184
176
  getId={(field) => field.id}
185
177
  readonly={readonly}
@@ -207,13 +199,13 @@ export const ViewEditor = ({
207
199
  data-testid='hide-field-button'
208
200
  icon='ph--eye-slash--regular'
209
201
  // TDOO(burdon): Is this the correct test?
210
- disabled={view.fields.length <= 1}
202
+ disabled={view.projection.fields.length <= 1}
211
203
  onClick={() => handleHide(field.id)}
212
204
  />
213
205
  {/* TODO(burdon): Remove unless implement undo. */}
214
206
  {!readonly && (
215
207
  <List.ItemDeleteButton
216
- disabled={view.fields.length <= 1}
208
+ disabled={view.projection.fields.length <= 1}
217
209
  onClick={() => handleDelete(field.id)}
218
210
  />
219
211
  )}
@@ -258,14 +250,7 @@ export const ViewEditor = ({
258
250
  </div>
259
251
 
260
252
  {field && (
261
- <FieldEditor
262
- key={field.id}
263
- view={view}
264
- projection={projection}
265
- field={field}
266
- registry={registry}
267
- onSave={handleClose}
268
- />
253
+ <FieldEditor key={field.id} projection={projection} field={field} registry={registry} onSave={handleClose} />
269
254
  )}
270
255
 
271
256
  {!readonly && !field && (
@@ -275,7 +260,7 @@ export const ViewEditor = ({
275
260
  label={t('button add property')}
276
261
  onClick={readonly ? undefined : handleAdd}
277
262
  // TODO(burdon): Show field limit in ux (not tooltip).
278
- disabled={view.fields.length >= VIEW_FIELD_LIMIT}
263
+ disabled={view.projection.fields.length >= VIEW_FIELD_LIMIT}
279
264
  classNames='flex is-full'
280
265
  />
281
266
  </div>
@@ -40,14 +40,14 @@ type Story = StoryObj<DeprecatedFormProps<TestType>>;
40
40
  export const Default: Story = {
41
41
  args: {
42
42
  object: live(testData),
43
- view: live(testView),
43
+ projection: live(testView).projection,
44
44
  schema: TestSchema,
45
45
  },
46
46
  };
47
47
 
48
48
  export const Empty: Story = {
49
49
  args: {
50
- view: live(testView),
50
+ projection: live(testView).projection,
51
51
  schema: TestSchema,
52
52
  },
53
53
  };
@@ -55,7 +55,7 @@ export const Empty: Story = {
55
55
  export const Readonly: Story = {
56
56
  args: {
57
57
  object: live(testData),
58
- view: live(testView),
58
+ projection: live(testView).projection,
59
59
  schema: TestSchema,
60
60
  readonly: true,
61
61
  },
@@ -8,10 +8,10 @@ import React, { useState } from 'react';
8
8
  import { findProperty } from '@dxos/effect';
9
9
  import { Input, type ThemedClassName, type TextInputProps as NativeTextInputProps } from '@dxos/react-ui';
10
10
  import { mx } from '@dxos/react-ui-theme';
11
- import { type ViewType, getFieldValue, setFieldValue } from '@dxos/schema';
11
+ import { getFieldValue, setFieldValue, type Projection } from '@dxos/schema';
12
12
 
13
13
  export type DeprecatedFormProps<T extends {} = {}> = ThemedClassName<{
14
- view: ViewType;
14
+ projection: Projection;
15
15
  object: T;
16
16
  schema?: Schema.Schema<T>;
17
17
  readonly?: boolean;
@@ -22,14 +22,14 @@ export type DeprecatedFormProps<T extends {} = {}> = ThemedClassName<{
22
22
  */
23
23
  export const DeprecatedForm = <T extends {} = {}>({
24
24
  classNames,
25
- view,
25
+ projection,
26
26
  object,
27
27
  schema,
28
28
  readonly,
29
29
  }: DeprecatedFormProps<T>) => {
30
30
  return (
31
31
  <div role='none' className={mx('flex flex-col w-full gap-2 p-2', classNames)}>
32
- {view.fields.map((field) => {
32
+ {projection.fields.map((field) => {
33
33
  const prop = schema && findProperty(schema, field.path);
34
34
  const title = (prop && pipe(SchemaAST.getTitleAnnotation(prop), Option.getOrUndefined)) ?? '';
35
35
  const description = (prop && pipe(SchemaAST.getDescriptionAnnotation(prop), Option.getOrUndefined)) ?? title;