Merge pull request #3378 from influxdata/migrate-to-simple-multi-grid

Use custom multigrid component
pull/10616/head
Brandon Farmer 2018-05-07 17:47:39 -07:00 committed by GitHub
commit f4830576d5
7 changed files with 976 additions and 24 deletions

View File

@ -16,6 +16,7 @@
1. [#3245](https://github.com/influxdata/chronograf/pull/3245): Display 'no results' on cells without results
1. [#3354](https://github.com/influxdata/chronograf/pull/3354): Disable template variables for non editing users
1. [#3353](https://github.com/influxdata/chronograf/pull/3353): YAxisLabels in Dashboard Graph Builder not showing until graph is redrawn
1. [#3378](https://github.com/influxdata/chronograf/pull/3378): Ensure table graphs have a consistent ux between chrome and firefox
### Bug Fixes

View File

@ -1,26 +1,64 @@
import _ from 'lodash'
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import {Scrollbars} from 'react-custom-scrollbars'
import {ErrorHandling} from 'src/shared/decorators/errors'
@ErrorHandling
class FancyScrollbar extends Component {
constructor(props) {
super(props)
}
interface DefaultProps {
autoHide: boolean
autoHeight: boolean
maxHeight: number
setScrollTop: (value: React.MouseEvent<JSX.Element>) => void
style: React.CSSProperties
}
static defaultProps = {
interface Props {
className?: string
scrollTop?: number
scrollLeft?: number
}
@ErrorHandling
class FancyScrollbar extends Component<Props & Partial<DefaultProps>> {
public static defaultProps = {
autoHide: true,
autoHeight: false,
maxHeight: null,
style: {},
setScrollTop: () => {},
}
handleMakeDiv = className => props => {
private ref: React.RefObject<Scrollbars>
constructor(props) {
super(props)
this.ref = React.createRef<Scrollbars>()
}
public updateScroll() {
const ref = this.ref.current
if (ref && !_.isNil(this.props.scrollTop)) {
ref.scrollTop(this.props.scrollTop)
}
if (ref && !_.isNil(this.props.scrollLeft)) {
ref.scrollLeft(this.props.scrollLeft)
}
}
public componentDidMount() {
this.updateScroll()
}
public componentDidUpdate() {
this.updateScroll()
}
public handleMakeDiv = (className: string) => (props): JSX.Element => {
return <div {...props} className={`fancy-scroll--${className}`} />
}
render() {
public render() {
const {
autoHide,
autoHeight,
@ -28,6 +66,7 @@ class FancyScrollbar extends Component {
className,
maxHeight,
setScrollTop,
style,
} = this.props
return (
@ -35,6 +74,8 @@ class FancyScrollbar extends Component {
className={classnames('fancy-scroll--container', {
[className]: className,
})}
ref={this.ref}
style={style}
onScroll={setScrollTop}
autoHide={autoHide}
autoHideTimeout={1000}
@ -53,15 +94,4 @@ class FancyScrollbar extends Component {
}
}
const {bool, func, node, number, string} = PropTypes
FancyScrollbar.propTypes = {
children: node.isRequired,
className: string,
autoHide: bool,
autoHeight: bool,
maxHeight: number,
setScrollTop: func,
}
export default FancyScrollbar

View File

@ -0,0 +1,105 @@
import {CellMeasurerCache} from 'react-virtualized'
interface CellMeasurerCacheDecoratorParams {
cellMeasurerCache: CellMeasurerCache
columnIndexOffset: number
rowIndexOffset: number
}
interface IndexParam {
index: number
}
class CellMeasurerCacheDecorator {
private cellMeasurerCache: CellMeasurerCache
private columnIndexOffset: number
private rowIndexOffset: number
constructor(params: Partial<CellMeasurerCacheDecoratorParams> = {}) {
const {
cellMeasurerCache,
columnIndexOffset = 0,
rowIndexOffset = 0,
} = params
this.cellMeasurerCache = cellMeasurerCache
this.columnIndexOffset = columnIndexOffset
this.rowIndexOffset = rowIndexOffset
}
public clear(rowIndex: number, columnIndex: number): void {
this.cellMeasurerCache.clear(
rowIndex + this.rowIndexOffset,
columnIndex + this.columnIndexOffset
)
}
public clearAll(): void {
this.cellMeasurerCache.clearAll()
}
public columnWidth = ({index}: IndexParam) => {
this.cellMeasurerCache.columnWidth({
index: index + this.columnIndexOffset,
})
}
get defaultHeight(): number {
return this.cellMeasurerCache.defaultHeight
}
get defaultWidth(): number {
return this.cellMeasurerCache.defaultWidth
}
public hasFixedHeight(): boolean {
return this.cellMeasurerCache.hasFixedHeight()
}
public hasFixedWidth(): boolean {
return this.cellMeasurerCache.hasFixedWidth()
}
public getHeight(rowIndex: number, columnIndex: number = 0): number | null {
return this.cellMeasurerCache.getHeight(
rowIndex + this.rowIndexOffset,
columnIndex + this.columnIndexOffset
)
}
public getWidth(rowIndex: number, columnIndex: number = 0): number | null {
return this.cellMeasurerCache.getWidth(
rowIndex + this.rowIndexOffset,
columnIndex + this.columnIndexOffset
)
}
public has(rowIndex: number, columnIndex: number = 0): boolean {
return this.cellMeasurerCache.has(
rowIndex + this.rowIndexOffset,
columnIndex + this.columnIndexOffset
)
}
public rowHeight = ({index}: IndexParam) => {
this.cellMeasurerCache.rowHeight({
index: index + this.rowIndexOffset,
})
}
public set(
rowIndex: number,
columnIndex: number,
width: number,
height: number
): void {
this.cellMeasurerCache.set(
rowIndex + this.rowIndexOffset,
columnIndex + this.columnIndexOffset,
width,
height
)
}
}
export default CellMeasurerCacheDecorator

View File

@ -0,0 +1,813 @@
import * as React from 'react'
import CellMeasurerCacheDecorator from './CellMeasurerCacheDecorator'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import {Grid} from 'react-virtualized'
const SCROLLBAR_SIZE_BUFFER = 20
interface Props {
columnCount?: number
classNameBottomLeftGrid?: string
classNameBottomRightGrid?: string
classNameTopLeftGrid?: string
classNameTopRightGrid?: string
enableFixedColumnScroll?: boolean
enableFixedRowScroll?: boolean
fixedColumnCount?: number
fixedRowCount?: number
style?: object
styleBottomLeftGrid?: object
styleBottomRightGrid?: object
styleTopLeftGrid?: object
styleTopRightGrid?: object
scrollTop?: number
scrollLeft?: number
rowCount?: number
rowHeight?: (arg: {index: number}) => {} | number
columnWidth?: (arg: object) => {} | number
onScroll?: (arg: object) => {}
width: number
height: number
scrollToRow?: () => {}
onSectionRendered?: () => {}
scrollToColumn?: () => {}
cellRenderer?: (arg: object) => JSX.Element
}
interface State {
scrollLeft: number
scrollTop: number
scrollbarSize: number
showHorizontalScrollbar: boolean
showVerticalScrollbar: boolean
}
/**
* 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.
*/
class MultiGrid extends React.PureComponent<Props, State> {
public static defaultProps = {
classNameBottomLeftGrid: '',
classNameBottomRightGrid: '',
classNameTopLeftGrid: '',
classNameTopRightGrid: '',
enableFixedColumnScroll: false,
enableFixedRowScroll: false,
fixedColumnCount: 0,
fixedRowCount: 0,
scrollToColumn: -1,
scrollToRow: -1,
style: {},
styleBottomLeftGrid: {},
styleBottomRightGrid: {},
styleTopLeftGrid: {},
styleTopRightGrid: {},
}
public static getDerivedStateFromProps(nextProps, prevState) {
if (
nextProps.scrollLeft !== prevState.scrollLeft ||
nextProps.scrollTop !== prevState.scrollTop
) {
return {
scrollLeft:
nextProps.scrollLeft != null && nextProps.scrollLeft >= 0
? nextProps.scrollLeft
: prevState.scrollLeft,
scrollTop:
nextProps.scrollTop != null && nextProps.scrollTop >= 0
? nextProps.scrollTop
: prevState.scrollTop,
}
}
return null
}
private deferredInvalidateColumnIndex: number = 0
private deferredInvalidateRowIndex: number = 0
private bottomLeftGrid: Grid
private bottomRightGrid: Grid
private topLeftGrid: Grid
private topRightGrid: Grid
private deferredMeasurementCacheBottomLeftGrid: CellMeasurerCacheDecorator
private deferredMeasurementCacheBottomRightGrid: CellMeasurerCacheDecorator
private deferredMeasurementCacheTopRightGrid: CellMeasurerCacheDecorator
private leftGridWidth: number | null = 0
private topGridHeight: number | null = 0
private lastRenderedColumnWidth: (arg: object) => {} | number
private lastRenderedFixedColumnCount: number = 0
private lastRenderedFixedRowCount: number = 0
private lastRenderedRowHeight: (arg: {index: number}) => {} | number
private bottomRightGridStyle: object | null
private topRightGridStyle: object | null
private lastRenderedStyle: object | null
private lastRenderedHeight: number = 0
private lastRenderedWidth: number = 0
private containerTopStyle: object | null
private containerBottomStyle: object | null
private containerOuterStyle: object | null
private lastRenderedStyleBottomLeftGrid: object | null
private lastRenderedStyleBottomRightGrid: object | null
private lastRenderedStyleTopLeftGrid: object | null
private lastRenderedStyleTopRightGrid: object | null
private bottomLeftGridStyle: object | null
private topLeftGridStyle: object | null
constructor(props, context) {
super(props, context)
this.state = {
scrollLeft: 0,
scrollTop: 0,
scrollbarSize: 0,
showHorizontalScrollbar: false,
showVerticalScrollbar: false,
}
const {deferredMeasurementCache, fixedColumnCount, fixedRowCount} = props
this.maybeCalculateCachedStyles(true)
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
}
}
public forceUpdateGrids() {
if (this.bottomLeftGrid) {
this.bottomLeftGrid.forceUpdate()
}
if (this.bottomRightGrid) {
this.bottomRightGrid.forceUpdate()
}
if (this.topLeftGrid) {
this.topLeftGrid.forceUpdate()
}
if (this.topRightGrid) {
this.topRightGrid.forceUpdate()
}
}
/** See Grid#invalidateCellSizeAfterRender */
public invalidateCellSizeAfterRender({columnIndex = 0, rowIndex = 0} = {}) {
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 */
public measureAllCells() {
if (this.bottomLeftGrid) {
this.bottomLeftGrid.measureAllCells()
}
if (this.bottomRightGrid) {
this.bottomRightGrid.measureAllCells()
}
if (this.topLeftGrid) {
this.topLeftGrid.measureAllCells()
}
if (this.topRightGrid) {
this.topRightGrid.measureAllCells()
}
}
public recomputeGridSize({columnIndex = 0, rowIndex = 0} = {}) {
const {fixedColumnCount, fixedRowCount} = this.props
const adjustedColumnIndex = Math.max(0, columnIndex - fixedColumnCount)
const adjustedRowIndex = Math.max(0, rowIndex - fixedRowCount)
if (this.bottomLeftGrid) {
this.bottomLeftGrid.recomputeGridSize({
columnIndex,
rowIndex: adjustedRowIndex,
})
}
if (this.bottomRightGrid) {
this.bottomRightGrid.recomputeGridSize({
columnIndex: adjustedColumnIndex,
rowIndex: adjustedRowIndex,
})
}
if (this.topLeftGrid) {
this.topLeftGrid.recomputeGridSize({
columnIndex,
rowIndex,
})
}
if (this.topRightGrid) {
this.topRightGrid.recomputeGridSize({
columnIndex: adjustedColumnIndex,
rowIndex,
})
}
this.leftGridWidth = null
this.topGridHeight = null
this.maybeCalculateCachedStyles(true)
}
public componentDidMount() {
const {scrollLeft, scrollTop} = this.props
if (scrollLeft > 0 || scrollTop > 0) {
const newState: Partial<State> = {}
if (scrollLeft > 0) {
newState.scrollLeft = scrollLeft
}
if (scrollTop > 0) {
newState.scrollTop = scrollTop
}
this.setState({...this.state, ...newState})
}
this.handleInvalidatedGridSize()
}
public componentDidUpdate() {
this.handleInvalidatedGridSize()
}
public render() {
const {
onScroll,
scrollLeft: scrollLeftProp, // eslint-disable-line no-unused-vars
onSectionRendered,
scrollToRow,
scrollToColumn,
scrollTop: scrollTopProp, // eslint-disable-line no-unused-vars
...rest
} = this.props
this.prepareForRender()
// 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
const {scrollLeft, scrollTop} = this.state
return (
<div style={this.containerOuterStyle}>
<div style={this.containerTopStyle}>
{this.renderTopLeftGrid(rest)}
{this.renderTopRightGrid({
...rest,
...onScroll,
scrollLeft,
})}
</div>
<div style={this.containerBottomStyle}>
{this.renderBottomLeftGrid({
...rest,
onScroll,
scrollTop,
})}
{this.renderBottomRightGrid({
...rest,
onScroll,
onSectionRendered,
scrollLeft,
scrollToColumn,
scrollToRow,
scrollTop,
})}
</div>
</div>
)
}
public cellRendererBottomLeftGrid = ({
rowIndex,
...rest
}: Partial<Props> & {rowIndex: number; key: string}): JSX.Element => {
const {cellRenderer, fixedRowCount, rowCount} = this.props
if (rowIndex === rowCount - fixedRowCount) {
return (
<div
key={rest.key}
style={{
...rest.style,
height: SCROLLBAR_SIZE_BUFFER,
}}
/>
)
} else {
return cellRenderer({
...rest,
parent: this,
rowIndex: rowIndex + fixedRowCount,
})
}
}
private getBottomGridHeight(props) {
const {height} = props
const topGridHeight = this.getTopGridHeight(props)
return height - topGridHeight
}
private getLeftGridWidth(props) {
const {fixedColumnCount, columnWidth} = props
if (this.leftGridWidth == null) {
if (typeof columnWidth === 'function') {
let leftGridWidth = 0
for (let index = 0; index < fixedColumnCount; index++) {
leftGridWidth += columnWidth({index})
}
this.leftGridWidth = leftGridWidth
} else {
this.leftGridWidth = columnWidth * fixedColumnCount
}
}
return this.leftGridWidth
}
private getRightGridWidth(props) {
const {width} = props
const leftGridWidth = this.getLeftGridWidth(props)
const result = width - leftGridWidth
return result
}
private getTopGridHeight(props) {
const {fixedRowCount, rowHeight} = props
if (this.topGridHeight == null) {
if (typeof rowHeight === 'function') {
let topGridHeight = 0
for (let index = 0; index < fixedRowCount; index++) {
topGridHeight += rowHeight({index})
}
this.topGridHeight = topGridHeight
} else {
this.topGridHeight = rowHeight * fixedRowCount
}
}
return this.topGridHeight
}
private onScrollbarsScroll = (e: React.MouseEvent<JSX.Element>) => {
const {target} = e
this.onScroll(target)
}
private onScroll = scrollInfo => {
const {scrollLeft, scrollTop} = scrollInfo
this.setState({
scrollLeft,
scrollTop,
})
const {onScroll} = this.props
if (onScroll) {
onScroll(scrollInfo)
}
}
private onScrollLeft = scrollInfo => {
const {scrollLeft} = scrollInfo
this.onScroll({
scrollLeft,
scrollTop: this.state.scrollTop,
})
}
private renderBottomLeftGrid(props) {
const {fixedColumnCount, fixedRowCount, rowCount} = props
if (!fixedColumnCount) {
return null
}
const width = this.getLeftGridWidth(props)
const height = this.getBottomGridHeight(props)
return (
<Grid
{...props}
cellRenderer={this.cellRendererBottomLeftGrid}
className={this.props.classNameBottomLeftGrid}
columnCount={fixedColumnCount}
deferredMeasurementCache={this.deferredMeasurementCacheBottomLeftGrid}
onScroll={this.onScroll}
height={height}
ref={this.bottomLeftGridRef}
rowCount={Math.max(0, rowCount - fixedRowCount)}
rowHeight={this.rowHeightBottomGrid}
style={{
...this.bottomLeftGridStyle,
}}
tabIndex={null}
width={width}
/>
)
}
private renderBottomRightGrid(props) {
const {
columnCount,
fixedColumnCount,
fixedRowCount,
rowCount,
scrollToColumn,
scrollToRow,
} = props
const width = this.getRightGridWidth(props)
const height = this.getBottomGridHeight(props)
return (
<FancyScrollbar
style={{...this.bottomRightGridStyle, width, height}}
autoHide={true}
scrollTop={this.state.scrollTop}
scrollLeft={this.state.scrollLeft}
setScrollTop={this.onScrollbarsScroll}
>
<Grid
{...props}
cellRenderer={this.cellRendererBottomRightGrid}
className={this.props.classNameBottomRightGrid}
columnCount={Math.max(0, columnCount - fixedColumnCount)}
columnWidth={this.columnWidthRightGrid}
deferredMeasurementCache={
this.deferredMeasurementCacheBottomRightGrid
}
height={height}
ref={this.bottomRightGridRef}
rowCount={Math.max(0, rowCount - fixedRowCount)}
rowHeight={this.rowHeightBottomGrid}
onScroll={this.onScroll}
scrollToColumn={scrollToColumn - fixedColumnCount}
scrollToRow={scrollToRow - fixedRowCount}
style={{
...this.bottomRightGridStyle,
overflowX: false,
overflowY: true,
left: 0,
}}
width={width}
/>
</FancyScrollbar>
)
}
private renderTopLeftGrid(props) {
const {fixedColumnCount, fixedRowCount} = props
if (!fixedColumnCount || !fixedRowCount) {
return null
}
return (
<Grid
{...props}
className={this.props.classNameTopLeftGrid}
columnCount={fixedColumnCount}
height={this.getTopGridHeight(props)}
ref={this.topLeftGridRef}
rowCount={fixedRowCount}
style={this.topLeftGridStyle}
tabIndex={null}
width={this.getLeftGridWidth(props)}
/>
)
}
private renderTopRightGrid(props) {
const {
columnCount,
enableFixedRowScroll,
fixedColumnCount,
fixedRowCount,
scrollLeft,
} = props
if (!fixedRowCount) {
return null
}
const width = this.getRightGridWidth(props)
const height = this.getTopGridHeight(props)
return (
<Grid
{...props}
cellRenderer={this.cellRendererTopRightGrid}
className={this.props.classNameTopRightGrid}
columnCount={Math.max(0, columnCount - fixedColumnCount)}
columnWidth={this.columnWidthRightGrid}
deferredMeasurementCache={this.deferredMeasurementCacheTopRightGrid}
height={height}
onScroll={enableFixedRowScroll ? this.onScrollLeft : undefined}
ref={this.topRightGridRef}
rowCount={fixedRowCount}
scrollLeft={scrollLeft}
style={this.topRightGridStyle}
tabIndex={null}
width={width}
/>
)
}
private rowHeightBottomGrid = ({index}) => {
const {fixedRowCount, rowCount, rowHeight} = this.props
const {scrollbarSize, showVerticalScrollbar} = this.state
// 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 (showVerticalScrollbar && index === rowCount - fixedRowCount) {
return scrollbarSize
}
return typeof rowHeight === 'function'
? rowHeight({index: index + fixedRowCount})
: rowHeight
}
private topLeftGridRef = ref => {
this.topLeftGrid = ref
}
private topRightGridRef = ref => {
this.topRightGrid = ref
}
/**
* Avoid recreating inline styles each render; this bypasses Grid's shallowCompare.
* This method recalculates styles only when specific props change.
*/
private maybeCalculateCachedStyles(resetAll) {
const {
columnWidth,
height,
fixedColumnCount,
fixedRowCount,
rowHeight,
style,
styleBottomLeftGrid,
styleBottomRightGrid,
styleTopLeftGrid,
styleTopRightGrid,
width,
} = this.props
const sizeChange =
resetAll ||
height !== this.lastRenderedHeight ||
width !== this.lastRenderedWidth
const leftSizeChange =
resetAll ||
columnWidth !== this.lastRenderedColumnWidth ||
fixedColumnCount !== this.lastRenderedFixedColumnCount
const topSizeChange =
resetAll ||
fixedRowCount !== this.lastRenderedFixedRowCount ||
rowHeight !== this.lastRenderedRowHeight
if (resetAll || sizeChange || style !== this.lastRenderedStyle) {
this.containerOuterStyle = {
height,
overflow: 'visible', // Let :focus outline show through
width,
...style,
}
}
if (resetAll || sizeChange || topSizeChange) {
this.containerTopStyle = {
height: this.getTopGridHeight(this.props),
position: 'relative',
width,
}
this.containerBottomStyle = {
height: height - this.getTopGridHeight(this.props),
overflow: 'visible', // Let :focus outline show through
position: 'relative',
width,
}
}
if (
resetAll ||
styleBottomLeftGrid !== this.lastRenderedStyleBottomLeftGrid
) {
this.bottomLeftGridStyle = {
left: 0,
overflowY: 'hidden',
overflowX: 'hidden',
position: 'absolute',
...styleBottomLeftGrid,
}
}
if (
resetAll ||
leftSizeChange ||
styleBottomRightGrid !== this.lastRenderedStyleBottomRightGrid
) {
this.bottomRightGridStyle = {
left: this.getLeftGridWidth(this.props),
position: 'absolute',
...styleBottomRightGrid,
}
}
if (resetAll || styleTopLeftGrid !== this.lastRenderedStyleTopLeftGrid) {
this.topLeftGridStyle = {
left: 0,
overflowX: 'hidden',
overflowY: 'hidden',
position: 'absolute',
top: 0,
...styleTopLeftGrid,
}
}
if (
resetAll ||
leftSizeChange ||
styleTopRightGrid !== this.lastRenderedStyleTopRightGrid
) {
this.topRightGridStyle = {
left: this.getLeftGridWidth(this.props),
overflowX: 'hidden',
overflowY: 'hidden',
position: 'absolute',
top: 0,
...styleTopRightGrid,
}
}
this.lastRenderedColumnWidth = columnWidth
this.lastRenderedFixedColumnCount = fixedColumnCount
this.lastRenderedFixedRowCount = fixedRowCount
this.lastRenderedHeight = height
this.lastRenderedRowHeight = rowHeight
this.lastRenderedStyle = style
this.lastRenderedStyleBottomLeftGrid = styleBottomLeftGrid
this.lastRenderedStyleBottomRightGrid = styleBottomRightGrid
this.lastRenderedStyleTopLeftGrid = styleTopLeftGrid
this.lastRenderedStyleTopRightGrid = styleTopRightGrid
this.lastRenderedWidth = width
}
private bottomLeftGridRef = ref => {
this.bottomLeftGrid = ref
}
private bottomRightGridRef = ref => {
this.bottomRightGrid = ref
}
private cellRendererBottomRightGrid = ({columnIndex, rowIndex, ...rest}) => {
const {cellRenderer, fixedColumnCount, fixedRowCount} = this.props
return cellRenderer({
...rest,
columnIndex: columnIndex + fixedColumnCount,
parent: this,
rowIndex: rowIndex + fixedRowCount,
})
}
private cellRendererTopRightGrid = ({columnIndex, ...rest}) => {
const {cellRenderer, columnCount, fixedColumnCount} = this.props
if (columnIndex === columnCount - fixedColumnCount) {
return (
<div
key={rest.key}
style={{
...rest.style,
width: SCROLLBAR_SIZE_BUFFER,
}}
/>
)
} else {
return cellRenderer({
...rest,
columnIndex: columnIndex + fixedColumnCount,
parent: this,
})
}
}
private columnWidthRightGrid = ({index}) => {
const {columnCount, fixedColumnCount, columnWidth} = this.props
const {scrollbarSize, showHorizontalScrollbar} = this.state
// 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 (showHorizontalScrollbar && index === columnCount - fixedColumnCount) {
return scrollbarSize
}
return typeof columnWidth === 'function'
? columnWidth({index: index + fixedColumnCount})
: columnWidth
}
private handleInvalidatedGridSize() {
if (typeof this.deferredInvalidateColumnIndex === 'number') {
const columnIndex = this.deferredInvalidateColumnIndex
const rowIndex = this.deferredInvalidateRowIndex
this.deferredInvalidateColumnIndex = null
this.deferredInvalidateRowIndex = null
this.recomputeGridSize({
columnIndex,
rowIndex,
})
this.forceUpdate()
}
}
private prepareForRender() {
if (
this.lastRenderedColumnWidth !== this.props.columnWidth ||
this.lastRenderedFixedColumnCount !== this.props.fixedColumnCount
) {
this.leftGridWidth = null
}
if (
this.lastRenderedFixedRowCount !== this.props.fixedRowCount ||
this.lastRenderedRowHeight !== this.props.rowHeight
) {
this.topGridHeight = null
}
this.maybeCalculateCachedStyles(false)
this.lastRenderedColumnWidth = this.props.columnWidth
this.lastRenderedFixedColumnCount = this.props.fixedColumnCount
this.lastRenderedFixedRowCount = this.props.fixedRowCount
this.lastRenderedRowHeight = this.props.rowHeight
}
}
export default MultiGrid

View File

@ -0,0 +1,2 @@
import MultiGrid from './MultiGrid'
export {MultiGrid}

View File

@ -4,7 +4,8 @@ import _ from 'lodash'
import classnames from 'classnames'
import {connect} from 'react-redux'
import {MultiGrid, ColumnSizer} from 'react-virtualized'
import {ColumnSizer} from 'react-virtualized'
import {MultiGrid} from 'src/shared/components/MultiGrid'
import {bindActionCreators} from 'redux'
import moment from 'moment'
import {reduce} from 'fast.js'

View File

@ -31,7 +31,7 @@
// Highlight
&:after {
content: '';
content: "";
position: absolute;
top: 0;
left: 0;
@ -80,8 +80,8 @@
padding-right: 17px;
&:before {
font-family: 'icomoon';
content: '\e902';
font-family: "icomoon";
content: "\e902";
font-size: 17px;
position: absolute;
top: 50%;