From 3641756539ea1fbdb7b6ca2610217c6d9139cc1b Mon Sep 17 00:00:00 2001 From: Lachlan Kermode Date: Thu, 14 May 2020 15:02:14 +0200 Subject: [PATCH] WIP: overridden colours and shapes --- src/actions/index.js | 2 +- src/common/global.js | 1 + src/common/utilities.js | 6 +- src/components/Layout.js | 7 +- src/components/Timeline.jsx | 7 +- src/components/TimelineAxis.jsx | 2 +- src/components/presentational/Map/Events.jsx | 4 +- .../presentational/Timeline/Events.js | 93 ++++++++++++------- .../presentational/Timeline/Markers.js | 25 +++-- src/reducers/schema/eventSchema.js | 4 +- src/selectors/index.js | 24 ----- src/store/initial.js | 6 +- 12 files changed, 101 insertions(+), 80 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index 6526f5b..438584d 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -35,7 +35,7 @@ export function fetchDomain () { .catch(() => handleError(domainMsg('categories'))) let narPromise = Promise.resolve([]) - if (process.env.features.USE_CATEGORIES) { + if (process.env.features.USE_NARRATIVES) { narPromise = fetch(NARRATIVE_URL) .then(response => response.json()) .catch(() => handleError(domainMsg('narratives'))) diff --git a/src/common/global.js b/src/common/global.js index e6d1991..f6a2800 100644 --- a/src/common/global.js +++ b/src/common/global.js @@ -10,6 +10,7 @@ export const sizes = { } export default { + fallbackEventColor: colors.fa_red, darkBackground: colors.black, primaryHighlight: colors.yellow, secondaryHighlight: colors.white, diff --git a/src/common/utilities.js b/src/common/utilities.js index f300806..7a59a9e 100644 --- a/src/common/utilities.js +++ b/src/common/utilities.js @@ -175,13 +175,13 @@ export function selectTypeFromPathWithPoster (path, poster) { return { type: typeForPath(path), path, poster } } -export function getEventOpacity (events) { +export function calcOpacity (num) { /* Events have opacity 0.5 by default, and get added to according to how many * other events there are in the same render. The idea here is that the * overlaying of events builds up a 'heat map' of the event space, where * darker areas represent more events with proportion */ - const base = events.length >= 1 ? 0.3 : 0 - return base + (Math.min(0.5, 0.08 * (events.length - 1))) + const base = num >= 1 ? 0.6 : 0 + return base + (Math.min(0.5, 0.08 * (num - 1))) } export const dateMin = function () { return Array.prototype.slice.call(arguments).reduce(function (a, b) { return a < b ? a : b }) } diff --git a/src/components/Layout.js b/src/components/Layout.js index 44a6186..0df780f 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -69,7 +69,12 @@ class Dashboard extends React.Component { } getCategoryColor (category) { - return this.props.ui.style.categories[category] || this.props.ui.style.categories['default'] + const cat = this.props.ui.style.categories[category] + if (cat) { + return cat + } else { + return this.props.ui.style.categories['default'] + } } getNarrativeLinks (event) { diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx index e4cfad2..a40121e 100644 --- a/src/components/Timeline.jsx +++ b/src/components/Timeline.jsx @@ -49,7 +49,7 @@ class Timeline extends React.Component { if ((hash(nextProps.domain.categories) !== hash(this.props.domain.categories)) || hash(nextProps.dimensions) !== hash(this.props.dimensions)) { const { trackHeight, marginTop } = nextProps.dimensions this.setState({ - scaleY: this.makeScaleY(nextProps.domain.categoriesWithTimeline, trackHeight, marginTop) + scaleY: this.makeScaleY(nextProps.domain.categories, trackHeight, marginTop) }) } @@ -319,7 +319,7 @@ class Timeline extends React.Component { onDragStart={() => { this.onDragStart() }} onDrag={() => { this.onDrag() }} onDragEnd={() => { this.onDragEnd() }} - categories={this.props.domain.categoriesWithTimeline} + categories={this.props.domain.categories} /> { + function renderDot (event, colour) { + const props = ({ + fill: colour, + fillOpacity: calcOpacity(1), + transition: `transform ${transitionDuration / 1000}s ease` + }) + return onSelect([event])} + category={event.category} + events={[event]} + x={getDatetimeX(event.timestamp)} + y={getCategoryY(event.category)} + r={sizes.eventDotR} + styleProps={props} + /> + } + + function renderBar (event, colour) { + const evOpacity = calcOpacity(1) + const props = { + fill: colour, + fillOpacity: HAS_PROJECTS + ? event.projectOffset >= 0 ? evOpacity : 0.05 + : 0.6 + } + return onSelect([event])} + category={event.category} + events={[event]} + x={getDatetimeX(event.timestamp)} + y={dims.marginTop} + width={sizes.eventDotR / 4} + height={dims.trackHeight} + styleProps={props} + /> + } + function renderDatetime (datetime) { + // narrative checking for non-rendering still uses datetimes as legacy TODO(lachlan) if (narrative) { const { steps } = narrative // check all events in the datetime before rendering in narrative @@ -56,35 +95,28 @@ const TimelineEvents = ({ } } + /* DEFAULTS TODO(lachlan): clean up */ 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 categoryColor = getCategoryColor(dot.category) + // default to category for colour, and located/unlocated for shape const locatedEvents = dot.events.filter(ev => ev.latitude && ev.longitude) const unlocatedEvents = dot.events.filter(ev => !ev.latitude || !ev.longitude) // TODO: work out smarter way to manage opacity w.r.t. length // i.e. render (count - 1) extra dots with a bit of noise in position // and that, when clicked, all open the same events. - const locatedProps = ({ - fill: categoryColor, - fillOpacity: getEventOpacity(locatedEvents), - transition: `transform ${transitionDuration / 1000}s ease`, - ...extraStyles - }) const unlocatedProps = { - fill: categoryColor, fillOpacity: HAS_PROJECTS - ? unlocatedEvents.some(ev => ev.projectOffset >= 0) ? getEventOpacity(unlocatedEvents) : 0.05 - : getEventOpacity(unlocatedEvents) / 4 + ? unlocatedEvents.some(ev => ev.projectOffset >= 0) ? calcOpacity(unlocatedEvents.length) : 0.05 + : calcOpacity(unlocatedEvents.length) / 4 } - const extraRender = customStyles[1] - let bar = onSelect(unlocatedEvents)} category={dot.category} @@ -112,16 +144,7 @@ const TimelineEvents = ({ } return ( - {locatedEvents.length >= 1 && onSelect(locatedEvents)} - category={dot.category} - events={locatedEvents} - x={getDatetimeX(datetime.timestamp)} - y={getCategoryY(dot.category)} - r={sizes.eventDotR} - styleProps={locatedProps} - extraRender={extraRender} - />} + {locatedEvents.length >= 1 && renderCircle()} {unlocatedEvents.length >= 1 && bar} {extraRender ? extraRender() : null} @@ -129,16 +152,19 @@ const TimelineEvents = ({ }) } - // const projOffsets = {} - // const pEvents = datetimes.filter(dt => dt.events.some(ev => ev.project !== null)) - // pEvents.forEach(({ events }) => { - // events.forEach(ev => { - // if (!projOffsets.hasOwnProperty(ev.project)) { - // projOffsets[ev.project] = ev.projectOffset - // } - // }) - // }) + function renderEvent (event) { + let renderShape = renderDot + if (event.shape) { + if (event.shape === 'bar') { + renderShape = renderBar + } + } + const colour = event.colour ? event.colour : getCategoryColor(event.category) + return renderShape(event, colour) + } + + /* set `renderProjects` */ let renderProjects = () => null if (process.env.features.ASSOCIATIVE_EVENTS_BY_TAG) { const projects = datetimes[1] @@ -160,7 +186,8 @@ const TimelineEvents = ({ clipPath={'url(#clip)'} > {renderProjects()} - {datetimes.map(datetime => renderDatetime(datetime))} + {/* {datetimes.map(datetime => renderDatetime(datetime))} */} + {events.map(event => renderEvent(event))} ) } diff --git a/src/components/presentational/Timeline/Markers.js b/src/components/presentational/Timeline/Markers.js index 9709f9f..db87e66 100644 --- a/src/components/presentational/Timeline/Markers.js +++ b/src/components/presentational/Timeline/Markers.js @@ -11,9 +11,8 @@ const TimelineMarkers = ({ noCategories }) => { function renderMarker (event) { - const isLocated = !!event.latitude && !!event.longitude - return isLocated ? ( - - ) : ( - - ) + } + const isLocated = !!event.latitude && !!event.longitude + switch (event.shape) { + case 'circle': + return renderCircle() + case 'bar': + return renderBar() + default: + return isLocated ? renderBar() : renderCircle() + } } return ( diff --git a/src/reducers/schema/eventSchema.js b/src/reducers/schema/eventSchema.js index 0465ca1..376315e 100644 --- a/src/reducers/schema/eventSchema.js +++ b/src/reducers/schema/eventSchema.js @@ -20,7 +20,9 @@ const eventSchema = Joi.object().keys({ time_display: Joi.string().allow(''), // nested - narrative___stepStyles: Joi.array() + narrative___stepStyles: Joi.array(), + shape: Joi.string().allow(''), + colour: Joi.string().allow('') }) .and('latitude', 'longitude') .and('date', 'timestamp') diff --git a/src/selectors/index.js b/src/selectors/index.js index cb3d162..aa1dedc 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -281,30 +281,6 @@ export const selectSelected = createSelector( } ) -/** - * Only categories that have events which are located should show on the - * timeline. - */ -export const selectCategoriesWithTimeline = createSelector( - [getCategories, getEvents], - (categories, events) => { - if (categories.length === 0) { - return categories - } - // check for located events in category - // shuffle first to improve chances of stopping more quickly - const hasLocated = {} - for (let event of shuffle(events)) { - if (Object.keys(hasLocated).length === categories.length) break - const cat = event.category - if (hasLocated[cat]) continue - const isLocated = !!event.longitude && !!event.latitude - if (isLocated) hasLocated[cat] = true - } - return categories.filter(cat => hasLocated[cat.category]) - } -) - export const selectDimensions = createSelector( [getTimelineDimensions], (dimensions) => { diff --git a/src/store/initial.js b/src/store/initial.js index 8cc7040..9274e49 100644 --- a/src/store/initial.js +++ b/src/store/initial.js @@ -1,5 +1,5 @@ import { mergeDeepLeft } from 'ramda' -import colors from '../common/global.js' +import global from '../common/global' const initial = { /* @@ -105,12 +105,12 @@ const initial = { tiles: 'openstreetmap', // ['openstreetmap', 'streets', 'satellite'] style: { categories: { - default: colors.fa_red + default: global.fallbackEventColor }, narratives: { default: { opacity: 0.9, - stroke: colors.fa_red, + stroke: global.fallbackEventColor, strokeWidth: 3 } },