@dxos/react-ui-form 0.8.3 → 0.8.4-main.1f223c7

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 (103) hide show
  1. package/dist/lib/browser/index.mjs +648 -581
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +648 -581
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/ControlSection/ControlSection.d.ts +2 -2
  8. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -1
  9. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +79 -4
  10. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +4 -5
  12. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  13. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +76 -6
  14. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/ArrayField.d.ts +2 -2
  16. package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
  17. package/dist/types/src/components/Form/Defaults.d.ts +5 -5
  18. package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/Form.d.ts +5 -4
  20. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/Form.stories.d.ts +126 -30
  22. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  23. package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
  24. package/dist/types/src/components/Form/FormContent.d.ts +16 -10
  25. package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
  26. package/dist/types/src/components/Form/FormContext.d.ts +1 -1
  27. package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/Input.d.ts +4 -1
  29. package/dist/types/src/components/Form/Input.d.ts.map +1 -1
  30. package/dist/types/src/components/Form/RefField.d.ts +10 -4
  31. package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
  32. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -1
  33. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
  34. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +1 -1
  35. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
  36. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +74 -3
  37. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -1
  39. package/dist/types/src/components/Form/factory.d.ts.map +1 -1
  40. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +5 -4
  41. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  42. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +78 -7
  43. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  44. package/dist/types/src/components/index.d.ts +0 -1
  45. package/dist/types/src/components/index.d.ts.map +1 -1
  46. package/dist/types/src/components/testing.d.ts +6 -3
  47. package/dist/types/src/components/testing.d.ts.map +1 -1
  48. package/dist/types/src/hooks/useForm.d.ts +1 -1
  49. package/dist/types/src/hooks/useForm.d.ts.map +1 -1
  50. package/dist/types/src/index.d.ts +1 -1
  51. package/dist/types/src/index.d.ts.map +1 -1
  52. package/dist/types/src/translations.d.ts +61 -57
  53. package/dist/types/src/translations.d.ts.map +1 -1
  54. package/dist/types/src/util/field.d.ts.map +1 -1
  55. package/dist/types/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +36 -33
  57. package/src/components/ControlSection/ControlSection.stories.tsx +10 -12
  58. package/src/components/ControlSection/ControlSection.tsx +26 -12
  59. package/src/components/FieldEditor/FieldEditor.stories.tsx +21 -20
  60. package/src/components/FieldEditor/FieldEditor.test.tsx +14 -13
  61. package/src/components/FieldEditor/FieldEditor.tsx +17 -22
  62. package/src/components/Form/ArrayField.tsx +9 -9
  63. package/src/components/Form/Defaults.tsx +22 -20
  64. package/src/components/Form/Form.stories.tsx +131 -95
  65. package/src/components/Form/Form.tsx +16 -12
  66. package/src/components/Form/FormActions.tsx +2 -1
  67. package/src/components/Form/FormContent.tsx +96 -34
  68. package/src/components/Form/FormContext.tsx +5 -5
  69. package/src/components/Form/Input.tsx +4 -1
  70. package/src/components/Form/RefField.tsx +126 -40
  71. package/src/components/Form/custom/GeoPointInput.tsx +29 -21
  72. package/src/components/Form/custom/SelectOptionsInput.stories.tsx +9 -8
  73. package/src/components/Form/custom/SelectOptionsInput.tsx +29 -30
  74. package/src/components/Form/custom/TupleInput.tsx +2 -2
  75. package/src/components/Form/factory.tsx +1 -1
  76. package/src/components/ViewEditor/ViewEditor.stories.tsx +70 -32
  77. package/src/components/ViewEditor/ViewEditor.test.tsx +38 -39
  78. package/src/components/ViewEditor/ViewEditor.tsx +159 -155
  79. package/src/components/index.ts +0 -2
  80. package/src/components/testing.tsx +15 -9
  81. package/src/hooks/useForm.ts +5 -3
  82. package/src/hooks/useRefQueryLookupHandler.ts +1 -1
  83. package/src/index.ts +1 -1
  84. package/src/translations.ts +15 -7
  85. package/src/util/field.ts +8 -18
  86. package/dist/lib/node/index.cjs +0 -2416
  87. package/dist/lib/node/index.cjs.map +0 -7
  88. package/dist/lib/node/meta.json +0 -1
  89. package/dist/types/src/components/ViewEditor/Test.stories.d.ts +0 -10
  90. package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +0 -1
  91. package/dist/types/src/components/deprecated/Form.d.ts +0 -15
  92. package/dist/types/src/components/deprecated/Form.d.ts.map +0 -1
  93. package/dist/types/src/components/deprecated/Form.stories.d.ts +0 -13
  94. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +0 -1
  95. package/dist/types/src/components/deprecated/FormInput.d.ts +0 -16
  96. package/dist/types/src/components/deprecated/FormInput.d.ts.map +0 -1
  97. package/dist/types/src/components/deprecated/index.d.ts +0 -2
  98. package/dist/types/src/components/deprecated/index.d.ts.map +0 -1
  99. package/src/components/ViewEditor/Test.stories.tsx +0 -45
  100. package/src/components/deprecated/Form.stories.tsx +0 -62
  101. package/src/components/deprecated/Form.tsx +0 -119
  102. package/src/components/deprecated/FormInput.tsx +0 -71
  103. package/src/components/deprecated/index.ts +0 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-form",
