@saas-ui/forms 0.3.3 → 0.5.0

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.
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ import { FieldValues, UseFormReturn } from 'react-hook-form';
3
+ import { HTMLChakraProps } from '@chakra-ui/system';
4
+ import { StepperStepsProps } from '@saas-ui/stepper';
5
+ import { ButtonProps } from '@saas-ui/button';
6
+ import { FormProps } from '.';
7
+ import { UseStepFormProps } from './use-step-form';
8
+ export interface StepFormProps<TFieldValues extends FieldValues = FieldValues> extends UseStepFormProps<TFieldValues>, FormProps<TFieldValues> {
9
+ }
10
+ export declare const StepForm: <TFieldValues extends FieldValues>(props: FormProps<TFieldValues> & {
11
+ ref?: React.ForwardedRef<UseFormReturn<TFieldValues, object>> | undefined;
12
+ }) => React.ReactElement;
13
+ export interface FormStepOptions {
14
+ /**
15
+ * The step name
16
+ */
17
+ name: string;
18
+ /**
19
+ * Schema
20
+ */
21
+ schema?: any;
22
+ }
23
+ export declare const FormStepper: React.FC<StepperStepsProps>;
24
+ export interface FormStepProps extends FormStepOptions, HTMLChakraProps<'div'> {
25
+ }
26
+ export declare const FormStep: React.FC<FormStepProps>;
27
+ export declare const PrevButton: React.FC<ButtonProps>;
28
+ export interface NextButtonProps extends ButtonProps {
29
+ submitLabel?: string;
30
+ }
31
+ export declare const NextButton: React.FC<NextButtonProps>;
32
+ //# sourceMappingURL=step-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-form.d.ts","sourceRoot":"","sources":["../src/step-form.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE5D,OAAO,EAEL,eAAe,EAGhB,MAAM,mBAAmB,CAAA;AAI1B,OAAO,EAGL,iBAAiB,EAGlB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAU,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAErD,OAAO,EAAQ,SAAS,EAAgB,MAAM,GAAG,CAAA;AAEjD,OAAO,EAIL,gBAAgB,EACjB,MAAM,iBAAiB,CAAA;AAExB,MAAM,WAAW,aAAa,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,CAC3E,SAAQ,gBAAgB,CAAC,YAAY,CAAC,EACpC,SAAS,CAAC,YAAY,CAAC;CAAG;AAE9B,eAAO,MAAM,QAAQ;;MA2BhB,MAAM,YAAY,CAAA;AAEvB,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,MAAM,CAAC,EAAE,GAAG,CAAA;CACb;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA4BnD,CAAA;AAED,MAAM,WAAW,aACf,SAAQ,eAAe,EACrB,eAAe,CAAC,KAAK,CAAC;CAAG;AAE7B,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAW5C,CAAA;AAMD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAY5C,CAAA;AAMD,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAYhD,CAAA"}
@@ -0,0 +1,44 @@
1
+ import * as React from 'react';
2
+ import { FieldValues, SubmitHandler } from 'react-hook-form';
3
+ import { UseStepperProps, UseStepperReturn } from '@saas-ui/stepper';
4
+ export interface StepState {
5
+ name: string;
6
+ schema?: any;
7
+ isActive?: boolean;
8
+ isCompleted?: boolean;
9
+ }
10
+ export interface StepFormContext extends UseStepperReturn {
11
+ updateStep(state: StepState): void;
12
+ steps: Record<string, StepState>;
13
+ }
14
+ export declare const StepFormProvider: React.Provider<StepFormContext>, useStepFormContext: () => StepFormContext;
15
+ import { FormProps } from './form';
16
+ export interface UseStepFormProps<TFieldValues extends FieldValues = FieldValues> extends UseStepperProps, FormProps<TFieldValues> {
17
+ }
18
+ export declare function useStepForm<TFieldValues extends FieldValues = FieldValues>(props: UseStepFormProps<TFieldValues>): {
19
+ stepsRef: React.MutableRefObject<string[]>;
20
+ activeStep: string;
21
+ activeIndex: number;
22
+ isFirstStep: boolean;
23
+ isLastStep: boolean;
24
+ isCompleted: boolean;
25
+ setIndex: React.Dispatch<React.SetStateAction<number>>;
26
+ setStep: (name: string) => void;
27
+ nextStep: () => void;
28
+ prevStep: () => void;
29
+ registerStep: (name: string) => void;
30
+ unregisterStep: (name: string) => void;
31
+ getFormProps: (props: any) => {
32
+ onSubmit: SubmitHandler<TFieldValues>;
33
+ schema: any;
34
+ };
35
+ updateStep: (step: any) => void;
36
+ steps: {};
37
+ };
38
+ export declare type UseStepFormReturn = ReturnType<typeof useStepForm>;
39
+ export interface UseFormStepProps {
40
+ name: string;
41
+ schema?: any;
42
+ }
43
+ export declare function useFormStep(props: UseFormStepProps): StepState;
44
+ //# sourceMappingURL=use-step-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-step-form.d.ts","sourceRoot":"","sources":["../src/use-step-form.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE5D,OAAO,EAGL,eAAe,EACf,gBAAgB,EACjB,MAAM,kBAAkB,CAAA;AAEzB,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,GAAG,CAAA;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;CACjC;AAED,eAAO,MAAO,gBAAgB,mCAAE,kBAAkB,uBAK9C,CAAA;AAEJ,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAElC,MAAM,WAAW,gBAAgB,CAC/B,YAAY,SAAS,WAAW,GAAG,WAAW,CAC9C,SAAQ,eAAe,EACrB,SAAS,CAAC,YAAY,CAAC;CAAG;AAE9B,wBAAgB,WAAW,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,EACxE,KAAK,EAAE,gBAAgB,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;EAyDtC;AAED,oBAAY,iBAAiB,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAA;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,GAAG,CAAA;CACb;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAc9D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saas-ui/forms",
3
- "version": "0.3.3",
3
+ "version": "0.5.0",
4
4
  "description": "Theme and components agnostic SaasProvider",
