autoforma 1.0.1 → 1.0.4
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/README.md +157 -157
- package/dist/{src/components → components}/AutoForm.d.ts +1 -2
- package/dist/index.cjs.js +18 -15
- package/dist/index.es.js +6261 -5405
- package/package.json +23 -58
- package/dist/test-utils/index.d.ts +0 -4
- package/dist/test-utils/render.d.ts +0 -1
- package/src/components/AutoForm.tsx +0 -201
- package/src/components/FieldRender.tsx +0 -217
- package/src/components/fields/ArrayField.tsx +0 -59
- package/src/components/fields/CheckField.tsx +0 -23
- package/src/components/fields/DateField.tsx +0 -26
- package/src/components/fields/DateTimeField.tsx +0 -26
- package/src/components/fields/NumberField.tsx +0 -20
- package/src/components/fields/ObjectField.tsx +0 -41
- package/src/components/fields/SelectField.tsx +0 -23
- package/src/components/fields/TextAreaField.tsx +0 -25
- package/src/components/fields/TextField.tsx +0 -20
- package/src/components/fields/TimeField.tsx +0 -32
- package/src/types/custom-render.ts +0 -22
- package/src/types/field.ts +0 -32
- package/src/types/form.ts +0 -8
- /package/dist/{src/components → components}/FieldRender.d.ts +0 -0
- /package/dist/{src/components → components}/fields/ArrayField.d.ts +0 -0
- /package/dist/{src/components → components}/fields/CheckField.d.ts +0 -0
- /package/dist/{src/components → components}/fields/DateField.d.ts +0 -0
- /package/dist/{src/components → components}/fields/DateTimeField.d.ts +0 -0
- /package/dist/{src/components → components}/fields/NumberField.d.ts +0 -0
- /package/dist/{src/components → components}/fields/ObjectField.d.ts +0 -0
- /package/dist/{src/components → components}/fields/SelectField.d.ts +0 -0
- /package/dist/{src/components → components}/fields/TextAreaField.d.ts +0 -0
- /package/dist/{src/components → components}/fields/TextField.d.ts +0 -0
- /package/dist/{src/components → components}/fields/TimeField.d.ts +0 -0
- /package/dist/{src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{src/stories → stories}/AutoForm.stories.d.ts +0 -0
- /package/dist/{src/stories → stories}/Fields.stories.d.ts +0 -0
- /package/dist/{src/theme.d.ts → theme.d.ts} +0 -0
- /package/dist/{src/types → types}/custom-render.d.ts +0 -0
- /package/dist/{src/types → types}/field.d.ts +0 -0
- /package/dist/{src/types → types}/form.d.ts +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autoforma",
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "AutoForma is a dynamic form builder based on Mantine and React",
|
|
5
|
+
"main": "./dist/index.cjs.js",
|
|
6
|
+
"module": "./dist/index.es.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.es.js",
|
|
11
|
+
"require": "./dist/index.cjs.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
3
18
|
"type": "module",
|
|
4
|
-
"version": "1.0.1",
|
|
5
19
|
"homepage": "http://sajinael98.github.io/AutoForma",
|
|
6
20
|
"scripts": {
|
|
7
21
|
"dev": "vite",
|
|
@@ -19,8 +33,7 @@
|
|
|
19
33
|
"storybook": "storybook dev -p 6006",
|
|
20
34
|
"storybook:build": "storybook build",
|
|
21
35
|
"predeploy": "npm run build-storybook",
|
|
22
|
-
"deploy-storybook": "gh-pages -d storybook-static"
|
|
23
|
-
"build-storybook": "build-storybook"
|
|
36
|
+
"deploy-storybook": "gh-pages -d storybook-static"
|
|
24
37
|
},
|
|
25
38
|
"dependencies": {
|
|
26
39
|
"@mantine/core": "7.17.5",
|
|
@@ -30,64 +43,16 @@
|
|
|
30
43
|
"dayjs": "^1.11.13",
|
|
31
44
|
"react": "^19.1.0",
|
|
32
45
|
"react-dom": "^19.1.0",
|
|
33
|
-
"react-router-dom": "^7.4.0"
|
|
34
|
-
"vite-plugin-dts": "^4.5.3"
|
|
46
|
+
"react-router-dom": "^7.4.0"
|
|
35
47
|
},
|
|
36
48
|
"devDependencies": {
|
|
37
|
-
"@chromatic-com/storybook": "^3.2.6",
|
|
38
|
-
"@eslint/js": "^9.23.0",
|
|
39
|
-
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
|
|
40
|
-
"@storybook/addon-essentials": "^8.6.12",
|
|
41
|
-
"@storybook/addon-onboarding": "^8.6.12",
|
|
42
|
-
"@storybook/blocks": "^8.6.12",
|
|
43
|
-
"@storybook/experimental-addon-test": "^8.6.12",
|
|
44
|
-
"@storybook/react": "^8.6.8",
|
|
45
|
-
"@storybook/react-vite": "^8.6.8",
|
|
46
|
-
"@storybook/test": "^8.6.12",
|
|
47
|
-
"@testing-library/dom": "^10.4.0",
|
|
48
|
-
"@testing-library/jest-dom": "^6.6.3",
|
|
49
|
-
"@testing-library/react": "^16.2.0",
|
|
50
|
-
"@testing-library/user-event": "^14.6.1",
|
|
51
|
-
"@types/node": "^22.13.11",
|
|
52
|
-
"@types/react": "^19.0.12",
|
|
53
|
-
"@types/react-dom": "^19.0.4",
|
|
54
49
|
"@vitejs/plugin-react": "^4.3.4",
|
|
55
|
-
"@vitest/browser": "^3.1.2",
|
|
56
|
-
"@vitest/coverage-v8": "^3.1.2",
|
|
57
|
-
"eslint": "^9.23.0",
|
|
58
|
-
"eslint-config-mantine": "^4.0.3",
|
|
59
|
-
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
60
|
-
"eslint-plugin-react": "^7.37.4",
|
|
61
|
-
"eslint-plugin-storybook": "^0.12.0",
|
|
62
|
-
"gh-pages": "^6.3.0",
|
|
63
|
-
"identity-obj-proxy": "^3.0.0",
|
|
64
|
-
"jsdom": "^26.0.0",
|
|
65
|
-
"playwright": "^1.52.0",
|
|
66
|
-
"postcss": "^8.5.3",
|
|
67
|
-
"postcss-preset-mantine": "1.17.0",
|
|
68
|
-
"postcss-simple-vars": "^7.0.1",
|
|
69
|
-
"prettier": "^3.5.3",
|
|
70
|
-
"prop-types": "^15.8.1",
|
|
71
|
-
"storybook": "^8.6.8",
|
|
72
|
-
"storybook-dark-mode": "^4.0.2",
|
|
73
|
-
"stylelint": "^16.16.0",
|
|
74
|
-
"stylelint-config-standard-scss": "^14.0.0",
|
|
75
|
-
"typescript": "^5.8.2",
|
|
76
|
-
"typescript-eslint": "^8.27.0",
|
|
77
50
|
"vite": "^6.2.2",
|
|
51
|
+
"vite-plugin-dts": "^4.5.3",
|
|
52
|
+
"typescript": "^5.8.2",
|
|
78
53
|
"vite-tsconfig-paths": "^5.1.4",
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
"packageManager": "yarn@4.9.1",
|
|
82
|
-
"eslintConfig": {
|
|
83
|
-
"extends": [
|
|
84
|
-
"plugin:storybook/recommended"
|
|
85
|
-
]
|
|
54
|
+
"@types/react": "^19.0.12",
|
|
55
|
+
"@types/react-dom": "^19.0.4"
|
|
86
56
|
},
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
"src/components",
|
|
90
|
-
"src/types",
|
|
91
|
-
"README.md"
|
|
92
|
-
]
|
|
93
|
-
}
|
|
57
|
+
"packageManager": "yarn@4.9.1"
|
|
58
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function render(ui: React.ReactNode): import('@testing-library/react').RenderResult<typeof import("@testing-library/dom/types/queries"), HTMLElement, HTMLElement>;
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo } from 'react';
|
|
2
|
-
import { createFormContext, FormValidateInput } from '@mantine/form';
|
|
3
|
-
import { useDebouncedCallback } from '@mantine/hooks';
|
|
4
|
-
import { FieldRenderCustomRender } from '@/types/custom-render';
|
|
5
|
-
import { FieldSchema, FieldType } from '@/types/field';
|
|
6
|
-
import FieldRender from './FieldRender';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export interface AutoFormProps {
|
|
10
|
-
values?: Record<string, any>;
|
|
11
|
-
schema: FieldSchema[];
|
|
12
|
-
onSubmit: (values: Record<string, any>) => void;
|
|
13
|
-
container: (Form: React.ReactNode, onSubmit: VoidFunction, readOnly?: true) => React.ReactNode;
|
|
14
|
-
fieldContainer?: (field: React.ReactNode, fieldSchema: FieldSchema) => React.ReactNode;
|
|
15
|
-
customRender?: FieldRenderCustomRender;
|
|
16
|
-
validate?: FormValidateInput<Record<string, any>>;
|
|
17
|
-
readOnly?: true;
|
|
18
|
-
onFieldChange?: (name: string, value: any, values: Record<string, any>) => Record<string, any>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const [FormProvider, _, useForm] = createFormContext<Record<string, any>>();
|
|
22
|
-
|
|
23
|
-
const getDefaultValueForField = (type: FieldType): any => {
|
|
24
|
-
switch (type) {
|
|
25
|
-
case 'number':
|
|
26
|
-
return 0;
|
|
27
|
-
case 'array':
|
|
28
|
-
return [];
|
|
29
|
-
case 'check':
|
|
30
|
-
return false;
|
|
31
|
-
case 'object':
|
|
32
|
-
return {};
|
|
33
|
-
case 'select':
|
|
34
|
-
case 'date':
|
|
35
|
-
case 'datetime':
|
|
36
|
-
return null;
|
|
37
|
-
default:
|
|
38
|
-
return '';
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const generateInitialValues = (schema: FieldSchema[]): Record<string, any> => {
|
|
43
|
-
const result: Record<string, any> = {};
|
|
44
|
-
|
|
45
|
-
for (const field of schema) {
|
|
46
|
-
if (field.type === 'object' && field.fields) {
|
|
47
|
-
result[field.name] = generateInitialValues(field.fields);
|
|
48
|
-
} else {
|
|
49
|
-
result[field.name] = field.initialValue ?? getDefaultValueForField(field.type);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return result;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const isValueEmpty = (value: any): boolean => {
|
|
57
|
-
return (
|
|
58
|
-
value === null ||
|
|
59
|
-
value === undefined ||
|
|
60
|
-
(typeof value === 'string' && value.trim() === '') ||
|
|
61
|
-
(Array.isArray(value) && value.length === 0)
|
|
62
|
-
);
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export const validateRequiredFields = (
|
|
66
|
-
schema: FieldSchema[],
|
|
67
|
-
values: Record<string, any>,
|
|
68
|
-
parentPath = ''
|
|
69
|
-
): Record<string, string> => {
|
|
70
|
-
const errors: Record<string, string> = {};
|
|
71
|
-
|
|
72
|
-
for (const field of schema) {
|
|
73
|
-
const fullName = parentPath ? `${parentPath}.${field.name}` : field.name;
|
|
74
|
-
const fieldValue = values?.[field.name];
|
|
75
|
-
|
|
76
|
-
if (field.type === 'object') {
|
|
77
|
-
Object.assign(errors, validateRequiredFields(field.fields, fieldValue || {}, fullName));
|
|
78
|
-
} else if (field.type === 'array') {
|
|
79
|
-
if (field.required && (!Array.isArray(fieldValue) || fieldValue.length === 0)) {
|
|
80
|
-
errors[fullName] = field.name + ' is required';
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (Array.isArray(fieldValue)) {
|
|
85
|
-
fieldValue.forEach((item, index) => {
|
|
86
|
-
Object.assign(errors, validateRequiredFields(field.fields, item, `${fullName}.${index}`));
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
} else if (field.required && isValueEmpty(fieldValue)) {
|
|
90
|
-
errors[fullName] = field.name + ' is required';
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return errors;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
function buildErrorObject(flatErrors) {
|
|
98
|
-
const error = {};
|
|
99
|
-
|
|
100
|
-
Object.entries(flatErrors).forEach(([key, value]) => {
|
|
101
|
-
const keys = key.split('.');
|
|
102
|
-
let curr: Record<string, string> = error;
|
|
103
|
-
|
|
104
|
-
keys.forEach((subKey, index) => {
|
|
105
|
-
const isLast = index === keys.length - 1;
|
|
106
|
-
const nextKey = keys[index + 1];
|
|
107
|
-
|
|
108
|
-
if (isLast) {
|
|
109
|
-
curr[subKey] = value;
|
|
110
|
-
} else {
|
|
111
|
-
if (!(subKey in curr)) {
|
|
112
|
-
curr[subKey] = {};
|
|
113
|
-
}
|
|
114
|
-
curr = curr[subKey];
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
return error;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const AutoForm: React.FC<AutoFormProps> = ({
|
|
123
|
-
onSubmit,
|
|
124
|
-
schema,
|
|
125
|
-
container,
|
|
126
|
-
fieldContainer,
|
|
127
|
-
customRender,
|
|
128
|
-
validate,
|
|
129
|
-
readOnly,
|
|
130
|
-
values,
|
|
131
|
-
onFieldChange,
|
|
132
|
-
}) => {
|
|
133
|
-
const form = useForm({
|
|
134
|
-
mode: 'uncontrolled',
|
|
135
|
-
initialValues: generateInitialValues(schema),
|
|
136
|
-
validate,
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const formValues = useMemo(() => form.getValues(), [form.getValues()]);
|
|
140
|
-
|
|
141
|
-
const onChange = useDebouncedCallback((name: string, value: any) => {
|
|
142
|
-
form.setFieldValue(name, value);
|
|
143
|
-
if (onFieldChange) {
|
|
144
|
-
const updates = onFieldChange(name, value, formValues);
|
|
145
|
-
form.setValues(updates);
|
|
146
|
-
}
|
|
147
|
-
}, 0);
|
|
148
|
-
|
|
149
|
-
const getFieldError = useCallback(
|
|
150
|
-
(type: FieldType, name: string) => {
|
|
151
|
-
const errors = form.errors;
|
|
152
|
-
|
|
153
|
-
if (type === 'object' || type === 'array') {
|
|
154
|
-
return buildErrorObject(errors)[name];
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return errors[name];
|
|
158
|
-
},
|
|
159
|
-
[form.errors]
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
const handleSubmit = useCallback(() => {
|
|
163
|
-
const currentValues = form.getValues();
|
|
164
|
-
const errors = validateRequiredFields(schema, currentValues);
|
|
165
|
-
|
|
166
|
-
if (Object.keys(errors).length > 0) {
|
|
167
|
-
form.setErrors(errors);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
form.onSubmit(onSubmit)();
|
|
172
|
-
}, [form, onSubmit, schema]);
|
|
173
|
-
|
|
174
|
-
useEffect(() => {
|
|
175
|
-
if (values) {
|
|
176
|
-
form.initialize(values);
|
|
177
|
-
}
|
|
178
|
-
}, [values]);
|
|
179
|
-
|
|
180
|
-
const content = (
|
|
181
|
-
<FormProvider form={form}>
|
|
182
|
-
{schema.map((field, index) => (
|
|
183
|
-
<FieldRender
|
|
184
|
-
key={index}
|
|
185
|
-
field={field}
|
|
186
|
-
formValues={formValues}
|
|
187
|
-
value={formValues[field.name]}
|
|
188
|
-
error={getFieldError(field.type, field.name)}
|
|
189
|
-
onChange={onChange}
|
|
190
|
-
fieldContainer={fieldContainer}
|
|
191
|
-
customRender={customRender}
|
|
192
|
-
readOnly={field.readOnly || readOnly}
|
|
193
|
-
/>
|
|
194
|
-
))}
|
|
195
|
-
</FormProvider>
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
return container(content, handleSubmit, readOnly);
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
export default AutoForm;
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
2
|
-
import { Fieldset, Text } from '@mantine/core';
|
|
3
|
-
import { FieldRenderCustomRender } from '@/types/custom-render';
|
|
4
|
-
import { ArrayFieldOptions, FieldSchema } from '@/types/field';
|
|
5
|
-
import ArrayField from './fields/ArrayField';
|
|
6
|
-
import CheckField from './fields/CheckField';
|
|
7
|
-
import DateField from './fields/DateField';
|
|
8
|
-
import DateTimeField from './fields/DateTimeField';
|
|
9
|
-
import NumberField from './fields/NumberField';
|
|
10
|
-
import ObjectField from './fields/ObjectField';
|
|
11
|
-
import SelectField from './fields/SelectField';
|
|
12
|
-
import TextAreaField from './fields/TextAreaField';
|
|
13
|
-
import TextField from './fields/TextField';
|
|
14
|
-
import TimeField from './fields/TimeField';
|
|
15
|
-
|
|
16
|
-
interface FieldRenderProps {
|
|
17
|
-
field: FieldSchema;
|
|
18
|
-
value: any;
|
|
19
|
-
error?: React.ReactNode | Record<string, React.ReactNode>;
|
|
20
|
-
onChange: (name: string, value: any) => void;
|
|
21
|
-
formValues: Record<string, any>;
|
|
22
|
-
fieldContainer?: (field: React.ReactNode, fieldSchema: FieldSchema) => React.ReactNode;
|
|
23
|
-
customRender?: FieldRenderCustomRender;
|
|
24
|
-
readOnly?: true;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function getArrayOptions(
|
|
28
|
-
name: string,
|
|
29
|
-
onChange: (name: string, value: any) => void,
|
|
30
|
-
value: Record<string, any>[]
|
|
31
|
-
): ArrayFieldOptions {
|
|
32
|
-
return {
|
|
33
|
-
addElement: (val) => {
|
|
34
|
-
onChange(name, [...value, val]);
|
|
35
|
-
},
|
|
36
|
-
replaceElement: (index, val) => {
|
|
37
|
-
const newValue = value.map((row, i) => (i === index ? val : row));
|
|
38
|
-
onChange(name, newValue);
|
|
39
|
-
},
|
|
40
|
-
removeElement: (index) => {
|
|
41
|
-
onChange(
|
|
42
|
-
name,
|
|
43
|
-
value.filter((_, i) => i !== index)
|
|
44
|
-
);
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const FieldRender: React.FC<FieldRenderProps> = ({
|
|
50
|
-
field,
|
|
51
|
-
value,
|
|
52
|
-
error,
|
|
53
|
-
onChange,
|
|
54
|
-
formValues,
|
|
55
|
-
fieldContainer = (field) => <>{field}</>,
|
|
56
|
-
customRender,
|
|
57
|
-
readOnly,
|
|
58
|
-
}) => {
|
|
59
|
-
const isVisible = useMemo(() => {
|
|
60
|
-
return typeof field.visible === 'function'
|
|
61
|
-
? field.visible(formValues)
|
|
62
|
-
: field.visible !== false;
|
|
63
|
-
}, [field.visible, formValues]);
|
|
64
|
-
|
|
65
|
-
const isDisabled = useMemo(() => {
|
|
66
|
-
return typeof field.disabled === 'function'
|
|
67
|
-
? field.disabled(formValues)
|
|
68
|
-
: field.disabled === true;
|
|
69
|
-
}, [field.disabled, formValues]);
|
|
70
|
-
|
|
71
|
-
const arrayOptions = useMemo(() => {
|
|
72
|
-
if (field.type !== 'array') {
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
return getArrayOptions(field.name, onChange, value);
|
|
76
|
-
}, [field.type, field.name, onChange, value]);
|
|
77
|
-
|
|
78
|
-
const customField = useMemo(() => {
|
|
79
|
-
return customRender?.(
|
|
80
|
-
field,
|
|
81
|
-
value,
|
|
82
|
-
error,
|
|
83
|
-
onChange,
|
|
84
|
-
formValues,
|
|
85
|
-
arrayOptions,
|
|
86
|
-
readOnly || field.readOnly
|
|
87
|
-
);
|
|
88
|
-
}, [
|
|
89
|
-
customRender,
|
|
90
|
-
field,
|
|
91
|
-
value,
|
|
92
|
-
error,
|
|
93
|
-
onChange,
|
|
94
|
-
formValues,
|
|
95
|
-
arrayOptions,
|
|
96
|
-
readOnly,
|
|
97
|
-
field.readOnly,
|
|
98
|
-
]);
|
|
99
|
-
|
|
100
|
-
if (!isVisible) {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (customField) {
|
|
105
|
-
return customField;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const renderField = () => {
|
|
109
|
-
switch (field.type) {
|
|
110
|
-
case 'text':
|
|
111
|
-
return (
|
|
112
|
-
<TextField
|
|
113
|
-
name={field.name}
|
|
114
|
-
value={value as string}
|
|
115
|
-
onChange={onChange}
|
|
116
|
-
readOnly={readOnly}
|
|
117
|
-
/>
|
|
118
|
-
);
|
|
119
|
-
case 'number':
|
|
120
|
-
return (
|
|
121
|
-
<NumberField
|
|
122
|
-
name={field.name}
|
|
123
|
-
value={value as number}
|
|
124
|
-
onChange={onChange}
|
|
125
|
-
readOnly={readOnly}
|
|
126
|
-
/>
|
|
127
|
-
);
|
|
128
|
-
case 'object':
|
|
129
|
-
return (
|
|
130
|
-
<ObjectField
|
|
131
|
-
name={field.name}
|
|
132
|
-
onChange={onChange}
|
|
133
|
-
value={value}
|
|
134
|
-
fields={field.fields ?? []}
|
|
135
|
-
formValues={formValues}
|
|
136
|
-
error={error as Record<string, React.ReactNode>}
|
|
137
|
-
readOnly={readOnly}
|
|
138
|
-
/>
|
|
139
|
-
);
|
|
140
|
-
case 'array':
|
|
141
|
-
return (
|
|
142
|
-
<ArrayField
|
|
143
|
-
name={field.name}
|
|
144
|
-
onChange={onChange}
|
|
145
|
-
value={value}
|
|
146
|
-
fields={field.fields as FieldSchema[]}
|
|
147
|
-
options={arrayOptions!}
|
|
148
|
-
error={error as Record<string, React.ReactNode>}
|
|
149
|
-
readOnly={readOnly}
|
|
150
|
-
/>
|
|
151
|
-
);
|
|
152
|
-
case 'check':
|
|
153
|
-
return (
|
|
154
|
-
<CheckField
|
|
155
|
-
name={field.name}
|
|
156
|
-
value={value as boolean}
|
|
157
|
-
onChange={onChange}
|
|
158
|
-
readOnly={readOnly}
|
|
159
|
-
label={field.label}
|
|
160
|
-
/>
|
|
161
|
-
);
|
|
162
|
-
case 'select':
|
|
163
|
-
return (
|
|
164
|
-
<SelectField
|
|
165
|
-
name={field.name}
|
|
166
|
-
value={value}
|
|
167
|
-
onChange={onChange}
|
|
168
|
-
readOnly={readOnly}
|
|
169
|
-
data={field.data as { label: string; value: string }[]}
|
|
170
|
-
/>
|
|
171
|
-
);
|
|
172
|
-
case 'textarea':
|
|
173
|
-
return (
|
|
174
|
-
<TextAreaField name={field.name} value={value} onChange={onChange} readOnly={readOnly} />
|
|
175
|
-
);
|
|
176
|
-
case 'date':
|
|
177
|
-
return (
|
|
178
|
-
<DateField name={field.name} value={value} onChange={onChange} readOnly={readOnly} />
|
|
179
|
-
);
|
|
180
|
-
case 'datetime':
|
|
181
|
-
return (
|
|
182
|
-
<DateTimeField name={field.name} value={value} onChange={onChange} readOnly={readOnly} />
|
|
183
|
-
);
|
|
184
|
-
case 'time':
|
|
185
|
-
return (
|
|
186
|
-
<TimeField name={field.name} value={value} onChange={onChange} readOnly={readOnly} />
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
default:
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
const content = (
|
|
195
|
-
<Fieldset
|
|
196
|
-
legend={field.type === 'check' ? undefined : field.label}
|
|
197
|
-
variant="unstyled"
|
|
198
|
-
disabled={isDisabled || readOnly}
|
|
199
|
-
styles={{
|
|
200
|
-
legend: {
|
|
201
|
-
fontWeight: 500,
|
|
202
|
-
},
|
|
203
|
-
}}
|
|
204
|
-
>
|
|
205
|
-
{renderField()}
|
|
206
|
-
{!['object', 'array'].includes(field.type) && typeof error === 'string' && (
|
|
207
|
-
<Text c="red" fz="sm" fw={500}>
|
|
208
|
-
{error}
|
|
209
|
-
</Text>
|
|
210
|
-
)}
|
|
211
|
-
</Fieldset>
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
return fieldContainer(content, field);
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
export default FieldRender;
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import React, { Fragment } from 'react';
|
|
2
|
-
import { Button, Text } from '@mantine/core';
|
|
3
|
-
import { BaseFieldProps, FieldSchema } from '@/types/field';
|
|
4
|
-
import FieldRender from '../FieldRender';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
interface ArrayFieldProps extends BaseFieldProps<Record<string, any>[]> {
|
|
8
|
-
fields: FieldSchema[];
|
|
9
|
-
options: {
|
|
10
|
-
addElement: (val: Record<string, any>) => void;
|
|
11
|
-
replaceElement: (index: number, val: Record<string, any>) => void;
|
|
12
|
-
removeElement: (index: number) => void;
|
|
13
|
-
};
|
|
14
|
-
error?: Record<string, React.ReactNode>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const ArrayField: React.FC<ArrayFieldProps> = (props) => {
|
|
18
|
-
const { fields, options, value, error = {}, name: parentName, readOnly } = props;
|
|
19
|
-
|
|
20
|
-
const content = value.map((row, rowIndex) => {
|
|
21
|
-
const onChange = (name: string, value: any) => {
|
|
22
|
-
row[name] = value;
|
|
23
|
-
options.replaceElement(rowIndex, row);
|
|
24
|
-
};
|
|
25
|
-
return fields.map((field, index) => {
|
|
26
|
-
return (
|
|
27
|
-
<Fragment key={index}>
|
|
28
|
-
<FieldRender
|
|
29
|
-
field={field}
|
|
30
|
-
formValues={row}
|
|
31
|
-
onChange={onChange}
|
|
32
|
-
value={row[field.name]}
|
|
33
|
-
error={field.name in (error[rowIndex] ?? {}) ? error[rowIndex][field.name] : undefined}
|
|
34
|
-
readOnly={readOnly}
|
|
35
|
-
/>
|
|
36
|
-
{!readOnly && <Button onClick={() => options.removeElement(rowIndex)}>Remove</Button>}
|
|
37
|
-
</Fragment>
|
|
38
|
-
);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<>
|
|
44
|
-
{typeof error === 'string' && (
|
|
45
|
-
<Text c="red" fz="sm" fw={500}>
|
|
46
|
-
{error}
|
|
47
|
-
</Text>
|
|
48
|
-
)}
|
|
49
|
-
{content}
|
|
50
|
-
{!readOnly && (
|
|
51
|
-
<Button display="block" onClick={() => options.addElement({})}>
|
|
52
|
-
Add
|
|
53
|
-
</Button>
|
|
54
|
-
)}
|
|
55
|
-
</>
|
|
56
|
-
);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export default ArrayField;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Checkbox } from '@mantine/core';
|
|
3
|
-
import { BaseFieldProps } from '@/types/field';
|
|
4
|
-
|
|
5
|
-
interface CheckFieldProps extends BaseFieldProps<boolean> {
|
|
6
|
-
label: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const CheckField: React.FC<CheckFieldProps> = (props) => {
|
|
10
|
-
const { name, onChange, value, readOnly, label } = props;
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<Checkbox
|
|
14
|
-
name={name}
|
|
15
|
-
checked={value}
|
|
16
|
-
readOnly={readOnly}
|
|
17
|
-
onChange={(e) => onChange(name, e.target.checked)}
|
|
18
|
-
label={label}
|
|
19
|
-
/>
|
|
20
|
-
);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export default CheckField;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import dayjs from 'dayjs';
|
|
2
|
-
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { DateInput } from '@mantine/dates';
|
|
5
|
-
import { BaseFieldProps } from '@/types/field';
|
|
6
|
-
|
|
7
|
-
dayjs.extend(customParseFormat);
|
|
8
|
-
|
|
9
|
-
interface DateFieldProps extends BaseFieldProps<Date> {}
|
|
10
|
-
|
|
11
|
-
const DateField: React.FC<DateFieldProps> = (props) => {
|
|
12
|
-
const { name, onChange, value, readOnly } = props;
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<DateInput
|
|
16
|
-
name={name}
|
|
17
|
-
onChange={(value) => onChange(name, value)}
|
|
18
|
-
value={value}
|
|
19
|
-
readOnly={readOnly}
|
|
20
|
-
valueFormat="YYYY-MM-DD"
|
|
21
|
-
clearable
|
|
22
|
-
/>
|
|
23
|
-
);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export default DateField;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import dayjs from 'dayjs';
|
|
2
|
-
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { DateTimePicker } from '@mantine/dates';
|
|
5
|
-
import { BaseFieldProps } from '@/types/field';
|
|
6
|
-
|
|
7
|
-
dayjs.extend(customParseFormat);
|
|
8
|
-
|
|
9
|
-
interface DateTimeFieldProps extends BaseFieldProps<Date> {}
|
|
10
|
-
|
|
11
|
-
const DateTimeField: React.FC<DateTimeFieldProps> = (props) => {
|
|
12
|
-
const { name, onChange, value, readOnly } = props;
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<DateTimePicker
|
|
16
|
-
name={name}
|
|
17
|
-
onChange={(value) => onChange(name, value)}
|
|
18
|
-
value={value}
|
|
19
|
-
readOnly={readOnly}
|
|
20
|
-
valueFormat="YYYY-MM-DD HH:mm:ss"
|
|
21
|
-
clearable
|
|
22
|
-
/>
|
|
23
|
-
);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export default DateTimeField;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { NumberInput } from '@mantine/core';
|
|
3
|
-
import { BaseFieldProps } from '@/types/field';
|
|
4
|
-
|
|
5
|
-
interface NumberFieldProps extends BaseFieldProps<number> {}
|
|
6
|
-
|
|
7
|
-
const NumberField: React.FC<NumberFieldProps> = (props) => {
|
|
8
|
-
const { name, onChange, value, readOnly } = props;
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<NumberInput
|
|
12
|
-
name={name}
|
|
13
|
-
onChange={(value) => onChange(name, value as number)}
|
|
14
|
-
value={value}
|
|
15
|
-
readOnly={readOnly}
|
|
16
|
-
/>
|
|
17
|
-
);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export default NumberField;
|