UNPKG

react-form-with-constraints

Version:
137 lines (136 loc) 5.58 kB
import * as React from 'react'; import { instanceOf } from 'prop-types'; import { assert } from './assert'; import { FieldsStore } from './FieldsStore'; import { InputElement } from './InputElement'; import { notUndefined } from './notUndefined'; import { withFieldDidResetEventEmitter } from './withFieldDidResetEventEmitter'; import { withFieldDidValidateEventEmitter } from './withFieldDidValidateEventEmitter'; import { withFieldWillValidateEventEmitter } from './withFieldWillValidateEventEmitter'; import { withValidateFieldEventEmitter } from './withValidateFieldEventEmitter'; class FormWithConstraintsComponent extends React.PureComponent { } export class FormWithConstraints extends withFieldDidResetEventEmitter(withFieldWillValidateEventEmitter(withFieldDidValidateEventEmitter(withValidateFieldEventEmitter(FormWithConstraintsComponent)))) { constructor() { super(...arguments); this.form = null; this.fieldsStore = new FieldsStore(); this.fieldFeedbacksKeyCounter = 0; } getChildContext() { return { form: this }; } computeFieldFeedbacksKey() { return `${this.fieldFeedbacksKeyCounter++}`; } validateFields(...inputsOrNames) { return this._validateFields(true, ...inputsOrNames); } validateForm() { return this.validateFieldsWithoutFeedback(); } validateFieldsWithoutFeedback(...inputsOrNames) { return this._validateFields(false, ...inputsOrNames); } async _validateFields(forceValidateFields, ...inputsOrNames) { const fields = new Array(); const inputs = this.normalizeInputs(...inputsOrNames); for (let i = 0; i < inputs.length; i++) { const input = inputs[i]; const field = await this.validateField(forceValidateFields, new InputElement(input), input); if (field !== undefined) fields.push(field); } return fields; } async validateField(forceValidateFields, input, nativeInput) { const fieldName = input.name; const field = this.fieldsStore.getField(fieldName); if (field === undefined) { } else if (forceValidateFields || !field.hasFeedbacks()) { field.element = nativeInput; field.clearValidations(); this.emitFieldWillValidateEvent(fieldName); const arrayOfArrays = await this.emitValidateFieldEvent(input); assert(JSON.stringify(arrayOfArrays.flat(Number.POSITIVE_INFINITY).filter(fieldFeedback => notUndefined(fieldFeedback))) === JSON.stringify(field.validations), `FieldsStore does not match emitValidateFieldEvent() result, did the user changed the input rapidly?`); this.emitFieldDidValidateEvent(field); } return field; } normalizeInputs(...inputsOrNames) { let inputs; if (inputsOrNames.length === 0) { inputs = Array.from(this.form.querySelectorAll('[name]')); inputs = inputs.filter(input => input.validity !== undefined); inputs .filter(input => input.type !== 'checkbox' && input.type !== 'radio') .map(input => input.name) .forEach((name, index, self) => { if (self.indexOf(name) !== index) { throw new Error(`Multiple elements matching '[name="${name}"]' inside the form`); } }); } else { inputs = inputsOrNames.map(input => { if (typeof input === 'string') { const query = `[name="${input}"]`; const elements = Array.from(this.form.querySelectorAll(query)); if (elements.some(el => el.validity === undefined)) { throw new Error(`'${query}' should match an <input>, <select> or <textarea>`); } if (elements.filter(el => el.type !== 'checkbox' && el.type !== 'radio').length > 1) { throw new Error(`Multiple elements matching '${query}' inside the form`); } const element = elements[0]; if (element === undefined) { throw new Error(`Could not find field '${query}' inside the form`); } return element; } return input; }); } return inputs; } isValid() { return this.fieldsStore.isValid(); } hasFeedbacks() { return this.fieldsStore.hasFeedbacks(); } reset() { return this.resetFields(); } resetFields(...inputsOrNames) { const fields = new Array(); const inputs = this.normalizeInputs(...inputsOrNames); inputs.forEach(input => { const field = this.resetField(new InputElement(input)); if (field !== undefined) fields.push(field); }); return fields; } resetField(input) { const fieldName = input.name; const field = this.fieldsStore.getField(fieldName); if (field === undefined) { } else { field.clearValidations(); this.emitFieldDidResetEvent(field); } return field; } render() { return React.createElement("form", { ref: form => (this.form = form), ...this.props }); } } FormWithConstraints.childContextTypes = { form: instanceOf(FormWithConstraints).isRequired };