@idem.agency/form-builder 0.0.11 → 0.0.13

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.
Files changed (44) hide show
  1. package/README.md +922 -12
  2. package/dist/index.cjs +272 -88
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +18 -16
  5. package/dist/index.d.mts +18 -16
  6. package/dist/index.mjs +273 -89
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +5 -2
  9. package/src/index.ts +19 -5
  10. package/CHANGELOG.md +0 -8
  11. package/eslint.config.js +0 -23
  12. package/public/index.html +0 -13
  13. package/public/main.tsx +0 -90
  14. package/src/app/debug.tsx +0 -0
  15. package/src/app/index.tsx +0 -80
  16. package/src/app/test.css +0 -1
  17. package/src/entity/inputs/index.ts +0 -2
  18. package/src/entity/inputs/ui/group/index.tsx +0 -28
  19. package/src/entity/inputs/ui/input/index.tsx +0 -31
  20. package/src/shared/hook/useUpdateEffect.tsx +0 -23
  21. package/src/shared/lib/VisibleCore.spec.ts +0 -103
  22. package/src/shared/lib/VisibleCore.ts +0 -43
  23. package/src/shared/lib/validation/core.spec.ts +0 -103
  24. package/src/shared/lib/validation/core.ts +0 -79
  25. package/src/shared/lib/validation/rules/base.ts +0 -10
  26. package/src/shared/lib/validation/rules/confirm.spec.ts +0 -17
  27. package/src/shared/lib/validation/rules/confirm.ts +0 -32
  28. package/src/shared/lib/validation/rules/email.spec.ts +0 -12
  29. package/src/shared/lib/validation/rules/email.ts +0 -13
  30. package/src/shared/lib/validation/rules/require.spec.ts +0 -13
  31. package/src/shared/lib/validation/rules/require.ts +0 -12
  32. package/src/shared/model/builder/createContext.tsx +0 -40
  33. package/src/shared/model/builder/index.ts +0 -6
  34. package/src/shared/model/index.ts +0 -12
  35. package/src/shared/model/store/createStoreContext.tsx +0 -74
  36. package/src/shared/model/store/index.ts +0 -46
  37. package/src/shared/model/store/store.ts +0 -27
  38. package/src/shared/types/common.ts +0 -79
  39. package/src/shared/utils.ts +0 -25
  40. package/src/widgets/dynamicBuilder/element.tsx +0 -31
  41. package/src/widgets/dynamicBuilder/index.tsx +0 -33
  42. package/tsconfig.json +0 -24
  43. package/tsdown.config.ts +0 -10
  44. package/vite.config.ts +0 -11
