@dxos/react-ui-form 0.8.4-main.7ace549 → 0.8.4-main.8360d9e660

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 (179) hide show
  1. package/README.md +0 -1
  2. package/dist/lib/browser/index.mjs +2298 -2290
  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 +2298 -2290
  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 +8 -7
  9. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  10. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +9 -3
  11. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/Form/Form.d.ts +81 -27
  13. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  14. package/dist/types/src/components/Form/Form.stories.d.ts +53 -92
  15. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  16. package/dist/types/src/components/Form/FormField.d.ts +37 -0
  17. package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
  18. package/dist/types/src/components/Form/FormFieldComponent.d.ts +77 -0
  19. package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -0
  20. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +9 -0
  21. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -0
  22. package/dist/types/src/components/Form/FormFieldSet.d.ts +18 -0
  23. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
  24. package/dist/types/src/components/Form/fields/ArrayField.d.ts +18 -0
  25. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -0
  26. package/dist/types/src/components/Form/fields/BooleanField.d.ts +4 -0
  27. package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -0
  28. package/dist/types/src/components/Form/fields/GeoPointField.d.ts +5 -0
  29. package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -0
  30. package/dist/types/src/components/Form/fields/MarkdownField.d.ts +4 -0
  31. package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
  32. package/dist/types/src/components/Form/fields/NumberField.d.ts +4 -0
  33. package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -0
  34. package/dist/types/src/components/Form/fields/RefField.d.ts +16 -0
  35. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
  36. package/dist/types/src/components/Form/fields/SelectField.d.ts +10 -0
  37. package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
  38. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
  39. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
  40. package/dist/types/src/components/{ControlSection/ControlSection.stories.d.ts → Form/fields/SelectOptionField.stories.d.ts} +8 -10
  41. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
  42. package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
  43. package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
  44. package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
  45. package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
  46. package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
  47. package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
  48. package/dist/types/src/components/Form/fields/index.d.ts +12 -0
  49. package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
  50. package/dist/types/src/components/Form/index.d.ts +2 -4
  51. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  52. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +14 -9
  53. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
  54. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +5 -2
  55. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
  56. package/dist/types/src/components/Settings/Settings.d.ts +56 -0
  57. package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
  58. package/dist/types/src/components/{Form/custom/SelectOptionsInput.stories.d.ts → Settings/Settings.stories.d.ts} +6 -4
  59. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
  60. package/dist/types/src/components/Settings/index.d.ts +2 -0
  61. package/dist/types/src/components/Settings/index.d.ts.map +1 -0
  62. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +9 -11
  63. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  64. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +7 -3
  65. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  66. package/dist/types/src/components/index.d.ts +1 -1
  67. package/dist/types/src/components/index.d.ts.map +1 -1
  68. package/dist/types/src/components/testing.d.ts +0 -2
  69. package/dist/types/src/components/testing.d.ts.map +1 -1
  70. package/dist/types/src/hooks/index.d.ts +2 -3
  71. package/dist/types/src/hooks/index.d.ts.map +1 -1
  72. package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
  73. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
  74. package/dist/types/src/hooks/useKeyHandler.d.ts +6 -0
  75. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
  76. package/dist/types/src/translations.d.ts +6 -3
  77. package/dist/types/src/translations.d.ts.map +1 -1
  78. package/dist/types/src/util/index.d.ts +1 -1
  79. package/dist/types/src/util/index.d.ts.map +1 -1
  80. package/dist/types/src/util/properties.d.ts +7 -0
  81. package/dist/types/src/util/properties.d.ts.map +1 -0
  82. package/dist/types/src/util/refs.d.ts +1 -2
  83. package/dist/types/src/util/refs.d.ts.map +1 -1
  84. package/dist/types/tsconfig.tsbuildinfo +1 -1
  85. package/package.json +50 -47
  86. package/src/components/FieldEditor/FieldEditor.stories.tsx +43 -24
  87. package/src/components/FieldEditor/FieldEditor.tsx +73 -77
  88. package/src/components/Form/Form.stories.tsx +147 -246
  89. package/src/components/Form/Form.tsx +347 -110
  90. package/src/components/Form/FormField.tsx +294 -0
  91. package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
  92. package/src/components/Form/FormFieldComponent.tsx +171 -0
  93. package/src/components/Form/FormFieldSet.tsx +127 -0
  94. package/src/components/Form/README.md +3 -0
  95. package/src/components/Form/fields/ArrayField.tsx +149 -0
  96. package/src/components/Form/fields/BooleanField.tsx +22 -0
  97. package/src/components/Form/fields/GeoPointField.tsx +120 -0
  98. package/src/components/Form/fields/MarkdownField.tsx +78 -0
  99. package/src/components/Form/fields/NumberField.tsx +39 -0
  100. package/src/components/Form/fields/RefField.tsx +194 -0
  101. package/src/components/Form/fields/SelectField.tsx +65 -0
  102. package/src/components/Form/{custom/SelectOptionsInput.stories.tsx → fields/SelectOptionField.stories.tsx} +19 -16
  103. package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +24 -18
  104. package/src/components/Form/fields/TextAreaField.tsx +38 -0
  105. package/src/components/Form/fields/TextField.tsx +38 -0
  106. package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +15 -8
  107. package/src/components/Form/fields/index.ts +15 -0
  108. package/src/components/Form/index.ts +3 -4
  109. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +15 -12
  110. package/src/components/ObjectPicker/ObjectPicker.tsx +76 -62
  111. package/src/components/Settings/Settings.stories.tsx +56 -0
  112. package/src/components/Settings/Settings.tsx +230 -0
  113. package/src/components/{ControlSection → Settings}/index.ts +1 -1
  114. package/src/components/ViewEditor/ViewEditor.stories.tsx +42 -36
  115. package/src/components/ViewEditor/ViewEditor.test.tsx +5 -4
  116. package/src/components/ViewEditor/ViewEditor.tsx +295 -197
  117. package/src/components/index.ts +1 -1
  118. package/src/components/testing.tsx +12 -7
  119. package/src/hooks/index.ts +2 -3
  120. package/src/hooks/useFormHandler.ts +380 -0
  121. package/src/hooks/useKeyHandler.tsx +37 -0
  122. package/src/translations.ts +8 -6
  123. package/src/util/index.ts +1 -1
  124. package/src/util/properties.ts +18 -0
  125. package/src/util/refs.ts +7 -13
  126. package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
  127. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
  128. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
  129. package/dist/types/src/components/ControlSection/index.d.ts +0 -2
  130. package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
  131. package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
  132. package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
  133. package/dist/types/src/components/Form/Defaults.d.ts +0 -14
  134. package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
  135. package/dist/types/src/components/Form/FormActions.d.ts +0 -9
  136. package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
  137. package/dist/types/src/components/Form/FormContent.d.ts +0 -63
  138. package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
  139. package/dist/types/src/components/Form/FormContext.d.ts +0 -23
  140. package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
  141. package/dist/types/src/components/Form/Input.d.ts +0 -29
  142. package/dist/types/src/components/Form/Input.d.ts.map +0 -1
  143. package/dist/types/src/components/Form/RefField.d.ts +0 -22
  144. package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
  145. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
  146. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
  147. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
  148. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
  149. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
  150. package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
  151. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
  152. package/dist/types/src/components/Form/custom/index.d.ts +0 -4
  153. package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
  154. package/dist/types/src/components/Form/factory.d.ts +0 -8
  155. package/dist/types/src/components/Form/factory.d.ts.map +0 -1
  156. package/dist/types/src/hooks/useForm.d.ts +0 -67
  157. package/dist/types/src/hooks/useForm.d.ts.map +0 -1
  158. package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -29
  159. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
  160. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
  161. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
  162. package/dist/types/src/util/schema.d.ts +0 -3
  163. package/dist/types/src/util/schema.d.ts.map +0 -1
  164. package/src/components/ControlSection/ControlSection.stories.tsx +0 -38
  165. package/src/components/ControlSection/ControlSection.tsx +0 -153
  166. package/src/components/Form/ArrayField.tsx +0 -127
  167. package/src/components/Form/Defaults.tsx +0 -205
  168. package/src/components/Form/FormActions.tsx +0 -59
  169. package/src/components/Form/FormContent.tsx +0 -362
  170. package/src/components/Form/FormContext.tsx +0 -110
  171. package/src/components/Form/Input.tsx +0 -54
  172. package/src/components/Form/RefField.tsx +0 -283
  173. package/src/components/Form/custom/GeoPointInput.tsx +0 -80
  174. package/src/components/Form/custom/index.ts +0 -7
  175. package/src/components/Form/factory.tsx +0 -34
  176. package/src/hooks/useForm.ts +0 -296
  177. package/src/hooks/useQueryRefOptions.ts +0 -62
  178. package/src/hooks/useRefQueryLookupHandler.ts +0 -39
  179. package/src/util/schema.ts +0 -12
