text: import React from 'react';
  import PropTypes from 'prop-types';
  import cn from 'classnames';
  import { FixedSizeGrid as Grid } from 'react-window';
  
  import Header from './TableHeader';
  import Footer from './TableFooter';
  
  /**
   * A wrapper of the Grid for internal only
   */
  class GridTable extends React.PureComponent {
    constructor(props) {
      super(props);
  
      this._setHeaderRef = this._setHeaderRef.bind(this);
      this._setFooterRef = this._setFooterRef.bind(this);
      this._setBodyRef = this._setBodyRef.bind(this);
      this._itemKey = this._itemKey.bind(this);
      this._handleItemsRendered = this._handleItemsRendered.bind(this);
  
      this.renderRow = this.renderRow.bind(this);
    }
  
    forceUpdateTable() {
      this.headerRef && this.headerRef.forceUpdate();
      this.footerRef && this.footerRef.forceUpdate();
      this.bodyRef && this.bodyRef.forceUpdate();
    }
  
    scrollToPosition(args) {
      this.headerRef && this.headerRef.scrollTo(args.scrollLeft);
      this.footerRef && this.footerRef.scrollTo(args.scrollLeft);
      this.bodyRef && this.bodyRef.scrollTo(args);
    }
  
    scrollToTop(scrollTop) {
      this.bodyRef && this.bodyRef.scrollTo({ scrollTop });
    }
  
    scrollToLeft(scrollLeft) {
      this.headerRef && this.headerRef.scrollTo(scrollLeft);
      this.footerRef && this.footerRef.scrollTo(scrollLeft);
      this.bodyRef && this.bodyRef.scrollToPosition({ scrollLeft });
    }
  
    scrollToRow(rowIndex = 0, align = 'auto') {
      this.bodyRef && this.bodyRef.scrollToItem({ rowIndex, align });
    }
  
    renderRow(args) {
      const { data, columns, rowRenderer } = this.props;
      const rowData = data[args.rowIndex];
      return rowRenderer({ ...args, columns, rowData });
    }
  
    render() {
      const {
        containerStyle,
        gridStyle,
        classPrefix,
        className,
        data,
        frozenData,
        width,
        height,
        rowHeight,
        headerWidth,
        bodyWidth,
        useIsScrolling,
        onScroll,
        hoveredRowKey,
        overscanRowCount,
        // omit from rest
        style,
        onScrollbarPresenceChange,
        ...rest
      } = this.props;
      const headerHeight = this._getHeaderHeight();
      const footerHeight = this._getFooterHeight();
      const frozenRowCount = frozenData.length;
      const frozenRowsHeight = rowHeight * frozenRowCount;
      const cls = cn(`${classPrefix}__table`, className);
      const containerProps = containerStyle ? { style: containerStyle } : null;
      return (
        <div role="table" className={cls} {...containerProps}>
        {footerHeight > 0 && (
            <Footer
              {...rest}
              className={`${classPrefix}__footer`}
              ref={this._setFooterRef}
              data={data}
              frozenData={frozenData}
              width={bodyWidth}
              height={footerHeight}
              rowWidth={headerWidth}
              rowHeight={rowHeight}
              footerHeight={this.props.footerHeight}
              headerRenderer={this.props.footerRenderer}
              rowRenderer={this.props.rowRenderer}
              hoveredRowKey={frozenRowCount > 0 ? hoveredRowKey : null}
            />
          )}
          <Grid
            {...rest}
            className={`${classPrefix}__body`}
            ref={this._setBodyRef}
            data={data}
            itemKey={this._itemKey}
            frozenData={frozenData}
            width={bodyWidth}
            height={Math.max(height - headerHeight - footerHeight - frozenRowsHeight, 0)}
            rowHeight={rowHeight}
            rowCount={data.length}
            overscanRowCount={overscanRowCount}
            columnWidth={bodyWidth}
            columnCount={1}
            overscanColumnCount={0}
            useIsScrolling={useIsScrolling}
            hoveredRowKey={hoveredRowKey}
            onScroll={onScroll}
            onItemsRendered={this._handleItemsRendered}
            children={this.renderRow}
            style={gridStyle}
          />
          {headerHeight + frozenRowsHeight > 0 && (
            // put header after body and reverse the display order via css
            // to prevent header's shadow being covered by body
            <Header
              {...rest}
              className={`${classPrefix}__header`}
              ref={this._setHeaderRef}
              data={data}
              frozenData={frozenData}
              width={bodyWidth}
              height={Math.min(headerHeight + frozenRowsHeight, height)}
              rowWidth={headerWidth}
              rowHeight={rowHeight}
              headerHeight={this.props.headerHeight}
              headerRenderer={this.props.headerRenderer}
              rowRenderer={this.props.rowRenderer}
              hoveredRowKey={frozenRowCount > 0 ? hoveredRowKey : null}
            />
          )}
        </div>
      );
    }
  
    _setHeaderRef(ref) {
      this.headerRef = ref;
    }
  
    _setFooterRef(ref){
      this.footerRef = ref;
    }
  
    _setBodyRef(ref) {
      this.bodyRef = ref;
    }
  
    _itemKey({ rowIndex }) {
      const { data, rowKey } = this.props;
      return data[rowIndex][rowKey];
    }
  
    _getHeaderHeight() {
      const { headerHeight } = this.props;
      if (Array.isArray(headerHeight)) {
        return headerHeight.reduce((sum, height) => sum + height, 0);
      }
      return headerHeight;
    }
  
    _getFooterHeight(){
      const { footerHeight } = this.props;
      return footerHeight || 0;
    }
  
    _handleItemsRendered({ overscanRowStartIndex, overscanRowStopIndex, visibleRowStartIndex, visibleRowStopIndex }) {
      this.props.onRowsRendered({
        overscanStartIndex: overscanRowStartIndex,
        overscanStopIndex: overscanRowStopIndex,
        startIndex: visibleRowStartIndex,
        stopIndex: visibleRowStopIndex,
      });
    }
  }
  
  GridTable.propTypes = {
    containerStyle: PropTypes.object,
    classPrefix: PropTypes.string,
    className: PropTypes.string,
    width: PropTypes.number.isRequired,
    height: PropTypes.number.isRequired,
    headerHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.arrayOf(PropTypes.number)]).isRequired,
    headerWidth: PropTypes.number.isRequired,
    bodyWidth: PropTypes.number.isRequired,
    rowHeight: PropTypes.number.isRequired,
    columns: PropTypes.arrayOf(PropTypes.object).isRequired,
    data: PropTypes.arrayOf(PropTypes.object).isRequired,
    rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
    frozenData: PropTypes.arrayOf(PropTypes.object),
    useIsScrolling: PropTypes.bool,
    overscanRowCount: PropTypes.number,
    hoveredRowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    style: PropTypes.object,
    onScrollbarPresenceChange: PropTypes.func,
  
    onScroll: PropTypes.func,
    onRowsRendered: PropTypes.func,
    headerRenderer: PropTypes.func.isRequired,
    rowRenderer: PropTypes.func.isRequired,
  };
  
  export default GridTable;
  