@saas-ui/forms 2.0.0-next.0 → 2.0.0-next.10

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 (61) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +53 -6
  3. package/dist/ajv/index.d.ts +359 -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 +708 -682
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +688 -662
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/yup/index.d.ts +581 -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 +581 -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 +24 -15
  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 +72 -52
  38. package/src/index.ts +44 -4
  39. package/src/input-right-button/input-right-button.stories.tsx +2 -2
  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.stories.tsx +3 -3
  43. package/src/number-input/number-input.tsx +9 -5
  44. package/src/object-field.tsx +13 -8
  45. package/src/password-input/password-input.stories.tsx +23 -2
  46. package/src/password-input/password-input.tsx +6 -6
  47. package/src/pin-input/pin-input.tsx +1 -5
  48. package/src/radio/radio-input.stories.tsx +1 -1
  49. package/src/radio/radio-input.tsx +12 -10
  50. package/src/select/native-select.tsx +1 -4
  51. package/src/select/select-context.tsx +130 -0
  52. package/src/select/select.stories.tsx +117 -86
  53. package/src/select/select.test.tsx +1 -1
  54. package/src/select/select.tsx +162 -146
  55. package/src/step-form.tsx +29 -11
  56. package/src/submit-button.tsx +5 -1
  57. package/src/types.ts +144 -0
  58. package/src/use-array-field.tsx +9 -3
  59. package/src/utils.ts +23 -1
  60. package/src/watch-field.tsx +2 -6
  61. /package/src/radio/{radio.test.tsx → radio-input.test.tsx} +0 -0
@@ -10,176 +10,192 @@ import {
10
10
  MenuListProps,
11
11
  MenuItemOption,
12
12
  MenuOptionGroup,
13
- MenuOptionGroupProps,
14
13
  Button,
15
14
  ButtonProps,
16
15
  omitThemingProps,
17
16
  useMultiStyleConfig,
18
17
  SystemStyleObject,
19
- useFormControl,
20
- HTMLChakraProps,
18
+ MenuItemOptionProps,
19
+ useFormControlContext,
21
20
  } from '@chakra-ui/react'
22
- import { ChevronDownIcon } from '@chakra-ui/icons'
23
- import { cx, __DEV__ } from '@chakra-ui/utils'
21
+ import { cx, dataAttr } from '@chakra-ui/utils'
22
+ import { ChevronDownIcon } from '@saas-ui/core'
24
23
 
25
- interface Option {
26
- value: string
27
- label?: string
28
- }
24
+ import { FieldOption } from '../types'
29
25
 
30
- interface SelectOptions {
31
- /**
32
- * An array of options
33
- * If you leave this empty the children prop will be rendered.
34
- */
35
- options?: Option[]
36
- /**
37
- * Props passed to the MenuList.
38
- */
39
- menuListProps?: MenuListProps
40
- /**
41
- * Customize how the value is rendered.
42
- * @type (value?: string[]) => React.ReactElement
43
- */
44
- renderValue?: (value?: string[]) => React.ReactElement | undefined
45
- /**
46
- * Enable multiple select.
47
- */
48
- multiple?: boolean
49
- }
26
+ import {
27
+ SelectOptions,
28
+ SelectProvider,
29
+ useSelect,
30
+ useSelectContext,
31
+ } from './select-context'
32
+
33
+ export interface SelectOption
34
+ extends Omit<MenuItemOptionProps, 'value'>,
35
+ FieldOption {}
50
36
 
51
37
  export interface SelectProps
52
38
  extends Omit<MenuProps, 'children'>,
53
- Pick<ButtonProps, 'isDisabled' | 'leftIcon' | 'rightIcon'>,
54
- Pick<MenuOptionGroupProps, 'onChange'>,
55
39
  SelectOptions {}
56
40
 
