@dxos/react-ui-form 0.8.4-main.b97322e → 0.8.4-main.c4373fc
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 +810 -594
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +810 -594
- 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 +81 -5
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts +2 -2
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.stories.d.ts +76 -5
- 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 +7 -6
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +129 -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 +14 -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 +76 -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 +16 -4
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +79 -7
- 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 +8 -3
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +11 -5
- 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 +41 -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 +14 -14
- package/src/components/FieldEditor/FieldEditor.test.tsx +3 -2
- package/src/components/FieldEditor/FieldEditor.tsx +26 -18
- package/src/components/Form/ArrayField.tsx +12 -11
- package/src/components/Form/Defaults.tsx +22 -20
- package/src/components/Form/Form.stories.tsx +135 -99
- package/src/components/Form/Form.tsx +26 -15
- package/src/components/Form/FormActions.tsx +2 -1
- package/src/components/Form/FormContent.tsx +121 -39
- package/src/components/Form/FormContext.tsx +8 -8
- package/src/components/Form/Input.tsx +5 -2
- package/src/components/Form/RefField.tsx +97 -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 +30 -31
- 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 +102 -44
- package/src/components/ViewEditor/ViewEditor.test.tsx +9 -12
- package/src/components/ViewEditor/ViewEditor.tsx +328 -220
- package/src/components/index.ts +1 -2
- package/src/components/testing.tsx +15 -9
- package/src/hooks/useForm.ts +8 -4
- package/src/hooks/useQueryRefOptions.ts +9 -3
- package/src/hooks/useRefQueryLookupHandler.ts +1 -1
- package/src/translations.ts +13 -6
- package/src/util/field.ts +9 -19
- package/src/util/formatting.ts +1 -1
- package/src/util/refs.ts +2 -2
- 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
|
@@ -2,24 +2,71 @@
|
|
|
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
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
9
7
|
import React, { useCallback, useState } from 'react';
|
|
10
8
|
|
|
11
9
|
import { ContactType } from '@dxos/client/testing';
|
|
12
|
-
import { type
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
10
|
+
import { type Type } from '@dxos/echo';
|
|
11
|
+
import { type BaseObject, Expando, Format, Ref, type TypeAnnotation, getObjectDXN } from '@dxos/echo/internal';
|
|
12
|
+
import { live } from '@dxos/echo/internal';
|
|
13
|
+
import { Tooltip } from '@dxos/react-ui';
|
|
14
|
+
import { withSurfaceVariantsLayout, withTheme } from '@dxos/react-ui/testing';
|
|
15
15
|
import { Testing } from '@dxos/schema/testing';
|
|
16
|
-
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
17
16
|
|
|
18
|
-
import { SelectInput } from './Defaults';
|
|
19
|
-
import { Form, type FormProps } from './Form';
|
|
20
17
|
import { translations } from '../../translations';
|
|
21
18
|
import { TestLayout, TestPanel } from '../testing';
|
|
22
19
|
|
|
20
|
+
import { SelectInput } from './Defaults';
|
|
21
|
+
import { Form, type FormProps } from './Form';
|
|
22
|
+
|
|
23
|
+
type StoryProps<T extends BaseObject> = {
|
|
24
|
+
debug?: boolean;
|
|
25
|
+
schema: Type.Obj.Any;
|
|
26
|
+
} & FormProps<T>;
|
|
27
|
+
|
|
28
|
+
const DefaultStory = <T extends BaseObject = any>({
|
|
29
|
+
debug,
|
|
30
|
+
schema,
|
|
31
|
+
values: initialValues,
|
|
32
|
+
...props
|
|
33
|
+
}: StoryProps<T>) => {
|
|
34
|
+
const [values, setValues] = useState(initialValues);
|
|
35
|
+
const handleSave = useCallback<NonNullable<FormProps<T>['onSave']>>((values) => {
|
|
36
|
+
setValues(values);
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
if (debug) {
|
|
40
|
+
return (
|
|
41
|
+
<Tooltip.Provider>
|
|
42
|
+
<TestLayout json={{ values, schema: schema.ast }}>
|
|
43
|
+
<TestPanel>
|
|
44
|
+
<Form<T> schema={schema} values={values} onSave={handleSave} {...props} />
|
|
45
|
+
</TestPanel>
|
|
46
|
+
</TestLayout>
|
|
47
|
+
</Tooltip.Provider>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return <Form<T> schema={schema} values={values} onSave={handleSave} {...props} />;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const RefStory = <T extends BaseObject = any>(props: StoryProps<T>) => {
|
|
55
|
+
const onQueryRefOptions = useCallback((typeInfo: TypeAnnotation) => {
|
|
56
|
+
switch (typeInfo.typename) {
|
|
57
|
+
case ContactType.typename:
|
|
58
|
+
return [
|
|
59
|
+
{ dxn: getObjectDXN(contact1)!, label: 'John Coltraine' },
|
|
60
|
+
{ dxn: getObjectDXN(contact2)!, label: 'Erykah Badu' },
|
|
61
|
+
];
|
|
62
|
+
default:
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}, []);
|
|
66
|
+
|
|
67
|
+
return <DefaultStory<T> onQueryRefOptions={onQueryRefOptions} {...props} />;
|
|
68
|
+
};
|
|
69
|
+
|
|
23
70
|
const AddressSchema = Schema.Struct({
|
|
24
71
|
street: Schema.optional(Schema.String.annotations({ title: 'Street' })),
|
|
25
72
|
city: Schema.optional(Schema.String.annotations({ title: 'City' })),
|
|
@@ -27,8 +74,7 @@ const AddressSchema = Schema.Struct({
|
|
|
27
74
|
location: Schema.optional(Format.GeoPoint.annotations({ title: 'Location' })),
|
|
28
75
|
}).annotations({ title: 'Address' });
|
|
29
76
|
|
|
30
|
-
|
|
31
|
-
const TestSchema = Schema.Struct({
|
|
77
|
+
const ContactSchema = Schema.Struct({
|
|
32
78
|
name: Schema.optional(Schema.String.annotations({ title: 'Name' })),
|
|
33
79
|
active: Schema.optional(Schema.Boolean.annotations({ title: 'Active' })),
|
|
34
80
|
rank: Schema.optional(Schema.Number.annotations({ title: 'Rank' })),
|
|
@@ -36,40 +82,15 @@ const TestSchema = Schema.Struct({
|
|
|
36
82
|
address: Schema.optional(AddressSchema),
|
|
37
83
|
}).pipe(Schema.mutable);
|
|
38
84
|
|
|
39
|
-
type
|
|
40
|
-
|
|
41
|
-
type StoryProps<T extends BaseObject> = { schema: Schema.Schema<T> } & FormProps<T>;
|
|
42
|
-
|
|
43
|
-
const DefaultStory = <T extends BaseObject>({ schema, values: initialValues, ...props }: StoryProps<T>) => {
|
|
44
|
-
const [values, setValues] = useState(initialValues);
|
|
45
|
-
const handleSave = useCallback<NonNullable<FormProps<T>['onSave']>>((values) => {
|
|
46
|
-
setValues(values);
|
|
47
|
-
}, []);
|
|
48
|
-
|
|
49
|
-
return <Form<T> schema={schema} values={values} onSave={handleSave} {...props} />;
|
|
50
|
-
};
|
|
85
|
+
type ContactSchema = Schema.Schema.Type<typeof ContactSchema>;
|
|
51
86
|
|
|
52
|
-
const
|
|
53
|
-
const [values, setValues] = useState(initialValues);
|
|
54
|
-
const handleSave = useCallback<NonNullable<FormProps<T>['onSave']>>((values) => {
|
|
55
|
-
setValues(values);
|
|
56
|
-
}, []);
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<TestLayout json={{ values, schema: schema.ast.toJSON() }}>
|
|
60
|
-
<TestPanel>
|
|
61
|
-
<Form<T> schema={schema} values={values} onSave={handleSave} {...props} />
|
|
62
|
-
</TestPanel>
|
|
63
|
-
</TestLayout>
|
|
64
|
-
);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const meta: Meta<StoryProps<any>> = {
|
|
87
|
+
const meta = {
|
|
68
88
|
title: 'ui/react-ui-form/Form',
|
|
69
|
-
component: Form
|
|
70
|
-
render:
|
|
71
|
-
decorators: [
|
|
89
|
+
component: Form as any,
|
|
90
|
+
render: DefaultStory,
|
|
91
|
+
decorators: [withTheme, withSurfaceVariantsLayout()],
|
|
72
92
|
parameters: {
|
|
93
|
+
layout: 'fullscreen',
|
|
73
94
|
translations,
|
|
74
95
|
},
|
|
75
96
|
argTypes: {
|
|
@@ -78,17 +99,15 @@ const meta: Meta<StoryProps<any>> = {
|
|
|
78
99
|
description: 'Readonly',
|
|
79
100
|
},
|
|
80
101
|
},
|
|
81
|
-
}
|
|
102
|
+
} satisfies Meta<StoryProps<any>>;
|
|
82
103
|
|
|
83
104
|
export default meta;
|
|
84
105
|
|
|
85
|
-
type Story
|
|
106
|
+
type Story = StoryObj<typeof meta>;
|
|
86
107
|
|
|
87
|
-
export const Default: Story
|
|
88
|
-
render: DefaultStory,
|
|
89
|
-
decorators: [withSurfaceVariantsLayout(), withTheme],
|
|
108
|
+
export const Default: Story = {
|
|
90
109
|
args: {
|
|
91
|
-
schema:
|
|
110
|
+
schema: ContactSchema,
|
|
92
111
|
values: {
|
|
93
112
|
name: 'DXOS',
|
|
94
113
|
active: true,
|
|
@@ -100,8 +119,9 @@ export const Default: Story<TestType> = {
|
|
|
100
119
|
},
|
|
101
120
|
};
|
|
102
121
|
|
|
103
|
-
export const Organization: Story
|
|
122
|
+
export const Organization: Story = {
|
|
104
123
|
args: {
|
|
124
|
+
debug: true,
|
|
105
125
|
schema: Testing.OrganizationSchema,
|
|
106
126
|
values: {
|
|
107
127
|
name: 'DXOS',
|
|
@@ -111,8 +131,9 @@ export const Organization: Story<Schema.Schema.Type<typeof Testing.OrganizationS
|
|
|
111
131
|
},
|
|
112
132
|
};
|
|
113
133
|
|
|
114
|
-
export const OrganizationAutoSave: Story
|
|
134
|
+
export const OrganizationAutoSave: Story = {
|
|
115
135
|
args: {
|
|
136
|
+
debug: true,
|
|
116
137
|
schema: Testing.OrganizationSchema,
|
|
117
138
|
values: {
|
|
118
139
|
name: 'DXOS',
|
|
@@ -125,15 +146,20 @@ export const OrganizationAutoSave: Story<Schema.Schema.Type<typeof Testing.Organ
|
|
|
125
146
|
|
|
126
147
|
// TODO(burdon): Type issue with employer reference.
|
|
127
148
|
// TODO(burdon): Test table/form with compound values (e.g., address).
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
149
|
+
// Property name is missing in type EncodedReference but required in type
|
|
150
|
+
export const Contact: Story = {
|
|
151
|
+
args: {
|
|
152
|
+
debug: true,
|
|
153
|
+
schema: Testing.ContactSchema,
|
|
154
|
+
values: {
|
|
155
|
+
name: 'Bot',
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
//
|
|
161
|
+
// Union
|
|
162
|
+
//
|
|
137
163
|
|
|
138
164
|
const ShapeSchema = Schema.Struct({
|
|
139
165
|
shape: Schema.optional(
|
|
@@ -150,10 +176,11 @@ const ShapeSchema = Schema.Struct({
|
|
|
150
176
|
),
|
|
151
177
|
}).pipe(Schema.mutable);
|
|
152
178
|
|
|
153
|
-
type
|
|
179
|
+
type ShapeSchema = Schema.Schema.Type<typeof ShapeSchema>;
|
|
154
180
|
|
|
155
|
-
export const DiscriminatedShape:
|
|
181
|
+
export const DiscriminatedShape: StoryObj<StoryProps<ShapeSchema>> = {
|
|
156
182
|
args: {
|
|
183
|
+
debug: true,
|
|
157
184
|
schema: ShapeSchema,
|
|
158
185
|
readonly: false,
|
|
159
186
|
values: {
|
|
@@ -176,15 +203,20 @@ export const DiscriminatedShape: Story<ShapeType> = {
|
|
|
176
203
|
},
|
|
177
204
|
};
|
|
178
205
|
|
|
206
|
+
//
|
|
207
|
+
// Arrays
|
|
208
|
+
//
|
|
209
|
+
|
|
179
210
|
const ArraysSchema = Schema.Struct({
|
|
180
211
|
names: Schema.Array(Schema.String.pipe(Schema.nonEmptyString())),
|
|
181
212
|
addresses: Schema.Array(AddressSchema),
|
|
182
213
|
}).pipe(Schema.mutable);
|
|
183
214
|
|
|
184
|
-
type
|
|
215
|
+
type ArraysSchema = Schema.Schema.Type<typeof ArraysSchema>;
|
|
185
216
|
|
|
186
|
-
export const Arrays: StoryObj<
|
|
217
|
+
export const Arrays: StoryObj<StoryProps<ArraysSchema>> = {
|
|
187
218
|
args: {
|
|
219
|
+
debug: true,
|
|
188
220
|
schema: ArraysSchema,
|
|
189
221
|
readonly: false,
|
|
190
222
|
values: {
|
|
@@ -194,6 +226,34 @@ export const Arrays: StoryObj<FormProps<ArraysType>> = {
|
|
|
194
226
|
},
|
|
195
227
|
};
|
|
196
228
|
|
|
229
|
+
//
|
|
230
|
+
// Tuple
|
|
231
|
+
//
|
|
232
|
+
|
|
233
|
+
// TODO(wittjosiah): Only GeoPoint is works currently.
|
|
234
|
+
const TupleSchema = Schema.Struct({
|
|
235
|
+
tuple: Schema.Tuple(Schema.Number, Schema.String, Schema.Boolean),
|
|
236
|
+
geopoint: Format.GeoPoint,
|
|
237
|
+
}).pipe(Schema.mutable);
|
|
238
|
+
|
|
239
|
+
type TupleSchema = Schema.Schema.Type<typeof TupleSchema>;
|
|
240
|
+
|
|
241
|
+
export const Tuple: StoryObj<StoryProps<TupleSchema>> = {
|
|
242
|
+
args: {
|
|
243
|
+
debug: true,
|
|
244
|
+
schema: TupleSchema,
|
|
245
|
+
readonly: false,
|
|
246
|
+
values: {
|
|
247
|
+
tuple: [1, 'a', true],
|
|
248
|
+
geopoint: [-122.41941, 37.77492],
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
//
|
|
254
|
+
// Enum
|
|
255
|
+
//
|
|
256
|
+
|
|
197
257
|
const ColorSchema = Schema.Struct({
|
|
198
258
|
color: Schema.Union(Schema.Literal('red'), Schema.Literal('green'), Schema.Literal('blue')).annotations({
|
|
199
259
|
title: 'Color',
|
|
@@ -202,8 +262,9 @@ const ColorSchema = Schema.Struct({
|
|
|
202
262
|
|
|
203
263
|
type ColorType = Schema.Schema.Type<typeof ColorSchema>;
|
|
204
264
|
|
|
205
|
-
export const Enum: StoryObj<
|
|
265
|
+
export const Enum: StoryObj<StoryProps<ColorType>> = {
|
|
206
266
|
args: {
|
|
267
|
+
debug: true,
|
|
207
268
|
schema: ColorSchema,
|
|
208
269
|
readonly: false,
|
|
209
270
|
values: {
|
|
@@ -212,6 +273,10 @@ export const Enum: StoryObj<FormProps<ColorType>> = {
|
|
|
212
273
|
},
|
|
213
274
|
};
|
|
214
275
|
|
|
276
|
+
//
|
|
277
|
+
// Refs
|
|
278
|
+
//
|
|
279
|
+
|
|
215
280
|
const RefSchema = Schema.Struct({
|
|
216
281
|
contact: Ref(ContactType).annotations({ title: 'Contact Reference' }),
|
|
217
282
|
optionalContact: Schema.optional(Ref(ContactType).annotations({ title: 'Optional Contact Reference' })),
|
|
@@ -219,48 +284,19 @@ const RefSchema = Schema.Struct({
|
|
|
219
284
|
unknownExpando: Schema.optional(Ref(Expando).annotations({ title: 'Optional Ref to an Expando (DXN Input)' })),
|
|
220
285
|
});
|
|
221
286
|
|
|
287
|
+
type RefSchema = Schema.Schema.Type<typeof RefSchema>;
|
|
288
|
+
|
|
222
289
|
const contact1 = live(ContactType, { identifiers: [] });
|
|
223
290
|
const contact2 = live(ContactType, { identifiers: [] });
|
|
224
291
|
|
|
225
|
-
const
|
|
226
|
-
const [values, setValues] = useState(initialValues);
|
|
227
|
-
const handleSave = useCallback<NonNullable<FormProps<any>['onSave']>>((values) => {
|
|
228
|
-
setValues(values);
|
|
229
|
-
}, []);
|
|
230
|
-
|
|
231
|
-
const onQueryRefOptions = useCallback((typeInfo: TypeAnnotation) => {
|
|
232
|
-
switch (typeInfo.typename) {
|
|
233
|
-
case ContactType.typename:
|
|
234
|
-
return [
|
|
235
|
-
{ dxn: getObjectDXN(contact1)!, label: 'John Coltraine' },
|
|
236
|
-
{ dxn: getObjectDXN(contact2)!, label: 'Erykah Badu' },
|
|
237
|
-
];
|
|
238
|
-
default:
|
|
239
|
-
return [];
|
|
240
|
-
}
|
|
241
|
-
}, []);
|
|
242
|
-
|
|
243
|
-
return (
|
|
244
|
-
<TestLayout json={{ values, schema: RefSchema.ast.toJSON() }}>
|
|
245
|
-
<TestPanel>
|
|
246
|
-
<Form
|
|
247
|
-
schema={RefSchema}
|
|
248
|
-
values={values}
|
|
249
|
-
readonly={readonly}
|
|
250
|
-
onSave={handleSave}
|
|
251
|
-
onQueryRefOptions={onQueryRefOptions}
|
|
252
|
-
/>
|
|
253
|
-
</TestPanel>
|
|
254
|
-
</TestLayout>
|
|
255
|
-
);
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
export const Refs: StoryObj<FormProps<ContactType>> = {
|
|
292
|
+
export const Refs: StoryObj<StoryProps<RefSchema>> = {
|
|
259
293
|
render: RefStory,
|
|
260
294
|
args: {
|
|
295
|
+
debug: true,
|
|
296
|
+
schema: RefSchema,
|
|
261
297
|
readonly: false,
|
|
262
298
|
values: {
|
|
263
299
|
refArray: [Ref.make(contact1), Ref.make(contact2)],
|
|
264
|
-
}
|
|
300
|
+
},
|
|
265
301
|
},
|
|
266
302
|
};
|
|
@@ -2,19 +2,21 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { useFocusFinders } from '@fluentui/react-tabster';
|
|
6
|
+
import type * as Schema from 'effect/Schema';
|
|
7
|
+
import React, { type ReactElement, useCallback, useEffect, useRef } from 'react';
|
|
7
8
|
|
|
8
|
-
import { type BaseObject, type PropertyKey } from '@dxos/echo
|
|
9
|
+
import { type BaseObject, type PropertyKey } from '@dxos/echo/internal';
|
|
9
10
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
11
|
import { cardDialogOverflow, cardSpacing } from '@dxos/react-ui-stack';
|
|
11
|
-
import { type SchemaProperty } from '@dxos/schema';
|
|
12
|
+
import { type ProjectionModel, type SchemaProperty } from '@dxos/schema';
|
|
13
|
+
|
|
14
|
+
import { type FormHandler, type FormOptions } from '../../hooks';
|
|
12
15
|
|
|
13
16
|
import { FormActions, type FormOuterSpacing } from './FormActions';
|
|
14
17
|
import { FormFields, type FormFieldsProps } from './FormContent';
|
|
15
18
|
import { FormProvider } from './FormContext';
|
|
16
|
-
import { type
|
|
17
|
-
import { type FormOptions } from '../../hooks';
|
|
19
|
+
import { type InputComponent, type InputProps } from './Input';
|
|
18
20
|
|
|
19
21
|
export type PropsFilter<T extends BaseObject> = (props: SchemaProperty<T>[]) => SchemaProperty<T>[];
|
|
20
22
|
|
|
@@ -33,6 +35,7 @@ export type FormProps<T extends BaseObject> = ThemedClassName<{
|
|
|
33
35
|
// TODO(burdon): Change to JsonPath includes/excludes.
|
|
34
36
|
filter?: PropsFilter<T>;
|
|
35
37
|
sort?: PropertyKey<T>[];
|
|
38
|
+
projection?: ProjectionModel;
|
|
36
39
|
autoSave?: boolean;
|
|
37
40
|
outerSpacing?: FormOuterSpacing;
|
|
38
41
|
onCancel?: () => void;
|
|
@@ -48,8 +51,8 @@ export const Form = <T extends BaseObject>({
|
|
|
48
51
|
readonly,
|
|
49
52
|
autoSave,
|
|
50
53
|
outerSpacing = true,
|
|
51
|
-
onCancel,
|
|
52
54
|
schema,
|
|
55
|
+
onCancel,
|
|
53
56
|
onValuesChanged,
|
|
54
57
|
onValidate,
|
|
55
58
|
onSave,
|
|
@@ -57,19 +60,27 @@ export const Form = <T extends BaseObject>({
|
|
|
57
60
|
}: FormProps<T>) => {
|
|
58
61
|
const formRef = useRef<HTMLDivElement>(null);
|
|
59
62
|
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
// Focus the first input element within this form.
|
|
63
|
+
// Focus the first focusable element within this form.
|
|
64
|
+
const { findFirstFocusable } = useFocusFinders();
|
|
64
65
|
useEffect(() => {
|
|
65
66
|
if (autoFocus && formRef.current) {
|
|
66
|
-
const
|
|
67
|
-
if (
|
|
68
|
-
|
|
67
|
+
const firstFocusable = findFirstFocusable(formRef.current);
|
|
68
|
+
if (firstFocusable) {
|
|
69
|
+
firstFocusable.focus();
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
}, [autoFocus]);
|
|
72
73
|
|
|
74
|
+
// TODO(burdon): Name?
|
|
75
|
+
const handleValid = useCallback(
|
|
76
|
+
async (values: any, meta: { changed: FormHandler<T>['changed'] }) => {
|
|
77
|
+
if (autoSave) {
|
|
78
|
+
await onSave?.(values, meta);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
[autoSave, onSave],
|
|
82
|
+
);
|
|
83
|
+
|
|
73
84
|
return (
|
|
74
85
|
<FormProvider
|
|
75
86
|
formRef={formRef}
|
|
@@ -99,7 +110,7 @@ export const Form = <T extends BaseObject>({
|
|
|
99
110
|
readonly={readonly}
|
|
100
111
|
schema={schema}
|
|
101
112
|
/>
|
|
102
|
-
{(onCancel || onSave) && !autoSave && (
|
|
113
|
+
{(onCancel || onSave) && !autoSave && !readonly && (
|
|
103
114
|
<FormActions readonly={readonly} onCancel={onCancel} outerSpacing={outerSpacing} />
|
|
104
115
|
)}
|
|
105
116
|
</div>
|
|
@@ -8,9 +8,10 @@ import { IconButton, useTranslation } from '@dxos/react-ui';
|
|
|
8
8
|
import { cardSpacing } from '@dxos/react-ui-stack';
|
|
9
9
|
import { mx } from '@dxos/react-ui-theme';
|
|
10
10
|
|
|
11
|
-
import { useFormContext } from './FormContext';
|
|
12
11
|
import { translationKey } from '../../translations';
|
|
13
12
|
|
|
13
|
+
import { useFormContext } from './FormContext';
|
|
14
|
+
|
|
14
15
|
export type FormOuterSpacing = boolean | 'blockStart-0' | 'scroll-fields';
|
|
15
16
|
|
|
16
17
|
export type FormActionsProps = {
|