@proteinjs/ui 1.0.1
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/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/dist/generated/index.d.ts +17 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/index.js +40 -0
- package/dist/generated/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/src/container/AccountIconButton.d.ts +20 -0
- package/dist/src/container/AccountIconButton.d.ts.map +1 -0
- package/dist/src/container/AccountIconButton.js +112 -0
- package/dist/src/container/AccountIconButton.js.map +1 -0
- package/dist/src/container/NavMenu.d.ts +16 -0
- package/dist/src/container/NavMenu.d.ts.map +1 -0
- package/dist/src/container/NavMenu.js +51 -0
- package/dist/src/container/NavMenu.js.map +1 -0
- package/dist/src/container/PageContainer.d.ts +31 -0
- package/dist/src/container/PageContainer.d.ts.map +1 -0
- package/dist/src/container/PageContainer.js +75 -0
- package/dist/src/container/PageContainer.js.map +1 -0
- package/dist/src/form/Field.d.ts +34 -0
- package/dist/src/form/Field.d.ts.map +1 -0
- package/dist/src/form/Field.js +29 -0
- package/dist/src/form/Field.js.map +1 -0
- package/dist/src/form/Form.d.ts +46 -0
- package/dist/src/form/Form.d.ts.map +1 -0
- package/dist/src/form/Form.js +377 -0
- package/dist/src/form/Form.js.map +1 -0
- package/dist/src/form/FormButton.d.ts +28 -0
- package/dist/src/form/FormButton.d.ts.map +1 -0
- package/dist/src/form/FormButton.js +18 -0
- package/dist/src/form/FormButton.js.map +1 -0
- package/dist/src/form/FunctionalForm.d.ts +1 -0
- package/dist/src/form/FunctionalForm.d.ts.map +1 -0
- package/dist/src/form/FunctionalForm.js +328 -0
- package/dist/src/form/FunctionalForm.js.map +1 -0
- package/dist/src/form/container/FormPage.d.ts +4 -0
- package/dist/src/form/container/FormPage.d.ts.map +1 -0
- package/dist/src/form/container/FormPage.js +29 -0
- package/dist/src/form/container/FormPage.js.map +1 -0
- package/dist/src/form/container/FormPaper.d.ts +4 -0
- package/dist/src/form/container/FormPaper.d.ts.map +1 -0
- package/dist/src/form/container/FormPaper.js +27 -0
- package/dist/src/form/container/FormPaper.js.map +1 -0
- package/dist/src/form/fields/TextField.d.ts +6 -0
- package/dist/src/form/fields/TextField.d.ts.map +1 -0
- package/dist/src/form/fields/TextField.js +70 -0
- package/dist/src/form/fields/TextField.js.map +1 -0
- package/dist/src/list/NestedList.d.ts +14 -0
- package/dist/src/list/NestedList.d.ts.map +1 -0
- package/dist/src/list/NestedList.js +44 -0
- package/dist/src/list/NestedList.js.map +1 -0
- package/dist/src/router/Page.d.ts +28 -0
- package/dist/src/router/Page.d.ts.map +1 -0
- package/dist/src/router/Page.js +7 -0
- package/dist/src/router/Page.js.map +1 -0
- package/dist/src/router/Router.d.ts +14 -0
- package/dist/src/router/Router.d.ts.map +1 -0
- package/dist/src/router/Router.js +66 -0
- package/dist/src/router/Router.js.map +1 -0
- package/dist/src/router/createUrlParams.d.ts +4 -0
- package/dist/src/router/createUrlParams.d.ts.map +1 -0
- package/dist/src/router/createUrlParams.js +19 -0
- package/dist/src/router/createUrlParams.js.map +1 -0
- package/dist/src/router/withRouter.d.ts +7 -0
- package/dist/src/router/withRouter.d.ts.map +1 -0
- package/dist/src/router/withRouter.js +28 -0
- package/dist/src/router/withRouter.js.map +1 -0
- package/dist/src/table/Table.d.ts +14 -0
- package/dist/src/table/Table.d.ts.map +1 -0
- package/dist/src/table/Table.js +170 -0
- package/dist/src/table/Table.js.map +1 -0
- package/dist/src/table/TableButton.d.ts +12 -0
- package/dist/src/table/TableButton.d.ts.map +1 -0
- package/dist/src/table/TableButton.js +3 -0
- package/dist/src/table/TableButton.js.map +1 -0
- package/dist/src/table/TableLoader.d.ts +16 -0
- package/dist/src/table/TableLoader.d.ts.map +1 -0
- package/dist/src/table/TableLoader.js +57 -0
- package/dist/src/table/TableLoader.js.map +1 -0
- package/dist/src/table/TableToolbar.d.ts +10 -0
- package/dist/src/table/TableToolbar.d.ts.map +1 -0
- package/dist/src/table/TableToolbar.js +59 -0
- package/dist/src/table/TableToolbar.js.map +1 -0
- package/generated/index.ts +37 -0
- package/index.ts +20 -0
- package/jest.config.js +18 -0
- package/package.json +52 -0
- package/src/container/AccountIconButton.tsx +118 -0
- package/src/container/NavMenu.tsx +91 -0
- package/src/container/PageContainer.tsx +108 -0
- package/src/form/Field.tsx +51 -0
- package/src/form/Form.tsx +405 -0
- package/src/form/FormButton.tsx +30 -0
- package/src/form/FunctionalForm.tsx +364 -0
- package/src/form/container/FormPage.tsx +23 -0
- package/src/form/container/FormPaper.tsx +16 -0
- package/src/form/fields/TextField.tsx +71 -0
- package/src/list/NestedList.tsx +61 -0
- package/src/router/Page.ts +28 -0
- package/src/router/Router.tsx +76 -0
- package/src/router/createUrlParams.ts +16 -0
- package/src/router/withRouter.tsx +21 -0
- package/src/table/Table.tsx +195 -0
- package/src/table/TableButton.ts +12 -0
- package/src/table/TableLoader.ts +19 -0
- package/src/table/TableToolbar.tsx +99 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Button, Container, Grid, IconButton, Typography, LinearProgress } from '@mui/material';
|
|
3
|
+
import queryString from 'query-string';
|
|
4
|
+
import { Field, FieldComponent, Fields } from './Field';
|
|
5
|
+
import { FormButton, FormButtons } from './FormButton';
|
|
6
|
+
import { withRouter, WithRouterProps } from '../router/withRouter';
|
|
7
|
+
|
|
8
|
+
export type FormProps<F extends Fields, B extends FormButtons<F>> = {
|
|
9
|
+
name?: string,
|
|
10
|
+
documentation?: React.ComponentType,
|
|
11
|
+
createFields: () => F,
|
|
12
|
+
fieldLayout?: (keyof F)[]|(keyof F)[][],
|
|
13
|
+
buttons: B,
|
|
14
|
+
onLoad?: (fields: F, buttons: B) => Promise<void>,
|
|
15
|
+
onLoadProgressMessage?: string
|
|
16
|
+
} & Partial<WithRouterProps>
|
|
17
|
+
|
|
18
|
+
export type FormState<F extends Fields> = {
|
|
19
|
+
status?: { message?: string, isError?: boolean },
|
|
20
|
+
fields: F,
|
|
21
|
+
progress?: { visible?: boolean, message?: string },
|
|
22
|
+
onLoadExecuted?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class FormComponent<F extends Fields, B extends FormButtons<F>> extends React.Component<FormProps<F, B>, FormState<F>> {
|
|
26
|
+
constructor(props: FormProps<F, B>) {
|
|
27
|
+
super(props);
|
|
28
|
+
this.state = { fields: props.createFields() };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
componentDidMount() {
|
|
32
|
+
if (this.state.onLoadExecuted)
|
|
33
|
+
return;
|
|
34
|
+
|
|
35
|
+
this.onLoad();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private async onLoad() {
|
|
39
|
+
this.setState({ progress: { visible: true, message: this.props.onLoadProgressMessage }});
|
|
40
|
+
const newFields = this.props.createFields();
|
|
41
|
+
try {
|
|
42
|
+
for (const fieldPropertyName in newFields) {
|
|
43
|
+
const field = newFields[fieldPropertyName].field;
|
|
44
|
+
if (!field)
|
|
45
|
+
continue;
|
|
46
|
+
|
|
47
|
+
if (field.onLoad)
|
|
48
|
+
await field.onLoad(newFields);
|
|
49
|
+
|
|
50
|
+
if (!field.accessibility)
|
|
51
|
+
field.accessibility = {};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (this.props.onLoad)
|
|
55
|
+
await this.props.onLoad(newFields, this.props.buttons);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error(`Failed while running onLoad functions`, error);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.setState({
|
|
61
|
+
status: {},
|
|
62
|
+
fields: newFields,
|
|
63
|
+
progress: { visible: false },
|
|
64
|
+
onLoadExecuted: true
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private async onChange(field: Field<any, any>, value: any, setFieldStatus: (message: string, isError: boolean) => void) {
|
|
69
|
+
field.value = value;
|
|
70
|
+
if (field.onChange) {
|
|
71
|
+
try {
|
|
72
|
+
await field.onChange(field.value, this.state.fields, setFieldStatus);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error(`Failed while running onChange for field: ${field.name}`, error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
this.setState(this.state);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private async onClick(button: FormButton<any>) {
|
|
82
|
+
if (button.onClick) {
|
|
83
|
+
this.setState({ progress: { visible: true, message: button.progressMessage ? button.progressMessage(this.state.fields) : undefined }});
|
|
84
|
+
try {
|
|
85
|
+
const successMessage = await button.onClick(this.state.fields, this.props.buttons);
|
|
86
|
+
if (successMessage)
|
|
87
|
+
this.setState({ status: { message: successMessage, isError: false}});
|
|
88
|
+
} catch (error: any) {
|
|
89
|
+
this.setState({ status: { message: error.message, isError: true}});
|
|
90
|
+
console.error(`Error when clicking button: ${button.name}`, error);
|
|
91
|
+
}
|
|
92
|
+
this.setState({ progress: { visible: false }});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (button.redirect) {
|
|
96
|
+
const redirect = await button.redirect(this.state.fields, this.props.buttons);
|
|
97
|
+
let path = redirect.path;
|
|
98
|
+
if (redirect.props)
|
|
99
|
+
path += `?${queryString.stringify(redirect.props)}`;
|
|
100
|
+
|
|
101
|
+
if (this.props.navigate)
|
|
102
|
+
this.props.navigate(path);
|
|
103
|
+
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (button.clearFormOnClick)
|
|
108
|
+
await this.onLoad();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
render() {
|
|
112
|
+
return (
|
|
113
|
+
<Container sx={{ padding: 0 }} maxWidth={this.getContainerMaxWidth()}>
|
|
114
|
+
<form autoComplete='off'>
|
|
115
|
+
<Grid container>
|
|
116
|
+
{this.Title()}
|
|
117
|
+
{this.Documentation()}
|
|
118
|
+
{this.Status()}
|
|
119
|
+
{this.Fields()}
|
|
120
|
+
{this.Progress()}
|
|
121
|
+
{this.Buttons()}
|
|
122
|
+
</Grid>
|
|
123
|
+
</form>
|
|
124
|
+
</Container>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private getContainerMaxWidth(): 'xs'|'sm' {
|
|
129
|
+
if (this.props.fieldLayout) { // TODO validate that fields are not hidden
|
|
130
|
+
if (this.props.fieldLayout.length < 2)
|
|
131
|
+
return 'xs';
|
|
132
|
+
|
|
133
|
+
for (const row of this.props.fieldLayout) {
|
|
134
|
+
if ((row as string[]).length > 1)
|
|
135
|
+
return 'sm';
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
const rows: boolean[] = [];
|
|
139
|
+
for (const fieldPropertyName in this.state.fields) {
|
|
140
|
+
const field = this.state.fields[fieldPropertyName].field;
|
|
141
|
+
if (field.accessibility?.hidden)
|
|
142
|
+
continue;
|
|
143
|
+
|
|
144
|
+
if (!field.layout)
|
|
145
|
+
continue;
|
|
146
|
+
|
|
147
|
+
if (rows[field.layout.row])
|
|
148
|
+
return 'sm';
|
|
149
|
+
|
|
150
|
+
rows[field.layout.row] = true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return 'xs';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private Title() {
|
|
158
|
+
if (!this.props.name)
|
|
159
|
+
return null;
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<Grid
|
|
163
|
+
container
|
|
164
|
+
xs={12}
|
|
165
|
+
justifyContent='flex-start'
|
|
166
|
+
alignItems='flex-start'
|
|
167
|
+
sx={(theme) => ({
|
|
168
|
+
marginTop: theme.spacing(1),
|
|
169
|
+
marginBottom: theme.spacing(3),
|
|
170
|
+
})}
|
|
171
|
+
>
|
|
172
|
+
<Typography
|
|
173
|
+
variant='h5'
|
|
174
|
+
>
|
|
175
|
+
{this.props.name}
|
|
176
|
+
</Typography>
|
|
177
|
+
</Grid>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private Documentation() {
|
|
182
|
+
if (!this.props.documentation)
|
|
183
|
+
return null;
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<Grid
|
|
187
|
+
container
|
|
188
|
+
xs={12}
|
|
189
|
+
justifyContent='flex-start'
|
|
190
|
+
alignItems='flex-start'
|
|
191
|
+
>
|
|
192
|
+
<Container>
|
|
193
|
+
<this.props.documentation />
|
|
194
|
+
</Container>
|
|
195
|
+
</Grid>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private Status() {
|
|
200
|
+
if (!this.state.status || !this.state.status.message)
|
|
201
|
+
return null;
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<Grid
|
|
205
|
+
container
|
|
206
|
+
xs={12}
|
|
207
|
+
>
|
|
208
|
+
<Container
|
|
209
|
+
sx={(theme) => ({
|
|
210
|
+
marginBottom: theme.spacing(1),
|
|
211
|
+
})}
|
|
212
|
+
>
|
|
213
|
+
<Typography
|
|
214
|
+
variant='subtitle1'
|
|
215
|
+
color={this.state.status.isError ? 'error' : 'primary'}
|
|
216
|
+
>
|
|
217
|
+
{this.state.status.message}
|
|
218
|
+
</Typography>
|
|
219
|
+
</Container>
|
|
220
|
+
</Grid>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private Fields() {
|
|
225
|
+
return (
|
|
226
|
+
<Grid
|
|
227
|
+
container
|
|
228
|
+
direction='column'
|
|
229
|
+
xs={12}
|
|
230
|
+
>
|
|
231
|
+
{
|
|
232
|
+
this.getFieldRows().map((fieldComponents, index) => {
|
|
233
|
+
if (!this.isFieldRowVisible(fieldComponents))
|
|
234
|
+
return null;
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<Grid
|
|
238
|
+
container
|
|
239
|
+
spacing={3}
|
|
240
|
+
alignItems='center'
|
|
241
|
+
sx={(theme) => ({
|
|
242
|
+
flexGrow: 1,
|
|
243
|
+
marginBottom: theme.spacing(3),
|
|
244
|
+
})}
|
|
245
|
+
key={index}
|
|
246
|
+
>
|
|
247
|
+
{
|
|
248
|
+
fieldComponents.filter((fieldComponent) => {
|
|
249
|
+
if (fieldComponent.field.accessibility?.hidden)
|
|
250
|
+
return false;
|
|
251
|
+
|
|
252
|
+
return true;
|
|
253
|
+
}).map((fieldComponent) => (
|
|
254
|
+
<Grid item xs key={fieldComponent.field.name}>
|
|
255
|
+
<fieldComponent.component
|
|
256
|
+
field={fieldComponent.field}
|
|
257
|
+
onChange={this.onChange.bind(this)}
|
|
258
|
+
/>
|
|
259
|
+
</Grid>
|
|
260
|
+
))
|
|
261
|
+
}
|
|
262
|
+
</Grid>
|
|
263
|
+
);
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
</Grid>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private getFieldRows(): FieldComponent<any, any>[][] {
|
|
271
|
+
const rows: FieldComponent<any, any>[][] = [];
|
|
272
|
+
if (!this.state.fields)
|
|
273
|
+
return rows;
|
|
274
|
+
|
|
275
|
+
if (this.props.fieldLayout) {
|
|
276
|
+
if (typeof this.props.fieldLayout[0] === 'string') {
|
|
277
|
+
for (let i = 0; i < this.props.fieldLayout.length; i++) {
|
|
278
|
+
const fieldPropertyName = this.props.fieldLayout[i] as string;
|
|
279
|
+
const fieldComponent = this.state.fields[fieldPropertyName];
|
|
280
|
+
fieldComponent.field.layout = { row: i, width: 12 };
|
|
281
|
+
rows.push([fieldComponent]);
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
for (let i = 0; i < this.props.fieldLayout.length; i++) {
|
|
285
|
+
const row = this.props.fieldLayout[i] as string[];
|
|
286
|
+
const columns = row.length;
|
|
287
|
+
if (columns > 6)
|
|
288
|
+
throw new Error(`When using FormProps.fieldLayout, the maximum number of fields per row is 6, provided: ${columns}. For more granular layout control use Field.layout`);
|
|
289
|
+
|
|
290
|
+
const currentRow: FieldComponent<any, any>[] = [];
|
|
291
|
+
for (const fieldPropertyName of row ) {
|
|
292
|
+
const fieldComponent = this.state.fields[fieldPropertyName];
|
|
293
|
+
fieldComponent.field.layout = { row: i, width: columns == 5 ? 2 : 12/columns as 1 };
|
|
294
|
+
currentRow.push(fieldComponent);
|
|
295
|
+
}
|
|
296
|
+
rows.push(currentRow);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
for (const fieldPropertyName in this.state.fields) {
|
|
301
|
+
const fieldComponent: FieldComponent<any, any> = this.state.fields[fieldPropertyName];
|
|
302
|
+
const field = fieldComponent.field;
|
|
303
|
+
if (!field.layout)
|
|
304
|
+
throw new Error(`Unless using FormProps.fieldLayout, Field.layout must be provided; layout not provided for field: ${field.name}`);
|
|
305
|
+
|
|
306
|
+
if (typeof rows[field.layout.row] === 'undefined')
|
|
307
|
+
rows[field.layout.row] = [];
|
|
308
|
+
|
|
309
|
+
if (field.layout.column && rows[field.layout.row].length >= field.layout.column) {
|
|
310
|
+
rows[field.layout.row].splice(field.layout.column - 1, 0, fieldComponent);
|
|
311
|
+
} else
|
|
312
|
+
rows[field.layout.row].push(fieldComponent);
|
|
313
|
+
|
|
314
|
+
const rowWidth = rows[field.layout.row].map((fieldComponent) => fieldComponent.field.layout?.width ? fieldComponent.field.layout.width as number : 0).reduce((accumulator, currentWidth) => accumulator + currentWidth);
|
|
315
|
+
if (rowWidth > 12)
|
|
316
|
+
throw new Error(`Width of row exceeds maximum width (12), row width: ${rowWidth}, row index: ${field.layout.row}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return rows;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private isFieldRowVisible(fieldComponents: FieldComponent<any, any>[]): boolean {
|
|
324
|
+
for (const fieldComponent of fieldComponents) {
|
|
325
|
+
if (!fieldComponent.field.accessibility || !fieldComponent.field.accessibility?.hidden)
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private Progress() {
|
|
333
|
+
if (!this.state.progress || !this.state.progress.visible)
|
|
334
|
+
return null;
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
<Grid
|
|
338
|
+
container
|
|
339
|
+
xs={12}
|
|
340
|
+
justifyContent='center'
|
|
341
|
+
alignItems='center'
|
|
342
|
+
spacing={2}
|
|
343
|
+
>
|
|
344
|
+
<Container>
|
|
345
|
+
<LinearProgress variant='indeterminate' color='primary' />
|
|
346
|
+
</Container>
|
|
347
|
+
</Grid>
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private Buttons() {
|
|
352
|
+
return (
|
|
353
|
+
<Grid
|
|
354
|
+
container
|
|
355
|
+
direction="row"
|
|
356
|
+
justifyContent="flex-end"
|
|
357
|
+
alignItems="center"
|
|
358
|
+
xs={12}
|
|
359
|
+
sx={(theme) => ({
|
|
360
|
+
marginTop: theme.spacing(2),
|
|
361
|
+
marginBottom: theme.spacing(1),
|
|
362
|
+
})}
|
|
363
|
+
>
|
|
364
|
+
{
|
|
365
|
+
Object.keys(this.props.buttons).map((buttonPropertyName) => this.props.buttons[buttonPropertyName]).filter((button) => {
|
|
366
|
+
if (button.accessibility?.hidden)
|
|
367
|
+
return false;
|
|
368
|
+
|
|
369
|
+
return true;
|
|
370
|
+
}).map((button, key) => {
|
|
371
|
+
return (
|
|
372
|
+
<Grid
|
|
373
|
+
key={key}
|
|
374
|
+
sx={(theme) => ({
|
|
375
|
+
marginLeft: theme.spacing(1),
|
|
376
|
+
})}
|
|
377
|
+
>
|
|
378
|
+
{ button.style.icon ?
|
|
379
|
+
<IconButton
|
|
380
|
+
disabled={button.accessibility?.disabled}
|
|
381
|
+
onClick={(event: any) => this.onClick(button)}
|
|
382
|
+
>
|
|
383
|
+
<button.style.icon />
|
|
384
|
+
</IconButton>
|
|
385
|
+
:
|
|
386
|
+
<Button
|
|
387
|
+
color={button.style.color}
|
|
388
|
+
variant={button.style.variant ? button.style.variant : 'contained'}
|
|
389
|
+
disabled={button.accessibility?.disabled}
|
|
390
|
+
onClick={event => this.onClick(button)}
|
|
391
|
+
>
|
|
392
|
+
{button.name}
|
|
393
|
+
</Button>
|
|
394
|
+
}
|
|
395
|
+
</Grid>
|
|
396
|
+
)
|
|
397
|
+
})
|
|
398
|
+
}
|
|
399
|
+
</Grid>
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
type FormType = <F extends Fields, B extends FormButtons<F>>(props: Omit<FormProps<F, B>, 'classes'>) => JSX.Element;
|
|
405
|
+
export const Form = withRouter(FormComponent) as FormType;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Fields } from './Field';
|
|
3
|
+
|
|
4
|
+
export type FormButton<F extends Fields> = {
|
|
5
|
+
name: string,
|
|
6
|
+
accessibility?: {
|
|
7
|
+
disabled?: boolean,
|
|
8
|
+
hidden?: boolean,
|
|
9
|
+
},
|
|
10
|
+
style: {
|
|
11
|
+
color?: 'inherit'|'primary'|'success'|'warning'|'secondary'|'error'|'info',
|
|
12
|
+
variant?: 'text'|'outlined'|'contained',
|
|
13
|
+
icon?: React.ComponentType,
|
|
14
|
+
},
|
|
15
|
+
clearFormOnClick?: boolean,
|
|
16
|
+
redirect?: (fields: F, buttons: FormButtons<F>) => Promise<{ path: string, props?: { [key: string]: any } }>,
|
|
17
|
+
onClick?: (fields: F, buttons: FormButtons<F>) => Promise<string|void>,
|
|
18
|
+
progressMessage?: (fields: F) => string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export abstract class FormButtons<F extends Fields> { [name: string]: FormButton<F> }
|
|
22
|
+
|
|
23
|
+
export const clearButton: FormButton<any> = {
|
|
24
|
+
name: 'Clear',
|
|
25
|
+
style: {
|
|
26
|
+
color: 'primary',
|
|
27
|
+
variant: 'text',
|
|
28
|
+
},
|
|
29
|
+
clearFormOnClick: true
|
|
30
|
+
};
|