final-form-calculate
Version:
Decorator for calculating field values based on other field values in 🏁 Final Form
178 lines (170 loc) • 6.68 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["final-form-calculate"] = {}));
})(this, (function (exports) { 'use strict';
var charCodeOfDot = ".".charCodeAt(0);
var reEscapeChar = /\\(\\)?/g;
var rePropName = RegExp(
// Match anything that isn't a dot or bracket.
"[^.[\\]]+" + "|" +
// Or match property names within brackets.
"\\[(?:" +
// Match a non-string expression.
"([^\"'][^[]*)" + "|" +
// Or match strings (supports escaping characters).
"([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2" + ")\\]" + "|" +
// Or match "" as the space between consecutive dots or empty brackets.
"(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))", "g");
/**
* Converts `string` to a property path array.
*
* @private
* @param {string} string The string to convert.
* @returns {Array} Returns the property path array.
*/
var stringToPath = function stringToPath(string) {
var result = [];
if (string.charCodeAt(0) === charCodeOfDot) {
result.push("");
}
string.replace(rePropName, function (match, expression, quote, subString) {
var key = match;
if (quote) {
key = subString.replace(reEscapeChar, "$1");
} else if (expression) {
key = expression.trim();
}
result.push(key);
return "";
});
return result;
};
var keysCache = {};
var keysRegex = /[.[\]]+/;
var toPath = function toPath(key) {
if (key === null || key === undefined || !key.length) {
return [];
}
if (typeof key !== "string") {
throw new Error("toPath() expects a string");
}
if (keysCache[key] == null) {
/**
* The following patch fixes issue 456, introduced since v4.20.3:
*
* Before v4.20.3, i.e. in v4.20.2, a `key` like 'choices[]' would map to ['choices']
* (e.g. an array of choices used where 'choices[]' is name attribute of an input of type checkbox).
*
* Since v4.20.3, a `key` like 'choices[]' would map to ['choices', ''] which is wrong and breaks
* this kind of inputs e.g. in React.
*
* v4.20.3 introduced an unwanted breaking change, this patch fixes it, see the issue at the link below.
*
* @see https://github.com/final-form/final-form/issues/456
*/
if (key.endsWith("[]")) {
// v4.20.2 (a `key` like 'choices[]' should map to ['choices'], which is fine).
keysCache[key] = key.split(keysRegex).filter(Boolean);
} else {
// v4.20.3 (a `key` like 'choices[]' maps to ['choices', ''], which breaks applications relying on inputs like `<input type="checkbox" name="choices[]" />`).
keysCache[key] = stringToPath(key);
}
}
return keysCache[key];
};
var getIn = function getIn(state, complexKey) {
// Intentionally using iteration rather than recursion
var path = toPath(complexKey);
var current = state;
for (var i = 0; i < path.length; i++) {
var key = path[i];
if (current === undefined || current === null || typeof current !== "object" || Array.isArray(current) && isNaN(Number(key))) {
return undefined;
}
current = current[key];
}
return current;
};
function isPromise(obj) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
var tripleEquals = function tripleEquals(a, b) {
return a === b;
};
var createDecorator = function createDecorator() {
for (var _len = arguments.length, calculations = new Array(_len), _key = 0; _key < _len; _key++) {
calculations[_key] = arguments[_key];
}
return function (form) {
var previousValues;
var unsubscribe = form.subscribe(function (_ref) {
var values = _ref.values;
form.batch(function () {
var runUpdates = function runUpdates(field, isEqual, updates) {
var next = values && getIn(values, field);
var previous = previousValues && getIn(previousValues, field);
if (!isEqual(next, previous)) {
if (typeof updates === 'function') {
var results = updates(next, field, values, previousValues);
if (isPromise(results)) {
results.then(function (resolved) {
Object.keys(resolved).forEach(function (destField) {
form.change(destField, resolved[destField]);
});
});
} else {
Object.keys(results).forEach(function (destField) {
form.change(destField, results[destField]);
});
}
} else {
Object.keys(updates).forEach(function (destField) {
var update = updates[destField];
var result = update(next, values, previousValues);
if (isPromise(result)) {
result.then(function (resolved) {
form.change(destField, resolved);
});
} else {
form.change(destField, result);
}
});
}
}
};
var fields = form.getRegisteredFields();
calculations.forEach(function (_ref2) {
var field = _ref2.field,
isEqual = _ref2.isEqual,
updates = _ref2.updates;
if (typeof field === 'string') {
runUpdates(field, isEqual || tripleEquals, updates);
} else {
// field is a either array or regex
var matches = Array.isArray(field) ? function (name) {
return ~field.indexOf(name) || field.findIndex(function (f) {
return f instanceof RegExp && f.test(name);
}) !== -1;
} : function (name) {
return field.test(name);
};
fields.forEach(function (fieldName) {
if (matches(fieldName)) {
runUpdates(fieldName, isEqual || tripleEquals, updates);
}
});
}
});
previousValues = values;
});
}, {
values: true
});
return unsubscribe;
};
};
exports.default = createDecorator;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=final-form-calculate.umd.js.map