@dxos/react-ui-form 0.8.4-main.c85a9c8dae → 0.8.4-main.d05539e30a

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 (147) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/browser/index.mjs +1028 -625
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/translations.mjs +82 -0
  6. package/dist/lib/browser/translations.mjs.map +7 -0
  7. package/dist/lib/node-esm/index.mjs +1028 -625
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/lib/node-esm/translations.mjs +84 -0
  11. package/dist/lib/node-esm/translations.mjs.map +7 -0
  12. package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
  13. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +63 -61
  14. package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Form/Form.d.ts +40 -12
  16. package/dist/types/src/components/Form/Form.d.ts.map +1 -1
  17. package/dist/types/src/components/Form/Form.stories.d.ts +76 -74
  18. package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
  19. package/dist/types/src/components/Form/FormField.d.ts +11 -5
  20. package/dist/types/src/components/Form/FormField.d.ts.map +1 -1
  21. package/dist/types/src/components/Form/FormFieldComponent.d.ts +8 -7
  22. package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -1
  23. package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -1
  24. package/dist/types/src/components/Form/FormFieldSet.d.ts +5 -9
  25. package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -1
  26. package/dist/types/src/components/Form/fields/ArrayField.d.ts +4 -4
  27. package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -1
  28. package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -1
  29. package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -1
  30. package/dist/types/src/components/Form/fields/MarkdownField.d.ts +8 -1
  31. package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -1
  32. package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts +84 -0
  33. package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts.map +1 -0
  34. package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -1
  35. package/dist/types/src/components/Form/fields/RefField.d.ts +15 -7
  36. package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -1
  37. package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -1
  38. package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -1
  39. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +63 -61
  40. package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -1
  41. package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -1
  42. package/dist/types/src/components/Form/fields/TextField.d.ts +1 -1
  43. package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -1
  44. package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -1
  45. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +9 -0
  46. package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -0
  47. package/dist/types/src/components/ObjectForm/index.d.ts +2 -0
  48. package/dist/types/src/components/ObjectForm/index.d.ts.map +1 -0
  49. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +26 -4
  50. package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
  51. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +63 -61
  52. package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
  53. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +9 -0
  54. package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -0
  55. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +127 -0
  56. package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -0
  57. package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts +2 -0
  58. package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts.map +1 -0
  59. package/dist/types/src/components/ObjectProperties/index.d.ts +2 -0
  60. package/dist/types/src/components/ObjectProperties/index.d.ts.map +1 -0
  61. package/dist/types/src/components/Settings/Settings.d.ts +28 -33
  62. package/dist/types/src/components/Settings/Settings.d.ts.map +1 -1
  63. package/dist/types/src/components/Settings/Settings.stories.d.ts +64 -61
  64. package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -1
  65. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +43 -0
  66. package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +1 -0
  67. package/dist/types/src/components/Settings/SettingsItem.d.ts +16 -0
  68. package/dist/types/src/components/Settings/SettingsItem.d.ts.map +1 -0
  69. package/dist/types/src/components/Settings/SettingsSelectField.d.ts +18 -0
  70. package/dist/types/src/components/Settings/SettingsSelectField.d.ts.map +1 -0
  71. package/dist/types/src/components/Settings/index.d.ts +2 -0
  72. package/dist/types/src/components/Settings/index.d.ts.map +1 -1
  73. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +3 -4
  74. package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
  75. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +65 -63
  76. package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
  77. package/dist/types/src/components/index.d.ts +2 -0
  78. package/dist/types/src/components/index.d.ts.map +1 -1
  79. package/dist/types/src/components/testing.d.ts +4 -4
  80. package/dist/types/src/components/testing.d.ts.map +1 -1
  81. package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -1
  82. package/dist/types/src/hooks/useKeyHandler.d.ts +2 -1
  83. package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -1
  84. package/dist/types/src/index.d.ts +0 -2
  85. package/dist/types/src/index.d.ts.map +1 -1
  86. package/dist/types/src/translations.d.ts +63 -61
  87. package/dist/types/src/translations.d.ts.map +1 -1
  88. package/dist/types/src/util/field-type.d.ts +19 -0
  89. package/dist/types/src/util/field-type.d.ts.map +1 -0
  90. package/dist/types/src/util/field.d.ts.map +1 -1
  91. package/dist/types/src/util/formatting.d.ts.map +1 -1
  92. package/dist/types/src/util/index.d.ts +1 -0
  93. package/dist/types/src/util/index.d.ts.map +1 -1
  94. package/dist/types/src/util/properties.d.ts +7 -1
  95. package/dist/types/src/util/properties.d.ts.map +1 -1
  96. package/dist/types/src/util/properties.test.d.ts +2 -0
  97. package/dist/types/src/util/properties.test.d.ts.map +1 -0
  98. package/dist/types/src/util/refs.d.ts +1 -0
  99. package/dist/types/src/util/refs.d.ts.map +1 -1
  100. package/dist/types/tsconfig.tsbuildinfo +1 -1
  101. package/package.json +43 -41
  102. package/src/components/FieldEditor/FieldEditor.stories.tsx +2 -2
  103. package/src/components/FieldEditor/FieldEditor.test.tsx +0 -1
  104. package/src/components/FieldEditor/FieldEditor.tsx +4 -3
  105. package/src/components/Form/AUDIT.md +17 -0
  106. package/src/components/Form/Form.stories.tsx +25 -38
  107. package/src/components/Form/Form.tsx +77 -48
  108. package/src/components/Form/FormField.tsx +27 -15
  109. package/src/components/Form/FormFieldComponent.tsx +6 -4
  110. package/src/components/Form/FormFieldSet.tsx +98 -77
  111. package/src/components/Form/fields/ArrayField.tsx +14 -8
  112. package/src/components/Form/fields/GeoPointField.tsx +8 -7
  113. package/src/components/Form/fields/MarkdownField.stories.tsx +206 -0
  114. package/src/components/Form/fields/MarkdownField.tsx +135 -45
  115. package/src/components/Form/fields/RefField.tsx +79 -39
  116. package/src/components/Form/fields/SelectOptionField.stories.tsx +8 -6
  117. package/src/components/Form/fields/SelectOptionField.tsx +12 -12
  118. package/src/components/Form/fields/TextField.tsx +2 -2
  119. package/src/components/Form/fields/TupleField.tsx +1 -1
  120. package/src/components/ObjectForm/ObjectForm.tsx +114 -0
  121. package/src/components/ObjectForm/index.ts +5 -0
  122. package/src/components/ObjectPicker/ObjectPicker.stories.tsx +12 -10
  123. package/src/components/ObjectPicker/ObjectPicker.tsx +92 -65
  124. package/src/components/ObjectProperties/ObjectProperties.stories.tsx +362 -0
  125. package/src/components/ObjectProperties/ObjectProperties.test.tsx +101 -0
  126. package/src/components/ObjectProperties/ObjectProperties.tsx +148 -0
  127. package/src/components/ObjectProperties/index.ts +5 -0
  128. package/src/components/Settings/AUDIT.md +51 -0
  129. package/src/components/Settings/Settings.stories.tsx +69 -21
  130. package/src/components/Settings/Settings.tsx +33 -183
  131. package/src/components/Settings/SettingsFieldSet.tsx +257 -0
  132. package/src/components/Settings/SettingsItem.tsx +58 -0
  133. package/src/components/Settings/SettingsSelectField.tsx +59 -0
  134. package/src/components/Settings/index.ts +2 -0
  135. package/src/components/ViewEditor/ViewEditor.stories.tsx +14 -15
  136. package/src/components/ViewEditor/ViewEditor.test.tsx +0 -1
  137. package/src/components/ViewEditor/ViewEditor.tsx +40 -36
  138. package/src/components/index.ts +2 -0
  139. package/src/components/testing.tsx +19 -11
  140. package/src/hooks/useKeyHandler.tsx +7 -7
  141. package/src/index.ts +0 -4
  142. package/src/translations.ts +66 -63
  143. package/src/util/field-type.ts +60 -0
  144. package/src/util/index.ts +1 -0
  145. package/src/util/properties.test.ts +63 -0
  146. package/src/util/properties.ts +14 -3
  147. package/src/util/refs.ts +7 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-form",
