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

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 (113) hide show
  1. package/README.md +0 -1
  2. package/dist/lib/browser/index.mjs +1003 -668
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/node-esm/index.mjs +1003 -668
  6. package/dist/lib/node-esm/index.mjs.map +4 -4
  7. package/dist/lib/node-esm/meta.json +1 -1
  8. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +3 -3
  9. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  10. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +62 -61
  11. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/Form.d.ts +23 -18
  13. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/Form.stories.d.ts +68 -67
  15. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/FormField.d.ts +7 -1
  17. package/dist/types/src/components/Form/FormField.d.ts.map +1 -1
  18. package/dist/types/src/components/Form/FormFieldComponent.d.ts +0 -1
  19. package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -1
  20. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +2 -2
  21. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -1
  22. package/dist/types/src/components/Form/FormFieldSet.d.ts +9 -10
  23. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -1
  24. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -1
  25. package/dist/types/src/components/Form/fields/RefField.d.ts +8 -6
  26. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -1
  27. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +62 -61
  29. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -1
  30. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +9 -0
  31. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -0
  32. package/dist/types/src/components/ObjectForm/index.d.ts +2 -0
  33. package/dist/types/src/components/ObjectForm/index.d.ts.map +1 -0
  34. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +10 -2
  35. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
  36. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +62 -61
  37. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +9 -0
  39. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -0
  40. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +83 -0
  41. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -0
  42. package/dist/types/src/components/ObjectProperties/index.d.ts +2 -0
  43. package/dist/types/src/components/ObjectProperties/index.d.ts.map +1 -0
  44. package/dist/types/src/components/Settings/Settings.d.ts +35 -0
  45. package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
  46. package/dist/types/src/components/Settings/Settings.stories.d.ts +82 -0
  47. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
  48. package/dist/types/src/components/Settings/index.d.ts +2 -0
  49. package/dist/types/src/components/Settings/index.d.ts.map +1 -0
  50. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +5 -5
  51. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  52. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +66 -64
  53. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  54. package/dist/types/src/components/index.d.ts +3 -1
  55. package/dist/types/src/components/index.d.ts.map +1 -1
  56. package/dist/types/src/components/testing.d.ts.map +1 -1
  57. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -1
  58. package/dist/types/src/hooks/useKeyHandler.d.ts +2 -1
  59. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -1
  60. package/dist/types/src/translations.d.ts +62 -61
  61. package/dist/types/src/translations.d.ts.map +1 -1
  62. package/dist/types/src/util/refs.d.ts +1 -0
  63. package/dist/types/src/util/refs.d.ts.map +1 -1
  64. package/dist/types/tsconfig.tsbuildinfo +1 -1
  65. package/package.json +37 -38
  66. package/src/components/FieldEditor/FieldEditor.stories.tsx +7 -8
  67. package/src/components/FieldEditor/FieldEditor.test.tsx +0 -1
  68. package/src/components/FieldEditor/FieldEditor.tsx +3 -4
  69. package/src/components/Form/AUDIT.md +17 -0
  70. package/src/components/Form/Form.stories.tsx +25 -26
  71. package/src/components/Form/Form.tsx +101 -66
  72. package/src/components/Form/FormField.tsx +32 -14
  73. package/src/components/Form/FormFieldComponent.stories.tsx +5 -5
  74. package/src/components/Form/FormFieldComponent.tsx +17 -20
  75. package/src/components/Form/FormFieldSet.tsx +126 -105
  76. package/src/components/Form/fields/ArrayField.tsx +11 -8
  77. package/src/components/Form/fields/GeoPointField.tsx +6 -6
  78. package/src/components/Form/fields/MarkdownField.tsx +1 -1
  79. package/src/components/Form/fields/RefField.tsx +37 -20
  80. package/src/components/Form/fields/SelectField.tsx +1 -1
  81. package/src/components/Form/fields/SelectOptionField.stories.tsx +2 -3
  82. package/src/components/Form/fields/SelectOptionField.tsx +9 -10
  83. package/src/components/Form/fields/TupleField.tsx +1 -1
  84. package/src/components/ObjectForm/ObjectForm.tsx +113 -0
  85. package/src/components/{ControlSection → ObjectForm}/index.ts +1 -1
  86. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +13 -18
  87. package/src/components/ObjectPicker/ObjectPicker.tsx +71 -60
  88. package/src/components/ObjectProperties/ObjectProperties.stories.tsx +63 -0
  89. package/src/components/ObjectProperties/ObjectProperties.tsx +129 -0
  90. package/src/components/ObjectProperties/index.ts +5 -0
  91. package/src/components/Settings/Settings.stories.tsx +55 -0
  92. package/src/components/Settings/Settings.tsx +129 -0
  93. package/src/components/Settings/index.ts +5 -0
  94. package/src/components/ViewEditor/ViewEditor.stories.tsx +36 -33
  95. package/src/components/ViewEditor/ViewEditor.test.tsx +3 -7
  96. package/src/components/ViewEditor/ViewEditor.tsx +117 -85
  97. package/src/components/index.ts +3 -1
  98. package/src/components/testing.tsx +20 -8
  99. package/src/hooks/useFormHandler.ts +42 -18
  100. package/src/hooks/useKeyHandler.tsx +7 -7
  101. package/src/translations.ts +63 -63
  102. package/src/util/refs.ts +7 -3
  103. package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
  104. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
  105. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +0 -87
  106. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
  107. package/dist/types/src/components/ControlSection/index.d.ts +0 -2
  108. package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
  109. package/dist/types/src/components/Form/Form-old.stories.d.ts +0 -131
  110. package/dist/types/src/components/Form/Form-old.stories.d.ts.map +0 -1
  111. package/src/components/ControlSection/ControlSection.stories.tsx +0 -38
  112. package/src/components/ControlSection/ControlSection.tsx +0 -153
  113. package/src/components/Form/Form-old.stories.tsx +0 -314
