diff --git a/example.config.js b/example.config.js
index dcb4618..9eac7ba 100644
--- a/example.config.js
+++ b/example.config.js
@@ -11,18 +11,6 @@ module.exports = {
SHAPES_EXT: '/api/example/export_shapes/columns',
INCOMING_DATETIME_FORMAT: '%m/%d/%YT%H:%M',
// MAPBOX_TOKEN: 'pk.YOUR_MAPBOX_TOKEN',
- features: {
- USE_COVER: false,
- USE_TAGS: false,
- USE_SEARCH: false,
- USE_SITES: true,
- USE_SOURCES: true,
- USE_SHAPES: false,
- CATEGORIES_AS_TAGS: true,
- /** setting this to true will 'graph' non-located events. TODO: document
- * and rename **/
- ASSOCIATIVE_EVENTS_BY_TAG: false
- },
store: {
app: {
map: {
@@ -40,6 +28,17 @@ module.exports = {
selectedEvent: {}
// tiles: 'your-mapbox-account-name/x5678-map-id'
}
+ },
+ features: {
+ USE_COVER: false,
+ USE_TAGS: false,
+ USE_SEARCH: false,
+ USE_SITES: true,
+ USE_SOURCES: true,
+ USE_SHAPES: false,
+ CATEGORIES_AS_TAGS: true,
+ GRAPH_NONLOCATED: false,
+ HIGHLIGHT_GROUPS: false
}
}
}
diff --git a/src/actions/index.js b/src/actions/index.js
index 438584d..dc14b2a 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -23,7 +23,8 @@ export function fetchDomain () {
return []
}
- return dispatch => {
+ return (dispatch, getState) => {
+ const features = getState().features
dispatch(toggleFetchingDomain())
const eventPromise = fetch(EVENT_DATA_URL)
@@ -35,21 +36,21 @@ export function fetchDomain () {
.catch(() => handleError(domainMsg('categories')))
let narPromise = Promise.resolve([])
- if (process.env.features.USE_NARRATIVES) {
+ if (features.USE_NARRATIVES) {
narPromise = fetch(NARRATIVE_URL)
.then(response => response.json())
.catch(() => handleError(domainMsg('narratives')))
}
let sitesPromise = Promise.resolve([])
- if (process.env.features.USE_SITES) {
+ if (features.USE_SITES) {
sitesPromise = fetch(SITES_URL)
.then(response => response.json())
.catch(() => handleError(domainMsg('sites')))
}
let tagsPromise = Promise.resolve([])
- if (process.env.features.USE_TAGS) {
+ if (features.USE_TAGS) {
if (!TAGS_URL) {
tagsPromise = Promise.resolve(handleError('USE_TAGS is true, but you have not provided a TAGS_EXT'))
} else {
@@ -60,7 +61,7 @@ export function fetchDomain () {
}
let sourcesPromise = Promise.resolve([])
- if (process.env.features.USE_SOURCES) {
+ if (features.USE_SOURCES) {
if (!SOURCES_URL) {
sourcesPromise = Promise.resolve(handleError('USE_SOURCES is true, but you have not provided a SOURCES_EXT'))
} else {
@@ -71,7 +72,7 @@ export function fetchDomain () {
}
let shapesPromise = Promise.resolve([])
- if (process.env.features.USE_SHAPES) {
+ if (features.USE_SHAPES) {
shapesPromise = fetch(SHAPES_URL)
.then(response => response.json())
.catch(() => handleError(domainMsg('shapes')))
diff --git a/src/components/Layout.js b/src/components/Layout.js
index 0df780f..73017af 100644
--- a/src/components/Layout.js
+++ b/src/components/Layout.js
@@ -108,12 +108,12 @@ class Dashboard extends React.Component {
}
render () {
- const { actions, app, domain, ui } = this.props
+ const { actions, app, domain, ui, features } = this.props
if (isMobile || window.innerWidth < 1000) {
return (
- {process.env.features.USE_COVER && (
+ {features.USE_COVER && (
{/* enable USE_COVER in config.js features, and customise your header */}
{/* pass 'actions.toggleCover' as a prop to your custom header */}
@@ -193,7 +193,7 @@ class Dashboard extends React.Component {
}
/>
) : null}
- {process.env.features.USE_COVER && (
+ {features.USE_COVER && (
{/* enable USE_COVER in config.js features, and customise your header */}
{/* pass 'actions.toggleCover' as a prop to your custom header */}
diff --git a/src/components/Map.jsx b/src/components/Map.jsx
index ae340bc..d73946d 100644
--- a/src/components/Map.jsx
+++ b/src/components/Map.jsx
@@ -34,7 +34,7 @@ class Map extends React.Component {
componentDidMount () {
if (this.map === null) {
- this.initializeMap()
+ // this.initializeMap()
}
}
@@ -139,7 +139,7 @@ class Map extends React.Component {
const pane = this.map.getPanes().overlayPane
const { width, height } = this.getClientDims()
- return (
+ return !!this.map ? (
- )
+ ) : null
}
renderSites () {
@@ -183,6 +183,7 @@ class Map extends React.Component {
styles={this.props.ui.narratives}
onSelect={this.props.methods.onSelect}
onSelectNarrative={this.props.methods.onSelectNarrative}
+ features={this.props.features}
/>
)
}
@@ -266,8 +267,8 @@ function mapStateToProps (state) {
locations: selectors.selectLocations(state),
narratives: selectors.selectNarratives(state),
categories: selectors.getCategories(state),
- sites: selectors.getSites(state),
- shapes: selectors.getShapes(state)
+ sites: selectors.selectSites(state),
+ shapes: selectors.selectShapes(state)
},
app: {
views: state.app.filters.views,
@@ -285,7 +286,8 @@ function mapStateToProps (state) {
narratives: state.ui.style.narratives,
mapSelectedEvents: state.ui.style.selectedEvents,
shapes: state.ui.style.shapes
- }
+ },
+ features: selectors.getFeatures(state)
}
}
diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx
index 928e600..8852398 100644
--- a/src/components/Timeline.jsx
+++ b/src/components/Timeline.jsx
@@ -287,6 +287,7 @@ class Timeline extends React.Component {
const heightStyle = { height: dims.height }
const extraStyle = { ...heightStyle, ...foldedStyle }
const contentHeight = { height: dims.contentHeight }
+ const { categories } = this.props.domain
return (
@@ -347,10 +348,17 @@ class Timeline extends React.Component {
narrative={this.props.app.narrative}
getDatetimeX={this.getDatetimeX}
getCategoryY={this.state.scaleY}
+ getHighlights={group => {
+ if (group === 'None') {
+ return []
+ }
+ return categories.map(c => c.group === group)
+ }}
getCategoryColor={this.props.methods.getCategoryColor}
transitionDuration={this.state.transitionDuration}
onSelect={this.props.methods.onSelect}
dims={dims}
+ features={this.props.features}
/>
@@ -378,7 +386,8 @@ function mapStateToProps (state) {
ui: {
dom: state.ui.dom,
styles: state.ui.style.selectedEvents
- }
+ },
+ features: selectors.getFeatures(state)
}
}
diff --git a/src/components/Toolbar/BottomActions.js b/src/components/Toolbar/BottomActions.js
index 9ccfb8d..58a5b2c 100644
--- a/src/components/Toolbar/BottomActions.js
+++ b/src/components/Toolbar/BottomActions.js
@@ -8,7 +8,7 @@ function BottomActions (props) {
function renderToggles () {
return [
- {process.env.features.USE_SITES ? : null}
@@ -20,7 +20,7 @@ function BottomActions (props) {
/>
,
- {process.env.features.USE_COVER ? : null}
diff --git a/src/components/Toolbar/Layout.js b/src/components/Toolbar/Layout.js
index ffc89b3..817dc91 100644
--- a/src/components/Toolbar/Layout.js
+++ b/src/components/Toolbar/Layout.js
@@ -32,7 +32,7 @@ class Toolbar extends React.Component {
}
renderSearch () {
- if (process.env.features.USE_SEARCH) {
+ if (this.props.features.USE_SEARCH) {
return (
@@ -154,7 +154,7 @@ class Toolbar extends React.Component {
const tagsLabel = copy[this.props.language].toolbar.tags_label
const categoriesLabel = 'Categories' // TODO:
const isTags = this.props.tags && this.props.tags.children
- const isCategories = process.env.features.CATEGORIES_AS_TAGS
+ const isCategories = this.props.features.CATEGORIES_AS_TAGS
return (
@@ -176,6 +176,7 @@ class Toolbar extends React.Component {
cover={{
toggle: this.props.actions.toggleCover
}}
+ features={this.props.features}
/>
)
@@ -195,6 +196,7 @@ class Toolbar extends React.Component {
function mapStateToProps (state) {
return {
+ features: selectors.getFeatures(state),
tags: selectors.getTagTree(state),
categories: selectors.getCategories(state),
narratives: selectors.selectNarratives(state),
@@ -202,7 +204,6 @@ function mapStateToProps (state) {
activeTags: selectors.getActiveTags(state),
activeCategories: selectors.getActiveCategories(state),
viewFilters: state.app.filters.views,
- features: state.app.features,
narrative: state.app.narrative,
sitesShowing: state.app.flags.isShowingSites,
infoShowing: state.app.flags.isInfopopup
diff --git a/src/components/presentational/Map/Narratives.js b/src/components/presentational/Map/Narratives.js
index b48fe8a..7cf73fe 100644
--- a/src/components/presentational/Map/Narratives.js
+++ b/src/components/presentational/Map/Narratives.js
@@ -10,7 +10,7 @@ const defaultStyles = {
stroke: 'none'
}
-function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, projectPoint }) {
+function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, projectPoint, features }) {
function getNarrativeStyle (narrativeId) {
const styleName = (narrativeId && narrativeId in styles)
? narrativeId
@@ -153,7 +153,7 @@ function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives,
return (
- {(process.env.features.NARRATIVE_STEP_STYLES
+ {(features.NARRATIVE_STEP_STYLES
? renderBetweenMarked(n)
: renderFullNarrative(n)
)}
diff --git a/src/components/presentational/Timeline/DatetimeBar.js b/src/components/presentational/Timeline/DatetimeBar.js
index 9660baf..3cb668b 100644
--- a/src/components/presentational/Timeline/DatetimeBar.js
+++ b/src/components/presentational/Timeline/DatetimeBar.js
@@ -1,7 +1,7 @@
import React from 'react'
export default ({
- category,
+ highlights,
events,
x,
y,
@@ -10,14 +10,34 @@ export default ({
onSelect,
styleProps,
extraRender
-}) => (
-
-)
+}) => {
+ if (highlights.length === 0) {
+ return (
+
+ )
+ }
+ const sectionHeight = height / highlights.length
+ return (
+
+ {highlights.map((h, idx) => (
+
+ ))}
+
+ )
+}
diff --git a/src/components/presentational/Timeline/Events.js b/src/components/presentational/Timeline/Events.js
index 2184e39..444b97c 100644
--- a/src/components/presentational/Timeline/Events.js
+++ b/src/components/presentational/Timeline/Events.js
@@ -7,8 +7,6 @@ import Project from './Project'
import { calcOpacity } from '../../../common/utilities'
import { sizes } from '../../../common/global'
-const GRAPH_NONLOCATED = 'GRAPH_NONLOCATED' in process.env.features && process.env.features.GRAPH_NONLOCATED
-
function renderDot (event, styles, props) {
return = 0 ? styles.opacity : 0.05
: 0.6
@@ -35,6 +33,7 @@ function renderBar (event, styles, props) {
width={sizes.eventDotR / 4}
height={props.dims.trackHeight}
styleProps={{ ...styles, fillOpacity }}
+ highlights={props.highlights}
/>
}
@@ -66,10 +65,11 @@ const TimelineEvents = ({
getDatetimeX,
getCategoryY,
getCategoryColor,
+ getHighlights,
onSelect,
transitionDuration,
- // styleDatetime,
- dims
+ dims,
+ features
}) => {
const narIds = narrative ? narrative.steps.map(s => s.id) : []
@@ -91,25 +91,28 @@ const TimelineEvents = ({
}
}
- const colour = event.colour ? event.colour : getCategoryColor(event.category)
+ let colour = event.colour ? event.colour : getCategoryColor(event.category)
const styles = {
fill: colour,
fillOpacity: calcOpacity(1),
transition: `transform ${transitionDuration / 1000}s ease`
}
+
return renderShape(event, styles, {
x: getDatetimeX(event.timestamp),
- y: (GRAPH_NONLOCATED && !event.latitude && !event.longitude)
+ y: (features.GRAPH_NONLOCATED && !event.latitude && !event.longitude)
? event.projectOffset >= 0 ? dims.trackHeight - event.projectOffset : dims.marginTop
- : getCategoryY(event.category),
+ : getCategoryY ? getCategoryY(event.category) : () => null,
onSelect: () => onSelect([event]),
- dims
+ dims,
+ highlights: features.HIGHLIGHT_GROUPS ? getHighlights(event.tags[0]) : [],
+ features
})
}
/* set `renderProjects` */
let renderProjects = () => null
- if (GRAPH_NONLOCATED) {
+ if (features.GRAPH_NONLOCATED) {
renderProjects = function () {
return
{projects.map(project => state.domain.events
@@ -11,18 +10,9 @@ export const getNarratives = state => state.domain.narratives
export const getActiveNarrative = state => state.app.narrative
export const getActiveStep = state => state.app.narrativeState.current
export const getSelected = state => state.app.selected
-export const getSites = (state) => {
- if (process.env.features.USE_SITES) return state.domain.sites.filter(s => !!(+s.enabled))
- return []
-}
-export const getSources = state => {
- if (process.env.features.USE_SOURCES) return state.domain.sources
- return {}
-}
-export const getShapes = state => {
- if (process.env.features.USE_SHAPES) return state.domain.shapes
- return []
-}
+export const getSites = state => state.domain.sites
+export const getSources = state => state.domain.sources
+export const getShapes = state => state.domain.shapes
export const getNotifications = state => state.domain.notifications
export const getTagTree = state => state.domain.tags
export const getActiveTags = state => state.app.filters.tags
@@ -30,6 +20,24 @@ export const getActiveCategories = state => state.app.filters.categories
export const getTimeRange = state => state.app.timeline.range
export const getTimelineDimensions = state => state.app.timeline.dimensions
export const selectNarrative = state => state.app.narrative
+export const getFeatures = state => state.features
+
+export const selectSites = createSelector([getSites, getFeatures], (sites, features) => {
+ if (features.USE_SITES) {
+ return sites.filter(s => !!(+s.enabled))
+ }
+ return []
+})
+
+export const selectSources = createSelector([getSources, getFeatures], (sources, features) => {
+ if (features.USE_SOURCES) return sources
+ return {}
+})
+
+export const selectShapes = createSelector([getShapes, getFeatures], (shapes, features) => {
+ if (features.USE_SHAPES) return shapes
+ return []
+})
/**
* Of all available events, selects those that
@@ -38,14 +46,14 @@ export const selectNarrative = state => state.app.narrative
* 3. exist in an active category
*/
export const selectEvents = createSelector(
- [getEvents, getActiveTags, getActiveCategories, getTimeRange],
- (events, activeTags, activeCategories, timeRange) => {
+ [getEvents, getActiveTags, getActiveCategories, getTimeRange, getFeatures],
+ (events, activeTags, activeCategories, timeRange, features) => {
return events.reduce((acc, event) => {
const isMatchingTag = (event.tags && event.tags.map(tag => activeTags.includes(tag)).some(s => s)) || activeTags.length === 0
const isActiveTag = isMatchingTag || activeTags.length === 0
const isActiveCategory = activeCategories.includes(event.category) || activeCategories.length === 0
let isActiveTime = isTimeRangedIn(event, timeRange)
- isActiveTime = GRAPH_NONLOCATED ? ((!event.latitude && !event.longitude) || isActiveTime) : isActiveTime
+ isActiveTime = features.GRAPH_NONLOCATED ? ((!event.latitude && !event.longitude) || isActiveTime) : isActiveTime
if (isActiveTime && isActiveTag && isActiveCategory) {
acc[event.id] = { ...event }
@@ -60,9 +68,9 @@ export const selectEvents = createSelector(
* and if TAGS are being used, select them if their tags are enabled
*/
export const selectNarratives = createSelector(
- [getEvents, getNarratives, getSources],
- (events, narrativesMeta, sources) => {
- if (!process.env.features.USE_NARRATIVES) {
+ [getEvents, getNarratives, getSources, getFeatures],
+ (events, narrativesMeta, sources, features) => {
+ if (!features.USE_NARRATIVES) {
return []
}
const narratives = {}
@@ -162,9 +170,9 @@ export const selectProjectedEvents = createSelector(
}
*/
export const selectEventsAndProjects = createSelector(
- [selectEvents],
- events => {
- if (!GRAPH_NONLOCATED) {
+ [selectEvents, getFeatures],
+ (events, features) => {
+ if (!features.GRAPH_NONLOCATED) {
return [events, []]
}
diff --git a/src/store/initial.js b/src/store/initial.js
index 9274e49..978940a 100644
--- a/src/store/initial.js
+++ b/src/store/initial.js
@@ -127,6 +127,19 @@ const initial = {
timeslider: 'timeslider',
map: 'map'
}
+ },
+
+ features: {
+ CATEGORIES_AS_TAGS: true,
+ USE_COVER: false,
+ USE_TAGS: false,
+ USE_SEARCH: false,
+ USE_SITES: false,
+ USE_SOURCES: false,
+ USE_SHAPES: false,
+ USE_NARRATIVES: false,
+ GRAPH_NONLOCATED: false,
+ HIGHLIGHT_GROUPS: false
}
}