@saas-ui/forms 0.5.2 → 0.6.0-next.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/auto-form.d.ts +2 -1
  3. package/dist/auto-form.d.ts.map +1 -1
  4. package/dist/field-resolver.d.ts +8 -0
  5. package/dist/field-resolver.d.ts.map +1 -0
  6. package/dist/field.d.ts +3 -2
  7. package/dist/field.d.ts.map +1 -1
  8. package/dist/fields.d.ts +2 -0
  9. package/dist/fields.d.ts.map +1 -1
  10. package/dist/form.d.ts +3 -3
  11. package/dist/form.d.ts.map +1 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.modern.js +1 -1
  17. package/dist/index.modern.js.map +1 -1
  18. package/dist/step-form.d.ts +4 -0
  19. package/dist/step-form.d.ts.map +1 -1
  20. package/dist/use-step-form.d.ts +3 -0
  21. package/dist/use-step-form.d.ts.map +1 -1
  22. package/dist/yup/index.js +2 -0
  23. package/dist/yup/index.js.map +1 -0
  24. package/dist/yup/index.modern.js +2 -0
  25. package/dist/yup/index.modern.js.map +1 -0
  26. package/dist/yup/src/index.d.ts +2 -0
  27. package/dist/yup/src/index.d.ts.map +1 -0
  28. package/dist/yup/src/yup.d.ts +23 -0
  29. package/dist/yup/src/yup.d.ts.map +1 -0
  30. package/package.json +11 -13
  31. package/src/auto-form.tsx +6 -5
  32. package/src/field-resolver.ts +39 -0
  33. package/src/field.tsx +3 -2
  34. package/src/fields.tsx +45 -30
  35. package/src/form.tsx +5 -12
  36. package/src/index.ts +1 -1
  37. package/src/step-form.tsx +6 -2
  38. package/src/use-step-form.tsx +5 -2
  39. package/yup/index.ts +1 -0
  40. package/yup/package.json +24 -0
  41. package/yup/src/index.ts +1 -0
  42. package/{src/resolvers → yup/src}/yup.ts +28 -7
  43. package/yup/tsconfig.json +8 -0
  44. package/dist/resolvers/yup.d.ts +0 -12
  45. package/dist/resolvers/yup.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saas-ui/forms",
3
- "version": "0.5.2",
3
+ "version": "0.6.0-next.1",
4
4
  "description": "Theme and components agnostic SaasProvider",
5
5
  "source": "src/index.ts",
6
6
  "exports": {
@@ -8,10 +8,6 @@
8
8
  "require": "./dist/index.js",
9
9
  "default": "./dist/index.modern.js"
10
10
  },
11
- "./resolvers/yup": {
12
- "require": "./dist/resolvers/yup.js",
13
- "default": "./dist/resolvers/yup.modern.js"
14
- },
15
11
  "./src": {
16
12
  "default": "./src/index.ts"
17
13
  }
@@ -21,14 +17,17 @@
21
17
  "types": "./dist/index.d.ts",
22
18
  "scripts": {
23
19
  "clean": "rimraf --no-glob ./dist",
24
- "build": "yarn clean && cross-env NODE_ENV=production microbundle --tsconfig ./tsconfig.json --jsx React.createElement --jsxFragment React.Fragment -f cjs,modern --compress",
20
+ "build": "yarn build:package && yarn build:yup",
21
+ "build:package": "yarn clean && cross-env NODE_ENV=production microbundle --tsconfig ./tsconfig.json --jsx React.createElement --jsxFragment React.Fragment -f cjs,modern --compress",
22
+ "build:yup": "cross-env NODE_ENV=production microbundle --cwd yup --tsconfig ./yup/tsconfig.json -f cjs,modern --compress",
25
23
  "lint": "eslint src --ext .ts,.tsx,.js,.jsx --config ../../.eslintrc.js",
26
24
  "lint:staged": "lint-staged --allow-empty --config ../../lint-staged.config.js",
27
25
  "typecheck": "tsc --noEmit"
28
26
  },
29
27
  "files": [
30
28
  "dist",
31
- "src"
29
+ "src",
30
+ "yup"
32
31
  ],
33
32
  "sideEffects": false,
