mirror of
https://github.com/bellingcat/ukraine-timemap.git
synced 2026-06-12 13:28:36 +03:00
WIP: filters as narratives always
This commit is contained in:
@@ -136,8 +136,10 @@ class CardStack extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { isCardstack, selected, narrative } = this.props
|
||||
const { isCardstack, selected, narrative, timelineDims } = this.props
|
||||
|
||||
// TODO: make '237px', which is the narrative header, less hard-coded
|
||||
const height = `calc(100% - 237px - ${timelineDims.height}px)`
|
||||
if (selected.length > 0) {
|
||||
if (!narrative) {
|
||||
return (
|
||||
@@ -159,6 +161,7 @@ class CardStack extends React.Component {
|
||||
className={`card-stack narrative-mode
|
||||
${isCardstack ? '' : ' folded'}`
|
||||
}
|
||||
style={{height}}
|
||||
>
|
||||
{this.renderNarrativeContent()}
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import NarrativeControls from './presentational/Narrative/Controls.js'
|
||||
import InfoPopUp from './InfoPopup.jsx'
|
||||
import Timeline from './Timeline.jsx'
|
||||
import Notification from './Notification.jsx'
|
||||
import StateOptions from './StateOptions.jsx'
|
||||
import StaticPage from './StaticPage'
|
||||
import TemplateCover from './TemplateCover'
|
||||
|
||||
@@ -27,6 +28,7 @@ class Dashboard extends React.Component {
|
||||
this.handleViewSource = this.handleViewSource.bind(this)
|
||||
this.handleHighlight = this.handleHighlight.bind(this)
|
||||
this.setNarrative = this.setNarrative.bind(this)
|
||||
this.setNarrativeFromFilters = this.setNarrativeFromFilters.bind(this)
|
||||
this.moveInNarrative = this.moveInNarrative.bind(this)
|
||||
this.handleSelect = this.handleSelect.bind(this)
|
||||
this.getCategoryColor = this.getCategoryColor.bind(this)
|
||||
@@ -115,13 +117,42 @@ class Dashboard extends React.Component {
|
||||
setNarrative (narrative) {
|
||||
// only handleSelect if narrative is not null
|
||||
if (narrative) {
|
||||
this.props.actions.clearFilter('filters')
|
||||
this.props.actions.clearFilter('categories')
|
||||
this.handleSelect([ narrative.steps[0] ])
|
||||
}
|
||||
this.props.actions.updateNarrative(narrative)
|
||||
}
|
||||
|
||||
setNarrativeFromFilters (withSteps) {
|
||||
const { app, domain } = this.props
|
||||
const activeFilters = app.filters.filters
|
||||
|
||||
if (activeFilters.length === 0) {
|
||||
alert('No filters selected, cant narrativise')
|
||||
return
|
||||
}
|
||||
|
||||
const evs = domain.events.filter(ev => {
|
||||
let hasOne = false
|
||||
// add event if it has at least one matching filter
|
||||
for (let i = 0; i < activeFilters.length; i++) {
|
||||
if (ev.filters.includes(activeFilters[i])) {
|
||||
hasOne = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (hasOne) return true
|
||||
return false
|
||||
})
|
||||
|
||||
const name = activeFilters.join('-')
|
||||
this.setNarrative({
|
||||
id: name,
|
||||
label: name,
|
||||
description: '',
|
||||
steps: evs
|
||||
})
|
||||
}
|
||||
|
||||
moveInNarrative (amt) {
|
||||
const { current } = this.props.app.narrativeState
|
||||
const { narrative } = this.props.app
|
||||
@@ -183,6 +214,7 @@ class Dashboard extends React.Component {
|
||||
}}
|
||||
/>
|
||||
<CardStack
|
||||
timelineDims={app.timeline.dimensions}
|
||||
onViewSource={this.handleViewSource}
|
||||
onSelect={this.handleSelect}
|
||||
onHighlight={this.handleHighlight}
|
||||
@@ -190,6 +222,11 @@ class Dashboard extends React.Component {
|
||||
getNarrativeLinks={event => this.getNarrativeLinks(event)}
|
||||
getCategoryColor={this.getCategoryColor}
|
||||
/>
|
||||
<StateOptions
|
||||
showing={!app.narrative && app.filters.filters.length > 0}
|
||||
timelineDims={app.timeline.dimensions}
|
||||
onClickHandler={this.setNarrativeFromFilters}
|
||||
/>
|
||||
<NarrativeControls
|
||||
narrative={app.narrative ? {
|
||||
...app.narrative,
|
||||
|
||||
@@ -174,10 +174,11 @@ class Map extends React.Component {
|
||||
}
|
||||
|
||||
renderNarratives () {
|
||||
const hasNarratives = this.props.domain.narratives.length > 0
|
||||
return (
|
||||
<Narratives
|
||||
svg={this.svgRef.current}
|
||||
narratives={this.props.domain.narratives}
|
||||
narratives={hasNarratives ? this.props.domain.narratives : [this.props.app.narrative]}
|
||||
projectPoint={this.projectPoint}
|
||||
narrative={this.props.app.narrative}
|
||||
styles={this.props.ui.narratives}
|
||||
|
||||
19
src/components/StateOptions.jsx
Normal file
19
src/components/StateOptions.jsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
export default ({ showing, onClickHandler, timelineDims }) => {
|
||||
const [checked, setChecked] = useState(true)
|
||||
const handleCheck = () => setChecked(!checked)
|
||||
const onNarrativise = () => onClickHandler(checked)
|
||||
|
||||
if (!showing) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <div className='stateoptions-panel' style={{ bottom: timelineDims.height }}>
|
||||
<div>
|
||||
<div className='button' onClick={onNarrativise}>Narrativise</div>
|
||||
<label for='withlines'>Connect by lines</label>
|
||||
<input name='withlines' onClick={handleCheck} checked={checked} type='checkbox' />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -132,19 +132,31 @@ function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives,
|
||||
const arrows = []
|
||||
|
||||
let lastMarked = null
|
||||
|
||||
for (let idx = 0; idx < n.steps.length; idx += 1) {
|
||||
const step = n.steps[idx]
|
||||
const _idx = step.narratives.indexOf(n.id)
|
||||
const stepStyle = step.narrative___stepStyles[_idx]
|
||||
|
||||
if (stepStyle !== 'None') {
|
||||
if (lastMarked) {
|
||||
arrows.push(renderBetweenSteps(lastMarked, step, styles.stepStyles[stepStyle]))
|
||||
}
|
||||
lastMarked = step
|
||||
if (lastMarked) {
|
||||
arrows.push(renderBetweenSteps(lastMarked, step, {
|
||||
strokeWidth: '1px',
|
||||
stroke: step.colour
|
||||
}))
|
||||
}
|
||||
lastMarked = step
|
||||
}
|
||||
|
||||
// for (let idx = 0; idx < n.steps.length; idx += 1) {
|
||||
// const step = n.steps[idx]
|
||||
// const _idx = step.narratives.indexOf(n.id)
|
||||
// const stepStyle = step.narrative___stepStyles[_idx]
|
||||
//
|
||||
// if (stepStyle !== 'None') {
|
||||
// if (lastMarked) {
|
||||
// arrows.push(renderBetweenSteps(lastMarked, step, styles.stepStyles[stepStyle]))
|
||||
// }
|
||||
// lastMarked = step
|
||||
// }
|
||||
// }
|
||||
|
||||
return arrows
|
||||
}
|
||||
|
||||
@@ -153,10 +165,11 @@ function MapNarratives ({ styles, onSelectNarrative, svg, narrative, narratives,
|
||||
|
||||
return (
|
||||
<g id={narrativeId} className='narrative'>
|
||||
{(features.NARRATIVE_STEP_STYLES
|
||||
? renderBetweenMarked(n)
|
||||
: renderFullNarrative(n)
|
||||
)}
|
||||
{renderBetweenMarked(n)}
|
||||
{/* {(features.NARRATIVE_STEP_STYLES */}
|
||||
{/* ? renderBetweenMarked(n) */}
|
||||
{/* : renderFullNarrative(n) */}
|
||||
{/* )} */}
|
||||
</g>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,3 +12,4 @@
|
||||
@import 'notification';
|
||||
@import 'mediaplayer';
|
||||
@import 'cover';
|
||||
@import 'stateoptions';
|
||||
|
||||
28
src/scss/stateoptions.scss
Normal file
28
src/scss/stateoptions.scss
Normal file
@@ -0,0 +1,28 @@
|
||||
.stateoptions-panel {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
box-sizing: border-box;
|
||||
margin: 1px 0 0 0;
|
||||
padding: 15px;
|
||||
border: 1px solid $black;
|
||||
transition: 0.2 ease;
|
||||
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;
|
||||
|
||||
.button {
|
||||
border: 1px solid black;
|
||||
padding: 0.3em;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
background-color: black;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user