@saas-ui/forms 1.0.0-rc.9 → 1.0.2
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 +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