@ws-serenity/react-auto-form
v1.2.0
Published
Component for creating forms by js objects
Downloads
26
Readme
Auto-form
Идея
Для каждого проекта инпуты, используемые в форме выглядят идентично. Отсюда вытекает проблема: шаблонное описание стейта и компонентов для каждой из форм
Компонент был разработан специально для админ-панелей, где размещение компонентов на форме строится по замечательному принципу "чтоб влезло". И все, что нам необходимо - указать уникальные пропсы для каждого из компонентов.
Управлением стейта займется сам компонент в связке с библиотекой react-hook-form.
Important
При описании типов пропса управляемого компонента (который передается в AutoForm.jsx
)
ОБЯЗАТЕЛЬНО НАСЛЕДОВАНИЕ от AfControllable
. Иначе магия работать не будет
Что это за тип такой?
Он обеспечивает интерфейс взаимодействия между инпутом и AutoForm
. Чтобы стейт обновлялся, валидация проходила.
Обязательность наследования сделана намеренной. Эти пропсы невозможно указать при настройки AutoForm.fields
. И это
тоже намеренно, чтобы ничего не сломалось
THE Main hero
Форма один раз настраивается для проекта:
// можем сами указывать компоненты, которые будут использоваться в качетсве инпутов. Описываем их тип (text, select) и принимаемые пропсы
type AfJsxConfig = { text: AfControllableTextFieldProps, select: AfControllableSelectProps }
// инпуты мы уже описали, больше передавать их не нужно, теперь нас интересует только все остальные поля
export type MuiAutoFormProps<Dto extends FieldValues> = Omit<AutoFormProps<AfJsxConfig, Dto>, 'jsx'>
export function MuiAutoForm<Dto extends FieldValues>(props: MuiAutoFormProps<Dto>) {
return <AutoForm<AfJsxConfig, Dto>
jsx={{
text: TextField,
select: Select,
}}
{...props}
/>
}
Примеры настройки компонентов
<input>
// Пропсы всех управляемых компонентов наследуются от типа AfControllable, // который обеспечивает совместимость с react-hook-form // AfControllable содержит все необходимые поля для управления инпутом. // !!!Эти типы не будут отображаться при указании пропсов в `AutoForm.fields`!!! // (и это специально, оно и не надо, чтобы ничего не сломать) // // ЭТО НАСЛЕДОВАНИЕ ОБЯЗАТЕЛЬНО // // также мы наследуемся от пропсов нашего компонента, чтобы мы могли их передавать // сами пропсы могут реализовать абсолютно любой интерфейс, для AutoForm это все не важно export type AfControllableTextFieldProps = TextFieldProps & AfControllable; export const TextField = forwardRef((props: AfControllableTextFieldProps, ref: any) => { // делаем что хотим с пропсами, это больше не является ответственностью AutoForm const { error, ...inputProps } = props; return ( <div className={'text-field'}> <MuiTextField {...inputProps} value={props.value} onChange={props.onChange} ref={ref}/> { !!error?.message && <Alert severity={'error'}>Error object {error.message}</Alert> } </div> ) })
<AutoForm>
interface NestedDto {
department: string;
person: {
firstName: string;
secondName: string;
};
contacts: {
phone: string
email?: string;
}
}
// переиспользуем настроенную форму с инпутами
function TheForm() {
return (
// заранее настроенная форма с компонентами ввода. Настраивается один раз под проект
<MuiAutoForm<NestedDto>
// передаем пропсы в хук, управляющий формой
formConfig = {{
reValidateMode: 'onChange',
mode:'onBlur',
// дефолтные значения
defaultValue:objectToEditFromApi
}}
// кнопки управления формой
actions={jsLikeActions()}
fields={{
// указываем ключи полей, как в react-hook-form как flat-json
// вложенные поля объединяем через "."
'person.firstName': {
// TypeScript может не подсказывать это свойство!!!
// оно совпадает с одним из тех полей, что мы указали AfJsxConfig выше
afType: 'text',
// пропсы, относящиеся конкретно к этому компоненту
label:'Имя',
placeholder:'Введите имя',
// валидация и всякая всячина для react-hook-form
registerOptions:{ ...requiredValidation, ...nameValidation},
},
'person.secondName': {
afType: 'text',
label: 'Фамилия',
registerOptions: { ...requiredValidation, ...nameValidation },
},
'contacts.email': {
afType: 'text',
label: 'Почта',
registerOptions:emailValidation,
},
// поле не является вложенным, просто поле O.O
department: {
// это будет Select
afType: 'select',
label:'Отдел',
registerOptions:requiredValidation,
// опции (<option>), требуемые интерфейсом компонта
options: departmentOptions,
},
}}
// автоматическое размещение компонентов внутри формы, в дальнейшем планируется добавление разных способов размещения компоненттов
// конкретно для gridProcessor указываются столбцы (внешний массив)
// а во внутренних массивах указывается количество занимаемого места каждым из компонентов
layout = {
// под капотом используется display: grid и grid-template-columns(rows)
gridProcessor([
// разместить 2 компонента равномерно
[ 1, 1 ],
// растянуть один компонент на все доступное место
[ 1 ],
// растянуть первы компонент на все доступное место, а второй занимает в 2 раза больше места
[ 1, 2 ],
])
}
onSubmit = {(value)=>alert(JSON.stringify(value))
}/>
)
}
Нестандартные кейсы
Если инпуту (или компоненту actions) требуется доступ ко всему состоянию формы, можно использовать хук useFormContext