@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 +1 @@
1
- {"version":3,"file":"index.mjs","names":["createStoreContext","createStoreContext"],"sources":["../src/shared/model/store/store.ts","../src/shared/model/store/createStoreContext.tsx","../src/shared/utils.ts","../src/shared/model/store/index.ts","../src/widgets/dynamicBuilder/element.tsx","../src/widgets/dynamicBuilder/index.tsx","../src/shared/model/builder/createContext.tsx","../src/shared/model/builder/index.ts","../src/app/index.tsx","../src/entity/inputs/ui/input/index.tsx","../src/entity/inputs/ui/group/index.tsx","../src/shared/model/index.ts"],"sourcesContent":["export type Reducer<S, A> = (state: S, action: A) => S;\n\nexport function createStore<S, A>(\n reducer: Reducer<S, A>,\n initialState: S\n) {\n let state = initialState;\n const listeners = new Set<() => void>();\n \n return {\n getState(): S {\n return state;\n },\n \n dispatch(action: A) {\n state = reducer(state, action);\n listeners.forEach(l => l());\n },\n \n subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n }\n };\n}\n\nexport type Store<S, A> = ReturnType<typeof createStore<S, A>>;\n","import {\n createContext,\n useContext,\n useRef,\n useSyncExternalStore\n} from \"react\";\nimport { createStore, type Reducer, type Store } from \"./store\";\n\nexport function createStoreContext<S, A>(\n reducer: Reducer<S, A>,\n initialState: S\n) {\n const StoreContext = createContext<Store<S, A> | null>(null);\n \n const Provider: React.FC<{ children: React.ReactNode }> = ({\n children\n }) => {\n const storeRef = useRef<Store<S, A>>(null);\n \n if (!storeRef.current) {\n storeRef.current = createStore(reducer, initialState);\n }\n\n return (\n <StoreContext.Provider value={storeRef.current}>\n {children}\n </StoreContext.Provider>\n );\n };\n \n function useStore<T>(\n selector: (state: S) => T\n ): T {\n const store = useContext(StoreContext);\n\n if (!store) {\n throw new Error(\"StoreProvider missing\");\n }\n \n return useSyncExternalStore(\n store.subscribe,\n () => selector(store.getState()),\n () => selector(store.getState())\n );\n }\n \n function useDispatch() {\n const store = useContext(StoreContext);\n \n if (!store) {\n throw new Error(\"StoreProvider missing\");\n }\n \n return store.dispatch;\n }\n \n function useSubmit(onSubmit: (state: S) => void) {\n const store = useContext(StoreContext);\n \n return () => {\n if (store) {\n const state = store.getState();\n onSubmit(state);\n }\n };\n }\n \n return {\n Provider,\n useStore,\n useSubmit,\n useDispatch\n };\n}\n","export function updateNestedValue(obj: any, path: string[], value: any): any {\n if (path.length === 0) {\n return value;\n }\n \n const newObj = Array.isArray(obj) ? [...obj] : { ...obj }; // Создаем копию\n const currentKey = path[0];\n \n if (path.length === 1) {\n // Последний элемент пути, просто обновляем значение\n newObj[currentKey] = value;\n } else {\n const remainingPath = path.slice(1);\n const nestedObj = newObj[currentKey];\n \n if (typeof nestedObj === 'undefined' || nestedObj === null) {\n newObj[currentKey] = typeof remainingPath[0] === 'number' ? [] : {};\n }\n newObj[currentKey] = updateNestedValue(newObj[currentKey], remainingPath, value);\n }\n return newObj;\n}\nexport function getNestedValue(obj: any, path: string[]): any {\n return path.reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), obj);\n}\n","import { createStoreContext } from \"./createStoreContext\";\nimport type {FormData} from \"@/shared/types/common\";\nimport {updateNestedValue} from \"@/shared/utils\";\n\ntype State = {\n formData: FormData\n errors: Record<string, any>\n};\n\ntype Action =\n | { type: \"setValue\"; path: string[]; value: unknown }\n | { type: \"setError\"; path: string[]; value?: string }\n | { type: \"reset\"; }\n | { type: \"setErrors\"; errors?: object };\n\nconst initialState: State = {\n formData: {},\n errors: {}\n};\n\nfunction reducer(state: State, action: Action): State {\n const newData = {...state};\n switch (action.type) {\n case 'setValue':\n newData.formData = updateNestedValue(newData.formData, action.path, action.value);\n break;\n case 'setError':\n newData.errors = updateNestedValue(newData.errors, action.path, action.value);\n break;\n case 'reset':\n newData.formData = {};\n newData.errors = {};\n break;\n case 'setErrors':\n newData.errors = {...action.errors};\n break;\n }\n return newData;\n}\n\nexport const {\n Provider: FormStoreProvider,\n useStore: useFormStore,\n useDispatch: useFormDispatch,\n useSubmit,\n} = createStoreContext(reducer, initialState);\n","import {useFormStore, useFormDispatch} from \"@/shared/model/store\";\nimport {getNestedValue} from \"@/shared/utils\";\n\nexport const BuilderElement = (props: any) => {\n const Element = props.element;\n const field = props.field;\n const currentFieldPath = [...props.path, field.name];\n const value = useFormStore((s=> getNestedValue(s.formData, currentFieldPath)));\n const errors = useFormStore((s=> getNestedValue(s.errors, currentFieldPath)));\n const dispatch= useFormDispatch();\n return <Element\n field={{...field}}\n builder={props.DynamicBuilder}\n path={currentFieldPath}\n plugins={props.plugins}\n value={value}\n errors={errors}\n onChange={(value: any) => {\n dispatch({\n type: 'setValue',\n path: currentFieldPath,\n value\n });\n dispatch({\n type: 'setError',\n path: currentFieldPath,\n value: ''\n });\n }}\n />;\n}\n","import type {TDynamicBuilder} from \"@/shared/types/common\";\nimport {BuilderElement} from \"@/widgets/dynamicBuilder/element\";\n// import {getNestedValue} from \"@/shared/utils\";\n// import {useCallback} from \"react\";\n// import {VisibleCore} from \"@/shared/lib/VisibleCore\";\n// import {use} from \"@/shared/model/store\";\n\nexport const DynamicBuilder: TDynamicBuilder = (props) => {\n const path = props.path ?? [];\n \n // const isShow = useCallback((field: FormFieldConfig) => {\n // let result = true;\n //\n // if (field.viewConfig) {\n // result = VisibleCore.isVisible(field.viewConfig, state.formData);\n // }\n // return result;\n // }, [state]);\n \n \n return props.layout.map((field, index) => {\n const FormElement = props.plugins[field.type];\n \n if (!FormElement) {\n console.warn(`Неизвестный тип поля: ${field.type}. Проверьте formRegistry.`);\n return null;\n }\n \n // const currentValue = getNestedValue(state.formData, currentFieldPath);\n // const currentErrors = getNestedValue(state.errors, currentFieldPath) as (Record<string, string> | undefined);\n return <BuilderElement key={`${field.name}${index}`} element={FormElement} path={path} field={field}/>\n })\n}\n","import React, {createContext, type ReactNode, useContext, useRef,} from \"react\";\nimport {DynamicBuilder} from \"@/widgets/dynamicBuilder\";\n\nexport function createStoreContext() {\n const BuilderContext = createContext<((layout: any, path: string[]|undefined, children?: ReactNode) => ReactNode) | null>(null);\n \n const Provider: React.FC<{ children: React.ReactNode, plugins: any }> = ({\n plugins,\n children\n }) => {\n const storeRef = useRef<(layout: any, path: string[]|undefined, children?: ReactNode) => ReactNode>(null);\n \n if (!storeRef.current) {\n storeRef.current = (layout: any, path: string[]|undefined, children?: ReactNode) => {\n return <DynamicBuilder layout={layout} path={path} plugins={plugins}>{children}</DynamicBuilder>\n };\n }\n \n return (\n <BuilderContext.Provider value={storeRef.current}>\n {children}\n </BuilderContext.Provider>\n );\n };\n \n function useBuilder(): (layout: any, path: string[]|undefined, children?: ReactNode) => ReactNode {\n const store = useContext(BuilderContext);\n \n if (!store) {\n throw new Error(\"StoreProvider missing\");\n }\n \n return store\n }\n \n return {\n Provider,\n useBuilder\n };\n}\n","import {createStoreContext} from \"@/shared/model/builder/createContext\";\n\nexport const {\n Provider: BuilderProvider,\n useBuilder,\n} = createStoreContext();\n","'use client';\n\nimport type {FormBuilderRef, TFormBuilder} from \"../shared/types/common\";\n// import {StoreContext, storeReducer,} from \"@/shared/model\";\n\nimport {DynamicBuilder} from \"../widgets/dynamicBuilder\";\nimport { forwardRef, useImperativeHandle} from \"react\";\n// import {ValidationCore} from \"@/shared/lib/validation/core\";\n// import { useUpdateEffect } from '@/shared/hook/useUpdateEffect';\nimport {FormStoreProvider, useSubmit} from \"@/shared/model/store\";\nimport {BuilderProvider} from \"@/shared/model/builder\";\n\nexport const FormBuilder = forwardRef<FormBuilderRef, TFormBuilder>((props, ref) => {\n // const validator = useMemo(() => {\n // return (new ValidationCore(props.layout, props.plugins))\n // }, [props.layout, props.plugins]);\n\n // const [state, dispatch] = useReducer(storeReducer, {\n // formData: props.formData ?? {},\n // errors: {},\n // });\n //\n // useUpdateEffect(() => {\n // if (props.onChange) {\n // props.onChange(state.formData);\n // }\n // }, [state.formData, props.onChange]);\n const submitHdl = useSubmit((data) => {\n if (props.onSubmit) {\n props.onSubmit(data);\n }\n });\n \n // const submit = () => {\n //\n // // const errors = validator.validate(state.formData);\n // // if (Object.keys(errors).length == 0) {\n // // if (props.onSubmit) {\n // // props.onSubmit(state.formData)\n // // }\n // // } else {\n // // dispatch({\n // // type: 'setErrors',\n // // payload: {\n // // errors\n // // }\n // // });\n // // }\n // }\n\n useImperativeHandle(ref, () => ({\n reset: () => {\n // dispatch({type: 'reset'})\n },\n submit: () => {\n submitHdl();\n },\n errors: () => {\n return {}\n // return state?.errors ?? {};\n }\n }), [props.onSubmit]);\n\n return <form onSubmit={(e) => {\n e.preventDefault();\n submitHdl();\n }}\n className={props?.className}\n >\n <FormStoreProvider>\n <BuilderProvider plugins={props.plugins}>\n <DynamicBuilder layout={props.layout} plugins={props.plugins} />\n </BuilderProvider>\n </FormStoreProvider>\n <input type=\"submit\" style={{ display: 'none' }}/>\n {props.children}\n </form>\n});\n\nexport default FormBuilder;\n","import type { FormElementProps, FormFieldConfig, FormFieldBase, RC } from '../../../../shared/types/common';\nimport { useId } from \"react\";\n\nexport type TextFieldConfig = FormFieldBase & { type: 'text' | 'email' | 'password'; placeholder?: string; };\n\nfunction isTextFieldConfig(field: FormFieldConfig): field is TextFieldConfig {\n return field.type === 'text' || field.type === 'email' || field.type === 'password';\n}\n\nexport const TextField: RC<FormElementProps> = ({ field, value, errors, onChange }) => {\n if (!isTextFieldConfig(field)) {\n console.warn(`TextField received an invalid field config for type: ${field.type}`);\n return null;\n }\n const id = useId();\n return (\n <div style={{ marginBottom: '15px' }}>\n <label htmlFor={id}>{field.label}:</label>\n <input\n type={field.type}\n id={id}\n name={field.name}\n placeholder={field.placeholder}\n value={value || ''}\n onChange={(e) => onChange(e.target.value)}\n style={{ borderColor: errors ? 'red' : '#ccc' }}\n />\n {errors && <p style={{ color: 'red', fontSize: '0.8em' }}>{Object.values(errors).join(', ')}</p>}\n </div>\n );\n};\n","import type {FormElementProps, FormFieldBase, FormFieldConfig, RC} from \"../../../../shared/types/common.ts\";\nimport clsx from \"clsx\";\nimport {useBuilder} from \"@/shared/model/builder\";\nexport type FormGroupConfig = FormFieldBase & { variant?: 'row' | 'col', fields: FormFieldConfig[] };\n\nfunction isGroupConfig(field: FormFieldConfig): field is FormGroupConfig {\n return field.fields;\n}\n\n\nexport const FormGroup: RC<FormElementProps<FormGroupConfig>> = ({field, path}) => {\n if (!isGroupConfig(field)) {\n return null;\n }\n const Builder = useBuilder()(field.fields, path);\n const variant = field.variant ?? 'col';\n \n const className = variant == 'col' ? 'flex-col' : 'flex-row';\n\n return <div>\n <div>{field.label}</div>\n <div className={clsx(className, 'flex')}>\n {Builder}\n </div>\n </div>;\n};\n\nFormGroup.fieldProps = ['fields'];\n","import { z } from 'zod';\n\nconst fieldShema = z.object({\n name: z.string(),\n label: z.string().optional(),\n type: z.string(),\n});\n\ntype TField = z.infer<typeof fieldShema>;\n\nexport type { TField };\nexport { fieldShema };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["base"],"sources":["../src/shared/model/store/store.ts","../src/shared/model/store/createStoreContext.tsx","../src/shared/utils.ts","../src/shared/model/store/index.tsx","../src/plugins/validation/rules/confirm.ts","../src/plugins/validation/rules/required.ts","../src/plugins/validation/rules/email.ts","../src/plugins/validation/rules/index.ts","../src/plugins/validation/validation.ts","../src/plugins/validation/provider.tsx","../src/plugins/validation/index.ts","../src/entity/dynamicBuilder/element.tsx","../src/entity/dynamicBuilder/model/createBuilderContext.tsx","../src/entity/dynamicBuilder/model/index.ts","../src/entity/dynamicBuilder/index.tsx","../src/widgets/form/form.tsx","../src/app/index.tsx","../src/entity/inputs/ui/input/index.tsx","../src/entity/inputs/ui/group/index.tsx","../src/shared/model/index.ts"],"sourcesContent":["export type Reducer<S, A> = (state: S, action: A) => S;\n\nexport function createStore<S, A>(\n reducer: Reducer<S, A>,\n initialState: S\n) {\n let state = initialState;\n const listeners = new Set<() => void>();\n \n return {\n getState(): S {\n return state;\n },\n \n dispatch(action: A) {\n state = reducer(state, action);\n listeners.forEach(l => l());\n },\n \n subscribe(listener: () => void) {\n listeners.add(listener);\n return () => { listeners.delete(listener) };\n },\n\n subscribeSelector<T>(selector: (state: S) => T, listener: (value: T) => void) {\n let prev = selector(state);\n return this.subscribe(() => {\n const next = selector(state);\n if (next !== prev) {\n prev = next;\n listener(next);\n }\n });\n }\n };\n}\n\nexport type Store<S, A> = ReturnType<typeof createStore<S, A>>;\n","import {\n createContext,\n useContext,\n useRef,\n useSyncExternalStore\n} from \"react\";\n\nimport { createStore, type Reducer, type Store } from \"./store\";\n\nexport function createStoreContext<S, A>(\n reducer: Reducer<S, A>,\n defaultState: S\n) {\n const StoreContext = createContext<Store<S, A> | null>(null);\n const Provider: React.FC<{ children: React.ReactNode, initialState?: S }> = ({\n children,\n initialState\n }) => {\n const storeRef = useRef<Store<S, A>>(createStore(reducer, initialState ?? defaultState));\n\n return (\n <StoreContext.Provider value={storeRef.current}>\n {children}\n </StoreContext.Provider>\n );\n };\n \n function useStore<T>(\n selector: (state: S) => T\n ): T {\n const store = useContext(StoreContext);\n\n if (!store) {\n throw new Error(\"StoreProvider missing\");\n }\n \n return useSyncExternalStore(\n store.subscribe,\n () => selector(store.getState()),\n () => selector(store.getState())\n );\n }\n \n function useDispatch() {\n const store = useContext(StoreContext);\n \n if (!store) {\n throw new Error(\"StoreProvider missing\");\n }\n \n return store.dispatch;\n }\n \n function useStoreInstance(): Store<S, A> {\n const store = useContext(StoreContext);\n\n if (!store) {\n throw new Error(\"StoreProvider missing\");\n }\n\n return store;\n }\n \n return {\n Provider,\n useStore,\n useDispatch,\n useStoreInstance,\n };\n}\n","export function updateNestedValue(obj: any, path: string, value: any): any {\n if (!path) return value;\n return _updateByKeys(obj, path.split('.'), value);\n}\n\nfunction _updateByKeys(obj: any, keys: string[], value: any): any {\n if (keys.length === 0) return value;\n\n const newObj = Array.isArray(obj) ? [...obj] : { ...obj };\n const currentKey = keys[0];\n\n if (currentKey === '__proto__' || currentKey === 'constructor' || currentKey === 'prototype') {\n throw new Error(`Forbidden path key: ${currentKey}`);\n }\n\n if (keys.length === 1) {\n newObj[currentKey] = value;\n } else {\n const remainingKeys = keys.slice(1);\n const nestedObj = newObj[currentKey];\n\n if (typeof nestedObj === 'undefined' || nestedObj === null) {\n newObj[currentKey] = typeof remainingKeys[0] === 'number' ? [] : {};\n }\n newObj[currentKey] = _updateByKeys(newObj[currentKey], remainingKeys, value);\n }\n return newObj;\n}\n\nexport function getNestedValue(obj: any, path: string): any {\n if (!path) return obj;\n return path.split('.').reduce(\n (acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined),\n obj\n );\n}\n","import { createStoreContext } from \"./createStoreContext\";\nimport type {FormData} from \"@/shared/types/common\";\nimport {updateNestedValue} from \"@/shared/utils\";\nimport type {Provider, ReactNode} from \"react\";\n\ntype State = {\n formData: FormData\n errors: Record<string, any>\n};\n\ntype Action =\n | { type: \"setValue\"; path: string; value: unknown }\n | { type: \"setFieldValue\"; path: string; value: unknown, errors?: string[] }\n | { type: \"setError\"; path: string; value?: string }\n | { type: \"reset\"; }\n | { type: \"setErrors\"; errors?: object };\n\n\nfunction reducer(state: State, action: Action): State {\n const newData = {...state};\n switch (action.type) {\n case 'setValue':\n newData.formData = updateNestedValue(newData.formData, action.path, action.value);\n break;\n case 'setFieldValue':\n newData.formData = updateNestedValue(newData.formData, action.path, action.value);\n newData.errors = updateNestedValue(newData.errors, action.path, action?.errors?.length ? action.errors : null);\n break;\n case 'setError':\n newData.errors = updateNestedValue(newData.errors, action.path, action.value);\n break;\n case 'reset':\n newData.formData = {};\n newData.errors = {};\n break;\n case 'setErrors':\n newData.errors = {...action.errors};\n break;\n }\n return newData;\n}\n\nconst {\n Provider,\n useStore: useFormStore,\n useDispatch: useFormDispatch,\n useStoreInstance: useFormStoreInstance,\n} = createStoreContext(reducer, { formData: {}, errors: {} });\n\nconst FormStoreProvider: React.FC<{ children: ReactNode; formData?: FormData }> = ({children, formData}) =>\n (<Provider initialState={{ formData: formData ?? {}, errors: {} }}>{children}</Provider>);\n\n\nexport { FormStoreProvider, useFormStore, useFormDispatch, useFormStoreInstance };\n","import type {IUserRule} from \"@/plugins/validation/types\";\nimport {getNestedValue} from \"@/shared/utils\";\n\nexport const confirm: IUserRule = {\n code: 'confirm',\n fn: (value, data, args) => {\n const attr = args[0] ?? false;\n return attr ? value == getNestedValue(data, attr) : false;\n },\n message: 'Поле не совпадает с ::attr(1)'\n};\n","import type {IUserRule} from \"@/plugins/validation/types\";\n\nexport const required: IUserRule = {\n code: 'required',\n fn: (value) => {\n return value !== undefined && value !== null && value.length > 0;\n },\n message: 'Поле обязательно для заполнения'\n};\n","import type {IUserRule} from \"@/plugins/validation/types\";\n\nconst emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/;\nexport const email: IUserRule = {\n code: 'email',\n fn: (value) => {\n return emailRegex.test(value);\n },\n message: 'Поле не является email'\n};\n","import { confirm } from \"@/plugins/validation/rules/confirm\";\nimport { required } from \"@/plugins/validation/rules/required\";\nimport {email} from \"@/plugins/validation/rules/email\";\n\nexport default [confirm, required, email]\n","import type {FormData} from \"@/shared/types/common\";\nimport base from './rules';\nimport type {IUserRule} from \"@/plugins/validation/types\";\nimport {getNestedValue} from \"@/shared/utils\";\n\nexport class Validation {\n private readonly registry: IUserRule[] = [];\n private registerFields: Record<string, string[]> = {};\n constructor(public readonly onSubmit: boolean, rules: IUserRule[] = []) {\n this.registry = [...rules, ...base];\n }\n \n \n private validateRule(rule: string, data: unknown, formData: FormData): string|null {\n const [code, rawArgs] = rule.split(':');\n const args = rawArgs ? rawArgs.split(',') : [];\n const userRule = this.registry.find(i => i.code == code);\n if (!userRule) {\n return null;\n }\n const validateStatus = userRule.fn(data, formData, args);\n return !validateStatus ? this.replaceMessageArgs(userRule.message, args) : null;\n }\n \n private replaceMessageArgs(message: string, args: string[] = []): string {\n const replaceArgs = args.reduce<Record<string, string>>((acc, arg, index) => {\n acc[`::attr(${index})`] = arg;\n return acc;\n }, {});\n\n return message.replace(/::attr\\(\\d\\)/g, i => replaceArgs[i]);\n }\n \n public registerField(path: string, validators: string[]): void {\n this.registerFields[path] = validators;\n }\n\n private _validate(rules: string[], data: unknown, formData: FormData): string[] {\n return rules.map((rule) => {\n return this.validateRule(rule, data, formData);\n }).filter(i => i !== null)\n }\n validate(rules: string[], data: unknown, formData: FormData): string[] {\n return !this.onSubmit ? this._validate(rules, data, formData) : [];\n }\n \n public validateAll(formData: FormData): Record<string, string[]> {\n return Object.entries(this.registerFields).reduce<Record<string, string[]>>((acc, [path, rules]) => {\n const value = getNestedValue(formData, path);\n const validationMessage = this._validate(rules, value, formData);\n if (validationMessage.length) {\n acc[path] = validationMessage;\n }\n return acc;\n } , {});\n }\n}\n","import React, {createContext, useContext, useMemo} from \"react\";\nimport type {IUserRule} from \"@/plugins/validation/types\";\nimport {Validation} from \"@/plugins/validation/validation\";\nimport type {FormData, FormFieldConfig} from \"@/shared/types/common\";\n\nexport type TValidator = {\n rules?: IUserRule[],\n onSubmit?: boolean,\n}\n\nexport function createValidationProvider() {\n const Context = createContext<Validation | null>(null);\n const Provider: React.FC<{\n children: React.ReactNode,\n validator?: TValidator,\n }> = ({\n validator = {\n onSubmit: false,\n rules: []\n },\n children\n }) => {\n const core = useMemo(() => new Validation(validator.onSubmit ?? false, validator.rules ?? []), [validator]);\n \n return (\n <Context.Provider value={core}>\n {children}\n </Context.Provider>\n );\n }\n \n const useValidate = (rules ?: string[]) => {\n const core = useContext(Context);\n if (!core) {\n throw new Error(\"ValidationProvider missing\");\n }\n return (data: unknown, formData: FormData) => {\n return core.validate(rules ?? [], data, formData);\n }\n }\n \n const useRegister = (currentFieldPath: string, field: FormFieldConfig) => {\n const core = useContext(Context);\n if (!core) {\n throw new Error(\"ValidationProvider missing\");\n }\n \n core.registerField(currentFieldPath, field.validation ?? []);\n\n return useValidate(field.validation);\n }\n \n const useSubmitValidation = () => {\n const core = useContext(Context);\n if (!core) {\n throw new Error(\"ValidationProvider missing\");\n }\n \n return (formData: FormData) => core.validateAll(formData);\n }\n \n return {\n Provider,\n useRegister,\n useSubmitValidation,\n }\n}\n","import {createValidationProvider } from \"./provider\";\nexport { type TValidator } from \"./provider\";\n\nexport const {\n Provider: ValidationProvider,\n useRegister,\n useSubmitValidation\n} = createValidationProvider();\n","import {useFormStore, useFormDispatch, useFormStoreInstance} from \"@/shared/model/store\";\nimport {getNestedValue} from \"@/shared/utils\";\n\nimport type { FormFieldConfig, FormElementComponent} from \"@/shared/types/common\";\nimport {useRegister} from \"@/plugins/validation\";\n\ntype BuilderElementProps = {\n element: FormElementComponent;\n field: FormFieldConfig;\n path: string;\n};\n\n\nexport const DynamicBuilderElement = (props: BuilderElementProps) => {\n const Element = props.element;\n const field = props.field;\n const currentFieldPath = props.path ? `${props.path}.${field.name}` : field.name;\n const value = useFormStore((s=> getNestedValue(s.formData, currentFieldPath)));\n const errors = useFormStore((s=> s.errors[currentFieldPath]));\n const dispatch= useFormDispatch();\n const store = useFormStoreInstance();\n const validator = useRegister(currentFieldPath, field);\n return <Element\n field={{...field}}\n path={currentFieldPath}\n value={value}\n errors={errors}\n onChange={(value: any) => {\n const data = store.getState().formData;\n const errors = validator(value, data);\n dispatch({\n type: 'setFieldValue',\n path: currentFieldPath,\n value,\n errors\n });\n }}\n />;\n}\n","import React, {createContext, type ReactNode, useCallback, useContext} from \"react\";\nimport {DynamicBuilder} from \"@/entity/dynamicBuilder\";\nimport type {FormElementRegistry} from \"~/src\";\n\nexport type TFnBuilder = (layout: any, path: string | undefined, children?: ReactNode) => ReactNode;\nexport function createBuilderContext() {\n const BuilderContext = createContext<{\n builder: TFnBuilder,\n fields: FormElementRegistry,\n } | null>(null);\n \n const Provider: React.FC<{ children: React.ReactNode, fields: FormElementRegistry }> = ({\n fields,\n children\n }) => {\n const builder = useCallback<TFnBuilder>((layout, path, children) => {\n return <DynamicBuilder layout={layout} path={path}>{children}</DynamicBuilder>\n }, [fields]);\n \n return (\n <BuilderContext.Provider value={{builder, fields}}>\n {children}\n </BuilderContext.Provider>\n );\n };\n \n function useBuilder(): TFnBuilder {\n const store = useContext(BuilderContext);\n \n if (!store) {\n throw new Error(\"StoreProvider missing\");\n }\n \n return store.builder\n }\n \n function useFields(): FormElementRegistry {\n const store = useContext(BuilderContext);\n \n if (!store) {\n throw new Error(\"StoreProvider missing\");\n }\n \n return store.fields\n }\n \n return {\n Provider,\n useBuilder,\n useFields,\n };\n}\n","import { createBuilderContext } from \"./createBuilderContext\";\n\nexport const {\n Provider: BuilderProvider,\n useBuilder,\n useFields,\n} = createBuilderContext();\n","import type {TDynamicBuilder} from \"@/shared/types/common\";\nimport {DynamicBuilderElement} from \"./element\";\nimport { useFields } from \"@/entity/dynamicBuilder/model\";\n\nexport const DynamicBuilder: TDynamicBuilder = (props) => {\n const path = props.path ?? '';\n const fields = useFields();\n\n return props.layout.map((field, index) => {\n const FormElement = fields[field.type];\n \n if (!FormElement) {\n console.warn(`Неизвестный тип поля: ${field.type}. Проверьте formRegistry.`);\n return null;\n }\n\n return <DynamicBuilderElement key={`${field.name}-${index}`} element={FormElement} path={path} field={field}/>\n })\n}\n\nexport * from './model';\n","'use client';\n\nimport {DynamicBuilder, BuilderProvider} from \"@/entity/dynamicBuilder\";\nimport {forwardRef, useCallback, useEffect, useImperativeHandle, useRef} from \"react\";\nimport {useFormDispatch, useFormStoreInstance} from \"@/shared/model/store\";\nimport type {FormBuilderRef, TFormBuilder} from \"@/shared/types/common\";\nimport { useSubmitValidation } from \"@/plugins/validation\";\n\nexport const Form = forwardRef<FormBuilderRef, TFormBuilder>((props, ref) => {\n const dispatch = useFormDispatch();\n const store = useFormStoreInstance();\n const changeRef = useRef(props.onChange);\n \n useEffect(() => {\n return store.subscribeSelector(\n (s) => s.formData,\n (formData) => changeRef.current?.(formData)\n );\n }, [store]);\n \n const validation = useSubmitValidation();\n \n const submitHdl = useCallback(() => {\n if (props.onSubmit) {\n const errors = validation(store.getState().formData);\n if (Object.keys(errors).length == 0) {\n props.onSubmit(store.getState().formData);\n } else {\n dispatch({type: 'setErrors', errors })\n }\n }\n }, [props.onSubmit, validation]);\n \n useImperativeHandle(ref, () => ({\n reset: () => dispatch({type: 'reset'}),\n submit: () => submitHdl(),\n errors: () => store.getState().errors,\n }), [submitHdl, dispatch, store]);\n \n return (\n <form onSubmit={(e) => {\n e.preventDefault();\n submitHdl();\n }}\n className={props?.className}\n >\n <BuilderProvider fields={props.fields}>\n <DynamicBuilder layout={props.layout}/>\n </BuilderProvider>\n <input type=\"submit\" style={{display: 'none'}}/>\n {props.children}\n </form>\n );\n});\n","import type {FormBuilderRef, TFormBuilder} from \"../shared/types/common\";\nimport {forwardRef} from \"react\";\nimport {FormStoreProvider} from \"@/shared/model/store\";\n\nimport { Form } from \"@/widgets/form/form\";\nimport {ValidationProvider} from \"@/plugins/validation\";\n\nexport const FormBuilder = forwardRef<FormBuilderRef, TFormBuilder>((props, ref) => {\n const {formData, ...innerProps} = props;\n\n return <FormStoreProvider formData={formData}>\n <ValidationProvider validator={props.validator}>\n <Form {...innerProps} ref={ref}></Form>\n </ValidationProvider>\n </FormStoreProvider>\n});\n\nexport default FormBuilder;\n","import type { FormElementProps, FormFieldConfig, FormFieldBase, RC } from '../../../../shared/types/common';\nimport { useId } from \"react\";\n\nexport type TextFieldConfig = FormFieldBase & { type: 'text' | 'email' | 'password'; placeholder?: string; };\n\nfunction isTextFieldConfig(field: FormFieldConfig): field is TextFieldConfig {\n return field.type === 'text' || field.type === 'email' || field.type === 'password';\n}\n\nexport const TextField: RC<FormElementProps> = ({ field, value, errors, onChange }) => {\n if (!isTextFieldConfig(field)) {\n console.warn(`TextField received an invalid field config for type: ${field.type}`);\n return null;\n }\n const id = useId();\n return (\n <div style={{ marginBottom: '15px' }}>\n <label htmlFor={id}>{field.label}:</label>\n <input\n type={field.type}\n id={id}\n name={field.name}\n placeholder={field.placeholder}\n value={value || ''}\n onChange={(e) => onChange(e.target.value)}\n style={{ borderColor: errors ? 'red' : '#ccc' }}\n />\n {errors && <p style={{ color: 'red', fontSize: '0.8em' }}>{Object.values(errors).join(', ')}</p>}\n </div>\n );\n};\n","import type {FormElementProps, FormFieldBase, FormFieldConfig, RC} from \"../../../../shared/types/common.ts\";\nimport clsx from \"clsx\";\nimport {useBuilder} from \"@/entity/dynamicBuilder\";\nexport type FormGroupConfig = FormFieldBase & { variant?: 'row' | 'col', fields: FormFieldConfig[] };\n\nfunction isGroupConfig(field: FormFieldConfig): field is FormGroupConfig {\n return Array.isArray(field.fields);\n}\n\n\nexport const FormGroup: RC<FormElementProps<FormGroupConfig>> = ({field, path}) => {\n if (!isGroupConfig(field)) {\n return null;\n }\n const Builder = useBuilder()(field.fields, path);\n const variant = field.variant ?? 'col';\n \n const className = variant == 'col' ? 'flex-col' : 'flex-row';\n\n return <div>\n <div>{field.label}</div>\n <div className={clsx(className, 'flex')}>\n {Builder}\n </div>\n </div>;\n};\n","import { z } from 'zod';\n\nconst fieldShema = z.object({\n name: z.string(),\n label: z.string().optional(),\n type: z.string(),\n});\n\ntype TField = z.infer<typeof fieldShema>;\n\nexport type { TField };\nexport { fieldShema };\n"],"mappings":";;;;;;AAEA,SAAgB,YACd,SACA,cACA;CACA,IAAI,QAAQ;CACZ,MAAM,4BAAY,IAAI,KAAiB;AAEvC,QAAO;EACL,WAAc;AACZ,UAAO;;EAGT,SAAS,QAAW;AAClB,WAAQ,QAAQ,OAAO,OAAO;AAC9B,aAAU,SAAQ,MAAK,GAAG,CAAC;;EAG7B,UAAU,UAAsB;AAC9B,aAAU,IAAI,SAAS;AACvB,gBAAa;AAAE,cAAU,OAAO,SAAS;;;EAG3C,kBAAqB,UAA2B,UAA8B;GAC5E,IAAI,OAAO,SAAS,MAAM;AAC1B,UAAO,KAAK,gBAAgB;IAC1B,MAAM,OAAO,SAAS,MAAM;AAC5B,QAAI,SAAS,MAAM;AACjB,YAAO;AACP,cAAS,KAAK;;KAEhB;;EAEL;;;;;ACzBH,SAAgB,mBACd,SACA,cACA;CACA,MAAM,eAAe,cAAkC,KAAK;CAC5D,MAAM,YAAuE,EAChB,UACA,mBACI;EAC/D,MAAM,WAAW,OAAoB,YAAY,SAAS,gBAAgB,aAAa,CAAC;AAExF,SACE,oBAAC,aAAa;GAAS,OAAO,SAAS;GACpC;IACqB;;CAI5B,SAAS,SACP,UACG;EACH,MAAM,QAAQ,WAAW,aAAa;AAEtC,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,wBAAwB;AAG1C,SAAO,qBACL,MAAM,iBACA,SAAS,MAAM,UAAU,CAAC,QAC1B,SAAS,MAAM,UAAU,CAAC,CACjC;;CAGH,SAAS,cAAc;EACrB,MAAM,QAAQ,WAAW,aAAa;AAEtC,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,wBAAwB;AAG1C,SAAO,MAAM;;CAGf,SAAS,mBAAgC;EACvC,MAAM,QAAQ,WAAW,aAAa;AAEtC,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,wBAAwB;AAG1C,SAAO;;AAGT,QAAO;EACL;EACA;EACA;EACA;EACD;;;;;ACpEH,SAAgB,kBAAkB,KAAU,MAAc,OAAiB;AACzE,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,cAAc,KAAK,KAAK,MAAM,IAAI,EAAE,MAAM;;AAGnD,SAAS,cAAc,KAAU,MAAgB,OAAiB;AAChE,KAAI,KAAK,WAAW,EAAG,QAAO;CAE9B,MAAM,SAAS,MAAM,QAAQ,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,GAAG,KAAK;CACzD,MAAM,aAAa,KAAK;AAExB,KAAI,eAAe,eAAe,eAAe,iBAAiB,eAAe,YAC/E,OAAM,IAAI,MAAM,uBAAuB,aAAa;AAGtD,KAAI,KAAK,WAAW,EAClB,QAAO,cAAc;MAChB;EACL,MAAM,gBAAgB,KAAK,MAAM,EAAE;EACnC,MAAM,YAAY,OAAO;AAEzB,MAAI,OAAO,cAAc,eAAe,cAAc,KACpD,QAAO,cAAc,OAAO,cAAc,OAAO,WAAW,EAAE,GAAG,EAAE;AAErE,SAAO,cAAc,cAAc,OAAO,aAAa,eAAe,MAAM;;AAE9E,QAAO;;AAGT,SAAgB,eAAe,KAAU,MAAmB;AAC1D,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,KAAK,MAAM,IAAI,CAAC,QACpB,KAAK,QAAS,OAAO,IAAI,SAAS,SAAY,IAAI,OAAO,QAC1D,IACD;;;;;AChBH,SAAS,QAAQ,OAAc,QAAuB;CACpD,MAAM,UAAU,EAAC,GAAG,OAAM;AAC1B,SAAQ,OAAO,MAAf;EACE,KAAK;AACH,WAAQ,WAAW,kBAAkB,QAAQ,UAAU,OAAO,MAAM,OAAO,MAAM;AACjF;EACF,KAAK;AACH,WAAQ,WAAW,kBAAkB,QAAQ,UAAU,OAAO,MAAM,OAAO,MAAM;AACjF,WAAQ,SAAS,kBAAkB,QAAQ,QAAQ,OAAO,MAAM,QAAQ,QAAQ,SAAS,OAAO,SAAS,KAAK;AAC9G;EACF,KAAK;AACH,WAAQ,SAAS,kBAAkB,QAAQ,QAAQ,OAAO,MAAM,OAAO,MAAM;AAC7E;EACF,KAAK;AACH,WAAQ,WAAW,EAAE;AACrB,WAAQ,SAAS,EAAE;AACnB;EACF,KAAK;AACH,WAAQ,SAAS,EAAC,GAAG,OAAO,QAAO;AACnC;;AAEJ,QAAO;;AAGT,MAAM,EACJ,UACA,UAAU,cACV,aAAa,iBACb,kBAAkB,yBAChB,mBAAmB,SAAS;CAAE,UAAU,EAAE;CAAE,QAAQ,EAAE;CAAE,CAAC;AAE7D,MAAM,qBAA6E,EAAC,UAAU,eAC3F,oBAAC;CAAU,cAAc;EAAE,UAAU,YAAY,EAAE;EAAE,QAAQ,EAAE;EAAE;CAAG;EAAoB;;;;AC/C3F,MAAa,UAAqB;CAChC,MAAM;CACN,KAAK,OAAO,MAAM,SAAS;EACzB,MAAM,OAAO,KAAK,MAAM;AACxB,SAAO,OAAO,SAAS,eAAe,MAAM,KAAK,GAAG;;CAEtD,SAAS;CACV;;;;ACRD,MAAa,WAAsB;CACjC,MAAM;CACN,KAAK,UAAU;AACb,SAAO,UAAU,UAAa,UAAU,QAAQ,MAAM,SAAS;;CAEjE,SAAS;CACV;;;;ACND,MAAM,aAAa;AACnB,MAAa,QAAmB;CAC9B,MAAM;CACN,KAAK,UAAU;AACb,SAAO,WAAW,KAAK,MAAM;;CAE/B,SAAS;CACV;;;;ACLD,oBAAe;CAAC;CAAS;CAAU;CAAM;;;;ACCzC,IAAa,aAAb,MAAwB;CACtB,AAAiB,WAAwB,EAAE;CAC3C,AAAQ,iBAA2C,EAAE;CACrD,YAAY,AAAgB,UAAmB,QAAqB,EAAE,EAAE;EAA5C;AAC1B,OAAK,WAAW,CAAC,GAAG,OAAO,GAAGA,cAAK;;CAIrC,AAAQ,aAAa,MAAc,MAAe,UAAiC;EACjF,MAAM,CAAC,MAAM,WAAW,KAAK,MAAM,IAAI;EACvC,MAAM,OAAO,UAAU,QAAQ,MAAM,IAAI,GAAG,EAAE;EAC9C,MAAM,WAAW,KAAK,SAAS,MAAK,MAAK,EAAE,QAAQ,KAAK;AACxD,MAAI,CAAC,SACH,QAAO;AAGT,SAAO,CADgB,SAAS,GAAG,MAAM,UAAU,KAAK,GAC/B,KAAK,mBAAmB,SAAS,SAAS,KAAK,GAAG;;CAG7E,AAAQ,mBAAmB,SAAiB,OAAiB,EAAE,EAAU;EACvE,MAAM,cAAc,KAAK,QAAgC,KAAK,KAAK,UAAU;AAC3E,OAAI,UAAU,MAAM,MAAM;AAC1B,UAAO;KACN,EAAE,CAAC;AAEN,SAAO,QAAQ,QAAQ,kBAAiB,MAAK,YAAY,GAAG;;CAG9D,AAAO,cAAc,MAAc,YAA4B;AAC7D,OAAK,eAAe,QAAQ;;CAG9B,AAAQ,UAAU,OAAiB,MAAe,UAA8B;AAC9E,SAAO,MAAM,KAAK,SAAS;AACzB,UAAO,KAAK,aAAa,MAAM,MAAM,SAAS;IAC9C,CAAC,QAAO,MAAK,MAAM,KAAK;;CAE5B,SAAS,OAAiB,MAAe,UAA8B;AACrE,SAAO,CAAC,KAAK,WAAW,KAAK,UAAU,OAAO,MAAM,SAAS,GAAG,EAAE;;CAGpE,AAAO,YAAY,UAA8C;AAC/D,SAAO,OAAO,QAAQ,KAAK,eAAe,CAAC,QAAkC,KAAK,CAAC,MAAM,WAAW;GAClG,MAAM,QAAQ,eAAe,UAAU,KAAK;GAC5C,MAAM,oBAAoB,KAAK,UAAU,OAAO,OAAO,SAAS;AAChE,OAAI,kBAAkB,OACpB,KAAI,QAAQ;AAEd,UAAO;KACL,EAAE,CAAC;;;;;;AC5CX,SAAgB,2BAA2B;CACzC,MAAM,UAAU,cAAiC,KAAK;CACtD,MAAM,YAGA,EACE,YAAY;EACV,UAAU;EACV,OAAO,EAAE;EACV,EACD,eACI;EACV,MAAM,OAAO,cAAc,IAAI,WAAW,UAAU,YAAY,OAAO,UAAU,SAAS,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC;AAE3G,SACE,oBAAC,QAAQ;GAAS,OAAO;GACtB;IACgB;;CAIvB,MAAM,eAAe,UAAsB;EACzC,MAAM,OAAO,WAAW,QAAQ;AAChC,MAAI,CAAC,KACH,OAAM,IAAI,MAAM,6BAA6B;AAE/C,UAAQ,MAAe,aAAuB;AAC5C,UAAO,KAAK,SAAS,SAAS,EAAE,EAAE,MAAM,SAAS;;;CAIrD,MAAM,eAAe,kBAA0B,UAA2B;EACxE,MAAM,OAAO,WAAW,QAAQ;AAChC,MAAI,CAAC,KACH,OAAM,IAAI,MAAM,6BAA6B;AAG/C,OAAK,cAAc,kBAAkB,MAAM,cAAc,EAAE,CAAC;AAE5D,SAAO,YAAY,MAAM,WAAW;;CAGtC,MAAM,4BAA6B;EACjC,MAAM,OAAO,WAAW,QAAQ;AAChC,MAAI,CAAC,KACH,OAAM,IAAI,MAAM,6BAA6B;AAG/C,UAAQ,aAAuB,KAAK,YAAY,SAAS;;AAG3D,QAAO;EACL;EACA;EACA;EACD;;;;;AC9DH,MAAa,EACX,UAAU,oBACV,aACA,wBACE,0BAA0B;;;;ACM9B,MAAa,yBAAyB,UAA+B;CACnE,MAAM,UAAU,MAAM;CACtB,MAAM,QAAQ,MAAM;CACpB,MAAM,mBAAmB,MAAM,OAAO,GAAG,MAAM,KAAK,GAAG,MAAM,SAAS,MAAM;CAC5E,MAAM,QAAQ,eAAc,MAAI,eAAe,EAAE,UAAU,iBAAiB,EAAE;CAC9E,MAAM,SAAS,eAAc,MAAI,EAAE,OAAO,mBAAmB;CAC7D,MAAM,WAAU,iBAAiB;CACjC,MAAM,QAAQ,sBAAsB;CACpC,MAAM,YAAY,YAAY,kBAAkB,MAAM;AACtD,QAAO,oBAAC;EACN,OAAO,EAAC,GAAG,OAAM;EACjB,MAAM;EACC;EACC;EACR,WAAW,UAAe;GACxB,MAAM,OAAO,MAAM,UAAU,CAAC;AAE9B,YAAS;IACP,MAAM;IACN,MAAM;IACN;IACA,QALa,UAAU,OAAO,KAAK;IAMpC,CAAC;;GAEJ;;;;;AChCJ,SAAgB,uBAAuB;CACrC,MAAM,iBAAiB,cAGb,KAAK;CAEf,MAAM,YAAkF,EACd,QACA,eACI;EAC5E,MAAM,UAAU,aAAyB,QAAQ,MAAM,aAAa;AAClE,UAAO,oBAAC;IAAuB;IAAc;IAAO;KAA0B;KAC7E,CAAC,OAAO,CAAC;AAEZ,SACE,oBAAC,eAAe;GAAS,OAAO;IAAC;IAAS;IAAO;GAC9C;IACuB;;CAI9B,SAAS,aAAyB;EAChC,MAAM,QAAQ,WAAW,eAAe;AAExC,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,wBAAwB;AAG1C,SAAO,MAAM;;CAGf,SAAS,YAAiC;EACxC,MAAM,QAAQ,WAAW,eAAe;AAExC,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,wBAAwB;AAG1C,SAAO,MAAM;;AAGf,QAAO;EACL;EACA;EACA;EACD;;;;;AChDH,MAAa,EACX,UAAU,iBACV,YACA,cACE,sBAAsB;;;;ACF1B,MAAa,kBAAmC,UAAU;CACxD,MAAM,OAAO,MAAM,QAAQ;CAC3B,MAAM,SAAS,WAAW;AAE1B,QAAO,MAAM,OAAO,KAAK,OAAO,UAAU;EACxC,MAAM,cAAc,OAAO,MAAM;AAEjC,MAAI,CAAC,aAAa;AAChB,WAAQ,KAAK,yBAAyB,MAAM,KAAK,2BAA2B;AAC5E,UAAO;;AAGT,SAAO,oBAAC;GAAqD,SAAS;GAAmB;GAAa;KAAnE,GAAG,MAAM,KAAK,GAAG,QAA0D;GAC9G;;;;;ACTJ,MAAa,OAAO,YAA0C,OAAO,QAAQ;CAC3E,MAAM,WAAW,iBAAiB;CAClC,MAAM,QAAQ,sBAAsB;CACpC,MAAM,YAAY,OAAO,MAAM,SAAS;AAExC,iBAAgB;AACd,SAAO,MAAM,mBACV,MAAM,EAAE,WACR,aAAa,UAAU,UAAU,SAAS,CAC5C;IACA,CAAC,MAAM,CAAC;CAEX,MAAM,aAAa,qBAAqB;CAExC,MAAM,YAAY,kBAAkB;AAClC,MAAI,MAAM,UAAU;GAClB,MAAM,SAAS,WAAW,MAAM,UAAU,CAAC,SAAS;AACpD,OAAI,OAAO,KAAK,OAAO,CAAC,UAAU,EAChC,OAAM,SAAS,MAAM,UAAU,CAAC,SAAS;OAEzC,UAAS;IAAC,MAAM;IAAa;IAAQ,CAAC;;IAGzC,CAAC,MAAM,UAAU,WAAW,CAAC;AAEhC,qBAAoB,YAAY;EAC9B,aAAa,SAAS,EAAC,MAAM,SAAQ,CAAC;EACtC,cAAc,WAAW;EACzB,cAAc,MAAM,UAAU,CAAC;EAChC,GAAG;EAAC;EAAW;EAAU;EAAM,CAAC;AAEjC,QACE,qBAAC;EAAK,WAAW,MAAM;AACrB,KAAE,gBAAgB;AAClB,cAAW;;EAEP,WAAW,OAAO;;GAEtB,oBAAC;IAAgB,QAAQ,MAAM;cAC7B,oBAAC,kBAAe,QAAQ,MAAM,SAAS;KACvB;GAClB,oBAAC;IAAM,MAAK;IAAS,OAAO,EAAC,SAAS,QAAO;KAAG;GAC/C,MAAM;;GACF;EAET;;;;AC9CF,MAAa,cAAc,YAA0C,OAAO,QAAQ;CAClF,MAAM,EAAC,UAAU,GAAG,eAAc;AAElC,QAAO,oBAAC;EAA4B;YAClC,oBAAC;GAAmB,WAAW,MAAM;aACnC,oBAAC;IAAK,GAAI;IAAiB;KAAY;IACpB;GACH;EACpB;;;;ACVF,SAAS,kBAAkB,OAAkD;AAC3E,QAAO,MAAM,SAAS,UAAU,MAAM,SAAS,WAAW,MAAM,SAAS;;AAG3E,MAAa,aAAmC,EAAE,OAAO,OAAO,QAAQ,eAAe;AACrF,KAAI,CAAC,kBAAkB,MAAM,EAAE;AAC7B,UAAQ,KAAK,wDAAwD,MAAM,OAAO;AAClF,SAAO;;CAET,MAAM,KAAK,OAAO;AAClB,QACE,qBAAC;EAAI,OAAO,EAAE,cAAc,QAAQ;;GAClC,qBAAC;IAAM,SAAS;eAAK,MAAM,OAAM;KAAS;GAC1C,oBAAC;IACC,MAAM,MAAM;IACR;IACJ,MAAM,MAAM;IACZ,aAAa,MAAM;IACnB,OAAO,SAAS;IAChB,WAAW,MAAM,SAAS,EAAE,OAAO,MAAM;IACzC,OAAO,EAAE,aAAa,SAAS,QAAQ,QAAQ;KAC/C;GACD,UAAU,oBAAC;IAAE,OAAO;KAAE,OAAO;KAAO,UAAU;KAAS;cAAG,OAAO,OAAO,OAAO,CAAC,KAAK,KAAK;KAAK;;GAC5F;;;;;ACvBV,SAAS,cAAc,OAAkD;AACvE,QAAO,MAAM,QAAQ,MAAM,OAAO;;AAIpC,MAAa,aAAoD,EAAC,OAAO,WAAU;AACjF,KAAI,CAAC,cAAc,MAAM,CACvB,QAAO;CAET,MAAM,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;CAGhD,MAAM,aAFU,MAAM,WAAW,UAEJ,QAAQ,aAAa;AAElD,QAAO,qBAAC,oBACN,oBAAC,mBAAK,MAAM,QAAY,EACxB,oBAAC;EAAI,WAAW,KAAK,WAAW,OAAO;YACpC;GACG,IACF;;;;;ACtBR,MAAM,aAAa,EAAE,OAAO;CAC1B,MAAM,EAAE,QAAQ;CAChB,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,MAAM,EAAE,QAAQ;CACjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idem.agency/form-builder",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "Построитель форм",
5
5
  "main": "src/index.ts",
6
6
  "author": "idem.agency",
@@ -23,6 +23,9 @@
23
23
  "react-dom": "19.2.3",
24
24
  "zod": "4.3.6"
25
25
  },
