react-formal
Version:
Classy HTML form management for React
242 lines (241 loc) • 8.12 kB
TypeScript
import React from 'react';
import { FieldMeta, MapFromValue, MapToValue, UseFieldProps, ValidateOnConfig } from './useField';
export type FieldEventHandlers = {
onBlur?: (...args: any[]) => any;
onChange?: (...args: any[]) => any;
};
export declare function useMergedEventHandlers({ onBlur: onFieldBlur, onChange: onFieldChange }: FieldEventHandlers, { onBlur, onChange }: FieldEventHandlers): {
onChange: (...args: any[]) => void;
onBlur: (...args: any[]) => void;
};
export type FieldRenderProps<TValue = any> = UseFieldProps<TValue> & {
type: string;
ref?: React.Ref<any>;
};
/**
* When Field renders an Element, it injects a few props.
* In the case none DOM elements it also injects `meta`
*/
export type FieldInjectedProps<TValue = any> = FieldRenderProps<TValue> & {
meta?: FieldMeta;
};
export type FieldProps<TAs extends React.ElementType = any> = {
/**
* The Component Input the form should render. You can sepcify a native element such as 'input' or 'select'
* or provide a Component type class directly. When no type is provided the Field will attempt determine
* the correct input from the Field's schema. A Field corresponding to a `yup.number()`
* will render a `type='number'` etc.
*
* ```jsx
* import Form from 'react-formal';
*
* function MyDateInput({ meta: _, ...props }) {
* return <input {...props} type='datetime-local' />
* }
*
* <Form>
* Provide a type directly
* <Form.Field
* name='dateOfBirth'
* type='time'
* placeholder='time only'
* />
*
* Use a custom Component
* (need native 'datetime' support to see it)
* <Form.Field
* name='dateOfBirth'
* as={MyDateInput}
* />
*
* </Form>
* ```
*
* Custom Inputs should comply with the basic input api contract: set a value via a `value` prop and
* broadcast changes to that value via an `onChange` handler.
*/
as?: TAs;
/**
* The Field name, which should be path corresponding to a specific form `value` path.
*
* ```jsx static
* // given the form value
* value = {
* name: { first: '' }
* languages: ['english', 'spanish']
* }
*
* // the path "name.first" would update the "first" property of the form value
* <Form.Field name='name.first' />
*
* // use indexes for paths that cross arrays
* <Form.Field name='languages[0]' />
*
* ```
*/
name: string | string;
/**
* Configure whether validation occur: onChange, onBlur, or both
* You can also specify a function that receives the Field `meta` object and returns a configuration map
* in order to change validation strategies based on validity or other metadata.
*/
validateOn?: ValidateOnConfig;
/**
* Customize how the Field value maps to the overall Form `value`.
* `mapFromValue` can be a a string property name or a function that returns a
* value for `name`'d path, allowing you to set commuted values from the Field.
*
* ```jsx static
* <Form.Field
* name='name'
* mapFromValue={fieldValue => `${fieldValue.first} ${fieldValue.last}`}
* />
* ```
*
* You can also provide an object hash, mapping paths of the Form `value`
* to fields in the field value using a string field name, or a function accessor.
*
* ```jsx
* import Form from '@docs/components/FormWithResult';
* import * as yup from 'yup';
*
* const getYear = () => (new Date()).getFullYear()
*
* const schema = yup.object({
* dateOfBirth: yup.date().required('Required'),
* age: yup.number()
* });
*
* <Form
* schema={schema}
* defaultValue={schema.default()}
* >
* <label>
* Date of Birth
* <Form.Field
* name='dateOfBirth'
* mapFromValue={{
* 'dateOfBirth': event => event.target.value,
* 'age': ({ target }) => target.valueAsDate ?
* getYear() - target.valueAsDate.getFullYear() : null
* }}/>
* </label>
* <label>
* Age
* <Form.Field name='age' />
* </label>
*
* <Form.Submit type='submit'>Submit</Form.Submit>
* </Form>
* ```
*/
mapFromValue?: MapFromValue;
/**
* Map the Form value to the Field value. By default
* the `name` of the Field is used to extract the relevant
* property from the Form value.
*
* ```jsx static
* <Form.Field
* name='location'
* type="dropdownlist"
* mapToValue={model=> pick(model, 'location', 'locationId')}
* />
* ```
*/
mapToValue?: MapToValue;
/**
* The css class added to the Field Input when it fails validation
*/
errorClass?: string;
/**
* Tells the Field to trigger validation for specific paths.
* Useful when used in conjuction with a `mapFromValue` hash that updates more than one value, or
* if you want to trigger validation for the parent path as well.
*
* > NOTE! This overrides the default behavior of validating the field itself by `name`,
* include the `name` if you want the field to validate itself.
*
* ```jsx static
* <Form.Field name='name.first' validates="name.last" />
* <Form.Field name='name' validates={['name', 'surname']} />
* ```
*/
validates?: string | string[];
/**
* Indicates whether child paths of the current Field
* affect the active state of the field.
*
* ```
* 'names'
* - 'first'
* - 'last'
* ```
*
* Are all considered "part" of a field named `'names'` by default. Does not
* affect which paths are validated, only whether `meta.valid` considers child
* paths for its state.
*/
exclusive?: boolean;
/**
* Disables validation for the Field.
*/
noValidate?: boolean;
/**
* When children is the traditional react element or nodes, they are
* passed through as-is to the Field `type` component.
*
* ```jsx static
* <Field type='select'>
* <option>red</option>
* <option>red</option>
* </Field>
* ```
*
* When `children` is a function, its called with the processed field
* props and field meta.
*
* **Tip:** you can pass `onChange` and `onBlur` handlers
* to the `<Field>` component and it will handle merging them with its own injected
* handlers.
*
* ```jsx static
* <Field name='birthDate'>
* {(props, meta) =>
* <DataProvider>
* <Input {...props} />
* </DataProvider>
* }
* </Field>
* ```
*/
children?: React.ReactNode | ((fieldProps: FieldRenderProps, meta: FieldMeta) => React.ReactNode);
/**
* A value to pass to checkboxs/radios/boolean inputs
*/
value?: any;
className?: string;
/**
* Instruct the field to not inject the `meta` prop into the input,
* defaults to `true` when `as` is a non DOM component
*/
injectMeta?: boolean;
/** An HTML input type attribute */
type?: string;
/** A local onChange handler, will be merged with the injected onChange */
onChange?: (...args: any[]) => any;
/** A local onBlur handler, will be merged with the injected onBlur */
onBlur?: (...args: any[]) => any;
};
export type FieldPropsWithAs<TAs extends React.ElementType> = FieldProps<TAs> & React.RefAttributes<any> & Omit<React.ComponentPropsWithoutRef<TAs>, keyof FieldProps | 'meta' | 'name' | 'value' | 'checked' | 'onChange' | 'onBlur'>;
export declare interface Field<TDefaultControl extends React.ElementType = 'input'> {
<TAs extends React.ElementType = TDefaultControl>(props: FieldPropsWithAs<TAs>): React.ReactElement | null;
displayName?: string;
propTypes?: any;
}
/**
* @alias Field
* @memberof Form
*/
declare const _Field: Field;
export default _Field;