57
- const SelectButton = forwardRef((props, ref) => {
58
- const styles = useMultiStyleConfig('Input', props)
59
-
60
- /* @ts-ignore */
61
- const focusStyles = styles.field._focusVisible
62
-
63
- const height = styles.field.h || styles.field.height
64
-
65
- const buttonStyles: SystemStyleObject = {
66
- fontWeight: 'normal',
67
- textAlign: 'left',
68
- color: 'inherit',
69
- _active: {
70
- bg: 'transparent',
71
- },
72
- minH: height,
73
- _focus: focusStyles,
74
- _expanded: focusStyles,
75
- ...styles.field,
76
- h: 'auto',
41
+ export interface SelectButtonProps extends ButtonProps {}
42
+
43
+ /**
44
+ * Button that opens the select menu and displays the selected value.
45
+ *
46
+ * @see https://saas-ui.dev/docs/components/forms/select
47
+ */
48
+ export const SelectButton = forwardRef<SelectButtonProps, 'button'>(
49
+ (props, ref) => {
50
+ const styles = useMultiStyleConfig('SuiSelect', props)
51
+
52
+ const {
53
+ displayValue,
54
+ renderValue,
55
+ placeholder,
56
+ isDisabled: isSelectDisabled,
57
+ } = useSelectContext()
58
+
59
+ const context = useFormControlContext()
60
+
61
+ const {
62
+ isInvalid,
63
+ isReadOnly,
64
+ isDisabled,
65
+ isFocused,
66
+ isRequired,
67
+ id,
68
+ onBlur,
69
+ onFocus,
70
+ } = context || {}
71
+
72
+ const { rightIcon = <ChevronDownIcon />, ...rest } = props
73
+
74
+ /* @ts-ignore */
75
+ const focusStyles = styles.field?._focusVisible
76
+ /* @ts-ignore */
77
+ const readOnlyStyles = styles.field?._readOnly
78
+ /* @ts-ignore */
79
+ const invalid = styles.field?._invalid
80
+
81
+ const height = styles.field?.h || styles.field?.height
82
+
83
+ const buttonStyles: SystemStyleObject = {
84
+ fontWeight: 'normal',
85
+ textAlign: 'left',
86
+ color: 'inherit',
87
+ _active: {
88
+ bg: 'transparent',
89
+ },
90
+ minH: height,
91
+ _focus: focusStyles,
92
+ _expanded: focusStyles,
93
+ _readOnly: readOnlyStyles,
94
+ _invalid: invalid,
95
+ ...styles.field,
96
+ h: 'auto',
97
+ }
98
+
99
+ // Using a Button, so we can simply use leftIcon and rightIcon
100
+ return (
101
+ <MenuButton
102
+ as={Button}
103
+ id={id || React.useId()}
104
+ {...rest}
105
+ onFocus={onFocus}
106
+ onBlur={onBlur}
107
+ isDisabled={isDisabled || isSelectDisabled}
108
+ data-invalid={dataAttr(isInvalid)}
109
+ data-read-only={dataAttr(isReadOnly)}
110
+ data-focus={dataAttr(isFocused)}
111
+ data-required={dataAttr(isRequired)}
112
+ rightIcon={rightIcon}
113
+ ref={ref}
114
+ sx={buttonStyles}
115
+ >
116
+ {renderValue(displayValue) || placeholder}
117
+ </MenuButton>
118
+ )
77
119
  }
120
+ )
78
121
 
79
- // Using a Button, so we can simply use leftIcon and rightIcon
80
- return <MenuButton as={Button} {...props} ref={ref} sx={buttonStyles} />
81
- })
82
-
83
- if (__DEV__) {
84
- SelectButton.displayName = 'SelectButton'
85
- }
122
+ SelectButton.displayName = 'SelectButton'
86
123
 
124
+ /**
125
+ * Allow users to select a value from a list of options.
126
+ *
127
+ * @see https://saas-ui.dev/docs/components/forms/select
128
+ */
87
129
  export const Select = forwardRef<SelectProps, 'select'>((props, ref) => {
88
- const {
89
- name,
90
- options,
91
- children,
92
- onChange,
93
- defaultValue,
94
- value,
95
- placeholder,
96
- isDisabled,
97
- leftIcon,
98
- rightIcon = <ChevronDownIcon />,
99
- multiple,
100
- size,
101
- variant,
102
- menuListProps,
103
- renderValue = (value) => value?.join(', '),
104
- ...rest
105
- } = props
106
- const menuProps = omitThemingProps(rest)
130
+ const { name, children, isDisabled, multiple, ...rest } = props
107
131
 
108
- const [currentValue, setCurrentValue] = React.useState(value || defaultValue)
109
-
110
- const controlProps = useFormControl({ name } as HTMLChakraProps<'input'>)
132
+ const menuProps = omitThemingProps(rest)
111
133
 
112
- const handleChange = (value: string | string[]) => {
113
- setCurrentValue(value)
114
- onChange?.(value)
115
- }
134
+ const context = useSelect(props)
116
135
 
117
- const buttonProps = {
118
- isDisabled,
119
- leftIcon,
120
- rightIcon,
121
- size,
122
- variant,
123
- }
136
+ const { value, controlProps } = context
124
137
 
125
- const getDisplayValue = React.useCallback(
126
- (value: string) => {
127
- if (!options) {
128
- return value
129
- }
130
-
131
- for (const option of options) {
132
- if (option.label && option.value === value) {
133
- return option.label
134
- }
135
- }
136
-
137
- return value
138
- },
139
- [options]
138
+ return (
139
+ <SelectProvider value={context}>
140
+ <Menu {...menuProps} closeOnSelect={!multiple}>
141
+ <chakra.div className={cx('sui-select')}>
142
+ {children}
143
+ <chakra.input
144
+ {...controlProps}
145
+ ref={ref}
146
+ name={name}
147
+ type="hidden"
148
+ value={value || ''}
149
+ className="saas-select__input"
150
+ />
151
+ </chakra.div>
152
+ </Menu>
153
+ </SelectProvider>
140
154
  )
155
+ })
156
+
157
+ export interface SelectListProps extends MenuListProps {}
141
158
 
142
- const displayValue = currentValue
143
- ? (Array.isArray(currentValue) ? currentValue : [currentValue]).map(
144
- getDisplayValue
145
- )
146
- : []
159
+ /**
160
+ * The list of options to choose from.
161
+ *
162
+ * @see https://saas-ui.dev/docs/components/forms/select
163
+ */
164
+ export const SelectList: React.FC<SelectListProps> = (props) => {
165
+ const { defaultValue, value, options, multiple, onChange } =
166
+ useSelectContext()
147
167
 
148
168
  return (
149
- <Menu {...menuProps} closeOnSelect={!multiple}>
150
- <chakra.div className={cx('sui-select')}>
151
- <SelectButton ref={ref} {...buttonProps}>
152
- {renderValue(displayValue) || placeholder}
153
- </SelectButton>
154
- <MenuList maxH="60vh" overflowY="auto" {...menuListProps}>
155
- <MenuOptionGroup
156
- defaultValue={
157
- (defaultValue || value) as string | string[] | undefined
158
- }
159
- onChange={handleChange}
160
- type={multiple ? 'checkbox' : 'radio'}
161
- >
162
- {options
163
- ? options.map(({ value, label, ...rest }, i) => (
164
- <MenuItemOption key={i} value={value} {...rest}>
165
- {label || value}
166
- </MenuItemOption>
167
- ))
168
- : children}
169
- </MenuOptionGroup>
170
- </MenuList>
171
- <chakra.input
172
- {...controlProps}
173
- name={name}
174
- type="hidden"
175
- value={currentValue}
176
- className="saas-select__input"
177
- />
178
- </chakra.div>
179
- </Menu>
169
+ <MenuList maxH="100vh" overflowY="auto" {...props}>
170
+ <MenuOptionGroup
171
+ defaultValue={(defaultValue || value) as string | string[] | undefined}
172
+ value={value}
173
+ onChange={onChange}
174
+ type={multiple ? 'checkbox' : 'radio'}
175
+ >
176
+ {options
177
+ ? options.map(({ value, label, ...rest }, i) => (
178
+ <SelectOption key={i} value={value} {...rest}>
179
+ {label || value}
180
+ </SelectOption>
181
+ ))
182
+ : props.children}
183
+ </MenuOptionGroup>
184
+ </MenuList>
180
185
  )
181
- })
182
-
183
- if (__DEV__) {
184
- Select.displayName = 'Select'
185
186
  }
187
+
188
+ Select.displayName = 'Select'
189
+
190
+ /**
191
+ * An option in a select list
192
+ *
193
+ * @see https://saas-ui.dev/docs/components/forms/select
194
+ */
195
+ export const SelectOption = forwardRef<MenuItemOptionProps, 'button'>(
196
+ (props, ref) => {
197
+ return <MenuItemOption ref={ref} {...props} />
198
+ }
199
+ )
200
+ SelectOption.id = 'MenuItemOption'
201
+ SelectOption.displayName = 'SelectOption'
package/src/step-form.tsx CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  ThemingProps,
11
11
  } from '@chakra-ui/react'
