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

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 (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"}