@saas-ui/forms 2.0.0-next.3 → 2.0.0-next.5
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +30 -0
- package/README.md +53 -6
- package/dist/ajv/index.d.ts +1 -1
- package/dist/ajv/index.js.map +1 -1
- package/dist/ajv/index.mjs.map +1 -1
- package/dist/index.d.ts +265 -166
- package/dist/index.js +2821 -556
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2814 -555
- package/dist/index.mjs.map +1 -1
- package/dist/yup/index.d.ts +98 -6
- package/dist/yup/index.js.map +1 -1
- package/dist/yup/index.mjs.map +1 -1
- package/dist/zod/index.d.ts +97 -4
- package/dist/zod/index.js.map +1 -1
- package/dist/zod/index.mjs.map +1 -1
- package/package.json +5 -3
- package/src/array-field.tsx +50 -30
- package/src/auto-form.tsx +7 -3
- package/src/base-field.tsx +59 -0
- package/src/create-field.tsx +143 -0
- package/src/create-form.tsx +31 -0
- package/src/default-fields.tsx +146 -0
- package/src/display-field.tsx +8 -9
- package/src/display-if.tsx +6 -5
- package/src/field-resolver.ts +1 -1
- package/src/field.tsx +14 -444
- package/src/fields-context.tsx +23 -0
- package/src/fields.tsx +18 -8
- package/src/form.tsx +27 -37
- package/src/index.ts +38 -0
- package/src/input-right-button/input-right-button.stories.tsx +1 -1
- package/src/input-right-button/input-right-button.tsx +0 -2
- package/src/layout.tsx +16 -11
- package/src/number-input/number-input.tsx +9 -5
- package/src/object-field.tsx +8 -7
- package/src/password-input/password-input.stories.tsx +23 -2
- package/src/password-input/password-input.tsx +5 -5
- 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.test.tsx +1 -1
- package/src/select/select.tsx +18 -14
- package/src/step-form.tsx +29 -11
- package/src/submit-button.tsx +5 -1
- package/src/types.ts +91 -0
- package/src/utils.ts +15 -0
- /package/src/radio/{radio.test.tsx → radio-input.test.tsx} +0 -0
package/src/index.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
export * from './display-field'
|
2
2
|
export * from './field'
|
3
3
|
export * from './fields'
|
4
|
+
export * from './fields-context'
|
4
5
|
export * from './form'
|
5
6
|
export * from './auto-form'
|
6
7
|
export * from './layout'
|
@@ -16,6 +17,43 @@ export * from './watch-field'
|
|
16
17
|
export * from './input-right-button'
|
17
18
|
export * from './select'
|
18
19
|
export * from './password-input'
|
20
|
+
export * from './radio'
|
21
|
+
|
22
|
+
export * from './base-field'
|
23
|
+
|
24
|
+
export {
|
25
|
+
CheckboxField,
|
26
|
+
InputField,
|
27
|
+
NativeSelectField,
|
28
|
+
NumberInputField,
|
29
|
+
PasswordInputField,
|
30
|
+
PinField,
|
31
|
+
RadioField,
|
32
|
+
SelectField,
|
33
|
+
SwitchField,
|
34
|
+
TextareaField,
|
35
|
+
defaultFieldTypes,
|
36
|
+
} from './default-fields'
|
37
|
+
|
38
|
+
export type {
|
39
|
+
DefaultFields,
|
40
|
+
InputFieldProps,
|
41
|
+
NumberInputFieldProps,
|
42
|
+
PinFieldProps,
|
43
|
+
} from './default-fields'
|
44
|
+
|
45
|
+
export type {
|
46
|
+
FieldProps,
|
47
|
+
WithFields,
|
48
|
+
BaseFieldProps,
|
49
|
+
FieldOptions,
|
50
|
+
} from './types'
|
51
|
+
|
52
|
+
export { createForm } from './create-form'
|
53
|
+
export type { CreateFormProps } from './create-form'
|
54
|
+
|
55
|
+
export { createField } from './create-field'
|
56
|
+
export type { CreateFieldOptions } from './create-field'
|
19
57
|
|
20
58
|
export type {
|
21
59
|
BatchFieldArrayUpdate,
|
package/src/layout.tsx
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
import * as React from 'react'
|
2
2
|
|
3
|
-
import {
|
4
|
-
|
3
|
+
import {
|
4
|
+
chakra,
|
5
|
+
ResponsiveValue,
|
6
|
+
SimpleGrid,
|
7
|
+
SimpleGridProps,
|
8
|
+
useTheme,
|
9
|
+
} from '@chakra-ui/react'
|
10
|
+
import { cx } from '@chakra-ui/utils'
|
5
11
|
|
6
|
-
export
|
12
|
+
export interface FormLayoutProps extends SimpleGridProps {}
|
7
13
|
|
8
14
|
interface FormLayoutItemProps {
|
9
15
|
children: React.ReactNode
|
@@ -13,20 +19,21 @@ const FormLayoutItem: React.FC<FormLayoutItemProps> = ({ children }) => {
|
|
13
19
|
return <chakra.div>{children}</chakra.div>
|
14
20
|
}
|
15
21
|
|
16
|
-
|
17
|
-
FormLayoutItem.displayName = 'FormLayoutItem'
|
18
|
-
}
|
22
|
+
FormLayoutItem.displayName = 'FormLayoutItem'
|
19
23
|
|
20
24
|
/**
|
21
|
-
*
|
25
|
+
* Create consistent field spacing and positioning.
|
26
|
+
*
|
22
27
|
*
|
23
28
|
* Renders form items in a `SimpleGrid`
|
24
29
|
* @see https://chakra-ui.com/docs/layout/simple-grid
|
30
|
+
*
|
31
|
+
* @see https://saas-ui.dev/docs/components/forms/form
|
25
32
|
*/
|
26
33
|
export const FormLayout = ({ children, ...props }: FormLayoutProps) => {
|
27
34
|
const theme = useTheme()
|
28
35
|
|
29
|
-
const defaultProps = theme.components?.
|
36
|
+
const defaultProps = theme.components?.SuiFormLayout?.defaultProps ?? {
|
30
37
|
spacing: 4,
|
31
38
|
}
|
32
39
|
|
@@ -50,6 +57,4 @@ export const FormLayout = ({ children, ...props }: FormLayoutProps) => {
|
|
50
57
|
)
|
51
58
|
}
|
52
59
|
|
53
|
-
|
54
|
-
FormLayout.displayName = 'FormLayout'
|
55
|
-
}
|
60
|
+
FormLayout.displayName = 'FormLayout'
|
@@ -9,7 +9,8 @@ import {
|
|
9
9
|
NumberDecrementStepper,
|
10
10
|
NumberInputProps as ChakraNumberInputProps,
|
11
11
|
} from '@chakra-ui/react'
|
12
|
-
|
12
|
+
|
13
|
+
import { ChevronDownIcon, ChevronUpIcon } from '@saas-ui/core'
|
13
14
|
|
14
15
|
interface NumberInputOptions {
|
15
16
|
/**
|
@@ -31,7 +32,12 @@ export interface NumberInputProps
|
|
31
32
|
NumberInputOptions {}
|
32
33
|
|
33
34
|
export const NumberInput = forwardRef<NumberInputProps, 'div'>((props, ref) => {
|
34
|
-
const {
|
35
|
+
const {
|
36
|
+
hideStepper,
|
37
|
+
incrementIcon = <ChevronUpIcon />,
|
38
|
+
decrementIcon = <ChevronDownIcon />,
|
39
|
+
...rest
|
40
|
+
} = props
|
35
41
|
|
36
42
|
return (
|
37
43
|
<ChakraNumberInput {...rest} ref={ref}>
|
@@ -51,6 +57,4 @@ NumberInput.defaultProps = {
|
|
51
57
|
hideStepper: false,
|
52
58
|
}
|
53
59
|
|
54
|
-
|
55
|
-
NumberInput.displayName = 'NumberInput'
|
56
|
-
}
|
60
|
+
NumberInput.displayName = 'NumberInput'
|
package/src/object-field.tsx
CHANGED
@@ -6,14 +6,13 @@ import {
|
|
6
6
|
ResponsiveValue,
|
7
7
|
useStyleConfig,
|
8
8
|
} from '@chakra-ui/react'
|
9
|
-
import { __DEV__ } from '@chakra-ui/utils'
|
10
9
|
|
11
10
|
import { FormLayout } from './layout'
|
12
|
-
import {
|
11
|
+
import { BaseFieldProps } from './types'
|
13
12
|
|
14
13
|
import { mapNestedFields } from './utils'
|
15
14
|
|
16
|
-
export interface ObjectFieldProps extends
|
15
|
+
export interface ObjectFieldProps extends BaseFieldProps {
|
17
16
|
name: string
|
18
17
|
children: React.ReactNode
|
19
18
|
columns?: ResponsiveValue<number>
|
@@ -24,7 +23,11 @@ export const FormLegend = (props: FormLabelProps) => {
|
|
24
23
|
const styles = useStyleConfig('SuiFormLegend')
|
25
24
|
return <FormLabel as="legend" sx={styles} {...props} />
|
26
25
|
}
|
27
|
-
|
26
|
+
/**
|
27
|
+
* The object field component.
|
28
|
+
*
|
29
|
+
* @see Docs https://saas-ui.dev/docs/components/forms/object-field
|
30
|
+
*/
|
28
31
|
export const ObjectField: React.FC<ObjectFieldProps> = (props) => {
|
29
32
|
const { name, label, hideLabel, children, columns, spacing, ...fieldProps } =
|
30
33
|
props
|
@@ -39,6 +42,4 @@ export const ObjectField: React.FC<ObjectFieldProps> = (props) => {
|
|
39
42
|
)
|
40
43
|
}
|
41
44
|
|
42
|
-
|
43
|
-
ObjectField.displayName = 'ObjectField'
|
44
|
-
}
|
45
|
+
ObjectField.displayName = 'ObjectField'
|
@@ -1,8 +1,15 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
Container,
|
3
|
+
FormControl,
|
4
|
+
FormLabel,
|
5
|
+
Icon,
|
6
|
+
InputLeftAddon,
|
7
|
+
InputLeftElement,
|
8
|
+
} from '@chakra-ui/react'
|
2
9
|
import { Story } from '@storybook/react'
|
3
10
|
import * as React from 'react'
|
4
11
|
|
5
|
-
import { FiEye, FiEyeOff } from 'react-icons/fi'
|
12
|
+
import { FiEye, FiEyeOff, FiLock } from 'react-icons/fi'
|
6
13
|
|
7
14
|
import { PasswordInput } from './password-input'
|
8
15
|
|
@@ -48,3 +55,17 @@ export const CustomWidth: Story = () => (
|
|
48
55
|
<PasswordInput name="password" width="200px" />
|
49
56
|
</FormControl>
|
50
57
|
)
|
58
|
+
|
59
|
+
export const LeftAddon: Story = () => (
|
60
|
+
<FormControl>
|
61
|
+
<FormLabel>Password</FormLabel>
|
62
|
+
<PasswordInput
|
63
|
+
name="password"
|
64
|
+
leftAddon={
|
65
|
+
<InputLeftElement>
|
66
|
+
<FiLock />
|
67
|
+
</InputLeftElement>
|
68
|
+
}
|
69
|
+
/>
|
70
|
+
</FormControl>
|
71
|
+
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import React, { useState } from 'react'
|
2
2
|
|
3
3
|
import { forwardRef, InputGroup, Input, InputProps } from '@chakra-ui/react'
|
4
|
-
|
4
|
+
|
5
5
|
import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons'
|
6
6
|
|
7
7
|
import { InputRightButton } from '../input-right-button'
|
@@ -9,6 +9,7 @@ import { InputRightButton } from '../input-right-button'
|
|
9
9
|
interface PasswordOptions {
|
10
10
|
viewIcon?: React.ReactNode
|
11
11
|
viewOffIcon?: React.ReactNode
|
12
|
+
leftAddon?: React.ReactNode
|
12
13
|
}
|
13
14
|
|
14
15
|
export interface PasswordInputProps extends InputProps, PasswordOptions {}
|
@@ -23,6 +24,7 @@ export const PasswordInput = forwardRef<PasswordInputProps, 'div'>(
|
|
23
24
|
width,
|
24
25
|
size,
|
25
26
|
variant,
|
27
|
+
leftAddon,
|
26
28
|
...inputProps
|
27
29
|
} = props
|
28
30
|
const [show, setShow] = useState(false)
|
@@ -45,13 +47,13 @@ export const PasswordInput = forwardRef<PasswordInputProps, 'div'>(
|
|
45
47
|
|
46
48
|
return (
|
47
49
|
<InputGroup {...groupProps}>
|
50
|
+
{leftAddon}
|
48
51
|
<Input
|
49
52
|
{...inputProps}
|
50
53
|
ref={ref}
|
51
54
|
type={show ? 'text' : 'password'}
|
52
55
|
autoComplete={show ? 'off' : autoComplete}
|
53
56
|
/>
|
54
|
-
|
55
57
|
<InputRightButton
|
56
58
|
onClick={handleClick}
|
57
59
|
aria-label={label}
|
@@ -64,6 +66,4 @@ export const PasswordInput = forwardRef<PasswordInputProps, 'div'>(
|
|
64
66
|
}
|
65
67
|
)
|
66
68
|
|
67
|
-
|
68
|
-
PasswordInput.displayName = 'PasswordInput'
|
69
|
-
}
|
69
|
+
PasswordInput.displayName = 'PasswordInput'
|
@@ -8,8 +8,6 @@ import {
|
|
8
8
|
SystemProps,
|
9
9
|
} from '@chakra-ui/react'
|
10
10
|
|
11
|
-
import { __DEV__ } from '@chakra-ui/utils'
|
12
|
-
|
13
11
|
interface PinInputOptions {
|
14
12
|
/**
|
15
13
|
* The pin length.
|
@@ -45,6 +43,4 @@ PinInput.defaultProps = {
|
|
45
43
|
pinLength: 4,
|
46
44
|
}
|
47
45
|
|
48
|
-
|
49
|
-
PinInput.displayName = 'PinInput'
|
50
|
-
}
|
46
|
+
PinInput.displayName = 'PinInput'
|
@@ -10,15 +10,17 @@ import {
|
|
10
10
|
SystemProps,
|
11
11
|
StackDirection,
|
12
12
|
} from '@chakra-ui/react'
|
13
|
-
import {
|
13
|
+
import { FieldOptions, FieldOption } from '../types'
|
14
|
+
import { mapOptions } from '../utils'
|
14
15
|
|
15
|
-
interface
|
16
|
-
value
|
17
|
-
|
18
|
-
|
16
|
+
export interface RadioOption
|
17
|
+
extends Omit<RadioProps, 'value' | 'label'>,
|
18
|
+
FieldOption {}
|
19
|
+
|
20
|
+
export type RadioOptions = FieldOptions<RadioOption>
|
19
21
|
|
20
22
|
interface RadioInputOptions {
|
21
|
-
options:
|
23
|
+
options: RadioOptions
|
22
24
|
spacing?: SystemProps['margin']
|
23
25
|
direction?: StackDirection
|
24
26
|
}
|
@@ -28,9 +30,11 @@ export interface RadioInputProps
|
|
28
30
|
RadioInputOptions {}
|
29
31
|
|
30
32
|
export const RadioInput = forwardRef<RadioInputProps, 'div'>(
|
31
|
-
({ options, spacing, direction, ...props }, ref) => {
|
33
|
+
({ options: optionsProp, spacing, direction, ...props }, ref) => {
|
32
34
|
const { onBlur, onChange, ...groupProps } = props
|
33
35
|
|
36
|
+
const options = mapOptions(optionsProp)
|
37
|
+
|
34
38
|
return (
|
35
39
|
<RadioGroup onChange={onChange} {...groupProps}>
|
36
40
|
<Stack spacing={spacing} direction={direction}>
|
@@ -53,6 +57,4 @@ export const RadioInput = forwardRef<RadioInputProps, 'div'>(
|
|
53
57
|
}
|
54
58
|
)
|
55
59
|
|
56
|
-
|
57
|
-
RadioInput.displayName = 'RadioInput'
|
58
|
-
}
|
60
|
+
RadioInput.displayName = 'RadioInput'
|
@@ -5,7 +5,6 @@ import {
|
|
5
5
|
Select as ChakraSelect,
|
6
6
|
SelectProps as ChakraSelectProps,
|
7
7
|
} from '@chakra-ui/react'
|
8
|
-
import { __DEV__ } from '@chakra-ui/utils'
|
9
8
|
|
10
9
|
interface Option {
|
11
10
|
value: string
|
@@ -37,6 +36,4 @@ export const NativeSelect = forwardRef<NativeSelectProps, 'select'>(
|
|
37
36
|
}
|
38
37
|
)
|
39
38
|
|
40
|
-
|
41
|
-
NativeSelect.displayName = 'NativeSelect'
|
42
|
-
}
|
39
|
+
NativeSelect.displayName = 'NativeSelect'
|
package/src/select/select.tsx
CHANGED
@@ -18,21 +18,24 @@ import {
|
|
18
18
|
SystemStyleObject,
|
19
19
|
useFormControl,
|
20
20
|
HTMLChakraProps,
|
21
|
+
MenuItemOptionProps,
|
21
22
|
} from '@chakra-ui/react'
|
22
|
-
import {
|
23
|
-
import {
|
23
|
+
import { cx } from '@chakra-ui/utils'
|
24
|
+
import { ChevronDownIcon } from '@saas-ui/core'
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
import { FieldOptions, FieldOption } from '../types'
|
27
|
+
import { mapOptions } from '../utils'
|
28
|
+
|
29
|
+
export interface SelectOption
|
30
|
+
extends Omit<MenuItemOptionProps, 'value'>,
|
31
|
+
FieldOption {}
|
29
32
|
|
30
33
|
interface SelectOptions {
|
31
34
|
/**
|
32
35
|
* An array of options
|
33
36
|
* If you leave this empty the children prop will be rendered.
|
34
37
|
*/
|
35
|
-
options?:
|
38
|
+
options?: FieldOptions<SelectOption>
|
36
39
|
/**
|
37
40
|
* Props passed to the MenuList.
|
38
41
|
*/
|
@@ -80,14 +83,12 @@ const SelectButton = forwardRef((props, ref) => {
|
|
80
83
|
return <MenuButton as={Button} {...props} ref={ref} sx={buttonStyles} />
|
81
84
|
})
|
82
85
|
|
83
|
-
|
84
|
-
SelectButton.displayName = 'SelectButton'
|
85
|
-
}
|
86
|
+
SelectButton.displayName = 'SelectButton'
|
86
87
|
|
87
88
|
export const Select = forwardRef<SelectProps, 'select'>((props, ref) => {
|
88
89
|
const {
|
89
90
|
name,
|
90
|
-
options,
|
91
|
+
options: optionsProp,
|
91
92
|
children,
|
92
93
|
onChange,
|
93
94
|
defaultValue,
|
@@ -109,6 +110,11 @@ export const Select = forwardRef<SelectProps, 'select'>((props, ref) => {
|
|
109
110
|
|
110
111
|
const controlProps = useFormControl({ name } as HTMLChakraProps<'input'>)
|
111
112
|
|
113
|
+
const options = React.useMemo(
|
114
|
+
() => optionsProp && mapOptions(optionsProp),
|
115
|
+
[optionsProp]
|
116
|
+
)
|
117
|
+
|
112
118
|
const handleChange = (value: string | string[]) => {
|
113
119
|
setCurrentValue(value)
|
114
120
|
onChange?.(value)
|
@@ -180,6 +186,4 @@ export const Select = forwardRef<SelectProps, 'select'>((props, ref) => {
|
|
180
186
|
)
|
181
187
|
})
|
182
188
|
|
183
|
-
|
184
|
-
Select.displayName = 'Select'
|
185
|
-
}
|
189
|
+
Select.displayName = 'Select'
|
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,91 @@
|
|
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
|
+
|
7
|
+
export type FieldOption = { label: string; value: string }
|
8
|
+
export type FieldOptions<TOption extends FieldOption = FieldOption> =
|
9
|
+
| Array<string>
|
10
|
+
| Array<TOption>
|
11
|
+
|
12
|
+
export type ValueOf<T> = T[keyof T]
|
13
|
+
export type ShallowMerge<A, B> = Omit<A, keyof B> & B
|
14
|
+
|
15
|
+
export interface BaseFieldProps<
|
16
|
+
TFieldValues extends FieldValues = FieldValues,
|
17
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
18
|
+
> extends Omit<FormControlProps, 'label' | 'type'> {
|
19
|
+
/**
|
20
|
+
* The field name
|
21
|
+
*/
|
22
|
+
name: TName
|
23
|
+
/**
|
24
|
+
* The field label
|
25
|
+
*/
|
26
|
+
label?: string
|
27
|
+
/**
|
28
|
+
* Hide the field label
|
29
|
+
*/
|
30
|
+
hideLabel?: boolean
|
31
|
+
/**
|
32
|
+
* Field help text
|
33
|
+
*/
|
34
|
+
help?: string
|
35
|
+
/**
|
36
|
+
* React hook form rules
|
37
|
+
*/
|
38
|
+
rules?: Omit<
|
39
|
+
RegisterOptions<TFieldValues, TName>,
|
40
|
+
'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
|
41
|
+
>
|
42
|
+
/**
|
43
|
+
* Build-in types:
|
44
|
+
* text, number, password, textarea, select, native-select, checkbox, radio, switch, pin
|
45
|
+
*
|
46
|
+
* Will default to a text field if there is no matching type.
|
47
|
+
*/
|
48
|
+
type?: string
|
49
|
+
/**
|
50
|
+
* The input placeholder
|
51
|
+
*/
|
52
|
+
placeholder?: string
|
53
|
+
}
|
54
|
+
|
55
|
+
type MergeFieldProps<
|
56
|
+
FieldDefs,
|
57
|
+
TFieldValues extends FieldValues = FieldValues
|
58
|
+
> = ValueOf<{
|
59
|
+
[K in keyof FieldDefs]: FieldDefs[K] extends React.FC<infer Props>
|
60
|
+
? { type?: K } & ShallowMerge<Props, BaseFieldProps<TFieldValues>>
|
61
|
+
: never
|
62
|
+
}>
|
63
|
+
|
64
|
+
export type FieldProps<TFieldValues extends FieldValues = FieldValues> =
|
65
|
+
MergeFieldProps<DefaultFields, TFieldValues>
|
66
|
+
|
67
|
+
export type FormChildren<
|
68
|
+
FieldDefs,
|
69
|
+
TFieldValues extends FieldValues = FieldValues,
|
70
|
+
TContext extends object = object
|
71
|
+
> = MaybeRenderProp<
|
72
|
+
FormRenderContext<
|
73
|
+
TFieldValues,
|
74
|
+
TContext,
|
75
|
+
MergeFieldProps<
|
76
|
+
FieldDefs extends never
|
77
|
+
? DefaultFields
|
78
|
+
: ShallowMerge<DefaultFields, FieldDefs>,
|
79
|
+
TFieldValues
|
80
|
+
>
|
81
|
+
>
|
82
|
+
>
|
83
|
+
|
84
|
+
export type WithFields<
|
85
|
+
TFormProps extends FormProps<any, any, any, any>,
|
86
|
+
FieldDefs
|
87
|
+
> = TFormProps extends FormProps<infer TFieldValues, infer TContext>
|
88
|
+
? Omit<TFormProps, 'children'> & {
|
89
|
+
children: FormChildren<FieldDefs, TFieldValues, TContext>
|
90
|
+
}
|
91
|
+
: never
|
package/src/utils.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
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) => {
|
@@ -11,3 +12,17 @@ export const mapNestedFields = (name: string, children: React.ReactNode) => {
|
|
11
12
|
return child
|
12
13
|
})
|
13
14
|
}
|
15
|
+
|
16
|
+
export const mapOptions = <TOption extends FieldOption = FieldOption>(
|
17
|
+
options: FieldOptions<TOption>
|
18
|
+
) => {
|
19
|
+
return options.map((option) => {
|
20
|
+
if (typeof option === 'string') {
|
21
|
+
return {
|
22
|
+
label: option,
|
23
|
+
value: option,
|
24
|
+
}
|
25
|
+
}
|
26
|
+
return option
|
27
|
+
})
|
28
|
+
}
|
File without changes
|