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

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 (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
+ }