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
}
},