@saas-ui/forms 2.0.0-next.9 → 2.0.0-rc.23

Sign up to get free protection for your applications and to get access to all the features.
package/src/step-form.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react'
2
2
 
3
- import { FieldValues, UseFormReturn } from 'react-hook-form'
3
+ import { FieldValues } from 'react-hook-form'
4
4
 
5
5
  import {
6
6
  chakra,
@@ -10,76 +10,48 @@ import {
10
10
  ThemingProps,
11
11
  } from '@chakra-ui/react'
12
12
 
13
- import { callAllHandlers, runIfFn, cx } from '@chakra-ui/utils'
13
+ import { callAllHandlers, cx } from '@chakra-ui/utils'
14
14
 
15
15
  import {
16
- StepperProvider,
17
- StepperSteps,
18
- StepperStepsProps,
19
- StepperStep,
16
+ Steps,
17
+ StepsItem,
18
+ StepsItemProps,
19
+ StepsProps,
20
20
  useStepperContext,
21
- StepperContainer,
22
- StepperProps,
23
21
  } from '@saas-ui/core'
24
22
 
25
- import { Form } from './form'
26
23
  import { SubmitButton } from './submit-button'
27
24
 
28
25
  import {
29
- useStepForm,
30
26
  useFormStep,
31
- StepFormProvider,
32
27
  UseStepFormProps,
33
28
  FormStepSubmitHandler,
34
29
  } from './use-step-form'
30
+ import { FieldProps } from './types'
31
+
32
+ export type StepsOptions<TSchema, TName extends string = string> = {
33
+ /**
34
+ * The step name
35
+ */
36
+ name: TName
37
+ /**
38
+ * Schema
39
+ */
40
+ schema?: TSchema
41
+ }[]
35
42
 
36
43
  export interface StepFormProps<
44
+ TSteps extends StepsOptions<any> = StepsOptions<any>,
37
45
  TFieldValues extends FieldValues = FieldValues,
38
- TContext extends object = object
39
- > extends UseStepFormProps<TFieldValues> {}
46
+ TContext extends object = object,
47
+ TFieldTypes = FieldProps<TFieldValues>
48
+ > extends UseStepFormProps<TSteps, TFieldValues, TContext, TFieldTypes> {}
40
49
 
41
- /**
42
- * The wrapper component provides context, state, and focus management.
43
- *
44
- * @see Docs https://saas-ui.dev/docs/components/forms/step-form
45
- */
46
- export const StepForm = React.forwardRef(
47
- <
48
- TFieldValues extends FieldValues = FieldValues,
49
- TContext extends object = object
50
- >(
51
- props: StepFormProps<TFieldValues, TContext>,
52
- ref: React.ForwardedRef<HTMLFormElement>
53
- ) => {
54
- const { children, ...rest } = props
55
-
56
- const stepper = useStepForm<TFieldValues>(props)
57
-
58
- const { getFormProps, ...ctx } = stepper
59
-
60
- const context = React.useMemo(() => ctx, [ctx])
61
-
62
- return (
63
- <StepperProvider value={context}>
64
- <StepFormProvider value={context}>
65
- <Form ref={ref} {...rest} {...getFormProps()}>
66
- {runIfFn(children, stepper)}
67
- </Form>
68
- </StepFormProvider>
69
- </StepperProvider>
70
- )
71
- }
72
- ) as <TFieldValues extends FieldValues>(
73
- props: StepFormProps<TFieldValues> & {
74
- ref?: React.ForwardedRef<HTMLFormElement>
75
- }
76
- ) => React.ReactElement
77
-
78
- export interface FormStepOptions {
50
+ export interface FormStepOptions<TName extends string = string> {
79
51
  /**
80
52
  * The step name
81
53
  */
82
- name: string
54
+ name: TName
83
55
  /**
84
56
  * Schema
85
57
  */
@@ -90,9 +62,9 @@ export interface FormStepOptions {
90
62
  resolver?: any
91
63
  }
92
64
 
93
- export interface FormStepperProps
94
- extends StepperStepsProps,
95
- ThemingProps<'Stepper'> {}
65
+ export interface FormStepperProps extends StepsProps, ThemingProps<'Stepper'> {
66
+ render?: StepsItemProps['render']
67
+ }
96
68
 
97
69
  /**
98
70
  * Renders a stepper that displays progress above the form.
@@ -102,7 +74,16 @@ export interface FormStepperProps
102
74
  export const FormStepper: React.FC<FormStepperProps> = (props) => {
103
75
  const { activeIndex, setIndex } = useStepperContext()
104
76
 
105
- const { children, orientation, variant, colorScheme, size, ...rest } = props
77
+ const {
78
+ children,
79
+ orientation,
80
+ variant,
81
+ colorScheme,
82
+ size,
83
+ onChange: onChangeProp,
84
+ render,
85
+ ...rest
86
+ } = props
106
87
 
107
88
  const elements = React.Children.map(children, (child) => {
108
89
  if (
@@ -111,14 +92,15 @@ export const FormStepper: React.FC<FormStepperProps> = (props) => {
111
92
  ) {
112
93
  const { isCompleted } = useFormStep(child.props) // Register this step
113
94
  return (
114
- <StepperStep
95
+ <StepsItem
96
+ render={render}
115
97
  name={child.props.name}
116
98
  title={child.props.title}
117
99
  isCompleted={isCompleted}
118
100
  {...rest}
119
101
  >
120
102
  {child.props.children}
121
- </StepperStep>
103
+ </StepsItem>
122
104
  )
123
105
  }
124
106
  return child
@@ -126,10 +108,11 @@ export const FormStepper: React.FC<FormStepperProps> = (props) => {
126
108
 
127
109
  const onChange = React.useCallback((i: number) => {
128
110
  setIndex(i)
111
+ onChangeProp?.(i)
129
112
  }, [])
130
113
 
131
114
  return (
132
- <StepperContainer
115
+ <Steps
133
116
  orientation={orientation}
134
117
  step={activeIndex}
135
118
  variant={variant}
@@ -137,15 +120,13 @@ export const FormStepper: React.FC<FormStepperProps> = (props) => {
137
120
  size={size}
138
121
  onChange={onChange}
139
122
  >
140
- <StepperSteps mb="4" {...props}>
141
- {elements}
142
- </StepperSteps>
143
- </StepperContainer>
123
+ {elements}
124
+ </Steps>
144
125
  )
145
126
  }
146
127
 
147
- export interface FormStepProps
148
- extends FormStepOptions,
128
+ export interface FormStepProps<TName extends string = string>
129
+ extends FormStepOptions<TName>,
149
130
  Omit<HTMLChakraProps<'div'>, 'onSubmit'> {
150
131
  onSubmit?: FormStepSubmitHandler
151
132
  }
@@ -154,10 +135,11 @@ export interface FormStepProps
154
135
  *
155
136
  * @see Docs https://saas-ui.dev/docs/components/forms/step-form
156
137
  */
157
- export const FormStep: React.FC<FormStepProps> = (props) => {
158
- const { name, schema, resolver, children, className, onSubmit, ...rest } =
159
- props
160
- const step = useFormStep({ name, schema, resolver, onSubmit })
138
+ export const FormStep = <TName extends string = string>(
139
+ props: FormStepProps<TName>
140
+ ) => {
141
+ const { name, children, className, onSubmit, ...rest } = props
142
+ const step = useFormStep({ name, onSubmit })
161
143
 
162
144
  const { isActive } = step
163
145
 
@@ -181,7 +163,7 @@ export const PrevButton: React.FC<ButtonProps> = (props) => {
181
163
  return (
182
164
  <Button
183
165
  isDisabled={isFirstStep || isCompleted}
184
- label="Back"
166
+ children="Back"
185
167
  {...props}
186
168
  className={cx('sui-form__prev-button', props.className)}
187
169
  onClick={callAllHandlers(props.onClick, prevStep)}
package/src/types.ts CHANGED
@@ -133,12 +133,17 @@ export type FieldOverrides<
133
133
 
134
134
  export type WithFields<
135
135
  TFormProps extends FormProps<any, any, any, any>,
136
- FieldDefs
137
- > = TFormProps extends FormProps<infer TFieldValues, infer TContext>
136
+ FieldDefs,
137
+ ExtraOverrides = object
138
+ > = TFormProps extends FormProps<
139
+ infer TSchema,
140
+ infer TFieldValues,
141
+ infer TContext
142
+ >
138
143
  ? Omit<TFormProps, 'children' | 'fields'> & {
139
144
  children?: FormChildren<FieldDefs, TFieldValues, TContext>
140
145
  fields?: FieldOverrides<FieldDefs, TFieldValues> & {
141
146
  submit?: SubmitButtonProps
142
- }
147
+ } & ExtraOverrides
143
148
  }
144
149
  : never
@@ -34,12 +34,43 @@ export const [StepFormProvider, useStepFormContext] =
34
34
  })
35
35
 
36
36
  import { FormProps } from './form'
37
+ import { FormStepProps, StepsOptions } from './step-form'
38
+ import { FieldProps } from './types'
39
+ import { FocusableElement } from '@chakra-ui/utils'
40
+ import { DisplayIfProps } from './display-if'
41
+ import { ArrayFieldProps } from './array-field'
42
+ import { UseArrayFieldReturn } from './use-array-field'
43
+ import { ObjectFieldProps } from './object-field'
44
+
45
+ type StepName<T extends { [k: number]: { readonly name: string } }> =
46
+ T[number]['name']
47
+
48
+ interface StepFormRenderContext<
49
+ TSteps extends StepsOptions<any> = StepsOptions<any>,
50
+ TFieldValues extends FieldValues = FieldValues,
51
+ TContext extends object = object,
52
+ TFieldTypes = FieldProps<TFieldValues>
53
+ > extends UseStepFormReturn<TFieldValues> {
54
+ Field: React.FC<TFieldTypes & React.RefAttributes<FocusableElement>>
55
+ FormStep: React.FC<FormStepProps<StepName<TSteps>>>
56
+ DisplayIf: React.FC<DisplayIfProps<TFieldValues>>
57
+ ArrayField: React.FC<
58
+ ArrayFieldProps<TFieldValues> & React.RefAttributes<UseArrayFieldReturn>
59
+ >
60
+ ObjectField: React.FC<ObjectFieldProps<TFieldValues>>
61
+ }
37
62
 
38
63
  export interface UseStepFormProps<
39
- TFieldValues extends FieldValues = FieldValues
64
+ TSteps extends StepsOptions<any> = StepsOptions<any>,
65
+ TFieldValues extends FieldValues = FieldValues,
66
+ TContext extends object = object,
67
+ TFieldTypes = FieldProps<TFieldValues>
40
68
  > extends Omit<UseStepperProps, 'onChange'>,
41
- Omit<FormProps<TFieldValues>, 'children'> {
42
- children: MaybeRenderProp<UseStepFormReturn<TFieldValues>>
69
+ Omit<FormProps<any, TFieldValues, TContext, TFieldTypes>, 'children'> {
70
+ steps?: TSteps
71
+ children: MaybeRenderProp<
72
+ StepFormRenderContext<TSteps, TFieldValues, TContext, TFieldTypes>
73
+ >
43
74
  }
44
75
 
45
76
  export interface UseStepFormReturn<
@@ -54,12 +85,19 @@ export interface UseStepFormReturn<
54
85
  steps: Record<string, any>
55
86
  }
56
87
 
57
- export function useStepForm<TFieldValues extends FieldValues = FieldValues>(
58
- props: UseStepFormProps<TFieldValues>
88
+ export function useStepForm<
89
+ TSteps extends StepsOptions<any> = StepsOptions<any>,
90
+ TFieldValues extends FieldValues = FieldValues,
91
+ TContext extends object = object,
92
+ TFieldTypes = FieldProps<TFieldValues>
93
+ >(
94
+ props: UseStepFormProps<TSteps, TFieldValues, TContext, TFieldTypes>
59
95
  ): UseStepFormReturn<TFieldValues> {
60
- const { onChange, ...rest } = props
96
+ const { onChange, steps: stepsOptions, resolver, ...rest } = props
61
97
  const stepper = useStepper(rest)
62
98
 
99
+ const [options, setOptions] = React.useState<TSteps | undefined>(stepsOptions)
100
+
63
101
  const { activeStep, isLastStep, nextStep } = stepper
64
102
 
65
103
  const [steps, updateSteps] = React.useState<Record<string, StepState>>({})
@@ -93,23 +131,30 @@ export function useStepForm<TFieldValues extends FieldValues = FieldValues>(
93
131
 
94
132
  const getFormProps = React.useCallback(() => {
95
133
  const step = steps[activeStep]
134
+
96
135
  return {
97
136
  onSubmit: onSubmitStep,
98
137
  schema: step?.schema,
99
- resolver: step?.resolver,
138
+ resolver: step?.schema
139
+ ? /* @todo fix resolver type */ (resolver as any)?.(step.schema)
140
+ : undefined,
100
141
  }
101
142
  }, [steps, onSubmitStep, activeStep])
102
143
 
103
144
  const updateStep = React.useCallback(
104
145
  (step: StepState) => {
146
+ const stepOptions = options?.find((s) => s.name === step.name)
105
147
  updateSteps((steps) => {
106
148
  return {
107
149
  ...steps,
108
- [step.name]: step,
150
+ [step.name]: {
151
+ ...step,
152
+ schema: stepOptions?.schema,
153
+ },
109
154
  }
110
155
  })
111
156
  },
112
- [steps]
157
+ [steps, options]
113
158
  )
114
159
 
115
160
  return {