@saas-ui/forms 2.0.0-next.3 → 2.0.0-next.6

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 (60) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +53 -6
  3. package/dist/ajv/index.d.ts +358 -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 +448 -247
  9. package/dist/index.js +707 -682
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +691 -666
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/yup/index.d.ts +580 -21
  14. package/dist/yup/index.js +6 -10
  15. package/dist/yup/index.js.map +1 -1
  16. package/dist/yup/index.mjs +4 -8
  17. package/dist/yup/index.mjs.map +1 -1
  18. package/dist/zod/index.d.ts +580 -11
  19. package/dist/zod/index.js +5 -0
  20. package/dist/zod/index.js.map +1 -1
  21. package/dist/zod/index.mjs +5 -1
  22. package/dist/zod/index.mjs.map +1 -1
  23. package/package.json +19 -10
  24. package/src/array-field.tsx +82 -45
  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 +54 -0
  29. package/src/default-fields.tsx +163 -0
  30. package/src/display-field.tsx +9 -11
  31. package/src/display-if.tsx +20 -13
  32. package/src/field-resolver.ts +10 -8
  33. package/src/field.tsx +18 -445
  34. package/src/fields-context.tsx +23 -0
  35. package/src/fields.tsx +34 -21
  36. package/src/form-context.tsx +84 -0
  37. package/src/form.tsx +69 -52
  38. package/src/index.ts +44 -4
  39. package/src/input-right-button/input-right-button.stories.tsx +1 -1
  40. package/src/input-right-button/input-right-button.tsx +0 -2
  41. package/src/layout.tsx +16 -11
  42. package/src/number-input/number-input.tsx +9 -5
  43. package/src/object-field.tsx +13 -8
  44. package/src/password-input/password-input.stories.tsx +23 -2
  45. package/src/password-input/password-input.tsx +6 -6
  46. package/src/pin-input/pin-input.tsx +1 -5
  47. package/src/radio/radio-input.stories.tsx +1 -1
  48. package/src/radio/radio-input.tsx +12 -10
  49. package/src/select/native-select.tsx +1 -4
  50. package/src/select/select-context.tsx +130 -0
  51. package/src/select/select.stories.tsx +116 -85
  52. package/src/select/select.test.tsx +1 -1
  53. package/src/select/select.tsx +160 -146
  54. package/src/step-form.tsx +29 -11
  55. package/src/submit-button.tsx +5 -1
  56. package/src/types.ts +144 -0
  57. package/src/use-array-field.tsx +9 -3
  58. package/src/utils.ts +23 -1
  59. package/src/watch-field.tsx +2 -6
  60. /package/src/radio/{radio.test.tsx → radio-input.test.tsx} +0 -0
