fixed-data-table-one.com
Version:
A React table component designed to allow presenting thousands of rows of data.
199 lines (166 loc) • 4.61 kB
JavaScript
"use strict";
import React from 'react';
import PropTypes from 'prop-types';
import except from 'except';
import examplePropTypes from './examplePropTypes';
function DataCtxt(Wrapped) {
class ContextClass extends React.Component {
constructor(props) {
super(props);
this.refresh = this.refresh.bind(this);
const data = this.props.data;
data.setCallback(this.refresh, 'data');
this.state = {
data: props.data,
version: 0,
};
}
getChildContext() {
return {
data: this.state.data,
version: this.state.version,
};
}
componentWillReceiveProps(nextProps) {
if (JSON.stringify(nextProps.data) !== JSON.stringify(this.state.data)) {
this.setState({
data: nextProps.data,
});
}
}
// Force a refresh or the page doesn't re-render
//
// The name of the state variable is irrelevant, it will simply trigger
// an update event that is propagated into the cells
refresh() {
this.setState({
version: this.state.version + 1,
});
}
render() {
const other = except(this.props, Object.keys(ContextClass.propTypes));
return (
<Wrapped
rowsCount={this.state.data.getSize()}
{...other}
/>);
}
}
ContextClass.childContextTypes = {
data: examplePropTypes.CtxtDataListStore,
version: PropTypes.number,
};
ContextClass.propTypes = {
data: examplePropTypes.CtxtDataListStore,
};
return ContextClass;
}
class DataListWrapper {
constructor(data, index = null) {
this._data = data;
this._indexMap = index;
}
// The callback is used for triggering re-rendering
setCallback(cb, id = 'wrapper') {
if (this._data.setCallback) {
this._data.setCallback(cb, id);
}
}
getSize() {
if (this._indexMap === null) {
return this._data.getSize();
}
return this._indexMap.length;
}
getObjectAt(index) {
if (this._indexMap === null) {
return this._data.getObjectAt(index);
}
return this._data.getObjectAt(
this._indexMap[index]
);
}
}
function AddFilter(TableComponent) {
class FilterTable extends React.Component {
constructor(props) {
super(props);
this.refresh = this.refresh.bind(this);
this.state = {
version: 0,
};
}
refresh() {
this.setState({
version: this.state.version + 1,
});
}
_getDataWrapper(indexMap = null) {
const filteredData = new DataListWrapper(this.props.data, indexMap);
filteredData.setCallback(this.refresh, 'filter');
return filteredData;
}
filter() {
// Get and prep filters
const filters = {};
Object
.keys(this.props.filters)
.filter(key => this.props.filters[key].length > 0)
.forEach((key) => {
filters[key] = this.props.filters[key].toLowerCase();
return (null);
});
const match = (haystack, needle) =>
haystack.toLowerCase().indexOf(needle) !== -1;
let filteredIndexes = null;
if (Object.keys(filters).length > 0) {
filteredIndexes = [];
for (let index = 0; index < this.props.data.getSize(); index += 1) {
const row = this.props.data.getObjectAt(index);
// If the object is null it may be loading and should therefore be kept
if (row === null) {
filteredIndexes.push(index);
continue;
}
// Loop through all the filters and check if there's a match
let found = true;
const keys = Object.keys(filters);
for (const key of keys) {
const value = row[key];
if (!match(value, filters[key])) {
found = false;
break;
}
}
if (found) {
filteredIndexes.push(index);
}
}
}
return (this._getDataWrapper(filteredIndexes));
}
render() {
const other = except(this.props, Object.keys(FilterTable.propTypes));
const filteredData = this.filter();
return (
<TableComponent
data={filteredData}
{...other}
>
{this.props.children}
</TableComponent>
);
}
}
FilterTable.propTypes = {
data: examplePropTypes.CtxtDataListStore,
children: PropTypes.node,
filters: examplePropTypes.FilterObject
};
return FilterTable;
}
// Export both HOC and the PropType for the data if required
export {
DataCtxt,
AddFilter,
};