labo-components
Version:
235 lines (205 loc) • 11.1 kB
JSX
import IDUtil from '../../util/IDUtil';
import ComponentUtil from '../../util/ComponentUtil';
import ElasticsearchDataUtil from '../../util/ElasticsearchDataUtil';
import classNames from 'classnames';
import PropTypes from 'prop-types';
//this component relies on the collection statistics as input
class FieldSelector extends React.Component {
constructor(props) {
super(props);
this.state = {
sortField: 'title',
sortOrder: 'down',
filters: {
keyword: '',
}
}
}
onSort(sortField){
this.setState({
sortField,
sortOrder: this.state.sortField === sortField ? (this.state.sortOrder === 'up' ? 'down' : 'up') : this.state.sortOrder,
})
}
// '~'' --> high ASCII, on end of sorting
getStringDescriptionOrEmpty(v) {
return v ? v.description || '~' : '~';
}
sortFields(fields) {
// default to id first; to prevent unwanted shuffling
fields = fields.sort((a,b)=>(a.id > b.id ? -1 : 1));
if(this.state.sortField === 'title') {
fields = fields.sort((a,b)=>(a.title > b.title ? -1 : 1));
} else if(this.state.sortField === 'description' && this.props.descriptions) {
fields = fields.sort((a,b) => {
const s1 = this.getStringDescriptionOrEmpty(this.props.descriptions[a.id]);
const s2 = this.getStringDescriptionOrEmpty(this.props.descriptions[b.id]);
return s1 > s2 ? -1 : 1
});
} else if(this.state.sortField === 'type') {
fields = fields.sort((a,b)=>(a.type > b.type ? -1 : 1));
} else if(this.state.sortField === 'level') {
fields = fields.sort((a,b)=>(a.level > b.level ? -1 : 1));
} else if(this.state.sortField === 'completeness') {
fields = fields.sort((a,b)=>(this.props.completeness['existCounts'][a.id] < this.props.completeness['existCounts'][b.id] ? -1 : 1));
}
switch(this.state.sortOrder){
case 'desc':
return fields.reverse();
default:
return fields;
}
}
filterFields(fields) {
if (this.state.filters.keyword){
const keywords = this.state.filters.keyword.toLowerCase().split(' ');
keywords.forEach((keyword)=>{
fields = fields.filter((field)=>(
field.title.toLowerCase().includes(keyword) ||
(field.level && field.level.toLowerCase().includes(keyword)) ||
(
field.id in this.props.descriptions &&
this.props.descriptions[field.id].description &&
this.props.descriptions[field.id].description.toLowerCase().includes(keyword)
) ||
(field.type && field.type.toLowerCase().includes(keyword))
));
});
}
return fields;
}
onKeywordFilter(e) {
this.setState({
filters: Object.assign({},this.state.filters, {
keyword: e.target.value
})
})
}
onSelect(field) {
this.props.onSelect(field);
}
onClose() {
this.props.onClose();
}
render() {
const fields = this.sortFields(this.filterFields(this.props.fields));
const sortArrow = <i className={"fas fa-sort-"+this.state.sortOrder}/>;
let levelTableHead = null;
if(this.props.showLevelColumn) {
levelTableHead = (
<th onClick={()=>{this.onSort('level')}}
className={classNames('level',{active:this.state.sortField === 'level'})}>
Level
{this.state.sortField === 'level' ? sortArrow : null}
</th>
)
}
return (
<div className={IDUtil.cssClassName('field-selector')}>
<div className="container">
<div className="close" onClick={this.onClose.bind(this)}>Close ❌</div>
<div className="row">
<div className="col-md-12">
<form>
<div className="input-group">
<input type="text"
className="form-control"
placeholder="Search fields"
onChange={this.onKeywordFilter.bind(this)}
value={this.state.filters.keywords}
/>
<span className="input-group-addon btn-effect"><i className="fas fa-search"/></span>
</div>
</form>
<table>
<thead>
<tr>
<th onClick={()=>{this.onSort('title')}}
className={classNames('title', { active:this.state.sortField === 'title'})}>
Field
{this.state.sortField === 'title' ? sortArrow : null}
</th>
{levelTableHead}
<th onClick={()=>{this.onSort('description')}}
className={classNames('description',{active:this.state.sortField === 'description'})}>
Description
{this.state.sortField === 'description' ? sortArrow : null}
</th>
<th onClick={()=>{this.onSort('type')}}
className={classNames('type',{active:this.state.sortField === 'type'})}>
Type
{this.state.sortField === 'type' ? sortArrow : null}
</th>
<th onClick={()=>{this.onSort('completeness')}}
className={classNames('completeness', {active:this.state.sortField === 'completeness'})}>
Completeness
{this.state.sortField === 'completeness' ? sortArrow : null}
</th>
<th className="select"/>
</tr>
</thead>
<tbody>
{fields.map((field)=>(
<tr className={classNames({current: this.props.selectedField === field.id})}>
<td className="title" title={field.id}>
{field.title}
</td>
{this.props.showLevelColumn ? <td className="level" title={field.level}>
{field.level}
</td> : null }
<td className="description">
{this.props.descriptions ?
(this.props.descriptions[field.id] !== undefined ?
<span>{this.props.descriptions[field.id].description || '-'}</span>
: '-')
: <i className="fas fa-circle-notch fa-spin"/>
}
</td>
<td className="type">
{field.type || '?'}
</td>
<td className="completeness">
{this.props.completeness ?
(this.props.completeness['existCounts'][field.id] !== undefined ?
<div>
<span>{
ComponentUtil.calcCompleteness(
this.props.completeness['existCounts'][field.id],
this.props.completeness['total']
)
}%</span>
<br/>
<span className="total">
{this.props.completeness['existCounts'][field.id]} / {this.props.completeness['total']}
</span>
</div> : '-')
: <i className="fas fa-circle-notch fa-spin"/>
}
</td>
<td className="select">
<button className="btn btn-primary"
onClick={this.onSelect.bind(this,field)}>
Select
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
)
}
};
FieldSelector.propTypes = {
fields : PropTypes.array.isRequired, //list of available fields
onClose : PropTypes.func.isRequired, //when the user closes this component from view
onSelect : PropTypes.func.isRequired, //when the user selects a field to be analysed
completeness : PropTypes.object, //completeness information per field
descriptions : PropTypes.object, //list of field descriptions, helping the user to understand what each field is for
showLevelColumn : PropTypes.bool, //whether or not to show an extra column with metadata hierarchy information
selectedField : PropTypes.string, //ID/name of the currently selected field
};
export default FieldSelector;