@saas-ui/forms 3.0.0-alpha.1 → 3.0.0-alpha.3

Sign up to get free protection for your applications and to get access to all the features.
package/src/fields.tsx DELETED
@@ -1,93 +0,0 @@
1
- import * as React from 'react'
2
-
3
- import { ArrayField } from './array-field'
4
- import { Field } from './field'
5
- import { FieldResolver } from './field-resolver'
6
- import { useFormContext } from './form-context'
7
- import { FormLayout } from './form-layout'
8
- import { ObjectField } from './object-field'
9
- import { BaseFieldProps } from './types'
10
-
11
- export interface FieldsProps<TSchema = any> {
12
- schema?: TSchema
13
- fieldResolver?: FieldResolver
14
- focusFirstField?: boolean
15
- }
16
-
17
- const mapNestedFields = (resolver: FieldResolver, name: string) => {
18
- return resolver
19
- .getNestedFields(name)
20
- ?.map(
21
- (
22
- { name, type, ...nestedFieldProps }: BaseFieldProps,
23
- i,
24
- ): React.ReactNode => (
25
- <Field
26
- key={name || i}
27
- name={name}
28
- type={type as any}
29
- {...nestedFieldProps}
30
- />
31
- ),
32
- )
33
- }
34
-
35
- export const AutoFields: React.FC<FieldsProps> = ({
36
- schema: schemaProp,
37
- fieldResolver: fieldResolverProp,
38
- focusFirstField,
39
- ...props
40
- }) => {
41
- const context = useFormContext()
42
- const schema = schemaProp || context.schema
43
- const fieldResolver = fieldResolverProp || context.fieldResolver
44
- const resolver = React.useMemo(() => fieldResolver, [schema, fieldResolver])
45
-
46
- const fields = React.useMemo(() => resolver?.getFields(), [resolver])
47
-
48
- const form = useFormContext()
49
-
50
- React.useEffect(() => {
51
- if (focusFirstField && fields?.[0]?.name) {
52
- form.setFocus(fields[0].name)
53
- }
54
- }, [schema, fieldResolver, focusFirstField])
55
-
56
- if (!resolver) {
57
- return null
58
- }
59
-
60
- return (
61
- <FormLayout {...props}>
62
- {fields?.map(
63
- ({ name, type, ...fieldProps }: BaseFieldProps): React.ReactNode => {
64
- if (type === 'array') {
65
- return (
66
- <ArrayField key={name} name={name} {...fieldProps}>
67
- {mapNestedFields(resolver, name)}
68
- </ArrayField>
69
- )
70
- } else if (type === 'object') {
71
- return (
72
- <ObjectField key={name} name={name} {...fieldProps}>
73
- {mapNestedFields(resolver, name)}
74
- </ObjectField>
75
- )
76
- }
77
-
78
- return (
79
- <Field
80
- key={name}
81
- name={name}
82
- type={type as any}
83
- // defaultValue={defaultValue}
84
- {...fieldProps}
85
- />
86
- )
87
- },
88
- )}
89
- </FormLayout>
90
- )
91
- }
92
-
93
- AutoFields.displayName = 'Fields'
@@ -1,80 +0,0 @@
1
- import * as React from 'react'
2
- import {
3
- FormProvider as HookFormProvider,
4
- FormProviderProps as HookFormProviderProps,
5
- useFormContext as useHookFormContext,
6
- FieldValues,
7
- } from 'react-hook-form'
8
- import { FieldResolver } from './field-resolver'
9
- import type { BaseFieldProps } from './types'
10
-
11
- export type FormContextValue<
12
- TFieldValues extends FieldValues = FieldValues,
13
- TContext = any,
14
- TSchema = any,
15
- > = {
16
- fieldResolver?: FieldResolver
17
- schema?: TSchema
18
- fields?: {
19
- [key: string]: unknown
20
- }
21
- }
22
-
23
- export type FormProviderProps<
24
- TFieldValues extends FieldValues = FieldValues,
25
- TContext = any,
26
- TSchema = any,
27
- > = HookFormProviderProps<TFieldValues, TContext> & {
28
- fieldResolver?: FieldResolver
29
- schema?: TSchema
30
- fields?: {
31
- [key: string]: unknown
32
- }
33
- }
34
-
35
- const FormContext = React.createContext<FormContextValue | null>(null)
36
-
37
- export const useFormContext = <
38
- TFieldValues extends FieldValues = FieldValues,
39
- TContext = any,
40
- TSchema = any,
41
- >() => {
42
- const context = React.useContext(FormContext)
43
- const hookContext = useHookFormContext<TFieldValues, TContext>()
44
-
45
- return {
46
- ...hookContext,
47
- ...context,
48
- }
49
- }
50
-
51
- export const useFieldProps = <TFieldValues extends FieldValues = FieldValues>(
52
- name: string
53
- ): BaseFieldProps<TFieldValues> | undefined => {
54
- const parsedName = name?.replace(/\.[0-9]/g, '.$')
55
- const context = useFormContext()
56
- return (context?.fields?.[parsedName] as any) || {}
57
- }
58
-
59
- export type UseFormReturn<
60
- TFieldValues extends FieldValues = FieldValues,
61
- TContext = any,
62
- TSchema = any,
63
- > = ReturnType<typeof useFormContext<TFieldValues, TContext, TSchema>>
64
-
65
- export const FormProvider = <
66
- TFieldValues extends FieldValues = FieldValues,
67
- TContext = any,
68
- TSchema = any,
69
- >(
70
- props: FormProviderProps<TFieldValues, TContext, TSchema>
71
- ) => {
72
- const { children, fieldResolver, schema, fields, ...rest } = props
73
- return (
74
- <HookFormProvider {...rest}>
75
- <FormContext.Provider value={{ fieldResolver, schema, fields }}>
76
- {children}
77
- </FormContext.Provider>
78
- </HookFormProvider>
79
- )
80
- }
@@ -1,59 +0,0 @@
1
- import { forwardRef } from 'react'
2
-
3
- import {
4
- type HTMLChakraProps,
5
- type RecipeProps,
6
- SimpleGrid,
7
- SimpleGridProps,
8
- useRecipe,
9
- } from '@chakra-ui/react'
10
- import { cx } from '@saas-ui/core/utils'
11
-
12
- export interface FormLayoutOptions {
13
- columns?: SimpleGridProps['columns']
14
- gap?: SimpleGridProps['gap']
15
- }
16
-
17
- export interface FormLayoutProps
18
- extends RecipeProps<'formLayout'>,
19
- FormLayoutOptions,
20
- Omit<HTMLChakraProps<'div'>, 'columns'> {}
21
-
22
- /**
23
- * Create consistent field spacing and positioning.
24
- *
25
- * Renders form items in a `SimpleGrid`
26
- * @see https://chakra-ui.com/docs/layout/simple-grid
27
- *
28
- * @see https://saas-ui.dev/docs/components/forms/form
29
- */
30
- export const FormLayout = forwardRef<HTMLDivElement, FormLayoutProps>(
31
- ({ children, rowGap = 4, ...props }, ref) => {
32
- const recipe = useRecipe({
33
- key: 'formLayout',
34
- })
35
-
36
- const [variantProps, gridProps] = recipe.splitVariantProps(props)
37
-
38
- const styles = recipe(variantProps)
39
-
40
- return (
41
- <SimpleGrid
42
- ref={ref}
43
- {...gridProps}
44
- className={cx('sui-form-layout', props.className)}
45
- css={[
46
- {
47
- rowGap,
48
- },
49
- styles,
50
- props.css,
51
- ]}
52
- >
53
- {children}
54
- </SimpleGrid>
55
- )
56
- },
57
- )
58
-
59
- FormLayout.displayName = 'FormLayout'
package/src/form.tsx DELETED
@@ -1,252 +0,0 @@
1
- import React, { forwardRef } from 'react'
2
-
3
- import { HTMLChakraProps, chakra } from '@chakra-ui/react'
4
- import { type MaybeRenderProp, cx, runIfFn } from '@saas-ui/core/utils'
5
- import {
6
- FieldValues,
7
- ResolverOptions,
8
- ResolverResult,
9
- SubmitErrorHandler,
10
- SubmitHandler,
11
- UseFormProps,
12
- UseFormReturn,
13
- WatchObserver,
14
- useForm,
15
- } from 'react-hook-form'
16
-
17
- import { ArrayField, ArrayFieldProps } from './array-field.tsx'
18
- import { DisplayIf, DisplayIfProps } from './display-if.tsx'
19
- import { FieldResolver } from './field-resolver.tsx'
20
- import { Field as DefaultField } from './field.tsx'
21
- import { AutoFields } from './fields.tsx'
22
- import { FormProvider } from './form-context.tsx'
23
- import { FormLayout } from './form-layout.tsx'
24
- import { ObjectField, ObjectFieldProps } from './object-field.tsx'
25
- import { SubmitButton } from './submit-button.tsx'
26
- import {
27
- DefaultFieldOverrides,
28
- FieldProps,
29
- type FocusableElement,
30
- } from './types.ts'
31
- import { UseArrayFieldReturn } from './use-array-field.tsx'
32
-
33
- export type { UseFormReturn, FieldValues, SubmitHandler }
34
-
35
- export interface FormRenderContext<
36
- TFieldValues extends FieldValues = FieldValues,
37
- TContext extends object = object,
38
- TFieldTypes = FieldProps<TFieldValues>,
39
- > extends UseFormReturn<TFieldValues, TContext> {
40
- Field: React.FC<TFieldTypes & React.RefAttributes<FocusableElement>>
41
- DisplayIf: React.FC<DisplayIfProps<TFieldValues>>
42
- ArrayField: React.FC<
43
- ArrayFieldProps<TFieldValues> & React.RefAttributes<UseArrayFieldReturn>
44
- >
45
- ObjectField: React.FC<ObjectFieldProps<TFieldValues>>
46
- }
47
-
48
- interface FormOptions<
49
- TSchema = unknown,
50
- TFieldValues extends FieldValues = FieldValues,
51
- TContext extends object = object,
52
- TExtraFieldProps extends object = object,
53
- TFieldTypes = FieldProps<TFieldValues, TExtraFieldProps>,
54
- > {
55
- /**
56
- * The form schema.
57
- */
58
- schema?: TSchema
59
- /**
60
- * Triggers when any of the field change.
61
- */
62
- onChange?: WatchObserver<TFieldValues>
63
- /**
64
- * The submit handler.
65
- */
66
- onSubmit: SubmitHandler<TFieldValues>
67
- /**
68
- * Triggers when there are validation errors.
69
- */
70
- onError?: SubmitErrorHandler<TFieldValues>
71
- /**
72
- * The Hook Form state ref.
73
- */
74
- formRef?: React.Ref<UseFormReturn<TFieldValues, TContext>>
75
- /**
76
- * The form children, can be a render prop or a ReactNode.
77
- */
78
- children?: MaybeRenderProp<
79
- FormRenderContext<TFieldValues, TContext, TFieldTypes>
80
- >
81
- /**
82
- * The field resolver, used to resolve the fields from schemas.
83
- */
84
- fieldResolver?: FieldResolver
85
- /**
86
- * Field overrides
87
- */
88
- fields?: DefaultFieldOverrides
89
- }
90
-
91
- export interface FormProps<
92
- TSchema = unknown,
93
- TFieldValues extends FieldValues = FieldValues,
94
- TContext extends object = object,
95
- TExtraFieldProps extends object = object,
96
- TFieldTypes = FieldProps<TFieldValues, TExtraFieldProps>,
97
- > extends UseFormProps<TFieldValues, TContext>,
98
- Omit<
99
- HTMLChakraProps<'form'>,
100
- 'children' | 'onChange' | 'onSubmit' | 'onError'
101
- >,
102
- FormOptions<
103
- TSchema,
104
- TFieldValues,
105
- TContext,
106
- TExtraFieldProps,
107
- TFieldTypes
108
- > {}
109
-
110
- /**
111
- * The wrapper component provides context, state, and focus management.
112
- *
113
- * @see Docs https://saas-ui.dev/docs/components/forms/form
114
- */
115
- export const Form = forwardRef(
116
- <
117
- TSchema = any,
118
- TFieldValues extends FieldValues = FieldValues,
119
- TContext extends object = object,
120
- TExtraFieldProps extends object = object,
121
- TFieldTypes = FieldProps<TFieldValues>,
122
- >(
123
- props: FormProps<
124
- TSchema,
125
- TFieldValues,
126
- TContext,
127
- TExtraFieldProps,
128
- TFieldTypes
129
- >,
130
- ref: React.ForwardedRef<HTMLFormElement>,
131
- ) => {
132
- const {
133
- mode = 'all',
134
- resolver,
135
- fieldResolver,
136
- fields,
137
- reValidateMode,
138
- shouldFocusError,
139
- shouldUnregister,
140
- shouldUseNativeValidation,
141
- criteriaMode,
142
- delayError,
143
- schema,
144
- defaultValues,
145
- values,
146
- context,
147
- resetOptions,
148
- onChange,
149
- onSubmit,
150
- onError,
151
- formRef,
152
- children,
153
- ...rest
154
- } = props
155
-
156
- const form = {
157
- mode,
158
- resolver,
159
- defaultValues,
160
- values,
161
- reValidateMode,
162
- shouldFocusError,
163
- shouldUnregister,
164
- shouldUseNativeValidation,
165
- criteriaMode,
166
- delayError,
167
- context,
168
- resetOptions,
169
- }
170
-
171
- const methods = useForm<TFieldValues, TContext>(form)
172
- const { handleSubmit } = methods
173
-
174
- // This exposes the useForm api through the forwarded ref
175
- React.useImperativeHandle(formRef, () => methods, [formRef, methods])
176
-
177
- React.useEffect(() => {
178
- let subscription: any
179
- if (onChange) {
180
- subscription = methods.watch(onChange)
181
- }
182
- return () => subscription?.unsubscribe()
183
- }, [methods, onChange])
184
-
185
- let _children = children
186
- if (!_children && fieldResolver) {
187
- _children = (
188
- <FormLayout>
189
- <AutoFields />
190
- <SubmitButton {...fields?.submit} />
191
- </FormLayout>
192
- )
193
- }
194
-
195
- return (
196
- <FormProvider
197
- {...methods}
198
- schema={schema}
199
- fieldResolver={fieldResolver}
200
- fields={fields}
201
- >
202
- <chakra.form
203
- ref={ref}
204
- onSubmit={handleSubmit(onSubmit, onError)}
205
- {...rest}
206
- className={cx('sui-form', props.className)}
207
- >
208
- {runIfFn(_children, {
209
- Field: DefaultField as any,
210
- DisplayIf: DisplayIf as any,
211
- ArrayField: ArrayField as any,
212
- ObjectField: ObjectField as any,
213
- ...methods,
214
- })}
215
- </chakra.form>
216
- </FormProvider>
217
- )
218
- },
219
- ) as FormComponent
220
-
221
- export type FormComponent = (<
222
- TSchema = unknown,
223
- TFieldValues extends FieldValues = FieldValues,
224
- TContext extends object = object,
225
- TExtraFieldProps extends object = object,
226
- TFieldTypes = FieldProps<TFieldValues>,
227
- >(
228
- props: FormProps<
229
- TSchema,
230
- TFieldValues,
231
- TContext,
232
- TExtraFieldProps,
233
- TFieldTypes
234
- > & {
235
- ref?: React.ForwardedRef<HTMLFormElement>
236
- },
237
- ) => React.ReactElement) & {
238
- displayName?: string
239
- }
240
-
241
- Form.displayName = 'Form'
242
-
243
- export type GetResolver = <
244
- TFieldValues extends FieldValues,
245
- TContext extends object,
246
- >(
247
- schema: unknown,
248
- ) => (
249
- values: TFieldValues,
250
- context: TContext | undefined,
251
- options: ResolverOptions<TFieldValues>,
252
- ) => Promise<ResolverResult<TFieldValues>>