@dxos/react-ui-form 0.8.2-staging.7ac8446 → 0.8.2
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 +1818 -1309
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +1774 -1273
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +1818 -1309
- 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.d.ts → ControlSection/ControlSection.d.ts} +9 -6
- package/dist/types/src/components/ControlSection/ControlSection.d.ts.map +1 -0
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts +8 -0
- package/dist/types/src/components/ControlSection/ControlSection.stories.d.ts.map +1 -0
- package/dist/types/src/components/ControlSection/index.d.ts +2 -0
- package/dist/types/src/components/ControlSection/index.d.ts.map +1 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.d.ts.map +1 -1
- package/dist/types/src/components/FieldEditor/FieldEditor.test.d.ts +2 -0
- package/dist/types/src/components/FieldEditor/FieldEditor.test.d.ts.map +1 -0
- package/dist/types/src/components/Form/ArrayField.d.ts.map +1 -1
- package/dist/types/src/components/Form/Defaults.d.ts +4 -4
- package/dist/types/src/components/Form/Defaults.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.d.ts +6 -4
- package/dist/types/src/components/Form/Form.d.ts.map +1 -1
- package/dist/types/src/components/Form/Form.stories.d.ts +38 -36
- package/dist/types/src/components/Form/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts +1 -1
- package/dist/types/src/components/Form/FormActions.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContent.d.ts +16 -12
- package/dist/types/src/components/Form/FormContent.d.ts.map +1 -1
- package/dist/types/src/components/Form/FormContext.d.ts +2 -2
- package/dist/types/src/components/Form/FormContext.d.ts.map +1 -1
- package/dist/types/src/components/Form/Input.d.ts.map +1 -1
- package/dist/types/src/components/Form/RefField.d.ts +12 -0
- package/dist/types/src/components/Form/RefField.d.ts.map +1 -0
- 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/Form/factory.d.ts +1 -1
- package/dist/types/src/components/Form/factory.d.ts.map +1 -1
- package/dist/types/src/components/Form/index.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/Test.stories.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts +2 -2
- package/dist/types/src/components/ViewEditor/ViewEditor.d.ts.map +1 -1
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts +5 -3
- package/dist/types/src/components/ViewEditor/ViewEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.d.ts +2 -2
- package/dist/types/src/components/deprecated/Form.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/Form.stories.d.ts.map +1 -1
- package/dist/types/src/components/deprecated/FormInput.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useForm.d.ts +2 -2
- package/dist/types/src/hooks/useForm.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueryRefOptions.d.ts +23 -0
- package/dist/types/src/hooks/useQueryRefOptions.d.ts.map +1 -0
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts +8 -0
- package/dist/types/src/hooks/useRefQueryLookupHandler.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +5 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- 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 +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/refs.d.ts +9 -0
- package/dist/types/src/util/refs.d.ts.map +1 -0
- package/dist/types/src/util/schema.d.ts +3 -0
- package/dist/types/src/util/schema.d.ts.map +1 -0
- package/dist/types/src/vitest-setup.d.ts +2 -0
- package/dist/types/src/vitest-setup.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +28 -25
- package/src/components/ControlSection/ControlSection.stories.tsx +29 -0
- package/src/components/{ControlSection.tsx → ControlSection/ControlSection.tsx} +40 -16
- package/src/components/ControlSection/index.ts +5 -0
- package/src/components/FieldEditor/FieldEditor.stories.tsx +1 -1
- package/src/components/FieldEditor/FieldEditor.test.tsx +49 -0
- package/src/components/FieldEditor/FieldEditor.tsx +3 -2
- package/src/components/Form/ArrayField.tsx +63 -50
- package/src/components/Form/Defaults.tsx +77 -48
- package/src/components/Form/Form.stories.tsx +134 -109
- package/src/components/Form/Form.tsx +8 -5
- package/src/components/Form/FormActions.tsx +15 -18
- package/src/components/Form/FormContent.tsx +116 -46
- package/src/components/Form/FormContext.tsx +24 -10
- package/src/components/Form/Input.tsx +4 -10
- package/src/components/Form/RefField.tsx +217 -0
- package/src/components/Form/custom/GeoPointInput.tsx +2 -2
- package/src/components/Form/custom/SelectOptionsInput.stories.tsx +6 -5
- package/src/components/Form/custom/SelectOptionsInput.tsx +2 -2
- package/src/components/Form/factory.tsx +1 -1
- package/src/components/Form/index.ts +0 -1
- package/src/components/ViewEditor/ViewEditor.stories.tsx +19 -9
- package/src/components/ViewEditor/ViewEditor.tsx +65 -35
- package/src/components/deprecated/Form.stories.tsx +6 -6
- package/src/components/deprecated/Form.tsx +6 -6
- package/src/components/deprecated/FormInput.tsx +1 -1
- package/src/components/index.ts +1 -0
- package/src/components/testing.tsx +5 -9
- package/src/hooks/index.ts +3 -1
- package/src/hooks/useForm.ts +4 -3
- package/src/hooks/useQueryRefOptions.ts +53 -0
- package/src/hooks/useRefQueryLookupHandler.ts +39 -0
- package/src/translations.ts +9 -2
- package/src/util/formatting.ts +15 -3
- package/src/util/index.ts +3 -1
- package/src/util/refs.ts +38 -0
- package/src/util/schema.ts +14 -0
- package/src/vitest-setup.ts +11 -0
- package/dist/types/src/components/ControlSection.d.ts.map +0 -1
|
@@ -5,36 +5,40 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
|
+
import { Schema } from 'effect';
|
|
8
9
|
import React, { useCallback, useState } from 'react';
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
+
import { ContactType } from '@dxos/client/testing';
|
|
12
|
+
import { type BaseObject, Expando, Format, getDXN, Ref, type TypeAnnotation } from '@dxos/echo-schema';
|
|
13
|
+
import { live } from '@dxos/live-object';
|
|
14
|
+
import { withSurfaceVariantsLayout } from '@dxos/react-ui/testing';
|
|
11
15
|
import { Testing } from '@dxos/schema/testing';
|
|
12
|
-
import {
|
|
16
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
13
17
|
|
|
14
18
|
import { SelectInput } from './Defaults';
|
|
15
19
|
import { Form, type FormProps } from './Form';
|
|
16
20
|
import translations from '../../translations';
|
|
17
21
|
import { TestLayout, TestPanel } from '../testing';
|
|
18
22
|
|
|
19
|
-
const AddressSchema =
|
|
20
|
-
street:
|
|
21
|
-
city:
|
|
22
|
-
zip:
|
|
23
|
-
location:
|
|
24
|
-
}).annotations({
|
|
23
|
+
const AddressSchema = Schema.Struct({
|
|
24
|
+
street: Schema.optional(Schema.String.annotations({ title: 'Street' })),
|
|
25
|
+
city: Schema.optional(Schema.String.annotations({ title: 'City' })),
|
|
26
|
+
zip: Schema.optional(Schema.String.pipe(Schema.pattern(/^\d{5}(-\d{4})?$/)).annotations({ title: 'ZIP' })),
|
|
27
|
+
location: Schema.optional(Format.GeoPoint.annotations({ title: 'Location' })),
|
|
28
|
+
}).annotations({ title: 'Address' });
|
|
25
29
|
|
|
26
30
|
// TODO(burdon): Translations?
|
|
27
|
-
const TestSchema =
|
|
28
|
-
name:
|
|
29
|
-
active:
|
|
30
|
-
rank:
|
|
31
|
-
website:
|
|
32
|
-
address:
|
|
33
|
-
}).pipe(
|
|
31
|
+
const TestSchema = Schema.Struct({
|
|
32
|
+
name: Schema.optional(Schema.String.annotations({ title: 'Name' })),
|
|
33
|
+
active: Schema.optional(Schema.Boolean.annotations({ title: 'Active' })),
|
|
34
|
+
rank: Schema.optional(Schema.Number.annotations({ title: 'Rank' })),
|
|
35
|
+
website: Schema.optional(Format.URL.annotations({ title: 'Website' })),
|
|
36
|
+
address: Schema.optional(AddressSchema),
|
|
37
|
+
}).pipe(Schema.mutable);
|
|
34
38
|
|
|
35
|
-
type TestType =
|
|
39
|
+
type TestType = Schema.Schema.Type<typeof TestSchema>;
|
|
36
40
|
|
|
37
|
-
type StoryProps<T extends BaseObject> = { schema:
|
|
41
|
+
type StoryProps<T extends BaseObject> = { schema: Schema.Schema<T> } & FormProps<T>;
|
|
38
42
|
|
|
39
43
|
const DefaultStory = <T extends BaseObject>({ schema, values: initialValues, ...props }: StoryProps<T>) => {
|
|
40
44
|
const [values, setValues] = useState(initialValues);
|
|
@@ -42,6 +46,15 @@ const DefaultStory = <T extends BaseObject>({ schema, values: initialValues, ...
|
|
|
42
46
|
setValues(values);
|
|
43
47
|
}, []);
|
|
44
48
|
|
|
49
|
+
return <Form<T> schema={schema} values={values} onSave={handleSave} {...props} />;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const DebugStory = <T extends BaseObject>({ schema, values: initialValues, ...props }: StoryProps<T>) => {
|
|
53
|
+
const [values, setValues] = useState(initialValues);
|
|
54
|
+
const handleSave = useCallback<NonNullable<FormProps<T>['onSave']>>((values) => {
|
|
55
|
+
setValues(values);
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
45
58
|
return (
|
|
46
59
|
<TestLayout json={{ values, schema: schema.ast.toJSON() }}>
|
|
47
60
|
<TestPanel>
|
|
@@ -54,11 +67,17 @@ const DefaultStory = <T extends BaseObject>({ schema, values: initialValues, ...
|
|
|
54
67
|
const meta: Meta<StoryProps<any>> = {
|
|
55
68
|
title: 'ui/react-ui-form/Form',
|
|
56
69
|
component: Form,
|
|
57
|
-
render:
|
|
58
|
-
decorators: [
|
|
70
|
+
render: DebugStory,
|
|
71
|
+
decorators: [withTheme],
|
|
59
72
|
parameters: {
|
|
60
73
|
translations,
|
|
61
74
|
},
|
|
75
|
+
argTypes: {
|
|
76
|
+
readonly: {
|
|
77
|
+
control: 'boolean',
|
|
78
|
+
description: 'Readonly',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
62
81
|
};
|
|
63
82
|
|
|
64
83
|
export default meta;
|
|
@@ -66,6 +85,8 @@ export default meta;
|
|
|
66
85
|
type Story<T extends BaseObject> = StoryObj<StoryProps<T>>;
|
|
67
86
|
|
|
68
87
|
export const Default: Story<TestType> = {
|
|
88
|
+
render: DefaultStory,
|
|
89
|
+
decorators: [withSurfaceVariantsLayout(), withTheme],
|
|
69
90
|
args: {
|
|
70
91
|
schema: TestSchema,
|
|
71
92
|
values: {
|
|
@@ -75,27 +96,30 @@ export const Default: Story<TestType> = {
|
|
|
75
96
|
zip: '11205',
|
|
76
97
|
},
|
|
77
98
|
},
|
|
99
|
+
readonly: false,
|
|
78
100
|
},
|
|
79
101
|
};
|
|
80
102
|
|
|
81
|
-
export const
|
|
103
|
+
export const Organization: Story<Testing.Organization> = {
|
|
82
104
|
args: {
|
|
83
|
-
schema: Testing.
|
|
105
|
+
schema: Testing.OrganizationSchema,
|
|
84
106
|
values: {
|
|
85
107
|
name: 'DXOS',
|
|
86
|
-
|
|
108
|
+
website: 'https://dxos.org',
|
|
87
109
|
},
|
|
110
|
+
readonly: false,
|
|
88
111
|
},
|
|
89
112
|
};
|
|
90
113
|
|
|
91
|
-
export const
|
|
114
|
+
export const OrganizationAutoSave: Story<Testing.Organization> = {
|
|
92
115
|
args: {
|
|
93
|
-
schema: Testing.
|
|
116
|
+
schema: Testing.OrganizationSchema,
|
|
94
117
|
values: {
|
|
95
118
|
name: 'DXOS',
|
|
96
|
-
|
|
119
|
+
website: 'https://dxos.org',
|
|
97
120
|
},
|
|
98
121
|
autoSave: true,
|
|
122
|
+
readonly: false,
|
|
99
123
|
},
|
|
100
124
|
};
|
|
101
125
|
|
|
@@ -111,96 +135,58 @@ export const OrgAutoSave: Story<Testing.Org> = {
|
|
|
111
135
|
// },
|
|
112
136
|
// };
|
|
113
137
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
S.Union(
|
|
121
|
-
S.Struct({
|
|
122
|
-
type: S.Literal('circle').annotations({ [AST.TitleAnnotationId]: 'Type' }),
|
|
123
|
-
radius: S.optional(S.Number.annotations({ [AST.TitleAnnotationId]: 'Radius' })),
|
|
138
|
+
const ShapeSchema = Schema.Struct({
|
|
139
|
+
shape: Schema.optional(
|
|
140
|
+
Schema.Union(
|
|
141
|
+
Schema.Struct({
|
|
142
|
+
type: Schema.Literal('circle').annotations({ title: 'Type' }),
|
|
143
|
+
radius: Schema.optional(Schema.Number.annotations({ title: 'Radius' })),
|
|
124
144
|
}),
|
|
125
|
-
|
|
126
|
-
type:
|
|
127
|
-
size:
|
|
145
|
+
Schema.Struct({
|
|
146
|
+
type: Schema.Literal('square').annotations({ title: 'Type' }),
|
|
147
|
+
size: Schema.optional(Schema.Number.pipe(Schema.nonNegative()).annotations({ title: 'Size' })),
|
|
128
148
|
}),
|
|
129
|
-
).annotations({
|
|
149
|
+
).annotations({ title: 'Shape' }),
|
|
130
150
|
),
|
|
131
|
-
}).pipe(
|
|
132
|
-
|
|
133
|
-
type ShapeType = S.Schema.Type<typeof ShapeSchema>;
|
|
151
|
+
}).pipe(Schema.mutable);
|
|
134
152
|
|
|
135
|
-
type
|
|
153
|
+
type ShapeType = Schema.Schema.Type<typeof ShapeSchema>;
|
|
136
154
|
|
|
137
|
-
const
|
|
138
|
-
const [values, setValues] = useState(initialValues);
|
|
139
|
-
const handleSave = useCallback<NonNullable<FormProps<ShapeType>['onSave']>>((values) => {
|
|
140
|
-
setValues(values);
|
|
141
|
-
}, []);
|
|
142
|
-
|
|
143
|
-
return (
|
|
144
|
-
<TestLayout json={{ values, schema: ShapeSchema.ast.toJSON() }}>
|
|
145
|
-
<TestPanel>
|
|
146
|
-
<Form<ShapeType>
|
|
147
|
-
schema={ShapeSchema}
|
|
148
|
-
values={values}
|
|
149
|
-
onSave={handleSave}
|
|
150
|
-
Custom={{
|
|
151
|
-
['shape.type' as const]: (props) => (
|
|
152
|
-
<SelectInput
|
|
153
|
-
{...props}
|
|
154
|
-
options={['circle', 'square'].map((value) => ({
|
|
155
|
-
value,
|
|
156
|
-
label: value,
|
|
157
|
-
}))}
|
|
158
|
-
/>
|
|
159
|
-
),
|
|
160
|
-
}}
|
|
161
|
-
/>
|
|
162
|
-
</TestPanel>
|
|
163
|
-
</TestLayout>
|
|
164
|
-
);
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
export const DiscriminatedShape: StoryObj<DiscriminatedUnionStoryProps> = {
|
|
168
|
-
render: DiscriminatedUnionStory,
|
|
155
|
+
export const DiscriminatedShape: Story<ShapeType> = {
|
|
169
156
|
args: {
|
|
157
|
+
schema: ShapeSchema,
|
|
158
|
+
readonly: false,
|
|
170
159
|
values: {
|
|
171
160
|
shape: {
|
|
172
161
|
type: 'circle',
|
|
173
162
|
radius: 5,
|
|
174
163
|
},
|
|
175
164
|
},
|
|
165
|
+
Custom: {
|
|
166
|
+
['shape.type' as const]: (props) => (
|
|
167
|
+
<SelectInput
|
|
168
|
+
{...props}
|
|
169
|
+
options={['circle', 'square'].map((value) => ({
|
|
170
|
+
value,
|
|
171
|
+
label: value,
|
|
172
|
+
}))}
|
|
173
|
+
/>
|
|
174
|
+
),
|
|
175
|
+
},
|
|
176
176
|
},
|
|
177
177
|
};
|
|
178
178
|
|
|
179
|
-
const ArraysSchema =
|
|
180
|
-
names:
|
|
181
|
-
addresses:
|
|
182
|
-
}).pipe(
|
|
183
|
-
|
|
184
|
-
type ArraysType = S.Schema.Type<typeof ArraysSchema>;
|
|
179
|
+
const ArraysSchema = Schema.Struct({
|
|
180
|
+
names: Schema.Array(Schema.String.pipe(Schema.nonEmptyString())),
|
|
181
|
+
addresses: Schema.Array(AddressSchema),
|
|
182
|
+
}).pipe(Schema.mutable);
|
|
185
183
|
|
|
186
|
-
|
|
187
|
-
const [values, setValues] = useState(initialValues);
|
|
188
|
-
const handleSave = useCallback<NonNullable<FormProps<ArraysType>['onSave']>>((values) => {
|
|
189
|
-
setValues(values);
|
|
190
|
-
}, []);
|
|
191
|
-
|
|
192
|
-
return (
|
|
193
|
-
<TestLayout json={{ values, schema: ArraysSchema.ast.toJSON() }}>
|
|
194
|
-
<TestPanel>
|
|
195
|
-
<Form<ArraysType> schema={ArraysSchema} values={values} onSave={handleSave} />
|
|
196
|
-
</TestPanel>
|
|
197
|
-
</TestLayout>
|
|
198
|
-
);
|
|
199
|
-
};
|
|
184
|
+
type ArraysType = Schema.Schema.Type<typeof ArraysSchema>;
|
|
200
185
|
|
|
201
186
|
export const Arrays: StoryObj<FormProps<ArraysType>> = {
|
|
202
|
-
render: ArraysStory,
|
|
203
187
|
args: {
|
|
188
|
+
schema: ArraysSchema,
|
|
189
|
+
readonly: false,
|
|
204
190
|
values: {
|
|
205
191
|
names: ['Alice', 'Bob'],
|
|
206
192
|
addresses: [],
|
|
@@ -208,34 +194,73 @@ export const Arrays: StoryObj<FormProps<ArraysType>> = {
|
|
|
208
194
|
},
|
|
209
195
|
};
|
|
210
196
|
|
|
211
|
-
const ColorSchema =
|
|
212
|
-
color:
|
|
213
|
-
|
|
197
|
+
const ColorSchema = Schema.Struct({
|
|
198
|
+
color: Schema.Union(Schema.Literal('red'), Schema.Literal('green'), Schema.Literal('blue')).annotations({
|
|
199
|
+
title: 'Color',
|
|
214
200
|
}),
|
|
215
|
-
}).pipe(
|
|
201
|
+
}).pipe(Schema.mutable);
|
|
202
|
+
|
|
203
|
+
type ColorType = Schema.Schema.Type<typeof ColorSchema>;
|
|
204
|
+
|
|
205
|
+
export const Enum: StoryObj<FormProps<ColorType>> = {
|
|
206
|
+
args: {
|
|
207
|
+
schema: ColorSchema,
|
|
208
|
+
readonly: false,
|
|
209
|
+
values: {
|
|
210
|
+
color: 'red',
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
216
214
|
|
|
217
|
-
|
|
215
|
+
const RefSchema = Schema.Struct({
|
|
216
|
+
contact: Ref(ContactType).annotations({ title: 'Contact Reference' }),
|
|
217
|
+
optionalContact: Schema.optional(Ref(ContactType).annotations({ title: 'Optional Contact Reference' })),
|
|
218
|
+
refArray: Schema.optional(Schema.Array(Ref(ContactType))),
|
|
219
|
+
unknownExpando: Schema.optional(Ref(Expando).annotations({ title: 'Optional Ref to an Expando (DXN Input)' })),
|
|
220
|
+
});
|
|
218
221
|
|
|
219
|
-
const
|
|
222
|
+
const contact1 = live(ContactType, { identifiers: [] });
|
|
223
|
+
const contact2 = live(ContactType, { identifiers: [] });
|
|
224
|
+
|
|
225
|
+
const RefStory = ({ values: initialValues, readonly }: FormProps<any>) => {
|
|
220
226
|
const [values, setValues] = useState(initialValues);
|
|
221
|
-
const handleSave = useCallback<NonNullable<FormProps<
|
|
227
|
+
const handleSave = useCallback<NonNullable<FormProps<any>['onSave']>>((values) => {
|
|
222
228
|
setValues(values);
|
|
223
229
|
}, []);
|
|
224
230
|
|
|
231
|
+
const onQueryRefOptions = useCallback((typeInfo: TypeAnnotation) => {
|
|
232
|
+
switch (typeInfo.typename) {
|
|
233
|
+
case ContactType.typename:
|
|
234
|
+
return [
|
|
235
|
+
{ dxn: getDXN(contact1)!, label: 'John Coltraine' },
|
|
236
|
+
{ dxn: getDXN(contact2)!, label: 'Erykah Badu' },
|
|
237
|
+
];
|
|
238
|
+
default:
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
}, []);
|
|
242
|
+
|
|
225
243
|
return (
|
|
226
|
-
<TestLayout json={{ values, schema:
|
|
244
|
+
<TestLayout json={{ values, schema: RefSchema.ast.toJSON() }}>
|
|
227
245
|
<TestPanel>
|
|
228
|
-
<Form
|
|
246
|
+
<Form
|
|
247
|
+
schema={RefSchema}
|
|
248
|
+
values={values}
|
|
249
|
+
readonly={readonly}
|
|
250
|
+
onSave={handleSave}
|
|
251
|
+
onQueryRefOptions={onQueryRefOptions}
|
|
252
|
+
/>
|
|
229
253
|
</TestPanel>
|
|
230
254
|
</TestLayout>
|
|
231
255
|
);
|
|
232
256
|
};
|
|
233
257
|
|
|
234
|
-
export const
|
|
235
|
-
render:
|
|
258
|
+
export const Refs: StoryObj<FormProps<ContactType>> = {
|
|
259
|
+
render: RefStory,
|
|
236
260
|
args: {
|
|
261
|
+
readonly: false,
|
|
237
262
|
values: {
|
|
238
|
-
|
|
239
|
-
},
|
|
263
|
+
refArray: [Ref.make(contact1), Ref.make(contact2)],
|
|
264
|
+
} as any,
|
|
240
265
|
},
|
|
241
266
|
};
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { type Schema } from 'effect';
|
|
5
6
|
import React, { type ReactElement, useEffect, useMemo, useRef } from 'react';
|
|
6
7
|
|
|
7
|
-
import { type BaseObject, type
|
|
8
|
+
import { type BaseObject, type PropertyKey } from '@dxos/echo-schema';
|
|
8
9
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
9
10
|
import { mx } from '@dxos/react-ui-theme';
|
|
10
11
|
import { type SchemaProperty } from '@dxos/schema';
|
|
@@ -13,13 +14,13 @@ import { FormActions } from './FormActions';
|
|
|
13
14
|
import { FormFields } from './FormContent';
|
|
14
15
|
import { FormProvider } from './FormContext';
|
|
15
16
|
import { type InputProps, type InputComponent } from './Input';
|
|
16
|
-
import { type FormOptions } from '../../hooks';
|
|
17
|
+
import { type FormOptions, type QueryRefOptions } from '../../hooks';
|
|
17
18
|
|
|
18
19
|
export type PropsFilter<T extends BaseObject> = (props: SchemaProperty<T>[]) => SchemaProperty<T>[];
|
|
19
20
|
|
|
20
21
|
export type ComponentLookup = (args: {
|
|
21
22
|
prop: string;
|
|
22
|
-
schema:
|
|
23
|
+
schema: Schema.Schema<any>;
|
|
23
24
|
inputProps: InputProps;
|
|
24
25
|
}) => ReactElement | undefined;
|
|
25
26
|
|
|
@@ -41,6 +42,7 @@ export type FormProps<T extends BaseObject> = ThemedClassName<
|
|
|
41
42
|
autoSave?: boolean;
|
|
42
43
|
testId?: string;
|
|
43
44
|
onCancel?: () => void;
|
|
45
|
+
onQueryRefOptions?: QueryRefOptions;
|
|
44
46
|
lookupComponent?: ComponentLookup;
|
|
45
47
|
/**
|
|
46
48
|
* Map of custom renderers for specific properties.
|
|
@@ -65,6 +67,7 @@ export const Form = <T extends BaseObject>({
|
|
|
65
67
|
onValidate,
|
|
66
68
|
onSave,
|
|
67
69
|
onCancel,
|
|
70
|
+
onQueryRefOptions,
|
|
68
71
|
lookupComponent,
|
|
69
72
|
Custom,
|
|
70
73
|
}: FormProps<T>) => {
|
|
@@ -92,14 +95,14 @@ export const Form = <T extends BaseObject>({
|
|
|
92
95
|
onValid={onValid}
|
|
93
96
|
onSave={onSave}
|
|
94
97
|
>
|
|
95
|
-
{
|
|
96
|
-
<div ref={formRef} role='none' className={mx('p-2', classNames)} data-testid={testId}>
|
|
98
|
+
<div ref={formRef} role='none' className={mx('flex flex-col gap-4', classNames)} data-testid={testId}>
|
|
97
99
|
<FormFields
|
|
98
100
|
schema={schema}
|
|
99
101
|
path={path}
|
|
100
102
|
readonly={readonly}
|
|
101
103
|
filter={filter}
|
|
102
104
|
sort={sort}
|
|
105
|
+
onQueryRefOptions={onQueryRefOptions}
|
|
103
106
|
lookupComponent={lookupComponent}
|
|
104
107
|
Custom={Custom}
|
|
105
108
|
/>
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import { IconButton, useTranslation } from '@dxos/react-ui';
|
|
8
|
-
import { mx } from '@dxos/react-ui-theme';
|
|
9
8
|
|
|
10
9
|
import { useFormContext } from './FormContext';
|
|
11
10
|
import { translationKey } from '../../translations';
|
|
@@ -15,27 +14,25 @@ export type FormActionsProps = {
|
|
|
15
14
|
onCancel?: () => void;
|
|
16
15
|
};
|
|
17
16
|
|
|
18
|
-
export const FormActions = ({
|
|
17
|
+
export const FormActions = ({ readonly, onCancel }: FormActionsProps) => {
|
|
19
18
|
const { t } = useTranslation(translationKey);
|
|
20
19
|
const { canSave, handleSave } = useFormContext();
|
|
21
20
|
|
|
22
21
|
return (
|
|
23
|
-
<div role='none' className='
|
|
24
|
-
|
|
25
|
-
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
)}
|
|
38
|
-
</div>
|
|
22
|
+
<div role='none' className='flex [&_button]:grow gap-1'>
|
|
23
|
+
{onCancel && !readonly && (
|
|
24
|
+
<IconButton data-testid='cancel-button' icon='ph--x--regular' label={t('button cancel')} onClick={onCancel} />
|
|
25
|
+
)}
|
|
26
|
+
{handleSave && (
|
|
27
|
+
<IconButton
|
|
28
|
+
type='submit'
|
|
29
|
+
data-testid='save-button'
|
|
30
|
+
disabled={!canSave}
|
|
31
|
+
icon='ph--check--regular'
|
|
32
|
+
label={t('button save')}
|
|
33
|
+
onClick={handleSave}
|
|
34
|
+
/>
|
|
35
|
+
)}
|
|
39
36
|
</div>
|
|
40
37
|
);
|
|
41
38
|
};
|