@saas-ui/forms 3.0.0-alpha.0 → 3.0.0-alpha.10
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.
- package/CHANGELOG.md +92 -0
- package/dist/index.d.mts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/zod/index.d.mts +5 -3
- package/dist/zod/index.d.ts +5 -3
- package/dist/zod/index.js +1 -1
- package/dist/zod/index.js.map +1 -1
- package/dist/zod/index.mjs +1 -1
- package/dist/zod/index.mjs.map +1 -1
- package/package.json +9 -29
- package/dist/ajv/index.d.mts +0 -35
- package/dist/ajv/index.d.ts +0 -35
- package/dist/ajv/index.js +0 -57
- package/dist/ajv/index.js.map +0 -1
- package/dist/ajv/index.mjs +0 -30
- package/dist/ajv/index.mjs.map +0 -1
- package/src/array-field.tsx +0 -266
- package/src/auto-form.tsx +0 -71
- package/src/base-field.tsx +0 -69
- package/src/create-field.tsx +0 -171
- package/src/create-form.tsx +0 -100
- package/src/create-step-form.tsx +0 -118
- package/src/default-fields.tsx +0 -233
- package/src/display-field.tsx +0 -46
- package/src/display-if.tsx +0 -69
- package/src/field-resolver.ts +0 -66
- package/src/field.tsx +0 -44
- package/src/fields-context.tsx +0 -33
- package/src/fields.tsx +0 -93
- package/src/form-context.tsx +0 -80
- package/src/form-layout.tsx +0 -59
- package/src/form.tsx +0 -252
- package/src/index.ts +0 -310
- package/src/object-field.tsx +0 -58
- package/src/step-form.tsx +0 -191
- package/src/submit-button.tsx +0 -71
- package/src/types.ts +0 -242
- package/src/use-array-field.tsx +0 -158
- package/src/use-form.tsx +0 -24
- package/src/use-step-form.tsx +0 -202
- package/src/utils.ts +0 -35
- package/src/watch-field.tsx +0 -37
package/src/display-if.tsx
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
import * as React from 'react'
|
2
|
-
import {
|
3
|
-
useWatch,
|
4
|
-
FieldValues,
|
5
|
-
UseFormReturn,
|
6
|
-
FieldPath,
|
7
|
-
} from 'react-hook-form'
|
8
|
-
|
9
|
-
import { useFormContext } from './form-context'
|
10
|
-
|
11
|
-
export interface DisplayIfProps<
|
12
|
-
TFieldValues extends FieldValues = FieldValues,
|
13
|
-
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
14
|
-
> {
|
15
|
-
children: React.ReactElement
|
16
|
-
name: TName
|
17
|
-
defaultValue?: unknown
|
18
|
-
isDisabled?: boolean
|
19
|
-
isExact?: boolean
|
20
|
-
condition?: (value: unknown, context: UseFormReturn<TFieldValues>) => boolean
|
21
|
-
onToggle?: (
|
22
|
-
conditionMatched: boolean,
|
23
|
-
context: UseFormReturn<TFieldValues>
|
24
|
-
) => void
|
25
|
-
}
|
26
|
-
/**
|
27
|
-
* Conditionally render parts of a form.
|
28
|
-
*
|
29
|
-
* @see Docs https://saas-ui.dev/docs/components/forms/form
|
30
|
-
*/
|
31
|
-
export const DisplayIf = <
|
32
|
-
TFieldValues extends FieldValues = FieldValues,
|
33
|
-
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
34
|
-
>({
|
35
|
-
children,
|
36
|
-
name,
|
37
|
-
defaultValue,
|
38
|
-
isDisabled,
|
39
|
-
isExact,
|
40
|
-
condition = (value) => !!value,
|
41
|
-
onToggle,
|
42
|
-
}: DisplayIfProps<TFieldValues, TName>) => {
|
43
|
-
const initializedRef = React.useRef(false)
|
44
|
-
const matchesRef = React.useRef(false)
|
45
|
-
|
46
|
-
const value = useWatch<TFieldValues>({
|
47
|
-
name,
|
48
|
-
defaultValue: defaultValue as any,
|
49
|
-
disabled: isDisabled,
|
50
|
-
exact: isExact,
|
51
|
-
})
|
52
|
-
const context = useFormContext() as any
|
53
|
-
|
54
|
-
const matches = condition(value, context)
|
55
|
-
|
56
|
-
React.useEffect(() => {
|
57
|
-
if (!initializedRef.current) {
|
58
|
-
initializedRef.current = true
|
59
|
-
return
|
60
|
-
}
|
61
|
-
if (matchesRef.current === matches) return
|
62
|
-
matchesRef.current = matches
|
63
|
-
onToggle?.(matches, context)
|
64
|
-
}, [value])
|
65
|
-
|
66
|
-
return matches ? children : null
|
67
|
-
}
|
68
|
-
|
69
|
-
DisplayIf.displayName = 'DisplayIf'
|
package/src/field-resolver.ts
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
import { get } from '@saas-ui/core/utils'
|
2
|
-
|
3
|
-
import { ArrayFieldProps } from './array-field'
|
4
|
-
import { DefaultFields } from './default-fields'
|
5
|
-
import { ObjectFieldProps } from './object-field'
|
6
|
-
import { BaseFieldProps, ValueOf } from './types'
|
7
|
-
|
8
|
-
export type FieldResolver = {
|
9
|
-
getFields(): BaseFieldProps[]
|
10
|
-
getNestedFields(name: string): BaseFieldProps[]
|
11
|
-
}
|
12
|
-
|
13
|
-
export type GetFieldResolver<TSchema = any> = (schema: TSchema) => FieldResolver
|
14
|
-
|
15
|
-
type FieldTypes<FieldDefs = DefaultFields> = ValueOf<{
|
16
|
-
[K in keyof FieldDefs]: FieldDefs[K] extends React.FC<infer Props>
|
17
|
-
? { type?: K } & Omit<Props, 'name'>
|
18
|
-
: never
|
19
|
-
}>
|
20
|
-
|
21
|
-
type SchemaField<FieldDefs = DefaultFields> =
|
22
|
-
| FieldTypes<FieldDefs>
|
23
|
-
| (Omit<ObjectFieldProps, 'name' | 'children'> & {
|
24
|
-
type: 'object'
|
25
|
-
properties?: Record<string, SchemaField<FieldDefs>>
|
26
|
-
})
|
27
|
-
| (Omit<ArrayFieldProps, 'name' | 'children'> & {
|
28
|
-
type: 'array'
|
29
|
-
items?: SchemaField<FieldDefs>
|
30
|
-
})
|
31
|
-
|
32
|
-
export type ObjectSchema<FieldDefs = DefaultFields> = Record<
|
33
|
-
string,
|
34
|
-
SchemaField<FieldDefs>
|
35
|
-
>
|
36
|
-
|
37
|
-
const mapFields = (schema: ObjectSchema): BaseFieldProps[] =>
|
38
|
-
schema &&
|
39
|
-
Object.entries(schema).map(([name, props]) => {
|
40
|
-
const { items, label, title, ...field } = props as any
|
41
|
-
return {
|
42
|
-
...field,
|
43
|
-
name,
|
44
|
-
label: label || title || name, // json schema compatibility
|
45
|
-
}
|
46
|
-
})
|
47
|
-
|
48
|
-
export const objectFieldResolver: GetFieldResolver<ObjectSchema> = (schema) => {
|
49
|
-
const getFields = (): BaseFieldProps[] => {
|
50
|
-
return mapFields(schema)
|
51
|
-
}
|
52
|
-
const getNestedFields = (name: string): BaseFieldProps[] => {
|
53
|
-
const field = get(schema, name)
|
54
|
-
|
55
|
-
if (!field) return []
|
56
|
-
|
57
|
-
if (field.items?.type === 'object') {
|
58
|
-
return mapFields(field.items.properties)
|
59
|
-
} else if (field.type === 'object') {
|
60
|
-
return mapFields(field.properties)
|
61
|
-
}
|
62
|
-
return [field.items]
|
63
|
-
}
|
64
|
-
|
65
|
-
return { getFields, getNestedFields }
|
66
|
-
}
|
package/src/field.tsx
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
import * as React from 'react'
|
2
|
-
|
3
|
-
import { FieldValues, RegisterOptions } from 'react-hook-form'
|
4
|
-
|
5
|
-
import { InputField } from './default-fields'
|
6
|
-
import { useField } from './fields-context'
|
7
|
-
import { useFieldProps } from './form-context'
|
8
|
-
import { FieldProps, type FocusableElement } from './types'
|
9
|
-
|
10
|
-
export type FieldRules = Pick<
|
11
|
-
RegisterOptions,
|
12
|
-
'required' | 'min' | 'max' | 'maxLength' | 'minLength' | 'pattern'
|
13
|
-
>
|
14
|
-
|
15
|
-
const defaultInputType = 'text'
|
16
|
-
|
17
|
-
/**
|
18
|
-
* Form field component.
|
19
|
-
*
|
20
|
-
* Build-in types:
|
21
|
-
* text, number, password, textarea, select, native-select, checkbox, radio, switch, pin
|
22
|
-
*
|
23
|
-
* Will default to a text field if there is no matching type.
|
24
|
-
|
25
|
-
* @see Docs https://saas-ui.dev/docs/components/forms/field
|
26
|
-
*/
|
27
|
-
export const Field = React.forwardRef(
|
28
|
-
<TFieldValues extends FieldValues = FieldValues>(
|
29
|
-
props: FieldProps<TFieldValues>,
|
30
|
-
ref: React.ForwardedRef<FocusableElement>,
|
31
|
-
) => {
|
32
|
-
const { type = defaultInputType, name } = props
|
33
|
-
const overrides = useFieldProps(name)
|
34
|
-
const InputComponent = useField(overrides?.type || type, InputField)
|
35
|
-
|
36
|
-
return <InputComponent ref={ref} {...props} {...overrides} />
|
37
|
-
},
|
38
|
-
) as (<TFieldValues extends FieldValues>(
|
39
|
-
props: FieldProps<TFieldValues> & {
|
40
|
-
ref?: React.ForwardedRef<FocusableElement>
|
41
|
-
},
|
42
|
-
) => React.ReactElement) & {
|
43
|
-
displayName?: string
|
44
|
-
}
|
package/src/fields-context.tsx
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
import React from 'react'
|
2
|
-
|
3
|
-
import type { GetBaseField } from './types'
|
4
|
-
|
5
|
-
export interface FieldsContextValue {
|
6
|
-
fields: Record<string, React.FC<any>>
|
7
|
-
getBaseField?: GetBaseField<any>
|
8
|
-
}
|
9
|
-
|
10
|
-
const FieldsContext = React.createContext<FieldsContextValue | null>(null)
|
11
|
-
|
12
|
-
export const FieldsProvider: React.FC<{
|
13
|
-
value: FieldsContextValue
|
14
|
-
children: React.ReactNode
|
15
|
-
}> = (props) => {
|
16
|
-
return (
|
17
|
-
<FieldsContext.Provider value={props.value}>
|
18
|
-
{props.children}
|
19
|
-
</FieldsContext.Provider>
|
20
|
-
)
|
21
|
-
}
|
22
|
-
|
23
|
-
export const useFieldsContext = () => {
|
24
|
-
return React.useContext(FieldsContext)
|
25
|
-
}
|
26
|
-
|
27
|
-
export const useField = (
|
28
|
-
type: string,
|
29
|
-
fallback: React.FC<any>,
|
30
|
-
): React.FC<any> => {
|
31
|
-
const context = React.useContext(FieldsContext)
|
32
|
-
return context?.fields?.[type] || fallback
|
33
|
-
}
|
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'
|
package/src/form-context.tsx
DELETED
@@ -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
|
-
}
|
package/src/form-layout.tsx
DELETED
@@ -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>>
|