@@ -0,0 +1,56 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React from 'react';
7
+
8
+ import { faker } from '@dxos/random';
9
+ import { Button, Input } from '@dxos/react-ui';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
+
12
+ import { translations } from '../../translations';
13
+
14
+ import { Settings } from './Settings';
15
+
16
+ faker.seed(132);
17
+
18
+ const DefaultStory = () => {
19
+ return (
20
+ <Settings.Root>
21
+ <Settings.Section title='Settings' description={faker.lorem.paragraphs(1)}>
22
+ <Settings.Group>
23
+ <Settings.ItemInput title={faker.lorem.sentence(2)} description={faker.lorem.paragraphs(1)}>
24
+ <Input.Root>
25
+ <Input.TextInput placeholder='Input' />
26
+ </Input.Root>
27
+ </Settings.ItemInput>
28
+ <Settings.ItemInput title={faker.lorem.sentence(2)} description={faker.lorem.paragraphs(2)}>
29
+ <Input.Root>
30
+ <Input.Switch />
31
+ </Input.Root>
32
+ </Settings.ItemInput>
33
+ <Settings.ItemInput title={faker.lorem.sentence(3)} description={faker.lorem.paragraphs(2)}>
34
+ <Button>Test</Button>
35
+ </Settings.ItemInput>
36
+ </Settings.Group>
37
+ </Settings.Section>
38
+ </Settings.Root>
39
+ );
40
+ };
41
+
42
+ const meta = {
43
+ title: 'ui/react-ui-form/Settings',
44
+ render: DefaultStory,
45
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
46
+ parameters: {
47
+ layout: 'fullscreen',
48
+ translations,
49
+ },
50
+ } satisfies Meta;
51
+
52
+ export default meta;
53
+
54
+ type Story = StoryObj<typeof meta>;
55
+
56
+ export const Default: Story = {};
@@ -0,0 +1,230 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { type PropsWithChildren } from 'react';
6
+
7
+ import {
8
+ Button,
9
+ type ButtonProps,
10
+ Input,
11
+ type Label,
12
+ ScrollArea,
13
+ type ThemedClassName,
14
+ toLocalizedString,
15
+ useTranslation,
16
+ } from '@dxos/react-ui';
17
+ import { mx } from '@dxos/ui-theme';
18
+
19
+ import { translationKey } from '../../translations';
20
+
21
+ const SETTINGS_ROOT_NAME = 'Settings.Root';
22
+ const SETTINGS_SECTION_NAME = 'Settings.Section';
23
+ const SETTINGS_SECTION_HEADING_NAME = 'Settings.SectionHeading';
24
+ const SETTINGS_GROUP_NAME = 'Settings.Group';
25
+ const SETTINGS_GROUP_BUTTON_NAME = 'Settings.GroupButton';
26
+ const SETTINGS_CONTAINER_NAME = 'Settings.Container';
27
+ const SETTINGS_FRAME_NAME = 'Settings.Frame';
28
+ const SETTINGS_FRAME_ITEM_NAME = 'Settings.FrameItem';
29
+ const SETTINGS_ITEM_NAME = 'Settings.Item';
30
+ const SETTINGS_ITEM_INPUT_NAME = 'Settings.ItemInput';
31
+
32
+ const styles = {
33
+ title: 'pb-trim-md text-base-surface-text text-lg',
34
+ description: 'text-base text-description',
35
+ grid: 'grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-x-trim-lg',
36
+ };
37
+
38
+ //
39
+ // Root
40
+ //
41
+
42
+ type SettingsRootProps = PropsWithChildren;
43
+
44
+ const SettingsRoot = ({ children }: SettingsRootProps) => {
45
+ return (
46
+ <ScrollArea.Root className='dx-document' orientation='vertical' margin>
47
+ <ScrollArea.Viewport classNames='p-trim-md'>{children}</ScrollArea.Viewport>
48
+ </ScrollArea.Root>
49
+ );
50
+ };
51
+
52
+ SettingsRoot.displayName = SETTINGS_ROOT_NAME;
53
+
54
+ //
55
+ // Section
56
+ //
57
+
58
+ type SettingsSectionProps = PropsWithChildren<{
59
+ title: Label;
60
+ description?: Label;
61
+ }>;
62
+
63
+ const SettingsSection = ({ title, description, children }: SettingsSectionProps) => {
64
+ return (
65
+ <>
66
+ <SettingsSectionHeading title={title} description={description} />
67
+ <div className='w-full pt-trim-md space-y-trim-md'>{children}</div>
68
+ </>
69
+ );
70
+ };
71
+
72
+ SettingsSection.displayName = SETTINGS_SECTION_NAME;
73
+
74
+ //
75
+ // Section Heading
76
+ //
77
+
78
+ const SettingsSectionHeading = ({ title, description }: Omit<SettingsSectionProps, 'children'>) => {
79
+ const { t } = useTranslation(translationKey);
80
+ return (
81
+ <>
82
+ <h2 className='px-trim-md mt-trim-md mb-trim-md text-xl'>{toLocalizedString(title, t)}</h2>
83
+ {description && <p className='px-trim-md my-trim-md text-description'>{toLocalizedString(description, t)}</p>}
84
+ </>
85
+ );
86
+ };
87
+
88
+ SettingsSectionHeading.displayName = SETTINGS_SECTION_HEADING_NAME;
89
+
90
+ //
91
+ // Group Button
92
+ //
93
+
94
+ const SettingsGroupButton = ({ classNames, ...props }: ButtonProps) => {
95
+ return <Button {...props} classNames={['md:col-span-2', classNames]} />;
96
+ };
97
+
98
+ SettingsGroupButton.displayName = SETTINGS_GROUP_BUTTON_NAME;
99
+
100
+ //
101
+ // Group
102
+ //
103
+
104
+ type SettingsGroupProps = ThemedClassName<PropsWithChildren>;
105
+
106
+ const SettingsGroup = ({ children, classNames }: SettingsGroupProps) => (
107
+ <div role='none' className={mx('group space-y-trim-md', classNames)}>
108
+ {children}
109
+ </div>
110
+ );
111
+
112
+ SettingsGroup.displayName = SETTINGS_GROUP_NAME;
113
+
114
+ //
115
+ // Frame
116
+ //
117
+
118
+ const SettingsFrame = ({ children }: SettingsGroupProps) => (
119
+ <div role='none' className='p-trim-md border border-separator rounded-md'>
120
+ {children}
121
+ </div>
122
+ );
123
+
124
+ SettingsFrame.displayName = SETTINGS_FRAME_NAME;
125
+
126
+ //
127
+ // Frame Item
128
+ //
129
+
130
+ const SettingsFrameItem = ({ title, description, children }: SettingsItemProps) => {
131
+ const { t } = useTranslation(translationKey);
132
+
133
+ return (
134
+ <div role='group' className='min-w-0'>
135
+ <h3 className='text-lg mb-2'>{toLocalizedString(title, t)}</h3>
136
+ {description && <p className='my-trim-sm md:mb-0 text-description'>{toLocalizedString(description, t)}</p>}
137
+ {children}
138
+ </div>
139
+ );
140
+ };
141
+
142
+ SettingsFrameItem.displayName = SETTINGS_FRAME_ITEM_NAME;
143
+
144
+ //
145
+ // Container
146
+ //
147
+
148
+ const SettingsContainer = ({ classNames, children }: ThemedClassName<PropsWithChildren>) => {
149
+ return (
150
+ <div
151
+ role='none'
152
+ className={mx([
153
+ 'dx-document',
154
+ '*:first:mt-0! *:last:mb-0! px-trim-md py-trim-md',
155
+ 'border border-separator rounded-md',
156
+ classNames,
157
+ ])}
158
+ >
159
+ {children}
160
+ </div>
161
+ );
162
+ };
163
+
164
+ SettingsContainer.displayName = SETTINGS_CONTAINER_NAME;
165
+
166
+ //
167
+ // Item
168
+ //
169
+
170
+ type SettingsItemProps = PropsWithChildren<{
171
+ title: Label;
172
+ description?: Label;
173
+ }>;
174
+
175
+ const SettingsItem = ({ title, description = '', children }: SettingsItemProps) => {
176
+ const { t } = useTranslation(translationKey);
177
+
178
+ return (
179
+ <SettingsContainer classNames={styles.grid}>
180
+ <h3 className={mx(styles.title, 'md:col-span-2')}>{toLocalizedString(title, t)}</h3>
181
+ <p className={styles.description}>{toLocalizedString(description, t)}</p>
182
+ <div role='none' className='overflow-hidden text-end py-1'>
183
+ {children}
184
+ </div>
185
+ </SettingsContainer>
186
+ );
187
+ };
188
+
189
+ SettingsItem.displayName = SETTINGS_ITEM_NAME;
190
+
191
+ //
192
+ // Item Input
193
+ //
194
+
195
+ const SettingsItemInput = ({ title, description = '', children }: SettingsItemProps) => {
196
+ const { t } = useTranslation(translationKey);
197
+
198
+ return (
199
+ <Input.Root>
200
+ <SettingsContainer classNames={styles.grid}>
201
+ <Input.Label classNames={mx(styles.title, 'md:col-span-2')}>{toLocalizedString(title, t)}</Input.Label>
202
+ <Input.DescriptionAndValidation>
203
+ <Input.Description classNames={styles.description}>{toLocalizedString(description, t)}</Input.Description>
204
+ </Input.DescriptionAndValidation>
205
+ <div role='none' className='text-end py-1'>
206
+ {children}
207
+ </div>
208
+ </SettingsContainer>
209
+ </Input.Root>
210
+ );
211
+ };
212
+
213
+ SettingsItemInput.displayName = SETTINGS_ITEM_INPUT_NAME;
214
+
215
+ //
216
+ // Settings
217
+ //
218
+
219
+ export const Settings = {
220
+ Root: SettingsRoot,
221
+ Section: SettingsSection,
222
+ SectionHeading: SettingsSectionHeading,
223
+ Group: SettingsGroup,
224
+ GroupButton: SettingsGroupButton,
225
+ Container: SettingsContainer,
226
+ Frame: SettingsFrame,
227
+ FrameItem: SettingsFrameItem,
228
+ Item: SettingsItem,
229
+ ItemInput: SettingsItemInput,
230
+ };
@@ -2,4 +2,4 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- export * from './ControlSection';
5
+ export * from './Settings';
@@ -6,17 +6,18 @@ 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';
10
- import { type EchoSchema, Format } from '@dxos/echo/internal';
9
+ import { DXN, Filter, JsonSchema, Obj, Query, type QueryAST, Tag, Type } from '@dxos/echo';
10
+ import { type View } from '@dxos/echo';
11
+ import { type EchoSchema, Format, type Mutable } from '@dxos/echo/internal';
11
12
  import { useQuery } from '@dxos/react-client/echo';
