UNPKG

react-uncontrolled-form

Version:

A 2kb library for building forms and validation with uncontrolled fields in React.

514 lines (439 loc) 15.3 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : (global = global || self, factory(global['react-uncontrolled-form'] = {}, global.React)); }(this, function (exports, React) { 'use strict'; React = React && React.hasOwnProperty('default') ? React['default'] : React; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } var Form = /*#__PURE__*/ function (_React$Component) { _inherits(Form, _React$Component); function Form(props) { var _this; _classCallCheck(this, Form); _this = _possibleConstructorReturn(this, _getPrototypeOf(Form).call(this, props)); _this.fields = {}; _this.invalidFields = {}; _this.state = { valid: true }; _this.handleSubmit = _this.handleSubmit.bind(_assertThisInitialized(_this)); _this.registerField = _this.registerField.bind(_assertThisInitialized(_this)); _this.unregisterField = _this.unregisterField.bind(_assertThisInitialized(_this)); return _this; } _createClass(Form, [{ key: "validate", value: function validate() { var _this2 = this; return new Promise(function (resolve) { _this2.invalidFields = {}; for (var fieldName in _this2.fields) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = _this2.fields[fieldName][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var field = _step.value; if (field && !field.validate()) { _this2.invalidFields[fieldName] = field; } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator["return"] != null) { _iterator["return"](); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } _this2.setState({ valid: Object.keys(_this2.invalidFields).length === 0 }, resolve); }); } }, { key: "handleSubmit", value: function handleSubmit(e) { var _this3 = this; e.preventDefault(); return this.validate().then(function () { if (_this3.props.onSubmit) { _this3.props.onSubmit({ valid: _this3.state.valid, values: _this3.values(), invalidFields: _this3.invalidFields }); } }); } }, { key: "getField", value: function getField(fieldName) { var fields = this.fields[fieldName] || []; return fields.sort(function (a, b) { return b.state.timestamp - a.state.timestamp; })[0]; } }, { key: "getCheckboxValues", value: function getCheckboxValues(fieldName) { var fieldValues = []; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = this.fields[fieldName][Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var field = _step2.value; if (field.state.value && field.state.checked) { fieldValues.push(field.props.value || field.state.value); } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2["return"] != null) { _iterator2["return"](); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return fieldValues; } }, { key: "values", value: function values() { var _this4 = this; var values = {}; Object.keys(this.fields).map(function (fieldName) { return _this4.getField(fieldName); }).filter(function (field) { return field && !field.props.exclude; }).forEach(function (field) { switch (field.state.type) { case 'checkbox': { var fieldValues = _this4.getCheckboxValues(field.name); if (fieldValues.length === 1) { values[field.name] = fieldValues[0]; } else if (fieldValues.length > 1) { values[field.name] = fieldValues; } break; } default: if (field.state.value !== undefined) { values[field.name] = field.state.value; } } }); return values; } }, { key: "addPropsToChildren", value: function addPropsToChildren(children) { var _this5 = this; if (!children || children.constructor === Function) { return children; } return React.Children.map(children, function (child) { if (child && child.props) { var props = { children: _this5.addPropsToChildren(child.props.children) }; if (child.type.constructor === Function) { props.form = { registerField: _this5.registerField, unregisterField: _this5.unregisterField, initialValues: _this5.props.values, messages: _this5.props.messages }; } child = React.cloneElement(child, props); } return child; }); } }, { key: "registerField", value: function registerField(field) { var name = field.name; if (!this.fields[name]) { this.fields[name] = []; } this.fields[name].push(field); } }, { key: "unregisterField", value: function unregisterField(field) { var fields = this.fields[field.name]; fields.splice(fields.indexOf(field), 1); } }, { key: "render", value: function render() { var children = this.addPropsToChildren(this.props.children); var formProps = Object.assign({}, this.props); delete formProps.values; delete formProps.messages; return React.createElement("form", _extends({}, formProps, { onSubmit: this.handleSubmit }), children); } }]); return Form; }(React.Component); Form.defaultProps = { values: {}, messages: {} }; var Field = /*#__PURE__*/ function (_React$Component) { _inherits(Field, _React$Component); function Field(props) { var _this; _classCallCheck(this, Field); _this = _possibleConstructorReturn(this, _getPrototypeOf(Field).call(this, props)); if (!props.form) { throw new Error('The prop `form` is required. If a Field component is nested ' + 'inside another component, you must pass the `form` prop to it.'); } _this.state = {}; _this.handleChange = _this.handleChange.bind(_assertThisInitialized(_this)); _this.validate = _this.validate.bind(_assertThisInitialized(_this)); return _this; } _createClass(Field, [{ key: "componentWillUnmount", value: function componentWillUnmount() { this.props.form.unregisterField(this); } }, { key: "componentDidMount", value: function componentDidMount() { this.setState({ checked: this.checked(), message: '', timestamp: 0, valid: true, value: this.props.form.initialValues[this.name] }); this.props.form.registerField(this); } }, { key: "validate", value: function validate() { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = this.props.validators[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var validator = _step.value; var result = validator(this.state.value); if (result !== undefined) { this.setState({ valid: false, message: result }); return false; } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator["return"] != null) { _iterator["return"](); } } finally { if (_didIteratorError) { throw _iteratorError; } } } this.setState({ valid: true, message: '' }); return true; } }, { key: "handleChange", value: function handleChange(event) { var _this2 = this; return new Promise(function (resolve) { var state = {}; if (event.target) { state.type = event.target.type; state.timestamp = event.timeStamp; switch (event.target.type) { case 'checkbox': state.checked = event.target.checked; state.value = event.target.checked ? event.target.value || true : null; break; case 'select-multiple': state.value = Array.from(event.target.options).map(function (option) { return option.selected ? option.value : null; }).filter(function (value) { return value; }); break; case 'file': state.value = event.target.files; break; default: state.value = event.target.value; } } else { state.value = event; } if (_this2.props.transform) { state.value = _this2.props.transform(state.value); } _this2.setState(state, function () { if (_this2.onChange) { _this2.onChange(event); } resolve(); }); }); } }, { key: "checked", value: function checked(value) { var initialValue = this.props.form.initialValues[this.name]; return value && value === initialValue || initialValue && !value || initialValue && initialValue.constructor === Array && initialValue.indexOf(value) >= 0; } }, { key: "addPropsToChildren", value: function addPropsToChildren(children) { var _this3 = this; return React.Children.map(children, function (child) { if (child && child.props) { var props = { children: _this3.addPropsToChildren(child.props.children) }; if (child.props.name) { _this3.name = child.props.name; _this3.onChange = child.props.onChange; props.defaultChecked = _this3.checked(child.props.value); props.onChange = _this3.handleChange; var defaultValue = child.props.value || _this3.props.form.initialValues[_this3.name]; var childIsComponent = child.type.constructor === Function; if (childIsComponent) { props.value = child.props.value; } else if (defaultValue) { props.defaultValue = defaultValue; props.value = undefined; } } child = React.cloneElement(child, props); } return child; }); } }, { key: "render", value: function render() { var state = { message: this.state.message || this.props.form.messages[this.name], valid: this.state.valid, value: this.state.value }; var children = this.props.children(state, this.validate); return this.addPropsToChildren(children); } }]); return Field; }(React.Component); Field.defaultProps = { validators: [] }; exports.Field = Field; exports.Form = Form; Object.defineProperty(exports, '__esModule', { value: true }); }));