@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.
- package/LICENSE +102 -5
- package/dist/lib/browser/index.mjs +1028 -625
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/translations.mjs +82 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1028 -625
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/translations.mjs +84 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +63 -61
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +40 -12
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +76 -74
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormField.d.ts +11 -5
- package/dist/types/src/components/Form/FormField.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldComponent.d.ts +8 -7
- package/dist/types/src/components/Form/FormFieldComponent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldComponent.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormFieldSet.d.ts +5 -9
- package/dist/types/src/components/Form/FormFieldSet.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/ArrayField.d.ts +4 -4
- package/dist/types/src/components/Form/fields/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/BooleanField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/GeoPointField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts +8 -1
- package/dist/types/src/components/Form/fields/MarkdownField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts +84 -0
- package/dist/types/src/components/Form/fields/MarkdownField.stories.d.ts.map +1 -0
- package/dist/types/src/components/Form/fields/NumberField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/RefField.d.ts +15 -7
- package/dist/types/src/components/Form/fields/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/SelectField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/SelectOptionField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts +63 -61
- package/dist/types/src/components/Form/fields/SelectOptionField.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/TextAreaField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/TextField.d.ts +1 -1
- package/dist/types/src/components/Form/fields/TextField.d.ts.map +1 -1
- package/dist/types/src/components/Form/fields/TupleField.d.ts.map +1 -1
- package/dist/types/src/components/ObjectForm/ObjectForm.d.ts +9 -0
- package/dist/types/src/components/ObjectForm/ObjectForm.d.ts.map +1 -0
- package/dist/types/src/components/ObjectForm/index.d.ts +2 -0
- package/dist/types/src/components/ObjectForm/index.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +26 -4
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +63 -61
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -1
- package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts +9 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.d.ts.map +1 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts +127 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts +2 -0
- package/dist/types/src/components/ObjectProperties/ObjectProperties.test.d.ts.map +1 -0
- package/dist/types/src/components/ObjectProperties/index.d.ts +2 -0
- package/dist/types/src/components/ObjectProperties/index.d.ts.map +1 -0
- package/dist/types/src/components/Settings/Settings.d.ts +28 -33
- package/dist/types/src/components/Settings/Settings.d.ts.map +1 -1
- package/dist/types/src/components/Settings/Settings.stories.d.ts +64 -61
- package/dist/types/src/components/Settings/Settings.stories.d.ts.map +1 -1
- package/dist/types/src/components/Settings/SettingsFieldSet.d.ts +43 -0
- package/dist/types/src/components/Settings/SettingsFieldSet.d.ts.map +1 -0
- package/dist/types/src/components/Settings/SettingsItem.d.ts +16 -0
- package/dist/types/src/components/Settings/SettingsItem.d.ts.map +1 -0
- package/dist/types/src/components/Settings/SettingsSelectField.d.ts +18 -0
- package/dist/types/src/components/Settings/SettingsSelectField.d.ts.map +1 -0
- package/dist/types/src/components/Settings/index.d.ts +2 -0
- package/dist/types/src/components/Settings/index.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +3 -4
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +65 -63
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +4 -4
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useFormHandler.d.ts.map +1 -1
- package/dist/types/src/hooks/useKeyHandler.d.ts +2 -1
- package/dist/types/src/hooks/useKeyHandler.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +63 -61
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/field-type.d.ts +19 -0
- package/dist/types/src/util/field-type.d.ts.map +1 -0
- package/dist/types/src/util/field.d.ts.map +1 -1
- package/dist/types/src/util/formatting.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/properties.d.ts +7 -1
- package/dist/types/src/util/properties.d.ts.map +1 -1
- package/dist/types/src/util/properties.test.d.ts +2 -0
- package/dist/types/src/util/properties.test.d.ts.map +1 -0
- package/dist/types/src/util/refs.d.ts +1 -0
- package/dist/types/src/util/refs.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +43 -41
- package/src/components/FieldEditor/FieldEditor.stories.tsx +2 -2
- package/src/components/FieldEditor/FieldEditor.test.tsx +0 -1
- package/src/components/FieldEditor/FieldEditor.tsx +4 -3
- package/src/components/Form/AUDIT.md +17 -0
- package/src/components/Form/Form.stories.tsx +25 -38
- package/src/components/Form/Form.tsx +77 -48
- package/src/components/Form/FormField.tsx +27 -15
- package/src/components/Form/FormFieldComponent.tsx +6 -4
- package/src/components/Form/FormFieldSet.tsx +98 -77
- package/src/components/Form/fields/ArrayField.tsx +14 -8
- package/src/components/Form/fields/GeoPointField.tsx +8 -7
- package/src/components/Form/fields/MarkdownField.stories.tsx +206 -0
- package/src/components/Form/fields/MarkdownField.tsx +135 -45
- package/src/components/Form/fields/RefField.tsx +79 -39
- package/src/components/Form/fields/SelectOptionField.stories.tsx +8 -6
- package/src/components/Form/fields/SelectOptionField.tsx +12 -12
- package/src/components/Form/fields/TextField.tsx +2 -2
- package/src/components/Form/fields/TupleField.tsx +1 -1
- package/src/components/ObjectForm/ObjectForm.tsx +114 -0
- package/src/components/ObjectForm/index.ts +5 -0
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +12 -10
- package/src/components/ObjectPicker/ObjectPicker.tsx +92 -65
- package/src/components/ObjectProperties/ObjectProperties.stories.tsx +362 -0
- package/src/components/ObjectProperties/ObjectProperties.test.tsx +101 -0
- package/src/components/ObjectProperties/ObjectProperties.tsx +148 -0
- package/src/components/ObjectProperties/index.ts +5 -0
- package/src/components/Settings/AUDIT.md +51 -0
- package/src/components/Settings/Settings.stories.tsx +69 -21
- package/src/components/Settings/Settings.tsx +33 -183
- package/src/components/Settings/SettingsFieldSet.tsx +257 -0
- package/src/components/Settings/SettingsItem.tsx +58 -0
- package/src/components/Settings/SettingsSelectField.tsx +59 -0
- package/src/components/Settings/index.ts +2 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +14 -15
- package/src/components/ViewEditor/ViewEditor.test.tsx +0 -1
- package/src/components/ViewEditor/ViewEditor.tsx +40 -36
- package/src/components/index.ts +2 -0
- package/src/components/testing.tsx +19 -11
- package/src/hooks/useKeyHandler.tsx +7 -7
- package/src/index.ts +0 -4
- package/src/translations.ts +66 -63
- package/src/util/field-type.ts +60 -0
- package/src/util/index.ts +1 -0
- package/src/util/properties.test.ts +63 -0
- package/src/util/properties.ts +14 -3
- 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.
|
|
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": "
|
|
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.
|
|
41
|
+
"date-fns": "^3.6.0",
|
|
38
42
|
"tabster": "^8.5.5",
|
|
39
|
-
"@dxos/async": "0.8.4-main.
|
|
40
|
-
"@dxos/echo": "0.8.4-main.
|
|
41
|
-
"@dxos/echo-
|
|
42
|
-
"@dxos/echo-
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/lit-ui": "0.8.4-main.
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/react-
|
|
49
|
-
"@dxos/react-ui-
|
|
50
|
-
"@dxos/react-ui-
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/react-ui-
|
|
54
|
-
"@dxos/
|
|
55
|
-
"@dxos/
|
|
56
|
-
"@dxos/
|
|
57
|
-
"@dxos/
|
|
58
|
-
"@dxos/
|
|
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.
|
|
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": "^
|
|
70
|
-
"@dxos/random": "0.8.4-main.
|
|
71
|
-
"@dxos/react-
|
|
72
|
-
"@dxos/react-ui
|
|
73
|
-
"@dxos/react-
|
|
74
|
-
"@dxos/ui-theme": "0.8.4-main.
|
|
75
|
-
"@dxos/storybook-utils": "0.8.4-main.
|
|
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.
|
|
80
|
+
"effect": "3.20.0",
|
|
79
81
|
"react": "~19.2.3",
|
|
80
82
|
"react-dom": "~19.2.3",
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/ui
|
|
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 '
|
|
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 '
|
|
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
|
|
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.
|
|
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 {
|
|
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 '
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
}:
|
|
85
|
+
}: DefaultStoryProps<T>) => {
|
|
86
86
|
const [values, setValues] = useState<Partial<T>>(valuesProp ?? {});
|
|
87
|
-
const
|
|
88
|
-
const space =
|
|
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
|
|
106
|
+
<TestLayout json={{ values, schema: schema?.ast }}>
|
|
103
107
|
<Form.Root
|
|
104
108
|
debug={debug}
|
|
105
109
|
schema={schema}
|
|
106
|
-
|
|
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
|
-
|
|
137
|
-
const space = client.spaces.default;
|
|
140
|
+
onCreateSpace: ({ space }) => {
|
|
138
141
|
[
|
|
139
|
-
Obj.make(
|
|
140
|
-
Obj.make(Organization, { name:
|
|
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<
|
|
152
|
+
} satisfies Meta<DefaultStoryProps<any>>;
|
|
155
153
|
|
|
156
154
|
export default meta;
|
|
157
155
|
|
|
158
|
-
type Story<T extends AnyProperties> = StoryObj<
|
|
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
|
-
|
|
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
|
-
//
|
|
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):
|
|
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<
|
|
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 =
|
|
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 = ({
|
|
206
|
+
const FormContent = composable<HTMLDivElement, FormContentProps>(({ children, ...props }, forwardedRef) => {
|
|
215
207
|
const { form, testId } = useFormContext(FORM_CONTENT_NAME);
|
|
216
|
-
const
|
|
217
|
-
|
|
208
|
+
const localRef = useRef<HTMLDivElement>(null);
|
|
209
|
+
const mergedRef = useMergeRefs([forwardedRef, localRef]);
|
|
210
|
+
useKeyHandler(localRef, form);
|
|
218
211
|
|
|
219
212
|
return (
|
|
220
|
-
<div
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|