@dxos/react-ui-form 0.8.4-main.406dc2a → 0.8.4-main.4668b7de9b

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 (283) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +0 -1
  3. package/dist/lib/browser/index.mjs +3504 -2302
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/translations.mjs +83 -0
  7. package/dist/lib/browser/translations.mjs.map +7 -0
  8. package/dist/lib/node-esm/index.mjs +3504 -2302
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/lib/node-esm/translations.mjs +85 -0
  12. package/dist/lib/node-esm/translations.mjs.map +7 -0
  13. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +8 -7
  14. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  15. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +69 -60
  16. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/Form/Form.d.ts +135 -26
  18. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/Form.stories.d.ts +175 -138
  20. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/FormField.d.ts +53 -0
  22. package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
  23. package/dist/types/src/components/Form/FormFieldComponent.d.ts +91 -0
  24. package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -0
  25. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts +9 -0
  26. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -0
  27. package/dist/types/src/components/Form/FormFieldSet.d.ts +21 -0
  28. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
  29. package/dist/types/src/components/Form/FormTooltipsContext.d.ts +19 -0
  30. package/dist/types/src/components/Form/FormTooltipsContext.d.ts.map +1 -0
  31. package/dist/types/src/components/Form/Layout/FormLayout.d.ts +50 -0
  32. package/dist/types/src/components/Form/Layout/FormLayout.d.ts.map +1 -0
  33. package/dist/types/src/components/Form/Layout/FormLayout.stories.d.ts +119 -0
  34. package/dist/types/src/components/Form/Layout/FormLayout.stories.d.ts.map +1 -0
  35. package/dist/types/src/components/Form/Layout/FormLayout.stories.test.d.ts +2 -0
  36. package/dist/types/src/components/Form/Layout/FormLayout.stories.test.d.ts.map +1 -0
  37. package/dist/types/src/components/Form/Layout/FormLayout.test.d.ts +2 -0
  38. package/dist/types/src/components/Form/Layout/FormLayout.test.d.ts.map +1 -0
  39. package/dist/types/src/components/Form/Layout/annotation.d.ts +30 -0
  40. package/dist/types/src/components/Form/Layout/annotation.d.ts.map +1 -0
  41. package/dist/types/src/components/Form/Layout/index.d.ts +4 -0
  42. package/dist/types/src/components/Form/Layout/index.d.ts.map +1 -0
  43. package/dist/types/src/components/Form/Layout/parser.d.ts +31 -0
  44. package/dist/types/src/components/Form/Layout/parser.d.ts.map +1 -0
  45. package/dist/types/src/components/Form/Layout/parser.test.d.ts +2 -0
  46. package/dist/types/src/components/Form/Layout/parser.test.d.ts.map +1 -0
  47. package/dist/types/src/components/Form/fields/ArrayField.d.ts +18 -0
  48. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -0
  49. package/dist/types/src/components/Form/fields/BooleanField.d.ts +4 -0
  50. package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -0
  51. package/dist/types/src/components/Form/fields/DateField.d.ts +4 -0
  52. package/dist/types/src/components/Form/fields/DateField.d.ts.map +1 -0
  53. package/dist/types/src/components/Form/fields/GeoPointField.d.ts +5 -0
  54. package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -0
  55. package/dist/types/src/components/Form/fields/InlineRefField.d.ts +13 -0
  56. package/dist/types/src/components/Form/fields/InlineRefField.d.ts.map +1 -0
  57. package/dist/types/src/components/Form/fields/MarkdownField.d.ts +11 -0
  58. package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
  59. package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts +85 -0
  60. package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts.map +1 -0
  61. package/dist/types/src/components/Form/fields/NumberField.d.ts +4 -0
  62. package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -0
  63. package/dist/types/src/components/Form/fields/RefField.d.ts +22 -0
  64. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
  65. package/dist/types/src/components/Form/fields/SelectField.d.ts +13 -0
  66. package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
  67. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
  68. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
  69. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +85 -0
  70. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
  71. package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
  72. package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
  73. package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
  74. package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
  75. package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
  76. package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
  77. package/dist/types/src/components/Form/fields/index.d.ts +14 -0
  78. package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
  79. package/dist/types/src/components/Form/index.d.ts +5 -4
  80. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  81. package/dist/types/src/components/Form/meta-tags.d.ts +30 -0
  82. package/dist/types/src/components/Form/meta-tags.d.ts.map +1 -0
  83. package/dist/types/src/components/Form/meta-tags.test.d.ts +2 -0
  84. package/dist/types/src/components/Form/meta-tags.test.d.ts.map +1 -0
  85. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +8 -0
  86. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -0
  87. package/dist/types/src/components/ObjectForm/index.d.ts +2 -0
  88. package/dist/types/src/components/ObjectForm/index.d.ts.map +1 -0
  89. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +39 -12
  90. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
  91. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +65 -59
  92. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
  93. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +9 -0
  94. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -0
  95. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +128 -0
  96. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -0
  97. package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts +2 -0
  98. package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts.map +1 -0
  99. package/dist/types/src/components/ObjectProperties/index.d.ts +2 -0
  100. package/dist/types/src/components/ObjectProperties/index.d.ts.map +1 -0
  101. package/dist/types/src/components/ObjectTree/ObjectTree.d.ts +9 -0
  102. package/dist/types/src/components/ObjectTree/ObjectTree.d.ts.map +1 -0
  103. package/dist/types/src/components/ObjectTree/ObjectTree.stories.d.ts +87 -0
  104. package/dist/types/src/components/ObjectTree/ObjectTree.stories.d.ts.map +1 -0
  105. package/dist/types/src/components/ObjectTree/index.d.ts +2 -0
  106. package/dist/types/src/components/ObjectTree/index.d.ts.map +1 -0
  107. package/dist/types/src/components/Settings/Settings.d.ts +51 -0
  108. package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
  109. package/dist/types/src/components/Settings/Settings.stories.d.ts +85 -0
  110. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
  111. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +43 -0
  112. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +1 -0
  113. package/dist/types/src/components/Settings/SettingsItem.d.ts +16 -0
  114. package/dist/types/src/components/Settings/SettingsItem.d.ts.map +1 -0
  115. package/dist/types/src/components/Settings/SettingsSelectField.d.ts +18 -0
  116. package/dist/types/src/components/Settings/SettingsSelectField.d.ts.map +1 -0
  117. package/dist/types/src/components/Settings/index.d.ts +4 -0
  118. package/dist/types/src/components/Settings/index.d.ts.map +1 -0
  119. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +16 -20
  120. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  121. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +72 -66
  122. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  123. package/dist/types/src/components/index.d.ts +4 -1
  124. package/dist/types/src/components/index.d.ts.map +1 -1
  125. package/dist/types/src/components/testing.d.ts +13 -6
  126. package/dist/types/src/components/testing.d.ts.map +1 -1
  127. package/dist/types/src/hooks/index.d.ts +2 -3
  128. package/dist/types/src/hooks/index.d.ts.map +1 -1
  129. package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
  130. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
  131. package/dist/types/src/hooks/useKeyHandler.d.ts +7 -0
  132. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
  133. package/dist/types/src/index.d.ts +0 -2
  134. package/dist/types/src/index.d.ts.map +1 -1
  135. package/dist/types/src/translations.d.ts +66 -60
  136. package/dist/types/src/translations.d.ts.map +1 -1
  137. package/dist/types/src/util/field-type.d.ts +19 -0
  138. package/dist/types/src/util/field-type.d.ts.map +1 -0
  139. package/dist/types/src/util/field.d.ts +3 -3
  140. package/dist/types/src/util/field.d.ts.map +1 -1
  141. package/dist/types/src/util/formatting.d.ts +2 -2
  142. package/dist/types/src/util/formatting.d.ts.map +1 -1
  143. package/dist/types/src/util/index.d.ts +2 -1
  144. package/dist/types/src/util/index.d.ts.map +1 -1
  145. package/dist/types/src/util/properties.d.ts +24 -0
  146. package/dist/types/src/util/properties.d.ts.map +1 -0
  147. package/dist/types/src/util/properties.test.d.ts +2 -0
  148. package/dist/types/src/util/properties.test.d.ts.map +1 -0
  149. package/dist/types/src/util/refs.d.ts +2 -2
  150. package/dist/types/src/util/refs.d.ts.map +1 -1
  151. package/dist/types/tsconfig.tsbuildinfo +1 -1
  152. package/package.json +62 -51
  153. package/src/components/FieldEditor/FieldEditor.stories.tsx +44 -25
  154. package/src/components/FieldEditor/FieldEditor.test.tsx +4 -2
  155. package/src/components/FieldEditor/FieldEditor.tsx +88 -86
  156. package/src/components/Form/AUDIT.md +17 -0
  157. package/src/components/Form/Form.stories.tsx +134 -252
  158. package/src/components/Form/Form.tsx +461 -106
  159. package/src/components/Form/FormField.tsx +363 -0
  160. package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
  161. package/src/components/Form/FormFieldComponent.tsx +229 -0
  162. package/src/components/Form/FormFieldSet.tsx +173 -0
  163. package/src/components/Form/FormTooltipsContext.ts +25 -0
  164. package/src/components/Form/Layout/FormLayout.stories.test.tsx +51 -0
  165. package/src/components/Form/Layout/FormLayout.stories.tsx +422 -0
  166. package/src/components/Form/Layout/FormLayout.test.ts +59 -0
  167. package/src/components/Form/Layout/FormLayout.tsx +251 -0
  168. package/src/components/Form/Layout/annotation.ts +38 -0
  169. package/src/components/Form/Layout/index.ts +7 -0
  170. package/src/components/Form/Layout/parser.test.ts +120 -0
  171. package/src/components/Form/Layout/parser.ts +167 -0
  172. package/src/components/Form/README.md +3 -0
  173. package/src/components/Form/fields/ArrayField.tsx +178 -0
  174. package/src/components/Form/fields/BooleanField.tsx +22 -0
  175. package/src/components/Form/fields/DateField.tsx +101 -0
  176. package/src/components/Form/fields/GeoPointField.tsx +124 -0
  177. package/src/components/Form/fields/InlineRefField.tsx +146 -0
  178. package/src/components/Form/fields/MarkdownField.stories.tsx +206 -0
  179. package/src/components/Form/fields/MarkdownField.tsx +168 -0
  180. package/src/components/Form/fields/NumberField.tsx +39 -0
  181. package/src/components/Form/fields/RefField.tsx +235 -0
  182. package/src/components/Form/fields/SelectField.tsx +77 -0
  183. package/src/components/Form/{custom/SelectOptionsInput.stories.tsx → fields/SelectOptionField.stories.tsx} +23 -18
  184. package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +31 -24
  185. package/src/components/Form/fields/TextAreaField.tsx +38 -0
  186. package/src/components/Form/fields/TextField.tsx +38 -0
  187. package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +17 -9
  188. package/src/components/Form/fields/index.ts +17 -0
  189. package/src/components/Form/index.ts +6 -4
  190. package/src/components/Form/meta-tags.test.ts +47 -0
  191. package/src/components/Form/meta-tags.ts +33 -0
  192. package/src/components/ObjectForm/ObjectForm.tsx +109 -0
  193. package/src/components/{ControlSection → ObjectForm}/index.ts +1 -1
  194. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +30 -25
  195. package/src/components/ObjectPicker/ObjectPicker.tsx +123 -82
  196. package/src/components/ObjectProperties/ObjectProperties.stories.tsx +350 -0
  197. package/src/components/ObjectProperties/ObjectProperties.test.tsx +111 -0
  198. package/src/components/ObjectProperties/ObjectProperties.tsx +144 -0
  199. package/src/components/ObjectProperties/index.ts +5 -0
  200. package/src/components/ObjectTree/ObjectTree.stories.tsx +134 -0
  201. package/src/components/ObjectTree/ObjectTree.tsx +190 -0
  202. package/src/components/ObjectTree/index.ts +5 -0
  203. package/src/components/Settings/AUDIT.md +51 -0
  204. package/src/components/Settings/Settings.stories.tsx +104 -0
  205. package/src/components/Settings/Settings.tsx +81 -0
  206. package/src/components/Settings/SettingsFieldSet.tsx +257 -0
  207. package/src/components/Settings/SettingsItem.tsx +58 -0
  208. package/src/components/Settings/SettingsSelectField.tsx +59 -0
  209. package/src/components/Settings/index.ts +7 -0
  210. package/src/components/ViewEditor/ViewEditor.stories.tsx +75 -77
  211. package/src/components/ViewEditor/ViewEditor.test.tsx +16 -13
  212. package/src/components/ViewEditor/ViewEditor.tsx +295 -215
  213. package/src/components/index.ts +4 -1
  214. package/src/components/testing.tsx +24 -11
  215. package/src/hooks/index.ts +2 -3
  216. package/src/hooks/useFormHandler.ts +380 -0
  217. package/src/hooks/useKeyHandler.tsx +37 -0
  218. package/src/index.ts +0 -4
  219. package/src/translations.ts +71 -65
  220. package/src/util/field-type.ts +60 -0
  221. package/src/util/field.ts +25 -25
  222. package/src/util/formatting.ts +22 -22
  223. package/src/util/index.ts +2 -1
  224. package/src/util/properties.test.ts +121 -0
  225. package/src/util/properties.ts +61 -0
  226. package/src/util/refs.ts +16 -18
  227. package/src/vite-env.d.ts +10 -0
  228. package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
  229. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
  230. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +0 -84
  231. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
  232. package/dist/types/src/components/ControlSection/index.d.ts +0 -2
  233. package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
  234. package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
  235. package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
  236. package/dist/types/src/components/Form/Defaults.d.ts +0 -14
  237. package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
  238. package/dist/types/src/components/Form/FormActions.d.ts +0 -9
  239. package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
  240. package/dist/types/src/components/Form/FormContent.d.ts +0 -63
  241. package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
  242. package/dist/types/src/components/Form/FormContext.d.ts +0 -23
  243. package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
  244. package/dist/types/src/components/Form/Input.d.ts +0 -29
  245. package/dist/types/src/components/Form/Input.d.ts.map +0 -1
  246. package/dist/types/src/components/Form/RefField.d.ts +0 -22
  247. package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
  248. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
  249. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
  250. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
  251. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
  252. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +0 -79
  253. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
  254. package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
  255. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
  256. package/dist/types/src/components/Form/custom/index.d.ts +0 -4
  257. package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
  258. package/dist/types/src/components/Form/factory.d.ts +0 -8
  259. package/dist/types/src/components/Form/factory.d.ts.map +0 -1
  260. package/dist/types/src/hooks/useForm.d.ts +0 -67
  261. package/dist/types/src/hooks/useForm.d.ts.map +0 -1
  262. package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -29
  263. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
  264. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
  265. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
  266. package/dist/types/src/util/schema.d.ts +0 -3
  267. package/dist/types/src/util/schema.d.ts.map +0 -1
  268. package/src/components/ControlSection/ControlSection.stories.tsx +0 -38
  269. package/src/components/ControlSection/ControlSection.tsx +0 -153
  270. package/src/components/Form/ArrayField.tsx +0 -125
  271. package/src/components/Form/Defaults.tsx +0 -205
  272. package/src/components/Form/FormActions.tsx +0 -54
  273. package/src/components/Form/FormContent.tsx +0 -322
  274. package/src/components/Form/FormContext.tsx +0 -110
  275. package/src/components/Form/Input.tsx +0 -54
  276. package/src/components/Form/RefField.tsx +0 -283
  277. package/src/components/Form/custom/GeoPointInput.tsx +0 -80
  278. package/src/components/Form/custom/index.ts +0 -7
  279. package/src/components/Form/factory.tsx +0 -31
  280. package/src/hooks/useForm.ts +0 -296
  281. package/src/hooks/useQueryRefOptions.ts +0 -62
  282. package/src/hooks/useRefQueryLookupHandler.ts +0 -39
  283. package/src/util/schema.ts +0 -12
