@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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../ajv/src/index.ts","../../ajv/src/ajv-resolver.ts","../../ajv/src/create-ajv-form.ts"],"sourcesContent":["export { ajvFieldResolver, ajvResolver } from './ajv-resolver'\nexport { createAjvForm } from './create-ajv-form'\nexport type { CreateAjvFormProps } from './create-ajv-form'\nexport type { JTDDataType, JTDSchemaType } from 'ajv/dist/jtd'\n\nimport { createAjvForm } from './create-ajv-form'\n\nexport const Form = createAjvForm()\n","import { ajvResolver } from '@hookform/resolvers/ajv'\nimport { objectFieldResolver } from '@saas-ui/forms'\nimport { JSONSchemaType } from 'ajv'\n\nexport { ajvResolver }\n\nexport const ajvFieldResolver = (schema: JSONSchemaType<unknown>) => {\n return objectFieldResolver(schema.properties)\n}\n","import {\n CreateFormProps,\n FormProps,\n WithFields,\n createForm,\n} from '@saas-ui/forms'\nimport { JTDDataType } from 'ajv/dist/jtd'\n\nimport { ajvFieldResolver, ajvResolver } from './ajv-resolver'\n\ntype ResolverArgs = Parameters<typeof ajvResolver>\n\nexport interface CreateAjvFormProps<FieldDefs>\n extends CreateFormProps<FieldDefs> {\n schemaOptions?: ResolverArgs[1]\n resolverOptions?: ResolverArgs[2]\n}\n\ntype ParseJsonSchema<T> = T extends { type: 'object' }\n ? JTDDataType<T> extends infer R\n ? R extends object\n ? R\n : never\n : never\n : never\n\nexport type AjvFormType<\n FieldDefs,\n ExtraProps = object,\n JsonSchema extends Record<string, any> = Record<string, any>,\n> = (<\n TSchema extends JsonSchema = JsonSchema,\n TFieldValues extends ParseJsonSchema<TSchema> = ParseJsonSchema<TSchema>,\n TContext extends object = object,\n>(\n props: WithFields<FormProps<TSchema, TFieldValues, TContext>, FieldDefs> & {\n ref?: React.ForwardedRef<HTMLFormElement>\n } & ExtraProps,\n) => React.ReactElement) & {\n displayName?: string\n id?: string\n}\n\n/**\n * Create a Form component with AJV validation that accepts JSON Type Definition schema\n *\n * @see Docs https://saas-ui.dev/docs/components/forms/form\n * @see https://ajv.js.org/json-type-definition.html\n */\nexport function createAjvForm<FieldDefs>(\n options?: CreateAjvFormProps<FieldDefs>,\n) {\n return createForm({\n resolver: (schema: any) =>\n ajvResolver(schema, options?.schemaOptions, options?.resolverOptions),\n fieldResolver: ajvFieldResolver,\n ...options,\n }) as AjvFormType<FieldDefs>\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,iBAA4B;AAC5B,mBAAoC;AAK7B,IAAM,mBAAmB,CAAC,WAAoC;AACnE,aAAO,kCAAoB,OAAO,UAAU;AAC9C;;;ACRA,IAAAA,gBAKO;AA4CA,SAAS,cACd,SACA;AACA,aAAO,0BAAW;AAAA,IAChB,UAAU,CAAC,eACT,wBAAY,QAAQ,mCAAS,eAAe,mCAAS,eAAe;AAAA,IACtE,eAAe;AAAA,IACf,GAAG;AAAA,EACL,CAAC;AACH;;;AFnDO,IAAM,OAAO,cAAc;","names":["import_forms"]}
@@ -1,30 +0,0 @@
1
- 'use client'
2
-
3
- // ajv/src/ajv-resolver.ts
4
- import { ajvResolver } from "@hookform/resolvers/ajv";
5
- import { objectFieldResolver } from "@saas-ui/forms";
6
- var ajvFieldResolver = (schema) => {
7
- return objectFieldResolver(schema.properties);
8
- };
9
-
10
- // ajv/src/create-ajv-form.ts
11
- import {
12
- createForm
13
- } from "@saas-ui/forms";
14
- function createAjvForm(options) {
15
- return createForm({
16
- resolver: (schema) => ajvResolver(schema, options == null ? void 0 : options.schemaOptions, options == null ? void 0 : options.resolverOptions),
17
- fieldResolver: ajvFieldResolver,
18
- ...options
19
- });
20
- }
21
-
22
- // ajv/src/index.ts
23
- var Form = createAjvForm();
24
- export {
25
- Form,
26
- ajvFieldResolver,
27
- ajvResolver,
28
- createAjvForm
29
- };
30
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../ajv/src/ajv-resolver.ts","../../ajv/src/create-ajv-form.ts","../../ajv/src/index.ts"],"sourcesContent":["import { ajvResolver } from '@hookform/resolvers/ajv'\nimport { objectFieldResolver } from '@saas-ui/forms'\nimport { JSONSchemaType } from 'ajv'\n\nexport { ajvResolver }\n\nexport const ajvFieldResolver = (schema: JSONSchemaType<unknown>) => {\n return objectFieldResolver(schema.properties)\n}\n","import {\n CreateFormProps,\n FormProps,\n WithFields,\n createForm,\n} from '@saas-ui/forms'\nimport { JTDDataType } from 'ajv/dist/jtd'\n\nimport { ajvFieldResolver, ajvResolver } from './ajv-resolver'\n\ntype ResolverArgs = Parameters<typeof ajvResolver>\n\nexport interface CreateAjvFormProps<FieldDefs>\n extends CreateFormProps<FieldDefs> {\n schemaOptions?: ResolverArgs[1]\n resolverOptions?: ResolverArgs[2]\n}\n\ntype ParseJsonSchema<T> = T extends { type: 'object' }\n ? JTDDataType<T> extends infer R\n ? R extends object\n ? R\n : never\n : never\n : never\n\nexport type AjvFormType<\n FieldDefs,\n ExtraProps = object,\n JsonSchema extends Record<string, any> = Record<string, any>,\n> = (<\n TSchema extends JsonSchema = JsonSchema,\n TFieldValues extends ParseJsonSchema<TSchema> = ParseJsonSchema<TSchema>,\n TContext extends object = object,\n>(\n props: WithFields<FormProps<TSchema, TFieldValues, TContext>, FieldDefs> & {\n ref?: React.ForwardedRef<HTMLFormElement>\n } & ExtraProps,\n) => React.ReactElement) & {\n displayName?: string\n id?: string\n}\n\n/**\n * Create a Form component with AJV validation that accepts JSON Type Definition schema\n *\n * @see Docs https://saas-ui.dev/docs/components/forms/form\n * @see https://ajv.js.org/json-type-definition.html\n */\nexport function createAjvForm<FieldDefs>(\n options?: CreateAjvFormProps<FieldDefs>,\n) {\n return createForm({\n resolver: (schema: any) =>\n ajvResolver(schema, options?.schemaOptions, options?.resolverOptions),\n fieldResolver: ajvFieldResolver,\n ...options,\n }) as AjvFormType<FieldDefs>\n}\n","export { ajvFieldResolver, ajvResolver } from './ajv-resolver'\nexport { createAjvForm } from './create-ajv-form'\nexport type { CreateAjvFormProps } from './create-ajv-form'\nexport type { JTDDataType, JTDSchemaType } from 'ajv/dist/jtd'\n\nimport { createAjvForm } from './create-ajv-form'\n\nexport const Form = createAjvForm()\n"],"mappings":";;;AAAA,SAAS,mBAAmB;AAC5B,SAAS,2BAA2B;AAK7B,IAAM,mBAAmB,CAAC,WAAoC;AACnE,SAAO,oBAAoB,OAAO,UAAU;AAC9C;;;ACRA;AAAA,EAIE;AAAA,OACK;AA4CA,SAAS,cACd,SACA;AACA,SAAO,WAAW;AAAA,IAChB,UAAU,CAAC,WACT,YAAY,QAAQ,mCAAS,eAAe,mCAAS,eAAe;AAAA,IACtE,eAAe;AAAA,IACf,GAAG;AAAA,EACL,CAAC;AACH;;;ACnDO,IAAM,OAAO,cAAc;","names":[]}
@@ -1,266 +0,0 @@
1
- import React, { forwardRef } from 'react'
2
-
3
- import { Button, ButtonProps, chakra } from '@chakra-ui/react'
4
- import type { MaybeRenderProp } from '@saas-ui/core/utils'
5
- import { MinusIcon, PlusIcon } from '@saas-ui/react/icons'
6
- import { FieldPath, FieldValues } from 'react-hook-form'
7
-
8
- import { BaseField } from './base-field'
9
- import { useFieldProps } from './form-context'
10
- import { FormLayout, FormLayoutProps } from './form-layout'
11
- import { BaseFieldProps } from './types'
12
- import {
13
- ArrayFieldOptions,
14
- ArrayFieldProvider,
15
- ArrayFieldRowProvider,
16
- UseArrayFieldReturn,
17
- useArrayField,
18
- useArrayFieldAddButton,
19
- useArrayFieldContext,
20
- useArrayFieldRemoveButton,
21
- useArrayFieldRow,
22
- useArrayFieldRowContext,
23
- } from './use-array-field'
24
- import { mapNestedFields } from './utils'
25
-
26
- export interface ArrayFieldButtonProps extends ButtonProps {}
27
-
28
- interface ArrayField {
29
- id: string
30
- [key: string]: unknown
31
- }
32
-
33
- interface ArrayFieldRowProps extends FormLayoutProps {
34
- /**
35
- * The array index
36
- */
37
- index: number
38
- /**
39
- * The fields
40
- */
41
- children: React.ReactNode
42
- }
43
-
44
- /**
45
- * Render prop component, to get access to the internal fields state. Must be a child of ArrayFieldContainer.
46
- *
47
- * @see Docs https://saas-ui.dev/docs/components/forms/array-field
48
- */
49
- export const ArrayFieldRow: React.FC<ArrayFieldRowProps> = ({
50
- children,
51
- index,
52
- ...rowFieldsProps
53
- }) => {
54
- return (
55
- <ArrayFieldRowContainer index={index}>
56
- <ArrayFieldRowFields {...rowFieldsProps}>{children}</ArrayFieldRowFields>
57
- <ArrayFieldRemoveButton />
58
- </ArrayFieldRowContainer>
59
- )
60
- }
61
-
62
- ArrayFieldRow.displayName = 'ArrayFieldRow'
63
-
64
- export interface ArrayFieldRowFieldsProps extends FormLayoutProps {
65
- /**
66
- * The fields
67
- */
68
- children: React.ReactNode
69
- }
70
- /**
71
- * Add the name prefix to the fields and acts as a horizontal form layout by default.
72
- *
73
- * @see Docs https://saas-ui.dev/docs/components/forms/array-field
74
- */
75
- export const ArrayFieldRowFields: React.FC<ArrayFieldRowFieldsProps> = ({
76
- children,
77
- ...layoutProps
78
- }) => {
79
- const { name } = useArrayFieldRowContext()
80
- return (
81
- <FormLayout flex="1" mr="2" {...layoutProps}>
82
- {mapNestedFields(name, children)}
83
- </FormLayout>
84
- )
85
- }
86
-
87
- ArrayFieldRowFields.displayName = 'ArrayFieldRowFields'
88
-
89
- /**
90
- * The row container component providers row context.
91
- *
92
- * @see Docs https://saas-ui.dev/docs/components/forms/array-field
93
- */
94
- export const ArrayFieldRowContainer: React.FC<ArrayFieldRowProps> = ({
95
- children,
96
- index,
97
- ...rest
98
- }) => {
99
- const context = useArrayFieldRow({ index })
100
-
101
- const styles = {
102
- display: 'flex',
103
- flexDirection: 'row',
104
- alignItems: 'flex-end',
105
- width: '100%',
106
- mb: 4,
107
- }
108
-
109
- return (
110
- <ArrayFieldRowProvider value={context}>
111
- <chakra.div {...rest} css={styles}>
112
- {children}
113
- </chakra.div>
114
- </ArrayFieldRowProvider>
115
- )
116
- }
117
-
118
- ArrayFieldRowContainer.displayName = 'ArrayFieldRowContainer'
119
-
120
- /**
121
- * The default remove button.
122
- *
123
- * @see Docs https://saas-ui.dev/docs/components/forms/array-field
124
- */
125
- export const ArrayFieldRemoveButton: React.FC<ArrayFieldButtonProps> = (
126
- props,
127
- ) => {
128
- return (
129
- <Button aria-label="Remove row" {...useArrayFieldRemoveButton()} {...props}>
130
- {props.children || <MinusIcon />}
131
- </Button>
132
- )
133
- }
134
-
135
- ArrayFieldRemoveButton.displayName = 'ArrayFieldRemoveButton'
136
-
137
- /**
138
- * The default add button.
139
- *
140
- * @see Docs https://saas-ui.dev/docs/components/forms/array-field
141
- */
142
- export const ArrayFieldAddButton: React.FC<ArrayFieldButtonProps> = (props) => {
143
- return (
144
- <Button
145
- aria-label="Add row"
146
- float="right"
147
- {...useArrayFieldAddButton()}
148
- {...props}
149
- >
150
- {props.children || <PlusIcon />}
151
- </Button>
152
- )
153
- }
154
-
155
- ArrayFieldAddButton.displayName = 'ArrayFieldAddButton'
156
-
157
- export interface ArrayFieldProps<
158
- TFieldValues extends FieldValues = FieldValues,
159
- TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
160
- > extends ArrayFieldOptions<TFieldValues, TName>,
161
- Omit<
162
- BaseFieldProps<TFieldValues, TName>,
163
- 'name' | 'defaultValue' | 'children'
164
- > {
165
- children: MaybeRenderProp<ArrayField[]>
166
- }
167
-
168
- /**
169
- * The wrapper component that composes the default ArrayField functionality.
170
- *
171
- * @see Docs https://saas-ui.dev/docs/components/forms/array-field
172
- */
173
- export const ArrayField = forwardRef(
174
- (props: ArrayFieldProps, ref: React.ForwardedRef<UseArrayFieldReturn>) => {
175
- const { children, ...containerProps } = props
176
-
177
- const rowFn =
178
- typeof children === 'function'
179
- ? children
180
- : (fields: ArrayField[]) => (
181
- <>
182
- {fields.map(({ id }, index: number) => (
183
- <ArrayFieldRow key={id} index={index}>
184
- {children}
185
- </ArrayFieldRow>
186
- )) || null}
187
- </>
188
- )
189
-
190
- return (
191
- <ArrayFieldContainer ref={ref} {...containerProps}>
192
- <ArrayFieldRows>{rowFn as any}</ArrayFieldRows>
193
- <ArrayFieldAddButton />
194
- </ArrayFieldContainer>
195
- )
196
- },
197
- ) as ((
198
- props: ArrayFieldProps & {
199
- ref?: React.ForwardedRef<UseArrayFieldReturn>
200
- },
201
- ) => React.ReactElement) & {
202
- displayName: string
203
- }
204
-
205
- ArrayField.displayName = 'ArrayField'
206
-
207
- export interface ArrayFieldRowsProps {
208
- children: (fields: ArrayField[]) => React.ReactElement | null
209
- }
210
-
211
- export const ArrayFieldRows = ({
212
- children,
213
- }: ArrayFieldRowsProps): React.ReactElement | null => {
214
- const { fields } = useArrayFieldContext()
215
- return children(fields)
216
- }
217
-
218
- ArrayFieldRows.displayName = 'ArrayFieldRows'
219
-
220
- export interface ArrayFieldContainerProps
221
- extends Omit<ArrayFieldProps, 'children'> {
222
- children: React.ReactNode
223
- }
224
-
225
- /**
226
- * The container component provides context and state management.
227
- *
228
- * @see Docs https://saas-ui.dev/docs/components/forms/array-field
229
- */
230
- export const ArrayFieldContainer = React.forwardRef(
231
- (
232
- {
233
- name,
234
- defaultValue,
235
- keyName,
236
- min,
237
- max,
238
- children,
239
- ...fieldProps
240
- }: ArrayFieldContainerProps,
241
- ref: React.ForwardedRef<UseArrayFieldReturn>,
242
- ) => {
243
- const overrides = useFieldProps(name)
244
-
245
- const context = useArrayField({
246
- name,
247
- defaultValue,
248
- keyName,
249
- min: min || (overrides as any)?.min,
250
- max: max || (overrides as any)?.max,
251
- })
252
-
253
- // This exposes the useArrayField api through the forwarded ref
254
- React.useImperativeHandle(ref, () => context, [ref, context])
255
-
256
- return (
257
- <ArrayFieldProvider value={context}>
258
- <BaseField name={name} {...fieldProps} {...overrides}>
259
- {children}
260
- </BaseField>
261
- </ArrayFieldProvider>
262
- )
263
- },
264
- )
265
-
266
- ArrayFieldContainer.displayName = 'ArrayFieldContainer'
package/src/auto-form.tsx DELETED
@@ -1,71 +0,0 @@
1
- import React, { forwardRef } from 'react'
2
-
3
- import { FieldValues } from 'react-hook-form'
4
-
5
- import { AutoFields } from './fields'
6
- import { Form, FormProps } from './form'
7
- import { FormLayout } from './form-layout'
8
- import { SubmitButton } from './submit-button'
9
-
10
- interface AutoFormOptions {
11
- /**
12
- * The submit button label.
13
- * Pass `null` to render no submit button.
14
- */
15
- submitLabel?: React.ReactNode
16
- /**
17
- * The schema.
18
- * Supports object schema, Zod, Yup or Ajv (JSON Schema).
19
- * @see https://www.saas-ui.dev/docs/forms/auto-form
20
- */
21
- schema: any
22
- /**
23
- * The field resolver.
24
- */
25
- fieldResolver?: any
26
- }
27
-
28
- export interface AutoFormProps<
29
- TFieldValues extends FieldValues,
30
- TContext extends object = object,
31
- > extends Omit<
32
- FormProps<TFieldValues, TContext>,
33
- 'schema' | 'children' | 'fieldResolver'
34
- >,
35
- AutoFormOptions {
36
- children?: React.ReactNode
37
- }
38
- /**
39
- * The wrapper component that manages context and state.
40
- *
41
- * @see Docs https://saas-ui.dev/docs/components/forms/auto-form
42
- */
43
- export const AutoForm = forwardRef(
44
- <
45
- TFieldValues extends FieldValues = FieldValues,
46
- TContext extends object = object,
47
- >(
48
- props: AutoFormProps<TFieldValues, TContext>,
49
- ref: React.ForwardedRef<HTMLFormElement>,
50
- ) => {
51
- const {
52
- schema,
53
- submitLabel = 'Submit',
54
- fieldResolver,
55
- children,
56
- ...rest
57
- } = props
58
-
59
- return (
60
- <Form {...rest} schema={schema} ref={ref}>
61
- <FormLayout>
62
- {<AutoFields schema={schema} fieldResolver={fieldResolver} />}
63
- {submitLabel && <SubmitButton>{submitLabel}</SubmitButton>}
64
- {children}
65
- </FormLayout>
66
- </Form>
67
- )
68
- },
69
- )
70
-
71
- AutoForm.displayName = 'AutoForm'
@@ -1,69 +0,0 @@
1
- import * as React from 'react'
2
-
3
- import { Box, Field } from '@chakra-ui/react'
4
- import { splitProps } from '@saas-ui/core/utils'
5
- import { FormState, get } from 'react-hook-form'
6
-
7
- import { useFormContext } from './form-context'
8
- import type { BaseFieldProps } from './types'
9
-
10
- const getError = (name: string, formState: FormState<{ [x: string]: any }>) => {
11
- return get(formState.errors, name)
12
- }
13
-
14
- const isTouched = (
15
- name: string,
16
- formState: FormState<{ [x: string]: any }>,
17
- ) => {
18
- return get(formState.touchedFields, name)
19
- }
20
-
21
- export const useBaseField = (props: BaseFieldProps) => {
22
- const [fieldProps] = splitProps(props, ['name', 'label', 'help', 'hideLabel'])
23
-
24
- const [controlProps] = splitProps(props, [
25
- // 'id',
26
- // 'orientation',
27
- // 'disabled',
28
- // 'invalid',
29
- // 'readOnly',
30
- // 'required',
31
- ])
32
-
33
- const { formState } = useFormContext()
34
-
35
- const error = getError(fieldProps.name, formState)
36
- const touched = isTouched(fieldProps.name, formState)
37
-
38
- return {
39
- ...fieldProps,
40
- controlProps,
41
- error,
42
- touched,
43
- }
44
- }
45
-
46
- /**
47
- * The default BaseField component
48
- * Composes the Chakra UI FormControl component, with FormLabel, FormHelperText and FormErrorMessage.
49
- */
50
- export const BaseField: React.FC<BaseFieldProps> = (props) => {
51
- const { label, help, hideLabel, error } = useBaseField(props)
52
-
53
- const isInvalid = !!error //|| controlProps.
54
-
55
- return (
56
- <Field.Root invalid={isInvalid} {...props}>
57
- {label && !hideLabel ? <Field.Label>{label}</Field.Label> : null}
58
- <Box width="full">
59
- {props.children}
60
- {help && !error?.message ? (
61
- <Field.HelperText>{help}</Field.HelperText>
62
- ) : null}
63
- {error?.message && <Field.ErrorText>{error?.message}</Field.ErrorText>}
64
- </Box>
65
- </Field.Root>
66
- )
67
- }
68
-
69
- BaseField.displayName = 'BaseField'
@@ -1,171 +0,0 @@
1
- import React, {
2
- type ForwardRefRenderFunction,
3
- type PropsWithoutRef,
4
- forwardRef,
5
- } from 'react'
6
-
7
- import { callAll, mergeRefs, splitProps } from '@saas-ui/core/utils'
8
- import { Controller, type RegisterOptions } from 'react-hook-form'
9
-
10
- import { BaseField } from './base-field'
11
- import { useFieldsContext } from './fields-context'
12
- import { useFormContext } from './form-context'
13
- import { BaseFieldProps, GetBaseField } from './types'
14
-
15
- interface CreateFieldProps<ExtraFieldProps extends object = object> {
16
- displayName: string
17
- hideLabel?: boolean
18
- getBaseField: GetBaseField<ExtraFieldProps>
19
- }
20
-
21
- const _createField = (
22
- InputComponent: React.FC<any>,
23
- { displayName, hideLabel, getBaseField: getBaseFieldProp }: CreateFieldProps,
24
- ) => {
25
- const Field = forwardRef<HTMLDivElement, any>((props, ref) => {
26
- const { id, name, label, isRequired, rules } = props
27
-
28
- const inputRules = {
29
- required: isRequired,
30
- ...rules,
31
- }
32
-
33
- const fieldContext = useFieldsContext()
34
-
35
- const getBaseField = fieldContext?.getBaseField ?? getBaseFieldProp
36
-
37
- const { extraProps, BaseField } = React.useMemo(
38
- () => getBaseField(),
39
- [getBaseField],
40
- )
41
-
42
- const [, inputProps] = splitProps(
43
- props,
44
- [
45
- 'children',
46
- 'name',
47
- 'label',
48
- 'required',
49
- 'disabled',
50
- 'invalid',
51
- 'readOnly',
52
- 'help',
53
- 'hideLabel',
54
- ].concat(extraProps),
55
- )
56
-
57
- return (
58
- <BaseField name={name} hideLabel={hideLabel} {...props}>
59
- <InputComponent
60
- ref={ref}
61
- id={id}
62
- name={name}
63
- label={hideLabel ? label : undefined} // Only pass down the label when it should be inline.
64
- {...inputProps}
65
- rules={inputRules}
66
- />
67
- </BaseField>
68
- )
69
- })
70
-
71
- Field.displayName = displayName
72
-
73
- return Field
74
- }
75
-
76
- const withControlledInput = (InputComponent: React.FC<any>) => {
77
- return forwardRef<typeof InputComponent, ControlProps>((props, ref) => {
78
- const { name, rules, ...inputProps } = props
79
- const { control } = useFormContext()
80
-
81
- const onChange = inputProps.onChange as (...event: any[]) => void
82
-
83
- return (
84
- <Controller
85
- name={name}
86
- control={control}
87
- rules={rules}
88
- render={({ field: { ref: _ref, ...field } }) => (
89
- <InputComponent
90
- {...field}
91
- {...inputProps}
92
- onChange={callAll(onChange, field.onChange)}
93
- onBlur={callAll(inputProps.onBlur, field.onBlur)}
94
- ref={mergeRefs(ref, _ref)}
95
- />
96
- )}
97
- />
98
- )
99
- })
100
- }
101
-
102
- const withUncontrolledInput = (InputComponent: React.FC<any>) => {
103
- return forwardRef<typeof InputComponent, ControlProps>(
104
- ({ name, rules, ...inputProps }, ref) => {
105
- const { register } = useFormContext()
106
-
107
- const { ref: _ref, ...field } = register(name, rules)
108
-
109
- const onChange = inputProps.onChange as (...event: any[]) => void
110
-
111
- return (
112
- <InputComponent
113
- {...field}
114
- {...inputProps}
115
- onChange={callAll(onChange, field.onChange)}
116
- onBlur={callAll(inputProps.onBlur, field.onBlur)}
117
- ref={mergeRefs(ref, _ref)}
118
- />
119
- )
120
- },
121
- )
122
- }
123
-
124
- export interface CreateFieldOptions {
125
- isControlled?: boolean
126
- hideLabel?: boolean
127
- BaseField?: React.FC<any>
128
- }
129
-
130
- interface ControlProps {
131
- name: string
132
- onChange: (...event: any[]) => void
133
- onBlur: (...event: any[]) => void
134
- value: unknown
135
- disabled?: boolean
136
- rules?: RegisterOptions
137
- }
138
-
139
- /**
140
- * Register a new field type
141
- * @param type The name for this field in kebab-case, eg `email` or `array-field`
142
- * @param component The React component
143
- * @param options
144
- * @param options.isControlled Set this to true if this is a controlled field.
145
- * @param options.hideLabel Hide the field label, for example for the checkbox field.
146
- */
147
- export const createField = <TType = unknown, TProps extends object = object>(
148
- component: ForwardRefRenderFunction<
149
- TType,
150
- PropsWithoutRef<TProps & ControlProps>
151
- >,
152
- options?: CreateFieldOptions,
153
- ) => {
154
- let InputComponent
155
- if (options?.isControlled) {
156
- InputComponent = withControlledInput(forwardRef(component))
157
- } else {
158
- InputComponent = withUncontrolledInput(forwardRef(component))
159
- }
160
-
161
- const Field = _createField(InputComponent, {
162
- displayName: `${component.displayName ?? 'Custom'}Field`,
163
- hideLabel: options?.hideLabel,
164
- getBaseField: () => ({
165
- extraProps: [],
166
- BaseField,
167
- }),
168
- }) as React.FC<Omit<BaseFieldProps, keyof TProps> & TProps>
169
-
170
- return Field
171
- }