@saas-ui/forms 2.0.0-next.2 → 2.0.0-next.21
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +194 -0
- package/README.md +53 -6
- package/dist/ajv/index.d.ts +24 -11
- package/dist/ajv/index.js +7 -9
- package/dist/ajv/index.js.map +1 -1
- package/dist/ajv/index.mjs +7 -10
- package/dist/ajv/index.mjs.map +1 -1
- package/dist/index.d.ts +519 -280
- package/dist/index.js +777 -696
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +756 -676
- package/dist/index.mjs.map +1 -1
- package/dist/yup/index.d.ts +525 -21
- package/dist/yup/index.js +21 -9
- package/dist/yup/index.js.map +1 -1
- package/dist/yup/index.mjs +21 -10
- package/dist/yup/index.mjs.map +1 -1
- package/dist/zod/index.d.ts +525 -12
- package/dist/zod/index.js +21 -1
- package/dist/zod/index.js.map +1 -1
- package/dist/zod/index.mjs +21 -3
- package/dist/zod/index.mjs.map +1 -1
- package/package.json +33 -10
- package/src/array-field.tsx +88 -48
- package/src/auto-form.tsx +7 -3
- package/src/base-field.tsx +54 -0
- package/src/create-field.tsx +144 -0
- package/src/create-form.tsx +68 -0
- package/src/create-step-form.tsx +100 -0
- package/src/default-fields.tsx +163 -0
- package/src/display-field.tsx +9 -11
- package/src/display-if.tsx +20 -13
- package/src/field-resolver.ts +10 -8
- package/src/field.tsx +18 -445
- package/src/fields-context.tsx +23 -0
- package/src/fields.tsx +34 -21
- package/src/form-context.tsx +84 -0
- package/src/form.tsx +77 -55
- package/src/index.ts +58 -4
- package/src/input-right-button/input-right-button.stories.tsx +1 -1
- package/src/input-right-button/input-right-button.tsx +0 -2
- package/src/layout.tsx +16 -11
- package/src/number-input/number-input.tsx +9 -5
- package/src/object-field.tsx +35 -13
- package/src/password-input/password-input.stories.tsx +23 -2
- package/src/password-input/password-input.tsx +6 -6
- package/src/pin-input/pin-input.tsx +1 -5
- package/src/radio/radio-input.stories.tsx +1 -1
- package/src/radio/radio-input.tsx +12 -10
- package/src/select/native-select.tsx +1 -4
- package/src/select/select-context.tsx +130 -0
- package/src/select/select.stories.tsx +116 -85
- package/src/select/select.test.tsx +1 -1
- package/src/select/select.tsx +162 -146
- package/src/step-form.tsx +76 -76
- package/src/submit-button.tsx +5 -1
- package/src/types.ts +149 -0
- package/src/use-array-field.tsx +9 -3
- package/src/use-step-form.tsx +54 -9
- package/src/utils.ts +23 -1
- package/src/watch-field.tsx +2 -6
- /package/src/radio/{radio.test.tsx → radio-input.test.tsx} +0 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
import * as React from 'react'
|
2
|
+
import { Controller } from 'react-hook-form'
|
3
|
+
|
4
|
+
import { forwardRef, useMergeRefs } from '@chakra-ui/react'
|
5
|
+
import { callAllHandlers } from '@chakra-ui/utils'
|
6
|
+
import { BaseFieldProps, FieldProps } from './types'
|
7
|
+
import { BaseField } from './base-field'
|
8
|
+
import { useFormContext } from './form-context'
|
9
|
+
|
10
|
+
interface CreateFieldProps {
|
11
|
+
displayName: string
|
12
|
+
hideLabel?: boolean
|
13
|
+
BaseField: React.FC<any>
|
14
|
+
}
|
15
|
+
|
16
|
+
const _createField = (
|
17
|
+
InputComponent: React.FC<any>,
|
18
|
+
{ displayName, hideLabel, BaseField }: CreateFieldProps
|
19
|
+
) => {
|
20
|
+
const Field = forwardRef((props, ref) => {
|
21
|
+
const {
|
22
|
+
id,
|
23
|
+
name,
|
24
|
+
label,
|
25
|
+
help,
|
26
|
+
isDisabled,
|
27
|
+
isInvalid,
|
28
|
+
isReadOnly,
|
29
|
+
isRequired,
|
30
|
+
rules,
|
31
|
+
...inputProps
|
32
|
+
} = props
|
33
|
+
|
34
|
+
const inputRules = {
|
35
|
+
required: isRequired,
|
36
|
+
...rules,
|
37
|
+
}
|
38
|
+
|
39
|
+
return (
|
40
|
+
<BaseField
|
41
|
+
id={id}
|
42
|
+
name={name}
|
43
|
+
label={label}
|
44
|
+
help={help}
|
45
|
+
hideLabel={hideLabel}
|
46
|
+
isDisabled={isDisabled}
|
47
|
+
isInvalid={isInvalid}
|
48
|
+
isReadOnly={isReadOnly}
|
49
|
+
isRequired={isRequired}
|
50
|
+
>
|
51
|
+
<InputComponent
|
52
|
+
ref={ref}
|
53
|
+
id={id}
|
54
|
+
name={name}
|
55
|
+
label={hideLabel ? label : undefined} // Only pass down the label when it should be inline.
|
56
|
+
rules={inputRules}
|
57
|
+
{...inputProps}
|
58
|
+
/>
|
59
|
+
</BaseField>
|
60
|
+
)
|
61
|
+
})
|
62
|
+
Field.displayName = displayName
|
63
|
+
|
64
|
+
return Field
|
65
|
+
}
|
66
|
+
|
67
|
+
const withControlledInput = (InputComponent: React.FC<any>) => {
|
68
|
+
return forwardRef<FieldProps, typeof InputComponent>(
|
69
|
+
({ name, rules, ...inputProps }, ref) => {
|
70
|
+
const { control } = useFormContext()
|
71
|
+
|
72
|
+
return (
|
73
|
+
<Controller
|
74
|
+
name={name}
|
75
|
+
control={control}
|
76
|
+
rules={rules}
|
77
|
+
render={({ field: { ref: _ref, ...field } }) => (
|
78
|
+
<InputComponent
|
79
|
+
{...field}
|
80
|
+
{...inputProps}
|
81
|
+
onChange={callAllHandlers(inputProps.onChange, field.onChange)}
|
82
|
+
onBlur={callAllHandlers(inputProps.onBlur, field.onBlur)}
|
83
|
+
ref={useMergeRefs(ref, _ref)}
|
84
|
+
/>
|
85
|
+
)}
|
86
|
+
/>
|
87
|
+
)
|
88
|
+
}
|
89
|
+
)
|
90
|
+
}
|
91
|
+
|
92
|
+
const withUncontrolledInput = (InputComponent: React.FC<any>) => {
|
93
|
+
return forwardRef<FieldProps, typeof InputComponent>(
|
94
|
+
({ name, rules, ...inputProps }, ref) => {
|
95
|
+
const { register } = useFormContext()
|
96
|
+
|
97
|
+
const { ref: _ref, ...field } = register(name, rules)
|
98
|
+
|
99
|
+
return (
|
100
|
+
<InputComponent
|
101
|
+
{...field}
|
102
|
+
{...inputProps}
|
103
|
+
onChange={callAllHandlers(inputProps.onChange, field.onChange)}
|
104
|
+
onBlur={callAllHandlers(inputProps.onBlur, field.onBlur)}
|
105
|
+
ref={useMergeRefs(ref, _ref)}
|
106
|
+
/>
|
107
|
+
)
|
108
|
+
}
|
109
|
+
)
|
110
|
+
}
|
111
|
+
|
112
|
+
export interface CreateFieldOptions {
|
113
|
+
isControlled?: boolean
|
114
|
+
hideLabel?: boolean
|
115
|
+
BaseField?: React.FC<any>
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Register a new field type
|
120
|
+
* @param type The name for this field in kebab-case, eg `email` or `array-field`
|
121
|
+
* @param component The React component
|
122
|
+
* @param options
|
123
|
+
* @param options.isControlled Set this to true if this is a controlled field.
|
124
|
+
* @param options.hideLabel Hide the field label, for example for the checkbox field.
|
125
|
+
*/
|
126
|
+
export const createField = <TProps extends object>(
|
127
|
+
component: React.FC<TProps>,
|
128
|
+
options?: CreateFieldOptions
|
129
|
+
) => {
|
130
|
+
let InputComponent
|
131
|
+
if (options?.isControlled) {
|
132
|
+
InputComponent = withControlledInput(component)
|
133
|
+
} else {
|
134
|
+
InputComponent = withUncontrolledInput(component)
|
135
|
+
}
|
136
|
+
|
137
|
+
const Field = _createField(InputComponent, {
|
138
|
+
displayName: `${component.displayName ?? 'Custom'}Field`,
|
139
|
+
hideLabel: options?.hideLabel,
|
140
|
+
BaseField: options?.BaseField || BaseField,
|
141
|
+
}) as React.FC<TProps & BaseFieldProps>
|
142
|
+
|
143
|
+
return Field
|
144
|
+
}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import React, { ForwardedRef } from 'react'
|
2
|
+
import { FieldsProvider } from './fields-context'
|
3
|
+
import { Form, FieldValues, FormProps, GetResolver } from './form'
|
4
|
+
import { DefaultFieldOverrides, 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 type FormType<
|
16
|
+
FieldDefs,
|
17
|
+
ExtraProps = object,
|
18
|
+
ExtraOverrides = object
|
19
|
+
> = (<
|
20
|
+
TSchema = unknown,
|
21
|
+
TFieldValues extends FieldValues = FieldValues,
|
22
|
+
TContext extends object = object
|
23
|
+
>(
|
24
|
+
props: WithFields<
|
25
|
+
FormProps<TSchema, TFieldValues, TContext>,
|
26
|
+
FieldDefs,
|
27
|
+
ExtraOverrides
|
28
|
+
> & {
|
29
|
+
ref?: React.ForwardedRef<HTMLFormElement>
|
30
|
+
} & ExtraProps
|
31
|
+
) => React.ReactElement) & {
|
32
|
+
displayName?: string
|
33
|
+
id?: string
|
34
|
+
}
|
35
|
+
|
36
|
+
export function createForm<FieldDefs>({
|
37
|
+
resolver,
|
38
|
+
fieldResolver = objectFieldResolver,
|
39
|
+
fields,
|
40
|
+
}: CreateFormProps<FieldDefs> = {}) {
|
41
|
+
const DefaultForm = forwardRef(
|
42
|
+
<
|
43
|
+
TSchema = any,
|
44
|
+
TFieldValues extends FieldValues = FieldValues,
|
45
|
+
TContext extends object = object
|
46
|
+
>(
|
47
|
+
props: WithFields<FormProps<TSchema, TFieldValues, TContext>, FieldDefs>,
|
48
|
+
ref: ForwardedRef<HTMLFormElement>
|
49
|
+
) => {
|
50
|
+
const { schema, ...rest } = props
|
51
|
+
return (
|
52
|
+
<FieldsProvider value={fields || {}}>
|
53
|
+
<Form
|
54
|
+
ref={ref}
|
55
|
+
resolver={resolver?.(props.schema)}
|
56
|
+
fieldResolver={fieldResolver?.(schema)}
|
57
|
+
{...rest}
|
58
|
+
/>
|
59
|
+
</FieldsProvider>
|
60
|
+
)
|
61
|
+
}
|
62
|
+
) as FormType<FieldDefs>
|
63
|
+
|
64
|
+
DefaultForm.displayName = 'Form'
|
65
|
+
DefaultForm.id = 'Form'
|
66
|
+
|
67
|
+
return DefaultForm
|
68
|
+
}
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import React, { useMemo } from 'react'
|
2
|
+
import { forwardRef } from '@chakra-ui/react'
|
3
|
+
import {
|
4
|
+
ArrayField,
|
5
|
+
DisplayIf,
|
6
|
+
FieldProps,
|
7
|
+
FieldValues,
|
8
|
+
FieldsProvider,
|
9
|
+
Form,
|
10
|
+
GetFieldResolver,
|
11
|
+
ObjectField,
|
12
|
+
} from './'
|
13
|
+
import { Field } from './field'
|
14
|
+
import { FormStep, StepsOptions } from './step-form'
|
15
|
+
import {
|
16
|
+
StepFormProvider,
|
17
|
+
UseStepFormProps,
|
18
|
+
useStepForm,
|
19
|
+
} from './use-step-form'
|
20
|
+
import { StepperProvider } from '@saas-ui/core'
|
21
|
+
import { runIfFn } from '@chakra-ui/utils'
|
22
|
+
import { GetResolver } from './form'
|
23
|
+
|
24
|
+
type StepFormType<FieldDefs, ExtraProps = object, ExtraOverrides = object> = (<
|
25
|
+
TSteps extends StepsOptions<any> = StepsOptions<any>,
|
26
|
+
TFieldValues extends FieldValues = FieldValues,
|
27
|
+
TContext extends object = object,
|
28
|
+
TFieldTypes = FieldProps<TFieldValues>
|
29
|
+
>(
|
30
|
+
props: UseStepFormProps<TSteps, TFieldValues, TContext, TFieldTypes> & {
|
31
|
+
ref?: React.ForwardedRef<HTMLFormElement>
|
32
|
+
}
|
33
|
+
) => React.ReactElement) & {
|
34
|
+
displayName?: string
|
35
|
+
id?: string
|
36
|
+
}
|
37
|
+
|
38
|
+
export type DefaultFormType<
|
39
|
+
FieldDefs = any,
|
40
|
+
ExtraProps = object,
|
41
|
+
ExtraOverrides = object
|
42
|
+
> = (<
|
43
|
+
TFieldValues extends Record<string, any> = any,
|
44
|
+
TContext extends object = object,
|
45
|
+
TSchema = unknown
|
46
|
+
>(
|
47
|
+
props: any
|
48
|
+
) => React.ReactElement) & {
|
49
|
+
displayName?: string
|
50
|
+
id?: string
|
51
|
+
}
|
52
|
+
|
53
|
+
export interface CreateFormProps<FieldDefs> {
|
54
|
+
resolver?: GetResolver
|
55
|
+
fieldResolver?: GetFieldResolver
|
56
|
+
fields?: FieldDefs extends Record<string, React.FC<any>> ? FieldDefs : never
|
57
|
+
}
|
58
|
+
|
59
|
+
export function createStepForm<
|
60
|
+
FieldDefs = any,
|
61
|
+
ExtraProps = object,
|
62
|
+
ExtraOverrides = object
|
63
|
+
>({ fields, resolver, fieldResolver }: CreateFormProps<FieldDefs> = {}) {
|
64
|
+
const StepForm = forwardRef<any, 'div'>((props, ref) => {
|
65
|
+
const { children, steps, ...rest } = props
|
66
|
+
|
67
|
+
const stepper = useStepForm({
|
68
|
+
resolver,
|
69
|
+
fieldResolver,
|
70
|
+
...props,
|
71
|
+
})
|
72
|
+
|
73
|
+
const { getFormProps, ...ctx } = stepper
|
74
|
+
|
75
|
+
const context = useMemo(() => ctx, [ctx])
|
76
|
+
|
77
|
+
return (
|
78
|
+
<StepperProvider value={context}>
|
79
|
+
<StepFormProvider value={context}>
|
80
|
+
<FieldsProvider value={fields || {}}>
|
81
|
+
<Form ref={ref} {...rest} {...getFormProps()}>
|
82
|
+
{runIfFn(children, {
|
83
|
+
...stepper,
|
84
|
+
Field: Field as any,
|
85
|
+
FormStep: FormStep as any,
|
86
|
+
DisplayIf: DisplayIf as any,
|
87
|
+
ArrayField: ArrayField as any,
|
88
|
+
ObjectField: ObjectField as any,
|
89
|
+
})}
|
90
|
+
</Form>
|
91
|
+
</FieldsProvider>
|
92
|
+
</StepFormProvider>
|
93
|
+
</StepperProvider>
|
94
|
+
)
|
95
|
+
}) as StepFormType<FieldDefs, ExtraProps, ExtraOverrides>
|
96
|
+
|
97
|
+
StepForm.displayName = `Step${Form.displayName || Form.name}`
|
98
|
+
|
99
|
+
return StepForm
|
100
|
+
}
|
@@ -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
|
package/src/display-field.tsx
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
import * as React from 'react'
|
2
|
-
import {
|
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 './
|
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
|
-
|
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
|
-
|
44
|
-
FormValue.displayName = 'FormValue'
|
45
|
-
}
|
43
|
+
FormValue.displayName = 'FormValue'
|
package/src/display-if.tsx
CHANGED
@@ -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:
|
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
|
-
|
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
|
44
|
+
const context = useFormContext() as any
|
36
45
|
return condition(value, context) ? children : null
|
37
46
|
}
|
38
47
|
|
39
|
-
|
40
|
-
DisplayIf.displayName = 'DisplayIf'
|
41
|
-
}
|
48
|
+
DisplayIf.displayName = 'DisplayIf'
|
package/src/field-resolver.ts
CHANGED
@@ -1,20 +1,22 @@
|
|
1
|
-
import {
|
1
|
+
import { BaseFieldProps } from './types'
|
2
2
|
|
3
3
|
import { get } from '@chakra-ui/utils'
|
4
4
|
|
5
5
|
export type FieldResolver = {
|
6
|
-
getFields():
|
7
|
-
getNestedFields(name: string):
|
6
|
+
getFields(): BaseFieldProps[]
|
7
|
+
getNestedFields(name: string): BaseFieldProps[]
|
8
8
|
}
|
9
9
|
|
10
|
-
|
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):
|
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
|
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 []
|