@saas-ui/forms 1.0.0-rc.9 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +104 -0
- package/ajv/package.json +2 -2
- package/dist/ajv/index.js +95 -95
- package/dist/ajv/index.js.map +1 -1
- package/dist/ajv/index.modern.mjs +94 -94
- package/dist/ajv/index.modern.mjs.map +1 -1
- package/dist/field-resolver.d.ts.map +1 -1
- package/dist/field.d.ts +1 -1
- package/dist/field.d.ts.map +1 -1
- package/dist/fields.d.ts +1 -0
- package/dist/fields.d.ts.map +1 -1
- package/dist/form.d.ts +3 -3
- package/dist/form.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.modern.mjs +1 -1
- package/dist/index.modern.mjs.map +1 -1
- package/dist/step-form.d.ts +3 -2
- package/dist/step-form.d.ts.map +1 -1
- package/dist/submit-button.d.ts +2 -1
- package/dist/submit-button.d.ts.map +1 -1
- package/dist/use-step-form.d.ts +3 -0
- package/dist/use-step-form.d.ts.map +1 -1
- package/dist/zod/index.js.map +1 -1
- package/dist/zod/index.modern.mjs.map +1 -1
- package/dist/zod/zod-resolver.d.ts +2 -2
- package/dist/zod/zod-resolver.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/auto-form.tsx +2 -2
- package/src/field-resolver.ts +2 -1
- package/src/field.tsx +1 -5
- package/src/fields.tsx +36 -25
- package/src/form.tsx +2 -3
- package/src/step-form.tsx +12 -7
- package/src/submit-button.tsx +32 -24
- package/src/use-step-form.tsx +18 -5
- package/yup/package.json +2 -2
- package/zod/package.json +2 -2
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@saas-ui/forms",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.2",
|
4
4
|
"description": "Fully functional forms for Chakra UI.",
|
5
5
|
"source": "src/index.ts",
|
6
6
|
"exports": {
|
@@ -93,16 +93,16 @@
|
|
93
93
|
"@chakra-ui/react-utils": "^2.0.1",
|
94
94
|
"@chakra-ui/utils": "^2.0.2",
|
95
95
|
"@hookform/resolvers": "^2.9.0",
|
96
|
-
"@saas-ui/button": "1.0.0
|
97
|
-
"@saas-ui/input-right-button": "1.0.
|
98
|
-
"@saas-ui/number-input": "1.0.0
|
99
|
-
"@saas-ui/password-input": "1.0.
|
100
|
-
"@saas-ui/pin-input": "1.0.0
|
101
|
-
"@saas-ui/radio": "1.0.0
|
102
|
-
"@saas-ui/react-utils": "1.0.0
|
103
|
-
"@saas-ui/select": "1.0.0
|
104
|
-
"@saas-ui/stepper": "1.0.0
|
105
|
-
"react-hook-form": "^7.
|
96
|
+
"@saas-ui/button": "1.0.0",
|
97
|
+
"@saas-ui/input-right-button": "1.0.1",
|
98
|
+
"@saas-ui/number-input": "1.0.0",
|
99
|
+
"@saas-ui/password-input": "1.0.1",
|
100
|
+
"@saas-ui/pin-input": "1.0.0",
|
101
|
+
"@saas-ui/radio": "1.0.0",
|
102
|
+
"@saas-ui/react-utils": "1.0.0",
|
103
|
+
"@saas-ui/select": "1.0.0",
|
104
|
+
"@saas-ui/stepper": "1.0.0",
|
105
|
+
"react-hook-form": "^7.33.1"
|
106
106
|
},
|
107
107
|
"peerDependencies": {
|
108
108
|
"@chakra-ui/react": ">=2.1.0",
|
package/src/auto-form.tsx
CHANGED
@@ -6,7 +6,7 @@ import { __DEV__ } from '@chakra-ui/utils'
|
|
6
6
|
import { Form, FormProps } from './form'
|
7
7
|
import { FormLayout } from './layout'
|
8
8
|
import { Fields } from './fields'
|
9
|
-
import { SubmitButton
|
9
|
+
import { SubmitButton } from './submit-button'
|
10
10
|
import { FieldResolver } from '.'
|
11
11
|
|
12
12
|
interface AutoFormOptions {
|
@@ -48,7 +48,7 @@ export const AutoForm = forwardRef(
|
|
48
48
|
<Form {...rest} schema={schema} ref={ref}>
|
49
49
|
<FormLayout>
|
50
50
|
{<Fields schema={schema} fieldResolver={fieldResolver} />}
|
51
|
-
{submitLabel && <SubmitButton
|
51
|
+
{submitLabel && <SubmitButton>{submitLabel}</SubmitButton>}
|
52
52
|
{children}
|
53
53
|
</FormLayout>
|
54
54
|
</Form>
|
package/src/field-resolver.ts
CHANGED
@@ -16,10 +16,11 @@ export type ObjectSchema = Record<string, SchemaField>
|
|
16
16
|
|
17
17
|
const mapFields = (schema: ObjectSchema): FieldProps[] =>
|
18
18
|
schema &&
|
19
|
-
Object.entries(schema).map(([name, { items, ...field }]) => {
|
19
|
+
Object.entries(schema).map(([name, { items, label, title, ...field }]) => {
|
20
20
|
return {
|
21
21
|
...field,
|
22
22
|
name,
|
23
|
+
label: label || title, // json schema compatibility
|
23
24
|
}
|
24
25
|
})
|
25
26
|
|
package/src/field.tsx
CHANGED
@@ -27,14 +27,13 @@ import {
|
|
27
27
|
TextareaProps,
|
28
28
|
SwitchProps,
|
29
29
|
CheckboxProps,
|
30
|
-
PinInputProps,
|
31
30
|
PinInputField,
|
32
31
|
HStack,
|
33
32
|
PinInput,
|
34
33
|
UsePinInputProps,
|
35
34
|
SystemProps,
|
36
35
|
} from '@chakra-ui/react'
|
37
|
-
import { __DEV__ } from '@chakra-ui/utils'
|
36
|
+
import { __DEV__, FocusableElement } from '@chakra-ui/utils'
|
38
37
|
|
39
38
|
import { NumberInput, NumberInputProps } from '@saas-ui/number-input'
|
40
39
|
import { PasswordInput, PasswordInputProps } from '@saas-ui/password-input'
|
@@ -46,7 +45,6 @@ import {
|
|
46
45
|
NativeSelect,
|
47
46
|
NativeSelectProps,
|
48
47
|
} from '@saas-ui/select'
|
49
|
-
import { FocusableElement } from '@chakra-ui/utils'
|
50
48
|
|
51
49
|
export interface Option {
|
52
50
|
value: string
|
@@ -217,7 +215,6 @@ const createField = (
|
|
217
215
|
isInvalid,
|
218
216
|
isReadOnly,
|
219
217
|
isRequired,
|
220
|
-
isOptional,
|
221
218
|
rules,
|
222
219
|
variant,
|
223
220
|
...inputProps
|
@@ -239,7 +236,6 @@ const createField = (
|
|
239
236
|
isInvalid={isInvalid}
|
240
237
|
isReadOnly={isReadOnly}
|
241
238
|
isRequired={isRequired}
|
242
|
-
isOptional={isOptional}
|
243
239
|
variant={variant}
|
244
240
|
>
|
245
241
|
<InputComponent
|
package/src/fields.tsx
CHANGED
@@ -8,10 +8,12 @@ import { Field, FieldProps } from './field'
|
|
8
8
|
import { ArrayField } from './array-field'
|
9
9
|
import { ObjectField } from './object-field'
|
10
10
|
import { FieldResolver } from './field-resolver'
|
11
|
+
import { useFormContext } from 'react-hook-form'
|
11
12
|
|
12
13
|
export interface FieldsProps {
|
13
14
|
schema: any
|
14
15
|
fieldResolver?: FieldResolver
|
16
|
+
focusFirstField?: boolean
|
15
17
|
}
|
16
18
|
|
17
19
|
const mapNestedFields = (resolver: FieldResolver, name: string) => {
|
@@ -27,6 +29,7 @@ const mapNestedFields = (resolver: FieldResolver, name: string) => {
|
|
27
29
|
export const Fields: React.FC<FieldsProps> = ({
|
28
30
|
schema,
|
29
31
|
fieldResolver,
|
32
|
+
focusFirstField,
|
30
33
|
...props
|
31
34
|
}) => {
|
32
35
|
const resolver = React.useMemo(
|
@@ -34,34 +37,42 @@ export const Fields: React.FC<FieldsProps> = ({
|
|
34
37
|
[schema, fieldResolver]
|
35
38
|
)
|
36
39
|
|
40
|
+
const fields = React.useMemo(() => resolver.getFields(), [resolver])
|
41
|
+
|
42
|
+
const form = useFormContext()
|
43
|
+
|
44
|
+
React.useEffect(() => {
|
45
|
+
if (focusFirstField && fields[0]?.name) {
|
46
|
+
form.setFocus(fields[0].name)
|
47
|
+
}
|
48
|
+
}, [schema, fieldResolver, focusFirstField])
|
49
|
+
|
37
50
|
return (
|
38
51
|
<FormLayout {...props}>
|
39
|
-
{
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
</ObjectField>
|
59
|
-
)
|
60
|
-
}
|
61
|
-
|
62
|
-
return <Field key={name} name={name} type={type} {...fieldProps} />
|
52
|
+
{fields.map(
|
53
|
+
({
|
54
|
+
name,
|
55
|
+
type,
|
56
|
+
defaultValue,
|
57
|
+
...fieldProps
|
58
|
+
}: FieldProps): React.ReactNode => {
|
59
|
+
if (type === 'array') {
|
60
|
+
return (
|
61
|
+
<ArrayField key={name} name={name} {...fieldProps}>
|
62
|
+
{mapNestedFields(resolver, name)}
|
63
|
+
</ArrayField>
|
64
|
+
)
|
65
|
+
} else if (type === 'object') {
|
66
|
+
return (
|
67
|
+
<ObjectField key={name} name={name} {...fieldProps}>
|
68
|
+
{mapNestedFields(resolver, name)}
|
69
|
+
</ObjectField>
|
70
|
+
)
|
63
71
|
}
|
64
|
-
|
72
|
+
|
73
|
+
return <Field key={name} name={name} type={type} {...fieldProps} />
|
74
|
+
}
|
75
|
+
)}
|
65
76
|
</FormLayout>
|
66
77
|
)
|
67
78
|
}
|
package/src/form.tsx
CHANGED
@@ -11,7 +11,6 @@ import {
|
|
11
11
|
FieldValues,
|
12
12
|
SubmitHandler,
|
13
13
|
SubmitErrorHandler,
|
14
|
-
UnpackNestedValue,
|
15
14
|
ResolverOptions,
|
16
15
|
ResolverResult,
|
17
16
|
} from 'react-hook-form'
|
@@ -35,7 +34,7 @@ interface FormOptions<TFieldValues extends FieldValues = FieldValues> {
|
|
35
34
|
/**
|
36
35
|
* Ref on the HTMLFormElement.
|
37
36
|
*/
|
38
|
-
formRef?: React.
|
37
|
+
formRef?: React.RefObject<HTMLFormElement>
|
39
38
|
}
|
40
39
|
|
41
40
|
/**
|
@@ -124,7 +123,7 @@ if (__DEV__) {
|
|
124
123
|
export type GetResolver = (
|
125
124
|
schema: any
|
126
125
|
) => <TFieldValues extends FieldValues, TContext>(
|
127
|
-
values:
|
126
|
+
values: TFieldValues,
|
128
127
|
context: TContext | undefined,
|
129
128
|
options: ResolverOptions<TFieldValues>
|
130
129
|
) => Promise<ResolverResult<TFieldValues>>
|
package/src/step-form.tsx
CHANGED
@@ -16,7 +16,7 @@ import {
|
|
16
16
|
} from '@saas-ui/stepper'
|
17
17
|
import { Button, ButtonProps } from '@saas-ui/button'
|
18
18
|
|
19
|
-
import { Form
|
19
|
+
import { Form } from './form'
|
20
20
|
import { SubmitButton } from './submit-button'
|
21
21
|
|
22
22
|
import {
|
@@ -24,6 +24,7 @@ import {
|
|
24
24
|
useFormStep,
|
25
25
|
StepFormProvider,
|
26
26
|
UseStepFormProps,
|
27
|
+
FormStepSubmitHandler,
|
27
28
|
} from './use-step-form'
|
28
29
|
|
29
30
|
export interface StepFormProps<TFieldValues extends FieldValues = FieldValues>
|
@@ -113,11 +114,14 @@ export const FormStepper: React.FC<StepperStepsProps> = (props) => {
|
|
113
114
|
|
114
115
|
export interface FormStepProps
|
115
116
|
extends FormStepOptions,
|
116
|
-
HTMLChakraProps<'div'> {
|
117
|
+
Omit<HTMLChakraProps<'div'>, 'onSubmit'> {
|
118
|
+
onSubmit?: FormStepSubmitHandler
|
119
|
+
}
|
117
120
|
|
118
121
|
export const FormStep: React.FC<FormStepProps> = (props) => {
|
119
|
-
const { name, schema, resolver, children, className, ...rest } =
|
120
|
-
|
122
|
+
const { name, schema, resolver, children, className, onSubmit, ...rest } =
|
123
|
+
props
|
124
|
+
const step = useFormStep({ name, schema, resolver, onSubmit })
|
121
125
|
|
122
126
|
const { isActive } = step
|
123
127
|
|
@@ -160,11 +164,12 @@ export const NextButton: React.FC<NextButtonProps> = (props) => {
|
|
160
164
|
|
161
165
|
return (
|
162
166
|
<SubmitButton
|
163
|
-
isDisabled={isCompleted}
|
164
|
-
label={isLastStep || isCompleted ? submitLabel : label}
|
165
167
|
{...rest}
|
168
|
+
isDisabled={isCompleted}
|
166
169
|
className={cx('saas-form__next-button', props.className)}
|
167
|
-
|
170
|
+
>
|
171
|
+
{isLastStep || isCompleted ? submitLabel : label}
|
172
|
+
</SubmitButton>
|
168
173
|
)
|
169
174
|
}
|
170
175
|
|
package/src/submit-button.tsx
CHANGED
@@ -4,7 +4,6 @@ import { useFormContext } from 'react-hook-form'
|
|
4
4
|
|
5
5
|
import { Button, ButtonProps } from '@saas-ui/button'
|
6
6
|
|
7
|
-
import { forwardRef } from '@chakra-ui/system'
|
8
7
|
import { __DEV__ } from '@chakra-ui/utils'
|
9
8
|
|
10
9
|
export interface SubmitButtonProps extends ButtonProps {
|
@@ -24,29 +23,38 @@ export interface SubmitButtonProps extends ButtonProps {
|
|
24
23
|
disableIfInvalid?: boolean
|
25
24
|
}
|
26
25
|
|
27
|
-
export const SubmitButton = forwardRef<
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
26
|
+
export const SubmitButton = React.forwardRef<
|
27
|
+
HTMLButtonElement,
|
28
|
+
SubmitButtonProps
|
29
|
+
>((props, ref) => {
|
30
|
+
const {
|
31
|
+
children,
|
32
|
+
disableIfUntouched,
|
33
|
+
disableIfInvalid,
|
34
|
+
isDisabled: isDisabledProp,
|
35
|
+
isLoading,
|
36
|
+
...rest
|
37
|
+
} = props
|
38
|
+
const { formState } = useFormContext()
|
39
|
+
|
40
|
+
const isDisabled =
|
41
|
+
(disableIfUntouched && !formState.isDirty) ||
|
42
|
+
(disableIfInvalid && !formState.isValid) ||
|
43
|
+
isDisabledProp
|
44
|
+
|
45
|
+
return (
|
46
|
+
<Button
|
47
|
+
{...rest}
|
48
|
+
ref={ref}
|
49
|
+
variant="primary"
|
50
|
+
type="submit"
|
51
|
+
isLoading={formState.isSubmitting || isLoading}
|
52
|
+
isDisabled={isDisabled}
|
53
|
+
>
|
54
|
+
{children}
|
55
|
+
</Button>
|
56
|
+
)
|
57
|
+
})
|
50
58
|
|
51
59
|
SubmitButton.defaultProps = {
|
52
60
|
label: 'Submit',
|
package/src/use-step-form.tsx
CHANGED
@@ -14,8 +14,13 @@ export interface StepState {
|
|
14
14
|
resolver?: any
|
15
15
|
isActive?: boolean
|
16
16
|
isCompleted?: boolean
|
17
|
+
onSubmit?: FormStepSubmitHandler
|
17
18
|
}
|
18
19
|
|
20
|
+
export type FormStepSubmitHandler<
|
21
|
+
TFieldValues extends FieldValues = FieldValues
|
22
|
+
> = (data: TFieldValues, stepper: UseStepperReturn) => Promise<void>
|
23
|
+
|
19
24
|
export interface StepFormContext extends UseStepperReturn {
|
20
25
|
updateStep(state: StepState): void
|
21
26
|
steps: Record<string, StepState>
|
@@ -61,11 +66,12 @@ export function useStepForm<TFieldValues extends FieldValues = FieldValues>(
|
|
61
66
|
|
62
67
|
const onSubmitStep: SubmitHandler<TFieldValues> = React.useCallback(
|
63
68
|
async (data) => {
|
69
|
+
const step = steps[activeStep]
|
70
|
+
|
64
71
|
if (isLastStep) {
|
65
72
|
return props
|
66
73
|
.onSubmit?.(data)
|
67
74
|
.then(() => {
|
68
|
-
const step = steps[activeStep]
|
69
75
|
updateStep({
|
70
76
|
...step,
|
71
77
|
isCompleted: true,
|
@@ -74,9 +80,15 @@ export function useStepForm<TFieldValues extends FieldValues = FieldValues>(
|
|
74
80
|
.then(nextStep) // Show completed step
|
75
81
|
}
|
76
82
|
|
77
|
-
|
83
|
+
try {
|
84
|
+
await step.onSubmit?.(data, stepper)
|
85
|
+
|
86
|
+
nextStep()
|
87
|
+
} catch (e) {
|
88
|
+
// Step submission failed.
|
89
|
+
}
|
78
90
|
},
|
79
|
-
[activeStep, isLastStep]
|
91
|
+
[steps, activeStep, isLastStep]
|
80
92
|
)
|
81
93
|
|
82
94
|
const getFormProps = React.useCallback(() => {
|
@@ -112,16 +124,17 @@ export interface UseFormStepProps {
|
|
112
124
|
name: string
|
113
125
|
schema?: any
|
114
126
|
resolver?: any
|
127
|
+
onSubmit?: FormStepSubmitHandler
|
115
128
|
}
|
116
129
|
|
117
130
|
export function useFormStep(props: UseFormStepProps): StepState {
|
118
|
-
const { name, schema, resolver } = props
|
131
|
+
const { name, schema, resolver, onSubmit } = props
|
119
132
|
const step = useStep({ name })
|
120
133
|
|
121
134
|
const { steps, updateStep } = useStepFormContext()
|
122
135
|
|
123
136
|
React.useEffect(() => {
|
124
|
-
updateStep({ name, schema, resolver })
|
137
|
+
updateStep({ name, schema, resolver, onSubmit })
|
125
138
|
}, [name, schema])
|
126
139
|
|
127
140
|
return {
|
package/yup/package.json
CHANGED
@@ -10,8 +10,8 @@
|
|
10
10
|
"author": "Eelco Wiersma <eelco@appulse.nl>",
|
11
11
|
"license": "MIT",
|
12
12
|
"peerDependencies": {
|
13
|
-
"@hookform/resolvers": "^2.9.
|
14
|
-
"react-hook-form": "^7.
|
13
|
+
"@hookform/resolvers": "^2.9.3",
|
14
|
+
"react-hook-form": "^7.33.1",
|
15
15
|
"yup": "^0.32.11"
|
16
16
|
}
|
17
17
|
}
|
package/zod/package.json
CHANGED