@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.
@@ -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
- }