3
- "version": "0.8.4-main.c85a9c8dae",
3
+ "version": "0.8.4-main.d05539e30a",
4
4
  "description": "Data components.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -8,78 +8,80 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/dxos/dxos"
10
10
  },
11
- "license": "MIT",
11
+ "license": "FSL-1.1-Apache-2.0",
12
12
  "author": "DXOS.org",
13
13
  "type": "module",
14
+ "imports": {
15
+ "#translations": "./src/translations.ts"
16
+ },
14
17
  "exports": {
15
18
  ".": {
16
19
  "source": "./src/index.ts",
17
20
  "types": "./dist/types/src/index.d.ts",
18
21
  "browser": "./dist/lib/browser/index.mjs",
19
22
  "node": "./dist/lib/node-esm/index.mjs"
23
+ },
24
+ "./translations": {
25
+ "source": "./src/translations.ts",
26
+ "types": "./dist/types/src/translations.d.ts",
27
+ "browser": "./dist/lib/browser/translations.mjs",
28
+ "node": "./dist/lib/node-esm/translations.mjs"
20
29
  }
21
30
  },
22
31
  "types": "dist/types/src/index.d.ts",
23
- "typesVersions": {
24
- "*": {}
25
- },
26
32
  "files": [
27
33
  "dist",
28
34
  "src"
29
35
  ],
