All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ducks.form.sagas.ts Maven / Gradle / Ivy

The newest version!
import { takeEvery, put, select, debounce } from 'redux-saga/effects'
import isEmpty from 'lodash/isEmpty'
import { isEqual } from 'lodash'

import { ModelPrefix } from '../../core/datasource/const'
import { Validation, ValidationsKey } from '../../core/validation/types'
import {
    updateModel,
    appendFieldToArray,
    removeFieldFromArray,
    copyFieldArray,
    setModel,
} from '../models/store'
import { failValidate, startValidate } from '../datasource/store'
import { FailValidateAction } from '../datasource/Actions'
import { dataSourceValidationSelector } from '../datasource/selectors'
import { getModelByPrefixAndNameSelector } from '../models/selectors'
import { State } from '../State'

import { makeFormByName, makeFormsByModel } from './selectors'
import {
    setFieldRequired,
    handleBlur,
    handleTouch,
} from './store'
import { Form } from './types'

const validateFields: Record = {}

const includesField = (validations: Validation[], actionField: string) => validations.some(
    validation => validation.on?.some(
        dependencyField => (
            dependencyField === actionField || // full equality
            dependencyField.startsWith(`${actionField}.`) || // fieldName: "field", on: "field.id"
            actionField.startsWith(`${dependencyField}.`) || // fieldName: "field.inner", on: "field"
            actionField.startsWith(`${dependencyField}[`) // fieldName: "field[index]", on: "field"
        ),
    ),
)

const getValidationFields = (state: State, id: string) => {
    const filterValidation = dataSourceValidationSelector(id, ValidationsKey.FilterValidations)(state)
    const validation = dataSourceValidationSelector(id, ValidationsKey.Validations)(state)

    return Object.keys(filterValidation).length ? filterValidation : validation
}

function diffKeys <
    TValue extends Record | undefined | null,
>(first: TValue, second: TValue) {
    if (!first || !second) {
        if (first) { return Object.keys(first) }
        if (second) { return Object.keys(second) }

        return []
    }

    return [...new Set([
        ...Object.keys(first),
        ...Object.keys(second),
    ])].filter(key => !isEqual(first[key], second[key]))
}

export const formPluginSagas = [
    takeEvery(failValidate, function* touchOnFailValidate({ payload, meta }: FailValidateAction) {
        if (!meta?.touched) { return }

        const { prefix, id: datasource, fields } = payload
        const keys = Object.keys(fields)
        const forms: Form[] = yield select(makeFormsByModel(datasource, prefix))

        for (const form of forms) {
            yield put(handleTouch(form.formName, keys))
        }
    }),
    takeEvery(setModel, function* addFieldToBuffer({ payload, meta }) {
        const { prefix, key: datasource, model, isDefault } = payload

        if (isDefault) {
            return
        }
        if (prefix === ModelPrefix.source || prefix === ModelPrefix.selected) {
            return
        }

        const forms: Form[] = yield select(makeFormsByModel(datasource, prefix))
        const form: Form = forms?.[0]

        if (isEmpty(form)) { return }

        const prevModel = getModelByPrefixAndNameSelector(prefix, datasource)(
            // @ts-ignore разобраться с типами
            meta.prevState,
        ) as Record | null
        // @ts-ignore разобраться с типами
        const fields = diffKeys(model, prevModel)

        if (!validateFields[datasource]) {
            validateFields[datasource] = []
        }

        const allValidations: ReturnType> = yield select(
            state => getValidationFields(state, datasource),
        )

        fields.forEach((field) => {
            if (!validateFields[datasource].includes(field)) {
                validateFields[datasource].push(field)
            }

            for (const [fieldName, validations] of Object.entries(allValidations || {})) {
                if (
                    includesField(validations, field) &&
                    !validateFields[datasource].includes(fieldName)
                ) {
                    validateFields[datasource].push(fieldName)
                }
            }
        })
    }),
    takeEvery([
        updateModel,
        appendFieldToArray,
        removeFieldFromArray,
        copyFieldArray,
    ], function* addFieldToBuffer({ meta }) {
        const { key: datasource, field } = meta

        if (!validateFields[datasource]) {
            validateFields[datasource] = []
        }

        if (!validateFields[datasource].includes(field)) {
            validateFields[datasource].push(field)
        }

        const allValidations: ReturnType> = yield select(
            state => getValidationFields(state, datasource),
        )

        for (const [fieldName, validations] of Object.entries(allValidations || {})) {
            if (
                includesField(validations, field) &&
                !validateFields[datasource].includes(fieldName)
            ) {
                validateFields[datasource].push(fieldName)
            }
        }
    }),
    takeEvery([
        handleBlur,
        setFieldRequired,
    ], function* addFieldToBuffer({ payload }) {
        const { formName, fieldName } = payload
        const { datasource }: Form = yield select(makeFormByName(formName))

        if (!validateFields[datasource]) {
            validateFields[datasource] = []
        }

        if (!validateFields[datasource].includes(fieldName)) {
            validateFields[datasource].push(fieldName)
        }
    }),
    debounce(200, setModel, function* startValidateSaga({ payload }) {
        const { prefix, key: datasource } = payload

        const forms: Form[] = yield select(makeFormsByModel(datasource, prefix))
        const form: Form = forms?.[0]

        if (isEmpty(form)) { return }

        const fields = validateFields[datasource]

        delete validateFields[datasource]

        if (!isEmpty(fields)) {
            // @ts-ignore FIXME разобраться TS2554: Expected 1 arguments, but got 5
            yield put(startValidate(datasource, form.validationKey, prefix, fields, { blurValidation: true }))
        }
    }),
    debounce(200, [
        updateModel,
        appendFieldToArray,
        removeFieldFromArray,
        copyFieldArray,
    ], function* startValidateSaga({ meta }) {
        const { key: datasource, prefix } = meta
        const forms: Form[] = yield select(makeFormsByModel(datasource, prefix))
        const form: Form = forms?.[0]

        if (isEmpty(form)) { return }

        const fields = validateFields[datasource]

        delete validateFields[datasource]

        if (!isEmpty(fields)) {
            // @ts-ignore FIXME разобраться TS2554: Expected 1 arguments, but got 5
            yield put(startValidate(datasource, form.validationKey, prefix, fields, { blurValidation: true }))
        }
    }),
    debounce(200, [
        handleBlur,
        setFieldRequired,
    ], function* startValidateSaga({ payload }) {
        const { formName } = payload
        const { datasource, modelPrefix, validationKey } = yield select(makeFormByName(formName))

        const fields = validateFields[datasource]

        delete validateFields[datasource]

        if (!isEmpty(fields)) {
            // @ts-ignore FIXME разобраться TS2554: Expected 1 arguments, but got 5
            yield put(startValidate(datasource, validationKey, modelPrefix, fields, { blurValidation: true }))
        }
    }),
]




© 2015 - 2024 Weber Informatics LLC | Privacy Policy