@saas-ui/forms 2.0.0-next.2 → 2.0.0-next.21

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +53 -6
  3. package/dist/ajv/index.d.ts +24 -11
  4. package/dist/ajv/index.js +7 -9
  5. package/dist/ajv/index.js.map +1 -1
  6. package/dist/ajv/index.mjs +7 -10
  7. package/dist/ajv/index.mjs.map +1 -1
  8. package/dist/index.d.ts +519 -280
  9. package/dist/index.js +777 -696
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +756 -676
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/yup/index.d.ts +525 -21
  14. package/dist/yup/index.js +21 -9
  15. package/dist/yup/index.js.map +1 -1
  16. package/dist/yup/index.mjs +21 -10
  17. package/dist/yup/index.mjs.map +1 -1
  18. package/dist/zod/index.d.ts +525 -12
  19. package/dist/zod/index.js +21 -1
  20. package/dist/zod/index.js.map +1 -1
  21. package/dist/zod/index.mjs +21 -3
  22. package/dist/zod/index.mjs.map +1 -1
  23. package/package.json +33 -10
  24. package/src/array-field.tsx +88 -48
  25. package/src/auto-form.tsx +7 -3
  26. package/src/base-field.tsx +54 -0
  27. package/src/create-field.tsx +144 -0
  28. package/src/create-form.tsx +68 -0
  29. package/src/create-step-form.tsx +100 -0
  30. package/src/default-fields.tsx +163 -0
  31. package/src/display-field.tsx +9 -11
  32. package/src/display-if.tsx +20 -13
  33. package/src/field-resolver.ts +10 -8
  34. package/src/field.tsx +18 -445
  35. package/src/fields-context.tsx +23 -0
  36. package/src/fields.tsx +34 -21
  37. package/src/form-context.tsx +84 -0
  38. package/src/form.tsx +77 -55
  39. package/src/index.ts +58 -4
  40. package/src/input-right-button/input-right-button.stories.tsx +1 -1
  41. package/src/input-right-button/input-right-button.tsx +0 -2
  42. package/src/layout.tsx +16 -11
  43. package/src/number-input/number-input.tsx +9 -5
  44. package/src/object-field.tsx +35 -13
  45. package/src/password-input/password-input.stories.tsx +23 -2
  46. package/src/password-input/password-input.tsx +6 -6
  47. package/src/pin-input/pin-input.tsx +1 -5
  48. package/src/radio/radio-input.stories.tsx +1 -1
  49. package/src/radio/radio-input.tsx +12 -10
  50. package/src/select/native-select.tsx +1 -4
  51. package/src/select/select-context.tsx +130 -0
  52. package/src/select/select.stories.tsx +116 -85
  53. package/src/select/select.test.tsx +1 -1
  54. package/src/select/select.tsx +162 -146
  55. package/src/step-form.tsx +76 -76
  56. package/src/submit-button.tsx +5 -1
  57. package/src/types.ts +149 -0
  58. package/src/use-array-field.tsx +9 -3
  59. package/src/use-step-form.tsx +54 -9
  60. package/src/utils.ts +23 -1
  61. package/src/watch-field.tsx +2 -6
  62. /package/src/radio/{radio.test.tsx → radio-input.test.tsx} +0 -0
