@saas-ui/forms 1.0.0-rc.6 → 1.0.0-rc.9
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 +37 -0
- package/README.md +29 -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 +10 -10
- package/src/array-field.tsx +21 -22
- package/src/field.tsx +185 -70
- package/src/form.tsx +0 -1
- package/src/step-form.tsx +7 -3
- package/src/submit-button.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
|
@@ -76,7 +76,7 @@ export interface FormStepOptions {
|
|
76
76
|
export const FormStepper: React.FC<StepperStepsProps> = (props) => {
|
77
77
|
const { activeIndex, setIndex } = useStepperContext()
|
78
78
|
|
79
|
-
const { children } = props
|
79
|
+
const { children, orientation } = props
|
80
80
|
|
81
81
|
const elements = React.Children.map(children, (child) => {
|
82
82
|
if (React.isValidElement(child) && child?.type === FormStep) {
|
@@ -99,7 +99,11 @@ export const FormStepper: React.FC<StepperStepsProps> = (props) => {
|
|
99
99
|
}, [])
|
100
100
|
|
101
101
|
return (
|
102
|
-
<StepperContainer
|
102
|
+
<StepperContainer
|
103
|
+
orientation={orientation}
|
104
|
+
step={activeIndex}
|
105
|
+
onChange={onChange}
|
106
|
+
>
|
103
107
|
<StepperSteps mb="4" {...props}>
|
104
108
|
{elements}
|
105
109
|
</StepperSteps>
|
package/src/submit-button.tsx
CHANGED
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<
|