@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.
Files changed (50) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +29 -0
  3. package/ajv/package.json +18 -0
  4. package/dist/ajv/ajv-resolver.d.ts +11 -0
  5. package/dist/ajv/ajv-resolver.d.ts.map +1 -0
  6. package/dist/ajv/index.d.ts +2 -0
  7. package/dist/ajv/index.d.ts.map +1 -0
  8. package/dist/ajv/index.js +104 -0
  9. package/dist/ajv/index.js.map +1 -0
  10. package/dist/ajv/index.modern.mjs +104 -0
  11. package/dist/ajv/index.modern.mjs.map +1 -0
  12. package/dist/array-field.d.ts +14 -3
  13. package/dist/array-field.d.ts.map +1 -1
  14. package/dist/auto-form.d.ts +13 -1
  15. package/dist/auto-form.d.ts.map +1 -1
  16. package/dist/field.d.ts +82 -27
  17. package/dist/field.d.ts.map +1 -1
  18. package/dist/form.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.modern.mjs +1 -1
  22. package/dist/index.modern.mjs.map +1 -1
  23. package/dist/step-form.d.ts +4 -4
  24. package/dist/step-form.d.ts.map +1 -1
  25. package/dist/submit-button.d.ts +2 -1
  26. package/dist/submit-button.d.ts.map +1 -1
  27. package/dist/use-step-form.d.ts +7 -3
  28. package/dist/use-step-form.d.ts.map +1 -1
  29. package/dist/yup/index.js +1 -1
  30. package/dist/yup/index.js.map +1 -1
  31. package/dist/yup/index.modern.mjs +1 -1
  32. package/dist/yup/index.modern.mjs.map +1 -1
  33. package/dist/zod/index.js +1 -1
  34. package/dist/zod/index.js.map +1 -1
  35. package/dist/zod/index.modern.mjs +1 -1
  36. package/dist/zod/index.modern.mjs.map +1 -1
  37. package/dist/zod/zod-resolver.d.ts +2 -2
  38. package/dist/zod/zod-resolver.d.ts.map +1 -1
  39. package/package.json +32 -17
  40. package/src/array-field.tsx +21 -22
  41. package/src/auto-form.tsx +22 -3
  42. package/src/field-resolver.ts +2 -2
  43. package/src/field.tsx +184 -73
  44. package/src/form.tsx +0 -1
  45. package/src/object-field.tsx +3 -3
  46. package/src/step-form.tsx +27 -20
  47. package/src/submit-button.tsx +32 -24
  48. package/src/use-step-form.tsx +27 -12
  49. package/yup/package.json +2 -2
  50. 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,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEvD,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,UAAU,CAAA;CAClB;AAED,eAAO,MAAM,OAAO,SAAU,OAAO,WAEpC,CAAA;AAED,eAAO,MAAM,YAAY,SAAU,MAAM,QAMxC,CAAA"}
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.0",
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.0",
83
- "@chakra-ui/react-utils": "^2.0.0",
84
- "@chakra-ui/utils": "^2.0.0",
85
- "@hookform/resolvers": "^2.8.10",
86
- "@saas-ui/button": "1.0.0-rc.0",
87
- "@saas-ui/input-right-button": "1.0.0-rc.0",
88
- "@saas-ui/number-input": "1.0.0-rc.0",
89
- "@saas-ui/password-input": "1.0.0-rc.0",
90
- "@saas-ui/pin-input": "1.0.0-rc.0",
91
- "@saas-ui/radio": "1.0.0-rc.0",
92
- "@saas-ui/react-utils": "1.0.0-rc.0",
93
- "@saas-ui/select": "1.0.0-rc.0",
94
- "@saas-ui/stepper": "1.0.0-rc.0",
95
- "react-hook-form": "^7.31.2"
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
  }
@@ -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 columns={columns} spacing={spacing}>
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 = React.forwardRef(
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
- submitLabel?: false | string
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 { schema, submitLabel = 'Submit', fieldResolver, ...rest } = props
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 label={submitLabel} />}
51
+ {submitLabel && <SubmitButton>{submitLabel}</SubmitButton>}
52
+ {children}
34
53
  </FormLayout>
35
54
  </Form>
36
55
  )
@@ -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
- import { PinInput } from '@saas-ui/pin-input'
32
- import { Select, NativeSelect } from '@saas-ui/select'
33
- import { FocusableElement } from '@chakra-ui/utils'
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?: FieldTypes
102
+ type?: string
108
103
  /**
109
104
  * The input placeholder
110
105
  */
111
106
  placeholder?: string
112
107
  }
113
108
 
114
- const inputTypes: Record<FieldTypes, any> = {}
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 const Field = forwardRef(
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
- [key: string]: unknown
178
- } & {
179
- ref?: React.ForwardedRef<FocusableElement>
180
- }
181
- ) => React.ReactElement
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<FieldProps, typeof FormControl>((props, ref) => {
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 or switch field.
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<any>,
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
- // @todo Consider not registering all fields by default to lower the package size and computations.
324
- // Not all types may be required in a project.
325
- export const InputField = registerFieldType('text', Input)
326
- export const NumberInputField = registerFieldType('number', NumberInput, {
327
- isControlled: true,
328
- })
329
- export const PasswordInputFIeld = registerFieldType('password', PasswordInput)
330
- export const TextareaField = registerFieldType('textarea', Textarea)
331
- export const SwitchField = registerFieldType(
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(({ label, ...props }: { label?: string }, ref) => {
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
- export const SelectField = registerFieldType('select', Select, {
392
+
393
+ export const SelectField = registerFieldType<SelectProps>('select', Select, {
346
394
  isControlled: true,
347
395
  })
348
- export const CheckboxField = registerFieldType(
396
+
397
+ export const CheckboxField = registerFieldType<CheckboxProps>(
349
398
  'checkbox',
350
- forwardRef(({ label, ...props }: { label?: string }, ref) => {
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
- export const RadioField = registerFieldType('radio', RadioInput, {
362
- isControlled: true,
363
- })
364
- export const PinField = registerFieldType('pin', PinInput, {
365
- isControlled: true,
366
- })
367
- export const NativeSelectField = registerFieldType(
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
@@ -16,7 +16,6 @@ import {
16
16
  ResolverResult,
17
17
  } from 'react-hook-form'
18
18
  import { objectFieldResolver, FieldResolver } from './field-resolver'
19
- import { css } from '@emotion/react'
20
19
 
21
20
  export type { UseFormReturn, FieldValues, SubmitHandler }
22
21
 
@@ -4,7 +4,7 @@ import {
4
4
  FormLabel,
5
5
  FormLabelProps,
6
6
  ResponsiveValue,
7
- useStyles,
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 = useStyles()
25
- return <FormLabel as="legend" sx={styles.legend} {...props} />
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) => {