26
+ "files": [
27
+ "dist"
28
+ ],
26
29
  "devDependencies": {
27
30
  "@eslint/js": "9.39.2",
28
31
  "@tailwindcss/vite": "4.1.18",
@@ -43,5 +46,5 @@
43
46
  "vite-tsconfig-paths": "6.0.5",
44
47
  "vitest": "4.0.17"
45
48
  },
46
- "gitHead": "5c2212eb5f6bc9bf7e422e68b90700cfecd03f41"
49
+ "gitHead": "700ffe97c5801f382c16129af4ecb85fd476e030"
47
50
  }
package/src/index.ts CHANGED
@@ -1,6 +1,20 @@
1
- import FormBuilder from "@/app";
2
- import type { IRule, TGroupRules, FormFieldConfig, FormElementProps, FormElementRegistry, FormBuilderRef} from "./shared/types/common.ts";
3
- export {FormGroup, TextField} from "./entity/inputs";
4
- export type {IRule, TGroupRules, FormFieldConfig, FormElementProps, FormElementRegistry, FormBuilderRef};
5
- export { FormBuilder };
1
+ import { createValidationPlugin } from './plugins/validation';
2
+ import { createVisibilityPlugin } from './plugins/visibility';
3
+ import { FormGroup, TextField } from "./entity/inputs";
4
+
5
+ export type { FormFieldConfig, FormElementProps, FormElementRegistry, FormBuilderRef } from "./shared/types/common.ts";
6
+ export type { IPlugin, IPluginContext } from './shared/model/plugins/types';
7
+ export type { TValidator, IUserRule } from './plugins/validation';
8
+
9
+ export { FormBuilder } from "@/app";
6
10
  export { fieldShema } from './shared/model';
