@dxos/react-ui-form 0.8.4-main.fffef41 → 0.8.4-staging.60fe92afc8

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 (301) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +0 -1
  3. package/dist/lib/browser/index.mjs +4005 -2402
  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 +87 -0
  7. package/dist/lib/browser/translations.mjs.map +7 -0
  8. package/dist/lib/node-esm/index.mjs +4005 -2402
  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 +89 -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 +73 -59
  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 -27
  18. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/Form.stories.d.ts +192 -137
  20. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/FormField.d.ts +52 -0
  22. package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
  23. package/dist/types/src/components/Form/FormFieldComponent.d.ts +98 -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 +26 -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 +123 -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 +89 -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 +34 -0
  64. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
  65. package/dist/types/src/components/Form/fields/RefField.test.d.ts +2 -0
  66. package/dist/types/src/components/Form/fields/RefField.test.d.ts.map +1 -0
  67. package/dist/types/src/components/Form/fields/SelectField.d.ts +13 -0
  68. package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
  69. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
  70. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
  71. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +89 -0
  72. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
  73. package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
  74. package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
  75. package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
  76. package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
  77. package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
  78. package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
  79. package/dist/types/src/components/Form/fields/index.d.ts +14 -0
  80. package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
  81. package/dist/types/src/components/Form/index.d.ts +5 -4
  82. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  83. package/dist/types/src/components/Form/meta-tags.d.ts +30 -0
  84. package/dist/types/src/components/Form/meta-tags.d.ts.map +1 -0
  85. package/dist/types/src/components/Form/meta-tags.test.d.ts +2 -0
  86. package/dist/types/src/components/Form/meta-tags.test.d.ts.map +1 -0
  87. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +8 -0
  88. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -0
  89. package/dist/types/src/components/ObjectForm/ObjectForm.stories.d.ts +87 -0
  90. package/dist/types/src/components/ObjectForm/ObjectForm.stories.d.ts.map +1 -0
  91. package/dist/types/src/components/ObjectForm/index.d.ts +2 -0
  92. package/dist/types/src/components/ObjectForm/index.d.ts.map +1 -0
  93. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +39 -12
  94. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
  95. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +69 -58
  96. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
  97. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +9 -0
  98. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -0
  99. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +132 -0
  100. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -0
  101. package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts +2 -0
  102. package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts.map +1 -0
  103. package/dist/types/src/components/ObjectProperties/index.d.ts +2 -0
  104. package/dist/types/src/components/ObjectProperties/index.d.ts.map +1 -0
  105. package/dist/types/src/components/ObjectTree/ObjectTree.d.ts +9 -0
  106. package/dist/types/src/components/ObjectTree/ObjectTree.d.ts.map +1 -0
  107. package/dist/types/src/components/ObjectTree/ObjectTree.stories.d.ts +91 -0
  108. package/dist/types/src/components/ObjectTree/ObjectTree.stories.d.ts.map +1 -0
  109. package/dist/types/src/components/ObjectTree/index.d.ts +2 -0
  110. package/dist/types/src/components/ObjectTree/index.d.ts.map +1 -0
  111. package/dist/types/src/components/RefEditor/RefEditor.d.ts +73 -0
  112. package/dist/types/src/components/RefEditor/RefEditor.d.ts.map +1 -0
  113. package/dist/types/src/components/RefEditor/RefEditor.stories.d.ts +92 -0
  114. package/dist/types/src/components/RefEditor/RefEditor.stories.d.ts.map +1 -0
  115. package/dist/types/src/components/RefEditor/index.d.ts +3 -0
  116. package/dist/types/src/components/RefEditor/index.d.ts.map +1 -0
  117. package/dist/types/src/components/RefEditor/ref-editor-extension.d.ts +47 -0
  118. package/dist/types/src/components/RefEditor/ref-editor-extension.d.ts.map +1 -0
  119. package/dist/types/src/components/Settings/Settings.d.ts +51 -0
  120. package/dist/types/src/components/Settings/Settings.d.ts.map +1 -0
  121. package/dist/types/src/components/Settings/Settings.stories.d.ts +89 -0
  122. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -0
  123. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +43 -0
  124. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +1 -0
  125. package/dist/types/src/components/Settings/SettingsItem.d.ts +16 -0
  126. package/dist/types/src/components/Settings/SettingsItem.d.ts.map +1 -0
  127. package/dist/types/src/components/Settings/SettingsSelectField.d.ts +18 -0
  128. package/dist/types/src/components/Settings/SettingsSelectField.d.ts.map +1 -0
  129. package/dist/types/src/components/Settings/index.d.ts +4 -0
  130. package/dist/types/src/components/Settings/index.d.ts.map +1 -0
  131. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +15 -19
  132. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  133. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +75 -64
  134. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  135. package/dist/types/src/components/index.d.ts +5 -1
  136. package/dist/types/src/components/index.d.ts.map +1 -1
  137. package/dist/types/src/components/testing.d.ts +13 -6
  138. package/dist/types/src/components/testing.d.ts.map +1 -1
  139. package/dist/types/src/hooks/index.d.ts +2 -3
  140. package/dist/types/src/hooks/index.d.ts.map +1 -1
  141. package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
  142. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
  143. package/dist/types/src/hooks/useKeyHandler.d.ts +7 -0
  144. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
  145. package/dist/types/src/index.d.ts +0 -2
  146. package/dist/types/src/index.d.ts.map +1 -1
  147. package/dist/types/src/translations.d.ts +70 -59
  148. package/dist/types/src/translations.d.ts.map +1 -1
  149. package/dist/types/src/util/field-type.d.ts +19 -0
  150. package/dist/types/src/util/field-type.d.ts.map +1 -0
  151. package/dist/types/src/util/field.d.ts +3 -3
  152. package/dist/types/src/util/field.d.ts.map +1 -1
  153. package/dist/types/src/util/formatting.d.ts +2 -2
  154. package/dist/types/src/util/formatting.d.ts.map +1 -1
  155. package/dist/types/src/util/index.d.ts +2 -1
  156. package/dist/types/src/util/index.d.ts.map +1 -1
  157. package/dist/types/src/util/properties.d.ts +35 -0
  158. package/dist/types/src/util/properties.d.ts.map +1 -0
  159. package/dist/types/src/util/properties.test.d.ts +2 -0
  160. package/dist/types/src/util/properties.test.d.ts.map +1 -0
  161. package/dist/types/src/util/refs.d.ts +2 -2
  162. package/dist/types/src/util/refs.d.ts.map +1 -1
  163. package/dist/types/tsconfig.tsbuildinfo +1 -1
  164. package/package.json +64 -52
  165. package/src/components/FieldEditor/FieldEditor.stories.tsx +44 -25
  166. package/src/components/FieldEditor/FieldEditor.test.tsx +4 -2
  167. package/src/components/FieldEditor/FieldEditor.tsx +91 -86
  168. package/src/components/Form/AUDIT.md +17 -0
  169. package/src/components/Form/Form.stories.tsx +147 -252
  170. package/src/components/Form/Form.tsx +461 -108
  171. package/src/components/Form/FormField.tsx +345 -0
  172. package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
  173. package/src/components/Form/FormFieldComponent.tsx +253 -0
  174. package/src/components/Form/FormFieldSet.tsx +213 -0
  175. package/src/components/Form/FormTooltipsContext.ts +25 -0
  176. package/src/components/Form/Layout/FormLayout.stories.test.tsx +51 -0
  177. package/src/components/Form/Layout/FormLayout.stories.tsx +422 -0
  178. package/src/components/Form/Layout/FormLayout.test.ts +59 -0
  179. package/src/components/Form/Layout/FormLayout.tsx +251 -0
  180. package/src/components/Form/Layout/annotation.ts +38 -0
  181. package/src/components/Form/Layout/index.ts +7 -0
  182. package/src/components/Form/Layout/parser.test.ts +120 -0
  183. package/src/components/Form/Layout/parser.ts +167 -0
  184. package/src/components/Form/README.md +3 -0
  185. package/src/components/Form/fields/ArrayField.tsx +173 -0
  186. package/src/components/Form/fields/BooleanField.tsx +22 -0
  187. package/src/components/Form/fields/DateField.tsx +101 -0
  188. package/src/components/Form/fields/GeoPointField.tsx +124 -0
  189. package/src/components/Form/fields/InlineRefField.tsx +138 -0
  190. package/src/components/Form/fields/MarkdownField.stories.tsx +206 -0
  191. package/src/components/Form/fields/MarkdownField.tsx +167 -0
  192. package/src/components/Form/fields/NumberField.tsx +76 -0
  193. package/src/components/Form/fields/RefField.test.ts +45 -0
  194. package/src/components/Form/fields/RefField.tsx +266 -0
  195. package/src/components/Form/fields/SelectField.tsx +77 -0
  196. package/src/components/Form/{custom/SelectOptionsInput.stories.tsx → fields/SelectOptionField.stories.tsx} +23 -18
  197. package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +33 -26
  198. package/src/components/Form/fields/TextAreaField.tsx +38 -0
  199. package/src/components/Form/fields/TextField.tsx +38 -0
  200. package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +17 -9
  201. package/src/components/Form/fields/index.ts +17 -0
  202. package/src/components/Form/index.ts +6 -4
  203. package/src/components/Form/meta-tags.test.ts +47 -0
  204. package/src/components/Form/meta-tags.ts +33 -0
  205. package/src/components/ObjectForm/ObjectForm.stories.tsx +62 -0
  206. package/src/components/ObjectForm/ObjectForm.tsx +111 -0
  207. package/src/components/{ControlSection → ObjectForm}/index.ts +1 -1
  208. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +26 -21
  209. package/src/components/ObjectPicker/ObjectPicker.tsx +123 -82
  210. package/src/components/ObjectProperties/ObjectProperties.stories.tsx +350 -0
  211. package/src/components/ObjectProperties/ObjectProperties.test.tsx +111 -0
  212. package/src/components/ObjectProperties/ObjectProperties.tsx +146 -0
  213. package/src/components/ObjectProperties/index.ts +5 -0
  214. package/src/components/ObjectTree/ObjectTree.stories.tsx +134 -0
  215. package/src/components/ObjectTree/ObjectTree.tsx +190 -0
  216. package/src/components/ObjectTree/index.ts +5 -0
  217. package/src/components/RefEditor/RefEditor.stories.tsx +148 -0
  218. package/src/components/RefEditor/RefEditor.tsx +275 -0
  219. package/src/components/RefEditor/index.ts +6 -0
  220. package/src/components/RefEditor/ref-editor-extension.ts +254 -0
  221. package/src/components/Settings/AUDIT.md +51 -0
  222. package/src/components/Settings/Settings.stories.tsx +104 -0
  223. package/src/components/Settings/Settings.tsx +81 -0
  224. package/src/components/Settings/SettingsFieldSet.tsx +257 -0
  225. package/src/components/Settings/SettingsItem.tsx +58 -0
  226. package/src/components/Settings/SettingsSelectField.tsx +59 -0
  227. package/src/components/Settings/index.ts +7 -0
  228. package/src/components/ViewEditor/ViewEditor.stories.tsx +70 -76
  229. package/src/components/ViewEditor/ViewEditor.test.tsx +17 -13
  230. package/src/components/ViewEditor/ViewEditor.tsx +340 -226
  231. package/src/components/index.ts +5 -1
  232. package/src/components/testing.tsx +24 -11
  233. package/src/hooks/index.ts +2 -3
  234. package/src/hooks/useFormHandler.ts +380 -0
  235. package/src/hooks/useKeyHandler.tsx +37 -0
  236. package/src/index.ts +0 -4
  237. package/src/translations.ts +76 -64
  238. package/src/util/field-type.ts +60 -0
  239. package/src/util/field.ts +25 -25
  240. package/src/util/formatting.ts +22 -22
  241. package/src/util/index.ts +2 -1
  242. package/src/util/properties.test.ts +153 -0
  243. package/src/util/properties.ts +100 -0
  244. package/src/util/refs.ts +16 -15
  245. package/src/vite-env.d.ts +10 -0
  246. package/dist/types/src/components/ControlSection/ControlSection.d.ts +0 -23
  247. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +0 -1
  248. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +0 -83
  249. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +0 -1
  250. package/dist/types/src/components/ControlSection/index.d.ts +0 -2
  251. package/dist/types/src/components/ControlSection/index.d.ts.map +0 -1
  252. package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
  253. package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
  254. package/dist/types/src/components/Form/Defaults.d.ts +0 -14
  255. package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
  256. package/dist/types/src/components/Form/FormActions.d.ts +0 -9
  257. package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
  258. package/dist/types/src/components/Form/FormContent.d.ts +0 -63
  259. package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
  260. package/dist/types/src/components/Form/FormContext.d.ts +0 -23
  261. package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
  262. package/dist/types/src/components/Form/Input.d.ts +0 -29
  263. package/dist/types/src/components/Form/Input.d.ts.map +0 -1
  264. package/dist/types/src/components/Form/RefField.d.ts +0 -22
  265. package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
  266. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
  267. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
  268. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
  269. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
  270. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +0 -78
  271. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
  272. package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
  273. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
  274. package/dist/types/src/components/Form/custom/index.d.ts +0 -4
  275. package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
  276. package/dist/types/src/components/Form/factory.d.ts +0 -8
  277. package/dist/types/src/components/Form/factory.d.ts.map +0 -1
  278. package/dist/types/src/hooks/useForm.d.ts +0 -67
  279. package/dist/types/src/hooks/useForm.d.ts.map +0 -1
  280. package/dist/types/src/hooks/useQueryRefOptions.d.ts +0 -29
  281. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +0 -1
  282. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
  283. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
  284. package/dist/types/src/util/schema.d.ts +0 -3
  285. package/dist/types/src/util/schema.d.ts.map +0 -1
  286. package/src/components/ControlSection/ControlSection.stories.tsx +0 -38
  287. package/src/components/ControlSection/ControlSection.tsx +0 -153
  288. package/src/components/Form/ArrayField.tsx +0 -125
  289. package/src/components/Form/Defaults.tsx +0 -205
  290. package/src/components/Form/FormActions.tsx +0 -54
  291. package/src/components/Form/FormContent.tsx +0 -362
  292. package/src/components/Form/FormContext.tsx +0 -110
  293. package/src/components/Form/Input.tsx +0 -54
  294. package/src/components/Form/RefField.tsx +0 -283
  295. package/src/components/Form/custom/GeoPointInput.tsx +0 -80
  296. package/src/components/Form/custom/index.ts +0 -7
  297. package/src/components/Form/factory.tsx +0 -31
  298. package/src/hooks/useForm.ts +0 -296
  299. package/src/hooks/useQueryRefOptions.ts +0 -62
  300. package/src/hooks/useRefQueryLookupHandler.ts +0 -39
  301. package/src/util/schema.ts +0 -12
