UNPKG

handsontable

Version:

Handsontable is a JavaScript Spreadsheet Component available for React, Angular and Vue.

1,235 lines (1,039 loc) • 52.2 kB
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