@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.
- package/CHANGELOG.md +95 -0
- package/README.md +53 -6
- package/dist/ajv/index.d.ts +359 -11
- package/dist/ajv/index.js +7 -9
- package/dist/ajv/index.js.map +1 -1
- package/dist/ajv/index.mjs +7 -10
- package/dist/ajv/index.mjs.map +1 -1
- package/dist/index.d.ts +448 -247
- package/dist/index.js +708 -682
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +688 -662
- package/dist/index.mjs.map +1 -1
- package/dist/yup/index.d.ts +581 -21
- package/dist/yup/index.js +6 -10
- package/dist/yup/index.js.map +1 -1
- package/dist/yup/index.mjs +4 -8
- package/dist/yup/index.mjs.map +1 -1
- package/dist/zod/index.d.ts +581 -11
- package/dist/zod/index.js +5 -0
- package/dist/zod/index.js.map +1 -1
- package/dist/zod/index.mjs +5 -1
- package/dist/zod/index.mjs.map +1 -1
- package/package.json +24 -15
- package/src/array-field.tsx +82 -45
- package/src/auto-form.tsx +7 -3
- package/src/base-field.tsx +54 -0
- package/src/create-field.tsx +144 -0
- package/src/create-form.tsx +54 -0
- package/src/default-fields.tsx +163 -0
- package/src/display-field.tsx +9 -11
- package/src/display-if.tsx +20 -13
- package/src/field-resolver.ts +10 -8
- package/src/field.tsx +18 -445
- package/src/fields-context.tsx +23 -0
- package/src/fields.tsx +34 -21
- package/src/form-context.tsx +84 -0
- package/src/form.tsx +72 -52
- package/src/index.ts +44 -4
- package/src/input-right-button/input-right-button.stories.tsx +2 -2
- package/src/input-right-button/input-right-button.tsx +0 -2
- package/src/layout.tsx +16 -11
- package/src/number-input/number-input.stories.tsx +3 -3
- package/src/number-input/number-input.tsx +9 -5
- package/src/object-field.tsx +13 -8
- package/src/password-input/password-input.stories.tsx +23 -2
- package/src/password-input/password-input.tsx +6 -6
- package/src/pin-input/pin-input.tsx +1 -5
- package/src/radio/radio-input.stories.tsx +1 -1
- package/src/radio/radio-input.tsx +12 -10
- package/src/select/native-select.tsx +1 -4
- package/src/select/select-context.tsx +130 -0
- package/src/select/select.stories.tsx +117 -86
- package/src/select/select.test.tsx +1 -1
- package/src/select/select.tsx +162 -146
- package/src/step-form.tsx +29 -11
- package/src/submit-button.tsx +5 -1
- package/src/types.ts +144 -0
- package/src/use-array-field.tsx +9 -3
- package/src/utils.ts +23 -1
- package/src/watch-field.tsx +2 -6
- /package/src/radio/{radio.test.tsx → radio-input.test.tsx} +0 -0
package/src/select/select.tsx
CHANGED
@@ -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
|
-
|
20
|
-
|
18
|
+
MenuItemOptionProps,
|
19
|
+
useFormControlContext,
|
21
20
|
} from '@chakra-ui/react'
|
22
|
-
import {
|
23
|
-
import {
|
21
|
+
import { cx, dataAttr } from '@chakra-ui/utils'
|
22
|
+
import { ChevronDownIcon } from '@saas-ui/core'
|
24
23
|
|
25
|
-
|
26
|
-
value: string
|
27
|
-
label?: string
|
28
|
-
}
|
24
|
+
import { FieldOption } from '../types'
|
29
25
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
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
|
109
|
-
|
110
|
-
const controlProps = useFormControl({ name } as HTMLChakraProps<'input'>)
|
132
|
+
const menuProps = omitThemingProps(rest)
|
111
133
|
|
112
|
-
const
|
113
|
-
setCurrentValue(value)
|
114
|
-
onChange?.(value)
|
115
|
-
}
|
134
|
+
const context = useSelect(props)
|
116
135
|
|
117
|
-
const
|
118
|
-
isDisabled,
|
119
|
-
leftIcon,
|
120
|
-
rightIcon,
|
121
|
-
size,
|
122
|
-
variant,
|
123
|
-
}
|
136
|
+
const { value, controlProps } = context
|
124
137
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
143
|
-
|
144
|
-
|
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
|
-
<
|
150
|
-
<
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
200
|
-
NextButton.displayName = 'NextButton'
|
201
|
-
}
|
219
|
+
NextButton.displayName = 'NextButton'
|
package/src/submit-button.tsx
CHANGED
@@ -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
|
package/src/use-array-field.tsx
CHANGED
@@ -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:
|
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}.${
|
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
|
+
}
|
package/src/watch-field.tsx
CHANGED
@@ -1,9 +1,5 @@
|
|
1
|
-
import {
|
2
|
-
|
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,
|
File without changes
|