5
5
  "source": "src/index.ts",
6
6
  "exports": {
@@ -65,13 +65,14 @@
65
65
  "@chakra-ui/icons": "^1.1.1",
66
66
  "@chakra-ui/react-utils": "^1.2.1",
67
67
  "@hookform/resolvers": "^2.8.3",
68
- "@saas-ui/button": "0.2.1",
69
- "@saas-ui/input-right-button": "0.2.1",
70
- "@saas-ui/number-input": "0.2.2",
71
- "@saas-ui/password-input": "0.2.1",
72
- "@saas-ui/pin-input": "0.2.1",
73
- "@saas-ui/radio": "0.2.1",
74
- "@saas-ui/select": "0.2.2",
68
+ "@saas-ui/button": "0.3.0",
69
+ "@saas-ui/input-right-button": "0.3.0",
70
+ "@saas-ui/number-input": "0.3.0",
71
+ "@saas-ui/password-input": "0.3.0",
72
+ "@saas-ui/pin-input": "0.3.0",
73
+ "@saas-ui/radio": "0.3.0",
74
+ "@saas-ui/react-utils": "0.1.0",
75
+ "@saas-ui/select": "0.3.0",
75
76
  "react-hook-form": "^7.22.0"
76
77
  },
77
78
  "peerDependencies": {
@@ -20,12 +20,17 @@ export const DisplayField: React.FC<DisplayFieldProps> = ({
20
20
  placeholder,
21
21
  ...props
22
22
  }) => {
23
- const { getValues } = useFormContext()
24
-
25
23
  return (
26
24
  <FormControl {...props}>
27
25
  {label ? <FormLabel htmlFor={name}>{label}</FormLabel> : null}
28
- <Text fontSize="md">{getValues(name)}</Text>
26
+ <Text fontSize="md">
27
+ <FormValue name={name} />
28
+ </Text>
29
29
  </FormControl>
30
30
  )
31
31
  }
32
+
33
+ export const FormValue: React.FC<{ name: string }> = ({ name }) => {
34
+ const { getValues } = useFormContext()
35
+ return getValues(name) || null
36
+ }
package/src/index.ts CHANGED
@@ -9,6 +9,8 @@ export * from './array-field'
9
9
  export * from './use-array-field'
10
10
  export * from './object-field'
11
11
  export * from './display-if'
12
+ export * from './step-form'
13
+ export * from './use-step-form'
12
14
 
13
15
  export * from '@saas-ui/input-right-button'
14
16
 
@@ -0,0 +1,165 @@
1
+ import * as React from 'react'
2
+
3
+ import { FieldValues, UseFormReturn } from 'react-hook-form'
4
+
5
+ import {
6
+ chakra,
7
+ HTMLChakraProps,
8
+ useMultiStyleConfig,
9
+ StylesProvider,
10
+ } from '@chakra-ui/system'
11
+
12
+ import { callAllHandlers, runIfFn, cx, __DEV__ } from '@chakra-ui/utils'
13
+
14
+ import {
15
+ StepperProvider,
16
+ StepperSteps,
17
+ StepperStepsProps,
18
+ StepperStep,
19
+ useStepperContext,
20
+ } from '@saas-ui/stepper'
21
+ import { Button, ButtonProps } from '@saas-ui/button'
22
+
23
+ import { Form, FormProps, SubmitButton } from '.'
24
+
25
+ import {
26
+ useStepForm,
27
+ useFormStep,
28
+ StepFormProvider,
29
+ UseStepFormProps,
30
+ } from './use-step-form'
31
+
32
+ export interface StepFormProps<TFieldValues extends FieldValues = FieldValues>
33
+ extends UseStepFormProps<TFieldValues>,
34
+ FormProps<TFieldValues> {}
35
+
36
+ export const StepForm = React.forwardRef(
37
+ <TFieldValues extends FieldValues = FieldValues>(
38
+ props: StepFormProps<TFieldValues>,
39
+ ref: React.ForwardedRef<UseFormReturn<TFieldValues>>
40
+ ) => {
41
+ const { children, onSubmit, ...rest } = props
42
+
43
+ const stepper = useStepForm<TFieldValues>(props)
44
+
45
+ const { getFormProps, ...ctx } = stepper
46
+
47
+ const context = React.useMemo(() => ctx, [ctx])
48
+
49
+ return (
50
+ <StepperProvider value={context}>
51
+ <StepFormProvider value={context}>
52
+ <Form ref={ref} {...rest} {...getFormProps(props)}>
53
+ {runIfFn(children, stepper)}
54
+ </Form>
55
+ </StepFormProvider>
56
+ </StepperProvider>
57
+ )
58
+ }
59
+ ) as <TFieldValues extends FieldValues>(
60
+ props: FormProps<TFieldValues> & {
61
+ ref?: React.ForwardedRef<UseFormReturn<TFieldValues>>
62
+ }
63
+ ) => React.ReactElement
64
+
65
+ export interface FormStepOptions {
66
+ /**
67
+ * The step name
68
+ */
69
+ name: string
70
+ /**
71
+ * Schema
72
+ */
73
+ schema?: any
74
+ }
75
+
76
+ export const FormStepper: React.FC<StepperStepsProps> = (props) => {
77
+ const styles = useMultiStyleConfig('Stepper', props)
78
+
79
+ const { children } = props
80
+
81
+ const elements = React.Children.map(children, (child) => {
82
+ if (React.isValidElement(child) && child?.type === FormStep) {
83
+ const { isCompleted } = useFormStep(child.props) // Register this step
84
+ return (
85
+ <StepperStep
86
+ name={child.props.name}
87
+ title={child.props.title}
88
+ isCompleted={isCompleted}
89
+ >
90
+ {child.props.children}
91
+ </StepperStep>
92
+ )
93
+ }
94
+ return child
95
+ })
96
+
97
+ return (
98
+ <StylesProvider value={styles}>
99
+ <StepperSteps mb="4" {...props}>
100
+ {elements}
101
+ </StepperSteps>
102
+ </StylesProvider>
103
+ )
104
+ }
105
+
106
+ export interface FormStepProps
107
+ extends FormStepOptions,
108
+ HTMLChakraProps<'div'> {}
109
+
110
+ export const FormStep: React.FC<FormStepProps> = (props) => {
111
+ const { name, schema, children, className, ...rest } = props
112
+ const step = useFormStep({ name, schema })
113
+
114
+ const { isActive } = step
115
+
116
+ return isActive ? (
117
+ <chakra.div {...rest} className={cx('saas-form__step', className)}>
118
+ {children}
119
+ </chakra.div>
120
+ ) : null
121
+ }
122
+
123
+ if (__DEV__) {
124
+ FormStep.displayName = 'FormStep'
125
+ }
126
+
127
+ export const PrevButton: React.FC<ButtonProps> = (props) => {
128
+ const { isFirstStep, isCompleted, prevStep } = useStepperContext()
129
+
130
+ return (
131
+ <Button
132
+ isDisabled={isFirstStep || isCompleted}
133
+ label="Back"
134
+ {...props}
135
+ className={cx('saas-form__prev-button', props.className)}
136
+ onClick={callAllHandlers(props.onClick, prevStep)}
137
+ />
138
+ )
139
+ }
140
+
141
+ if (__DEV__) {
142
+ PrevButton.displayName = 'PrevButton'
143
+ }
144
+
145
+ export interface NextButtonProps extends ButtonProps {
146
+ submitLabel?: string
147
+ }
148
+
149
+ export const NextButton: React.FC<NextButtonProps> = (props) => {
150
+ const { label = 'Next', submitLabel = 'Complete', ...rest } = props
151
+ const { isLastStep, isCompleted } = useStepperContext()
152
+
153
+ return (
154
+ <SubmitButton
155
+ isDisabled={isCompleted}
156
+ label={isLastStep || isCompleted ? submitLabel : label}
157
+ {...rest}
158
+ className={cx('saas-form__next-button', props.className)}
159
+ />
160
+ )
161
+ }
162
+
163
+ if (__DEV__) {
164
+ NextButton.displayName = 'NextButton'
165
+ }
@@ -0,0 +1,118 @@
1
+ import * as React from 'react'
2
+ import { FieldValues, SubmitHandler } from 'react-hook-form'
3
+ import { createContext } from '@chakra-ui/react-utils'
4
+ import {
5
+ useStepper,
6
+ useStep,
7
+ UseStepperProps,
8
+ UseStepperReturn,
9
+ } from '@saas-ui/stepper'
10
+
11
+ export interface StepState {
12
+ name: string
13
+ schema?: any
14
+ isActive?: boolean
15
+ isCompleted?: boolean
16
+ }
17
+
18
+ export interface StepFormContext extends UseStepperReturn {
19
+ updateStep(state: StepState): void
20
+ steps: Record<string, StepState>
21
+ }
22
+
23
+ export const [StepFormProvider, useStepFormContext] =
24
+ createContext<StepFormContext>({
25
+ name: 'StepFormContext',
26
+ errorMessage:
27
+ 'useStepFormContext: `context` is undefined. Seems you forgot to wrap step form components in `<StepForm />`',
28
+ })
29
+
30
+ import { FormProps } from './form'
31
+
32
+ export interface UseStepFormProps<
33
+ TFieldValues extends FieldValues = FieldValues
34
+ > extends UseStepperProps,
35
+ FormProps<TFieldValues> {}
36
+
37
+ export function useStepForm<TFieldValues extends FieldValues = FieldValues>(
38
+ props: UseStepFormProps<TFieldValues>
39
+ ) {
40
+ const stepper = useStepper(props)
41
+
42
+ const { activeStep, isLastStep, nextStep } = stepper
43
+
44
+ const [steps, updateSteps] = React.useState({})
45
+
46
+ const onSubmitStep: SubmitHandler<TFieldValues> = React.useCallback(
47
+ async (data) => {
48
+ if (isLastStep) {
49
+ return props
50
+ .onSubmit?.(data)
51
+ .then(() => {
52
+ const step = steps[activeStep]
53
+ updateStep({
54
+ ...step,
55
+ isCompleted: true,
56
+ })
57
+ })
58
+ .then(nextStep) // Show completed step
59
+ }
60
+
61
+ nextStep()
62
+ },
63
+ [activeStep, isLastStep]
64
+ )
65
+
66
+ const getFormProps = React.useCallback(
67
+ (props) => {
68
+ const step = steps[activeStep]
69
+ return {
70
+ onSubmit: onSubmitStep,
71
+ schema: step?.schema,
72
+ }
73
+ },
74
+ [steps, onSubmitStep, activeStep]
75
+ )
76
+
77
+ const updateStep = React.useCallback(
78
+ (step) => {
79
+ updateSteps((steps) => {
80
+ return {
81
+ ...steps,
82
+ [step.name]: step,
83
+ }
84
+ })
85
+ },
86
+ [steps]
87
+ )
88
+
89
+ return {
90
+ getFormProps,
91
+ updateStep,
92
+ steps,
93
+ ...stepper,
94
+ }
95
+ }
96
+
97
+ export type UseStepFormReturn = ReturnType<typeof useStepForm>
98
+
99
+ export interface UseFormStepProps {
100
+ name: string
101
+ schema?: any
102
+ }
103
+
104
+ export function useFormStep(props: UseFormStepProps): StepState {
105
+ const { name, schema } = props
106
+ const step = useStep({ name })
107
+
108
+ const { steps, updateStep } = useStepFormContext()
109
+
110
+ React.useEffect(() => {
111
+ updateStep({ name, schema })
112
+ }, [name, schema])
113
+
114
+ return {
115
+ ...step,
116
+ ...(steps[name] || {}),
117
+ }
118
+ }