@saas-ui/forms 2.0.0-next.2 → 2.0.0-next.20
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 +188 -0
- package/README.md +53 -6
- package/dist/ajv/index.d.ts +24 -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 +519 -280
- package/dist/index.js +777 -696
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +756 -676
- package/dist/index.mjs.map +1 -1
- package/dist/yup/index.d.ts +525 -21
- package/dist/yup/index.js +21 -9
- package/dist/yup/index.js.map +1 -1
- package/dist/yup/index.mjs +21 -10
- package/dist/yup/index.mjs.map +1 -1
- package/dist/zod/index.d.ts +525 -12
- package/dist/zod/index.js +21 -1
- package/dist/zod/index.js.map +1 -1
- package/dist/zod/index.mjs +21 -3
- package/dist/zod/index.mjs.map +1 -1
- package/package.json +33 -10
- package/src/array-field.tsx +88 -48
- 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 +68 -0
- package/src/create-step-form.tsx +100 -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 +77 -55
- package/src/index.ts +58 -4
- 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 +35 -13
- 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 +116 -85
- package/src/select/select.test.tsx +1 -1
- package/src/select/select.tsx +162 -146
- package/src/step-form.tsx +76 -76
- package/src/submit-button.tsx +5 -1
- package/src/types.ts +149 -0
- package/src/use-array-field.tsx +9 -3
- package/src/use-step-form.tsx +54 -9
- 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/types.ts
ADDED
@@ -0,0 +1,149 @@
|
|
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
|
+
ExtraOverrides = object
|
138
|
+
> = TFormProps extends FormProps<
|
139
|
+
infer TSchema,
|
140
|
+
infer TFieldValues,
|
141
|
+
infer TContext
|
142
|
+
>
|
143
|
+
? Omit<TFormProps, 'children' | 'fields'> & {
|
144
|
+
children?: FormChildren<FieldDefs, TFieldValues, TContext>
|
145
|
+
fields?: FieldOverrides<FieldDefs, TFieldValues> & {
|
146
|
+
submit?: SubmitButtonProps
|
147
|
+
} & ExtraOverrides
|
148
|
+
}
|
149
|
+
: 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/use-step-form.tsx
CHANGED
@@ -34,12 +34,43 @@ export const [StepFormProvider, useStepFormContext] =
|
|
34
34
|
})
|
35
35
|
|
36
36
|
import { FormProps } from './form'
|
37
|
+
import { FormStepProps, StepsOptions } from './step-form'
|
38
|
+
import { FieldProps } from './types'
|
39
|
+
import { FocusableElement } from '@chakra-ui/utils'
|
40
|
+
import { DisplayIfProps } from './display-if'
|
41
|
+
import { ArrayFieldProps } from './array-field'
|
42
|
+
import { UseArrayFieldReturn } from './use-array-field'
|
43
|
+
import { ObjectFieldProps } from './object-field'
|
44
|
+
|
45
|
+
type StepName<T extends { [k: number]: { readonly name: string } }> =
|
46
|
+
T[number]['name']
|
47
|
+
|
48
|
+
interface StepFormRenderContext<
|
49
|
+
TSteps extends StepsOptions<any> = StepsOptions<any>,
|
50
|
+
TFieldValues extends FieldValues = FieldValues,
|
51
|
+
TContext extends object = object,
|
52
|
+
TFieldTypes = FieldProps<TFieldValues>
|
53
|
+
> extends UseStepFormReturn<TFieldValues> {
|
54
|
+
Field: React.FC<TFieldTypes & React.RefAttributes<FocusableElement>>
|
55
|
+
FormStep: React.FC<FormStepProps<StepName<TSteps>>>
|
56
|
+
DisplayIf: React.FC<DisplayIfProps<TFieldValues>>
|
57
|
+
ArrayField: React.FC<
|
58
|
+
ArrayFieldProps<TFieldValues> & React.RefAttributes<UseArrayFieldReturn>
|
59
|
+
>
|
60
|
+
ObjectField: React.FC<ObjectFieldProps<TFieldValues>>
|
61
|
+
}
|
37
62
|
|
38
63
|
export interface UseStepFormProps<
|
39
|
-
|
64
|
+
TSteps extends StepsOptions<any> = StepsOptions<any>,
|
65
|
+
TFieldValues extends FieldValues = FieldValues,
|
66
|
+
TContext extends object = object,
|
67
|
+
TFieldTypes = FieldProps<TFieldValues>
|
40
68
|
> extends Omit<UseStepperProps, 'onChange'>,
|
41
|
-
Omit<FormProps<TFieldValues>, 'children'> {
|
42
|
-
|
69
|
+
Omit<FormProps<any, TFieldValues, TContext, TFieldTypes>, 'children'> {
|
70
|
+
steps?: TSteps
|
71
|
+
children: MaybeRenderProp<
|
72
|
+
StepFormRenderContext<TSteps, TFieldValues, TContext, TFieldTypes>
|
73
|
+
>
|
43
74
|
}
|
44
75
|
|
45
76
|
export interface UseStepFormReturn<
|
@@ -54,12 +85,19 @@ export interface UseStepFormReturn<
|
|
54
85
|
steps: Record<string, any>
|
55
86
|
}
|
56
87
|
|
57
|
-
export function useStepForm<
|
58
|
-
|
88
|
+
export function useStepForm<
|
89
|
+
TSteps extends StepsOptions<any> = StepsOptions<any>,
|
90
|
+
TFieldValues extends FieldValues = FieldValues,
|
91
|
+
TContext extends object = object,
|
92
|
+
TFieldTypes = FieldProps<TFieldValues>
|
93
|
+
>(
|
94
|
+
props: UseStepFormProps<TSteps, TFieldValues, TContext, TFieldTypes>
|
59
95
|
): UseStepFormReturn<TFieldValues> {
|
60
|
-
const { onChange, ...rest } = props
|
96
|
+
const { onChange, steps: stepsOptions, resolver, ...rest } = props
|
61
97
|
const stepper = useStepper(rest)
|
62
98
|
|
99
|
+
const [options, setOptions] = React.useState<TSteps | undefined>(stepsOptions)
|
100
|
+
|
63
101
|
const { activeStep, isLastStep, nextStep } = stepper
|
64
102
|
|
65
103
|
const [steps, updateSteps] = React.useState<Record<string, StepState>>({})
|
@@ -93,23 +131,30 @@ export function useStepForm<TFieldValues extends FieldValues = FieldValues>(
|
|
93
131
|
|
94
132
|
const getFormProps = React.useCallback(() => {
|
95
133
|
const step = steps[activeStep]
|
134
|
+
|
96
135
|
return {
|
97
136
|
onSubmit: onSubmitStep,
|
98
137
|
schema: step?.schema,
|
99
|
-
resolver: step?.
|
138
|
+
resolver: step?.schema
|
139
|
+
? /* @todo fix resolver type */ (resolver as any)?.(step.schema)
|
140
|
+
: undefined,
|
100
141
|
}
|
101
142
|
}, [steps, onSubmitStep, activeStep])
|
102
143
|
|
103
144
|
const updateStep = React.useCallback(
|
104
145
|
(step: StepState) => {
|
146
|
+
const stepOptions = options?.find((s) => s.name === step.name)
|
105
147
|
updateSteps((steps) => {
|
106
148
|
return {
|
107
149
|
...steps,
|
108
|
-
[step.name]:
|
150
|
+
[step.name]: {
|
151
|
+
...step,
|
152
|
+
schema: stepOptions?.schema,
|
153
|
+
},
|
109
154
|
}
|
110
155
|
})
|
111
156
|
},
|
112
|
-
[steps]
|
157
|
+
[steps, options]
|
113
158
|
)
|
114
159
|
|
115
160
|
return {
|
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
|