labo-components
Version:
228 lines (197 loc) • 9.72 kB
JSX
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...
<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
};