@idem.agency/form-builder 0.0.12 → 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.
- package/package.json +5 -2
- package/CHANGELOG.md +0 -16
- package/eslint.config.js +0 -23
- package/public/index.html +0 -13
- package/public/main.tsx +0 -97
- package/src/app/index.test.tsx +0 -137
- package/src/app/index.tsx +0 -34
- package/src/app/test.css +0 -1
- package/src/entity/dynamicBuilder/element.tsx +0 -71
- package/src/entity/dynamicBuilder/index.tsx +0 -21
- package/src/entity/dynamicBuilder/model/createBuilderContext.tsx +0 -52
- package/src/entity/dynamicBuilder/model/index.ts +0 -7
- package/src/entity/inputs/index.ts +0 -2
- package/src/entity/inputs/ui/group/index.test.tsx +0 -46
- package/src/entity/inputs/ui/group/index.tsx +0 -26
- package/src/entity/inputs/ui/input/index.test.tsx +0 -73
- package/src/entity/inputs/ui/input/index.tsx +0 -31
- package/src/plugins/validation/index.ts +0 -39
- package/src/plugins/validation/rules/confirm.test.ts +0 -23
- package/src/plugins/validation/rules/confirm.ts +0 -11
- package/src/plugins/validation/rules/email.test.ts +0 -20
- package/src/plugins/validation/rules/email.ts +0 -10
- package/src/plugins/validation/rules/index.ts +0 -5
- package/src/plugins/validation/rules/required.test.ts +0 -24
- package/src/plugins/validation/rules/required.ts +0 -9
- package/src/plugins/validation/types/index.ts +0 -7
- package/src/plugins/validation/validation.test.ts +0 -69
- package/src/plugins/validation/validation.ts +0 -57
- package/src/plugins/visibility/core.ts +0 -56
- package/src/plugins/visibility/index.ts +0 -25
- package/src/plugins/visibility/types.ts +0 -20
- package/src/shared/hook/useUpdateEffect.tsx +0 -23
- package/src/shared/model/index.ts +0 -12
- package/src/shared/model/plugins/EventBus.ts +0 -15
- package/src/shared/model/plugins/FieldRegistry.ts +0 -15
- package/src/shared/model/plugins/HookRegistry.ts +0 -21
- package/src/shared/model/plugins/MiddlewareRegistry.ts +0 -17
- package/src/shared/model/plugins/PipelineRegistry.ts +0 -73
- package/src/shared/model/plugins/PluginManager.ts +0 -84
- package/src/shared/model/plugins/context.tsx +0 -10
- package/src/shared/model/plugins/hooks.ts +0 -22
- package/src/shared/model/plugins/index.ts +0 -23
- package/src/shared/model/plugins/types.ts +0 -99
- package/src/shared/model/store/createStoreContext.tsx +0 -70
- package/src/shared/model/store/index.test.tsx +0 -113
- package/src/shared/model/store/index.tsx +0 -96
- package/src/shared/model/store/store.test.ts +0 -64
- package/src/shared/model/store/store.ts +0 -38
- package/src/shared/test-utils.tsx +0 -33
- package/src/shared/types/common.ts +0 -45
- package/src/shared/utils.test.ts +0 -55
- package/src/shared/utils.ts +0 -36
- package/src/widgets/form/form.tsx +0 -59
- package/tsconfig.json +0 -24
- package/tsdown.config.ts +0 -10
- package/vite.config.ts +0 -12
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import {type FC, type ReactNode} from "react";
|
|
2
|
-
import type { TField } from '@/shared/model';
|
|
3
|
-
import type { IPlugin } from '@/shared/model/plugins/types';
|
|
4
|
-
|
|
5
|
-
export type ConfigFunctionComponent<P> = FC<P> & {
|
|
6
|
-
fieldProps?: string[];
|
|
7
|
-
}
|
|
8
|
-
export type RC<T = {}> = ConfigFunctionComponent<T & { className?: string, children?: ReactNode, }>;
|
|
9
|
-
export type FormData = Record<string, any>;
|
|
10
|
-
|
|
11
|
-
export type FormFieldConfig = TField & Record<string, any>;
|
|
12
|
-
|
|
13
|
-
export type FormElementProps<F extends FormFieldConfig = FormFieldConfig> = {
|
|
14
|
-
field: F;
|
|
15
|
-
path: string;
|
|
16
|
-
value: any;
|
|
17
|
-
errors?: Record<string, string>;
|
|
18
|
-
onChange: (value: any) => void;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export type FormElementComponent<F extends FormFieldConfig = FormFieldConfig> = RC<FormElementProps<F>>;
|
|
22
|
-
export type FormElementRegistry = Record<string, FormElementComponent<any>>;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
export type TFormBuilder = {
|
|
26
|
-
formData?: FormData,
|
|
27
|
-
className?: string,
|
|
28
|
-
layout: FormFieldConfig[],
|
|
29
|
-
fields: FormElementRegistry,
|
|
30
|
-
plugins?: IPlugin[],
|
|
31
|
-
onChange?: (formData: any) => void,
|
|
32
|
-
onSubmit?: (formData: any) => void,
|
|
33
|
-
children?: ReactNode,
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export type TDynamicBuilder = RC<{
|
|
37
|
-
layout: FormFieldConfig[],
|
|
38
|
-
path?: string,
|
|
39
|
-
}>;
|
|
40
|
-
|
|
41
|
-
export type FormBuilderRef = {
|
|
42
|
-
reset: () => void;
|
|
43
|
-
submit: () => Promise<void>;
|
|
44
|
-
errors: () => Record<string, any>
|
|
45
|
-
}
|
package/src/shared/utils.test.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { updateNestedValue, getNestedValue } from './utils'
|
|
3
|
-
|
|
4
|
-
describe('updateNestedValue', () => {
|
|
5
|
-
it('updates top-level key', () => {
|
|
6
|
-
const result = updateNestedValue({ a: 1 }, 'a', 2)
|
|
7
|
-
expect(result).toEqual({ a: 2 })
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
it('updates nested key (a.b.c)', () => {
|
|
11
|
-
const result = updateNestedValue({ a: { b: { c: 1 } } }, 'a.b.c', 42)
|
|
12
|
-
expect(result).toEqual({ a: { b: { c: 42 } } })
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('returns value directly when path is empty', () => {
|
|
16
|
-
const result = updateNestedValue({ a: 1 }, '', 'newValue')
|
|
17
|
-
expect(result).toBe('newValue')
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('auto-creates intermediate objects', () => {
|
|
21
|
-
const result = updateNestedValue({}, 'a.b.c', 'val')
|
|
22
|
-
expect(result).toEqual({ a: { b: { c: 'val' } } })
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('throws on __proto__ path', () => {
|
|
26
|
-
expect(() => updateNestedValue({}, '__proto__.x', 1)).toThrow()
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('throws on constructor path', () => {
|
|
30
|
-
expect(() => updateNestedValue({}, 'constructor', 1)).toThrow()
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('throws on prototype path', () => {
|
|
34
|
-
expect(() => updateNestedValue({}, 'prototype', 1)).toThrow()
|
|
35
|
-
})
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
describe('getNestedValue', () => {
|
|
39
|
-
it('gets top-level key', () => {
|
|
40
|
-
expect(getNestedValue({ a: 1 }, 'a')).toBe(1)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('gets nested key (a.b.c)', () => {
|
|
44
|
-
expect(getNestedValue({ a: { b: { c: 42 } } }, 'a.b.c')).toBe(42)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('returns undefined for missing key', () => {
|
|
48
|
-
expect(getNestedValue({ a: 1 }, 'b')).toBeUndefined()
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('returns obj when path is empty', () => {
|
|
52
|
-
const obj = { a: 1 }
|
|
53
|
-
expect(getNestedValue(obj, '')).toBe(obj)
|
|
54
|
-
})
|
|
55
|
-
})
|
package/src/shared/utils.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export function updateNestedValue(obj: any, path: string, value: any): any {
|
|
2
|
-
if (!path) return value;
|
|
3
|
-
return _updateByKeys(obj, path.split('.'), value);
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
function _updateByKeys(obj: any, keys: string[], value: any): any {
|
|
7
|
-
if (keys.length === 0) return value;
|
|
8
|
-
|
|
9
|
-
const newObj = Array.isArray(obj) ? [...obj] : { ...obj };
|
|
10
|
-
const currentKey = keys[0];
|
|
11
|
-
|
|
12
|
-
if (currentKey === '__proto__' || currentKey === 'constructor' || currentKey === 'prototype') {
|
|
13
|
-
throw new Error(`Forbidden path key: ${currentKey}`);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (keys.length === 1) {
|
|
17
|
-
newObj[currentKey] = value;
|
|
18
|
-
} else {
|
|
19
|
-
const remainingKeys = keys.slice(1);
|
|
20
|
-
const nestedObj = newObj[currentKey];
|
|
21
|
-
|
|
22
|
-
if (typeof nestedObj === 'undefined' || nestedObj === null) {
|
|
23
|
-
newObj[currentKey] = typeof remainingKeys[0] === 'number' ? [] : {};
|
|
24
|
-
}
|
|
25
|
-
newObj[currentKey] = _updateByKeys(newObj[currentKey], remainingKeys, value);
|
|
26
|
-
}
|
|
27
|
-
return newObj;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function getNestedValue(obj: any, path: string): any {
|
|
31
|
-
if (!path) return obj;
|
|
32
|
-
return path.split('.').reduce(
|
|
33
|
-
(acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined),
|
|
34
|
-
obj
|
|
35
|
-
);
|
|
36
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { DynamicBuilder, BuilderProvider } from "@/entity/dynamicBuilder";
|
|
4
|
-
import { forwardRef, useCallback, useEffect, useImperativeHandle } from "react";
|
|
5
|
-
import { useFormDispatch, useFormStoreInstance } from "@/shared/model/store";
|
|
6
|
-
import type { FormBuilderRef, TFormBuilder } from "@/shared/types/common";
|
|
7
|
-
import { useCallEvents, useRunPipeline } from '@/shared/model/plugins/hooks';
|
|
8
|
-
|
|
9
|
-
export const Form = forwardRef<FormBuilderRef, TFormBuilder>((props, ref) => {
|
|
10
|
-
const dispatch = useFormDispatch();
|
|
11
|
-
const store = useFormStoreInstance();
|
|
12
|
-
const callEvents = useCallEvents();
|
|
13
|
-
const runPipeline = useRunPipeline();
|
|
14
|
-
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
return store.subscribeSelector(
|
|
17
|
-
(s) => s.formData,
|
|
18
|
-
(formData) => props.onChange ? props.onChange?.(formData): null
|
|
19
|
-
);
|
|
20
|
-
}, [props.onChange]);
|
|
21
|
-
|
|
22
|
-
const submitHdl = useCallback(async () => {
|
|
23
|
-
if (props.onSubmit) {
|
|
24
|
-
const currentFormData = store.getState().formData;
|
|
25
|
-
await callEvents('form:submit:before', { formData: currentFormData });
|
|
26
|
-
const { formData: transformed } = await runPipeline('form:submit', { formData: currentFormData });
|
|
27
|
-
const { errors } = await runPipeline('form:validate', { formData: transformed, errors: {} });
|
|
28
|
-
|
|
29
|
-
if (Object.keys(errors).length === 0) {
|
|
30
|
-
props.onSubmit(transformed);
|
|
31
|
-
} else {
|
|
32
|
-
dispatch({ type: 'setErrors', errors });
|
|
33
|
-
}
|
|
34
|
-
await callEvents('form:submit:after', { formData: transformed, errors });
|
|
35
|
-
}
|
|
36
|
-
}, [props.onSubmit, callEvents, runPipeline, store, dispatch]);
|
|
37
|
-
|
|
38
|
-
useImperativeHandle(ref, () => ({
|
|
39
|
-
reset: () => dispatch({ type: 'reset' }),
|
|
40
|
-
submit: () => submitHdl(),
|
|
41
|
-
errors: () => store.getState().errors,
|
|
42
|
-
}), [submitHdl, dispatch, store]);
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<form
|
|
46
|
-
onSubmit={async (e) => {
|
|
47
|
-
e.preventDefault();
|
|
48
|
-
await submitHdl();
|
|
49
|
-
}}
|
|
50
|
-
className={props?.className}
|
|
51
|
-
>
|
|
52
|
-
<BuilderProvider fields={props.fields}>
|
|
53
|
-
<DynamicBuilder layout={props.layout} />
|
|
54
|
-
</BuilderProvider>
|
|
55
|
-
<input type="submit" style={{ display: 'none' }} />
|
|
56
|
-
{props.children}
|
|
57
|
-
</form>
|
|
58
|
-
);
|
|
59
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
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"]
|
|
24
|
-
}
|
package/tsdown.config.ts
DELETED
package/vite.config.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config'
|
|
2
|
-
import tsconfigPaths from 'vite-tsconfig-paths'
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
root: "./public",
|
|
6
|
-
plugins: [tsconfigPaths({ root: __dirname })],
|
|
7
|
-
test: {
|
|
8
|
-
globals: true,
|
|
9
|
-
environment: 'jsdom',
|
|
10
|
-
include: ['../src/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
|
|
11
|
-
}
|
|
12
|
-
})
|