@saas-ui/forms 3.0.0-alpha.1 → 3.0.0-alpha.3
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 +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
|
-
}
|