@@ -0,0 +1,129 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { type PropsWithChildren } from 'react';
6
+
7
+ import { Input, type Label, ScrollArea, type ThemedClassName, toLocalizedString, useTranslation } from '@dxos/react-ui';
8
+ import { composable, composableProps, mx } from '@dxos/ui-theme';
9
+
10
+ import { translationKey } from '../../translations';
11
+
12
+ const SETTINGS_ROOT_NAME = 'Settings.Root';
13
+ const SETTINGS_VIEWPORT_NAME = 'Settings.Viewport';
14
+ const SETTINGS_SECTION_NAME = 'Settings.Section';
15
+ const SETTINGS_PANEL_NAME = 'Settings.Panel';
16
+ const SETTINGS_ITEM_NAME = 'Settings.Item';
17
+
18
+ //
19
+ // Root (headless — no styling, no scroll).
20
+ //
21
+
22
+ type SettingsRootProps = ThemedClassName<PropsWithChildren>;
23
+
24
+ const SettingsRoot = ({ children, classNames }: SettingsRootProps) => {
25
+ return <div className={mx('flex flex-col gap-2', classNames)}>{children}</div>;
26
+ };
27
+
28
+ SettingsRoot.displayName = SETTINGS_ROOT_NAME;
29
+
30
+ //
31
+ // Viewport (scroll area + padding + document layout).
32
+ //
33
+
34
+ const SettingsViewport = composable<HTMLDivElement>(({ children, ...props }, forwardedRef) => {
35
+ return (
36
+ <ScrollArea.Root {...composableProps(props)} orientation='vertical' centered thin ref={forwardedRef}>
37
+ <ScrollArea.Viewport classNames='px-4 pointer-coarse:px-2'>
38
+ <div role='none' className='dx-document flex flex-col gap-2 py-4'>
39
+ {children}
40
+ </div>
41
+ </ScrollArea.Viewport>
42
+ </ScrollArea.Root>
43
+ );
44
+ });
45
+
46
+ SettingsViewport.displayName = SETTINGS_VIEWPORT_NAME;
47
+
48
+ //
49
+ // Section (title + optional description + children with spacing).
50
+ //
51
+
52
+ type SettingsSectionProps = PropsWithChildren<{
53
+ title: Label;
54
+ description?: Label;
55
+ }>;
56
+
57
+ const SettingsSection = ({ title, description, children }: SettingsSectionProps) => {
58
+ const { t } = useTranslation(translationKey);
59
+ return (
60
+ <>
61
+ <h2 className='px-trim-md mt-trim-md text-xl'>{toLocalizedString(title, t)}</h2>
62
+ {description && <p className='px-trim-md text-description'>{toLocalizedString(description, t)}</p>}
63
+ <div className='w-full pt-trim-md space-y-trim-md'>{children}</div>
64
+ </>
65
+ );
66
+ };
67
+
68
+ SettingsSection.displayName = SETTINGS_SECTION_NAME;
69
+
70
+ //
71
+ // Panel (bordered card container).
72
+ //
73
+
74
+ type SettingsPanelProps = ThemedClassName<PropsWithChildren>;
75
+
76
+ const SettingsPanel = ({ classNames, children }: SettingsPanelProps) => {
77
+ return (
78
+ <div role='none' className={mx('flex flex-col gap-3 p-trim-md border border-separator rounded-sm', classNames)}>
79
+ {children}
80
+ </div>
81
+ );
82
+ };
83
+
84
+ SettingsPanel.displayName = SETTINGS_PANEL_NAME;
85
+
86
+ //
87
+ // Item (label + optional description + control, proper label semantics).
88
+ //
89
+
90
+ type SettingsItemProps = PropsWithChildren<{
91
+ title: Label;
92
+ description?: Label;
93
+ }>;
94
+
95
+ const SettingsItem = ({ title, description = '', children }: SettingsItemProps) => {
96
+ const { t } = useTranslation(translationKey);
97
+
98
+ return (
99
+ <Input.Root>
100
+ <SettingsPanel classNames='grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-x-trim-lg gap-y-0'>
101
+ <Input.Label classNames='pb-trim-md text-base-surface-text text-lg md:col-span-2'>
102
+ {toLocalizedString(title, t)}
103
+ </Input.Label>
104
+ <Input.DescriptionAndValidation>
105
+ <Input.Description classNames='text-base text-description'>
106
+ {toLocalizedString(description, t)}
107
+ </Input.Description>
108
+ </Input.DescriptionAndValidation>
109
+ <div role='none' className='text-end py-1'>
110
+ {children}
111
+ </div>
112
+ </SettingsPanel>
113
+ </Input.Root>
114
+ );
115
+ };
116
+
117
+ SettingsItem.displayName = SETTINGS_ITEM_NAME;
118
+
119
+ //
120
+ // Settings
121
+ //
122
+
123
+ export const Settings = {
124
+ Root: SettingsRoot,
125
+ Viewport: SettingsViewport,
126
+ Section: SettingsSection,
127
+ Panel: SettingsPanel,
128
+ Item: SettingsItem,
129
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './Settings';
@@ -6,25 +6,25 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import * as Schema from 'effect/Schema';
7
7
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
8
8
 
9
- import { DXN, Filter, Obj, Query, type QueryAST, Tag, Type } from '@dxos/echo';
9
+ import { DXN, Filter, JsonSchema, Obj, Query, type QueryAST, Tag, Type } from '@dxos/echo';
10
+ import { type View } from '@dxos/echo';
10
11
  import { type EchoSchema, Format, type Mutable } from '@dxos/echo/internal';
11
12
  import { useQuery } from '@dxos/react-client/echo';
12
13
  import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
13
14
  import { useAsyncEffect } from '@dxos/react-ui';
14
- import { withTheme } from '@dxos/react-ui/testing';
15
- import { type ProjectionModel, View, getTypenameFromQuery } from '@dxos/schema';
16
- import { Employer, Organization, Person, Project } from '@dxos/types';
15
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
16
+ import { type ProjectionModel, ViewModel, getTypenameFromQuery } from '@dxos/schema';
17
+ import { Employer, Organization, Person, Pipeline } from '@dxos/types';
17
18
 
18
19
  import { translations } from '../../translations';
19
20
  import { TestLayout, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
20
-
21
21
  import { ViewEditor, type ViewEditorProps } from './ViewEditor';
22
22
 
23
23
  const types = [
24
24
  // TODO(burdon): Get label from annotation.
25
25
  { value: Organization.Organization.typename, label: 'Organization' },
26
26
  { value: Person.Person.typename, label: 'Person' },
27
- { value: Project.Project.typename, label: 'Project' },
27
+ { value: Pipeline.Pipeline.typename, label: 'Project' },
28
28
  { value: Employer.Employer.typename, label: 'Employer' },
29
29
  ];
30
30
 
@@ -35,9 +35,9 @@ export type ViewEditorDebugObjects = {
35
35
  projection: ProjectionModel;
36
36
  };
37
37
 
38
- type StoryProps = Pick<ViewEditorProps, 'readonly' | 'mode'>;
38
+ type DefaultStoryProps = Pick<ViewEditorProps, 'readonly' | 'mode'>;
39
39
 
40
- const DefaultStory = (props: StoryProps) => {
40
+ const DefaultStory = (props: DefaultStoryProps) => {
41
41
  const { space } = useClientStory();
42
42
  const [schema, setSchema] = useState<EchoSchema>();
43
43
  const [view, setView] = useState<View.View>();
@@ -53,7 +53,7 @@ const DefaultStory = (props: StoryProps) => {
53
53
  salary: Format.Currency(),
54
54
  }).pipe(
55
55
  Type.object({
56
- typename: 'example.com/type/Test',
56
+ typename: 'com.example.type.test',
57
57
  version: '0.1.0',
58
58
  }),
59
59
  );
@@ -64,16 +64,16 @@ const DefaultStory = (props: StoryProps) => {
64
64
  completed: Schema.Boolean,
65
65
  }).pipe(
66
66
  Type.object({
67
- typename: 'example.com/type/Alternate',
67
+ typename: 'com.example.type.alternate',
68
68
  version: '0.1.0',
69
69
  }),
70
70
  );
71
71
 
72
72
  const [testSchema] = await space.db.schemaRegistry.register([TestSchema, AlternateSchema]);
73
- const view = View.make({
73
+ const view = ViewModel.make({
74
74
  name: 'Test',
75
75
  query: Query.select(Filter.type(TestSchema)),
76
- jsonSchema: Type.toJsonSchema(TestSchema),
76
+ jsonSchema: JsonSchema.toJsonSchema(TestSchema),
77
77
  });
78
78
 
79
79
  setSchema(testSchema);
@@ -89,9 +89,9 @@ const DefaultStory = (props: StoryProps) => {
89
89
 
90
90
  if (props.mode === 'tag') {
91
91
  const queue = target && DXN.tryParse(target) ? target : undefined;
92
- const query = queue ? Query.fromAst(newQuery).options({ queues: [queue] }) : Query.fromAst(newQuery);
93
- Obj.change(view, (v) => {
94
- v.query.ast = query.ast as Mutable<typeof query.ast>;
92
+ const query = queue ? Query.fromAst(newQuery).from({ queues: [queue] }) : Query.fromAst(newQuery);
93
+ Obj.change(view, (view) => {
94
+ view.query.ast = query.ast as Mutable<typeof query.ast>;
95
95
  });
96
96
 
97
97
  const typename = getTypenameFromQuery(query.ast);
@@ -100,17 +100,17 @@ const DefaultStory = (props: StoryProps) => {
100
100
  return;
101
101
  }
102
102
 
103
- const newView = View.make({
103
+ const newView = ViewModel.make({
104
104
  query,
105
105
  jsonSchema: newSchema.jsonSchema,
106
106
  });
107
- Obj.change(view, (v) => {
108
- v.projection = Obj.getSnapshot(newView).projection as Mutable<typeof v.projection>;
107
+ Obj.change(view, (view) => {
108
+ view.projection = Obj.getSnapshot(newView).projection as Mutable<typeof view.projection>;
109
109
  });
110
110
  setSchema(() => newSchema);
111
111
  } else {
112
- Obj.change(view, (v) => {
113
- v.query.ast = newQuery as Mutable<typeof newQuery>;
112
+ Obj.change(view, (view) => {
113
+ view.query.ast = newQuery as Mutable<typeof newQuery>;
114
114
  });
115
115
  schema.updateTypename(getTypenameFromQuery(newQuery));
116
116
  }
@@ -146,18 +146,20 @@ const DefaultStory = (props: StoryProps) => {
146
146
 
147
147
  return (
148
148
  <TestLayout json={json}>
149
- <ViewEditor
150
- ref={projectionRef}
151
- schema={schema}
152
- view={view}
153
- registry={space?.db.schemaRegistry}
154
- mode={props.mode}
155
- readonly={props.readonly}
156
- types={types}
157
- tags={tags}
158
- onQueryChanged={updateViewQuery}
159
- onDelete={handleDelete}
160
- />
149
+ <div role='none' className='col-span-full'>
150
+ <ViewEditor
151
+ ref={projectionRef}
152
+ schema={schema}
153
+ view={view}
154
+ registry={space?.db.schemaRegistry}
155
+ mode={props.mode}
156
+ readonly={props.readonly}
157
+ types={types}
158
+ tags={tags}
159
+ onQueryChanged={updateViewQuery}
160
+ onDelete={handleDelete}
161
+ />
162
+ </div>
161
163
  </TestLayout>
162
164
  );
163
165
  };
@@ -175,7 +177,8 @@ const meta = {
175
177
  space.db.add(Tag.make({ label: 'New' }));
176
178
  },
177
179
  }),
178
- withTheme,
180
+ withTheme(),
181
+ withLayout({ layout: 'fullscreen' }),
179
182
  ],
180
183
  parameters: {
181
184
  layout: 'fullscreen',
@@ -6,11 +6,11 @@ import { composeStories } from '@storybook/react';
6
6
  import { cleanup, fireEvent, screen, waitFor } from '@testing-library/react';
7
7
  import { afterEach, describe, expect, test } from 'vitest';
8
8
 
9
+ import { View } from '@dxos/echo';
9
10
  import { EchoSchema, isInstanceOf } from '@dxos/echo/internal';
10
- import { ProjectionModel, View } from '@dxos/schema';
11
+ import { ProjectionModel } from '@dxos/schema';
11
12
 
12
13
  import { VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
13
-
14
14
  import * as stories from './ViewEditor.stories';
15
15
  import { type ViewEditorDebugObjects } from './ViewEditor.stories';
16
16
 
@@ -89,14 +89,10 @@ describe('ViewEditor', () => {
89
89
  await Default.run();
90
90
  await waitForViewEditor();
91
91
 
92
- // Click the add property button.
92
+ // Click the add property button. The new field is auto-expanded by handleAdd.
93
93
  const addButton = screen.getByText('Add property');
94
94
  fireEvent.click(addButton);
95
95
 
96
- // Open the last field.
97
- const buttons = screen.getAllByTestId('field.toggle');
98
- fireEvent.click(buttons.at(-1)!);
99
-
100
96
  // Fill out the property field.
101
97
  const fieldInput = screen.getByPlaceholderText('Property name');
102
98
  fireEvent.change(fieldInput, { target: { value: 'added_property' } });