UNPKG

handsontable

Version:

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

138 lines (132 loc) 5.98 kB
"use strict"; exports.__esModule = true; require("core-js/modules/es.error.cause.js"); require("core-js/modules/es.array.push.js"); var _constants = require("../constants"); var _viewOrder = require("./viewOrder"); 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 */ /** * A class which is responsible for generating commands/leads which has to be executed * to achieve new DOM nodes order. * * @class {ViewDiffer} */ class ViewDiffer { constructor(sizeSet) { _defineProperty(this, "sizeSet", void 0); this.sizeSet = sizeSet; } /** * A method which generates commands/leads which has to be executed to achieve new DOM * nodes order based on new offset and view size. * * For example, if current order looks like this (offset = 0, viewSize = 1): * <body> (root node) * └ <tr> (row 0) * and next order should look like this (offset: 0, viewSize = 5): * <body> (root node) * ├ <tr> (row 0) * ├ <tr> (row 1) * ├ <tr> (row 2) * ├ <tr> (row 3) * └ <tr> (row 4) * the generated commands/leads will look like this: * <body> (root node) * ├ <tr> (none, do nothing, leave as it is) * ├ <tr> (append this element at index 1) * ├ <tr> (append this element at index 2) * ├ <tr> (append this element at index 3) * └ <tr> (append this element at index 4) * * @returns {Array[]} Returns an array with generated commands/leads. */ diff() { const { sizeSet } = this; const { currentSize: currentViewSize, nextSize: nextViewSize } = sizeSet.getViewSize(); let maxSize = Math.max(nextViewSize, currentViewSize); if (maxSize === 0) { return []; } const { currentOffset, nextOffset } = sizeSet.getViewSize(); // @TODO(perf-tip): Creating an array (createRange) is not necessary it would be enough to generate // commands based on numeric values. const currentViewOrder = new _viewOrder.ViewOrder(currentOffset, currentViewSize); const nextViewOrder = new _viewOrder.ViewOrder(nextOffset, nextViewSize); const leads = []; for (let i = 0; i < maxSize; i++) { const currentIndex = currentViewOrder.get(i); const nextIndex = nextViewOrder.get(i); // Current index exceeds the next DOM index so it is necessary to generate a "remove" command // to achieve new order. if (nextIndex === -1) { leads.push(['remove', currentIndex]); // Next index exceeds the current DOM index so it is necessary to generate a "append" command // to achieve new order. } else if (currentIndex === -1) { // Check what command should be generated (depends on if this work as a shared root node // and in what position or not) if (!sizeSet.isShared() || sizeSet.isShared() && sizeSet.isPlaceOn(_constants.WORKING_SPACE_BOTTOM)) { /** * If the differ has only one root node to manage with, the "append" command is generated. * * For example: * ┌─────────── <body> (root node) * │ <tr> * │ <tr> * (managed by one <tr> * differ - TRs) <tr> * │ <tr> * └─────────── <tr> <--- Generates "append" command (which later executes `rootNode.appendChild(node)`) */ leads.push(['append', nextIndex]); } else { /** * If the differ is sharing root node, the "prepend" command is generated. * * For example: * ┌─────────── <tr> (root node, shared between two differs) * (first differ) <th> <--- Generates "prepend" command (which later executes `rootNode.insertBefore(...)`) * ├─────────── <td> * │ <td> * (second differ <td> * - TDs) <td> * │ <td> * └─────────── <td> */ leads.push(['prepend', nextIndex]); } } else if (nextIndex > currentIndex) { // This emulates DOM behavior when we try to append (or replace) an element which is already // mounted. The old index in the array has to be popped out indicating that an element was // moved to a different position. if (currentViewOrder.has(nextIndex)) { currentViewOrder.remove(nextIndex); // Decrease loop size to prevent generating "remove" leads. "remove" leads are necessary only for nodes // which are not mounted in current DOM order. if (nextViewSize <= currentViewOrder.length) { maxSize -= 1; } } leads.push(['replace', nextIndex, currentIndex]); } else if (nextIndex < currentIndex) { const indexToRemove = currentViewOrder.prepend(nextIndex); leads.push(['insert_before', nextIndex, currentIndex, indexToRemove]); } else { // for the same current and next indexes do nothing. leads.push(['none', nextIndex]); } } return leads; } } exports.ViewDiffer = ViewDiffer;