Files
ukraine-timemap/src/components/time/atoms/Events.js
Lachlan Kermode ea1dc45ea7 Fix warnings
2022-03-03 11:59:41 -05:00

260 lines
6.3 KiB
JavaScript

import React from "react";
import DatetimeBar from "./DatetimeBar";
import DatetimeSquare from "./DatetimeSquare";
import DatetimeStar from "./DatetimeStar";
import DatetimeTriangle from "./DatetimeTriangle";
import DatetimePentagon from "./DatetimePentagon";
import Project from "./Project";
import ColoredMarkers from "../../atoms/ColoredMarkers";
import hash from "object-hash";
import {
calcOpacity,
getEventCategories,
zipColorsToPercentages,
calculateColorPercentages,
isLatitude,
isLongitude,
} from "../../../common/utilities";
import { AVAILABLE_SHAPES } from "../../../common/constants";
function renderDot(event, styles, props) {
const colorPercentages = calculateColorPercentages(
[event],
props.coloringSet
);
return (
<g
key={hash(event)}
className="timeline-event"
onClick={props.onSelect}
transform={`translate(${props.x}, ${props.y})`}
>
<ColoredMarkers
radius={props.eventRadius}
colorPercentMap={zipColorsToPercentages(
props.filterColors,
colorPercentages
)}
styles={{
...styles,
}}
className="event"
/>
</g>
);
}
function renderBar(event, styles, props) {
const fillOpacity = props.features.GRAPH_NONLOCATED
? event.projectOffset >= 0
? styles.opacity
: 0.5
: calcOpacity(1);
return (
<DatetimeBar
onSelect={props.onSelect}
category={event.category}
events={[event]}
x={props.x}
y={props.dims.marginTop}
width={props.eventRadius / 4}
height={props.dims.trackHeight}
styleProps={{ ...styles, fillOpacity }}
highlights={props.highlights}
/>
);
}
function renderDiamond(event, styles, props) {
return (
<DatetimeSquare
onSelect={props.onSelect}
x={props.x}
y={props.y - 1.8 * props.eventRadius}
r={1.8 * props.eventRadius}
styleProps={styles}
transform={`rotate(45, ${props.x}, ${props.y})`}
/>
);
}
function renderSquare(event, styles, props) {
return (
<DatetimeSquare
onSelect={props.onSelect}
x={props.x}
y={props.y - (1.8 * props.eventRadius) / 2}
r={1.8 * props.eventRadius}
styleProps={styles}
/>
);
}
function renderTriangle(event, styles, props) {
return (
<DatetimeTriangle
onSelect={props.onSelect}
x={props.x}
y={props.y}
r={1.5 * props.eventRadius}
styleProps={styles}
/>
);
}
function renderPentagon(event, styles, props) {
return (
<DatetimePentagon
onSelect={props.onSelect}
x={props.x}
y={props.y}
r={1.5 * props.eventRadius}
styleProps={styles}
/>
);
}
function renderStar(event, styles, props) {
return (
<DatetimeStar
onSelect={props.onSelect}
x={props.x}
y={props.y}
r={1.8 * props.eventRadius}
styleProps={{ ...styles, fillRule: "nonzero" }}
transform={`rotate(180, ${props.x}, ${props.y})`}
/>
);
}
const TimelineEvents = ({
events,
projects,
categories,
narrative,
getDatetimeX,
getY,
getCategoryColor,
getHighlights,
onSelect,
transitionDuration,
dims,
features,
setLoading,
setNotLoading,
eventRadius,
filterColors,
coloringSet,
}) => {
const narIds = narrative ? narrative.steps.map((s) => s.id) : [];
function renderEvent(acc, event) {
if (narrative) {
if (!narIds.includes(event.id)) {
return null;
}
}
const isDot =
(isLatitude(event.latitude) && isLongitude(event.longitude)) ||
(features.GRAPH_NONLOCATED && event.projectOffset !== -1);
const { shape: eventShape } = event;
let renderShape = isDot ? renderDot : renderBar;
if (eventShape && eventShape.shape) {
if (eventShape.shape === AVAILABLE_SHAPES.BAR) {
renderShape = renderBar;
} else if (eventShape.shape === AVAILABLE_SHAPES.DIAMOND) {
renderShape = renderDiamond;
} else if (eventShape.shape === AVAILABLE_SHAPES.STAR) {
renderShape = renderStar;
} else if (eventShape.shape === AVAILABLE_SHAPES.TRIANGLE) {
renderShape = renderTriangle;
} else if (eventShape.shape === AVAILABLE_SHAPES.PENTAGON) {
renderShape = renderPentagon;
} else if (eventShape.shape === AVAILABLE_SHAPES.SQUARE) {
renderShape = renderSquare;
} else {
renderShape = renderDot;
}
}
// if an event has multiple categories, it should be rendered on each of
// those timelines: so we create as many event 'shadows' as there are
// categories
const evShadows = getEventCategories(event, categories).map((cat) => {
const y = getY({ ...event, category: cat });
const colour = event.colour ? event.colour : getCategoryColor(cat.title);
const styles = {
fill: colour,
fillOpacity: y > 0 ? calcOpacity(1) : 0,
transition: `transform ${transitionDuration / 1000}s ease`,
};
return { y, styles };
});
function getRender(y, styles) {
return renderShape(event, styles, {
x: getDatetimeX(event.datetime),
y,
eventRadius,
onSelect: () => onSelect(event),
dims,
highlights: features.HIGHLIGHT_GROUPS
? getHighlights(
event.filters[
features.HIGHLIGHT_GROUPS.filterIndexIndicatingGroup
]
)
: [],
features,
filterColors,
coloringSet,
});
}
if (evShadows.length === 0) {
acc.push(getRender(getY(event), { fill: getCategoryColor(null) }));
} else {
evShadows.forEach((evShadow) => {
acc.push(getRender(evShadow.y, evShadow.styles));
});
}
return acc;
}
let renderProjects = () => null;
if (features.GRAPH_NONLOCATED) {
renderProjects = function () {
return (
<>
{Object.values(projects).map((project) => (
<Project
key={project.id}
{...project}
eventRadius={eventRadius}
onClick={() => console.log(project)}
getX={getDatetimeX}
dims={dims}
colour={getCategoryColor(project.category)}
/>
))}
</>
);
};
}
return (
<g clipPath="url(#clip)">
{renderProjects()}
{events.reduce(renderEvent, [])}
</g>
);
};
export default TimelineEvents;