11
+
12
+ export const plugins = {
13
+ createValidationPlugin,
14
+ createVisibilityPlugin
15
+ };
16
+
17
+ export const inputs = {
18
+ FormGroup,
19
+ TextField
20
+ };
package/CHANGELOG.md DELETED
@@ -1,8 +0,0 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
-
6
- ## [0.0.11](https://gitlab.idem.agency/idem-project/front-package/compare/@idem.agency/form-builder@0.0.10...@idem.agency/form-builder@0.0.11) (2026-02-09)
7
-
8
- **Note:** Version bump only for package @idem.agency/form-builder
package/eslint.config.js DELETED
@@ -1,23 +0,0 @@
1
- import js from '@eslint/js'
2
- import globals from 'globals'
3
- import reactHooks from 'eslint-plugin-react-hooks'
4
- import reactRefresh from 'eslint-plugin-react-refresh'
5
- import tseslint from 'typescript-eslint'
6
- import { globalIgnores } from 'eslint/config'
7
-
8
- export default tseslint.config([
9
- globalIgnores(['dist']),
10
- {
11
- files: ['**/*.{ts,tsx}'],
12
- extends: [
13
- js.configs.recommended,
14
- tseslint.configs.recommended,
15
- reactHooks.configs['recommended-latest'],
16
- reactRefresh.configs.vite,
17
- ],
18
- languageOptions: {
19
- ecmaVersion: 2020,
20
- globals: globals.browser,
21
- },
22
- },
23
- ])
package/public/index.html DELETED
@@ -1,13 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Виджет для генерации форм</title>
8
- </head>
9
- <body>
10
- <div id="root"></div>
11
- <script type="module" src="main.tsx"></script>
12
- </body>
13
- </html>
package/public/main.tsx DELETED
@@ -1,90 +0,0 @@
1
- import {StrictMode} from 'react'
2
- import {createRoot} from 'react-dom/client'
3
- import { FormBuilder, FormGroup, TextField } from "~/src/index";
4
- import type {FormBuilderRef, FormElementRegistry, FormFieldConfig} from "~/src/index";
5
- import {useRef} from "react";
6
-
7
- function Debug() {
8
-
9
- const plugins: FormElementRegistry = {
10
- text: TextField,
11
- group: FormGroup,
12
- }
13
-
14
- const layout: FormFieldConfig[] = [
15
- {
16
- name: 'username',
17
- label: 'Имя пользователя',
18
- type: 'text',
19
- placeholder: 'Введите ваше имя',
20
- defaultValue: 'Мое имя',
21
- validation: ['required', 'confirm:personal.username_2,Имя 2 пользователя'],
22
- viewConfig: {
23
- logic: 'and',
24
- rules: [
25
- {
26
- field: 'personal.username_2',
27
- operator: 'in',
28
- value: ['Alex']
29
- },
30
- ]
31
- }
32
- },
33
- {
34
- name: 'personal',
35
- label: 'Группа',
36
- type: 'group',
37
- variant: 'col',
38
- fields: [
39
- {
40
- id: 'username_2',
41
- name: 'username_2',
42
- label: 'Имя 2 пользователя',
43
- type: 'text',
44
- placeholder: 'Введите ваше имя 2',
45
- defaultValue: 'Мое имя',
46
- validation: ['required'],
47
- },
48
- {
49
- name: 'username_3',
50
- label: 'Имя 3 пользователя',
51
- type: 'text',
52
- placeholder: 'Введите ваше имя 2',
53
- defaultValue: 'Мое имя',
54
- validation: ['required'],
55
- },
56
- {
57
- name: 'username_4',
58
- label: 'Имя 4 пользователя',
59
- type: 'text',
60
- placeholder: 'Введите ваше имя 2',
61
- defaultValue: 'Мое имя',
62
- validation: ['required'],
63
- },
64
- ],
65
- },
66
- ];
67
-
68
- const ref = useRef<FormBuilderRef>(null);
69
- return <div>
70
- <FormBuilder ref={ref} layout={layout} plugins={plugins} formData={{
71
- personal: {
72
- username_2: "Alex"
73
- }
74
- }}></FormBuilder>
75
- <button onClick={() => {
76
- ref.current?.reset();
77
- }}>Сбросить
78
- </button>
79
- <button onClick={() => {
80
- ref.current?.submit();
81
- }}>Отправить
82
- </button>
83
- </div>
84
- }
85
-
86
- createRoot(document.getElementById('root')!).render(
87
- <StrictMode>
88
- <Debug/>
89
- </StrictMode>,
90
- )
package/src/app/debug.tsx DELETED
File without changes
package/src/app/index.tsx DELETED
@@ -1,80 +0,0 @@
1
- 'use client';
2
-
3
- import type {FormBuilderRef, TFormBuilder} from "../shared/types/common";
4
- // import {StoreContext, storeReducer,} from "@/shared/model";
5
-
6
- import {DynamicBuilder} from "../widgets/dynamicBuilder";
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";
12
-
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]);
17
-
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);
31
- }
32
- });
33
-
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
- // }
50
-
51
- useImperativeHandle(ref, () => ({
52
- reset: () => {
53
- // dispatch({type: 'reset'})
54
- },
55
- submit: () => {
56
- submitHdl();
57
- },
58
- errors: () => {
59
- return {}
60
- // return state?.errors ?? {};
61
- }
62
- }), [props.onSubmit]);
63
-
64
- return <form onSubmit={(e) => {
65
- e.preventDefault();
66
- submitHdl();
67
- }}
68
- className={props?.className}
69
- >
70
- <FormStoreProvider>
71
- <BuilderProvider plugins={props.plugins}>
72
- <DynamicBuilder layout={props.layout} plugins={props.plugins} />
73
- </BuilderProvider>
74
- </FormStoreProvider>
75
- <input type="submit" style={{ display: 'none' }}/>
76
- {props.children}
77
- </form>
78
- });
79
-
80
- export default FormBuilder;
package/src/app/test.css DELETED
@@ -1 +0,0 @@
1
- @import "tailwindcss";
@@ -1,2 +0,0 @@
1
- export * from './ui/input/index'
2
- export * from './ui/group/index'
@@ -1,28 +0,0 @@
1
- import type {FormElementProps, FormFieldBase, FormFieldConfig, RC} from "../../../../shared/types/common.ts";
2
- import clsx from "clsx";
3
- import {useBuilder} from "@/shared/model/builder";
4
- export type FormGroupConfig = FormFieldBase & { variant?: 'row' | 'col', fields: FormFieldConfig[] };
5
-
6
- function isGroupConfig(field: FormFieldConfig): field is FormGroupConfig {
7
- return field.fields;
8
- }
9
-
10
-
11
- export const FormGroup: RC<FormElementProps<FormGroupConfig>> = ({field, path}) => {
12
- if (!isGroupConfig(field)) {
13
- return null;
14
- }
15
- const Builder = useBuilder()(field.fields, path);
16
- const variant = field.variant ?? 'col';
17
-
18
- const className = variant == 'col' ? 'flex-col' : 'flex-row';
19
-
20
- return <div>
21
- <div>{field.label}</div>
22
- <div className={clsx(className, 'flex')}>
23
- {Builder}
24
- </div>
25
- </div>;
26
- };
27
-
28
- FormGroup.fieldProps = ['fields'];
@@ -1,31 +0,0 @@
1
- import type { FormElementProps, FormFieldConfig, FormFieldBase, RC } from '../../../../shared/types/common';
2
- import { useId } from "react";
3
-
4
- export type TextFieldConfig = FormFieldBase & { type: 'text' | 'email' | 'password'; placeholder?: string; };
5
-
6
- function isTextFieldConfig(field: FormFieldConfig): field is TextFieldConfig {
7
- return field.type === 'text' || field.type === 'email' || field.type === 'password';
8
- }
9
-
10
- export const TextField: RC<FormElementProps> = ({ field, value, errors, onChange }) => {
11
- if (!isTextFieldConfig(field)) {
12
- console.warn(`TextField received an invalid field config for type: ${field.type}`);
13
- return null;
14
- }
15
- const id = useId();
16
- return (
17
- <div style={{ marginBottom: '15px' }}>
18
- <label htmlFor={id}>{field.label}:</label>
19
- <input
20
- type={field.type}
21
- id={id}
22
- name={field.name}
23
- placeholder={field.placeholder}
24
- value={value || ''}
25
- onChange={(e) => onChange(e.target.value)}
26
- style={{ borderColor: errors ? 'red' : '#ccc' }}
27
- />
28
- {errors && <p style={{ color: 'red', fontSize: '0.8em' }}>{Object.values(errors).join(', ')}</p>}
29
- </div>
30
- );
31
- };
@@ -1,23 +0,0 @@
1
- import {
2
- useEffect,
3
- useRef,
4
- type DependencyList,
5
- type EffectCallback,
6
- } from 'react';
7
-
8
- /**
9
- * Хук как useEffect, но не выполняется на первом рендере.
10
- * Выполняется только на обновлениях, когда меняются зависимости.
11
- */
12
- export const useUpdateEffect = (effect: EffectCallback, deps: DependencyList): void => {
13
- const isFirstRenderRef = useRef(true);
14
-
15
- useEffect(() => {
16
- if (isFirstRenderRef.current) {
17
- isFirstRenderRef.current = false;
18
- return;
19
- }
20
-
21
- return effect();
22
- }, deps);
23
- };
@@ -1,103 +0,0 @@
1
- import {expect, test} from "vitest";
2
- import {VisibleCore} from "./VisibleCore.ts";
3
- import type { TGroupRules } from "../../index.ts";
4
-
5
- test.each([
6
- {
7
- operator: 'logic: and, operator: in',
8
- setting: {
9
- logic: 'and',
10
- rules: [
11
- {
12
- field: 'test',
13
- operator: 'in',
14
- value: ['Alex']
15
- },
16
- ]
17
- } as TGroupRules,
18
- formData: {},
19
- result: false,
20
- },
21
- {
22
- operator: 'logic: and, operator: in',
23
- setting: {
24
- logic: 'and',
25
- rules: [
26
- {
27
- field: 'test',
28
- operator: 'in',
29
- value: ['Alex', 'Habr']
30
- },
31
- ]
32
- } as TGroupRules,
33
- formData: {test: 'Alex'},
34
- result: true,
35
- },
36
- {
37
- operator: 'logic: and, operator =',
38
- setting: {
39
- logic: 'and',
40
- rules: [
41
- {
42
- field: 'test',
43
- operator: '=',
44
- value: "habr"
45
- },
46
- ]
47
- } as TGroupRules,
48
- formData: {test: 'habr'},
49
- result: true,
50
- },
51
- {
52
- operator: 'logic: or, operator =',
53
- setting: {
54
- logic: 'or',
55
- rules: [
56
- {
57
- field: 'test',
58
- operator: '=',
59
- value: "habr"
60
- },
61
- {
62
- field: 'operator',
63
- operator: '=',
64
- value: "habr"
65
- },
66
- ]
67
- } as TGroupRules,
68
- formData: {operator: 'habr'},
69
- result: true,
70
- },
71
- {
72
- operator: 'logic: or, deep rules, operator =',
73
- setting: {
74
- logic: 'or',
75
- rules: [
76
- {
77
- field: 'test',
78
- operator: '=',
79
- value: "habr"
80
- },
81
- {
82
- logic: 'and',
83
- rules: [
84
- {
85
- field: 'test',
86
- operator: '=',
87
- value: "1"
88
- },
89
- {
90
- field: 'test2',
91
- operator: '=',
92
- value: "2"
93
- }
94
- ]
95
- },
96
- ]
97
- } as TGroupRules,
98
- formData: {test: '1', test2: '2'},
99
- result: true,
100
- },
101
- ])('is visible for operation $operator with data $formData', ({setting, formData, result}) => {
102
- expect(VisibleCore.isVisible(setting, formData)).toBe(result)
103
- });
@@ -1,43 +0,0 @@
1
- import type {TCommonRule, TGroupRules, FormData, TInRule, TEqualRule} from "../types/common";
2
- import {getNestedValue} from "../utils";
3
-
4
- export class VisibleCore {
5
-
6
- private static isGroupRule(i: TGroupRules | TCommonRule): i is TGroupRules {
7
- return 'rules' in i;
8
- }
9
-
10
- private static isInOperator(i: TCommonRule): i is TInRule {
11
- return i.operator == 'in' ;
12
- }
13
-
14
- private static isEqualOperand(i: TCommonRule): i is TEqualRule {
15
- return i.operator == '=' ;
16
- }
17
-
18
- static checkGroup(groupRule: TGroupRules, formData: FormData): boolean {
19
- const logic = groupRule.logic;
20
- const items: boolean[] = [];
21
- groupRule.rules.forEach((rule: TGroupRules | TCommonRule) => {
22
- if (this.isGroupRule(rule)) {
23
- items.push(this.checkGroup(rule, formData));
24
- } else {
25
- const value = getNestedValue(formData, rule.field.split('.'))
26
- if (this.isInOperator(rule)) {
27
- items.push(rule.value.indexOf(value) !== -1);
28
- } else if (this.isEqualOperand(rule)) {
29
- items.push(rule.value == value);
30
- }
31
- }
32
- })
33
- if (logic == 'or') {
34
- return items.indexOf(true) != -1
35
- } else {
36
- return items.indexOf(false) == -1;
37
- }
38
- }
39
-
40
- static isVisible(setting: TGroupRules, formData: FormData): boolean {
41
- return this.checkGroup(setting, formData);
42
- }
43
- }