UNPKG

labo-components

Version:
223 lines (196 loc) 6.87 kB
import React from 'react'; import PropTypes from 'prop-types'; import IconUtil from '../../util/IconUtil'; import trunc from '../../util/Trunc'; import IDUtil from '../../util/IDUtil'; import RegexUtil from '../../util/RegexUtil'; export default class SearchSnippet extends React.Component { constructor(props) { super(props); this.CLASS_PREFIX = "ss"; } static createMarkup(text) { return { __html: text }; } //NOTE: when needed this function can be extended with an anonymous user check in combination with: //collectionConfig.getAnonymousUserRestrictions().prohibitThumbnails === true renderPosterImage = posterURL => { if (!posterURL) return null; return ( <div className={IDUtil.cssClassName("poster", this.CLASS_PREFIX)}> <img className="media-object" src="/static/images/placeholder.2b77091b.svg" data-src={posterURL} alt="Could not find image" /> </div> ); }; renderMediaIcons = mediaTypes => { if (!mediaTypes) return null; return mediaTypes.map(mt => { let title = null; switch(mt) { case 'video' : title = "Video content"; break; case 'audio' : title = "Audio content"; break; case 'image' : title = "Image content"; break; case 'text' : title = "Textual content"; break; } return ( <span key={'__mi__' + mt} className={IconUtil.getMimeTypeIcon(mt, false, true, false)} title={title}/> ); }); }; renderAccessabilityIcon = isPlayable => { //Note: assigning a media type (to the result data) automatically means it's accessible in the media suite! if (isPlayable) { // Data should be visible by default, so prevent any clutter // by showing for all items they are visible. return null; } else { return ( <span className={IconUtil.getMediaObjectAccessIcon(false, false, true, true, false)} title="Media object(s) not accessible"/> ); } }; renderTags = tags => { if (!tags) return null; return tags.map(t => { return <span>{t}</span>; }); }; renderFragmentIcon = () => { return ( <span className={IconUtil.getMimeTypeIcon("fragment", true, true)} title="Media fragment" /> ); }; renderFragmentSnippet = fragment => { if (!fragment) return null; return ( <div className={IDUtil.cssClassName("fragment", this.CLASS_PREFIX)}> {fragment.snippet} </div> ); }; renderHighlights = (collectionConfig, snippets, highlightRegex) => { if(!snippets) return null; //TODO use collectionConfig.getNumberOfHighlightsToDisplay to cull the amount of snippets shown const markup = Object.keys(snippets).map(field => { return "</br><span class=fieldNameText>" + collectionConfig.toPrettyFieldName(field) + "</span>: &nbsp;" + snippets[field].map(hl => RegexUtil.highlightText(hl, highlightRegex)) }); return <div className={IDUtil.cssClassName("snippet-description", this.CLASS_PREFIX)} dangerouslySetInnerHTML={SearchSnippet.createMarkup(markup)} /> }; fixMsg = s => (s ? s.replace(new RegExp(/\|$/,"g"), "") : null); //possible default fields: posterURL, title, description, tags render() { const poster = this.renderPosterImage(this.props.data.posterURL); const mediaTypes = this.renderMediaIcons(this.props.data.mediaTypes); const accessIcon = this.renderAccessabilityIcon( this.props.data.playable ); const tags = this.renderTags(this.props.data.tags); let fragmentIcon, fragmentSnippet = null; if (this.props.data.type === "media_fragment") { fragmentIcon = this.renderFragmentIcon(); fragmentSnippet = this.renderFragmentSnippet( this.props.data.mediaFragment ); } const classNames = [IDUtil.cssClassName("search-snippet")]; const title = this.props.data.title ? this.props.data.title + " " : ""; const date = this.props.data.date ? this.props.data.date : ""; const subHeading = ( <div> <i>{date}</i> <span>{this.fixMsg(this.props.data.highlightMsg)}</span> </div> ); const highlights = this.renderHighlights( this.props.collectionConfig, this.props.data.matchesPerField, this.props.data.highlightRegex ); return ( <div className={classNames.join(" ")} onClick={this.props.onClick}> {poster} <div className={IDUtil.cssClassName('media-body', this.CLASS_PREFIX)}> {/* Title */} <h3 className={IDUtil.cssClassName('title', this.CLASS_PREFIX)} title={this.props.data.id}> <span dangerouslySetInnerHTML={ SearchSnippet.createMarkup(RegexUtil.highlightText( title ? trunc(title, 200) : "", this.props.searchRegex )) }/> {/* Icons access */} <span className={IDUtil.cssClassName("icon-access", this.CLASS_PREFIX)}>{accessIcon}</span> </h3> {/* Description snippet with possible highlights */} <div className={IDUtil.cssClassName("snippet-description", this.CLASS_PREFIX)} dangerouslySetInnerHTML={SearchSnippet.createMarkup( RegexUtil.highlightText( RegexUtil.findFirstHighlightInText( this.props.data.description, this.props.searchRegex, 35 ), this.props.searchRegex ) )} /> {highlights} {fragmentSnippet} {/* Info heading/icons */} <div className={IDUtil.cssClassName("info", this.CLASS_PREFIX)}> {/* Icons type */} <div className={IDUtil.cssClassName('icon-type', this.CLASS_PREFIX)}> {mediaTypes}{fragmentIcon} </div> {subHeading} </div> {tags} </div> </div> ); } } SearchSnippet.propTypes = { onClick: PropTypes.func.isRequired, // click callback collectionConfig: PropTypes.object.isRequired, searchRegex: PropTypes.instanceOf(RegExp), //the search regex that was used to find this hit (if there is a search term) data: PropTypes.shape({ //all the data required to draw the information of this result snippet (see SearchResult.toSearchResultSnippet()) id: PropTypes.string.isRequired, title: PropTypes.string.isRequired, description: PropTypes.string, date: PropTypes.string, posterURL: PropTypes.string, //for loading a poster image playable: PropTypes.bool, //for drawing the accessability icon mediaTypes: PropTypes.arrayOf(PropTypes.string), //for drawing media type icons tags: PropTypes.array, //for drawing any type of annotations (e.g. based on a the current user) highlightRegex : PropTypes.regexp, matchesPerField: PropTypes.object, //key = field name, value = array with highlights {text : '', regex : ''} highlightMsg: PropTypes.string, //this is only available when the search is configured to return media fragments (inner_hits) mediaFragment: PropTypes.shape({ assetId: PropTypes.string, url: PropTypes.string, start: PropTypes.number, end: PropTypes.number, snippet: PropTypes.string, layer: PropTypes.string }) }).isRequired };