12
12
 
13
- import { callAllHandlers, runIfFn, cx, __DEV__ } from '@chakra-ui/utils'
13
+ import { callAllHandlers, runIfFn, cx } from '@chakra-ui/utils'
14
14
 
15
15
  import {
16
16
  StepperProvider,
@@ -38,6 +38,11 @@ export interface StepFormProps<
38
38
  TContext extends object = object
39
39
  > extends UseStepFormProps<TFieldValues> {}
40
40
 
41
+ /**
42
+ * The wrapper component provides context, state, and focus management.
43
+ *
44
+ * @see Docs https://saas-ui.dev/docs/components/forms/step-form
45
+ */
41
46
  export const StepForm = React.forwardRef(
42
47
  <
43
48
  TFieldValues extends FieldValues = FieldValues,
@@ -89,6 +94,11 @@ export interface FormStepperProps
89
94
  extends StepperStepsProps,
90
95
  ThemingProps<'Stepper'> {}
91
96
 
97
+ /**
98
+ * Renders a stepper that displays progress above the form.
99
+ *
100
+ * @see Docs https://saas-ui.dev/docs/components/forms/step-form
101
+ */
92
102
  export const FormStepper: React.FC<FormStepperProps> = (props) => {
93
103
  const { activeIndex, setIndex } = useStepperContext()
94
104
 
@@ -139,7 +149,11 @@ export interface FormStepProps
139
149
  Omit<HTMLChakraProps<'div'>, 'onSubmit'> {
140
150
  onSubmit?: FormStepSubmitHandler
141
151
  }
142
-
152
+ /**
153
+ * The form step containing fields for a specific step.
154
+ *
155
+ * @see Docs https://saas-ui.dev/docs/components/forms/step-form
156
+ */
143
157
  export const FormStep: React.FC<FormStepProps> = (props) => {
144
158
  const { name, schema, resolver, children, className, onSubmit, ...rest } =
145
159
  props
@@ -154,10 +168,13 @@ export const FormStep: React.FC<FormStepProps> = (props) => {
154
168
  ) : null
155
169
  }
156
170
 
157
- if (__DEV__) {
158
- FormStep.displayName = 'FormStep'
159
- }
171
+ FormStep.displayName = 'FormStep'
160
172
 
173
+ /**
174
+ * A button that this opens the previous step when clicked. Disabled on the first step.
175
+ *
176
+ * @see Docs https://saas-ui.dev/docs/components/forms/step-form
177
+ */
161
178
  export const PrevButton: React.FC<ButtonProps> = (props) => {
162
179
  const { isFirstStep, isCompleted, prevStep } = useStepperContext()
163
180
 
@@ -172,15 +189,18 @@ export const PrevButton: React.FC<ButtonProps> = (props) => {
172
189
  )
173
190
  }
174
191
 
175
- if (__DEV__) {
176
- PrevButton.displayName = 'PrevButton'
177
- }
192
+ PrevButton.displayName = 'PrevButton'
178
193
 
179
194
  export interface NextButtonProps extends Omit<ButtonProps, 'children'> {
180
195
  submitLabel?: string
181
196
  label?: string
182
197
  }
183
198
 
199
+ /**
200
+ * A button that submits the active step.
201
+ *
202
+ * @see Docs https://saas-ui.dev/docs/components/forms/step-form
203
+ */
184
204
  export const NextButton: React.FC<NextButtonProps> = (props) => {
185
205
  const { label = 'Next', submitLabel = 'Complete', ...rest } = props
186
206
  const { isLastStep, isCompleted } = useStepperContext()
@@ -196,6 +216,4 @@ export const NextButton: React.FC<NextButtonProps> = (props) => {
196
216
  )
197
217
  }
198
218
 
199
- if (__DEV__) {
200
- NextButton.displayName = 'NextButton'
201
- }
219
+ NextButton.displayName = 'NextButton'
@@ -20,7 +20,11 @@ export interface SubmitButtonProps extends ButtonProps {
20
20
  */
21
21
  disableIfInvalid?: boolean
22
22
  }
23
-
23
+ /**
24
+ * A button with type submit and default color scheme primary and isLoading state when the form is submitting.
25
+ *
26
+ * @see Docs https://saas-ui.dev/docs/components/forms/form
27
+ */
24
28
  export const SubmitButton = forwardRef<SubmitButtonProps, 'button'>(
25
29
  (props, ref) => {
26
30
  const {
package/src/types.ts ADDED
@@ -0,0 +1,144 @@
1
+ import { FormControlProps } from '@chakra-ui/react'
2
+ import { MaybeRenderProp } from '@chakra-ui/react-utils'
3
+ import { FieldPath, FieldValues, RegisterOptions } from 'react-hook-form'
4
+ import { DefaultFields } from './default-fields'
5
+ import { FormProps, FormRenderContext } from './form'
6
+ import { SubmitButtonProps } from './submit-button'
7
+
8
+ export type FieldOption = { label?: string; value: string }
9
+ export type FieldOptions<TOption extends FieldOption = FieldOption> =
10
+ | Array<string>
11
+ | Array<TOption>
12
+
13
+ export type ValueOf<T> = T[keyof T]
14
+ export type ShallowMerge<A, B> = Omit<A, keyof B> & B
15
+
16
+ type Split<S extends string, D extends string> = string extends S
17
+ ? string[]
18
+ : S extends ''
19
+ ? []
20
+ : S extends `${infer T}${D}${infer U}`
21
+ ? [T, ...Split<U, D>]
22
+ : [S]
23
+
24
+ type MapPath<T extends string[]> = T extends [infer U, ...infer R]
25
+ ? U extends string
26
+ ? `${U extends `${number}` ? '$' : U}${R[0] extends string
27
+ ? '.'
28
+ : ''}${R extends string[] ? MapPath<R> : ''}`
29
+ : ''
30
+ : ''
31
+
32
+ type TransformPath<T extends string> = MapPath<Split<T, '.'>>
33
+
34
+ export type ArrayFieldPath<Name extends string> = Name extends string
35
+ ? TransformPath<Name>
36
+ : never
37
+
38
+ export interface BaseFieldProps<
39
+ TFieldValues extends FieldValues = FieldValues,
40
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
41
+ > extends Omit<FormControlProps, 'label' | 'type'> {
42
+ /**
43
+ * The field name
44
+ */
45
+ name: TName | ArrayFieldPath<TName>
46
+ /**
47
+ * The field label
48
+ */
49
+ label?: string
50
+ /**
51
+ * Hide the field label
52
+ */
53
+ hideLabel?: boolean
54
+ /**
55
+ * Field help text
56
+ */
57
+ help?: string
58
+ /**
59
+ * React hook form rules
60
+ */
61
+ rules?: Omit<
62
+ RegisterOptions<TFieldValues, TName>,
63
+ 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
64
+ >
65
+ /**
66
+ * Build-in types:
67
+ * text, number, password, textarea, select, native-select, checkbox, radio, switch, pin
68
+ *
69
+ * Will default to a text field if there is no matching type.
70
+ */
71
+ type?: string
72
+ /**
73
+ * The input placeholder
74
+ */
75
+ placeholder?: string
76
+ }
77
+
78
+ type FieldPathWithArray<
79
+ TFieldValues extends FieldValues,
80
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
81
+ > = TName | ArrayFieldPath<TName>
82
+
83
+ type MergeFieldProps<
84
+ FieldDefs,
85
+ TFieldValues extends FieldValues = FieldValues,
86
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
87
+ > = ValueOf<{
88
+ [K in keyof FieldDefs]: FieldDefs[K] extends React.FC<infer Props>
89
+ ? { type?: K } & ShallowMerge<Props, BaseFieldProps<TFieldValues, TName>>
90
+ : never
91
+ }>
92
+
93
+ export type FieldProps<TFieldValues extends FieldValues = FieldValues> =
94
+ MergeFieldProps<DefaultFields, TFieldValues>
95
+
96
+ export type FormChildren<
97
+ FieldDefs,
98
+ TFieldValues extends FieldValues = FieldValues,
99
+ TContext extends object = object
100
+ > = MaybeRenderProp<
101
+ FormRenderContext<
102
+ TFieldValues,
103
+ TContext,
104
+ MergeFieldProps<
105
+ FieldDefs extends never
106
+ ? DefaultFields
107
+ : ShallowMerge<DefaultFields, FieldDefs>,
108
+ TFieldValues
109
+ >
110
+ >
111
+ >
112
+
113
+ export type DefaultFieldOverrides = {
114
+ submit?: SubmitButtonProps
115
+ [key: string]: any
116
+ }
117
+
118
+ export type FieldOverrides<
119
+ FieldDefs,
120
+ TFieldValues extends FieldValues = FieldValues,
121
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
122
+ > = {
123
+ [K in FieldPathWithArray<TFieldValues, TName>]?: Omit<
124
+ MergeFieldProps<
125
+ FieldDefs extends never
126
+ ? DefaultFields
127
+ : ShallowMerge<DefaultFields, FieldDefs>,
128
+ TFieldValues
129
+ >,
130
+ 'name'
131
+ >
132
+ }
133
+
134
+ export type WithFields<
135
+ TFormProps extends FormProps<any, any, any, any>,
136
+ FieldDefs
137
+ > = TFormProps extends FormProps<infer TFieldValues, infer TContext>
138
+ ? Omit<TFormProps, 'children' | 'fields'> & {
139
+ children?: FormChildren<FieldDefs, TFieldValues, TContext>
140
+ fields?: FieldOverrides<FieldDefs, TFieldValues> & {
141
+ submit?: SubmitButtonProps
142
+ }
143
+ }
144
+ : never
@@ -1,10 +1,13 @@
1
1
  import * as React from 'react'
2
2
  import {
3
3
  useFieldArray,
4
- useFormContext,
5
4
  UseFieldArrayReturn,
5
+ FieldValues,
6
+ FieldPath,
6
7
  } from 'react-hook-form'
7
8
 
9
+ import { useFormContext } from './form-context'
10
+
8
11
  import { createContext } from '@chakra-ui/react-utils'
9
12
 
10
13
  export interface UseArrayFieldReturn extends UseFieldArrayReturn {
@@ -59,11 +62,14 @@ export const [ArrayFieldRowProvider, useArrayFieldRowContext] =
59
62
  name: 'ArrayFieldRowContext',
60
63
  })
61
64
 
62
- export interface ArrayFieldOptions {
65
+ export interface ArrayFieldOptions<
66
+ TFieldValues extends FieldValues = FieldValues,
67
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
68
+ > {
63
69
  /**
64
70
  * The field name
65
71
  */
66
- name: string
72
+ name: TName
67
73
  /**
68
74
  * Default value for new values in the array
69
75
  */
package/src/utils.ts CHANGED
@@ -1,13 +1,35 @@
1
1
  import * as React from 'react'
2
+ import { FieldOption, FieldOptions } from './types'
2
3
 
3
4
  export const mapNestedFields = (name: string, children: React.ReactNode) => {
4
5
  return React.Children.map(children, (child) => {
5
6
  if (React.isValidElement(child) && child.props.name) {
7
+ let childName = child.props.name
8
+ if (childName.includes('.')) {
9
+ childName = childName.replace(/^.*\.(.*)/, '$1')
10
+ } else if (childName.includes('.$')) {
11
+ childName = childName.replace(/^.*\.\$(.*)/, '$1')
12
+ }
13
+
6
14
  return React.cloneElement(child, {
7
15
  ...child.props,
8
- name: `${name}.${child.props.name}`,
16
+ name: `${name}.${childName}`,
9
17
  })
10
18
  }
11
19
  return child
12
20
  })
13
21
  }
22
+
23
+ export const mapOptions = <TOption extends FieldOption = FieldOption>(
24
+ options: FieldOptions<TOption>
25
+ ) => {
26
+ return options.map((option) => {
27
+ if (typeof option === 'string') {
28
+ return {
29
+ label: option,
30
+ value: option,
31
+ }
32
+ }
33
+ return option
34
+ })
35
+ }
@@ -1,9 +1,5 @@
1
- import {
2
- FieldValues,
3
- useFormContext,
4
- UseFormReturn,
5
- useWatch,
6
- } from 'react-hook-form'
1
+ import { FieldValues, useWatch } from 'react-hook-form'
2
+ import { useFormContext, UseFormReturn } from './form-context'
7
3
 
8
4
  export interface WatchFieldProps<
9
5
  Value = unknown,