Merge pull request #81 from forensic-architecture/topic/restyle-narrative-card

Topic/restyle narrative card
This commit is contained in:
Lachlan Kermode
2019-01-09 17:47:43 +00:00
committed by GitHub
10 changed files with 236 additions and 101 deletions

View File

@@ -9,15 +9,13 @@ import LoadingOverlay from './presentational/LoadingOverlay'
import Map from './Map.jsx'
import Toolbar from './Toolbar.jsx'
import CardStack from './CardStack.jsx'
import NarrativeCard from './NarrativeCard.js'
import NarrativeControls from './presentational/NarrativeControls.js'
import InfoPopUp from './InfoPopup.jsx'
import Timeline from './Timeline.jsx'
import Notification from './Notification.jsx'
import { parseDate } from '../js/utilities'
import { injectNarrative } from '../js/utilities'
class Dashboard extends React.Component {
constructor(props) {
super(props)
@@ -119,13 +117,6 @@ class Dashboard extends React.Component {
getCategoryColor: category => this.getCategoryColor(category)
}}
/>
<NarrativeCard
methods={{
onNext: () => this.moveInNarrative(1),
onPrev: () => this.moveInNarrative(-1),
onSelectNarrative: this.setNarrative
}}
/>
<CardStack
onViewSource={this.handleViewSource}
onSelect={this.handleSelect}
@@ -134,6 +125,17 @@ class Dashboard extends React.Component {
getNarrativeLinks={event => this.getNarrativeLinks(event)}
getCategoryColor={category => this.getCategoryColor(category)}
/>
<NarrativeControls
narrative={!!app.narrative ? {
...app.narrative,
current: app.narrativeState.current
} : null}
methods={{
onNext: () => this.moveInNarrative(1),
onPrev: () => this.moveInNarrative(-1),
onSelectNarrative: this.setNarrative
}}
/>
<InfoPopUp
ui={ui}
app={app}
@@ -167,16 +169,6 @@ function mapDispatchToProps(dispatch) {
}
}
function injectSource(id) {
return state => ({
...state,
app: {
...state.app,
source: state.domain.sources[id]
}
})
}
export default connect(
state => state,
mapDispatchToProps,

View File

@@ -1,65 +0,0 @@
import React from 'react'
import { connect } from 'react-redux'
import { selectActiveNarrative } from '../selectors'
function NarrativeCard ({ narrative, methods }) {
const { onSelectNarrative, onNext, onPrev } = methods
function renderClose() {
return (
<button
className='side-menu-burg is-active'
onClick={() => { onSelectNarrative(null) }}
>
<span></span>
</button>
)
}
function _renderActions(current, steps) {
const prevExists = current !== 0
const nextExists = current < steps.length - 1
return (
<div className='actions'>
<div
className={`${prevExists ? '' : 'disabled'} action`}
onClick={prevExists ? onPrev : null}>&larr;
</div>
<div
className={`${nextExists ? '' : 'disabled'} action`}
onClick={nextExists ? onNext : null}>&rarr;
</div>
</div>
)
}
// no display if no narrative
if (!narrative) return null
const { steps, current } = narrative
if (steps[current]) {
const step = steps[current]
return (
<div className='narrative-info'>
{renderClose()}
<h3>{narrative.label}</h3>
<p>{narrative.description}</p>
<h6>
<i className='material-icons left'>location_on</i>
{current + 1}/{steps.length}. {step.location}
</h6>
{_renderActions(current, steps)}
</div>
)
} else {
return null
}
}
function mapStateToProps(state) {
return {
narrative: selectActiveNarrative(state)
}
}
export default connect(mapStateToProps)(NarrativeCard)

View File

@@ -28,7 +28,7 @@ class Timeline extends React.Component {
height_controls: 115,
margin_left: 120,
margin_top: 20,
trackHeight: 80
trackHeight: 80
},
scaleX: null,
scaleY: null,
@@ -228,7 +228,7 @@ class Timeline extends React.Component {
transitionDuration={this.state.transitionDuration}
scaleX={this.state.scaleX}
/>
<TimelineCategories
<TimelineCategories
dims={dims}
onDragStart={() => { this.onDragStart() }}
onDrag={() => { this.onDrag() }}
@@ -264,7 +264,7 @@ class Timeline extends React.Component {
onSelect={this.props.methods.onSelect}
/>
</svg>
);
)
}
render() {

View File

@@ -0,0 +1,16 @@
import React from 'react'
export default ({ isDisabled, direction, onClickHandler }) => {
return (
<div
className={`narrative-adjust ${direction}`}
onClick={!isDisabled ? onClickHandler : null}
>
<i
className={`material-icons ${isDisabled ? 'disabled' : ''}`}
>
{`chevron_${direction}`}
</i>
</div>
)
}

View File

@@ -0,0 +1,40 @@
import React from 'react'
import { connect } from 'react-redux'
import { selectActiveNarrative } from '../../selectors'
function NarrativeCard ({ narrative }) {
// no display if no narrative
const { steps, current } = narrative
if (steps[current]) {
const step = steps[current]
return (
<div className='narrative-info'>
<div className='narrative-info-header'>
<div className='count-container'>
<div className='count'>
{current + 1}/{steps.length}
</div>
</div>
<div>
<h3>{narrative.label}</h3>
</div>
</div>
{/* <i className='material-icons left'>location_on</i> */}
{/* {_renderActions(current, steps)} */}
<p className='narrative-info-desc'>{narrative.description}</p>
</div>
)
} else {
return null
}
}
function mapStateToProps(state) {
return {
narrative: selectActiveNarrative(state)
}
}
export default connect(mapStateToProps)(NarrativeCard)

View File

@@ -0,0 +1,17 @@
import React from 'react'
export default ({ onClickHandler, closeMsg }) => {
return (
<div
className='narrative-close'
onClick={onClickHandler}
>
<button
className='side-menu-burg is-active'
>
<span></span>
</button>
<div className='close-text'>{closeMsg}</div>
</div>
)
}

View File

@@ -0,0 +1,32 @@
import React from 'react'
import NarrativeCard from './NarrativeCard'
import NarrativeAdjust from './NarrativeAdjust'
import NarrativeClose from './NarrativeClose'
export default ({ narrative, methods }) => {
if (!narrative) return null
const { current, steps } = narrative
const prevExists = current !== 0
const nextExists = current < steps.length - 1
return (
<React.Fragment>
<NarrativeCard narrative={narrative} />
<NarrativeAdjust
isDisabled={!prevExists}
direction='left'
onClickHandler={methods.onPrev}
/>
<NarrativeAdjust
isDisabled={!nextExists}
direction='right'
onClickHandler={methods.onNext}
/>
<NarrativeClose
onClickHandler={() => methods.onSelectNarrative(null)}
closeMsg='-- exit from narrative --'
/>
</React.Fragment>
)
}

View File

@@ -42,10 +42,6 @@
margin: 5px 0 10px 0;
padding-bottom: 10px;
&.details {
border-bottom: 1px solid $lightwhite;
}
.card-cell {
flex: 1;
}

View File

@@ -17,10 +17,10 @@ $timeline-height: 170px;
color: white;
&.narrative-mode {
right: auto;
left: 10px;
top: $narrative-info-max-height + 12px;
height: calc(100% - #{$narrative-info-max-height} - #{$timeline-height} - 12px);
right: 10px;
left: auto;
top: $narrative-info-max-height + 12px + 20px;
height: calc(100% - #{$narrative-info-max-height} - #{$timeline-height} - 12px - 20px);
}
&.full-height {

View File

@@ -1,25 +1,51 @@
$narrative-info-width: 370px;
$timeline-height: 170px;
/*
NARRATIVE INFO
*/
.narrative-info {
position: fixed;
top: 10px;
left: 10px;
// height: auto;
top: 30px;
left: auto;
right: 10px;
height: 170px;
width: $narrative-info-width;
box-sizing: border-box;
padding: 15px;
max-height: calc(100% - 250px);
overflow: auto;
box-shadow: 0 19px 38px rgba($black, 0.3), 0 15px 12px rgba($black, 0.22);
background: $black;
border: 1px solid $midgrey;
color: $offwhite;
font-family: 'Merriweather', 'Georgia', serif;
.narrative-info-header {
display: flex;
justify-content: space-between;
align-items: stretch;
border-bottom: 1px solid $darkwhite;
padding: 0 15px;
.count-container {
display: flex;
justify-content: center;
align-items: center;
border-right: 1px solid $darkwhite;
}
.count {
position: relative;
padding-right: 15px;
}
}
.narrative-info-desc {
overflow: auto;
}
p {
padding: 0 15px;
}
h3, h6 {
text-align: center;
}
@@ -75,3 +101,84 @@ NARRATIVE INFO
}
}
}
.narrative-adjust {
position: fixed;
// top: calc(50vh - 100pt);
bottom: $timeline-height;
// top: 0;
right: auto;
background-color: rgba(0,0,0,0.8);
z-index: 15; // z-index of card-stack is 10
&.left {
right: calc(#{$narrative-info-width} - 70px);
}
&.right {
// right: calc(#{$narrative-info-width} + 10px);
right: 10px;
}
.material-icons {
font-size: 60pt;
color: $offwhite;
transition: color 0.2s ease;
&.disabled {
color: $darkgrey;
}
&:hover {
cursor: pointer;
color: $darkgrey;
}
}
}
.narrative-close {
display: flex;
justify-content: flex-start;
position: fixed;
padding: 2px 5px 0 5px;
// right: $narrative-info-width - 15px - 10px;
right: 10px;
top: 5px;
width: $narrative-info-width - 10px - 2px;
// width: 15px;
background-color: black;
height: 20px;
transition: background-color 0.2s ease;
border: 1px solid black;
button {
height: 15px;
width: 15px;
}
.close-text {
display: none;
color: $midgrey;
flex: 1;
width: 100%;
justify-content: center;
font-size: 10pt;
}
// disable whitening of crosshair on hover
button {
span, span:before, span:after {
background: $midwhite !important;
}
}
&:hover {
cursor: pointer;
background-color: $offwhite;
color: black;
.close-text {
display: flex;
}
}
}