@dxos/react-ui-form 0.9.0 → 0.9.1-staging.ee54ba693a

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 (372) hide show
  1. package/dist/lib/browser/index.mjs +2337 -1919
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +2337 -1919
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/annotations.d.ts +47 -0
  8. package/dist/types/src/annotations.d.ts.map +1 -0
  9. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +3 -3
  10. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  11. package/dist/types/src/components/Form/Form.d.ts +23 -110
  12. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  13. package/dist/types/src/components/Form/Form.stories.d.ts +48 -101
  14. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/Form.theme.d.ts +252 -0
  16. package/dist/types/src/components/Form/Form.theme.d.ts.map +1 -0
  17. package/dist/types/src/components/Form/Form.theme.test.d.ts +2 -0
  18. package/dist/types/src/components/Form/Form.theme.test.d.ts.map +1 -0
  19. package/dist/types/src/components/Form/FormControls.d.ts +75 -0
  20. package/dist/types/src/components/Form/FormControls.d.ts.map +1 -0
  21. package/dist/types/src/components/Form/FormField/FormField.d.ts +44 -0
  22. package/dist/types/src/components/Form/FormField/FormField.d.ts.map +1 -0
  23. package/dist/types/src/components/Form/FormField/FormFieldHeader.d.ts +27 -0
  24. package/dist/types/src/components/Form/FormField/FormFieldHeader.d.ts.map +1 -0
  25. package/dist/types/src/components/Form/FormField/FormRow.d.ts +85 -0
  26. package/dist/types/src/components/Form/FormField/FormRow.d.ts.map +1 -0
  27. package/dist/types/src/components/Form/{FormFieldComponent.stories.d.ts → FormField/FormRow.stories.d.ts} +6 -3
  28. package/dist/types/src/components/Form/FormField/FormRow.stories.d.ts.map +1 -0
  29. package/dist/types/src/components/Form/{fields → FormField/fields/ArrayField}/ArrayField.d.ts +2 -2
  30. package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.d.ts.map +1 -0
  31. package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.d.ts +88 -0
  32. package/dist/types/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.d.ts.map +1 -0
  33. package/dist/types/src/components/Form/FormField/fields/ArrayField/index.d.ts +2 -0
  34. package/dist/types/src/components/Form/FormField/fields/ArrayField/index.d.ts.map +1 -0
  35. package/dist/types/src/components/Form/FormField/fields/AsyncSelectField/AsyncSelectField.d.ts +12 -0
  36. package/dist/types/src/components/Form/FormField/fields/AsyncSelectField/AsyncSelectField.d.ts.map +1 -0
  37. package/dist/types/src/components/Form/FormField/fields/AsyncSelectField/index.d.ts +2 -0
  38. package/dist/types/src/components/Form/FormField/fields/AsyncSelectField/index.d.ts.map +1 -0
  39. package/dist/types/src/components/Form/FormField/fields/AutofillField/AutofillField.d.ts +12 -0
  40. package/dist/types/src/components/Form/FormField/fields/AutofillField/AutofillField.d.ts.map +1 -0
  41. package/dist/types/src/components/Form/FormField/fields/AutofillField/index.d.ts +2 -0
  42. package/dist/types/src/components/Form/FormField/fields/AutofillField/index.d.ts.map +1 -0
  43. package/dist/types/src/components/Form/FormField/fields/BooleanField/BooleanField.d.ts +4 -0
  44. package/dist/types/src/components/Form/FormField/fields/BooleanField/BooleanField.d.ts.map +1 -0
  45. package/dist/types/src/components/{Settings/Settings.stories.d.ts → Form/FormField/fields/BooleanField/BooleanField.stories.d.ts} +1 -2
  46. package/dist/types/src/components/Form/FormField/fields/BooleanField/BooleanField.stories.d.ts.map +1 -0
  47. package/dist/types/src/components/Form/FormField/fields/BooleanField/index.d.ts +2 -0
  48. package/dist/types/src/components/Form/FormField/fields/BooleanField/index.d.ts.map +1 -0
  49. package/dist/types/src/components/Form/FormField/fields/ComboboxField/ComboboxField.d.ts +12 -0
  50. package/dist/types/src/components/Form/FormField/fields/ComboboxField/ComboboxField.d.ts.map +1 -0
  51. package/dist/types/src/components/Form/FormField/fields/ComboboxField/index.d.ts +2 -0
  52. package/dist/types/src/components/Form/FormField/fields/ComboboxField/index.d.ts.map +1 -0
  53. package/dist/types/src/components/Form/FormField/fields/DateField/DateField.d.ts +4 -0
  54. package/dist/types/src/components/Form/FormField/fields/DateField/DateField.d.ts.map +1 -0
  55. package/dist/types/src/components/Form/FormField/fields/DateField/DateField.stories.d.ts +88 -0
  56. package/dist/types/src/components/Form/FormField/fields/DateField/DateField.stories.d.ts.map +1 -0
  57. package/dist/types/src/components/Form/FormField/fields/DateField/index.d.ts +2 -0
  58. package/dist/types/src/components/Form/FormField/fields/DateField/index.d.ts.map +1 -0
  59. package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.d.ts +5 -0
  60. package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.d.ts.map +1 -0
  61. package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.stories.d.ts +88 -0
  62. package/dist/types/src/components/Form/FormField/fields/GeoPointField/GeoPointField.stories.d.ts.map +1 -0
  63. package/dist/types/src/components/Form/FormField/fields/GeoPointField/index.d.ts +2 -0
  64. package/dist/types/src/components/Form/FormField/fields/GeoPointField/index.d.ts.map +1 -0
  65. package/dist/types/src/components/Form/{fields → FormField/fields/MarkdownField}/MarkdownField.d.ts +2 -2
  66. package/dist/types/src/components/Form/FormField/fields/MarkdownField/MarkdownField.d.ts.map +1 -0
  67. package/dist/types/src/components/Form/FormField/fields/MarkdownField/MarkdownField.stories.d.ts.map +1 -0
  68. package/dist/types/src/components/Form/FormField/fields/MarkdownField/index.d.ts +2 -0
  69. package/dist/types/src/components/Form/FormField/fields/MarkdownField/index.d.ts.map +1 -0
  70. package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.d.ts +15 -0
  71. package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.d.ts.map +1 -0
  72. package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.stories.d.ts +88 -0
  73. package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.stories.d.ts.map +1 -0
  74. package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.test.d.ts +2 -0
  75. package/dist/types/src/components/Form/FormField/fields/NumberField/NumberField.test.d.ts.map +1 -0
  76. package/dist/types/src/components/Form/FormField/fields/NumberField/index.d.ts +2 -0
  77. package/dist/types/src/components/Form/FormField/fields/NumberField/index.d.ts.map +1 -0
  78. package/dist/types/src/components/Form/FormField/fields/PasswordField/PasswordField.d.ts +4 -0
  79. package/dist/types/src/components/Form/FormField/fields/PasswordField/PasswordField.d.ts.map +1 -0
  80. package/dist/types/src/components/Form/FormField/fields/PasswordField/PasswordField.stories.d.ts +88 -0
  81. package/dist/types/src/components/Form/FormField/fields/PasswordField/PasswordField.stories.d.ts.map +1 -0
  82. package/dist/types/src/components/Form/FormField/fields/PasswordField/index.d.ts +2 -0
  83. package/dist/types/src/components/Form/FormField/fields/PasswordField/index.d.ts.map +1 -0
  84. package/dist/types/src/components/Form/FormField/fields/RefField/InlineRefField.d.ts.map +1 -0
  85. package/dist/types/src/components/Form/FormField/fields/RefField/RefField.d.ts +16 -0
  86. package/dist/types/src/components/Form/FormField/fields/RefField/RefField.d.ts.map +1 -0
  87. package/dist/types/src/components/Form/FormField/fields/RefField/RefField.stories.d.ts +92 -0
  88. package/dist/types/src/components/Form/FormField/fields/RefField/RefField.stories.d.ts.map +1 -0
  89. package/dist/types/src/components/Form/FormField/fields/RefField/RefField.test.d.ts.map +1 -0
  90. package/dist/types/src/components/Form/FormField/fields/RefField/index.d.ts +3 -0
  91. package/dist/types/src/components/Form/FormField/fields/RefField/index.d.ts.map +1 -0
  92. package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.d.ts +29 -0
  93. package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.d.ts.map +1 -0
  94. package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.stories.d.ts +88 -0
  95. package/dist/types/src/components/Form/FormField/fields/SelectField/SelectField.stories.d.ts.map +1 -0
  96. package/dist/types/src/components/Form/FormField/fields/SelectField/index.d.ts +2 -0
  97. package/dist/types/src/components/Form/FormField/fields/SelectField/index.d.ts.map +1 -0
  98. package/dist/types/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.d.ts +5 -0
  99. package/dist/types/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.d.ts.map +1 -0
  100. package/dist/types/src/components/Form/FormField/fields/SelectOptionField/SelectOptionField.stories.d.ts.map +1 -0
  101. package/dist/types/src/components/Form/FormField/fields/SelectOptionField/index.d.ts +2 -0
  102. package/dist/types/src/components/Form/FormField/fields/SelectOptionField/index.d.ts.map +1 -0
  103. package/dist/types/src/components/Form/FormField/fields/TextAreaField/TextAreaField.d.ts +4 -0
  104. package/dist/types/src/components/Form/FormField/fields/TextAreaField/TextAreaField.d.ts.map +1 -0
  105. package/dist/types/src/components/Form/FormField/fields/TextAreaField/TextAreaField.stories.d.ts +88 -0
  106. package/dist/types/src/components/Form/FormField/fields/TextAreaField/TextAreaField.stories.d.ts.map +1 -0
  107. package/dist/types/src/components/Form/FormField/fields/TextAreaField/index.d.ts +2 -0
  108. package/dist/types/src/components/Form/FormField/fields/TextAreaField/index.d.ts.map +1 -0
  109. package/dist/types/src/components/Form/FormField/fields/TextField/TextField.d.ts +4 -0
  110. package/dist/types/src/components/Form/FormField/fields/TextField/TextField.d.ts.map +1 -0
  111. package/dist/types/src/components/Form/FormField/fields/TextField/TextField.stories.d.ts +88 -0
  112. package/dist/types/src/components/Form/FormField/fields/TextField/TextField.stories.d.ts.map +1 -0
  113. package/dist/types/src/components/Form/FormField/fields/TextField/index.d.ts +2 -0
  114. package/dist/types/src/components/Form/FormField/fields/TextField/index.d.ts.map +1 -0
  115. package/dist/types/src/components/Form/{fields → FormField/fields/TupleField}/TupleField.d.ts +2 -2
  116. package/dist/types/src/components/Form/FormField/fields/TupleField/TupleField.d.ts.map +1 -0
  117. package/dist/types/src/components/Form/FormField/fields/TupleField/TupleField.stories.d.ts +88 -0
  118. package/dist/types/src/components/Form/FormField/fields/TupleField/TupleField.stories.d.ts.map +1 -0
  119. package/dist/types/src/components/Form/FormField/fields/TupleField/index.d.ts +2 -0
  120. package/dist/types/src/components/Form/FormField/fields/TupleField/index.d.ts.map +1 -0
  121. package/dist/types/src/components/Form/{fields → FormField/fields}/index.d.ts +4 -1
  122. package/dist/types/src/components/Form/FormField/fields/index.d.ts.map +1 -0
  123. package/dist/types/src/components/Form/FormField/index.d.ts +6 -0
  124. package/dist/types/src/components/Form/FormField/index.d.ts.map +1 -0
  125. package/dist/types/src/components/Form/FormField/presentation.d.ts +22 -0
  126. package/dist/types/src/components/Form/FormField/presentation.d.ts.map +1 -0
  127. package/dist/types/src/components/Form/FormFieldSet/FormFieldSet.d.ts +30 -0
  128. package/dist/types/src/components/Form/FormFieldSet/FormFieldSet.d.ts.map +1 -0
  129. package/dist/types/src/components/Form/FormFieldSet/FormFieldSetContainer.d.ts +22 -0
  130. package/dist/types/src/components/Form/FormFieldSet/FormFieldSetContainer.d.ts.map +1 -0
  131. package/dist/types/src/components/Form/FormFieldSet/index.d.ts +3 -0
  132. package/dist/types/src/components/Form/FormFieldSet/index.d.ts.map +1 -0
  133. package/dist/types/src/components/Form/{Layout → FormLayout}/FormLayout.d.ts +2 -0
  134. package/dist/types/src/components/Form/FormLayout/FormLayout.d.ts.map +1 -0
  135. package/dist/types/src/components/Form/{Layout → FormLayout}/FormLayout.stories.d.ts +5 -5
  136. package/dist/types/src/components/Form/FormLayout/FormLayout.stories.d.ts.map +1 -0
  137. package/dist/types/src/components/Form/FormLayout/FormLayout.stories.test.d.ts.map +1 -0
  138. package/dist/types/src/components/Form/FormLayout/FormLayout.test.d.ts.map +1 -0
  139. package/dist/types/src/components/Form/FormLayout/index.d.ts +3 -0
  140. package/dist/types/src/components/Form/FormLayout/index.d.ts.map +1 -0
  141. package/dist/types/src/components/Form/FormLayout/parser.d.ts.map +1 -0
  142. package/dist/types/src/components/Form/FormLayout/parser.test.d.ts.map +1 -0
  143. package/dist/types/src/components/Form/index.d.ts +3 -5
  144. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  145. package/dist/types/src/components/Form/meta-tags.d.ts +1 -12
  146. package/dist/types/src/components/Form/meta-tags.d.ts.map +1 -1
  147. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -1
  148. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +6 -23
  149. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
  150. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +2 -1
  151. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -1
  152. package/dist/types/src/components/RefEditor/RefEditor.stories.d.ts +2 -2
  153. package/dist/types/src/components/RefEditor/RefEditor.stories.d.ts.map +1 -1
  154. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +1 -1
  155. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  156. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +2 -2
  157. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  158. package/dist/types/src/components/index.d.ts +0 -1
  159. package/dist/types/src/components/index.d.ts.map +1 -1
  160. package/dist/types/src/hooks/index.d.ts +2 -0
  161. package/dist/types/src/hooks/index.d.ts.map +1 -1
  162. package/dist/types/src/hooks/useAsyncFieldEffect.d.ts +24 -0
  163. package/dist/types/src/hooks/useAsyncFieldEffect.d.ts.map +1 -0
  164. package/dist/types/src/hooks/useFormContext.d.ts +42 -0
  165. package/dist/types/src/hooks/useFormContext.d.ts.map +1 -0
  166. package/dist/types/src/hooks/useFormHandler.d.ts +9 -6
  167. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -1
  168. package/dist/types/src/index.d.ts +2 -0
  169. package/dist/types/src/index.d.ts.map +1 -1
  170. package/dist/types/src/{components/testing.d.ts → testing/TestLayout.d.ts} +2 -2
  171. package/dist/types/src/testing/TestLayout.d.ts.map +1 -0
  172. package/dist/types/src/testing/index.d.ts +3 -0
  173. package/dist/types/src/testing/index.d.ts.map +1 -0
  174. package/dist/types/src/testing/schema.d.ts +40 -0
  175. package/dist/types/src/testing/schema.d.ts.map +1 -0
  176. package/dist/types/src/types.d.ts +161 -0
  177. package/dist/types/src/types.d.ts.map +1 -0
  178. package/dist/types/src/util/index.d.ts +1 -1
  179. package/dist/types/src/util/index.d.ts.map +1 -1
  180. package/dist/types/src/util/omit.d.ts +12 -0
  181. package/dist/types/src/util/omit.d.ts.map +1 -0
  182. package/dist/types/tsconfig.tsbuildinfo +1 -1
  183. package/package.json +43 -41
  184. package/src/annotations.ts +63 -0
  185. package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
  186. package/src/components/FieldEditor/FieldEditor.test.tsx +1 -1
  187. package/src/components/FieldEditor/FieldEditor.tsx +10 -6
  188. package/src/components/Form/DESIGN.md +24 -0
  189. package/src/components/Form/Form.stories.tsx +260 -61
  190. package/src/components/Form/Form.theme.test.ts +33 -0
  191. package/src/components/Form/Form.theme.ts +86 -0
  192. package/src/components/Form/Form.tsx +30 -453
  193. package/src/components/Form/FormControls.tsx +377 -0
  194. package/src/components/Form/{FormField.tsx → FormField/FormField.tsx} +98 -71
  195. package/src/components/Form/FormField/FormFieldHeader.tsx +68 -0
  196. package/src/components/Form/FormField/FormRow.stories.tsx +97 -0
  197. package/src/components/Form/FormField/FormRow.tsx +298 -0
  198. package/src/components/Form/FormField/fields/ArrayField/ArrayField.stories.tsx +119 -0
  199. package/src/components/Form/FormField/fields/ArrayField/ArrayField.tsx +328 -0
  200. package/src/components/Form/FormField/fields/ArrayField/index.ts +5 -0
  201. package/src/components/Form/FormField/fields/AsyncSelectField/AsyncSelectField.tsx +59 -0
  202. package/src/components/Form/FormField/fields/AsyncSelectField/index.ts +5 -0
  203. package/src/components/Form/FormField/fields/AutofillField/AutofillField.tsx +67 -0
  204. package/src/components/Form/FormField/fields/AutofillField/index.ts +5 -0
  205. package/src/components/Form/FormField/fields/BooleanField/BooleanField.stories.tsx +52 -0
  206. package/src/components/Form/FormField/fields/BooleanField/BooleanField.tsx +28 -0
  207. package/src/components/Form/FormField/fields/BooleanField/index.ts +5 -0
  208. package/src/components/Form/FormField/fields/ComboboxField/ComboboxField.tsx +104 -0
  209. package/src/components/Form/FormField/fields/ComboboxField/index.ts +5 -0
  210. package/src/components/Form/FormField/fields/DateField/DateField.stories.tsx +60 -0
  211. package/src/components/Form/{fields → FormField/fields/DateField}/DateField.tsx +12 -10
  212. package/src/components/Form/FormField/fields/DateField/index.ts +5 -0
  213. package/src/components/Form/FormField/fields/GeoPointField/GeoPointField.stories.tsx +53 -0
  214. package/src/components/Form/{fields → FormField/fields/GeoPointField}/GeoPointField.tsx +19 -21
  215. package/src/components/Form/FormField/fields/GeoPointField/index.ts +5 -0
  216. package/src/components/Form/{fields → FormField/fields/MarkdownField}/MarkdownField.stories.tsx +8 -8
  217. package/src/components/Form/{fields → FormField/fields/MarkdownField}/MarkdownField.tsx +41 -41
  218. package/src/components/Form/FormField/fields/MarkdownField/index.ts +5 -0
  219. package/src/components/Form/FormField/fields/NumberField/NumberField.stories.tsx +52 -0
  220. package/src/components/Form/FormField/fields/NumberField/NumberField.test.ts +29 -0
  221. package/src/components/Form/FormField/fields/NumberField/NumberField.tsx +140 -0
  222. package/src/components/Form/FormField/fields/NumberField/index.ts +5 -0
  223. package/src/components/Form/FormField/fields/PasswordField/PasswordField.stories.tsx +53 -0
  224. package/src/components/Form/FormField/fields/PasswordField/PasswordField.tsx +41 -0
  225. package/src/components/Form/FormField/fields/PasswordField/index.ts +5 -0
  226. package/src/components/Form/{fields → FormField/fields/RefField}/InlineRefField.tsx +21 -14
  227. package/src/components/Form/FormField/fields/RefField/RefField.stories.tsx +97 -0
  228. package/src/components/Form/{fields → FormField/fields/RefField}/RefField.test.ts +10 -1
  229. package/src/components/Form/{fields → FormField/fields/RefField}/RefField.tsx +55 -47
  230. package/src/components/Form/FormField/fields/RefField/index.ts +6 -0
  231. package/src/components/Form/FormField/fields/SelectField/SelectField.stories.tsx +53 -0
  232. package/src/components/Form/FormField/fields/SelectField/SelectField.tsx +136 -0
  233. package/src/components/Form/FormField/fields/SelectField/index.ts +5 -0
  234. package/src/components/Form/{fields → FormField/fields/SelectOptionField}/SelectOptionField.stories.tsx +5 -5
  235. package/src/components/Form/{fields → FormField/fields/SelectOptionField}/SelectOptionField.tsx +56 -54
  236. package/src/components/Form/FormField/fields/SelectOptionField/index.ts +5 -0
  237. package/src/components/Form/FormField/fields/TextAreaField/TextAreaField.stories.tsx +53 -0
  238. package/src/components/Form/{fields → FormField/fields/TextAreaField}/TextAreaField.tsx +6 -4
  239. package/src/components/Form/FormField/fields/TextAreaField/index.ts +5 -0
  240. package/src/components/Form/FormField/fields/TextField/TextField.stories.tsx +52 -0
  241. package/src/components/Form/{fields → FormField/fields/TextField}/TextField.tsx +6 -4
  242. package/src/components/Form/FormField/fields/TextField/index.ts +5 -0
  243. package/src/components/Form/FormField/fields/TupleField/TupleField.stories.tsx +55 -0
  244. package/src/components/Form/{fields → FormField/fields/TupleField}/TupleField.tsx +4 -2
  245. package/src/components/Form/FormField/fields/TupleField/index.ts +5 -0
  246. package/src/components/Form/{fields → FormField/fields}/index.ts +4 -1
  247. package/src/components/Form/FormField/index.ts +10 -0
  248. package/src/components/Form/FormField/presentation.tsx +32 -0
  249. package/src/components/Form/{FormFieldSet.tsx → FormFieldSet/FormFieldSet.tsx} +53 -90
  250. package/src/components/Form/FormFieldSet/FormFieldSetContainer.tsx +93 -0
  251. package/src/components/Form/FormFieldSet/index.ts +6 -0
  252. package/src/components/Form/{Layout → FormLayout}/FormLayout.stories.tsx +11 -9
  253. package/src/components/Form/{Layout → FormLayout}/FormLayout.tsx +29 -18
  254. package/src/components/Form/FormLayout/index.ts +6 -0
  255. package/src/components/Form/index.ts +3 -5
  256. package/src/components/Form/meta-tags.test.ts +1 -1
  257. package/src/components/Form/meta-tags.ts +1 -1
  258. package/src/components/ObjectForm/ObjectForm.tsx +15 -14
  259. package/src/components/ObjectPicker/ObjectPicker.tsx +20 -27
  260. package/src/components/ObjectProperties/ObjectProperties.stories.tsx +2 -2
  261. package/src/components/ObjectProperties/ObjectProperties.test.tsx +1 -1
  262. package/src/components/ObjectProperties/ObjectProperties.tsx +13 -8
  263. package/src/components/RefEditor/RefEditor.stories.tsx +2 -3
  264. package/src/components/RefEditor/RefEditor.tsx +1 -1
  265. package/src/components/ViewEditor/ViewEditor.stories.tsx +6 -7
  266. package/src/components/ViewEditor/ViewEditor.test.tsx +9 -3
  267. package/src/components/ViewEditor/ViewEditor.tsx +98 -128
  268. package/src/components/index.ts +0 -1
  269. package/src/hooks/index.ts +2 -0
  270. package/src/hooks/useAsyncFieldEffect.ts +81 -0
  271. package/src/hooks/useFormContext.ts +89 -0
  272. package/src/hooks/useFormHandler.ts +12 -10
  273. package/src/index.ts +2 -0
  274. package/src/{components/testing.tsx → testing/TestLayout.tsx} +16 -13
  275. package/src/testing/index.ts +7 -0
  276. package/src/testing/schema.ts +59 -0
  277. package/src/types.ts +186 -0
  278. package/src/util/index.ts +1 -1
  279. package/src/util/omit.ts +61 -0
  280. package/src/util/properties.test.ts +110 -28
  281. package/src/util/refs.ts +1 -1
  282. package/src/vitest-setup.ts +40 -0
  283. package/dist/types/src/components/Form/FormField.d.ts +0 -52
  284. package/dist/types/src/components/Form/FormField.d.ts.map +0 -1
  285. package/dist/types/src/components/Form/FormFieldComponent.d.ts +0 -98
  286. package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +0 -1
  287. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +0 -1
  288. package/dist/types/src/components/Form/FormFieldSet.d.ts +0 -26
  289. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +0 -1
  290. package/dist/types/src/components/Form/FormTooltipsContext.d.ts +0 -19
  291. package/dist/types/src/components/Form/FormTooltipsContext.d.ts.map +0 -1
  292. package/dist/types/src/components/Form/Layout/FormLayout.d.ts.map +0 -1
  293. package/dist/types/src/components/Form/Layout/FormLayout.stories.d.ts.map +0 -1
  294. package/dist/types/src/components/Form/Layout/FormLayout.stories.test.d.ts.map +0 -1
  295. package/dist/types/src/components/Form/Layout/FormLayout.test.d.ts.map +0 -1
  296. package/dist/types/src/components/Form/Layout/annotation.d.ts +0 -30
  297. package/dist/types/src/components/Form/Layout/annotation.d.ts.map +0 -1
  298. package/dist/types/src/components/Form/Layout/index.d.ts +0 -4
  299. package/dist/types/src/components/Form/Layout/index.d.ts.map +0 -1
  300. package/dist/types/src/components/Form/Layout/parser.d.ts.map +0 -1
  301. package/dist/types/src/components/Form/Layout/parser.test.d.ts.map +0 -1
  302. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +0 -1
  303. package/dist/types/src/components/Form/fields/BooleanField.d.ts +0 -4
  304. package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +0 -1
  305. package/dist/types/src/components/Form/fields/DateField.d.ts +0 -4
  306. package/dist/types/src/components/Form/fields/DateField.d.ts.map +0 -1
  307. package/dist/types/src/components/Form/fields/GeoPointField.d.ts +0 -5
  308. package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +0 -1
  309. package/dist/types/src/components/Form/fields/InlineRefField.d.ts.map +0 -1
  310. package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +0 -1
  311. package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts.map +0 -1
  312. package/dist/types/src/components/Form/fields/NumberField.d.ts +0 -4
  313. package/dist/types/src/components/Form/fields/NumberField.d.ts.map +0 -1
  314. package/dist/types/src/components/Form/fields/RefField.d.ts +0 -34
  315. package/dist/types/src/components/Form/fields/RefField.d.ts.map +0 -1
  316. package/dist/types/src/components/Form/fields/RefField.test.d.ts.map +0 -1
  317. package/dist/types/src/components/Form/fields/SelectField.d.ts +0 -13
  318. package/dist/types/src/components/Form/fields/SelectField.d.ts.map +0 -1
  319. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +0 -5
  320. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +0 -1
  321. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +0 -1
  322. package/dist/types/src/components/Form/fields/TextAreaField.d.ts +0 -4
  323. package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +0 -1
  324. package/dist/types/src/components/Form/fields/TextField.d.ts +0 -4
  325. package/dist/types/src/components/Form/fields/TextField.d.ts.map +0 -1
  326. package/dist/types/src/components/Form/fields/TupleField.d.ts.map +0 -1
  327. package/dist/types/src/components/Form/fields/index.d.ts.map +0 -1
  328. package/dist/types/src/components/Settings/Settings.d.ts +0 -51
  329. package/dist/types/src/components/Settings/Settings.d.ts.map +0 -1
  330. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +0 -1
  331. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +0 -43
  332. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +0 -1
  333. package/dist/types/src/components/Settings/SettingsItem.d.ts +0 -16
  334. package/dist/types/src/components/Settings/SettingsItem.d.ts.map +0 -1
  335. package/dist/types/src/components/Settings/SettingsSelectField.d.ts +0 -18
  336. package/dist/types/src/components/Settings/SettingsSelectField.d.ts.map +0 -1
  337. package/dist/types/src/components/Settings/index.d.ts +0 -4
  338. package/dist/types/src/components/Settings/index.d.ts.map +0 -1
  339. package/dist/types/src/components/testing.d.ts.map +0 -1
  340. package/dist/types/src/util/field-type.d.ts +0 -19
  341. package/dist/types/src/util/field-type.d.ts.map +0 -1
  342. package/src/components/Form/AUDIT.md +0 -17
  343. package/src/components/Form/FormFieldComponent.stories.tsx +0 -65
  344. package/src/components/Form/FormFieldComponent.tsx +0 -253
  345. package/src/components/Form/FormTooltipsContext.ts +0 -25
  346. package/src/components/Form/Layout/annotation.ts +0 -38
  347. package/src/components/Form/Layout/index.ts +0 -7
  348. package/src/components/Form/README.md +0 -3
  349. package/src/components/Form/fields/ArrayField.tsx +0 -173
  350. package/src/components/Form/fields/BooleanField.tsx +0 -22
  351. package/src/components/Form/fields/NumberField.tsx +0 -76
  352. package/src/components/Form/fields/SelectField.tsx +0 -77
  353. package/src/components/Settings/AUDIT.md +0 -51
  354. package/src/components/Settings/Settings.stories.tsx +0 -104
  355. package/src/components/Settings/Settings.tsx +0 -81
  356. package/src/components/Settings/SettingsFieldSet.tsx +0 -257
  357. package/src/components/Settings/SettingsItem.tsx +0 -58
  358. package/src/components/Settings/SettingsSelectField.tsx +0 -59
  359. package/src/components/Settings/index.ts +0 -7
  360. package/src/util/field-type.ts +0 -60
  361. /package/dist/types/src/components/Form/{fields → FormField/fields/MarkdownField}/MarkdownField.stories.d.ts +0 -0
  362. /package/dist/types/src/components/Form/{fields → FormField/fields/RefField}/InlineRefField.d.ts +0 -0
  363. /package/dist/types/src/components/Form/{fields → FormField/fields/RefField}/RefField.test.d.ts +0 -0
  364. /package/dist/types/src/components/Form/{fields → FormField/fields/SelectOptionField}/SelectOptionField.stories.d.ts +0 -0
  365. /package/dist/types/src/components/Form/{Layout → FormLayout}/FormLayout.stories.test.d.ts +0 -0
  366. /package/dist/types/src/components/Form/{Layout → FormLayout}/FormLayout.test.d.ts +0 -0
  367. /package/dist/types/src/components/Form/{Layout → FormLayout}/parser.d.ts +0 -0
  368. /package/dist/types/src/components/Form/{Layout → FormLayout}/parser.test.d.ts +0 -0
  369. /package/src/components/Form/{Layout → FormLayout}/FormLayout.stories.test.tsx +0 -0
  370. /package/src/components/Form/{Layout → FormLayout}/FormLayout.test.ts +0 -0
  371. /package/src/components/Form/{Layout → FormLayout}/parser.test.ts +0 -0
  372. /package/src/components/Form/{Layout → FormLayout}/parser.ts +0 -0
