text: /**
   * Copyright (c) 2015, Facebook, Inc.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree. An additional grant
   * of patent rights can be found in the PATENTS file in the same directory.
   *
   * @providesModule FixedDataTableNew.react
   * @typechecks
   * @noflow
   */
  
  /*eslint no-bitwise:1*/
  
  'use strict';
  
  var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  
  var React = require('./React');
  var ReactComponentWithPureRenderMixin = require('./ReactComponentWithPureRenderMixin');
  var ReactWheelHandler = require('./ReactWheelHandler');
  var Scrollbar = require('./Scrollbar.react');
  var FixedDataTableBufferedRows = require('./FixedDataTableBufferedRows.react');
  var FixedDataTableColumnResizeHandle = require('./FixedDataTableColumnResizeHandle.react');
  var FixedDataTableRow = require('./FixedDataTableRow.react');
  var FixedDataTableScrollHelper = require('./FixedDataTableScrollHelper');
  var FixedDataTableWidthHelper = require('./FixedDataTableWidthHelper');
  
  var cx = require('./cx');
  var debounceCore = require('./debounceCore');
  var emptyFunction = require('./emptyFunction');
  var invariant = require('./invariant');
  var joinClasses = require('./joinClasses');
  var shallowEqual = require('./shallowEqual');
  var translateDOMPositionXY = require('./translateDOMPositionXY');
  
  var PropTypes = React.PropTypes;
  
  var ReactChildren = React.Children;
  
  var EMPTY_OBJECT = {};
  var BORDER_HEIGHT = 1;
  var HEADER = 'header';
  var FOOTER = 'footer';
  var CELL = 'cell';
  
  /**
   * Data grid component with fixed or scrollable header and columns.
   *
   * The layout of the data table is as follows:
   *
   * ```
   * +---------------------------------------------------+
   * | Fixed Column Group    | Scrollable Column Group   |
   * | Header                | Header                    |
   * |                       |                           |
   * +---------------------------------------------------+
   * |                       |                           |
   * | Fixed Header Columns  | Scrollable Header Columns |
   * |                       |                           |
   * +-----------------------+---------------------------+
   * |                       |                           |
   * | Fixed Body Columns    | Scrollable Body Columns   |
   * |                       |                           |
   * +-----------------------+---------------------------+
   * |                       |                           |
   * | Fixed Footer Columns  | Scrollable Footer Columns |
   * |                       |                           |
   * +-----------------------+---------------------------+
   * ```
   *
   * - Fixed Column Group Header: These are the headers for a group
   *   of columns if included in the table that do not scroll
   *   vertically or horizontally.
   *
   * - Scrollable Column Group Header: The header for a group of columns
   *   that do not move while scrolling vertically, but move horizontally
   *   with the horizontal scrolling.
   *
   * - Fixed Header Columns: The header columns that do not move while scrolling
   *   vertically or horizontally.
   *
   * - Scrollable Header Columns: The header columns that do not move
   *   while scrolling vertically, but move horizontally with the horizontal
   *   scrolling.
   *
   * - Fixed Body Columns: The body columns that do not move while scrolling
   *   horizontally, but move vertically with the vertical scrolling.
   *
   * - Scrollable Body Columns: The body columns that move while scrolling
   *   vertically or horizontally.
   */
  var FixedDataTable = React.createClass({
    displayName: 'FixedDataTable',
  
    propTypes: {
  
      /**
       * Pixel width of table. If all columns do not fit,
       * a horizontal scrollbar will appear.
       */
      width: PropTypes.number.isRequired,
  
      /**
       * Pixel height of table. If all rows do not fit,
       * a vertical scrollbar will appear.
       *
       * Either `height` or `maxHeight` must be specified.
       */
      height: PropTypes.number,
  
      /**
       * Maximum pixel height of table. If all rows do not fit,
       * a vertical scrollbar will appear.
       *
       * Either `height` or `maxHeight` must be specified.
       */
      maxHeight: PropTypes.number,
  
      /**
       * Pixel height of table's owner, this is used in a managed scrolling
       * situation when you want to slide the table up from below the fold
       * without having to constantly update the height on every scroll tick.
       * Instead, vary this property on scroll. By using `ownerHeight`, we
       * over-render the table while making sure the footer and horizontal
       * scrollbar of the table are visible when the current space for the table
       * in view is smaller than the final, over-flowing height of table. It
       * allows us to avoid resizing and reflowing table when it is moving in the
       * view.
       *
       * This is used if `ownerHeight < height` (or `maxHeight`).
       */
      ownerHeight: PropTypes.number,
  
      overflowX: PropTypes.oneOf(['hidden', 'auto']),
      overflowY: PropTypes.oneOf(['hidden', 'auto']),
  
      /**
       * Number of rows in the table.
       */
      rowsCount: PropTypes.number.isRequired,
  
      /**
       * Pixel height of rows unless `rowHeightGetter` is specified and returns
       * different value.
       */
      rowHeight: PropTypes.number.isRequired,
  
      /**
       * If specified, `rowHeightGetter(index)` is called for each row and the
       * returned value overrides `rowHeight` for particular row.
       */
      rowHeightGetter: PropTypes.func,
  
      /**
       * To get any additional CSS classes that should be added to a row,
       * `rowClassNameGetter(index)` is called.
       */
      rowClassNameGetter: PropTypes.func,
  
      /**
       * Pixel height of the column group header.
       */
      groupHeaderHeight: PropTypes.number,
  
      /**
       * Pixel height of header.
       */
      headerHeight: PropTypes.number.isRequired,
  
      /**
       * Pixel height of footer.
       */
      footerHeight: PropTypes.number,
  
      /**
       * Value of horizontal scroll.
       */
      scrollLeft: PropTypes.number,
  
      /**
       * Index of column to scroll to.
       */
      scrollToColumn: PropTypes.number,
  
      /**
       * Value of vertical scroll.
       */
      scrollTop: PropTypes.number,
  
      /**
       * Index of row to scroll to.
       */
      scrollToRow: PropTypes.number,
  
      /**
       * Callback that is called when scrolling starts with current horizontal
       * and vertical scroll values.
       */
      onScrollStart: PropTypes.func,
  
      /**
       * Callback that is called when scrolling ends or stops with new horizontal
       * and vertical scroll values.
       */
      onScrollEnd: PropTypes.func,
  
      /**
       * Callback that is called when `rowHeightGetter` returns a different height
       * for a row than the `rowHeight` prop. This is necessary because initially
       * table estimates heights of some parts of the content.
       */
      onContentHeightChange: PropTypes.func,
  
      /**
       * Callback that is called when a row is clicked.
       */
      onRowClick: PropTypes.func,
  
      /**
       * Callback that is called when a row is double clicked.
       */
      onRowDoubleClick: PropTypes.func,
  
      /**
       * Callback that is called when a mouse-down event happens on a row.
       */
      onRowMouseDown: PropTypes.func,
  
      /**
       * Callback that is called when a mouse-enter event happens on a row.
       */
      onRowMouseEnter: PropTypes.func,
  
      /**
       * Callback that is called when a mouse-leave event happens on a row.
       */
      onRowMouseLeave: PropTypes.func,
  
      /**
       * Callback that is called when resizer has been released
       * and column needs to be updated.
       *
       * Required if the isResizable property is true on any column.
       *
       * ```
       * function(
       *   newColumnWidth: number,
       *   columnKey: string,
       * )
       * ```
       */
      onColumnResizeEndCallback: PropTypes.func,
  
      /**
       * Whether a column is currently being resized.
       */
      isColumnResizing: PropTypes.bool
    },
  
    getDefaultProps: function getDefaultProps() /*object*/{
      return {
        footerHeight: 0,
        groupHeaderHeight: 0,
        headerHeight: 0,
        scrollLeft: 0,
        scrollTop: 0
      };
    },
  
    getInitialState: function getInitialState() /*object*/{
      var props = this.props;
      var viewportHeight = (props.height === undefined ? props.maxHeight : props.height) - (props.headerHeight || 0) - (props.footerHeight || 0) - (props.groupHeaderHeight || 0);
      this._scrollHelper = new FixedDataTableScrollHelper(props.rowsCount, props.rowHeight, viewportHeight, props.rowHeightGetter);
      if (props.scrollTop) {
        this._scrollHelper.scrollTo(props.scrollTop);
      }
      this._didScrollStop = debounceCore(this._didScrollStop, 200, this);
  
      return this._calculateState(this.props);
    },
  
    componentWillMount: function componentWillMount() {
      var scrollToRow = this.props.scrollToRow;
      if (scrollToRow !== undefined && scrollToRow !== null) {
        this._rowToScrollTo = scrollToRow;
      }
      var scrollToColumn = this.props.scrollToColumn;
      if (scrollToColumn !== undefined && scrollToColumn !== null) {
        this._columnToScrollTo = scrollToColumn;
      }
      this._wheelHandler = new ReactWheelHandler(this._onWheel, this._shouldHandleWheelX, this._shouldHandleWheelY);
    },
  
    _shouldHandleWheelX: function _shouldHandleWheelX( /*number*/delta) /*boolean*/{
      if (this.props.overflowX === 'hidden') {
        return false;
      }
  
      delta = Math.round(delta);
      if (delta === 0) {
        return false;
      }
  
      return delta < 0 && this.state.scrollX > 0 || delta >= 0 && this.state.scrollX < this.state.maxScrollX;
    },
  
    _shouldHandleWheelY: function _shouldHandleWheelY( /*number*/delta) /*boolean*/{
      if (this.props.overflowY === 'hidden' || delta === 0) {
        return false;
      }
  
      delta = Math.round(delta);
      if (delta === 0) {
        return false;
      }
  
      return delta < 0 && this.state.scrollY > 0 || delta >= 0 && this.state.scrollY < this.state.maxScrollY;
    },
  
    _reportContentHeight: function _reportContentHeight() {
      var scrollContentHeight = this.state.scrollContentHeight;
      var reservedHeight = this.state.reservedHeight;
      var requiredHeight = scrollContentHeight + reservedHeight;
      var contentHeight;
      var useMaxHeight = this.props.height === undefined;
      if (useMaxHeight && this.props.maxHeight > requiredHeight) {
        contentHeight = requiredHeight;
      } else if (this.state.height > requiredHeight && this.props.ownerHeight) {
        contentHeight = Math.max(requiredHeight, this.props.ownerHeight);
      } else {
        contentHeight = this.state.height + this.state.maxScrollY;
      }
      if (contentHeight !== this._contentHeight && this.props.onContentHeightChange) {
        this.props.onContentHeightChange(contentHeight);
      }
      this._contentHeight = contentHeight;
    },
  
    componentDidMount: function componentDidMount() {
      this._reportContentHeight();
    },
  
    componentWillReceiveProps: function componentWillReceiveProps( /*object*/nextProps) {
      var scrollToRow = nextProps.scrollToRow;
      if (scrollToRow !== undefined && scrollToRow !== null) {
        this._rowToScrollTo = scrollToRow;
      }
      var scrollToColumn = nextProps.scrollToColumn;
      if (scrollToColumn !== undefined && scrollToColumn !== null) {
        this._columnToScrollTo = scrollToColumn;
      }
  
      var newOverflowX = nextProps.overflowX;
      var newOverflowY = nextProps.overflowY;
      if (newOverflowX !== this.props.overflowX || newOverflowY !== this.props.overflowY) {
        this._wheelHandler = new ReactWheelHandler(this._onWheel, newOverflowX !== 'hidden', // Should handle horizontal scroll
        newOverflowY !== 'hidden' // Should handle vertical scroll
        );
      }
  
      // In the case of controlled scrolling, notify.
      if (this.props.ownerHeight !== nextProps.ownerHeight || this.props.scrollTop !== nextProps.scrollTop) {
        this._didScrollStart();
      }
      this._didScrollStop();
  
      this.setState(this._calculateState(nextProps, this.state));
    },
  
    componentDidUpdate: function componentDidUpdate() {
      this._reportContentHeight();
    },
  
    render: function render() /*object*/{
      var state = this.state;
      var props = this.props;
  
      var groupHeader;
      if (state.useGroupHeader) {
        groupHeader = React.createElement(FixedDataTableRow, {
          key: 'group_header',
          isScrolling: this._isScrolling,
          className: joinClasses(cx('fixedDataTableLayout/header'), cx('public/fixedDataTable/header')),
          width: state.width,
          height: state.groupHeaderHeight,
          index: 0,
          zIndex: 1,
          offsetTop: 0,
          scrollLeft: state.scrollX,
          fixedColumns: state.groupHeaderFixedColumns,
          scrollableColumns: state.groupHeaderScrollableColumns,
          onColumnResize: this._onColumnResize
        });
      }
  
      var maxScrollY = this.state.maxScrollY;
      var showScrollbarX = state.maxScrollX > 0 && state.overflowX !== 'hidden';
      var showScrollbarY = maxScrollY > 0 && state.overflowY !== 'hidden';
      var scrollbarXHeight = showScrollbarX ? Scrollbar.SIZE : 0;
      var scrollbarYHeight = state.height - scrollbarXHeight - 2 * BORDER_HEIGHT - state.footerHeight;
  
      var headerOffsetTop = state.useGroupHeader ? state.groupHeaderHeight : 0;
      var bodyOffsetTop = headerOffsetTop + state.headerHeight;
      scrollbarYHeight -= bodyOffsetTop;
      var bottomSectionOffset = 0;
      var footOffsetTop = props.maxHeight != null ? bodyOffsetTop + state.bodyHeight : bodyOffsetTop + scrollbarYHeight;
      var rowsContainerHeight = footOffsetTop + state.footerHeight;
  
      if (props.ownerHeight !== undefined && props.ownerHeight < state.height) {
        bottomSectionOffset = props.ownerHeight - state.height;
  
        footOffsetTop = Math.min(footOffsetTop, props.ownerHeight - state.footerHeight - scrollbarXHeight);
  
        scrollbarYHeight = Math.max(0, footOffsetTop - bodyOffsetTop);
      }
  
      var verticalScrollbar;
      if (showScrollbarY) {
        verticalScrollbar = React.createElement(Scrollbar, {
          size: scrollbarYHeight,
          contentSize: scrollbarYHeight + maxScrollY,
          onScroll: this._onVerticalScroll,
          verticalTop: bodyOffsetTop,
          position: state.scrollY
        });
      }
  
      var horizontalScrollbar;
      if (showScrollbarX) {
        var scrollbarXWidth = state.width;
        horizontalScrollbar = React.createElement(HorizontalScrollbar, {
          contentSize: scrollbarXWidth + state.maxScrollX,
          offset: bottomSectionOffset,
          onScroll: this._onHorizontalScroll,
          position: state.scrollX,
          size: scrollbarXWidth
        });
      }
  
      var dragKnob = React.createElement(FixedDataTableColumnResizeHandle, {
        height: state.height,
        initialWidth: state.columnResizingData.width || 0,
        minWidth: state.columnResizingData.minWidth || 0,
        maxWidth: state.columnResizingData.maxWidth || Number.MAX_VALUE,
        visible: !!state.isColumnResizing,
        leftOffset: state.columnResizingData.left || 0,
        knobHeight: state.headerHeight,
        initialEvent: state.columnResizingData.initialEvent,
        onColumnResizeEnd: props.onColumnResizeEndCallback,
        columnKey: state.columnResizingData.key
      });
  
      var footer = null;
      if (state.footerHeight) {
        footer = React.createElement(FixedDataTableRow, {
          key: 'footer',
          isScrolling: this._isScrolling,
          className: joinClasses(cx('fixedDataTableLayout/footer'), cx('public/fixedDataTable/footer')),
          width: state.width,
          height: state.footerHeight,
          index: -1,
          zIndex: 1,
          offsetTop: footOffsetTop,
          fixedColumns: state.footFixedColumns,
          scrollableColumns: state.footScrollableColumns,
          scrollLeft: state.scrollX
        });
      }
  
      var rows = this._renderRows(bodyOffsetTop);
  
      var header = React.createElement(FixedDataTableRow, {
        key: 'header',
        isScrolling: this._isScrolling,
        className: joinClasses(cx('fixedDataTableLayout/header'), cx('public/fixedDataTable/header')),
        width: state.width,
        height: state.headerHeight,
        index: -1,
        zIndex: 1,
        offsetTop: headerOffsetTop,
        scrollLeft: state.scrollX,
        fixedColumns: state.headFixedColumns,
        scrollableColumns: state.headScrollableColumns,
        onColumnResize: this._onColumnResize
      });
  
      var topShadow;
      var bottomShadow;
      if (state.scrollY) {
        topShadow = React.createElement('div', {
          className: joinClasses(cx('fixedDataTableLayout/topShadow'), cx('public/fixedDataTable/topShadow')),
          style: { top: bodyOffsetTop }
        });
      }
  
      if (state.ownerHeight != null && state.ownerHeight < state.height && state.scrollContentHeight + state.reservedHeight > state.ownerHeight || state.scrollY < maxScrollY) {
        bottomShadow = React.createElement('div', {
          className: joinClasses(cx('fixedDataTableLayout/bottomShadow'), cx('public/fixedDataTable/bottomShadow')),
          style: { top: footOffsetTop }
        });
      }
  
      return React.createElement(
        'div',
        {
          className: joinClasses(cx('fixedDataTableLayout/main'), cx('public/fixedDataTable/main')),
          onWheel: this._wheelHandler.onWheel,
          style: { height: state.height, width: state.width } },
        React.createElement(
          'div',
          {
            className: cx('fixedDataTableLayout/rowsContainer'),
            style: { height: rowsContainerHeight, width: state.width } },
          dragKnob,
          groupHeader,
          header,
          rows,
          footer,
          topShadow,
          bottomShadow
        ),
        verticalScrollbar,
        horizontalScrollbar
      );
    },
  
    _renderRows: function _renderRows( /*number*/offsetTop) /*object*/{
      var state = this.state;
  
      return React.createElement(FixedDataTableBufferedRows, {
        isScrolling: this._isScrolling,
        defaultRowHeight: state.rowHeight,
        firstRowIndex: state.firstRowIndex,
        firstRowOffset: state.firstRowOffset,
        fixedColumns: state.bodyFixedColumns,
        height: state.bodyHeight,
        offsetTop: offsetTop,
        onRowClick: state.onRowClick,
        onRowDoubleClick: state.onRowDoubleClick,
        onRowMouseDown: state.onRowMouseDown,
        onRowMouseEnter: state.onRowMouseEnter,
        onRowMouseLeave: state.onRowMouseLeave,
        rowClassNameGetter: state.rowClassNameGetter,
        rowsCount: state.rowsCount,
        rowGetter: state.rowGetter,
        rowHeightGetter: state.rowHeightGetter,
        scrollLeft: state.scrollX,
        scrollableColumns: state.bodyScrollableColumns,
        showLastRowBorder: true,
        width: state.width,
        rowPositionGetter: this._scrollHelper.getRowPosition
      });
    },
  
    /**
     * This is called when a cell that is in the header of a column has its
     * resizer knob clicked on. It displays the resizer and puts in the correct
     * location on the table.
     */
    _onColumnResize: function _onColumnResize(
    /*number*/combinedWidth,
    /*number*/leftOffset,
    /*number*/cellWidth,
    /*?number*/cellMinWidth,
    /*?number*/cellMaxWidth,
    /*number|string*/columnKey,
    /*object*/event) {
      this.setState({
        isColumnResizing: true,
        columnResizingData: {
          left: leftOffset + combinedWidth - cellWidth,
          width: cellWidth,
          minWidth: cellMinWidth,
          maxWidth: cellMaxWidth,
          initialEvent: {
            clientX: event.clientX,
            clientY: event.clientY,
            preventDefault: emptyFunction
          },
          key: columnKey
        }
      });
    },
  
    _areColumnSettingsIdentical: function _areColumnSettingsIdentical(oldColumns, newColumns) {
      if (oldColumns.length !== newColumns.length) {
        return false;
      }
      for (var index = 0; index < oldColumns.length; ++index) {
        if (!shallowEqual(oldColumns[index].props, newColumns[index].props)) {
          return false;
        }
      }
      return true;
    },
  
    _populateColumnsAndColumnData: function _populateColumnsAndColumnData(columns, columnGroups, oldState) {
      var canReuseColumnSettings = false;
      var canReuseColumnGroupSettings = false;
  
      if (oldState && oldState.columns) {
        canReuseColumnSettings = this._areColumnSettingsIdentical(columns, oldState.columns);
      }
      if (oldState && oldState.columnGroups && columnGroups) {
        canReuseColumnGroupSettings = this._areColumnSettingsIdentical(columnGroups, oldState.columnGroups);
      }
  
      var columnInfo = {};
      if (canReuseColumnSettings) {
        columnInfo.bodyFixedColumns = oldState.bodyFixedColumns;
        columnInfo.bodyScrollableColumns = oldState.bodyScrollableColumns;
        columnInfo.headFixedColumns = oldState.headFixedColumns;
        columnInfo.headScrollableColumns = oldState.headScrollableColumns;
        columnInfo.footFixedColumns = oldState.footFixedColumns;
        columnInfo.footScrollableColumns = oldState.footScrollableColumns;
      } else {
        var bodyColumnTypes = this._splitColumnTypes(columns);
        columnInfo.bodyFixedColumns = bodyColumnTypes.fixed;
        columnInfo.bodyScrollableColumns = bodyColumnTypes.scrollable;
  
        var headColumnTypes = this._splitColumnTypes(this._selectColumnElement(HEADER, columns));
        columnInfo.headFixedColumns = headColumnTypes.fixed;
        columnInfo.headScrollableColumns = headColumnTypes.scrollable;
  
        var footColumnTypes = this._splitColumnTypes(this._selectColumnElement(FOOTER, columns));
        columnInfo.footFixedColumns = footColumnTypes.fixed;
        columnInfo.footScrollableColumns = footColumnTypes.scrollable;
      }
  
      if (canReuseColumnGroupSettings) {
        columnInfo.groupHeaderFixedColumns = oldState.groupHeaderFixedColumns;
        columnInfo.groupHeaderScrollableColumns = oldState.groupHeaderScrollableColumns;
      } else {
        if (columnGroups) {
          var groupHeaderColumnTypes = this._splitColumnTypes(this._selectColumnElement(HEADER, columnGroups));
          columnInfo.groupHeaderFixedColumns = groupHeaderColumnTypes.fixed;
          columnInfo.groupHeaderScrollableColumns = groupHeaderColumnTypes.scrollable;
        }
      }
  
      return columnInfo;
    },
  
    _calculateState: function _calculateState( /*object*/props, /*?object*/oldState) /*object*/{
      invariant(props.height !== undefined || props.maxHeight !== undefined, 'You must set either a height or a maxHeight');
  
      var children = [];
      ReactChildren.forEach(props.children, function (child, index) {
        if (child == null) {
          return;
        }
        invariant(child.type.__TableColumnGroup__ || child.type.__TableColumn__, 'child type should be <FixedDataTableColumn /> or ' + '<FixedDataTableColumnGroup />');
        children.push(child);
      });
  
      var useGroupHeader = false;
      if (children.length && children[0].type.__TableColumnGroup__) {
        useGroupHeader = true;
      }
  
      var firstRowIndex = oldState && oldState.firstRowIndex || 0;
      var firstRowOffset = oldState && oldState.firstRowOffset || 0;
      var scrollX, scrollY;
      if (oldState && props.overflowX !== 'hidden') {
        scrollX = oldState.scrollX;
      } else {
        scrollX = props.scrollLeft;
      }
      if (oldState && props.overflowY !== 'hidden') {
        scrollY = oldState.scrollY;
      } else {
        scrollState = this._scrollHelper.scrollTo(props.scrollTop);
        firstRowIndex = scrollState.index;
        firstRowOffset = scrollState.offset;
        scrollY = scrollState.position;
      }
  
      if (this._rowToScrollTo !== undefined) {
        scrollState = this._scrollHelper.scrollRowIntoView(this._rowToScrollTo);
        firstRowIndex = scrollState.index;
        firstRowOffset = scrollState.offset;
        scrollY = scrollState.position;
        delete this._rowToScrollTo;
      }
  
      var groupHeaderHeight = useGroupHeader ? props.groupHeaderHeight : 0;
  
      if (oldState && props.rowsCount !== oldState.rowsCount) {
        // Number of rows changed, try to scroll to the row from before the
        // change
        var viewportHeight = (props.height === undefined ? props.maxHeight : props.height) - (props.headerHeight || 0) - (props.footerHeight || 0) - (props.groupHeaderHeight || 0);
        this._scrollHelper = new FixedDataTableScrollHelper(props.rowsCount, props.rowHeight, viewportHeight, props.rowHeightGetter);
        var scrollState = this._scrollHelper.scrollToRow(firstRowIndex, firstRowOffset);
        firstRowIndex = scrollState.index;
        firstRowOffset = scrollState.offset;
        scrollY = scrollState.position;
      } else if (oldState && props.rowHeightGetter !== oldState.rowHeightGetter) {
        this._scrollHelper.setRowHeightGetter(props.rowHeightGetter);
      }
  
      var columnResizingData;
      if (props.isColumnResizing) {
        columnResizingData = oldState && oldState.columnResizingData;
      } else {
        columnResizingData = EMPTY_OBJECT;
      }
  
      var columns;
      var columnGroups;
  
      if (useGroupHeader) {
        var columnGroupSettings = FixedDataTableWidthHelper.adjustColumnGroupWidths(children, props.width);
        columns = columnGroupSettings.columns;
        columnGroups = columnGroupSettings.columnGroups;
      } else {
        columns = FixedDataTableWidthHelper.adjustColumnWidths(children, props.width);
      }
  
      var columnInfo = this._populateColumnsAndColumnData(columns, columnGroups, oldState);
  
      if (this._columnToScrollTo !== undefined) {
        // If selected column is a fixed column, don't scroll
        var fixedColumnsCount = columnInfo.bodyFixedColumns.length;
        if (this._columnToScrollTo >= fixedColumnsCount) {
          var totalFixedColumnsWidth = 0;
          var i, column;
          for (i = 0; i < columnInfo.bodyFixedColumns.length; ++i) {
            column = columnInfo.bodyFixedColumns[i];
            totalFixedColumnsWidth += column.props.width;
          }
  
          var scrollableColumnIndex = Math.min(this._columnToScrollTo - fixedColumnsCount, columnInfo.bodyScrollableColumns.length - 1);
  
          var previousColumnsWidth = 0;
          for (i = 0; i < scrollableColumnIndex; ++i) {
            column = columnInfo.bodyScrollableColumns[i];
            previousColumnsWidth += column.props.width;
          }
  
          var availableScrollWidth = props.width - totalFixedColumnsWidth;
          var selectedColumnWidth = columnInfo.bodyScrollableColumns[scrollableColumnIndex].props.width;
          var minAcceptableScrollPosition = previousColumnsWidth + selectedColumnWidth - availableScrollWidth;
  
          if (scrollX < minAcceptableScrollPosition) {
            scrollX = minAcceptableScrollPosition;
          }
  
          if (scrollX > previousColumnsWidth) {
            scrollX = previousColumnsWidth;
          }
        }
        delete this._columnToScrollTo;
      }
  
      var useMaxHeight = props.height === undefined;
      var height = Math.round(useMaxHeight ? props.maxHeight : props.height);
      var totalHeightReserved = props.footerHeight + props.headerHeight + groupHeaderHeight + 2 * BORDER_HEIGHT;
      var bodyHeight = height - totalHeightReserved;
      var scrollContentHeight = this._scrollHelper.getContentHeight();
      var totalHeightNeeded = scrollContentHeight + totalHeightReserved;
      var scrollContentWidth = FixedDataTableWidthHelper.getTotalWidth(columns);
  
      var horizontalScrollbarVisible = scrollContentWidth > props.width && props.overflowX !== 'hidden';
  
      if (horizontalScrollbarVisible) {
        bodyHeight -= Scrollbar.SIZE;
        totalHeightNeeded += Scrollbar.SIZE;
        totalHeightReserved += Scrollbar.SIZE;
      }
  
      var maxScrollX = Math.max(0, scrollContentWidth - props.width);
      var maxScrollY = Math.max(0, scrollContentHeight - bodyHeight);
      scrollX = Math.min(scrollX, maxScrollX);
      scrollY = Math.min(scrollY, maxScrollY);
  
      if (!maxScrollY) {
        // no vertical scrollbar necessary, use the totals we tracked so we
        // can shrink-to-fit vertically
        if (useMaxHeight) {
          height = totalHeightNeeded;
        }
        bodyHeight = totalHeightNeeded - totalHeightReserved;
      }
  
      this._scrollHelper.setViewportHeight(bodyHeight);
  
      // The order of elements in this object metters and bringing bodyHeight,
      // height or useGroupHeader to the top can break various features
      var newState = _extends({
        isColumnResizing: oldState && oldState.isColumnResizing
      }, columnInfo, props, {
  
        columns: columns,
        columnGroups: columnGroups,
        columnResizingData: columnResizingData,
        firstRowIndex: firstRowIndex,
        firstRowOffset: firstRowOffset,
        horizontalScrollbarVisible: horizontalScrollbarVisible,
        maxScrollX: maxScrollX,
        maxScrollY: maxScrollY,
        reservedHeight: totalHeightReserved,
        scrollContentHeight: scrollContentHeight,
        scrollX: scrollX,
        scrollY: scrollY,
  
        // These properties may overwrite properties defined in
        // columnInfo and props
        bodyHeight: bodyHeight,
        height: height,
        groupHeaderHeight: groupHeaderHeight,
        useGroupHeader: useGroupHeader
      });
  
      return newState;
    },
  
    _selectColumnElement: function _selectColumnElement( /*string*/type, /*array*/columns) /*array*/{
      var newColumns = [];
      for (var i = 0; i < columns.length; ++i) {
        var column = columns[i];
        newColumns.push(React.cloneElement(column, {
          cell: type ? column.props[type] : column.props[CELL]
        }));
      }
      return newColumns;
    },
  
    _splitColumnTypes: function _splitColumnTypes( /*array*/columns) /*object*/{
      var fixedColumns = [];
      var scrollableColumns = [];
      for (var i = 0; i < columns.length; ++i) {
        if (columns[i].props.fixed) {
          fixedColumns.push(columns[i]);
        } else {
          scrollableColumns.push(columns[i]);
        }
      }
      return {
        fixed: fixedColumns,
        scrollable: scrollableColumns
      };
    },
  
    _onWheel: function _onWheel( /*number*/deltaX, /*number*/deltaY) {
      if (this.isMounted()) {
        if (!this._isScrolling) {
          this._didScrollStart();
        }
        var x = this.state.scrollX;
        if (Math.abs(deltaY) > Math.abs(deltaX) && this.props.overflowY !== 'hidden') {
          var scrollState = this._scrollHelper.scrollBy(Math.round(deltaY));
          var maxScrollY = Math.max(0, scrollState.contentHeight - this.state.bodyHeight);
          this.setState({
            firstRowIndex: scrollState.index,
            firstRowOffset: scrollState.offset,
            scrollY: scrollState.position,
            scrollContentHeight: scrollState.contentHeight,
            maxScrollY: maxScrollY
          });
        } else if (deltaX && this.props.overflowX !== 'hidden') {
          x += deltaX;
          x = x < 0 ? 0 : x;
          x = x > this.state.maxScrollX ? this.state.maxScrollX : x;
          this.setState({
            scrollX: x
          });
        }
  
        this._didScrollStop();
      }
    },
  
    _onHorizontalScroll: function _onHorizontalScroll( /*number*/scrollPos) {
      if (this.isMounted() && scrollPos !== this.state.scrollX) {
        if (!this._isScrolling) {
          this._didScrollStart();
        }
        this.setState({
          scrollX: scrollPos
        });
        this._didScrollStop();
      }
    },
  
    _onVerticalScroll: function _onVerticalScroll( /*number*/scrollPos) {
      if (this.isMounted() && scrollPos !== this.state.scrollY) {
        if (!this._isScrolling) {
          this._didScrollStart();
        }
        var scrollState = this._scrollHelper.scrollTo(Math.round(scrollPos));
        this.setState({
          firstRowIndex: scrollState.index,
          firstRowOffset: scrollState.offset,
          scrollY: scrollState.position,
          scrollContentHeight: scrollState.contentHeight
        });
        this._didScrollStop();
      }
    },
  
    _didScrollStart: function _didScrollStart() {
      if (this.isMounted() && !this._isScrolling) {
        this._isScrolling = true;
        if (this.props.onScrollStart) {
          this.props.onScrollStart(this.state.scrollX, this.state.scrollY);
        }
      }
    },
  
    _didScrollStop: function _didScrollStop() {
      if (this.isMounted() && this._isScrolling) {
        this._isScrolling = false;
        this.setState({ redraw: true });
        if (this.props.onScrollEnd) {
          this.props.onScrollEnd(this.state.scrollX, this.state.scrollY);
        }
      }
    }
  });
  
  var HorizontalScrollbar = React.createClass({
    displayName: 'HorizontalScrollbar',
  
    mixins: [ReactComponentWithPureRenderMixin],
    propTypes: {
      contentSize: PropTypes.number.isRequired,
      offset: PropTypes.number.isRequired,
      onScroll: PropTypes.func.isRequired,
      position: PropTypes.number.isRequired,
      size: PropTypes.number.isRequired
    },
  
    render: function render() /*object*/{
      var outerContainerStyle = {
        height: Scrollbar.SIZE,
        width: this.props.size
      };
      var innerContainerStyle = {
        height: Scrollbar.SIZE,
        position: 'absolute',
        overflow: 'hidden',
        width: this.props.size
      };
      translateDOMPositionXY(innerContainerStyle, 0, this.props.offset);
  
      return React.createElement(
        'div',
        {
          className: joinClasses(cx('fixedDataTableLayout/horizontalScrollbar'), cx('public/fixedDataTable/horizontalScrollbar')),
          style: outerContainerStyle },
        React.createElement(
          'div',
          { style: innerContainerStyle },
          React.createElement(Scrollbar, _extends({}, this.props, {
            isOpaque: true,
            orientation: 'horizontal',
            offset: undefined
          }))
        )
      );
    }
  });
  
  module.exports = FixedDataTable;
  // isColumnResizing should be overwritten by value from props if
  // avaialble