Skip to content

Instantly share code, notes, and snippets.

@bvaughn
Created May 14, 2016 20:20
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bvaughn/3a358dda3654e1e93fba35890a093c19 to your computer and use it in GitHub Desktop.
Save bvaughn/3a358dda3654e1e93fba35890a093c19 to your computer and use it in GitHub Desktop.
Example Grid with columns and rows, built using :cellRangeRenderer (with react-virtualized 7.x)
/** @flow */
import Immutable from 'immutable'
import React, { Component, PropTypes } from 'react'
import { ContentBox, ContentBoxHeader, ContentBoxParagraph } from '../demo/ContentBox'
import { LabeledInput, InputRow } from '../demo/LabeledInput'
import AutoSizer from '../AutoSizer'
import Grid from './Grid'
import shallowCompare from 'react-addons-shallow-compare'
import cn from 'classnames'
import styles from './Grid.example.css'
const FIXED_CELL_ZINDEX = 2
const FIXED_LEFT_COLUMN_WIDTH = 50
const FIXED_TOP_ROW_HEIGHT = 40
export default class GridExample extends Component {
static propTypes = {
list: PropTypes.instanceOf(Immutable.List).isRequired
}
constructor (props, context) {
super(props, context)
this.state = {
columnWidth: 100,
columnCount: 1000,
height: 300,
rowHeight: 40,
rowCount: 1000,
scrollToColumn: undefined,
scrollToRow: undefined,
useDynamicRowHeight: false
}
this._cellRangeRenderer = this._cellRangeRenderer.bind(this)
this._cellRenderer = this._cellRenderer.bind(this)
this._getColumnWidth = this._getColumnWidth.bind(this)
this._getRowClassName = this._getRowClassName.bind(this)
this._getRowHeight = this._getRowHeight.bind(this)
}
render () {
const {
columnCount,
height,
rowHeight,
rowCount,
scrollToColumn,
scrollToRow,
useDynamicRowHeight
} = this.state
return (
<ContentBox {...this.props}>
<ContentBoxHeader
text='Grid'
sourceLink='https://github.com/bvaughn/react-virtualized/blob/master/source/Grid/Grid.example.js'
docsLink='https://github.com/bvaughn/react-virtualized/blob/master/docs/Grid.md'
/>
<AutoSizer disableHeight>
{({ width }) => (
<Grid
cellRangeRenderer={this._cellRangeRenderer}
cellRenderer={this._cellRenderer}
className={styles.BodyGrid}
columnWidth={this._getColumnWidth}
columnCount={columnCount}
height={height}
overscanColumnCount={0}
overscanRowCount={0}
rowHeight={useDynamicRowHeight ? this._getRowHeight : rowHeight}
rowCount={rowCount}
scrollToColumn={scrollToColumn}
scrollToRow={scrollToRow}
width={width}
/>
)}
</AutoSizer>
</ContentBox>
)
}
shouldComponentUpdate (nextProps, nextState) {
return shallowCompare(this, nextProps, nextState)
}
// Forked from defaultCellRangeRenderer.js
_cellRangeRenderer ({
cellCache,
cellRenderer,
columnSizeAndPositionManager,
columnStartIndex,
columnStopIndex,
isScrolling,
rowSizeAndPositionManager,
rowStartIndex,
rowStopIndex,
scrollLeft,
scrollTop
}) {
const renderedCells = []
// Top-left corner piece
renderedCells.push(
<div
key='fixed-fixed'
className={cn('Grid__cell', styles.cell, styles.topLeftCell)}
style={{
height: FIXED_TOP_ROW_HEIGHT,
left: scrollLeft,
position: 'fixed',
top: scrollTop,
width: FIXED_LEFT_COLUMN_WIDTH,
zIndex: FIXED_CELL_ZINDEX + 1
}}
>
&nbsp;
</div>
)
// Render fixed header row
for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) {
let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex)
renderedCells.push(
<div
key={`fixed-${columnIndex}`}
className={cn('Grid__cell', styles.cell, styles.headerCell)}
style={{
height: FIXED_TOP_ROW_HEIGHT,
left: columnDatum.offset + FIXED_LEFT_COLUMN_WIDTH,
position: 'fixed',
top: scrollTop,
width: columnDatum.size,
zIndex: FIXED_CELL_ZINDEX
}}
>
H{columnIndex}
</div>
)
}
// Render fixed left column
for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) {
let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex)
let datum = this._getDatum(rowIndex)
renderedCells.push(
<div
key={`${rowIndex}-fixed`}
className={cn('Grid__cell', styles.cell, styles.letterCell)}
style={{
backgroundColor: datum.color,
height: rowDatum.size,
left: scrollLeft,
position: 'fixed',
top: rowDatum.offset + FIXED_TOP_ROW_HEIGHT,
width: FIXED_LEFT_COLUMN_WIDTH,
zIndex: FIXED_CELL_ZINDEX
}}
>
{datum.name.charAt(0)}
</div>
)
}
// Forked from defaultCellRangeRenderer.js
for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) {
let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex)
for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) {
let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex)
let key = `${rowIndex}-${columnIndex}`
let renderedCell
// Avoid re-creating cells while scrolling.
// This can lead to the same cell being created many times and can cause performance issues for "heavy" cells.
// If a scroll is in progress- cache and reuse cells.
// This cache will be thrown away once scrolling complets.
if (isScrolling) {
if (!cellCache[key]) {
cellCache[key] = cellRenderer({
columnIndex,
isScrolling,
rowIndex
})
}
renderedCell = cellCache[key]
// If the user is no longer scrolling, don't cache cells.
// This makes dynamic cell content difficult for users and would also lead to a heavier memory footprint.
} else {
renderedCell = cellRenderer({
columnIndex,
isScrolling,
rowIndex
})
}
if (renderedCell == null || renderedCell === false) {
continue
}
let child = (
<div
key={key}
className='Grid__cell'
style={{
height: rowDatum.size,
left: columnDatum.offset + FIXED_LEFT_COLUMN_WIDTH,
position: 'fixed',
top: rowDatum.offset + FIXED_TOP_ROW_HEIGHT,
width: columnDatum.size
}}
>
{renderedCell}
</div>
)
renderedCells.push(child)
}
}
return renderedCells
}
_cellRenderer ({ columnIndex, rowIndex }) {
const rowClass = this._getRowClassName(rowIndex)
const datum = this._getDatum(rowIndex)
let content
switch (columnIndex) {
case 0:
content = datum.name
break
case 1:
content = datum.random
break
default:
content = (
<div>
c:{columnIndex}
<br />
r:{rowIndex}
</div>
)
break
}
const classNames = cn(rowClass, styles.cell, {
[styles.centeredCell]: columnIndex > 2
})
return (
<div className={classNames}>
{content}
</div>
)
}
_getColumnWidth ({ index }) {
switch (index) {
case 0:
return 100
case 1:
return 300
default:
return 50
}
}
_getDatum (index) {
const { list } = this.props
return list.get(index % list.size)
}
_getRowClassName (row) {
return row % 2 === 0 ? styles.evenRow : styles.oddRow
}
_getRowHeight ({ index }) {
return this._getDatum(index).size
}
}
@bvaughn
Copy link
Author

bvaughn commented Sep 19, 2016

Hey @vinayaknagpal. Any chance you'd be willing to share a Gist or Plunker of your react-virtualized + zynga-scroller-es6 combination? 😄

@vinayaknagpal
Copy link

@bvaughn
Sure. Will put together an example and share in a few days. Racing a release deadline at the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment