UNPKG

labo-components

Version:
228 lines (197 loc) 9.72 kB
import React from "react"; import PropTypes from 'prop-types'; import IDUtil from '../../util/IDUtil'; import Query from '../../model/Query'; import FlexRouter from '../../util/FlexRouter'; import LocalStorageHandler from '../../util/LocalStorageHandler'; import Loading from '../shared/Loading'; //So this viewer basically fetches the entity based on the provided entityID and //then applies an entity mapping (for that entity type) by fetching it from the collectionConfig export default class QuickEntityViewer extends React.Component { constructor(props) { super(props); this.CLASS_PREFIX = "qev"; this.state = { entityData : {} // per entity type stores the entity data }; } componentDidMount = () => { const entityTypeConfig = this.__getEntityTypeConfig( this.props.collectionConfig, this.props.entityType ); //if the collection config information about which person properties to show is available if(entityTypeConfig && typeof(entityTypeConfig.fetchEntityDetails) === "function") { entityTypeConfig.fetchEntityDetails( this.props.entityID, this.__updateEntityData ) } else { console.error("Missing entity details function for entity type " + this.props.entityType); } }; __updateEntityData = data => { const entityData = this.state.entityData; entityData[this.props.entityType] = data && !data['error'] ? data : null; this.setState({ entityData : entityData }); }; //TODO move this function to the collectionconfig or somewhere else that all compoments can easily use __getEntityTypeConfig = (collectionConfig, entityType) => { const entityConfig = collectionConfig ? collectionConfig.getEntityConfig() : null; if(!entityConfig || !entityConfig[entityType]) return null; return entityConfig[entityType]; }; onSearch = (entityData) => { const entityTypeConfig = this.__getEntityTypeConfig( this.props.collectionConfig, this.props.entityType ); let otherLabels = entityData[this.props.entityType]["OtherLabels"].map(label => label.value); let entity = entityTypeConfig.formatEntityForSearch(this.props.entityID, entityData[this.props.entityType]["Name"][0]['value'], //this is the first prefLabel so should be the GTAA label otherLabels); //create query const query = Query.construct({term: "", entities: [entity], fieldCategory: this.props.fieldCategories}, this.props.collectionConfig); //store the query in the cache LocalStorageHandler.storeJSONInLocalStorage( 'stored-search-results', {query: query} //only the query, the Search page will fill the results ); //open the Search page FlexRouter.gotoSingleSearch('cache'); }; //Note: this function now assumes that the imageUrl can handle the width parameter, which //of course is not standard. For now this is fine, since we only have entity data from wikimedia commons wired up: //https://commons.wikimedia.org/wiki/Special:FilePath/someimage.jpg?width=200px //Should we link entities to other sources, we should adapt this function (and the entityConfig) toBrowserFriendlyImage = imageUrl => imageUrl + '?width=200px'; //TODO render the image much nicer renderEntityImage = (entityData, entityType) => { if(!entityData || !entityData[entityType]) return null; if(!entityData[entityType].ImageUrl || entityData[entityType].ImageUrl.length === 0) return null; let imgSrc = entityData[entityType].ImageUrl[0].value; return ( <tr className="poster"> <td colSpan="2"> <div style={{width: '200px'}}> <picture> <source srcSet={this.toBrowserFriendlyImage(imgSrc)} /> <img src={imgSrc} alt="image of person" style={{width:'100%'}}/> </picture> </div> </td> </tr>); } //TODO render this in a nicer way. renderEntityMetadata = (entityData, entityType) => { if(!entityData || !entityData[entityType]) return null; const details = []; Object.entries(entityData[entityType]).forEach(([propertyName, valueList], i)=> { details.push( <tr className={IDUtil.cssClassName('id', this.CLASS_PREFIX)} key={'props__key__' + i}> <td colSpan="2"><span className="prop-name">{propertyName}</span></td> </tr> ); if(valueList && typeof(valueList) === "object" && valueList.length !== undefined) { valueList.forEach((value, j)=> { let valueText = value['valueLabel'] ? value['valueLabel']: value['value'] if(valueText.startsWith("http")) { details.push( <tr key={'subprops__' + i + '__'+ j}> <td></td><td><a href={valueText} target='_blank'>{valueText}</a></td> </tr> ); } else { details.push( <tr key={'subprops__' + i + '__'+ j}> <td></td><td>{valueText}</td> </tr> ); } }); } }); return details.length > 0 ? details : null; }; renderLoadMessage = () => <tr><td> Loading information... &nbsp; <div className="spinner-border"> <span className="sr-only">Loading...</span> </div> </td></tr>; renderSources = (entityData, entityType) => { if(!entityData || typeof(entityData) !== "object" || !entityData[entityType]) return null; if(!entityData[entityType].Source || entityData[entityType].Source.length === 0) return null; let entityLabel = 'this ' + entityType; if(entityType === 'person' && entityData[entityType]["Name"] && entityData[entityType]["Name"].length > 0) { entityLabel = `${entityData[entityType]["Name"][0]['value']}`; } const details = []; const sourceList = entityData[entityType].Source if(sourceList && typeof(sourceList ) === "object" && sourceList.length !== undefined) { sourceList.forEach((value, j)=> { let sourceLabel = value['valueLabel'] ? value['valueLabel']: value['value'] let source = value['value'] ? value['value']: null details.push( <tr key={'subprops__' + j}> <td><a href={source} target='_blank'>Click here to view {entityLabel} in {sourceLabel}</a></td> </tr> ); }); } return details } renderSearchButton = (entityID, entityData, entityType) => { if(!entityData || typeof(entityData) !== "object" || !entityData[entityType]) return null; let entityLabel = 'this ' + entityType; if(entityType === 'person' && entityData[entityType]["Name"] && entityData[entityType]["Name"].length > 0) { entityLabel = `${entityData[entityType]["Name"][0]['value']}`; } //TODO more entity types return ( <tr> <td colSpan="2"> <div className="actions"> <button className="btn-secondary" onClick={() => {this.onSearch(entityData)}}> Search for {entityLabel} in the current collection </button> </div> </td> </tr> ); } render() { //render the image of the person if available const personImage = this.renderEntityImage(this.state.entityData, this.props.entityType); const links = this.renderSources(this.state.entityData, this.props.entityType); const sourceLinks = links ? links : this.renderLoadMessage(); //for each property in the data, display the label in a row, with //the values in the rows below //Entity metadata is excluded at present as the current thinking is to link through to the external source //const entityMetadata = this.renderEntityMetadata(this.state.entityData, this.props.entityType); //const tableData = entityMetadata ? entityMetadata : this.renderLoadMessage(); const searchButton = this.renderSearchButton(this.props.entityID, this.state.entityData, this.props.entityType) //build a table with all the parts of the person data const classNames = ['table', IDUtil.cssClassName('metadata-table')]; return ( <div className={IDUtil.cssClassName('quick-entity-viewer')}> <table className={classNames.join(' ')}> <tbody> {searchButton} {personImage} {sourceLinks} </tbody> </table> </div> ); } } QuickEntityViewer.propTypes = { entityID : PropTypes.string.isRequired, entityType : PropTypes.string.isRequired, collectionConfig : PropTypes.object.isRequired, fieldCategories: PropTypes.array.isRequired, //which field clusters/categories are selected };