"use strict";

exports.__esModule = true;
require("core-js/modules/es.array.push.js");
require("core-js/modules/es.error.cause.js");
var _element = require("./helpers/dom/element");
var _eventManager = _interopRequireDefault(require("./eventManager"));
var _event = require("./helpers/dom/event");
var _src = _interopRequireDefault(require("./3rdparty/walkontable/src"));
var _mouseEventHandler = require("./selection/mouseEventHandler");
var _rootInstance = require("./utils/rootInstance");
var _a11y = require("./helpers/a11y");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
const privatePool = new WeakMap();

/**
 * @class TableView
 * @private
 */
var _columnHeadersCount = /*#__PURE__*/new WeakMap();
var _rowHeadersCount = /*#__PURE__*/new WeakMap();
class TableView {
  /**
   * @param {Hanstontable} instance Instance of {@link Handsontable}.
   */
  constructor(instance) {
    /**
     * Instance of {@link Handsontable}.
     *
     * @private
     * @type {Handsontable}
     */
    _defineProperty(this, "instance", void 0);
    /**
     * Instance of {@link EventManager}.
     *
     * @private
     * @type {EventManager}
     */
    _defineProperty(this, "eventManager", void 0);
    /**
     * Current Handsontable's GridSettings object.
     *
     * @private
     * @type {GridSettings}
     */
    _defineProperty(this, "settings", void 0);
    /**
     * Main <THEAD> element.
     *
     * @private
     * @type {HTMLTableSectionElement}
     */
    _defineProperty(this, "THEAD", void 0);
    /**
     * Main <TBODY> element.
     *
     * @private
     * @type {HTMLTableSectionElement}
     */
    _defineProperty(this, "TBODY", void 0);
    /**
     * Main Walkontable instance.
     *
     * @private
     * @type {Walkontable}
     */
    _defineProperty(this, "_wt", void 0);
    /**
     * Main Walkontable instance.
     *
     * @private
     * @type {Walkontable}
     */
    _defineProperty(this, "activeWt", void 0);
    /**
     * The total number of the column header renderers applied to the table through the
     * `afterGetColumnHeaderRenderers` hook.
     *
     * @type {number}
     */
    _classPrivateFieldInitSpec(this, _columnHeadersCount, {
      writable: true,
      value: 0
    });
    /**
     * The total number of the row header renderers applied to the table through the
     * `afterGetRowHeaderRenderers` hook.
     *
     * @type {number}
     */
    _classPrivateFieldInitSpec(this, _rowHeadersCount, {
      writable: true,
      value: 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}
     */
    _defineProperty(this, "postponedAdjustElementsSize", false);
    this.instance = instance;
    this.eventManager = new _eventManager.default(this.instance);
    this.settings = this.instance.getSettings();
    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.
   */
  render() {
    if (!this.instance.isRenderSuspended()) {
      this.instance.runHooks('beforeRender', this.instance.forceFullRender);
      if (this.postponedAdjustElementsSize) {
        this.postponedAdjustElementsSize = false;
        this.adjustElementsSize(true);
      }
      this._wt.draw(!this.instance.forceFullRender);
      this.instance.runHooks('afterRender', 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.
   */
  adjustElementsSize() {
    let 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}
   */
  getCellAtCoords(coords, topmost) {
    const 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}
   */
  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}
   */
  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}
   */
  scrollViewportVertically(row, snapToTop, snapToBottom) {
    return this._wt.scrollViewportVertically(row, snapToTop, snapToBottom);
  }

  /**
   * Prepares DOMElements and adds correct className to the root element.
   *
   * @private
   */
  createElements() {
    const priv = privatePool.get(this);
    const {
      rootElement,
      rootDocument
    } = this.instance;
    const 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
    }

    (0, _element.addClass)(rootElement, 'handsontable');
    priv.table = rootDocument.createElement('TABLE');
    (0, _element.addClass)(priv.table, 'htCore');
    if (this.instance.getSettings().tableClassName) {
      (0, _element.addClass)(priv.table, this.instance.getSettings().tableClassName);
    }
    if (this.settings.ariaTags) {
      (0, _element.setAttribute)(priv.table, [(0, _a11y.A11Y_PRESENTATION)()]);
      (0, _element.setAttribute)(rootElement, [(0, _a11y.A11Y_TREEGRID)(), (0, _a11y.A11Y_ROWCOUNT)(this.instance.countRows()), (0, _a11y.A11Y_COLCOUNT)(this.instance.countCols()), (0, _a11y.A11Y_MULTISELECTABLE)()]);
    }
    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
   */
  registerEvents() {
    const priv = privatePool.get(this);
    const {
      rootElement,
      rootDocument,
      selection
    } = this.instance;
    const documentElement = rootDocument.documentElement;
    this.eventManager.addEventListener(rootElement, 'mousedown', event => {
      priv.selectionMouseDown = true;
      if (!this.isTextSelectionAllowed(event.target)) {
        const {
          rootWindow
        } = this.instance;
        (0, _element.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', () => {
      priv.selectionMouseDown = false;
    });
    this.eventManager.addEventListener(rootElement, 'mousemove', event => {
      if (priv.selectionMouseDown && !this.isTextSelectionAllowed(event.target)) {
        // Clear selection only when fragmentSelection is enabled, otherwise clearing selection breaks the IME editor.
        if (this.settings.fragmentSelection) {
          (0, _element.clearTextSelection)(this.instance.rootWindow);
        }
        event.preventDefault();
      }
    });
    this.eventManager.addEventListener(documentElement, 'keyup', event => {
      // TODO: is it the best place and way to finish cell selection?
      if (selection.isInProgress() && !event.shiftKey) {
        selection.finish();
      }
    });
    this.eventManager.addEventListener(documentElement, 'mouseup', event => {
      if (selection.isInProgress() && (0, _event.isLeftClick)(event)) {
        selection.finish();
      }
      priv.mouseDown = false;
      const isOutsideInputElement = (0, _element.isOutsideInput)(rootDocument.activeElement);
      if (!isOutsideInputElement) {
        return;
      }
      if (isOutsideInputElement || !selection.isSelected() && !selection.isSelectedByAnyHeader() && !rootElement.contains(event.target) && !(0, _event.isRightClick)(event)) {
        this.instance.unlisten();
      }
    });
    this.eventManager.addEventListener(documentElement, 'contextmenu', event => {
      if (selection.isInProgress() && (0, _event.isRightClick)(event)) {
        selection.finish();
        priv.mouseDown = false;
      }
    });
    this.eventManager.addEventListener(documentElement, 'touchend', () => {
      if (selection.isInProgress()) {
        selection.finish();
      }
      priv.mouseDown = false;
    });
    this.eventManager.addEventListener(documentElement, 'mousedown', event => {
      const originalTarget = event.target;
      const eventX = event.x || event.clientX;
      const eventY = event.y || event.clientY;
      let 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
      const {
        holder
      } = this.instance.view._wt.wtTable;
      if (next === holder) {
        const scrollbarWidth = (0, _element.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!
      const 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', event => {
      if (this.settings.fragmentSelection || (0, _element.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}
   */
  translateFromRenderableToVisualCoords(_ref) {
    let {
      row,
      col
    } = _ref;
    // TODO: To consider an idea to reusing the CellCoords instance instead creating new one.
    return this.instance._createCellCoords(...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[]}
   */
  translateFromRenderableToVisualIndex(renderableRow, renderableColumn) {
    // TODO: Some helper may be needed.
    // We perform translation for indexes (without headers).
    let visualRow = renderableRow >= 0 ? this.instance.rowIndexMapper.getVisualFromRenderableIndex(renderableRow) : renderableRow;
    let 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|*}
   */
  countRenderableIndexes(indexMapper, maxElements) {
    const consideredElements = Math.min(indexMapper.getNotTrimmedIndexesLength(), maxElements);
    // Don't take hidden indexes into account. We are looking just for renderable indexes.
    const firstNotHiddenIndex = indexMapper.getNearestNotHiddenIndex(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}
   */
  countRenderableColumns() {
    return this.countRenderableIndexes(this.instance.columnIndexMapper, this.settings.maxCols);
  }

  /**
   * Returns the number of renderable rows.
   *
   * @returns {number}
   */
  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}
   */
  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}
   */
  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}
   */
  countNotHiddenIndexes(visualIndex, incrementBy, indexMapper, renderableIndexesCount) {
    if (isNaN(visualIndex) || visualIndex < 0) {
      return 0;
    }
    const firstVisibleIndex = indexMapper.getNearestNotHiddenIndex(visualIndex, incrementBy);
    const renderableIndex = indexMapper.getRenderableFromVisualIndex(firstVisibleIndex);
    if (!Number.isInteger(renderableIndex)) {
      return 0;
    }
    let 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;
  }

  /**
   * The function returns the number of not hidden column indexes that fit between the first and
   * last fixed column in the left (or right in RTL mode) overlay.
   *
   * @returns {number}
   */
  countNotHiddenFixedColumnsStart() {
    const countCols = this.instance.countCols();
    const visualFixedColumnsStart = Math.min(parseInt(this.settings.fixedColumnsStart, 10), countCols) - 1;
    return this.countNotHiddenColumnIndexes(visualFixedColumnsStart, -1);
  }

  /**
   * The function returns the number of not hidden row indexes that fit between the first and
   * last fixed row in the top overlay.
   *
   * @returns {number}
   */
  countNotHiddenFixedRowsTop() {
    const countRows = this.instance.countRows();
    const visualFixedRowsTop = Math.min(parseInt(this.settings.fixedRowsTop, 10), countRows) - 1;
    return this.countNotHiddenRowIndexes(visualFixedRowsTop, -1);
  }

  /**
   * The function returns the number of not hidden row indexes that fit between the first and
   * last fixed row in the bottom overlay.
   *
   * @returns {number}
   */
  countNotHiddenFixedRowsBottom() {
    const countRows = this.instance.countRows();
    const visualFixedRowsBottom = Math.max(countRows - parseInt(this.settings.fixedRowsBottom, 10), 0);
    return this.countNotHiddenRowIndexes(visualFixedRowsBottom, 1);
  }

  /**
   * The function returns the number of renderable column indexes within the passed range of the visual indexes.
   *
   * @param {number} columnStart The column visual start index.
   * @param {number} columnEnd The column visual end index.
   * @returns {number}
   */
  countRenderableColumnsInRange(columnStart, columnEnd) {
    let count = 0;
    for (let column = columnStart; column <= columnEnd; column++) {
      if (this.instance.columnIndexMapper.getRenderableFromVisualIndex(column) !== null) {
        count += 1;
      }
    }
    return count;
  }

  /**
   * The function returns the number of renderable row indexes within the passed range of the visual indexes.
   *
   * @param {number} rowStart The row visual start index.
   * @param {number} rowEnd The row visual end index.
   * @returns {number}
   */
  countRenderableRowsInRange(rowStart, rowEnd) {
    let count = 0;
    for (let row = rowStart; row <= rowEnd; row++) {
      if (this.instance.rowIndexMapper.getRenderableFromVisualIndex(row) !== null) {
        count += 1;
      }
    }
    return count;
  }

  /**
   * Checks if at least one cell than belongs to the main table is not covered by the top, left or
   * bottom overlay.
   *
   * @returns {boolean}
   */
  isMainTableNotFullyCoveredByOverlays() {
    const fixedAllRows = this.countNotHiddenFixedRowsTop() + this.countNotHiddenFixedRowsBottom();
    const fixedAllColumns = this.countNotHiddenFixedColumnsStart();
    return this.instance.countRenderedRows() > fixedAllRows && this.instance.countRenderedCols() > fixedAllColumns;
  }

  /**
   * Defines default configuration and initializes WalkOnTable instance.
   *
   * @private
   */
  initializeWalkontable() {
    const priv = privatePool.get(this);
    const walkontableConfig = {
      ariaTags: this.settings.ariaTags,
      rtlMode: this.instance.isRtl(),
      externalRowCalculator: this.instance.getPlugin('autoRowSize') && this.instance.getPlugin('autoRowSize').isEnabled(),
      table: priv.table,
      isDataViewInstance: () => (0, _rootInstance.isRootInstance)(this.instance),
      preventOverflow: () => this.settings.preventOverflow,
      preventWheel: () => this.settings.preventWheel,
      stretchH: () => this.settings.stretchH,
      data: (renderableRow, renderableColumn) => {
        return this.instance.getDataAtCell(...this.translateFromRenderableToVisualIndex(renderableRow, renderableColumn));
      },
      totalRows: () => this.countRenderableRows(),
      totalColumns: () => this.countRenderableColumns(),
      // Number of renderable columns for the left overlay.
      fixedColumnsStart: () => this.countNotHiddenFixedColumnsStart(),
      // Number of renderable rows for the top overlay.
      fixedRowsTop: () => this.countNotHiddenFixedRowsTop(),
      // Number of renderable rows for the bottom overlay.
      fixedRowsBottom: () => this.countNotHiddenFixedRowsBottom(),
      // Enable the inline start overlay when conditions are met.
      shouldRenderInlineStartOverlay: () => {
        return this.settings.fixedColumnsStart > 0 || walkontableConfig.rowHeaders().length > 0;
      },
      // Enable the top overlay when conditions are met.
      shouldRenderTopOverlay: () => {
        return this.settings.fixedRowsTop > 0 || walkontableConfig.columnHeaders().length > 0;
      },
      // Enable the bottom overlay when conditions are met.
      shouldRenderBottomOverlay: () => {
        return this.settings.fixedRowsBottom > 0;
      },
      minSpareRows: () => this.settings.minSpareRows,
      renderAllRows: this.settings.renderAllRows,
      rowHeaders: () => {
        const headerRenderers = [];
        if (this.instance.hasRowHeaders()) {
          headerRenderers.push((renderableRowIndex, TH) => {
            // TODO: Some helper may be needed.
            // We perform translation for row indexes (without row headers).
            const visualRowIndex = renderableRowIndex >= 0 ? this.instance.rowIndexMapper.getVisualFromRenderableIndex(renderableRowIndex) : renderableRowIndex;
            this.appendRowHeader(visualRowIndex, TH);
          });
        }
        this.instance.runHooks('afterGetRowHeaderRenderers', headerRenderers);
        _classPrivateFieldSet(this, _rowHeadersCount, headerRenderers.length);
        return headerRenderers;
      },
      columnHeaders: () => {
        const headerRenderers = [];
        if (this.instance.hasColHeaders()) {
          headerRenderers.push((renderedColumnIndex, TH) => {
            // TODO: Some helper may be needed.
            // We perform translation for columns indexes (without column headers).
            const visualColumnsIndex = renderedColumnIndex >= 0 ? this.instance.columnIndexMapper.getVisualFromRenderableIndex(renderedColumnIndex) : renderedColumnIndex;
            this.appendColHeader(visualColumnsIndex, TH);
          });
        }
        this.instance.runHooks('afterGetColumnHeaderRenderers', headerRenderers);
        _classPrivateFieldSet(this, _columnHeadersCount, headerRenderers.length);
        return headerRenderers;
      },
      columnWidth: renderedColumnIndex => {
        const visualIndex = this.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 indexes that are not displayed (indexes that are beyond the grid's boundaries), i.e. when `fixedColumnsStart` > `startCols` (wrong config?) or
        // scrolling and dataset is empty (scroll should handle that?).
        return this.instance.getColWidth(visualIndex === null ? renderedColumnIndex : visualIndex);
      },
      rowHeight: renderedRowIndex => {
        const visualIndex = this.instance.rowIndexMapper.getVisualFromRenderableIndex(renderedRowIndex);
        return this.instance.getRowHeight(visualIndex === null ? renderedRowIndex : visualIndex);
      },
      cellRenderer: (renderedRowIndex, renderedColumnIndex, TD) => {
        const [visualRowIndex, visualColumnIndex] = this.translateFromRenderableToVisualIndex(renderedRowIndex, renderedColumnIndex);

        // Coords may be modified. For example, by the `MergeCells` plugin. It should affect cell value and cell meta.
        const modifiedCellCoords = this.instance.runHooks('modifyGetCellCoords', visualRowIndex, visualColumnIndex);
        let visualRowToCheck = visualRowIndex;
        let visualColumnToCheck = visualColumnIndex;
        if (Array.isArray(modifiedCellCoords)) {
          [visualRowToCheck, visualColumnToCheck] = modifiedCellCoords;
        }
        const cellProperties = this.instance.getCellMeta(visualRowToCheck, visualColumnToCheck);
        const prop = this.instance.colToProp(visualColumnToCheck);
        let value = this.instance.getDataAtRowProp(visualRowToCheck, prop);
        if (this.instance.hasHook('beforeValueRender')) {
          value = this.instance.runHooks('beforeValueRender', value, cellProperties);
        }
        this.instance.runHooks('beforeRenderer', TD, visualRowIndex, visualColumnIndex, prop, value, cellProperties);
        this.instance.getCellRenderer(cellProperties)(this.instance, TD, visualRowIndex, visualColumnIndex, prop, value, cellProperties);
        this.instance.runHooks('afterRenderer', TD, visualRowIndex, visualColumnIndex, prop, value, cellProperties);
      },
      selections: this.instance.selection.highlight,
      hideBorderOnMouseDownOver: () => this.settings.fragmentSelection,
      onWindowResize: () => {
        if (this.instance && !this.instance.isDestroyed) {
          this.instance.refreshDimensions();
        }
      },
      onContainerElementResize: () => {
        if (this.instance && !this.instance.isDestroyed && (0, _element.isVisible)(this.instance.rootElement)) {
          this.instance.refreshDimensions();
        }
      },
      onCellMouseDown: (event, coords, TD, wt) => {
        const visualCoords = this.translateFromRenderableToVisualCoords(coords);
        const controller = {
          row: false,
          column: false,
          cell: false
        };
        this.instance.listen();
        this.activeWt = wt;
        priv.mouseDown = true;
        this.instance.runHooks('beforeOnCellMouseDown', event, visualCoords, TD, controller);
        if ((0, _event.isImmediatePropagationStopped)(event)) {
          return;
        }
        (0, _mouseEventHandler.handleMouseEvent)(event, {
          coords: visualCoords,
          selection: this.instance.selection,
          controller,
          cellCoordsFactory: (row, column) => this.instance._createCellCoords(row, column)
        });
        this.instance.runHooks('afterOnCellMouseDown', event, visualCoords, TD);
        this.activeWt = this._wt;
      },
      onCellContextMenu: (event, coords, TD, wt) => {
        const visualCoords = this.translateFromRenderableToVisualCoords(coords);
        this.activeWt = wt;
        priv.mouseDown = false;
        if (this.instance.selection.isInProgress()) {
          this.instance.selection.finish();
        }
        this.instance.runHooks('beforeOnCellContextMenu', event, visualCoords, TD);
        if ((0, _event.isImmediatePropagationStopped)(event)) {
          return;
        }
        this.instance.runHooks('afterOnCellContextMenu', event, visualCoords, TD);
        this.activeWt = this._wt;
      },
      onCellMouseOut: (event, coords, TD, wt) => {
        const visualCoords = this.translateFromRenderableToVisualCoords(coords);
        this.activeWt = wt;
        this.instance.runHooks('beforeOnCellMouseOut', event, visualCoords, TD);
        if ((0, _event.isImmediatePropagationStopped)(event)) {
          return;
        }
        this.instance.runHooks('afterOnCellMouseOut', event, visualCoords, TD);
        this.activeWt = this._wt;
      },
      onCellMouseOver: (event, coords, TD, wt) => {
        const visualCoords = this.translateFromRenderableToVisualCoords(coords);
        const controller = {
          row: false,
          column: false,
          cell: false
        };
        this.activeWt = wt;
        this.instance.runHooks('beforeOnCellMouseOver', event, visualCoords, TD, controller);
        if ((0, _event.isImmediatePropagationStopped)(event)) {
          return;
        }
        if (priv.mouseDown) {
          (0, _mouseEventHandler.handleMouseEvent)(event, {
            coords: visualCoords,
            selection: this.instance.selection,
            controller,
            cellCoordsFactory: (row, column) => this.instance._createCellCoords(row, column)
          });
        }
        this.instance.runHooks('afterOnCellMouseOver', event, visualCoords, TD);
        this.activeWt = this._wt;
      },
      onCellMouseUp: (event, coords, TD, wt) => {
        const visualCoords = this.translateFromRenderableToVisualCoords(coords);
        this.activeWt = wt;
        this.instance.runHooks('beforeOnCellMouseUp', event, visualCoords, TD);

        // TODO: The second condition check is a 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 ((0, _event.isImmediatePropagationStopped)(event) || this.instance.isDestroyed) {
          return;
        }
        this.instance.runHooks('afterOnCellMouseUp', event, visualCoords, TD);
        this.activeWt = this._wt;
      },
      onCellCornerMouseDown: event => {
        event.preventDefault();
        this.instance.runHooks('afterOnCellCornerMouseDown', event);
      },
      onCellCornerDblClick: event => {
        event.preventDefault();
        this.instance.runHooks('afterOnCellCornerDblClick', event);
      },
      beforeDraw: (force, skipRender) => this.beforeRender(force, skipRender),
      onDraw: force => this.afterRender(force),
      onBeforeViewportScrollVertically: renderableRow => {
        const rowMapper = this.instance.rowIndexMapper;
        const areColumnHeadersSelected = renderableRow < 0;
        let visualRow = renderableRow;
        if (!areColumnHeadersSelected) {
          visualRow = rowMapper.getVisualFromRenderableIndex(renderableRow);

          // for an empty data return index as is
          if (visualRow === null) {
            return renderableRow;
          }
        }
        visualRow = this.instance.runHooks('beforeViewportScrollVertically', visualRow);
        this.instance.runHooks('beforeViewportScroll');
        if (!areColumnHeadersSelected) {
          return rowMapper.getRenderableFromVisualIndex(visualRow);
        }
        return visualRow;
      },
      onBeforeViewportScrollHorizontally: renderableColumn => {
        const columnMapper = this.instance.columnIndexMapper;
        const areRowHeadersSelected = renderableColumn < 0;
        let visualColumn = renderableColumn;
        if (!areRowHeadersSelected) {
          visualColumn = columnMapper.getVisualFromRenderableIndex(renderableColumn);

          // for an empty data return index as is
          if (visualColumn === null) {
            return renderableColumn;
          }
        }
        visualColumn = this.instance.runHooks('beforeViewportScrollHorizontally', visualColumn);
        this.instance.runHooks('beforeViewportScroll');
        if (!areRowHeadersSelected) {
          return columnMapper.getRenderableFromVisualIndex(visualColumn);
        }
        return visualColumn;
      },
      onScrollVertically: () => {
        this.instance.runHooks('afterScrollVertically');
        this.instance.runHooks('afterScroll');
      },
      onScrollHorizontally: () => {
        this.instance.runHooks('afterScrollHorizontally');
        this.instance.runHooks('afterScroll');
      },
      onBeforeRemoveCellClassNames: () => this.instance.runHooks('beforeRemoveCellClassNames'),
      onBeforeHighlightingRowHeader: (renderableRow, headerLevel, highlightMeta) => {
        const rowMapper = this.instance.rowIndexMapper;
        const areColumnHeadersSelected = renderableRow < 0;
        let visualRow = renderableRow;
        if (!areColumnHeadersSelected) {
          visualRow = rowMapper.getVisualFromRenderableIndex(renderableRow);
        }
        const newVisualRow = this.instance.runHooks('beforeHighlightingRowHeader', visualRow, headerLevel, highlightMeta);
        if (!areColumnHeadersSelected) {
          return rowMapper.getRenderableFromVisualIndex(rowMapper.getNearestNotHiddenIndex(newVisualRow, 1));
        }
        return newVisualRow;
      },
      onBeforeHighlightingColumnHeader: (renderableColumn, headerLevel, highlightMeta) => {
        const columnMapper = this.instance.columnIndexMapper;
        const areRowHeadersSelected = renderableColumn < 0;
        let visualColumn = renderableColumn;
        if (!areRowHeadersSelected) {
          visualColumn = columnMapper.getVisualFromRenderableIndex(renderableColumn);
        }
        const newVisualColumn = this.instance.runHooks('beforeHighlightingColumnHeader', visualColumn, headerLevel, highlightMeta);
        if (!areRowHeadersSelected) {
          return columnMapper.getRenderableFromVisualIndex(columnMapper.getNearestNotHiddenIndex(newVisualColumn, 1));
        }
        return newVisualColumn;
      },
      onAfterDrawSelection: (currentRow, currentColumn, layerLevel) => {
        let cornersOfSelection;
        const [visualRowIndex, visualColumnIndex] = this.translateFromRenderableToVisualIndex(currentRow, currentColumn);
        const selectedRange = this.instance.selection.getSelectedRange();
        const selectionRangeSize = selectedRange.size();
        if (selectionRangeSize > 0) {
          const selectionForLayer = selectedRange.peekByIndex(layerLevel !== null && layerLevel !== void 0 ? layerLevel : 0);
          cornersOfSelection = [selectionForLayer.from.row, selectionForLayer.from.col, selectionForLayer.to.row, selectionForLayer.to.col];
        }
        return this.instance.runHooks('afterDrawSelection', visualRowIndex, visualColumnIndex, cornersOfSelection, layerLevel);
      },
      onBeforeDrawBorders: (corners, borderClassName) => {
        const [startRenderableRow, startRenderableColumn, endRenderableRow, endRenderableColumn] = corners;
        const visualCorners = [this.instance.rowIndexMapper.getVisualFromRenderableIndex(startRenderableRow), this.instance.columnIndexMapper.getVisualFromRenderableIndex(startRenderableColumn), this.instance.rowIndexMapper.getVisualFromRenderableIndex(endRenderableRow), this.instance.columnIndexMapper.getVisualFromRenderableIndex(endRenderableColumn)];
        return this.instance.runHooks('beforeDrawBorders', visualCorners, borderClassName);
      },
      onBeforeTouchScroll: () => this.instance.runHooks('beforeTouchScroll'),
      onAfterMomentumScroll: () => this.instance.runHooks('afterMomentumScroll'),
      onBeforeStretchingColumnWidth: (stretchedWidth, renderedColumnIndex) => {
        const visualColumnIndex = this.instance.columnIndexMapper.getVisualFromRenderableIndex(renderedColumnIndex);
        return this.instance.runHooks('beforeStretchingColumnWidth', stretchedWidth, visualColumnIndex);
      },
      onModifyRowHeaderWidth: rowHeaderWidth => this.instance.runHooks('modifyRowHeaderWidth', rowHeaderWidth),
      onModifyGetCellCoords: (renderableRowIndex, renderableColumnIndex, topmost) => {
        const rowMapper = this.instance.rowIndexMapper;
        const columnMapper = this.instance.columnIndexMapper;

        // Callback handle also headers. We shouldn't translate them.
        const visualColumnIndex = renderableColumnIndex >= 0 ? columnMapper.getVisualFromRenderableIndex(renderableColumnIndex) : renderableColumnIndex;
        const visualRowIndex = renderableRowIndex >= 0 ? rowMapper.getVisualFromRenderableIndex(renderableRowIndex) : renderableRowIndex;
        const visualIndexes = this.instance.runHooks('modifyGetCellCoords', visualRowIndex, visualColumnIndex, topmost);
        if (Array.isArray(visualIndexes)) {
          const [visualRowFrom, visualColumnFrom, visualRowTo, visualColumnTo] = visualIndexes;

          // Result of the hook is handled by the Walkontable (renderable indexes).
          return [visualRowFrom >= 0 ? rowMapper.getRenderableFromVisualIndex(rowMapper.getNearestNotHiddenIndex(visualRowFrom, 1)) : visualRowFrom, visualColumnFrom >= 0 ? columnMapper.getRenderableFromVisualIndex(columnMapper.getNearestNotHiddenIndex(visualColumnFrom, 1)) : visualColumnFrom, visualRowTo >= 0 ? rowMapper.getRenderableFromVisualIndex(rowMapper.getNearestNotHiddenIndex(visualRowTo, -1)) : visualRowTo, visualColumnTo >= 0 ? columnMapper.getRenderableFromVisualIndex(columnMapper.getNearestNotHiddenIndex(visualColumnTo, -1)) : visualColumnTo];
        }
      },
      viewportRowCalculatorOverride: calc => {
        let viewportOffset = this.settings.viewportRowRenderingOffset;
        if (viewportOffset === 'auto' && this.settings.fixedRowsTop) {
          viewportOffset = 10;
        }
        if (viewportOffset > 0 || viewportOffset === 'auto') {
          const renderableRows = this.countRenderableRows();
          const firstRenderedRow = calc.startRow;
          const lastRenderedRow = calc.endRow;
          if (typeof viewportOffset === 'number') {
            calc.startRow = Math.max(firstRenderedRow - viewportOffset, 0);
            calc.endRow = Math.min(lastRenderedRow + viewportOffset, renderableRows - 1);
          } else if (viewportOffset === 'auto') {
            const offset = Math.ceil(lastRenderedRow / renderableRows * 12);
            calc.startRow = Math.max(firstRenderedRow - offset, 0);
            calc.endRow = Math.min(lastRenderedRow + offset, renderableRows - 1);
          }
        }
        this.instance.runHooks('afterViewportRowCalculatorOverride', calc);
      },
      viewportColumnCalculatorOverride: calc => {
        let viewportOffset = this.settings.viewportColumnRenderingOffset;
        if (viewportOffset === 'auto' && this.settings.fixedColumnsStart) {
          viewportOffset = 10;
        }
        if (viewportOffset > 0 || viewportOffset === 'auto') {
          const renderableColumns = this.countRenderableColumns();
          const firstRenderedColumn = calc.startColumn;
          const lastRenderedColumn = calc.endColumn;
          if (typeof viewportOffset === 'number') {
            calc.startColumn = Math.max(firstRenderedColumn - viewportOffset, 0);
            calc.endColumn = Math.min(lastRenderedColumn + viewportOffset, renderableColumns - 1);
          }
          if (viewportOffset === 'auto') {
            const offset = Math.ceil(lastRenderedColumn / renderableColumns * 6);
            calc.startColumn = Math.max(firstRenderedColumn - offset, 0);
            calc.endColumn = Math.min(lastRenderedColumn + offset, renderableColumns - 1);
          }
        }
        this.instance.runHooks('afterViewportColumnCalculatorOverride', calc);
      },
      rowHeaderWidth: () => this.settings.rowHeaderWidth,
      columnHeaderHeight: () => {
        const columnHeaderHeight = this.instance.runHooks('modifyColumnHeaderHeight');
        return this.settings.columnHeaderHeight || columnHeaderHeight;
      }
    };
    this.instance.runHooks('beforeInitWalkontable', walkontableConfig);
    this._wt = new _src.default(walkontableConfig);
    this.activeWt = this._wt;
    const spreader = this._wt.wtTable.spreader;
    // We have to cache width and height after Walkontable initialization.
    const {
      width,
      height
    } = this.instance.rootElement.getBoundingClientRect();
    this.setLastSize(width, height);
    this.eventManager.addEventListener(spreader, 'mousedown', 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', 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', () => {
      if (this.settings.observeDOMVisibility) {
        if (this._wt.drawInterrupted) {
          this.instance.forceFullRender = true;
          this.render();
        }
      }
    });
  }

  /**
   * Checks if it's possible to create text selection in element.
   *
   * @private
   * @param {HTMLElement} el The element to check.
   * @returns {boolean}
   */
  isTextSelectionAllowed(el) {
    if ((0, _element.isInput)(el)) {
      return true;
    }
    const isChildOfTableBody = (0, _element.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}
   */
  isMouseDown() {
    return privatePool.get(this).mouseDown;
  }

  /**
   * Check if selected only one cell.
   *
   * @private
   * @returns {boolean}
   */
  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.isSingleCell()) !== null && _this$instance$getSel !== void 0 ? _this$instance$getSel : false;
  }

  /**
   * Checks if active cell is editing.
   *
   * @private
   * @returns {boolean}
   */
  isCellEdited() {
    const 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 {object} skipRender Object with `skipRender` property, if it is set to `true ` the next rendering
   *                            cycle will be skipped.
   */
  beforeRender(force, skipRender) {
    if (force) {
      // this.instance.forceFullRender = did Handsontable request full render?
      this.instance.runHooks('beforeViewRender', this.instance.forceFullRender, skipRender);
    }
  }

  /**
   * `afterRender` 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.
   */
  afterRender(force) {
    if (force) {
      // this.instance.forceFullRender = did Handsontable request full render?
      this.instance.runHooks('afterViewRender', 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.
   */
  appendRowHeader(visualRowIndex, TH) {
    if (TH.firstChild) {
      const container = TH.firstChild;
      if (!(0, _element.hasClass)(container, 'relative')) {
        (0, _element.empty)(TH);
        this.appendRowHeader(visualRowIndex, TH);
        return;
      }
      this.updateCellHeader(container.querySelector('.rowHeader'), visualRowIndex, this.instance.getRowHeader);
    } else {
      const {
        rootDocument,
        getRowHeader
      } = this.instance;
      const div = rootDocument.createElement('div');
      const 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 {HTMLTableCellElement} TH The table header element.
   * @param {Function} [label] The function that returns the header label.
   * @param {number} [headerLevel=0] The index of header level counting from the top (positive
   *                                 values counting from 0 to N).
   */
  appendColHeader(visualColumnIndex, TH) {
    let label = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.instance.getColHeader;
    let headerLevel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
    if (TH.firstChild) {
      const container = TH.firstChild;
      if ((0, _element.hasClass)(container, 'relative')) {
        this.updateCellHeader(container.querySelector('.colHeader'), visualColumnIndex, label, headerLevel);
      } else {
        (0, _element.empty)(TH);
        this.appendColHeader(visualColumnIndex, TH, label, headerLevel);
      }
    } else {
      const {
        rootDocument
      } = this.instance;
      const div = rootDocument.createElement('div');
      const span = rootDocument.createElement('span');
      div.className = 'relative';
      span.className = 'colHeader';
      this.updateCellHeader(span, visualColumnIndex, label, headerLevel);
      div.appendChild(span);
      TH.appendChild(div);
    }
    this.instance.runHooks('afterGetColHeader', visualColumnIndex, TH, headerLevel);
  }

  /**
   * Updates header cell content.
   *
   * @private
   * @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.
   * @param {number} [headerLevel=0] The index of header level counting from the top (positive
   *                                 values counting from 0 to N).
   */
  updateCellHeader(element, index, content) {
    let headerLevel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
    let renderedIndex = index;
    const parentOverlay = this._wt.wtOverlays.getParentOverlay(element) || this._wt;

    // prevent wrong calculations from SampleGenerator
    if (element.parentNode) {
      if ((0, _element.hasClass)(element, 'colHeader')) {
        renderedIndex = parentOverlay.wtTable.columnFilter.sourceToRendered(index);
      } else if ((0, _element.hasClass)(element, 'rowHeader')) {
        renderedIndex = parentOverlay.wtTable.rowFilter.sourceToRendered(index);
      }
    }
    if (renderedIndex > -1) {
      (0, _element.fastInnerHTML)(element, content(index, headerLevel));
    } else {
      // workaround for https://github.com/handsontable/handsontable/issues/1946
      (0, _element.fastInnerText)(element, String.fromCharCode(160));
      (0, _element.addClass)(element, 'cornerHeader');
    }
  }

  /**
   * Given a element's left (or right in RTL mode) position relative to the viewport, returns maximum
   * element width until the right (or left) edge of the viewport (before scrollbar).
   *
   * @private
   * @param {number} inlineOffset The left (or right in RTL mode) offset.
   * @returns {number}
   */
  maximumVisibleElementWidth(inlineOffset) {
    const workspaceWidth = this._wt.wtViewport.getWorkspaceWidth();
    const maxWidth = workspaceWidth - inlineOffset;
    return maxWidth > 0 ? maxWidth : 0;
  }

  /**
   * Given a element's top position relative to the viewport, returns maximum element height until the bottom
   * edge of the viewport (before scrollbar).
   *
   * @private
   * @param {number} topOffset The top offset.
   * @returns {number}
   */
  maximumVisibleElementHeight(topOffset) {
    const workspaceHeight = this._wt.wtViewport.getWorkspaceHeight();
    const maxHeight = workspaceHeight - topOffset;
    return maxHeight > 0 ? maxHeight : 0;
  }

  /**
   * Sets new dimensions of the container.
   *
   * @param {number} width The table width.
   * @param {number} height The table height.
   */
  setLastSize(width, height) {
    const priv = privatePool.get(this);
    [priv.lastWidth, priv.lastHeight] = [width, height];
  }

  /**
   * Returns cached dimensions.
   *
   * @returns {object}
   */
  getLastSize() {
    const priv = privatePool.get(this);
    return {
      width: priv.lastWidth,
      height: priv.lastHeight
    };
  }

  /**
   * Returns the first fully visible row in the table viewport.
   *
   * @returns {number}
   */
  getFirstFullyVisibleRow() {
    return this.instance.rowIndexMapper.getVisualFromRenderableIndex(this.instance.view._wt.wtScroll.getFirstVisibleRow());
  }

  /**
   * Returns the last fully visible row in the table viewport.
   *
   * @returns {number}
   */
  getLastFullyVisibleRow() {
    return this.instance.rowIndexMapper.getVisualFromRenderableIndex(this.instance.view._wt.wtScroll.getLastVisibleRow());
  }

  /**
   * Returns the first fully visible column in the table viewport.
   *
   * @returns {number}
   */
  getFirstFullyVisibleColumn() {
    return this.instance.columnIndexMapper.getVisualFromRenderableIndex(this.instance.view._wt.wtScroll.getFirstVisibleColumn());
  }

  /**
   * Returns the last fully visible column in the table viewport.
   *
   * @returns {number}
   */
  getLastFullyVisibleColumn() {
    return this.instance.columnIndexMapper.getVisualFromRenderableIndex(this.instance.view._wt.wtScroll.getLastVisibleColumn());
  }

  /**
   * Returns the total count of the rendered column headers.
   *
   * @returns {number}
   */
  getColumnHeadersCount() {
    return _classPrivateFieldGet(this, _columnHeadersCount);
  }

  /**
   * Returns the total count of the rendered row headers.
   *
   * @returns {number}
   */
  getRowHeadersCount() {
    return _classPrivateFieldGet(this, _rowHeadersCount);
  }

  /**
   * Destroys internal WalkOnTable's instance. Detaches all of the bonded listeners.
   *
   * @private
   */
  destroy() {
    this._wt.destroy();
    this.eventManager.destroy();
  }
}
var _default = exports.default = TableView;