handsontable
Version:
Handsontable is a JavaScript Spreadsheet Component available for React, Angular and Vue.
1,235 lines (1,039 loc) • 52.2 kB
JavaScript
import "core-js/modules/es.array.iterator.js";
import "core-js/modules/es.number.constructor.js";
import "core-js/modules/es.number.is-integer.js";
import "core-js/modules/es.object.to-string.js";
import "core-js/modules/es.string.iterator.js";
import "core-js/modules/es.weak-map.js";
import "core-js/modules/web.dom-collections.iterator.js";
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 _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; 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"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
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 _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 _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
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 _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; }
import { addClass, clearTextSelection, empty, fastInnerHTML, fastInnerText, getScrollbarWidth, hasClass, isChildOf, isInput, isOutsideInput } from "./helpers/dom/element.mjs";
import EventManager from "./eventManager.mjs";
import { isImmediatePropagationStopped, isRightClick, isLeftClick } from "./helpers/dom/event.mjs";
import Walkontable, { CellCoords } from "./3rdparty/walkontable/src/index.mjs";
import { handleMouseEvent } from "./selection/mouseEventHandler.mjs";
var privatePool = new WeakMap();
/**
* @class TableView
* @private
*/
var TableView = /*#__PURE__*/function () {
/**
* @param {Hanstontable} instance Instance of {@link Handsontable}.
*/
function TableView(instance) {
_classCallCheck(this, TableView);
/**
* Instance of {@link Handsontable}.
*
* @private
* @type {Handsontable}
*/
this.instance = instance;
/**
* Instance of {@link EventManager}.
*
* @private
* @type {EventManager}
*/
this.eventManager = new EventManager(instance);
/**
* Current Handsontable's GridSettings object.
*
* @private
* @type {GridSettings}
*/
this.settings = instance.getSettings();
/**
* Main <THEAD> element.
*
* @type {HTMLTableSectionElement}
*/
this.THEAD = void 0;
/**
* Main <TBODY> element.
*
* @type {HTMLTableSectionElement}
*/
this.TBODY = void 0;
/**
* Main Walkontable instance.
*
* @type {Walkontable}
*/
this.wt = void 0;
/**
* Main Walkontable instance.
*
* @private
* @type {Walkontable}
*/
this.activeWt = void 0;
/**
* The flag determines if the `adjustElementsSize` method call was made during
* the render suspending. If true, the method has to be triggered once after render
* resuming.
*
* @private
* @type {boolean}
*/
this.postponedAdjustElementsSize = false;
privatePool.set(this, {
/**
* Defines if the text should be selected during mousemove.
*
* @private
* @type {boolean}
*/
selectionMouseDown: false,
/**
* @private
* @type {boolean}
*/
mouseDown: void 0,
/**
* Main <TABLE> element.
*
* @private
* @type {HTMLTableElement}
*/
table: void 0,
/**
* Cached width of the rootElement.
*
* @type {number}
*/
lastWidth: 0,
/**
* Cached height of the rootElement.
*
* @type {number}
*/
lastHeight: 0
});
this.createElements();
this.registerEvents();
this.initializeWalkontable();
}
/**
* Renders WalkontableUI.
*/
_createClass(TableView, [{
key: "render",
value: function render() {
if (!this.instance.isRenderSuspended()) {
if (this.postponedAdjustElementsSize) {
this.postponedAdjustElementsSize = false;
this.adjustElementsSize(true);
}
this.wt.draw(!this.instance.forceFullRender);
this.instance.forceFullRender = false;
this.instance.renderCall = false;
}
}
/**
* Adjust overlays elements size and master table size.
*
* @param {boolean} [force=false] When `true`, it adjust the DOM nodes sizes for all overlays.
*/
}, {
key: "adjustElementsSize",
value: function adjustElementsSize() {
var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
if (this.instance.isRenderSuspended()) {
this.postponedAdjustElementsSize = true;
} else {
this.wt.wtOverlays.adjustElementsSize(force);
}
}
/**
* Returns td object given coordinates.
*
* @param {CellCoords} coords Renderable cell coordinates.
* @param {boolean} topmost Indicates whether the cell should be calculated from the topmost.
* @returns {HTMLTableCellElement|null}
*/
}, {
key: "getCellAtCoords",
value: function getCellAtCoords(coords, topmost) {
var td = this.wt.getCell(coords, topmost);
if (td < 0) {
// there was an exit code (cell is out of bounds)
return null;
}
return td;
}
/**
* Scroll viewport to a cell.
*
* @param {CellCoords} coords Renderable cell coordinates.
* @param {boolean} [snapToTop] If `true`, viewport is scrolled to show the cell on the top of the table.
* @param {boolean} [snapToRight] If `true`, viewport is scrolled to show the cell on the right side of the table.
* @param {boolean} [snapToBottom] If `true`, viewport is scrolled to show the cell on the bottom side of the table.
* @param {boolean} [snapToLeft] If `true`, viewport is scrolled to show the cell on the left side of the table.
* @returns {boolean}
*/
}, {
key: "scrollViewport",
value: function scrollViewport(coords, snapToTop, snapToRight, snapToBottom, snapToLeft) {
return this.wt.scrollViewport(coords, snapToTop, snapToRight, snapToBottom, snapToLeft);
}
/**
* Scroll viewport to a column.
*
* @param {number} column Renderable column index.
* @param {boolean} [snapToRight] If `true`, viewport is scrolled to show the cell on the right side of the table.
* @param {boolean} [snapToLeft] If `true`, viewport is scrolled to show the cell on the left side of the table.
* @returns {boolean}
*/
}, {
key: "scrollViewportHorizontally",
value: function scrollViewportHorizontally(column, snapToRight, snapToLeft) {
return this.wt.scrollViewportHorizontally(column, snapToRight, snapToLeft);
}
/**
* Scroll viewport to a row.
*
* @param {number} row Renderable row index.
* @param {boolean} [snapToTop] If `true`, viewport is scrolled to show the cell on the top of the table.
* @param {boolean} [snapToBottom] If `true`, viewport is scrolled to show the cell on the bottom side of the table.
* @returns {boolean}
*/
}, {
key: "scrollViewportVertically",
value: function scrollViewportVertically(row, snapToTop, snapToBottom) {
return this.wt.scrollViewportVertically(row, snapToTop, snapToBottom);
}
/**
* Prepares DOMElements and adds correct className to the root element.
*
* @private
*/
}, {
key: "createElements",
value: function createElements() {
var priv = privatePool.get(this);
var _this$instance = this.instance,
rootElement = _this$instance.rootElement,
rootDocument = _this$instance.rootDocument;
var originalStyle = rootElement.getAttribute('style');
if (originalStyle) {
rootElement.setAttribute('data-originalstyle', originalStyle); // needed to retrieve original style in jsFiddle link generator in HT examples. may be removed in future versions
}
addClass(rootElement, 'handsontable');
priv.table = rootDocument.createElement('TABLE');
addClass(priv.table, 'htCore');
if (this.instance.getSettings().tableClassName) {
addClass(priv.table, this.instance.getSettings().tableClassName);
}
this.THEAD = rootDocument.createElement('THEAD');
priv.table.appendChild(this.THEAD);
this.TBODY = rootDocument.createElement('TBODY');
priv.table.appendChild(this.TBODY);
this.instance.table = priv.table;
this.instance.container.insertBefore(priv.table, this.instance.container.firstChild);
}
/**
* Attaches necessary listeners.
*
* @private
*/
}, {
key: "registerEvents",
value: function registerEvents() {
var _this = this;
var priv = privatePool.get(this);
var _this$instance2 = this.instance,
rootElement = _this$instance2.rootElement,
rootDocument = _this$instance2.rootDocument,
selection = _this$instance2.selection;
var documentElement = rootDocument.documentElement;
this.eventManager.addEventListener(rootElement, 'mousedown', function (event) {
priv.selectionMouseDown = true;
if (!_this.isTextSelectionAllowed(event.target)) {
var rootWindow = _this.instance.rootWindow;
clearTextSelection(rootWindow);
event.preventDefault();
rootWindow.focus(); // make sure that window that contains HOT is active. Important when HOT is in iframe.
}
});
this.eventManager.addEventListener(rootElement, 'mouseup', function () {
priv.selectionMouseDown = false;
});
this.eventManager.addEventListener(rootElement, 'mousemove', function (event) {
if (priv.selectionMouseDown && !_this.isTextSelectionAllowed(event.target)) {
// Clear selection only when fragmentSelection is enabled, otherwise clearing selection breakes the IME editor.
if (_this.settings.fragmentSelection) {
clearTextSelection(_this.instance.rootWindow);
}
event.preventDefault();
}
});
this.eventManager.addEventListener(documentElement, 'keyup', function (event) {
if (selection.isInProgress() && !event.shiftKey) {
selection.finish();
}
});
this.eventManager.addEventListener(documentElement, 'mouseup', function (event) {
if (selection.isInProgress() && isLeftClick(event)) {
// is left mouse button
selection.finish();
}
priv.mouseDown = false;
if (isOutsideInput(rootDocument.activeElement) || !selection.isSelected() && !selection.isSelectedByAnyHeader() && !rootElement.contains(event.target) && !isRightClick(event)) {
_this.instance.unlisten();
}
});
this.eventManager.addEventListener(documentElement, 'contextmenu', function (event) {
if (selection.isInProgress() && isRightClick(event)) {
selection.finish();
priv.mouseDown = false;
}
});
this.eventManager.addEventListener(documentElement, 'touchend', function () {
if (selection.isInProgress()) {
selection.finish();
}
priv.mouseDown = false;
});
this.eventManager.addEventListener(documentElement, 'mousedown', function (event) {
var originalTarget = event.target;
var eventX = event.x || event.clientX;
var eventY = event.y || event.clientY;
var next = event.target;
if (priv.mouseDown || !rootElement || !_this.instance.view) {
return; // it must have been started in a cell
} // immediate click on "holder" means click on the right side of vertical scrollbar
var holder = _this.instance.view.wt.wtTable.holder;
if (next === holder) {
var scrollbarWidth = getScrollbarWidth(rootDocument);
if (rootDocument.elementFromPoint(eventX + scrollbarWidth, eventY) !== holder || rootDocument.elementFromPoint(eventX, eventY + scrollbarWidth) !== holder) {
return;
}
} else {
while (next !== documentElement) {
if (next === null) {
if (event.isTargetWebComponent) {
break;
} // click on something that was a row but now is detached (possibly because your click triggered a rerender)
return;
}
if (next === rootElement) {
// click inside container
return;
}
next = next.parentNode;
}
} // function did not return until here, we have an outside click!
var outsideClickDeselects = typeof _this.settings.outsideClickDeselects === 'function' ? _this.settings.outsideClickDeselects(originalTarget) : _this.settings.outsideClickDeselects;
if (outsideClickDeselects) {
_this.instance.deselectCell();
} else {
_this.instance.destroyEditor(false, false);
}
});
this.eventManager.addEventListener(priv.table, 'selectstart', function (event) {
if (_this.settings.fragmentSelection || isInput(event.target)) {
return;
} // https://github.com/handsontable/handsontable/issues/160
// Prevent text from being selected when performing drag down.
event.preventDefault();
});
}
/**
* Translate renderable cell coordinates to visual coordinates.
*
* @param {CellCoords} coords The cell coordinates.
* @returns {CellCoords}
*/
}, {
key: "translateFromRenderableToVisualCoords",
value: function translateFromRenderableToVisualCoords(_ref) {
var row = _ref.row,
col = _ref.col;
// TODO: To consider an idea to reusing the CellCoords instance instead creating new one.
return _construct(CellCoords, _toConsumableArray(this.translateFromRenderableToVisualIndex(row, col)));
}
/**
* Translate renderable row and column indexes to visual row and column indexes.
*
* @param {number} renderableRow Renderable row index.
* @param {number} renderableColumn Renderable columnIndex.
* @returns {number[]}
*/
}, {
key: "translateFromRenderableToVisualIndex",
value: function translateFromRenderableToVisualIndex(renderableRow, renderableColumn) {
// TODO: Some helper may be needed.
// We perform translation for indexes (without headers).
var visualRow = renderableRow >= 0 ? this.instance.rowIndexMapper.getVisualFromRenderableIndex(renderableRow) : renderableRow;
var visualColumn = renderableColumn >= 0 ? this.instance.columnIndexMapper.getVisualFromRenderableIndex(renderableColumn) : renderableColumn;
if (visualRow === null) {
visualRow = renderableRow;
}
if (visualColumn === null) {
visualColumn = renderableColumn;
}
return [visualRow, visualColumn];
}
/**
* Returns the number of renderable indexes.
*
* @private
* @param {IndexMapper} indexMapper The IndexMapper instance for specific axis.
* @param {number} maxElements Maximum number of elements (rows or columns).
*
* @returns {number|*}
*/
}, {
key: "countRenderableIndexes",
value: function countRenderableIndexes(indexMapper, maxElements) {
var consideredElements = Math.min(indexMapper.getNotTrimmedIndexesLength(), maxElements); // Don't take hidden indexes into account. We are looking just for renderable indexes.
var firstNotHiddenIndex = indexMapper.getFirstNotHiddenIndex(consideredElements - 1, -1); // There are no renderable indexes.
if (firstNotHiddenIndex === null) {
return 0;
}
return indexMapper.getRenderableFromVisualIndex(firstNotHiddenIndex) + 1;
}
/**
* Returns the number of renderable columns.
*
* @returns {number}
*/
}, {
key: "countRenderableColumns",
value: function countRenderableColumns() {
return this.countRenderableIndexes(this.instance.columnIndexMapper, this.settings.maxCols);
}
/**
* Returns the number of renderable rows.
*
* @returns {number}
*/
}, {
key: "countRenderableRows",
value: function countRenderableRows() {
return this.countRenderableIndexes(this.instance.rowIndexMapper, this.settings.maxRows);
}
/**
* Returns number of not hidden row indexes counting from the passed starting index.
* The counting direction can be controlled by `incrementBy` argument.
*
* @param {number} visualIndex The visual index from which the counting begins.
* @param {number} incrementBy If `-1` then counting is backwards or forward when `1`.
* @returns {number}
*/
}, {
key: "countNotHiddenRowIndexes",
value: function countNotHiddenRowIndexes(visualIndex, incrementBy) {
return this.countNotHiddenIndexes(visualIndex, incrementBy, this.instance.rowIndexMapper, this.countRenderableRows());
}
/**
* Returns number of not hidden column indexes counting from the passed starting index.
* The counting direction can be controlled by `incrementBy` argument.
*
* @param {number} visualIndex The visual index from which the counting begins.
* @param {number} incrementBy If `-1` then counting is backwards or forward when `1`.
* @returns {number}
*/
}, {
key: "countNotHiddenColumnIndexes",
value: function countNotHiddenColumnIndexes(visualIndex, incrementBy) {
return this.countNotHiddenIndexes(visualIndex, incrementBy, this.instance.columnIndexMapper, this.countRenderableColumns());
}
/**
* Returns number of not hidden indexes counting from the passed starting index.
* The counting direction can be controlled by `incrementBy` argument.
*
* @param {number} visualIndex The visual index from which the counting begins.
* @param {number} incrementBy If `-1` then counting is backwards or forward when `1`.
* @param {IndexMapper} indexMapper The IndexMapper instance for specific axis.
* @param {number} renderableIndexesCount Total count of renderable indexes for specific axis.
* @returns {number}
*/
}, {
key: "countNotHiddenIndexes",
value: function countNotHiddenIndexes(visualIndex, incrementBy, indexMapper, renderableIndexesCount) {
if (isNaN(visualIndex) || visualIndex < 0) {
return 0;
}
var firstVisibleIndex = indexMapper.getFirstNotHiddenIndex(visualIndex, incrementBy);
var renderableIndex = indexMapper.getRenderableFromVisualIndex(firstVisibleIndex);
if (!Number.isInteger(renderableIndex)) {
return 0;
}
var notHiddenIndexes = 0;
if (incrementBy < 0) {
// Zero-based numbering for renderable indexes corresponds to a number of not hidden indexes.
notHiddenIndexes = renderableIndex + 1;
} else if (incrementBy > 0) {
notHiddenIndexes = renderableIndexesCount - renderableIndex;
}
return notHiddenIndexes;
}
/**
* Defines default configuration and initializes WalkOnTable intance.
*
* @private
*/
}, {
key: "initializeWalkontable",
value: function initializeWalkontable() {
var _this2 = this;
var priv = privatePool.get(this);
var walkontableConfig = {
externalRowCalculator: this.instance.getPlugin('autoRowSize') && this.instance.getPlugin('autoRowSize').isEnabled(),
table: priv.table,
preventOverflow: function preventOverflow() {
return _this2.settings.preventOverflow;
},
preventWheel: function preventWheel() {
return _this2.settings.preventWheel;
},
stretchH: function stretchH() {
return _this2.settings.stretchH;
},
data: function data(renderableRow, renderableColumn) {
var _this2$instance;
return (_this2$instance = _this2.instance).getDataAtCell.apply(_this2$instance, _toConsumableArray(_this2.translateFromRenderableToVisualIndex(renderableRow, renderableColumn)));
},
totalRows: function totalRows() {
return _this2.countRenderableRows();
},
totalColumns: function totalColumns() {
return _this2.countRenderableColumns();
},
// Number of renderable columns for the left overlay.
fixedColumnsLeft: function fixedColumnsLeft() {
var countCols = _this2.instance.countCols();
var visualFixedColumnsLeft = Math.min(parseInt(_this2.settings.fixedColumnsLeft, 10), countCols) - 1;
return _this2.countNotHiddenColumnIndexes(visualFixedColumnsLeft, -1);
},
// Number of renderable rows for the top overlay.
fixedRowsTop: function fixedRowsTop() {
var countRows = _this2.instance.countRows();
var visualFixedRowsTop = Math.min(parseInt(_this2.settings.fixedRowsTop, 10), countRows) - 1;
return _this2.countNotHiddenRowIndexes(visualFixedRowsTop, -1);
},
// Number of renderable rows for the bottom overlay.
fixedRowsBottom: function fixedRowsBottom() {
var countRows = _this2.instance.countRows();
var visualFixedRowsBottom = Math.max(countRows - parseInt(_this2.settings.fixedRowsBottom, 10), 0);
return _this2.countNotHiddenRowIndexes(visualFixedRowsBottom, 1);
},
// Enable the left overlay when conditions are met.
shouldRenderLeftOverlay: function shouldRenderLeftOverlay() {
return _this2.settings.fixedColumnsLeft > 0 || walkontableConfig.rowHeaders().length > 0;
},
// Enable the top overlay when conditions are met.
shouldRenderTopOverlay: function shouldRenderTopOverlay() {
return _this2.settings.fixedRowsTop > 0 || walkontableConfig.columnHeaders().length > 0;
},
// Enable the bottom overlay when conditions are met.
shouldRenderBottomOverlay: function shouldRenderBottomOverlay() {
return _this2.settings.fixedRowsBottom > 0;
},
minSpareRows: function minSpareRows() {
return _this2.settings.minSpareRows;
},
renderAllRows: this.settings.renderAllRows,
rowHeaders: function rowHeaders() {
var headerRenderers = [];
if (_this2.instance.hasRowHeaders()) {
headerRenderers.push(function (renderableRowIndex, TH) {
// TODO: Some helper may be needed.
// We perform translation for row indexes (without row headers).
var visualRowIndex = renderableRowIndex >= 0 ? _this2.instance.rowIndexMapper.getVisualFromRenderableIndex(renderableRowIndex) : renderableRowIndex;
_this2.appendRowHeader(visualRowIndex, TH);
});
}
_this2.instance.runHooks('afterGetRowHeaderRenderers', headerRenderers);
return headerRenderers;
},
columnHeaders: function columnHeaders() {
var headerRenderers = [];
if (_this2.instance.hasColHeaders()) {
headerRenderers.push(function (renderedColumnIndex, TH) {
// TODO: Some helper may be needed.
// We perform translation for columns indexes (without column headers).
var visualColumnsIndex = renderedColumnIndex >= 0 ? _this2.instance.columnIndexMapper.getVisualFromRenderableIndex(renderedColumnIndex) : renderedColumnIndex;
_this2.appendColHeader(visualColumnsIndex, TH);
});
}
_this2.instance.runHooks('afterGetColumnHeaderRenderers', headerRenderers);
return headerRenderers;
},
columnWidth: function columnWidth(renderedColumnIndex) {
var visualIndex = _this2.instance.columnIndexMapper.getVisualFromRenderableIndex(renderedColumnIndex); // It's not a bug that we can't find visual index for some handled by method indexes. The function is called also
// for not displayed indexes (beyond the table boundaries), i.e. when `fixedColumnsLeft` > `startCols` (wrong config?) or
// scrolling and dataset is empty (scroll should handle that?).
return _this2.instance.getColWidth(visualIndex === null ? renderedColumnIndex : visualIndex);
},
rowHeight: function rowHeight(renderedRowIndex) {
var visualIndex = _this2.instance.rowIndexMapper.getVisualFromRenderableIndex(renderedRowIndex);
return _this2.instance.getRowHeight(visualIndex === null ? renderedRowIndex : visualIndex);
},
cellRenderer: function cellRenderer(renderedRowIndex, renderedColumnIndex, TD) {
var _this2$translateFromR = _this2.translateFromRenderableToVisualIndex(renderedRowIndex, renderedColumnIndex),
_this2$translateFromR2 = _slicedToArray(_this2$translateFromR, 2),
visualRowIndex = _this2$translateFromR2[0],
visualColumnIndex = _this2$translateFromR2[1]; // Coords may be modified. For example, by the `MergeCells` plugin. It should affect cell value and cell meta.
var modifiedCellCoords = _this2.instance.runHooks('modifyGetCellCoords', visualRowIndex, visualColumnIndex);
var visualRowToCheck = visualRowIndex;
var visualColumnToCheck = visualColumnIndex;
if (Array.isArray(modifiedCellCoords)) {
var _modifiedCellCoords = _slicedToArray(modifiedCellCoords, 2);
visualRowToCheck = _modifiedCellCoords[0];
visualColumnToCheck = _modifiedCellCoords[1];
}
var cellProperties = _this2.instance.getCellMeta(visualRowToCheck, visualColumnToCheck);
var prop = _this2.instance.colToProp(visualColumnToCheck);
var value = _this2.instance.getDataAtRowProp(visualRowToCheck, prop);
if (_this2.instance.hasHook('beforeValueRender')) {
value = _this2.instance.runHooks('beforeValueRender', value, cellProperties);
}
_this2.instance.runHooks('beforeRenderer', TD, visualRowIndex, visualColumnIndex, prop, value, cellProperties);
_this2.instance.getCellRenderer(cellProperties)(_this2.instance, TD, visualRowIndex, visualColumnIndex, prop, value, cellProperties);
_this2.instance.runHooks('afterRenderer', TD, visualRowIndex, visualColumnIndex, prop, value, cellProperties);
},
selections: this.instance.selection.highlight,
hideBorderOnMouseDownOver: function hideBorderOnMouseDownOver() {
return _this2.settings.fragmentSelection;
},
onWindowResize: function onWindowResize() {
if (!_this2.instance || _this2.instance.isDestroyed) {
return;
}
_this2.instance.refreshDimensions();
},
onCellMouseDown: function onCellMouseDown(event, coords, TD, wt) {
var visualCoords = _this2.translateFromRenderableToVisualCoords(coords);
var blockCalculations = {
row: false,
column: false,
cell: false
};
_this2.instance.listen();
_this2.activeWt = wt;
priv.mouseDown = true;
_this2.instance.runHooks('beforeOnCellMouseDown', event, visualCoords, TD, blockCalculations);
if (isImmediatePropagationStopped(event)) {
return;
}
handleMouseEvent(event, {
coords: visualCoords,
selection: _this2.instance.selection,
controller: blockCalculations
});
_this2.instance.runHooks('afterOnCellMouseDown', event, visualCoords, TD);
_this2.activeWt = _this2.wt;
},
onCellContextMenu: function onCellContextMenu(event, coords, TD, wt) {
var visualCoords = _this2.translateFromRenderableToVisualCoords(coords);
_this2.activeWt = wt;
priv.mouseDown = false;
if (_this2.instance.selection.isInProgress()) {
_this2.instance.selection.finish();
}
_this2.instance.runHooks('beforeOnCellContextMenu', event, visualCoords, TD);
if (isImmediatePropagationStopped(event)) {
return;
}
_this2.instance.runHooks('afterOnCellContextMenu', event, visualCoords, TD);
_this2.activeWt = _this2.wt;
},
onCellMouseOut: function onCellMouseOut(event, coords, TD, wt) {
var visualCoords = _this2.translateFromRenderableToVisualCoords(coords);
_this2.activeWt = wt;
_this2.instance.runHooks('beforeOnCellMouseOut', event, visualCoords, TD);
if (isImmediatePropagationStopped(event)) {
return;
}
_this2.instance.runHooks('afterOnCellMouseOut', event, visualCoords, TD);
_this2.activeWt = _this2.wt;
},
onCellMouseOver: function onCellMouseOver(event, coords, TD, wt) {
var visualCoords = _this2.translateFromRenderableToVisualCoords(coords);
var blockCalculations = {
row: false,
column: false,
cell: false
};
_this2.activeWt = wt;
_this2.instance.runHooks('beforeOnCellMouseOver', event, visualCoords, TD, blockCalculations);
if (isImmediatePropagationStopped(event)) {
return;
}
if (priv.mouseDown) {
handleMouseEvent(event, {
coords: visualCoords,
selection: _this2.instance.selection,
controller: blockCalculations
});
}
_this2.instance.runHooks('afterOnCellMouseOver', event, visualCoords, TD);
_this2.activeWt = _this2.wt;
},
onCellMouseUp: function onCellMouseUp(event, coords, TD, wt) {
var visualCoords = _this2.translateFromRenderableToVisualCoords(coords);
_this2.activeWt = wt;
_this2.instance.runHooks('beforeOnCellMouseUp', event, visualCoords, TD); // TODO: Second argument is for workaround. Callback corresponding the method `updateSettings` disable plugin
// and enable it again. Disabling plugin closes the menu. Thus, calling the `updateSettings` in a body of
// any callback executed right after some context-menu action breaks the table (#7231).
if (isImmediatePropagationStopped(event) || _this2.instance.isDestroyed) {
return;
}
_this2.instance.runHooks('afterOnCellMouseUp', event, visualCoords, TD);
_this2.activeWt = _this2.wt;
},
onCellCornerMouseDown: function onCellCornerMouseDown(event) {
event.preventDefault();
_this2.instance.runHooks('afterOnCellCornerMouseDown', event);
},
onCellCornerDblClick: function onCellCornerDblClick(event) {
event.preventDefault();
_this2.instance.runHooks('afterOnCellCornerDblClick', event);
},
beforeDraw: function beforeDraw(force, skipRender) {
return _this2.beforeRender(force, skipRender);
},
onDraw: function onDraw(force) {
return _this2.onDraw(force);
},
onScrollVertically: function onScrollVertically() {
return _this2.instance.runHooks('afterScrollVertically');
},
onScrollHorizontally: function onScrollHorizontally() {
return _this2.instance.runHooks('afterScrollHorizontally');
},
onBeforeRemoveCellClassNames: function onBeforeRemoveCellClassNames() {
return _this2.instance.runHooks('beforeRemoveCellClassNames');
},
onAfterDrawSelection: function onAfterDrawSelection(currentRow, currentColumn, layerLevel) {
var cornersOfSelection;
var _this2$translateFromR3 = _this2.translateFromRenderableToVisualIndex(currentRow, currentColumn),
_this2$translateFromR4 = _slicedToArray(_this2$translateFromR3, 2),
visualRowIndex = _this2$translateFromR4[0],
visualColumnIndex = _this2$translateFromR4[1];
var selectedRange = _this2.instance.selection.getSelectedRange();
var selectionRangeSize = selectedRange.size();
if (selectionRangeSize > 0) {
// Selection layers are stored from the "oldest" to the "newest". We should calculate the offset.
// Please look at the `SelectedRange` class and it's method for getting selection's layer for more information.
var selectionOffset = (layerLevel !== null && layerLevel !== void 0 ? layerLevel : 0) + 1 - selectionRangeSize;
var selectionForLayer = selectedRange.peekByIndex(selectionOffset);
cornersOfSelection = [selectionForLayer.from.row, selectionForLayer.from.col, selectionForLayer.to.row, selectionForLayer.to.col];
}
return _this2.instance.runHooks('afterDrawSelection', visualRowIndex, visualColumnIndex, cornersOfSelection, layerLevel);
},
onBeforeDrawBorders: function onBeforeDrawBorders(corners, borderClassName) {
var _corners = _slicedToArray(corners, 4),
startRenderableRow = _corners[0],
startRenderableColumn = _corners[1],
endRenderableRow = _corners[2],
endRenderableColumn = _corners[3];
var visualCorners = [_this2.instance.rowIndexMapper.getVisualFromRenderableIndex(startRenderableRow), _this2.instance.columnIndexMapper.getVisualFromRenderableIndex(startRenderableColumn), _this2.instance.rowIndexMapper.getVisualFromRenderableIndex(endRenderableRow), _this2.instance.columnIndexMapper.getVisualFromRenderableIndex(endRenderableColumn)];
return _this2.instance.runHooks('beforeDrawBorders', visualCorners, borderClassName);
},
onBeforeTouchScroll: function onBeforeTouchScroll() {
return _this2.instance.runHooks('beforeTouchScroll');
},
onAfterMomentumScroll: function onAfterMomentumScroll() {
return _this2.instance.runHooks('afterMomentumScroll');
},
onBeforeStretchingColumnWidth: function onBeforeStretchingColumnWidth(stretchedWidth, renderedColumnIndex) {
var visualColumnIndex = _this2.instance.columnIndexMapper.getVisualFromRenderableIndex(renderedColumnIndex);
return _this2.instance.runHooks('beforeStretchingColumnWidth', stretchedWidth, visualColumnIndex);
},
onModifyRowHeaderWidth: function onModifyRowHeaderWidth(rowHeaderWidth) {
return _this2.instance.runHooks('modifyRowHeaderWidth', rowHeaderWidth);
},
onModifyGetCellCoords: function onModifyGetCellCoords(renderableRowIndex, renderableColumnIndex, topmost) {
var rowMapper = _this2.instance.rowIndexMapper;
var columnMapper = _this2.instance.columnIndexMapper; // Callback handle also headers. We shouldn't translate them.
var visualColumnIndex = renderableColumnIndex >= 0 ? columnMapper.getVisualFromRenderableIndex(renderableColumnIndex) : renderableColumnIndex;
var visualRowIndex = renderableRowIndex >= 0 ? rowMapper.getVisualFromRenderableIndex(renderableRowIndex) : renderableRowIndex;
var visualIndexes = _this2.instance.runHooks('modifyGetCellCoords', visualRowIndex, visualColumnIndex, topmost);
if (Array.isArray(visualIndexes)) {
var _visualIndexes = _slicedToArray(visualIndexes, 4),
visualRowFrom = _visualIndexes[0],
visualColumnFrom = _visualIndexes[1],
visualRowTo = _visualIndexes[2],
visualColumnTo = _visualIndexes[3]; // Result of the hook is handled by the Walkontable (renderable indexes).
return [visualRowFrom >= 0 ? rowMapper.getRenderableFromVisualIndex(rowMapper.getFirstNotHiddenIndex(visualRowFrom, 1)) : visualRowFrom, visualColumnFrom >= 0 ? columnMapper.getRenderableFromVisualIndex(columnMapper.getFirstNotHiddenIndex(visualColumnFrom, 1)) : visualColumnFrom, visualRowTo >= 0 ? rowMapper.getRenderableFromVisualIndex(rowMapper.getFirstNotHiddenIndex(visualRowTo, -1)) : visualRowTo, visualColumnTo >= 0 ? columnMapper.getRenderableFromVisualIndex(columnMapper.getFirstNotHiddenIndex(visualColumnTo, -1)) : visualColumnTo];
}
},
viewportRowCalculatorOverride: function viewportRowCalculatorOverride(calc) {
var viewportOffset = _this2.settings.viewportRowRenderingOffset;
if (viewportOffset === 'auto' && _this2.settings.fixedRowsTop) {
viewportOffset = 10;
}
if (viewportOffset > 0 || viewportOffset === 'auto') {
var rows = _this2.countRenderableRows();
if (typeof viewportOffset === 'number') {
calc.startRow = Math.max(calc.startRow - viewportOffset, 0);
calc.endRow = Math.min(calc.endRow + viewportOffset, rows - 1);
} else if (viewportOffset === 'auto') {
var center = calc.startRow + calc.endRow - calc.startRow;
var offset = Math.ceil(center / rows * 12);
calc.startRow = Math.max(calc.startRow - offset, 0);
calc.endRow = Math.min(calc.endRow + offset, rows - 1);
}
}
_this2.instance.runHooks('afterViewportRowCalculatorOverride', calc);
},
viewportColumnCalculatorOverride: function viewportColumnCalculatorOverride(calc) {
var viewportOffset = _this2.settings.viewportColumnRenderingOffset;
if (viewportOffset === 'auto' && _this2.settings.fixedColumnsLeft) {
viewportOffset = 10;
}
if (viewportOffset > 0 || viewportOffset === 'auto') {
var cols = _this2.countRenderableColumns();
if (typeof viewportOffset === 'number') {
calc.startColumn = Math.max(calc.startColumn - viewportOffset, 0);
calc.endColumn = Math.min(calc.endColumn + viewportOffset, cols - 1);
}
if (viewportOffset === 'auto') {
var center = calc.startColumn + calc.endColumn - calc.startColumn;
var offset = Math.ceil(center / cols * 12);
calc.startRow = Math.max(calc.startColumn - offset, 0);
calc.endColumn = Math.min(calc.endColumn + offset, cols - 1);
}
}
_this2.instance.runHooks('afterViewportColumnCalculatorOverride', calc);
},
rowHeaderWidth: function rowHeaderWidth() {
return _this2.settings.rowHeaderWidth;
},
columnHeaderHeight: function columnHeaderHeight() {
var columnHeaderHeight = _this2.instance.runHooks('modifyColumnHeaderHeight');
return _this2.settings.columnHeaderHeight || columnHeaderHeight;
}
};
this.instance.runHooks('beforeInitWalkontable', walkontableConfig);
this.wt = new Walkontable(walkontableConfig);
this.activeWt = this.wt;
var spreader = this.wt.wtTable.spreader; // We have to cache width and height after Walkontable initialization.
var _this$instance$rootEl = this.instance.rootElement.getBoundingClientRect(),
width = _this$instance$rootEl.width,
height = _this$instance$rootEl.height;
this.setLastSize(width, height);
this.eventManager.addEventListener(spreader, 'mousedown', function (event) {
// right mouse button exactly on spreader means right click on the right hand side of vertical scrollbar
if (event.target === spreader && event.which === 3) {
event.stopPropagation();
}
});
this.eventManager.addEventListener(spreader, 'contextmenu', function (event) {
// right mouse button exactly on spreader means right click on the right hand side of vertical scrollbar
if (event.target === spreader && event.which === 3) {
event.stopPropagation();
}
});
this.eventManager.addEventListener(this.instance.rootDocument.documentElement, 'click', function () {
if (_this2.settings.observeDOMVisibility) {
if (_this2.wt.drawInterrupted) {
_this2.instance.forceFullRender = true;
_this2.render();
}
}
});
}
/**
* Checks if it's possible to create text selection in element.
*
* @private
* @param {HTMLElement} el The element to check.
* @returns {boolean}
*/
}, {
key: "isTextSelectionAllowed",
value: function isTextSelectionAllowed(el) {
if (isInput(el)) {
return true;
}
var isChildOfTableBody = isChildOf(el, this.instance.view.wt.wtTable.spreader);
if (this.settings.fragmentSelection === true && isChildOfTableBody) {
return true;
}
if (this.settings.fragmentSelection === 'cell' && this.isSelectedOnlyCell() && isChildOfTableBody) {
return true;
}
if (!this.settings.fragmentSelection && this.isCellEdited() && this.isSelectedOnlyCell()) {
return true;
}
return false;
}
/**
* Checks if user's been called mousedown.
*
* @private
* @returns {boolean}
*/
}, {
key: "isMouseDown",
value: function isMouseDown() {
return privatePool.get(this).mouseDown;
}
/**
* Check if selected only one cell.
*
* @private
* @returns {boolean}
*/
}, {
key: "isSelectedOnlyCell",
value: function isSelectedOnlyCell() {
var _this$instance$getSel, _this$instance$getSel2;
return (_this$instance$getSel = (_this$instance$getSel2 = this.instance.getSelectedRangeLast()) === null || _this$instance$getSel2 === void 0 ? void 0 : _this$instance$getSel2.isSingle()) !== null && _this$instance$getSel !== void 0 ? _this$instance$getSel : false;
}
/**
* Checks if active cell is editing.
*
* @private
* @returns {boolean}
*/
}, {
key: "isCellEdited",
value: function isCellEdited() {
var activeEditor = this.instance.getActiveEditor();
return activeEditor && activeEditor.isOpened();
}
/**
* `beforeDraw` callback.
*
* @private
* @param {boolean} force If `true` rendering was triggered by a change of settings or data or `false` if
* rendering was triggered by scrolling or moving selection.
* @param {boolean} skipRender Indicates whether the rendering is skipped.
*/
}, {
key: "beforeRender",
value: function beforeRender(force, skipRender) {
if (force) {
// this.instance.forceFullRender = did Handsontable request full render?
this.instance.runHooks('beforeRender', this.instance.forceFullRender, skipRender);
}
}
/**
* `onDraw` callback.
*
* @private
* @param {boolean} force If `true` rendering was triggered by a change of settings or data or `false` if
* rendering was triggered by scrolling or moving selection.
*/
}, {
key: "onDraw",
value: function onDraw(force) {
if (force) {
// this.instance.forceFullRender = did Handsontable request full render?
this.instance.runHooks('afterRender', this.instance.forceFullRender);
}
}
/**
* Append row header to a TH element.
*
* @private
* @param {number} visualRowIndex The visual row index.
* @param {HTMLTableHeaderCellElement} TH The table header element.
*/
}, {
key: "appendRowHeader",
value: function appendRowHeader(visualRowIndex, TH) {
if (TH.firstChild) {
var container = TH.firstChild;
if (!hasClass(container, 'relative')) {
empty(TH);
this.appendRowHeader(visualRowIndex, TH);
return;
}
this.updateCellHeader(container.querySelector('.rowHeader'), visualRowIndex, this.instance.getRowHeader);
} else {
var _this$instance3 = this.instance,
rootDocument = _this$instance3.rootDocument,
getRowHeader = _this$instance3.getRowHeader;
var div = rootDocument.createElement('div');
var span = rootDocument.createElement('span');
div.className = 'relative';
span.className = 'rowHeader';
this.updateCellHeader(span, visualRowIndex, getRowHeader);
div.appendChild(span);
TH.appendChild(div);
}
this.instance.runHooks('afterGetRowHeader', visualRowIndex, TH);
}
/**
* Append column header to a TH element.
*
* @private
* @param {number} visualColumnIndex Visual column index.
* @param {HTMLTableHeaderCellElement} TH The table header element.
*/
}, {
key: "appendColHeader",
value: function appendColHeader(visualColumnIndex, TH) {
if (TH.firstChild) {
var container = TH.firstChild;
if (hasClass(container, 'relative')) {
this.updateCellHeader(container.querySelector('.colHeader'), visualColumnIndex, this.instance.getColHeader);
} else {
empty(TH);
this.appendColHeader(visualColumnIndex, TH);
}
} else {
var rootDocument = this.instance.rootDocument;
var div = rootDocument.createElement('div');
var span = rootDocument.createElement('span');
div.className = 'relative';
span.className = 'colHeader';
this.updateCellHeader(span, visualColumnIndex, this.instance.getColHeader);
div.appendChild(span);
TH.appendChild(div);
}
this.instance.runHooks('afterGetColHeader', visualColumnIndex, TH);
}
/**
* Updates header cell content.
*
* @since 0.15.0-beta4
* @param {HTMLElement} element Element to update.
* @param {number} index Row index or column index.
* @param {Function} content Function which should be returns content for this cell.
*/
}, {
key: "updateCellHeader",
value: function updateCellHeader(element, index, content) {
var renderedIndex = index;
var parentOverlay = this.wt.wtOverlays.getParentOverlay(element) || this.wt; // prevent wrong calculations from SampleGenerator
if (element.parentNode) {
if (hasClass(element, 'colHeader')) {
renderedIndex = parentOverlay.wtTable.columnFilter.sourceToRendered(index);
} else if (hasClass(element, 'rowHeader')) {
renderedIndex = parentOverlay.wtTable.rowFilter.sourceToRendered(index);
}
}
if (renderedIndex > -1) {
fastInnerHTML(element, content(index));
} else {
// workaround for https://github.com/handsontable/handsontable/issues/1946
fastInnerText(element, String.fromCharCode(160));
addClass(element, 'cornerHeader');
}
}
/**
* Given a element's left position relative to the viewport, returns maximum element width until the right
* edg