@@ -0,0 +1,54 @@
1
+ import React, { ForwardedRef } from 'react'
2
+ import { FieldsProvider } from './fields-context'
3
+ import { Form, FieldValues, FormProps, GetResolver } from './form'
4
+ import { WithFields } from './types'
5
+ import { objectFieldResolver } from './field-resolver'
6
+ import { GetFieldResolver } from './field-resolver'
7
+ import { forwardRef } from '@chakra-ui/react'
8
+
9
+ export interface CreateFormProps<FieldDefs> {
10
+ resolver?: GetResolver
11
+ fieldResolver?: GetFieldResolver
12
+ fields?: FieldDefs extends Record<string, React.FC<any>> ? FieldDefs : never
13
+ }
14
+
15
+ export function createForm<FieldDefs, Schema = any>({
16
+ resolver,
17
+ fieldResolver = objectFieldResolver,
18
+ fields,
19
+ }: CreateFormProps<FieldDefs> = {}) {
20
+ const CreateForm = forwardRef(
21
+ <
22
+ TFieldValues extends FieldValues,
23
+ TContext extends object = object,
24
+ TSchema extends Schema = Schema
25
+ >(
26
+ props: WithFields<FormProps<TFieldValues, TContext, TSchema>, FieldDefs>,
27
+ ref: ForwardedRef<HTMLFormElement>
28
+ ) => {
29
+ const { schema, ...rest } = props
30
+ return (
31
+ <FieldsProvider value={fields || {}}>
32
+ <Form
33
+ ref={ref}
34
+ resolver={resolver?.(props.schema)}
35
+ fieldResolver={fieldResolver?.(schema)}
36
+ {...rest}
37
+ />
38
+ </FieldsProvider>
39
+ )
40
+ }
41
+ ) as (<
42
+ TFieldValues extends FieldValues,
43
+ TContext extends object = object,
44
+ TSchema extends Schema = Schema
45
+ >(
46
+ props: WithFields<FormProps<TFieldValues, TContext, TSchema>, FieldDefs> & {
47
+ ref?: React.ForwardedRef<HTMLFormElement>
48
+ }
49
+ ) => React.ReactElement) & {
50
+ displayName?: string
51
+ }
52
+
53
+ return CreateForm
54
+ }
@@ -0,0 +1,163 @@
1
+ import * as React from 'react'
2
+
3
+ import {
4
+ forwardRef,
5
+ Input,
6
+ Textarea,
7
+ Checkbox,
8
+ Switch,
9
+ InputGroup,
10
+ InputProps,
11
+ TextareaProps,
12
+ SwitchProps,
13
+ CheckboxProps,
14
+ PinInputField,
15
+ HStack,
16
+ PinInput,
17
+ UsePinInputProps,
18
+ SystemProps,
19
+ } from '@chakra-ui/react'
20
+
21
+ import { NumberInput, NumberInputProps } from './number-input'
22
+ import { PasswordInput, PasswordInputProps } from './password-input'
23
+ import { RadioInput, RadioInputProps } from './radio'
24
+
25
+ import {
26
+ Select,
27
+ SelectButton,
28
+ SelectList,
29
+ SelectProps,
30
+ NativeSelect,
31
+ NativeSelectProps,
32
+ } from './select'
33
+
34
+ import { createField } from './create-field'
35
+
36
+ export interface InputFieldProps extends InputProps {
37
+ type?: string
38
+ leftAddon?: React.ReactNode
39
+ rightAddon?: React.ReactNode
40
+ }
41
+
42
+ export const InputField = createField<InputFieldProps>(
43
+ forwardRef(({ type = 'text', leftAddon, rightAddon, size, ...rest }, ref) => {
44
+ const input = <Input type={type} size={size} {...rest} ref={ref} />
45
+ if (leftAddon || rightAddon) {
46
+ return (
47
+ <InputGroup size={size}>
48
+ {leftAddon}
49
+ {input}
50
+ {rightAddon}
51
+ </InputGroup>
52
+ )
53
+ }
54
+ return input
55
+ })
56
+ )
57
+
58
+ export interface NumberInputFieldProps extends NumberInputProps {
59
+ type: 'number'
60
+ }
61
+
62
+ export const NumberInputField = createField<NumberInputFieldProps>(
63
+ NumberInput,
64
+ {
65
+ isControlled: true,
66
+ }
67
+ )
68
+
69
+ export const PasswordInputField = createField<PasswordInputProps>(
70
+ forwardRef((props, ref) => <PasswordInput ref={ref} {...props} />)
71
+ )
72
+
73
+ export const TextareaField = createField<TextareaProps>(Textarea)
74
+
75
+ export const SwitchField = createField<SwitchProps>(
76
+ forwardRef(({ type, value, ...rest }, ref) => {
77
+ return <Switch isChecked={!!value} {...rest} ref={ref} />
78
+ }),
79
+ {
80
+ isControlled: true,
81
+ }
82
+ )
83
+
84
+ export const SelectField = createField<SelectProps>(
85
+ forwardRef((props, ref) => {
86
+ return (
87
+ <Select ref={ref} {...props}>
88
+ <SelectButton />
89
+ <SelectList />
90
+ </Select>
91
+ )
92
+ }),
93
+ {
94
+ isControlled: true,
95
+ }
96
+ )
97
+
98
+ export const CheckboxField = createField<CheckboxProps>(
99
+ forwardRef(({ label, type, ...props }, ref) => {
100
+ return (
101
+ <Checkbox ref={ref} {...props}>
102
+ {label}
103
+ </Checkbox>
104
+ )
105
+ }),
106
+ {
107
+ hideLabel: true,
108
+ }
109
+ )
110
+
111
+ export const RadioField = createField<RadioInputProps>(RadioInput, {
112
+ isControlled: true,
113
+ })
114
+
115
+ export const NativeSelectField = createField<NativeSelectProps>(NativeSelect, {
116
+ isControlled: true,
117
+ })
118
+
119
+ export interface PinFieldProps extends Omit<UsePinInputProps, 'type'> {
120
+ pinLength?: number
121
+ pinType?: 'alphanumeric' | 'number'
122
+ spacing?: SystemProps['margin']
123
+ }
124
+
125
+ export const PinField = createField<PinFieldProps>(
126
+ forwardRef((props, ref) => {
127
+ const { pinLength = 4, pinType, spacing, ...inputProps } = props
128
+
129
+ const inputs: React.ReactNode[] = []
130
+ for (let i = 0; i < pinLength; i++) {
131
+ inputs.push(<PinInputField key={i} ref={ref} />)
132
+ }
133
+
134
+ return (
135
+ <HStack spacing={spacing}>
136
+ <PinInput {...inputProps} type={pinType}>
137
+ {inputs}
138
+ </PinInput>
139
+ </HStack>
140
+ )
141
+ }),
142
+ {
143
+ isControlled: true,
144
+ }
145
+ )
146
+
147
+ export const defaultFieldTypes = {
148
+ text: InputField,
149
+ email: InputField,
150
+ url: InputField,
151
+ phone: InputField,
152
+ number: NumberInputField,
153
+ password: PasswordInputField,
154
+ textarea: TextareaField,
155
+ switch: SwitchField,
156
+ select: SelectField,
157
+ checkbox: CheckboxField,
158
+ radio: RadioField,
159
+ pin: PinField,
160
+ 'native-select': NativeSelectField,
161
+ }
162
+
163
+ export type DefaultFields = typeof defaultFieldTypes
@@ -1,7 +1,5 @@
1
1
  import * as React from 'react'
