@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/CHANGELOG.md +21 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -28
- 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 -201
- package/src/utils.ts +0 -35
- package/src/watch-field.tsx +0 -37
package/src/create-form.tsx
DELETED
@@ -1,100 +0,0 @@
|
|
1
|
-
import React, { ForwardedRef, forwardRef, useMemo } from 'react'
|
2
|
-
|
3
|
-
import { defaultFieldTypes } from './default-fields'
|
4
|
-
import { objectFieldResolver } from './field-resolver'
|
5
|
-
import { GetFieldResolver } from './field-resolver'
|
6
|
-
import { FieldsProvider } from './fields-context'
|
7
|
-
import { FieldValues, Form, FormProps, GetResolver } from './form'
|
8
|
-
import { GetBaseField, WithFields } from './types'
|
9
|
-
|
10
|
-
export interface CreateFormProps<
|
11
|
-
FieldDefs,
|
12
|
-
TGetBaseField extends GetBaseField = GetBaseField,
|
13
|
-
> {
|
14
|
-
resolver?: GetResolver
|
15
|
-
fieldResolver?: GetFieldResolver
|
16
|
-
fields?: FieldDefs extends Record<string, React.FC<any>> ? FieldDefs : never
|
17
|
-
getBaseField?: TGetBaseField
|
18
|
-
}
|
19
|
-
|
20
|
-
export type FormType<
|
21
|
-
FieldDefs,
|
22
|
-
ExtraProps = object,
|
23
|
-
ExtraFieldProps extends object = object,
|
24
|
-
ExtraOverrides = object,
|
25
|
-
> = (<
|
26
|
-
TSchema = unknown,
|
27
|
-
TFieldValues extends FieldValues = FieldValues,
|
28
|
-
TContext extends object = object,
|
29
|
-
>(
|
30
|
-
props: WithFields<
|
31
|
-
FormProps<TSchema, TFieldValues, TContext, ExtraFieldProps>,
|
32
|
-
FieldDefs,
|
33
|
-
ExtraOverrides
|
34
|
-
> & {
|
35
|
-
ref?: React.ForwardedRef<HTMLFormElement>
|
36
|
-
} & ExtraProps,
|
37
|
-
) => React.ReactElement) & {
|
38
|
-
displayName?: string
|
39
|
-
id?: string
|
40
|
-
}
|
41
|
-
|
42
|
-
export function createForm<
|
43
|
-
FieldDefs,
|
44
|
-
TGetBaseField extends GetBaseField<any> = GetBaseField<any>,
|
45
|
-
>({
|
46
|
-
resolver,
|
47
|
-
fieldResolver = objectFieldResolver,
|
48
|
-
fields,
|
49
|
-
getBaseField,
|
50
|
-
}: CreateFormProps<FieldDefs, TGetBaseField> = {}) {
|
51
|
-
type ExtraFieldProps =
|
52
|
-
TGetBaseField extends GetBaseField<infer ExtraFieldProps>
|
53
|
-
? ExtraFieldProps
|
54
|
-
: object
|
55
|
-
|
56
|
-
const DefaultForm = forwardRef(
|
57
|
-
<
|
58
|
-
TSchema = any,
|
59
|
-
TFieldValues extends FieldValues = FieldValues,
|
60
|
-
TContext extends object = object,
|
61
|
-
>(
|
62
|
-
props: WithFields<
|
63
|
-
FormProps<TSchema, TFieldValues, TContext, ExtraFieldProps>,
|
64
|
-
FieldDefs
|
65
|
-
>,
|
66
|
-
ref: ForwardedRef<HTMLFormElement>,
|
67
|
-
) => {
|
68
|
-
const {
|
69
|
-
schema,
|
70
|
-
resolver: resolverProp,
|
71
|
-
fieldResolver: fieldResolverProp,
|
72
|
-
...rest
|
73
|
-
} = props
|
74
|
-
|
75
|
-
const fieldsContext = useMemo(
|
76
|
-
() => ({
|
77
|
-
fields: { ...defaultFieldTypes, ...fields },
|
78
|
-
getBaseField,
|
79
|
-
}),
|
80
|
-
[fields, getBaseField],
|
81
|
-
)
|
82
|
-
|
83
|
-
return (
|
84
|
-
<FieldsProvider value={fieldsContext}>
|
85
|
-
<Form
|
86
|
-
ref={ref}
|
87
|
-
resolver={resolverProp ?? resolver?.(props.schema)}
|
88
|
-
fieldResolver={fieldResolverProp ?? fieldResolver?.(schema)}
|
89
|
-
{...rest}
|
90
|
-
/>
|
91
|
-
</FieldsProvider>
|
92
|
-
)
|
93
|
-
},
|
94
|
-
) as FormType<FieldDefs, object, ExtraFieldProps>
|
95
|
-
|
96
|
-
DefaultForm.displayName = 'Form'
|
97
|
-
DefaultForm.id = 'Form'
|
98
|
-
|
99
|
-
return DefaultForm
|
100
|
-
}
|
package/src/create-step-form.tsx
DELETED
@@ -1,118 +0,0 @@
|
|
1
|
-
import React, { forwardRef, useMemo } from 'react'
|
2
|
-
|
3
|
-
import { StepperProvider } from '@saas-ui/core'
|
4
|
-
import { runIfFn } from '@saas-ui/core/utils'
|
5
|
-
|
6
|
-
import {
|
7
|
-
ArrayField,
|
8
|
-
DisplayIf,
|
9
|
-
FieldProps,
|
10
|
-
FieldValues,
|
11
|
-
FieldsProvider,
|
12
|
-
GetFieldResolver,
|
13
|
-
ObjectField,
|
14
|
-
defaultFieldTypes,
|
15
|
-
} from './'
|
16
|
-
import { Field } from './field'
|
17
|
-
import { Form } from './form'
|
18
|
-
import { GetResolver } from './form'
|
19
|
-
import { FormStep, StepsOptions } from './step-form'
|
20
|
-
import { GetBaseField, WithStepFields } from './types'
|
21
|
-
import {
|
22
|
-
StepFormProvider,
|
23
|
-
UseStepFormProps,
|
24
|
-
useStepForm,
|
25
|
-
} from './use-step-form'
|
26
|
-
|
27
|
-
export type StepFormType<
|
28
|
-
FieldDefs,
|
29
|
-
ExtraProps = object,
|
30
|
-
ExtraFieldProps extends object = object,
|
31
|
-
ExtraOverrides = object,
|
32
|
-
> = (<
|
33
|
-
TSteps extends StepsOptions<any> = StepsOptions<any>,
|
34
|
-
TFieldValues extends FieldValues = FieldValues,
|
35
|
-
TContext extends object = object,
|
36
|
-
TFieldTypes = FieldProps<TFieldValues, ExtraFieldProps>,
|
37
|
-
>(
|
38
|
-
props: WithStepFields<
|
39
|
-
UseStepFormProps<TSteps, TFieldValues, TContext>,
|
40
|
-
FieldDefs,
|
41
|
-
ExtraOverrides
|
42
|
-
> & {
|
43
|
-
ref?: React.ForwardedRef<HTMLFormElement>
|
44
|
-
} & ExtraProps,
|
45
|
-
) => React.ReactElement) & {
|
46
|
-
displayName?: string
|
47
|
-
id?: string
|
48
|
-
}
|
49
|
-
|
50
|
-
export interface CreateStepFormProps<
|
51
|
-
FieldDefs,
|
52
|
-
TGetBaseField extends GetBaseField = GetBaseField,
|
53
|
-
> {
|
54
|
-
resolver?: GetResolver
|
55
|
-
fieldResolver?: GetFieldResolver
|
56
|
-
fields?: FieldDefs extends Record<string, React.FC<any>> ? FieldDefs : never
|
57
|
-
getBaseField?: TGetBaseField
|
58
|
-
}
|
59
|
-
|
60
|
-
export function createStepForm<
|
61
|
-
FieldDefs,
|
62
|
-
TGetBaseField extends GetBaseField<any> = GetBaseField<any>,
|
63
|
-
>({
|
64
|
-
fields,
|
65
|
-
resolver,
|
66
|
-
fieldResolver,
|
67
|
-
getBaseField,
|
68
|
-
}: CreateStepFormProps<FieldDefs, TGetBaseField> = {}) {
|
69
|
-
type ExtraFieldProps =
|
70
|
-
TGetBaseField extends GetBaseField<infer ExtraFieldProps>
|
71
|
-
? ExtraFieldProps
|
72
|
-
: object
|
73
|
-
|
74
|
-
const StepForm = forwardRef<HTMLFormElement, any>((props, ref) => {
|
75
|
-
const { children, steps, ...rest } = props
|
76
|
-
|
77
|
-
const stepper = useStepForm({
|
78
|
-
// resolver: resolver(props),
|
79
|
-
// fieldResolver: fieldResolver(,
|
80
|
-
...props,
|
81
|
-
})
|
82
|
-
|
83
|
-
const { getFormProps, ...ctx } = stepper
|
84
|
-
|
85
|
-
const context = useMemo(() => ctx, [ctx])
|
86
|
-
|
87
|
-
const fieldsContext = {
|
88
|
-
fields: {
|
89
|
-
...defaultFieldTypes,
|
90
|
-
...fields,
|
91
|
-
},
|
92
|
-
getBaseField,
|
93
|
-
}
|
94
|
-
|
95
|
-
return (
|
96
|
-
<StepperProvider value={context}>
|
97
|
-
<StepFormProvider value={context}>
|
98
|
-
<FieldsProvider value={fieldsContext}>
|
99
|
-
<Form ref={ref} {...rest} {...getFormProps()}>
|
100
|
-
{runIfFn(children, {
|
101
|
-
...stepper,
|
102
|
-
Field: Field as any,
|
103
|
-
FormStep: FormStep as any,
|
104
|
-
DisplayIf: DisplayIf as any,
|
105
|
-
ArrayField: ArrayField as any,
|
106
|
-
ObjectField: ObjectField as any,
|
107
|
-
})}
|
108
|
-
</Form>
|
109
|
-
</FieldsProvider>
|
110
|
-
</StepFormProvider>
|
111
|
-
</StepperProvider>
|
112
|
-
)
|
113
|
-
}) as StepFormType<FieldDefs, object, ExtraFieldProps>
|
114
|
-
|
115
|
-
StepForm.displayName = `Step${Form.displayName || Form.name}`
|
116
|
-
|
117
|
-
return StepForm
|
118
|
-
}
|
package/src/default-fields.tsx
DELETED
@@ -1,233 +0,0 @@
|
|
1
|
-
import React from 'react'
|
2
|
-
|
3
|
-
import {
|
4
|
-
Input,
|
5
|
-
InputProps,
|
6
|
-
Stack,
|
7
|
-
type SystemStyleObject,
|
8
|
-
Textarea,
|
9
|
-
TextareaProps,
|
10
|
-
createListCollection,
|
11
|
-
} from '@chakra-ui/react'
|
12
|
-
import { Checkbox, type CheckboxProps } from '@saas-ui/react/checkbox'
|
13
|
-
import { InputGroup } from '@saas-ui/react/input-group'
|
14
|
-
import { NumberInput, type NumberInputProps } from '@saas-ui/react/number-input'
|
15
|
-
import {
|
16
|
-
PasswordInput,
|
17
|
-
type PasswordInputProps,
|
18
|
-
} from '@saas-ui/react/password-input'
|
19
|
-
import { PinInput, type PinInputProps } from '@saas-ui/react/pin-input'
|
20
|
-
import { Radio, RadioGroup, type RadioGroupProps } from '@saas-ui/react/radio'
|
21
|
-
import { Select } from '@saas-ui/react/select'
|
22
|
-
import { Switch, type SwitchProps } from '@saas-ui/react/switch'
|
23
|
-
|
24
|
-
import { createField } from './create-field.tsx'
|
25
|
-
import type { FieldOption, FieldOptions } from './types.ts'
|
26
|
-
|
27
|
-
export interface InputFieldProps extends InputProps {
|
28
|
-
type?: string
|
29
|
-
startElement?: React.ReactNode
|
30
|
-
endElement?: React.ReactNode
|
31
|
-
}
|
32
|
-
|
33
|
-
export const InputField = createField<HTMLInputElement, InputFieldProps>(
|
34
|
-
({ type = 'text', startElement, endElement, size, ...rest }, ref) => {
|
35
|
-
return (
|
36
|
-
<InputGroup startElement={startElement} endElement={endElement}>
|
37
|
-
<Input type={type} size={size} {...rest} ref={ref} />
|
38
|
-
</InputGroup>
|
39
|
-
)
|
40
|
-
},
|
41
|
-
)
|
42
|
-
|
43
|
-
export interface NumberInputFieldProps extends NumberInputProps {
|
44
|
-
type: 'number'
|
45
|
-
}
|
46
|
-
|
47
|
-
export const NumberInputField = createField<
|
48
|
-
HTMLInputElement,
|
49
|
-
NumberInputFieldProps
|
50
|
-
>((props, ref) => <NumberInput {...props} ref={ref} />, {
|
51
|
-
isControlled: true,
|
52
|
-
})
|
53
|
-
|
54
|
-
export const PasswordInputField = createField<
|
55
|
-
HTMLInputElement,
|
56
|
-
PasswordInputProps
|
57
|
-
>(({ type = 'password', ...props }, ref) => (
|
58
|
-
<PasswordInput ref={ref} {...props} />
|
59
|
-
))
|
60
|
-
|
61
|
-
export interface TextareaFieldProps extends TextareaProps {}
|
62
|
-
|
63
|
-
export const TextareaField = createField<
|
64
|
-
HTMLTextAreaElement,
|
65
|
-
TextareaFieldProps
|
66
|
-
>((props, ref) => <Textarea {...props} ref={ref} />)
|
67
|
-
|
68
|
-
export interface SwitchFieldProps extends SwitchProps {
|
69
|
-
type: 'switch'
|
70
|
-
}
|
71
|
-
|
72
|
-
export const SwitchField = createField<HTMLInputElement, SwitchFieldProps>(
|
73
|
-
({ type, value, ...rest }, ref) => {
|
74
|
-
return <Switch checked={!!value} {...rest} ref={ref} />
|
75
|
-
},
|
76
|
-
{
|
77
|
-
isControlled: true,
|
78
|
-
},
|
79
|
-
)
|
80
|
-
|
81
|
-
export interface SelectFieldProps
|
82
|
-
extends Omit<Select.RootProps<FieldOption>, 'collection'> {
|
83
|
-
options: FieldOptions
|
84
|
-
placeholder?: string
|
85
|
-
triggerProps?: Select.TriggerProps
|
86
|
-
contentProps?: Select.ContentProps
|
87
|
-
}
|
88
|
-
|
89
|
-
export const SelectField = createField<HTMLDivElement, SelectFieldProps>(
|
90
|
-
(props, ref) => {
|
91
|
-
const {
|
92
|
-
triggerProps,
|
93
|
-
contentProps,
|
94
|
-
options,
|
95
|
-
placeholder,
|
96
|
-
onChange,
|
97
|
-
onValueChange,
|
98
|
-
onBlur,
|
99
|
-
...rest
|
100
|
-
} = props
|
101
|
-
|
102
|
-
const collection = createListCollection({
|
103
|
-
items: options,
|
104
|
-
})
|
105
|
-
|
106
|
-
return (
|
107
|
-
<Select.Root
|
108
|
-
ref={ref}
|
109
|
-
collection={collection}
|
110
|
-
onValueChange={(details) => {
|
111
|
-
onChange(details.value)
|
112
|
-
}}
|
113
|
-
onInteractOutside={() => onBlur()}
|
114
|
-
{...rest}
|
115
|
-
>
|
116
|
-
<Select.Trigger {...triggerProps}>
|
117
|
-
<Select.ValueText placeholder={placeholder} />
|
118
|
-
</Select.Trigger>
|
119
|
-
<Select.Content {...contentProps}>
|
120
|
-
{collection.items.map((option) => (
|
121
|
-
<Select.Item key={option.value} item={option}>
|
122
|
-
{option.label || option.value}
|
123
|
-
</Select.Item>
|
124
|
-
))}
|
125
|
-
</Select.Content>
|
126
|
-
</Select.Root>
|
127
|
-
)
|
128
|
-
},
|
129
|
-
{
|
130
|
-
isControlled: true,
|
131
|
-
},
|
132
|
-
)
|
133
|
-
|
134
|
-
export interface CheckboxFieldProps extends CheckboxProps {
|
135
|
-
type: 'checkbox'
|
136
|
-
label?: string
|
137
|
-
}
|
138
|
-
|
139
|
-
export const CheckboxField = createField<HTMLInputElement, CheckboxFieldProps>(
|
140
|
-
({ label, type, ...props }, ref) => {
|
141
|
-
return (
|
142
|
-
<Checkbox ref={ref} {...props}>
|
143
|
-
{label}
|
144
|
-
</Checkbox>
|
145
|
-
)
|
146
|
-
},
|
147
|
-
{
|
148
|
-
hideLabel: true,
|
149
|
-
},
|
150
|
-
)
|
151
|
-
|
152
|
-
export interface RadioFieldProps extends RadioGroupProps {
|
153
|
-
type: 'radio'
|
154
|
-
options: FieldOptions
|
155
|
-
flexDirection?: SystemStyleObject['flexDirection']
|
156
|
-
gap?: SystemStyleObject['gap']
|
157
|
-
}
|
158
|
-
|
159
|
-
export const RadioField = createField<HTMLInputElement, RadioFieldProps>(
|
160
|
-
(props, ref) => {
|
161
|
-
const { options, onChange, flexDirection = 'column', gap, ...rest } = props
|
162
|
-
return (
|
163
|
-
<RadioGroup
|
164
|
-
ref={ref}
|
165
|
-
onValueChange={({ value }) => {
|
166
|
-
onChange?.(value)
|
167
|
-
}}
|
168
|
-
{...rest}
|
169
|
-
>
|
170
|
-
<Stack flexDirection={flexDirection} gap={gap}>
|
171
|
-
{options.map((option) => (
|
172
|
-
<Radio key={option.value} value={option.value}>
|
173
|
-
{option.label || option.value}
|
174
|
-
</Radio>
|
175
|
-
))}
|
176
|
-
</Stack>
|
177
|
-
</RadioGroup>
|
178
|
-
)
|
179
|
-
},
|
180
|
-
{
|
181
|
-
isControlled: true,
|
182
|
-
},
|
183
|
-
)
|
184
|
-
|
185
|
-
export interface PinFieldProps
|
186
|
-
extends Omit<PinInputProps, 'type' | 'value' | 'onChange'> {
|
187
|
-
type: 'pin'
|
188
|
-
pinLength?: number
|
189
|
-
pinType?: PinInputProps['type']
|
190
|
-
value?: string
|
191
|
-
onChange?: (value: string) => void
|
192
|
-
}
|
193
|
-
|
194
|
-
export const PinField = createField<HTMLInputElement, PinFieldProps>(
|
195
|
-
(props, ref) => {
|
196
|
-
const { pinType, value: valueProp, onChange, ...inputProps } = props
|
197
|
-
|
198
|
-
const value = valueProp?.split('') || []
|
199
|
-
|
200
|
-
return (
|
201
|
-
<PinInput
|
202
|
-
ref={ref}
|
203
|
-
{...inputProps}
|
204
|
-
value={value}
|
205
|
-
onValueChange={(details) => {
|
206
|
-
onChange(details.valueAsString)
|
207
|
-
}}
|
208
|
-
type={pinType}
|
209
|
-
/>
|
210
|
-
)
|
211
|
-
},
|
212
|
-
{
|
213
|
-
isControlled: true,
|
214
|
-
},
|
215
|
-
)
|
216
|
-
|
217
|
-
export const defaultFieldTypes = {
|
218
|
-
text: InputField,
|
219
|
-
email: InputField,
|
220
|
-
url: InputField,
|
221
|
-
phone: InputField,
|
222
|
-
time: InputField,
|
223
|
-
number: NumberInputField,
|
224
|
-
pin: PinField,
|
225
|
-
checkbox: CheckboxField,
|
226
|
-
radio: RadioField,
|
227
|
-
password: PasswordInputField,
|
228
|
-
select: SelectField,
|
229
|
-
switch: SwitchField,
|
230
|
-
textarea: TextareaField,
|
231
|
-
}
|
232
|
-
|
233
|
-
export type DefaultFields = typeof defaultFieldTypes
|
package/src/display-field.tsx
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
import * as React from 'react'
|
2
|
-
|
3
|
-
import { Field as FieldPrimivite, Text } from '@chakra-ui/react'
|
4
|
-
import type { FieldPath, FieldValues } from 'react-hook-form'
|
5
|
-
|
6
|
-
import { useFormContext } from './form-context.tsx'
|
7
|
-
import type { ArrayFieldPath } from './types.ts'
|
8
|
-
|
9
|
-
export interface DisplayFieldProps<
|
10
|
-
TFieldValues extends FieldValues = FieldValues,
|
11
|
-
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
12
|
-
> extends Omit<FieldPrimivite.RootProps, 'type' | 'onChange' | 'defaultValue'> {
|
13
|
-
name: TName | ArrayFieldPath<TName>
|
14
|
-
label?: string
|
15
|
-
}
|
16
|
-
|
17
|
-
/**
|
18
|
-
* Display a field value.
|
19
|
-
*
|
20
|
-
* @see Docs https://saas-ui.dev/
|
21
|
-
*/
|
22
|
-
export const DisplayField: React.FC<DisplayFieldProps> = ({
|
23
|
-
name,
|
24
|
-
label,
|
25
|
-
...props
|
26
|
-
}) => {
|
27
|
-
return (
|
28
|
-
<FieldPrimivite.Root {...props}>
|
29
|
-
{label ? (
|
30
|
-
<FieldPrimivite.Label htmlFor={name}>{label}</FieldPrimivite.Label>
|
31
|
-
) : null}
|
32
|
-
<Text fontSize="md">
|
33
|
-
<FormValue name={name} />
|
34
|
-
</Text>
|
35
|
-
</FieldPrimivite.Root>
|
36
|
-
)
|
37
|
-
}
|
38
|
-
|
39
|
-
DisplayField.displayName = 'DisplayField'
|
40
|
-
|
41
|
-
export const FormValue: React.FC<{ name: string }> = ({ name }) => {
|
42
|
-
const { getValues } = useFormContext()
|
43
|
-
return getValues(name) || null
|
44
|
-
}
|
45
|
-
|
46
|
-
FormValue.displayName = 'FormValue'
|
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
|
-
}
|