package/src/types.ts ADDED
@@ -0,0 +1,149 @@
1
+ import { FormControlProps } from '@chakra-ui/react'
2
+ import { MaybeRenderProp } from '@chakra-ui/react-utils'
3
+ import { FieldPath, FieldValues, RegisterOptions } from 'react-hook-form'
4
+ import { DefaultFields } from './default-fields'
5
+ import { FormProps, FormRenderContext } from './form'
6
+ import { SubmitButtonProps } from './submit-button'
7
+
8
+ export type FieldOption = { label?: string; value: string }
9
+ export type FieldOptions<TOption extends FieldOption = FieldOption> =
10
+ | Array<string>
11
+ | Array<TOption>
12
+
13
+ export type ValueOf<T> = T[keyof T]
14
+ export type ShallowMerge<A, B> = Omit<A, keyof B> & B
15
+
16
+ type Split<S extends string, D extends string> = string extends S
17
+ ? string[]
18
+ : S extends ''
19
+ ? []
20
+ : S extends `${infer T}${D}${infer U}`
21
+ ? [T, ...Split<U, D>]
22
+ : [S]
23
+
24
+ type MapPath<T extends string[]> = T extends [infer U, ...infer R]
25
+ ? U extends string
26
+ ? `${U extends `${number}` ? '$' : U}${R[0] extends string
27
+ ? '.'
28
+ : ''}${R extends string[] ? MapPath<R> : ''}`
29
+ : ''
30
+ : ''
31
+
32
+ type TransformPath<T extends string> = MapPath<Split<T, '.'>>
33
+
34
+ export type ArrayFieldPath<Name extends string> = Name extends string
35
+ ? TransformPath<Name>
36
+ : never
37
+
38
+ export interface BaseFieldProps<
39
+ TFieldValues extends FieldValues = FieldValues,
40
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
41
+ > extends Omit<FormControlProps, 'label' | 'type'> {
42
+ /**
43
+ * The field name
44
+ */
45
+ name: TName | ArrayFieldPath<TName>
46
+ /**
47
+ * The field label
48
+ */
49
+ label?: string
50
+ /**
51
+ * Hide the field label
52
+ */
53
+ hideLabel?: boolean
54
+ /**
55
+ * Field help text
56
+ */
57
+ help?: string
58
+ /**
59
+ * React hook form rules
60
+ */
61
+ rules?: Omit<
62
+ RegisterOptions<TFieldValues, TName>,
63
+ 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
64
+ >
65
+ /**
66
+ * Build-in types:
67
+ * text, number, password, textarea, select, native-select, checkbox, radio, switch, pin
68
+ *
69
+ * Will default to a text field if there is no matching type.
70
+ */
71
+ type?: string
72
+ /**
73
+ * The input placeholder
74
+ */
75
+ placeholder?: string
76
+ }
77
+
78
+ type FieldPathWithArray<
79
+ TFieldValues extends FieldValues,
80
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
81
+ > = TName | ArrayFieldPath<TName>
82
+
83
+ type MergeFieldProps<
84
+ FieldDefs,
85
+ TFieldValues extends FieldValues = FieldValues,
86
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
87
+ > = ValueOf<{
88
+ [K in keyof FieldDefs]: FieldDefs[K] extends React.FC<infer Props>
89
+ ? { type?: K } & ShallowMerge<Props, BaseFieldProps<TFieldValues, TName>>
90
+ : never
91
+ }>
92
+
93
+ export type FieldProps<TFieldValues extends FieldValues = FieldValues> =
94
+ MergeFieldProps<DefaultFields, TFieldValues>
95
+
96
+ export type FormChildren<
97
+ FieldDefs,
98
+ TFieldValues extends FieldValues = FieldValues,
99
+ TContext extends object = object
100
+ > = MaybeRenderProp<
101
+ FormRenderContext<
102
+ TFieldValues,
103
+ TContext,
104
+ MergeFieldProps<
105
+ FieldDefs extends never
106
+ ? DefaultFields
107
+ : ShallowMerge<DefaultFields, FieldDefs>,
108
+ TFieldValues
109
+ >
110
+ >
111
+ >
112
+
113
+ export type DefaultFieldOverrides = {
114
+ submit?: SubmitButtonProps
115
+ [key: string]: any
116
+ }
117
+
118
+ export type FieldOverrides<
119
+ FieldDefs,
120
+ TFieldValues extends FieldValues = FieldValues,
121
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
122
+ > = {
123
+ [K in FieldPathWithArray<TFieldValues, TName>]?: Omit<
124
+ MergeFieldProps<
125
+ FieldDefs extends never
126
+ ? DefaultFields
127
+ : ShallowMerge<DefaultFields, FieldDefs>,
128
+ TFieldValues
129
+ >,
130
+ 'name'
131
+ >
132
+ }
133
+
134
+ export type WithFields<
135
+ TFormProps extends FormProps<any, any, any, any>,
136
+ FieldDefs,
137
+ ExtraOverrides = object
138
+ > = TFormProps extends FormProps<
139
+ infer TSchema,
140
+ infer TFieldValues,
141
+ infer TContext
142
+ >
143
+ ? Omit<TFormProps, 'children' | 'fields'> & {
144
+ children?: FormChildren<FieldDefs, TFieldValues, TContext>
145
+ fields?: FieldOverrides<FieldDefs, TFieldValues> & {
146
+ submit?: SubmitButtonProps
147
+ } & ExtraOverrides
148
+ }
149
+ : never
@@ -1,10 +1,13 @@
1
1
  import * as React from 'react'
