@saas-ui/forms 2.0.0-next.3 → 2.0.0-next.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +53 -6
  3. package/dist/ajv/index.d.ts +358 -11
  4. package/dist/ajv/index.js +7 -9
  5. package/dist/ajv/index.js.map +1 -1
  6. package/dist/ajv/index.mjs +7 -10
  7. package/dist/ajv/index.mjs.map +1 -1
  8. package/dist/index.d.ts +448 -247
  9. package/dist/index.js +707 -682
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +691 -666
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/yup/index.d.ts +580 -21
  14. package/dist/yup/index.js +6 -10
  15. package/dist/yup/index.js.map +1 -1
  16. package/dist/yup/index.mjs +4 -8
  17. package/dist/yup/index.mjs.map +1 -1
  18. package/dist/zod/index.d.ts +580 -11
  19. package/dist/zod/index.js +5 -0
  20. package/dist/zod/index.js.map +1 -1
  21. package/dist/zod/index.mjs +5 -1
  22. package/dist/zod/index.mjs.map +1 -1
  23. package/package.json +19 -10
  24. package/src/array-field.tsx +82 -45
  25. package/src/auto-form.tsx +7 -3
  26. package/src/base-field.tsx +54 -0
  27. package/src/create-field.tsx +144 -0
  28. package/src/create-form.tsx +54 -0
  29. package/src/default-fields.tsx +163 -0
  30. package/src/display-field.tsx +9 -11
  31. package/src/display-if.tsx +20 -13
  32. package/src/field-resolver.ts +10 -8
  33. package/src/field.tsx +18 -445
  34. package/src/fields-context.tsx +23 -0
  35. package/src/fields.tsx +34 -21
  36. package/src/form-context.tsx +84 -0
  37. package/src/form.tsx +69 -52
  38. package/src/index.ts +44 -4
  39. package/src/input-right-button/input-right-button.stories.tsx +1 -1
  40. package/src/input-right-button/input-right-button.tsx +0 -2
  41. package/src/layout.tsx +16 -11
  42. package/src/number-input/number-input.tsx +9 -5
  43. package/src/object-field.tsx +13 -8
  44. package/src/password-input/password-input.stories.tsx +23 -2
  45. package/src/password-input/password-input.tsx +6 -6
  46. package/src/pin-input/pin-input.tsx +1 -5
  47. package/src/radio/radio-input.stories.tsx +1 -1
  48. package/src/radio/radio-input.tsx +12 -10
  49. package/src/select/native-select.tsx +1 -4
  50. package/src/select/select-context.tsx +130 -0
  51. package/src/select/select.stories.tsx +116 -85
  52. package/src/select/select.test.tsx +1 -1
  53. package/src/select/select.tsx +160 -146
  54. package/src/step-form.tsx +29 -11
  55. package/src/submit-button.tsx +5 -1
  56. package/src/types.ts +144 -0
  57. package/src/use-array-field.tsx +9 -3
  58. package/src/utils.ts +23 -1
  59. package/src/watch-field.tsx +2 -6
  60. /package/src/radio/{radio.test.tsx → radio-input.test.tsx} +0 -0
package/src/field.tsx CHANGED
@@ -1,45 +1,10 @@
1
1
  import * as React from 'react'
2
- import {
3
- useFormContext,
4
- FormState,
5
- Controller,
6
- get,
7
- RegisterOptions,
8
- FieldValues,
9
- FieldPath,
10
- } from 'react-hook-form'
11
-
12
- import {
13
- forwardRef,
14
- Box,
15
- FormControl,
16
- FormControlProps,
17
- FormLabel,
18
- FormHelperText,
19
- FormErrorMessage,
20
- Input,
21
- Textarea,
22
- Checkbox,
23
- Switch,
24
- useMergeRefs,
25
- InputGroup,
26
- InputProps,
27
- TextareaProps,
28
- SwitchProps,
29
- CheckboxProps,
30
- PinInputField,
31
- HStack,
32
- PinInput,
33
- UsePinInputProps,
34
- SystemProps,
35
- } from '@chakra-ui/react'
36
- import { __DEV__, FocusableElement, callAllHandlers } from '@chakra-ui/utils'
37
-
38
- import { NumberInput, NumberInputProps } from './number-input'
39
- import { PasswordInput, PasswordInputProps } from './password-input'
40
- import { RadioInput, RadioInputProps } from './radio'
2
+ import { RegisterOptions, FieldValues } from 'react-hook-form'
41
3
 
