@saas-ui/forms 1.0.0-rc.1 → 1.0.0-rc.10
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 +107 -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 +97 -0
- package/dist/ajv/index.js.map +1 -0
- package/dist/ajv/index.modern.mjs +97 -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/layout.d.ts +0 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/step-form.d.ts +4 -4
- package/dist/step-form.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/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/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 +186 -71
- package/src/form.tsx +0 -1
- package/src/object-field.tsx +3 -3
- package/src/step-form.tsx +23 -17
- package/src/submit-button.tsx +1 -1
- package/src/use-step-form.tsx +27 -12
- package/yup/package.json +2 -2
- package/zod/package.json +4 -4
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
@@ -6,12 +6,24 @@ 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 } from './submit-button'
|
9
|
+
import { SubmitButton, SubmitButtonProps } 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
51
|
{submitLabel && <SubmitButton label={submitLabel} />}
|
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,14 +22,30 @@ import {
|
|
22
22
|
Checkbox,
|
23
23
|
Switch,
|
24
24
|
useMergeRefs,
|
25
|
+
InputGroup,
|
26
|
+
InputProps,
|
27
|
+
TextareaProps,
|
28
|
+
SwitchProps,
|
29
|
+
CheckboxProps,
|
30
|
+
PinInputProps,
|
31
|
+
PinInputField,
|
32
|
+
HStack,
|
33
|
+
PinInput,
|
34
|
+
UsePinInputProps,
|
35
|
+
SystemProps,
|
25
36
|
} from '@chakra-ui/react'
|
26
37
|
import { __DEV__ } from '@chakra-ui/utils'
|
27
38
|
|
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 {
|
39
|
+
import { NumberInput, NumberInputProps } from '@saas-ui/number-input'
|
40
|
+
import { PasswordInput, PasswordInputProps } from '@saas-ui/password-input'
|
41
|
+
import { RadioInput, RadioInputProps } from '@saas-ui/radio'
|
42
|
+
|
43
|
+
import {
|
44
|
+
Select,
|
45
|
+
SelectProps,
|
46
|
+
NativeSelect,
|
47
|
+
NativeSelectProps,
|
48
|
+
} from '@saas-ui/select'
|
33
49
|
import { FocusableElement } from '@chakra-ui/utils'
|
34
50
|
|
35
51
|
export interface Option {
|
@@ -43,19 +59,6 @@ export type FieldRules = Pick<
|
|
43
59
|
'required' | 'min' | 'max' | 'maxLength' | 'minLength' | 'pattern'
|
44
60
|
>
|
45
61
|
|
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
62
|
export interface FieldProps<
|
60
63
|
TFieldValues extends FieldValues = FieldValues,
|
61
64
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
@@ -84,11 +87,6 @@ export interface FieldProps<
|
|
84
87
|
'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
|
85
88
|
>
|
86
89
|
/**
|
87
|
-
* Options used for selects and radio fields
|
88
|
-
*/
|
89
|
-
options?: Option[]
|
90
|
-
/**
|
91
|
-
* The field type
|
92
90
|
* Build-in types:
|
93
91
|
* - text
|
94
92
|
* - number
|
@@ -102,16 +100,15 @@ export interface FieldProps<
|
|
102
100
|
* - pin
|
103
101
|
*
|
104
102
|
* Will default to a text field if there is no matching type.
|
105
|
-
* @default 'text'
|
106
103
|
*/
|
107
|
-
type?:
|
104
|
+
type?: string
|
108
105
|
/**
|
109
106
|
* The input placeholder
|
110
107
|
*/
|
111
108
|
placeholder?: string
|
112
109
|
}
|
113
110
|
|
114
|
-
const inputTypes: Record<
|
111
|
+
const inputTypes: Record<string, React.FC<any>> = {}
|
115
112
|
|
116
113
|
const defaultInputType = 'text'
|
117
114
|
|
@@ -160,11 +157,30 @@ if (__DEV__) {
|
|
160
157
|
BaseField.displayName = 'BaseField'
|
161
158
|
}
|
162
159
|
|
163
|
-
export
|
160
|
+
export type As<Props = any> = React.ElementType<Props>
|
161
|
+
|
162
|
+
export type PropsOf<T extends As> = React.ComponentPropsWithoutRef<T> & {
|
163
|
+
type?: FieldTypes
|
164
|
+
}
|
165
|
+
|
166
|
+
/**
|
167
|
+
* Build-in types:
|
168
|
+
* - text
|
169
|
+
* - number
|
170
|
+
* - password
|
171
|
+
* - textarea
|
172
|
+
* - select
|
173
|
+
* - native-select
|
174
|
+
* - checkbox
|
175
|
+
* - radio
|
176
|
+
* - switch
|
177
|
+
* - pin
|
178
|
+
*
|
179
|
+
* Will default to a text field if there is no matching type.
|
180
|
+
*/
|
181
|
+
export const Field = React.forwardRef(
|
164
182
|
<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
|
-
},
|
183
|
+
props: FieldProps<TFieldValues> | FieldTypeProps,
|
168
184
|
ref: React.ForwardedRef<FocusableElement>
|
169
185
|
) => {
|
170
186
|
const { type = defaultInputType } = props
|
@@ -172,13 +188,14 @@ export const Field = forwardRef(
|
|
172
188
|
|
173
189
|
return <InputComponent ref={ref} {...props} />
|
174
190
|
}
|
175
|
-
) as <TFieldValues extends FieldValues>(
|
176
|
-
props: FieldProps<TFieldValues> &
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
191
|
+
) as (<TFieldValues extends FieldValues>(
|
192
|
+
props: FieldProps<TFieldValues> &
|
193
|
+
FieldTypeProps & {
|
194
|
+
ref?: React.ForwardedRef<FocusableElement>
|
195
|
+
}
|
196
|
+
) => React.ReactElement) & {
|
197
|
+
displayName?: string
|
198
|
+
}
|
182
199
|
|
183
200
|
interface CreateFieldProps {
|
184
201
|
displayName: string
|
@@ -190,7 +207,7 @@ const createField = (
|
|
190
207
|
InputComponent: React.FC<any>,
|
191
208
|
{ displayName, hideLabel, BaseField }: CreateFieldProps
|
192
209
|
) => {
|
193
|
-
const Field = forwardRef
|
210
|
+
const Field = forwardRef((props, ref) => {
|
194
211
|
const {
|
195
212
|
id,
|
196
213
|
name,
|
@@ -200,6 +217,7 @@ const createField = (
|
|
200
217
|
isInvalid,
|
201
218
|
isReadOnly,
|
202
219
|
isRequired,
|
220
|
+
isOptional,
|
203
221
|
rules,
|
204
222
|
variant,
|
205
223
|
...inputProps
|
@@ -221,13 +239,14 @@ const createField = (
|
|
221
239
|
isInvalid={isInvalid}
|
222
240
|
isReadOnly={isReadOnly}
|
223
241
|
isRequired={isRequired}
|
242
|
+
isOptional={isOptional}
|
224
243
|
variant={variant}
|
225
244
|
>
|
226
245
|
<InputComponent
|
227
246
|
ref={ref}
|
228
247
|
id={id}
|
229
248
|
name={name}
|
230
|
-
label={label}
|
249
|
+
label={hideLabel ? label : undefined} // Only pass down the label when it should be inline.
|
231
250
|
rules={inputRules}
|
232
251
|
{...inputProps}
|
233
252
|
/>
|
@@ -239,7 +258,7 @@ const createField = (
|
|
239
258
|
return Field
|
240
259
|
}
|
241
260
|
|
242
|
-
export const withControlledInput = (InputComponent: any) => {
|
261
|
+
export const withControlledInput = (InputComponent: React.FC<any>) => {
|
243
262
|
return forwardRef<FieldProps, typeof InputComponent>(
|
244
263
|
({ name, rules, ...inputProps }, ref) => {
|
245
264
|
const { control } = useFormContext()
|
@@ -262,7 +281,7 @@ export const withControlledInput = (InputComponent: any) => {
|
|
262
281
|
)
|
263
282
|
}
|
264
283
|
|
265
|
-
export const withUncontrolledInput = (InputComponent: any) => {
|
284
|
+
export const withUncontrolledInput = (InputComponent: React.FC<any>) => {
|
266
285
|
return forwardRef<FieldProps, typeof InputComponent>(
|
267
286
|
({ name, rules, ...inputProps }, ref) => {
|
268
287
|
const { register } = useFormContext()
|
@@ -292,11 +311,11 @@ export interface RegisterFieldTypeOptions {
|
|
292
311
|
* @param component The React component
|
293
312
|
* @param options
|
294
313
|
* @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
|
314
|
+
* @param options.hideLabel Hide the field label, for example for the checkbox field.
|
296
315
|
*/
|
297
|
-
export const registerFieldType = (
|
316
|
+
export const registerFieldType = <T extends object>(
|
298
317
|
type: string,
|
299
|
-
component: React.FC<
|
318
|
+
component: React.FC<T>,
|
300
319
|
options?: RegisterFieldTypeOptions
|
301
320
|
) => {
|
302
321
|
let InputComponent
|
@@ -313,41 +332,75 @@ export const registerFieldType = (
|
|
313
332
|
.join('')}Field`,
|
314
333
|
hideLabel: options?.hideLabel,
|
315
334
|
BaseField: options?.BaseField || BaseField,
|
316
|
-
})
|
335
|
+
}) as React.FC<T & FieldProps>
|
317
336
|
|
318
337
|
inputTypes[type] = Field
|
319
338
|
|
320
339
|
return Field
|
321
340
|
}
|
322
341
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
export const
|
330
|
-
|
331
|
-
|
342
|
+
export interface InputFieldProps extends InputProps {
|
343
|
+
type?: string
|
344
|
+
leftAddon?: React.ReactNode
|
345
|
+
rightAddon?: React.ReactNode
|
346
|
+
}
|
347
|
+
|
348
|
+
export const InputField = registerFieldType<InputFieldProps>(
|
349
|
+
'text',
|
350
|
+
forwardRef(({ type = 'text', leftAddon, rightAddon, size, ...rest }, ref) => {
|
351
|
+
const input = <Input type={type} size={size} {...rest} ref={ref} />
|
352
|
+
if (leftAddon || rightAddon) {
|
353
|
+
return (
|
354
|
+
<InputGroup size={size}>
|
355
|
+
{leftAddon}
|
356
|
+
{input}
|
357
|
+
{rightAddon}
|
358
|
+
</InputGroup>
|
359
|
+
)
|
360
|
+
}
|
361
|
+
return input
|
362
|
+
})
|
363
|
+
)
|
364
|
+
|
365
|
+
export interface NumberInputFieldProps extends NumberInputProps {
|
366
|
+
type: 'number'
|
367
|
+
}
|
368
|
+
|
369
|
+
export const NumberInputField = registerFieldType<NumberInputFieldProps>(
|
370
|
+
'number',
|
371
|
+
NumberInput,
|
372
|
+
{
|
373
|
+
isControlled: true,
|
374
|
+
}
|
375
|
+
)
|
376
|
+
|
377
|
+
export const PasswordInputField = registerFieldType<PasswordInputProps>(
|
378
|
+
'password',
|
379
|
+
forwardRef((props, ref) => <PasswordInput ref={ref} {...props} />)
|
380
|
+
)
|
381
|
+
|
382
|
+
export const TextareaField = registerFieldType<TextareaProps>(
|
383
|
+
'textarea',
|
384
|
+
Textarea
|
385
|
+
)
|
386
|
+
|
387
|
+
export const SwitchField = registerFieldType<SwitchProps>(
|
332
388
|
'switch',
|
333
|
-
forwardRef(({
|
334
|
-
return
|
335
|
-
<Switch ref={ref} {...props}>
|
336
|
-
{label}
|
337
|
-
</Switch>
|
338
|
-
)
|
389
|
+
forwardRef(({ type, ...rest }, ref) => {
|
390
|
+
return <Switch {...rest} ref={ref} />
|
339
391
|
}),
|
340
392
|
{
|
341
393
|
isControlled: true,
|
342
|
-
hideLabel: true,
|
343
394
|
}
|
344
395
|
)
|
345
|
-
|
396
|
+
|
397
|
+
export const SelectField = registerFieldType<SelectProps>('select', Select, {
|
346
398
|
isControlled: true,
|
347
399
|
})
|
348
|
-
|
400
|
+
|
401
|
+
export const CheckboxField = registerFieldType<CheckboxProps>(
|
349
402
|
'checkbox',
|
350
|
-
forwardRef(({ label, ...props }
|
403
|
+
forwardRef(({ label, type, ...props }, ref) => {
|
351
404
|
return (
|
352
405
|
<Checkbox ref={ref} {...props}>
|
353
406
|
{label}
|
@@ -358,14 +411,76 @@ export const CheckboxField = registerFieldType(
|
|
358
411
|
hideLabel: true,
|
359
412
|
}
|
360
413
|
)
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
414
|
+
|
415
|
+
export const RadioField = registerFieldType<RadioInputProps>(
|
416
|
+
'radio',
|
417
|
+
RadioInput,
|
418
|
+
{
|
419
|
+
isControlled: true,
|
420
|
+
}
|
421
|
+
)
|
422
|
+
|
423
|
+
export const NativeSelectField = registerFieldType<NativeSelectProps>(
|
368
424
|
'native-select',
|
369
425
|
NativeSelect,
|
370
426
|
{ isControlled: true }
|
371
427
|
)
|
428
|
+
|
429
|
+
export interface PinFieldProps extends Omit<UsePinInputProps, 'type'> {
|
430
|
+
pinLength?: number
|
431
|
+
pinType?: 'alphanumeric' | 'number'
|
432
|
+
spacing?: SystemProps['margin']
|
433
|
+
}
|
434
|
+
|
435
|
+
export const PinField = registerFieldType<PinFieldProps>(
|
436
|
+
'pin',
|
437
|
+
forwardRef((props, ref) => {
|
438
|
+
const { pinLength = 4, pinType, spacing, ...inputProps } = props
|
439
|
+
|
440
|
+
const inputs: React.ReactNode[] = []
|
441
|
+
for (let i = 0; i < pinLength; i++) {
|
442
|
+
inputs.push(<PinInputField key={i} ref={ref} />)
|
443
|
+
}
|
444
|
+
|
445
|
+
return (
|
446
|
+
<HStack spacing={spacing}>
|
447
|
+
<PinInput {...inputProps} type={pinType}>
|
448
|
+
{inputs}
|
449
|
+
</PinInput>
|
450
|
+
</HStack>
|
451
|
+
)
|
452
|
+
}),
|
453
|
+
{
|
454
|
+
isControlled: true,
|
455
|
+
}
|
456
|
+
)
|
457
|
+
|
458
|
+
const fieldTypes = {
|
459
|
+
text: InputField,
|
460
|
+
email: InputField,
|
461
|
+
url: InputField,
|
462
|
+
phone: InputField,
|
463
|
+
number: NumberInputField,
|
464
|
+
password: PasswordInputField,
|
465
|
+
textarea: TextareaField,
|
466
|
+
switch: SwitchField,
|
467
|
+
checkbox: CheckboxField,
|
468
|
+
radio: RadioField,
|
469
|
+
pin: PinField,
|
470
|
+
select: SelectField,
|
471
|
+
'native-select': NativeSelectField,
|
472
|
+
}
|
473
|
+
|
474
|
+
type FieldTypes = typeof fieldTypes
|
475
|
+
|
476
|
+
type FieldType<Props = any> = React.ElementType<Props>
|
477
|
+
|
478
|
+
type TypeProps<P extends FieldType, T> = React.ComponentPropsWithoutRef<P> & {
|
479
|
+
type: T
|
480
|
+
}
|
481
|
+
|
482
|
+
type FieldTypeProps =
|
483
|
+
| {
|
484
|
+
[Property in keyof FieldTypes]: TypeProps<FieldTypes[Property], Property>
|
485
|
+
}[keyof FieldTypes]
|
486
|
+
| { 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) => {
|
package/src/step-form.tsx
CHANGED
@@ -2,13 +2,7 @@ import * as React from 'react'
|
|
2
2
|
|
3
3
|
import { FieldValues, UseFormReturn } from 'react-hook-form'
|
4
4
|
|
5
|
-
import {
|
6
|
-
chakra,
|
7
|
-
HTMLChakraProps,
|
8
|
-
useMultiStyleConfig,
|
9
|
-
StylesProvider,
|
10
|
-
SystemStyleObject,
|
11
|
-
} from '@chakra-ui/system'
|
5
|
+
import { chakra, HTMLChakraProps } from '@chakra-ui/system'
|
12
6
|
|
13
7
|
import { callAllHandlers, runIfFn, cx, __DEV__ } from '@chakra-ui/utils'
|
14
8
|
|
@@ -18,10 +12,11 @@ import {
|
|
18
12
|
StepperStepsProps,
|
19
13
|
StepperStep,
|
20
14
|
useStepperContext,
|
15
|
+
StepperContainer,
|
21
16
|
} from '@saas-ui/stepper'
|
22
17
|
import { Button, ButtonProps } from '@saas-ui/button'
|
23
18
|
|
24
|
-
import { Form
|
19
|
+
import { Form } from './form'
|
25
20
|
import { SubmitButton } from './submit-button'
|
26
21
|
|
27
22
|
import {
|
@@ -29,7 +24,7 @@ import {
|
|
29
24
|
useFormStep,
|
30
25
|
StepFormProvider,
|
31
26
|
UseStepFormProps,
|
32
|
-
|
27
|
+
FormStepSubmitHandler,
|
33
28
|
} from './use-step-form'
|
34
29
|
|
35
30
|
export interface StepFormProps<TFieldValues extends FieldValues = FieldValues>
|
@@ -59,7 +54,7 @@ export const StepForm = React.forwardRef(
|
|
59
54
|
)
|
60
55
|
}
|
61
56
|
) as <TFieldValues extends FieldValues>(
|
62
|
-
props:
|
57
|
+
props: StepFormProps<TFieldValues> & {
|
63
58
|
ref?: React.ForwardedRef<UseFormReturn<TFieldValues>>
|
64
59
|
}
|
65
60
|
) => React.ReactElement
|
@@ -80,9 +75,9 @@ export interface FormStepOptions {
|
|
80
75
|
}
|
81
76
|
|
82
77
|
export const FormStepper: React.FC<StepperStepsProps> = (props) => {
|
83
|
-
const
|
78
|
+
const { activeIndex, setIndex } = useStepperContext()
|
84
79
|
|
85
|
-
const { children } = props
|
80
|
+
const { children, orientation } = props
|
86
81
|
|
87
82
|
const elements = React.Children.map(children, (child) => {
|
88
83
|
if (React.isValidElement(child) && child?.type === FormStep) {
|
@@ -100,22 +95,33 @@ export const FormStepper: React.FC<StepperStepsProps> = (props) => {
|
|
100
95
|
return child
|
101
96
|
})
|
102
97
|
|
98
|
+
const onChange = React.useCallback((i: number) => {
|
99
|
+
setIndex(i)
|
100
|
+
}, [])
|
101
|
+
|
103
102
|
return (
|
104
|
-
<
|
103
|
+
<StepperContainer
|
104
|
+
orientation={orientation}
|
105
|
+
step={activeIndex}
|
106
|
+
onChange={onChange}
|
107
|
+
>
|
105
108
|
<StepperSteps mb="4" {...props}>
|
106
109
|
{elements}
|
107
110
|
</StepperSteps>
|
108
|
-
</
|
111
|
+
</StepperContainer>
|
109
112
|
)
|
110
113
|
}
|
111
114
|
|
112
115
|
export interface FormStepProps
|
113
116
|
extends FormStepOptions,
|
114
|
-
HTMLChakraProps<'div'> {
|
117
|
+
Omit<HTMLChakraProps<'div'>, 'onSubmit'> {
|
118
|
+
onSubmit?: FormStepSubmitHandler
|
119
|
+
}
|
115
120
|
|
116
121
|
export const FormStep: React.FC<FormStepProps> = (props) => {
|
117
|
-
const { name, schema, resolver, children, className, ...rest } =
|
118
|
-
|
122
|
+
const { name, schema, resolver, children, className, onSubmit, ...rest } =
|
123
|
+
props
|
124
|
+
const step = useFormStep({ name, schema, resolver, onSubmit })
|
119
125
|
|
120
126
|
const { isActive } = step
|
121
127
|
|
package/src/submit-button.tsx
CHANGED