12
- import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
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
- import { TestLayout, TestPanel, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
20
+ import { TestLayout, VIEW_EDITOR_DEBUG_SYMBOL } from '../testing';
20
21
 
21
22
  import { ViewEditor, type ViewEditorProps } from './ViewEditor';
22
23
 
@@ -24,7 +25,7 @@ const types = [
24
25
  // TODO(burdon): Get label from annotation.
25
26
  { value: Organization.Organization.typename, label: 'Organization' },
26
27
  { value: Person.Person.typename, label: 'Person' },
27
- { value: Type.getTypename(Project.Project), label: 'Project' },
28
+ { value: Pipeline.Pipeline.typename, label: 'Project' },
28
29
  { value: Employer.Employer.typename, label: 'Employer' },
29
30
  ];
30
31
 
@@ -38,12 +39,12 @@ export type ViewEditorDebugObjects = {
38
39
  type StoryProps = Pick<ViewEditorProps, 'readonly' | 'mode'>;
39
40
 
40
41
  const DefaultStory = (props: StoryProps) => {
41
- const { space } = useClientProvider();
42
+ const { space } = useClientStory();
42
43
  const [schema, setSchema] = useState<EchoSchema>();
43
44
  const [view, setView] = useState<View.View>();
44
45
  const projectionRef = useRef<ProjectionModel>(null);
45
46
 
46
- const tags = useQuery(space, Filter.type(Tag.Tag));
47
+ const tags = useQuery(space?.db, Filter.type(Tag.Tag));
47
48
 
48
49
  useAsyncEffect(async () => {
49
50
  if (space) {
@@ -52,8 +53,8 @@ const DefaultStory = (props: StoryProps) => {
52
53
  email: Format.Email,
53
54
  salary: Format.Currency(),
54
55
  }).pipe(
55
- Type.Obj({
56
- typename: 'example.com/type/Test',
56
+ Type.object({
57
+ typename: 'com.example.type.test',
57
58
  version: '0.1.0',
58
59
  }),
59
60
  );
@@ -63,17 +64,17 @@ const DefaultStory = (props: StoryProps) => {
63
64
  description: Schema.String,
64
65
  completed: Schema.Boolean,
65
66
  }).pipe(
66
- Type.Obj({
67
- typename: 'example.com/type/Alternate',
67
+ Type.object({
68
+ typename: 'com.example.type.alternate',
68
69
  version: '0.1.0',
69
70
  }),
70
71
  );
71
72
 
72
73
  const [testSchema] = await space.db.schemaRegistry.register([TestSchema, AlternateSchema]);
73
- const view = View.make({
74
+ const view = ViewModel.make({
74
75
  name: 'Test',
75
76
  query: Query.select(Filter.type(TestSchema)),
76
- jsonSchema: Type.toJsonSchema(TestSchema),
77
+ jsonSchema: JsonSchema.toJsonSchema(TestSchema),
77
78
  });
78
79
 
79
80
  setSchema(testSchema);
@@ -89,8 +90,10 @@ const DefaultStory = (props: StoryProps) => {
89
90
 
90
91
  if (props.mode === 'tag') {
91
92
  const queue = target && DXN.tryParse(target) ? target : undefined;
92
- const query = queue ? Query.fromAst(newQuery).options({ queues: [queue] }) : Query.fromAst(newQuery);
93
- view.query.ast = query.ast;
93
+ const query = queue ? Query.fromAst(newQuery).from({ queues: [queue] }) : Query.fromAst(newQuery);
94
+ Obj.change(view, (v) => {
95
+ v.query.ast = query.ast as Mutable<typeof query.ast>;
96
+ });
94
97
 
95
98
  const typename = getTypenameFromQuery(query.ast);
96
99
  const [newSchema] = await space.db.schemaRegistry.query({ typename }).run();
@@ -98,14 +101,18 @@ const DefaultStory = (props: StoryProps) => {
98
101
  return;
99
102
  }
100
103
 
101
- const newView = View.make({
104
+ const newView = ViewModel.make({
102
105
  query,
103
106
  jsonSchema: newSchema.jsonSchema,
104
107
  });
105
- view.projection = Obj.getSnapshot(newView).projection;
108
+ Obj.change(view, (v) => {
109
+ v.projection = Obj.getSnapshot(newView).projection as Mutable<typeof v.projection>;
110
+ });
106
111
  setSchema(() => newSchema);
107
112
  } else {
108
- view.query.ast = newQuery;
113
+ Obj.change(view, (v) => {
114
+ v.query.ast = newQuery as Mutable<typeof newQuery>;
115
+ });
109
116
  schema.updateTypename(getTypenameFromQuery(newQuery));
110
117
  }
111
118
  },
@@ -140,20 +147,18 @@ const DefaultStory = (props: StoryProps) => {
140
147
 
141
148
  return (
142
149
  <TestLayout json={json}>
143
- <TestPanel>
144
- <ViewEditor
145
- ref={projectionRef}
146
- schema={schema}
147
- view={view}
148
- registry={space?.db.schemaRegistry}
149
- mode={props.mode}
150
- readonly={props.readonly}
151
- types={types}
152
- tags={tags}
153
- onQueryChanged={updateViewQuery}
154
- onDelete={handleDelete}
155
- />
156
- </TestPanel>
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
+ />
157
162
  </TestLayout>
158
163
  );
159
164
  };
@@ -171,7 +176,8 @@ const meta = {
171
176
  space.db.add(Tag.make({ label: 'New' }));
172
177
  },
173
178
  }),
174
- withTheme,
179
+ withTheme(),
180
+ withLayout({ layout: 'fullscreen' }),
175
181
  ],
176
182
  parameters: {
177
183
  layout: 'fullscreen',
@@ -6,8 +6,9 @@ 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
 
@@ -54,7 +55,7 @@ describe('ViewEditor', () => {
54
55
  const nameField = screen.getByText('name');
55
56
  fireEvent.click(nameField);
56
57
 
57
- const fieldInput = screen.getByPlaceholderText('Field name.');
58
+ const fieldInput = screen.getByPlaceholderText('Property name');
58
59
  fireEvent.change(fieldInput, { target: { value: 'new_property' } });
59
60
 
60
61
  fireEvent.click(screen.getByTestId('save-button'));
@@ -89,12 +90,12 @@ describe('ViewEditor', () => {
89
90
  await Default.run();
90
91
  await waitForViewEditor();
91
92
 
92
- // Click the add property button.
93
+ // Click the add property button. The new field is auto-expanded by handleAdd.
93
94
  const addButton = screen.getByText('Add property');
94
95
  fireEvent.click(addButton);
95
96
 
96
97
  // Fill out the property field.
97
- const fieldInput = screen.getByPlaceholderText('Field name.');
98
+ const fieldInput = screen.getByPlaceholderText('Property name');
98
99
  fireEvent.change(fieldInput, { target: { value: 'added_property' } });
99
100
 
100
101
  // Click the format combo box and select the first option.