Additional card cleanup

This commit is contained in:
Franc Camps-Febrer
2018-11-27 12:44:40 -05:00
parent b658356448
commit 20051db90a
20 changed files with 347 additions and 191 deletions

View File

@@ -1,27 +1,35 @@
import '../scss/main.scss';
import copy from '../js/data/copy.json';
import {isNotNullNorUndefined} from '../js/data/utilities';
import React from 'react';
import Spinner from './presentational/Spinner';
import CardTimestamp from './presentational/CardTimestamp';
import CardLocation from './presentational/CardLocation';
import CardCaret from './presentational/CardCaret';
import CardTags from './presentational/CardTags';
import CardSummary from './presentational/CardSummary';
import CardSource from './presentational/CardSource';
import CardCategory from './presentational/CardCategory';
import CardNarrative from './presentational/CardNarrative';
class Card extends React.Component {
constructor(props) {
super(props);
this.state = {
isFolded: true
isHighlighted: false
};
this.toggle = this.toggle.bind(this);
}
toggle() {
if (this.state.isFolded) {
this.props.highlight(this.props.event);
} else {
this.props.highlight();
}
this.setState({
isFolded: !this.state.isFolded
isHighlighted: !this.state.isHighlighted
}, () => {
if (this.state.isHighlighted) {
this.props.highlight(this.props.event);
} else {
this.props.highlight();
}
});
}
@@ -31,192 +39,148 @@ class Card extends React.Component {
return 'other';
}
renderWarning() {
const warning_lang = copy[this.props.language].cardstack.warning;
if (this.props.event.tags) {
const tagsArray = this.props.event.tags.split(",");
/* TODO: This needs to be generalized */
if (tagsArray.some(tag => {
return (tag.name === 'contradicción' ||
tag.name === 'declaración con sospecha de tortura')
})) {
return (<div className="warning event-card-section">{warning_lang}</div>);
}
}
makeTimelabel(timestamp) {
if (timestamp === null) return null;
const parsedTimestamp = this.props.tools.parser(timestamp);
const timelabel = this.props.tools.formatterWithYear(parsedTimestamp);
return timelabel;
}
renderCategory() {
const category_lang = copy[this.props.language].cardstack.category;
const categoryTitle = copy[this.props.language].cardstack.category;
const colorType = this.getCategoryColorClass(this.props.event.category);
const categoryLabel = this.props.getCategoryLabel(this.props.event.category);
return (<div className="event-card-section category">
<h4>{category_lang}</h4>
<p><span className={`color-category ${colorType}`}/>{categoryLabel}</p>
</div>);
return (
<CardCategory
categoryTitle={categoryTitle}
categoryLabel={categoryLabel}
colorType={colorType}
/>
);
}
renderSummary() {
const summary = copy[this.props.language].cardstack.description;
const desc = this.props.event.description;
const description = (this.state.isFolded) ? `${desc.substring(0, 40)}...` : desc;
return (<div className="event-card-section summary">
<h4>{summary}</h4>
<p>{description}</p>
</div>);
return (
<CardSummary
language={this.props.language}
description={this.props.event.description}
isHighlighted={this.state.isHighlighted}
/>
)
}
renderTags() {
const people_lang = copy[this.props.language].cardstack.people;
const peopleTags = []; //this.props.event.tags.filter(tag => tag.type === 'people');
return (<div className="event-card-section tags">
<h4>{people_lang}</h4>
<p>{
peopleTags.map((tag, idx) => {
return (<span className="tag">
{tag.name}
{
(idx < peopleTags.length - 1)
? ','
: ''
}
</span>);
})
}</p>
</div>);
return (
<CardTags
tags={this.props.tags || []}
language={this.props.language}
/>
)
}
// NB: is this function for a future feature? Should also be internaionalized.
renderLocation() {
const location_lang = copy[this.props.language].cardstack.location;
if (isNotNullNorUndefined(this.props.event.location)) {
return (<p className="event-card-section location">
<h4>{location_lang}</h4>
<p>{this.props.event.location}</p>
</p>);
} else {
return (<p className="event-card-section location">
<h4>{location_lang}</h4>
<p>Sin localización conocida.</p>
</p>);
}
return (
<CardLocation
language={this.props.language}
location={this.props.event.location}
/>
)
}
renderSource() {
const source_lang = copy[this.props.language].cardstack.source;
return (<div className="event-card-section source">
<h4>{source_lang}</h4>
<p>{this.props.event.source}</p>
</div>);
}
getTimeLabel(){
const timestamp = this.props.tools.parser(this.props.event.timestamp);
const timelabel = this.props.tools.formatterWithYear(timestamp);
return timelabel;
return (
<CardSource
language={this.props.language}
source={this.props.event.source}
/>
)
}
// NB: should be internaionalized.
renderTimestamp() {
const daytime_lang = copy[this.props.language].cardstack.timestamp;
const estimated_lang = copy[this.props.language].cardstack.estimated;
if (isNotNullNorUndefined(this.props.event.timestamp)) {
const timelabel = this.getTimeLabel();
return (<div className="event-card-section timestamp">
<h4>{daytime_lang}</h4>
{timelabel}
</div>);
} else {
return (<div className="event-card-section timestamp">
<h4>{daytime_lang}</h4>
Hora no conocida
</div>);
}
}
renderCardLink(event, direction) {
if (event !== null) {
const timelabel = this.getTimeLabel();
return (<a onClick={() => this.props.select([event])}>
{`${timelabel} - ${event.location}`}
</a>);
}
return (<a className="disabled">None</a>);
return (
<CardTimestamp
makeTimelabel={(timestamp) => this.makeTimelabel(timestamp)}
language={this.props.language}
timestamp={this.props.event.timestamp}
/>
);
}
renderNarrative() {
const links = this.props.getNarrativeLinks(this.props.event);
if (links !== null) {
return (<div className="event-card-section">
<h4>Connected events</h4>
<p>Next: {this.renderCardLink(links.next, 'next')}</p>
<p>Previous: {this.renderCardLink(links.prev, 'prev')}</p>
</div>);
return (
<CardNarrative
select={(event) => this.props.select([event])}
makeTimelabel={(timestamp) => this.makeTimelabel(timestamp)}
next={links.next}
prev={links.prev}
/>
)
}
}
renderSpinner() {
return (<div className="spinner">
<div className="double-bounce1"></div>
<div className="double-bounce2"></div>
</div>);
renderLoadingCard() {
return (
<li className='event-card'>
<div className="card-bottomhalf">
<Spinner />
</div>
</li>
);
}
renderHeader() {
return (<div className="card-collapsed">
{this.renderWarning()}
{this.renderCategory()}
{this.renderTimestamp()}
{this.renderSummary()}
</div>);
return (
<div className="card-collapsed">
{this.renderCategory()}
{this.renderTimestamp()}
{this.renderSummary()}
</div>
);
}
renderContent() {
if (this.state.isFolded) {
return (<div className="card-bottomhalf folded"></div>);
} else if (this.props.isFetchingEvents) {
return (<div className="card-bottomhalf">
{this.renderSpinner()}
</div>);
if (!this.state.isHighlighted) {
return (
<div className="card-bottomhalf folded"></div>
);
} else {
return (<div className="card-bottomhalf">
{this.renderLocation()}
{this.renderTags()}
{this.renderSource()}
{this.renderNarrative()}
</div>);
return (
<div className="card-bottomhalf">
{this.renderLocation()}
{this.renderTags()}
{this.renderSource()}
{this.renderNarrative()}
</div>
);
}
}
renderArrow() {
let classes = (this.state.isFolded)
? 'arrow-down folded'
: 'arrow-down';
return (<div className="card-toggle" onClick={() => this.toggle()}>
<p>
<i className={classes}></i>
</p>
</div>);
renderCaret() {
return (
<CardCaret
toggle={() => this.toggle()}
isHighlighted={this.state.isHighlighted}
/>
)
}
render() {
if (this.props.isLoading) {
return (<li className='event-card'>
<div className="card-bottomhalf">
{this.renderSpinner()}
</div>
</li>);
return this.renderLoadingCard();
} else {
return (<li className='event-card'>
{this.renderHeader()}
{this.renderContent()}
{this.renderArrow()}
</li>);
return (
<li className='event-card'>
{this.renderHeader()}
{this.renderContent()}
{this.renderCaret()}
</li>
);
}
}
}

View File

@@ -1,4 +1,3 @@
import '../scss/main.scss';
import React from 'react';
import Card from './Card.jsx';
import copy from '../js/data/copy.json';
@@ -20,17 +19,17 @@ class CardStack extends React.Component {
return (
<Card
event={event}
shouldCardUpdate={shouldCardUpdate}
language={this.props.language}
tools={this.props.tools}
isFetchingEvents={this.props.isFetchingEvents}
getNarrativeLinks={this.props.getNarrativeLinks}
getCategoryGroup={this.props.getCategoryGroup}
getCategoryGroupColor={this.props.getCategoryGroupColor}
getCategoryLabel={this.props.getCategoryLabel}
highlight={this.props.highlight}
select={this.props.select}
event={event}
shouldCardUpdate={shouldCardUpdate}
language={this.props.language}
tools={this.props.tools}
isLoading={this.props.isFetchingEvents}
getNarrativeLinks={this.props.getNarrativeLinks}
getCategoryGroup={this.props.getCategoryGroup}
getCategoryGroupColor={this.props.getCategoryGroupColor}
getCategoryLabel={this.props.getCategoryLabel}
highlight={this.props.highlight}
select={this.props.select}
/>
);
});
@@ -50,30 +49,34 @@ class CardStack extends React.Component {
return '';
}
renderIsLoading() {
return (
<div id="card-stack" className={`card-stack ${this.props.isCardstack ? '' : ' folded'}`}>
<div
id='card-stack-header'
className='card-stack-header'
onClick={() => this.props.toggle('TOGGLE_CARDSTACK')}
>
<button className="side-menu-burg is-active"><span></span></button>
<p className="header-copy top">{copy[this.props.language].loading}</p>
</div>
<div id="card-stack-content" className="card-stack-content">
<ul>
<Card
language={this.props.language}
isLoading={true}
/>
</ul>
</div>
</div>
)
}
render() {
const header_lang = copy[this.props.language].cardstack.header;
if (this.props.isFetchingEvents) {
return (
<div id="card-stack" className={`card-stack ${this.props.isCardstack ? '' : ' folded'}`}>
<div
id='card-stack-header'
className='card-stack-header'
onClick={() => this.props.toggle('TOGGLE_CARDSTACK')}
>
<button className="side-menu-burg is-active"><span></span></button>
<p className="header-copy top">{copy[this.props.language].loading}</p>
</div>
<div id="card-stack-content" className="card-stack-content">
<ul>
<Card
language={this.props.language}
isLoading={true}
/>
</ul>
</div>
</div>
);
return this.renderIsLoading();
} else if (this.props.selected.length > 0) {
return (
<div id="card-stack" className={`card-stack ${this.props.isCardstack ? '' : ' folded'}`}>

View File

@@ -1,4 +1,3 @@
import '../scss/main.scss';
import React from 'react';
import { bindActionCreators } from 'redux';

View File

@@ -1,6 +1,5 @@
import '../scss/main.scss';
import React from 'react';
import Checkbox from './Checkbox.jsx';
import Checkbox from './presentational/Checkbox.jsx';
class TagFilter extends React.Component {
constructor(props) {

View File

@@ -1,6 +1,5 @@
import '../scss/main.scss';
import React from 'react';
import Checkbox from './Checkbox.jsx';
import Checkbox from './presentational/Checkbox.jsx';
class TagListPanel extends React.Component {

View File

@@ -1,4 +1,3 @@
import '../scss/main.scss';
import copy from '../js/data/copy.json';
import React from 'react';
import TimelineLogic from '../js/timeline/timeline.js';
@@ -77,6 +76,7 @@ class Timeline extends React.Component {
let classes = `timeline-wrapper ${(this.state.isFolded) ? ' folded' : ''}`;
const date0 = this.props.tools.formatterWithYear(this.props.timerange[0]);
const date1 = this.props.tools.formatterWithYear(this.props.timerange[1]);
return (
<div className={classes}>
<div className="timeline-header">

View File

@@ -1,4 +1,4 @@
import '../scss/main.scss';
import React from 'react';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
import Search from './Search.jsx';

View File

@@ -1,4 +1,3 @@
import '../scss/main.scss';
import React from 'react';
import Map from '../js/map/map.js';
import { areEqual } from '../js/data/utilities.js';

View File

@@ -0,0 +1,18 @@
import React from 'react';
const CardCaret = ({ isHighlighted, toggle }) => {
let classes = (isHighlighted)
? 'arrow-down'
: 'arrow-down folded';
return (
<div className="card-toggle" onClick={toggle}>
<p>
<i className={classes}></i>
</p>
</div>
);
}
export default CardCaret;

View File

@@ -0,0 +1,13 @@
import React from 'react';
const CardCategory = ({ categoryTitle, categoryLabel, colorType }) => (
<div className="event-card-section category">
<h4>{categoryTitle}</h4>
<p>
<span className={`color-category ${colorType}`}/>
{categoryLabel}
</p>
</div>
);
export default CardCategory;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import copy from '../../js/data/copy.json';
import {isNotNullNorUndefined} from '../../js/data/utilities';
const CardLocation = ({ language, location }) => {
const location_lang = copy[language].cardstack.location;
if (isNotNullNorUndefined(location)) {
return (
<p className="event-card-section location">
<h4>{location_lang}</h4>
<p>{location}</p>
</p>
);
} else {
return (
<p className="event-card-section location">
<h4>{location_lang}</h4>
<p>Sin localización conocida.</p>
</p>
);
}
}
export default CardLocation;

View File

@@ -0,0 +1,13 @@
import React from 'react';
import CardNarrativeLink from './CardNarrativeLink';
const CardNarrative = (props) => (
<div className="event-card-section">
<h4>Connected events</h4>
<p>Next: <CardNarrativeLink {...props} event={props.next}/></p>
<p>Previous: <CardNarrativeLink {...props} event={props.prev} /></p>
</div>
);
export default CardNarrative;

View File

@@ -0,0 +1,17 @@
import React from 'react';
const CardNarrativeLink = ({ event, makeTimelabel, select }) => {
if (event !== null) {
const timelabel = makeTimelabel(event.timestamp);
return (
<a onClick={() => select(event)}>
{`${timelabel} - ${event.location}`}
</a>
);
}
return (<a className="disabled">None</a>);
}
export default CardNarrativeLink;

View File

@@ -0,0 +1,16 @@
import React from 'react';
import copy from '../../js/data/copy.json';
const CardSource = ({ source, language }) => {
const source_lang = copy[language].cardstack.source;
return (
<div className="event-card-section source">
<h4>{source_lang}</h4>
<p>{source}</p>
</div>
);
}
export default CardSource;

View File

@@ -0,0 +1,18 @@
import React from 'react';
import copy from '../../js/data/copy.json';
const CardSummary = ({ language, description, isHighlighted }) => {
const summary = copy[language].cardstack.description;
const descriptionText = (isHighlighted) ? description : `${description.substring(0, 40)}...`;
return (
<div className="event-card-section summary">
<h4>{summary}</h4>
<p>{descriptionText}</p>
</div>
);
}
export default CardSummary;

View File

@@ -0,0 +1,29 @@
import React from 'react';
import copy from '../../js/data/copy.json';
const CardTags = ({ tags, language }) => {
const label = copy[language].cardstack.people;
return (
<div className="event-card-section tags">
<h4>{label}</h4>
<p>{
tags.map((tag, idx) => {
return (
<span className="tag">
{tag.name}
{
(idx < tags.length - 1)
? ','
: ''
}
</span>
);
})
}</p>
</div>
);
}
export default CardTags;

View File

@@ -0,0 +1,30 @@
import React from 'react';
import copy from '../../js/data/copy.json';
import {isNotNullNorUndefined} from '../../js/data/utilities';
const CardTimestamp = ({ makeTimelabel, language, timestamp }) => {
const daytime_lang = copy[language].cardstack.timestamp;
const estimated_lang = copy[language].cardstack.estimated;
const unknown_lang = copy[language].cardstack.unknown_time;
if (isNotNullNorUndefined(timestamp)) {
const timelabel = makeTimelabel(timestamp);
return (
<div className="event-card-section timestamp">
<h4>{daytime_lang}</h4>
{timelabel}
</div>
);
} else {
return (
<div className="event-card-section timestamp">
<h4>{daytime_lang}</h4>
{unknown_lang}
</div>
);
}
}
export default CardTimestamp;

View File

@@ -1,4 +1,3 @@
import '../scss/main.scss';
import React from 'react';
export default ({ label, isActive, onClickCheckbox }) => (

View File

@@ -0,0 +1,12 @@
import React from 'react';
const Spinner = ({}) => {
return (
<div className="spinner">
<div className="double-bounce1"></div>
<div className="double-bounce2"></div>
</div>
)
}
export default Spinner;

View File

@@ -56,6 +56,7 @@
"cardstack": {
"header": "eventos seleccionados",
"unknown_location": "Localización desconocida",
"unknown_time": "Día y hora desconocida",
"timestamp": "Día y hora",
"estimated": "aproximado",
"location": "Localización",
@@ -129,6 +130,7 @@
"timestamp": "Day and time",
"unknown_location": "Unknown location",
"estimated": "estimated",
"unknown_time": "Unknown time",
"location": "Localization",
"incident_type": "Type of action",
"description": "Summary of facts",