@ws-serenity/react-auto-form
TypeScript icon, indicating that this package has built-in type declarations

1.2.0 • Public • Published

Auto-form

Repository

Идея

Для каждого проекта инпуты, используемые в форме выглядят идентично. Отсюда вытекает проблема: шаблонное описание стейта и компонентов для каждой из форм

Компонент был разработан специально для админ-панелей, где размещение компонентов на форме строится по замечательному принципу "чтоб влезло". И все, что нам необходимо - указать уникальные пропсы для каждого из компонентов.

Управлением стейта займется сам компонент в связке с библиотекой 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

/@ws-serenity/react-auto-form/

    Package Sidebar

    Install

    npm i @ws-serenity/react-auto-form

    Weekly Downloads

    3

    Version

    1.2.0

    License

    ISC

    Unpacked Size

    24.3 kB

    Total Files

    13

    Last publish

    Collaborators

    • skukartsev
    • ra.vi.an
    • gransly
    • d.duda
    • blablaprincess
    • a.manakina
    • f.ishchenko