UNPKG

@allpro/form-manager

Version:

Data Manager for React Forms, or for any form data

1,869 lines (1,584 loc) 217 kB
import { useState } from 'react'; import PropTypes from 'prop-types'; function toInteger (dirtyNumber) { if (dirtyNumber === null || dirtyNumber === true || dirtyNumber === false) { return NaN } var number = Number(dirtyNumber); if (isNaN(number)) { return number } return number < 0 ? Math.ceil(number) : Math.floor(number) } var MILLISECONDS_IN_MINUTE = 60000; /** * Google Chrome as of 67.0.3396.87 introduced timezones with offset that includes seconds. * They usually appear for dates that denote time before the timezones were introduced * (e.g. for 'Europe/Prague' timezone the offset is GMT+00:57:44 before 1 October 1891 * and GMT+01:00:00 after that date) * * Date#getTimezoneOffset returns the offset in minutes and would return 57 for the example above, * which would lead to incorrect calculations. * * This function returns the timezone offset in milliseconds that takes seconds in account. */ function getTimezoneOffsetInMilliseconds (dirtyDate) { var date = new Date(dirtyDate.getTime()); var baseTimezoneOffset = date.getTimezoneOffset(); date.setSeconds(0, 0); var millisecondsPartOfTimezoneOffset = date.getTime() % MILLISECONDS_IN_MINUTE; return baseTimezoneOffset * MILLISECONDS_IN_MINUTE + millisecondsPartOfTimezoneOffset } var MILLISECONDS_IN_HOUR = 3600000; var MILLISECONDS_IN_MINUTE$1 = 60000; var DEFAULT_ADDITIONAL_DIGITS = 2; var patterns = { dateTimeDelimiter: /[T ]/, timeZoneDelimiter: /[Z ]/i, timezone: /([Z+-].*)$/ }; var dateRegex = /^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/; var timeRegex = /^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/; var timezoneRegex = /^([+-])(\d{2})(?::?(\d{2}))?$/; /** * @name parseISO * @category Common Helpers * @summary Parse ISO string * * @description * Parse the given string in ISO 8601 format and return an instance of Date. * * Function accepts complete ISO 8601 formats as well as partial implementations. * ISO 8601: http://en.wikipedia.org/wiki/ISO_8601 * * If the argument isn't a string, the function cannot parse the string or * the values are invalid, it returns Invalid Date. * * ### v2.0.0 breaking changes: * * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes). * * - The previous `parse` implementation was renamed to `parseISO`. * * ```javascript * // Before v2.0.0 * parse('2016-01-01') * * // v2.0.0 onward * parseISO('2016-01-01') * ``` * * - `parseISO` now validates separate date and time values in ISO-8601 strings * and returns `Invalid Date` if the date is invalid. * * ```javascript * parseISO('2018-13-32') * //=> Invalid Date * ``` * * - `parseISO` now doesn't fall back to `new Date` constructor * if it fails to parse a string argument. Instead, it returns `Invalid Date`. * * @param {String} argument - the value to convert * @param {Object} [options] - an object with options. * @param {0|1|2} [options.additionalDigits=2] - the additional number of digits in the extended year format * @returns {Date} the parsed date in the local time zone * @throws {TypeError} 1 argument required * @throws {RangeError} `options.additionalDigits` must be 0, 1 or 2 * * @example * // Convert string '2014-02-11T11:30:30' to date: * var result = parseISO('2014-02-11T11:30:30') * //=> Tue Feb 11 2014 11:30:30 * * @example * // Convert string '+02014101' to date, * // if the additional number of digits in the extended year format is 1: * var result = parseISO('+02014101', { additionalDigits: 1 }) * //=> Fri Apr 11 2014 00:00:00 */ function parseISO(argument, dirtyOptions) { if (arguments.length < 1) { throw new TypeError( '1 argument required, but only ' + arguments.length + ' present' ) } var options = dirtyOptions || {}; var additionalDigits = options.additionalDigits == null ? DEFAULT_ADDITIONAL_DIGITS : toInteger(options.additionalDigits); if ( additionalDigits !== 2 && additionalDigits !== 1 && additionalDigits !== 0 ) { throw new RangeError('additionalDigits must be 0, 1 or 2') } if ( !( typeof argument === 'string' || Object.prototype.toString.call(argument) === '[object String]' ) ) { return new Date(NaN) } var dateStrings = splitDateString(argument); var date; if (dateStrings.date) { var parseYearResult = parseYear(dateStrings.date, additionalDigits); date = parseDate(parseYearResult.restDateString, parseYearResult.year); } if (isNaN(date) || !date) { return new Date(NaN) } var timestamp = date.getTime(); var time = 0; var offset; if (dateStrings.time) { time = parseTime(dateStrings.time); if (isNaN(time) || time === null) { return new Date(NaN) } } if (dateStrings.timezone) { offset = parseTimezone(dateStrings.timezone); if (isNaN(offset)) { return new Date(NaN) } } else { var fullTime = timestamp + time; var fullTimeDate = new Date(fullTime); offset = getTimezoneOffsetInMilliseconds(fullTimeDate); // Adjust time when it's coming from DST var fullTimeDateNextDay = new Date(fullTime); fullTimeDateNextDay.setDate(fullTimeDate.getDate() + 1); var offsetDiff = getTimezoneOffsetInMilliseconds(fullTimeDateNextDay) - offset; if (offsetDiff > 0) { offset += offsetDiff; } } return new Date(timestamp + time + offset) } function splitDateString(dateString) { var dateStrings = {}; var array = dateString.split(patterns.dateTimeDelimiter); var timeString; if (/:/.test(array[0])) { dateStrings.date = null; timeString = array[0]; } else { dateStrings.date = array[0]; timeString = array[1]; if (patterns.timeZoneDelimiter.test(dateStrings.date)) { dateStrings.date = dateString.split(patterns.timeZoneDelimiter)[0]; timeString = dateString.substr(dateStrings.date.length, dateString.length); } } if (timeString) { var token = patterns.timezone.exec(timeString); if (token) { dateStrings.time = timeString.replace(token[1], ''); dateStrings.timezone = token[1]; } else { dateStrings.time = timeString; } } return dateStrings } function parseYear(dateString, additionalDigits) { var regex = new RegExp( '^(?:(\\d{4}|[+-]\\d{' + (4 + additionalDigits) + '})|(\\d{2}|[+-]\\d{' + (2 + additionalDigits) + '})$)' ); var captures = dateString.match(regex); // Invalid ISO-formatted year if (!captures) return { year: null } var year = captures[1] && parseInt(captures[1]); var century = captures[2] && parseInt(captures[2]); return { year: century == null ? year : century * 100, restDateString: dateString.slice((captures[1] || captures[2]).length) } } function parseDate(dateString, year) { // Invalid ISO-formatted year if (year === null) return null var captures = dateString.match(dateRegex); // Invalid ISO-formatted string if (!captures) return null var isWeekDate = !!captures[4]; var dayOfYear = parseDateUnit(captures[1]); var month = parseDateUnit(captures[2]) - 1; var day = parseDateUnit(captures[3]); var week = parseDateUnit(captures[4]) - 1; var dayOfWeek = parseDateUnit(captures[5]) - 1; if (isWeekDate) { if (!validateWeekDate(year, week, dayOfWeek)) { return new Date(NaN) } return dayOfISOWeekYear(year, week, dayOfWeek) } else { var date = new Date(0); if ( !validateDate(year, month, day) || !validateDayOfYearDate(year, dayOfYear) ) { return new Date(NaN) } date.setUTCFullYear(year, month, Math.max(dayOfYear, day)); return date } } function parseDateUnit(value) { return value ? parseInt(value) : 1 } function parseTime(timeString) { var captures = timeString.match(timeRegex); if (!captures) return null // Invalid ISO-formatted time var hours = parseTimeUnit(captures[1]); var minutes = parseTimeUnit(captures[2]); var seconds = parseTimeUnit(captures[3]); if (!validateTime(hours, minutes, seconds)) { return NaN } return ( (hours % 24) * MILLISECONDS_IN_HOUR + minutes * MILLISECONDS_IN_MINUTE$1 + seconds * 1000 ) } function parseTimeUnit(value) { return (value && parseFloat(value.replace(',', '.'))) || 0 } function parseTimezone(timezoneString) { if (timezoneString === 'Z') return 0 var captures = timezoneString.match(timezoneRegex); if (!captures) return 0 var sign = captures[1] === '+' ? -1 : 1; var hours = parseInt(captures[2]); var minutes = (captures[3] && parseInt(captures[3])) || 0; if (!validateTimezone(hours, minutes)) { return NaN } return ( sign * (hours * MILLISECONDS_IN_HOUR + minutes * MILLISECONDS_IN_MINUTE$1) ) } function dayOfISOWeekYear(isoWeekYear, week, day) { var date = new Date(0); date.setUTCFullYear(isoWeekYear, 0, 4); var fourthOfJanuaryDay = date.getUTCDay() || 7; var diff = (week || 0) * 7 + (day || 0) + 1 - fourthOfJanuaryDay; date.setUTCDate(date.getUTCDate() + diff); return date } // Validation functions // February is null to handle the leap year (using ||) var daysInMonths = [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; function isLeapYearIndex(year) { return year % 400 === 0 || (year % 4 === 0 && year % 100) } function validateDate(year, month, date) { return !( month < 0 || month > 11 || date < 1 || date > (daysInMonths[month] || (isLeapYearIndex(year) ? 29 : 28)) ) } function validateDayOfYearDate(year, dayOfYear) { return !(dayOfYear < 1 || dayOfYear > (isLeapYearIndex(year) ? 366 : 365)) } function validateWeekDate(_year, week, day) { return !(week < 0 || week > 52 || day < 0 || day > 6) } function validateTime(hours, minutes, seconds) { return !( seconds < 0 || seconds >= 60 || minutes < 0 || minutes >= 60 || hours < 0 || hours >= 25 ) } function validateTimezone(_hours, minutes) { return !(minutes < 0 || minutes > 59) } /** * @name toDate * @category Common Helpers * @summary Convert the given argument to an instance of Date. * * @description * Convert the given argument to an instance of Date. * * If the argument is an instance of Date, the function returns its clone. * * If the argument is a number, it is treated as a timestamp. * * If the argument is none of the above, the function returns Invalid Date. * * **Note**: *all* Date arguments passed to any *date-fns* function is processed by `toDate`. * * @param {Date|Number} argument - the value to convert * @returns {Date} the parsed date in the local time zone * @throws {TypeError} 1 argument required * * @example * // Clone the date: * var result = toDate(new Date(2014, 1, 11, 11, 30, 30)) * //=> Tue Feb 11 2014 11:30:30 * * @example * // Convert the timestamp to date: * var result = toDate(1392098430000) * //=> Tue Feb 11 2014 11:30:30 */ function toDate(argument) { if (arguments.length < 1) { throw new TypeError( '1 argument required, but only ' + arguments.length + ' present' ) } var argStr = Object.prototype.toString.call(argument); // Clone the date if ( argument instanceof Date || (typeof argument === 'object' && argStr === '[object Date]') ) { // Prevent the date to lose the milliseconds when passed to new Date() in IE10 return new Date(argument.getTime()) } else if (typeof argument === 'number' || argStr === '[object Number]') { return new Date(argument) } else { if ( (typeof argument === 'string' || argStr === '[object String]') && typeof console !== 'undefined' ) { console.warn( "Starting with v2.0.0-beta.1 date-fns doesn't accept strings as arguments. Please use `parseISO` to parse strings. See: https://git.io/fjule" ); console.warn(new Error().stack); } return new Date(NaN) } } /** * @name isDate * @category Common Helpers * @summary Is the given value a date? * * @description * Returns true if the given value is an instance of Date. The function works for dates transferred across iframes. * * ### v2.0.0 breaking changes: * * - [Changes that are common for the whole library](https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#Common-Changes). * * @param {*} value - the value to check * @returns {boolean} true if the given value is a date * @throws {TypeError} 1 arguments required * * @example * // For a valid date: * var result = isDate(new Date()) * //=> true * * @example * // For an invalid date: * var result = isDate(new Date(NaN)) * //=> true * * @example * // For some value: * var result = isDate('2014-02-31') * //=> false * * @example * // For an object: * var result = isDate({}) * //=> false */ function isDate(value) { if (arguments.length < 1) { throw new TypeError( '1 argument required, but only ' + arguments.length + ' present' ) } return ( value instanceof Date || (typeof value === 'object' && Object.prototype.toString.call(value) === '[object Date]') ) } var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var jsx = function () { var REACT_ELEMENT_TYPE = typeof Symbol === "function" && Symbol.for && Symbol.for("react.element") || 0xeac7; return function createRawReactElement(type, props, key, children) { var defaultProps = type && type.defaultProps; var childrenLength = arguments.length - 3; if (!props && childrenLength !== 0) { props = {}; } if (props && defaultProps) { for (var propName in defaultProps) { if (props[propName] === void 0) { props[propName] = defaultProps[propName]; } } } else if (!props) { props = defaultProps || {}; } if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { var childArray = Array(childrenLength); for (var i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 3]; } props.children = childArray; } return { $$typeof: REACT_ELEMENT_TYPE, type: type, key: key === undefined ? null : '' + key, ref: null, props: props, _owner: null }; }; }(); var asyncIterator = function (iterable) { if (typeof Symbol === "function") { if (Symbol.asyncIterator) { var method = iterable[Symbol.asyncIterator]; if (method != null) return method.call(iterable); } if (Symbol.iterator) { return iterable[Symbol.iterator](); } } throw new TypeError("Object is not async iterable"); }; var asyncGenerator = function () { function AwaitValue(value) { this.value = value; } function AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; if (value instanceof AwaitValue) { Promise.resolve(value.value).then(function (arg) { resume("next", arg); }, function (arg) { resume("throw", arg); }); } else { settle(result.done ? "return" : "normal", result.value); } } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen.return !== "function") { this.return = undefined; } } if (typeof Symbol === "function" && Symbol.asyncIterator) { AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; }; } AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); }; AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw", arg); }; AsyncGenerator.prototype.return = function (arg) { return this._invoke("return", arg); }; return { wrap: function (fn) { return function () { return new AsyncGenerator(fn.apply(this, arguments)); }; }, await: function (value) { return new AwaitValue(value); } }; }(); var asyncGeneratorDelegate = function (inner, awaitWrap) { var iter = {}, waiting = false; function pump(key, value) { waiting = true; value = new Promise(function (resolve) { resolve(inner[key](value)); }); return { done: false, value: awaitWrap(value) }; } if (typeof Symbol === "function" && Symbol.iterator) { iter[Symbol.iterator] = function () { return this; }; } iter.next = function (value) { if (waiting) { waiting = false; return value; } return pump("next", value); }; if (typeof inner.throw === "function") { iter.throw = function (value) { if (waiting) { waiting = false; throw value; } return pump("throw", value); }; } if (typeof inner.return === "function") { iter.return = function (value) { return pump("return", value); }; } return iter; }; var asyncToGenerator = function (fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var defineEnumerableProperties = function (obj, descs) { for (var key in descs) { var desc = descs[key]; desc.configurable = desc.enumerable = true; if ("value" in desc) desc.writable = true; Object.defineProperty(obj, key, desc); } return obj; }; var defaults = function (obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }; var defineProperty = function (obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }; var _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; }; var get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; var inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }; var _instanceof = function (left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return right[Symbol.hasInstance](left); } else { return left instanceof right; } }; var interopRequireDefault = function (obj) { return obj && obj.__esModule ? obj : { default: obj }; }; var interopRequireWildcard = function (obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }; var newArrowCheck = function (innerThis, boundThis) { if (innerThis !== boundThis) { throw new TypeError("Cannot instantiate an arrow function"); } }; var objectDestructuringEmpty = function (obj) { if (obj == null) throw new TypeError("Cannot destructure undefined"); }; var objectWithoutProperties = function (obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var selfGlobal = typeof global === "undefined" ? self : global; var set = function set(object, property, value, receiver) { var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent !== null) { set(parent, property, value, receiver); } } else if ("value" in desc && desc.writable) { desc.value = value; } else { var setter = desc.set; if (setter !== undefined) { setter.call(receiver, value); } } return value; }; var slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var slicedToArrayLoose = function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; for (var _iterator = arr[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) { _arr.push(_step.value); if (i && _arr.length === i) break; } return _arr; } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; var taggedTemplateLiteral = function (strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }; var taggedTemplateLiteralLoose = function (strings, raw) { strings.raw = raw; return strings; }; var temporalRef = function (val, name, undef) { if (val === undef) { throw new ReferenceError(name + " is not defined - temporal dead zone"); } else { return val; } }; var temporalUndefined = {}; var toArray = function (arr) { return Array.isArray(arr) ? arr : Array.from(arr); }; var toConsumableArray = function (arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }; var babelHelpers = /*#__PURE__*/Object.freeze({ jsx: jsx, asyncIterator: asyncIterator, asyncGenerator: asyncGenerator, asyncGeneratorDelegate: asyncGeneratorDelegate, asyncToGenerator: asyncToGenerator, classCallCheck: classCallCheck, createClass: createClass, defineEnumerableProperties: defineEnumerableProperties, defaults: defaults, defineProperty: defineProperty, get: get, inherits: inherits, interopRequireDefault: interopRequireDefault, interopRequireWildcard: interopRequireWildcard, newArrowCheck: newArrowCheck, objectDestructuringEmpty: objectDestructuringEmpty, objectWithoutProperties: objectWithoutProperties, possibleConstructorReturn: possibleConstructorReturn, selfGlobal: selfGlobal, set: set, slicedToArray: slicedToArray, slicedToArrayLoose: slicedToArrayLoose, taggedTemplateLiteral: taggedTemplateLiteral, taggedTemplateLiteralLoose: taggedTemplateLiteralLoose, temporalRef: temporalRef, temporalUndefined: temporalUndefined, toArray: toArray, toConsumableArray: toConsumableArray, typeof: _typeof, extends: _extends, instanceof: _instanceof }); /** * Helpers to avoid importing Lodash methods. * Some ideas here taken from Lodash. */ var UNDEFINED = void 0; var MAX_SAFE_INTEGER = 9007199254740991; var protoToString = Object.prototype.toString; var isUndefined = function isUndefined(v) { return v === UNDEFINED; }; var isNil = function isNil(v) { return v === UNDEFINED || v === null; }; function isType(v, type) { if (isNil(v)) return false; var t = typeof v === 'undefined' ? 'undefined' : _typeof(v); if (t === type.toLowerCase()) return true; return t === 'object' && protoToString.call(v) === '[object ' + type + ']'; } var isArray = function isArray(v) { return Array.isArray(v); }; var isBoolean = function isBoolean(v) { return v === true || v === false; }; var isString = function isString(v) { return isType(v, 'String'); }; var isRegExp = function isRegExp(v) { return isType(v, 'RexExp'); }; var isObject = function isObject(v) { return v != null && (typeof v === 'undefined' ? 'undefined' : _typeof(v)) === 'object'; }; var isFunction = function isFunction(v) { return isType(v, 'Function'); }; var isNumber = function isNumber(v) { return isType(v, 'Number'); }; var isInteger = function isInteger(v) { return isNumber(v) && v % 1 === 0; }; var isSafeInteger = function isSafeInteger(v) { return isInteger(v) && v < MAX_SAFE_INTEGER; }; var toString = function toString(v) { return isNil(v) ? '' : typeof v === 'string' ? v : '' + v; }; var toNumber = function toNumber(v) { return isNumber(v) ? v : isBoolean(v) ? v ? 1 : 0 : v ? +v : 0; }; var toInteger$1 = function toInteger(v) { return isInteger(v) ? v : Math.floor(toNumber(v)); }; var trim = function trim(v) { return isString(v) ? v.trim() : v; }; var inRange = function inRange(v, v1, v2) { return v >= v1 && v <= v2 || v <= v1 && v >= v2; }; var omit = function omit(o, a) { var keys = isArray(a) ? a : [a]; var obj = Object.assign({}, o); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = keys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var key = _step.value; if (key) delete obj[key]; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return obj; }; var uniq = function uniq(arr) { return Array.from(new Set(arr)); }; var isPlainObject = function isPlainObject(v) { if (!isObject(v) || protoToString.call(v) !== '[object Object]') { return false; } if (Object.getPrototypeOf(v) === null) { return true; } var proto = v; while (Object.getPrototypeOf(proto) !== null) { proto = Object.getPrototypeOf(proto); } return Object.getPrototypeOf(v) === proto; }; var forOwn = function forOwn(o, fn) { return Object.keys(o).forEach(function (key) { return fn(o[key], key, o); }); }; var isEmpty = function isEmpty(v) { if (isNil(v)) return true; if (isArray(v) || isString(v)) return !v.length(); if (isPlainObject(v)) return !Object.keys(v).length; if (isType(v, 'Map') || isType(v, 'Set')) return !v.size; return false; }; // export const defaults = (o, d) => o // export const defaultsDeep = (o, d) => o var clone = function clone(v) { return !v // Exit early for performance ? v : isPlainObject(v) ? Object.assign({}, v) : isArray(v) ? v.slice(v) : v; }; function recursiveClone(branch) { // Exit early for performance if (!branch) return branch; if (isPlainObject(branch)) { var obj = {}; for (var key in branch) { obj[key] = recursiveClone(branch[key]); } return obj; } if (isArray(branch)) { var len = branch.length; var arr = new Array(len); for (var i = 0; i < len; i++) { arr[i] = recursiveClone(branch[i]); } return arr; } // Return any other object or simple value unchanged return branch; } var cloneDeep = function cloneDeep(obj) { return recursiveClone(obj); }; function mergeBranch(target, source) { for (var key in source) { var targetVal = target[key]; var sourceVal = source[key]; if (isPlainObject(targetVal) && isPlainObject(sourceVal)) { mergeBranch(targetVal, sourceVal); } else if (!isUndefined(sourceVal)) { target[key] = sourceVal; } } } var merge = function merge(target) { for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { sources[_key - 1] = arguments[_key]; } if (isPlainObject(target)) { var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = sources[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var source = _step2.value; if (isPlainObject(source)) { mergeBranch(target, source); } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } return target; }; function setDefaults(source, defaults, deep) { if (!isPlainObject(source) || !isPlainObject(defaults)) return; forOwn(defaults, function (val, key) { var srcVal = source[key]; var defVal = defaults[key]; if (isUndefined(srcVal)) { source[key] = isPlainObject(defVal) || isArray(defVal) ? cloneDeep(defVal) : defVal; } else if (deep && isPlainObject(srcVal) && isPlainObject(defVal)) { setDefaults(srcVal, defVal, true); } }); } var defaults$1 = function defaults(srcObj) { for (var _len2 = arguments.length, defObjs = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { defObjs[_key2 - 1] = arguments[_key2]; } defObjs.forEach(function (defs) { return setDefaults(srcObj, defs); }); }; var defaultsDeep = function defaultsDeep(srcObj) { for (var _len3 = arguments.length, defObjs = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { defObjs[_key3 - 1] = arguments[_key3]; } defObjs.forEach(function (defs) { return setDefaults(srcObj, defs, true); }); }; var isEqual = function isEqual(v1, v2) { var simpleValue = function simpleValue(val) { // Undefined and Null are equal to "" for data comparison. if (isNil(val)) return ''; // Stringify Array and Hash values for simple comparison; rarely needed. if (isPlainObject(val) || isArray(val)) return JSON.stringify(val); // Convert data objects to number for comparison if (isDate(val)) return val.getTime(); return val; }; return simpleValue(v1) === simpleValue(v2); }; // Match timestamps with no date portion, like: "08:30" or "14:00:00" var reTimeOnly = /^[\d]{2}:[\d]{2}[\d:]*$/; var fixTimestamp = function fixTimestamp(date) { return isString(date) && reTimeOnly.test(date) ? '1970-01-01T' + date : date; }; /** * PUBLIC UTILITY to create Date object, AND handle time-only strings * * @public * @param {(string|number|Object)} date * @returns {Date|*} */ var parseDate$1 = function parseDate(date) { return isDate(date) ? date : isString(date) ? parseISO(fixTimestamp(date)) : toDate(date); }; /** * Helper methods used by FormManager; exported as an object of methods */ function itemToArray(item) { // Avoid creating an array with just an empty string or other falsey value if (!item) return []; // ARRAY already - ASSUME has no nested arrays if (isArray(item)) return item; // HASH value; like a field-errors object if (isPlainObject(item)) { var arrItems = []; // init, may change forOwn(item, function (value) { if (value && value.length) { if (isArray(value)) { arrItems = arrItems.concat(value); } else { arrItems.push(value); } } }); return arrItems; } // STRING or OTHER item return [item]; } /** * Change all empty-string and undefined values to Null. * Mutate the original object - caller should clone first if necessary. * * @param {Object} data */ var emptyValuesToNull = function emptyValuesToNull(data) { forOwn(data, function (value, key) { if (value === '' || value === undefined) { data[key] = null; } }); }; /** * Convert a fieldName into an array of keys - may only be one key. * NOT exported - helper for get/setObjectValue methods * * @param {string} path */ function pathToKeysArray(path) { // Dots delimit an 'object path' return isArray(path) ? path : path.split('.'); } /** * * @param {Object} hash Object to modify * @param {(string|Array)} path String-path like 'data/who/gender' * @param {Object} [opts] Configuration options * @returns {*} Value at path or undefined if not found */ function getObjectValue(hash, path) { var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; if (!hash || !isPlainObject(hash)) return undefined; var branch = hash; // If a path was passed, trace the path inside state.form if (path && path !== '/') { // Slash(es) in the path indicate that we should recurse downward var keys = pathToKeysArray(path); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = keys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var key = _step.value; // If branch is not an object, then cannot recurse; abort if (!isPlainObject(branch)) return undefined; branch = branch[key]; // If requested key not found, abort if (isUndefined(branch)) return undefined; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } return opts.cloneDeep ? cloneDeep(branch) : opts.clone ? clone(branch) : branch; } /** * * @param {Object} hash Object to modify * @param {(string|Array)} path String-path like 'data/who/gender' * @param {*} value Value - could be anything! * @param {Object} [opts] Configuration * @returns {boolean} True if value set; false if value unchanged */ function setObjectValue(hash, path, value) { var opts = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; if (!hash || !isPlainObject(hash)) return undefined; var getValue = function getValue(val) { return opts.cloneDeep ? cloneDeep(val) : opts.clone ? clone(val) : val; }; // If a path was passed, recurse into the object if (path && path !== '/') { var keys = pathToKeysArray(path); var lastIdx = keys.length - 1; var lastKey = keys[lastIdx]; var branch = hash; // Recurse into data hash for name-paths like 'who/location/address1' for (var idx = 0; idx < lastIdx; idx++) { var key = keys[idx]; var branchValue = branch[key]; // Create a branch (hash) here if it doesn't exist yet // OVERWRITE any non-object value because path specifies it! if (!isPlainObject(branchValue)) { branch[key] = {}; } // Update branch for next loop branch = branch[key]; } var oldValue = branch[lastKey]; // Check whether value has changed - ignore objects, assume changed if (!isObject(value) && value === oldValue) { return false; // Value was NOT updated } // If passed value is undefined, then DELETE the specified path if (value === undefined) { delete branch[lastKey]; // OK if key does not exist return !isUndefined(oldValue); // True IF a value existed before } // Write the passed value at end of the path (last branch) // Ignore any existing value - we do not merge data here. if (opts.merge && isPlainObject(branch[lastKey]) && isPlainObject(value)) { merge(branch[lastKey], getValue(value)); } // Set value at path specified; clone value to break byRef else { branch[lastKey] = getValue(value); } return true; // Value was updated } else if (isPlainObject(path)) { // The path is an object (key/value pairs) to merge into hash-root merge(hash, getValue(path)); return true; // Values were updated } else { console.warn('FormManager: No path specified to set value: "' + value + '"'); return false; // Value was NOT updated } } /** * FormManager sub-component to handle form-field data * * @param {Object} formManager FormManager instance object * @param {Object} components Hash of FormManager sub-components * @param {Object} components.config FormManagerConfig instance object * @param {Object} components.state FormManagerState instance object * @returns {Object} Config API for this instance * @constructor */ function Data(formManager, components) { // Auto-instantiate so 'new' keyword is NOT required // (It's best practice to not require 'new' as it's an internal design) if (!(this instanceof Data)) { return new Data(formManager, components); } // NOTE: Cannot extract 'validation' here because NOT YET in 'components'! var internal = components.internal, config = components.config, state = components.state; // Extract helper methods for brevity var fireEventCallback = internal.fireEventCallback, triggerComponentUpdate = internal.triggerComponentUpdate; var withFieldDefaults = config.withFieldDefaults, aliasToRealName = config.aliasToRealName; // Create local aliases for config objects var formatters = config.get('formatters'); var converters = config.get('converters'); // DATA CACHES - both server and form versions of data var stateOfValues = {}; var stateOfData = {}; var stateOfInitialData = {}; var dirtyFields = new Set(); return { // Methods used internally by FormManager components init: init, get: getData, // GETTER for stateOfData set: setData, // SETTER for stateOfData getValue: getValue, // GETTER for one field-value getValues: getValues, // GETTER for all field-values setValue: setValue, // SETTER for one field-value setValues: setValues, // SETTER for multiple field-values cleanValue: cleanValue, // GETTER/PROCESSOR for one field // Methods exposed in FormManager API publicAPI: { isDirty: isDirty, // Has ANY value in data changed? isClean: isClean, // Has NO values in data changed? getChanges: getChanges, // Return hash containing only changed data getData: getData, // GETTER for stateOfData getFieldData: getFieldData, // GETTER for one field-data setData: setData, // SETTER for stateOfData setFieldData: setFieldData, // SETTER for one field-data getValue: getValue, // GETTER for one field-value getValues: getValues, // GETTER for all field-values setValue: setValue, // SETTER for one field-value setValues: setValues, // SETTER for multiple field-values cleanField: cleanField, // SETTER for one field // ALIASES data: getData, value: getValue, values: getValue, changes: getChanges, cleanFieldValue: cleanField } /** * Initialize form data, state and errors using only the config-options * * @param {(undefined|Object)} [initialData] * @param {(undefined|Object)} [data] */ };function init(initialData, data) { stateOfData = {}; // In case called by form.reset() if (initialData) setData(initialData); if (data) setData(data); } /** * PUBLIC GETTER for a specific field value. * * @public * @param {string} name Field-name or alias-name * @param {Object} [opts] Options */ function getValue(name) { var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var fieldName = aliasToRealName(name); var fieldConfig = config.getField(fieldName) || {}; var value = stateOfValues[fieldName]; // Init or refresh the cached field-value in stateOfValues if (opts.refresh || isUndefined(value)) { setFormValueFromData(fieldName, undefined, { clean: true }); return stateOfValues[fieldName]; } // Clean field-value - NOT cached! return opts.clean ? cleanValue(value, fieldConfig) : value; } /** * PUBLIC GETTER for ALL field values. * * @public */ function getValues() { return cloneDeep(stateOfValues); } /** * PUBLIC SETTER for a specific field value. * * @public * @param {string} name Field-name or alias-name * @param {*} value New value * @param {Object} [opts] Options * @returns {Object} All SETTERS return instance for chaining */ function setValue(name, value) { var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var fieldName = aliasToRealName(name); var fieldConfig = config.getField(fieldName); var setOpts = { clone: true }; var fieldValue = processFieldValue(value, fieldConfig, opts); var dataValue = processDataValue(fieldValue, fieldConfig); // Update the stateOfValues cache stateOfValues[fieldName] = clone(fieldValue); // Non-data fields are cached in state if (fieldConfig && fieldConfig.isData === false) { state.set(fieldName, dataValue, setOpts); } // Anything else is a data field, even if no field-config found // because not all fields require a field configuration. else { setObjectValue(stateOfData, fieldName, dataValue, setOpts); setIsDirty(fieldName, dataValue); } // Get methods from Validation component var _components$validatio = components.validation, isValidationEvent = _components$validatio.isValidationEvent, validate = _components$validatio.validate; var event = opts.event; var validationEvent = opts.validate ? 'validate' : event; var needsValidation = isValidationEvent(fieldName, validationEvent); var validationPromise = null; var onChangeForm = config.get('onChange'); var onChangeField = (config.getField(name) || {}).onChange; // When triggered by a field-event, validate BEFORE firing callbacks if (needsValidation) { // A validation event MAY trigger validation & callback. // Validation MAY run, depending on event-type. // Validation is async so always returns a promise. validationPromise = validate(name, dataValue, validationEvent, { update: true }).then(function () { // If field value was changed, then fire events // noinspection JSIncompatibleTypesComparison if (event === 'change') { fireEventCallback(onChangeField, value, name); fireEventCallback(onChangeForm, value, name); } }); } else { // noinspection JSIncompatibleTypesComparison if (event === 'change') { // Just fire form-level onChange, if one exists fireEventCallback(onChangeForm, value, name); } } // Note: opts.update default == true; must pass false to prevent update if (opts.update !== false) { triggerComponentUpdate(); } return validationPromise || formManager; } /** * PUBLIC SETTER for a specific field value. * * @public * @param {Object} data Hash of new values keyed by fieldname or alias * @param {Object} [opts] Options * @returns {Object} All SETTERS return instance for chaining */ function setValues(data) { var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var update = opts.update; var setOpts = _extends({}, opts, { update: false }); var validationPromises = []; forOwn(data, function (value, name) { var retVal = setValue(name, value, setOpts); if (retVal.then) validationPromises.push(retVal); }); // Trigger an immediate, synchronous render triggerComponentUpdate(); if (validationPromises.length) { var promise = Promise.all(validationPromises); if (update) promise.then(triggerComponentUpdate); return promise; } return formManager; } /** * INTERNAL HELPER to parse a field-value from data-value * * @param {string} fieldName * @param {*} [newDataValue] * @param {Object} [opts] Options; defaults: { clean: false} */ function setFormValueFromData(fieldName, newDataValue) { var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var fieldConfig = config.getField(fieldName); var value = !isUndefined(newDataValue) ? newDataValue : getFieldData(fieldName); value = processFieldValue(value, fieldConfig, opts); stateOfValues[fieldName] = clone(value); } /** * INTERNAL HELPER to clean a field value * * @param {*} value * @param {Object} fldCfg * @returns {*} */ function cleanValue(value, fldCfg) { if (!value) return value; var val = value; // The formatter may change the data-type, like number -> string var reformat = withFieldDefaults(fldCfg, 'cleaning.reformat'); if (reformat) val = formatValue(val, reformat); // Only string values can be trimmed if (isString(value)) { var trimOuter = withFieldDefaults(fldCfg, 'cleaning.trim'); var trimInner = withFieldDefaults(fldCfg, 'cleaning.trimInner'); if (trimOuter) val = val.trim(); if (trimInner) val = val.replace(/\s+/g, ' '); } return val; } function cleanField(name) { var fieldName = aliasToRealName(name); var fieldConfig = config.getField(fieldName); // Field may NOT have a configuration, or could be a 'state' value if (fieldConfig) { var curValue = getValue(fieldName); var newValue = cleanValue(curValue, fieldConfig); if (newValue !== curValue) { // Pass 'change' event so will fire event with new value setValue(fieldName, newValue, { event: 'change' }); } } return formManager; } /** * PUBLIC GETTER * * @param {string} name * @returns {*} Data value for one-field */ function getFieldData(name) { var fieldName = aliasToRealName(name); var fieldConfig = config.getField(fieldName) || {}; return fieldConfig.isData === false ? state.get(fieldName) : getObjectValue(stateOfData, fieldName); } /** * @returns {Object} All data */ function getData() { return cloneDeep(stateOfData); } /** * INTERNAL HELPER to convert, format and clean a FIELD-VALUE * * @param {*} value * @param {(Object|undefined)} fieldConfig * @param {Object} [opts] * @returns {*} */ function processFieldValue(value, fieldConfig) { var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var val = value; // Field may NOT have a configuration... if (fieldConfig) { // NOTE: A state-value is processed the same as a field-value val = convertDataType(val, fieldConfig.valueType); val = formatValue(val, fieldConfig.valueFormat); val = und