UNPKG

input-props

Version:

A React component that provides a two-way data-binding feel to your forms controlled by a mobx state.

499 lines (412 loc) 15.1 kB
import { useObserver } from 'mobx-react'; import React, { useCallback } from 'react'; import { isObservableProp, observable, computed, action } from 'mobx'; import { __decorate } from 'tslib'; 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 _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } it = o[Symbol.iterator](); return it.next.bind(it); } function isUpdatable(v) { return typeof v === 'object' && (v === null || v === void 0 ? void 0 : v.hasOwnProperty('value')) && (v === null || v === void 0 ? void 0 : v.hasOwnProperty('updated')); } /** * similar to fieldProps, but will work with any property that is not an updatable * @param parentObject object that holds the property * @param propertyName name of the property that will be updated * @param onValueChange optional - callback whenever the field changes. This callback has to return true if it accepts the new value, or false if not */ function fieldValueProps(parentObject, propertyName, onValueChange, variant, config, onValueChanged, paramsToValueChange) { if (variant === void 0) { variant = 'all'; } if (config === void 0) { config = {}; } if (onValueChanged === void 0) { onValueChanged = undefined; } if (paramsToValueChange === void 0) { paramsToValueChange = {}; } var getVal = function getVal() { var property = parentObject[propertyName]; if (isUpdatable(property)) { return property.value; } else { return property; } }; var setVal = function setVal(value) { var property = parentObject[propertyName]; if (isUpdatable(property)) { property.value = value; if (!property.updated) { property.updated = true; } } else { parentObject[propertyName] = value; } }; var setValue = function setValue(value) { var old = getVal(); setVal(value); if (onValueChanged) { onValueChanged(old, value, paramsToValueChange); } }; if (isUpdatable(parentObject[propertyName])) { if (!isObservableProp(parentObject[propertyName], 'value')) { throw new Error("Property 'value' on the updatable object is not a mobx observable."); } } else { if (!isObservableProp(parentObject, propertyName)) { throw new Error("Property " + propertyName + " is not an mobx observable."); } } var onChange = function onChange(event) { try { var value = !event || !event.target ? event : event.target.type === 'checkbox' ? returnCheckboxValue(event.target.checked, config.isCheckbox) : returnNormalValue(event.target.value, variant, config); return Promise.resolve(function () { if (getVal() !== value) { if (!checkValue(value, variant, config)) { return; } var _temp2 = function () { if (onValueChange) { return Promise.resolve(onValueChange(value, paramsToValueChange)).then(function (valueChangeRet) { if (typeof valueChangeRet !== "boolean" || valueChangeRet) { setValue(value); } }); } else { setValue(value); } }(); if (_temp2 && _temp2.then) return _temp2.then(function () {}); } }()); } catch (e) { return Promise.reject(e); } }; return { onChange: onChange, value: formatElementValue(getVal(), variant, config) }; } function formatElementValue(value, variant, config) { if (config === void 0) { config = {}; } if (variant === 'numeric') { if (value === undefined || value === null) { value = ""; } else { value = value.toString().trim(); if (value === "-" || value === "0.") ; else if (value === ".") { value = "0."; } else { var _config$numberOfDecim; var split = value.split('.'); var intValue = split[0]; var thousandsSep = config.thousandsSeparator || ','; intValue = intValue.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSep); var decSep = config.decimalsSeparator || '.'; var forceDecimals = typeof config.numberOfDecimalsAlwaysAppearing === 'number' && ((_config$numberOfDecim = config.numberOfDecimalsAlwaysAppearing) !== null && _config$numberOfDecim !== void 0 ? _config$numberOfDecim : 0) > 0; if (split.length > 1) { value = intValue + decSep; if (forceDecimals) { value += split[1].padEnd(config.numberOfDecimalsAlwaysAppearing, '0'); } else { value += split[1]; } } else { value = intValue; if (forceDecimals) { value += "." + "".padEnd(config.numberOfDecimalsAlwaysAppearing, '0'); } } } } } if (config.elementValueModifiers) { for (var _iterator = _createForOfIteratorHelperLoose(config.elementValueModifiers), _step; !(_step = _iterator()).done;) { var modifier = _step.value; value = modifier(value); } } if (value === undefined || value === null) { if (config.elementValueForUndefinedOrNull) { return config.elementValueForUndefinedOrNull(value); } else { return ""; } } return value; } function returnNormalValue(value, variant, config) { if (config === void 0) { config = {}; } if (config.isCheckbox) { throw new Error("This element was configured or inferred as 'checkbox' but it's actually not because event.target.type is not equal to 'checkbox'. Either pass config.isCheckbox as false to InputProps or change the children component to an input type that is a checkbox."); } if (variant === 'numeric') { if (!value) { value = ""; } else { var thousandsSep = config.thousandsSeparator || ','; value = value.toString().trim(); if (value === '.') { value = "0."; } else if (value === '0.') ; else { if (value.length > 1 && value[0] === '0' && value[1] !== '.') { value = value.replace('0', ''); } value = value.replace(new RegExp(thousandsSep, 'g'), ''); var decSep = config.decimalsSeparator || '.'; value = value.replace(decSep, '.'); } } } if (config.stateModifiers) { for (var _iterator2 = _createForOfIteratorHelperLoose(config.stateModifiers), _step2; !(_step2 = _iterator2()).done;) { var modifier = _step2.value; value = modifier(value); } } return value; } function returnCheckboxValue(value, shouldBeCheckbox) { if (!shouldBeCheckbox) { throw new Error("This element was configured or inferred as not being a 'checkbox' but it actually is because event.target.type is equal to 'checkbox'. Either pass config.isCheckbox as true to InputProps or change the children component to an input type that is not a checkbox."); } return value; } function countDecimals(number) { if (Math.floor(number.valueOf()) === number.valueOf()) return 0; var split = number.toString().split("."); return split.length > 1 && split[1].length || 0; } function countIntegerLength(number) { var split = number.toString().split("."); return split[0].length || 0; } function checkValue(value, variant, config) { if (config === void 0) { config = {}; } if (!!config.valueRestrictors) { for (var _iterator3 = _createForOfIteratorHelperLoose(config.valueRestrictors), _step3; !(_step3 = _iterator3()).done;) { var restrictor = _step3.value; if (!restrictor(value)) { return false; } } } switch (variant) { case 'onlyNumbers': case 'numericString': case 'numeric': if (value !== undefined && value !== null && value !== "" && value !== "-") { if (isNaN(value)) { return false; } if (config.maxDecimalPlaces !== undefined && countDecimals(value) > config.maxDecimalPlaces) { return false; } if (config.maxIntegerLength !== undefined && countIntegerLength(value) > config.maxIntegerLength) { return false; } if (config.onlyPositives && value < 0) { return false; } if (config.maxDecimalPlaces === 0 && (value || "").includes('.')) { return false; } } else { if (value === "-" && config.onlyPositives) { return false; } } break; } return true; } /** * A React component that provides a two-way data-binding feel to your forms controlled by a mobx state. * It works by creating the `onChange` and `value` props and passing then down to the children input automatically. * It also passes down the 'helperText' and 'error' props down if an error handler is being used */ function InputProps(_ref) { var _ref$config = _ref.config, config = _ref$config === void 0 ? {} : _ref$config, props = _objectWithoutPropertiesLoose(_ref, ["config"]); var _useObserver = useObserver(function () { var _props$stateObject$pr; return [props.stateObject, props.propertyName, props.onValueChange, props.onValueChanged, config.isCheckbox, props.errorHandler, props.variant, config, props.additionalChangeData, props.stateObject[props.propertyName], props.errorHandler && props.errorHandler.errors && props.errorHandler.getFieldError(props.propertyName), (_props$stateObject$pr = props.stateObject[props.propertyName]) === null || _props$stateObject$pr === void 0 ? void 0 : _props$stateObject$pr.value]; }), stateObject = _useObserver[0], propertyName = _useObserver[1], onValueChange = _useObserver[2], onValueChanged = _useObserver[3], isCheckboxProps = _useObserver[4], errorHandler = _useObserver[5], variant = _useObserver[6], obsConfig = _useObserver[7], additionalChangeData = _useObserver[8], value = _useObserver[9]; var isCheckbox = isCheckboxProps === undefined ? isUpdatable(value) ? typeof value.value === 'boolean' : typeof value === 'boolean' : isCheckboxProps; var newFieldProps = fieldValueProps(stateObject, propertyName, onValueChange, variant, _extends({}, obsConfig, { isCheckbox: isCheckbox }), onValueChanged, additionalChangeData); var onChange = useCallback(newFieldProps.onChange, [stateObject]); var errorProps = !!errorHandler && errorHandler.getFieldError(propertyName); var newProps = isCheckbox ? _extends({ onChange: onChange, checked: newFieldProps.value }, errorProps) : _extends({ onChange: onChange, value: newFieldProps.value }, errorProps); return useObserver(function () { return React.cloneElement(props.children, newProps); }); } var FormErrorHandler = /*#__PURE__*/function () { function FormErrorHandler() { this.errors = []; } /** whether there's any error in any field */ var _proto = FormErrorHandler.prototype; /** use this method to add an error to field. * field must be the the string property name of the field in the model that you are setting the error */ _proto.error = function error(field, _error) { this.errors.push({ field: field, error: _error }); } /** checks whether a field has any error */ ; _proto.fieldHasError = function fieldHasError(field) { return !!this.errors.find(function (item) { return item.field === field; }); } /** gets the error for any specific field */ ; _proto.getFieldError = function getFieldError(field) { var error = this.errors.find(function (item) { return item.field === field; }); if (error) { return { error: true, helperText: error.error }; } else { return { error: false, helperText: "" }; } } /** reset error for specific field */ ; _proto.resetFieldError = function resetFieldError(field) { this.errors = this.errors.filter(function (e) { return e.field !== field; }); } /** reset all errors */ ; _proto.reset = function reset() { this.errors = []; }; _createClass(FormErrorHandler, [{ key: "hasError", get: function get() { return !!this.errors.length; } }]); return FormErrorHandler; }(); __decorate([observable], FormErrorHandler.prototype, "errors", void 0); __decorate([computed], FormErrorHandler.prototype, "hasError", null); __decorate([action], FormErrorHandler.prototype, "error", null); __decorate([action], FormErrorHandler.prototype, "resetFieldError", null); __decorate([action], FormErrorHandler.prototype, "reset", null); export { FormErrorHandler, InputProps }; //# sourceMappingURL=input-props.esm.js.map