@@ -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,62 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React from 'react';
7
+
8
+ import { Filter, Obj, Ref, Tag } from '@dxos/echo';
9
+ import { useQuery } from '@dxos/react-client/echo';
10
+ import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
11
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
12
+ import { Person } from '@dxos/types';
13
+
14
+ import { translations } from '#translations';
15
+
16
+ import { ObjectForm } from './ObjectForm';
17
+
18
+ const DefaultStory = () => {
19
+ const { space } = useClientStory();
20
+ const [person] = useQuery(space?.db, Filter.type(Person.Person));
21
+ if (!person) {
22
+ return <></>;
23
+ }
24
+
25
+ return <ObjectForm object={person} type={Person.Person} />;
26
+ };
27
+
28
+ const meta = {
29
+ title: 'ui/react-ui-form/ObjectForm',
30
+ render: DefaultStory,
31
+ decorators: [
32
+ withTheme(),
33
+ withLayout({ layout: 'column', classNames: 'p-2', scroll: true }),
34
+ withClientProvider({
35
+ types: [Person.Person, Tag.Tag],
36
+ createIdentity: true,
37
+ createSpace: true,
38
+ onCreateSpace: async ({ space }) => {
39
+ const tag = space.db.add(Tag.make({ label: 'Friend', hue: 'emerald' }));
40
+ const person = space.db.add(
41
+ Obj.make(Person.Person, {
42
+ fullName: 'Alice Carroll',
43
+ jobTitle: 'Engineer',
44
+ emails: [{ value: 'alice@example.com' }],
45
+ }),
46
+ );
47
+ Obj.update(person, (person) => {
48
+ Obj.getMeta(person).tags = [Ref.make(tag)];
49
+ });
50
+ },
51
+ }),
52
+ ],
53
+ parameters: {
54
+ translations,
55
+ },
56
+ } satisfies Meta<typeof DefaultStory>;
57
+
58
+ export default meta;
59
+
60
+ type Story = StoryObj<typeof meta>;
61
+
62
+ export const Default: Story = {};
@@ -0,0 +1,111 @@
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 { SchemaEx } from '@dxos/effect';
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<SchemaEx.JsonPath, boolean> },
58
+ ) => {
59
+ if (!isValid) {
60
+ return;
61
+ }
62
+
63
+ const changedPaths = Object.keys(changed).filter(
64
+ (path) => changed[path as SchemaEx.JsonPath],
65
+ ) as SchemaEx.JsonPath[];
66
+
67
+ // Handle meta-tags separately using Obj.update.
68
+ const hasTagsChange = changedPaths.some((path) => SchemaEx.splitJsonPath(path)[0] === META_TAGS_KEY);
69
+ if (hasTagsChange) {
70
+ Obj.update(object, (object) => {
71
+ // Copy so later in-place form mutations don't bypass the `Obj.update` boundary.
72
+ Obj.getMeta(object).tags = Array.isArray(metaTags) ? [...(metaTags as Ref.Ref<Tag.Tag>[])] : [];
73
+ });
74
+ }
75
+
76
+ // Handle other property changes.
77
+ const nonTagPaths = changedPaths.filter((path) => SchemaEx.splitJsonPath(path)[0] !== META_TAGS_KEY);
78
+ if (nonTagPaths.length > 0) {
79
+ Obj.update(object, () => {
80
+ for (const path of nonTagPaths) {
81
+ const parts = SchemaEx.splitJsonPath(path);
82
+ const value = Obj.getValue(values, parts);
83
+ Obj.setValue(object, parts, value);
84
+ }
85
+ });
86
+ }
87
+ },
88
+ [object],
89
+ );
90
+
91
+ return (
92
+ <Form.Root
93
+ schema={formSchema}
94
+ defaultValues={values}
95
+ createTypename={Type.getTypename(Tag.Tag)}
96
+ createOptionIcon='ph--plus--regular'
97
+ createOptionLabel={['add-tag.label', { ns: translationKey }]}
98
+ createInitialValuePath='label'
99
+ createFieldMap={createFieldMap}
100
+ db={db}
101
+ onValuesChanged={handleChange}
102
+ onCreate={handleCreate}
103
+ >
104
+ <Form.Viewport>
105
+ <Form.Content>
106
+ <Form.FieldSet />
107
+ </Form.Content>
108
+ </Form.Viewport>
109
+ </Form.Root>
110
+ );
111
+ };
@@ -2,4 +2,4 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- export * from './ControlSection';
5
+ export * from './ObjectForm';
@@ -7,34 +7,35 @@ import * as Schema from 'effect/Schema';
7
7
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
8
8
  import { expect, fn, userEvent, within } from 'storybook/test';
