mirror of
https://github.com/bellingcat/ukraine-timemap.git
synced 2026-06-11 12:58:35 +03:00
184 lines
4.5 KiB
JavaScript
184 lines
4.5 KiB
JavaScript
import React from 'react'
|
|
import { connect } from 'react-redux'
|
|
import * as selectors from '../selectors'
|
|
|
|
import Card from './Card.jsx'
|
|
import copy from '../common/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
|
|
|
|
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 -= 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()
|
|
}
|
|
|
|
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 (<Card
|
|
event={event}
|
|
ref={thisRef}
|
|
sourceError={this.props.sourceError}
|
|
language={this.props.language}
|
|
isLoading={this.props.isLoading}
|
|
isSelected={selections[idx]}
|
|
getNarrativeLinks={this.props.getNarrativeLinks}
|
|
getCategoryGroup={this.props.getCategoryGroup}
|
|
getCategoryColor={this.props.getCategoryColor}
|
|
getCategoryLabel={this.props.getCategoryLabel}
|
|
onViewSource={this.props.onViewSource}
|
|
onHighlight={this.props.onHighlight}
|
|
onSelect={this.props.onSelect}
|
|
/>)
|
|
})
|
|
}
|
|
|
|
renderSelectedCards () {
|
|
const { selected } = this.props
|
|
if (selected.length > 0) {
|
|
return this.renderCards(selected)
|
|
}
|
|
return null
|
|
}
|
|
|
|
renderNarrativeCards () {
|
|
const { narrative } = this.props
|
|
const showing = narrative.steps
|
|
|
|
const selections = showing
|
|
.map((_, idx) => (idx === narrative.current))
|
|
|
|
return this.renderCards(showing, selections)
|
|
}
|
|
|
|
renderCardStackHeader () {
|
|
const headerLang = copy[this.props.language].cardstack.header
|
|
|
|
return (
|
|
<div
|
|
id='card-stack-header'
|
|
className='card-stack-header'
|
|
onClick={() => this.props.onToggleCardstack()}
|
|
>
|
|
<button className='side-menu-burg is-active'><span /></button>
|
|
<p className='header-copy top'>
|
|
{`${this.props.selected.length} ${headerLang}`}
|
|
</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
renderCardStackContent () {
|
|
return (
|
|
<div id='card-stack-content' className='card-stack-content'>
|
|
<ul>
|
|
{this.renderSelectedCards()}
|
|
</ul>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
renderNarrativeContent () {
|
|
return (
|
|
<div id='card-stack-content' className='card-stack-content'
|
|
ref={this.refCardStackContent}
|
|
>
|
|
<ul>
|
|
{this.renderNarrativeCards()}
|
|
</ul>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
render () {
|
|
const { isCardstack, selected, narrative } = this.props
|
|
|
|
if (selected.length > 0) {
|
|
if (!narrative) {
|
|
return (
|
|
<div
|
|
id='card-stack'
|
|
className={`card-stack
|
|
${isCardstack ? '' : ' folded'}`
|
|
}
|
|
>
|
|
{this.renderCardStackHeader()}
|
|
{this.renderCardStackContent()}
|
|
</div>
|
|
)
|
|
} else {
|
|
return (
|
|
<div
|
|
id='card-stack'
|
|
ref={this.refCardStack}
|
|
className={`card-stack narrative-mode
|
|
${isCardstack ? '' : ' folded'}`
|
|
}
|
|
>
|
|
{this.renderNarrativeContent()}
|
|
</div>
|
|
)
|
|
}
|
|
}
|
|
|
|
return <div />
|
|
}
|
|
}
|
|
|
|
function mapStateToProps (state) {
|
|
return {
|
|
narrative: selectors.selectActiveNarrative(state),
|
|
selected: selectors.selectSelected(state),
|
|
sourceError: state.app.errors.source,
|
|
language: state.app.language,
|
|
isCardstack: state.app.flags.isCardstack,
|
|
isLoading: state.app.flags.isFetchingSources
|
|
}
|
|
}
|
|
|
|
export default connect(mapStateToProps)(CardStack)
|