diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx index 7673d7f..a107f55 100644 --- a/src/components/Timeline.jsx +++ b/src/components/Timeline.jsx @@ -19,6 +19,7 @@ class Timeline extends React.Component { constructor(props) { super(props); this.styleDatetime = this.styleDatetime.bind(this) + this.getDatetimeX = this.getDatetimeX.bind(this) this.svgRef = React.createRef() this.state = { isFolded: false, @@ -82,22 +83,6 @@ class Timeline extends React.Component { } } - /** - * Get x position of eventPoint, considering the time scale - * @param {object} eventPoint: regular eventPoint data - */ - getEventX(eventPoint) { - return this.state.scaleX(parseDate(eventPoint.timestamp)); - } - - /** - * Get y height of eventPoint, considering the ordinal Y scale - * @param {object} eventPoint: regular eventPoint data - */ - getEventY(eventPoint) { - return this.state.scaleY(eventPoint.category); - } - /** * Returns the time scale (x) extent in minutes */ @@ -211,8 +196,16 @@ class Timeline extends React.Component { this.props.methods.onUpdateTimerange(this.state.timerange); } + getDatetimeX(dt) { + return this.state.scaleX(parseDate(dt.timestamp)) + } + /** - * Determines additional styles on the for each location. + * Determines additional styles on the for each timestamp. Note that + * timestamp visualisation functions slightly differently from locations, as + * a timestamp can be shown as multiple s (one per category of the + * events contained therein). Thus the function below has a category as an + * argumnent as well, in case timestamps ought to be styled per category. * A datetime consists of an array of events (see selectors). The function * also has full access to the domain and redux state to derive values if * necessary. The function should return an array, where the value at the @@ -220,7 +213,7 @@ class Timeline extends React.Component { * at the second index is an optional function that renders additional * components in the div. */ - styleDatetime(timestamp) { + styleDatetime(timestamp, category) { return [] } @@ -285,8 +278,8 @@ class Timeline extends React.Component { datetimes={this.props.domain.datetimes} styleDatetime={this.styleDatetime} narrative={this.props.app.narrative} - getDatetimeX={(e) => this.getEventX(e)} - getDatetimeY={(e) => this.getEventY(e)} + getDatetimeX={this.getDatetimeX} + getCategoryY={this.state.scaleY} getCategoryColor={this.props.methods.getCategoryColor} transitionDuration={this.state.transitionDuration} onSelect={this.props.methods.onSelect} diff --git a/src/components/presentational/DatetimeDot.js b/src/components/presentational/DatetimeDot.js new file mode 100644 index 0000000..3167821 --- /dev/null +++ b/src/components/presentational/DatetimeDot.js @@ -0,0 +1,27 @@ +import React from 'react' + +export default ({ + category, + events, + x, + y, + styleProps, + extraRender +}) => ( + onSelect(datetime.events)} + > + + + { extraRender ? extraRender() : null } + +) + diff --git a/src/components/presentational/TimelineEvents.js b/src/components/presentational/TimelineEvents.js index 32f7448..17966ed 100644 --- a/src/components/presentational/TimelineEvents.js +++ b/src/components/presentational/TimelineEvents.js @@ -1,27 +1,38 @@ import React from 'react'; +import DatetimeDot from './DatetimeDot' + +// return a list of lists, where each list corresponds to a single category +function getDotsToRender(events) { + // each datetime needs to render as many dots as there are distinct + // categories in the events contained by the datetime. + // To this end, eventsByCategory is an intermediate data structure that + // groups a datetime's events by distinct categories + const eventsByCategory = {} + events.forEach(ev => { + if (eventsByCategory[ev.category]) { + eventsByCategory[ev.category].events.push((ev)) + } else { + eventsByCategory[ev.category] = { + category: ev.category, + events: [ ev ] + } + } + }) + + return Object.values(eventsByCategory) +} const TimelineEvents = ({ datetimes, narrative, getDatetimeX, - getDatetimeY, + getCategoryY, getCategoryColor, onSelect, transitionDuration, styleDatetime }) => { function renderDatetime(datetime) { - const customStyles = styleDatetime ? styleDatetime(datetime) : null - const extraStyles = customStyles[0] - const extraRender = customStyles[1] - - const styleProps = ({ - fill: getCategoryColor(datetime.events[0].category), - fillOpacity: 1, - transition: `transform ${transitionDuration / 1000}s ease`, - ...extraStyles - }); - if (narrative) { const { steps } = narrative const isInNarrative = steps.map(s => s.id).includes(event.id) @@ -31,23 +42,31 @@ const TimelineEvents = ({ } } - return ( - onSelect(datetime.events)} - > - - - { extraRender ? extraRender() : null } - - ) + const dotsToRender = getDotsToRender(datetime.events) + + return dotsToRender.map(dot => { + const customStyles = styleDatetime ? styleDatetime(datetime, dot.category) : null + const extraStyles = customStyles[0] + const extraRender = customStyles[1] + + const styleProps = ({ + fill: getCategoryColor(dot.category), + fillOpacity: 1, + transition: `transform ${transitionDuration / 1000}s ease`, + ...extraStyles + }) + + return ( + + ) + }) } return (