diff --git a/src/actions/index.js b/src/actions/index.js index e065b25..10b3c79 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -239,3 +239,11 @@ export function fetchSourceError(msg) { msg } } + +export const TOGGLE_MAPVIEW = 'TOGGLE_MAPVIEW'; +export function toggleMapView(layer) { + return { + type: TOGGLE_MAPVIEW, + layer + } +} diff --git a/src/components/Toolbar.jsx b/src/components/Toolbar.jsx index 5b2c2db..0b3fab0 100644 --- a/src/components/Toolbar.jsx +++ b/src/components/Toolbar.jsx @@ -5,7 +5,10 @@ import * as selectors from '../selectors' import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; import Search from './Search.jsx'; import TagListPanel from './TagListPanel.jsx'; -import Icon from './Icon.jsx'; +import SitesIcon from './presentational/Icons/SitesIcon.js'; +import RefreshIcon from './presentational/Icons/RefreshIcon.js'; +import CoeventIcon from './presentational/Icons/CoeventIcon.js'; +import RouteIcon from './presentational/Icons/RouteIcon.js'; import copy from '../js/data/copy.json'; // NB: i think this entire component can actually be part of a future feature... @@ -40,52 +43,24 @@ class Toolbar extends React.Component { } toggleMapViews(layer) { - const isLayerInView = !this.props.viewFilters[layer]; - const newViews = {}; - newViews[layer] = isLayerInView; - const views = Object.assign({}, this.props.viewFilters, newViews); - this.props.actions.updateFilters({ views }); + this.props.actions.toggleMapView(layer); } renderMapActions() { - const isViewLayer = this.props.viewFilters; - const routeClass = (isViewLayer.routes) ? 'action-button active disabled' : 'action-button disabled' - const sitesClass = (isViewLayer.sites) ? 'action-button active disabled' : 'action-button disabled'; - const coeventsClass = (isViewLayer.coevents) ? 'action-button active disabled' : 'action-button disabled'; - return (
- - - + this.toggleMapViews(view)} + isEnabled={this.props.viewFilters.routes} + /> + this.toggleMapViews(view)} + isEnabled={this.props.viewFilters.sites} + /> + this.toggleMapViews(view)} + isEnabled={this.props.viewFilters.coevents} + />
); } @@ -102,11 +77,7 @@ class Toolbar extends React.Component { i @@ -124,11 +95,10 @@ class Toolbar extends React.Component { renderToolbarTab(tabNum, key) { const isActive = (tabNum === this.state.tab); - //let caption_lang = copy[this.props.language].toolbar.tabs[tabNum]; + let classes = (isActive) ? 'toolbar-tab active' : 'toolbar-tab'; return (
{ this.toggleTab(tabNum); }}> - {/**/}
{key}
); @@ -145,20 +115,6 @@ class Toolbar extends React.Component { return ''; } - renderToolbarTabs() { - const title = copy[this.props.language].toolbar.title; - return ( -
-

{title}

-
- {/*this.renderToolbarTab(0, 'search')*/} - {this.renderToolbarTagRoot()} -
- {/* {this.renderBottomActions()} */} -
- ) - } - renderTagListPanel(tagType) { const panels_lang = copy[this.props.language].toolbar.panels; const title = (panels_lang[tagType]) ? panels_lang[tagType].title : tagType; @@ -211,6 +167,39 @@ class Toolbar extends React.Component { return ''; } + renderToolbarNavs() { + if (this.props.narratives) { + return this.props.narratives.map((nar, idx) => { + const isActive = (idx === this.state.tab); + + let classes = (isActive) ? 'toolbar-tab active' : 'toolbar-tab'; + + return ( +
{ this.toggleTab(idx); }}> +
{nar.label}
+
+ ); + }) + } + return ''; + } + + renderToolbarTabs() { + const title = copy[this.props.language].toolbar.title; + return ( +
+

{title}

+
+ {/*this.renderToolbarTab(0, 'search')*/} + {(this.props.isModeGuided) + ? this.renderToolbarNavs() + : this.renderToolbarTagRoot()} +
+ {/* {this.renderBottomActions()} */} +
+ ) + } + render() { let classes = (this.state.tab !== -1) ? 'toolbar-panels' : 'toolbar-panels folded'; @@ -237,7 +226,8 @@ function mapStateToProps(state) { tagFilters: selectors.selectTagList(state), categoryFilter: state.app.filters.categories, viewFilters: state.app.filters.views, - features: state.app.features + features: state.app.features, + isModeGuided: state.app.isModeGuided } } diff --git a/src/components/presentational/Icons/CoeventIcon.js b/src/components/presentational/Icons/CoeventIcon.js new file mode 100644 index 0000000..ffa5db7 --- /dev/null +++ b/src/components/presentational/Icons/CoeventIcon.js @@ -0,0 +1,24 @@ +import React from 'react'; + +const CoeventIcon = ({ isEnabled, toggleMapViews }) => { + + const classes = (isEnabled) ? 'action-button active disabled' : 'action-button disabled'; + + return ( + + ); +} + +export default CoeventIcon; diff --git a/src/components/presentational/Icons/RefreshIcon.js b/src/components/presentational/Icons/RefreshIcon.js new file mode 100644 index 0000000..1e1eb03 --- /dev/null +++ b/src/components/presentational/Icons/RefreshIcon.js @@ -0,0 +1,14 @@ +import React from 'react'; + +const RefreshIcon = ({ }) => { + + return ( + + + + + ); +} + +export default RefreshIcon; diff --git a/src/components/presentational/Icons/RouteIcon.js b/src/components/presentational/Icons/RouteIcon.js new file mode 100644 index 0000000..4febda9 --- /dev/null +++ b/src/components/presentational/Icons/RouteIcon.js @@ -0,0 +1,20 @@ +import React from 'react'; + +const RouteIcon = ({ isEnabled, toggleMapViews }) => { + + const classes = (isEnabled) ? 'action-button active disabled' : 'action-button disabled'; + + return ( + + ); +} + +export default RouteIcon; diff --git a/src/components/presentational/Icons/SitesIcon.js b/src/components/presentational/Icons/SitesIcon.js new file mode 100644 index 0000000..a4461eb --- /dev/null +++ b/src/components/presentational/Icons/SitesIcon.js @@ -0,0 +1,19 @@ +import React from 'react'; + +const SitesIcon = ({ isEnabled, toggleMapViews }) => { + + const classes = (isEnabled) ? 'action-button active disabled' : 'action-button disabled'; + + return ( + + ); +} + +export default SitesIcon; diff --git a/src/js/map/map.js b/src/js/map/map.js index a40fa90..6133b98 100644 --- a/src/js/map/map.js +++ b/src/js/map/map.js @@ -291,7 +291,7 @@ Stop and start the development process in terminal after you have added your tok eventsDom .enter().append('circle') .attr('class', 'location-event-marker') - .style('fill', (d, i) => getCategoryColor(domain.categories[i])) + .style('fill', (d, i) => getCategoryColor(domain.categories[i].category)) .transition() .duration(500) .attr('r', d => (d) ? Math.sqrt(16 * d) + 3 : 0); diff --git a/src/reducers/app.js b/src/reducers/app.js index d9bd94a..85459f2 100644 --- a/src/reducers/app.js +++ b/src/reducers/app.js @@ -7,6 +7,7 @@ import { UPDATE_TIMERANGE, RESET_ALLFILTERS, TOGGLE_LANGUAGE, + TOGGLE_MAPVIEW, FETCH_ERROR, } from '../actions'; @@ -74,6 +75,18 @@ function toggleLanguage(appState, action) { }); } +function toggleMapView(appState, action) { + const isLayerInView = !appState.views[layer]; + const newViews = {}; + newViews[layer] = isLayerInView; + const views = Object.assign({}, appState.views, newViews); + return Object.assign({}, appState, { + filters: Object.assign({}, appState.filters, { + views + }) + }); +} + function fetchError(state, action) { return { ...state, @@ -97,6 +110,8 @@ function app(appState = initial.app, action) { return resetAllFilters(appState, action); case TOGGLE_LANGUAGE: return toggleLanguage(appState, action); + case TOGGLE_MAPVIEW: + return toggleMapView(appState, action); case FETCH_ERROR: return fetchError(appState, action); default: diff --git a/src/selectors/index.js b/src/selectors/index.js index b502d7c..40336e8 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -96,7 +96,7 @@ export const selectNarratives = createSelector( if (isTimeRanged && isTagged && isInNarrative) { if (!narratives[evt.narrative]) { - narratives[evt.narrative] = { key: evt.narrative, steps: [], byId: {} }; + narratives[evt.narrative] = { id: evt.narrative, steps: [], byId: {} }; } narratives[evt.narrative].steps.push(evt); narratives[evt.narrative].byId[evt.id] = { next: null, prev: null }; @@ -105,15 +105,19 @@ export const selectNarratives = createSelector( Object.keys(narratives).forEach((key) => { const steps = narratives[key].steps; + steps.sort((a, b) => { return (parseTimestamp(a.timestamp) > parseTimestamp(b.timestamp)); }); + steps.forEach((step, i) => { narratives[key].byId[step.id].next = (i < steps.length - 2) ? steps[i + 1] : null; narratives[key].byId[step.id].prev = (i > 0) ? steps[i - 1] : null; }); + + narratives[key] = Object.assign(narrativeMetadata.find(n => n.id === key), narratives[key]); }); -console.log(narrativeMetadata, narratives) + return Object.values(narratives); }); @@ -152,7 +156,7 @@ export const selectLocations = createSelector( export const selectCategories = createSelector( [getCategories], (categories) => { - return categories.map(v => v.category); + return Object.values(categories); } ); diff --git a/src/store/initial.js b/src/store/initial.js index abdf16c..333100f 100644 --- a/src/store/initial.js +++ b/src/store/initial.js @@ -44,6 +44,7 @@ const initial = { }, base_uri: 'http://127.0.0.1:8000/', // Modify accordingly on production setup. isMobile: (/Mobi/.test(navigator.userAgent)), + isModeGuided: true, language: 'en-US', mapAnchor: process.env.MAP_ANCHOR, zoomLevels: [{ diff --git a/webpack.config.js b/webpack.config.js index 4706f03..ba1c538 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,7 +1,7 @@ const webpack = require('webpack'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -const userConfig = require('./config'); +const userConfig = require('./dev.config'); const userConfigJSON = {}; const devMode = process.env.NODE_ENV !== 'production';