34
33
  "publishConfig": {
@@ -66,20 +65,19 @@
66
65
  "@chakra-ui/react-utils": "^1.2.1",
67
66
  "@chakra-ui/utils": "^1.10.4",
68
67
  "@hookform/resolvers": "^2.8.3",
69
- "@saas-ui/button": "0.3.0",
70
- "@saas-ui/input-right-button": "0.3.0",
68
+ "@saas-ui/button": "0.4.0-next.0",
69
+ "@saas-ui/input-right-button": "0.3.1-next.0",
71
70
  "@saas-ui/number-input": "0.3.0",
72
- "@saas-ui/password-input": "0.3.0",
71
+ "@saas-ui/password-input": "0.3.1-next.0",
73
72
  "@saas-ui/pin-input": "0.3.0",
74
73
  "@saas-ui/radio": "0.3.0",
75
74
  "@saas-ui/react-utils": "0.1.0",
76
- "@saas-ui/select": "0.3.0",
75
+ "@saas-ui/select": "0.4.0-next.0",
77
76
  "react-hook-form": "^7.22.0"
78
77
  },
79
78
  "peerDependencies": {
80
79
  "@chakra-ui/react": ">=1.8.0",
81
80
  "@chakra-ui/system": ">=1.0.0",
82
- "react": ">=16.8.6",
83
- "yup": "^0.32.11"
81
+ "react": ">=16.8.6"
84
82
  }
85
83
  }
package/src/auto-form.tsx CHANGED
@@ -3,14 +3,14 @@ import { FieldValues, UseFormReturn } from 'react-hook-form'
3
3
  import { forwardRef } from '@chakra-ui/react'
4
4
 
5
5
  import { Form, FormProps } from './form'
6
-
7
6
  import { FormLayout } from './layout'
8
7
  import { Fields } from './fields'
9
8
  import { SubmitButton } from './submit-button'
10
9
 
11
10
  interface AutoFormOptions {
12
- schema: any
13
11
  submitLabel?: false | string
12
+ schema: any
13
+ fieldResolver?: any
14
14
  }
15
15
 
16
16
  export interface AutoFormProps<TFieldValues extends FieldValues>
