@saas-ui/forms 1.0.0-rc.0 → 1.0.0-rc.11
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +130 -0
- package/README.md +29 -0
- package/ajv/package.json +18 -0
- package/dist/ajv/ajv-resolver.d.ts +11 -0
- package/dist/ajv/ajv-resolver.d.ts.map +1 -0
- package/dist/ajv/index.d.ts +2 -0
- package/dist/ajv/index.d.ts.map +1 -0
- package/dist/ajv/index.js +104 -0
- package/dist/ajv/index.js.map +1 -0
- package/dist/ajv/index.modern.mjs +104 -0
- package/dist/ajv/index.modern.mjs.map +1 -0
- package/dist/array-field.d.ts +14 -3
- package/dist/array-field.d.ts.map +1 -1
- package/dist/auto-form.d.ts +13 -1
- package/dist/auto-form.d.ts.map +1 -1
- package/dist/field.d.ts +82 -27
- package/dist/field.d.ts.map +1 -1
- 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 +4 -4
- 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 +7 -3
- package/dist/use-step-form.d.ts.map +1 -1
- package/dist/yup/index.js +1 -1
- package/dist/yup/index.js.map +1 -1
- package/dist/yup/index.modern.mjs +1 -1
- package/dist/yup/index.modern.mjs.map +1 -1
- package/dist/zod/index.js +1 -1
- package/dist/zod/index.js.map +1 -1
- package/dist/zod/index.modern.mjs +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 +32 -17
- package/src/array-field.tsx +21 -22
- package/src/auto-form.tsx +22 -3
- package/src/field-resolver.ts +2 -2
- package/src/field.tsx +184 -73
- package/src/form.tsx +0 -1
- package/src/object-field.tsx +3 -3
- package/src/step-form.tsx +27 -20
- package/src/submit-button.tsx +32 -24
- package/src/use-step-form.tsx +27 -12
- package/yup/package.json +2 -2
- package/zod/package.json +4 -4
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"zod-resolver.d.ts","sourceRoot":"","sources":["../../zod/src/zod-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAErD,OAAO,EAAE,UAAU,EAAE,
|
1
|
+
{"version":3,"file":"zod-resolver.d.ts","sourceRoot":"","sources":["../../zod/src/zod-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EAAE,WAAW,EAAE,CAAA;AAEtB,oBAAY,OAAO,GAAG;IACpB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAsBD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,WAAY,EAAE,UAAU,KAAG,UAAU,EA+BpE,CAAA;AAED,eAAO,MAAM,eAAe,WAAY,EAAE,UAAU,QAAQ,MAAM,QAEjE,CAAA;AAED,eAAO,MAAM,gBAAgB;;0BAKH,MAAM;CAI/B,CAAA;AAED,eAAO,MAAM,OAAO;;;;;8BANM,MAAM;;CAgB/B,CAAA;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,eAAO,MAAM,OAAO,SAAU,OAAO,WAEpC,CAAA;AAED,eAAO,MAAM,YAAY,SAAU,MAAM,QAMxC,CAAA"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@saas-ui/forms",
|
3
|
-
"version": "1.0.0-rc.
|
3
|
+
"version": "1.0.0-rc.11",
|
4
4
|
"description": "Fully functional forms for Chakra UI.",
|
5
5
|
"source": "src/index.ts",
|
6
6
|
"exports": {
|
@@ -24,6 +24,13 @@
|
|
24
24
|
},
|
25
25
|
"./zod/src": {
|
26
26
|
"default": "./zod/src/index.ts"
|
27
|
+
},
|
28
|
+
"./ajv": {
|
29
|
+
"require": "./dist/ajv/index.js",
|
30
|
+
"default": "./dist/ajv/index.modern.mjs"
|
31
|
+
},
|
32
|
+
"./ajv/src": {
|
33
|
+
"default": "./ajv/src/index.ts"
|
27
34
|
}
|
28
35
|
},
|
29
36
|
"main": "./dist/index.js",
|
@@ -31,10 +38,11 @@
|
|
31
38
|
"types": "./dist/index.d.ts",
|
32
39
|
"scripts": {
|
33
40
|
"clean": "rimraf --no-glob ./dist",
|
34
|
-
"build": "yarn build:package && yarn build:yup && yarn build:zod",
|
41
|
+
"build": "yarn build:package && yarn build:yup && yarn build:zod && yarn build:ajv",
|
35
42
|
"build:package": "yarn clean && cross-env NODE_ENV=production microbundle --tsconfig ./tsconfig.json --jsx React.createElement --jsxFragment React.Fragment -f cjs,modern --compress",
|
36
43
|
"build:yup": "cross-env NODE_ENV=production microbundle --cwd yup --tsconfig ./yup/tsconfig.json -f cjs,modern --compress",
|
37
44
|
"build:zod": "cross-env NODE_ENV=production microbundle --cwd zod --tsconfig ./zod/tsconfig.json -f cjs,modern --compress",
|
45
|
+
"build:ajv": "cross-env NODE_ENV=production microbundle --cwd ajv --tsconfig ./ajv/tsconfig.json -f cjs,modern --compress",
|
38
46
|
"lint": "eslint src --ext .ts,.tsx,.js,.jsx --config ../../.eslintrc.js",
|
39
47
|
"lint:staged": "lint-staged --allow-empty --config ../../lint-staged.config.js",
|
40
48
|
"typecheck": "tsc --noEmit"
|
@@ -45,7 +53,9 @@
|
|
45
53
|
"yup/package.json",
|
46
54
|
"yup/src",
|
47
55
|
"zod/package.json",
|
48
|
-
"zod/src"
|
56
|
+
"zod/src",
|
57
|
+
"ajv/package.json",
|
58
|
+
"ajv/src"
|
49
59
|
],
|
50
60
|
"sideEffects": false,
|
51
61
|
"publishConfig": {
|
@@ -79,24 +89,29 @@
|
|
79
89
|
"url": "https://storybook.saas-ui.dev"
|
80
90
|
},
|
81
91
|
"dependencies": {
|
82
|
-
"@chakra-ui/icons": "^2.0.
|
83
|
-
"@chakra-ui/react-utils": "^2.0.
|
84
|
-
"@chakra-ui/utils": "^2.0.
|
85
|
-
"@hookform/resolvers": "^2.
|
86
|
-
"@saas-ui/button": "1.0.0-rc.
|
87
|
-
"@saas-ui/input-right-button": "1.0.0-rc.
|
88
|
-
"@saas-ui/number-input": "1.0.0-rc.
|
89
|
-
"@saas-ui/password-input": "1.0.0-rc.
|
90
|
-
"@saas-ui/pin-input": "1.0.0-rc.
|
91
|
-
"@saas-ui/radio": "1.0.0-rc.
|
92
|
-
"@saas-ui/react-utils": "1.0.0-rc.
|
93
|
-
"@saas-ui/select": "1.0.0-rc.
|
94
|
-
"@saas-ui/stepper": "1.0.0-rc.
|
95
|
-
"react-hook-form": "^7.31.
|
92
|
+
"@chakra-ui/icons": "^2.0.2",
|
93
|
+
"@chakra-ui/react-utils": "^2.0.1",
|
94
|
+
"@chakra-ui/utils": "^2.0.2",
|
95
|
+
"@hookform/resolvers": "^2.9.0",
|
96
|
+
"@saas-ui/button": "1.0.0-rc.5",
|
97
|
+
"@saas-ui/input-right-button": "1.0.0-rc.5",
|
98
|
+
"@saas-ui/number-input": "1.0.0-rc.4",
|
99
|
+
"@saas-ui/password-input": "1.0.0-rc.5",
|
100
|
+
"@saas-ui/pin-input": "1.0.0-rc.5",
|
101
|
+
"@saas-ui/radio": "1.0.0-rc.4",
|
102
|
+
"@saas-ui/react-utils": "1.0.0-rc.4",
|
103
|
+
"@saas-ui/select": "1.0.0-rc.4",
|
104
|
+
"@saas-ui/stepper": "1.0.0-rc.5",
|
105
|
+
"react-hook-form": "^7.31.3"
|
96
106
|
},
|
97
107
|
"peerDependencies": {
|
98
108
|
"@chakra-ui/react": ">=2.1.0",
|
99
109
|
"@chakra-ui/system": ">=2.1.0",
|
100
110
|
"react": ">=18.0.0"
|
111
|
+
},
|
112
|
+
"devDependencies": {
|
113
|
+
"ajv": "^8.11.0",
|
114
|
+
"yup": "^0.32.11",
|
115
|
+
"zod": "^3.17.3"
|
101
116
|
}
|
102
117
|
}
|
package/src/array-field.tsx
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
import * as React from 'react'
|
2
2
|
|
3
|
-
import { chakra, ResponsiveValue } from '@chakra-ui/system'
|
3
|
+
import { chakra, ResponsiveValue, forwardRef } from '@chakra-ui/system'
|
4
4
|
import { __DEV__ } from '@chakra-ui/utils'
|
5
5
|
import { AddIcon, MinusIcon } from '@chakra-ui/icons'
|
6
6
|
import { IconButton, ButtonProps } from '@saas-ui/button'
|
7
7
|
|
8
|
-
import { FormLayout } from './layout'
|
8
|
+
import { FormLayout, FormLayoutProps } from './layout'
|
9
9
|
import { BaseField, FieldProps } from './field'
|
10
10
|
|
11
11
|
import { mapNestedFields } from './utils'
|
@@ -28,7 +28,7 @@ interface ArrayField {
|
|
28
28
|
[key: string]: unknown
|
29
29
|
}
|
30
30
|
|
31
|
-
interface ArrayFieldRowProps {
|
31
|
+
interface ArrayFieldRowProps extends FormLayoutProps {
|
32
32
|
/**
|
33
33
|
* Amount of field columns
|
34
34
|
*/
|
@@ -41,21 +41,20 @@ interface ArrayFieldRowProps {
|
|
41
41
|
* The array index
|
42
42
|
*/
|
43
43
|
index: number
|
44
|
-
|
44
|
+
/**
|
45
|
+
* The fields
|
46
|
+
*/
|
45
47
|
children: React.ReactNode
|
46
48
|
}
|
47
49
|
|
48
50
|
export const ArrayFieldRow: React.FC<ArrayFieldRowProps> = ({
|
49
51
|
children,
|
50
|
-
columns,
|
51
|
-
spacing,
|
52
52
|
index,
|
53
|
+
...rowFieldsProps
|
53
54
|
}) => {
|
54
55
|
return (
|
55
56
|
<ArrayFieldRowContainer index={index}>
|
56
|
-
<ArrayFieldRowFields
|
57
|
-
{children}
|
58
|
-
</ArrayFieldRowFields>
|
57
|
+
<ArrayFieldRowFields {...rowFieldsProps}>{children}</ArrayFieldRowFields>
|
59
58
|
<ArrayFieldRemoveButton />
|
60
59
|
</ArrayFieldRowContainer>
|
61
60
|
)
|
@@ -65,7 +64,7 @@ if (__DEV__) {
|
|
65
64
|
ArrayFieldRow.displayName = 'ArrayFieldRow'
|
66
65
|
}
|
67
66
|
|
68
|
-
export interface ArrayFieldRowFieldsProps {
|
67
|
+
export interface ArrayFieldRowFieldsProps extends FormLayoutProps {
|
69
68
|
/**
|
70
69
|
* Amount of field columns
|
71
70
|
*/
|
@@ -74,25 +73,19 @@ export interface ArrayFieldRowFieldsProps {
|
|
74
73
|
* Spacing between fields
|
75
74
|
*/
|
76
75
|
spacing?: ResponsiveValue<string | number>
|
77
|
-
|
76
|
+
/**
|
77
|
+
* The fields
|
78
|
+
*/
|
78
79
|
children: React.ReactNode
|
79
80
|
}
|
80
81
|
|
81
82
|
export const ArrayFieldRowFields: React.FC<ArrayFieldRowFieldsProps> = ({
|
82
83
|
children,
|
83
|
-
columns,
|
84
|
-
spacing,
|
85
84
|
...layoutProps
|
86
85
|
}) => {
|
87
86
|
const { name } = useArrayFieldRowContext()
|
88
87
|
return (
|
89
|
-
<FormLayout
|
90
|
-
flex="1"
|
91
|
-
columns={columns}
|
92
|
-
gridGap={spacing}
|
93
|
-
mr="2"
|
94
|
-
{...layoutProps}
|
95
|
-
>
|
88
|
+
<FormLayout flex="1" mr="2" {...layoutProps}>
|
96
89
|
{mapNestedFields(name, children)}
|
97
90
|
</FormLayout>
|
98
91
|
)
|
@@ -162,7 +155,7 @@ export interface ArrayFieldProps
|
|
162
155
|
extends ArrayFieldOptions,
|
163
156
|
Omit<FieldProps, 'defaultValue'> {}
|
164
157
|
|
165
|
-
export const ArrayField =
|
158
|
+
export const ArrayField = forwardRef(
|
166
159
|
(props: ArrayFieldProps, ref: React.ForwardedRef<UseArrayFieldReturn>) => {
|
167
160
|
const { children, ...containerProps } = props
|
168
161
|
|
@@ -183,7 +176,13 @@ export const ArrayField = React.forwardRef(
|
|
183
176
|
</ArrayFieldContainer>
|
184
177
|
)
|
185
178
|
}
|
186
|
-
)
|
179
|
+
) as ((
|
180
|
+
props: ArrayFieldProps & {
|
181
|
+
ref?: React.ForwardedRef<UseArrayFieldReturn>
|
182
|
+
}
|
183
|
+
) => React.ReactElement) & {
|
184
|
+
displayName: string
|
185
|
+
}
|
187
186
|
|
188
187
|
if (__DEV__) {
|
189
188
|
ArrayField.displayName = 'ArrayField'
|
package/src/auto-form.tsx
CHANGED
@@ -10,8 +10,20 @@ import { SubmitButton } from './submit-button'
|
|
10
10
|
import { FieldResolver } from '.'
|
11
11
|
|
12
12
|
interface AutoFormOptions {
|
13
|
-
|
13
|
+
/**
|
14
|
+
* The submit button label.
|
15
|
+
* Pass `null` to render no submit button.
|
16
|
+
*/
|
17
|
+
submitLabel?: React.ReactNode
|
18
|
+
/**
|
19
|
+
* The schema.
|
20
|
+
* Supports object schema, Yup or Zod.
|
21
|
+
* @see https://www.saas-ui.dev/docs/forms/auto-form
|
22
|
+
*/
|
14
23
|
schema: any
|
24
|
+
/**
|
25
|
+
* The field resolver.
|
26
|
+
*/
|
15
27
|
fieldResolver?: any
|
16
28
|
}
|
17
29
|
|
@@ -24,13 +36,20 @@ export const AutoForm = forwardRef(
|
|
24
36
|
props: AutoFormProps<TFieldValues>,
|
25
37
|
ref: React.ForwardedRef<UseFormReturn<TFieldValues>>
|
26
38
|
) => {
|
27
|
-
const {
|
39
|
+
const {
|
40
|
+
schema,
|
41
|
+
submitLabel = 'Submit',
|
42
|
+
fieldResolver,
|
43
|
+
children,
|
44
|
+
...rest
|
45
|
+
} = props
|
28
46
|
|
29
47
|
return (
|
30
48
|
<Form {...rest} schema={schema} ref={ref}>
|
31
49
|
<FormLayout>
|
32
50
|
{<Fields schema={schema} fieldResolver={fieldResolver} />}
|
33
|
-
{submitLabel && <SubmitButton
|
51
|
+
{submitLabel && <SubmitButton>{submitLabel}</SubmitButton>}
|
52
|
+
{children}
|
34
53
|
</FormLayout>
|
35
54
|
</Form>
|
36
55
|
)
|
package/src/field-resolver.ts
CHANGED
@@ -14,9 +14,9 @@ interface SchemaField extends FieldProps {
|
|
14
14
|
|
15
15
|
export type ObjectSchema = Record<string, SchemaField>
|
16
16
|
|
17
|
-
const mapFields = (schema: ObjectSchema) =>
|
17
|
+
const mapFields = (schema: ObjectSchema): FieldProps[] =>
|
18
18
|
schema &&
|
19
|
-
Object.entries(schema).map(([name, field]) => {
|
19
|
+
Object.entries(schema).map(([name, { items, ...field }]) => {
|
20
20
|
return {
|
21
21
|
...field,
|
22
22
|
name,
|
package/src/field.tsx
CHANGED
@@ -22,15 +22,29 @@ import {
|
|
22
22
|
Checkbox,
|
23
23
|
Switch,
|
24
24
|
useMergeRefs,
|
25
|
+
InputGroup,
|
26
|
+
InputProps,
|
27
|
+
TextareaProps,
|
28
|
+
SwitchProps,
|
29
|
+
CheckboxProps,
|
30
|
+
PinInputField,
|
31
|
+
HStack,
|
32
|
+
PinInput,
|
33
|
+
UsePinInputProps,
|
34
|
+
SystemProps,
|
25
35
|
} from '@chakra-ui/react'
|
26
|
-
import { __DEV__ } from '@chakra-ui/utils'
|
36
|
+
import { __DEV__, FocusableElement } from '@chakra-ui/utils'
|
27
37
|
|
28
|
-
import { NumberInput } from '@saas-ui/number-input'
|
29
|
-
import { PasswordInput } from '@saas-ui/password-input'
|
30
|
-
import { RadioInput } from '@saas-ui/radio'
|
31
|
-
|
32
|
-
import {
|
33
|
-
|
38
|
+
import { NumberInput, NumberInputProps } from '@saas-ui/number-input'
|
39
|
+
import { PasswordInput, PasswordInputProps } from '@saas-ui/password-input'
|
40
|
+
import { RadioInput, RadioInputProps } from '@saas-ui/radio'
|
41
|
+
|
42
|
+
import {
|
43
|
+
Select,
|
44
|
+
SelectProps,
|
45
|
+
NativeSelect,
|
46
|
+
NativeSelectProps,
|
47
|
+
} from '@saas-ui/select'
|
34
48
|
|
35
49
|
export interface Option {
|
36
50
|
value: string
|
@@ -43,19 +57,6 @@ export type FieldRules = Pick<
|
|
43
57
|
'required' | 'min' | 'max' | 'maxLength' | 'minLength' | 'pattern'
|
44
58
|
>
|
45
59
|
|
46
|
-
export type FieldTypes =
|
47
|
-
| 'text'
|
48
|
-
| 'number'
|
49
|
-
| 'password'
|
50
|
-
| 'textarea'
|
51
|
-
| 'select'
|
52
|
-
| 'native-select'
|
53
|
-
| 'checkbox'
|
54
|
-
| 'radio'
|
55
|
-
| 'switch'
|
56
|
-
| 'pin'
|
57
|
-
| string
|
58
|
-
|
59
60
|
export interface FieldProps<
|
60
61
|
TFieldValues extends FieldValues = FieldValues,
|
61
62
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
@@ -84,11 +85,6 @@ export interface FieldProps<
|
|
84
85
|
'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
|
85
86
|
>
|
86
87
|
/**
|
87
|
-
* Options used for selects and radio fields
|
88
|
-
*/
|
89
|
-
options?: Option[]
|
90
|
-
/**
|
91
|
-
* The field type
|
92
88
|
* Build-in types:
|
93
89
|
* - text
|
94
90
|
* - number
|
@@ -102,16 +98,15 @@ export interface FieldProps<
|
|
102
98
|
* - pin
|
103
99
|
*
|
104
100
|
* Will default to a text field if there is no matching type.
|
105
|
-
* @default 'text'
|
106
101
|
*/
|
107
|
-
type?:
|
102
|
+
type?: string
|
108
103
|
/**
|
109
104
|
* The input placeholder
|
110
105
|
*/
|
111
106
|
placeholder?: string
|
112
107
|
}
|
113
108
|
|
114
|
-
const inputTypes: Record<
|
109
|
+
const inputTypes: Record<string, React.FC<any>> = {}
|
115
110
|
|
116
111
|
const defaultInputType = 'text'
|
117
112
|
|
@@ -160,11 +155,30 @@ if (__DEV__) {
|
|
160
155
|
BaseField.displayName = 'BaseField'
|
161
156
|
}
|
162
157
|
|
163
|
-
export
|
158
|
+
export type As<Props = any> = React.ElementType<Props>
|
159
|
+
|
160
|
+
export type PropsOf<T extends As> = React.ComponentPropsWithoutRef<T> & {
|
161
|
+
type?: FieldTypes
|
162
|
+
}
|
163
|
+
|
164
|
+
/**
|
165
|
+
* Build-in types:
|
166
|
+
* - text
|
167
|
+
* - number
|
168
|
+
* - password
|
169
|
+
* - textarea
|
170
|
+
* - select
|
171
|
+
* - native-select
|
172
|
+
* - checkbox
|
173
|
+
* - radio
|
174
|
+
* - switch
|
175
|
+
* - pin
|
176
|
+
*
|
177
|
+
* Will default to a text field if there is no matching type.
|
178
|
+
*/
|
179
|
+
export const Field = React.forwardRef(
|
164
180
|
<TFieldValues extends FieldValues = FieldValues>(
|
165
|
-
props: FieldProps<TFieldValues>
|
166
|
-
[key: string]: unknown // Make sure attributes of custom components work. Need to change this to a global typedef at some point.
|
167
|
-
},
|
181
|
+
props: FieldProps<TFieldValues> | FieldTypeProps,
|
168
182
|
ref: React.ForwardedRef<FocusableElement>
|
169
183
|
) => {
|
170
184
|
const { type = defaultInputType } = props
|
@@ -172,13 +186,14 @@ export const Field = forwardRef(
|
|
172
186
|
|
173
187
|
return <InputComponent ref={ref} {...props} />
|
174
188
|
}
|
175
|
-
) as <TFieldValues extends FieldValues>(
|
176
|
-
props: FieldProps<TFieldValues> &
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
189
|
+
) as (<TFieldValues extends FieldValues>(
|
190
|
+
props: FieldProps<TFieldValues> &
|
191
|
+
FieldTypeProps & {
|
192
|
+
ref?: React.ForwardedRef<FocusableElement>
|
193
|
+
}
|
194
|
+
) => React.ReactElement) & {
|
195
|
+
displayName?: string
|
196
|
+
}
|
182
197
|
|
183
198
|
interface CreateFieldProps {
|
184
199
|
displayName: string
|
@@ -190,7 +205,7 @@ const createField = (
|
|
190
205
|
InputComponent: React.FC<any>,
|
191
206
|
{ displayName, hideLabel, BaseField }: CreateFieldProps
|
192
207
|
) => {
|
193
|
-
const Field = forwardRef
|
208
|
+
const Field = forwardRef((props, ref) => {
|
194
209
|
const {
|
195
210
|
id,
|
196
211
|
name,
|
@@ -227,7 +242,7 @@ const createField = (
|
|
227
242
|
ref={ref}
|
228
243
|
id={id}
|
229
244
|
name={name}
|
230
|
-
label={label}
|
245
|
+
label={hideLabel ? label : undefined} // Only pass down the label when it should be inline.
|
231
246
|
rules={inputRules}
|
232
247
|
{...inputProps}
|
233
248
|
/>
|
@@ -239,7 +254,7 @@ const createField = (
|
|
239
254
|
return Field
|
240
255
|
}
|
241
256
|
|
242
|
-
export const withControlledInput = (InputComponent: any) => {
|
257
|
+
export const withControlledInput = (InputComponent: React.FC<any>) => {
|
243
258
|
return forwardRef<FieldProps, typeof InputComponent>(
|
244
259
|
({ name, rules, ...inputProps }, ref) => {
|
245
260
|
const { control } = useFormContext()
|
@@ -262,7 +277,7 @@ export const withControlledInput = (InputComponent: any) => {
|
|
262
277
|
)
|
263
278
|
}
|
264
279
|
|
265
|
-
export const withUncontrolledInput = (InputComponent: any) => {
|
280
|
+
export const withUncontrolledInput = (InputComponent: React.FC<any>) => {
|
266
281
|
return forwardRef<FieldProps, typeof InputComponent>(
|
267
282
|
({ name, rules, ...inputProps }, ref) => {
|
268
283
|
const { register } = useFormContext()
|
@@ -292,11 +307,11 @@ export interface RegisterFieldTypeOptions {
|
|
292
307
|
* @param component The React component
|
293
308
|
* @param options
|
294
309
|
* @param options.isControlled Set this to true if this is a controlled field.
|
295
|
-
* @param options.hideLabel Hide the field label, for example for checkbox
|
310
|
+
* @param options.hideLabel Hide the field label, for example for the checkbox field.
|
296
311
|
*/
|
297
|
-
export const registerFieldType = (
|
312
|
+
export const registerFieldType = <T extends object>(
|
298
313
|
type: string,
|
299
|
-
component: React.FC<
|
314
|
+
component: React.FC<T>,
|
300
315
|
options?: RegisterFieldTypeOptions
|
301
316
|
) => {
|
302
317
|
let InputComponent
|
@@ -313,41 +328,75 @@ export const registerFieldType = (
|
|
313
328
|
.join('')}Field`,
|
314
329
|
hideLabel: options?.hideLabel,
|
315
330
|
BaseField: options?.BaseField || BaseField,
|
316
|
-
})
|
331
|
+
}) as React.FC<T & FieldProps>
|
317
332
|
|
318
333
|
inputTypes[type] = Field
|
319
334
|
|
320
335
|
return Field
|
321
336
|
}
|
322
337
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
export const
|
330
|
-
|
331
|
-
|
338
|
+
export interface InputFieldProps extends InputProps {
|
339
|
+
type?: string
|
340
|
+
leftAddon?: React.ReactNode
|
341
|
+
rightAddon?: React.ReactNode
|
342
|
+
}
|
343
|
+
|
344
|
+
export const InputField = registerFieldType<InputFieldProps>(
|
345
|
+
'text',
|
346
|
+
forwardRef(({ type = 'text', leftAddon, rightAddon, size, ...rest }, ref) => {
|
347
|
+
const input = <Input type={type} size={size} {...rest} ref={ref} />
|
348
|
+
if (leftAddon || rightAddon) {
|
349
|
+
return (
|
350
|
+
<InputGroup size={size}>
|
351
|
+
{leftAddon}
|
352
|
+
{input}
|
353
|
+
{rightAddon}
|
354
|
+
</InputGroup>
|
355
|
+
)
|
356
|
+
}
|
357
|
+
return input
|
358
|
+
})
|
359
|
+
)
|
360
|
+
|
361
|
+
export interface NumberInputFieldProps extends NumberInputProps {
|
362
|
+
type: 'number'
|
363
|
+
}
|
364
|
+
|
365
|
+
export const NumberInputField = registerFieldType<NumberInputFieldProps>(
|
366
|
+
'number',
|
367
|
+
NumberInput,
|
368
|
+
{
|
369
|
+
isControlled: true,
|
370
|
+
}
|
371
|
+
)
|
372
|
+
|
373
|
+
export const PasswordInputField = registerFieldType<PasswordInputProps>(
|
374
|
+
'password',
|
375
|
+
forwardRef((props, ref) => <PasswordInput ref={ref} {...props} />)
|
376
|
+
)
|
377
|
+
|
378
|
+
export const TextareaField = registerFieldType<TextareaProps>(
|
379
|
+
'textarea',
|
380
|
+
Textarea
|
381
|
+
)
|
382
|
+
|
383
|
+
export const SwitchField = registerFieldType<SwitchProps>(
|
332
384
|
'switch',
|
333
|
-
forwardRef(({
|
334
|
-
return
|
335
|
-
<Switch ref={ref} {...props}>
|
336
|
-
{label}
|
337
|
-
</Switch>
|
338
|
-
)
|
385
|
+
forwardRef(({ type, ...rest }, ref) => {
|
386
|
+
return <Switch {...rest} ref={ref} />
|
339
387
|
}),
|
340
388
|
{
|
341
389
|
isControlled: true,
|
342
|
-
hideLabel: true,
|
343
390
|
}
|
344
391
|
)
|
345
|
-
|
392
|
+
|
393
|
+
export const SelectField = registerFieldType<SelectProps>('select', Select, {
|
346
394
|
isControlled: true,
|
347
395
|
})
|
348
|
-
|
396
|
+
|
397
|
+
export const CheckboxField = registerFieldType<CheckboxProps>(
|
349
398
|
'checkbox',
|
350
|
-
forwardRef(({ label, ...props }
|
399
|
+
forwardRef(({ label, type, ...props }, ref) => {
|
351
400
|
return (
|
352
401
|
<Checkbox ref={ref} {...props}>
|
353
402
|
{label}
|
@@ -358,14 +407,76 @@ export const CheckboxField = registerFieldType(
|
|
358
407
|
hideLabel: true,
|
359
408
|
}
|
360
409
|
)
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
410
|
+
|
411
|
+
export const RadioField = registerFieldType<RadioInputProps>(
|
412
|
+
'radio',
|
413
|
+
RadioInput,
|
414
|
+
{
|
415
|
+
isControlled: true,
|
416
|
+
}
|
417
|
+
)
|
418
|
+
|
419
|
+
export const NativeSelectField = registerFieldType<NativeSelectProps>(
|
368
420
|
'native-select',
|
369
421
|
NativeSelect,
|
370
422
|
{ isControlled: true }
|
371
423
|
)
|
424
|
+
|
425
|
+
export interface PinFieldProps extends Omit<UsePinInputProps, 'type'> {
|
426
|
+
pinLength?: number
|
427
|
+
pinType?: 'alphanumeric' | 'number'
|
428
|
+
spacing?: SystemProps['margin']
|
429
|
+
}
|
430
|
+
|
431
|
+
export const PinField = registerFieldType<PinFieldProps>(
|
432
|
+
'pin',
|
433
|
+
forwardRef((props, ref) => {
|
434
|
+
const { pinLength = 4, pinType, spacing, ...inputProps } = props
|
435
|
+
|
436
|
+
const inputs: React.ReactNode[] = []
|
437
|
+
for (let i = 0; i < pinLength; i++) {
|
438
|
+
inputs.push(<PinInputField key={i} ref={ref} />)
|
439
|
+
}
|
440
|
+
|
441
|
+
return (
|
442
|
+
<HStack spacing={spacing}>
|
443
|
+
<PinInput {...inputProps} type={pinType}>
|
444
|
+
{inputs}
|
445
|
+
</PinInput>
|
446
|
+
</HStack>
|
447
|
+
)
|
448
|
+
}),
|
449
|
+
{
|
450
|
+
isControlled: true,
|
451
|
+
}
|
452
|
+
)
|
453
|
+
|
454
|
+
const fieldTypes = {
|
455
|
+
text: InputField,
|
456
|
+
email: InputField,
|
457
|
+
url: InputField,
|
458
|
+
phone: InputField,
|
459
|
+
number: NumberInputField,
|
460
|
+
password: PasswordInputField,
|
461
|
+
textarea: TextareaField,
|
462
|
+
switch: SwitchField,
|
463
|
+
checkbox: CheckboxField,
|
464
|
+
radio: RadioField,
|
465
|
+
pin: PinField,
|
466
|
+
select: SelectField,
|
467
|
+
'native-select': NativeSelectField,
|
468
|
+
}
|
469
|
+
|
470
|
+
type FieldTypes = typeof fieldTypes
|
471
|
+
|
472
|
+
type FieldType<Props = any> = React.ElementType<Props>
|
473
|
+
|
474
|
+
type TypeProps<P extends FieldType, T> = React.ComponentPropsWithoutRef<P> & {
|
475
|
+
type: T
|
476
|
+
}
|
477
|
+
|
478
|
+
type FieldTypeProps =
|
479
|
+
| {
|
480
|
+
[Property in keyof FieldTypes]: TypeProps<FieldTypes[Property], Property>
|
481
|
+
}[keyof FieldTypes]
|
482
|
+
| { type?: string }
|
package/src/form.tsx
CHANGED
package/src/object-field.tsx
CHANGED
@@ -4,7 +4,7 @@ import {
|
|
4
4
|
FormLabel,
|
5
5
|
FormLabelProps,
|
6
6
|
ResponsiveValue,
|
7
|
-
|
7
|
+
useStyleConfig,
|
8
8
|
} from '@chakra-ui/react'
|
9
9
|
import { __DEV__ } from '@chakra-ui/utils'
|
10
10
|
|
@@ -21,8 +21,8 @@ export interface ObjectFieldProps extends FieldProps {
|
|
21
21
|
}
|
22
22
|
|
23
23
|
export const FormLegend = (props: FormLabelProps) => {
|
24
|
-
const styles =
|
25
|
-
return <FormLabel as="legend" sx={styles
|
24
|
+
const styles = useStyleConfig('FormLegend')
|
25
|
+
return <FormLabel as="legend" sx={styles} {...props} />
|
26
26
|
}
|
27
27
|
|
28
28
|
export const ObjectField: React.FC<ObjectFieldProps> = (props) => {
|