@saas-ui/forms 1.0.0-rc.8 → 1.0.0-rc.9
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +13 -0
- package/dist/ajv/index.js +95 -95
- package/dist/ajv/index.js.map +1 -1
- package/dist/ajv/index.modern.mjs +94 -94
- package/dist/ajv/index.modern.mjs.map +1 -1
- package/dist/array-field.d.ts +14 -3
- package/dist/array-field.d.ts.map +1 -1
- package/dist/field.d.ts +81 -26
- 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 +1 -2
- package/dist/step-form.d.ts.map +1 -1
- package/dist/use-step-form.d.ts +2 -1
- package/dist/use-step-form.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/array-field.tsx +21 -22
- package/src/field.tsx +185 -70
- package/src/form.tsx +0 -1
- package/src/step-form.tsx +1 -1
- package/src/use-step-form.tsx +2 -4
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,6 +239,7 @@ 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
|
@@ -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()
|
@@ -294,9 +313,9 @@ export interface RegisterFieldTypeOptions {
|
|
294
313
|
* @param options.isControlled Set this to true if this is a controlled field.
|
295
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,25 +332,59 @@ 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
|
-
export
|
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>(
|
324
349
|
'text',
|
325
|
-
forwardRef(({ type = 'text', ...rest }, ref) => {
|
326
|
-
|
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
|
327
362
|
})
|
328
363
|
)
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
export const
|
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>(
|
335
388
|
'switch',
|
336
389
|
forwardRef(({ type, ...rest }, ref) => {
|
337
390
|
return <Switch {...rest} ref={ref} />
|
@@ -340,32 +393,94 @@ export const SwitchField = registerFieldType(
|
|
340
393
|
isControlled: true,
|
341
394
|
}
|
342
395
|
)
|
343
|
-
|
396
|
+
|
397
|
+
export const SelectField = registerFieldType<SelectProps>('select', Select, {
|
344
398
|
isControlled: true,
|
345
399
|
})
|
346
|
-
|
400
|
+
|
401
|
+
export const CheckboxField = registerFieldType<CheckboxProps>(
|
347
402
|
'checkbox',
|
348
|
-
forwardRef(
|
349
|
-
(
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
}
|
356
|
-
),
|
403
|
+
forwardRef(({ label, type, ...props }, ref) => {
|
404
|
+
return (
|
405
|
+
<Checkbox ref={ref} {...props}>
|
406
|
+
{label}
|
407
|
+
</Checkbox>
|
408
|
+
)
|
409
|
+
}),
|
357
410
|
{
|
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/step-form.tsx
CHANGED
@@ -53,7 +53,7 @@ export const StepForm = React.forwardRef(
|
|
53
53
|
)
|
54
54
|
}
|
55
55
|
) as <TFieldValues extends FieldValues>(
|
56
|
-
props:
|
56
|
+
props: StepFormProps<TFieldValues> & {
|
57
57
|
ref?: React.ForwardedRef<UseFormReturn<TFieldValues>>
|
58
58
|
}
|
59
59
|
) => React.ReactElement
|
package/src/use-step-form.tsx
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import * as React from 'react'
|
2
2
|
import { FieldValues, SubmitHandler } from 'react-hook-form'
|
3
|
-
import { createContext } from '@chakra-ui/react-utils'
|
3
|
+
import { createContext, MaybeRenderProp } from '@chakra-ui/react-utils'
|
4
4
|
import {
|
5
5
|
useStepper,
|
6
6
|
useStep,
|
@@ -34,9 +34,7 @@ export interface UseStepFormProps<
|
|
34
34
|
TFieldValues extends FieldValues = FieldValues
|
35
35
|
> extends Omit<UseStepperProps, 'onChange'>,
|
36
36
|
Omit<FormProps<TFieldValues>, 'children'> {
|
37
|
-
children:
|
38
|
-
| React.ReactNode
|
39
|
-
| ((stepper: UseStepFormReturn<TFieldValues>) => React.ReactElement)
|
37
|
+
children: MaybeRenderProp<UseStepFormReturn<TFieldValues>>
|
40
38
|
}
|
41
39
|
|
42
40
|
export interface UseStepFormReturn<
|