@codeleap/form 4.3.9 → 5.0.1
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 +13 -14
- package/package.json.bak +10 -11
- package/src/fields/bool.ts +21 -0
- package/src/fields/date.ts +22 -0
- package/src/fields/file.ts +19 -0
- package/src/fields/group.ts +21 -0
- package/src/fields/index.ts +28 -0
- package/src/fields/list.ts +36 -0
- package/src/fields/number.ts +33 -0
- package/src/fields/selectable.ts +32 -0
- package/src/fields/text.ts +24 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useField.ts +14 -0
- package/src/index.ts +5 -6
- package/src/lib/Field.ts +310 -0
- package/src/lib/Form.ts +172 -0
- package/src/lib/factories.tsx +19 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/useFieldBinding.ts +40 -0
- package/src/types/field.ts +38 -0
- package/src/types/form.ts +36 -0
- package/src/types/globals.ts +15 -0
- package/src/types/index.ts +4 -0
- package/src/types/validation.ts +10 -0
- package/src/validators/index.ts +1 -0
- package/src/validators/zod.ts +37 -0
- package/src/constants.ts +0 -33
- package/src/createForm.ts +0 -105
- package/src/presets.ts +0 -14
- package/src/types.ts +0 -268
- package/src/useForm.ts +0 -269
- package/src/utils.ts +0 -55
package/src/lib/Form.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { createStateSlice, GlobalState, globalState } from "@codeleap/store"
|
|
2
|
+
|
|
3
|
+
import { TypeGuards } from "@codeleap/types"
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
|
5
|
+
import { FieldPaths, FieldPropertyTuples, FieldTuples, FormDef, FormValues, PropertyForKeys, ValidationResult } from "../types"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function buildState<T extends FormDef>(def: T) {
|
|
11
|
+
const stateArg = {}
|
|
12
|
+
|
|
13
|
+
for(const [name, field] of Object.entries(def)) {
|
|
14
|
+
stateArg[name] = field.value
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return stateArg as FormValues<T>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
type FormSelector<T extends FormDef, S> = (form: Form<T>) => S
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Form<T extends FormDef> {
|
|
26
|
+
id: string
|
|
27
|
+
fields: T
|
|
28
|
+
state: GlobalState<FormValues<T>>
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
constructor(id: string, shape: T) {
|
|
32
|
+
this.id = id
|
|
33
|
+
this.fields = shape
|
|
34
|
+
|
|
35
|
+
this.state = globalState(
|
|
36
|
+
buildState(this.fields)
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
this.attachState()
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get values(){
|
|
44
|
+
return this.state.get()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get isValid(){
|
|
48
|
+
const res = this.validate()
|
|
49
|
+
|
|
50
|
+
return Object.values(res).every((result: ValidationResult<any, any>) => result.isValid)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
slice<K extends FieldPaths<T>>(field: K) {
|
|
54
|
+
|
|
55
|
+
const fieldSlice = createStateSlice(
|
|
56
|
+
this.state,
|
|
57
|
+
(v) => v[field],
|
|
58
|
+
(value) => {
|
|
59
|
+
return {
|
|
60
|
+
[field]: value
|
|
61
|
+
} as FormValues<T>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return fieldSlice
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
iterFields<V>(cb: (field: FieldTuples<T>, index: number) => V){
|
|
70
|
+
const results:V[] = []
|
|
71
|
+
let index = 0
|
|
72
|
+
|
|
73
|
+
for(const [name, field] of Object.entries(this.fields)) {
|
|
74
|
+
const result = cb([ name, field ] as FieldTuples<T>, index)
|
|
75
|
+
|
|
76
|
+
results.push(result)
|
|
77
|
+
|
|
78
|
+
index++
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return results
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
attachState(){
|
|
85
|
+
this.iterFields(([name, field]) => {
|
|
86
|
+
field.options.name = name
|
|
87
|
+
|
|
88
|
+
field.attach(
|
|
89
|
+
this.slice(name)
|
|
90
|
+
)
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
firstInvalid() {
|
|
95
|
+
for(const [fieldName, field] of Object.entries(this.fields)){
|
|
96
|
+
const validation = field.validate()
|
|
97
|
+
|
|
98
|
+
if(!validation.isValid) return {
|
|
99
|
+
field,
|
|
100
|
+
validation,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
validate<Fields extends FieldPaths<T>[] = FieldPaths<T>[]>(fields?: Fields): PropertyForKeys<T, Fields[number], '__validationRes'> {
|
|
106
|
+
|
|
107
|
+
const validateFields = fields ?? Object.keys(this.fields)
|
|
108
|
+
|
|
109
|
+
const results = this.iterFields(([name, field]) => {
|
|
110
|
+
if(!validateFields.includes(name)) return null
|
|
111
|
+
|
|
112
|
+
return [name, field.validate()] as FieldPropertyTuples<T, '__validationRes'>
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const resultMap = Object.fromEntries(
|
|
116
|
+
results.filter(v => !TypeGuards.isNil(v))
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return resultMap as unknown as PropertyForKeys<T, Fields[number], '__validationRes'>
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
register(field: FieldPaths<T>) {
|
|
125
|
+
if(!this.fields[field]){
|
|
126
|
+
throw new Error(`Field "${field}" not found in "${this.id}" form`)
|
|
127
|
+
}
|
|
128
|
+
return this.fields[field].props()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
use<Selected>(selector: FormSelector<T, Selected>): Selected {
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
const [selected, setSelected] = useState(() => selector(this))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
const reselect = useCallback(() => {
|
|
139
|
+
setSelected(selector(this))
|
|
140
|
+
}, [selector])
|
|
141
|
+
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
return this.state.listen((value, previous) => {
|
|
144
|
+
if(value != previous){
|
|
145
|
+
reselect()
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
}, [reselect])
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
return selected
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
export function useForm<T extends FormDef>(name: string, def: T) {
|
|
162
|
+
const form = useMemo(() => {
|
|
163
|
+
return new Form(name, def)
|
|
164
|
+
}, [name])
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
return form
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function form<Def extends FormDef>(...args: ConstructorParameters<typeof Form<Def>>) {
|
|
171
|
+
return new Form(...args)
|
|
172
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
import { FormDef, Validator } from "../types"
|
|
3
|
+
import { Field } from "./Field"
|
|
4
|
+
|
|
5
|
+
type FieldBuilder<T, Validate extends Validator<any,any,any>> = typeof Field<T, Validate>
|
|
6
|
+
|
|
7
|
+
export function fieldFactory<
|
|
8
|
+
T extends FieldBuilder<any,any>,
|
|
9
|
+
Value = T extends FieldBuilder<infer V, any> ? V : never,
|
|
10
|
+
Validator = T extends FieldBuilder<infer VL, any> ? VL : never,
|
|
11
|
+
>(cls: T) {
|
|
12
|
+
return <A extends ConstructorParameters<T>[0]>(options?: A): Field<Value, A['validate']> => {
|
|
13
|
+
|
|
14
|
+
// @ts-expect-error
|
|
15
|
+
return new cls(options ?? {})
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
package/src/lib/index.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useImperativeHandle } from "react"
|
|
2
|
+
import { IFieldRef } from "../types"
|
|
3
|
+
|
|
4
|
+
export function useFieldBinding<T>(ref: React.Ref<IFieldRef<T>>, impl: Partial<IFieldRef<T>>, deps = []){
|
|
5
|
+
|
|
6
|
+
const notImplemented = (method: string) => {
|
|
7
|
+
throw new Error(`ref.${method} not implemented for ${this._type} field`)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
useImperativeHandle(ref, ( ) => ({
|
|
12
|
+
blur: () => {
|
|
13
|
+
notImplemented('blur')
|
|
14
|
+
},
|
|
15
|
+
emit: () => {
|
|
16
|
+
notImplemented('emit')
|
|
17
|
+
},
|
|
18
|
+
focus: () => {
|
|
19
|
+
notImplemented('focus')
|
|
20
|
+
},
|
|
21
|
+
// @ts-expect-error
|
|
22
|
+
getValue: () => {
|
|
23
|
+
notImplemented('getValue')
|
|
24
|
+
},
|
|
25
|
+
scrollIntoView: async () => {
|
|
26
|
+
notImplemented('scrollIntoView')
|
|
27
|
+
},
|
|
28
|
+
hideValue() {
|
|
29
|
+
notImplemented('hideValue')
|
|
30
|
+
},
|
|
31
|
+
revealValue(){
|
|
32
|
+
notImplemented('revealValue')
|
|
33
|
+
},
|
|
34
|
+
toggleValueVisibility(){
|
|
35
|
+
notImplemented('toggleValueVisibility')
|
|
36
|
+
},
|
|
37
|
+
...impl
|
|
38
|
+
}), deps)
|
|
39
|
+
|
|
40
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { WritableStore } from 'nanostores'
|
|
2
|
+
import { Validator } from './validation'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export type FieldState<T> = WritableStore<T>
|
|
7
|
+
|
|
8
|
+
export interface ExtraFieldOptions {
|
|
9
|
+
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type FieldOptions<
|
|
13
|
+
T,
|
|
14
|
+
Validate extends Validator<T, any, any>
|
|
15
|
+
> = {
|
|
16
|
+
name?: string
|
|
17
|
+
defaultValue?: T | null
|
|
18
|
+
state?: FieldState<T>
|
|
19
|
+
|
|
20
|
+
validate?: Validate
|
|
21
|
+
|
|
22
|
+
loader?: (form: any) => Partial<
|
|
23
|
+
Omit<FieldOptions<T, Validate >, 'loader'>
|
|
24
|
+
>
|
|
25
|
+
|
|
26
|
+
onValueChange?: (newValue: T) => void
|
|
27
|
+
|
|
28
|
+
} & ExtraFieldOptions
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
export type FieldMeasureResult = {
|
|
32
|
+
x?: number
|
|
33
|
+
y?: number
|
|
34
|
+
width?: number
|
|
35
|
+
height?: number
|
|
36
|
+
pageX?: number
|
|
37
|
+
pageY?: number
|
|
38
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Field } from "../lib/Field"
|
|
2
|
+
import { ValidationResult } from "./validation"
|
|
3
|
+
|
|
4
|
+
export type FormDef = Record<string, Field<any,any>>
|
|
5
|
+
|
|
6
|
+
export type NarrowKeyof<T> = Extract<keyof T, string>
|
|
7
|
+
|
|
8
|
+
export type FormValues<T extends FormDef> = {
|
|
9
|
+
[K in NarrowKeyof<T>]: T[K]['value']
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type FormErrors<T extends FormDef> = {
|
|
13
|
+
[K in NarrowKeyof<T>]: ReturnType<T[K]['validate']> extends ValidationResult<any, infer E> ? E : never
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type FormResults<T extends FormDef> = {
|
|
17
|
+
[K in NarrowKeyof<T>]: ReturnType<T[K]['validate']> extends ValidationResult<infer R, any> ? R : never
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type FieldTuples<T extends FormDef> = {
|
|
21
|
+
[K in NarrowKeyof<T>]: [K, T[K]]
|
|
22
|
+
}[NarrowKeyof<T>]
|
|
23
|
+
|
|
24
|
+
export type PropertyForKeys<T extends FormDef, Keys extends FieldPaths<T>, Property extends keyof Field<any,any>> = {
|
|
25
|
+
[K in Keys]: T[K][Property]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type FieldPropertyTuples<T extends FormDef, Property extends keyof Field<any,any>> = {
|
|
29
|
+
[K in NarrowKeyof<T>]: [K, T[K][Property]]
|
|
30
|
+
}[NarrowKeyof<T>]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
export type FieldPaths<T extends FormDef> = ({
|
|
35
|
+
[K in NarrowKeyof<T>]: K
|
|
36
|
+
})[NarrowKeyof<T>]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface IFieldRef<T> {
|
|
2
|
+
getValue(): T
|
|
3
|
+
scrollIntoView(): Promise<void>
|
|
4
|
+
focus(): void
|
|
5
|
+
blur(): void
|
|
6
|
+
revealValue(): void
|
|
7
|
+
toggleValueVisibility(): void
|
|
8
|
+
hideValue(): void
|
|
9
|
+
emit(event: string, ...args: any[]): void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export interface IFieldProps {
|
|
14
|
+
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ValidationResult<Result, Err> = {
|
|
2
|
+
isValid: boolean
|
|
3
|
+
result?: Result
|
|
4
|
+
error?: Err
|
|
5
|
+
readableError?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type ValidatorFunction<Value, Result, Err> = (value: Value, form: any) => ValidationResult<Result, Err>
|
|
9
|
+
|
|
10
|
+
export type Validator<Value, Result, Err> = ValidatorFunction<Value, Result, Err>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './zod'
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z, ZodIssue } from 'zod'
|
|
2
|
+
import { ValidationResult } from '../types'
|
|
3
|
+
import { TypeGuards } from '@codeleap/types'
|
|
4
|
+
|
|
5
|
+
type ZodValidationResult<T extends z.ZodType> = ValidationResult<z.infer<T>, z.ZodError['issues']>
|
|
6
|
+
|
|
7
|
+
export function zodValidator<T extends z.ZodType>(model: T) {
|
|
8
|
+
return (value): ZodValidationResult<T> => {
|
|
9
|
+
const result = model.safeParse(value)
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
isValid: result.success,
|
|
13
|
+
error: result.error?.issues,
|
|
14
|
+
result: result.data
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const isZodIssue = (val: any): val is ZodIssue => {
|
|
21
|
+
return ['code','expected','received','path','message'].every(x => x in val)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isZodValidationResult(val: any): val is ZodValidationResult<any> {
|
|
25
|
+
const isValidABoolean = TypeGuards.isBoolean(val.isValid)
|
|
26
|
+
|
|
27
|
+
if(isValidABoolean) {
|
|
28
|
+
if(!val.isValid) {
|
|
29
|
+
return TypeGuards.isArray(val.error) && val.error.every(isZodIssue)
|
|
30
|
+
} else {
|
|
31
|
+
return 'result' in val
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return false
|
|
36
|
+
|
|
37
|
+
}
|
package/src/constants.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import * as Form from './types'
|
|
2
|
-
|
|
3
|
-
export const defaultFieldValues: Partial<Record<Form.FormField['type'], any>> =
|
|
4
|
-
{
|
|
5
|
-
checkbox: false,
|
|
6
|
-
text: '',
|
|
7
|
-
|
|
8
|
-
file: null,
|
|
9
|
-
multipleFile: [],
|
|
10
|
-
'range-slider': [0, 100],
|
|
11
|
-
slider: 1,
|
|
12
|
-
number: '',
|
|
13
|
-
list: [],
|
|
14
|
-
switch: false,
|
|
15
|
-
date: null,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const changeEventNames: Partial<Record<Form.FormField['type'], string>> =
|
|
19
|
-
{
|
|
20
|
-
checkbox: 'onValueChange',
|
|
21
|
-
'range-slider': 'onValueChange',
|
|
22
|
-
slider: 'onValueChange',
|
|
23
|
-
select: 'onValueChange',
|
|
24
|
-
radio: 'onValueChange',
|
|
25
|
-
text: 'onChangeText',
|
|
26
|
-
file: 'onFileSelect',
|
|
27
|
-
multipleFile: 'onFileSelect',
|
|
28
|
-
number: 'onChangeText',
|
|
29
|
-
list: 'onValueChange',
|
|
30
|
-
switch: 'onValueChange',
|
|
31
|
-
date: 'onValueChange',
|
|
32
|
-
}
|
|
33
|
-
|
package/src/createForm.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { defaultFieldValues } from './constants'
|
|
2
|
-
import * as Form from './types'
|
|
3
|
-
import * as yup from 'yup'
|
|
4
|
-
import { humanizeCamelCase } from '../../utils'
|
|
5
|
-
import { changeEventNames } from './constants'
|
|
6
|
-
|
|
7
|
-
function getDefaultValue(field: Partial<Form.FormField>) {
|
|
8
|
-
switch (field.type) {
|
|
9
|
-
case 'radio':
|
|
10
|
-
case 'select':
|
|
11
|
-
return field.options?.[0]?.value || ''
|
|
12
|
-
default:
|
|
13
|
-
return defaultFieldValues[field.type]
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function getValidator(validate: Form.Validator<any>): Form.ValidatorFunction {
|
|
18
|
-
if (!validate) return undefined
|
|
19
|
-
|
|
20
|
-
if (typeof validate === 'function') {
|
|
21
|
-
return validate
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const yupModel = validate as yup.StringSchema
|
|
25
|
-
|
|
26
|
-
return (value) => {
|
|
27
|
-
try {
|
|
28
|
-
yupModel.validateSync(value)
|
|
29
|
-
return { valid: true, message: '' }
|
|
30
|
-
} catch (e) {
|
|
31
|
-
return { valid: false, message: e?.errors?.join(' ') || '' }
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function buildInitialFormState<T extends Form.FieldsMap>(
|
|
37
|
-
name: string,
|
|
38
|
-
form: T,
|
|
39
|
-
inside = [],
|
|
40
|
-
) {
|
|
41
|
-
const state = {} as Form.MapValues<T>
|
|
42
|
-
const props = {}
|
|
43
|
-
let numberOfTextFields = 0
|
|
44
|
-
for (const [k, value] of Object.entries(form)) {
|
|
45
|
-
const { defaultValue, label, validate, type, ...fieldConfig } = value
|
|
46
|
-
|
|
47
|
-
const key = k as keyof Form.MapValues<T>
|
|
48
|
-
|
|
49
|
-
const fieldPathParts = [...inside, key]
|
|
50
|
-
const fieldPath = fieldPathParts.join('.')
|
|
51
|
-
|
|
52
|
-
let fieldValue = null
|
|
53
|
-
if (type === 'text') numberOfTextFields += 1
|
|
54
|
-
if (false) {
|
|
55
|
-
// const { props: subFieldProps, state } = buildInitialFormState(
|
|
56
|
-
// name,
|
|
57
|
-
// value.fields,
|
|
58
|
-
// fieldPathParts,
|
|
59
|
-
// )
|
|
60
|
-
// fieldValue = state
|
|
61
|
-
|
|
62
|
-
// props = {
|
|
63
|
-
// ...props,
|
|
64
|
-
// ...subFieldProps,
|
|
65
|
-
// }
|
|
66
|
-
} else {
|
|
67
|
-
fieldValue =
|
|
68
|
-
typeof defaultValue !== 'undefined'
|
|
69
|
-
? defaultValue
|
|
70
|
-
: getDefaultValue(value)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const fieldProps: any = {
|
|
74
|
-
id: `form-${name}:${fieldPath}`,
|
|
75
|
-
label: label || humanizeCamelCase(k),
|
|
76
|
-
changeEventName: changeEventNames[type],
|
|
77
|
-
type,
|
|
78
|
-
...fieldConfig,
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
delete fieldProps.fields
|
|
82
|
-
|
|
83
|
-
if (validate) fieldProps.validate = getValidator(validate)
|
|
84
|
-
|
|
85
|
-
props[fieldPath] = fieldProps
|
|
86
|
-
state[key] = fieldValue
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return { state, props, numberOfTextFields }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function createForm<T extends Form.FieldsMap>(
|
|
93
|
-
name: string,
|
|
94
|
-
formArgs: Form.FormConfig<T>,
|
|
95
|
-
): Form.CreateFormReturn<T> {
|
|
96
|
-
const { state, props, numberOfTextFields } = buildInitialFormState(name, formArgs)
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
config: formArgs as T,
|
|
100
|
-
defaultValue: state,
|
|
101
|
-
staticFieldProps: props,
|
|
102
|
-
name,
|
|
103
|
-
numberOfTextFields,
|
|
104
|
-
}
|
|
105
|
-
}
|