@@ -0,0 +1,140 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import * as Option from 'effect/Option';
6
+ import * as SchemaAST from 'effect/SchemaAST';
7
+ import React, { useCallback, useEffect, useState } from 'react';
8
+
9
+ import { Input, type TextInputProps } from '@dxos/react-ui';
10
+ import { safeParseFloat } from '@dxos/util';
11
+
12
+ import { type FormFieldRendererProps } from '#types';
13
+
14
+ import { FormRow } from '../../FormRow';
15
+
16
+ /**
17
+ * Extracts numeric constraints (`minimum`/`maximum` bounds and whether the value must be an integer)
18
+ * by walking the chain of Refinement ASTs and reading each one's JSON schema annotation (e.g. produced
19
+ * by `Schema.between` / `Schema.int`). Returns empty constraints when none are declared.
20
+ */
21
+ export const getNumericConstraints = (ast: SchemaAST.AST): { min?: number; max?: number; integer: boolean } => {
22
+ let node: SchemaAST.AST | undefined = ast;
23
+ let min: number | undefined;
24
+ let max: number | undefined;
25
+ let integer = false;
26
+ // Nested refinements (e.g. `Schema.int()` + `Schema.between()`) each carry their own JSON schema
27
+ // fragment, so accumulate across the chain rather than reading only the outermost node.
28
+ while (node && SchemaAST.isRefinement(node)) {
29
+ const jsonSchema = Option.getOrUndefined(SchemaAST.getJSONSchemaAnnotation(node));
30
+ if (jsonSchema != null) {
31
+ // Stacked refinements intersect: keep the strictest bound (largest min, smallest max).
32
+ if ('minimum' in jsonSchema && typeof jsonSchema.minimum === 'number') {
33
+ min = min === undefined ? jsonSchema.minimum : Math.max(min, jsonSchema.minimum);
34
+ }
35
+ if ('maximum' in jsonSchema && typeof jsonSchema.maximum === 'number') {
36
+ max = max === undefined ? jsonSchema.maximum : Math.min(max, jsonSchema.maximum);
37
+ }
38
+ if (
39
+ ('type' in jsonSchema && jsonSchema.type === 'integer') ||
40
+ ('multipleOf' in jsonSchema && jsonSchema.multipleOf === 1)
41
+ ) {
42
+ integer = true;
43
+ }
44
+ }
45
+ node = node.from;
46
+ }
47
+ return { min, max, integer };
48
+ };
49
+
50
+ export const NumberField = ({
51
+ type,
52
+ readonly,
53
+ placeholder,
54
+ getValue,
55
+ onValueChange,
56
+ onBlur,
57
+ ...props
58
+ }: FormFieldRendererProps<number>) => {
59
+ const { min, max, integer } = getNumericConstraints(type);
60
+
61
+ // Clamp to the declared bounds (and round when integer) so the committed value respects the schema's
62
+ // natural limits — `<input type="number">`'s min/max only constrain the spinner, not typed input.
63
+ const clamp = useCallback(
64
+ (n: number) => {
65
+ let value = integer ? Math.round(n) : n;
66
+ if (min !== undefined) {
67
+ value = Math.max(min, value);
68
+ }
69
+ if (max !== undefined) {
70
+ value = Math.min(max, value);
71
+ }
72
+ return value;
73
+ },
74
+ [min, max, integer],
75
+ );
76
+
77
+ // Track raw string input so the user can clear the field before typing a new number.
78
+ // We only commit to onValueChange when the raw string parses to a valid number.
79
+ const [raw, setRaw] = useState<string>(() => {
80
+ const v = getValue();
81
+ return v !== undefined ? String(v) : '';
82
+ });
83
+
84
+ // Sync display when an external change updates the committed value (e.g. reactive form
85
+ // calculations). Only overwrite raw when the external value differs from what raw parses
86
+ // to, preserving partial edits like "1." which correctly parse to 1.
87
+ const externalValue = getValue();
88
+ useEffect(() => {
89
+ if (externalValue !== safeParseFloat(raw)) {
90
+ setRaw(externalValue !== undefined ? String(externalValue) : '');
91
+ }
92
+ }, [externalValue]); // eslint-disable-line react-hooks/exhaustive-deps
93
+
94
+ const handleChange = useCallback<NonNullable<TextInputProps['onChange']>>(
95
+ (event) => {
96
+ const value = event.target.value;
97
+ setRaw(value);
98
+ const parsed = safeParseFloat(value);
99
+ if (parsed !== undefined) {
100
+ onValueChange(type, clamp(parsed));
101
+ }
102
+ },
103
+ [type, onValueChange, clamp],
104
+ );
105
+
106
+ const handleBlur = useCallback(
107
+ (event: React.FocusEvent<HTMLElement>) => {
108
+ const parsed = safeParseFloat(raw);
109
+ if (parsed === undefined) {
110
+ // The field was left empty or invalid; reset to the last committed value.
111
+ const committed = getValue();
112
+ setRaw(committed !== undefined ? String(committed) : '');
113
+ } else {
114
+ // Reflect the clamped value even when it equals the previously committed one (so `externalValue`
115
+ // doesn't change), otherwise the input keeps showing the out-of-range text the user typed.
116
+ setRaw(String(clamp(parsed)));
117
+ }
118
+ onBlur(event);
119
+ },
120
+ [raw, getValue, onBlur, clamp],
121
+ );
122
+
123
+ return (
124
+ <FormRow<number> readonly={readonly} getValue={getValue} {...props}>
125
+ {() => (
126
+ <Input.TextInput
127
+ type='number'
128
+ disabled={!!readonly}
129
+ placeholder={placeholder}
130
+ value={raw}
131
+ min={min}
132
+ max={max}
133
+ step={integer ? 1 : undefined}
134
+ onChange={handleChange}
135
+ onBlur={handleBlur}
136
+ />
137
+ )}
138
+ </FormRow>
139
+ );
140
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './NumberField';
@@ -0,0 +1,53 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Schema from 'effect/Schema';
7
+ import React, { useState } from 'react';
8
+
9
+ import { Format } from '@dxos/echo';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
+
12
+ import { translations } from '#translations';
13
+
14
+ import { TestLayout } from '../../../../../testing';
15
+ import { Form } from '../../../Form';
16
+
17
+ const schema = Schema.Struct({
18
+ name: Schema.String.annotations({ title: 'Name' }).pipe(Format.FormatAnnotation.set(Format.TypeFormat.Password)),
19
+ }).pipe(Schema.mutable);
20
+
21
+ type Values = Schema.Schema.Type<typeof schema>;
22
+
23
+ const DefaultStory = () => {
24
+ const [values, setValues] = useState<Values>({ name: 'Alice' });
25
+ return (
26
+ <TestLayout json={values}>
27
+ <Form.Root
28
+ schema={schema}
29
+ values={values}
30
+ onValuesChanged={(values) => setValues((prev) => ({ ...prev, ...values }))}
31
+ >
32
+ <Form.Viewport>
33
+ <Form.Content>
34
+ <Form.FieldSet />
35
+ </Form.Content>
36
+ </Form.Viewport>
37
+ </Form.Root>
38
+ </TestLayout>
39
+ );
40
+ };
41
+
42
+ const meta = {
43
+ title: 'ui/react-ui-form/FormField/PasswordField',
44
+ render: DefaultStory,
45
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
46
+ parameters: { layout: 'fullscreen', translations },
47
+ } satisfies Meta<typeof DefaultStory>;
48
+
49
+ export default meta;
50
+
51
+ type Story = StoryObj<typeof meta>;
52
+
53
+ export const Default: Story = {};
@@ -0,0 +1,41 @@
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 FormFieldRendererProps } from '#types';
10
+
11
+ import { FormRow } from '../../FormRow';
12
+
13
+ export const PasswordField = ({
14
+ type,
15
+ readonly,
16
+ placeholder,
17
+ onBlur,
18
+ onValueChange,
19
+ ...props
20
+ }: FormFieldRendererProps<string>) => {
21
+ const handleChange = useCallback<NonNullable<TextInputProps['onChange']>>(
22
+ (event) => onValueChange(type, event.target.value),
23
+ [type, onValueChange],
24
+ );
25
+
26
+ return (
27
+ <FormRow<string> readonly={readonly} {...props}>
28
+ {({ value = '' }) => (
29
+ <Input.TextInput
30
+ type='password'
31
+ noAutoFill
32
+ disabled={!!readonly}
33
+ placeholder={placeholder}
34
+ value={value}
35
+ onBlur={onBlur}
36
+ onChange={handleChange}
37
+ />
38
+ )}
39
+ </FormRow>
40
+ );
41
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './PasswordField';
@@ -10,12 +10,14 @@ import { useType as defaultUseType } from '@dxos/echo-react';
10
10
  import { ReferenceAnnotationId, type ReferenceAnnotationValue } from '@dxos/echo/Annotation';
11
11
  import { SchemaEx } from '@dxos/effect';
12
12
  import { DXN, URI } from '@dxos/keys';
13
- import { Button, Icon, Input, useTranslation } from '@dxos/react-ui';
13
+ import { IconButton, Input, useTranslation } from '@dxos/react-ui';
14
14
 
15
15
  import { translationKey } from '#translations';
16
16
 
17
- import { Form, omitId } from '../Form';
18
- import { FormFieldLabel } from '../FormFieldComponent';
17
+ import { omitId } from '../../../../../util';
18
+ import { FormContent, FormFieldSetController, FormRoot } from '../../../FormControls';
19
+ import { FormFieldLabel } from '../../FormRow';
20
+ import { presentationFor } from '../../presentation';
19
21
  import { type RefFieldProps } from './RefField';
20
22
 
21
23
  /**
@@ -33,7 +35,8 @@ export const InlineRefField = (props: RefFieldProps) => {
33
35
  readonly,
34
36
  label,
35
37
  jsonPath,
36
- layout,
38
+ presentation,
39
+ required,
37
40
  db,
38
41
  getValue,
39
42
  onValueChange,
@@ -41,6 +44,7 @@ export const InlineRefField = (props: RefFieldProps) => {
41
44
  useType = defaultUseType,
42
45
  } = props;
43
46
  const { t } = useTranslation(translationKey);
47
+ const resolved = presentationFor(presentation);
44
48
 
45
49
  const reference = getValue() as Ref.Ref<any> | undefined;
46
50
  const typename = useMemo(
@@ -66,16 +70,19 @@ export const InlineRefField = (props: RefFieldProps) => {
66
70
 
67
71
  return (
68
72
  <Input.Root>
69
- {layout !== 'inline' && <FormFieldLabel readonly={readonly} label={label} path={jsonPath} />}
73
+ {resolved.showLabel && <FormFieldLabel readonly={readonly} required={required} label={label} path={jsonPath} />}
70
74
  {reference ? (
71
75
  <InlineForm reference={reference} db={db} readonly={readonly} useType={useType} />
72
76
  ) : (
73
77
  !readonly &&
74
78
  onCreate && (
75
- <Button classNames='w-full gap-form-gap' disabled={!createType || !db} onClick={() => void handleCreate()}>
76
- <Icon icon='ph--plus--regular' size={4} />
77
- <span>{label || t('ref-field.placeholder')}</span>
78
- </Button>
79
+ <IconButton
80
+ classNames='w-full gap-form-gap'
81
+ disabled={!createType || !db}
82
+ icon='ph--plus--regular'
83
+ label={label || t('ref-field.placeholder')}
84
+ onClick={() => void handleCreate()}
85
+ />
79
86
  )
80
87
  )}
81
88
  </Input.Root>
@@ -129,10 +136,10 @@ const InlineForm = ({ reference, db, readonly, useType = defaultUseType }: Inlin
129
136
  }
130
137
 
131
138
  return (
132
- <Form.Root db={db} schema={formSchema} defaultValues={defaultValues as any} onValuesChanged={handleChange}>
133
- <Form.Content>
134
- <Form.FieldSet collapsible readonly={readonly} />
135
- </Form.Content>
136
- </Form.Root>
139
+ <FormRoot db={db} schema={formSchema} defaultValues={defaultValues as any} onValuesChanged={handleChange}>
140
+ <FormContent>
141
+ <FormFieldSetController collapsible readonly={readonly} />
142
+ </FormContent>
143
+ </FormRoot>
137
144
  );
138
145
  };
@@ -0,0 +1,97 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Schema from 'effect/Schema';
7
+ import React, { useState } from 'react';
8
+
9
+ import { Annotation, Obj, Ref } from '@dxos/echo';
10
+ import { useSpaces } from '@dxos/react-client/echo';
11
+ import { withClientProvider } from '@dxos/react-client/testing';
12
+ import { Loading, withLayout, withTheme } from '@dxos/react-ui/testing';
13
+
14
+ import { translations } from '#translations';
15
+
16
+ import { Organization, TestLayout } from '../../../../../testing';
17
+ import { Form } from '../../../Form';
18
+
19
+ // Picker: select an existing Organization.
20
+ const RefSchema = Schema.Struct({
21
+ employer: Ref.Ref(Organization).annotations({ title: 'Employer' }),
22
+ }).pipe(Schema.mutable);
23
+
24
+ // Inline: edit the referenced Organization's fields in a nested form.
25
+ const InlineSchema = Schema.Struct({
26
+ employer: Ref.Ref(Organization).pipe(
27
+ Schema.annotations({ title: 'Employer' }),
28
+ Annotation.FormInlineAnnotation.set(true),
29
+ ),
30
+ }).pipe(Schema.mutable);
31
+
32
+ const RefStory = ({ schema }: { schema: Schema.Schema<any> }) => {
33
+ const spaces = useSpaces();
34
+ const space = spaces[0];
35
+ const [values, setValues] = useState<Record<string, unknown>>({});
36
+ if (!space) {
37
+ return <Loading />;
38
+ }
39
+
40
+ return (
41
+ <TestLayout json={values}>
42
+ <Form.Root
43
+ schema={schema}
44
+ values={values}
45
+ db={space.db}
46
+ onValuesChanged={(values) => setValues((prev) => ({ ...prev, ...values }))}
47
+ // `name` is required; the inline create starts from `{}`, so seed a default the form can then edit.
48
+ onCreate={(_type, props: any) => space.db.add(Obj.make(Organization, { name: 'New organization', ...props }))}
49
+ >
50
+ <Form.Viewport>
51
+ <Form.Content>
52
+ <Form.FieldSet />
53
+ </Form.Content>
54
+ </Form.Viewport>
55
+ </Form.Root>
56
+ </TestLayout>
57
+ );
58
+ };
59
+
60
+ const meta = {
61
+ title: 'ui/react-ui-form/FormField/RefField',
62
+ render: RefStory,
63
+ decorators: [
64
+ withTheme(),
65
+ withLayout({ layout: 'fullscreen' }),
66
+ withClientProvider({
67
+ createIdentity: true,
68
+ createSpace: true,
69
+ types: [Organization],
70
+ onCreateSpace: ({ space }) => {
71
+ Array.from({ length: 8 }).forEach((_, i) =>
72
+ space.db.add(Obj.make(Organization, { name: `Organization ${i}` })),
73
+ );
74
+ },
75
+ }),
76
+ ],
77
+ parameters: {
78
+ layout: 'fullscreen',
79
+ translations,
80
+ },
81
+ } satisfies Meta<typeof RefStory>;
82
+
83
+ export default meta;
84
+
85
+ type Story = StoryObj<typeof meta>;
86
+
87
+ export const Default: Story = {
88
+ args: {
89
+ schema: RefSchema,
90
+ },
91
+ };
92
+
93
+ export const Inline: Story = {
94
+ args: {
95
+ schema: InlineSchema,
96
+ },
97
+ };
@@ -4,7 +4,8 @@
4
4
 
5
5
  import { describe, test } from 'vitest';
6
6
 
7
- import { type RefOption } from '../../ObjectPicker';
7
+ import { type RefOption } from '#types';
8
+
8
9
  import { findRefOption } from './RefField';
9
10
 
10
11
  // Sample qualified self URIs as produced by an in-space object (Entity.getURI / `echo://<space>/<id>`).
@@ -28,6 +29,14 @@ describe('findRefOption', () => {
28
29
  expect(findRefOption({ '/': LOCAL }, options)?.label).toBe('Target');
29
30
  });
30
31
 
32
+ test('matches a keyed-entity ref by direct URI equality', ({ expect }) => {
33
+ // Keyed/registry entities (skills, operations) are referenced by a named DXN with no parseable
34
+ // EID; the option id and the ref uri are the same named URI, so they match by direct equality.
35
+ const named = 'dxn:type:example.com/skill/research';
36
+ const keyedOptions: RefOption[] = [...options, { id: named, label: 'Research' }];
37
+ expect(findRefOption({ '/': named }, keyedOptions)?.label).toBe('Research');
38
+ });
39
+
31
40
  test('returns undefined for a non-ref value', ({ expect }) => {
32
41
  expect(findRefOption('not-a-ref', options)).toBeUndefined();
33
42
  expect(findRefOption(undefined, options)).toBeUndefined();
@@ -5,8 +5,8 @@
5
5
  import React, { useCallback, useMemo, useState } from 'react';
6
6
 
7
7
  import '@dxos/lit-ui/dx-tag-picker.pcss';
8
- import { type Database, Entity, Filter, Obj, Query, Ref, Scope, Type } from '@dxos/echo';
9
- import { useQuery, useType as defaultUseType } from '@dxos/echo-react';
8
+ import { Entity, Filter, Obj, Query, Ref, Scope, Type } from '@dxos/echo';
9
+ import { useType as defaultUseType, useQuery } from '@dxos/echo-react';
10
10
  import { ANY_OBJECT_TYPENAME, ReferenceAnnotationId, type ReferenceAnnotationValue } from '@dxos/echo/internal';
11
11
  import { SchemaEx } from '@dxos/effect';
12
12
  import { DXN, EID, URI } from '@dxos/keys';
@@ -15,10 +15,12 @@ import { Button, Icon, Input, useTranslation } from '@dxos/react-ui';
15
15
  import { ParentLabelAnnotationId } from '@dxos/schema';
16
16
 
17
17
  import { translationKey } from '#translations';
18
+ import { type CreateOptions, type FormFieldRendererProps, type RefFieldDataProps, type RefOption } from '#types';
18
19
 
19
- import { ObjectPicker, type ObjectPickerContentProps, type RefOption } from '../../ObjectPicker';
20
- import { omitHiddenFormFields, omitId } from '../Form';
21
- import { type FormFieldComponentProps, FormFieldLabel } from '../FormFieldComponent';
20
+ import { omitHiddenFormFields, omitId } from '../../../../../util';
21
+ import { ObjectPicker } from '../../../../ObjectPicker';
22
+ import { FormFieldLabel } from '../../FormRow';
23
+ import { presentationFor } from '../../presentation';
22
24
 
23
25
  // TODO(burdon): Factor out.
24
26
  const isRefSnapshot = (val: any): val is { '/': string } => {
@@ -39,7 +41,14 @@ export const findRefOption = (value: unknown, options: RefOption[]): RefOption |
39
41
  if (!isRef && !isRefSnapshot(value)) {
40
42
  return undefined;
41
43
  }
42
- const valueEid = EID.tryParse(isRef ? value.uri : value['/']);
44
+ const valueUri = isRef ? value.uri : value['/'];
45
+ // Keyed/registry entities (skills, operations) are referenced by a named DXN rather than an
46
+ // entity-id, so they carry no parseable EID; match those by direct URI equality against the option id.
47
+ const directMatch = options.find((option) => option.id === valueUri);
48
+ if (directMatch) {
49
+ return directMatch;
50
+ }
51
+ const valueEid = EID.tryParse(valueUri);
43
52
  if (!valueEid) {
44
53
  return undefined;
45
54
  }
@@ -85,31 +94,11 @@ const defaultUseResults: NonNullable<RefFieldProps['useResults']> = (db, typenam
85
94
  : typename === ANY_OBJECT_TYPENAME
86
95
  ? // Untyped refs show space objects only; the registry is too broad for "any".
87
96
  Query.select(Filter.everything())
88
- : // Include registry scope so keyed entities (blueprints, operations) appear as options.
97
+ : // Include registry scope so keyed entities (skills, operations) appear as options.
89
98
  Query.select(Filter.type(DXN.make(typename))).from(Scope.space(), Scope.registry()),
90
99
  );
91
100
 
92
- export type RefFieldProps = FormFieldComponentProps &
93
- Pick<
94
- ObjectPickerContentProps,
95
- 'createOptionLabel' | 'createOptionIcon' | 'createInitialValuePath' | 'createFieldMap'
96
- > & {
97
- db?: Database.Database;
98
- // TODO(burdon): Replace hooks with callbacks.
99
- useType?: (db?: Database.Database, typeUri?: URI.URI) => Type.AnyEntity;
100
- useResults?: (db?: Database.Database, typename?: string) => Entity.Any[];
101
- getOptions?: (
102
- objects: Entity.Any[],
103
- options?: { parentLabel?: boolean; getTypePlaceholder?: (typename: string) => string },
104
- ) => RefOption[];
105
- /**
106
- * Persist a newly-created object. Called after the user fills out the
107
- * inline create form and clicks Save. Should add the object to the
108
- * database and return it (sync or async). The returned object is then
109
- * wired into this slot's form value as a Ref.
110
- */
111
- onCreate?: (schema: Type.AnyEntity, values: any) => Obj.Unknown | Promise<Obj.Unknown> | undefined | void;
112
- };
101
+ export type RefFieldProps = FormFieldRendererProps & RefFieldDataProps & CreateOptions;
113
102
 