30
36
  "dependencies": {
31
- "@atlaskit/pragmatic-drag-and-drop": "1.7.7",
32
- "@atlaskit/pragmatic-drag-and-drop-hitbox": "1.1.0",
33
37
  "@effect-atom/atom-react": "^0.5.0",
34
38
  "@fluentui/react-tabster": "9.26.11",
35
39
  "@radix-ui/react-context": "1.1.1",
36
40
  "@radix-ui/react-use-controllable-state": "1.1.0",
37
- "date-fns": "^3.3.1",
41
+ "date-fns": "^3.6.0",
38
42
  "tabster": "^8.5.5",
39
- "@dxos/async": "0.8.4-main.c85a9c8dae",
40
- "@dxos/echo": "0.8.4-main.c85a9c8dae",
41
- "@dxos/echo-react": "0.8.4-main.c85a9c8dae",
42
- "@dxos/echo-query": "0.8.4-main.c85a9c8dae",
43
- "@dxos/invariant": "0.8.4-main.c85a9c8dae",
44
- "@dxos/effect": "0.8.4-main.c85a9c8dae",
45
- "@dxos/lit-ui": "0.8.4-main.c85a9c8dae",
46
- "@dxos/keys": "0.8.4-main.c85a9c8dae",
47
- "@dxos/log": "0.8.4-main.c85a9c8dae",
48
- "@dxos/react-client": "0.8.4-main.c85a9c8dae",
49
- "@dxos/react-ui-components": "0.8.4-main.c85a9c8dae",
50
- "@dxos/react-ui-editor": "0.8.4-main.c85a9c8dae",
51
- "@dxos/debug": "0.8.4-main.c85a9c8dae",
52
- "@dxos/react-ui-list": "0.8.4-main.c85a9c8dae",
53
- "@dxos/react-ui-mosaic": "0.8.4-main.c85a9c8dae",
54
- "@dxos/react-ui-stack": "0.8.4-main.c85a9c8dae",
55
- "@dxos/react-ui-pickers": "0.8.4-main.c85a9c8dae",
56
- "@dxos/schema": "0.8.4-main.c85a9c8dae",
57
- "@dxos/react-ui-searchlist": "0.8.4-main.c85a9c8dae",
58
- "@dxos/types": "0.8.4-main.c85a9c8dae",
59
- "@dxos/ui-types": "0.8.4-main.c85a9c8dae",
60
- "@dxos/util": "0.8.4-main.c85a9c8dae"
43
+ "@dxos/async": "0.8.4-main.d05539e30a",
44
+ "@dxos/echo": "0.8.4-main.d05539e30a",
45
+ "@dxos/echo-db": "0.8.4-main.d05539e30a",
46
+ "@dxos/echo-atom": "0.8.4-main.d05539e30a",
47
+ "@dxos/keys": "0.8.4-main.d05539e30a",
48
+ "@dxos/echo-react": "0.8.4-main.d05539e30a",
49
+ "@dxos/lit-ui": "0.8.4-main.d05539e30a",
50
+ "@dxos/log": "0.8.4-main.d05539e30a",
51
+ "@dxos/react-client": "0.8.4-main.d05539e30a",
52
+ "@dxos/react-ui-editor": "0.8.4-main.d05539e30a",
53
+ "@dxos/react-ui-list": "0.8.4-main.d05539e30a",
54
+ "@dxos/react-ui-components": "0.8.4-main.d05539e30a",
55
+ "@dxos/react-ui-pickers": "0.8.4-main.d05539e30a",
56
+ "@dxos/schema": "0.8.4-main.d05539e30a",
57
+ "@dxos/react-ui-search": "0.8.4-main.d05539e30a",
58
+ "@dxos/types": "0.8.4-main.d05539e30a",
59
+ "@dxos/ui-editor": "0.8.4-main.d05539e30a",
60
+ "@dxos/util": "0.8.4-main.d05539e30a",
61
+ "@dxos/effect": "0.8.4-main.d05539e30a",
62
+ "@dxos/invariant": "0.8.4-main.d05539e30a"
61
63
  },