@@ -0,0 +1,77 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import { Icon, Input, Select, type SelectRootProps } from '@dxos/react-ui';
8
+ import { getStyles } from '@dxos/ui-theme';
9
+
10
+ import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldComponent';
11
+
12
+ export type SelectFieldOptions = FormFieldComponentProps & {
13
+ options?: Array<{ value: string | number; label?: string; secondaryLabel?: string; icon?: string; iconHue?: string }>;
14
+ };
15
+
16
+ export const SelectField = ({
17
+ type,
18
+ readonly,
19
+ label,
20
+ jsonPath,
21
+ layout,
22
+ placeholder,
23
+ options,
24
+ getStatus,
25
+ getValue,
26
+ onValueChange,
27
+ }: SelectFieldOptions) => {
28
+ const { status, error } = getStatus();
29
+ const value = getValue() as string | undefined;
30
+
31
+ const handleValueChange = useCallback<NonNullable<SelectRootProps['onValueChange']>>(
32
+ (value) => onValueChange(type, value),
33
+ [type, onValueChange],
34
+ );
35
+
36
+ if ((readonly || layout === 'static') && value == null) {
37
+ return null;
38
+ }
39
+
40
+ return (
41
+ <Input.Root validationValence={status}>
42
+ {layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />}
43
+ {layout === 'static' ? (
44
+ <p>{options?.find(({ value: optionValue }) => optionValue === value)?.label ?? String(value)}</p>
45
+ ) : (
46
+ <Select.Root value={value} onValueChange={handleValueChange}>
47
+ {/* TODO(burdon): Placeholder not working? */}
48
+ <Select.TriggerButton classNames='w-full' disabled={!!readonly} placeholder={placeholder} />
49
+ <Select.Portal>
50
+ <Select.Content>
51
+ <Select.Viewport>
52
+ {options?.map(({ value, label, secondaryLabel, icon, iconHue }) => (
53
+ // NOTE: Numeric values are converted to and from strings.
54
+ <Select.Option key={String(value)} value={String(value)}>
55
+ <span className='flex items-center flex-row gap-2'>
56
+ {icon && <Icon icon={icon} classNames={getIconHueStyles(iconHue)} />}
57
+ {label ?? String(value)}
58
+ {secondaryLabel && <span className='text-subdued text-xs'>{secondaryLabel}</span>}
59
+ </span>
60
+ </Select.Option>
61
+ ))}
62
+ </Select.Viewport>
63
+ <Select.Arrow />
64
+ </Select.Content>
65
+ </Select.Portal>
66
+ </Select.Root>
67
+ )}
68
+ {layout === 'full' && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
69
+ </Input.Root>
70
+ );
71
+ };
72
+
73
+ const getIconHueStyles = (iconHue?: string): string | undefined => {
74
+ const styles = iconHue ? getStyles(iconHue) : undefined;
75
+
76
+ return styles?.fg;
77
+ };
@@ -6,17 +6,17 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import * as Schema from 'effect/Schema';
7
7
  import React, { useState } from 'react';
8
8
 
9
- import { type SelectOption, SelectOptionSchema } from '@dxos/echo/internal';
10
- import { withTheme } from '@dxos/react-ui/testing';
9
+ import { SelectOption } from '@dxos/echo/internal';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
11
 
12
- import { translations } from '../../../translations';
13
- import { TestLayout, TestPanel } from '../../testing';
14
- import { Form } from '../Form';
12
+ import { translations } from '#translations';
15
13
 
16
- import { SelectOptionInput } from './SelectOptionsInput';
14
+ import { TestLayout } from '../../testing';
15
+ import { Form } from '../Form';
16
+ import { SelectOptionField } from './SelectOptionField';
17
17
 
18
18
  const schema = Schema.Struct({
19
- options: Schema.Array(SelectOptionSchema).pipe(Schema.mutable),
19
+ options: Schema.Array(SelectOption).pipe(Schema.mutable),
20
20
  }).pipe(Schema.mutable);
21
21
 
22
22
  const DefaultStory = () => {
@@ -31,23 +31,28 @@ const DefaultStory = () => {
31
31
 
32
32
  return (
33
33
  <TestLayout>
34
- <TestPanel>
35
- <Form
36
- schema={schema}
37
- values={values}
38
- onSave={(vals) => setValues(vals)}
39
- Custom={{ options: SelectOptionInput }}
40
- />
41
- </TestPanel>
34
+ <Form.Root
35
+ schema={schema}
36
+ values={values}
37
+ fieldMap={{ options: SelectOptionField }}
38
+ onSave={(values) => setValues(values)}
39
+ >
40
+ <Form.Viewport>
41
+ <Form.Content>
42
+ <Form.FieldSet />
43
+ <Form.Actions />
44
+ </Form.Content>
45
+ </Form.Viewport>
46
+ </Form.Root>
42
47
  </TestLayout>
43
48
  );
44
49
  };
45
50
 
46
51
  const meta = {
47
- title: 'ui/react-ui-form/SelectOptionsInput',
48
- component: SelectOptionInput as any,
52
+ title: 'ui/react-ui-form/SelectOptionField',
53
+ component: SelectOptionField as any,
49
54
  render: DefaultStory,
50
- decorators: [withTheme],
55
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
51
56
  parameters: {
52
57
  layout: 'fullscreen',
53
58
  translations,
@@ -9,25 +9,34 @@ import { PublicKey } from '@dxos/keys';
9
9
  import { type ChromaticPalette, IconButton, Input, Tag, useTranslation } from '@dxos/react-ui';
10
10
  import { List } from '@dxos/react-ui-list';
11
11
  import { HuePicker } from '@dxos/react-ui-pickers';
12
- import { hues, subtleHover } from '@dxos/react-ui-theme';
12
+ import { hues, osTranslations } from '@dxos/ui-theme';
13
13
 
14
- import { translationKey } from '../../../translations';
15
- import { InputHeader, type InputProps } from '../Input';
14
+ import { translationKey } from '#translations';
16
15
 
17
- export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue, onValueChange }: InputProps) => {
16
+ import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldComponent';
17
+
18
+ export const SelectOptionField = ({
19
+ type,
20
+ readonly,
21
+ label,
22
+ jsonPath,
23
+ getStatus,
24
+ getValue,
25
+ onValueChange,
26
+ }: FormFieldComponentProps<SelectOption[] | undefined>) => {
27
+ const { t } = useTranslation(translationKey);
28
+ const { status, error } = getStatus();
18
29
  const [selected, setSelectedId] = useState<string | null>(null);
19
30
  const [isNewOption, setIsNewOption] = useState(false);
20
31
  const inputRef = useRef<HTMLInputElement>(null);
21
- const { t } = useTranslation(translationKey);
22
- const { status, error } = getStatus();
23
- const options = getValue<SelectOption[] | undefined>();
32
+ const options = getValue();
24
33
 
25
34
  // Initialization.
26
35
  useEffect(() => {
27
36
  if (options === undefined) {
28
37
  onValueChange(type, []);
29
38
  }
30
- }, [options, onValueChange, type]);
39
+ }, [options, type, onValueChange]);
31
40
 
32
41
  const randomHue = () => {
33
42
  const usedHues = new Set(options?.map((option) => option.color) ?? []);
@@ -88,8 +97,8 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
88
97
  [options, type, onValueChange],
89
98
  );
90
99
 
91
- const handleKeyDown = useCallback((e: KeyboardEvent<HTMLInputElement>) => {
92
- if (e.key === 'Enter') {
100
+ const handleKeyDown = useCallback((ev: KeyboardEvent<HTMLInputElement>) => {
101
+ if (ev.key === 'Enter') {
93
102
  setSelectedId(null);
94
103
  }
95
104
  }, []);
@@ -111,12 +120,12 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
111
120
 
112
121
  return (
113
122
  <Input.Root validationValence={status}>
114
- <InputHeader error={error} label={label} />
115
- <div role='none'>
123
+ <FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />
124
+ <div>
116
125
  {options && (
117
126
  <List.Root
118
127
  items={options}
119
- isItem={(item) => true}
128
+ isItem={(_item) => true}
120
129
  onMove={readonly ? undefined : handleMove}
121
130
  readonly={!!readonly}
122
131
  >
@@ -127,7 +136,7 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
127
136
  <List.Item
128
137
  role='button'
129
138
  item={item}
130
- classNames={[subtleHover, 'flex flex-col cursor-pointer rounded-sm']}
139
+ classNames='flex flex-col cursor-pointer rounded-xs dx-hover'
131
140
  aria-expanded={selected === item.id}
132
141
  >
133
142
  <div className='flex items-center'>
@@ -139,32 +148,30 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
139
148
  <IconButton
140
149
  iconOnly
141
150
  variant='ghost'
142
- label={t(selected === item.id ? 'collapse label' : 'expand label', { ns: 'os' })}
151
+ label={t(selected === item.id ? 'collapse.label' : 'expand.label', { ns: osTranslations })}
143
152
  icon={selected === item.id ? 'ph--caret-down--regular' : 'ph--caret-right--regular'}
144
153
  onClick={() => handleClick(item.id)}
145
154
  />
146
155
  </div>
147
156
  {selected === item.id && (
148
- <div className='pli-cardSpacingInline plb-cardSpacingBlock flex flex-col gap-1 density-fine'>
149
- {/* 16px to match drag handle width. */}
150
- <Input.Label classNames='text-xs'>{t('select option label')}</Input.Label>
151
- <div className='grid grid-cols-[1fr_min-content_min-content] gap-1'>
157
+ <div className='flex flex-col p-form-padding gap-form-gap dx-density-md'>
158
+ <Input.Label classNames='text-sm'>{t('select-option.label')}</Input.Label>
159
+ <div className='grid grid-cols-[1fr_min-content_min-content] gap-form-gap'>
152
160
  <Input.TextInput
153
161
  disabled={!!readonly}
154
- placeholder={t('select option label placeholder')}
162
+ placeholder={t('select-option-label.placeholder')}
155
163
  ref={selected === item.id ? inputRef : undefined}
156
164
  value={item.title}
157
165
  onChange={handleTitleChange(item.id)}
158
166
  onKeyDown={handleKeyDown}
159
167
  classNames='flex-1'
160
- data-no-submit
161
168
  />
162
169
  <HuePicker disabled={!!readonly} value={item.color} onChange={handleColorChange(item.id)} />
163
170
  <IconButton
164
171
  disabled={!!readonly}
165
172
  icon='ph--trash--fill'
166
173
  iconOnly
167
- label={t('select option delete')}
174
+ label={t('select-option-delete.button')}
168
175
  onClick={() => handleDelete(item.id)}
169
176
  />
170
177
  </div>
@@ -176,10 +183,10 @@ export const SelectOptionInput = ({ type, label, readonly, getStatus, getValue,
176
183
  <IconButton
177
184
  variant='ghost'
178
185
  icon='ph--plus--regular'
179
- label={t('select option add')}
186
+ label={t('select-option-add.button')}
180
187
  onClick={handleAdd}
181
188
  disabled={!!readonly}
182
- classNames='is-full'
189
+ classNames='w-full'
183
190
  />
184
191
  </div>
185
192
  )}
@@ -0,0 +1,38 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import { Input, type TextAreaProps } from '@dxos/react-ui';
8
+
9
+ import { type FormFieldComponentProps, FormFieldWrapper } from '../FormFieldComponent';
10
+
11
+ export const TextAreaField = ({
12
+ type,
13
+ readonly,
14
+ placeholder,
15
+ onValueChange,
16
+ onBlur,
17
+ ...props
18
+ }: FormFieldComponentProps<string>) => {
19
+ const handleChange = useCallback<NonNullable<TextAreaProps['onChange']>>(
20
+ (event) => onValueChange(type, event.target.value),
21
+ [type, onValueChange],
22
+ );
23
+
24
+ return (
25
+ <FormFieldWrapper<string> readonly={readonly} {...props}>
26
+ {({ value = '' }) => (
27
+ <Input.TextArea
28
+ rows={5}
29
+ disabled={!!readonly}
30
+ placeholder={placeholder}
31
+ value={value}
32
+ onChange={handleChange}
33
+ onBlur={onBlur}
34
+ />
35
+ )}
36
+ </FormFieldWrapper>
37
+ );
38
+ };
@@ -0,0 +1,38 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import { Input, type TextInputProps } from '@dxos/react-ui';
8
+
9
+ import { type FormFieldComponentProps, FormFieldWrapper } from '../FormFieldComponent';
10
+
11
+ export const TextField = ({
12
+ type,
13
+ readonly,
14
+ placeholder,
15
+ onBlur,
16
+ onValueChange,
17
+ ...props
18
+ }: FormFieldComponentProps<string>) => {
19
+ const handleChange = useCallback<NonNullable<TextInputProps['onChange']>>(
20
+ (event) => onValueChange(type, event.target.value),
21
+ [type, onValueChange],
22
+ );
23
+
24
+ return (
25
+ <FormFieldWrapper<string> readonly={readonly} {...props}>
26
+ {({ value = '' }) => (
27
+ <Input.TextInput
28
+ noAutoFill
29
+ disabled={!!readonly}
30
+ placeholder={placeholder}
31
+ value={value}
32
+ onBlur={onBlur}
33
+ onChange={handleChange}
34
+ />
35
+ )}
36
+ </FormFieldWrapper>
37
+ );
38
+ };
@@ -5,39 +5,47 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { Input } from '@dxos/react-ui';
8
- import { mx } from '@dxos/react-ui-theme';
8
+ import { mx } from '@dxos/ui-theme';
9
9
  import { safeParseFloat } from '@dxos/util';
10
10
 
11
- import { InputHeader, type InputProps } from '../Input';
11
+ import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldComponent';
12
12
 
13
13
  const gridCols = ['grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4'];
14
14
 
15
- export const TupleInput = ({
15
+ export const TupleField = ({
16
16
  binding,
17
17
  type,
18
- label,
19
18
  readonly,
19
+ label,
20
+ jsonPath,
20
21
  getStatus,
21
22
  getValue,
22
23
  onValueChange,
23
24
  onBlur,
24
- }: InputProps & { binding: string[] }) => {
25
+ }: FormFieldComponentProps<Record<string, number>> & {
26
+ binding: string[];
27
+ }) => {
25
28
  const { status, error } = getStatus();
26
29
 
27
30
  // TODO(burdon): Generalize number/float/string, etc.
28
- const values: Record<string, number> = getValue<Record<string, number>>() ?? {};
31
+ const values: Record<string, number> = getValue() ?? {};
29
32
 
30
33
  return (
31
34
  <Input.Root validationValence={status}>
32
- <InputHeader error={error} label={label} />
33
- <div className={mx('grid gap-2', gridCols[binding.length - 1])}>
35
+ <FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />
36
+ <div className={mx('grid gap-form-gap', gridCols[binding.length - 1])}>
34
37
  {binding.map((prop) => (
35
38
  <Input.TextInput
36
39
  key={prop}
37
40
  type='number'
38
41
  disabled={!!readonly}
39
42
  value={values[prop]}
40
- onChange={(event) => onValueChange(type, { ...values, [prop]: safeParseFloat(event.target.value, 0) })}
43
+ onChange={(event) => {
44
+ onValueChange(type, {
45
+ ...values,
46
+ [prop]: safeParseFloat(event.target.value, 0),
47
+ });
48
+ }}
41
49
  onBlur={onBlur}
42
50
  />
43
51
  ))}
@@ -0,0 +1,17 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './ArrayField';
6
+ export * from './BooleanField';
7
+ export * from './DateField';
8
+ export * from './GeoPointField';
9
+ export * from './InlineRefField';
10
+ export * from './MarkdownField';
11
+ export * from './NumberField';
12
+ export * from './RefField';
13
+ export * from './SelectField';
14
+ export * from './SelectOptionField';
15
+ export * from './TextField';
16
+ export * from './TextAreaField';
17
+ export * from './TupleField';
@@ -2,8 +2,10 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- export * from './custom';
6
- export * from './Defaults';
5
+ export * from './fields';
6
+
7
7
  export * from './Form';
8
- export * from './FormContext';
9
- export * from './Input';
8
+ export * from './FormFieldComponent';
9
+ export * from './FormTooltipsContext';
10
+ export * from './meta-tags';
11
+ export * from './Layout';
@@ -0,0 +1,47 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+ import * as SchemaAST from 'effect/SchemaAST';
7
+ import { describe, test } from 'vitest';
8
+
9
+ import { Obj, Ref, Tag } from '@dxos/echo';
10
+
11
+ import { META_TAGS_KEY, withMetaTags } from './meta-tags';
12
+
13
+ // A feed-host schema whose own `tags` field is a `TagIndex` record (tagId -> objectId[]), not an
14
+ // array — mirrors plugin-commerce `Search`, plugin-inbox `Mailbox`, plugin-feed `Subscription`.
15
+ const TagIndexHost = Schema.Struct({
16
+ name: Schema.String.pipe(Schema.optional),
17
+ tags: Schema.Record({ key: Schema.String, value: Schema.Array(Obj.ID) }).pipe(Schema.optional),
18
+ });
19
+
20
+ const propertyNames = (schema: Schema.Schema.AnyNoContext): string[] =>
21
+ SchemaAST.getPropertySignatures(schema.ast).map((property) => property.name.toString());
22
+
23
+ describe('withMetaTags', () => {
24
+ test('keying meta-tags as `tags` collides with a schema-declared `tags` record (regression)', ({ expect }) => {
25
+ // The pre-fix shape: splice a `tags: Array<Ref<Tag>>` onto a schema that already declares `tags`.
26
+ // `Schema.extend` cannot merge an array with a record at the same key, hence the companion crash:
27
+ // "overlapping types at path: [\"tags\"]".
28
+ expect(() =>
29
+ Schema.Struct({ tags: Schema.Array(Ref.Ref(Tag.Tag)).pipe(Schema.optional) }).pipe(Schema.extend(TagIndexHost)),
30
+ ).toThrow(/tags/i);
31
+ });
32
+
33
+ test('splices meta-tags under `_tags` without colliding with a schema-declared `tags`', ({ expect }) => {
34
+ const schema = withMetaTags(TagIndexHost);
35
+ const names = propertyNames(schema);
36
+ expect(names).toContain(META_TAGS_KEY);
37
+ expect(names).toContain('tags');
38
+ expect(names).not.toContain('id');
39
+ });
40
+
41
+ test('still splices meta-tags onto a schema with no `tags` field', ({ expect }) => {
42
+ const Plain = Schema.Struct({ name: Schema.String });
43
+ const names = propertyNames(withMetaTags(Plain));
44
+ expect(names).toContain(META_TAGS_KEY);
45
+ expect(names).toContain('name');
46
+ });
47
+ });
@@ -0,0 +1,33 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+
7
+ import { Ref, Tag } from '@dxos/echo';
8
+
9
+ import { omitId } from './Form';
10
+
11
+ /**
12
+ * Form-field key for the synthetic meta-tags array spliced into property/create forms.
13
+ *
14
+ * An object's tags live in `Obj.getMeta(obj).tags` (off-schema), so the form surfaces them as an
15
+ * extra field rather than reading the schema. The key is underscore-prefixed so it never collides
16
+ * with a `tags` field an object schema declares itself — e.g. a `TagIndex` record on a feed host,
17
+ * whose `tags` is `Record<tagId, objectId[]>`, not an array. Extending such a schema with a `tags`
18
+ * array threw "overlapping types at path: [\"tags\"]"; the disjoint `_tags` key avoids it. The
19
+ * human-facing label is restored via the `title` annotation on {@link withMetaTags}.
20
+ */
21
+ export const META_TAGS_KEY = '_tags';
22
+
23
+ /**
24
+ * Splices the synthetic meta-tags array (an object's `meta.tags`, surfaced as {@link Tag} refs) onto
25
+ * a schema for editing and drops `id`. Keyed by {@link META_TAGS_KEY} so it never collides with a
26
+ * `tags` field the schema declares itself.
27
+ */
28
+ export const withMetaTags = (schema: Schema.Schema.AnyNoContext) =>
29
+ omitId(
30
+ Schema.Struct({
31
+ [META_TAGS_KEY]: Schema.Array(Ref.Ref(Tag.Tag)).pipe(Schema.annotations({ title: 'Tags' }), Schema.optional),
32
+ }).pipe(Schema.extend(schema)),
33
+ );
@@ -0,0 +1,109 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback, useMemo } from 'react';
6
+
7
+ import { Obj, Ref, Tag, Type } from '@dxos/echo';
8
+ import { type JsonPath, splitJsonPath } from '@dxos/echo/internal';
9
+ import { invariant } from '@dxos/invariant';
10
+ import { HuePicker } from '@dxos/react-ui-pickers';
11
+
12
+ import { translationKey } from '#translations';
13
+
14
+ import { Form, type FormFieldMap, META_TAGS_KEY, withMetaTags } from '../Form';
15
+
16
+ export type ObjectFormProps = {
17
+ type: Type.AnyEntity;
18
+ object: Obj.Unknown;
19
+ };
20
+
21
+ const createFieldMap: FormFieldMap = {
22
+ hue: ({ type, label, layout, getValue, onValueChange }) => {
23
+ const handleChange = useCallback((nextHue: string) => onValueChange(type, nextHue), [onValueChange, type]);
24
+ const handleReset = useCallback(() => onValueChange(type, undefined), [onValueChange, type]);
25
+ return (
26
+ <>
27
+ {layout !== 'inline' && <Form.Label label={label} />}
28
+ <HuePicker value={getValue()} onChange={handleChange} onReset={handleReset} />
29
+ </>
30
+ );
31
+ },
32
+ };
33
+
34
+ export const ObjectForm = ({ object, type }: ObjectFormProps) => {
35
+ const db = Obj.getDatabase(object);
36
+ const meta = Obj.getMeta(object);
37
+ // `meta.tags` already holds `Ref<Tag>`s (materialized by the database handler).
38
+ const tags = [...meta.tags];
39
+ const values = useMemo(() => ({ [META_TAGS_KEY]: tags, ...object }), [object, tags]);
40
+ const formSchema = useMemo(() => withMetaTags(Type.getSchema(type)), [type]);
41
+
42
+ const handleCreate = useCallback((type: Type.AnyEntity, values: any) => {
43
+ invariant(db);
44
+ invariant(Type.isObject(type));
45
+ const newObject = db.add(Obj.make(type, values));
46
+ if (Obj.instanceOf(Tag.Tag, newObject)) {
47
+ Obj.update(object, (object) => {
48
+ Obj.getMeta(object).tags = [...Obj.getMeta(object).tags, Ref.make(newObject)];
49
+ });
50
+ }
51
+ }, []);
52
+
53
+ // TODO(wittjosiah): Use FormRootProps type.
54
+ const handleChange = useCallback(
55
+ (
56
+ { [META_TAGS_KEY]: metaTags, ...values }: any,
57
+ { isValid, changed }: { isValid: boolean; changed: Record<JsonPath, boolean> },
58
+ ) => {
59
+ if (!isValid) {
60
+ return;
61
+ }
62
+
63
+ const changedPaths = Object.keys(changed).filter((path) => changed[path as JsonPath]) as JsonPath[];
64
+
65
+ // Handle meta-tags separately using Obj.update.
66
+ const hasTagsChange = changedPaths.some((path) => splitJsonPath(path)[0] === META_TAGS_KEY);
67
+ if (hasTagsChange) {
68
+ Obj.update(object, (object) => {
69
+ // Copy so later in-place form mutations don't bypass the `Obj.update` boundary.
70
+ Obj.getMeta(object).tags = Array.isArray(metaTags) ? [...(metaTags as Ref.Ref<Tag.Tag>[])] : [];
71
+ });
72
+ }
73
+
74
+ // Handle other property changes.
75
+ const nonTagPaths = changedPaths.filter((path) => splitJsonPath(path)[0] !== META_TAGS_KEY);
76
+ if (nonTagPaths.length > 0) {
77
+ Obj.update(object, () => {
78
+ for (const path of nonTagPaths) {
79
+ const parts = splitJsonPath(path);
80
+ const value = Obj.getValue(values, parts);
81
+ Obj.setValue(object, parts, value);
82
+ }
83
+ });
84
+ }
85
+ },
86
+ [object],
87
+ );
88
+
89
+ return (
90
+ <Form.Root
91
+ schema={formSchema}
92
+ defaultValues={values}
93
+ createTypename={Type.getTypename(Tag.Tag)}
94
+ createOptionIcon='ph--plus--regular'
95
+ createOptionLabel={['add-tag.label', { ns: translationKey }]}
96
+ createInitialValuePath='label'
97
+ createFieldMap={createFieldMap}
98
+ db={db}
99
+ onValuesChanged={handleChange}
100
+ onCreate={handleCreate}
101
+ >
102
+ <Form.Viewport>
103
+ <Form.Content>
104
+ <Form.FieldSet />
105
+ </Form.Content>
106
+ </Form.Viewport>
107
+ </Form.Root>
108
+ );
109
+ };
@@ -2,4 +2,4 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- export * from './ControlSection';
5
+ export * from './ObjectForm';