diff --git a/src/components/lazy-rendered-list.jsx b/src/components/lazy-rendered-list.jsx new file mode 100644 index 000000000..29ca468de --- /dev/null +++ b/src/components/lazy-rendered-list.jsx @@ -0,0 +1,83 @@ +import React, {Component, PropTypes} from 'react' +import {findDOMNode} from 'react-dom' + + +const MIN_RANGE_SIZE = 2 + +function getRange({total, itemHeight, containerHeight, scrollTop}) { + const itemsPerBody = Math.floor((containerHeight) / itemHeight); + const start = Math.max(0, Math.floor(scrollTop / itemHeight) - (itemsPerBody * 2)); + const end = Math.max(MIN_RANGE_SIZE, Math.min(start + (4 * itemsPerBody), total)); + return {start, end} +} + +class LazyRenderedList extends Component { + static propTypes = { + items: PropTypes.array, + itemHeight: PropTypes.number, + containerHeight: PropTypes.number, + BufferTag: PropTypes.string, + ItemRenderer: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + RootRenderer: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + } + + static defaultProps = { + itemHeight: 30, + containerHeight: 150, + BufferTag: 'div', + } + + constructor(props) { + super(props) + this.state = {start: 0, end: MIN_RANGE_SIZE} + } + + componentWillReceiveProps(nextProps) { + this.updateRangeState(nextProps) + } + + onScroll() { + this.updateRangeState(this.props) + } + + updateRangeState({itemHeight, items, containerHeight}) { + const {scrollTop} = findDOMNode(this) + this.setState(getRange({total: items.length, itemHeight, containerHeight, scrollTop})) + } + + renderItems() { + const {items, itemHeight, BufferTag, ItemRenderer} = this.props + const {start, end} = this.state + const topHeight = start * itemHeight + const bottomHeight = (items.length - end) * itemHeight + + const top = + const bottom = + const elements = items.slice(start, end).map((item, idx) => ( + + )) + elements.unshift(top) + elements.push(bottom) + + return elements + } + + render() { + const {RootRenderer, containerHeight} = this.props + return ( + + {this.renderItems()} + + ) + } +} + + +export default LazyRenderedList diff --git a/src/components/selectable-table.jsx b/src/components/selectable-table.jsx index 0282ba275..cc809da85 100644 --- a/src/components/selectable-table.jsx +++ b/src/components/selectable-table.jsx @@ -13,8 +13,8 @@ export class SelectableCell extends Component { static propTypes = { className: PropTypes.string, tableData: Table.propTypes.tableData, - rowIdx: TableCell.propTypes.rowIdx, - colIdx: TableCell.propTypes.colIdx, + rowIdx: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + colIdx: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), selection: PropTypes.object, onSetSelection: PropTypes.func.isRequired, } diff --git a/src/components/table.jsx b/src/components/table.jsx index 50c8d25ba..5574179b8 100644 --- a/src/components/table.jsx +++ b/src/components/table.jsx @@ -1,47 +1,28 @@ import _ from 'underscore' import classnames from 'classnames' import React, {Component, PropTypes} from 'react' +import LazyRenderedList from './lazy-rendered-list' -// TODO Ugh gross. Use flow -const RowDataType = PropTypes.arrayOf(PropTypes.node) const RendererType = PropTypes.oneOfType([PropTypes.func, PropTypes.string]) const IndexType = PropTypes.oneOfType([PropTypes.number, PropTypes.string]) const TablePropTypes = { idx: IndexType, renderer: RendererType, tableData: PropTypes.shape({ - rows: PropTypes.arrayOf(RowDataType), + rows: PropTypes.array, }), } - -export class TableCell extends Component { - - static propTypes = { - className: PropTypes.string, - isHeader: PropTypes.bool, - tableData: TablePropTypes.tableData.isRequired, - rowIdx: TablePropTypes.idx.isRequired, - colIdx: TablePropTypes.idx.isRequired, - } - - static defaultProps = { - className: '', - } - - render() { - const {className, isHeader, children, ...props} = this.props - const CellTag = isHeader ? 'th' : 'td' - return ( - - {children} - - ) - } +export function TableCell({className = '', isHeader, children, ...props}) { + const CellTag = isHeader ? 'th' : 'td' + return ( + + {children} + + ) } - export class TableRow extends Component { static propTypes = { @@ -102,6 +83,8 @@ export default class Table extends Component { className: PropTypes.string, displayHeader: PropTypes.bool, displayNumbers: PropTypes.bool, + rowHeight: PropTypes.number, + bodyHeight: PropTypes.number, tableData: TablePropTypes.tableData.isRequired, extraProps: PropTypes.object, RowRenderer: TablePropTypes.renderer, @@ -116,10 +99,10 @@ export default class Table extends Component { } renderBody() { - const {tableData, displayNumbers, displayHeader, extraProps, RowRenderer, CellRenderer} = this.props + const {tableData, rowHeight, bodyHeight, displayNumbers, displayHeader, extraProps, RowRenderer, CellRenderer} = this.props const rows = displayHeader ? tableData.rows.slice(1) : tableData.rows - const rowElements = rows.map((row, idx) => { + const itemRenderer = ({idx}) => { const rowIdx = displayHeader ? idx + 1 : idx; return ( ) - }) + } return ( - - {rowElements} - + ) } @@ -161,10 +149,10 @@ export default class Table extends Component { } render() { - const {className} = this.props + const {className, ...otherProps} = this.props return ( -
+
{this.renderHeader()} {this.renderBody()} diff --git a/src/pro b/src/pro index 91b58e912..a79583931 160000 --- a/src/pro +++ b/src/pro @@ -1 +1 @@ -Subproject commit 91b58e912a7f2d9d68632eb2b434ba185af352ba +Subproject commit a795839311c962f5e84fe29b753e91a1949f6999 diff --git a/static/components/table.less b/static/components/table.less index a0cbdd89c..036ae577a 100644 --- a/static/components/table.less +++ b/static/components/table.less @@ -7,7 +7,12 @@ .nylas-table { height: 100%; width: 100%; - overflow: scroll; + overflow-y: hidden; + overflow-x: scroll; + + thead, tbody { + display: block; + } .table-row {