9
9
 
10
- import { Obj } from '@dxos/echo';
11
- import { faker } from '@dxos/random';
12
- import { Filter, useQuery } from '@dxos/react-client/echo';
13
- import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
10
+ import { Filter, Obj, Type } from '@dxos/echo';
11
+ import { random } from '@dxos/random';
12
+ import { useQuery } from '@dxos/react-client/echo';
13
+ import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
14
14
  import { Button } from '@dxos/react-ui';
15
15
  import { withTheme } from '@dxos/react-ui/testing';
16
16
  import { Person } from '@dxos/types';
17
+ import { osTranslations } from '@dxos/ui-theme';
17
18
 
18
- import { translations } from '../../translations';
19
+ import { translations } from '#translations';
19
20
 
20
21
  import { ObjectPicker } from './ObjectPicker';
21
22
 
22
- faker.seed(1);
23
+ random.seed(1);
23
24
 
24
25
  const createPerson = () =>
25
26
  Obj.make(Person.Person, {
26
- fullName: faker.person.fullName(),
27
+ fullName: random.person.fullName(),
27
28
  });
28
29
 
29
30
  const omitId = Schema.omit<any, any, ['id']>('id');
30
- const personSchema = omitId(Person.Person);
31
+ const personSchema = omitId(Type.getSchema(Person.Person));
31
32
 
