labo-components
Version:
190 lines (164 loc) • 6.1 kB
JSX
import AnnotationUtil from '../../../util/AnnotationUtil';
import IDUtil from '../../../util/IDUtil';
import PropTypes from 'prop-types';
/**
* Display a bookmark/annotation result list and handle the filtering and sorting
*/
class NestedTable extends React.PureComponent {
constructor(props) {
super(props);
// retrieve persistent filters from localstorage
this.filterKey = props.uid + '-filter';
const filter = { keywords:''}
this.state = {
filteredItems: [],
visibleItems: [],
loading: true,
order: 'created',
filter,
};
}
//load and filter data
reloadData() {
// filter
const filtered = this.props.filterItems(
this.props.items,
this.state.filter
);
// sort
const sorted = this.props.sortItems(filtered, this.state.order);
// update state
this.setState({
filteredItems: filtered,
visibleItems: sorted
});
}
setSort(field) {
this.setState({
order: field,
// filter list from original items to keep sort list consistent
visibleItems: this.props.sortItems(this.state.filteredItems, field)
});
}
//Listen for update, request new data if filter has been changed
componentDidUpdate(prevProps, prevState) {
//listen for items change
if (prevProps.items != this.props.items) {
this.reloadData();
return;
}
// listen for filter change
if (this.lastFilter !== this.state.filter) {
this.lastFilter = this.state.filter;
// throttle data requests
if (this.requestDataTimeout) {
clearTimeout(this.requestDataTimeout);
this.requestDataTimeout = setTimeout(this.reloadData.bind(this), 300);
} else {
// firstrun
this.reloadData();
}
}
}
// user changes a filter
filterChange(key, e) {
let filter = {};
filter[key] = e.target.value;
// create filter
filter = Object.assign({}, this.state.filter, filter);
// update state
this.setState({
filter
});
}
//when the sort type changes
sortChange(e) {
this.setSort(e.target.value);
}
// render filters
renderFilters(filters){
return filters.map((filter, index)=>{
switch(filter.type){
case 'search':
return(<div className="filter-container">
<input
key={index}
className="search"
type="text"
placeholder={filter.placeholder || "Search"}
value={this.state.filter[filter.key]}
onChange={this.filterChange.bind(this, filter.key)}
/>
</div>)
break;
case 'select':
return (<div className="filter-container">
<span key={index}>
<label className="type-label" title={filter.titleAttr ? filter.titleAttr : null}>{filter.title}</label>
<select
disabled={filter.options.length == 0}
className="type-select"
value={this.state.filter[filter.key]}
onChange={this.filterChange.bind(this, filter.key)}>
<option />
{filter.options.map((option, index) => (
<option key={index} value={option.value} disabled={option.disabled}>
{option.name}
</option>
))}
</select>
</span>
</div>)
break;
default:
console.error("Unknown filter type", filter);
}
return null
});
}
render() {
return (
<div className={IDUtil.cssClassName('nested-table')}>
<div className="tools">
{/*<div className="export-button btn primary"
onClick={this.props.onExport.bind(this, this.state.visibleItems)}>
Export all
</div>*/}
<div className="left">
<h3>Filters</h3>
{this.renderFilters(this.props.filters)}
</div>
</div>
<div className="results">
<div className="sort">
<h3>Order</h3>
<select
value={this.state.order}
onChange={this.sortChange.bind(this)}>
{this.props.orders.map((type, index) => (
<option key={index} value={type.value}>
{type.name}
</option>
))}
</select>
</div>
{this.props.renderResults(this.state)}
</div>
</div>
)
}
}
NestedTable.propTypes = {
filterItems: PropTypes.func.isRequired,
filters: PropTypes.object,
items: PropTypes.array.isRequired,
onExport: PropTypes.func.isRequired,
orders: PropTypes.array.isRequired,
renderResults: PropTypes.func.isRequired,
selection: PropTypes.array,
sortItems: PropTypes.func.isRequired
};
NestedTable.defaultProps = {
filters: {}
};
export default NestedTable;