Files
2017-05-10 16:33:00 +08:00

716 lines
24 KiB
JavaScript

import _extends from 'babel-runtime/helpers/extends';
import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _createClass from 'babel-runtime/helpers/createClass';
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
import _inherits from 'babel-runtime/helpers/inherits';
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import CellMeasurerCacheDecorator from './CellMeasurerCacheDecorator';
import Grid from '../Grid';
var SCROLLBAR_SIZE_BUFFER = 20;
/**
* Renders 1, 2, or 4 Grids depending on configuration.
* A main (body) Grid will always be rendered.
* Optionally, 1-2 Grids for sticky header rows will also be rendered.
* If no sticky columns, only 1 sticky header Grid will be rendered.
* If sticky columns, 2 sticky header Grids will be rendered.
*/
var MultiGrid = function (_PureComponent) {
_inherits(MultiGrid, _PureComponent);
function MultiGrid(props, context) {
_classCallCheck(this, MultiGrid);
var _this = _possibleConstructorReturn(this, (MultiGrid.__proto__ || _Object$getPrototypeOf(MultiGrid)).call(this, props, context));
_this.state = {
scrollLeft: 0,
scrollTop: 0
};
_this._deferredInvalidateColumnIndex = null;
_this._deferredInvalidateRowIndex = null;
_this._bottomLeftGridRef = _this._bottomLeftGridRef.bind(_this);
_this._bottomRightGridRef = _this._bottomRightGridRef.bind(_this);
_this._cellRendererBottomLeftGrid = _this._cellRendererBottomLeftGrid.bind(_this);
_this._cellRendererBottomRightGrid = _this._cellRendererBottomRightGrid.bind(_this);
_this._cellRendererTopRightGrid = _this._cellRendererTopRightGrid.bind(_this);
_this._columnWidthRightGrid = _this._columnWidthRightGrid.bind(_this);
_this._onScroll = _this._onScroll.bind(_this);
_this._rowHeightBottomGrid = _this._rowHeightBottomGrid.bind(_this);
_this._topLeftGridRef = _this._topLeftGridRef.bind(_this);
_this._topRightGridRef = _this._topRightGridRef.bind(_this);
return _this;
}
_createClass(MultiGrid, [{
key: 'forceUpdateGrids',
value: function forceUpdateGrids() {
this._bottomLeftGrid && this._bottomLeftGrid.forceUpdate();
this._bottomRightGrid && this._bottomRightGrid.forceUpdate();
this._topLeftGrid && this._topLeftGrid.forceUpdate();
this._topRightGrid && this._topRightGrid.forceUpdate();
}
/** See Grid#invalidateCellSizeAfterRender */
}, {
key: 'invalidateCellSizeAfterRender',
value: function invalidateCellSizeAfterRender() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$columnIndex = _ref.columnIndex,
columnIndex = _ref$columnIndex === undefined ? 0 : _ref$columnIndex,
_ref$rowIndex = _ref.rowIndex,
rowIndex = _ref$rowIndex === undefined ? 0 : _ref$rowIndex;
this._deferredInvalidateColumnIndex = typeof this._deferredInvalidateColumnIndex === 'number' ? Math.min(this._deferredInvalidateColumnIndex, columnIndex) : columnIndex;
this._deferredInvalidateRowIndex = typeof this._deferredInvalidateRowIndex === 'number' ? Math.min(this._deferredInvalidateRowIndex, rowIndex) : rowIndex;
}
/** See Grid#measureAllCells */
}, {
key: 'measureAllCells',
value: function measureAllCells() {
this._bottomLeftGrid && this._bottomLeftGrid.measureAllCells();
this._bottomRightGrid && this._bottomRightGrid.measureAllCells();
this._topLeftGrid && this._topLeftGrid.measureAllCells();
this._topRightGrid && this._topRightGrid.measureAllCells();
}
/** See Grid#recomputeGridSize */
}, {
key: 'recomputeGridSize',
value: function recomputeGridSize() {
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref2$columnIndex = _ref2.columnIndex,
columnIndex = _ref2$columnIndex === undefined ? 0 : _ref2$columnIndex,
_ref2$rowIndex = _ref2.rowIndex,
rowIndex = _ref2$rowIndex === undefined ? 0 : _ref2$rowIndex;
var _props = this.props,
fixedColumnCount = _props.fixedColumnCount,
fixedRowCount = _props.fixedRowCount;
var adjustedColumnIndex = Math.max(0, columnIndex - fixedColumnCount);
var adjustedRowIndex = Math.max(0, rowIndex - fixedRowCount);
this._bottomLeftGrid && this._bottomLeftGrid.recomputeGridSize({
columnIndex: columnIndex,
rowIndex: adjustedRowIndex
});
this._bottomRightGrid && this._bottomRightGrid.recomputeGridSize({
columnIndex: adjustedColumnIndex,
rowIndex: adjustedRowIndex
});
this._topLeftGrid && this._topLeftGrid.recomputeGridSize({
columnIndex: columnIndex,
rowIndex: rowIndex
});
this._topRightGrid && this._topRightGrid.recomputeGridSize({
columnIndex: adjustedColumnIndex,
rowIndex: rowIndex
});
this._leftGridWidth = null;
this._topGridHeight = null;
this._maybeCalculateCachedStyles(null, this.props, null, this.state);
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
var _props2 = this.props,
scrollLeft = _props2.scrollLeft,
scrollTop = _props2.scrollTop;
if (scrollLeft > 0 || scrollTop > 0) {
var newState = {};
if (scrollLeft > 0) {
newState.scrollLeft = scrollLeft;
}
if (scrollTop > 0) {
newState.scrollTop = scrollTop;
}
this.setState(newState);
}
this._handleInvalidatedGridSize();
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate(prevProps, prevState) {
this._handleInvalidatedGridSize();
}
}, {
key: 'componentWillMount',
value: function componentWillMount() {
var _props3 = this.props,
deferredMeasurementCache = _props3.deferredMeasurementCache,
fixedColumnCount = _props3.fixedColumnCount,
fixedRowCount = _props3.fixedRowCount;
this._maybeCalculateCachedStyles(null, this.props, null, this.state);
if (deferredMeasurementCache) {
this._deferredMeasurementCacheBottomLeftGrid = fixedRowCount > 0 ? new CellMeasurerCacheDecorator({
cellMeasurerCache: deferredMeasurementCache,
columnIndexOffset: 0,
rowIndexOffset: fixedRowCount
}) : deferredMeasurementCache;
this._deferredMeasurementCacheBottomRightGrid = fixedColumnCount > 0 || fixedRowCount > 0 ? new CellMeasurerCacheDecorator({
cellMeasurerCache: deferredMeasurementCache,
columnIndexOffset: fixedColumnCount,
rowIndexOffset: fixedRowCount
}) : deferredMeasurementCache;
this._deferredMeasurementCacheTopRightGrid = fixedColumnCount > 0 ? new CellMeasurerCacheDecorator({
cellMeasurerCache: deferredMeasurementCache,
columnIndexOffset: fixedColumnCount,
rowIndexOffset: 0
}) : deferredMeasurementCache;
}
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps, nextState) {
var _props4 = this.props,
columnWidth = _props4.columnWidth,
fixedColumnCount = _props4.fixedColumnCount,
fixedRowCount = _props4.fixedRowCount,
rowHeight = _props4.rowHeight;
if (columnWidth !== nextProps.columnWidth || fixedColumnCount !== nextProps.fixedColumnCount) {
this._leftGridWidth = null;
}
if (fixedRowCount !== nextProps.fixedRowCount || rowHeight !== nextProps.rowHeight) {
this._topGridHeight = null;
}
if (nextProps.scrollLeft !== this.props.scrollLeft || nextProps.scrollTop !== this.props.scrollTop) {
var newState = {};
if (nextProps.scrollLeft != null && nextProps.scrollLeft >= 0) {
newState.scrollLeft = nextProps.scrollLeft;
}
if (nextProps.scrollTop != null && nextProps.scrollTop >= 0) {
newState.scrollTop = nextProps.scrollTop;
}
this.setState(newState);
}
this._maybeCalculateCachedStyles(this.props, nextProps, this.state, nextState);
}
}, {
key: 'render',
value: function render() {
var _props5 = this.props,
onScroll = _props5.onScroll,
onSectionRendered = _props5.onSectionRendered,
scrollLeftProp = _props5.scrollLeft,
scrollToColumn = _props5.scrollToColumn,
scrollTopProp = _props5.scrollTop,
scrollToRow = _props5.scrollToRow,
rest = _objectWithoutProperties(_props5, ['onScroll', 'onSectionRendered', 'scrollLeft', 'scrollToColumn', 'scrollTop', 'scrollToRow']);
// Don't render any of our Grids if there are no cells.
// This mirrors what Grid does,
// And prevents us from recording inaccurage measurements when used with CellMeasurer.
if (this.props.width === 0 || this.props.height === 0) {
return null;
}
// scrollTop and scrollLeft props are explicitly filtered out and ignored
var _state = this.state,
scrollLeft = _state.scrollLeft,
scrollTop = _state.scrollTop;
return React.createElement(
'div',
{ style: this._containerOuterStyle },
React.createElement(
'div',
{ style: this._containerTopStyle },
this._renderTopLeftGrid(rest),
this._renderTopRightGrid(_extends({}, rest, {
scrollLeft: scrollLeft
}))
),
React.createElement(
'div',
{ style: this._containerBottomStyle },
this._renderBottomLeftGrid(_extends({}, rest, {
scrollTop: scrollTop
})),
this._renderBottomRightGrid(_extends({}, rest, {
onScroll: onScroll,
onSectionRendered: onSectionRendered,
scrollLeft: scrollLeft,
scrollToColumn: scrollToColumn,
scrollToRow: scrollToRow,
scrollTop: scrollTop
}))
)
);
}
}, {
key: '_bottomLeftGridRef',
value: function _bottomLeftGridRef(ref) {
this._bottomLeftGrid = ref;
}
}, {
key: '_bottomRightGridRef',
value: function _bottomRightGridRef(ref) {
this._bottomRightGrid = ref;
}
}, {
key: '_cellRendererBottomLeftGrid',
value: function _cellRendererBottomLeftGrid(_ref3) {
var rowIndex = _ref3.rowIndex,
rest = _objectWithoutProperties(_ref3, ['rowIndex']);
var _props6 = this.props,
cellRenderer = _props6.cellRenderer,
fixedRowCount = _props6.fixedRowCount,
rowCount = _props6.rowCount;
if (rowIndex === rowCount - fixedRowCount) {
return React.createElement('div', {
key: rest.key,
style: _extends({}, rest.style, {
height: SCROLLBAR_SIZE_BUFFER
})
});
} else {
return cellRenderer(_extends({}, rest, {
parent: this,
rowIndex: rowIndex + fixedRowCount
}));
}
}
}, {
key: '_cellRendererBottomRightGrid',
value: function _cellRendererBottomRightGrid(_ref4) {
var columnIndex = _ref4.columnIndex,
rowIndex = _ref4.rowIndex,
rest = _objectWithoutProperties(_ref4, ['columnIndex', 'rowIndex']);
var _props7 = this.props,
cellRenderer = _props7.cellRenderer,
fixedColumnCount = _props7.fixedColumnCount,
fixedRowCount = _props7.fixedRowCount;
return cellRenderer(_extends({}, rest, {
columnIndex: columnIndex + fixedColumnCount,
parent: this,
rowIndex: rowIndex + fixedRowCount
}));
}
}, {
key: '_cellRendererTopRightGrid',
value: function _cellRendererTopRightGrid(_ref5) {
var columnIndex = _ref5.columnIndex,
rest = _objectWithoutProperties(_ref5, ['columnIndex']);
var _props8 = this.props,
cellRenderer = _props8.cellRenderer,
columnCount = _props8.columnCount,
fixedColumnCount = _props8.fixedColumnCount;
if (columnIndex === columnCount - fixedColumnCount) {
return React.createElement('div', {
key: rest.key,
style: _extends({}, rest.style, {
width: SCROLLBAR_SIZE_BUFFER
})
});
} else {
return cellRenderer(_extends({}, rest, {
columnIndex: columnIndex + fixedColumnCount,
parent: this
}));
}
}
}, {
key: '_columnWidthRightGrid',
value: function _columnWidthRightGrid(_ref6) {
var index = _ref6.index;
var _props9 = this.props,
columnCount = _props9.columnCount,
fixedColumnCount = _props9.fixedColumnCount,
columnWidth = _props9.columnWidth;
// An extra cell is added to the count
// This gives the smaller Grid extra room for offset,
// In case the main (bottom right) Grid has a scrollbar
// If no scrollbar, the extra space is overflow:hidden anyway
if (index === columnCount - fixedColumnCount) {
return SCROLLBAR_SIZE_BUFFER;
}
return typeof columnWidth === 'function' ? columnWidth({ index: index + fixedColumnCount }) : columnWidth;
}
}, {
key: '_getBottomGridHeight',
value: function _getBottomGridHeight(props) {
var height = props.height;
var topGridHeight = this._getTopGridHeight(props);
return height - topGridHeight;
}
}, {
key: '_getLeftGridWidth',
value: function _getLeftGridWidth(props) {
var fixedColumnCount = props.fixedColumnCount,
columnWidth = props.columnWidth;
if (this._leftGridWidth == null) {
if (typeof columnWidth === 'function') {
var leftGridWidth = 0;
for (var index = 0; index < fixedColumnCount; index++) {
leftGridWidth += columnWidth({ index: index });
}
this._leftGridWidth = leftGridWidth;
} else {
this._leftGridWidth = columnWidth * fixedColumnCount;
}
}
return this._leftGridWidth;
}
}, {
key: '_getRightGridWidth',
value: function _getRightGridWidth(props) {
var width = props.width;
var leftGridWidth = this._getLeftGridWidth(props);
return width - leftGridWidth;
}
}, {
key: '_getTopGridHeight',
value: function _getTopGridHeight(props) {
var fixedRowCount = props.fixedRowCount,
rowHeight = props.rowHeight;
if (this._topGridHeight == null) {
if (typeof rowHeight === 'function') {
var topGridHeight = 0;
for (var index = 0; index < fixedRowCount; index++) {
topGridHeight += rowHeight({ index: index });
}
this._topGridHeight = topGridHeight;
} else {
this._topGridHeight = rowHeight * fixedRowCount;
}
}
return this._topGridHeight;
}
}, {
key: '_handleInvalidatedGridSize',
value: function _handleInvalidatedGridSize() {
if (typeof this._deferredInvalidateColumnIndex === 'number') {
var columnIndex = this._deferredInvalidateColumnIndex;
var rowIndex = this._deferredInvalidateRowIndex;
this._deferredInvalidateColumnIndex = null;
this._deferredInvalidateRowIndex = null;
this.recomputeGridSize({
columnIndex: columnIndex,
rowIndex: rowIndex
});
this.forceUpdate();
}
}
/**
* Avoid recreating inline styles each render; this bypasses Grid's shallowCompare.
* This method recalculates styles only when specific props change.
*/
}, {
key: '_maybeCalculateCachedStyles',
value: function _maybeCalculateCachedStyles(prevProps, props, prevState, state) {
var columnWidth = props.columnWidth,
height = props.height,
fixedColumnCount = props.fixedColumnCount,
fixedRowCount = props.fixedRowCount,
rowHeight = props.rowHeight,
style = props.style,
styleBottomLeftGrid = props.styleBottomLeftGrid,
styleBottomRightGrid = props.styleBottomRightGrid,
styleTopLeftGrid = props.styleTopLeftGrid,
styleTopRightGrid = props.styleTopRightGrid,
width = props.width;
var firstRender = !prevProps;
var sizeChange = firstRender || height !== prevProps.height || width !== prevProps.width;
var leftSizeChange = firstRender || columnWidth !== prevProps.columnWidth || fixedColumnCount !== prevProps.fixedColumnCount;
var topSizeChange = firstRender || fixedRowCount !== prevProps.fixedRowCount || rowHeight !== prevProps.rowHeight;
if (firstRender || sizeChange || style !== prevProps.style) {
this._containerOuterStyle = _extends({
height: height,
overflow: 'visible', // Let :focus outline show through
width: width
}, style);
}
if (firstRender || sizeChange || topSizeChange) {
this._containerTopStyle = {
height: this._getTopGridHeight(props),
position: 'relative',
width: width
};
this._containerBottomStyle = {
height: height - this._getTopGridHeight(props),
overflow: 'visible', // Let :focus outline show through
position: 'relative',
width: width
};
}
if (firstRender || styleBottomLeftGrid !== prevProps.styleBottomLeftGrid) {
this._bottomLeftGridStyle = _extends({
left: 0,
overflowX: 'hidden',
overflowY: 'hidden',
position: 'absolute'
}, styleBottomLeftGrid);
}
if (firstRender || leftSizeChange || styleBottomRightGrid !== prevProps.styleBottomRightGrid) {
this._bottomRightGridStyle = _extends({
left: this._getLeftGridWidth(props),
position: 'absolute'
}, styleBottomRightGrid);
}
if (firstRender || styleTopLeftGrid !== prevProps.styleTopLeftGrid) {
this._topLeftGridStyle = _extends({
left: 0,
overflowX: 'hidden',
overflowY: 'hidden',
position: 'absolute',
top: 0
}, styleTopLeftGrid);
}
if (firstRender || leftSizeChange || styleTopRightGrid !== prevProps.styleTopRightGrid) {
this._topRightGridStyle = _extends({
left: this._getLeftGridWidth(props),
overflowX: 'hidden',
overflowY: 'hidden',
position: 'absolute',
top: 0
}, styleTopRightGrid);
}
}
}, {
key: '_onScroll',
value: function _onScroll(scrollInfo) {
var scrollLeft = scrollInfo.scrollLeft,
scrollTop = scrollInfo.scrollTop;
this.setState({
scrollLeft: scrollLeft,
scrollTop: scrollTop
});
var onScroll = this.props.onScroll;
if (onScroll) {
onScroll(scrollInfo);
}
}
}, {
key: '_renderBottomLeftGrid',
value: function _renderBottomLeftGrid(props) {
var fixedColumnCount = props.fixedColumnCount,
fixedRowCount = props.fixedRowCount,
rowCount = props.rowCount,
scrollTop = props.scrollTop;
if (!fixedColumnCount) {
return null;
}
return React.createElement(Grid, _extends({}, props, {
cellRenderer: this._cellRendererBottomLeftGrid,
columnCount: fixedColumnCount,
deferredMeasurementCache: this._deferredMeasurementCacheBottomLeftGrid,
height: this._getBottomGridHeight(props),
ref: this._bottomLeftGridRef,
rowCount: Math.max(0, rowCount - fixedRowCount) + 1 /* See _rowHeightBottomGrid */,
rowHeight: this._rowHeightBottomGrid,
scrollTop: scrollTop,
style: this._bottomLeftGridStyle,
tabIndex: null,
width: this._getLeftGridWidth(props)
}));
}
}, {
key: '_renderBottomRightGrid',
value: function _renderBottomRightGrid(props) {
var columnCount = props.columnCount,
fixedColumnCount = props.fixedColumnCount,
fixedRowCount = props.fixedRowCount,
rowCount = props.rowCount,
scrollToColumn = props.scrollToColumn,
scrollToRow = props.scrollToRow;
return React.createElement(Grid, _extends({}, props, {
cellRenderer: this._cellRendererBottomRightGrid,
columnCount: Math.max(0, columnCount - fixedColumnCount),
columnWidth: this._columnWidthRightGrid,
deferredMeasurementCache: this._deferredMeasurementCacheBottomRightGrid,
height: this._getBottomGridHeight(props),
onScroll: this._onScroll,
ref: this._bottomRightGridRef,
rowCount: Math.max(0, rowCount - fixedRowCount),
rowHeight: this._rowHeightBottomGrid,
scrollToColumn: scrollToColumn - fixedColumnCount,
scrollToRow: scrollToRow - fixedRowCount,
style: this._bottomRightGridStyle,
width: this._getRightGridWidth(props)
}));
}
}, {
key: '_renderTopLeftGrid',
value: function _renderTopLeftGrid(props) {
var fixedColumnCount = props.fixedColumnCount,
fixedRowCount = props.fixedRowCount;
if (!fixedColumnCount || !fixedRowCount) {
return null;
}
return React.createElement(Grid, _extends({}, props, {
columnCount: fixedColumnCount,
height: this._getTopGridHeight(props),
ref: this._topLeftGridRef,
rowCount: fixedRowCount,
style: this._topLeftGridStyle,
tabIndex: null,
width: this._getLeftGridWidth(props)
}));
}
}, {
key: '_renderTopRightGrid',
value: function _renderTopRightGrid(props) {
var columnCount = props.columnCount,
fixedColumnCount = props.fixedColumnCount,
fixedRowCount = props.fixedRowCount,
scrollLeft = props.scrollLeft;
if (!fixedRowCount) {
return null;
}
return React.createElement(Grid, _extends({}, props, {
cellRenderer: this._cellRendererTopRightGrid,
columnCount: Math.max(0, columnCount - fixedColumnCount) + 1 /* See _columnWidthRightGrid */,
columnWidth: this._columnWidthRightGrid,
deferredMeasurementCache: this._deferredMeasurementCacheTopRightGrid,
height: this._getTopGridHeight(props),
ref: this._topRightGridRef,
rowCount: fixedRowCount,
scrollLeft: scrollLeft,
style: this._topRightGridStyle,
tabIndex: null,
width: this._getRightGridWidth(props)
}));
}
}, {
key: '_rowHeightBottomGrid',
value: function _rowHeightBottomGrid(_ref7) {
var index = _ref7.index;
var _props10 = this.props,
fixedRowCount = _props10.fixedRowCount,
rowCount = _props10.rowCount,
rowHeight = _props10.rowHeight;
// An extra cell is added to the count
// This gives the smaller Grid extra room for offset,
// In case the main (bottom right) Grid has a scrollbar
// If no scrollbar, the extra space is overflow:hidden anyway
if (index === rowCount - fixedRowCount) {
return SCROLLBAR_SIZE_BUFFER;
}
return typeof rowHeight === 'function' ? rowHeight({ index: index + fixedRowCount }) : rowHeight;
}
}, {
key: '_topLeftGridRef',
value: function _topLeftGridRef(ref) {
this._topLeftGrid = ref;
}
}, {
key: '_topRightGridRef',
value: function _topRightGridRef(ref) {
this._topRightGrid = ref;
}
}]);
return MultiGrid;
}(PureComponent);
MultiGrid.defaultProps = {
fixedColumnCount: 0,
fixedRowCount: 0,
style: {},
styleBottomLeftGrid: {},
styleBottomRightGrid: {},
styleTopLeftGrid: {},
styleTopRightGrid: {}
};
export default MultiGrid;
process.env.NODE_ENV !== "production" ? MultiGrid.propTypes = {
fixedColumnCount: PropTypes.number.isRequired,
fixedRowCount: PropTypes.number.isRequired,
style: PropTypes.object.isRequired,
styleBottomLeftGrid: PropTypes.object.isRequired,
styleBottomRightGrid: PropTypes.object.isRequired,
styleTopLeftGrid: PropTypes.object.isRequired,
styleTopRightGrid: PropTypes.object.isRequired
} : void 0;