ES6 Plato on Github
Report Home
Summary Display
components/Logbook/List/EventList.js
Maintainability
63.70
Lines of code
249
Difficulty
46.54
Estimated Errors
2.56
Function weight
By Complexity
By SLOC
import React from 'react'; import PropTypes from 'prop-types' import _ from 'lodash' import Moment from 'moment' import { Table, OverlayTrigger, Tooltip, Glyphicon, Button, Label } from 'react-bootstrap' import { getContent, convertImagesToThumbnails, getPreviousVersionNumber } from '../../../helpers/EventHelpers'; import { getOriginalEvent } from '../../../helpers/EventHelpers' import EventIcon from './EventIcon.js'; import LazyLoad from 'react-lazyload'; import { EVENT_CATEGORY_COMMANDLINE, NOTIFICATION } from '../../../constants/EventTypes.js'; import TagListInLine from '../Tag/TagListInLine'; import LogbookPager from '../LogbookPager'; require("./eventList.css"); /** * The list of the all events */ class EventList extends React.Component { collapse(items) { let collapsed = []; for (let i = 0; i < items.length; i++) { const event = items[i]; if (event.category) { if (event.category.toLowerCase() === EVENT_CATEGORY_COMMANDLINE && !event.previousVersionEvent) { let lastEvent = collapsed[collapsed.length - 1]; if (lastEvent && lastEvent.category) { if (lastEvent.category.toLowerCase() === EVENT_CATEGORY_COMMANDLINE) { if (!lastEvent.events) { lastEvent.events = [event]; } else { lastEvent.events.push(event); } continue; } } else { lastEvent.events = [event]; } } } collapsed.push(event); } return collapsed; } /** Returns the list of items to be displayed in the table: events + days */ getItems() { let eventsCopy = _.cloneDeep(this.props.events); let items = []; let lastDate = null; // format DDMMYYYY for (let i = 0; i < eventsCopy.length; i++) { let date = Moment(getOriginalEvent(eventsCopy[i]).creationDate).format("MMMM Do YYYY"); if (date !== lastDate) { lastDate = date; items.push({ text: date, type: "date", anchor: date }); } items.push(eventsCopy[i]); } /** Add shadow to events that are before a date */ for (let i = 0; i < items.length; i++) { if (items[i].type === "date") { if (items[i - 1]) { items[i - 1].shadowBottomBorder = true; } } } /** Add shadow to the last one */ if (items.length > 0) { items[items.length - 1].shadowBottomBorder = true; } return this.collapse(items); } render() { if (!this.props.events || this.props.events.length === 0) { return null; } return <div> <Table responsive style={{ fontSize: '12px', borderCollapse: 'collapse' }}> <tbody> {this.getItems().map((event, index) => { if (event.type === "date") { return <tr key={index} style={{ backgroundColor: '#F0F0F6', color: "gray", height: '50px' }} ><td style={{ textAlign: 'center', fontSize: '18px', fontWeight: 'bold' }} colSpan={6} ><a name={event.anchor}></a> {event.text}<br /></td></tr>; } return <Event key={index} event={event} logbookContext={this.props.logbookContext} onEventClicked={this.props.onEventClicked} ></Event> })} </tbody> </Table> <div style={{ position: 'relative', height: '45px' }}> <div style={{ position: 'absolute', left: '50%', marginLeft: '-125px', width: '250px' }}> <LogbookPager activePage={this.props.activePage} eventCount={this.props.eventCountBySelectionFilter} onPageClicked={this.props.onPageClicked} /> </div> </div> </div> } } EventList.propTypes = { /** the array of unsorted events as provided by the ICAT+ server */ events: PropTypes.array.isRequired, /** the logbook context */ logbookContext: PropTypes.object, /** Callback function triggered when the user clicks the edit icon */ onEventCliked: PropTypes.func } export default EventList; /** React componnent which renders an event. Here 'event can be the classical event as found in the logbook but * could also be a list of event corresponding to a collapsed line containing several events */ export class Event extends React.Component { constructor(props) { super(props) this.state = { collapsed: true } this.handleClick = this.handleClick.bind(this); } render() { let events = [this.props.event]; if (this.props.event.events && !this.state.collapsed) { events = events.concat(this.props.event.events); }; /** Will print bottom shadow when it is the last event */ var getShadow = (event) => { if (event.shadowBottomBorder) { return '0px 0 15px -15px rgba(100,100,100, 0.8)'; } return ' 0px 0px 9px 0px white, 0 9px 0px 0px white, 0 -9px 0px 0px white, 0px 0 15px -4px rgba(100,100,100, 0.8), 0px 0 15px -4px rgba(100,100,100, 0.8)'; } var getButtonIcon = (event) => { if (this.props.logbookContext.isReleased === true) { return <Glyphicon glyph='eye-open' />; } else { if (getPreviousVersionNumber(event) === 0) { return <div><Glyphicon glyph='comment' style={{ width: '30px' }} />Comment</div>; } return <span><Glyphicon glyph='edit' style={{ width: '30px' }} /> Edit</span>; } } return events.map((event, index) => { return <tr key={index} style={{ backgroundColor: '#F0F0F6' }}> <td style={{ width: '16px', border: 0 }}> <EventIcon event={event} /> </td> <td className='borderTopSeparatorBetweenEvents' style={{ width: '16px' }}> <OverlayTrigger placement="right" overlay={<Tooltip id="tooltip"> <p> Events created on {Moment(getOriginalEvent(event).creationDate).format("MMMM Do YYYY, h:mm:ss a")} </p> </Tooltip>}> <span style={{ cursor: 'pointer', color: 'black', margin: '0px' }}>{Moment(getOriginalEvent(event).creationDate).format("HH:mm:ss")} </span> </OverlayTrigger> </td> <td className='borderTopSeparatorBetweenEvents' style={{ width: '16px' }}> </td> <td className='borderTopSeparatorBetweenEventsInline' style={{ paddingBottom: 0, backgroundColor: 'white', boxShadow: getShadow(event) }}> <div style={{ marginLeft: '20px', backgroundColor: 'white' }}> <LazyContentEvent event={event} /> {event.events && this.state.collapsed ? <Label style={{ color: "blue", backgroundColor: "#F8F8F8", cursor: "pointer" }} onClick={this.handleClick} >.... {event.events.length} command lines more</Label> : null} </div> </td> <td className='borderTopSeparatorBetweenEvents' style={{ width: '200px' }}> <TagListInLine tags={event.tag} /> </td> <td className='borderTopSeparatorBetweenEvents' style={{ width: '50px' }}> <Button bsStyle="default" bsSize="small" style={{ width: '100px', position: 'static', padding: '0px' }} onClick={() => this.props.onEventClicked(event)}> {getButtonIcon(event)} </Button> </td> </tr>; }); } handleClick(e) { this.setState({ collapsed: !this.state.collapsed }); } getUncollapsedEvents() { return (<tbody> {this.props.event.events.map((event, index) => { return this.getEventContentBody(event); })}</tbody>); } } Event.protypes = { /** A classical event or a structure representing a collapsed line containing several similar events */ event: PropTypes.object, /** Context in which the logbook is run */ logbookContext: PropTypes.object, /** Callback function triggered which the user clicks a link to edit/consult the detailed event */ onEventClicked: PropTypes.func, } class LazyContentEvent extends React.Component { getHTMLContent(event) { return getContent(event.content, 'html') ? convertImagesToThumbnails(getContent(event.content, 'html')) : convertImagesToThumbnails(getContent(event.content, 'plainText')); } getClassName(event) { if (event.type.toLowerCase() === "annotation") { return 'text-primary'; } if (event.category.toLowerCase() === "error") { return 'text-danger'; } if (event.category.toLowerCase() === "commandline") { return 'text-muted'; } return 'text-primary'; } render() { let content = this.getHTMLContent(this.props.event) if (content) { content = content.replace(/\<img/g, "<img onClick='makeFullScreen(this)' "); /** For performance only events with images are lazy loaded */ if ((this.props.event.type === NOTIFICATION) && this.props.event.previousVersionEvent) { return <LazyLoad> <div className={this.getClassName(this.props.event)} dangerouslySetInnerHTML={{ __html: this.getHTMLContent(getOriginalEvent(this.props.event)) }} /> <div className={this.getClassName(this.props.event)} dangerouslySetInnerHTML={{ __html: content }} /> </LazyLoad>; } /** For performance only events with images are lazy loaded */ if (content.indexOf("img") !== -1) { return <LazyLoad once> <div className={this.getClassName(this.props.event)} dangerouslySetInnerHTML={{ __html: content }} /> </LazyLoad>; } return <div className={this.getClassName(this.props.event)} dangerouslySetInnerHTML={{ __html: content }} />; } else { return <div style={{ fontStyle: 'italic', color: '#888888' }}> There is no content </div> } } }