mirror of
https://github.com/bellingcat/ukraine-timemap.git
synced 2026-06-12 05:18:34 +03:00
Clean icons in toolbar
This commit is contained in:
@@ -239,3 +239,11 @@ export function fetchSourceError(msg) {
|
||||
msg
|
||||
}
|
||||
}
|
||||
|
||||
export const TOGGLE_MAPVIEW = 'TOGGLE_MAPVIEW';
|
||||
export function toggleMapView(layer) {
|
||||
return {
|
||||
type: TOGGLE_MAPVIEW,
|
||||
layer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<div className="bottom-action-block">
|
||||
<button
|
||||
className={routeClass}
|
||||
onClick={() => this.toggleMapViews('routes')}
|
||||
>
|
||||
<svg x="0px" y="0px" width="30px" height="20px" viewBox="0 0 30 20" enableBackground="new 0 0 30 20">
|
||||
<path d="M0.806,13.646h7.619c2.762,0,3-0.238,3-3v-0.414c0-2.762,0.301-3,3.246-3h14.523"/>
|
||||
<polyline points="16.671,9.228 19.103,7.233 16.671,5.237 "/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
className={sitesClass}
|
||||
onClick={() => this.toggleMapViews('sites')}
|
||||
>
|
||||
<svg x="0px" y="0px" width="30px" height="20px" viewBox="0 0 30 20" enableBackground="new 0 0 30 20">
|
||||
<path d="M24.615,6.793H5.385c-2.761,0-3,0.239-3,3v0.414
|
||||
c0,2.762,0.239,3,3,3h7.621l1.996,2.432l1.996-2.432h7.618c2.762,0,3-0.238,3-3V9.793C27.615,7.032,27.377,6.793,24.615,6.793z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
className={coeventsClass}
|
||||
onClick={() => this.toggleMapViews('coevents')}
|
||||
>
|
||||
<svg className="coevents" x="0px" y="0px" width="30px" height="20px" viewBox="0 0 30 20" enableBackground="new 0 0 30 20">
|
||||
<polygon stroke-linejoin="round" stroke-miterlimit="10" points="19.178,20 10.823,20 10.473,14.081
|
||||
10,13.396 10,6.084 20,6.084 20,13.396 19.445,14.021 "/>
|
||||
<rect className="no-fill" x="11.4" y="7.867" width="7.2" height="3.35"/>
|
||||
<line stroke-linejoin="round" stroke-miterlimit="10" x1="12.125" y1="1" x2="12.125" y2="5.35"/>
|
||||
<rect x="11.4" y="4.271" width="1.496" height="1.079"/>
|
||||
<rect x="17.104" y="4.271" width="1.496" height="1.079"/>
|
||||
</svg>
|
||||
</button>
|
||||
<RouteIcon
|
||||
onClick={(view) => this.toggleMapViews(view)}
|
||||
isEnabled={this.props.viewFilters.routes}
|
||||
/>
|
||||
<SitesIcon
|
||||
onClick={(view) => this.toggleMapViews(view)}
|
||||
isEnabled={this.props.viewFilters.sites}
|
||||
/>
|
||||
<CoeventIcon
|
||||
onClick={(view) => this.toggleMapViews(view)}
|
||||
isEnabled={this.props.viewFilters.coevents}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -102,11 +77,7 @@ class Toolbar extends React.Component {
|
||||
i
|
||||
</button>
|
||||
<button className="action-button tiny" onClick={() => this.resetAllFilters()}>
|
||||
<svg className="reset" x="0px" y="0px" width="25px" height="25px" viewBox="7.5 7.5 25 25" enableBackground="new 7.5 7.5 25 25">
|
||||
<path stroke-width="2" stroke-miterlimit="10" d="M28.822,16.386c1.354,3.219,0.898,7.064-1.5,9.924
|
||||
c-3.419,4.073-9.49,4.604-13.562,1.186c-4.073-3.417-4.604-9.49-1.187-13.562c1.987-2.368,4.874-3.54,7.74-3.433" />
|
||||
<polygon points="26.137,12.748 27.621,19.464 28.9,16.741 31.898,16.503" />
|
||||
</svg>
|
||||
<RefreshIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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 (
|
||||
<div className={classes} onClick={() => { this.toggleTab(tabNum); }}>
|
||||
{/*<Icon iconType={key} />*/}
|
||||
<div className="tab-caption">{key}</div>
|
||||
</div>
|
||||
);
|
||||
@@ -145,20 +115,6 @@ class Toolbar extends React.Component {
|
||||
return '';
|
||||
}
|
||||
|
||||
renderToolbarTabs() {
|
||||
const title = copy[this.props.language].toolbar.title;
|
||||
return (
|
||||
<div className="toolbar">
|
||||
<div className="toolbar-header"><p>{title}</p></div>
|
||||
<div className="toolbar-tabs">
|
||||
{/*this.renderToolbarTab(0, 'search')*/}
|
||||
{this.renderToolbarTagRoot()}
|
||||
</div>
|
||||
{/* {this.renderBottomActions()} */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className={classes} onClick={() => { this.toggleTab(idx); }}>
|
||||
<div className="tab-caption">{nar.label}</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
renderToolbarTabs() {
|
||||
const title = copy[this.props.language].toolbar.title;
|
||||
return (
|
||||
<div className="toolbar">
|
||||
<div className="toolbar-header"><p>{title}</p></div>
|
||||
<div className="toolbar-tabs">
|
||||
{/*this.renderToolbarTab(0, 'search')*/}
|
||||
{(this.props.isModeGuided)
|
||||
? this.renderToolbarNavs()
|
||||
: this.renderToolbarTagRoot()}
|
||||
</div>
|
||||
{/* {this.renderBottomActions()} */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
24
src/components/presentational/Icons/CoeventIcon.js
Normal file
24
src/components/presentational/Icons/CoeventIcon.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
|
||||
const CoeventIcon = ({ isEnabled, toggleMapViews }) => {
|
||||
|
||||
const classes = (isEnabled) ? 'action-button active disabled' : 'action-button disabled';
|
||||
|
||||
return (
|
||||
<button
|
||||
className={sitesClass}
|
||||
onClick={() => toggleMapViews('coevents')}
|
||||
>
|
||||
<svg className="coevents" x="0px" y="0px" width="30px" height="20px" viewBox="0 0 30 20" enableBackground="new 0 0 30 20">
|
||||
<polygon stroke-linejoin="round" stroke-miterlimit="10" points="19.178,20 10.823,20 10.473,14.081
|
||||
10,13.396 10,6.084 20,6.084 20,13.396 19.445,14.021 "/>
|
||||
<rect className="no-fill" x="11.4" y="7.867" width="7.2" height="3.35"/>
|
||||
<line stroke-linejoin="round" stroke-miterlimit="10" x1="12.125" y1="1" x2="12.125" y2="5.35"/>
|
||||
<rect x="11.4" y="4.271" width="1.496" height="1.079"/>
|
||||
<rect x="17.104" y="4.271" width="1.496" height="1.079"/>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default CoeventIcon;
|
||||
14
src/components/presentational/Icons/RefreshIcon.js
Normal file
14
src/components/presentational/Icons/RefreshIcon.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
const RefreshIcon = ({ }) => {
|
||||
|
||||
return (
|
||||
<svg className="reset" x="0px" y="0px" width="25px" height="25px" viewBox="7.5 7.5 25 25" enableBackground="new 7.5 7.5 25 25">
|
||||
<path stroke-width="2" stroke-miterlimit="10" d="M28.822,16.386c1.354,3.219,0.898,7.064-1.5,9.924
|
||||
c-3.419,4.073-9.49,4.604-13.562,1.186c-4.073-3.417-4.604-9.49-1.187-13.562c1.987-2.368,4.874-3.54,7.74-3.433" />
|
||||
<polygon points="26.137,12.748 27.621,19.464 28.9,16.741 31.898,16.503" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default RefreshIcon;
|
||||
20
src/components/presentational/Icons/RouteIcon.js
Normal file
20
src/components/presentational/Icons/RouteIcon.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
const RouteIcon = ({ isEnabled, toggleMapViews }) => {
|
||||
|
||||
const classes = (isEnabled) ? 'action-button active disabled' : 'action-button disabled';
|
||||
|
||||
return (
|
||||
<button
|
||||
className={sitesClass}
|
||||
onClick={() => toggleMapViews('routes')}
|
||||
>
|
||||
<svg x="0px" y="0px" width="30px" height="20px" viewBox="0 0 30 20" enableBackground="new 0 0 30 20">
|
||||
<path d="M0.806,13.646h7.619c2.762,0,3-0.238,3-3v-0.414c0-2.762,0.301-3,3.246-3h14.523"/>
|
||||
<polyline points="16.671,9.228 19.103,7.233 16.671,5.237 "/>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default RouteIcon;
|
||||
19
src/components/presentational/Icons/SitesIcon.js
Normal file
19
src/components/presentational/Icons/SitesIcon.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
|
||||
const SitesIcon = ({ isEnabled, toggleMapViews }) => {
|
||||
|
||||
const classes = (isEnabled) ? 'action-button active disabled' : 'action-button disabled';
|
||||
|
||||
return (
|
||||
<button
|
||||
className={sitesClass}
|
||||
onClick={() => toggleMapViews('sites')}
|
||||
>
|
||||
<svg x="0px" y="0px" width="30px" height="20px" viewBox="0 0 30 20" enableBackground="new 0 0 30 20">
|
||||
<path d="M24.615,6.793H5.385c-2.761,0-3,0.239-3,3v0.414c0,2.762,0.239,3,3,3h7.621l1.996,2.432l1.996-2.432h7.618c2.762,0,3-0.238,3-3V9.793C27.615,7.032,27.377,6.793,24.615,6.793z"/>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default SitesIcon;
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -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: [{
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user