@idem.agency/form-builder 0.0.10 → 0.0.11
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 +1 -33
- package/dist/index.cjs +325 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +86 -0
- package/dist/index.d.mts +86 -0
- package/dist/index.mjs +292 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +8 -4
- package/{index.html → public/index.html} +1 -2
- package/public/main.tsx +90 -0
- package/src/app/debug.tsx +0 -89
- package/src/app/index.tsx +51 -40
- package/src/{widgets/form → entity/inputs}/ui/group/index.tsx +4 -3
- package/src/{widgets/form → entity/inputs}/ui/input/index.tsx +5 -3
- package/src/index.ts +2 -2
- package/src/shared/lib/validation/core.ts +3 -3
- package/src/shared/lib/validation/rules/confirm.ts +2 -2
- package/src/shared/lib/validation/rules/email.ts +1 -1
- package/src/shared/lib/validation/rules/require.ts +1 -1
- package/src/shared/model/builder/createContext.tsx +40 -0
- package/src/shared/model/builder/index.ts +6 -0
- package/src/shared/model/index.ts +0 -1
- package/src/shared/model/store/createStoreContext.tsx +74 -0
- package/src/shared/model/store/index.ts +46 -0
- package/src/shared/model/store/store.ts +27 -0
- package/src/shared/types/common.ts +1 -1
- package/src/widgets/dynamicBuilder/element.tsx +31 -0
- package/src/widgets/dynamicBuilder/index.tsx +28 -58
- package/tsconfig.json +22 -5
- package/tsdown.config.ts +10 -0
- package/vite.config.ts +7 -16
- package/src/main.tsx +0 -9
- package/src/shared/model/store.tsx +0 -52
- package/tsconfig.app.json +0 -26
- package/tsconfig.node.json +0 -24
- package/vite-env.d.ts +0 -1
- /package/src/{widgets/form → entity/inputs}/index.ts +0 -0
package/src/app/index.tsx
CHANGED
|
@@ -1,66 +1,77 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import type {FormBuilderRef, TFormBuilder} from "../shared/types/common";
|
|
4
|
-
import {StoreContext, storeReducer,} from "
|
|
4
|
+
// import {StoreContext, storeReducer,} from "@/shared/model";
|
|
5
|
+
|
|
5
6
|
import {DynamicBuilder} from "../widgets/dynamicBuilder";
|
|
6
|
-
import { forwardRef, useImperativeHandle
|
|
7
|
-
import {ValidationCore} from "
|
|
8
|
-
import { useUpdateEffect } from '
|
|
7
|
+
import { forwardRef, useImperativeHandle} from "react";
|
|
8
|
+
// import {ValidationCore} from "@/shared/lib/validation/core";
|
|
9
|
+
// import { useUpdateEffect } from '@/shared/hook/useUpdateEffect';
|
|
10
|
+
import {FormStoreProvider, useSubmit} from "@/shared/model/store";
|
|
11
|
+
import {BuilderProvider} from "@/shared/model/builder";
|
|
9
12
|
|
|
10
13
|
export const FormBuilder = forwardRef<FormBuilderRef, TFormBuilder>((props, ref) => {
|
|
14
|
+
// const validator = useMemo(() => {
|
|
15
|
+
// return (new ValidationCore(props.layout, props.plugins))
|
|
16
|
+
// }, [props.layout, props.plugins]);
|
|
11
17
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
},
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
// const [state, dispatch] = useReducer(storeReducer, {
|
|
19
|
+
// formData: props.formData ?? {},
|
|
20
|
+
// errors: {},
|
|
21
|
+
// });
|
|
22
|
+
//
|
|
23
|
+
// useUpdateEffect(() => {
|
|
24
|
+
// if (props.onChange) {
|
|
25
|
+
// props.onChange(state.formData);
|
|
26
|
+
// }
|
|
27
|
+
// }, [state.formData, props.onChange]);
|
|
28
|
+
const submitHdl = useSubmit((data) => {
|
|
29
|
+
if (props.onSubmit) {
|
|
30
|
+
props.onSubmit(data);
|
|
24
31
|
}
|
|
25
|
-
}
|
|
32
|
+
});
|
|
26
33
|
|
|
27
|
-
const submit = () => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
34
|
+
// const submit = () => {
|
|
35
|
+
//
|
|
36
|
+
// // const errors = validator.validate(state.formData);
|
|
37
|
+
// // if (Object.keys(errors).length == 0) {
|
|
38
|
+
// // if (props.onSubmit) {
|
|
39
|
+
// // props.onSubmit(state.formData)
|
|
40
|
+
// // }
|
|
41
|
+
// // } else {
|
|
42
|
+
// // dispatch({
|
|
43
|
+
// // type: 'setErrors',
|
|
44
|
+
// // payload: {
|
|
45
|
+
// // errors
|
|
46
|
+
// // }
|
|
47
|
+
// // });
|
|
48
|
+
// // }
|
|
49
|
+
// }
|
|
42
50
|
|
|
43
51
|
useImperativeHandle(ref, () => ({
|
|
44
52
|
reset: () => {
|
|
45
|
-
dispatch({type: 'reset'})
|
|
53
|
+
// dispatch({type: 'reset'})
|
|
46
54
|
},
|
|
47
55
|
submit: () => {
|
|
48
|
-
|
|
56
|
+
submitHdl();
|
|
49
57
|
},
|
|
50
58
|
errors: () => {
|
|
51
|
-
return
|
|
59
|
+
return {}
|
|
60
|
+
// return state?.errors ?? {};
|
|
52
61
|
}
|
|
53
|
-
}), [
|
|
62
|
+
}), [props.onSubmit]);
|
|
54
63
|
|
|
55
64
|
return <form onSubmit={(e) => {
|
|
56
65
|
e.preventDefault();
|
|
57
|
-
|
|
66
|
+
submitHdl();
|
|
58
67
|
}}
|
|
59
68
|
className={props?.className}
|
|
60
69
|
>
|
|
61
|
-
<
|
|
62
|
-
<
|
|
63
|
-
|
|
70
|
+
<FormStoreProvider>
|
|
71
|
+
<BuilderProvider plugins={props.plugins}>
|
|
72
|
+
<DynamicBuilder layout={props.layout} plugins={props.plugins} />
|
|
73
|
+
</BuilderProvider>
|
|
74
|
+
</FormStoreProvider>
|
|
64
75
|
<input type="submit" style={{ display: 'none' }}/>
|
|
65
76
|
{props.children}
|
|
66
77
|
</form>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {FormElementProps, FormFieldBase, FormFieldConfig, RC} from "../../../../shared/types/common.ts";
|
|
2
2
|
import clsx from "clsx";
|
|
3
|
+
import {useBuilder} from "@/shared/model/builder";
|
|
3
4
|
export type FormGroupConfig = FormFieldBase & { variant?: 'row' | 'col', fields: FormFieldConfig[] };
|
|
4
5
|
|
|
5
6
|
function isGroupConfig(field: FormFieldConfig): field is FormGroupConfig {
|
|
@@ -7,19 +8,19 @@ function isGroupConfig(field: FormFieldConfig): field is FormGroupConfig {
|
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
export const FormGroup: RC<FormElementProps<FormGroupConfig>> = ({field, path
|
|
11
|
+
export const FormGroup: RC<FormElementProps<FormGroupConfig>> = ({field, path}) => {
|
|
11
12
|
if (!isGroupConfig(field)) {
|
|
12
13
|
return null;
|
|
13
14
|
}
|
|
15
|
+
const Builder = useBuilder()(field.fields, path);
|
|
14
16
|
const variant = field.variant ?? 'col';
|
|
15
|
-
const Builder = builder;
|
|
16
17
|
|
|
17
18
|
const className = variant == 'col' ? 'flex-col' : 'flex-row';
|
|
18
19
|
|
|
19
20
|
return <div>
|
|
20
21
|
<div>{field.label}</div>
|
|
21
22
|
<div className={clsx(className, 'flex')}>
|
|
22
|
-
|
|
23
|
+
{Builder}
|
|
23
24
|
</div>
|
|
24
25
|
</div>;
|
|
25
26
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {FormElementProps, FormFieldConfig, FormFieldBase, RC} from '../../../../shared/types/common';
|
|
1
|
+
import type { FormElementProps, FormFieldConfig, FormFieldBase, RC } from '../../../../shared/types/common';
|
|
2
|
+
import { useId } from "react";
|
|
2
3
|
|
|
3
4
|
export type TextFieldConfig = FormFieldBase & { type: 'text' | 'email' | 'password'; placeholder?: string; };
|
|
4
5
|
|
|
@@ -11,12 +12,13 @@ export const TextField: RC<FormElementProps> = ({ field, value, errors, onChange
|
|
|
11
12
|
console.warn(`TextField received an invalid field config for type: ${field.type}`);
|
|
12
13
|
return null;
|
|
13
14
|
}
|
|
15
|
+
const id = useId();
|
|
14
16
|
return (
|
|
15
17
|
<div style={{ marginBottom: '15px' }}>
|
|
16
|
-
<label htmlFor={
|
|
18
|
+
<label htmlFor={id}>{field.label}:</label>
|
|
17
19
|
<input
|
|
18
20
|
type={field.type}
|
|
19
|
-
id={
|
|
21
|
+
id={id}
|
|
20
22
|
name={field.name}
|
|
21
23
|
placeholder={field.placeholder}
|
|
22
24
|
value={value || ''}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import FormBuilder from "
|
|
1
|
+
import FormBuilder from "@/app";
|
|
2
2
|
import type { IRule, TGroupRules, FormFieldConfig, FormElementProps, FormElementRegistry, FormBuilderRef} from "./shared/types/common.ts";
|
|
3
|
-
export {FormGroup, TextField} from "./
|
|
3
|
+
export {FormGroup, TextField} from "./entity/inputs";
|
|
4
4
|
export type {IRule, TGroupRules, FormFieldConfig, FormElementProps, FormElementRegistry, FormBuilderRef};
|
|
5
5
|
export { FormBuilder };
|
|
6
6
|
export { fieldShema } from './shared/model';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {FormData, FormElementRegistry, FormFieldConfig, IRule} from "
|
|
2
|
-
import {getNestedValue, updateNestedValue} from "
|
|
1
|
+
import type {FormData, FormElementRegistry, FormFieldConfig, IRule} from "@/shared/types/common";
|
|
2
|
+
import {getNestedValue, updateNestedValue} from "@/shared/utils";
|
|
3
3
|
import {RequireRule} from "./rules/require";
|
|
4
4
|
import {EmailRule} from "./rules/email";
|
|
5
5
|
import {ConfirmRule} from "./rules/confirm";
|
|
@@ -19,7 +19,7 @@ export class ValidationCore {
|
|
|
19
19
|
private checkRules(layout: FormFieldConfig[], data: FormData, path: string[] = [], errors = {}) {
|
|
20
20
|
layout.map((i) => {
|
|
21
21
|
const element = this.plugins[i.type];
|
|
22
|
-
const currentPath = [...path, i.
|
|
22
|
+
const currentPath = [...path, i.name];
|
|
23
23
|
|
|
24
24
|
if (element && element.fieldProps) {
|
|
25
25
|
element.fieldProps.forEach((fieldProps: string) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {IRule, FormData} from "
|
|
1
|
+
import type {IRule, FormData} from "@/shared/types/common";
|
|
2
2
|
import {BaseRule} from "./base";
|
|
3
|
-
import {getNestedValue} from "
|
|
3
|
+
import {getNestedValue} from "@/shared/utils";
|
|
4
4
|
|
|
5
5
|
export class ConfirmRule extends BaseRule implements IRule {
|
|
6
6
|
protected confirmField?: string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, {createContext, type ReactNode, useContext, useRef,} from "react";
|
|
2
|
+
import {DynamicBuilder} from "@/widgets/dynamicBuilder";
|
|
3
|
+
|
|
4
|
+
export function createStoreContext() {
|
|
5
|
+
const BuilderContext = createContext<((layout: any, path: string[]|undefined, children?: ReactNode) => ReactNode) | null>(null);
|
|
6
|
+
|
|
7
|
+
const Provider: React.FC<{ children: React.ReactNode, plugins: any }> = ({
|
|
8
|
+
plugins,
|
|
9
|
+
children
|
|
10
|
+
}) => {
|
|
11
|
+
const storeRef = useRef<(layout: any, path: string[]|undefined, children?: ReactNode) => ReactNode>(null);
|
|
12
|
+
|
|
13
|
+
if (!storeRef.current) {
|
|
14
|
+
storeRef.current = (layout: any, path: string[]|undefined, children?: ReactNode) => {
|
|
15
|
+
return <DynamicBuilder layout={layout} path={path} plugins={plugins}>{children}</DynamicBuilder>
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<BuilderContext.Provider value={storeRef.current}>
|
|
21
|
+
{children}
|
|
22
|
+
</BuilderContext.Provider>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function useBuilder(): (layout: any, path: string[]|undefined, children?: ReactNode) => ReactNode {
|
|
27
|
+
const store = useContext(BuilderContext);
|
|
28
|
+
|
|
29
|
+
if (!store) {
|
|
30
|
+
throw new Error("StoreProvider missing");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return store
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
Provider,
|
|
38
|
+
useBuilder
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useRef,
|
|
5
|
+
useSyncExternalStore
|
|
6
|
+
} from "react";
|
|
7
|
+
import { createStore, type Reducer, type Store } from "./store";
|
|
8
|
+
|
|
9
|
+
export function createStoreContext<S, A>(
|
|
10
|
+
reducer: Reducer<S, A>,
|
|
11
|
+
initialState: S
|
|
12
|
+
) {
|
|
13
|
+
const StoreContext = createContext<Store<S, A> | null>(null);
|
|
14
|
+
|
|
15
|
+
const Provider: React.FC<{ children: React.ReactNode }> = ({
|
|
16
|
+
children
|
|
17
|
+
}) => {
|
|
18
|
+
const storeRef = useRef<Store<S, A>>(null);
|
|
19
|
+
|
|
20
|
+
if (!storeRef.current) {
|
|
21
|
+
storeRef.current = createStore(reducer, initialState);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<StoreContext.Provider value={storeRef.current}>
|
|
26
|
+
{children}
|
|
27
|
+
</StoreContext.Provider>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function useStore<T>(
|
|
32
|
+
selector: (state: S) => T
|
|
33
|
+
): T {
|
|
34
|
+
const store = useContext(StoreContext);
|
|
35
|
+
|
|
36
|
+
if (!store) {
|
|
37
|
+
throw new Error("StoreProvider missing");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return useSyncExternalStore(
|
|
41
|
+
store.subscribe,
|
|
42
|
+
() => selector(store.getState()),
|
|
43
|
+
() => selector(store.getState())
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function useDispatch() {
|
|
48
|
+
const store = useContext(StoreContext);
|
|
49
|
+
|
|
50
|
+
if (!store) {
|
|
51
|
+
throw new Error("StoreProvider missing");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return store.dispatch;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function useSubmit(onSubmit: (state: S) => void) {
|
|
58
|
+
const store = useContext(StoreContext);
|
|
59
|
+
|
|
60
|
+
return () => {
|
|
61
|
+
if (store) {
|
|
62
|
+
const state = store.getState();
|
|
63
|
+
onSubmit(state);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
Provider,
|
|
70
|
+
useStore,
|
|
71
|
+
useSubmit,
|
|
72
|
+
useDispatch
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createStoreContext } from "./createStoreContext";
|
|
2
|
+
import type {FormData} from "@/shared/types/common";
|
|
3
|
+
import {updateNestedValue} from "@/shared/utils";
|
|
4
|
+
|
|
5
|
+
type State = {
|
|
6
|
+
formData: FormData
|
|
7
|
+
errors: Record<string, any>
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type Action =
|
|
11
|
+
| { type: "setValue"; path: string[]; value: unknown }
|
|
12
|
+
| { type: "setError"; path: string[]; value?: string }
|
|
13
|
+
| { type: "reset"; }
|
|
14
|
+
| { type: "setErrors"; errors?: object };
|
|
15
|
+
|
|
16
|
+
const initialState: State = {
|
|
17
|
+
formData: {},
|
|
18
|
+
errors: {}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function reducer(state: State, action: Action): State {
|
|
22
|
+
const newData = {...state};
|
|
23
|
+
switch (action.type) {
|
|
24
|
+
case 'setValue':
|
|
25
|
+
newData.formData = updateNestedValue(newData.formData, action.path, action.value);
|
|
26
|
+
break;
|
|
27
|
+
case 'setError':
|
|
28
|
+
newData.errors = updateNestedValue(newData.errors, action.path, action.value);
|
|
29
|
+
break;
|
|
30
|
+
case 'reset':
|
|
31
|
+
newData.formData = {};
|
|
32
|
+
newData.errors = {};
|
|
33
|
+
break;
|
|
34
|
+
case 'setErrors':
|
|
35
|
+
newData.errors = {...action.errors};
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
return newData;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const {
|
|
42
|
+
Provider: FormStoreProvider,
|
|
43
|
+
useStore: useFormStore,
|
|
44
|
+
useDispatch: useFormDispatch,
|
|
45
|
+
useSubmit,
|
|
46
|
+
} = createStoreContext(reducer, initialState);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type Reducer<S, A> = (state: S, action: A) => S;
|
|
2
|
+
|
|
3
|
+
export function createStore<S, A>(
|
|
4
|
+
reducer: Reducer<S, A>,
|
|
5
|
+
initialState: S
|
|
6
|
+
) {
|
|
7
|
+
let state = initialState;
|
|
8
|
+
const listeners = new Set<() => void>();
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
getState(): S {
|
|
12
|
+
return state;
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
dispatch(action: A) {
|
|
16
|
+
state = reducer(state, action);
|
|
17
|
+
listeners.forEach(l => l());
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
subscribe(listener: () => void) {
|
|
21
|
+
listeners.add(listener);
|
|
22
|
+
return () => listeners.delete(listener);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type Store<S, A> = ReturnType<typeof createStore<S, A>>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {useFormStore, useFormDispatch} from "@/shared/model/store";
|
|
2
|
+
import {getNestedValue} from "@/shared/utils";
|
|
3
|
+
|
|
4
|
+
export const BuilderElement = (props: any) => {
|
|
5
|
+
const Element = props.element;
|
|
6
|
+
const field = props.field;
|
|
7
|
+
const currentFieldPath = [...props.path, field.name];
|
|
8
|
+
const value = useFormStore((s=> getNestedValue(s.formData, currentFieldPath)));
|
|
9
|
+
const errors = useFormStore((s=> getNestedValue(s.errors, currentFieldPath)));
|
|
10
|
+
const dispatch= useFormDispatch();
|
|
11
|
+
return <Element
|
|
12
|
+
field={{...field}}
|
|
13
|
+
builder={props.DynamicBuilder}
|
|
14
|
+
path={currentFieldPath}
|
|
15
|
+
plugins={props.plugins}
|
|
16
|
+
value={value}
|
|
17
|
+
errors={errors}
|
|
18
|
+
onChange={(value: any) => {
|
|
19
|
+
dispatch({
|
|
20
|
+
type: 'setValue',
|
|
21
|
+
path: currentFieldPath,
|
|
22
|
+
value
|
|
23
|
+
});
|
|
24
|
+
dispatch({
|
|
25
|
+
type: 'setError',
|
|
26
|
+
path: currentFieldPath,
|
|
27
|
+
value: ''
|
|
28
|
+
});
|
|
29
|
+
}}
|
|
30
|
+
/>;
|
|
31
|
+
}
|
|
@@ -1,63 +1,33 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import type {TDynamicBuilder} from "@/shared/types/common";
|
|
2
|
+
import {BuilderElement} from "@/widgets/dynamicBuilder/element";
|
|
3
|
+
// import {getNestedValue} from "@/shared/utils";
|
|
4
|
+
// import {useCallback} from "react";
|
|
5
|
+
// import {VisibleCore} from "@/shared/lib/VisibleCore";
|
|
6
|
+
// import {use} from "@/shared/model/store";
|
|
6
7
|
|
|
7
8
|
export const DynamicBuilder: TDynamicBuilder = (props) => {
|
|
8
|
-
const { state, dispatch } = useStore();
|
|
9
9
|
const path = props.path ?? [];
|
|
10
|
-
|
|
11
|
-
const isShow = useCallback((field: FormFieldConfig) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
|
|
11
|
+
// const isShow = useCallback((field: FormFieldConfig) => {
|
|
12
|
+
// let result = true;
|
|
13
|
+
//
|
|
14
|
+
// if (field.viewConfig) {
|
|
15
|
+
// result = VisibleCore.isVisible(field.viewConfig, state.formData);
|
|
16
|
+
// }
|
|
17
|
+
// return result;
|
|
18
|
+
// }, [state]);
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
return props.layout.map((field, index) => {
|
|
22
|
+
const FormElement = props.plugins[field.type];
|
|
23
|
+
|
|
24
|
+
if (!FormElement) {
|
|
25
|
+
console.warn(`Неизвестный тип поля: ${field.type}. Проверьте formRegistry.`);
|
|
26
|
+
return null;
|
|
16
27
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const FormElement = props.plugins[field.type];
|
|
23
|
-
|
|
24
|
-
if (!FormElement) {
|
|
25
|
-
console.warn(`Неизвестный тип поля: ${field.type}. Проверьте formRegistry.`);
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const currentFieldPath = [...path, field.id];
|
|
30
|
-
|
|
31
|
-
const currentValue = getNestedValue(state.formData, currentFieldPath);
|
|
32
|
-
const currentErrors = getNestedValue(state.errors, currentFieldPath) as (Record<string, string> | undefined);
|
|
33
|
-
|
|
34
|
-
return isShow(field) ? (
|
|
35
|
-
<FormElement
|
|
36
|
-
key={field.id}
|
|
37
|
-
field={{...field}}
|
|
38
|
-
builder={DynamicBuilder}
|
|
39
|
-
path={currentFieldPath}
|
|
40
|
-
plugins={props.plugins}
|
|
41
|
-
value={currentValue}
|
|
42
|
-
errors={currentErrors}
|
|
43
|
-
onChange={(value: any) => {
|
|
44
|
-
dispatch({
|
|
45
|
-
type: 'setValue',
|
|
46
|
-
payload: {
|
|
47
|
-
path: currentFieldPath,
|
|
48
|
-
value
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
dispatch({
|
|
53
|
-
type: 'setError',
|
|
54
|
-
payload: {
|
|
55
|
-
path: currentFieldPath,
|
|
56
|
-
undefined
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
}}
|
|
60
|
-
/>
|
|
61
|
-
) : null;
|
|
62
|
-
})
|
|
28
|
+
|
|
29
|
+
// const currentValue = getNestedValue(state.formData, currentFieldPath);
|
|
30
|
+
// const currentErrors = getNestedValue(state.errors, currentFieldPath) as (Record<string, string> | undefined);
|
|
31
|
+
return <BuilderElement key={`${field.name}${index}`} element={FormElement} path={path} field={field}/>
|
|
32
|
+
})
|
|
63
33
|
}
|
package/tsconfig.json
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"declaration": true,
|
|
4
|
+
"jsx": "react-jsx",
|
|
5
|
+
"emitDeclarationOnly": true,
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"target": "ESNext",
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"verbatimModuleSyntax": true,
|
|
11
|
+
"moduleDetection": "force",
|
|
12
|
+
"baseUrl": ".",
|
|
13
|
+
"paths": {
|
|
14
|
+
"@/*": ["./src/*"],
|
|
15
|
+
"~/*": ["./*"]
|
|
16
|
+
},
|
|
17
|
+
"strict": true,
|
|
18
|
+
"noUnusedLocals": true,
|
|
19
|
+
"noUnusedParameters": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"noUncheckedSideEffectImports": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["src/**/*", "./*.ts", "./public/*.tsx"]
|
|
7
24
|
}
|
package/tsdown.config.ts
ADDED
package/vite.config.ts
CHANGED
|
@@ -1,20 +1,11 @@
|
|
|
1
|
-
import { defineConfig } from '
|
|
2
|
-
import
|
|
3
|
-
import tailwindcss from "@tailwindcss/vite";
|
|
4
|
-
import dts from 'vite-plugin-dts';
|
|
1
|
+
import { defineConfig } from 'vitest/config'
|
|
2
|
+
import tsconfigPaths from 'vite-tsconfig-paths'
|
|
5
3
|
|
|
6
4
|
export default defineConfig({
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
fileName: "index",
|
|
13
|
-
formats: ['es']
|
|
14
|
-
},
|
|
15
|
-
copyPublicDir: false,
|
|
16
|
-
rollupOptions: {
|
|
17
|
-
external: ['react', 'react/jsx-runtime'],
|
|
18
|
-
}
|
|
5
|
+
root: "./public",
|
|
6
|
+
plugins: [tsconfigPaths({ root: __dirname })],
|
|
7
|
+
test: {
|
|
8
|
+
globals: true,
|
|
9
|
+
environment: 'jsdom',
|
|
19
10
|
}
|
|
20
11
|
})
|