@@ -1,103 +0,0 @@
1
- import {expect, test} from "vitest";
2
- import {ValidationCore} from "./core.ts";
3
- import {FormGroup, TextField} from "../../../widgets/form";
4
-
5
- import type {FormElementRegistry, FormFieldConfig} from "../../../index.ts";
6
- import {RequireRule} from "./rules/require.ts";
7
-
8
- const testFormConfig: FormFieldConfig[] = [{
9
- id: 'test',
10
- name: 'test',
11
- type: 'text',
12
- label: '',
13
- }];
14
-
15
- const testFormPlugins: FormElementRegistry = {
16
- text: TextField,
17
- group: FormGroup,
18
- };
19
-
20
- test('Validation core empty data with required has error', () => {
21
- testFormConfig[0].validation = ['required'];
22
- const validationCode = new ValidationCore(testFormConfig, testFormPlugins);
23
- expect(validationCode.validate({})).not.toBe({})
24
- });
25
-
26
- test('Validation core has data not errors', () => {
27
- testFormConfig[0].validation = ['required'];
28
- const validationCode = new ValidationCore(testFormConfig, testFormPlugins);
29
- expect(validationCode.validate({test: 'Alex'})).toEqual({});
30
- });
31
-
32
- test('Validation core: rule as class', () => {
33
- testFormConfig[0].validation = [new RequireRule()];
34
- const validationCode = new ValidationCore(testFormConfig, testFormPlugins);
35
- expect(validationCode.validate({test: 'Alex'})).toEqual({});
36
- });
37
-
38
- test('Validation core: rule as string with params', () => {
39
- testFormConfig[0].validation = ['confirm:confirm'];
40
- const validationCode = new ValidationCore(testFormConfig, testFormPlugins);
41
- expect(validationCode.validate({test: 'Alex', confirm: 'Alex'})).toEqual({});
42
- });
43
-
44
- test('Validation core: rule as string with params get correct message', () => {
45
- testFormConfig[0].validation = ['required', 'email', 'confirm:confirm,подтверждением'];
46
- const validationCode = new ValidationCore(testFormConfig, testFormPlugins);
47
- expect(validationCode.validate({test: 1})).toStrictEqual({
48
- "test": {
49
- "required": "Ошибка валидации поля",
50
- "email": "Ошибка валидации поля",
51
- "confirm": "Поле не совпадает с подтверждением",
52
- }
53
- });
54
- });
55
-
56
-
57
- test('Validation core: check empty data', () => {
58
- testFormConfig[0].validation = ['required', 'email'];
59
- const validationCode = new ValidationCore(testFormConfig, testFormPlugins);
60
- expect(validationCode.validate({})).toStrictEqual({
61
- "test": {
62
- "required": "Ошибка валидации поля",
63
- "email": "Ошибка валидации поля",
64
- }
65
- });
66
- });
67
-
68
- test('Validation core: check in deep data group', () => {
69
- const config: FormFieldConfig[] = [{
70
- id: 'group',
71
- name: 'group',
72
- type: 'group',
73
- label: '',
74
- fields: [
75
- {
76
- id: 'personal',
77
- name: 'personal',
78
- type: 'group',
79
- label: '',
80
- fields: [
81
- {
82
- id: 'test',
83
- name: 'test',
84
- type: 'text',
85
- label: '',
86
- validation: ['required', 'email']
87
- }
88
- ]
89
- }
90
- ]
91
- }];
92
- const validationCode = new ValidationCore(config, testFormPlugins);
93
- expect(validationCode.validate({})).toStrictEqual({
94
- "group": {
95
- "personal": {
96
- "test": {
97
- "required": "Ошибка валидации поля",
98
- "email": "Ошибка валидации поля",
99
- }
100
- }
101
- }
102
- });
103
- });
@@ -1,79 +0,0 @@
1
- import type {FormData, FormElementRegistry, FormFieldConfig, IRule} from "@/shared/types/common";
2
- import {getNestedValue, updateNestedValue} from "@/shared/utils";
3
- import {RequireRule} from "./rules/require";
4
- import {EmailRule} from "./rules/email";
5
- import {ConfirmRule} from "./rules/confirm";
6
-
7
- export class ValidationCore {
8
- private readonly layout: FormFieldConfig[];
9
- private readonly plugins: FormData;
10
- constructor(layout: FormFieldConfig[], plugins: FormElementRegistry) {
11
- this.layout = layout;
12
- this.plugins = plugins
13
- }
14
-
15
- private validationIsConfig(i: any): i is FormFieldConfig[] {
16
- return i && Array.isArray(i);
17
- }
18
-
19
- private checkRules(layout: FormFieldConfig[], data: FormData, path: string[] = [], errors = {}) {
20
- layout.map((i) => {
21
- const element = this.plugins[i.type];
22
- const currentPath = [...path, i.name];
23
-
24
- if (element && element.fieldProps) {
25
- element.fieldProps.forEach((fieldProps: string) => {
26
- if (this.validationIsConfig(i[fieldProps])) {
27
- errors = this.checkRules(i[fieldProps], data, currentPath, errors);
28
- }
29
- });
30
- }
31
-
32
- if (i.validation && Array.isArray(i.validation)) {
33
- i.validation.forEach(validation => {
34
- let validationResult: boolean | string = true;
35
- let ruleName: string;
36
-
37
- const value = getNestedValue(data, currentPath);
38
-
39
- let validationClass: IRule|null = null;
40
- if (typeof validation === "string") {
41
- const [validator, rawParams] = validation.split(':');
42
- const params: string[] = (rawParams ?? '').split(',');
43
- switch (validator) {
44
- case "required":
45
- validationClass = new RequireRule(...params);
46
- break;
47
- case "email":
48
- validationClass = new EmailRule(...params);
49
- break;
50
- case "confirm":
51
- validationClass = new ConfirmRule(...params);
52
- break;
53
- }
54
- } else {
55
- validationClass = validation;
56
- }
57
-
58
- if (validationClass) {
59
- ruleName = validationClass.getName(),
60
- validationResult = validationClass.validate(value, data);
61
- } else {
62
- ruleName = '';
63
- validationResult = true;
64
- }
65
-
66
- if (validationClass && validationResult !== true) {
67
- errors = updateNestedValue(errors, [...currentPath, ruleName], validationClass.message())
68
- }
69
- })
70
- }
71
- });
72
-
73
- return errors;
74
- }
75
-
76
- validate(data: FormData) {
77
- return this.checkRules(this.layout, data);
78
- }
79
- }
@@ -1,10 +0,0 @@
1
- export class BaseRule {
2
- params: string[];
3
- constructor(...params: string[]) {
4
- this.params = params;
5
- }
6
-
7
- message() {
8
- return 'Ошибка валидации поля';
9
- }
10
- }
@@ -1,17 +0,0 @@
1
- import {expect, test} from 'vitest'
2
- import {ConfirmRule} from "./confirm.ts"
3
- test('Confirm rule. Empty data', () => {
4
- const rule = new ConfirmRule('personal');
5
- expect(rule.validate('test', {})).toBe(false);
6
- })
7
-
8
- test('Confirm rule. Success data', () => {
9
- const rule = new ConfirmRule('personal');
10
- expect(rule.validate('test', {personal: 'test'})).toBe(true);
11
- })
12
-
13
- test('Confirm rule. Set error message', () => {
14
- const rule = new ConfirmRule('personal', 'Personal');
15
- const validator = rule.validate('test', {personal2: 'test'});
16
- expect(!validator ? rule.message() : '').toBe('Поле не совпадает с Personal');
17
- })
@@ -1,32 +0,0 @@
1
- import type {IRule, FormData} from "@/shared/types/common";
2
- import {BaseRule} from "./base";
3
- import {getNestedValue} from "@/shared/utils";
4
-
5
- export class ConfirmRule extends BaseRule implements IRule {
6
- protected confirmField?: string;
7
- protected confirmLabel?: string;
8
- constructor(...args: string[] ) {
9
- super(...args);
10
- const confirmField = args.shift();
11
- const confirmLabel = args.shift();
12
-
13
- if (confirmField) {
14
- this.confirmField = confirmField;
15
- }
16
-
17
- if (confirmLabel) {
18
- this.confirmLabel = confirmLabel;
19
- }
20
- }
21
- getName(): string {
22
- return "confirm";
23
- }
24
-
25
- validate(value: string, formData: FormData): boolean {
26
- return this.confirmField ? value == getNestedValue(formData, this.confirmField.split('.')) : false;
27
- }
28
-
29
- message(): string {
30
- return `Поле не совпадает с ${this.confirmLabel ?? this.confirmField}`;
31
- }
32
- }
@@ -1,12 +0,0 @@
1
- import {expect, test} from 'vitest'
2
- import {EmailRule} from "./email.ts";
3
-
4
- const rule = new EmailRule();
5
- test.each([
6
- ['test@test.ru', true],
7
- ['123123123', false],
8
- ['', false],
9
- ['123123123@', false],
10
- ])('Email validate %s -> %d', ($email, $result) => {
11
- expect(rule.validate($email)).toBe($result);
12
- });
@@ -1,13 +0,0 @@
1
- import type {IRule} from "@/shared/types/common";
2
- import {BaseRule} from "./base";
3
-
4
- export class EmailRule extends BaseRule implements IRule {
5
- private emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
6
- getName(): string {
7
- return "email";
8
- }
9
-
10
- validate(value: string): boolean {
11
- return this.emailRegex.test(value);
12
- }
13
- }
@@ -1,13 +0,0 @@
1
- import {expect, test} from 'vitest'
2
- import {RequireRule} from "./require.ts";
3
-
4
- const rule = new RequireRule();
5
-
6
- test.each([
7
- ['1123123', true],
8
- ['sdfsdfsdf', true],
9
- ['', false],
10
- [null, false],
11
- ])('Required validate %s -> %d', ($data, $result) => {
12
- expect(rule.validate($data as string)).toBe($result);
13
- });
@@ -1,12 +0,0 @@
1
- import type {IRule} from "@/shared/types/common";
2
- import {BaseRule} from "./base";
3
-
4
- export class RequireRule extends BaseRule implements IRule{
5
- getName(): string {
6
- return "required";
7
- }
8
-
9
- validate(value: string | null | undefined): boolean {
10
- return value !== undefined && value !== null && value.length > 0;
11
- }
12
- }
@@ -1,40 +0,0 @@
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
- }
@@ -1,6 +0,0 @@
1
- import {createStoreContext} from "@/shared/model/builder/createContext";
2
-
3
- export const {
4
- Provider: BuilderProvider,
5
- useBuilder,
6
- } = createStoreContext();
@@ -1,12 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- const fieldShema = z.object({
4
- name: z.string(),
5
- label: z.string().optional(),
6
- type: z.string(),
7
- });
8
-
9
- type TField = z.infer<typeof fieldShema>;
10
-
11
- export type { TField };
12
- export { fieldShema };
@@ -1,74 +0,0 @@
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
- }
@@ -1,46 +0,0 @@
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);
@@ -1,27 +0,0 @@
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>>;
@@ -1,79 +0,0 @@
1
- import {type FC, type ReactNode} from "react";
2
- import type { TField } from '@/shared/model';
3
-
4
- export type ConfigFunctionComponent<P> = FC<P> & {
5
- fieldProps?: string[];
6
- }
7
- export type RC<T = {}> = ConfigFunctionComponent<T & { className?: string, children?: ReactNode, }>;
8
- export type FormData = Record<string, any>;
9
-
10
- export interface IRule {
11
- getName: () => string;
12
- validate: (value: string, formData: FormData) => boolean;
13
- message: () => string;
14
- }
15
-
16
- export type FormFieldBase = {
17
- validation?: (string | IRule)[];
18
- viewConfig?: TGroupRules
19
- } & TField;
20
-
21
- export type FormFieldConfig = FormFieldBase & Record<string, any>;
22
-
23
- export type FormElementProps<F extends FormFieldConfig = FormFieldConfig> = {
24
- field: F;
25
- builder: TDynamicBuilder,
26
- plugins: FormElementRegistry,
27
- path: string[];
28
- value: any;
29
- errors?: Record<string, string>;
30
- onChange: (value: any) => void;
31
- };
32
-
33
- export type FormElementComponent<F extends FormFieldConfig = FormFieldConfig> = RC<FormElementProps<F>>;
34
- export type FormElementRegistry = Record<string, FormElementComponent<any>>;
35
-
36
-
37
- export type TFormBuilder = {
38
- formData?: FormData,
39
- className?: string,
40
- layout: FormFieldConfig[],
41
- plugins: FormElementRegistry,
42
- onChange?: (formData: any) => void,
43
- onSubmit?: (formData: any) => void,
44
- children?: ReactNode,
45
- }
46
-
47
- export type TDynamicBuilder = RC<{
48
- layout: FormFieldConfig[],
49
- plugins: FormElementRegistry,
50
- path?: string[],
51
- }>;
52
-
53
-
54
- export type TInRule = {
55
- operator: 'in',
56
- value: (string | number | boolean)[]
57
- } & TCommonRule
58
-
59
- export type TEqualRule = {
60
- operator: '=',
61
- value: string | number | boolean
62
- } & TCommonRule
63
-
64
- export type TCommonRule = {
65
- operator: string,
66
- field: string,
67
- value: any,
68
- };
69
-
70
- export type TGroupRules = {
71
- logic: 'and' | 'or',
72
- rules: (TGroupRules | TCommonRule)[]
73
- }
74
-
75
- export type FormBuilderRef = {
76
- reset: () => void;
77
- submit: () => void;
78
- errors: () => Record<string, any>
79
- }
@@ -1,25 +0,0 @@
1
- export function updateNestedValue(obj: any, path: string[], value: any): any {
2
- if (path.length === 0) {
3
- return value;
4
- }
5
-
6
- const newObj = Array.isArray(obj) ? [...obj] : { ...obj }; // Создаем копию
7
- const currentKey = path[0];
8
-
9
- if (path.length === 1) {
10
- // Последний элемент пути, просто обновляем значение
11
- newObj[currentKey] = value;
12
- } else {
13
- const remainingPath = path.slice(1);
14
- const nestedObj = newObj[currentKey];
15
-
16
- if (typeof nestedObj === 'undefined' || nestedObj === null) {
17
- newObj[currentKey] = typeof remainingPath[0] === 'number' ? [] : {};
18
- }
19
- newObj[currentKey] = updateNestedValue(newObj[currentKey], remainingPath, value);
20
- }
21
- return newObj;
22
- }
23
- export function getNestedValue(obj: any, path: string[]): any {
24
- return path.reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), obj);
25
- }
@@ -1,31 +0,0 @@
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,33 +0,0 @@
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";
7
-
8
- export const DynamicBuilder: TDynamicBuilder = (props) => {
9
- const path = props.path ?? [];
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;
27
- }
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
- })
33
- }