From d29a6f3bf361b1ec6d68cc379e2a5d357652e8aa Mon Sep 17 00:00:00 2001 From: Lachlan Kermode Date: Wed, 30 Jan 2019 16:50:25 +0000 Subject: [PATCH 01/18] categories and filters from different icons --- src/components/CategoriesListPanel.jsx | 39 ++++++++++++++++++++++++++ src/components/TagListPanel.jsx | 26 +---------------- src/components/Toolbar.jsx | 29 +++++++++++++++---- src/js/data/copy.json | 10 ++++--- 4 files changed, 69 insertions(+), 35 deletions(-) create mode 100644 src/components/CategoriesListPanel.jsx diff --git a/src/components/CategoriesListPanel.jsx b/src/components/CategoriesListPanel.jsx new file mode 100644 index 0000000..64f88e6 --- /dev/null +++ b/src/components/CategoriesListPanel.jsx @@ -0,0 +1,39 @@ +import React from 'react' +import Checkbox from './presentational/Checkbox' +import copy from '../js/data/copy.json' + +export default (props) => { + function onClickCheckbox (obj, type) { + obj.active = !obj.active + props.onCategoryFilter(obj) + } + + function renderCategoryTree () { + return ( +
+

{copy[props.language].toolbar.categories}

+ {props.categories.map(cat => { + return (
  • + onClickCheckbox(cat, 'category')} + /> +
  • ) + })} +
    + ) + } + + return ( +
    +

    {copy[props.language].toolbar.explore_by_category__title}

    +

    {copy[props.language].toolbar.explore_by_category__description}

    + {renderCategoryTree()} +
    + ) +} diff --git a/src/components/TagListPanel.jsx b/src/components/TagListPanel.jsx index c153a7b..1d88b73 100644 --- a/src/components/TagListPanel.jsx +++ b/src/components/TagListPanel.jsx @@ -22,8 +22,7 @@ class TagListPanel extends React.Component { onClickCheckbox (obj, type) { obj.active = !obj.active - if (type === 'category') this.props.onCategoryFilter(obj) - if (type === 'tag') this.props.onTagFilter(obj) + this.props.onTagFilter(obj) } createNodeComponent (node, depth) { @@ -70,34 +69,11 @@ class TagListPanel extends React.Component { ) } - renderCategoryTree () { - return ( -
    -

    {copy[this.props.language].toolbar.categories}

    - {this.props.categories.map(cat => { - return (
  • - this.onClickCheckbox(cat, 'category')} - /> -
  • ) - }) - } -
    - ) - } - render () { return (

    {copy[this.props.language].toolbar.explore_by_tag__title}

    {copy[this.props.language].toolbar.explore_by_tag__description}

    - {this.renderCategoryTree()} {this.renderTree()}
    ) diff --git a/src/components/Toolbar.jsx b/src/components/Toolbar.jsx index 52aa54c..c072634 100644 --- a/src/components/Toolbar.jsx +++ b/src/components/Toolbar.jsx @@ -7,6 +7,7 @@ import * as selectors from '../selectors' import { Tabs, TabPanel } from 'react-tabs' import Search from './Search.jsx' import TagListPanel from './TagListPanel.jsx' +import CategoriesListPanel from './CategoriesListPanel.jsx' import ToolbarBottomActions from './ToolbarBottomActions.jsx' import copy from '../js/data/copy.json' import { trimAndEllipse } from '../js/utilities.js' @@ -71,6 +72,21 @@ class Toolbar extends React.Component { ) } + renderToolbarCategoriesPanel () { + if (process.env.features.CATEGORIES_AS_TAGS) { + return ( + + + + ) + } + } + renderToolbarTagPanel () { if (process.env.features.USE_TAGS && this.props.tags.children) { @@ -78,17 +94,14 @@ class Toolbar extends React.Component { ) } - return '' + return null } renderToolbarTab (_selected, label, iconKey) { @@ -110,6 +123,7 @@ class Toolbar extends React.Component { {this.renderClosePanel()} {this.renderToolbarNarrativePanel()} + {this.renderToolbarCategoriesPanel()} {this.renderToolbarTagPanel()}} @@ -130,7 +144,7 @@ class Toolbar extends React.Component { ) }) } - return '' + return null } renderToolbarTabs () { @@ -138,14 +152,17 @@ class Toolbar extends React.Component { if (process.env.title) title = process.env.title const narrativesLabel = copy[this.props.language].toolbar.narratives_label const tagsLabel = copy[this.props.language].toolbar.tags_label + const categoriesLabel = 'Categories' // TODO: const isTags = this.props.tags && this.props.tags.children + const isCategories = true return (

    {title}

    {this.renderToolbarTab(0, narrativesLabel, 'timeline')} - {(isTags) ? this.renderToolbarTab(1, tagsLabel, 'style') : ''} + {(isCategories) ? this.renderToolbarTab(1, categoriesLabel, 'category') : null} + {(isTags) ? this.renderToolbarTab(2, tagsLabel, 'filter_list') : null}
    Date: Wed, 30 Jan 2019 18:06:43 +0000 Subject: [PATCH 02/18] fix source scrolling, layout and arrows --- src/components/Dashboard.jsx | 6 +- src/components/SourceOverlay.jsx | 64 +++++++++++------- src/scss/main.scss | 2 +- .../{mediaoverlay.scss => sourceoverlay.scss} | 67 +++++++------------ 4 files changed, 69 insertions(+), 70 deletions(-) rename src/scss/{mediaoverlay.scss => sourceoverlay.scss} (82%) diff --git a/src/components/Dashboard.jsx b/src/components/Dashboard.jsx index fb60c28..0e770de 100644 --- a/src/components/Dashboard.jsx +++ b/src/components/Dashboard.jsx @@ -14,7 +14,7 @@ import InfoPopUp from './InfoPopup.jsx' import Timeline from './Timeline.jsx' import Notification from './Notification.jsx' -import { parseDate } from '../js/utilities' +import { parseDate, injectSource } from '../js/utilities' class Dashboard extends React.Component { constructor (props) { @@ -170,7 +170,7 @@ function mapDispatchToProps (dispatch) { } export default connect( - state => state, - // state => injectSource("Youtube - Novodvirske Tank Separatist Patrol Video"), + // state => state, + state => injectSource("Hromadske.tv - Paratroopers Video"), mapDispatchToProps )(Dashboard) diff --git a/src/components/SourceOverlay.jsx b/src/components/SourceOverlay.jsx index cce77f5..aab94bf 100644 --- a/src/components/SourceOverlay.jsx +++ b/src/components/SourceOverlay.jsx @@ -9,16 +9,7 @@ import NoSource from './presentational/NoSource' class SourceOverlay extends React.Component { constructor () { super() - - this.state = { - idx: 0 - } - } - - renderError () { - return ( - - ) + this.state = { idx: 0 } } renderImage (path) { @@ -27,7 +18,7 @@ class SourceOverlay extends React.Component {
    } + loader={
    } unloader={} /> @@ -35,7 +26,6 @@ class SourceOverlay extends React.Component { } renderVideo (path) { - // NB: assume only one video return (
    { - counts[m.type] += 1 - }) - return counts + return media.reduce( + (acc, vl) => { + acc[vl.type] += 1 + return acc + }, + { Image: 0, Video: 0, Text: 0 } + ) } _renderPath (media) { @@ -122,26 +114,52 @@ class SourceOverlay extends React.Component { _renderContent (media) { const el = document.querySelector(`.source-media-gallery`) - const shiftW = (el) ? el.getBoundingClientRect().width : 0 + const shiftW = el ? el.getBoundingClientRect().width : 0 return ( -
    +
    {media.map((m) => this._renderPath(m))}
    ) } onShiftGallery (shift) { + // no more left if (this.state.idx === 0 && shift === -1) return - if (this.state.idx - 1 === this.props.source.paths.length && shift === 1) return + // no more right + if (this.state.idx === this.props.source.paths.length - 1 && shift === 1) return this.setState({ idx: this.state.idx + shift }) } _renderControls () { + const backArrow = this.state.idx !== 0 ? ( +
    this.onShiftGallery(-1)} + > + + + +
    + ) : null + + console.log(this.state.idx) + console.log(this.props.source.paths.length) + const forwardArrow = this.state.idx < this.props.source.paths.length - 1 ? ( +
    this.onShiftGallery(1)} + > + + + +
    + ) : null + if (this.props.source.paths.length > 1) { return (
    -
    this.onShiftGallery(-1)}>
    -
    this.onShiftGallery(1)}>
    + {backArrow} + {forwardArrow}
    ) } @@ -159,7 +177,7 @@ class SourceOverlay extends React.Component { return (
    -
    { e.stopPropagation() }}> +
    e.stopPropagation()}>
    diff --git a/src/scss/main.scss b/src/scss/main.scss index 4d9ded2..e2d7a82 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -7,7 +7,7 @@ @import 'header'; @import 'cardstack'; @import 'narrativecard'; -@import 'mediaoverlay'; +@import 'sourceoverlay'; @import 'map'; @import 'timeline'; @import 'tag-filters'; diff --git a/src/scss/mediaoverlay.scss b/src/scss/sourceoverlay.scss similarity index 82% rename from src/scss/mediaoverlay.scss rename to src/scss/sourceoverlay.scss index 0bad5a2..08fdd7d 100644 --- a/src/scss/mediaoverlay.scss +++ b/src/scss/sourceoverlay.scss @@ -1,5 +1,5 @@ $panel-width: 1000px; -$panel-height: 700px; +$panel-height: 1000px; $vimeo-width: $panel-width - 100; $vimeo-height: $panel-height / 2; @@ -22,20 +22,18 @@ $header-inset: 10px; .mo-container { background-color: rgba(239, 239, 239, 0.9); - // max-width: $panel-width; - // min-width: $panel-width; - // max-height: $panel-height; - // min-height: $panel-height; display: flex; flex-direction: column; align-items: center; - height: 80vh; + height: $panel-height; width: $panel-width; max-width: 90vw; box-shadow: 0 19px 19px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22); .back, .next { width: 30px; + max-width: 30px; + max-height: 30px; height: 30px; background: $darkgrey; color: $offwhite; @@ -84,15 +82,13 @@ $header-inset: 10px; .mo-media-container { flex: 1; - /*display: flex; flex-direction: row; justify-content: center; - align-items: center;*/ display: inline-block; - overflow-x: hidden; + overflow: hidden; box-sizing: border-box; width: 100%; - max-height: 60vh; + max-height: calc(#{$panel-height} - 100px); padding: 20px; font-family: "Lato", Helvetica, sans-serif; @@ -109,7 +105,7 @@ $header-inset: 10px; .media-gallery-controls { height: 100%; display: flex; - justify-content: center; + justify-content: space-between; align-items: center; margin-top: -50%; } @@ -127,7 +123,7 @@ $header-inset: 10px; box-sizing: border-box; min-height: 100px; width: 100%; - padding: $padding; + // padding: $padding; .mo-box-title { display: flex; @@ -184,20 +180,6 @@ $header-inset: 10px; background-color: black; } -.media-player { - min-width: $vimeo-width; - max-width: $vimeo-width; - min-height: $vimeo-height; - max-height: $vimeo-height; - border: none; - - iframe, video { - width: $vimeo-width; - height: $vimeo-height - 50; - border: none; - } -} - .media-controls { padding: 0 50px; } @@ -230,22 +212,22 @@ $header-inset: 10px; display: flex; flex-direction: row; height: 100%; - transition: transform 0.6s ease 0s; width: 100%; - // min-width: $panel-width - 30px; - // min-height: $panel-height; + margin: 0; + transition: transform 0.2s ease; } .source-text-container { padding: 20px; display: flex; justify-content: center; - background: $lightwhite; + // background: $lightwhite; box-sizing: border-box; padding: 0 calc(50% - 400px); overflow-y: scroll; font-family: 'Merriweather', Georgia, serif; line-height: 1.5em; + min-width: 100%; a { color: $darkgrey; @@ -262,37 +244,36 @@ $header-inset: 10px; .source-image-container, .media-player { display: flex; justify-content: center; - width: calc(100% - 20px); - height: 100%; - min-width: calc(100% - 20px); - margin: 0 10px; - background: $lightwhite; - border-radius: 2px; + // height: 100%; + // min-width: calc(100% - 20px); + padding: 20px; } .media-player { box-sizing: border-box; width: 100%; min-width: 100%; - max-width: 100%; height: 100%; min-height: 100%; - max-height: 100%; - padding: 20px 10%; - align-self: center; + align-self: center; } .source-image, .source-video { - max-width: calc(100% - 20px); - max-height: calc(100% - 20px); + // max-width: calc(100% - 20px); + // max-height: calc(100% - 20px); padding: 0px; font-family: 'Lato', Helvetica, sans-serif; } +.source-image-loader { + width: 400px; + height: 400px; +} + .video-react .video-react-progress-control { align-self: center; } .video-react .video-react-control { min-height: 100%; -} \ No newline at end of file +} From c2cd703f0669d3c4db951eb71781b8f6edd70afd Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Thu, 31 Jan 2019 12:42:52 -0500 Subject: [PATCH 03/18] Add enabled sites marker from data object --- src/components/Dashboard.jsx | 5 +++-- src/components/SourceOverlay.jsx | 2 +- src/components/Toolbar.jsx | 2 +- src/reducers/schema/siteSchema.js | 3 ++- src/scss/sourceoverlay.scss | 1 + src/selectors/index.js | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/Dashboard.jsx b/src/components/Dashboard.jsx index 0e770de..4e5168a 100644 --- a/src/components/Dashboard.jsx +++ b/src/components/Dashboard.jsx @@ -92,6 +92,7 @@ class Dashboard extends React.Component { render () { const { actions, app, domain, ui } = this.props + console.log(app) return (
    state, - state => injectSource("Hromadske.tv - Paratroopers Video"), + state => state, + // state => injectSource("Hromadske.tv - Paratroopers Video"), mapDispatchToProps )(Dashboard) diff --git a/src/components/SourceOverlay.jsx b/src/components/SourceOverlay.jsx index aab94bf..c5214c9 100644 --- a/src/components/SourceOverlay.jsx +++ b/src/components/SourceOverlay.jsx @@ -141,7 +141,7 @@ class SourceOverlay extends React.Component {
    ) : null - + console.log(this.props) console.log(this.state.idx) console.log(this.props.source.paths.length) const forwardArrow = this.state.idx < this.props.source.paths.length - 1 ? ( diff --git a/src/components/Toolbar.jsx b/src/components/Toolbar.jsx index c072634..519c6f0 100644 --- a/src/components/Toolbar.jsx +++ b/src/components/Toolbar.jsx @@ -161,7 +161,7 @@ class Toolbar extends React.Component {

    {title}

    {this.renderToolbarTab(0, narrativesLabel, 'timeline')} - {(isCategories) ? this.renderToolbarTab(1, categoriesLabel, 'category') : null} + {(isCategories) ? this.renderToolbarTab(1, categoriesLabel, 'widgets') : null} {(isTags) ? this.renderToolbarTab(2, tagsLabel, 'filter_list') : null}
    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 + if (process.env.features.USE_SITES) return state.domain.sites.filter(s => !!(+s.enabled)) return [] } export const getSources = state => { From 3eed45a5ef408139e1c789af944be68568533405 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Thu, 31 Jan 2019 13:19:39 -0500 Subject: [PATCH 04/18] Allow customization of selected event ring on map, via config --- src/components/Map.jsx | 2 ++ src/components/presentational/Map/SelectedEvents.jsx | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 7074381..aec6800 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -220,6 +220,7 @@ class Map extends React.Component { svg={this.svgRef.current} selected={this.props.app.selected} projectPoint={this.projectPoint} + styles={this.props.ui.mapSelectedEvents} /> ) } @@ -279,6 +280,7 @@ function mapStateToProps (state) { tiles: state.ui.tiles, dom: state.ui.dom, narratives: state.ui.style.narratives, + mapSelectedEvents: state.ui.style.mapSelectedEvents, shapes: state.ui.style.shapes } } diff --git a/src/components/presentational/Map/SelectedEvents.jsx b/src/components/presentational/Map/SelectedEvents.jsx index abbf984..f252686 100644 --- a/src/components/presentational/Map/SelectedEvents.jsx +++ b/src/components/presentational/Map/SelectedEvents.jsx @@ -4,21 +4,23 @@ import { Portal } from 'react-portal' class MapSelectedEvents extends React.Component { renderMarker (event) { const { x, y } = this.props.projectPoint([event.latitude, event.longitude]) + const styles = this.props.styles + const r = styles ? styles.r : 24 return ( ) From 7eed1d6fc3d370939982ff87ffbe1f5ed83d3061 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Thu, 31 Jan 2019 15:28:46 -0500 Subject: [PATCH 05/18] Sync styling of marker in both map and timeline from config --- src/components/Dashboard.jsx | 2 +- src/components/Map.jsx | 2 +- src/components/Timeline.jsx | 8 +++----- src/components/presentational/Timeline/Markers.js | 8 +++++++- src/scss/timeline.scss | 7 ------- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/components/Dashboard.jsx b/src/components/Dashboard.jsx index 4e5168a..19d5637 100644 --- a/src/components/Dashboard.jsx +++ b/src/components/Dashboard.jsx @@ -92,7 +92,7 @@ class Dashboard extends React.Component { render () { const { actions, app, domain, ui } = this.props - console.log(app) + return (
    - {/* */} { +const TimelineMarkers = ({ styles, getEventX, getCategoryY, transitionDuration, selected }) => { function renderMarker (event) { return ( Date: Thu, 31 Jan 2019 15:49:27 -0500 Subject: [PATCH 06/18] Add absolute time limites to timeline --- src/components/Timeline.jsx | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx index b131f73..d5f2e73 100644 --- a/src/components/Timeline.jsx +++ b/src/components/Timeline.jsx @@ -172,9 +172,28 @@ class Timeline extends React.Component { const extent = this.getTimeScaleExtent() const newCentralTime = d3.timeMinute.offset(this.state.scaleX.domain()[0], extent / 2) + let newDomain0 = d3.timeMinute.offset(newCentralTime, -zoom.duration / 2) + let newDomainF = d3.timeMinute.offset(newCentralTime, zoom.duration / 2) + + if (this.props.app.timeline.rangeLimits) { + // If the store contains absolute time limits, + // make sure the zoom doesn't go over them + const minDate = parseDate(this.props.app.timeline.rangeLimits[0]) + const maxDate = parseDate(this.props.app.timeline.rangeLimits[1]) + + if (newDomain0 < minDate) { + newDomain0 = minDate + newDomainF = d3.timeMinute.offset(newDomain0, zoom.duration) + } + if (newDomainF > maxDate) { + newDomainF = maxDate + newDomain0 = d3.timeMinute.offset(newDomainF, -zoom.duration) + } + } + this.setState({ timerange: [ - d3.timeMinute.offset(newCentralTime, -zoom.duration / 2), - d3.timeMinute.offset(newCentralTime, zoom.duration / 2) + newDomain0, + newDomainF ] }, () => { this.props.methods.onUpdateTimerange(this.state.timerange) }) @@ -205,8 +224,18 @@ class Timeline extends React.Component { const timeShift = (drag0 - dragNow) / 1000 const { range } = this.props.app.timeline - const newDomain0 = d3.timeSecond.offset(range[0], timeShift) - const newDomainF = d3.timeSecond.offset(range[1], timeShift) + let newDomain0 = d3.timeSecond.offset(range[0], timeShift) + let newDomainF = d3.timeSecond.offset(range[1], timeShift) + + if (this.props.app.timeline.rangeLimits) { + // If the store contains absolute time limits, + // make sure the zoom doesn't go over them + const minDate = parseDate(this.props.app.timeline.rangeLimits[0]) + const maxDate = parseDate(this.props.app.timeline.rangeLimits[1]) + + newDomain0 = (newDomain0 < minDate) ? minDate : newDomain0 + newDomainF = (newDomainF > maxDate) ? maxDate : newDomainF + } // Updates components without updating timerange this.onSoftTimeRangeUpdate([newDomain0, newDomainF]) From 4e000c8137c73057cf6af3c643235df97a9e2129 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Thu, 31 Jan 2019 18:06:03 -0500 Subject: [PATCH 07/18] Make map events as slices based on categories --- src/components/Map.jsx | 1 + src/components/presentational/Map/Events.jsx | 106 +++++++++++++------ src/scss/map.scss | 3 +- 3 files changed, 77 insertions(+), 33 deletions(-) diff --git a/src/components/Map.jsx b/src/components/Map.jsx index ed1721c..97cf11b 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -192,6 +192,7 @@ class Map extends React.Component { */ styleLocation (location) { const noEvents = location.events.length + return [ null, () => noEvents > 1 ? {noEvents} : null diff --git a/src/components/presentational/Map/Events.jsx b/src/components/presentational/Map/Events.jsx index 968fddf..71d248e 100644 --- a/src/components/presentational/Map/Events.jsx +++ b/src/components/presentational/Map/Events.jsx @@ -2,20 +2,73 @@ import React from 'react' import { Portal } from 'react-portal' function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, narrative, onSelect, svg, locations }) { - // function getLocationEventsDistribution (location) { - // const eventCount = {} - // - // categories.forEach(cat => { - // eventCount[cat.category] = [] - // }) - // - // location.events.forEach((event) => { - // ; - // eventCount[event.category].push(event) - // }) - // - // return eventCount - // } + + function getCoordinatesForPercent(radius, percent) { + const x = radius * Math.cos(2 * Math.PI * percent); + const y = radius * Math.sin(2 * Math.PI * percent); + return [x, y]; + } + + function renderLocationSlicesByCategory(location) { + + const locCategory = location.events.length > 0 ? location.events[0].category : 'default' + const customStyles = styleLocation ? styleLocation(location) : null + const extraStyles = customStyles[0] + + let styles = ({ + fill: getCategoryColor(locCategory), + stroke: '#ffffff', + strokeWidth: 0, + fillOpacity: 0.85, + ...extraStyles + }) + + const colorSlices = location.events.map(e => getCategoryColor(e.category)) + + let cumulativeAngleSweep = 0; + + return ( + + {colorSlices.map((color, idx) => { + const r = 10 + + // Based on the number of events in each location, + // create a slice per event filled with its category color + const [startX, startY] = getCoordinatesForPercent(r, cumulativeAngleSweep); + + cumulativeAngleSweep = (idx + 1) / colorSlices.length; + + const [endX, endY] = getCoordinatesForPercent(r, cumulativeAngleSweep); + + // if the slices are less than 2, take the long arc + const largeArcFlag = (colorSlices.length === 1) ? 1 : 0; + + // create an array and join it just for code readability + const arc = [ + `M ${startX} ${startY}`, // Move + `A ${r} ${r} 0 ${largeArcFlag} 1 ${endX} ${endY}`, // Arc + `L 0 0 `, // Line + `L ${startX} ${startY} `, // Line + ].join(' '); + + const extraStyles = ({ + ...styles, + fill: color + }) + + return ( + + ) + })} + + + ) + } function renderLocation (location) { /** @@ -26,19 +79,8 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, longitude: '32.2' } */ - const { x, y } = projectPoint([location.latitude, location.longitude]) - // const eventsByCategory = getLocationEventsDistribution(location); + const { x, y } = projectPoint([location.latitude, location.longitude]) - const locCategory = location.events.length > 0 ? location.events[0].category : 'default' - const customStyles = styleLocation ? styleLocation(location) : null - const extraStyles = customStyles[0] - const extraRender = customStyles[1] - - const styles = ({ - fill: getCategoryColor(locCategory), - fillOpacity: 1, - ...extraStyles - }) // in narrative mode, only render events in narrative if (narrative) { @@ -51,19 +93,19 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, } } + const customStyles = styleLocation ? styleLocation(location) : null + const extraRender = (customStyles) ? customStyles[1] : null + return ( onSelect(location.events)} + onClick={() => onSelect(location.events)} > - + {renderLocationSlicesByCategory(location)} {extraRender ? extraRender() : null} + ) } diff --git a/src/scss/map.scss b/src/scss/map.scss index 054895e..ec81350 100644 --- a/src/scss/map.scss +++ b/src/scss/map.scss @@ -181,7 +181,8 @@ .location-count { z-index: 100; - fill: #a4a4a4; + font-weight: 900; + fill: #d0d0d0; } From 8147af2620b780def4616fbf64f7aeadfe65e175 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Thu, 31 Jan 2019 18:37:23 -0500 Subject: [PATCH 08/18] Non-structure locations are marked as approximated --- src/components/Card.jsx | 1 + src/components/presentational/Card/Location.js | 4 ++-- src/components/presentational/Map/Events.jsx | 6 +++--- src/scss/map.scss | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/Card.jsx b/src/components/Card.jsx index 4757b32..027ffb9 100644 --- a/src/components/Card.jsx +++ b/src/components/Card.jsx @@ -61,6 +61,7 @@ class Card extends React.Component { ) } diff --git a/src/components/presentational/Card/Location.js b/src/components/presentational/Card/Location.js index e828b67..fb2b26a 100644 --- a/src/components/presentational/Card/Location.js +++ b/src/components/presentational/Card/Location.js @@ -3,13 +3,13 @@ import React from 'react' import copy from '../../../js/data/copy.json' import { isNotNullNorUndefined } from '../../../js/utilities' -const CardLocation = ({ language, location }) => { +const CardLocation = ({ language, location, isPrecise }) => { if (isNotNullNorUndefined(location)) { return (

    location_on - {location} + {`${location}${(isPrecise) ? '' : ' (Approximated)'}`}

    ) diff --git a/src/components/presentational/Map/Events.jsx b/src/components/presentational/Map/Events.jsx index 71d248e..715e4f0 100644 --- a/src/components/presentational/Map/Events.jsx +++ b/src/components/presentational/Map/Events.jsx @@ -28,7 +28,7 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, let cumulativeAngleSweep = 0; return ( - + {colorSlices.map((color, idx) => { const r = 10 @@ -48,7 +48,7 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, `M ${startX} ${startY}`, // Move `A ${r} ${r} 0 ${largeArcFlag} 1 ${endX} ${endY}`, // Arc `L 0 0 `, // Line - `L ${startX} ${startY} `, // Line + `L ${startX} ${startY} Z`, // Line ].join(' '); const extraStyles = ({ @@ -66,7 +66,7 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, ) })} - + ) } diff --git a/src/scss/map.scss b/src/scss/map.scss index ec81350..9c9a463 100644 --- a/src/scss/map.scss +++ b/src/scss/map.scss @@ -170,6 +170,7 @@ } .location-event-marker { + pointer-events: all; fill: $event_default; stroke-width: 0; } From 92d2fa9a218522cd82caa77580b856d1872a6445 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Thu, 31 Jan 2019 18:43:39 -0500 Subject: [PATCH 09/18] Map events are now clickable again --- src/components/presentational/Map/Events.jsx | 1 + src/scss/map.scss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/presentational/Map/Events.jsx b/src/components/presentational/Map/Events.jsx index 715e4f0..e2f9992 100644 --- a/src/components/presentational/Map/Events.jsx +++ b/src/components/presentational/Map/Events.jsx @@ -58,6 +58,7 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, return ( { console.log('uo')}} class='location-event-marker' id={`arc_${idx}`} d={arc} diff --git a/src/scss/map.scss b/src/scss/map.scss index 9c9a463..72d0506 100644 --- a/src/scss/map.scss +++ b/src/scss/map.scss @@ -170,7 +170,7 @@ } .location-event-marker { - pointer-events: all; + pointer-events: all !important; fill: $event_default; stroke-width: 0; } From ac740596fa6b1945c0308d757d6f43939cbff964 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Thu, 31 Jan 2019 19:21:23 -0500 Subject: [PATCH 10/18] Time display on Card display precision --- src/components/Card.jsx | 20 +++++++++++++++++-- .../presentational/Card/Timestamp.js | 7 +++---- src/components/presentational/Map/Events.jsx | 1 - src/reducers/schema/eventSchema.js | 1 + 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/components/Card.jsx b/src/components/Card.jsx index 027ffb9..4c4e141 100644 --- a/src/components/Card.jsx +++ b/src/components/Card.jsx @@ -88,11 +88,27 @@ class Card extends React.Component { // NB: should be internaionalized. renderTimestamp () { + let timelabel = this.makeTimelabel(this.props.event.timestamp) + + let precision = this.props.event.time_display; + if (precision === '_date_only') { + precision = '' + timelabel = timelabel.substring(0, 11) + } else if (precision === '_approximate_date_only') { + precision = ' (Approximate date)' + timelabel = timelabel.substring(0, 11) + } else if (precision === '_approximate_datetime') { + precision = ' (Approximate datetime)' + } else { + timelabel = timelabel.substring(0, 11) + } + return ( this.makeTimelabel(timestamp)} + makeTimelabel={timelabel} language={this.props.language} - timestamp={this.props.event.timestamp} + timelabel={timelabel} + precision={precision} /> ) } diff --git a/src/components/presentational/Card/Timestamp.js b/src/components/presentational/Card/Timestamp.js index ee95994..f08d660 100644 --- a/src/components/presentational/Card/Timestamp.js +++ b/src/components/presentational/Card/Timestamp.js @@ -3,18 +3,17 @@ import React from 'react' import copy from '../../../js/data/copy.json' import { isNotNullNorUndefined } from '../../../js/utilities' -const CardTimestamp = ({ makeTimelabel, language, timestamp }) => { +const CardTimestamp = ({ timelabel, language, precision }) => { // const daytimeLang = copy[language].cardstack.timestamp // const estimatedLang = copy[language].cardstack.estimated const unknownLang = copy[language].cardstack.unknown_time - if (isNotNullNorUndefined(timestamp)) { - const timelabel = makeTimelabel(timestamp) + if (isNotNullNorUndefined(timelabel)) { return (

    today - {timelabel} + {timelabel}{(precision !== '') ? ` - ${precision}` : ''}

    ) diff --git a/src/components/presentational/Map/Events.jsx b/src/components/presentational/Map/Events.jsx index e2f9992..715e4f0 100644 --- a/src/components/presentational/Map/Events.jsx +++ b/src/components/presentational/Map/Events.jsx @@ -58,7 +58,6 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, return ( { console.log('uo')}} class='location-event-marker' id={`arc_${idx}`} d={arc} diff --git a/src/reducers/schema/eventSchema.js b/src/reducers/schema/eventSchema.js index f351f7c..f11c4fa 100644 --- a/src/reducers/schema/eventSchema.js +++ b/src/reducers/schema/eventSchema.js @@ -16,6 +16,7 @@ const eventSchema = Joi.object().keys({ tags: Joi.array().allow(''), comments: Joi.string().allow(''), timestamp: Joi.string(), + time_display: Joi.string().allow(''), // nested narrative___stepStyles: Joi.array() From f0e75093333887c3aa45a6fd2d21303e319b3d01 Mon Sep 17 00:00:00 2001 From: Lachlan Kermode Date: Fri, 1 Feb 2019 15:15:25 +0000 Subject: [PATCH 11/18] maintain aspect ratio in images --- src/components/SourceOverlay.jsx | 5 +---- src/scss/sourceoverlay.scss | 13 ++++++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/SourceOverlay.jsx b/src/components/SourceOverlay.jsx index c5214c9..5b3a254 100644 --- a/src/components/SourceOverlay.jsx +++ b/src/components/SourceOverlay.jsx @@ -141,9 +141,6 @@ class SourceOverlay extends React.Component {
    ) : null - console.log(this.props) - console.log(this.state.idx) - console.log(this.props.source.paths.length) const forwardArrow = this.state.idx < this.props.source.paths.length - 1 ? (
    -
    {this.props.source.title}
    +
    {this.props.source.title.substring(0, 200)}
    {this._renderContent(media)} diff --git a/src/scss/sourceoverlay.scss b/src/scss/sourceoverlay.scss index a580cee..6909eac 100644 --- a/src/scss/sourceoverlay.scss +++ b/src/scss/sourceoverlay.scss @@ -130,6 +130,7 @@ $header-inset: 10px; display: flex; flex-direction: row; justify-content: space-between; + padding: 0 20px; } .mo-box { @@ -222,7 +223,6 @@ $header-inset: 10px; padding: 20px; display: flex; justify-content: center; - // background: $lightwhite; box-sizing: border-box; padding: 0 calc(50% - 400px); overflow-y: scroll; @@ -245,9 +245,8 @@ $header-inset: 10px; .source-image-container, .media-player { display: flex; justify-content: center; - // height: 100%; - // min-width: calc(100% - 20px); padding: 20px; + min-width: calc(100% - 40px); } .media-player { @@ -260,10 +259,14 @@ $header-inset: 10px; } .source-image, .source-video { - // max-width: calc(100% - 20px); - // max-height: calc(100% - 20px); padding: 0px; font-family: 'Lato', Helvetica, sans-serif; + max-width: calc(#{$panel-width} - 100px); + max-height: $panel-height; + margin: auto; + width: auto; + height: auto; + object-fit: contain; } .source-image-loader { From 5caab85d056580e20daa7b17d3d0abf646881b94 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Fri, 1 Feb 2019 10:37:39 -0500 Subject: [PATCH 12/18] Added arrows on Narrtive steps that are movements --- .../presentational/Map/Narratives.jsx | 44 +++++++++++++++++++ src/scss/map.scss | 4 ++ 2 files changed, 48 insertions(+) diff --git a/src/components/presentational/Map/Narratives.jsx b/src/components/presentational/Map/Narratives.jsx index 4424900..9e9f9e4 100644 --- a/src/components/presentational/Map/Narratives.jsx +++ b/src/components/presentational/Map/Narratives.jsx @@ -1,5 +1,7 @@ import React from 'react' import { Portal } from 'react-portal' +import { concatStatic } from 'rxjs/operator/concat'; +import { single } from 'rxjs/operator/single'; function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, projectPoint }) { function getNarrativeStyle (narrativeId) { @@ -61,7 +63,29 @@ function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, function _renderNarrativeStep (p1, p2, styles) { const { stroke, strokeWidth, strokeDasharray, strokeOpacity } = styles + const distance = Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)) + const theta = Math.atan2(p2.y - p1.y, p2.x - p1.x) // Angle of narrative step line + const alpha = Math.atan2(1, 2) // Angle of arrow overture + const edge = 10 // Arrow edge length + const offset = (distance < 24) ? distance / 2 : 24; + + // Arrow corners + const coord0 = { + x: p2.x - offset * Math.cos(theta), + y: p2.y - offset * Math.sin(theta) + } + const coord1 = { + x: coord0.x - edge * Math.cos(-theta - alpha), + y: coord0.y + edge * Math.sin(-theta - alpha) + } + const coord2 = { + x: coord0.x - edge * Math.cos(-theta + alpha), + y: coord0.y + edge * Math.sin(-theta + alpha) + } + + return ( + + {(stroke !== 'none') + ? () + : '' + } + ) } diff --git a/src/scss/map.scss b/src/scss/map.scss index 72d0506..5b78777 100644 --- a/src/scss/map.scss +++ b/src/scss/map.scss @@ -175,6 +175,10 @@ stroke-width: 0; } +.narrative-step-arrow { + pointer-events: all !important; +} + .path-polyline { stroke: $darkgrey; stroke-width: 2px; From dc3db3a63dbef059949e8aaa2cec60ca3f2c17dc Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Fri, 1 Feb 2019 10:41:53 -0500 Subject: [PATCH 13/18] Some cleanup --- .../presentational/Map/Narratives.jsx | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/components/presentational/Map/Narratives.jsx b/src/components/presentational/Map/Narratives.jsx index 9e9f9e4..e73f77f 100644 --- a/src/components/presentational/Map/Narratives.jsx +++ b/src/components/presentational/Map/Narratives.jsx @@ -61,8 +61,7 @@ function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, } } - function _renderNarrativeStep (p1, p2, styles) { - const { stroke, strokeWidth, strokeDasharray, strokeOpacity } = styles + function _renderNarrativeStepArrow (p1, p2, styles) { const distance = Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)) const theta = Math.atan2(p2.y - p1.y, p2.x - p1.x) // Angle of narrative step line const alpha = Math.atan2(1, 2) // Angle of arrow overture @@ -82,7 +81,24 @@ function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, x: coord0.x - edge * Math.cos(-theta + alpha), y: coord0.y + edge * Math.sin(-theta + alpha) } - + + return () + } + + function _renderNarrativeStep (p1, p2, styles) { + const { stroke, strokeWidth, strokeDasharray, strokeOpacity } = styles return ( @@ -102,22 +118,7 @@ function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, }} /> {(stroke !== 'none') - ? () + ? _renderNarrativeStepArrow(p1, p2, styles) : '' } From 0f542777d7d00786b9ea02c5794bdda8b6d29716 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Fri, 1 Feb 2019 10:54:03 -0500 Subject: [PATCH 14/18] Update example config --- example.config.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/example.config.js b/example.config.js index 852b944..51891ce 100644 --- a/example.config.js +++ b/example.config.js @@ -20,19 +20,26 @@ module.exports = { }, store: { app: { - mapAnchor: [31.356397, 34.784818], - filters: { - // timerange: [ - // new Date(2015, 7, 9), - // new Date(2015, 10, 6, 23) - // ] - } + map: { + anchor: [31.356397, 34.784818] + }, + timeline: { + range: [ + new Date(2014, 7, 9), + new Date(2014, 10, 6, 23) + ], + rangeLimits: [ + new Date(2014, 5, 9), + new Date(2018, 1, 6, 23) + ] + } } }, ui: { style: { categories: {}, shapes: {}, - narratives: {} + narratives: {}, + selectedEvent: {}, } } } From 6906adc2586de1d6ccf55156e3815581662783ee Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Fri, 1 Feb 2019 11:06:36 -0500 Subject: [PATCH 15/18] Hide other narratives when one is selected --- src/components/presentational/Map/Narratives.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/presentational/Map/Narratives.jsx b/src/components/presentational/Map/Narratives.jsx index e73f77f..ed73495 100644 --- a/src/components/presentational/Map/Narratives.jsx +++ b/src/components/presentational/Map/Narratives.jsx @@ -30,7 +30,7 @@ function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, // 0 if not in narrative mode, 1 if active narrative, 0.1 if inactive let styles = { strokeOpacity: (n === null) ? 0 - : (step && (n.id === narrative.id)) ? 1 : 0.1, + : (step && (n.id === narrative.id)) ? 1 : 0.0, strokeWidth: 0, strokeDasharray: 'none', stroke: 'none' From 4af0c9c2ef9739a4751eac0d0a89b56ee8cb453f Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Fri, 1 Feb 2019 12:23:11 -0500 Subject: [PATCH 16/18] fix lint errors --- src/components/Card.jsx | 9 ++- src/components/Dashboard.jsx | 3 +- src/components/Timeline.jsx | 2 +- src/components/presentational/Map/Events.jsx | 43 +++++++------- .../presentational/Map/Narratives.jsx | 56 +++++++++---------- .../presentational/Map/SelectedEvents.jsx | 2 +- src/scss/card.scss | 8 ++- src/scss/narrativecard.scss | 6 +- 8 files changed, 67 insertions(+), 62 deletions(-) diff --git a/src/components/Card.jsx b/src/components/Card.jsx index 4c4e141..b8820c6 100644 --- a/src/components/Card.jsx +++ b/src/components/Card.jsx @@ -90,7 +90,7 @@ class Card extends React.Component { renderTimestamp () { let timelabel = this.makeTimelabel(this.props.event.timestamp) - let precision = this.props.event.time_display; + let precision = this.props.event.time_display if (precision === '_date_only') { precision = '' timelabel = timelabel.substring(0, 11) @@ -160,9 +160,12 @@ class Card extends React.Component { } render () { - const { isSelected } = this.props + const { isSelected, idx } = this.props return ( -
  • +
  • {this.renderMain()} {this.state.isOpen ? this.renderExtra() : null} {isSelected ? this.renderCaret() : null} diff --git a/src/components/Dashboard.jsx b/src/components/Dashboard.jsx index 19d5637..10e1485 100644 --- a/src/components/Dashboard.jsx +++ b/src/components/Dashboard.jsx @@ -14,7 +14,7 @@ import InfoPopUp from './InfoPopup.jsx' import Timeline from './Timeline.jsx' import Notification from './Notification.jsx' -import { parseDate, injectSource } from '../js/utilities' +import { parseDate } from '../js/utilities' class Dashboard extends React.Component { constructor (props) { @@ -172,6 +172,5 @@ function mapDispatchToProps (dispatch) { export default connect( state => state, - // state => injectSource("Hromadske.tv - Paratroopers Video"), mapDispatchToProps )(Dashboard) diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx index d5f2e73..2f489ea 100644 --- a/src/components/Timeline.jsx +++ b/src/components/Timeline.jsx @@ -232,7 +232,7 @@ class Timeline extends React.Component { // make sure the zoom doesn't go over them const minDate = parseDate(this.props.app.timeline.rangeLimits[0]) const maxDate = parseDate(this.props.app.timeline.rangeLimits[1]) - + newDomain0 = (newDomain0 < minDate) ? minDate : newDomain0 newDomainF = (newDomainF > maxDate) ? maxDate : newDomainF } diff --git a/src/components/presentational/Map/Events.jsx b/src/components/presentational/Map/Events.jsx index 715e4f0..e4ca77c 100644 --- a/src/components/presentational/Map/Events.jsx +++ b/src/components/presentational/Map/Events.jsx @@ -2,15 +2,13 @@ import React from 'react' import { Portal } from 'react-portal' function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, narrative, onSelect, svg, locations }) { - - function getCoordinatesForPercent(radius, percent) { - const x = radius * Math.cos(2 * Math.PI * percent); - const y = radius * Math.sin(2 * Math.PI * percent); - return [x, y]; + function getCoordinatesForPercent (radius, percent) { + const x = radius * Math.cos(2 * Math.PI * percent) + const y = radius * Math.sin(2 * Math.PI * percent) + return [x, y] } - function renderLocationSlicesByCategory(location) { - + function renderLocationSlicesByCategory (location) { const locCategory = location.events.length > 0 ? location.events[0].category : 'default' const customStyles = styleLocation ? styleLocation(location) : null const extraStyles = customStyles[0] @@ -25,7 +23,7 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, const colorSlices = location.events.map(e => getCategoryColor(e.category)) - let cumulativeAngleSweep = 0; + let cumulativeAngleSweep = 0 return ( @@ -34,22 +32,22 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, // Based on the number of events in each location, // create a slice per event filled with its category color - const [startX, startY] = getCoordinatesForPercent(r, cumulativeAngleSweep); - - cumulativeAngleSweep = (idx + 1) / colorSlices.length; - - const [endX, endY] = getCoordinatesForPercent(r, cumulativeAngleSweep); + const [startX, startY] = getCoordinatesForPercent(r, cumulativeAngleSweep) + + cumulativeAngleSweep = (idx + 1) / colorSlices.length + + const [endX, endY] = getCoordinatesForPercent(r, cumulativeAngleSweep) // if the slices are less than 2, take the long arc - const largeArcFlag = (colorSlices.length === 1) ? 1 : 0; + const largeArcFlag = (colorSlices.length === 1) ? 1 : 0 // create an array and join it just for code readability const arc = [ - `M ${startX} ${startY}`, // Move + `M ${startX} ${startY}`, // Move `A ${r} ${r} 0 ${largeArcFlag} 1 ${endX} ${endY}`, // Arc - `L 0 0 `, // Line - `L ${startX} ${startY} Z`, // Line - ].join(' '); + `L 0 0 `, // Line + `L ${startX} ${startY} Z` // Line + ].join(' ') const extraStyles = ({ ...styles, @@ -65,9 +63,9 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, /> ) })} - + - ) + ) } function renderLocation (location) { @@ -79,8 +77,7 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, longitude: '32.2' } */ - const { x, y } = projectPoint([location.latitude, location.longitude]) - + const { x, y } = projectPoint([location.latitude, location.longitude]) // in narrative mode, only render events in narrative if (narrative) { @@ -100,7 +97,7 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, onSelect(location.events)} + onClick={() => onSelect(location.events)} > {renderLocationSlicesByCategory(location)} {extraRender ? extraRender() : null} diff --git a/src/components/presentational/Map/Narratives.jsx b/src/components/presentational/Map/Narratives.jsx index ed73495..e3957d7 100644 --- a/src/components/presentational/Map/Narratives.jsx +++ b/src/components/presentational/Map/Narratives.jsx @@ -1,7 +1,7 @@ import React from 'react' import { Portal } from 'react-portal' -import { concatStatic } from 'rxjs/operator/concat'; -import { single } from 'rxjs/operator/single'; +// import { concatStatic } from 'rxjs/operator/concat' +// import { single } from 'rxjs/operator/single' function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, projectPoint }) { function getNarrativeStyle (narrativeId) { @@ -66,22 +66,22 @@ function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, const theta = Math.atan2(p2.y - p1.y, p2.x - p1.x) // Angle of narrative step line const alpha = Math.atan2(1, 2) // Angle of arrow overture const edge = 10 // Arrow edge length - const offset = (distance < 24) ? distance / 2 : 24; + const offset = (distance < 24) ? distance / 2 : 24 // Arrow corners const coord0 = { x: p2.x - offset * Math.cos(theta), y: p2.y - offset * Math.sin(theta) - } + } const coord1 = { x: coord0.x - edge * Math.cos(-theta - alpha), y: coord0.y + edge * Math.sin(-theta - alpha) - } + } const coord2 = { x: coord0.x - edge * Math.cos(-theta + alpha), y: coord0.y + edge * Math.sin(-theta + alpha) - } - + } + return () + />) } function _renderNarrativeStep (p1, p2, styles) { @@ -102,25 +102,25 @@ function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives, return ( - onSelectNarrative(n)} - style={{ - strokeWidth, - strokeDasharray, - strokeOpacity, - stroke - }} - /> - {(stroke !== 'none') - ? _renderNarrativeStepArrow(p1, p2, styles) - : '' - } + onSelectNarrative(n)} + style={{ + strokeWidth, + strokeDasharray, + strokeOpacity, + stroke + }} + /> + {(stroke !== 'none') + ? _renderNarrativeStepArrow(p1, p2, styles) + : '' + } ) } diff --git a/src/components/presentational/Map/SelectedEvents.jsx b/src/components/presentational/Map/SelectedEvents.jsx index f252686..f47b538 100644 --- a/src/components/presentational/Map/SelectedEvents.jsx +++ b/src/components/presentational/Map/SelectedEvents.jsx @@ -20,7 +20,7 @@ class MapSelectedEvents extends React.Component { stroke-linejoin='round' stroke-dasharray={styles ? styles['stroke-dasharray'] : '2,2'} fill='none' - d={`M0,0a${r},${r} 0 1,0 ${r*2},0 a${r},${r} 0 1,0 -${r*2},0 `} + d={`M0,0a${r},${r} 0 1,0 ${r * 2},0 a${r},${r} 0 1,0 -${r * 2},0 `} /> ) diff --git a/src/scss/card.scss b/src/scss/card.scss index a1b03de..a6484b5 100644 --- a/src/scss/card.scss +++ b/src/scss/card.scss @@ -5,13 +5,19 @@ border: 1px solid $black; // border-radius: 3px; transition: 0.2 ease; - background: $darkwhite; + background: $midwhite; color: $darkgrey; box-shadow: 0 19px 19px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22); font-size: $large; line-height: $xxlarge; height: auto; opacity: 0.9; + transition: background-color 0.4s; + + &:hover { + background: $lightwhite; + transition: background-color 0.4s; + } h4 { margin-bottom: 0; diff --git a/src/scss/narrativecard.scss b/src/scss/narrativecard.scss index adf5e8b..37b6c22 100644 --- a/src/scss/narrativecard.scss +++ b/src/scss/narrativecard.scss @@ -122,17 +122,17 @@ NARRATIVE INFO } .material-icons { - font-size: 60pt; + font-size: 40pt; color: $offwhite; transition: color 0.2s ease; &.disabled { - color: $darkgrey; + color: $midgrey; } &:hover { cursor: pointer; - color: $darkgrey; + color: $midwhite; } } } From 3ea3de84b287f75c9cbcd61b9d4c9dc738734055 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Fri, 1 Feb 2019 14:07:00 -0500 Subject: [PATCH 17/18] Automatically scroll to current Card in Narrative --- src/components/Card.jsx | 6 +++- src/components/CardStack.jsx | 68 ++++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/components/Card.jsx b/src/components/Card.jsx index b8820c6..31978fb 100644 --- a/src/components/Card.jsx +++ b/src/components/Card.jsx @@ -161,10 +161,12 @@ class Card extends React.Component { render () { const { isSelected, idx } = this.props + return (
  • {this.renderMain()} {this.state.isOpen ? this.renderExtra() : null} @@ -174,4 +176,6 @@ class Card extends React.Component { } } -export default Card +// The ref to each card will be used in CardStack for programmatic scrolling +export default React.forwardRef((props, ref) => ); + diff --git a/src/components/CardStack.jsx b/src/components/CardStack.jsx index f2e908c..9f23157 100644 --- a/src/components/CardStack.jsx +++ b/src/components/CardStack.jsx @@ -6,13 +6,63 @@ import Card from './Card.jsx' import copy from '../js/data/copy.json' class CardStack extends React.Component { + + constructor() { + super() + this.refs = {} + this.refCardStack = React.createRef() + this.refCardStackContent = React.createRef() + } + + componentDidUpdate() { + const isNarrative = !!this.props.narrative + + if (isNarrative) { + this.scrollToCard() + } + } + + scrollToCard() { + const duration = 500 + const element = this.refCardStack.current + const cardScroll = this.refs[this.props.narrative.current].current.offsetTop - 20 + + let start = element.scrollTop; + let change = cardScroll - start; + let currentTime = 0; + const increment = 20; + + //t = current time + //b = start value + //c = change in value + //d = duration + Math.easeInOutQuad = function (t, b, c, d) { + t /= d / 2; + if (t < 1) return c / 2 * t * t + b; + t--; + return - c / 2 * (t * (t - 2) - 1) + b; + }; + + const animateScroll = function() { + currentTime += increment; + const val = Math.easeInOutQuad(currentTime, start, change, duration); + element.scrollTop = val; + if (currentTime < duration) setTimeout(animateScroll, increment); + }; + animateScroll(); + } + renderCards (events, selections) { // if no selections provided, select all if (!selections) { selections = events.map(e => true) } + this.refs = [] - return events.map((event, idx) => ( - { + const thisRef = React.createRef() + this.refs[idx] = thisRef + return ( - )) + />) + }) } renderSelectedCards () { @@ -38,9 +88,10 @@ class CardStack extends React.Component { renderNarrativeCards () { const { narrative } = this.props - const showing = narrative.steps.slice(narrative.current) + const showing = narrative.steps + const selections = showing - .map((_, idx) => (idx === 0)) + .map((_, idx) => (idx === narrative.current)) return this.renderCards(showing, selections) } @@ -74,7 +125,9 @@ class CardStack extends React.Component { renderNarrativeContent () { return ( -
    +
      {this.renderNarrativeCards()}
    @@ -102,6 +155,7 @@ class CardStack extends React.Component { return (
    Date: Mon, 4 Feb 2019 10:06:26 +0000 Subject: [PATCH 18/18] lint --- src/components/Card.jsx | 3 +-- src/components/CardStack.jsx | 47 ++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/components/Card.jsx b/src/components/Card.jsx index 31978fb..5b27314 100644 --- a/src/components/Card.jsx +++ b/src/components/Card.jsx @@ -177,5 +177,4 @@ class Card extends React.Component { } // The ref to each card will be used in CardStack for programmatic scrolling -export default React.forwardRef((props, ref) => ); - +export default React.forwardRef((props, ref) => ) diff --git a/src/components/CardStack.jsx b/src/components/CardStack.jsx index 9f23157..c23a83c 100644 --- a/src/components/CardStack.jsx +++ b/src/components/CardStack.jsx @@ -6,15 +6,14 @@ import Card from './Card.jsx' import copy from '../js/data/copy.json' class CardStack extends React.Component { - - constructor() { + constructor () { super() this.refs = {} this.refCardStack = React.createRef() this.refCardStackContent = React.createRef() } - componentDidUpdate() { + componentDidUpdate () { const isNarrative = !!this.props.narrative if (isNarrative) { @@ -22,34 +21,34 @@ class CardStack extends React.Component { } } - scrollToCard() { + scrollToCard () { const duration = 500 const element = this.refCardStack.current const cardScroll = this.refs[this.props.narrative.current].current.offsetTop - 20 - let start = element.scrollTop; - let change = cardScroll - start; - let currentTime = 0; - const increment = 20; + let start = element.scrollTop + let change = cardScroll - start + let currentTime = 0 + const increment = 20 - //t = current time - //b = start value - //c = change in value - //d = duration + // t = current time + // b = start value + // c = change in value + // d = duration Math.easeInOutQuad = function (t, b, c, d) { - t /= d / 2; - if (t < 1) return c / 2 * t * t + b; - t--; - return - c / 2 * (t * (t - 2) - 1) + b; - }; + t /= d / 2 + if (t < 1) return c / 2 * t * t + b + t -= 1 + return -c / 2 * (t * (t - 2) - 1) + b + } - const animateScroll = function() { - currentTime += increment; - const val = Math.easeInOutQuad(currentTime, start, change, duration); - element.scrollTop = val; - if (currentTime < duration) setTimeout(animateScroll, increment); - }; - animateScroll(); + const animateScroll = function () { + currentTime += increment + const val = Math.easeInOutQuad(currentTime, start, change, duration) + element.scrollTop = val + if (currentTime < duration) setTimeout(animateScroll, increment) + } + animateScroll() } renderCards (events, selections) {