@saas-ui/forms 1.0.0-rc.0 → 1.0.0-rc.11
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 +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) => {
|