114
103
  export const RefField = (props: RefFieldProps) => {
115
104
  const {
@@ -118,7 +107,8 @@ export const RefField = (props: RefFieldProps) => {
118
107
  label,
119
108
  jsonPath,
120
109
  placeholder,
121
- layout,
110
+ presentation,
111
+ required,
122
112
  getStatus,
123
113
  getValue,
124
114
  createOptionLabel,
@@ -130,10 +120,12 @@ export const RefField = (props: RefFieldProps) => {
130
120
  useResults = defaultUseResults,
131
121
  getOptions = defaultGetOptions,
132
122
  onCreate,
123
+ resolveCreateEntry,
133
124
  onValueChange,
134
125
  } = props;
135
126
  const { t } = useTranslation(translationKey);
136
127
  const { status, error } = getStatus();
128
+ const resolved = presentationFor(presentation);
137
129
 
138
130
  const typename = useMemo(
139
131
  () => (type ? SchemaEx.findAnnotation<ReferenceAnnotationValue>(type, ReferenceAnnotationId)?.typename : undefined),
@@ -164,24 +156,31 @@ export const RefField = (props: RefFieldProps) => {
164
156
  const selectedIds = useMemo(() => (item ? [item.id] : []), [item]);
165
157
  const createSchema = useType(db, typename && typename !== ANY_OBJECT_TYPENAME ? DXN.make(typename) : undefined);
166
158
 
159
+ // Per-typename override supplied by the caller (e.g. SpaceCapabilities.CreateObjectEntry).
160
+ const createEntry = typename ? resolveCreateEntry?.(typename) : undefined;
161
+
167
162
  // Lift the popover open state so we can dismiss it after a successful create.
168
163
  const [open, setOpen] = useState(false);
169
164
 
170
165
  const handleCreate = useCallback(
171
166
  async (values: any) => {
172
- if (!createSchema || !onCreate) {
173
- return;
174
- }
175
- const newObject = await onCreate(createSchema, values);
176
- if (newObject) {
177
- // Wire the newly-created object into this slot's form value. ArrayField
178
- // owns the array; this RefField represents a single slot, so writing a
179
- // Ref via `onValueChange` populates that slot.
180
- onValueChange(type, Ref.make(newObject));
167
+ // Wire the newly-created object into this slot's form value. ArrayField
168
+ // owns the array; this RefField represents a single slot, so writing a
169
+ // Ref via `onValueChange` populates that slot.
170
+ if (createEntry?.createObject && db) {
171
+ const newObject = await createEntry.createObject(values, db);
172
+ if (newObject) {
173
+ onValueChange(type, Ref.make(newObject));
174
+ }
175
+ } else if (createSchema && onCreate) {
176
+ const newObject = await onCreate(createSchema, values);
177
+ if (newObject) {
178
+ onValueChange(type, Ref.make(newObject));
179
+ }
181
180
  }
182
181
  setOpen(false);
183
182
  },
184
- [createSchema, onCreate, type, onValueChange],
183
+ [createEntry, db, createSchema, onCreate, type, onValueChange],
185
184
  );
186
185
 
187
186
  const handleUpdate = useCallback(
@@ -204,13 +203,15 @@ export const RefField = (props: RefFieldProps) => {
204
203
  [item, handleUpdate],
205
204
  );
206
205
 
207
- if (!typename || ((readonly || layout === 'static') && !item)) {
206
+ if (!typename || ((readonly || resolved.isStatic) && !item)) {
208
207
  return null;
209
208
  }
210
209
 
211
210
  return (
212
211
  <Input.Root validationValence={status}>
213
- {layout !== 'inline' && <FormFieldLabel error={error} readonly={readonly} label={label} path={jsonPath} />}
212
+ {resolved.showLabel && (
213
+ <FormFieldLabel error={error} readonly={readonly} required={required} label={label} path={jsonPath} />
214
+ )}
214
215
  <div>
215
216
  {readonly ? (
216
217
  !item ? (
@@ -245,22 +246,29 @@ export const RefField = (props: RefFieldProps) => {
245
246
  classNames='dx-card-popover-width'
246
247
  options={options}
247
248
  selectedIds={selectedIds}
248
- // Strip hidden (`FormInputAnnotation.set(false)`) fields so the
249
- // form's validator doesn't reject required-but-hidden fields
250
- // such as backing-object refs supplied by a `FactoryAnnotation`.
251
- createSchema={createSchema && omitHiddenFormFields(omitId(Type.getSchema(createSchema)))}
249
+ // Prefer the plugin-registered inputSchema; fall back to stripping hidden fields
250
+ // (`FormInputAnnotation.set(false)`) from the raw ECHO type schema so the form's
251
+ // validator doesn't reject required-but-hidden fields such as backing-object refs
252
+ // supplied by a `FactoryAnnotation`.
253
+ createSchema={
254
+ createEntry?.inputSchema ??
255
+ (createSchema && omitHiddenFormFields(omitId(Type.getSchema(createSchema))))
256
+ }
252
257
  createOptionLabel={createOptionLabel}
253
258
  createOptionIcon={createOptionIcon}
254
259
  createInitialValuePath={createInitialValuePath}
255
260
  createFieldMap={createFieldMap}
256
- onCreate={handleCreate}
261
+ // Offer inline create when the caller wired a handler OR a plugin-registered
262
+ // createObject override is available. A resolvable `createSchema` alone is not
263
+ // enough (e.g. operation refs, whose objects can't be created ad hoc).
264
+ onCreate={onCreate || createEntry?.createObject ? handleCreate : undefined}
257
265
  onSelect={handleSelect}
258
266
  />
259
267
  </ObjectPicker.Portal>
260
268
  </ObjectPicker.Root>
261
269
  )}
262
270
  </div>
263
- {layout === 'full' && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
271
+ {resolved.showError && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
264
272
  </Input.Root>
265
273
  );
266
274
  };
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './RefField';
6
+ export * from './InlineRefField';