import React, { ComponentProps, ElementType, LegacyRef } from 'react'; import { FieldNameContext } from './FieldNameContext'; import { InjectedFieldProps } from './InjectedFieldProps'; import useStandardFormInput from './useStandardField'; import { NormalizationFunction } from '../Normalization/NormalizationFunction'; import { ValidationFunction } from '../Validation/ValidationFunction'; import { required as requiredValidator } from '../Validation/validators'; // we attempted to support generic components but failed // so, we assume the actual TRenderComponent has no generic arguments // instead, any concrete TRenderComponent can utilize a TRenderComponent as needed export type RenderComponent< TValue, TRenderComponent extends ElementType > = Partial> extends Partial< InjectedFieldProps > ? TRenderComponent : never; export type RenderComponentProps< TValue, TRenderComponent extends ElementType > = Partial> extends Partial< InjectedFieldProps > ? ComponentProps : never; /** A specific Field instance to be rendered by the given TRenderComponent or by whatever default is reasonable */ export type FieldProps< TForm extends object, TProp extends keyof TForm, TRenderComponent extends ElementType > = { /** Name of the field. Used on submission. */ name: TProp; // somewhat duplicated from useStandardFormInputProps but better for autocomplete /** Component to be rendered. Usually this is a type of input group e.g. `` */ Component: RenderComponent; /** Id of the field. */ id?: string; /** Whether the field should be disabled. */ disabled?: boolean; /** Client side validation functions */ validate?: | ValidationFunction | ValidationFunction[]; /** Function to modify the field value without making the form dirty. (e.g. phone number) */ normalize?: NormalizationFunction; } & Omit< RenderComponentProps, keyof InjectedFieldProps >; /** * Renders whatever Component is passed - injecting the formik values needed to finish wiring up that individual field. * Should no Component be used then the default will be provided by the default lookup based on typeof(TForm[TProp]) */ function Field< TForm extends object, TProp extends keyof TForm, TRenderComponent extends ElementType >( { name, Component, id, disabled, validate, normalize, ...rest }: FieldProps, ref: LegacyRef ) { const [input, meta] = useStandardFormInput({ name: String(name), id: id, disabled: disabled, validate: validate, normalize: normalize, }); const isRequired = rest?.required !== undefined ? rest.required : validate === requiredValidator || (Array.isArray(validate) && validate.includes(requiredValidator)); // a bit of a hack so JSX is happy with us const Wrapped = Component as React.ComponentType< InjectedFieldProps >; return ( ); } // hack to get forwarded refs to work const FieldWithRef = React.forwardRef(Field as any); export default FieldWithRef as typeof Field;