@saas-ui/forms 1.0.0-rc.9 → 1.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saas-ui/forms",
3
- "version": "1.0.0-rc.9",
3
+ "version": "1.0.2",
4
4
  "description": "Fully functional forms for Chakra UI.",
5
5
  "source": "src/index.ts",
6
6
  "exports": {
@@ -93,16 +93,16 @@
93
93
  "@chakra-ui/react-utils": "^2.0.1",
94
94
  "@chakra-ui/utils": "^2.0.2",
95
95
  "@hookform/resolvers": "^2.9.0",
96
- "@saas-ui/button": "1.0.0-rc.4",
97
- "@saas-ui/input-right-button": "1.0.0-rc.4",
98
- "@saas-ui/number-input": "1.0.0-rc.3",
99
- "@saas-ui/password-input": "1.0.0-rc.4",
100
- "@saas-ui/pin-input": "1.0.0-rc.4",
101
- "@saas-ui/radio": "1.0.0-rc.3",
102
- "@saas-ui/react-utils": "1.0.0-rc.3",
103
- "@saas-ui/select": "1.0.0-rc.3",
104
- "@saas-ui/stepper": "1.0.0-rc.4",
105
- "react-hook-form": "^7.31.3"
96
+ "@saas-ui/button": "1.0.0",
97
+ "@saas-ui/input-right-button": "1.0.1",
98
+ "@saas-ui/number-input": "1.0.0",
99
+ "@saas-ui/password-input": "1.0.1",
100
+ "@saas-ui/pin-input": "1.0.0",
101
+ "@saas-ui/radio": "1.0.0",
102
+ "@saas-ui/react-utils": "1.0.0",
103
+ "@saas-ui/select": "1.0.0",
104
+ "@saas-ui/stepper": "1.0.0",
105
+ "react-hook-form": "^7.33.1"
106
106
  },
107
107
  "peerDependencies": {
108
108
  "@chakra-ui/react": ">=2.1.0",
package/src/auto-form.tsx CHANGED
@@ -6,7 +6,7 @@ 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, SubmitButtonProps } from './submit-button'
9
+ import { SubmitButton } from './submit-button'
10
10
  import { FieldResolver } from '.'
11
11
 