2
2
  import {
3
3
  useFieldArray,
4
- useFormContext,
5
4
  UseFieldArrayReturn,
5
+ FieldValues,
6
+ FieldPath,
6
7
  } from 'react-hook-form'
7
8
 
9
+ import { useFormContext } from './form-context'
10
+
8
11
  import { createContext } from '@chakra-ui/react-utils'
9
12
 
10
13
  export interface UseArrayFieldReturn extends UseFieldArrayReturn {
@@ -59,11 +62,14 @@ export const [ArrayFieldRowProvider, useArrayFieldRowContext] =
59
62
  name: 'ArrayFieldRowContext',
60
63
  })
61
64
 
62
- export interface ArrayFieldOptions {
65
+ export interface ArrayFieldOptions<
66
+ TFieldValues extends FieldValues = FieldValues,
67
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
68
+ > {
63
69
  /**
64
70
  * The field name
65
71
  */
66
- name: string
72
+ name: TName
67
73
  /**
68
74
  * Default value for new values in the array
69
75
  */
@@ -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 {
package/src/utils.ts CHANGED
@@ -1,13 +1,35 @@
1
1
  import * as React from 'react'
2
+ import { FieldOption, FieldOptions } from './types'
2
3
 
3
4
  export const mapNestedFields = (name: string, children: React.ReactNode) => {
4
5
  return React.Children.map(children, (child) => {
5
6
  if (React.isValidElement(child) && child.props.name) {
7
+ let childName = child.props.name
8
+ if (childName.includes('.')) {
9
+ childName = childName.replace(/^.*\.(.*)/, '$1')
10
+ } else if (childName.includes('.$')) {
11
+ childName = childName.replace(/^.*\.\$(.*)/, '$1')
12
+ }
13
+
6
14
  return React.cloneElement(child, {
7
15
  ...child.props,
8
- name: `${name}.${child.props.name}`,
16
+ name: `${name}.${childName}`,
9
17
  })
10
18
  }
11
19
  return child
12
20
  })
13
21
  }
22
+
23
+ export const mapOptions = <TOption extends FieldOption = FieldOption>(
24
+ options: FieldOptions<TOption>
25
+ ) => {
26
+ return options.map((option) => {
27
+ if (typeof option === 'string') {
28
+ return {
29
+ label: option,
30
+ value: option,
31
+ }
32
+ }
33
+ return option
34
+ })
35
+ }
@@ -1,9 +1,5 @@
1
- import {
2
- FieldValues,
3
- useFormContext,
4
- UseFormReturn,
5
- useWatch,
6
- } from 'react-hook-form'
1
+ import { FieldValues, useWatch } from 'react-hook-form'
2
+ import { useFormContext, UseFormReturn } from './form-context'
7
3
 
8
4
  export interface WatchFieldProps<
9
5
  Value = unknown,