@dxos/react-ui-form 0.8.4-main.3a94e84 → 0.8.4-main.3c1ae3b

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 (197) hide show
  1. package/dist/lib/browser/index.mjs +2138 -1738
  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 +2138 -1738
  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/components/ControlSection/ControlSection.d.ts +2 -2
  8. package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -1
  9. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +84 -5
  10. package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +5 -5
  12. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  13. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +82 -5
  14. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/Form-old.stories.d.ts +172 -0
  16. package/dist/types/src/components/Form/Form-old.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/Form/Form.d.ts +78 -25
  18. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/Form.stories.d.ts +123 -51
  20. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/FormField.d.ts +37 -0
  22. package/dist/types/src/components/Form/FormField.d.ts.map +1 -0
  23. package/dist/types/src/components/Form/FormFieldComponent.d.ts +78 -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 +19 -0
  28. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -0
  29. package/dist/types/src/components/Form/fields/ArrayField.d.ts +18 -0
  30. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -0
  31. package/dist/types/src/components/Form/fields/BooleanField.d.ts +4 -0
  32. package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -0
  33. package/dist/types/src/components/Form/fields/GeoPointField.d.ts +5 -0
  34. package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -0
  35. package/dist/types/src/components/Form/fields/MarkdownField.d.ts +4 -0
  36. package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -0
  37. package/dist/types/src/components/Form/fields/NumberField.d.ts +4 -0
  38. package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -0
  39. package/dist/types/src/components/Form/fields/RefField.d.ts +18 -0
  40. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -0
  41. package/dist/types/src/components/Form/fields/SelectField.d.ts +10 -0
  42. package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -0
  43. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts +5 -0
  44. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -0
  45. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +82 -0
  46. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -0
  47. package/dist/types/src/components/Form/fields/TextAreaField.d.ts +4 -0
  48. package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -0
  49. package/dist/types/src/components/Form/fields/TextField.d.ts +4 -0
  50. package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -0
  51. package/dist/types/src/components/Form/fields/TupleField.d.ts +6 -0
  52. package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -0
  53. package/dist/types/src/components/Form/fields/index.d.ts +12 -0
  54. package/dist/types/src/components/Form/fields/index.d.ts.map +1 -0
  55. package/dist/types/src/components/Form/index.d.ts +2 -4
  56. package/dist/types/src/components/Form/index.d.ts.map +1 -1
  57. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +41 -0
  58. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
  59. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +85 -0
  60. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -0
  61. package/dist/types/src/components/ObjectPicker/index.d.ts +2 -0
  62. package/dist/types/src/components/ObjectPicker/index.d.ts.map +1 -0
  63. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +21 -9
  64. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  65. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +83 -8
  66. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  67. package/dist/types/src/components/index.d.ts +1 -1
  68. package/dist/types/src/components/index.d.ts.map +1 -1
  69. package/dist/types/src/components/testing.d.ts +4 -3
  70. package/dist/types/src/components/testing.d.ts.map +1 -1
  71. package/dist/types/src/hooks/index.d.ts +3 -2
  72. package/dist/types/src/hooks/index.d.ts.map +1 -1
  73. package/dist/types/src/hooks/useFormHandler.d.ts +82 -0
  74. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -0
  75. package/dist/types/src/hooks/useKeyHandler.d.ts +6 -0
  76. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -0
  77. package/dist/types/src/hooks/useQueryRefOptions.d.ts +13 -6
  78. package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -1
  79. package/dist/types/src/hooks/useRefQueryOptions.d.ts +8 -0
  80. package/dist/types/src/hooks/useRefQueryOptions.d.ts.map +1 -0
  81. package/dist/types/src/translations.d.ts +17 -8
  82. package/dist/types/src/translations.d.ts.map +1 -1
  83. package/dist/types/src/util/field.d.ts +3 -3
  84. package/dist/types/src/util/field.d.ts.map +1 -1
  85. package/dist/types/src/util/formatting.d.ts +2 -2
  86. package/dist/types/src/util/formatting.d.ts.map +1 -1
  87. package/dist/types/src/util/index.d.ts +1 -1
  88. package/dist/types/src/util/index.d.ts.map +1 -1
  89. package/dist/types/src/util/properties.d.ts +7 -0
  90. package/dist/types/src/util/properties.d.ts.map +1 -0
  91. package/dist/types/src/util/refs.d.ts +2 -3
  92. package/dist/types/src/util/refs.d.ts.map +1 -1
  93. package/dist/types/tsconfig.tsbuildinfo +1 -1
  94. package/package.json +43 -36
  95. package/src/components/ControlSection/ControlSection.stories.tsx +11 -14
  96. package/src/components/ControlSection/ControlSection.tsx +26 -12
  97. package/src/components/FieldEditor/FieldEditor.stories.tsx +20 -20
  98. package/src/components/FieldEditor/FieldEditor.test.tsx +3 -2
  99. package/src/components/FieldEditor/FieldEditor.tsx +75 -77
  100. package/src/components/Form/Form-old.stories.tsx +314 -0
  101. package/src/components/Form/Form.stories.tsx +163 -219
  102. package/src/components/Form/Form.tsx +306 -98
  103. package/src/components/Form/FormField.tsx +280 -0
  104. package/src/components/Form/FormFieldComponent.stories.tsx +65 -0
  105. package/src/components/Form/FormFieldComponent.tsx +173 -0
  106. package/src/components/Form/FormFieldSet.tsx +124 -0
  107. package/src/components/Form/README.md +3 -0
  108. package/src/components/Form/fields/ArrayField.tsx +154 -0
  109. package/src/components/Form/fields/BooleanField.tsx +22 -0
  110. package/src/components/Form/fields/GeoPointField.tsx +120 -0
  111. package/src/components/Form/fields/MarkdownField.tsx +78 -0
  112. package/src/components/Form/fields/NumberField.tsx +39 -0
  113. package/src/components/Form/fields/RefField.tsx +206 -0
  114. package/src/components/Form/fields/SelectField.tsx +65 -0
  115. package/src/components/Form/{custom/SelectOptionsInput.stories.tsx → fields/SelectOptionField.stories.tsx} +25 -22
  116. package/src/components/Form/{custom/SelectOptionsInput.tsx → fields/SelectOptionField.tsx} +47 -42
  117. package/src/components/Form/fields/TextAreaField.tsx +38 -0
  118. package/src/components/Form/fields/TextField.tsx +38 -0
  119. package/src/components/Form/{custom/TupleInput.tsx → fields/TupleField.tsx} +15 -8
  120. package/src/components/Form/fields/index.ts +15 -0
  121. package/src/components/Form/index.ts +3 -4
  122. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +207 -0
  123. package/src/components/ObjectPicker/ObjectPicker.tsx +166 -0
  124. package/src/components/ObjectPicker/index.ts +5 -0
  125. package/src/components/ViewEditor/ViewEditor.stories.tsx +131 -61
  126. package/src/components/ViewEditor/ViewEditor.test.tsx +19 -17
  127. package/src/components/ViewEditor/ViewEditor.tsx +277 -197
  128. package/src/components/index.ts +1 -2
  129. package/src/components/testing.tsx +20 -10
  130. package/src/hooks/index.ts +3 -2
  131. package/src/hooks/useFormHandler.ts +348 -0
  132. package/src/hooks/useKeyHandler.tsx +37 -0
  133. package/src/hooks/useQueryRefOptions.ts +49 -32
  134. package/src/hooks/useRefQueryOptions.ts +37 -0
  135. package/src/translations.ts +20 -11
  136. package/src/util/field.ts +33 -43
  137. package/src/util/formatting.ts +22 -22
  138. package/src/util/index.ts +1 -1
  139. package/src/util/properties.ts +18 -0
  140. package/src/util/refs.ts +12 -18
  141. package/dist/types/src/components/Form/ArrayField.d.ts +0 -16
  142. package/dist/types/src/components/Form/ArrayField.d.ts.map +0 -1
  143. package/dist/types/src/components/Form/Defaults.d.ts +0 -14
  144. package/dist/types/src/components/Form/Defaults.d.ts.map +0 -1
  145. package/dist/types/src/components/Form/FormActions.d.ts +0 -9
  146. package/dist/types/src/components/Form/FormActions.d.ts.map +0 -1
  147. package/dist/types/src/components/Form/FormContent.d.ts +0 -57
  148. package/dist/types/src/components/Form/FormContent.d.ts.map +0 -1
  149. package/dist/types/src/components/Form/FormContext.d.ts +0 -23
  150. package/dist/types/src/components/Form/FormContext.d.ts.map +0 -1
  151. package/dist/types/src/components/Form/Input.d.ts +0 -26
  152. package/dist/types/src/components/Form/Input.d.ts.map +0 -1
  153. package/dist/types/src/components/Form/RefField.d.ts +0 -12
  154. package/dist/types/src/components/Form/RefField.d.ts.map +0 -1
  155. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +0 -4
  156. package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +0 -1
  157. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +0 -4
  158. package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +0 -1
  159. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +0 -7
  160. package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +0 -1
  161. package/dist/types/src/components/Form/custom/TupleInput.d.ts +0 -6
  162. package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +0 -1
  163. package/dist/types/src/components/Form/custom/index.d.ts +0 -4
  164. package/dist/types/src/components/Form/custom/index.d.ts.map +0 -1
  165. package/dist/types/src/components/Form/factory.d.ts +0 -8
  166. package/dist/types/src/components/Form/factory.d.ts.map +0 -1
  167. package/dist/types/src/components/deprecated/Form.d.ts +0 -15
  168. package/dist/types/src/components/deprecated/Form.d.ts.map +0 -1
  169. package/dist/types/src/components/deprecated/Form.stories.d.ts +0 -13
  170. package/dist/types/src/components/deprecated/Form.stories.d.ts.map +0 -1
  171. package/dist/types/src/components/deprecated/FormInput.d.ts +0 -16
  172. package/dist/types/src/components/deprecated/FormInput.d.ts.map +0 -1
  173. package/dist/types/src/components/deprecated/index.d.ts +0 -2
  174. package/dist/types/src/components/deprecated/index.d.ts.map +0 -1
  175. package/dist/types/src/hooks/useForm.d.ts +0 -67
  176. package/dist/types/src/hooks/useForm.d.ts.map +0 -1
  177. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +0 -8
  178. package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +0 -1
  179. package/dist/types/src/util/schema.d.ts +0 -3
  180. package/dist/types/src/util/schema.d.ts.map +0 -1
  181. package/src/components/Form/ArrayField.tsx +0 -124
  182. package/src/components/Form/Defaults.tsx +0 -203
  183. package/src/components/Form/FormActions.tsx +0 -53
  184. package/src/components/Form/FormContent.tsx +0 -240
  185. package/src/components/Form/FormContext.tsx +0 -110
  186. package/src/components/Form/Input.tsx +0 -51
  187. package/src/components/Form/RefField.tsx +0 -213
  188. package/src/components/Form/custom/GeoPointInput.tsx +0 -72
  189. package/src/components/Form/custom/index.ts +0 -7
  190. package/src/components/Form/factory.tsx +0 -31
  191. package/src/components/deprecated/Form.stories.tsx +0 -62
  192. package/src/components/deprecated/Form.tsx +0 -119
  193. package/src/components/deprecated/FormInput.tsx +0 -71
  194. package/src/components/deprecated/index.ts +0 -5
  195. package/src/hooks/useForm.ts +0 -291
  196. package/src/hooks/useRefQueryLookupHandler.ts +0 -39
  197. package/src/util/schema.ts +0 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-form",
