UNPKG

labo-components

Version:
235 lines (205 loc) 11.1 kB
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;