2
- import { __DEV__ } from '@chakra-ui/utils'
3
- import { useFormContext } from 'react-hook-form'
4
-
2
+ import { useFormContext } from './form-context'
5
3
  import {
6
4
  Text,
7
5
  FormControl,
@@ -9,12 +7,16 @@ import {
9
7
  FormLabel,
10
8
  } from '@chakra-ui/react'
11
9
 
12
- import { FieldProps } from './field'
10
+ import { FieldProps } from './types'
13
11
 
14
12
  export interface DisplayFieldProps
15
13
  extends FormControlProps,
16
14
  Omit<FieldProps, 'type' | 'label'> {}
17
-
15
+ /**
16
+ *
17
+ *
18
+ * @see Docs https://saas-ui.dev/
19
+ */
18
20
  export const DisplayField: React.FC<DisplayFieldProps> = ({
19
21
  name,
20
22
  label,
@@ -31,15 +33,11 @@ export const DisplayField: React.FC<DisplayFieldProps> = ({
31
33
  )
32
34
  }
33
35
 
34
- if (__DEV__) {
35
- DisplayField.displayName = 'DisplayField'
36
- }
36
+ DisplayField.displayName = 'DisplayField'
37
37
 
38
38
  export const FormValue: React.FC<{ name: string }> = ({ name }) => {
39
39
  const { getValues } = useFormContext()
40
40
  return getValues(name) || null
41
41
  }
42
42
 
43
- if (__DEV__) {
44
- FormValue.displayName = 'FormValue'
45
- }
43
+ FormValue.displayName = 'FormValue'
@@ -1,41 +1,48 @@
1
1
  import * as React from 'react'
2
- import { __DEV__ } from '@chakra-ui/utils'
3
2
  import {
4
- useFormContext,
5
3
  useWatch,
6
4
  FieldValues,
7
5
  UseFormReturn,
6
+ FieldPath,
8
7
  } from 'react-hook-form'
9
8
 
9
+ import { useFormContext } from './form-context'
10
+
10
11
  export interface DisplayIfProps<
11
- TFieldValues extends FieldValues = FieldValues
12
+ TFieldValues extends FieldValues = FieldValues,
13
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
12
14
  > {
13
15
  children: React.ReactElement
14
- name: string
16
+ name: TName
15
17
  defaultValue?: unknown
16
18
  isDisabled?: boolean
17
19
  isExact?: boolean
18
20
  condition?: (value: unknown, context: UseFormReturn<TFieldValues>) => boolean
19
21
  }
20
-
21
- export const DisplayIf = <TFieldValues extends FieldValues = FieldValues>({
22
+ /**
23
+ * Conditionally render parts of a form.
24
+ *
25
+ * @see Docs https://saas-ui.dev/docs/components/forms/form
26
+ */
27
+ export const DisplayIf = <
28
+ TFieldValues extends FieldValues = FieldValues,
29
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
30
+ >({
22
31
  children,
23
32
  name,
24
33
  defaultValue,
25
34
  isDisabled,
26
35
  isExact,
27
36
  condition = (value) => !!value,
28
- }: DisplayIfProps<TFieldValues>) => {
29
- const value = useWatch({
37
+ }: DisplayIfProps<TFieldValues, TName>) => {
38
+ const value = useWatch<TFieldValues>({
30
39
  name,
31
- defaultValue,
40
+ defaultValue: defaultValue as any,
32
41
  disabled: isDisabled,
33
42
  exact: isExact,
34
43
  })
35
- const context = useFormContext<TFieldValues>()
44
+ const context = useFormContext() as any
36
45
  return condition(value, context) ? children : null
37
46
  }
38
47
 
39
- if (__DEV__) {
40
- DisplayIf.displayName = 'DisplayIf'
41
- }
48
+ DisplayIf.displayName = 'DisplayIf'
@@ -1,20 +1,22 @@
1
- import { FieldProps } from './field'
1
+ import { BaseFieldProps } from './types'
2
2
 
3
3
  import { get } from '@chakra-ui/utils'
4
4
 
5
5
  export type FieldResolver = {
6
- getFields(): FieldProps[]
7
- getNestedFields(name: string): FieldProps[]
6
+ getFields(): BaseFieldProps[]
7
+ getNestedFields(name: string): BaseFieldProps[]
8
8
  }
9
9
 
10
- interface SchemaField extends FieldProps {
10
+ export type GetFieldResolver<TSchema = any> = (schema: TSchema) => FieldResolver
11
+
12
+ interface SchemaField extends BaseFieldProps {
11
13
  items?: SchemaField[]
12
14
  properties?: Record<string, SchemaField>
13
15
  }
14
16
 
15
17
  export type ObjectSchema = Record<string, SchemaField>
16
18
 
17
- const mapFields = (schema: ObjectSchema): FieldProps[] =>
19
+ const mapFields = (schema: ObjectSchema): BaseFieldProps[] =>
18
20
  schema &&
19
21
  Object.entries(schema).map(([name, { items, label, title, ...field }]) => {
20
22
  return {
@@ -24,11 +26,11 @@ const mapFields = (schema: ObjectSchema): FieldProps[] =>
24
26
  }
25
27
  })
26
28
 
27
- export const objectFieldResolver = (schema: ObjectSchema): FieldResolver => {
28
- const getFields = () => {
29
+ export const objectFieldResolver: GetFieldResolver<ObjectSchema> = (schema) => {
30
+ const getFields = (): BaseFieldProps[] => {
29
31
  return mapFields(schema)
30
32
  }
31
- const getNestedFields = (name: string) => {
33
+ const getNestedFields = (name: string): BaseFieldProps[] => {
32
34
  const field = get(schema, name)
33
35
 
34
36
  if (!field) return []