42
- import { Select, SelectProps, NativeSelect, NativeSelectProps } from './select'
4
+ import { FocusableElement } from '@chakra-ui/utils'
5
+ import { useField } from './fields-context'
6
+ import { FieldProps } from './types'
7
+ import { useFieldProps } from './form-context'
43
8
 
44
9
  export interface Option {
45
10
  value: string
@@ -52,425 +17,33 @@ export type FieldRules = Pick<
52
17
  'required' | 'min' | 'max' | 'maxLength' | 'minLength' | 'pattern'
53
18
  >
54
19
 
55
- export interface FieldProps<
56
- TFieldValues extends FieldValues = FieldValues,
57
- TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
58
- > extends Omit<FormControlProps, 'label' | 'type'> {
59
- /**
60
- * The field name
61
- */
62
- name: TName
63
- /**
64
- * The field label
65
- */
66
- label?: string
67
- /**
68
- * Hide the field label
69
- */
70
- hideLabel?: boolean
71
- /**
72
- * Field help text
73
- */
74
- help?: string
75
- /**
76
- * React hook form rules
77
- */
78
- rules?: Omit<
79
- RegisterOptions<TFieldValues, TName>,
80
- 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
81
- >
82
- /**
83
- * Build-in types:
84
- * - text
85
- * - number
86
- * - password
87
- * - textarea
88
- * - select
89
- * - native-select
90
- * - checkbox
91
- * - radio
92
- * - switch
93
- * - pin
94
- *
95
- * Will default to a text field if there is no matching type.
96
- */
97
- type?: string
98
- /**
99
- * The input placeholder
100
- */
101
- placeholder?: string
102
- }
103
-
104
- const inputTypes: Record<string, React.FC<any>> = {}
105
-
106
20
  const defaultInputType = 'text'
107
21
 
108
- const getInput = (type: string) => {
109
- return inputTypes[type] || inputTypes[defaultInputType]
110
- }
111
-
112
- const getError = (name: string, formState: FormState<{ [x: string]: any }>) => {
113
- return get(formState.errors, name)
114
- }
115
-
116
- const isTouched = (
117
- name: string,
118
- formState: FormState<{ [x: string]: any }>
119
- ) => {
120
- return get(formState.touchedFields, name)
121
- }
122
-
123
- export const BaseField: React.FC<FieldProps> = (props) => {
124
- const { name, label, help, hideLabel, children, ...controlProps } = props
125
-
126
- const { formState } = useFormContext()
127
-
128
- const error = getError(name, formState)
129
-
130
- return (
131
- <FormControl {...controlProps} isInvalid={!!error}>
132
- {label && !hideLabel ? <FormLabel>{label}</FormLabel> : null}
133
- <Box>
134
- {children}
135
- {help && !error?.message ? (
136
- <FormHelperText>{help}</FormHelperText>
137
- ) : null}
138
- {error?.message && (
139
- <FormErrorMessage>{error?.message}</FormErrorMessage>
140
- )}
141
- </Box>
142
- </FormControl>
143
- )
144
- }
145
-
146
- if (__DEV__) {
147
- BaseField.displayName = 'BaseField'
148
- }
149
-
150
- export type As<Props = any> = React.ElementType<Props>
151
-
152
- export type PropsOf<T extends As> = React.ComponentPropsWithoutRef<T> & {
153
- type?: FieldTypes
154
- }
155
-
156
22
  /**
23
+ * Form field component.
24
+ *
157
25
  * Build-in types:
158
- * - text
159
- * - number
160
- * - password
161
- * - textarea
162
- * - select
163
- * - native-select
164
- * - checkbox
165
- * - radio
166
- * - switch
167
- * - pin
26
+ * text, number, password, textarea, select, native-select, checkbox, radio, switch, pin
168
27
  *
169
28
  * Will default to a text field if there is no matching type.
29
+
30
+ * @see Docs https://saas-ui.dev/docs/components/forms/field
170
31
  */
171
32
  export const Field = React.forwardRef(
172
33
  <TFieldValues extends FieldValues = FieldValues>(
173
- props: FieldProps<TFieldValues> | FieldTypeProps,
34
+ props: FieldProps<TFieldValues>,
174
35
  ref: React.ForwardedRef<FocusableElement>
175
36
  ) => {
176
- const { type = defaultInputType } = props
177
- const InputComponent = getInput(type)
37
+ const { type = defaultInputType, name } = props
38
+ const overrides = useFieldProps(name)
39
+ const InputComponent = useField(overrides?.type || type)
178
40
 
179
- return <InputComponent ref={ref} {...props} />
41
+ return <InputComponent ref={ref} {...props} {...overrides} />
180
42
  }
181
43
  ) as (<TFieldValues extends FieldValues>(
182
- props: FieldProps<TFieldValues> &
183
- FieldTypeProps & {
184
- ref?: React.ForwardedRef<FocusableElement>
185
- }
44
+ props: FieldProps<TFieldValues> & {
45
+ ref?: React.ForwardedRef<FocusableElement>
46
+ }
186
47
  ) => React.ReactElement) & {
187
48
  displayName?: string
188
49
  }
189
-
190
- interface CreateFieldProps {
191
- displayName: string
192
- hideLabel?: boolean
193
- BaseField: React.FC<any>
194
- }
195
-
196
- const createField = (
197
- InputComponent: React.FC<any>,
198
- { displayName, hideLabel, BaseField }: CreateFieldProps
199
- ) => {
200
- const Field = forwardRef((props, ref) => {
201
- const {
202
- id,
203
- name,
204
- label,
205
- help,
206
- isDisabled,
207
- isInvalid,
208
- isReadOnly,
209
- isRequired,
210
- rules,
211
- ...inputProps
212
- } = props
213
-
214
- const inputRules = {
215
- required: isRequired,
216
- ...rules,
217
- }
218
-
219
- return (
220
- <BaseField
221
- id={id}
222
- name={name}
223
- label={label}
224
- help={help}
225
- hideLabel={hideLabel}
226
- isDisabled={isDisabled}
227
- isInvalid={isInvalid}
228
- isReadOnly={isReadOnly}
229
- isRequired={isRequired}
230
- >
231
- <InputComponent
232
- ref={ref}
233
- id={id}
234
- name={name}
235
- label={hideLabel ? label : undefined} // Only pass down the label when it should be inline.
236
- rules={inputRules}
237
- {...inputProps}
238
- />
239
- </BaseField>
240
- )
241
- })
242
- Field.displayName = displayName
243
-
244
- return Field
245
- }
246
-
247
- export const withControlledInput = (InputComponent: React.FC<any>) => {
248
- return forwardRef<FieldProps, typeof InputComponent>(
249
- ({ name, rules, ...inputProps }, ref) => {
250
- const { control } = useFormContext()
251
-
252
- return (
253
- <Controller
254
- name={name}
255
- control={control}
256
- rules={rules}
257
- render={({ field: { ref: _ref, ...field } }) => (
258
- <InputComponent
259
- {...field}
260
- {...inputProps}
261
- onChange={callAllHandlers(inputProps.onChange, field.onChange)}
262
- onBlur={callAllHandlers(inputProps.onBlur, field.onBlur)}
263
- ref={useMergeRefs(ref, _ref)}
264
- />
265
- )}
266
- />
267
- )
268
- }
269
- )
270
- }
271
-
272
- export const withUncontrolledInput = (InputComponent: React.FC<any>) => {
273
- return forwardRef<FieldProps, typeof InputComponent>(
274
- ({ name, rules, ...inputProps }, ref) => {
275
- const { register } = useFormContext()
276
-
277
- const { ref: _ref, ...field } = register(name, rules)
278
-
279
- return (
280
- <InputComponent
281
- {...field}
282
- {...inputProps}
283
- onChange={callAllHandlers(inputProps.onChange, field.onChange)}
284
- onBlur={callAllHandlers(inputProps.onBlur, field.onBlur)}
285
- ref={useMergeRefs(ref, _ref)}
286
- />
287
- )
288
- }
289
- )
290
- }
291
-
292
- export interface RegisterFieldTypeOptions {
293
- isControlled?: boolean
294
- hideLabel?: boolean
295
- BaseField?: React.FC<any>
296
- }
297
-
298
- /**
299
- * Register a new field type
300
- * @param type The name for this field in kebab-case, eg `email` or `array-field`
301
- * @param component The React component
302
- * @param options
303
- * @param options.isControlled Set this to true if this is a controlled field.
304
- * @param options.hideLabel Hide the field label, for example for the checkbox field.
305
- */
306
- export const registerFieldType = <T extends object>(
307
- type: string,
308
- component: React.FC<T>,
309
- options?: RegisterFieldTypeOptions
310
- ) => {
311
- let InputComponent
312
- if (options?.isControlled) {
313
- InputComponent = withControlledInput(component)
314
- } else {
315
- InputComponent = withUncontrolledInput(component)
316
- }
317
-
318
- const Field = createField(InputComponent, {
319
- displayName: `${type
320
- .split('-')
321
- .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
322
- .join('')}Field`,
323
- hideLabel: options?.hideLabel,
324
- BaseField: options?.BaseField || BaseField,
325
- }) as React.FC<T & FieldProps>
326
-
327
- inputTypes[type] = Field
328
-
329
- return Field
330
- }
331
-
332
- export interface InputFieldProps extends InputProps {
333
- type?: string
334
- leftAddon?: React.ReactNode
335
- rightAddon?: React.ReactNode
336
- }
337
-
338
- export const InputField = registerFieldType<InputFieldProps>(
339
- 'text',
340
- forwardRef(({ type = 'text', leftAddon, rightAddon, size, ...rest }, ref) => {
341
- const input = <Input type={type} size={size} {...rest} ref={ref} />
342
- if (leftAddon || rightAddon) {
343
- return (
344
- <InputGroup size={size}>
345
- {leftAddon}
346
- {input}
347
- {rightAddon}
348
- </InputGroup>
349
- )
350
- }
351
- return input
352
- })
353
- )
354
-
355
- export interface NumberInputFieldProps extends NumberInputProps {
356
- type: 'number'
357
- }
358
-
359
- export const NumberInputField = registerFieldType<NumberInputFieldProps>(
360
- 'number',
361
- NumberInput,
362
- {
363
- isControlled: true,
364
- }
365
- )
366
-
367
- export const PasswordInputField = registerFieldType<PasswordInputProps>(
368
- 'password',
369
- forwardRef((props, ref) => <PasswordInput ref={ref} {...props} />)
370
- )
371
-
372
- export const TextareaField = registerFieldType<TextareaProps>(
373
- 'textarea',
374
- Textarea
375
- )
376
-
377
- export const SwitchField = registerFieldType<SwitchProps>(
378
- 'switch',
379
- forwardRef(({ type, value, ...rest }, ref) => {
380
- return <Switch isChecked={!!value} {...rest} ref={ref} />
381
- }),
382
- {
383
- isControlled: true,
384
- }
385
- )
386
-
387
- export const SelectField = registerFieldType<SelectProps>('select', Select, {
388
- isControlled: true,
389
- })
390
-
391
- export const CheckboxField = registerFieldType<CheckboxProps>(
392
- 'checkbox',
393
- forwardRef(({ label, type, ...props }, ref) => {
394
- return (
395
- <Checkbox ref={ref} {...props}>
396
- {label}
397
- </Checkbox>
398
- )
399
- }),
400
- {
401
- hideLabel: true,
402
- }
403
- )
404
-
405
- export const RadioField = registerFieldType<RadioInputProps>(
406
- 'radio',
407
- RadioInput,
408
- {
409
- isControlled: true,
410
- }
411
- )
412
-
413
- export const NativeSelectField = registerFieldType<NativeSelectProps>(
414
- 'native-select',
415
- NativeSelect,
416
- { isControlled: true }
417
- )
418
-
419
- export interface PinFieldProps extends Omit<UsePinInputProps, 'type'> {
420
- pinLength?: number
421
- pinType?: 'alphanumeric' | 'number'
422
- spacing?: SystemProps['margin']
423
- }
424
-
425
- export const PinField = registerFieldType<PinFieldProps>(
426
- 'pin',
427
- forwardRef((props, ref) => {
428
- const { pinLength = 4, pinType, spacing, ...inputProps } = props
429
-
430
- const inputs: React.ReactNode[] = []
431
- for (let i = 0; i < pinLength; i++) {
432
- inputs.push(<PinInputField key={i} ref={ref} />)
433
- }
434
-
435
- return (
436
- <HStack spacing={spacing}>
437
- <PinInput {...inputProps} type={pinType}>
438
- {inputs}
439
- </PinInput>
440
- </HStack>
441
- )
442
- }),
443
- {
444
- isControlled: true,
445
- }
446
- )
447
-
448
- const fieldTypes = {
449
- text: InputField,
450
- email: InputField,
451
- url: InputField,
452
- phone: InputField,
453
- number: NumberInputField,
454
- password: PasswordInputField,
455
- textarea: TextareaField,
456
- switch: SwitchField,
457
- checkbox: CheckboxField,
458
- radio: RadioField,
459
- pin: PinField,
460
- select: SelectField,
461
- 'native-select': NativeSelectField,
462
- }
463
-
464
- type FieldTypes = typeof fieldTypes
465
-
466
- type FieldType<Props = any> = React.ElementType<Props>
467
-
468
- type TypeProps<P extends FieldType, T> = React.ComponentPropsWithoutRef<P> & {
469
- type: T
470
- }
471
-
472
- type FieldTypeProps =
473
- | {
474
- [Property in keyof FieldTypes]: TypeProps<FieldTypes[Property], Property>
475
- }[keyof FieldTypes]
476
- | { type?: string }
@@ -0,0 +1,23 @@
1
+ import React from 'react'
2
+ import { defaultFieldTypes, InputField } from './default-fields'
3
+
4
+ const FieldsContext = React.createContext<Record<string, React.FC<any>> | null>(
5
+ null
6
+ )
7
+
8
+ export const FieldsProvider: React.FC<{
9
+ value: Record<string, React.FC<any>>
10
+ children: React.ReactNode
11
+ }> = (props) => {
12
+ const fields = { ...defaultFieldTypes, ...props.value }
13
+ return (
14
+ <FieldsContext.Provider value={fields}>
15
+ {props.children}
16
+ </FieldsContext.Provider>
17
+ )
18
+ }
19
+
20
+ export const useField = (type: string): React.FC<any> => {
21
+ const context = React.useContext(FieldsContext)
22
+ return context?.[type] || InputField
23
+ }
package/src/fields.tsx CHANGED
@@ -1,17 +1,16 @@
1
1
  import * as React from 'react'
2
- import { __DEV__ } from '@chakra-ui/utils'
3
2
 
4
- import { Form } from './form'
5
3
  import { FormLayout } from './layout'
6
- import { Field, FieldProps } from './field'
4
+ import { BaseFieldProps } from './types'
5
+ import { Field } from './field'
7
6
 
8
7
  import { ArrayField } from './array-field'
9
8
  import { ObjectField } from './object-field'
10
9
  import { FieldResolver } from './field-resolver'
11
- import { useFormContext } from 'react-hook-form'
10
+ import { useFormContext } from './form-context'
12
11
 
13
- export interface FieldsProps {
14
- schema: any
12
+ export interface FieldsProps<TSchema = any> {
13
+ schema?: TSchema
15
14
  fieldResolver?: FieldResolver
16
15
  focusFirstField?: boolean
17
16
  }
@@ -20,42 +19,54 @@ const mapNestedFields = (resolver: FieldResolver, name: string) => {
20
19
  return resolver
21
20
  .getNestedFields(name)
22
21
  ?.map(
23
- ({ name, type, ...nestedFieldProps }: FieldProps, i): React.ReactNode => (
24
- <Field key={name || i} name={name} type={type} {...nestedFieldProps} />
22
+ (
23
+ { name, type, ...nestedFieldProps }: BaseFieldProps,
24
+ i
25
+ ): React.ReactNode => (
26
+ <Field
27
+ key={name || i}
28
+ name={name}
29
+ type={type as any}
30
+ {...nestedFieldProps}
31
+ />
25
32
  )
26
33
  )
27
34
  }
28
35
 
29
- export const Fields: React.FC<FieldsProps> = ({
30
- schema,
31
- fieldResolver,
36
+ export const AutoFields: React.FC<FieldsProps> = ({
37
+ schema: schemaProp,
38
+ fieldResolver: fieldResolverProp,
32
39
  focusFirstField,
33
40
  ...props
34
41
  }) => {
35
- const resolver = React.useMemo(
36
- () => fieldResolver || Form.getFieldResolver(schema),
37
- [schema, fieldResolver]
38
- )
42
+ const context = useFormContext()
43
+ const schema = schemaProp || context.schema
44
+ const fieldResolver = fieldResolverProp || context.fieldResolver
45
+ const resolver = React.useMemo(() => fieldResolver, [schema, fieldResolver])
39
46
 
40
- const fields = React.useMemo(() => resolver.getFields(), [resolver])
47
+ const fields = React.useMemo(() => resolver?.getFields(), [resolver])
41
48
 
42
49
  const form = useFormContext()
43
50
 
44
51
  React.useEffect(() => {
45
- if (focusFirstField && fields[0]?.name) {
52
+ if (focusFirstField && fields?.[0]?.name) {
46
53
  form.setFocus(fields[0].name)
47
54
  }
48
55
  }, [schema, fieldResolver, focusFirstField])
49
56
 
57
+ if (!resolver) {
58
+ return null
59
+ }
60
+
50
61
  return (
51
62
  <FormLayout {...props}>
52
- {fields.map(
63
+ {fields?.map(
53
64
  ({
54
65
  name,
55
66
  type,
56
67
  defaultValue,
57
68
  ...fieldProps
58
- }: FieldProps): React.ReactNode => {
69
+ }: BaseFieldProps): React.ReactNode => {
59
70
  if (type === 'array') {
60
71
  return (
61
72
  <ArrayField key={name} name={name} {...fieldProps}>
@@ -70,11 +81,13 @@ export const Fields: React.FC<FieldsProps> = ({
70
81
  )
71
82
  }
72
83
 
73
- return <Field key={name} name={name} type={type} {...fieldProps} />
84
+ return (
85
+ <Field key={name} name={name} type={type as any} {...fieldProps} />
86
+ )
74
87
  }
75
88
  )}
76
89
  </FormLayout>
77
90
  )
78
91
  }
79
92
 
80
- Fields.displayName = 'Fields'
93
+ AutoFields.displayName = 'Fields'
@@ -0,0 +1,84 @@
1
+ import { createContext, useContext } from 'react'
2
+ import {
3
+ FormProvider as HookFormProvider,
4
+ FormProviderProps as HookFormProviderProps,
5
+ useFormContext as useHookFormContext,
6
+ FieldValues,
7
+ } from 'react-hook-form'
8
+ import { FieldResolver } from './field-resolver'
9
+ import { BaseFieldProps, FieldProps } from './types'
10
+
11
+ export type FormContextValue<
12
+ TFieldValues extends FieldValues = FieldValues,
13
+ TContext = any,
14
+ TSchema = any
15
+ > = {
16
+ fieldResolver?: FieldResolver
17
+ schema?: TSchema
18
+ fields?: {
19
+ [key: string]: unknown
20
+ }
21
+ }
22
+
23
+ export type FormProviderProps<
24
+ TFieldValues extends FieldValues = FieldValues,
25
+ TContext = any,
26
+ TSchema = any
27
+ > = HookFormProviderProps<TFieldValues, TContext> & {
28
+ fieldResolver?: FieldResolver
29
+ schema?: TSchema
30
+ fields?: {
31
+ [key: string]: unknown
32
+ }
33
+ }
34
+
35
+ const FormContext = createContext<FormContextValue | null>(null)
36
+
37
+ export const useFormContext = <
38
+ TFieldValues extends FieldValues = FieldValues,
39
+ TContext = any,
40
+ TSchema = any
41
+ >() => {
42
+ const context = useContext(FormContext)
43
+ const hookContext = useHookFormContext()
44
+
45
+ if (!context) {
46
+ throw new Error('FormProvider must be used within a Form component')
47
+ }
48
+
49
+ return {
50
+ ...hookContext,
51
+ ...context,
52
+ }
53
+ }
54
+
55
+ export const useFieldProps = <TFieldValues extends FieldValues = FieldValues>(
56
+ name: string
57
+ ): BaseFieldProps<TFieldValues> | undefined => {
58
+ const parsedName = name?.replace(/\.[0-9]/g, '.$')
59
+ const context = useFormContext()
60
+ return context?.fields?.[parsedName] as any
61
+ }
62
+
63
+ export type UseFormReturn<
64
+ TFieldValues extends FieldValues = FieldValues,
65
+ TContext = any,
66
+ TSchema = any
67
+ > = ReturnType<typeof useFormContext>
68
+
69
+ export const FormProvider = <
70
+ TFieldValues extends FieldValues = FieldValues,
71
+ TContext = any,
72
+ TSchema = any
73
+ >(
74
+ props: FormProviderProps<TFieldValues, TContext, TSchema>
75
+ ) => {
76
+ const { children, fieldResolver, schema, fields, ...rest } = props
77
+ return (
78
+ <HookFormProvider {...rest}>
79
+ <FormContext.Provider value={{ fieldResolver, schema, fields }}>
80
+ {children}
81
+ </FormContext.Provider>
82
+ </HookFormProvider>
83
+ )
84
+ }