@codeleap/form 4.3.8 → 5.0.0
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/types.ts
DELETED
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react'
|
|
2
|
-
import { Join, Paths, Prev } from '@codeleap/types'
|
|
3
|
-
import * as yup from 'yup'
|
|
4
|
-
import { WebInputFile, MobileInputFile, DeepPartial, RNMaskedTextTypes } from '../../types'
|
|
5
|
-
import { AnyObject } from 'yup/lib/object'
|
|
6
|
-
|
|
7
|
-
type ValidationReturn = { message?: Label; valid?: boolean }
|
|
8
|
-
|
|
9
|
-
export type ValidatorFunction<T = any, F = any> = (
|
|
10
|
-
value: T,
|
|
11
|
-
formValues: F
|
|
12
|
-
) => ValidationReturn
|
|
13
|
-
export type ValidatorFunctionWithoutForm<T = any> = (
|
|
14
|
-
value: T
|
|
15
|
-
) => ValidationReturn
|
|
16
|
-
|
|
17
|
-
export type WithTransformer<T> = {
|
|
18
|
-
transformer: (value: T) => any
|
|
19
|
-
}
|
|
20
|
-
export type Validator<T> = T extends boolean
|
|
21
|
-
?
|
|
22
|
-
| ValidatorFunction<true>
|
|
23
|
-
| ValidatorFunction<false>
|
|
24
|
-
| yup.BooleanSchema<boolean, AnyObject, true>
|
|
25
|
-
| yup.BooleanSchema<boolean, AnyObject, false>
|
|
26
|
-
: ValidatorFunction<T> | yup.SchemaOf<T>
|
|
27
|
-
|
|
28
|
-
export type ValidatorWithoutForm<T> = T extends boolean
|
|
29
|
-
?
|
|
30
|
-
| ValidatorFunction<true>
|
|
31
|
-
| ValidatorFunction<false>
|
|
32
|
-
| yup.BooleanSchema<boolean, AnyObject, true>
|
|
33
|
-
| yup.BooleanSchema<boolean, AnyObject, false>
|
|
34
|
-
: ValidatorFunctionWithoutForm<T> | yup.SchemaOf<T>
|
|
35
|
-
|
|
36
|
-
export type Options<T> = { label: Label; value: T }[]
|
|
37
|
-
export type Option<T> = Options<T>[number]
|
|
38
|
-
type FormValidateOn = 'change'
|
|
39
|
-
|
|
40
|
-
export type FormOutput = 'json'
|
|
41
|
-
|
|
42
|
-
export type CommonSliderTypes = {
|
|
43
|
-
min?: number
|
|
44
|
-
max?: number
|
|
45
|
-
trackMarks?: number[] | Record<string|number, string>
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
type Mask = Partial<RNMaskedTextTypes.TextInputMaskProps> &{
|
|
49
|
-
saveFormatted?: boolean
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type InputValueTypes = {
|
|
53
|
-
checkbox: boolean
|
|
54
|
-
switch: boolean
|
|
55
|
-
text: string
|
|
56
|
-
select: any
|
|
57
|
-
radio: any
|
|
58
|
-
file: AnyFile
|
|
59
|
-
multipleFile: AnyFile[]
|
|
60
|
-
'range-slider': number[]
|
|
61
|
-
slider: number[]
|
|
62
|
-
list: any[]
|
|
63
|
-
date: Date
|
|
64
|
-
number: number
|
|
65
|
-
}
|
|
66
|
-
export type Label = string | ReactNode
|
|
67
|
-
|
|
68
|
-
export type CheckboxField = {
|
|
69
|
-
type: 'checkbox'
|
|
70
|
-
defaultValue: boolean
|
|
71
|
-
validate?: Validator<boolean>
|
|
72
|
-
required?: boolean
|
|
73
|
-
} & WithTransformer<boolean>
|
|
74
|
-
|
|
75
|
-
export type SwitchField = {
|
|
76
|
-
type: 'switch'
|
|
77
|
-
defaultValue: boolean
|
|
78
|
-
validate?: Validator<boolean>
|
|
79
|
-
required?: boolean
|
|
80
|
-
} & WithTransformer<boolean>
|
|
81
|
-
|
|
82
|
-
export type ListField<T = any> = {
|
|
83
|
-
type: 'list'
|
|
84
|
-
defaultValue: T[]
|
|
85
|
-
validate?: Validator<T[]>
|
|
86
|
-
required?: boolean
|
|
87
|
-
options?: Options<T>
|
|
88
|
-
placeholder?: string
|
|
89
|
-
} & WithTransformer<T[]>
|
|
90
|
-
|
|
91
|
-
export type SliderField = {
|
|
92
|
-
type: 'slider'
|
|
93
|
-
defaultValue: number
|
|
94
|
-
validate?: Validator<number>
|
|
95
|
-
required?: boolean
|
|
96
|
-
|
|
97
|
-
} & CommonSliderTypes & WithTransformer<number>
|
|
98
|
-
|
|
99
|
-
export type RangeSliderField = {
|
|
100
|
-
type: 'range-slider'
|
|
101
|
-
defaultValue: number[]
|
|
102
|
-
validate?: Validator<number[]>
|
|
103
|
-
required?: boolean
|
|
104
|
-
} & CommonSliderTypes & WithTransformer<number[]>
|
|
105
|
-
|
|
106
|
-
export type TextField = {
|
|
107
|
-
type: 'text'
|
|
108
|
-
password?: boolean
|
|
109
|
-
defaultValue: string
|
|
110
|
-
placeholder?: string
|
|
111
|
-
validate?: Validator<string>
|
|
112
|
-
required?: boolean
|
|
113
|
-
masking?: Mask
|
|
114
|
-
multiline?: boolean
|
|
115
|
-
} & WithTransformer<string>
|
|
116
|
-
export type NumberField = {
|
|
117
|
-
type: 'number'
|
|
118
|
-
|
|
119
|
-
defaultValue: number
|
|
120
|
-
placeholder?: string
|
|
121
|
-
validate?: Validator<number>
|
|
122
|
-
required?: boolean
|
|
123
|
-
precision?: number
|
|
124
|
-
masking?: Mask
|
|
125
|
-
min?: number
|
|
126
|
-
max?: number
|
|
127
|
-
|
|
128
|
-
} & WithTransformer<number>
|
|
129
|
-
export type SelectField<T = any> = {
|
|
130
|
-
type: 'select'
|
|
131
|
-
options: Options<T>
|
|
132
|
-
defaultValue: T
|
|
133
|
-
native?: boolean
|
|
134
|
-
placeholder?: string
|
|
135
|
-
validate?: Validator<T>
|
|
136
|
-
required?: boolean
|
|
137
|
-
} & WithTransformer<T>
|
|
138
|
-
|
|
139
|
-
export type RadioField<T = any> = {
|
|
140
|
-
type: 'radio'
|
|
141
|
-
options: Options<T>
|
|
142
|
-
defaultValue: T
|
|
143
|
-
validate?: Validator<T>
|
|
144
|
-
required?: boolean
|
|
145
|
-
} & WithTransformer<T>
|
|
146
|
-
|
|
147
|
-
export type AnyFile = WebInputFile | MobileInputFile|string|number
|
|
148
|
-
export type FileField = {
|
|
149
|
-
type: 'file'
|
|
150
|
-
allow?: string[]
|
|
151
|
-
defaultValue: AnyFile
|
|
152
|
-
imageToBase64?: boolean
|
|
153
|
-
multiple?: boolean
|
|
154
|
-
validate?: Validator<AnyFile>
|
|
155
|
-
required?: boolean
|
|
156
|
-
} & WithTransformer<AnyFile>
|
|
157
|
-
|
|
158
|
-
export type MultipleFileField = {
|
|
159
|
-
type: 'multipleFile'
|
|
160
|
-
allow?: string[]
|
|
161
|
-
defaultValue: AnyFile[]
|
|
162
|
-
imageToBase64?: boolean
|
|
163
|
-
multiple?: boolean
|
|
164
|
-
validate?: Validator<AnyFile[]>
|
|
165
|
-
required?: boolean
|
|
166
|
-
} & WithTransformer<AnyFile[]>
|
|
167
|
-
|
|
168
|
-
export type DateField = {
|
|
169
|
-
type: 'date'
|
|
170
|
-
defaultValue: Date
|
|
171
|
-
validate?: Validator<Date>
|
|
172
|
-
required?: boolean
|
|
173
|
-
minimumDate?: Date
|
|
174
|
-
maximumDate?: Date
|
|
175
|
-
} & WithTransformer<Date>
|
|
176
|
-
|
|
177
|
-
export type AllFields =
|
|
178
|
-
| CheckboxField
|
|
179
|
-
| SwitchField
|
|
180
|
-
| TextField
|
|
181
|
-
| SelectField
|
|
182
|
-
| RadioField
|
|
183
|
-
| FileField
|
|
184
|
-
| SliderField
|
|
185
|
-
| RangeSliderField
|
|
186
|
-
| ListField
|
|
187
|
-
| NumberField
|
|
188
|
-
| MultipleFileField
|
|
189
|
-
| DateField
|
|
190
|
-
|
|
191
|
-
export type FormField = {
|
|
192
|
-
disabled?: boolean
|
|
193
|
-
label?: Label
|
|
194
|
-
autoCapitalize?: boolean | string
|
|
195
|
-
keyboardType?: string
|
|
196
|
-
returnKeyType?: string
|
|
197
|
-
textContentType?: string
|
|
198
|
-
autoComplete?: string
|
|
199
|
-
description?: Label
|
|
200
|
-
debugName?: string
|
|
201
|
-
required?: boolean
|
|
202
|
-
debounce?: number
|
|
203
|
-
componentProps?: any
|
|
204
|
-
|
|
205
|
-
} & AllFields
|
|
206
|
-
|
|
207
|
-
export type FieldsMap = Record<string, Partial<FormField>>
|
|
208
|
-
|
|
209
|
-
export type ResolveOptionsFieldValidation<
|
|
210
|
-
T extends RadioField<any> | SelectField<any>
|
|
211
|
-
> = T extends RadioField<any>
|
|
212
|
-
? RadioField<T['options'][number]['value']>
|
|
213
|
-
: SelectField<T['options'][number]['value']>
|
|
214
|
-
|
|
215
|
-
export type ValidateFieldsMap<T extends FieldsMap> = {
|
|
216
|
-
[Property in keyof T]: T[Property] extends RadioField<any> | SelectField<any>
|
|
217
|
-
? ResolveOptionsFieldValidation<T[Property]>
|
|
218
|
-
: T[Property];
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export type FormConfig<T extends FieldsMap> = ValidateFieldsMap<T>
|
|
222
|
-
|
|
223
|
-
export type FlattenFields<T extends FieldsMap> = {
|
|
224
|
-
[Property in keyof T]: T[Property] extends RadioField<any> | SelectField
|
|
225
|
-
? T[Property]['options'][number]['value']
|
|
226
|
-
: InputValueTypes[T[Property]['type']];
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export type MapValues<T extends FieldsMap> = {
|
|
230
|
-
[Property in keyof T]: T[Property] extends RadioField<any> | SelectField
|
|
231
|
-
? T[Property]['options'][number]['value']
|
|
232
|
-
: InputValueTypes[T[Property]['type']];
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
export type CreateFormReturn<T extends FieldsMap> = {
|
|
236
|
-
config: T
|
|
237
|
-
defaultValue: MapValues<ValidateFieldsMap<T>>
|
|
238
|
-
staticFieldProps: Record<string, any>
|
|
239
|
-
name: string
|
|
240
|
-
numberOfTextFields: number
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export type FormStep<Keys extends string = string> = 'setValue' | 'validate' | `validate.${Keys}` | `setValue.${Keys}`
|
|
244
|
-
export type UseFormConfig<T extends Record<string, any>> = {
|
|
245
|
-
log?: FormStep<Exclude<keyof T, symbol|number>>[]
|
|
246
|
-
initialState?: DeepPartial<T>
|
|
247
|
-
validateOn?: 'change' | 'none'
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
export type PathsWithValues<T, D extends number = 2> = [D] extends [never]
|
|
251
|
-
? never
|
|
252
|
-
: T extends Record<string, any>
|
|
253
|
-
? {
|
|
254
|
-
[K in keyof T]-?: K extends string | number
|
|
255
|
-
?
|
|
256
|
-
| [`${K}`, T[K]]
|
|
257
|
-
| [
|
|
258
|
-
Join<K, Paths<T[K], Prev[D]>>,
|
|
259
|
-
T[K] extends FlattenFields<any> ? T[K][keyof T[K]] : T[K]
|
|
260
|
-
]
|
|
261
|
-
: never;
|
|
262
|
-
}[keyof T]
|
|
263
|
-
: ''
|
|
264
|
-
|
|
265
|
-
export type FormShape<Form extends CreateFormReturn<any>> = MapValues<Form['config']>
|
|
266
|
-
export type FormSetters<Values> = {
|
|
267
|
-
[Property in keyof Values]: (value: Values[Property]) => void
|
|
268
|
-
}
|
package/src/useForm.ts
DELETED
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import * as FormTypes from './types'
|
|
2
|
-
import { TypeGuards } from '@codeleap/types'
|
|
3
|
-
import { deepGet, deepSet, deepMerge } from '@codeleap/utils'
|
|
4
|
-
import { usePartialState, useMemo } from '@codeleap/hooks'
|
|
5
|
-
import { FunctionType } from '../../types'
|
|
6
|
-
import { createRef, useCallback, useRef } from 'react'
|
|
7
|
-
import { useI18N } from '@codeleap/i18n'
|
|
8
|
-
|
|
9
|
-
export * as FormTypes from './types'
|
|
10
|
-
|
|
11
|
-
function testBrowser() {
|
|
12
|
-
try {
|
|
13
|
-
// @ts-ignore
|
|
14
|
-
return typeof localStorage !== 'undefined'
|
|
15
|
-
} catch {
|
|
16
|
-
return false
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const isBrowser = testBrowser()
|
|
21
|
-
|
|
22
|
-
const SCOPE = 'useForm'
|
|
23
|
-
|
|
24
|
-
const shouldLog = (
|
|
25
|
-
x: FormTypes.FormStep,
|
|
26
|
-
config: FormTypes.UseFormConfig<any>,
|
|
27
|
-
) => {
|
|
28
|
-
return (config.log || []).includes(x)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function useForm<
|
|
32
|
-
Form extends FormTypes.CreateFormReturn<any>,
|
|
33
|
-
FieldPaths extends FormTypes.PathsWithValues<
|
|
34
|
-
FormTypes.FlattenFields<Form['config']>
|
|
35
|
-
>,
|
|
36
|
-
Values extends FormTypes.MapValues<Form['config']> = FormTypes.MapValues<
|
|
37
|
-
Form['config']
|
|
38
|
-
>
|
|
39
|
-
>(formParam: (() => Form) | Form, formConfig: FormTypes.UseFormConfig<Values> = {}) {
|
|
40
|
-
|
|
41
|
-
const i18n = useI18N()
|
|
42
|
-
|
|
43
|
-
const form = useMemo(() => {
|
|
44
|
-
return TypeGuards.isFunction(formParam) ? formParam() : formParam
|
|
45
|
-
}, [formParam, i18n?.locale])
|
|
46
|
-
|
|
47
|
-
const config:FormTypes.UseFormConfig<Values> = {
|
|
48
|
-
validateOn: 'change',
|
|
49
|
-
...formConfig,
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const getInitialState = useCallback(() => {
|
|
53
|
-
return deepMerge(form.defaultValue, config.initialState || {}) as Values
|
|
54
|
-
}, [form.defaultValue, config.initialState])
|
|
55
|
-
|
|
56
|
-
const getInitialErrors = useCallback(() => {
|
|
57
|
-
const errors = Object.keys(form.staticFieldProps).map((key) => [key, ''])
|
|
58
|
-
|
|
59
|
-
return Object.fromEntries(errors)
|
|
60
|
-
}, [form.staticFieldProps])
|
|
61
|
-
|
|
62
|
-
const [formValues, setFormValues] = usePartialState<Values>(getInitialState)
|
|
63
|
-
const [fieldErrors, setFieldErrors] = usePartialState(getInitialErrors)
|
|
64
|
-
// @ts-ignore
|
|
65
|
-
function setFieldValue(...args: FieldPaths) {
|
|
66
|
-
// @ts-ignore
|
|
67
|
-
const transform = form?.staticFieldProps[args[0]]?.transformer || (x => x)
|
|
68
|
-
// @ts-ignore
|
|
69
|
-
const val = deepSet([args[0], transform(args[1])])
|
|
70
|
-
|
|
71
|
-
if (shouldLog('setValue', config) || shouldLog(`setValue.${args[0]}`, config)) {
|
|
72
|
-
// @ts-ignore
|
|
73
|
-
logger.log(
|
|
74
|
-
// @ts-ignore
|
|
75
|
-
`Set ${form.name}/${args[0]} to ${String(args[1])}`,
|
|
76
|
-
'',
|
|
77
|
-
SCOPE,
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
setFormValues(val)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function validateField(field: FieldPaths[0], set = false, val?: any) {
|
|
84
|
-
// @ts-ignore
|
|
85
|
-
const { validate } = form.staticFieldProps[field as string]
|
|
86
|
-
|
|
87
|
-
if (validate) {
|
|
88
|
-
const value = val !== undefined ? val : deepGet(field, formValues)
|
|
89
|
-
// @ts-ignore
|
|
90
|
-
const result = validate(
|
|
91
|
-
value,
|
|
92
|
-
formValues,
|
|
93
|
-
)
|
|
94
|
-
if (set) {
|
|
95
|
-
setFieldErrors(() => ({
|
|
96
|
-
[field]: result.valid ? '' : result.message,
|
|
97
|
-
}))
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return result
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
valid: true,
|
|
105
|
-
message: '',
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function validateMultiple<T extends readonly FieldPaths[0][]>(fields: T, set = false) {
|
|
110
|
-
// @ts-ignore
|
|
111
|
-
const results = fields.map((field) => [field, validateField(field, set)])
|
|
112
|
-
|
|
113
|
-
const overallValid = results.every(([, result]) => result.valid)
|
|
114
|
-
|
|
115
|
-
const fieldResults = Object.fromEntries(results) as Record<T[number], ReturnType<FormTypes.ValidatorFunction>>
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
valid: overallValid,
|
|
119
|
-
results: fieldResults,
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function validateAll(set = false) {
|
|
124
|
-
const errors = { ...fieldErrors }
|
|
125
|
-
for (const [path] of Object.entries(form.staticFieldProps)) {
|
|
126
|
-
const result = validateField(path)
|
|
127
|
-
errors[path] = result.valid ? '' : result?.message
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (set) {
|
|
131
|
-
setFieldErrors(errors)
|
|
132
|
-
}
|
|
133
|
-
return Object.values(errors).join('').length === 0
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const inputRefs = useRef([])
|
|
137
|
-
|
|
138
|
-
function focus(idx:number) {
|
|
139
|
-
// if (!(idx < inputRefs.current.length)) return
|
|
140
|
-
const nextRef = inputRefs?.current?.[idx]?.current
|
|
141
|
-
if (nextRef?.focus && nextRef?.isTextInput) {
|
|
142
|
-
nextRef.focus?.()
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const registeredFields = useRef([])
|
|
147
|
-
let registeredTextRefsOnThisRender = 0
|
|
148
|
-
function register(field: FieldPaths[0], transformProps = (p) => p) {
|
|
149
|
-
const nFields = registeredFields.current.length
|
|
150
|
-
|
|
151
|
-
const { changeEventName, validate, type, componentProps, ...staticProps } =
|
|
152
|
-
// @ts-ignore
|
|
153
|
-
form.staticFieldProps[field as string]
|
|
154
|
-
|
|
155
|
-
const dynamicProps: any = {
|
|
156
|
-
value: deepGet(field, formValues),
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (type === 'number') {
|
|
160
|
-
dynamicProps.value = Number.isNaN(dynamicProps.value) ? '' : String(dynamicProps.value)
|
|
161
|
-
if (isBrowser) {
|
|
162
|
-
dynamicProps.type = 'number'
|
|
163
|
-
} else {
|
|
164
|
-
dynamicProps.keyboardType = 'numeric'
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (changeEventName) {
|
|
169
|
-
dynamicProps[changeEventName] = (value) => {
|
|
170
|
-
if (type === 'number') {
|
|
171
|
-
value = Number(value)
|
|
172
|
-
if (typeof staticProps.precision !== 'undefined') {
|
|
173
|
-
value = Number(value.toFixed(staticProps.precision))
|
|
174
|
-
}
|
|
175
|
-
} else if (type === 'file') {
|
|
176
|
-
value = TypeGuards.isArray(value) && !!value.length ? value[0] : null
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// @ts-ignore
|
|
180
|
-
setFieldValue(field, value)
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (type === 'text' || type === 'number') {
|
|
185
|
-
if (!isBrowser && !staticProps.multiline) {
|
|
186
|
-
dynamicProps.returnKeyType = 'next'
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const thisRefIdx = registeredTextRefsOnThisRender
|
|
190
|
-
|
|
191
|
-
if (thisRefIdx >= inputRefs.current.length) {
|
|
192
|
-
inputRefs.current.push(createRef())
|
|
193
|
-
}
|
|
194
|
-
dynamicProps.ref = inputRefs.current[thisRefIdx]
|
|
195
|
-
registeredTextRefsOnThisRender++
|
|
196
|
-
if (!isBrowser && !staticProps.multiline) {
|
|
197
|
-
dynamicProps.onSubmitEditing = () => {
|
|
198
|
-
const nextRef = thisRefIdx + 1
|
|
199
|
-
if (inputRefs.current.length <= nextRef) return
|
|
200
|
-
focus(nextRef)
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
registeredFields.current.push(type)
|
|
207
|
-
|
|
208
|
-
const resolvedProps = {
|
|
209
|
-
...staticProps,
|
|
210
|
-
...dynamicProps,
|
|
211
|
-
validate: (v) => {
|
|
212
|
-
return validateField(field, true, v)
|
|
213
|
-
},
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const componentPropsConfig = TypeGuards.isFunction(componentProps) ? componentProps(resolvedProps) : (componentProps || {})
|
|
217
|
-
|
|
218
|
-
const t = transformProps({
|
|
219
|
-
...resolvedProps,
|
|
220
|
-
...componentProps,
|
|
221
|
-
...componentPropsConfig,
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
return t
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function getTransformedValue(): Values {
|
|
228
|
-
return formValues as Values
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
async function onSubmit(cb: FunctionType<[Values], any>, e?: any) {
|
|
232
|
-
if (e?.preventDefault) e.preventDefault()
|
|
233
|
-
|
|
234
|
-
await cb(getTransformedValue())
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function reset(states?: ('values'|'errors')[]) {
|
|
238
|
-
const resetStates = states || ['values', 'errors']
|
|
239
|
-
if (resetStates.includes('values')) {
|
|
240
|
-
setFormValues(getInitialState())
|
|
241
|
-
}
|
|
242
|
-
if (resetStates.includes('errors')) {
|
|
243
|
-
setFieldErrors(getInitialErrors())
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const setters = {} as FormTypes.FormSetters<Values>
|
|
248
|
-
|
|
249
|
-
for (const fieldName in formValues) {
|
|
250
|
-
// @ts-ignore
|
|
251
|
-
setters[fieldName] = (value) => setFieldValue(fieldName, value)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
setFieldValue,
|
|
256
|
-
values: formValues as Values,
|
|
257
|
-
register,
|
|
258
|
-
validateAll,
|
|
259
|
-
validateField,
|
|
260
|
-
onSubmit,
|
|
261
|
-
fieldErrors,
|
|
262
|
-
getTransformedValue,
|
|
263
|
-
setFormValues,
|
|
264
|
-
reset,
|
|
265
|
-
validateMultiple,
|
|
266
|
-
isValid: validateAll(),
|
|
267
|
-
setters,
|
|
268
|
-
}
|
|
269
|
-
}
|
package/src/utils.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { ReactNode, useMemo, useRef, useState } from 'react'
|
|
2
|
-
import { onUpdate } from '@codeleap/hooks'
|
|
3
|
-
import { ValidatorFunctionWithoutForm } from './types'
|
|
4
|
-
import { getValidator } from './createForm'
|
|
5
|
-
|
|
6
|
-
import * as yup from 'yup'
|
|
7
|
-
|
|
8
|
-
const emptyValues = ['', null, undefined]
|
|
9
|
-
|
|
10
|
-
export function useValidate(value: any, validator: yup.SchemaOf<any> | ValidatorFunctionWithoutForm) {
|
|
11
|
-
|
|
12
|
-
const isEmpty = emptyValues.includes(value)
|
|
13
|
-
|
|
14
|
-
const [_message, setMessage] = useState<ReactNode>('')
|
|
15
|
-
const [isValid, setIsValid] = useState<boolean>(true)
|
|
16
|
-
const updateErrorOnChange = useRef(false)
|
|
17
|
-
|
|
18
|
-
const _validator = useMemo(() => getValidator(validator), [validator])
|
|
19
|
-
|
|
20
|
-
onUpdate(() => {
|
|
21
|
-
if (!updateErrorOnChange.current || !_validator) return
|
|
22
|
-
|
|
23
|
-
const { valid, message } = _validator(value, {})
|
|
24
|
-
|
|
25
|
-
setIsValid(valid)
|
|
26
|
-
setMessage(message)
|
|
27
|
-
}, [value])
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
onInputBlurred: () => {
|
|
31
|
-
|
|
32
|
-
if (!_validator) return
|
|
33
|
-
|
|
34
|
-
updateErrorOnChange.current = false
|
|
35
|
-
const { valid, message } = _validator(value, {})
|
|
36
|
-
|
|
37
|
-
setIsValid(valid)
|
|
38
|
-
setMessage(message)
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
onInputFocused: () => {
|
|
42
|
-
|
|
43
|
-
if (isValid) return
|
|
44
|
-
updateErrorOnChange.current = true
|
|
45
|
-
},
|
|
46
|
-
message: _message,
|
|
47
|
-
isValid,
|
|
48
|
-
showError: !isValid && !isEmpty,
|
|
49
|
-
error: {
|
|
50
|
-
message: _message,
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|