@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/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
- import { PinInput } from '@saas-ui/pin-input'
32
- import { Select, NativeSelect } from '@saas-ui/select'
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?: FieldTypes
104
+ type?: string
108
105
  /**
109
106
  * The input placeholder
110
107
  */
111
108
  placeholder?: string
112
109
  }
113
110
 
114
- const inputTypes: Record<FieldTypes, any> = {}
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 const Field = forwardRef(
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
- [key: string]: unknown
178
- } & {
179
- ref?: React.ForwardedRef<FocusableElement>
180
- }
181
- ) => React.ReactElement
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<FieldProps, typeof FormControl>((props, ref) => {
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<any>,
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 const InputField = registerFieldType(
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
- return <Input type={type} {...rest} ref={ref} />
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
- export const NumberInputField = registerFieldType('number', NumberInput, {
330
- isControlled: true,
331
- })
332
- export const PasswordInputFIeld = registerFieldType('password', PasswordInput)
333
- export const TextareaField = registerFieldType('textarea', Textarea)
334
- export const SwitchField = registerFieldType(
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
- export const SelectField = registerFieldType('select', Select, {
396
+
397
+ export const SelectField = registerFieldType<SelectProps>('select', Select, {
344
398
  isControlled: true,
345
399
  })
346
- export const CheckboxField = registerFieldType(
400
+
401
+ export const CheckboxField = registerFieldType<CheckboxProps>(
347
402
  'checkbox',
348
- forwardRef(
349
- ({ label, type, ...props }: { label?: string; type: string }, ref) => {
350
- return (
351
- <Checkbox ref={ref} {...props}>
352
- {label}
353
- </Checkbox>
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
- 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(
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
@@ -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
 
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: FormProps<TFieldValues> & {
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 step={activeIndex} onChange={onChange}>
102
+ <StepperContainer
103
+ orientation={orientation}
104
+ step={activeIndex}
105
+ onChange={onChange}
106
+ >
103
107
  <StepperSteps mb="4" {...props}>
104
108
  {elements}
105
109
  </StepperSteps>
@@ -37,7 +37,7 @@ export const SubmitButton = forwardRef<SubmitButtonProps, 'button'>(
37
37
  <Button
38
38
  type="submit"
39
39
  isLoading={formState.isSubmitting}
40
- isPrimary
40
+ colorScheme="primary"
41
41
  ref={ref}
42
42
  isDisabled={isDisabled}
43
43
  {...rest}
@@ -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<