32
33
  // Mock functions for testing
33
34
  const mockHandleSelect = fn();
34
35
  const mockHandleCreate = fn();
35
36
 
36
37
  const DefaultStory = () => {
37
- const { space } = useClientProvider();
38
+ const { space } = useClientStory();
38
39
  const [isOpen, setIsOpen] = useState(false);
39
40
 
40
41
  // Create sample Person objects
@@ -47,8 +48,8 @@ const DefaultStory = () => {
47
48
  }
48
49
  }, [space]);
49
50
 
50
- // Get all objects in the space (similar to BoardContainer)
51
- const allObjects = useQuery(space, Filter.everything());
51
+ // Get all objects in the space (similar to BoardArticle)
52
+ const allObjects = useQuery(space?.db, Filter.everything());
52
53
 
53
54
  // Map objects to options format expected by ObjectPicker
54
55
  const options = useMemo(
@@ -64,7 +65,9 @@ const DefaultStory = () => {
64
65
  const handleCreateCallback = useCallback(
65
66
  (values: any) => {
66
67
  console.log('[on create]', values);
67
- if (!space) return;
68
+ if (!space) {
69
+ return;
70
+ }
68
71
  const newPerson = space.db.add(Obj.make(Person.Person, values));
69
72
  mockHandleCreate(values);
70
73
  return newPerson;
@@ -73,22 +76,22 @@ const DefaultStory = () => {
73
76
  );
74
77
 
75
78
  return (
76
- <div className='p-4'>
79
+ <div className='w-96'>
77
80
  <ObjectPicker.Root open={isOpen} onOpenChange={setIsOpen}>
78
81
  <ObjectPicker.Trigger asChild>
79
- <Button variant='primary' data-testid='trigger'>
82
+ <Button variant='primary' data-testid='trigger' classNames='w-full'>
80
83
  Select Person
81
84
  </Button>
82
85
  </ObjectPicker.Trigger>
83
86
  <ObjectPicker.Content
87
+ classNames='dx-card-popover-width'
84
88
  options={options}
85
- onSelect={mockHandleSelect}
86
89
  createSchema={personSchema}
87
- createOptionLabel={['create new person label', { ns: 'os' }]}
90
+ createOptionLabel={['create-new-person.label', { ns: osTranslations }]}
88
91
  createOptionIcon='ph--user-plus--regular'
89
92
  createInitialValuePath='fullName'
90
93
  onCreate={handleCreateCallback}
91
- classNames='popover-card-width'
94
+ onSelect={mockHandleSelect}
92
95
  />
93
96
  </ObjectPicker.Root>
94
97
  </div>
@@ -99,7 +102,7 @@ const meta = {
99
102
  title: 'ui/react-ui-form/ObjectPicker',
100
103
  render: DefaultStory,
101
104
  decorators: [
102
- withTheme,
105
+ withTheme(),
103
106
  withClientProvider({
104
107
  types: [Person.Person],
105
108
  createIdentity: true,
@@ -200,8 +203,10 @@ export const WithTest: Story = {
200
203
  await userEvent.click(saveButton);
201
204
 
202
205
  // Check that clicking the save button calls onCreate with expected values.
203
- await expect(mockHandleCreate).toHaveBeenCalledWith({
204
- fullName: unrelatedTerm,
205
- });
206
+ await expect(mockHandleCreate).toHaveBeenCalledWith(
207
+ expect.objectContaining({
208
+ fullName: unrelatedTerm,
209
+ }),
210
+ );
206
211
  },
207
212
  };