handsontable
Version:
Handsontable is a JavaScript Spreadsheet Component available for React, Angular and Vue.
1,212 lines (983 loc) • 48.2 kB
JavaScript
"use strict";
require("core-js/modules/es.object.set-prototype-of.js");
require("core-js/modules/es.object.get-prototype-of.js");
require("core-js/modules/es.reflect.construct.js");
require("core-js/modules/es.reflect.get.js");
require("core-js/modules/es.object.get-own-property-descriptor.js");
require("core-js/modules/es.symbol.js");
require("core-js/modules/es.symbol.description.js");
require("core-js/modules/es.symbol.iterator.js");
require("core-js/modules/es.array.slice.js");
require("core-js/modules/es.function.name.js");
require("core-js/modules/es.array.from.js");
exports.__esModule = true;
exports.Formulas = exports.PLUGIN_PRIORITY = exports.PLUGIN_KEY = void 0;
require("core-js/modules/es.array.concat.js");
require("core-js/modules/web.dom-collections.for-each.js");
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.set.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
require("core-js/modules/es.array.map.js");
require("core-js/modules/es.array.includes.js");
require("core-js/modules/es.string.includes.js");
require("core-js/modules/es.array.reverse.js");
require("core-js/modules/es.array.sort.js");
require("core-js/modules/es.weak-map.js");
var _base = require("../base");
var _autofill = require("./autofill");
var _staticRegister = _interopRequireDefault(require("../../utils/staticRegister"));
var _console = require("../../helpers/console");
var _mixed = require("../../helpers/mixed");
var _register = require("./engine/register");
var _utils = require("./utils");
var _settings = require("./engine/settings");
var _data = require("../../helpers/data");
var _string = require("../../helpers/string");
var _pluginHooks = _interopRequireDefault(require("../../pluginHooks"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
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 _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_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"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
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 _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
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 _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _defineProperty(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; }
function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
var PLUGIN_KEY = 'formulas';
exports.PLUGIN_KEY = PLUGIN_KEY;
var PLUGIN_PRIORITY = 260;
exports.PLUGIN_PRIORITY = PLUGIN_PRIORITY;
_pluginHooks.default.getSingleton().register('afterNamedExpressionAdded');
_pluginHooks.default.getSingleton().register('afterNamedExpressionRemoved');
_pluginHooks.default.getSingleton().register('afterSheetAdded');
_pluginHooks.default.getSingleton().register('afterSheetRemoved');
_pluginHooks.default.getSingleton().register('afterSheetRenamed');
_pluginHooks.default.getSingleton().register('afterFormulasValuesUpdate');
/**
* This plugin allows you to perform Excel-like calculations in your business applications. It does it by an
* integration with our other product, [HyperFormula](https://github.com/handsontable/hyperformula/), which is a
* powerful calculation engine with an extensive number of features.
*
* @plugin Formulas
* @class Formulas
*/
var _internalOperationPending = /*#__PURE__*/new WeakMap();
var _hotWasInitializedWithEmptyData = /*#__PURE__*/new WeakMap();
var _engineListeners = /*#__PURE__*/new WeakMap();
var Formulas = /*#__PURE__*/function (_BasePlugin) {
_inherits(Formulas, _BasePlugin);
var _super = _createSuper(Formulas);
function Formulas() {
var _this;
_classCallCheck(this, Formulas);
for (var _len = arguments.length, _args = new Array(_len), _key = 0; _key < _len; _key++) {
_args[_key] = arguments[_key];
}
_this = _super.call.apply(_super, [this].concat(_args));
_internalOperationPending.set(_assertThisInitialized(_this), {
writable: true,
value: false
});
_hotWasInitializedWithEmptyData.set(_assertThisInitialized(_this), {
writable: true,
value: false
});
_engineListeners.set(_assertThisInitialized(_this), {
writable: true,
value: [['valuesUpdated', function () {
var _this2;
return (_this2 = _this).onEngineValuesUpdated.apply(_this2, arguments);
}], ['namedExpressionAdded', function () {
var _this3;
return (_this3 = _this).onEngineNamedExpressionsAdded.apply(_this3, arguments);
}], ['namedExpressionRemoved', function () {
var _this4;
return (_this4 = _this).onEngineNamedExpressionsRemoved.apply(_this4, arguments);
}], ['sheetAdded', function () {
var _this5;
return (_this5 = _this).onEngineSheetAdded.apply(_this5, arguments);
}], ['sheetRenamed', function () {
var _this6;
return (_this6 = _this).onEngineSheetRenamed.apply(_this6, arguments);
}], ['sheetRemoved', function () {
var _this7;
return (_this7 = _this).onEngineSheetRemoved.apply(_this7, arguments);
}]]
});
_defineProperty(_assertThisInitialized(_this), "staticRegister", (0, _staticRegister.default)('formulas'));
_defineProperty(_assertThisInitialized(_this), "engine", null);
_defineProperty(_assertThisInitialized(_this), "sheetName", null);
return _this;
}
_createClass(Formulas, [{
key: "sheetId",
get:
/**
* HyperFormula's sheet id.
*
* @type {number|null}
*/
function get() {
return this.sheetName === null ? null : this.engine.getSheetId(this.sheetName);
}
/**
* Checks if the plugin is enabled in the handsontable settings. This method is executed in {@link Hooks#beforeInit}
* hook and if it returns `true` than the {@link Formulas#enablePlugin} method is called.
*
* @returns {boolean}
*/
}, {
key: "isEnabled",
value: function isEnabled() {
/* eslint-disable no-unneeded-ternary */
return this.hot.getSettings()[PLUGIN_KEY] ? true : false;
}
/**
* Enables the plugin functionality for this Handsontable instance.
*/
}, {
key: "enablePlugin",
value: function enablePlugin() {
var _setupEngine,
_this8 = this;
if (this.enabled) {
return;
}
this.engine = (_setupEngine = (0, _register.setupEngine)(this.hot)) !== null && _setupEngine !== void 0 ? _setupEngine : this.engine;
if (!this.engine) {
(0, _console.warn)('Missing the required `engine` key in the Formulas settings. Please fill it with either an' + ' engine class or an engine instance.');
return;
} // Useful for disabling -> enabling the plugin using `updateSettings` or the API.
if (this.sheetName !== null && !this.engine.doesSheetExist(this.sheetName)) {
this.sheetName = this.addSheet(this.sheetName, this.hot.getSourceDataArray());
}
this.addHook('beforeLoadData', function () {
return _this8.onBeforeLoadData.apply(_this8, arguments);
});
this.addHook('afterLoadData', function () {
return _this8.onAfterLoadData.apply(_this8, arguments);
});
this.addHook('modifyData', function () {
return _this8.onModifyData.apply(_this8, arguments);
});
this.addHook('modifySourceData', function () {
return _this8.onModifySourceData.apply(_this8, arguments);
});
this.addHook('beforeValidate', function () {
return _this8.onBeforeValidate.apply(_this8, arguments);
});
this.addHook('afterSetSourceDataAtCell', function () {
return _this8.onAfterSetSourceDataAtCell.apply(_this8, arguments);
});
this.addHook('afterSetDataAtCell', function () {
return _this8.onAfterSetDataAtCell.apply(_this8, arguments);
});
this.addHook('afterSetDataAtRowProp', function () {
return _this8.onAfterSetDataAtCell.apply(_this8, arguments);
});
this.addHook('beforeCreateRow', function () {
return _this8.onBeforeCreateRow.apply(_this8, arguments);
});
this.addHook('beforeCreateCol', function () {
return _this8.onBeforeCreateCol.apply(_this8, arguments);
});
this.addHook('afterCreateRow', function () {
return _this8.onAfterCreateRow.apply(_this8, arguments);
});
this.addHook('afterCreateCol', function () {
return _this8.onAfterCreateCol.apply(_this8, arguments);
});
this.addHook('beforeRemoveRow', function () {
return _this8.onBeforeRemoveRow.apply(_this8, arguments);
});
this.addHook('beforeRemoveCol', function () {
return _this8.onBeforeRemoveCol.apply(_this8, arguments);
});
this.addHook('afterRemoveRow', function () {
return _this8.onAfterRemoveRow.apply(_this8, arguments);
});
this.addHook('afterRemoveCol', function () {
return _this8.onAfterRemoveCol.apply(_this8, arguments);
});
var autofillHooks = (0, _autofill.createAutofillHooks)(this);
this.addHook('beforeAutofill', autofillHooks.beforeAutofill);
this.addHook('afterAutofill', autofillHooks.afterAutofill);
_classPrivateFieldGet(this, _engineListeners).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
eventName = _ref2[0],
listener = _ref2[1];
return _this8.engine.on(eventName, listener);
});
_get(_getPrototypeOf(Formulas.prototype), "enablePlugin", this).call(this);
}
/**
* Disables the plugin functionality for this Handsontable instance.
*/
}, {
key: "disablePlugin",
value: function disablePlugin() {
var _this9 = this;
_classPrivateFieldGet(this, _engineListeners).forEach(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
eventName = _ref4[0],
listener = _ref4[1];
return _this9.engine.off(eventName, listener);
});
(0, _register.unregisterEngine)(this.engine, this.hot);
_get(_getPrototypeOf(Formulas.prototype), "disablePlugin", this).call(this);
}
/**
* Triggered on `updateSettings`.
*
* @private
* @param {object} newSettings New set of settings passed to the `updateSettings` method.
*/
}, {
key: "updatePlugin",
value: function updatePlugin(newSettings) {
this.engine.updateConfig((0, _settings.getEngineSettingsWithOverrides)(this.hot.getSettings()));
var pluginSettings = this.hot.getSettings()[PLUGIN_KEY];
if ((0, _mixed.isDefined)(pluginSettings) && (0, _mixed.isDefined)(pluginSettings.sheetName) && pluginSettings.sheetName !== this.sheetName) {
this.switchSheet(pluginSettings.sheetName);
} // If no data was passed to the `updateSettings` method and no sheet is connected to the instance -> create a
// new sheet using the currently used data. Otherwise, it will be handled by the `afterLoadData` call.
if (!newSettings.data && this.sheetName === null) {
var sheetName = this.hot.getSettings()[PLUGIN_KEY].sheetName;
if (sheetName && this.engine.doesSheetExist(sheetName)) {
this.switchSheet(this.sheetName);
} else {
this.sheetName = this.addSheet(sheetName !== null && sheetName !== void 0 ? sheetName : void 0, this.hot.getSourceDataArray());
}
}
_get(_getPrototypeOf(Formulas.prototype), "updatePlugin", this).call(this, newSettings);
}
/**
* Destroys the plugin instance.
*/
}, {
key: "destroy",
value: function destroy() {
var _this10 = this;
_classPrivateFieldGet(this, _engineListeners).forEach(function (_ref5) {
var _this10$engine;
var _ref6 = _slicedToArray(_ref5, 2),
eventName = _ref6[0],
listener = _ref6[1];
return (_this10$engine = _this10.engine) === null || _this10$engine === void 0 ? void 0 : _this10$engine.off(eventName, listener);
});
_classPrivateFieldSet(this, _engineListeners, null);
(0, _register.unregisterEngine)(this.engine, this.hot);
_get(_getPrototypeOf(Formulas.prototype), "destroy", this).call(this);
}
/**
* Helper function for `toPhysicalRowPosition` and `toPhysicalColumnPosition`.
*
* @private
* @param {number} visualIndex Visual entry index.
* @param {number} physicalIndex Physical entry index.
* @param {number} entriesCount Visual entries count.
* @param {number} sourceEntriesCount Source entries count.
* @param {boolean} contained `true` if it should return only indexes within boundaries of the table (basically
* `toPhysical` alias.
* @returns {*}
*/
}, {
key: "getPhysicalIndexPosition",
value: function getPhysicalIndexPosition(visualIndex, physicalIndex, entriesCount, sourceEntriesCount, contained) {
if (!contained) {
if (visualIndex >= entriesCount) {
return sourceEntriesCount + (visualIndex - entriesCount);
}
}
return physicalIndex;
}
/**
* Returns the physical row index. The difference between this and Core's `toPhysical` is that it doesn't return
* `null` on rows with indexes higher than the number of rows.
*
* @private
* @param {number} row Visual row index.
* @param {boolean} [contained] `true` if it should return only indexes within boundaries of the table (basically
* `toPhysical` alias.
* @returns {number} The physical row index.
*/
}, {
key: "toPhysicalRowPosition",
value: function toPhysicalRowPosition(row) {
var contained = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
return this.getPhysicalIndexPosition(row, this.hot.toPhysicalRow(row), this.hot.countRows(), this.hot.countSourceRows(), contained);
}
/**
* Returns the physical column index. The difference between this and Core's `toPhysical` is that it doesn't return
* `null` on columns with indexes higher than the number of columns.
*
* @private
* @param {number} column Visual column index.
* @param {boolean} [contained] `true` if it should return only indexes within boundaries of the table (basically
* `toPhysical` alias.
* @returns {number} The physical column index.
*/
}, {
key: "toPhysicalColumnPosition",
value: function toPhysicalColumnPosition(column) {
var contained = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
return this.getPhysicalIndexPosition(column, this.hot.toPhysicalColumn(column), this.hot.countCols(), this.hot.countSourceCols(), contained);
}
/**
* Add a sheet to the shared HyperFormula instance.
*
* @param {string|null} [sheetName] The new sheet name. If not provided (or a null is passed), will be
* auto-generated by HyperFormula.
* @param {Array} [sheetData] Data passed to the shared HyperFormula instance. Has to be declared as an array of
* arrays - array of objects is not supported in this scenario.
* @returns {boolean|string} `false` if the data format is unusable or it is impossible to add a new sheet to the
* engine, the created sheet name otherwise.
*/
}, {
key: "addSheet",
value: function addSheet(sheetName, sheetData) {
if ((0, _mixed.isDefined)(sheetData) && !(0, _data.isArrayOfArrays)(sheetData)) {
(0, _console.warn)('The provided data should be an array of arrays.');
return false;
}
if (sheetName !== void 0 && sheetName !== null && this.engine.doesSheetExist(sheetName)) {
(0, _console.warn)('Sheet with the provided name already exists.');
return false;
}
try {
var actualSheetName = this.engine.addSheet(sheetName !== null && sheetName !== void 0 ? sheetName : void 0);
if (sheetData) {
this.engine.setSheetContent(actualSheetName, sheetData);
}
return actualSheetName;
} catch (e) {
(0, _console.warn)(e.message);
return false;
}
}
/**
* Switch the sheet used as data in the Handsontable instance (it loads the data from the shared HyperFormula
* instance).
*
* @param {string} sheetName Sheet name used in the shared HyperFormula instance.
*/
}, {
key: "switchSheet",
value: function switchSheet(sheetName) {
if (!this.engine.doesSheetExist(sheetName)) {
(0, _console.error)("The sheet named `".concat(sheetName, "` does not exist, switch aborted."));
return;
}
this.sheetName = sheetName;
var serialized = this.engine.getSheetSerialized(this.sheetId);
if (serialized.length > 0) {
this.hot.loadData(serialized, "".concat((0, _string.toUpperCaseFirst)(PLUGIN_KEY), ".switchSheet"));
}
}
/**
* Get the cell type under specified visual coordinates.
*
* @param {number} row Visual row index.
* @param {number} column Visual column index.
* @param {number} [sheet] The target sheet id, defaults to the current sheet.
* @returns {string} Possible values: 'FORMULA' | 'VALUE' | 'MATRIX' | 'EMPTY'.
*/
}, {
key: "getCellType",
value: function getCellType(row, column) {
var sheet = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.sheetId;
return this.engine.getCellType({
sheet: sheet,
row: this.hot.toPhysicalRow(row),
col: this.hot.toPhysicalColumn(column)
});
}
/**
* Returns `true` if under specified visual coordinates is formula.
*
* @param {number} row Visual row index.
* @param {number} column Visual column index.
* @param {number} [sheet] The target sheet id, defaults to the current sheet.
* @returns {boolean}
*/
}, {
key: "isFormulaCellType",
value: function isFormulaCellType(row, column) {
var sheet = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.sheetId;
var cellType = this.getCellType(row, column, sheet);
return cellType === 'FORMULA' || cellType === 'MATRIX';
}
/**
* Renders dependent sheets (handsontable instances) based on the changes - list of the
* recalculated dependent cells.
*
* @private
* @param {object[]} dependentCells The values and location of applied changes within HF engine.
* @param {boolean} [renderSelf] `true` if it's supposed to render itself, `false` otherwise.
*/
}, {
key: "renderDependentSheets",
value: function renderDependentSheets(dependentCells) {
var _this11 = this;
var renderSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var affectedSheetIds = new Set();
dependentCells.forEach(function (change) {
var _change$address;
// For the Named expression the address is empty, hence the `sheetId` is undefined.
var sheetId = change === null || change === void 0 ? void 0 : (_change$address = change.address) === null || _change$address === void 0 ? void 0 : _change$address.sheet;
if (sheetId !== void 0) {
if (!affectedSheetIds.has(sheetId)) {
affectedSheetIds.add(sheetId);
}
}
});
(0, _register.getRegisteredHotInstances)(this.engine).forEach(function (relatedHot, sheetId) {
if ((renderSelf || sheetId !== _this11.sheetId) && affectedSheetIds.has(sheetId)) {
var _relatedHot$view;
relatedHot.render();
(_relatedHot$view = relatedHot.view) === null || _relatedHot$view === void 0 ? void 0 : _relatedHot$view.adjustElementsSize();
}
});
}
/**
* Validates dependent cells based on the cells that are modified by the change.
*
* @private
* @param {object[]} dependentCells The values and location of applied changes within HF engine.
* @param {object[]} [changedCells] The values and location of applied changes by developer (through API or UI).
*/
}, {
key: "validateDependentCells",
value: function validateDependentCells(dependentCells) {
var _this12 = this;
var changedCells = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var stringifyAddress = function stringifyAddress(change) {
var _change$address2;
var _ref7 = (_change$address2 = change === null || change === void 0 ? void 0 : change.address) !== null && _change$address2 !== void 0 ? _change$address2 : {},
row = _ref7.row,
col = _ref7.col,
sheet = _ref7.sheet;
return (0, _mixed.isDefined)(sheet) ? "".concat(sheet, ":").concat(row, "x").concat(col) : '';
};
var changedCellsSet = new Set(changedCells.map(function (change) {
return stringifyAddress(change);
}));
dependentCells.forEach(function (change) {
var _change$address3, _change$address4;
var _ref8 = (_change$address3 = change.address) !== null && _change$address3 !== void 0 ? _change$address3 : {},
row = _ref8.row,
col = _ref8.col;
var visualRow = (0, _mixed.isDefined)(row) ? _this12.hot.toVisualRow(row) : null;
var visualColumn = (0, _mixed.isDefined)(col) ? _this12.hot.toVisualColumn(col) : null; // Don't try to validate cells outside of the visual part of the table.
if (visualRow === null || visualColumn === null) {
return;
} // For the Named expression the address is empty, hence the `sheetId` is undefined.
var sheetId = change === null || change === void 0 ? void 0 : (_change$address4 = change.address) === null || _change$address4 === void 0 ? void 0 : _change$address4.sheet;
var addressId = stringifyAddress(change); // Validate the cells that depend on the calculated formulas. Skip that cells
// where the user directly changes the values - the Core triggers those validators.
if (sheetId !== void 0 && !changedCellsSet.has(addressId)) {
var hot = (0, _register.getRegisteredHotInstances)(_this12.engine).get(sheetId); // It will just re-render certain cell when necessary.
hot.validateCell(hot.getDataAtCell(visualRow, visualColumn), hot.getCellMeta(visualRow, visualColumn), function () {});
}
});
}
/**
* Sync a change from the change-related hooks with the engine.
*
* @private
* @param {number} row Visual row index.
* @param {number} column Visual column index.
* @param {Handsontable.CellValue} newValue New value.
* @returns {Array} Array of changes exported from the engine.
*/
}, {
key: "syncChangeWithEngine",
value: function syncChangeWithEngine(row, column, newValue) {
var address = {
row: this.toPhysicalRowPosition(row),
col: this.toPhysicalColumnPosition(column),
sheet: this.sheetId
};
if (!this.engine.isItPossibleToSetCellContents(address)) {
(0, _console.warn)("Not possible to set cell data at ".concat(JSON.stringify(address)));
return;
}
return this.engine.setCellContents(address, newValue);
}
/**
* The hook allows to translate the formula value to calculated value before it goes to the
* validator function.
*
* @private
* @param {*} value The cell value to validate.
* @param {number} visualRow The visual row index.
* @param {number|string} prop The visual column index or property name of the column.
* @returns {*} Returns value to validate.
*/
}, {
key: "onBeforeValidate",
value: function onBeforeValidate(value, visualRow, prop) {
var visualColumn = this.hot.propToCol(prop);
if (this.isFormulaCellType(visualRow, visualColumn)) {
var address = {
row: this.hot.toPhysicalRow(visualRow),
col: this.hot.toPhysicalColumn(visualColumn),
sheet: this.sheetId
};
var cellValue = this.engine.getCellValue(address); // If `cellValue` is an object it is expected to be an error
return _typeof(cellValue) === 'object' && cellValue !== null ? cellValue.value : cellValue;
}
return value;
}
/**
* `beforeLoadData` hook callback.
*
* @param {Array} sourceData Array of arrays or array of objects containing data.
* @param {boolean} initialLoad Flag that determines whether the data has been loaded during the initialization.
* @param {string} [source] Source of the call.
* @private
*/
}, {
key: "onBeforeLoadData",
value: function onBeforeLoadData(sourceData, initialLoad) {
var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
if (source.includes((0, _string.toUpperCaseFirst)(PLUGIN_KEY))) {
return;
} // This flag needs to be defined, because not passing data to HOT results in HOT auto-generating a `null`-filled
// initial dataset.
_classPrivateFieldSet(this, _hotWasInitializedWithEmptyData, (0, _mixed.isUndefined)(this.hot.getSettings().data));
}
/**
* `afterLoadData` hook callback.
*
* @param {Array} sourceData Array of arrays or array of objects containing data.
* @param {boolean} initialLoad Flag that determines whether the data has been loaded during the initialization.
* @param {string} [source] Source of the call.
* @private
*/
}, {
key: "onAfterLoadData",
value: function onAfterLoadData(sourceData, initialLoad) {
var source = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
if (source.includes((0, _string.toUpperCaseFirst)(PLUGIN_KEY))) {
return;
}
this.sheetName = (0, _register.setupSheet)(this.engine, this.hot.getSettings()[PLUGIN_KEY].sheetName);
if (!_classPrivateFieldGet(this, _hotWasInitializedWithEmptyData)) {
var sourceDataArray = this.hot.getSourceDataArray();
if (this.engine.isItPossibleToReplaceSheetContent(this.sheetName, sourceDataArray)) {
_classPrivateFieldSet(this, _internalOperationPending, true);
var dependentCells = this.engine.setSheetContent(this.sheetName, this.hot.getSourceDataArray());
this.renderDependentSheets(dependentCells);
_classPrivateFieldSet(this, _internalOperationPending, false);
}
} else {
this.switchSheet(this.sheetName);
}
}
/**
* `modifyData` hook callback.
*
* @private
* @param {number} row Physical row height.
* @param {number} column Physical column index.
* @param {object} valueHolder Object which contains original value which can be modified by overwriting `.value`
* property.
* @param {string} ioMode String which indicates for what operation hook is fired (`get` or `set`).
*/
}, {
key: "onModifyData",
value: function onModifyData(row, column, valueHolder, ioMode) {
if (ioMode !== 'get' || _classPrivateFieldGet(this, _internalOperationPending) || this.sheetName === null || !this.engine.doesSheetExist(this.sheetName)) {
return;
} // `column` is here as visual index because of inconsistencies related to hook execution in `src/dataMap`.
var isFormulaCellType = this.isFormulaCellType(this.hot.toVisualRow(row), column);
if (!isFormulaCellType) {
if ((0, _utils.isEscapedFormulaExpression)(valueHolder.value)) {
valueHolder.value = (0, _utils.unescapeFormulaExpression)(valueHolder.value);
}
return;
} // `toPhysicalColumn` is here because of inconsistencies related to hook execution in `src/dataMap`.
var address = {
row: row,
col: this.toPhysicalColumnPosition(column),
sheet: this.sheetId
};
var cellValue = this.engine.getCellValue(address); // If `cellValue` is an object it is expected to be an error
var value = _typeof(cellValue) === 'object' && cellValue !== null ? cellValue.value : cellValue;
valueHolder.value = value;
}
/**
* `modifySourceData` hook callback.
*
* @private
* @param {number} row Physical row index.
* @param {number|string} columnOrProp Physical column index or prop.
* @param {object} valueHolder Object which contains original value which can be modified by overwriting `.value`
* property.
* @param {string} ioMode String which indicates for what operation hook is fired (`get` or `set`).
*/
}, {
key: "onModifySourceData",
value: function onModifySourceData(row, columnOrProp, valueHolder, ioMode) {
if (ioMode !== 'get' || _classPrivateFieldGet(this, _internalOperationPending) || this.sheetName === null || !this.engine.doesSheetExist(this.sheetName)) {
return;
}
var visualColumn = this.hot.propToCol(columnOrProp); // `column` is here as visual index because of inconsistencies related to hook execution in `src/dataMap`.
var isFormulaCellType = this.isFormulaCellType(this.hot.toVisualRow(row), visualColumn);
if (!isFormulaCellType) {
return;
}
var dimensions = this.engine.getSheetDimensions(this.engine.getSheetId(this.sheetName)); // Don't actually change the source data if HyperFormula is not
// initialized yet. This is done to allow the `afterLoadData` hook to
// load the existing source data with `Handsontable#getSourceDataArray`
// properly.
if (dimensions.width === 0 && dimensions.height === 0) {
return;
}
var address = {
row: row,
// Workaround for inconsistencies in `src/dataSource.js`
col: this.toPhysicalColumnPosition(visualColumn),
sheet: this.sheetId
};
valueHolder.value = this.engine.getCellSerialized(address);
}
/**
* `onAfterSetDataAtCell` hook callback.
*
* @private
* @param {Array[]} changes An array of changes in format [[row, prop, oldValue, value], ...].
*/
}, {
key: "onAfterSetDataAtCell",
value: function onAfterSetDataAtCell(changes) {
var _this13 = this;
var dependentCells = [];
var outOfBoundsChanges = [];
var changedCells = [];
changes.forEach(function (_ref9) {
var _ref10 = _slicedToArray(_ref9, 4),
row = _ref10[0],
prop = _ref10[1],
newValue = _ref10[3];
var column = _this13.hot.propToCol(prop);
var physicalRow = _this13.hot.toPhysicalRow(row);
var physicalColumn = _this13.hot.toPhysicalColumn(column);
var address = {
row: physicalRow,
col: physicalColumn,
sheet: _this13.sheetId
};
if (physicalRow !== null && physicalColumn !== null) {
dependentCells.push.apply(dependentCells, _toConsumableArray(_this13.syncChangeWithEngine(row, column, newValue)));
} else {
outOfBoundsChanges.push([row, column, newValue]);
}
changedCells.push({
address: address
});
});
if (outOfBoundsChanges.length) {
// Workaround for rows/columns being created two times (by HOT and the engine).
// (unfortunately, this requires an extra re-render)
this.hot.addHookOnce('afterChange', function () {
var outOfBoundsDependentCells = [];
outOfBoundsChanges.forEach(function (_ref11) {
var _ref12 = _slicedToArray(_ref11, 3),
row = _ref12[0],
column = _ref12[1],
newValue = _ref12[2];
outOfBoundsDependentCells.push.apply(outOfBoundsDependentCells, _toConsumableArray(_this13.syncChangeWithEngine(row, column, newValue)));
});
_this13.renderDependentSheets(outOfBoundsDependentCells, true);
});
}
this.renderDependentSheets(dependentCells);
this.validateDependentCells(dependentCells, changedCells);
}
/**
* `onAfterSetSourceDataAtCell` hook callback.
*
* @private
* @param {Array[]} changes An array of changes in format [[row, column, oldValue, value], ...].
*/
}, {
key: "onAfterSetSourceDataAtCell",
value: function onAfterSetSourceDataAtCell(changes) {
var _this14 = this;
var dependentCells = [];
var changedCells = [];
changes.forEach(function (_ref13) {
var _ref14 = _slicedToArray(_ref13, 4),
row = _ref14[0],
column = _ref14[1],
newValue = _ref14[3];
var address = {
row: row,
col: _this14.toPhysicalColumnPosition(column),
sheet: _this14.sheetId
};
if (!_this14.engine.isItPossibleToSetCellContents(address)) {
(0, _console.warn)("Not possible to set source cell data at ".concat(JSON.stringify(address)));
return;
}
changedCells.push({
address: address
});
dependentCells.push.apply(dependentCells, _toConsumableArray(_this14.engine.setCellContents(address, newValue)));
});
this.renderDependentSheets(dependentCells);
this.validateDependentCells(dependentCells, changedCells);
}
/**
* `beforeCreateRow` hook callback.
*
* @private
* @param {number} row Represents the visual index of first newly created row in the data source array.
* @param {number} amount Number of newly created rows in the data source array.
* @returns {*|boolean} If false is returned the action is canceled.
*/
}, {
key: "onBeforeCreateRow",
value: function onBeforeCreateRow(row, amount) {
if (!this.engine.isItPossibleToAddRows(this.sheetId, [this.toPhysicalRowPosition(row), amount])) {
return false;
}
}
/**
* `beforeCreateCol` hook callback.
*
* @private
* @param {number} col Represents the visual index of first newly created column in the data source.
* @param {number} amount Number of newly created columns in the data source.
* @returns {*|boolean} If false is returned the action is canceled.
*/
}, {
key: "onBeforeCreateCol",
value: function onBeforeCreateCol(col, amount) {
if (!this.engine.isItPossibleToAddColumns(this.sheetId, [this.toPhysicalColumnPosition(col), amount])) {
return false;
}
}
/**
* `beforeRemoveRow` hook callback.
*
* @private
* @param {number} row Visual index of starter row.
* @param {number} amount Amount of rows to be removed.
* @param {number[]} physicalRows An array of physical rows removed from the data source.
* @returns {*|boolean} If false is returned the action is canceled.
*/
}, {
key: "onBeforeRemoveRow",
value: function onBeforeRemoveRow(row, amount, physicalRows) {
var _this15 = this;
var possible = physicalRows.every(function (physicalRow) {
return _this15.engine.isItPossibleToRemoveRows(_this15.sheetId, [physicalRow, 1]);
});
return possible === false ? false : void 0;
}
/**
* `beforeRemoveCol` hook callback.
*
* @private
* @param {number} col Visual index of starter column.
* @param {number} amount Amount of columns to be removed.
* @param {number[]} physicalColumns An array of physical columns removed from the data source.
* @returns {*|boolean} If false is returned the action is canceled.
*/
}, {
key: "onBeforeRemoveCol",
value: function onBeforeRemoveCol(col, amount, physicalColumns) {
var _this16 = this;
var possible = physicalColumns.every(function (physicalColumn) {
return _this16.engine.isItPossibleToRemoveColumns(_this16.sheetId, [physicalColumn, 1]);
});
return possible === false ? false : void 0;
}
/**
* `afterCreateRow` hook callback.
*
* @private
* @param {number} row Represents the visual index of first newly created row in the data source array.
* @param {number} amount Number of newly created rows in the data source array.
*/
}, {
key: "onAfterCreateRow",
value: function onAfterCreateRow(row, amount) {
var changes = this.engine.addRows(this.sheetId, [this.toPhysicalRowPosition(row), amount]);
this.renderDependentSheets(changes);
}
/**
* `afterCreateCol` hook callback.
*
* @private
* @param {number} col Represents the visual index of first newly created column in the data source.
* @param {number} amount Number of newly created columns in the data source.
*/
}, {
key: "onAfterCreateCol",
value: function onAfterCreateCol(col, amount) {
var changes = this.engine.addColumns(this.sheetId, [this.toPhysicalColumnPosition(col), amount]);
this.renderDependentSheets(changes);
}
/**
* `afterRemoveRow` hook callback.
*
* @private
* @param {number} row Visual index of starter row.
* @param {number} amount An amount of removed rows.
* @param {number[]} physicalRows An array of physical rows removed from the data source.
*/
}, {
key: "onAfterRemoveRow",
value: function onAfterRemoveRow(row, amount, physicalRows) {
var _this17 = this;
var descendingPhysicalRows = physicalRows.sort().reverse();
var changes = this.engine.batch(function () {
descendingPhysicalRows.forEach(function (physicalRow) {
_this17.engine.removeRows(_this17.sheetId, [physicalRow, 1]);
});
});
this.renderDependentSheets(changes);
}
/**
* `afterRemoveCol` hook callback.
*
* @private
* @param {number} col Visual index of starter column.
* @param {number} amount An amount of removed columns.
* @param {number[]} physicalColumns An array of physical columns removed from the data source.
*/
}, {
key: "onAfterRemoveCol",
value: function onAfterRemoveCol(col, amount, physicalColumns) {
var _this18 = this;
var descendingPhysicalColumns = physicalColumns.sort().reverse();
var changes = this.engine.batch(function () {
descendingPhysicalColumns.forEach(function (physicalColumn) {
_this18.engine.removeColumns(_this18.sheetId, [physicalColumn, 1]);
});
});
this.renderDependentSheets(changes);
}
/**
* Called when a value is updated in the engine.
*
* @private
* @fires Hooks#afterFormulasValuesUpdate
* @param {Array} changes The values and location of applied changes.
*/
}, {
key: "onEngineValuesUpdated",
value: function onEngineValuesUpdated(changes) {
this.hot.runHooks('afterFormulasValuesUpdate', changes);
}
/**
* Called when a named expression is added to the engine instance.
*
* @private
* @fires Hooks#afterNamedExpressionAdded
* @param {string} namedExpressionName The name of the added expression.
* @param {Array} changes The values and location of applied changes.
*/
}, {
key: "onEngineNamedExpressionsAdded",
value: function onEngineNamedExpressionsAdded(namedExpressionName, changes) {
this.hot.runHooks('afterNamedExpressionAdded', namedExpressionName, changes);
}
/**
* Called when a named expression is removed from the engine instance.
*
* @private
* @fires Hooks#afterNamedExpressionRemoved
* @param {string} namedExpressionName The name of the removed expression.
* @param {Array} changes The values and location of applied changes.
*/
}, {
key: "onEngineNamedExpressionsRemoved",
value: function onEngineNamedExpressionsRemoved(namedExpressionName, changes) {
this.hot.runHooks('afterNamedExpressionRemoved', namedExpressionName, changes);
}
/**
* Called when a new sheet is added to the engine instance.
*
* @private
* @fires Hooks#afterSheetAdded
* @param {string} addedSheetDisplayName The name of the added sheet.
*/
}, {
key: "onEngineSheetAdded",
value: function onEngineSheetAdded(addedSheetDisplayName) {
this.hot.runHooks('afterSheetAdded', addedSheetDisplayName);
}
/**
* Called when a sheet in the engine instance is renamed.
*
* @private
* @fires Hooks#afterSheetRenamed
* @param {string} oldDisplayName The old name of the sheet.
* @param {string} newDisplayName The new name of the sheet.
*/
}, {
key: "onEngineSheetRenamed",
value: function onEngineSheetRenamed(oldDisplayName, newDisplayName) {
this.hot.runHooks('afterSheetRenamed', oldDisplayName, newDisplayName);
}
/**
* Called when a sheet is removed from the engine instance.
*
* @private
* @fires Hooks#afterSheetRemoved
* @param {string} removedSheetDisplayName The removed sheet name.
* @param {Array} changes The values and location of applied changes.
*/
}, {
key: "onEngineSheetRemoved",
value: function onEngineSheetRemoved(removedSheetDisplayName, changes) {
this.hot.runHooks('afterSheetRemoved', removedSheetDisplayName, changes);
}
}], [{
key: "PLUGIN_KEY",
get: function get() {
return PLUGIN_KEY;
}
}, {
key: "PLUGIN_PRIORITY",
get: function get() {
return PLUGIN_PRIORITY;
}
/**
* Flag used to bypass hooks in internal operations.
*
* @private
* @type {boolean}
*/
}]);
return Formulas;
}(_base.BasePlugin);
exports.Formulas = Formulas;