function getFieldFromState(state, model) { const form = getForm(state, toPath(model)[0]); if (!form) return null; return getField(form, toPath(model).slice(1)); }
persistKeys.forEach((persistKey) => { const newPersistKeyPath = toPath(persistKey); const currentIndex = newPersistKeyPath[localPath.length]; const mappedIndex = persistKeysIndexMapping[currentIndex]; if (typeof mappedIndex !== 'undefined') { newPersistKeyPath[localPath.length] = mappedIndex; } else { newPersistKeyPath[localPath.length] = persistKeysIndexMapping[currentIndex] = ++nextIndex; } const persistField = getField(state, persistKey); // Remove old key setFieldDirtyState = icepick.updateIn( setFieldDirtyState, ['fields'], (field) => icepick.dissoc(field, persistKey)); // Update field to new key setFieldDirtyState = setInField( setFieldDirtyState, newPersistKeyPath, persistField); });
export default function combinePath(...paths) { const combinedPath = []; for (const path of paths) { const pathArray = isArrayLike(path) && typeof path !== 'string' ? toArray(path) : toPath(path); combinedPath.push(...pathArray) } return combinedPath; }
return function _createModelReducer(model, initialState = initialModelState) { const modelPath = toPath(model); return (state = initialState, action) => { if (!action.model) { return state; } const path = toPath(action.model); if (!isEqual(path.slice(0, modelPath.length), modelPath)) { return state; } const localPath = path.slice(modelPath.length); switch (action.type) { case actionTypes.CHANGE: if (!localPath.length) { return action.value; } if (isEqual(getter(state, localPath), action.value)) { return state; } return setter(state, localPath, action.value); case actionTypes.RESET: if (!localPath.length) { return initialState; } if (isEqual(getter(state, localPath), getter(initialState, localPath))) { return state; } return setter( state, localPath, getter(initialState, localPath) ); default: return state; } }; };
function getField(state, path) { if (!isPlainObject(state) || !state.fields) { throw new Error(`Error when trying to retrieve field '${path}' from an invalid/empty form state. Must pass in a valid form state as the first argument.`); } const localPath = toPath(path); if (!localPath.length) { return state; } return get( state, ['fields', localPath.join('.')], initialFieldState); }
persistKeys.forEach((persistKey, index) => { const newPersistKeyPath = toPath(persistKey); newPersistKeyPath[localPath.length] = index; const persistField = getField(state, persistKey); // Remove old key setFieldDirtyState = icepick.updateIn( setFieldDirtyState, ['fields'], (field) => icepick.dissoc(field, persistKey)); // Update field to new key setFieldDirtyState = setInField( setFieldDirtyState, newPersistKeyPath, persistField); });
export const setProperty = (state, action) => { if (action.type === SET_PROPERTY) { return setIn(state, toPath(action.path), action.value); } return state; };
return (state = createInitialFormState(model), action) => { if (!action.model) return state; const path = toPath(action.model); if (!isEqual(path.slice(0, modelPath.length), modelPath)) { return state; } const localPath = path.slice(modelPath.length); switch (action.type) { case actionTypes.FOCUS: return setField(state, localPath, { focus: true, blur: false }); case actionTypes.CHANGE: case actionTypes.SET_DIRTY: state = icepick.merge(state, { dirty: true, pristine: false, }); return setField(state, localPath, { dirty: true, pristine: false }); case actionTypes.BLUR: case actionTypes.SET_TOUCHED: return setField(state, localPath, { touched: true, untouched: false, focus: false, blur: true }); case actionTypes.SET_PENDING: return setField(state, localPath, { pending: action.pending, submitted: false }); case actionTypes.SET_VALIDITY: const errors = isPlainObject(action.validity) ? { ...getField(state, localPath).errors, ...mapValues(action.validity, (valid) => !valid) } : !action.validity; state = setField(state, localPath, { errors, valid: isBoolean(errors) ? errors : every(errors, (error) => !error) }); return icepick.merge(state, { valid: every(mapValues(state.fields, (field) => field.valid)) && every(state.errors, (error) => !error) }); case actionTypes.SET_PRISTINE: state = setField(state, localPath, { dirty: false, pristine: true }); const formIsPristine = every(mapValues(state.fields, (field) => field.pristine)); return icepick.merge(state, { pristine: formIsPristine, dirty: !formIsPristine }); case actionTypes.SET_UNTOUCHED: return setField(state, localPath, { touched: false, untouched: true }); case actionTypes.SET_SUBMITTED: return setField(state, localPath, { pending: false, submitted: !!action.submitted }); case actionTypes.SET_INITIAL: case actionTypes.RESET: return resetField(state, localPath); case actionTypes.SET_VIEW_VALUE: return setField(state, localPath, { viewValue: action.value }); default: return state; } };
const formReducer = (state = localInitialFormState, action) => { if (!action.model) { return state; } const path = toPath(action.model); if (!isEqual(path.slice(0, modelPath.length), modelPath)) { return state; } const localPath = path.slice(modelPath.length); let errors; let validity; switch (action.type) { case actionTypes.BATCH: return action.actions.reduce(formReducer, state); case actionTypes.FOCUS: return setField(state, localPath, { blur: false, // will be deprecated focus: true, array: Array.isArray(action.value), }); case actionTypes.CHANGE: { if (action.silent) return state; let setFieldDirtyState = setField(state, localPath, { dirty: true, // will be deprecated pristine: false, value: action.value, }); if (action.removeKeys) { const persistKeys = []; const removeKeys = Object.keys(state.fields).filter((fieldKey) => { const localStringPath = localPath.join('.'); for (const removeKey of action.removeKeys) { const removeKeyPath = `${localStringPath}.${removeKey}`; if (startsWith(fieldKey, removeKeyPath)) return true; } if (startsWith(fieldKey, `${localStringPath}.`)) { persistKeys.push(fieldKey); } return false; }); removeKeys.forEach((removeKey) => { setFieldDirtyState = icepick.updateIn( setFieldDirtyState, ['fields'], (field) => icepick.dissoc(field, removeKey)); }); persistKeys.forEach((persistKey, index) => { const newPersistKeyPath = toPath(persistKey); newPersistKeyPath[localPath.length] = index; const persistField = getField(state, persistKey); // Remove old key setFieldDirtyState = icepick.updateIn( setFieldDirtyState, ['fields'], (field) => icepick.dissoc(field, persistKey)); // Update field to new key setFieldDirtyState = setInField( setFieldDirtyState, newPersistKeyPath, persistField); }); } return icepick.merge(setFieldDirtyState, { dirty: true, // will be deprecated pristine: false, valid: formIsValid(setFieldDirtyState), }); } case actionTypes.SET_DIRTY: { const setDirtyState = icepick.merge(state, { dirty: true, // will be deprecated pristine: false, }); return setField(setDirtyState, localPath, { dirty: true, // will be deprecated pristine: false, }); } case actionTypes.BLUR: case actionTypes.SET_TOUCHED: { const fieldState = setField(state, localPath, { focus: false, touched: true, retouched: state.submitted || state.submitFailed, blur: true, // will be deprecated untouched: false, // will be deprecated }); return icepick.merge(fieldState, { touched: true, retouched: state.submitted || state.submitFailed, untouched: false, // will be deprecated }); } case actionTypes.SET_PENDING: return setField(state, localPath, { pending: action.pending, submitted: false, submitFailed: false, retouched: false, }); case actionTypes.SET_VALIDITY: { if (isPlainObject(action.validity)) { errors = mapValues(action.validity, valid => !valid); } else { errors = !action.validity; } const formIsValidState = setInField(state, localPath, { errors, validity: action.validity, valid: isBoolean(errors) ? !errors : every(errors, error => !error), }); return icepick.merge(formIsValidState, { valid: formIsValid(formIsValidState), }); } case actionTypes.SET_FIELDS_VALIDITY: return map(action.fieldsValidity, (fieldValidity, field) => actions.setValidity(`${model}.${field}`, fieldValidity, action.options) ).reduce(formReducer, state); case actionTypes.SET_ERRORS: { if (isPlainObject(action.errors)) { validity = mapValues(action.errors, error => !error); } else { validity = !action.errors; } const setErrorsState = setInField(state, localPath, { errors: action.errors, validity, valid: isValid(validity), }); return icepick.merge(setErrorsState, { valid: formIsValid(setErrorsState), }); } case actionTypes.RESET_VALIDITY: { let resetValidityState; if (!localPath.length) { resetValidityState = icepick.setIn( state, ['valid'], true); resetValidityState = icepick.setIn( resetValidityState, ['validity'], initialFieldState.validity); resetValidityState = icepick.setIn( resetValidityState, ['errors'], initialFieldState.errors); Object.keys(resetValidityState.fields).forEach((field) => { resetValidityState = icepick.setIn( resetValidityState, ['fields', field, 'valid'], true); resetValidityState = icepick.setIn( resetValidityState, ['fields', field, 'validity'], initialFieldState.validity); resetValidityState = icepick.setIn( resetValidityState, ['fields', field, 'errors'], initialFieldState.errors); }); } else { resetValidityState = icepick.setIn( state, ['fields', localPath.join('.'), 'valid'], true ); resetValidityState = icepick.setIn( resetValidityState, ['fields', localPath.join('.'), 'validity'], initialFieldState.validity ); resetValidityState = icepick.setIn( resetValidityState, ['fields', localPath.join('.'), 'errors'], initialFieldState.errors ); } return icepick.merge(resetValidityState, { valid: formIsValid(resetValidityState), }); } case actionTypes.SET_PRISTINE: { let formIsPristine; let setPristineState; if (!localPath.length) { formIsPristine = true; setPristineState = icepick.merge(state, { fields: mapValues(state.fields, field => ({ ...field, dirty: false, // will be deprecated pristine: true, })), }); } else { setPristineState = setField(state, localPath, { dirty: false, // will be deprecated pristine: true, }); formIsPristine = every(mapValues(setPristineState.fields, field => field.pristine)); } return icepick.merge(setPristineState, { dirty: !formIsPristine, // will be deprecated pristine: formIsPristine, }); } case actionTypes.SET_UNTOUCHED: return setField(state, localPath, { touched: false, untouched: true, // will be deprecated }); case actionTypes.SET_SUBMITTED: return setField(state, localPath, { pending: false, submitted: !!action.submitted, submitFailed: false, touched: true, untouched: false, // will be deprecated }); case actionTypes.SET_SUBMIT_FAILED: return setField(state, localPath, { pending: false, submitted: false, submitFailed: true, touched: true, untouched: false, // will be deprecated }); case actionTypes.SET_INITIAL: case actionTypes.RESET: if (!localPath.length) { return localInitialFormState; } return resetField(state, localPath); case actionTypes.SET_VIEW_VALUE: return setField(state, localPath, { viewValue: action.value, }); default: return state; } };