UNPKG

handsontable

Version:

Handsontable is a JavaScript Data Grid available for React, Angular and Vue.

335 lines (323 loc) 13.5 kB
"use strict"; exports.__esModule = true; require("core-js/modules/es.error.cause.js"); require("core-js/modules/es.array.push.js"); require("core-js/modules/es.set.difference.v2.js"); require("core-js/modules/es.set.intersection.v2.js"); require("core-js/modules/es.set.is-disjoint-from.v2.js"); require("core-js/modules/es.set.is-subset-of.v2.js"); require("core-js/modules/es.set.is-superset-of.v2.js"); require("core-js/modules/es.set.symmetric-difference.v2.js"); require("core-js/modules/es.set.union.v2.js"); require("core-js/modules/esnext.iterator.constructor.js"); require("core-js/modules/esnext.iterator.filter.js"); var _utils = require("./utils"); var _number = require("../../helpers/number"); function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* eslint-disable jsdoc/require-description-complete-sentence */ /** * @class LazyFactoryMap * * The LazyFactoryMap object holds key-value pairs in the structure similar to the * regular Map. Once created, items can be moved around a grid depending on the operations * performed on that grid - adding or removing rows. The collection requires "key" * to be a zero-based index. * * It's essential to notice that the "key" index under which the item was created * is volatile. After altering the grid, the "key" index can change. * * Having created N items with corresponding example data where the data has 10 * holes (`undefined` values) within (that's why internal storage index counts from 10). * +------+------+------+------+------+. * | 0/10 | 1/11 | 2/12 | 3/13 | 4/14 | Keys (volatile zero-based index / internal storage index) * +------+------+------+------+------+. * │ │ │ │ │ * +------+------+------+------+------+. * | AAA | BBB | CCC | DDD | EEE | Data * +------+------+------+------+------+. * * Map.obtain(0) // returns "AAA" * map.obtain(2) // returns "CCC". * * After inserting 2 new rows, keys that hold the data positioned after the place * where the new rows are added are upshifted by 2. * │ * │ Insert 2 rows * \│/ * +------+------+------+------+------+. * | 0/10 | 1/11 | 2/12 | 3/13 | 4/14 | Keys before * +------+------+------+------+------+. * * / 2 new rows \ * +------+------+------+------+------+------+------+. * | 0/10 | 1/11 | 2/15 | 3/16 | 4/12 | 5/13 | 6/14 | Keys after * +------+------+------+------+------+------+------+. * │ │ │ │ │ │ │ * │ │ └──────┼──────┼──────┼┐ │ * │ │ └──────┼──────┼┼────┼┐ * │ │ ┌─────────────┘ ││ ││ * │ │ │ ┌─────────────┘│ ││ * │ │ │ │ ┌───────┼────┘│ * │ │ │ │ │ │ │ * +------+------+------+------+------+------+------+. * | AAA | BBB | CCC | DDD | EEE | FFF | GGG | Data * +------+------+------+------+------+------+------+ * * Now at index 2 and 3 we have access to new items. * * map.obtain(2) // returns new value "FFF" for newly created row. * map.obtain(4) // index shifted by 2 has access to the old "CCC" value, as before inserting. * * after removing 4 rows, keys that hold the data positioned after the place where the * rows are removed are downshifted by 4. * │ * │ Remove 4 rows * ├───────────────────────────┐ * \│/ │ * +------+------+------+------+------+------+------+ * | 0/10 | 1/11 | 2/15 | 3/16 | 4/12 | 5/13 | 6/14 | Keys after * +------+------+------+------+------+------+------+ * │ │ │ │ │ │ │ * │ │ └──────┼──────┼──────┼┐ │ * │ │ └──────┼──────┼┼────┼┐ * │ │ ┌─────────────┘ ││ ││ * │ │ │ ┌─────────────┘│ ││ * │ │ │ │ ┌───────┼────┘│ * │ │ │ │ │ │ │ * +------+------+------+------+------+------+------+ * | AAA | BBB | CCC | DDD | EEE | FFF | GGG | Data * +------+------+------+------+------+------+------+ * * +------+------+------+ * | 0/10 | 1/13 | 2/14 | Keys after * +------+------+------+ * │ │ │ * │ │ └─────────────┐ * │ └────────────┐ │ * │ │ │ * │ │ │ * │ │ │ * │ │ │ * +------+------+------+------+------+------+------+ * | AAA | BBB | CCC | DDD | EEE | FFF | GGG | Data * +------+------+------+------+------+------+------+ * /│\ /│\ /│\ /│\ * └──┬──┘ └──┬──┘ * This data is marked as "hole" which * means that can be replaced by new item * when that will be created. * * map.obtain(2) // returns the value ("EEE") as it should. Access to the value is * // changed (the key was downshifted). However, the internal index has not changed, * // which means that the data does not need to be changed (spliced) too. * * After previous remove operation which creates some "holes" obtaining new * items replaces that "holes" as follows: * * // Obtains new item * map.obtain(90) // Returns "NEW" value * * +------+------+------+...+------+ * | 0/10 | 1/13 | 2/14 | | 90/0 | Keys after * +------+------+------+...+------+ * │ │ │ │ * │ │ └──────────┼────────────┐ * │ └─────────────────┼─────┐ │ * └──────────┐ │ │ │ * │ │ │ │ * ┌──────────┼──────────────┘ │ │ * │ │ │ │ * +------+...+------+------+------+------+------+-----+ * | NEW | | AAA | BBB | CCC | DDD | EEE | FFF | Data * +------+...+------+------+------+------+------+-----+ * /│\ * │ * The first "hole" (at index 0) item is permanently removed and replaced by a new item. * The hole index is taken from the hole collection which act as FIFO (First In First Out). */ /* eslint-enable jsdoc/require-description-complete-sentence */ class LazyFactoryMap { constructor(valueFactory) { /** * The data factory function. * * @type {Function} */ _defineProperty(this, "valueFactory", void 0); /** * An array which contains data. * * @type {Array} */ _defineProperty(this, "data", []); /** * An array of indexes where the key of the array is mapped to the value which points to the * specific position of the data array. * * @type {number[]} */ _defineProperty(this, "index", []); /** * The collection of indexes that points to the data items which can be replaced by obtaining new * ones. The "holes" are an intended effect of deleting entries. * * The idea of "holes" generally allows us to not modify the "data" structure while removing * items from the collection. * * @type {Set<number>} */ _defineProperty(this, "holes", new Set()); this.valueFactory = valueFactory; } /** * Gets or if data not exist creates and returns new data. * * @param {number} key The item key as zero-based index. * @returns {*} */ obtain(key) { (0, _utils.assert)(() => (0, _number.isUnsignedNumber)(key), 'Expecting an unsigned number.'); const dataIndex = this._getStorageIndexByKey(key); let result; if (dataIndex >= 0) { result = this.data[dataIndex]; if (result === undefined) { result = this.valueFactory(key); this.data[dataIndex] = result; } } else { result = this.valueFactory(key); if (this.holes.size > 0) { const reuseIndex = this.holes.values().next().value; // Gets first item from the collection this.holes.delete(reuseIndex); this.data[reuseIndex] = result; this.index[key] = reuseIndex; } else { this.data.push(result); this.index[key] = this.data.length - 1; } } return result; } /** * Inserts an empty data to the map. This method creates an empty space for obtaining * new data. * * @param {number} key The key as volatile zero-based index at which to begin inserting space for new data. * @param {number} [amount=1] Amount of data to insert. */ insert(key) { let amount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; (0, _utils.assert)(() => (0, _number.isUnsignedNumber)(key) || (0, _utils.isNullish)(key), 'Expecting an unsigned number or null/undefined argument.'); const newIndexes = []; const dataLength = this.data.length; for (let i = 0; i < amount; i++) { newIndexes.push(dataLength + i); this.data.push(undefined); } const insertionIndex = (0, _utils.isNullish)(key) ? this.index.length : key; this.index = [...this.index.slice(0, insertionIndex), ...newIndexes, ...this.index.slice(insertionIndex)]; } /** * Removes (soft remove) data from "index" and according to the amount of data. * * @param {number} key The key as volatile zero-based index at which to begin removing the data. * @param {number} [amount=1] Amount data to remove. */ remove(key) { let amount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; (0, _utils.assert)(() => (0, _number.isUnsignedNumber)(key) || (0, _utils.isNullish)(key), 'Expecting an unsigned number or null/undefined argument.'); const removed = this.index.splice((0, _utils.isNullish)(key) ? this.index.length - amount : key, amount); for (let i = 0; i < removed.length; i++) { const removedIndex = removed[i]; if (typeof removedIndex === 'number') { this.holes.add(removedIndex); } } } /** * Returns the size of the data which this map holds. * * @returns {number} */ size() { return this.data.length - this.holes.size; } /** * Returns a new Iterator object that contains the values for each item in the LazyMap object. * * @returns {Iterator} */ values() { return this.data.filter((meta, index) => { return meta !== undefined && !this.holes.has(index); })[Symbol.iterator](); } /** * Returns a new Iterator object that contains an array of `[index, value]` for each item in the LazyMap object. * * @returns {Iterator} */ entries() { const validEntries = []; for (let i = 0; i < this.data.length; i++) { const keyIndex = this._getKeyByStorageIndex(i); if (keyIndex !== -1 && this.data[i] !== undefined) { validEntries.push([keyIndex, this.data[i]]); } } let dataIndex = 0; return { next: () => { if (dataIndex < validEntries.length) { const value = validEntries[dataIndex]; dataIndex += 1; return { value, done: false }; } return { done: true }; } }; } /** * Clears the map. */ clear() { this.data = []; this.index = []; this.holes.clear(); } /** * Gets storage index calculated from the key associated with the specified value. * * @param {number} key Volatile zero-based index. * @returns {number} Returns index 0-N or -1 if no storage index found. */ _getStorageIndexByKey(key) { return this.index.length > key ? this.index[key] : -1; } /** * Gets the key associated with the specified value calculated from storage index. * * @param {number} dataIndex Zero-based storage index. * @returns {number} Returns index 0-N or -1 if no key found. */ _getKeyByStorageIndex(dataIndex) { return this.index.indexOf(dataIndex); } /** * Makes this object iterable. * * @returns {Iterator} */ [Symbol.iterator]() { return this.entries(); } } exports.default = LazyFactoryMap;