3
- "version": "0.8.3",
3
+ "version": "0.8.4-main.1f223c7",
4
4
  "description": "Data components.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -9,6 +9,7 @@
9
9
  "type": "module",
10
10
  "exports": {
11
11
  ".": {
12
+ "source": "./src/index.ts",
12
13
  "types": "./dist/types/src/index.d.ts",
13
14
  "browser": "./dist/lib/browser/index.mjs",
14
15
  "node": "./dist/lib/node-esm/index.mjs"
@@ -25,54 +26,56 @@
25
26
  "dependencies": {
26
27
  "@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
27
28
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
29
+ "@fluentui/react-tabster": "^9.24.2",
28
30
  "@preact-signals/safe-react": "^0.9.0",
29
31
  "@radix-ui/react-context": "1.1.1",
30
32
  "date-fns": "^3.3.1",
31
33
  "jsonpath": "^1.1.1",
32
- "@dxos/debug": "0.8.3",
33
- "@dxos/echo": "0.8.3",
34
- "@dxos/effect": "0.8.3",
35
- "@dxos/echo-db": "0.8.3",
36
- "@dxos/async": "0.8.3",
37
- "@dxos/invariant": "0.8.3",
38
- "@dxos/echo-schema": "0.8.3",
39
- "@dxos/keys": "0.8.3",
40
- "@dxos/lit-ui": "0.8.3",
41
- "@dxos/live-object": "0.8.3",
42
- "@dxos/log": "0.8.3",
43
- "@dxos/react-client": "0.8.3",
44
- "@dxos/react-ui-list": "0.8.3",
45
- "@dxos/react-ui-pickers": "0.8.3",
46
- "@dxos/react-ui-stack": "0.8.3",
47
- "@dxos/react-ui-tag-picker": "0.8.3",
48
- "@dxos/react-ui-types": "0.8.3",
49
- "@dxos/util": "0.8.3",
50
- "@dxos/schema": "0.8.3"
34
+ "@dxos/async": "0.8.4-main.1f223c7",
35
+ "@dxos/debug": "0.8.4-main.1f223c7",
36
+ "@dxos/echo": "0.8.4-main.1f223c7",
37
+ "@dxos/echo-db": "0.8.4-main.1f223c7",
38
+ "@dxos/echo-schema": "0.8.4-main.1f223c7",
39
+ "@dxos/effect": "0.8.4-main.1f223c7",
40
+ "@dxos/keys": "0.8.4-main.1f223c7",
41
+ "@dxos/invariant": "0.8.4-main.1f223c7",
42
+ "@dxos/live-object": "0.8.4-main.1f223c7",
43
+ "@dxos/log": "0.8.4-main.1f223c7",
44
+ "@dxos/react-client": "0.8.4-main.1f223c7",
45
+ "@dxos/react-ui-components": "0.8.4-main.1f223c7",
46
+ "@dxos/react-ui-editor": "0.8.4-main.1f223c7",
47
+ "@dxos/react-ui-list": "0.8.4-main.1f223c7",
48
+ "@dxos/react-ui-pickers": "0.8.4-main.1f223c7",
49
+ "@dxos/react-ui-searchlist": "0.8.4-main.1f223c7",
50
+ "@dxos/react-ui-stack": "0.8.4-main.1f223c7",
51
+ "@dxos/lit-ui": "0.8.4-main.1f223c7",
52
+ "@dxos/react-ui-tag-picker": "0.8.4-main.1f223c7",
53
+ "@dxos/react-ui-types": "0.8.4-main.1f223c7",
54
+ "@dxos/schema": "0.8.4-main.1f223c7",
55
+ "@dxos/util": "0.8.4-main.1f223c7"
51
56
  },
52
57
  "devDependencies": {
53
- "@phosphor-icons/react": "^2.1.5",
54
58
  "@types/jsonpath": "^0.2.4",
55
59
  "@types/react": "~18.2.0",
56
60
  "@types/react-dom": "~18.2.0",
57
- "effect": "3.14.21",
61
+ "effect": "3.17.7",
58
62
  "fast-check": "^3.19.0",
59
63
  "react": "~18.2.0",
60
64
  "react-dom": "~18.2.0",
61
- "vite": "5.4.7",
62
- "@dxos/echo-signals": "0.8.3",
63
- "@dxos/random": "0.8.3",
64
- "@dxos/react-ui": "0.8.3",
65
- "@dxos/react-ui-syntax-highlighter": "0.8.3",
66
- "@dxos/react-ui-theme": "0.8.3",
67
- "@dxos/storybook-utils": "0.8.3"
65
+ "vite": "7.1.1",
66
+ "@dxos/random": "0.8.4-main.1f223c7",
67
+ "@dxos/react-ui": "0.8.4-main.1f223c7",
68
+ "@dxos/react-ui-theme": "0.8.4-main.1f223c7",
69
+ "@dxos/storybook-utils": "0.8.4-main.1f223c7",
70
+ "@dxos/echo-signals": "0.8.4-main.1f223c7",
71
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.1f223c7"
68
72
  },
69
73
  "peerDependencies": {
70
- "@phosphor-icons/react": "^2.1.5",
71
- "effect": "3.13.3",
74
+ "effect": "^3.13.3",
72
75
  "react": "~18.2.0",
73
76
  "react-dom": "~18.2.0",
74
- "@dxos/react-ui": "0.8.3",
75
- "@dxos/react-ui-theme": "0.8.3"
77
+ "@dxos/react-ui": "0.8.4-main.1f223c7",
78
+ "@dxos/react-ui-theme": "0.8.4-main.1f223c7"
76
79
  },
77
80
  "publishConfig": {
78
81
  "access": "public"
@@ -4,34 +4,32 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type Meta, type StoryObj } from '@storybook/react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import React from 'react';
9
9
 
10
- import { StackItem } from '@dxos/react-ui-stack';
11
10
  import { withLayout, withTheme } from '@dxos/storybook-utils';
12
11
 
13
- import { ControlPage, ControlSection, type ControlSectionProps } from './ControlSection';
14
- import translations from '../../translations';
12
+ import { translations } from '../../translations';
15
13
 
16
- const meta: Meta<ControlSectionProps> = {
14
+ import { ControlPage, ControlSection } from './ControlSection';
15
+
16
+ const meta = {
17
17
  title: 'ui/react-ui-form/ControlSection',
18
18
  component: ControlSection,
19
19
  render: (args) => (
20
- <StackItem.Content classNames='w-[40rem]'>
21
- <ControlPage>
22
- <ControlSection {...args} />
23
- </ControlPage>
24
- </StackItem.Content>
20
+ <ControlPage classNames='w-[40rem]'>
21
+ <ControlSection {...args} />
22
+ </ControlPage>
25
23
  ),
26
24
  decorators: [withLayout({ fullscreen: true, classNames: 'justify-center' }), withTheme],
27
25
  parameters: {
28
26
  translations,
29
27
  },
30
- };
28
+ } satisfies Meta<typeof ControlSection>;
31
29
 
32
30
  export default meta;
33
31
 
34
- type Story = StoryObj<ControlSectionProps>;
32
+ type Story = StoryObj<typeof meta>;
35
33
 
36
34
  export const Default: Story = {
37
35
  args: {
@@ -13,6 +13,7 @@ import {
13
13
  toLocalizedString,
14
14
  useTranslation,
15
15
  } from '@dxos/react-ui';
16
+ import { StackItem } from '@dxos/react-ui-stack';
16
17
  import { mx } from '@dxos/react-ui-theme';
17
18
 
18
19
  import { translationKey } from '../../translations';
@@ -21,9 +22,11 @@ export type ControlPageProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
21
22
 
22
23
  export const ControlPage = ({ children, classNames, ...props }: ControlPageProps) => {
23
24
  return (
24
- <div role='none' className={mx('pli-cardSpacingInline', classNames)} {...props}>
25
- {children}
26
- </div>
25
+ <StackItem.Content classNames='block overflow-y-auto [--control-spacing:var(--dx-trimMd)]'>
26
+ <div role='none' className={mx('pli-cardSpacingInline pbe-trimLg', classNames)} {...props}>
27
+ {children}
28
+ </div>
29
+ </StackItem.Content>
27
30
  );
28
31
  };
29
32
 
@@ -59,12 +62,16 @@ export const ControlGroupButton = ({ classNames, ...props }: ButtonProps) => {
59
62
  return <Button {...props} classNames={['md:col-span-2', classNames]} />;
60
63
  };
61
64
 
62
- export type ControlGroupProps = ThemedClassName<PropsWithChildren<{}>>;
65
+ export type ControlGroupProps = ThemedClassName<PropsWithChildren>;
63
66
 
64
67
  export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
65
68
  <div
66
69
  role='none'
67
- className={mx('group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-trimMd', classNames)}
70
+ className={mx(
71
+ 'group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-trimMd',
72
+ '[--control-spacing:0px] [&_input]:justify-self-end [&_button]:justify-self-end',
73
+ classNames,
74
+ )}
68
75
  >
69
76
  {children}
70
77
  </div>
@@ -73,23 +80,30 @@ export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
73
80
  export const ControlFrame = ({ children }: ControlGroupProps) => (
74
81
  <div
75
82
  role='none'
76
- className='p-trimMd container-max-width grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-trimSm md:gap-trimMd border border-separator rounded-md'
83
+ className={mx(
84
+ 'container-max-width grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-trimSm md:gap-trimMd p-trimMd',
85
+ 'border border-separator rounded-md',
86
+ )}
77
87
  >
78
88
  {children}
79
89
  </div>
80
90
  );
81
91
 
92
+ export const controlItemClasses = mx([
93
+ 'container-max-width grid md:col-span-2 grid-cols-subgrid gap-trimSm items-center',
94
+ // TODO(burdon): Use grid gap consistently or apply margins consistently?
95
+ 'mlb-[--control-spacing] *:first:!mbs-0 *:last:!mbe-0 pli-trimMd plb-trimMd',
96
+ 'border border-separator rounded-md',
97
+ ]);
98
+
99
+ const controlItemTitleClasses = 'mbe-0 text-lg text-baseText font-normal';
100
+ const controlItemDescriptionClasses = 'mlb-trimSm md:mbe-0 text-base text-description';
101
+
82
102
  export type ControlItemProps = PropsWithChildren<{
83
103
  title: Label;
84
104
  description?: Label;
85
105
  }>;
86
106
 
87
- export const controlItemClasses =
88
- 'pli-trimMd plb-trimMd container-max-width grid md:col-span-2 grid-cols-subgrid items-center gap-trimSm border border-separator rounded-md *:first:!mbs-0 *:last:!mbe-0';
89
-
90
- const controlItemTitleClasses = 'text-lg font-normal';
91
- const controlItemDescriptionClasses = 'text-base mlb-trimSm md:mbe-0 text-description';
92
-
93
107
  export const ControlItem = ({ title, description, children }: ControlItemProps) => {
94
108
  const { t } = useTranslation(translationKey);
95
109
 
@@ -4,29 +4,27 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type Meta, type StoryObj } from '@storybook/react';
8
- import React, { useMemo, useEffect } from 'react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
+ import React, { useEffect, useMemo } 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
 
16
+ import { translations } from '../../translations';
17
+ import { FIELD_EDITOR_DEBUG_SYMBOL, TestLayout, TestPanel } from '../testing';
18
+
16
19
  import { FieldEditor, type FieldEditorProps } from './FieldEditor';
17
- import translations from '../../translations';
18
- import { TestLayout, TestPanel, FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
19
20
 
20
21
  // Type definition for debug objects exposed to tests.
21
22
  export type FieldEditorDebugObjects = {
22
23
  props: FieldEditorProps;
23
- projection: ViewProjection;
24
+ projection: ProjectionModel;
24
25
  };
25
26
 
26
- type StoryProps = FieldEditorProps;
27
-
28
27
  const DefaultStory = (props: FieldEditorProps) => {
29
- const projection = useMemo(() => new ViewProjection(createEchoSchema(TestSchema).jsonSchema, testView), []);
30
28
  const handleComplete: FieldEditorProps['onSave'] = () => {
31
29
  log.info('onClose', { props });
32
30
  };
@@ -34,43 +32,46 @@ const DefaultStory = (props: FieldEditorProps) => {
34
32
  // Expose objects on window for test access.
35
33
  useEffect(() => {
36
34
  if (typeof window !== 'undefined') {
37
- (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = { props, projection } satisfies FieldEditorDebugObjects;
35
+ (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = {
36
+ props,
37
+ projection: props.projection,
38
+ } satisfies FieldEditorDebugObjects;
38
39
  }
39
- }, [props, projection]);
40
+ }, [props, props.projection]);
40
41
 
41
42
  // NOTE(ZaymonFC): This looks awkward but it resolves an infinite parsing issue with sb.
42
43
  const json = useMemo(
43
- () => JSON.parse(JSON.stringify({ props, projection })),
44
- [JSON.stringify(props), JSON.stringify(projection)],
44
+ () => JSON.parse(JSON.stringify({ props, projection: props.projection })),
45
+ [JSON.stringify(props), JSON.stringify(props.projection)],
45
46
  );
46
47
 
47
48
  return (
48
49
  <TestLayout json={json}>
49
50
  <TestPanel>
50
- <FieldEditor {...props} projection={projection} onSave={handleComplete} />
51
+ <FieldEditor {...props} onSave={handleComplete} />
51
52
  </TestPanel>
52
53
  </TestLayout>
53
54
  );
54
55
  };
55
56
 
56
- const meta: Meta<StoryProps> = {
57
+ const meta = {
57
58
  title: 'ui/react-ui-form/FieldEditor',
58
- component: FieldEditor,
59
+ component: FieldEditor as any,
59
60
  render: DefaultStory,
60
61
  decorators: [withLayout({ fullscreen: true }), withTheme],
61
62
  parameters: {
62
63
  translations,
63
64
  },
64
- };
65
+ } satisfies Meta<typeof DefaultStory>;
65
66
 
66
67
  export default meta;
67
68
 
68
- type Story = StoryObj<StoryProps>;
69
+ type Story = StoryObj<typeof meta>;
69
70
 
70
71
  export const Default: Story = {
71
72
  args: {
72
- view: testView,
73
- field: testView.fields[0],
73
+ projection: new ProjectionModel(createEchoSchema(TestSchema).jsonSchema, testView.projection),
74
+ field: testView.projection.fields[0],
74
75
  },
75
76
  parameters: { controls: { disabled: true } },
76
77
  };
@@ -3,25 +3,18 @@
3
3
  //
4
4
 
5
5
  import { composeStories } from '@storybook/react';
6
- import { screen, cleanup, fireEvent, act } from '@testing-library/react';
6
+ import { act, cleanup, fireEvent, screen } 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
+
11
+ import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
10
12
 
11
13
  import * as stories from './FieldEditor.stories';
12
14
  import { type FieldEditorDebugObjects } from './FieldEditor.stories';
13
- import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
14
15
 
15
16
  const { Default } = composeStories(stories);
16
17
 
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
18
  describe('FieldEditor', () => {
26
19
  afterEach(() => {
27
20
  cleanup();
@@ -33,7 +26,7 @@ describe('FieldEditor', () => {
33
26
  expect(screen.getByText('Type format')).toBeInTheDocument();
34
27
  expect(screen.getByText('String')).toBeInTheDocument();
35
28
 
36
- // Verify the initial field shows as String type
29
+ // Verify the initial field shows as String type.
37
30
  expect(screen.getByText('String')).toBeInTheDocument();
38
31
 
39
32
  fireEvent.click(screen.getByRole('combobox'));
@@ -49,9 +42,17 @@ describe('FieldEditor', () => {
49
42
  // Access the live objects directly from window using symbol.
50
43
  const debugObjects = getFieldEditorDebugObjects();
51
44
 
52
- const name = debugObjects.projection.schema.properties!.name;
45
+ const name = debugObjects.projection.baseSchema.properties!.name;
53
46
  expect(name.type).toBe('number');
54
47
  expect(name.format).toBe('number');
55
48
  expect(name.description).toBe('Full name.');
56
49
  });
57
50
  });
51
+
52
+ const getFieldEditorDebugObjects = (): FieldEditorDebugObjects => {
53
+ const debugObjects = (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] as FieldEditorDebugObjects;
54
+ expect(debugObjects).toBeDefined();
55
+ expect(debugObjects.props).toBeInstanceOf(Object); // Props object
56
+ expect(debugObjects.projection).toBeInstanceOf(ProjectionModel);
57
+ return debugObjects;
58
+ };
@@ -5,37 +5,35 @@
5
5
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
6
 
7
7
  import { type SchemaRegistry } from '@dxos/echo-db';
8
- import { FormatEnum, FormatEnums, formatToType, type EchoSchema } from '@dxos/echo-schema';
8
+ import { type EchoSchema, FormatEnum, FormatEnums, formatToType } from '@dxos/echo-schema';
9
9
  import { log } from '@dxos/log';
10
- import { useTranslation } from '@dxos/react-ui';
10
+ import { useAsyncEffect, useTranslation } from '@dxos/react-ui';
11
11
  import {
12
- getFormatSchema,
13
- getSchemaProperties,
14
- sortProperties,
15
12
  type FieldType,
13
+ type ProjectionModel,
16
14
  type PropertyType,
17
- type ViewType,
18
- type ViewProjection,
19
15
  type SchemaProperty,
16
+ getFormatSchema,
17
+ getSchemaProperties,
18
+ sortProperties,
20
19
  } from '@dxos/schema';
21
20
 
22
21
  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;
31
29
  onCancel?: () => void;
32
- } & Pick<FormProps<any>, 'outerSpacing'>;
30
+ } & Pick<FormProps<any>, 'outerSpacing' | 'readonly'>;
33
31
 
34
32
  /**
35
33
  * Displays a Form representing the metadata for a given `Field` and `View`.
36
34
  */
37
35
  export const FieldEditor = ({
38
- view,
36
+ readonly,
39
37
  projection,
40
38
  field,
41
39
  registry,
@@ -48,7 +46,7 @@ export const FieldEditor = ({
48
46
  useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
49
47
 
50
48
  const [schemas, setSchemas] = useState<EchoSchema[]>([]);
51
- useEffect(() => {
49
+ useAsyncEffect(async () => {
52
50
  if (!registry) {
53
51
  return;
54
52
  }
@@ -56,14 +54,10 @@ export const FieldEditor = ({
56
54
  const subscription = registry.query().subscribe((query) => setSchemas(query.results), { fire: true });
57
55
 
58
56
  // TODO(dmaretskyi): This shouldn't be needed.
59
- const t = setTimeout(async () => {
60
- const schemas = await registry.query().run();
61
- setSchemas(schemas);
62
- });
63
- return () => {
64
- clearTimeout(t);
65
- subscription?.();
66
- };
57
+ const schemas = await registry.query().run();
58
+ setSchemas(schemas);
59
+
60
+ return () => subscription?.();
67
61
  }, [registry]);
68
62
 
69
63
  const [referenceSchema, setReferenceSchema] = useState<EchoSchema>();
@@ -112,7 +106,7 @@ export const FieldEditor = ({
112
106
 
113
107
  const handleValidate = useCallback<NonNullable<FormProps<PropertyType>['onValidate']>>(
114
108
  ({ property }) => {
115
- if (property && view.fields.find((f) => f.path === property && f.path !== field.path)) {
109
+ if (property && projection.fields.find((f) => f.path === property && f.path !== field.path)) {
116
110
  return [
117
111
  {
118
112
  path: 'property',
@@ -121,7 +115,7 @@ export const FieldEditor = ({
121
115
  ];
122
116
  }
123
117
  },
124
- [view.fields, field],
118
+ [projection.fields, field],
125
119
  );
126
120
 
127
121
  const handleSave = useCallback<NonNullable<FormProps<PropertyType>['onSave']>>(
@@ -188,6 +182,7 @@ export const FieldEditor = ({
188
182
  <Form<PropertyType>
189
183
  key={field.id}
190
184
  autoFocus
185
+ readonly={readonly}
191
186
  values={props}
192
187
  schema={fieldSchema}
193
188
  filter={propIsNotType}
@@ -6,26 +6,26 @@ import { SchemaAST, pipe } from 'effect';
6
6
  import { capitalize } from 'effect/String';
7
7
  import React, { Fragment, useCallback } from 'react';
8
8
 
9
- import { findNode, getDiscriminatedType, isDiscriminatedUnion, SimpleType } from '@dxos/effect';
9
+ import { SimpleType, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
10
10
  import { invariant } from '@dxos/invariant';
11
11
  import { IconButton, useTranslation } from '@dxos/react-ui';
12
- import { getSchemaProperties, type SchemaProperty } from '@dxos/schema';
12
+ import { type SchemaProperty, getSchemaProperties } from '@dxos/schema';
13
13
 
14
- import { type ComponentLookup } from './Form';
15
- import { FormField } from './FormContent';
16
- import { useFormValues, type FormInputStateProps } from './FormContext';
17
- import { InputHeader, type InputComponent } from './Input';
18
14
  import { translationKey } from '../../translations';
19
15
  import { findArrayElementType } from '../../util';
20
16
 
17
+ import { type ComponentLookup } from './Form';
18
+ import { FormField, type FormFieldProps } from './FormContent';
19
+ import { type FormInputStateProps, useFormValues } from './FormContext';
20
+ import { type InputComponent, InputHeader } from './Input';
21
+
21
22
  type ArrayFieldProps = {
22
23
  property: SchemaProperty<any>;
23
- readonly?: boolean;
24
24
  inputProps: FormInputStateProps;
25
25
  path?: (string | number)[];
26
26
  Custom?: Partial<Record<string, InputComponent>>;
27
27
  lookupComponent?: ComponentLookup;
28
- };
28
+ } & Pick<FormFieldProps, 'readonly'>;
29
29
 
30
30
  export const ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponent }: ArrayFieldProps) => {
31
31
  const { t } = useTranslation(translationKey);
@@ -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}