12
12
  interface AutoFormOptions {
@@ -48,7 +48,7 @@ export const AutoForm = forwardRef(
48
48
  <Form {...rest} schema={schema} ref={ref}>
49
49
  <FormLayout>
50
50
  {<Fields schema={schema} fieldResolver={fieldResolver} />}
51
- {submitLabel && <SubmitButton label={submitLabel} />}
51
+ {submitLabel && <SubmitButton>{submitLabel}</SubmitButton>}
52
52
  {children}
53
53
  </FormLayout>
54
54
  </Form>
@@ -16,10 +16,11 @@ export type ObjectSchema = Record<string, SchemaField>
16
16
 
17
17
  const mapFields = (schema: ObjectSchema): FieldProps[] =>
18
18
  schema &&
19
- Object.entries(schema).map(([name, { items, ...field }]) => {
19
+ Object.entries(schema).map(([name, { items, label, title, ...field }]) => {
20
20
  return {
21
21
  ...field,
22
22
  name,
23
+ label: label || title, // json schema compatibility
23
24
  }
24
25
  })
25
26
 
package/src/field.tsx CHANGED
@@ -27,14 +27,13 @@ import {
27
27
  TextareaProps,
28
28
  SwitchProps,
29
29
  CheckboxProps,
30
- PinInputProps,
31
30
  PinInputField,
32
31
  HStack,
33
32
  PinInput,
34
33
  UsePinInputProps,
35
34
  SystemProps,
36
35
  } from '@chakra-ui/react'
37
- import { __DEV__ } from '@chakra-ui/utils'
36
+ import { __DEV__, FocusableElement } from '@chakra-ui/utils'
38
37
 
39
38
  import { NumberInput, NumberInputProps } from '@saas-ui/number-input'
40
39
  import { PasswordInput, PasswordInputProps } from '@saas-ui/password-input'
@@ -46,7 +45,6 @@ import {
46
45
  NativeSelect,
47
46
  NativeSelectProps,
48
47
  } from '@saas-ui/select'
49
- import { FocusableElement } from '@chakra-ui/utils'
50
48
 
51
49
  export interface Option {
52
50
  value: string
@@ -217,7 +215,6 @@ const createField = (
217
215
  isInvalid,
218
216
  isReadOnly,
219
217
  isRequired,
220
- isOptional,
221
218
  rules,
222
219
  variant,
223
220
  ...inputProps
@@ -239,7 +236,6 @@ const createField = (
239
236
  isInvalid={isInvalid}
240
237
  isReadOnly={isReadOnly}
241
238
  isRequired={isRequired}
242
- isOptional={isOptional}
243
239
  variant={variant}
244
240
  >
245
241
  <InputComponent
package/src/fields.tsx CHANGED
@@ -8,10 +8,12 @@ import { Field, FieldProps } from './field'
8
8
  import { ArrayField } from './array-field'
9
9
  import { ObjectField } from './object-field'
10
10
  import { FieldResolver } from './field-resolver'
11
+ import { useFormContext } from 'react-hook-form'
11
12
 
12
13
  export interface FieldsProps {
13
14
  schema: any
14
15
  fieldResolver?: FieldResolver
16
+ focusFirstField?: boolean
15
17
  }
16
18
 
17
19
  const mapNestedFields = (resolver: FieldResolver, name: string) => {
@@ -27,6 +29,7 @@ const mapNestedFields = (resolver: FieldResolver, name: string) => {
27
29
  export const Fields: React.FC<FieldsProps> = ({
28
30
  schema,
29
31
  fieldResolver,
32
+ focusFirstField,
30
33
  ...props
31
34
  }) => {
32
35
  const resolver = React.useMemo(
@@ -34,34 +37,42 @@ export const Fields: React.FC<FieldsProps> = ({
34
37
  [schema, fieldResolver]
35
38
  )
36
39
 
40
+ const fields = React.useMemo(() => resolver.getFields(), [resolver])
41
+
42
+ const form = useFormContext()
43
+
44
+ React.useEffect(() => {
45
+ if (focusFirstField && fields[0]?.name) {
46
+ form.setFocus(fields[0].name)
47
+ }
48
+ }, [schema, fieldResolver, focusFirstField])
49
+
37
50
  return (
38
51
  <FormLayout {...props}>
39
- {resolver
40
- .getFields()
41
- .map(
42
- ({
43
- name,
44
- type,
45
- defaultValue,
46
- ...fieldProps
47
- }: FieldProps): React.ReactNode => {
48
- if (type === 'array') {
49
- return (
50
- <ArrayField key={name} name={name} {...fieldProps}>
51
- {mapNestedFields(resolver, name)}
52
- </ArrayField>
53
- )
54
- } else if (type === 'object') {
55
- return (
56
- <ObjectField key={name} name={name} {...fieldProps}>
57
- {mapNestedFields(resolver, name)}
58
- </ObjectField>
59
- )
60
- }
61
-
62
- return <Field key={name} name={name} type={type} {...fieldProps} />
52
+ {fields.map(
53
+ ({
54
+ name,
55
+ type,
56
+ defaultValue,
57
+ ...fieldProps
58
+ }: FieldProps): React.ReactNode => {
59
+ if (type === 'array') {
60
+ return (
61
+ <ArrayField key={name} name={name} {...fieldProps}>
62
+ {mapNestedFields(resolver, name)}
63
+ </ArrayField>
64
+ )
65
+ } else if (type === 'object') {
66
+ return (
67
+ <ObjectField key={name} name={name} {...fieldProps}>
68
+ {mapNestedFields(resolver, name)}
69
+ </ObjectField>
70
+ )
63
71
  }
64
- )}
72
+
73
+ return <Field key={name} name={name} type={type} {...fieldProps} />
74
+ }
75
+ )}
65
76
  </FormLayout>
66
77
  )
67
78
  }
package/src/form.tsx CHANGED
@@ -11,7 +11,6 @@ import {
11
11
  FieldValues,
12
12
  SubmitHandler,
13
13
  SubmitErrorHandler,
14
- UnpackNestedValue,
15
14
  ResolverOptions,
16
15
  ResolverResult,
17
16
  } from 'react-hook-form'
@@ -35,7 +34,7 @@ interface FormOptions<TFieldValues extends FieldValues = FieldValues> {
35
34
  /**
36
35
  * Ref on the HTMLFormElement.
37
36
  */
38
- formRef?: React.MutableRefObject<HTMLFormElement>
37
+ formRef?: React.RefObject<HTMLFormElement>
39
38
  }
40
39
 
41
40
  /**
@@ -124,7 +123,7 @@ if (__DEV__) {
124
123
  export type GetResolver = (
125
124
  schema: any
126
125
  ) => <TFieldValues extends FieldValues, TContext>(
127
- values: UnpackNestedValue<TFieldValues>,
126
+ values: TFieldValues,
128
127
  context: TContext | undefined,
129
128
  options: ResolverOptions<TFieldValues>
130
129
  ) => Promise<ResolverResult<TFieldValues>>
package/src/step-form.tsx CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  } from '@saas-ui/stepper'
17
17
  import { Button, ButtonProps } from '@saas-ui/button'
18
18
 
19
- import { Form, FormProps } from './form'
19
+ import { Form } from './form'
20
20
  import { SubmitButton } from './submit-button'
21
21
 
22
22
  import {
@@ -24,6 +24,7 @@ import {
24
24
  useFormStep,
25
25
  StepFormProvider,
26
26
  UseStepFormProps,
27
+ FormStepSubmitHandler,
27
28
  } from './use-step-form'
28
29
 
29
30
  export interface StepFormProps<TFieldValues extends FieldValues = FieldValues>
@@ -113,11 +114,14 @@ export const FormStepper: React.FC<StepperStepsProps> = (props) => {
113
114
 
114
115
  export interface FormStepProps
115
116
  extends FormStepOptions,
116
- HTMLChakraProps<'div'> {}
117
+ Omit<HTMLChakraProps<'div'>, 'onSubmit'> {
118
+ onSubmit?: FormStepSubmitHandler
119
+ }
117
120
 
118
121
  export const FormStep: React.FC<FormStepProps> = (props) => {
119
- const { name, schema, resolver, children, className, ...rest } = props
120
- const step = useFormStep({ name, schema, resolver })
122
+ const { name, schema, resolver, children, className, onSubmit, ...rest } =
123
+ props
124
+ const step = useFormStep({ name, schema, resolver, onSubmit })
121
125
 
122
126
  const { isActive } = step
123
127
 
@@ -160,11 +164,12 @@ export const NextButton: React.FC<NextButtonProps> = (props) => {
160
164
 
161
165
  return (
162
166
  <SubmitButton
163
- isDisabled={isCompleted}
164
- label={isLastStep || isCompleted ? submitLabel : label}
165
167
  {...rest}
168
+ isDisabled={isCompleted}
166
169
  className={cx('saas-form__next-button', props.className)}
167
- />
170
+ >
171
+ {isLastStep || isCompleted ? submitLabel : label}
172
+ </SubmitButton>
168
173
  )
169
174
  }
170
175
 
@@ -4,7 +4,6 @@ import { useFormContext } from 'react-hook-form'
4
4
 
5
5
  import { Button, ButtonProps } from '@saas-ui/button'
6
6
 
7
- import { forwardRef } from '@chakra-ui/system'
8
7
  import { __DEV__ } from '@chakra-ui/utils'
9
8
 
10
9
  export interface SubmitButtonProps extends ButtonProps {
@@ -24,29 +23,38 @@ export interface SubmitButtonProps extends ButtonProps {
24
23
  disableIfInvalid?: boolean
25
24
  }
26
25
 
27
- export const SubmitButton = forwardRef<SubmitButtonProps, 'button'>(
28
- (props, ref) => {
29
- const { children, disableIfUntouched, disableIfInvalid, ...rest } = props
30
- const { formState } = useFormContext()
31
-
32
- const isDisabled =
33
- (disableIfUntouched && !formState.isDirty) ||
34
- (disableIfInvalid && !formState.isValid)
35
-
36
- return (
37
- <Button
38
- type="submit"
39
- isLoading={formState.isSubmitting}
40
- colorScheme="primary"
41
- ref={ref}
42
- isDisabled={isDisabled}
43
- {...rest}
44
- >
45
- {children}
46
- </Button>
47
- )
48
- }
49
- )
26
+ export const SubmitButton = React.forwardRef<
27
+ HTMLButtonElement,
28
+ SubmitButtonProps
29
+ >((props, ref) => {
30
+ const {
31
+ children,
32
+ disableIfUntouched,
33
+ disableIfInvalid,
34
+ isDisabled: isDisabledProp,
35
+ isLoading,
36
+ ...rest
37
+ } = props
38
+ const { formState } = useFormContext()
39
+
40
+ const isDisabled =
41
+ (disableIfUntouched && !formState.isDirty) ||
42
+ (disableIfInvalid && !formState.isValid) ||
43
+ isDisabledProp
44
+
45
+ return (
46
+ <Button
47
+ {...rest}
48
+ ref={ref}
49
+ variant="primary"
50
+ type="submit"
51
+ isLoading={formState.isSubmitting || isLoading}
52
+ isDisabled={isDisabled}
53
+ >
54
+ {children}
55
+ </Button>
56
+ )
57
+ })
50
58
 
51
59
  SubmitButton.defaultProps = {
52
60
  label: 'Submit',
@@ -14,8 +14,13 @@ export interface StepState {
14
14
  resolver?: any
15
15
  isActive?: boolean
16
16
  isCompleted?: boolean
17
+ onSubmit?: FormStepSubmitHandler
17
18
  }
18
19
 
20
+ export type FormStepSubmitHandler<
21
+ TFieldValues extends FieldValues = FieldValues
22
+ > = (data: TFieldValues, stepper: UseStepperReturn) => Promise<void>
23
+
19
24
  export interface StepFormContext extends UseStepperReturn {
20
25
  updateStep(state: StepState): void
21
26
  steps: Record<string, StepState>
@@ -61,11 +66,12 @@ export function useStepForm<TFieldValues extends FieldValues = FieldValues>(
61
66
 
62
67
  const onSubmitStep: SubmitHandler<TFieldValues> = React.useCallback(
63
68
  async (data) => {
69
+ const step = steps[activeStep]
70
+
64
71
  if (isLastStep) {
65
72
  return props
66
73
  .onSubmit?.(data)
67
74
  .then(() => {
68
- const step = steps[activeStep]
69
75
  updateStep({
70
76
  ...step,
71
77
  isCompleted: true,
@@ -74,9 +80,15 @@ export function useStepForm<TFieldValues extends FieldValues = FieldValues>(
74
80
  .then(nextStep) // Show completed step
75
81
  }
76
82
 
77
- nextStep()
83
+ try {
84
+ await step.onSubmit?.(data, stepper)
85
+
86
+ nextStep()
87
+ } catch (e) {
88
+ // Step submission failed.
89
+ }
78
90
  },
79
- [activeStep, isLastStep]
91
+ [steps, activeStep, isLastStep]
80
92
  )
81
93
 
82
94
  const getFormProps = React.useCallback(() => {
@@ -112,16 +124,17 @@ export interface UseFormStepProps {
112
124
  name: string
113
125
  schema?: any
114
126
  resolver?: any
127
+ onSubmit?: FormStepSubmitHandler
115
128
  }
116
129
 
117
130
  export function useFormStep(props: UseFormStepProps): StepState {
118
- const { name, schema, resolver } = props
131
+ const { name, schema, resolver, onSubmit } = props
119
132
  const step = useStep({ name })
120
133
 
121
134
  const { steps, updateStep } = useStepFormContext()
122
135
 
123
136
  React.useEffect(() => {
124
- updateStep({ name, schema, resolver })
137
+ updateStep({ name, schema, resolver, onSubmit })
125
138
  }, [name, schema])
126
139
 
127
140
  return {
package/yup/package.json CHANGED
@@ -10,8 +10,8 @@
10
10
  "author": "Eelco Wiersma <eelco@appulse.nl>",
11
11
  "license": "MIT",
12
12
  "peerDependencies": {
13
- "@hookform/resolvers": "^2.9.0",
14
- "react-hook-form": "^7.31.3",
13
+ "@hookform/resolvers": "^2.9.3",
14
+ "react-hook-form": "^7.33.1",
15
15
  "yup": "^0.32.11"
16
16
  }
17
17
  }
package/zod/package.json CHANGED
@@ -11,8 +11,8 @@
11
11
  "license": "MIT",
12
12
  "peerDependencies": {
13
13
  "@chakra-ui/utils": "^2.0.2",
14
- "@hookform/resolvers": "^2.9.0",
15
- "react-hook-form": "^7.31.3",
14
+ "@hookform/resolvers": "^2.9.3",
15
+ "react-hook-form": "^7.33.1",
16
16
  "zod": "^3.17.3"
17
17
  }
18
18
  }