@dxos/react-ui-form 0.8.3-main.7f5a14c → 0.8.3
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 +190 -174
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +150 -134
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +190 -174
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.d.ts +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -1
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +3 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +0 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +6 -15
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +2 -2
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts +3 -1
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts +30 -3
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts +6 -4
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts.map +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.map +1 -1
- package/dist/types/src/components/Form/custom/SelectOptionsInput.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/custom/TupleInput.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +3 -2
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +0 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -1
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +3 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/package.json +28 -27
- package/src/components/ControlSection/ControlSection.stories.tsx +15 -3
- package/src/components/ControlSection/ControlSection.tsx +11 -9
- package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -4
- package/src/components/FieldEditor/FieldEditor.test.tsx +2 -1
- package/src/components/FieldEditor/FieldEditor.tsx +11 -2
- package/src/components/Form/ArrayField.tsx +1 -1
- package/src/components/Form/Defaults.tsx +5 -25
- package/src/components/Form/Form.stories.tsx +5 -5
- package/src/components/Form/Form.tsx +44 -52
- package/src/components/Form/FormActions.tsx +17 -2
- package/src/components/Form/FormContent.tsx +46 -46
- package/src/components/Form/Input.tsx +13 -8
- package/src/components/Form/RefField.tsx +1 -5
- package/src/components/Form/custom/GeoPointInput.tsx +1 -3
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +7 -5
- package/src/components/Form/custom/SelectOptionsInput.tsx +1 -3
- package/src/components/Form/custom/TupleInput.tsx +1 -3
- package/src/components/ViewEditor/ViewEditor.stories.tsx +1 -4
- package/src/components/ViewEditor/ViewEditor.test.tsx +2 -1
- package/src/components/ViewEditor/ViewEditor.tsx +66 -66
- package/src/components/testing.tsx +12 -7
- package/src/translations.ts +4 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../../src/components/testing.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../../src/components/testing.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAKtD,eAAO,MAAM,UAAU,GAAI,gCAAgC,eAAe,CAAC,iBAAiB,CAAC;IAAE,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC,sBAK5G,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,0BAA0B,eAAe,CAAC,iBAAiB,CAAC,sBAErF,CAAC;AAGF,eAAO,MAAM,wBAAwB,eAA4B,CAAC;AAClE,eAAO,MAAM,yBAAyB,eAA6B,CAAC"}
|
|
@@ -23,6 +23,7 @@ declare const _default: {
|
|
|
23
23
|
'format date-time': string;
|
|
24
24
|
'format did': string;
|
|
25
25
|
'format duration': string;
|
|
26
|
+
'format dxn': string;
|
|
26
27
|
'format email': string;
|
|
27
28
|
'format formula': string;
|
|
28
29
|
'format hostname': string;
|
|
@@ -35,12 +36,14 @@ declare const _default: {
|
|
|
35
36
|
'format regex': string;
|
|
36
37
|
'format string': string;
|
|
37
38
|
'format single-select': string;
|
|
39
|
+
'format multi-select': string;
|
|
38
40
|
'format text': string;
|
|
39
41
|
'format time': string;
|
|
40
42
|
'format timestamp': string;
|
|
41
43
|
'format user': string;
|
|
42
44
|
'format number': string;
|
|
43
45
|
'format uri': string;
|
|
46
|
+
'format url': string;
|
|
44
47
|
'format uuid': string;
|
|
45
48
|
'placeholder latitude': string;
|
|
46
49
|
'placeholder longitude': string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,kBAAkB,CAAC
|
|
1
|
+
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9C,wBAuEE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-form",
|
|
3
|
-
"version": "0.8.3
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Data components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -29,24 +29,25 @@
|
|
|
29
29
|
"@radix-ui/react-context": "1.1.1",
|
|
30
30
|
"date-fns": "^3.3.1",
|
|
31
31
|
"jsonpath": "^1.1.1",
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/echo
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/echo-
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/live-object": "0.8.3
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/react-client": "0.8.3
|
|
44
|
-
"@dxos/react-ui-
|
|
45
|
-
"@dxos/react-ui-
|
|
46
|
-
"@dxos/react-ui-
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/
|
|
32
|
+
"@dxos/debug": "0.8.3",
|
|
33
|
+
"@dxos/echo": "0.8.3",
|
|
34
|
+
"@dxos/effect": "0.8.3",
|
|
35
|
+
"@dxos/echo-db": "0.8.3",
|
|
36
|
+
"@dxos/async": "0.8.3",
|
|
37
|
+
"@dxos/invariant": "0.8.3",
|
|
38
|
+
"@dxos/echo-schema": "0.8.3",
|
|
39
|
+
"@dxos/keys": "0.8.3",
|
|
40
|
+
"@dxos/lit-ui": "0.8.3",
|
|
41
|
+
"@dxos/live-object": "0.8.3",
|
|
42
|
+
"@dxos/log": "0.8.3",
|
|
43
|
+
"@dxos/react-client": "0.8.3",
|
|
44
|
+
"@dxos/react-ui-list": "0.8.3",
|
|
45
|
+
"@dxos/react-ui-pickers": "0.8.3",
|
|
46
|
+
"@dxos/react-ui-stack": "0.8.3",
|
|
47
|
+
"@dxos/react-ui-tag-picker": "0.8.3",
|
|
48
|
+
"@dxos/react-ui-types": "0.8.3",
|
|
49
|
+
"@dxos/util": "0.8.3",
|
|
50
|
+
"@dxos/schema": "0.8.3"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
53
|
"@phosphor-icons/react": "^2.1.5",
|
|
@@ -58,20 +59,20 @@
|
|
|
58
59
|
"react": "~18.2.0",
|
|
59
60
|
"react-dom": "~18.2.0",
|
|
60
61
|
"vite": "5.4.7",
|
|
61
|
-
"@dxos/echo-signals": "0.8.3
|
|
62
|
-
"@dxos/random": "0.8.3
|
|
63
|
-
"@dxos/react-ui": "0.8.3
|
|
64
|
-
"@dxos/react-ui-
|
|
65
|
-
"@dxos/react-ui-
|
|
66
|
-
"@dxos/storybook-utils": "0.8.3
|
|
62
|
+
"@dxos/echo-signals": "0.8.3",
|
|
63
|
+
"@dxos/random": "0.8.3",
|
|
64
|
+
"@dxos/react-ui": "0.8.3",
|
|
65
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.3",
|
|
66
|
+
"@dxos/react-ui-theme": "0.8.3",
|
|
67
|
+
"@dxos/storybook-utils": "0.8.3"
|
|
67
68
|
},
|
|
68
69
|
"peerDependencies": {
|
|
69
70
|
"@phosphor-icons/react": "^2.1.5",
|
|
70
71
|
"effect": "3.13.3",
|
|
71
72
|
"react": "~18.2.0",
|
|
72
73
|
"react-dom": "~18.2.0",
|
|
73
|
-
"@dxos/react-ui": "0.8.3
|
|
74
|
-
"@dxos/react-ui-theme": "0.8.3
|
|
74
|
+
"@dxos/react-ui": "0.8.3",
|
|
75
|
+
"@dxos/react-ui-theme": "0.8.3"
|
|
75
76
|
},
|
|
76
77
|
"publishConfig": {
|
|
77
78
|
"access": "public"
|
|
@@ -5,16 +5,25 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
|
+
import React from 'react';
|
|
8
9
|
|
|
10
|
+
import { StackItem } from '@dxos/react-ui-stack';
|
|
9
11
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
10
12
|
|
|
11
|
-
import { ControlSection, type ControlSectionProps } from './ControlSection';
|
|
13
|
+
import { ControlPage, ControlSection, type ControlSectionProps } from './ControlSection';
|
|
12
14
|
import translations from '../../translations';
|
|
13
15
|
|
|
14
16
|
const meta: Meta<ControlSectionProps> = {
|
|
15
17
|
title: 'ui/react-ui-form/ControlSection',
|
|
16
18
|
component: ControlSection,
|
|
17
|
-
|
|
19
|
+
render: (args) => (
|
|
20
|
+
<StackItem.Content classNames='w-[40rem]'>
|
|
21
|
+
<ControlPage>
|
|
22
|
+
<ControlSection {...args} />
|
|
23
|
+
</ControlPage>
|
|
24
|
+
</StackItem.Content>
|
|
25
|
+
),
|
|
26
|
+
decorators: [withLayout({ fullscreen: true, classNames: 'justify-center' }), withTheme],
|
|
18
27
|
parameters: {
|
|
19
28
|
translations,
|
|
20
29
|
},
|
|
@@ -25,5 +34,8 @@ export default meta;
|
|
|
25
34
|
type Story = StoryObj<ControlSectionProps>;
|
|
26
35
|
|
|
27
36
|
export const Default: Story = {
|
|
28
|
-
args: {
|
|
37
|
+
args: {
|
|
38
|
+
title: 'Control Section',
|
|
39
|
+
description: 'This is a control section',
|
|
40
|
+
},
|
|
29
41
|
};
|
|
@@ -7,8 +7,8 @@ import React, { type ComponentPropsWithoutRef, type PropsWithChildren } from 're
|
|
|
7
7
|
import {
|
|
8
8
|
Button,
|
|
9
9
|
type ButtonProps,
|
|
10
|
-
type Label,
|
|
11
10
|
Input,
|
|
11
|
+
type Label,
|
|
12
12
|
type ThemedClassName,
|
|
13
13
|
toLocalizedString,
|
|
14
14
|
useTranslation,
|
|
@@ -21,7 +21,7 @@ export type ControlPageProps = ThemedClassName<ComponentPropsWithoutRef<'div'>>;
|
|
|
21
21
|
|
|
22
22
|
export const ControlPage = ({ children, classNames, ...props }: ControlPageProps) => {
|
|
23
23
|
return (
|
|
24
|
-
<div role='none' className={mx('pli-
|
|
24
|
+
<div role='none' className={mx('pli-cardSpacingInline', classNames)} {...props}>
|
|
25
25
|
{children}
|
|
26
26
|
</div>
|
|
27
27
|
);
|
|
@@ -45,9 +45,11 @@ export const ControlSectionHeading = ({ title, description }: Omit<ControlSectio
|
|
|
45
45
|
const { t } = useTranslation(translationKey);
|
|
46
46
|
return (
|
|
47
47
|
<>
|
|
48
|
-
<h2 className='pli-
|
|
48
|
+
<h2 className='pli-trimMd container-max-width text-xl mbs-trimLg mbe-trimMd'>{toLocalizedString(title, t)}</h2>
|
|
49
49
|
{description && (
|
|
50
|
-
<p className='pli-
|
|
50
|
+
<p className='pli-trimMd mlb-trimMd container-max-width text-description'>
|
|
51
|
+
{toLocalizedString(description, t)}
|
|
52
|
+
</p>
|
|
51
53
|
)}
|
|
52
54
|
</>
|
|
53
55
|
);
|
|
@@ -62,7 +64,7 @@ export type ControlGroupProps = ThemedClassName<PropsWithChildren<{}>>;
|
|
|
62
64
|
export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
|
|
63
65
|
<div
|
|
64
66
|
role='none'
|
|
65
|
-
className={mx('group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-
|
|
67
|
+
className={mx('group container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content] gap-trimMd', classNames)}
|
|
66
68
|
>
|
|
67
69
|
{children}
|
|
68
70
|
</div>
|
|
@@ -71,7 +73,7 @@ export const ControlGroup = ({ children, classNames }: ControlGroupProps) => (
|
|
|
71
73
|
export const ControlFrame = ({ children }: ControlGroupProps) => (
|
|
72
74
|
<div
|
|
73
75
|
role='none'
|
|
74
|
-
className='p-
|
|
76
|
+
className='p-trimMd container-max-width grid grid-cols-1 md:grid-cols-[1fr_1fr] gap-trimSm md:gap-trimMd border border-separator rounded-md'
|
|
75
77
|
>
|
|
76
78
|
{children}
|
|
77
79
|
</div>
|
|
@@ -83,10 +85,10 @@ export type ControlItemProps = PropsWithChildren<{
|
|
|
83
85
|
}>;
|
|
84
86
|
|
|
85
87
|
export const controlItemClasses =
|
|
86
|
-
'
|
|
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';
|
|
87
89
|
|
|
88
90
|
const controlItemTitleClasses = 'text-lg font-normal';
|
|
89
|
-
const controlItemDescriptionClasses = 'text-base mlb-
|
|
91
|
+
const controlItemDescriptionClasses = 'text-base mlb-trimSm md:mbe-0 text-description';
|
|
90
92
|
|
|
91
93
|
export const ControlItem = ({ title, description, children }: ControlItemProps) => {
|
|
92
94
|
const { t } = useTranslation(translationKey);
|
|
@@ -130,7 +132,7 @@ export const ControlFrameItem = ({ title, description, children }: ControlItemPr
|
|
|
130
132
|
return (
|
|
131
133
|
<div role='group' className='min-is-0'>
|
|
132
134
|
<h3 className='text-lg mbe-2'>{toLocalizedString(title, t)}</h3>
|
|
133
|
-
{description && <p className='mlb-
|
|
135
|
+
{description && <p className='mlb-trimSm md:mbe-0 text-description'>{toLocalizedString(description, t)}</p>}
|
|
134
136
|
{children}
|
|
135
137
|
</div>
|
|
136
138
|
);
|
|
@@ -15,10 +15,7 @@ import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
|
15
15
|
|
|
16
16
|
import { FieldEditor, type FieldEditorProps } from './FieldEditor';
|
|
17
17
|
import translations from '../../translations';
|
|
18
|
-
import { TestLayout, TestPanel } from '../testing';
|
|
19
|
-
|
|
20
|
-
// Symbol for accessing debug objects in tests.
|
|
21
|
-
export const FIELD_EDITOR_DEBUG_SYMBOL = Symbol('fieldEditorDebug');
|
|
18
|
+
import { TestLayout, TestPanel, FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
22
19
|
|
|
23
20
|
// Type definition for debug objects exposed to tests.
|
|
24
21
|
export type FieldEditorDebugObjects = {
|
|
@@ -9,7 +9,8 @@ import { afterEach, describe, expect, test } from 'vitest';
|
|
|
9
9
|
import { ViewProjection } from '@dxos/schema';
|
|
10
10
|
|
|
11
11
|
import * as stories from './FieldEditor.stories';
|
|
12
|
-
import {
|
|
12
|
+
import { type FieldEditorDebugObjects } from './FieldEditor.stories';
|
|
13
|
+
import { FIELD_EDITOR_DEBUG_SYMBOL } from '../testing';
|
|
13
14
|
|
|
14
15
|
const { Default } = composeStories(stories);
|
|
15
16
|
|
|
@@ -29,12 +29,20 @@ export type FieldEditorProps = {
|
|
|
29
29
|
registry?: SchemaRegistry;
|
|
30
30
|
onSave: () => void;
|
|
31
31
|
onCancel?: () => void;
|
|
32
|
-
}
|
|
32
|
+
} & Pick<FormProps<any>, 'outerSpacing'>;
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Displays a Form representing the metadata for a given `Field` and `View`.
|
|
36
36
|
*/
|
|
37
|
-
export const FieldEditor = ({
|
|
37
|
+
export const FieldEditor = ({
|
|
38
|
+
view,
|
|
39
|
+
projection,
|
|
40
|
+
field,
|
|
41
|
+
registry,
|
|
42
|
+
onSave,
|
|
43
|
+
onCancel,
|
|
44
|
+
outerSpacing,
|
|
45
|
+
}: FieldEditorProps) => {
|
|
38
46
|
const { t } = useTranslation(translationKey);
|
|
39
47
|
const [props, setProps] = useState<PropertyType>(projection.getFieldProjection(field.id).props);
|
|
40
48
|
useEffect(() => setProps(projection.getFieldProjection(field.id).props), [field, projection]);
|
|
@@ -189,6 +197,7 @@ export const FieldEditor = ({ view, projection, field, registry, onSave, onCance
|
|
|
189
197
|
onSave={handleSave}
|
|
190
198
|
onCancel={handleCancel}
|
|
191
199
|
Custom={custom}
|
|
200
|
+
outerSpacing={outerSpacing}
|
|
192
201
|
/>
|
|
193
202
|
);
|
|
194
203
|
};
|
|
@@ -26,11 +26,7 @@ export const TextInput = ({
|
|
|
26
26
|
<p>{getValue() ?? ''}</p>
|
|
27
27
|
) : (
|
|
28
28
|
<Input.Root validationValence={status}>
|
|
29
|
-
{!inputOnly &&
|
|
30
|
-
<InputHeader error={error}>
|
|
31
|
-
<Input.Label>{label}</Input.Label>
|
|
32
|
-
</InputHeader>
|
|
33
|
-
)}
|
|
29
|
+
{!inputOnly && <InputHeader error={error} label={label} />}
|
|
34
30
|
{disabled ? (
|
|
35
31
|
<p>{getValue() ?? ''}</p>
|
|
36
32
|
) : (
|
|
@@ -64,11 +60,7 @@ export const NumberInput = ({
|
|
|
64
60
|
<p>{getValue() ?? ''}</p>
|
|
65
61
|
) : (
|
|
66
62
|
<Input.Root validationValence={status}>
|
|
67
|
-
{!inputOnly &&
|
|
68
|
-
<InputHeader error={error}>
|
|
69
|
-
<Input.Label>{label}</Input.Label>
|
|
70
|
-
</InputHeader>
|
|
71
|
-
)}
|
|
63
|
+
{!inputOnly && <InputHeader error={error} label={label} />}
|
|
72
64
|
{disabled ? (
|
|
73
65
|
<p>{getValue() ?? ''}</p>
|
|
74
66
|
) : (
|
|
@@ -93,11 +85,7 @@ export const BooleanInput = ({ type, label, inputOnly, getStatus, getValue, onVa
|
|
|
93
85
|
|
|
94
86
|
return (
|
|
95
87
|
<Input.Root validationValence={status}>
|
|
96
|
-
{!inputOnly &&
|
|
97
|
-
<InputHeader error={error}>
|
|
98
|
-
<Input.Label>{label}</Input.Label>
|
|
99
|
-
</InputHeader>
|
|
100
|
-
)}
|
|
88
|
+
{!inputOnly && <InputHeader error={error} label={label} />}
|
|
101
89
|
{disabled ? (
|
|
102
90
|
<p>{t(checked ? 'boolean input true value' : 'boolean input false value')}</p>
|
|
103
91
|
) : (
|
|
@@ -130,11 +118,7 @@ export const SelectInput = ({
|
|
|
130
118
|
|
|
131
119
|
return disabled && !value ? null : (
|
|
132
120
|
<Input.Root validationValence={status}>
|
|
133
|
-
{!inputOnly &&
|
|
134
|
-
<InputHeader error={error}>
|
|
135
|
-
<Input.Label>{label}</Input.Label>
|
|
136
|
-
</InputHeader>
|
|
137
|
-
)}
|
|
121
|
+
{!inputOnly && <InputHeader error={error} label={label} />}
|
|
138
122
|
{disabled ? (
|
|
139
123
|
<p>{options?.find(({ value: optionValue }) => optionValue === value)?.label ?? String(value)}</p>
|
|
140
124
|
) : (
|
|
@@ -197,11 +181,7 @@ export const MarkdownInput = ({
|
|
|
197
181
|
|
|
198
182
|
return (
|
|
199
183
|
<Input.Root validationValence={status}>
|
|
200
|
-
{!inputOnly &&
|
|
201
|
-
<InputHeader error={error}>
|
|
202
|
-
<Input.Label>{label}</Input.Label>
|
|
203
|
-
</InputHeader>
|
|
204
|
-
)}
|
|
184
|
+
{!inputOnly && <InputHeader error={error} label={label} />}
|
|
205
185
|
{disabled ? (
|
|
206
186
|
<p>{getValue() ?? ''}</p>
|
|
207
187
|
) : (
|
|
@@ -13,7 +13,7 @@ import { type BaseObject, Expando, Format, getObjectDXN, Ref, type TypeAnnotatio
|
|
|
13
13
|
import { live } from '@dxos/live-object';
|
|
14
14
|
import { withSurfaceVariantsLayout } from '@dxos/react-ui/testing';
|
|
15
15
|
import { Testing } from '@dxos/schema/testing';
|
|
16
|
-
import { withTheme } from '@dxos/storybook-utils';
|
|
16
|
+
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
17
17
|
|
|
18
18
|
import { SelectInput } from './Defaults';
|
|
19
19
|
import { Form, type FormProps } from './Form';
|
|
@@ -66,9 +66,9 @@ const DebugStory = <T extends BaseObject>({ schema, values: initialValues, ...pr
|
|
|
66
66
|
|
|
67
67
|
const meta: Meta<StoryProps<any>> = {
|
|
68
68
|
title: 'ui/react-ui-form/Form',
|
|
69
|
-
component: Form
|
|
69
|
+
component: Form<any>,
|
|
70
70
|
render: DebugStory,
|
|
71
|
-
decorators: [withTheme],
|
|
71
|
+
decorators: [withLayout({ fullscreen: true }), withTheme],
|
|
72
72
|
parameters: {
|
|
73
73
|
translations,
|
|
74
74
|
},
|
|
@@ -100,7 +100,7 @@ export const Default: Story<TestType> = {
|
|
|
100
100
|
},
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
-
export const Organization: Story<Testing.
|
|
103
|
+
export const Organization: Story<Schema.Schema.Type<typeof Testing.OrganizationSchema>> = {
|
|
104
104
|
args: {
|
|
105
105
|
schema: Testing.OrganizationSchema,
|
|
106
106
|
values: {
|
|
@@ -111,7 +111,7 @@ export const Organization: Story<Testing.Organization> = {
|
|
|
111
111
|
},
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
-
export const OrganizationAutoSave: Story<Testing.
|
|
114
|
+
export const OrganizationAutoSave: Story<Schema.Schema.Type<typeof Testing.OrganizationSchema>> = {
|
|
115
115
|
args: {
|
|
116
116
|
schema: Testing.OrganizationSchema,
|
|
117
117
|
values: {
|
|
@@ -7,15 +7,14 @@ import React, { type ReactElement, useEffect, useMemo, useRef } from 'react';
|
|
|
7
7
|
|
|
8
8
|
import { type BaseObject, type PropertyKey } from '@dxos/echo-schema';
|
|
9
9
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
|
-
import { cardSpacing } from '@dxos/react-ui-stack';
|
|
11
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
10
|
+
import { cardDialogOverflow, cardSpacing } from '@dxos/react-ui-stack';
|
|
12
11
|
import { type SchemaProperty } from '@dxos/schema';
|
|
13
12
|
|
|
14
|
-
import { FormActions } from './FormActions';
|
|
15
|
-
import { FormFields } from './FormContent';
|
|
13
|
+
import { FormActions, type FormOuterSpacing } from './FormActions';
|
|
14
|
+
import { FormFields, type FormFieldsProps } from './FormContent';
|
|
16
15
|
import { FormProvider } from './FormContext';
|
|
17
16
|
import { type InputProps, type InputComponent } from './Input';
|
|
18
|
-
import { type FormOptions
|
|
17
|
+
import { type FormOptions } from '../../hooks';
|
|
19
18
|
|
|
20
19
|
export type PropsFilter<T extends BaseObject> = (props: SchemaProperty<T>[]) => SchemaProperty<T>[];
|
|
21
20
|
|
|
@@ -27,55 +26,39 @@ export type ComponentLookup = (args: {
|
|
|
27
26
|
|
|
28
27
|
export type CustomInputMap = Partial<Record<string, InputComponent>>;
|
|
29
28
|
|
|
30
|
-
export type FormProps<T extends BaseObject> = ThemedClassName<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
autoSave?: boolean;
|
|
44
|
-
flush?: boolean;
|
|
45
|
-
testId?: string;
|
|
46
|
-
onCancel?: () => void;
|
|
47
|
-
onQueryRefOptions?: QueryRefOptions;
|
|
48
|
-
lookupComponent?: ComponentLookup;
|
|
49
|
-
/**
|
|
50
|
-
* Map of custom renderers for specific properties.
|
|
51
|
-
* Prefer lookupComponent for plugin specific input surfaces.
|
|
52
|
-
*/
|
|
53
|
-
Custom?: CustomInputMap;
|
|
54
|
-
} & Pick<FormOptions<T>, 'schema' | 'onValuesChanged' | 'onValidate' | 'onSave'>
|
|
55
|
-
>;
|
|
29
|
+
export type FormProps<T extends BaseObject> = ThemedClassName<{
|
|
30
|
+
values: Partial<T>;
|
|
31
|
+
// TODO(burdon): Autofocus first input.
|
|
32
|
+
autoFocus?: boolean;
|
|
33
|
+
// TODO(burdon): Change to JsonPath includes/excludes.
|
|
34
|
+
filter?: PropsFilter<T>;
|
|
35
|
+
sort?: PropertyKey<T>[];
|
|
36
|
+
autoSave?: boolean;
|
|
37
|
+
outerSpacing?: FormOuterSpacing;
|
|
38
|
+
onCancel?: () => void;
|
|
39
|
+
}> &
|
|
40
|
+
Pick<FormOptions<T>, 'schema' | 'onValuesChanged' | 'onValidate' | 'onSave'> &
|
|
41
|
+
FormFieldsProps;
|
|
56
42
|
|
|
57
43
|
export const Form = <T extends BaseObject>({
|
|
58
44
|
classNames,
|
|
59
|
-
|
|
45
|
+
testId,
|
|
60
46
|
values: initialValues,
|
|
61
|
-
path = [],
|
|
62
47
|
autoFocus,
|
|
63
48
|
readonly,
|
|
64
|
-
filter,
|
|
65
|
-
sort,
|
|
66
49
|
autoSave,
|
|
67
|
-
|
|
68
|
-
|
|
50
|
+
outerSpacing = true,
|
|
51
|
+
onCancel,
|
|
52
|
+
schema,
|
|
69
53
|
onValuesChanged,
|
|
70
54
|
onValidate,
|
|
71
55
|
onSave,
|
|
72
|
-
|
|
73
|
-
onQueryRefOptions,
|
|
74
|
-
lookupComponent,
|
|
75
|
-
Custom,
|
|
56
|
+
...props
|
|
76
57
|
}: FormProps<T>) => {
|
|
77
58
|
const formRef = useRef<HTMLDivElement>(null);
|
|
78
|
-
|
|
59
|
+
|
|
60
|
+
// TODO(burdon): Rename.
|
|
61
|
+
const handleValid = useMemo(() => (autoSave ? onSave : undefined), [autoSave, onSave]);
|
|
79
62
|
|
|
80
63
|
// Focus the first input element within this form.
|
|
81
64
|
useEffect(() => {
|
|
@@ -95,21 +78,30 @@ export const Form = <T extends BaseObject>({
|
|
|
95
78
|
initialValues={initialValues}
|
|
96
79
|
onValuesChanged={onValuesChanged}
|
|
97
80
|
onValidate={onValidate}
|
|
98
|
-
onValid={
|
|
81
|
+
onValid={handleValid}
|
|
99
82
|
onSave={onSave}
|
|
100
83
|
>
|
|
101
|
-
<div
|
|
84
|
+
<div role='none' className='contents' data-testid={testId}>
|
|
102
85
|
<FormFields
|
|
103
|
-
|
|
104
|
-
|
|
86
|
+
{...props}
|
|
87
|
+
ref={formRef}
|
|
88
|
+
classNames={[
|
|
89
|
+
outerSpacing === 'blockStart-0'
|
|
90
|
+
? 'pli-cardSpacingInline mbe-cardSpacingBlock [&>.mbs-inputSpacingBlock:first-child]:!mbs-0'
|
|
91
|
+
: outerSpacing === 'scroll-fields'
|
|
92
|
+
? 'pli-cardSpacingInline pbe-cardSpacingBlock'
|
|
93
|
+
: outerSpacing
|
|
94
|
+
? cardSpacing
|
|
95
|
+
: false,
|
|
96
|
+
outerSpacing === 'scroll-fields' && cardDialogOverflow,
|
|
97
|
+
classNames,
|
|
98
|
+
]}
|
|
105
99
|
readonly={readonly}
|
|
106
|
-
|
|
107
|
-
sort={sort}
|
|
108
|
-
onQueryRefOptions={onQueryRefOptions}
|
|
109
|
-
lookupComponent={lookupComponent}
|
|
110
|
-
Custom={Custom}
|
|
100
|
+
schema={schema}
|
|
111
101
|
/>
|
|
112
|
-
{(onCancel || onSave) && !autoSave &&
|
|
102
|
+
{(onCancel || onSave) && !autoSave && (
|
|
103
|
+
<FormActions readonly={readonly} onCancel={onCancel} outerSpacing={outerSpacing} />
|
|
104
|
+
)}
|
|
113
105
|
</div>
|
|
114
106
|
</FormProvider>
|
|
115
107
|
);
|
|
@@ -5,21 +5,36 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import { IconButton, useTranslation } from '@dxos/react-ui';
|
|
8
|
+
import { cardSpacing } from '@dxos/react-ui-stack';
|
|
9
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
8
10
|
|
|
9
11
|
import { useFormContext } from './FormContext';
|
|
10
12
|
import { translationKey } from '../../translations';
|
|
11
13
|
|
|
14
|
+
export type FormOuterSpacing = boolean | 'blockStart-0' | 'scroll-fields';
|
|
15
|
+
|
|
12
16
|
export type FormActionsProps = {
|
|
13
17
|
readonly?: boolean;
|
|
14
18
|
onCancel?: () => void;
|
|
19
|
+
outerSpacing?: FormOuterSpacing;
|
|
15
20
|
};
|
|
16
21
|
|
|
17
|
-
export const FormActions = ({ readonly, onCancel }: FormActionsProps) => {
|
|
22
|
+
export const FormActions = ({ readonly, onCancel, outerSpacing = true }: FormActionsProps) => {
|
|
18
23
|
const { t } = useTranslation(translationKey);
|
|
19
24
|
const { canSave, handleSave } = useFormContext();
|
|
20
25
|
|
|
21
26
|
return (
|
|
22
|
-
<div
|
|
27
|
+
<div
|
|
28
|
+
role='none'
|
|
29
|
+
className={mx(
|
|
30
|
+
outerSpacing === 'scroll-fields'
|
|
31
|
+
? 'pli-cardSpacingInline mbe-cardSpacingBlock'
|
|
32
|
+
: outerSpacing
|
|
33
|
+
? cardSpacing
|
|
34
|
+
: 'mbs-cardSpacingBlock',
|
|
35
|
+
'flex [&_button]:grow gap-1 first:mbs-0',
|
|
36
|
+
)}
|
|
37
|
+
>
|
|
23
38
|
{onCancel && !readonly && (
|
|
24
39
|
<IconButton data-testid='cancel-button' icon='ph--x--regular' label={t('button cancel')} onClick={onCancel} />
|
|
25
40
|
)}
|