@@ -22,11 +22,12 @@ export const AutoForm = forwardRef(
22
22
  props: AutoFormProps<TFieldValues>,
23
23
  ref: React.ForwardedRef<UseFormReturn<TFieldValues>>
24
24
  ) => {
25
- const { schema, submitLabel = 'Submit', ...rest } = props
25
+ const { schema, submitLabel = 'Submit', fieldResolver, ...rest } = props
26
+
26
27
  return (
27
- <Form {...rest} schema={schema} ref={ref}>
28
+ <Form {...rest} ref={ref}>
28
29
  <FormLayout>
29
- {<Fields schema={schema} />}
30
+ {<Fields schema={schema} fieldResolver={fieldResolver} />}
30
31
  {submitLabel && <SubmitButton label={submitLabel} />}
31
32
  </FormLayout>
32
33
  </Form>
@@ -0,0 +1,39 @@
1
+ import { FieldProps } from './field'
2
+
3
+ import { get } from '@chakra-ui/utils'
4
+
5
+ export type FieldResolver = {
6
+ getFields(): FieldProps[]
7
+ getNestedFields(name: string): FieldProps[]
8
+ }
9
+
10
+ // @todo finalize this
11
+ export type FormSchema = Record<string, any>
12
+
13
+ const mapFields = (schema: FormSchema) =>
14
+ Object.entries(schema).map(([name, field]) => {
15
+ return {
16
+ name,
17
+ ...field,
18
+ }
19
+ })
20
+
21
+ export const defaultFieldResolver = (schema: FormSchema): FieldResolver => {
22
+ const getFields = () => {
23
+ return mapFields(schema)
24
+ }
25
+ const getNestedFields = (name: string) => {
26
+ const field = get(schema, name)
27
+
28
+ if (!field) return []
29
+
30
+ if (field.items?.type === 'object') {
31
+ return mapFields(field.items.properties)
32
+ } else if (field.type === 'object') {
33
+ return mapFields(field.properties)
34
+ }
35
+ return [field.items]
36
+ }
37
+
38
+ return { getFields, getNestedFields }
39
+ }
package/src/field.tsx CHANGED
@@ -29,6 +29,7 @@ import { PasswordInput } from '@saas-ui/password-input'
29
29
  import { RadioInput } from '@saas-ui/radio'
30
30
  import { PinInput } from '@saas-ui/pin-input'
31
31
  import { Select, NativeSelect } from '@saas-ui/select'
32
+ import { FocusableElement } from '@chakra-ui/utils'
32
33
 
33
34
  export interface Option {
34
35
  value: string
@@ -158,7 +159,7 @@ export const Field = forwardRef(
158
159
  props: FieldProps<TFieldValues> & {
159
160
  [key: string]: unknown // Make sure attributes of custom components work. Need to change this to a global typedef at some point.
160
161
  },
161
- ref: React.ForwardedRef<typeof FormControl>
162
+ ref: React.ForwardedRef<FocusableElement>
162
163
  ) => {
163
164
  const { type = defaultInputType } = props
164
165
  const InputComponent = getInput(type)
@@ -169,7 +170,7 @@ export const Field = forwardRef(
169
170
  props: FieldProps<TFieldValues> & {
170
171
  [key: string]: unknown
171
172
  } & {
172
- ref?: React.ForwardedRef<typeof FormControl>
173
+ ref?: React.ForwardedRef<FocusableElement>
173
174
  }
174
175
  ) => React.ReactElement
175
176
 
package/src/fields.tsx CHANGED
@@ -1,51 +1,66 @@
1
1
  import * as React from 'react'
2
- import { getFieldsFromSchema, getNestedSchema } from './resolvers/yup'
3
2
 
4
3
  import { FormLayout } from './layout'
5
4
  import { Field, FieldProps } from './field'
6
5
 
7
6
  import { ArrayField } from './array-field'
8
7
  import { ObjectField } from './object-field'
8
+ import { FieldResolver } from './field-resolver'
9
+ import { defaultFieldResolver } from './field-resolver'
9
10
 
10
11
  export interface FieldsProps {
11
12
  schema: any
13
+ fieldResolver?: FieldResolver
12
14
  }
13
15
 
14
- const getNestedFields = (schema: any, name: string) => {
15
- return getFieldsFromSchema(getNestedSchema(schema, name)).map(
16
- ({ name, type, ...nestedFieldProps }: FieldProps): React.ReactNode => (
17
- <Field key={name} name={name} type={type} {...nestedFieldProps} />
16
+ const mapNestedFields = (resolver: FieldResolver, name: string) => {
17
+ return resolver
18
+ .getNestedFields(name)
19
+ ?.map(
20
+ ({ name, type, ...nestedFieldProps }: FieldProps, i): React.ReactNode => (
21
+ <Field key={name || i} name={name} type={type} {...nestedFieldProps} />
22
+ )
18
23
  )
19
- )
20
24
  }
21
25
 
22
- export const Fields: React.FC<FieldsProps> = ({ schema, ...props }) => {
26
+ export const Fields: React.FC<FieldsProps> = ({
27
+ schema,
28
+ fieldResolver,
29
+ ...props
30
+ }) => {
31
+ const resolver = React.useMemo(
32
+ () => fieldResolver || defaultFieldResolver(schema),
33
+ [schema, fieldResolver]
34
+ )
35
+
23
36
  return (
24
37
  <FormLayout {...props}>
25
- {getFieldsFromSchema(schema).map(
26
- ({
27
- name,
28
- type,
29
- defaultValue,
30
- ...fieldProps
31
- }: FieldProps): React.ReactNode => {
32
- if (type === 'array') {
33
- return (
34
- <ArrayField name={name} {...fieldProps}>
35
- {getNestedFields(schema, name)}
36
- </ArrayField>
37
- )
38
- } else if (type === 'object') {
39
- return (
40
- <ObjectField name={name} {...fieldProps}>
41
- {getNestedFields(schema, name)}
42
- </ObjectField>
43
- )
44
- }
38
+ {resolver
39
+ .getFields()
40
+ .map(
41
+ ({
42
+ name,
43
+ type,
44
+ defaultValue,
45
+ ...fieldProps
46
+ }: FieldProps): React.ReactNode => {
47
+ if (type === 'array') {
48
+ return (
49
+ <ArrayField key={name} name={name} {...fieldProps}>
50
+ {mapNestedFields(resolver, name)}
51
+ </ArrayField>
52
+ )
53
+ } else if (type === 'object') {
54
+ return (
55
+ <ObjectField key={name} name={name} {...fieldProps}>
56
+ {mapNestedFields(resolver, name)}
57
+ </ObjectField>
58
+ )
59
+ }
45
60
 
46
- return <Field key={name} name={name} type={type} {...fieldProps} />
47
- }
48
- )}
61
+ return <Field key={name} name={name} type={type} {...fieldProps} />
62
+ }
63
+ )}
49
64
  </FormLayout>
50
65
  )
51
66
  }
package/src/form.tsx CHANGED
@@ -12,9 +12,7 @@ import {
12
12
  SubmitErrorHandler,
13
13
  } from 'react-hook-form'
14
14
 
15
- import { yupResolver } from './resolvers/yup'
16
-
17
- export type { UseFormReturn }
15
+ export type { UseFormReturn, FieldValues, SubmitHandler }
18
16
 
19
17
  interface FormOptions<TFieldValues extends FieldValues = FieldValues> {
20
18
  /**
@@ -35,15 +33,15 @@ interface FormOptions<TFieldValues extends FieldValues = FieldValues> {
35
33
  formRef?: React.MutableRefObject<HTMLFormElement>
36
34
  }
37
35
 
36
+ /**
37
+ * @todo Figure out how to pass down FieldValues to all Field components,
38
+ * if at all possible.
39
+ */
38
40
  export interface FormProps<TFieldValues extends FieldValues = FieldValues>
39
41
  extends UseFormProps<TFieldValues>,
40
42
  Omit<HTMLChakraProps<'form'>, 'onSubmit' | 'onError'>,
41
43
  FormOptions<TFieldValues> {}
42
44
 
43
- /**
44
- * @todo Figure out how to pass down FieldValues to all Field components,
45
- * if at all possible.
46
- */
47
45
  export const Form = forwardRef(
48
46
  <TFieldValues extends FieldValues = FieldValues>(
49
47
  props: FormProps<TFieldValues>,
@@ -79,11 +77,6 @@ export const Form = forwardRef(
79
77
  delayError,
80
78
  }
81
79
 
82
- // @todo remove yup dependency and just use resolver prop?
83
- if (schema) {
84
- form.resolver = yupResolver(schema)
85
- }
86
-
87
80
  const methods = useForm<TFieldValues>(form)
88
81
  const { handleSubmit } = methods
89
82
 
package/src/index.ts CHANGED
@@ -11,7 +11,7 @@ export * from './object-field'
11
11
  export * from './display-if'
12
12
  export * from './step-form'
13
13
  export * from './use-step-form'
14
-
14
+ export * from './field-resolver'
15
15
  export * from '@saas-ui/input-right-button'
16
16
 
17
17
  export type { FieldErrors } from 'react-hook-form'
package/src/step-form.tsx CHANGED
@@ -72,6 +72,10 @@ export interface FormStepOptions {
72
72
  * Schema
73
73
  */
74
74
  schema?: any
75
+ /**
76
+ * Hook Form Resolver
77
+ */
78
+ resolver?: any
75
79
  }
76
80
 
77
81
  export const FormStepper: React.FC<StepperStepsProps> = (props) => {
@@ -109,8 +113,8 @@ export interface FormStepProps
109
113
  HTMLChakraProps<'div'> {}
110
114
 
111
115
  export const FormStep: React.FC<FormStepProps> = (props) => {
112
- const { name, schema, children, className, ...rest } = props
113
- const step = useFormStep({ name, schema })
116
+ const { name, schema, resolver, children, className, ...rest } = props
117
+ const step = useFormStep({ name, schema, resolver })
114
118
 
115
119
  const { isActive } = step
116
120
 
@@ -11,6 +11,7 @@ import {
11
11
  export interface StepState {
12
12
  name: string
13
13
  schema?: any
14
+ resolver?: any
14
15
  isActive?: boolean
15
16
  isCompleted?: boolean
16
17
  }
@@ -69,6 +70,7 @@ export function useStepForm<TFieldValues extends FieldValues = FieldValues>(
69
70
  return {
70
71
  onSubmit: onSubmitStep,
71
72
  schema: step?.schema,
73
+ resolver: step?.resolver,
72
74
  }
73
75
  },
74
76
  [steps, onSubmitStep, activeStep]
@@ -99,16 +101,17 @@ export type UseStepFormReturn = ReturnType<typeof useStepForm>
99
101
  export interface UseFormStepProps {
100
102
  name: string
101
103
  schema?: any
104
+ resolver?: any
102
105
  }
103
106
 
104
107
  export function useFormStep(props: UseFormStepProps): StepState {
105
- const { name, schema } = props
108
+ const { name, schema, resolver } = props
106
109
  const step = useStep({ name })
107
110
 
108
111
  const { steps, updateStep } = useStepFormContext()
109
112
 
110
113
  React.useEffect(() => {
111
- updateStep({ name, schema })
114
+ updateStep({ name, schema, resolver })
112
115
  }, [name, schema])
113
116
 
114
117
  return {
package/yup/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './src'
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "yup",
3
+ "description": "Saas UI Forms field resolver: yup",
4
+ "version": "1.0.0",
5
+ "private": true,
6
+ "source": "./src/index.ts",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./../dist/yup/index.js",
10
+ "types": "./../dist/yup/src/index.d.ts",
11
+ "default": "./../dist/yup/index.modern.js"
12
+ }
13
+ },
14
+ "main": "../dist/yup/index.js",
15
+ "module": "../dist/yup/index.modern.js",
16
+ "types": "../dist/yup/src/index.d.ts",
17
+ "author": "Eelco Wiersma <eelco@appulse.nl>",
18
+ "license": "MIT",
19
+ "peerDependencies": {
20
+ "@hookform/resolvers": "^2.8.3",
21
+ "react-hook-form": "^7.22.0",
22
+ "yup": "^0.32.11"
23
+ }
24
+ }
@@ -0,0 +1 @@
1
+ export * from './yup'
@@ -1,7 +1,7 @@
1
- import { SchemaOf, AnySchema, reach } from 'yup'
2
- export { yupResolver } from '@hookform/resolvers/yup'
1
+ import { reach, AnyObjectSchema } from 'yup'
2
+ import { yupResolver } from '@hookform/resolvers/yup'
3
3
 
4
- import { FieldProps } from '../field'
4
+ import { FieldProps } from '../../src/field'
5
5
 
6
6
  // @TODO get proper typings for the schema fields
7
7
 
@@ -42,9 +42,7 @@ const getArrayOption = (field: any, name: string) => {
42
42
  * @param schema The Yup schema
43
43
  * @returns {FieldProps[]}
44
44
  */
45
- export const getFieldsFromSchema = (
46
- schema: SchemaOf<AnySchema>
47
- ): FieldProps[] => {
45
+ export const getFieldsFromSchema = (schema: AnyObjectSchema): FieldProps[] => {
48
46
  const fields = []
49
47
 
50
48
  let schemaFields: Record<string, any> = {}
@@ -74,6 +72,29 @@ export const getFieldsFromSchema = (
74
72
  return fields
75
73
  }
76
74
 
77
- export const getNestedSchema = (schema: SchemaOf<AnySchema>, path: string) => {
75
+ export const getNestedSchema = (schema: AnyObjectSchema, path: string) => {
78
76
  return reach(schema, path)
79
77
  }
78
+
79
+ export const fieldResolver = (schema: AnyObjectSchema) => {
80
+ return {
81
+ getFields() {
82
+ return getFieldsFromSchema(schema)
83
+ },
84
+ getNestedFields(name: string) {
85
+ return getFieldsFromSchema(getNestedSchema(schema, name))
86
+ },
87
+ }
88
+ }
89
+
90
+ export const yupForm = (
91
+ schema: AnyObjectSchema,
92
+ schemaOptions = {},
93
+ resolverOptions = {}
94
+ ) => {
95
+ return {
96
+ schema,
97
+ resolver: yupResolver(schema, schemaOptions, resolverOptions),
98
+ fieldResolver: fieldResolver(schema),
99
+ }
100
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "include": ["src/*"],
4
+ "compilerOptions": {
5
+ "outDir": "../dist",
6
+ "declarationDir": "../dist"
7
+ }
8
+ }
@@ -1,12 +0,0 @@
1
- import { SchemaOf, AnySchema } from 'yup';
2
- export { yupResolver } from '@hookform/resolvers/yup';
3
- import { FieldProps } from '../field';
4
- /**
5
- * A helper function to render forms automatically based on a Yup schema
6
- *
7
- * @param schema The Yup schema
8
- * @returns {FieldProps[]}
9
- */
10
- export declare const getFieldsFromSchema: (schema: SchemaOf<AnySchema>) => FieldProps[];
11
- export declare const getNestedSchema: (schema: SchemaOf<AnySchema>, path: string) => any;
12
- //# sourceMappingURL=yup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"yup.d.ts","sourceRoot":"","sources":["../../src/resolvers/yup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAS,MAAM,KAAK,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAmCrC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,WACtB,SAAS,SAAS,CAAC,KAC1B,UAAU,EA4BZ,CAAA;AAED,eAAO,MAAM,eAAe,WAAY,SAAS,SAAS,CAAC,QAAQ,MAAM,QAExE,CAAA"}