Browse Source

test(handsotable): 优化测试

zhangweicheng 5 years atrás
parent
commit
d40a8664b8
1 changed files with 490 additions and 0 deletions
  1. 490 0
      handsontable/src/3rdparty/walkontable/src/viewport copy.js

+ 490 - 0
handsontable/src/3rdparty/walkontable/src/viewport copy.js

@@ -0,0 +1,490 @@
+import {
+  getScrollbarWidth,
+  getStyle,
+  offset,
+  outerHeight,
+  outerWidth,
+} from './../../../helpers/dom/element';
+import { objectEach } from './../../../helpers/object';
+import EventManager from './../../../eventManager';
+import ViewportColumnsCalculator from './calculator/viewportColumns';
+import ViewportRowsCalculator from './calculator/viewportRows';
+
+/**
+ * @class Viewport
+ */
+class Viewport {
+  /**
+   * @param wotInstance
+   */
+  constructor(wotInstance) {
+    this.wot = wotInstance;
+    // legacy support
+    this.instance = this.wot;
+
+    this.oversizedRows = [];
+    this.oversizedColumnHeaders = [];
+    this.hasOversizedColumnHeadersMarked = {};
+    this.clientHeight = 0;
+    this.containerWidth = NaN;
+    this.rowHeaderWidth = NaN;
+    this.rowsVisibleCalculator = null;
+    this.columnsVisibleCalculator = null;
+
+    this.eventManager = new EventManager(this.wot);
+    this.eventManager.addEventListener(window, 'resize', () => {
+      this.clientHeight = this.getWorkspaceHeight();
+    });
+    this.offset = offset(this.wot.wtTable.TABLE);
+  }
+
+  /**
+   * @returns {number}
+   */
+  getWorkspaceHeight() {
+    const trimmingContainer = this.instance.wtOverlays.topOverlay.trimmingContainer;
+    let elemHeight;
+    let height = 0;
+
+    if (trimmingContainer === window) {
+      height = document.documentElement.clientHeight;
+
+    } else {
+      elemHeight = outerHeight(trimmingContainer);
+      // returns height without DIV scrollbar
+      height = (elemHeight > 0 && trimmingContainer.clientHeight > 0) ? trimmingContainer.clientHeight : Infinity;
+    }
+
+    return height;
+  }
+
+  getWorkspaceWidth() {
+    let width;
+    const totalColumns = this.wot.getSetting('totalColumns');
+    const trimmingContainer = this.instance.wtOverlays.leftOverlay.trimmingContainer;
+    let overflow;
+    const stretchSetting = this.wot.getSetting('stretchH');
+    const docOffsetWidth = window.innerWidth; // document.documentElement.offsetWidth;
+    const preventOverflow = this.wot.getSetting('preventOverflow');
+
+    if (preventOverflow) {
+      return outerWidth(this.instance.wtTable.wtRootElement);
+    }
+
+    if (this.wot.getSetting('freezeOverlays')) {
+      width = Math.min(docOffsetWidth - this.getWorkspaceOffset().left, docOffsetWidth);
+    } else {
+      width = Math.min(this.getContainerFillWidth(), docOffsetWidth - this.getWorkspaceOffset().left, docOffsetWidth);
+    }
+
+    if (trimmingContainer === window && totalColumns > 0 && this.sumColumnWidths(0, totalColumns - 1) > width) {
+      // in case sum of column widths is higher than available stylesheet width, let's assume using the whole window
+      // otherwise continue below, which will allow stretching
+      // this is used in `scroll_window.html`
+      // TODO test me
+      return document.documentElement.clientWidth;
+    }
+
+    if (trimmingContainer !== window) {
+      overflow = 'hidden';// getStyle(this.instance.wtOverlays.leftOverlay.trimmingContainer, 'overflow');
+
+      if (overflow === 'scroll' || overflow === 'hidden' || overflow === 'auto') {
+        // this is used in `scroll.html`
+        // TODO test me
+        return Math.max(width, this.instance.wtOverlays.leftOverlay.trimmingContainerClientWidth);// Math.max(width, trimmingContainer.clientWidth);
+      }
+    }
+
+    if (stretchSetting === 'none' || !stretchSetting) {
+      // if no stretching is used, return the maximum used workspace width
+      return Math.max(width, outerWidth(this.instance.wtTable.TABLE));
+    }
+
+    // if stretching is used, return the actual container width, so the columns can fit inside it
+    return width;
+  }
+
+  /**
+   * Checks if viewport has vertical scroll
+   *
+   * @returns {Boolean}
+   */
+  hasVerticalScroll() {
+    return this.getWorkspaceActualHeight() > this.getWorkspaceHeight();
+  }
+
+  /**
+   * Checks if viewport has horizontal scroll
+   *
+   * @returns {Boolean}
+   */
+  hasHorizontalScroll() {
+    return this.getWorkspaceActualWidth() > this.getWorkspaceWidth();
+  }
+
+  /**
+   * @param from
+   * @param length
+   * @returns {Number}
+   */
+  sumColumnWidths(from, length) {
+    const wtTable = this.wot.wtTable;
+    let sum = 0;
+    let column = from;
+
+    while (column < length) {
+      sum += wtTable.getColumnWidth(column);
+      column += 1;
+    }
+
+    return sum;
+  }
+
+  /**
+   * @returns {Number}
+   */
+  getContainerFillWidth() {
+    if (this.containerWidth) {
+      return this.containerWidth;
+    }
+
+    const mainContainer = this.instance.wtTable.holder;
+    const dummyElement = document.createElement('div');
+
+    dummyElement.style.width = '100%';
+    dummyElement.style.height = '1px';
+    mainContainer.appendChild(dummyElement);
+
+    const fillWidth = dummyElement.offsetWidth;
+
+    this.containerWidth = fillWidth;
+    mainContainer.removeChild(dummyElement);
+
+    return fillWidth;
+  }
+
+  /**
+   * @returns {Number}
+   */
+  getWorkspaceOffset() {
+    return this.offset;// offset(this.wot.wtTable.TABLE);
+  }
+
+  /**
+   * @returns {Number}
+   */
+  getWorkspaceActualHeight() {
+    return outerHeight(this.wot.wtTable.TABLE);
+  }
+
+  /**
+   * @returns {Number}
+   */
+  getWorkspaceActualWidth() {
+    return outerWidth(this.wot.wtTable.TABLE) ||
+      outerWidth(this.wot.wtTable.TBODY) ||
+      outerWidth(this.wot.wtTable.THEAD); // IE8 reports 0 as <table> offsetWidth;
+  }
+
+  /**
+   * @returns {Number}
+   */
+  getColumnHeaderHeight() {
+    if (isNaN(this.columnHeaderHeight)) {
+      this.columnHeaderHeight = outerHeight(this.wot.wtTable.THEAD);
+    }
+
+    return this.columnHeaderHeight;
+  }
+
+  /**
+   * @returns {Number}
+   */
+  getViewportHeight() {
+    let containerHeight = this.getWorkspaceHeight();
+
+    if (containerHeight === Infinity) {
+      return containerHeight;
+    }
+
+    const columnHeaderHeight = this.getColumnHeaderHeight();
+
+    if (columnHeaderHeight > 0) {
+      containerHeight -= columnHeaderHeight;
+    }
+
+    return containerHeight;
+  }
+
+  /**
+   * @returns {Number}
+   */
+  getRowHeaderWidth() {
+    const rowHeadersHeightSetting = this.instance.getSetting('rowHeaderWidth');
+    const rowHeaders = this.instance.getSetting('rowHeaders');
+
+    if (rowHeadersHeightSetting) {
+      this.rowHeaderWidth = 0;
+
+      for (let i = 0, len = rowHeaders.length; i < len; i++) {
+        this.rowHeaderWidth += rowHeadersHeightSetting[i] || rowHeadersHeightSetting;
+      }
+    }
+
+    if (this.wot.cloneSource) {
+      return this.wot.cloneSource.wtViewport.getRowHeaderWidth();
+    }
+
+    if (isNaN(this.rowHeaderWidth)) {
+
+      if (rowHeaders.length) {
+        let TH = this.instance.wtTable.TABLE.querySelector('TH');
+        this.rowHeaderWidth = 0;
+
+        for (let i = 0, len = rowHeaders.length; i < len; i++) {
+          if (TH) {
+            this.rowHeaderWidth += outerWidth(TH);
+            TH = TH.nextSibling;
+
+          } else {
+            // yes this is a cheat but it worked like that before, just taking assumption from CSS instead of measuring.
+            // TODO: proper fix
+            this.rowHeaderWidth += 50;
+          }
+        }
+      } else {
+        this.rowHeaderWidth = 0;
+      }
+    }
+
+    this.rowHeaderWidth = this.instance.getSetting('onModifyRowHeaderWidth', this.rowHeaderWidth) || this.rowHeaderWidth;
+
+    return this.rowHeaderWidth;
+  }
+
+  /**
+   * @returns {Number}
+   */
+  getViewportWidth() {
+    const containerWidth = this.getWorkspaceWidth();
+
+    if (containerWidth === Infinity) {
+      return containerWidth;
+    }
+
+    const rowHeaderWidth = this.getRowHeaderWidth();
+
+    if (rowHeaderWidth > 0) {
+      return containerWidth - rowHeaderWidth;
+    }
+
+    return containerWidth;
+  }
+
+  /**
+   * Creates:
+   *  - rowsRenderCalculator (before draw, to qualify rows for rendering)
+   *  - rowsVisibleCalculator (after draw, to measure which rows are actually visible)
+   *
+   * @returns {ViewportRowsCalculator}
+   */
+  createRowsCalculator(visible = false) {
+    let height;
+    let scrollbarHeight;
+    let fixedRowsHeight;
+
+    this.rowHeaderWidth = NaN;
+
+    if (this.wot.wtSettings.settings.renderAllRows && !visible) {
+      height = Infinity;
+    } else {
+      height = this.getViewportHeight();
+    }
+
+    let pos = this.wot.wtOverlays.topOverlay.getScrollPosition() - this.wot.wtOverlays.topOverlay.getTableParentOffset();
+
+    if (pos < 0) {
+      pos = 0;
+    }
+
+    const fixedRowsTop = this.wot.getSetting('fixedRowsTop');
+    const fixedRowsBottom = this.wot.getSetting('fixedRowsBottom');
+    const totalRows = this.wot.getSetting('totalRows');
+
+    if (fixedRowsTop) {
+      fixedRowsHeight = this.wot.wtOverlays.topOverlay.sumCellSizes(0, fixedRowsTop);
+      pos += fixedRowsHeight;
+      height -= fixedRowsHeight;
+    }
+
+    if (fixedRowsBottom && this.wot.wtOverlays.bottomOverlay.clone) {
+      fixedRowsHeight = this.wot.wtOverlays.bottomOverlay.sumCellSizes(totalRows - fixedRowsBottom, totalRows);
+
+      height -= fixedRowsHeight;
+    }
+
+    if (this.wot.wtTable.holder.clientHeight === this.wot.wtTable.holder.offsetHeight) {
+      scrollbarHeight = 0;
+    } else {
+      scrollbarHeight = getScrollbarWidth();
+    }
+
+    return new ViewportRowsCalculator(
+      height,
+      pos,
+      this.wot.getSetting('totalRows'),
+      sourceRow => this.wot.wtTable.getRowHeight(sourceRow),
+      visible ? null : this.wot.wtSettings.settings.viewportRowCalculatorOverride,
+      visible,
+      scrollbarHeight
+    );
+  }
+
+  /**
+   * Creates:
+   *  - columnsRenderCalculator (before draw, to qualify columns for rendering)
+   *  - columnsVisibleCalculator (after draw, to measure which columns are actually visible)
+   *
+   * @returns {ViewportRowsCalculator}
+   */
+  createColumnsCalculator(visible = false) {
+    let width = this.getViewportWidth();
+    let pos = this.wot.wtOverlays.leftOverlay.getScrollPosition() - this.wot.wtOverlays.leftOverlay.getTableParentOffset();
+
+    this.columnHeaderHeight = NaN;
+
+    if (pos < 0) {
+      pos = 0;
+    }
+
+    const fixedColumnsLeft = this.wot.getSetting('fixedColumnsLeft');
+
+    if (fixedColumnsLeft) {
+      const fixedColumnsWidth = this.wot.wtOverlays.leftOverlay.sumCellSizes(0, fixedColumnsLeft);
+      pos += fixedColumnsWidth;
+      width -= fixedColumnsWidth;
+    }
+    if (this.wot.wtTable.holder.clientWidth !== this.wot.wtTable.holder.offsetWidth) {
+      width -= getScrollbarWidth();
+    }
+
+    return new ViewportColumnsCalculator(
+      width,
+      pos,
+      this.wot.getSetting('totalColumns'),
+      sourceCol => this.wot.wtTable.getColumnWidth(sourceCol),
+      visible ? null : this.wot.wtSettings.settings.viewportColumnCalculatorOverride,
+      visible,
+      this.wot.getSetting('stretchH'),
+      (stretchedWidth, column) => this.wot.getSetting('onBeforeStretchingColumnWidth', stretchedWidth, column)
+    );
+  }
+
+  /**
+   * Creates rowsRenderCalculator and columnsRenderCalculator (before draw, to determine what rows and
+   * cols should be rendered)
+   *
+   * @param fastDraw {Boolean} If `true`, will try to avoid full redraw and only update the border positions.
+   *                           If `false` or `undefined`, will perform a full redraw
+   * @returns fastDraw {Boolean} The fastDraw value, possibly modified
+   */
+  createRenderCalculators(fastDraw = false) {
+    let runFastDraw = fastDraw;
+
+    if (runFastDraw) {
+      const proposedRowsVisibleCalculator = this.createRowsCalculator(true);
+      const proposedColumnsVisibleCalculator = this.createColumnsCalculator(true);
+
+      if (!(this.areAllProposedVisibleRowsAlreadyRendered(proposedRowsVisibleCalculator) &&
+          this.areAllProposedVisibleColumnsAlreadyRendered(proposedColumnsVisibleCalculator))) {
+        runFastDraw = false;
+      }
+    }
+
+    if (!runFastDraw) {
+      this.rowsRenderCalculator = this.createRowsCalculator();
+      this.columnsRenderCalculator = this.createColumnsCalculator();
+    }
+    // delete temporarily to make sure that renderers always use rowsRenderCalculator, not rowsVisibleCalculator
+    this.rowsVisibleCalculator = null;
+    this.columnsVisibleCalculator = null;
+
+    return runFastDraw;
+  }
+
+  /**
+   * Creates rowsVisibleCalculator and columnsVisibleCalculator (after draw, to determine what are
+   * the actually visible rows and columns)
+   */
+  createVisibleCalculators() {
+    this.rowsVisibleCalculator = this.createRowsCalculator(true);
+    this.columnsVisibleCalculator = this.createColumnsCalculator(true);
+  }
+
+  /**
+   * Returns information whether proposedRowsVisibleCalculator viewport
+   * is contained inside rows rendered in previous draw (cached in rowsRenderCalculator)
+   *
+   * @param {Object} proposedRowsVisibleCalculator
+   * @returns {Boolean} Returns `true` if all proposed visible rows are already rendered (meaning: redraw is not needed).
+   *                    Returns `false` if at least one proposed visible row is not already rendered (meaning: redraw is needed)
+   */
+  areAllProposedVisibleRowsAlreadyRendered(proposedRowsVisibleCalculator) {
+    if (this.rowsVisibleCalculator) {
+      if (proposedRowsVisibleCalculator.startRow < this.rowsRenderCalculator.startRow ||
+          (proposedRowsVisibleCalculator.startRow === this.rowsRenderCalculator.startRow &&
+          proposedRowsVisibleCalculator.startRow > 0)) {
+        return false;
+
+      } else if (proposedRowsVisibleCalculator.endRow > this.rowsRenderCalculator.endRow ||
+          (proposedRowsVisibleCalculator.endRow === this.rowsRenderCalculator.endRow &&
+          proposedRowsVisibleCalculator.endRow < this.wot.getSetting('totalRows') - 1)) {
+        return false;
+
+      }
+      return true;
+
+    }
+
+    return false;
+  }
+
+  /**
+   * Returns information whether proposedColumnsVisibleCalculator viewport
+   * is contained inside column rendered in previous draw (cached in columnsRenderCalculator)
+   *
+   * @param {Object} proposedColumnsVisibleCalculator
+   * @returns {Boolean} Returns `true` if all proposed visible columns are already rendered (meaning: redraw is not needed).
+   *                    Returns `false` if at least one proposed visible column is not already rendered (meaning: redraw is needed)
+   */
+  areAllProposedVisibleColumnsAlreadyRendered(proposedColumnsVisibleCalculator) {
+    if (this.columnsVisibleCalculator) {
+      if (proposedColumnsVisibleCalculator.startColumn < this.columnsRenderCalculator.startColumn ||
+          (proposedColumnsVisibleCalculator.startColumn === this.columnsRenderCalculator.startColumn &&
+          proposedColumnsVisibleCalculator.startColumn > 0)) {
+        return false;
+
+      } else if (proposedColumnsVisibleCalculator.endColumn > this.columnsRenderCalculator.endColumn ||
+          (proposedColumnsVisibleCalculator.endColumn === this.columnsRenderCalculator.endColumn &&
+          proposedColumnsVisibleCalculator.endColumn < this.wot.getSetting('totalColumns') - 1)) {
+        return false;
+
+      }
+      return true;
+
+    }
+
+    return false;
+  }
+
+  /**
+   * Resets values in keys of the hasOversizedColumnHeadersMarked object after updateSettings.
+   */
+  resetHasOversizedColumnHeadersMarked() {
+    objectEach(this.hasOversizedColumnHeadersMarked, (value, key, object) => {
+      object[key] = void 0;
+    });
+  }
+}
+
+export default Viewport;