62
64
  "devDependencies": {
63
65
  "@types/react": "~19.2.7",
64
66
  "@types/react-dom": "~19.2.3",
65
- "effect": "3.19.16",
67
+ "effect": "3.20.0",
66
68
  "fast-check": "^3.19.0",
67
69
  "react": "~19.2.3",
68
70
  "react-dom": "~19.2.3",
69
- "vite": "^7.1.11",
70
- "@dxos/random": "0.8.4-main.c85a9c8dae",
71
- "@dxos/react-ui": "0.8.4-main.c85a9c8dae",
72
- "@dxos/react-ui-syntax-highlighter": "0.8.4-main.c85a9c8dae",
73
- "@dxos/react-client": "0.8.4-main.c85a9c8dae",
74
- "@dxos/ui-theme": "0.8.4-main.c85a9c8dae",
75
- "@dxos/storybook-utils": "0.8.4-main.c85a9c8dae"
71
+ "vite": "^8.0.13",
72
+ "@dxos/random": "0.8.4-main.d05539e30a",
73
+ "@dxos/react-client": "0.8.4-main.d05539e30a",
74
+ "@dxos/react-ui": "0.8.4-main.d05539e30a",
75
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.d05539e30a",
76
+ "@dxos/ui-theme": "0.8.4-main.d05539e30a",
77
+ "@dxos/storybook-utils": "0.8.4-main.d05539e30a"
76
78
  },
77
79
  "peerDependencies": {
78
- "effect": "3.19.16",
80
+ "effect": "3.20.0",
79
81
  "react": "~19.2.3",
80
82
  "react-dom": "~19.2.3",
81
- "@dxos/react-ui": "0.8.4-main.c85a9c8dae",
82
- "@dxos/ui-theme": "0.8.4-main.c85a9c8dae"
83
+ "@dxos/ui-theme": "0.8.4-main.d05539e30a",
84
+ "@dxos/react-ui": "0.8.4-main.d05539e30a"
83
85
  },
