@dxos/react-ui-form 0.8.4-main.f9ba587 → 0.8.4-main.fffef41
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/dist/lib/browser/index.mjs +884 -637
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +884 -637
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.d.ts +2 -2
- package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +80 -5
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +4 -5
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +77 -7
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/ArrayField.d.ts +2 -2
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts +5 -5
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +8 -6
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +128 -32
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts +17 -11
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts +3 -3
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts +5 -2
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts +15 -5
- package/dist/types/src/components/Form/RefField.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts +1 -1
- package/dist/types/src/components/Form/custom/GeoPointInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts +75 -4
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts +1 -1
- package/dist/types/src/components/Form/factory.d.ts +1 -1
- package/dist/types/src/components/Form/factory.d.ts.map +1 -1
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts +41 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts +81 -0
- package/dist/types/src/components/ObjectPicker/ObjectPicker.stories.d.ts.map +1 -0
- package/dist/types/src/components/ObjectPicker/index.d.ts +2 -0
- package/dist/types/src/components/ObjectPicker/index.d.ts.map +1 -0
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +20 -6
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +80 -9
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +6 -3
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +3 -3
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueryRefOptions.d.ts +9 -3
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -1
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +13 -8
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util/field.d.ts +1 -1
- package/dist/types/src/util/field.d.ts.map +1 -1
- package/dist/types/src/util/formatting.d.ts +1 -1
- package/dist/types/src/util/formatting.d.ts.map +1 -1
- package/dist/types/src/util/refs.d.ts +1 -1
- package/dist/types/src/util/refs.d.ts.map +1 -1
- package/dist/types/src/util/schema.d.ts +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +42 -36
- package/src/components/ControlSection/ControlSection.stories.tsx +11 -14
- package/src/components/ControlSection/ControlSection.tsx +26 -12
- package/src/components/FieldEditor/FieldEditor.stories.tsx +26 -24
- package/src/components/FieldEditor/FieldEditor.test.tsx +14 -13
- package/src/components/FieldEditor/FieldEditor.tsx +21 -23
- package/src/components/Form/ArrayField.tsx +13 -12
- package/src/components/Form/Defaults.tsx +23 -21
- package/src/components/Form/Form.stories.tsx +135 -99
- package/src/components/Form/Form.tsx +30 -16
- package/src/components/Form/FormActions.tsx +4 -3
- package/src/components/Form/FormContent.tsx +171 -49
- package/src/components/Form/FormContext.tsx +8 -8
- package/src/components/Form/Input.tsx +5 -2
- package/src/components/Form/RefField.tsx +111 -41
- package/src/components/Form/custom/GeoPointInput.tsx +29 -21
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +11 -11
- package/src/components/Form/custom/SelectOptionsInput.tsx +31 -32
- package/src/components/Form/custom/TupleInput.tsx +2 -2
- package/src/components/Form/factory.tsx +2 -2
- package/src/components/ObjectPicker/ObjectPicker.stories.tsx +207 -0
- package/src/components/ObjectPicker/ObjectPicker.tsx +161 -0
- package/src/components/ObjectPicker/index.ts +5 -0
- package/src/components/ViewEditor/ViewEditor.stories.tsx +130 -49
- package/src/components/ViewEditor/ViewEditor.test.tsx +19 -19
- package/src/components/ViewEditor/ViewEditor.tsx +281 -233
- package/src/components/index.ts +1 -2
- package/src/components/testing.tsx +15 -9
- package/src/hooks/useForm.ts +11 -6
- package/src/hooks/useQueryRefOptions.ts +15 -6
- package/src/hooks/useRefQueryLookupHandler.ts +3 -3
- package/src/translations.ts +15 -9
- package/src/util/field.ts +9 -19
- package/src/util/formatting.ts +1 -1
- package/src/util/refs.ts +7 -7
- package/src/util/schema.ts +2 -4
- package/dist/types/src/components/deprecated/Form.d.ts +0 -15
- package/dist/types/src/components/deprecated/Form.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts +0 -13
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/FormInput.d.ts +0 -16
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/index.d.ts +0 -2
- package/dist/types/src/components/deprecated/index.d.ts.map +0 -1
- package/src/components/deprecated/Form.stories.tsx +0 -62
- package/src/components/deprecated/Form.tsx +0 -119
- package/src/components/deprecated/FormInput.tsx +0 -71
- package/src/components/deprecated/index.ts +0 -5
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.fffef41",
|
|
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,57 @@
|
|
|
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",
|
|
30
33
|
"date-fns": "^3.3.1",
|
|
31
34
|
"jsonpath": "^1.1.1",
|
|
32
|
-
"@dxos/async": "0.8.4-main.
|
|
33
|
-
"@dxos/debug": "0.8.4-main.
|
|
34
|
-
"@dxos/echo": "0.8.4-main.
|
|
35
|
-
"@dxos/echo-
|
|
36
|
-
"@dxos/echo
|
|
37
|
-
"@dxos/effect": "0.8.4-main.
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/lit-ui": "0.8.4-main.
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/log": "0.8.4-main.
|
|
43
|
-
"@dxos/react-
|
|
44
|
-
"@dxos/react-ui-
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/react-ui-pickers": "0.8.4-main.
|
|
47
|
-
"@dxos/react-ui-
|
|
48
|
-
"@dxos/react-ui-types": "0.8.4-main.
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
35
|
+
"@dxos/async": "0.8.4-main.fffef41",
|
|
36
|
+
"@dxos/debug": "0.8.4-main.fffef41",
|
|
37
|
+
"@dxos/echo-db": "0.8.4-main.fffef41",
|
|
38
|
+
"@dxos/echo-query": "0.8.4-main.fffef41",
|
|
39
|
+
"@dxos/echo": "0.8.4-main.fffef41",
|
|
40
|
+
"@dxos/effect": "0.8.4-main.fffef41",
|
|
41
|
+
"@dxos/keys": "0.8.4-main.fffef41",
|
|
42
|
+
"@dxos/live-object": "0.8.4-main.fffef41",
|
|
43
|
+
"@dxos/lit-ui": "0.8.4-main.fffef41",
|
|
44
|
+
"@dxos/react-client": "0.8.4-main.fffef41",
|
|
45
|
+
"@dxos/log": "0.8.4-main.fffef41",
|
|
46
|
+
"@dxos/react-ui-components": "0.8.4-main.fffef41",
|
|
47
|
+
"@dxos/react-ui-editor": "0.8.4-main.fffef41",
|
|
48
|
+
"@dxos/invariant": "0.8.4-main.fffef41",
|
|
49
|
+
"@dxos/react-ui-pickers": "0.8.4-main.fffef41",
|
|
50
|
+
"@dxos/react-ui-searchlist": "0.8.4-main.fffef41",
|
|
51
|
+
"@dxos/react-ui-types": "0.8.4-main.fffef41",
|
|
52
|
+
"@dxos/react-ui-stack": "0.8.4-main.fffef41",
|
|
53
|
+
"@dxos/react-ui-list": "0.8.4-main.fffef41",
|
|
54
|
+
"@dxos/schema": "0.8.4-main.fffef41",
|
|
55
|
+
"@dxos/types": "0.8.4-main.fffef41",
|
|
56
|
+
"@dxos/util": "0.8.4-main.fffef41"
|
|
51
57
|
},
|
|
52
58
|
"devDependencies": {
|
|
53
59
|
"@types/jsonpath": "^0.2.4",
|
|
54
|
-
"@types/react": "~
|
|
55
|
-
"@types/react-dom": "~
|
|
56
|
-
"effect": "3.
|
|
60
|
+
"@types/react": "~19.2.2",
|
|
61
|
+
"@types/react-dom": "~19.2.2",
|
|
62
|
+
"effect": "3.18.3",
|
|
57
63
|
"fast-check": "^3.19.0",
|
|
58
|
-
"react": "~
|
|
59
|
-
"react-dom": "~
|
|
60
|
-
"vite": "
|
|
61
|
-
"@dxos/echo-signals": "0.8.4-main.
|
|
62
|
-
"@dxos/
|
|
63
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
64
|
-
"@dxos/
|
|
65
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
66
|
-
"@dxos/storybook-utils": "0.8.4-main.
|
|
64
|
+
"react": "~19.2.0",
|
|
65
|
+
"react-dom": "~19.2.0",
|
|
66
|
+
"vite": "7.1.9",
|
|
67
|
+
"@dxos/echo-signals": "0.8.4-main.fffef41",
|
|
68
|
+
"@dxos/react-ui": "0.8.4-main.fffef41",
|
|
69
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.fffef41",
|
|
70
|
+
"@dxos/random": "0.8.4-main.fffef41",
|
|
71
|
+
"@dxos/react-ui-theme": "0.8.4-main.fffef41",
|
|
72
|
+
"@dxos/storybook-utils": "0.8.4-main.fffef41"
|
|
67
73
|
},
|
|
68
74
|
"peerDependencies": {
|
|
69
75
|
"effect": "^3.13.3",
|
|
70
|
-
"react": "
|
|
71
|
-
"react-dom": "
|
|
72
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
73
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
76
|
+
"react": "^19.0.0",
|
|
77
|
+
"react-dom": "^19.0.0",
|
|
78
|
+
"@dxos/react-ui": "0.8.4-main.fffef41",
|
|
79
|
+
"@dxos/react-ui-theme": "0.8.4-main.fffef41"
|
|
74
80
|
},
|
|
75
81
|
"publishConfig": {
|
|
76
82
|
"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 {
|
|
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
|
-
|
|
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
|
-
<
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
</ControlPage>
|
|
24
|
-
</StackItem.Content>
|
|
18
|
+
<ControlPage classNames='is-[40rem]'>
|
|
19
|
+
<ControlSection {...args} />
|
|
20
|
+
</ControlPage>
|
|
25
21
|
),
|
|
26
|
-
decorators: [
|
|
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<
|
|
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
|
-
<
|
|
25
|
-
{
|
|
26
|
-
|
|
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(
|
|
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=
|
|
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,75 +2,77 @@
|
|
|
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, {
|
|
6
|
+
import React, { useEffect, useMemo } from 'react';
|
|
9
7
|
|
|
10
|
-
import { createEchoSchema } from '@dxos/
|
|
8
|
+
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
9
|
import { log } from '@dxos/log';
|
|
12
|
-
import {
|
|
10
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
11
|
+
import { ProjectionModel } from '@dxos/schema';
|
|
13
12
|
import { TestSchema, testView } from '@dxos/schema/testing';
|
|
14
|
-
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
15
13
|
|
|
16
|
-
import { FieldEditor, type FieldEditorProps } from './FieldEditor';
|
|
17
14
|
import { translations } from '../../translations';
|
|
18
|
-
import { TestLayout, TestPanel
|
|
15
|
+
import { FIELD_EDITOR_DEBUG_SYMBOL, TestLayout, TestPanel } 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 = {
|
|
22
21
|
props: FieldEditorProps;
|
|
23
|
-
projection:
|
|
22
|
+
projection: ProjectionModel;
|
|
24
23
|
};
|
|
25
24
|
|
|
26
|
-
type StoryProps = FieldEditorProps;
|
|
27
|
-
|
|
28
25
|
const DefaultStory = (props: FieldEditorProps) => {
|
|
29
|
-
const projection = useMemo(() => new ViewProjection(createEchoSchema(TestSchema).jsonSchema, testView), []);
|
|
30
26
|
const handleComplete: FieldEditorProps['onSave'] = () => {
|
|
31
27
|
log.info('onClose', { props });
|
|
32
28
|
};
|
|
33
29
|
|
|
34
30
|
// Expose objects on window for test access.
|
|
35
31
|
useEffect(() => {
|
|
32
|
+
props.projection.normalizeView();
|
|
33
|
+
|
|
36
34
|
if (typeof window !== 'undefined') {
|
|
37
|
-
(window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = {
|
|
35
|
+
(window as any)[FIELD_EDITOR_DEBUG_SYMBOL] = {
|
|
36
|
+
props,
|
|
37
|
+
projection: props.projection,
|
|
38
|
+
} satisfies FieldEditorDebugObjects;
|
|
38
39
|
}
|
|
39
|
-
}, [props, projection]);
|
|
40
|
+
}, [props, props.projection]);
|
|
40
41
|
|
|
41
42
|
// NOTE(ZaymonFC): This looks awkward but it resolves an infinite parsing issue with sb.
|
|
42
43
|
const json = useMemo(
|
|
43
|
-
() => JSON.parse(JSON.stringify({ props, projection })),
|
|
44
|
-
[JSON.stringify(props), JSON.stringify(projection)],
|
|
44
|
+
() => JSON.parse(JSON.stringify({ props, projection: props.projection })),
|
|
45
|
+
[JSON.stringify(props), JSON.stringify(props.projection)],
|
|
45
46
|
);
|
|
46
47
|
|
|
47
48
|
return (
|
|
48
49
|
<TestLayout json={json}>
|
|
49
50
|
<TestPanel>
|
|
50
|
-
<FieldEditor {...props}
|
|
51
|
+
<FieldEditor {...props} onSave={handleComplete} />
|
|
51
52
|
</TestPanel>
|
|
52
53
|
</TestLayout>
|
|
53
54
|
);
|
|
54
55
|
};
|
|
55
56
|
|
|
56
|
-
const meta
|
|
57
|
+
const meta = {
|
|
57
58
|
title: 'ui/react-ui-form/FieldEditor',
|
|
58
|
-
component: FieldEditor,
|
|
59
|
+
component: FieldEditor as any,
|
|
59
60
|
render: DefaultStory,
|
|
60
|
-
decorators: [
|
|
61
|
+
decorators: [withTheme],
|
|
61
62
|
parameters: {
|
|
63
|
+
layout: 'fullscreen',
|
|
62
64
|
translations,
|
|
63
65
|
},
|
|
64
|
-
}
|
|
66
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
65
67
|
|
|
66
68
|
export default meta;
|
|
67
69
|
|
|
68
|
-
type Story = StoryObj<
|
|
70
|
+
type Story = StoryObj<typeof meta>;
|
|
69
71
|
|
|
70
72
|
export const Default: Story = {
|
|
71
73
|
args: {
|
|
72
|
-
|
|
73
|
-
field: testView.fields[0],
|
|
74
|
+
projection: new ProjectionModel(createEchoSchema(TestSchema).jsonSchema, testView.projection),
|
|
75
|
+
field: testView.projection.fields[0],
|
|
74
76
|
},
|
|
75
77
|
parameters: { controls: { disabled: true } },
|
|
76
78
|
};
|
|
@@ -3,25 +3,18 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { composeStories } from '@storybook/react';
|
|
6
|
-
import {
|
|
6
|
+
import { act, cleanup, fireEvent, screen } from '@testing-library/react';
|
|
7
7
|
import { afterEach, describe, expect, test } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { ProjectionModel } from '@dxos/schema';
|
|
10
|
+
|
|
11
|
+
import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
10
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
|
|
|
17
|
-
const getFieldEditorDebugObjects = (): FieldEditorDebugObjects => {
|
|
18
|
-
const debugObjects = (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] as FieldEditorDebugObjects;
|
|
19
|
-
expect(debugObjects).toBeDefined();
|
|
20
|
-
expect(debugObjects.props).toBeInstanceOf(Object); // Props object
|
|
21
|
-
expect(debugObjects.projection).toBeInstanceOf(ViewProjection);
|
|
22
|
-
return debugObjects;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
18
|
describe('FieldEditor', () => {
|
|
26
19
|
afterEach(() => {
|
|
27
20
|
cleanup();
|
|
@@ -33,7 +26,7 @@ describe('FieldEditor', () => {
|
|
|
33
26
|
expect(screen.getByText('Type format')).toBeInTheDocument();
|
|
34
27
|
expect(screen.getByText('String')).toBeInTheDocument();
|
|
35
28
|
|
|
36
|
-
// Verify the initial field shows as String type
|
|
29
|
+
// Verify the initial field shows as String type.
|
|
37
30
|
expect(screen.getByText('String')).toBeInTheDocument();
|
|
38
31
|
|
|
39
32
|
fireEvent.click(screen.getByRole('combobox'));
|
|
@@ -49,9 +42,17 @@ describe('FieldEditor', () => {
|
|
|
49
42
|
// Access the live objects directly from window using symbol.
|
|
50
43
|
const debugObjects = getFieldEditorDebugObjects();
|
|
51
44
|
|
|
52
|
-
const name = debugObjects.projection.
|
|
45
|
+
const name = debugObjects.projection.baseSchema.properties!.name;
|
|
53
46
|
expect(name.type).toBe('number');
|
|
54
47
|
expect(name.format).toBe('number');
|
|
55
48
|
expect(name.description).toBe('Full name.');
|
|
56
49
|
});
|
|
57
50
|
});
|
|
51
|
+
|
|
52
|
+
const getFieldEditorDebugObjects = (): FieldEditorDebugObjects => {
|
|
53
|
+
const debugObjects = (window as any)[FIELD_EDITOR_DEBUG_SYMBOL] as FieldEditorDebugObjects;
|
|
54
|
+
expect(debugObjects).toBeDefined();
|
|
55
|
+
expect(debugObjects.props).toBeInstanceOf(Object); // Props object
|
|
56
|
+
expect(debugObjects.projection).toBeInstanceOf(ProjectionModel);
|
|
57
|
+
return debugObjects;
|
|
58
|
+
};
|
|
@@ -4,38 +4,37 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
6
|
|
|
7
|
+
import { type EchoSchema, FormatEnum, FormatEnums, formatToType } from '@dxos/echo/internal';
|
|
7
8
|
import { type SchemaRegistry } from '@dxos/echo-db';
|
|
8
|
-
import { FormatEnum, FormatEnums, formatToType, type EchoSchema } from '@dxos/echo-schema';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
-
import { useTranslation } from '@dxos/react-ui';
|
|
10
|
+
import { useAsyncEffect, useTranslation } from '@dxos/react-ui';
|
|
11
11
|
import {
|
|
12
|
-
getFormatSchema,
|
|
13
|
-
getSchemaProperties,
|
|
14
|
-
sortProperties,
|
|
15
12
|
type FieldType,
|
|
13
|
+
type ProjectionModel,
|
|
16
14
|
type PropertyType,
|
|
17
|
-
type ViewType,
|
|
18
|
-
type ViewProjection,
|
|
19
15
|
type SchemaProperty,
|
|
16
|
+
formatToAdditionalPropertyAttributes,
|
|
17
|
+
getFormatSchema,
|
|
18
|
+
getSchemaProperties,
|
|
19
|
+
sortProperties,
|
|
20
20
|
} from '@dxos/schema';
|
|
21
21
|
|
|
22
22
|
import { translationKey } from '../../translations';
|
|
23
23
|
import { Form, type FormProps, type InputComponent, SelectInput, SelectOptionInput } from '../Form';
|
|
24
24
|
|
|
25
25
|
export type FieldEditorProps = {
|
|
26
|
-
|
|
27
|
-
projection: ViewProjection;
|
|
26
|
+
projection: ProjectionModel;
|
|
28
27
|
field: FieldType;
|
|
29
28
|
registry?: SchemaRegistry;
|
|
30
29
|
onSave: () => void;
|
|
31
30
|
onCancel?: () => void;
|
|
32
|
-
} & Pick<FormProps<any>, 'outerSpacing'>;
|
|
31
|
+
} & Pick<FormProps<any>, 'outerSpacing' | 'readonly'>;
|
|
33
32
|
|
|
34
33
|
/**
|
|
35
34
|
* Displays a Form representing the metadata for a given `Field` and `View`.
|
|
36
35
|
*/
|
|
37
36
|
export const FieldEditor = ({
|
|
38
|
-
|
|
37
|
+
readonly,
|
|
39
38
|
projection,
|
|
40
39
|
field,
|
|
41
40
|
registry,
|
|
@@ -48,7 +47,7 @@ export const FieldEditor = ({
|
|
|
48
47
|
useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
|
|
49
48
|
|
|
50
49
|
const [schemas, setSchemas] = useState<EchoSchema[]>([]);
|
|
51
|
-
|
|
50
|
+
useAsyncEffect(async () => {
|
|
52
51
|
if (!registry) {
|
|
53
52
|
return;
|
|
54
53
|
}
|
|
@@ -56,14 +55,10 @@ export const FieldEditor = ({
|
|
|
56
55
|
const subscription = registry.query().subscribe((query) => setSchemas(query.results), { fire: true });
|
|
57
56
|
|
|
58
57
|
// TODO(dmaretskyi): This shouldn't be needed.
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return () => {
|
|
64
|
-
clearTimeout(t);
|
|
65
|
-
subscription?.();
|
|
66
|
-
};
|
|
58
|
+
const schemas = await registry.query().run();
|
|
59
|
+
setSchemas(schemas);
|
|
60
|
+
|
|
61
|
+
return () => subscription?.();
|
|
67
62
|
}, [registry]);
|
|
68
63
|
|
|
69
64
|
const [referenceSchema, setReferenceSchema] = useState<EchoSchema>();
|
|
@@ -100,8 +95,10 @@ export const FieldEditor = ({
|
|
|
100
95
|
|
|
101
96
|
setProps((props) => {
|
|
102
97
|
const type = formatToType[_props.format as keyof typeof formatToType];
|
|
98
|
+
const additionalProps =
|
|
99
|
+
formatToAdditionalPropertyAttributes[_props.format as keyof typeof formatToAdditionalPropertyAttributes];
|
|
103
100
|
if (props.type !== type) {
|
|
104
|
-
return { ...props, ..._props, type };
|
|
101
|
+
return { ...props, ..._props, ...additionalProps, type } as PropertyType;
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
return _props as PropertyType;
|
|
@@ -112,7 +109,7 @@ export const FieldEditor = ({
|
|
|
112
109
|
|
|
113
110
|
const handleValidate = useCallback<NonNullable<FormProps<PropertyType>['onValidate']>>(
|
|
114
111
|
({ property }) => {
|
|
115
|
-
if (property &&
|
|
112
|
+
if (property && projection.fields.find((f) => f.path === property && f.path !== field.path)) {
|
|
116
113
|
return [
|
|
117
114
|
{
|
|
118
115
|
path: 'property',
|
|
@@ -121,7 +118,7 @@ export const FieldEditor = ({
|
|
|
121
118
|
];
|
|
122
119
|
}
|
|
123
120
|
},
|
|
124
|
-
[
|
|
121
|
+
[projection.fields, field],
|
|
125
122
|
);
|
|
126
123
|
|
|
127
124
|
const handleSave = useCallback<NonNullable<FormProps<PropertyType>['onSave']>>(
|
|
@@ -188,6 +185,7 @@ export const FieldEditor = ({
|
|
|
188
185
|
<Form<PropertyType>
|
|
189
186
|
key={field.id}
|
|
190
187
|
autoFocus
|
|
188
|
+
readonly={readonly}
|
|
191
189
|
values={props}
|
|
192
190
|
schema={fieldSchema}
|
|
193
191
|
filter={propIsNotType}
|
|
@@ -2,30 +2,31 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as Function from 'effect/Function';
|
|
6
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
7
|
+
import * as String from 'effect/String';
|
|
7
8
|
import React, { Fragment, useCallback } from 'react';
|
|
8
9
|
|
|
9
|
-
import { findNode, getDiscriminatedType, isDiscriminatedUnion
|
|
10
|
+
import { SimpleType, findNode, getDiscriminatedType, isDiscriminatedUnion } from '@dxos/effect';
|
|
10
11
|
import { invariant } from '@dxos/invariant';
|
|
11
12
|
import { IconButton, useTranslation } from '@dxos/react-ui';
|
|
12
|
-
import {
|
|
13
|
+
import { type SchemaProperty, getSchemaProperties } from '@dxos/schema';
|
|
13
14
|
|
|
14
|
-
import { type ComponentLookup } from './Form';
|
|
15
|
-
import { FormField } from './FormContent';
|
|
16
|
-
import { useFormValues, type FormInputStateProps } from './FormContext';
|
|
17
|
-
import { InputHeader, type InputComponent } from './Input';
|
|
18
15
|
import { translationKey } from '../../translations';
|
|
19
16
|
import { findArrayElementType } from '../../util';
|
|
20
17
|
|
|
18
|
+
import { type ComponentLookup } from './Form';
|
|
19
|
+
import { FormField, type FormFieldProps } from './FormContent';
|
|
20
|
+
import { type FormInputStateProps, useFormValues } from './FormContext';
|
|
21
|
+
import { type InputComponent, InputHeader } from './Input';
|
|
22
|
+
|
|
21
23
|
type ArrayFieldProps = {
|
|
22
24
|
property: SchemaProperty<any>;
|
|
23
|
-
readonly?: boolean;
|
|
24
25
|
inputProps: FormInputStateProps;
|
|
25
26
|
path?: (string | number)[];
|
|
26
27
|
Custom?: Partial<Record<string, InputComponent>>;
|
|
27
28
|
lookupComponent?: ComponentLookup;
|
|
28
|
-
}
|
|
29
|
+
} & Pick<FormFieldProps, 'readonly'>;
|
|
29
30
|
|
|
30
31
|
export const ArrayField = ({ property, readonly, path, inputProps, Custom, lookupComponent }: ArrayFieldProps) => {
|
|
31
32
|
const { t } = useTranslation(translationKey);
|
|
@@ -34,7 +35,7 @@ export const ArrayField = ({ property, readonly, path, inputProps, Custom, looku
|
|
|
34
35
|
// It doesn't become live until another field is touched, but that's better than the whole form crashing.
|
|
35
36
|
const values = (useFormValues(path ?? []) ?? []) as any[];
|
|
36
37
|
invariant(Array.isArray(values), `Values at path ${path?.join('.')} must be an array.`);
|
|
37
|
-
const label = title ?? pipe(name, capitalize);
|
|
38
|
+
const label = title ?? Function.pipe(name, String.capitalize);
|
|
38
39
|
|
|
39
40
|
const elementType = findArrayElementType(ast);
|
|
40
41
|
|
|
@@ -113,7 +114,7 @@ export const ArrayField = ({ property, readonly, path, inputProps, Custom, looku
|
|
|
113
114
|
</div>
|
|
114
115
|
{!readonly && (
|
|
115
116
|
<IconButton
|
|
116
|
-
classNames='is-full mlb-
|
|
117
|
+
classNames='is-full mlb-cardSpacingBlock flex'
|
|
117
118
|
icon='ph--plus--regular'
|
|
118
119
|
label={t('add field')}
|
|
119
120
|
onClick={handleAdd}
|