3
- "version": "0.8.4-main.3a94e84",
3
+ "version": "0.8.4-main.3c1ae3b",
4
4
  "description": "Data components.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -9,6 +9,7 @@
9
9
  "type": "module",
10
10
  "exports": {
11
11
  ".": {
12
+ "source": "./src/index.ts",
12
13
  "types": "./dist/types/src/index.d.ts",
13
14
  "browser": "./dist/lib/browser/index.mjs",
14
15
  "node": "./dist/lib/node-esm/index.mjs"
@@ -25,52 +26,58 @@
25
26
  "dependencies": {
26
27
  "@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
27
28
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
29
+ "@fluentui/react-tabster": "^9.24.2",
28
30
  "@preact-signals/safe-react": "^0.9.0",
31
+ "@preact/signals-react": "^3.2.0",
29
32
  "@radix-ui/react-context": "1.1.1",
33
+ "@radix-ui/react-use-controllable-state": "^1.2.2",
30
34
  "date-fns": "^3.3.1",
31
35
  "jsonpath": "^1.1.1",
32
- "@dxos/async": "0.8.4-main.3a94e84",
33
- "@dxos/debug": "0.8.4-main.3a94e84",
34
- "@dxos/echo": "0.8.4-main.3a94e84",
35
- "@dxos/echo-schema": "0.8.4-main.3a94e84",
36
- "@dxos/effect": "0.8.4-main.3a94e84",
37
- "@dxos/echo-db": "0.8.4-main.3a94e84",
38
- "@dxos/invariant": "0.8.4-main.3a94e84",
39
- "@dxos/keys": "0.8.4-main.3a94e84",
40
- "@dxos/lit-ui": "0.8.4-main.3a94e84",
41
- "@dxos/live-object": "0.8.4-main.3a94e84",
42
- "@dxos/log": "0.8.4-main.3a94e84",
43
- "@dxos/react-client": "0.8.4-main.3a94e84",
44
- "@dxos/react-ui-list": "0.8.4-main.3a94e84",
45
- "@dxos/react-ui-pickers": "0.8.4-main.3a94e84",
46
- "@dxos/react-ui-stack": "0.8.4-main.3a94e84",
47
- "@dxos/react-ui-tag-picker": "0.8.4-main.3a94e84",
48
- "@dxos/schema": "0.8.4-main.3a94e84",
49
- "@dxos/react-ui-types": "0.8.4-main.3a94e84",
50
- "@dxos/util": "0.8.4-main.3a94e84"
36
+ "@dxos/async": "0.8.4-main.3c1ae3b",
37
+ "@dxos/debug": "0.8.4-main.3c1ae3b",
38
+ "@dxos/echo": "0.8.4-main.3c1ae3b",
39
+ "@dxos/echo-db": "0.8.4-main.3c1ae3b",
40
+ "@dxos/effect": "0.8.4-main.3c1ae3b",
41
+ "@dxos/echo-query": "0.8.4-main.3c1ae3b",
42
+ "@dxos/invariant": "0.8.4-main.3c1ae3b",
43
+ "@dxos/keys": "0.8.4-main.3c1ae3b",
44
+ "@dxos/lit-ui": "0.8.4-main.3c1ae3b",
45
+ "@dxos/live-object": "0.8.4-main.3c1ae3b",
46
+ "@dxos/log": "0.8.4-main.3c1ae3b",
47
+ "@dxos/react-client": "0.8.4-main.3c1ae3b",
48
+ "@dxos/react-ui-components": "0.8.4-main.3c1ae3b",
49
+ "@dxos/react-ui-editor": "0.8.4-main.3c1ae3b",
50
+ "@dxos/react-ui-pickers": "0.8.4-main.3c1ae3b",
51
+ "@dxos/react-ui-stack": "0.8.4-main.3c1ae3b",
52
+ "@dxos/react-ui-types": "0.8.4-main.3c1ae3b",
53
+ "@dxos/react-ui-searchlist": "0.8.4-main.3c1ae3b",
54
+ "@dxos/schema": "0.8.4-main.3c1ae3b",
55
+ "@dxos/types": "0.8.4-main.3c1ae3b",
56
+ "@dxos/util": "0.8.4-main.3c1ae3b",
57
+ "@dxos/react-ui-list": "0.8.4-main.3c1ae3b"
51
58
  },
52
59
  "devDependencies": {
53
60
  "@types/jsonpath": "^0.2.4",
54
- "@types/react": "~18.2.0",
55
- "@types/react-dom": "~18.2.0",
56
- "effect": "3.17.0",
61
+ "@types/react": "~19.2.2",
62
+ "@types/react-dom": "~19.2.2",
63
+ "effect": "3.18.3",
57
64
  "fast-check": "^3.19.0",
58
- "react": "~18.2.0",
59
- "react-dom": "~18.2.0",
60
- "vite": "5.4.7",
61
- "@dxos/echo-signals": "0.8.4-main.3a94e84",
62
- "@dxos/random": "0.8.4-main.3a94e84",
63
- "@dxos/react-ui": "0.8.4-main.3a94e84",
64
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.3a94e84",
65
- "@dxos/react-ui-theme": "0.8.4-main.3a94e84",
66
- "@dxos/storybook-utils": "0.8.4-main.3a94e84"
65
+ "react": "~19.2.0",
66
+ "react-dom": "~19.2.0",
67
+ "vite": "7.1.9",
68
+ "@dxos/echo-signals": "0.8.4-main.3c1ae3b",
69
+ "@dxos/random": "0.8.4-main.3c1ae3b",
70
+ "@dxos/react-ui-theme": "0.8.4-main.3c1ae3b",
71
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.3c1ae3b",
72
+ "@dxos/react-ui": "0.8.4-main.3c1ae3b",
73
+ "@dxos/storybook-utils": "0.8.4-main.3c1ae3b"
67
74
  },
68
75
  "peerDependencies": {
69
76
  "effect": "^3.13.3",
70
- "react": "~18.2.0",
71
- "react-dom": "~18.2.0",
72
- "@dxos/react-ui": "0.8.4-main.3a94e84",
73
- "@dxos/react-ui-theme": "0.8.4-main.3a94e84"
77
+ "react": "^19.0.0",
78
+ "react-dom": "^19.0.0",
79
+ "@dxos/react-ui": "0.8.4-main.3c1ae3b",
80
+ "@dxos/react-ui-theme": "0.8.4-main.3c1ae3b"
74
81
  },
75
82
  "publishConfig": {
76
83
  "access": "public"
@@ -2,36 +2,33 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
10
- import { StackItem } from '@dxos/react-ui-stack';
11
- import { withLayout, withTheme } from '@dxos/storybook-utils';
8
+ import { withTheme } from '@dxos/react-ui/testing';
12
9
 
13
- import { ControlPage, ControlSection, type ControlSectionProps } from './ControlSection';
14
10
  import { translations } from '../../translations';
15
11
 
16
- const meta: Meta<ControlSectionProps> = {
12
+ import { ControlPage, ControlSection } from './ControlSection';
13
+
14
+ const meta = {
17
15
  title: 'ui/react-ui-form/ControlSection',
18
16
  component: ControlSection,
19
17
  render: (args) => (
20
- <StackItem.Content classNames='w-[40rem]'>
21
- <ControlPage>
22
- <ControlSection {...args} />
23
- </ControlPage>
24
- </StackItem.Content>
18
+ <ControlPage classNames='is-[40rem]'>
19
+ <ControlSection {...args} />
20
+ </ControlPage>
25
21
  ),
26
- decorators: [withLayout({ fullscreen: true, classNames: 'justify-center' }), withTheme],
22
+ decorators: [withTheme],
27
23
  parameters: {
24
+ layout: 'centered',
28
25
  translations,
29
26
  },
30
- };
27
+ } satisfies Meta<typeof ControlSection>;
31
28
 
32
29
  export default meta;
33
30
 
34
- type Story = StoryObj<ControlSectionProps>;
31
+ type Story = StoryObj<typeof meta>;
35
32
 
36
33
  export const Default: Story = {
37
34
  args: {
@@ -13,6 +13,7 @@ import {
13
13
  toLocalizedString,
14
14
  useTranslation,
15
15
  } from '@dxos/react-ui';
16
+ import { StackItem } from '@dxos/react-ui-stack';
16
17
  import { mx } from '@dxos/react-ui-theme';
17
18
 
18
19
  import { translationKey } from '../../translations';
@@ -21,9 +22,11 @@ export type ControlPageProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
21
22
 
22
23
  export const ControlPage = ({ children, classNames, ...props }: ControlPageProps) => {
23
24
  return (
24
- <div role='none' className={mx('pli-cardSpacingInline', classNames)} {...props}>
25
- {children}
26
- </div>
25
+ <StackItem.Content scrollable classNames='[--control-spacing:var(--dx-trimMd)]'>
26
+ <div role='none' className={mx('pli-cardSpacingInline pbe-trimLg', classNames)} {...props}>
27
+ {children}
28
+ </div>
29
+ </StackItem.Content>
27
30
  );
28
31
  };
29
32
 
@@ -59,12 +62,16 @@ export const ControlGroupButton = ({ classNames, ...props }: ButtonProps) => {
59
62
  return <Button {...props} classNames={['md:col-span-2', classNames]} />;
60
63
  };
61
64
 
62
- export type ControlGroupProps = ThemedClassName<PropsWithChildren<{}>>;
65
+ export type ControlGroupProps = ThemedClassName<PropsWithChildren>;
63
66
 
64
67
  export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
65
68
  <div
66
69
  role='none'
67
- className={mx('group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-trimMd', classNames)}
70
+ className={mx(
71
+ 'group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-trimMd',
72
+ '[--control-spacing:0px] [&_input]:justify-self-end [&_button]:justify-self-end',
73
+ classNames,
74
+ )}
68
75
  >
69
76
  {children}
70
77
  </div>
@@ -73,23 +80,30 @@ export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
73
80
  export const ControlFrame = ({ children }: ControlGroupProps) => (
74
81
  <div
75
82
  role='none'
76
- className='p-trimMd container-max-width grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-trimSm md:gap-trimMd border border-separator rounded-md'
83
+ className={mx(
84
+ 'container-max-width grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-trimSm md:gap-trimMd p-trimMd',
85
+ 'border border-separator rounded-md',
86
+ )}
77
87
  >
78
88
  {children}
79
89
  </div>
80
90
  );
81
91
 
92
+ export const controlItemClasses = mx([
93
+ 'container-max-width grid md:col-span-2 grid-cols-subgrid gap-trimSm items-center',
94
+ // TODO(burdon): Use grid gap consistently or apply margins consistently?
95
+ 'mlb-[--control-spacing] *:first:!mbs-0 *:last:!mbe-0 pli-trimMd plb-trimMd',
96
+ 'border border-separator rounded-md',
97
+ ]);
98
+
99
+ const controlItemTitleClasses = 'mbe-0 text-lg text-baseText font-normal';
100
+ const controlItemDescriptionClasses = 'mlb-trimSm md:mbe-0 text-base text-description';
101
+
82
102
  export type ControlItemProps = PropsWithChildren<{
83
103
  title: Label;
84
104
  description?: Label;
85
105
  }>;
86
106
 
87
- export const controlItemClasses =
88
- 'pli-trimMd plb-trimMd container-max-width grid md:col-span-2 grid-cols-subgrid items-center gap-trimSm border border-separator rounded-md *:first:!mbs-0 *:last:!mbe-0';
89
-
90
- const controlItemTitleClasses = 'text-lg font-normal';
91
- const controlItemDescriptionClasses = 'text-base mlb-trimSm md:mbe-0 text-description';
92
-
93
107
  export const ControlItem = ({ title, description, children }: ControlItemProps) => {
94
108
  const { t } = useTranslation(translationKey);
95
109
 
@@ -2,20 +2,19 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
- import React, { useMemo, useEffect } from 'react';
6
+ import React, { useEffect, useMemo } from 'react';
9
7
 
10
- import { createEchoSchema } from '@dxos/live-object/testing';
8
+ import { createEchoSchema } from '@dxos/echo/testing';
11
9
  import { log } from '@dxos/log';
10
+ import { withTheme } from '@dxos/react-ui/testing';
12
11
  import { ProjectionModel } from '@dxos/schema';
13
- import { TestSchema, testView } from '@dxos/schema/testing';
14
- import { withLayout, withTheme } from '@dxos/storybook-utils';
12
+ import { Example, testView } from '@dxos/schema/testing';
15
13
 
16
- import { FieldEditor, type FieldEditorProps } from './FieldEditor';
17
14
  import { translations } from '../../translations';
18
- import { TestLayout, TestPanel, FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
15
+ import { FIELD_EDITOR_DEBUG_SYMBOL, TestLayout } from '../testing';
16
+
17
+ import { FieldEditor, type FieldEditorProps } from './FieldEditor';
19
18
 
20
19
  // Type definition for debug objects exposed to tests.
21
20
  export type FieldEditorDebugObjects = {
@@ -23,8 +22,6 @@ export type FieldEditorDebugObjects = {
23
22
  projection: ProjectionModel;
24
23
  };
25
24
 
26
- type StoryProps = FieldEditorProps;
27
-
28
25
  const DefaultStory = (props: FieldEditorProps) => {
29
26
  const handleComplete: FieldEditorProps['onSave'] = () => {
30
27
  log.info('onClose', { props });
@@ -32,6 +29,8 @@ const DefaultStory = (props: FieldEditorProps) => {
32
29
 
33
30
  // Expose objects on window for test access.
34
31
  useEffect(() => {
32
+ props.projection.normalizeView();
33
+
35
34
  if (typeof window !== 'undefined') {
36
35
  (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = {
37
36
  props,
@@ -48,31 +47,32 @@ const DefaultStory = (props: FieldEditorProps) => {
48
47
 
49
48
  return (
50
49
  <TestLayout json={json}>
51
- <TestPanel>
52
- <FieldEditor {...props} onSave={handleComplete} />
53
- </TestPanel>
50
+ <FieldEditor {...props} onSave={handleComplete} />
54
51
  </TestLayout>
55
52
  );
56
53
  };
57
54
 
58
- const meta: Meta<StoryProps> = {
55
+ const meta = {
59
56
  title: 'ui/react-ui-form/FieldEditor',
60
- component: FieldEditor,
57
+ component: FieldEditor as any,
61
58
  render: DefaultStory,
62
- decorators: [withLayout({ fullscreen: true }), withTheme],
59
+ decorators: [withTheme],
63
60
  parameters: {
61
+ layout: 'fullscreen',
64
62
  translations,
63
+ controls: {
64
+ disabled: true,
65
+ },
65
66
  },
66
- };
67
+ } satisfies Meta<typeof DefaultStory>;
67
68
 
68
69
  export default meta;
69
70
 
70
- type Story = StoryObj<StoryProps>;
71
+ type Story = StoryObj<typeof meta>;
71
72
 
72
73
  export const Default: Story = {
73
74
  args: {
74
- projection: new ProjectionModel(createEchoSchema(TestSchema).jsonSchema, testView.projection),
75
+ projection: new ProjectionModel(createEchoSchema(Example).jsonSchema, testView.projection),
75
76
  field: testView.projection.fields[0],
76
77
  },
77
- parameters: { controls: { disabled: true } },
78
78
  };
@@ -3,14 +3,15 @@
3
3
  //
4
4
 
5
5
  import { composeStories } from '@storybook/react';
6
- import { screen, cleanup, fireEvent, act } from '@testing-library/react';
6
+ import { act, cleanup, fireEvent, screen } from '@testing-library/react';
7
7
  import { afterEach, describe, expect, test } from 'vitest';
8
8
 
9
9
  import { ProjectionModel } from '@dxos/schema';
10
10
 
11
+ import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
12
+
11
13
  import * as stories from './FieldEditor.stories';
12
14
  import { type FieldEditorDebugObjects } from './FieldEditor.stories';
13
- import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
14
15
 
15
16
  const { Default } = composeStories(stories);
16
17
 
@@ -4,41 +4,41 @@
4
4
 
5
5
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
6
 
7
- import { type SchemaRegistry } from '@dxos/echo-db';
8
- import { FormatEnum, FormatEnums, formatToType, type EchoSchema } from '@dxos/echo-schema';
7
+ import { type SchemaRegistry } from '@dxos/echo';
8
+ import { type EchoSchema, Format, FormatEnums, formatToType } from '@dxos/echo/internal';
9
+ import { type SchemaProperty } from '@dxos/effect';
9
10
  import { log } from '@dxos/log';
10
- import { useTranslation } from '@dxos/react-ui';
11
+ import { useAsyncEffect, useTranslation } from '@dxos/react-ui';
11
12
  import {
12
- getFormatSchema,
13
- getSchemaProperties,
14
- sortProperties,
15
13
  type FieldType,
16
- type PropertyType,
17
14
  type ProjectionModel,
18
- type SchemaProperty,
15
+ type PropertyType,
16
+ formatToAdditionalPropertyAttributes,
17
+ getFormatSchema,
19
18
  } from '@dxos/schema';
20
19
 
21
20
  import { translationKey } from '../../translations';
22
- import { Form, type FormProps, type InputComponent, SelectInput, SelectOptionInput } from '../Form';
21
+ import { getFormProperties } from '../../util';
22
+ import { Form, type FormFieldMap, type FormRootProps, SelectField, SelectOptionField } from '../Form';
23
23
 
24
24
  export type FieldEditorProps = {
25
25
  projection: ProjectionModel;
26
26
  field: FieldType;
27
- registry?: SchemaRegistry;
27
+ registry?: SchemaRegistry.SchemaRegistry;
28
28
  onSave: () => void;
29
29
  onCancel?: () => void;
30
- } & Pick<FormProps<any>, 'outerSpacing'>;
30
+ } & Pick<FormRootProps<any>, 'readonly'>;
31
31
 
32
32
  /**
33
33
  * Displays a Form representing the metadata for a given `Field` and `View`.
34
34
  */
35
- export const FieldEditor = ({ projection, field, registry, onSave, onCancel, outerSpacing }: FieldEditorProps) => {
35
+ export const FieldEditor = ({ readonly, projection, field, registry, onSave, onCancel }: FieldEditorProps) => {
36
36
  const { t } = useTranslation(translationKey);
37
37
  const [props, setProps] = useState<PropertyType>(projection.getFieldProjection(field.id).props);
38
38
  useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
39
39
 
40
40
  const [schemas, setSchemas] = useState<EchoSchema[]>([]);
41
- useEffect(() => {
41
+ useAsyncEffect(async () => {
42
42
  if (!registry) {
43
43
  return;
44
44
  }
@@ -46,14 +46,10 @@ export const FieldEditor = ({ projection, field, registry, onSave, onCancel, out
46
46
  const subscription = registry.query().subscribe((query) => setSchemas(query.results), { fire: true });
47
47
 
48
48
  // TODO(dmaretskyi): This shouldn't be needed.
49
- const t = setTimeout(async () => {
50
- const schemas = await registry.query().run();
51
- setSchemas(schemas);
52
- });
53
- return () => {
54
- clearTimeout(t);
55
- subscription?.();
56
- };
49
+ const schemas = await registry.query().run();
50
+ setSchemas(schemas);
51
+
52
+ return () => subscription?.();
57
53
  }, [registry]);
58
54
 
59
55
  const [referenceSchema, setReferenceSchema] = useState<EchoSchema>();
@@ -65,11 +61,47 @@ export const FieldEditor = ({ projection, field, registry, onSave, onCancel, out
65
61
  // Class constructor SchemaClass cannot be invoked without 'new'.
66
62
  const [{ fieldSchema }, setFieldSchema] = useState({ fieldSchema: getFormatSchema(props?.format) });
67
63
 
68
- const handleValuesChanged = useCallback<NonNullable<FormProps<PropertyType>['onValuesChanged']>>(
64
+ const fieldMap = useMemo<FormFieldMap>(
65
+ () => ({
66
+ ['format' satisfies keyof PropertyType]: (props) => (
67
+ <SelectField
68
+ {...props}
69
+ options={FormatEnums.filter((value) => value !== Format.TypeFormat.None).map((value) => ({
70
+ value,
71
+ label: t(`format ${value}`),
72
+ }))}
73
+ />
74
+ ),
75
+ ['referenceSchema' satisfies keyof PropertyType]: (props) => (
76
+ <SelectField
77
+ {...props}
78
+ options={schemas.map((schema) => ({
79
+ value: schema.typename,
80
+ }))}
81
+ />
82
+ ),
83
+ ['referencePath' satisfies keyof PropertyType]: (props) => (
84
+ <SelectField
85
+ {...props}
86
+ options={
87
+ referenceSchema
88
+ ? getFormProperties(referenceSchema.ast)
89
+ .sort((a, b) => a.name.toString().localeCompare(b.name.toString()))
90
+ .map((p) => ({ value: p.name.toString() }))
91
+ : []
92
+ }
93
+ />
94
+ ),
95
+ ['options' satisfies keyof PropertyType]: (props) => <SelectOptionField {...props} />,
96
+ }),
97
+ [t, schemas, referenceSchema],
98
+ );
99
+
100
+ const propIsNotType = useCallback((props: SchemaProperty[]) => props.filter((prop) => prop.name !== 'type'), []);
101
+
102
+ const handleValuesChanged = useCallback<NonNullable<FormRootProps<PropertyType>['onValuesChanged']>>(
69
103
  (_props) => {
70
- // TODO(burdon): Callback should pass `changed` to indicate which fields have changed.
71
- // TODO(Zaymon): Workout why old and new format values are the same sometimes even when
72
- // selecting novel format values.
104
+ // TODO(Zaymon): Workout why old and new format values are the same sometimes even when selecting novel format values.
73
105
  setFieldSchema((prev) => {
74
106
  const fieldSchema = getFormatSchema(_props.format);
75
107
  if (prev.fieldSchema === fieldSchema) {
@@ -90,8 +122,10 @@ export const FieldEditor = ({ projection, field, registry, onSave, onCancel, out
90
122
 
91
123
  setProps((props) => {
92
124
  const type = formatToType[_props.format as keyof typeof formatToType];
125
+ const additionalProps =
126
+ formatToAdditionalPropertyAttributes[_props.format as keyof typeof formatToAdditionalPropertyAttributes];
93
127
  if (props.type !== type) {
94
- return { ...props, ..._props, type };
128
+ return { ...props, ..._props, ...additionalProps, type } as PropertyType;
95
129
  }
96
130
 
97
131
  return _props as PropertyType;
@@ -100,7 +134,7 @@ export const FieldEditor = ({ projection, field, registry, onSave, onCancel, out
100
134
  [schemas, props.format, props.referenceSchema],
101
135
  );
102
136
 
103
- const handleValidate = useCallback<NonNullable<FormProps<PropertyType>['onValidate']>>(
137
+ const handleValidate = useCallback<NonNullable<FormRootProps<PropertyType>['onValidate']>>(
104
138
  ({ property }) => {
105
139
  if (property && projection.fields.find((f) => f.path === property && f.path !== field.path)) {
106
140
  return [
@@ -114,7 +148,7 @@ export const FieldEditor = ({ projection, field, registry, onSave, onCancel, out
114
148
  [projection.fields, field],
115
149
  );
116
150
 
117
- const handleSave = useCallback<NonNullable<FormProps<PropertyType>['onSave']>>(
151
+ const handleSave = useCallback<NonNullable<FormRootProps<PropertyType>['onSave']>>(
118
152
  (props) => {
119
153
  projection.setFieldProjection({ field, props });
120
154
  onSave();
@@ -122,72 +156,36 @@ export const FieldEditor = ({ projection, field, registry, onSave, onCancel, out
122
156
  [projection, field, onSave],
123
157
  );
124
158
 
125
- const handleCancel = useCallback<NonNullable<FormProps<PropertyType>['onCancel']>>(() => {
159
+ const handleCancel = useCallback<NonNullable<FormRootProps<PropertyType>['onCancel']>>(() => {
160
+ onSave();
126
161
  // Need to defer to allow form to close.
127
162
  requestAnimationFrame(() => onCancel?.());
128
- onSave();
129
163
  }, [onSave]);
130
164
 
131
- const custom: Partial<Record<string, InputComponent>> = useMemo(
132
- () => ({
133
- ['format' satisfies keyof PropertyType]: (props) => (
134
- <SelectInput
135
- {...props}
136
- options={FormatEnums.filter((value) => value !== FormatEnum.None).map((value) => ({
137
- value,
138
- label: t(`format ${value}`),
139
- }))}
140
- />
141
- ),
142
- ['referenceSchema' satisfies keyof PropertyType]: (props) => (
143
- <SelectInput
144
- {...props}
145
- options={schemas.map((schema) => ({
146
- value: schema.typename,
147
- }))}
148
- />
149
- ),
150
- ['referencePath' satisfies keyof PropertyType]: (props) => (
151
- <SelectInput
152
- {...props}
153
- options={
154
- referenceSchema
155
- ? getSchemaProperties(referenceSchema.ast)
156
- .sort(sortProperties)
157
- .map((p) => ({ value: p.name }))
158
- : []
159
- }
160
- />
161
- ),
162
- ['options' satisfies keyof PropertyType]: (props) => <SelectOptionInput {...props} />,
163
- }),
164
- [t, schemas, referenceSchema],
165
- );
166
-
167
- const propIsNotType = useCallback(
168
- (props: SchemaProperty<PropertyType>[]) => props.filter((p) => p.name !== 'type'),
169
- [],
170
- );
171
-
172
165
  if (!fieldSchema) {
173
166
  log.warn('invalid format', { props });
174
167
  return null;
175
168
  }
176
169
 
177
170
  return (
178
- <Form<PropertyType>
171
+ <Form.Root<PropertyType>
179
172
  key={field.id}
173
+ fieldMap={fieldMap}
180
174
  autoFocus
181
- values={props}
175
+ readonly={readonly}
182
176
  schema={fieldSchema}
183
- filter={propIsNotType}
177
+ values={props}
178
+ exclude={propIsNotType}
184
179
  sort={['property', 'format']}
185
180
  onValuesChanged={handleValuesChanged}
186
181
  onValidate={handleValidate}
187
182
  onSave={handleSave}
188
183
  onCancel={handleCancel}
189
- Custom={custom}
190
- outerSpacing={outerSpacing}
191
- />
184
+ >
185
+ <Form.Content>
186
+ <Form.FieldSet />
187
+ <Form.Actions />
188
+ </Form.Content>
189
+ </Form.Root>
192
190
  );
193
191
  };