84
86
  "publishConfig": {
85
87
  "access": "public"
@@ -12,9 +12,9 @@ import { withLayout, withTheme } from '@dxos/react-ui/testing';
12
12
  import { ProjectionModel, ViewModel, createEchoChangeCallback } from '@dxos/schema';
13
13
  import { Example } from '@dxos/schema/testing';
14
14
 
15
- import { translations } from '../../translations';
16
- import { FIELD_EDITOR_DEBUG_SYMBOL, TestLayout } from '../testing';
15
+ import { translations } from '#translations';
17
16
 
17
+ import { FIELD_EDITOR_DEBUG_SYMBOL, TestLayout } from '../testing';
18
18
  import { FieldEditor, type FieldEditorProps } from './FieldEditor';
19
19
 
20
20
  export type FieldEditorDebugObjects = {
@@ -9,7 +9,6 @@ import { afterEach, describe, expect, test } from 'vitest';
9
9
  import { ProjectionModel } from '@dxos/schema';
10
10
 
11
11
  import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
12
-
13
12
  import * as stories from './FieldEditor.stories';
14
13
  import { type FieldEditorDebugObjects } from './FieldEditor.stories';
15
14
 
@@ -16,7 +16,8 @@ import {
16
16
  getFormatSchema,
17
17
  } from '@dxos/schema';
18
18
 
19
- import { translationKey } from '../../translations';
19
+ import { translationKey } from '#translations';
20
+
20
21
  import { getFormProperties } from '../../util';
21
22
  import { Form, type FormFieldMap, type FormRootProps, SelectField, SelectOptionField } from '../Form';
22
23
 
@@ -68,7 +69,7 @@ export const FieldEditor = ({ readonly, projection, field, registry, view, onSav
68
69
  {...props}
69
70
  options={FormatEnums.filter((value) => value !== Format.TypeFormat.None).map((value) => ({
70
71
  value,
71
- label: t(`format ${value}`),
72
+ label: t(`format.${value}.label`),
72
73
  }))}
73
74
  />
74
75
  ),
@@ -151,7 +152,7 @@ export const FieldEditor = ({ readonly, projection, field, registry, view, onSav
151
152
  const handleSave = useCallback<NonNullable<FormRootProps<PropertyType>['onSave']>>(
152
153
  (props) => {
153
154
  if (view) {
154
- Obj.change(view, () => {
155
+ Obj.update(view, () => {
155
156
  projection.setFieldProjection({ field, props });
156
157
  });
157
158
  } else {
@@ -0,0 +1,17 @@
1
+ # Form
2
+
3
+ ## New features/polish
4
+
5
+ - [x] Unify readonly/inline modes.
6
+ - [x] Refs
7
+ - [x] Single-select (fix popover)
8
+ - [x] Multi-select (array)
9
+ - [ ] auto save doesn't work for combobox + select due to only firing on blur (workaround is to use onValuesChanged).
10
+ - [ ] Don't call save/autoSave if value hasn't changed.
11
+ - [ ] Fix onCancel (restore values).
12
+ - [ ] Fix useSchema Type.AnyObj cast.
13
+ - [ ] TableCellEditor (handleEnter/ModalController).
14
+ - [ ] Use FormFieldWrapper uniformly
15
+ - [ ] Inline tables.
16
+ - [ ] Defer query until popover.
17
+ - [ ] Omit id from sub properties (tune Omit)
@@ -9,21 +9,21 @@ import React, { useCallback, useState } from 'react';
9
9
  import { Annotation, Format, Obj, Ref, Tag, Type } from '@dxos/echo';
10
10
  import { type AnyProperties } from '@dxos/echo/internal';
11
11
  import { log } from '@dxos/log';
12
- import { useClient } from '@dxos/react-client';
12
+ import { useSpaces } from '@dxos/react-client/echo';
13
13
  import { withClientProvider } from '@dxos/react-client/testing';
14
14
  import { Tooltip } from '@dxos/react-ui';
15
- import { withLayout, withTheme } from '@dxos/react-ui/testing';
15
+ import { Loading, withLayout, withTheme } from '@dxos/react-ui/testing';
16
16
 
17
- import { translations } from '../../translations';
18
- import { TestLayout } from '../testing';
17
+ import { translations } from '#translations';
19
18
 
19
+ import { TestLayout } from '../testing';
20
20
  import { type ExcludeId, Form, type FormRootProps, omitId } from './Form';
21
21
 
22
22
  const Organization = Schema.Struct({
23
23
  name: Schema.String.pipe(Schema.minLength(1)).annotations({ title: 'Full name' }),
24
24
  }).pipe(
25
25
  Type.object({
26
- typename: 'example.com/type/Organization', // TODO(burdon): Change all types to /schema
26
+ typename: 'com.example.type.organization',
27
27
  version: '0.1.0',
28
28
  }),
29
29
  );
@@ -43,7 +43,7 @@ const Person = Schema.Struct({
43
43
  title: 'State',
44
44
  description: 'State code',
45
45
  }),
46
- zip: Schema.Number,
46
+ zip: Schema.Number.annotations({ title: 'ZIP Code' }),
47
47
  }).annotations({ title: 'Address' }),
48
48
  ),
49
49
  employer: Schema.optional(Ref.Ref(Organization).annotations({ title: 'Employer' })),
@@ -65,16 +65,16 @@ const Person = Schema.Struct({
65
65
  ),
66
66
  }).pipe(
67
67
  Type.object({
68
- typename: 'dxos.org/type/Person', // TODO(burdon): Change all types to /schema
68
+ typename: 'org.dxos.type.person', // TODO(burdon): Change all types to /schema
69
69
  version: '0.1.0',
70
70
  }),
71
71
  );
72
72
 
73
73
  export interface Person extends Schema.Schema.Type<typeof Person> {}
74
74
 
75
- type StoryProps<T extends AnyProperties> = {
75
+ type DefaultStoryProps<T extends AnyProperties> = {
76
+ schema?: Schema.Schema<T>;
76
77
  debug?: boolean;
77
- schema: Schema.Schema<T>;
78
78
  } & FormRootProps<T>;
79
79
 
80
80
  const DefaultStory = <T extends AnyProperties = AnyProperties>({
@@ -82,10 +82,10 @@ const DefaultStory = <T extends AnyProperties = AnyProperties>({
82
82
  schema,
83
83
  values: valuesProp,
84
84
  ...props
85
- }: StoryProps<T>) => {
85
+ }: DefaultStoryProps<T>) => {
86
86
  const [values, setValues] = useState<Partial<T>>(valuesProp ?? {});
87
- const client = useClient();
88
- const space = client.spaces.default;
87
+ const spaces = useSpaces();
88
+ const space = spaces[0];
89
89
 
90
90
  const handleSave = useCallback<NonNullable<FormRootProps<T>['onSave']>>((values) => {
91
91
  log.info('save', { values, meta });
@@ -97,13 +97,17 @@ const DefaultStory = <T extends AnyProperties = AnyProperties>({
97
97
  setValues(valuesProp ?? {});
98
98
  }, []);
99
99
 
100
+ if (!space) {
101
+ return <Loading />;
102
+ }
103
+
100
104
  return (
101
105
  <Tooltip.Provider>
102
- <TestLayout json={{ values, schema: schema.ast }}>
106
+ <TestLayout json={{ values, schema: schema?.ast }}>
103
107
  <Form.Root
104
108
  debug={debug}
105
109
  schema={schema}
106
- values={values}
110
+ defaultValues={values}
107
111
  db={space.db}
108
112
  onSave={handleSave}
109
113
  onCancel={handleCancel}
@@ -111,6 +115,7 @@ const DefaultStory = <T extends AnyProperties = AnyProperties>({
111
115
  >
112
116
  <Form.Viewport>
113
117
  <Form.Content>
118
+ <Form.Section label='Section' description='This is a section' />
114
119
  <Form.FieldSet />
115
120
  <Form.Actions />
116
121
  </Form.Content>
@@ -125,7 +130,6 @@ const meta = {
125
130
  title: 'ui/react-ui-form/Form',
126
131
  component: Form.Root,
127
132
  render: DefaultStory,
128
-
129
133
  decorators: [
130
134
  withTheme(),
131
135
  withLayout({ layout: 'fullscreen' }),
@@ -133,16 +137,10 @@ const meta = {
133
137
  createIdentity: true,
134
138
  createSpace: true,
135
139
  types: [Tag.Tag, Organization, Person],
136
- onCreateIdentity: ({ client }) => {
137
- const space = client.spaces.default;
140
+ onCreateSpace: ({ space }) => {
138
141
  [
139
- Obj.make(Organization, { name: 'DXOS' }),
140
- Obj.make(Organization, { name: 'BlueYard' }),
141
- Obj.make(Organization, { name: 'Backed' }),
142
- Obj.make(Organization, { name: 'BCV' }),
143
- Tag.make({ label: 'Tag 1' }),
144
- Tag.make({ label: 'Tag 2' }),
145
- Tag.make({ label: 'Tag 3' }),
142
+ ...Array.from({ length: 3 }).map((_, i) => Obj.make(Tag.Tag, { label: `Tag ${i}` })),
143
+ ...Array.from({ length: 50 }).map((_, i) => Obj.make(Organization, { name: `Organization ${i}` })),
146
144
  ].map((obj) => space.db.add(obj));
147
145
  },
148
146
  }),
@@ -151,11 +149,11 @@ const meta = {
151
149
  layout: 'fullscreen',
152
150
  translations,
153
151
  },
154
- } satisfies Meta<StoryProps<any>>;
152
+ } satisfies Meta<DefaultStoryProps<any>>;
155
153
 
156
154
  export default meta;
157
155
 
158
- type Story<T extends AnyProperties> = StoryObj<StoryProps<T>>;
156
+ type Story<T extends AnyProperties> = StoryObj<DefaultStoryProps<T>>;
159
157
 
160
158
  const values: Partial<Person> = {
161
159
  name: 'Alice',
@@ -188,16 +186,5 @@ export const Static: Story<ExcludeId<typeof Person>> = {
188
186
  };
189
187
 
190
188
  export const Empty: Story<ExcludeId<typeof Person>> = {
191
- render: () => (
192
- <TestLayout>
193
- <Form.Root>
194
- <Form.Viewport>
195
- <Form.Content>
196
- <Form.FieldSet />
197
- <Form.Actions />
198
- </Form.Content>
199
- </Form.Viewport>
200
- </Form.Root>
201
- </TestLayout>
202
- ),
189
+ args: {},
203
190
  };
@@ -3,21 +3,25 @@
3
3
  //
4
4
 
5
5
  import { createContext } from '@radix-ui/react-context';
6
+ import * as Option from 'effect/Option';
6
7
  import * as Schema from 'effect/Schema';
7
8
  import * as SchemaAST from 'effect/SchemaAST';
8
9
  import React, { type PropsWithChildren, useEffect, useMemo, useRef } from 'react';
9
10
 
11
+ import { Annotation as EchoAnnotation } from '@dxos/echo';
10
12
  import { type AnyProperties } from '@dxos/echo/internal';
11
13
  import { createJsonPath, getValue as getValue$ } from '@dxos/effect';
12
14
  import {
13
15
  IconButton,
14
16
  type IconButtonProps,
15
17
  ScrollArea,
16
- type ScrollAreaRootProps,
17
18
  type ThemedClassName,
19
+ useMergeRefs,
18
20
  useTranslation,
19
21
  } from '@dxos/react-ui';
20
- import { mx } from '@dxos/ui-theme';
22
+ import { composable, composableProps, mx, withColumn } from '@dxos/ui-theme';
23
+
24
+ import { translationKey } from '#translations';
21
25
 
22
26
  import {
23
27
  type FormHandler,
@@ -26,36 +30,35 @@ import {
26
30
  useFormHandler,
27
31
  useKeyHandler,
28
32
  } from '../../hooks';
29
- import { translationKey } from '../../translations';
30
-
31
33
  import { FormFieldLabel, type FormFieldLabelProps, type FormFieldStateProps } from './FormFieldComponent';
32
34
  import {
33
35
  FormFieldSet as NaturalFormFieldSet,
34
36
  type FormFieldSetProps as NaturalFormFieldSetProps,
35
37
  } from './FormFieldSet';
36
38
 
37
- // New features/polish
38
- // [x] Unify readonly/inline modes.
39
- // [x] Refs
40
- // [x] Single-select (fix popover)
41
- // [x] Multi-select (array)
42
- // [ ] auto save doesn't work for combobox + select due to only firing on blur (workaround is to use onValuesChanged).
43
- // [ ] Don't call save/autoSave if value hasn't changed.
44
- // [ ] Fix onCancel (restore values).
45
- // [ ] Fix useSchema Type.AnyObj cast.
46
- // [ ] TableCellEditor (handleEnter/ModalController).
47
- // [ ] Use FormFieldWrapper uniformly
48
- // [ ] Inline tables.
49
- // [ ] Defer query until popover.
50
- // [ ] Omit id from sub properties.
39
+ // TODO(burdon): Move styles to form.ts (as with ui-theme).
51
40
 
52
- // TODO(burdon): Move to @dxos/schema (re-export here).
41
+ // TODO(burdon): Reconcile with @dxos/echo.
53
42
  export type ExcludeId<S extends Schema.Schema.AnyNoContext> = Omit<Schema.Schema.Type<S>, 'id'>;
54
43
 
55
44
  // TODO(burdon): Move to @dxos/schema (re-export here).
56
45
  export const omitId = <S extends Schema.Schema.AnyNoContext>(schema: S): Schema.Schema<ExcludeId<S>, ExcludeId<S>> =>
57
46
  schema.pipe(Schema.omit('id')) as any;
58
47
 
48
+ /**
49
+ * Drop fields annotated with `FormInputAnnotation.set(false)` from a schema so
50
+ * the form's validator doesn't trip on required-but-hidden fields. Used by
51
+ * the picker's inline create form, where a `FactoryAnnotation` typically
52
+ * supplies the hidden values (e.g. a backing-object Ref) outside the form.
53
+ */
54
+ export const omitHiddenFormFields = <S extends Schema.Schema.AnyNoContext>(schema: S): S => {
55
+ const properties = SchemaAST.getPropertySignatures(schema.ast);
56
+ const hidden = properties
57
+ .filter((prop) => Option.getOrElse(EchoAnnotation.FormInputAnnotation.getFromAst(prop.type), () => true) === false)
58
+ .map((prop) => prop.name as string);
59
+ return hidden.length === 0 ? schema : (schema.pipe(Schema.omit(...(hidden as [string, ...string[]]))) as any);
60
+ };
61
+
59
62
  //
60
63
  // Context
61
64
  //
@@ -75,7 +78,10 @@ type FormContextValue<T extends AnyProperties = any> = {
75
78
  * Testing.
76
79
  */
77
80
  testId?: string;
78
- } & Pick<NaturalFormFieldSetProps<T>, 'readonly' | 'layout' | 'fieldMap' | 'fieldProvider' | 'projection'>;
81
+ } & Pick<
82
+ NaturalFormFieldSetProps<T>,
83
+ 'readonly' | 'layout' | 'fieldMap' | 'fieldProvider' | 'projection' | 'createTypename' | 'createFieldMap' | 'db'
84
+ >;
79
85
 
80
86
  const [FormContextProvider, useFormContext] = createContext<FormContextValue>('Form');
81
87
 
@@ -177,29 +183,15 @@ FormRoot.displayName = 'Form.Root';
177
183
 
178
184
  const FORM_VIEWPORT_NAME = 'Form.Viewport';
179
185
 
180
- type FormViewportProps = PropsWithChildren<ScrollAreaRootProps>;
186
+ type FormViewportProps = {};
181
187
 
182
- const FormViewport = ({
183
- children,
184
- classNames,
185
- margin = true,
186
- padding = true,
187
- thin = true,
188
- ...props
189
- }: FormViewportProps) => {
188
+ const FormViewport = composable<HTMLDivElement>(({ children, ...props }, forwardedRef) => {
190
189
  return (
191
- <ScrollArea.Root
192
- orientation='vertical'
193
- classNames={classNames}
194
- margin={margin}
195
- padding={padding}
196
- thin={thin}
197
- {...props}
198
- >
190
+ <ScrollArea.Root {...composableProps(props)} orientation='vertical' centered padding thin ref={forwardedRef}>
199
191
  <ScrollArea.Viewport>{children}</ScrollArea.Viewport>
200
192
  </ScrollArea.Root>
201
193
  );
202
- };
194
+ });
203
195
 
204
196
  FormViewport.displayName = FORM_VIEWPORT_NAME;
205
197
 
@@ -211,17 +203,25 @@ const FORM_CONTENT_NAME = 'Form.Content';
211
203
 
212
204
  type FormContentProps = ThemedClassName<PropsWithChildren<{}>>;
213
205
 
214
- const FormContent = ({ classNames, children }: FormContentProps) => {
206
+ const FormContent = composable<HTMLDivElement, FormContentProps>(({ children, ...props }, forwardedRef) => {
215
207
  const { form, testId } = useFormContext(FORM_CONTENT_NAME);
216
- const ref = useRef<HTMLDivElement>(null);
217
- useKeyHandler(ref.current, form);
208
+ const localRef = useRef<HTMLDivElement>(null);
209
+ const mergedRef = useMergeRefs([forwardedRef, localRef]);
210
+ useKeyHandler(localRef, form);
218
211
 
219
212
  return (
220
- <div ref={ref} role='form' className={mx('w-full flex flex-col gap-form-padding', classNames)} data-testid={testId}>
213
+ <div
214
+ {...composableProps(props, {
215
+ role: 'form',
216
+ classNames: mx(withColumn.center(), 'flex flex-col w-full pb-form-gap'),
217
+ })}
218
+ data-testid={testId}
219
+ ref={mergedRef}
220
+ >
221
221
  {children}
222
222
  </div>
223
223
  );
224
- };
224
+ });
225
225
 
226
226
  FormContent.displayName = FORM_CONTENT_NAME;
227
227
 
@@ -265,12 +265,14 @@ const FormActions = ({ classNames }: FormActionsProps) => {
265
265
  // Deprecate FormSubmit ans use FormActions without Cancel button if no callback is supplied.
266
266
 
267
267
  return (
268
- <div role='none' className={mx('grid grid-flow-col gap-2 auto-cols-fr py-form-padding', classNames)}>
268
+ <div
269
+ className={mx(withColumn.center(), 'grid grid-flow-col gap-form-gap auto-cols-fr py-form-padding', classNames)}
270
+ >
269
271
  {onCancel && (
270
272
  <IconButton
271
273
  icon='ph--x--regular'
272
274
  iconEnd
273
- label={t('cancel button label')}
275
+ label={t('cancel-button.label')}
274
276
  onClick={onCancel}
275
277
  data-testid='cancel-button'
276
278
  />
@@ -282,7 +284,7 @@ const FormActions = ({ classNames }: FormActionsProps) => {
282
284
  disabled={!canSave}
283
285
  icon='ph--check--regular'
284
286
  iconEnd
285
- label={t('save button label')}
287
+ label={t('save-button.label')}
286
288
  onClick={onSave}
287
289
  data-testid='save-button'
288
290
  />
@@ -293,6 +295,31 @@ const FormActions = ({ classNames }: FormActionsProps) => {
293
295
 
294
296
  FormActions.displayName = FORM_ACTIONS_NAME;
295
297
 
298
+ //
299
+ // Section
300
+ //
301
+
302
+ const FORM_SECTION_NAME = 'Form.Section';
303
+
304
+ type FormSectionProps = ThemedClassName<{ label?: string; description?: string }>;
305
+
306
+ const FormSection = composable<HTMLDivElement, FormSectionProps>(
307
+ ({ children, label, description, ...props }, forwardedRef) => {
308
+ return (
309
+ <div
310
+ {...composableProps(props, { classNames: 'flex flex-col pt-form-section-gap first:pt-0' })}
311
+ ref={forwardedRef}
312
+ >
313
+ {label && <h2 className='text-lg'>{label}</h2>}
314
+ {description && <p className='text-description'>{description}</p>}
315
+ {children}
316
+ </div>
317
+ );
318
+ },
319
+ );
320
+
321
+ FormSection.displayName = FORM_SECTION_NAME;
322
+
296
323
  //
297
324
  // Submit
298
325
  //
@@ -314,14 +341,14 @@ const FormSubmit = ({ classNames, label, icon, disabled }: FormSubmitProps) => {
314
341
  }
315
342
 
316
343
  return (
317
- <div role='none' className={mx('flex w-full pt-form-padding', classNames)}>
344
+ <div className={mx('flex w-full pt-form-padding', classNames)}>
318
345
  <IconButton
319
346
  classNames='w-full'
320
347
  type='submit'
321
348
  variant='primary'
322
349
  disabled={disabled ?? !canSave}
323
350
  icon={icon ?? 'ph--check--regular'}
324
- label={label ?? t('save button label')}
351
+ label={label ?? t('save-button.label')}
325
352
  onClick={onSave}
326
353
  data-testid='save-button'
327
354
  />
@@ -340,6 +367,7 @@ export const Form = {
340
367
  Root: FormRoot,
341
368
  Viewport: FormViewport,
342
369
  Content: FormContent,
370
+ Section: FormSection,
343
371
  FieldSet: FormFieldSet,
344
372
  Label: FormFieldLabel,
345
373
  Actions: FormActions,
@@ -352,6 +380,7 @@ export type {
352
380
  FormRootProps,
353
381
  FormViewportProps,
354
382
  FormContentProps,
383
+ FormSectionProps,
355
384
  FormFieldSetProps,
356
385
  FormFieldLabelProps,
357
386
  FormActionsProps,