feat(main): style changes and mobile version (#58)

Co-authored-by: msramalho <19508417+msramalho@users.noreply.github.com>
This commit is contained in:
Andrew Mudrov
2022-10-31 21:40:35 +04:00
committed by GitHub
parent 64d6b34469
commit 53d6f389e3
37 changed files with 966 additions and 584 deletions

View File

@@ -1,7 +1,7 @@
const one_day = 1440;
module.exports = {
title: "ukraine",
display_title: "Civilian Harm in Ukraine",
display_title: "Civilian Harm\nin Ukraine",
SERVER_ROOT: "https://ukraine.bellingcat.com/ukraine-server",
EVENTS_EXT: "/api/ukraine/export_events/deeprows",
SOURCES_EXT: "/api/ukraine/export_sources/deepids",
@@ -29,8 +29,8 @@ module.exports = {
},
timeline: {
dimensions: {
height: 150,
contentHeight: 150,
height: 90,
contentHeight: 90,
},
zoomLevels: [
{ label: "Zoom to 2 weeks", duration: 14 * one_day },
@@ -59,9 +59,8 @@ module.exports = {
},
},
intro: [
'<div style="display:flex; flex-direction: row; width: 100%; min-width: calc(100% - 20px); max-width: 25vw; margin-top: 20px; gap: 20px; justify-content: space-between;"><img style="max-width:35vw; width:50%;" src="https://bellingcat-embeds.ams3.cdn.digitaloceanspaces.com/ukraine-timemap/cover01-s.jpg" frameborder="0"></img><img style="max-width:35vw; width:50%;" src="https://bellingcat-embeds.ams3.cdn.digitaloceanspaces.com/ukraine-timemap/cover02-s.jpg" frameborder="0"></img></div>',
'<div class="two-columns"><div class="two-columns_column"><figure><img style="width: 100%; display:block;" src="https://bellingcat-embeds.ams3.cdn.digitaloceanspaces.com/ukraine-timemap/cover01-s.jpg" frameborder="0"><figcaption>Image: Vyacheslav Madiyevskyy/Reuters</figcaption></figure></div><div class="two-columns_column"><figure><img style="width: 100%; display:block;" src="https://bellingcat-embeds.ams3.cdn.digitaloceanspaces.com/ukraine-timemap/cover02-s.jpg" frameborder="0"><figcaption>Image: Järva Teataja/Scanpix Baltics via Reuters</figcaption></figure></div></div>',
'This map plots out and highlights incidents that have resulted in potential civilian impact or harm since Russia began its invasion of Ukraine. The incidents detailed have been collected by Bellingcat researchers. Included in the map are instances where civilian areas and infrastructure have been damaged or destroyed, where the presence of civilian injuries are visible and/or there is the presence of immobile civilian bodies. Collection for the incidences contained in this map began on February 24, 2022. Users can explore incidents by date and location. We intend this to be a living project that will continue to be updated as long as the conflict persists. For more detailed information about the entries included in this map, please refer to our methodology and explainer article which can be read <a href="https://www.bellingcat.com/news/2022/03/17/hospitals-bombed-and-apartments-destroyed-mapping-incidents-of-civilian-harm-in-ukraine/" >here</a>. ',
"Image left: Vyacheslav Madiyevskyy/Reuters. Image right: Järva Teataja/Scanpix Baltics via Reuters.",
],
flags: { isInfopoup: false, isCover: false },

View File

@@ -1,23 +1,39 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>TimeMap - Forensic Architecture</title>
<link rel="stylesheet" href="https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.css">
<link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
<!-- <link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet"> -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Styles are always go to the head, never the body -->
<style>
@media (hover: none) {
#id {
display: none;
}
#nodisplay {
display: block;
}
}
@media (hover: hover) {
#nodisplay {
display: none;
}
}
</style>
</head>
<body>
<style>
@media (hover: none) {
#id { display: none; }
#nodisplay { display: block; }
}
@media (hover: hover) {
#nodisplay {display: none; }
}
</style>
<div class="page">
<div class="page">
<div id="explore-app"></div>
@@ -27,4 +43,5 @@
</div>
</div>
</body>
</html>
</html>

View File

@@ -12,11 +12,26 @@
"Cada evento estará coloreado según la persona que dio el testimonio del evento."
],
"colors": [
{ "class": "category_group00", "label": "Categoría Grupo 00" },
{ "class": "category_group01", "label": "Categoría Grupo 01" },
{ "class": "category_group02", "label": "Categoría Grupo 02" },
{ "class": "category_group03", "label": "Categoría Grupo 03" },
{ "class": "other", "label": "Otras categorías" }
{
"class": "category_group00",
"label": "Categoría Grupo 00"
},
{
"class": "category_group01",
"label": "Categoría Grupo 01"
},
{
"class": "category_group02",
"label": "Categoría Grupo 02"
},
{
"class": "category_group03",
"label": "Categoría Grupo 03"
},
{
"class": "other",
"label": "Otras categorías"
}
]
},
"default": {
@@ -54,12 +69,30 @@
},
"timeline": {
"zoomLevels": [
{ "label": "20 años", "duration": 10512000 },
{ "label": "2 años", "duration": 1051200 },
{ "label": "3 meses", "duration": 129600 },
{ "label": "3 días", "duration": 4320 },
{ "label": "12 horas", "duration": 720 },
{ "label": "1 hora", "duration": 60 }
{
"label": "20 años",
"duration": 10512000
},
{
"label": "2 años",
"duration": 1051200
},
{
"label": "3 meses",
"duration": 129600
},
{
"label": "3 días",
"duration": 4320
},
{
"label": "12 horas",
"duration": 720
},
{
"label": "1 hora",
"duration": 60
}
],
"labels_title": "Testimonios",
"labels": [
@@ -106,11 +139,26 @@
"Each event is colored according the person that gave category of the event."
],
"colors": [
{ "class": "category_group00", "label": "Category Group 00" },
{ "class": "category_group01", "label": "Category Group 01" },
{ "class": "category_group02", "label": "Category Group 02" },
{ "class": "category_group03", "label": "Category Group 03" },
{ "class": "other", "label": "Other categories" }
{
"class": "category_group00",
"label": "Category Group 00"
},
{
"class": "category_group01",
"label": "Category Group 01"
},
{
"class": "category_group02",
"label": "Category Group 02"
},
{
"class": "category_group03",
"label": "Category Group 03"
},
{
"class": "other",
"label": "Other categories"
}
]
},
"default": {
@@ -149,7 +197,7 @@
"filters": "Filters",
"filters_label": "Filters",
"explore_by_filter__title": "Explore by filter",
"explore_by_filter__description": "'Filters' refer to the types of incident. Select multiple filters to introduce colour-coding, up to a maximum of four filters.<br><br>If no filters are selected, all datapoints are displayed.",
"explore_by_filter__description": "'Filters' refer to the types of incident. Select multiple filters to introduce colour-coding, up to a maximum of four filters.<br><br><span class='hint'>If no filters are selected, all datapoints are displayed.</span>",
"categories": "Categories",
"categories_label": "Categories",
"explore_by_category__title": "Explore events by category",
@@ -187,7 +235,7 @@
"Testimony Group 03",
"Other"
],
"info": "Seeing %n events that occurred between",
"info": "Showing <span>%n events</span> that occurred between",
"default_categories_label": ""
},
"cardstack": {

View File

@@ -2,7 +2,6 @@ import React from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { isMobileOnly } from "react-device-detect";
import * as actions from "../actions";
import * as selectors from "../selectors";
@@ -248,18 +247,8 @@ class Dashboard extends React.Component {
}
renderIntroPopup(styles) {
const checkMobile = isMobileOnly || window.innerWidth < 600;
const { app, actions } = this.props;
const extraContent = checkMobile ? (
<div style={{ position: "relative", bottom: 0 }}>
<h3 style={{ color: "var(--error-red)" }}>
This platform may not work correctly on mobile. If possible, please
re-visit the site on a device with a larger screen.
</h3>
</div>
) : null;
let searchParams = new URLSearchParams(window.location.href.split("?")[1]);
let rememberDismissedIntro =
localStorage.getItem("rememberDismissedIntro") === "true";
@@ -281,9 +270,7 @@ class Dashboard extends React.Component {
}}
content={app.intro}
styles={styles}
>
{extraContent}
</Popup>
></Popup>
);
} else {
return null;
@@ -292,33 +279,12 @@ class Dashboard extends React.Component {
render() {
const { actions, app, domain, timeline, features } = this.props;
const dateHeight = 80;
const padding = 2;
const checkMobile = isMobileOnly || window.innerWidth < 600;
const popupStyles = {
height: checkMobile ? "100vh" : "fit-content",
display: checkMobile ? "block" : "table",
width: checkMobile
? "100vw"
: window.innerWidth > 768
? "60vw"
: "calc(100vw - var(--toolbar-width))",
maxWidth: checkMobile ? "100vw" : 600,
maxHeight: checkMobile
? "100vh"
: window.innerHeight > 768
? `calc(100vh - ${timeline.dimensions.height}px - ${dateHeight}px)`
: "100vh",
left: checkMobile ? padding : "var(--toolbar-width)",
top: 0,
overflowY: "scroll",
textAlign: "justify",
};
const popupStyles = {};
return (
<div>
{checkMobile ? null : (
{
<Toolbar
isNarrative={!!app.associations.narrative}
domain={domain}
@@ -332,7 +298,7 @@ class Dashboard extends React.Component {
onSelectNarrative: this.setNarrative,
}}
/>
)}
}
<Space
kind={"map" in app ? "map" : "space3d"}
onKeyDown={this.onKeyDown}
@@ -344,7 +310,7 @@ class Dashboard extends React.Component {
: (ev) => this.handleSelect(ev, 1),
}}
/>
{checkMobile ? null : (
{
<Timeline
onKeyDown={this.onKeyDown}
methods={{
@@ -355,7 +321,7 @@ class Dashboard extends React.Component {
getCategoryColor: this.getCategoryColor,
}}
/>
)}
}
<CardStack
timelineDims={timeline.dimensions}
onViewSource={this.handleViewSource}

View File

@@ -186,10 +186,7 @@ class TemplateCover extends React.Component {
</video>
</div>
) : null}
<h2
style={{ margin: 0 }}
dangerouslySetInnerHTML={{ __html: marked(this.props.cover.title) }}
/>
<h2 dangerouslySetInnerHTML={{ __html: this.props.cover.title }} />
{this.props.cover.subtitle ? (
<h3 style={{ marginTop: 0 }}>{this.props.cover.subtitle}</h3>
) : null}

View File

@@ -29,12 +29,15 @@ class Toolbar extends React.Component {
constructor(props) {
super(props);
this.onSelectFilter = this.onSelectFilter.bind(this);
this.state = { _selected: -1 };
this.state = { _selected: 0, _active: false };
}
selectTab(selected) {
const _selected = this.state._selected === selected ? -1 : selected;
this.setState({ _selected });
let active = true;
if (this.state._selected === selected && this.state._active === true) {
active = false;
}
this.setState({ _selected: selected, _active: active });
}
onSelectFilter(key, matchingKeys) {
@@ -79,14 +82,17 @@ class Toolbar extends React.Component {
renderClosePanel() {
return (
<div className="panel-header" onClick={() => this.selectTab(-1)}>
<div
className="panel-header"
onClick={() => this.selectTab(this.state._selected)}
>
<div className="caret" />
</div>
);
}
goToNarrative(narrative) {
this.selectTab(-1); // set all unselected within this component
// this.selectTab(-1); // set all unselected within this component
this.props.methods.onSelectNarrative(narrative);
}
@@ -202,7 +208,9 @@ class Toolbar extends React.Component {
key={key}
label={label}
iconKey={iconKey}
isActive={this.state._selected === _selected}
isActive={
this.state._selected === _selected && this.state._active === true
}
onClick={() => {
this.selectTab(_selected);
}}
@@ -229,7 +237,7 @@ class Toolbar extends React.Component {
renderToolbarPanels() {
const { features, narratives } = this.props;
const classes =
this.state._selected >= 0 ? "toolbar-panels" : "toolbar-panels folded";
this.state._active === true ? "toolbar-panels" : "toolbar-panels folded";
return (
<div className={classes}>
{this.renderClosePanel()}
@@ -247,7 +255,8 @@ class Toolbar extends React.Component {
renderToolbarNavs() {
if (this.props.narratives) {
return this.props.narratives.map((nar, idx) => {
const isActive = idx === this.state._selected;
const isActive =
idx === this.state._selected && this.state._active === true;
const classes = isActive ? "toolbar-tab active" : "toolbar-tab";
@@ -344,6 +353,14 @@ class Toolbar extends React.Component {
}}
features={this.props.features}
/>
<div id="made-with">
Made with{" "}
<a href="https://github.com/forensic-architecture/timemap">TimeMap</a>
<br />
Free software from{" "}
<a href="https://forensic-architecture.org">Forensic Architecture</a>
</div>
</div>
);
}

View File

@@ -13,14 +13,26 @@ const Checkbox = ({ label, isActive, onClickCheckbox, color, styleProps }) => {
const checkboxStyles = styleProps
? styleProps.checkboxStyles
: baseStyles.checkboxStyles;
const generatedId = label.toLowerCase().replaceAll(" ", "-");
const onClickCheckboxWrapper = (e) => {
// stop propagation in order to call method only one time
e.stopPropagation();
onClickCheckbox(e);
};
return (
<div className={isActive ? "item active" : "item"}>
<span style={{ color: color }}>{label}</span>
<button onClick={onClickCheckbox}>
<div
className={isActive ? "item active" : "item"}
onClick={onClickCheckboxWrapper}
>
<button id={generatedId} onClick={onClickCheckboxWrapper}>
<div className="border" style={containerStyles}>
<div className="checkbox" style={checkboxStyles} />
</div>
</button>
<label htmlFor={generatedId} style={{ color: color }}>
{label}
</label>
</div>
);
};

View File

@@ -13,6 +13,10 @@ const Popup = ({
children,
}) => (
<div>
<div
className={`infopopup__bg ${isOpen ? "" : "hidden"}`}
onClick={onClose}
></div>
<div
className={`infopopup ${isOpen ? "" : "hidden"} ${
theme === "dark" ? "dark" : "light"

View File

@@ -67,13 +67,20 @@ export const generateCardLayout = {
scaleFont: 1.1,
},
],
...event.sources.flatMap((source) => [
source.paths.map((p) => ({
kind: "media",
title: "Media",
value: [{ src: p, title: null, graphic: event.graphic === "TRUE" }],
})),
]),
[
{
kind: "sources",
values: event.sources.flatMap((source) => [
source.paths.map((p) => ({
kind: "media",
title: "Media",
value: [
{ src: p, title: null, graphic: event.graphic === "TRUE" },
],
})),
]),
},
],
];
},
};
@@ -210,12 +217,14 @@ export const Card = ({
}
}
function renderRow(row, cardIdx) {
function renderRow(row, cardIdx, salt) {
return (
<div className="card-row" key={hash(row)}>
<div className="card-row" key={hash({ ...row, salt })}>
{row.map((field) => (
// src by src meaning wrapGrahpic must be called around a map of renderField for sources
<span key={hash(field)}>{renderField(field, cardIdx)}</span>
<span key={hash({ ...field, row: row })}>
{renderField(field, cardIdx)}
</span>
))}
</div>
);
@@ -230,14 +239,34 @@ export const Card = ({
className={`event-card ${isSelected ? "selected" : ""}`}
onClick={onSelect}
>
{content.map((row) => renderRow(row, cardIdx))}
{isOpen && (
{content.map((row) => {
if (row[0].kind === "sources" && row[0].values.length > 0) {
return (
<div>
<details open="true">
<summary>
<span className="summary-line"></span>
<span className="summary-text">
<span className="summary-show">Show</span>{" "}
<span className="summary-hide">Hide</span> sources (
{row[0].values.length})
</span>
<span className="summary-line"></span>
</summary>
{row[0].values.map((r) => renderRow(r, cardIdx, row[0]))}
</details>
</div>
);
} else return renderRow(row, cardIdx);
})}
{/* {isOpen && (
<div className="card-bottomhalf">
{sources.map(() => (
<div className="card-row"></div>
))}
</div>
)}
)} */}
{sources.length > 0 ? renderCaret() : null}
</li>
);

View File

@@ -126,7 +126,10 @@ class CardStack extends React.Component {
renderCardStackContent() {
return (
<div id="card-stack-content" className="card-stack-content">
<div
id="card-stack-content"
className="card-stack-content scrollbar-black"
>
<ul>{this.renderSelectedCards()}</ul>
</div>
);

View File

@@ -4,8 +4,15 @@ import { DownloadButton } from "./DownloadButton";
const DownloadPanel = ({ language, title, description, domain }) => {
return (
<div className="react-innertabpanel">
<h2>{title}</h2>
<p>{description}</p>
<div className="sticky-header">
<h2>{title}</h2>
</div>
<div
className="panel-description"
dangerouslySetInnerHTML={{
__html: description,
}}
/>
<hr />
<DownloadButton language={language} domain={domain} format="csv" />
<DownloadButton language={language} domain={domain} format="json" />

View File

@@ -56,7 +56,10 @@ function FilterListPanel({
<Checkbox
label={pathLeaf}
isActive={activeFilters.includes(key)}
onClickCheckbox={() => onSelectFilter(key, matchingKeys)}
onClickCheckbox={(e) => {
e.preventDefault();
onSelectFilter(key, matchingKeys);
}}
color={assignedColor}
/>
{Object.keys(children).length > 0 ? (
@@ -74,18 +77,21 @@ function FilterListPanel({
const aggregatedFilterPaths = aggregateFilterPaths(filters);
return (
<div>
<div className="scrolled-area">
{Object.entries(aggregatedFilterPaths).map((filter) =>
createNodeComponent(filter, 1)
createNodeComponent(filter, 0)
)}
</div>
);
}
return (
<div className="react-innertabpanel">
<h2>{title}</h2>
<p
<div>
<div className="sticky-header">
<h2>{title}</h2>
</div>
<div
className="panel-description"
dangerouslySetInnerHTML={{
__html: marked(description),
}}

View File

@@ -12,7 +12,6 @@ const PanelTree = ({ data, activeValues, onSelect, type }) => {
<li
key={val.title.replace(/ /g, "_")}
className="filter-filter active"
style={{ marginLeft: "20px" }}
>
<Checkbox
label={val.title}

View File

@@ -7,6 +7,7 @@ const CardText = ({ title, value, hoverValue = null }) => {
<div className="card-cell">
{title ? <h4>{title}</h4> : null}
<div
className="card-cell__text"
style={{
width: `fit-content`,
}}

View File

@@ -31,12 +31,12 @@ class TimelineAxis extends React.Component {
fstFmt = "";
}
const { marginTop, contentHeight } = this.props.dims;
let { marginTop } = this.props.dims;
if (this.props.scaleX) {
this.x0 = axisBottom(this.props.scaleX)
.ticks(this.props.ticks)
.tickPadding(0)
.tickSize(contentHeight - TEXT_HEIGHT - marginTop)
.tickPadding(marginTop + 30)
.tickSize(100 - TEXT_HEIGHT - marginTop)
.tickFormat(timeFormat(fstFmt));
this.x1 = axisBottom(this.props.scaleX)
@@ -66,7 +66,7 @@ class TimelineAxis extends React.Component {
<>
<g
ref={this.xAxis0Ref}
transform={`translate(0, ${this.props.dims.marginTop})`}
transform={`translate(0, 24)`}
clipPath="url(#clip)"
className="axis xAxis"
/>

View File

@@ -40,15 +40,11 @@ class TimelineCategories extends React.Component {
className="tick"
style={{ strokeWidth }}
opacity="0.5"
transform={`translate(0,${this.props.getCategoryY(cat)})`}
transform={`translate(0, 66)`}
>
<line x1={dims.marginLeft} x2={dims.width - dims.width_controls} />
<line x1={dims.marginLeft} x2={dims.width - dims.marginLeft} />
</g>
<g
className="tick"
opacity="1"
transform={`translate(0,${this.props.getCategoryY(cat)})`}
>
<g className="tick" opacity="1" transform={`translate(0, 66)`}>
<text x={dims.marginLeft - 5} dy="0.32em">
{cat}
</text>
@@ -72,10 +68,7 @@ class TimelineCategories extends React.Component {
className="drag-grabber"
x={dims.marginLeft}
y={dims.marginTop}
width={Math.max(
0,
dims.width - dims.marginLeft - dims.width_controls
)}
width={Math.max(0, dims.width - dims.marginLeft * 2)}
height={dims.contentHeight}
/>
</g>

View File

@@ -97,7 +97,7 @@ class Timeline extends React.Component {
.domain(this.state.timerange)
.range([
this.state.dims.marginLeft,
this.state.dims.width - this.state.dims.width_controls,
this.state.dims.width - this.state.dims.marginLeft,
]);
}
@@ -369,12 +369,6 @@ class Timeline extends React.Component {
let classes = `timeline-wrapper ${this.state.isFolded ? " folded" : ""}`;
classes += app.narrative !== null ? " narrative-mode" : "";
const { dims } = this.state;
const foldedStyle = {
bottom: this.state.isFolded ? -dims.height : 0,
left: 110,
};
const heightStyle = { height: dims.height };
const extraStyle = { ...heightStyle, ...foldedStyle };
const contentHeight = { height: dims.contentHeight };
const { activeCategories: categories } = this.props;
@@ -384,12 +378,7 @@ class Timeline extends React.Component {
);
return (
<div
className={classes}
style={extraStyle}
onKeyDown={this.props.onKeyDown}
tabIndex="1"
>
<div className={classes} onKeyDown={this.props.onKeyDown} tabIndex="1">
<Header
title={title}
from={this.state.timerange[0]}
@@ -399,87 +388,94 @@ class Timeline extends React.Component {
}}
hideInfo={isNarrative}
/>
<div className="timeline-content" style={heightStyle}>
<div
id={this.props.ui.dom.timeline}
className="timeline"
style={contentHeight}
>
<svg ref={this.svgRef} width={dims.width} style={contentHeight}>
<Clip dims={dims} />
<Axis
ticks={timeline.dimensions.ticks}
dims={dims}
extent={this.getTimeScaleExtent()}
transitionDuration={this.state.transitionDuration}
scaleX={this.state.scaleX}
/>
<Categories
dims={dims}
getCategoryY={(category) =>
this.getY({ category, project: null })
}
onDragStart={this.onDragStart}
onDrag={this.onDrag}
onDragEnd={this.onDragEnd}
categories={categories}
features={this.props.features}
fallbackLabel={
copy[this.props.app.language].timeline
.default_categories_label
}
/>
{timeline.dimensions.ticks === 1 && (
<Handles
<div className="timeline-content">
<div id={this.props.ui.dom.timeline} className="timeline">
<div className="timeline-container">
<svg ref={this.svgRef} width={dims.width} style={contentHeight}>
<Clip dims={dims} />
<Axis
ticks={timeline.dimensions.ticks}
dims={dims}
onMoveTime={(dir) => {
this.onMoveTime(dir);
}}
extent={this.getTimeScaleExtent()}
transitionDuration={this.state.transitionDuration}
scaleX={this.state.scaleX}
/>
)}
<Categories
dims={dims}
getCategoryY={(category) =>
this.getY({ category, project: null })
}
onDragStart={this.onDragStart}
onDrag={this.onDrag}
onDragEnd={this.onDragEnd}
categories={categories}
features={this.props.features}
fallbackLabel={
copy[this.props.app.language].timeline
.default_categories_label
}
/>
<Markers
dims={dims}
selected={this.props.app.selected}
getEventX={(ev) => this.getDatetimeX(ev.datetime)}
getEventY={this.getY}
categories={categories}
transitionDuration={this.state.transitionDuration}
styles={this.props.ui.styles}
features={this.props.features}
eventRadius={this.props.ui.eventRadius}
/>
<Events
events={this.props.domain.events}
projects={this.props.domain.projects}
categories={categories}
styleDatetime={this.styleDatetime}
narrative={this.props.app.narrative}
getDatetimeX={this.getDatetimeX}
getY={this.getY}
getHighlights={(group) => {
if (group === "None") {
return [];
}
return categories.map((c) => c.group === group);
}}
getCategoryColor={this.props.methods.getCategoryColor}
transitionDuration={this.state.transitionDuration}
onSelect={this.onSelect}
dims={dims}
features={this.props.features}
setLoading={this.props.actions.setLoading}
setNotLoading={this.props.actions.setNotLoading}
eventRadius={this.props.ui.eventRadius}
filterColors={this.props.ui.filterColors}
coloringSet={this.props.app.coloringSet}
/>
</svg>
</div>
<div className="timeline-bottom">
<Handles
dims={dims}
onMoveTime={(dir) => {
this.onMoveTime(dir);
}}
backward={true}
/>
<ZoomControls
extent={this.getTimeScaleExtent()}
zoomLevels={timeline.zoomLevels}
dims={dims}
onApplyZoom={this.onApplyZoom}
/>
<Markers
<Handles
dims={dims}
selected={this.props.app.selected}
getEventX={(ev) => this.getDatetimeX(ev.datetime)}
getEventY={this.getY}
categories={categories}
transitionDuration={this.state.transitionDuration}
styles={this.props.ui.styles}
features={this.props.features}
eventRadius={this.props.ui.eventRadius}
/>
<Events
events={this.props.domain.events}
projects={this.props.domain.projects}
categories={categories}
styleDatetime={this.styleDatetime}
narrative={this.props.app.narrative}
getDatetimeX={this.getDatetimeX}
getY={this.getY}
getHighlights={(group) => {
if (group === "None") {
return [];
}
return categories.map((c) => c.group === group);
onMoveTime={(dir) => {
this.onMoveTime(dir);
}}
getCategoryColor={this.props.methods.getCategoryColor}
transitionDuration={this.state.transitionDuration}
onSelect={this.onSelect}
dims={dims}
features={this.props.features}
setLoading={this.props.actions.setLoading}
setNotLoading={this.props.actions.setNotLoading}
eventRadius={this.props.ui.eventRadius}
filterColors={this.props.ui.filterColors}
coloringSet={this.props.app.coloringSet}
backward={false}
/>
</svg>
</div>
</div>
</div>
</div>

View File

@@ -5,7 +5,7 @@ const TimelineClip = ({ dims }) => (
<rect
x={dims.marginLeft}
y="0"
width={Math.max(0, dims.width - dims.marginLeft - dims.width_controls)}
width={Math.max(0, dims.width - dims.marginLeft * 2)}
height={dims.contentHeight}
/>
</clipPath>

View File

@@ -26,7 +26,7 @@ function renderDot(event, styles, props) {
key={event.id}
className="timeline-event"
onClick={props.onSelect}
transform={`translate(${props.x}, ${props.y})`}
transform={`translate(${props.x}, ${props.y + 40})`}
>
<ColoredMarkers
radius={props.eventRadius}

View File

@@ -1,34 +1,21 @@
import React from "react";
const TimelineHandles = ({ dims, onMoveTime }) => {
const transform = "scale(1.5,1.5)";
const size = 45;
const handleOffset = dims.contentHeight / 2;
const TimelineHandles = ({ dims, onMoveTime, backward }) => {
if (backward === true) {
return (
<div className="timeline-handle" onClick={() => onMoveTime("backwards")}>
<span className="timeline-handle__triangle"></span>
</div>
);
}
return (
<g className="time-controls-inline">
<g
transform={`translate(${dims.marginLeft - 20}, ${handleOffset})`}
onClick={() => onMoveTime("backwards")}
>
<circle r={size} />
<path
d="M0,-7.847549217020565L6.796176979388489,3.9237746085102825L-6.796176979388489,3.9237746085102825Z"
transform={`rotate(270) ${transform}`}
/>
</g>
<g
transform={`translate(${
dims.width - dims.width_controls + 20
}, ${handleOffset})`}
onClick={() => onMoveTime("forward")}
>
<circle r={size} />
<path
d="M0,-7.847549217020565L6.796176979388489,3.9237746085102825L-6.796176979388489,3.9237746085102825Z"
transform={`rotate(90) ${transform}`}
/>
</g>
</g>
<div
className="timeline-handle right"
onClick={() => onMoveTime("forward")}
>
<span className="timeline-handle__triangle"></span>
</div>
);
};

View File

@@ -12,7 +12,7 @@ const TimelineHeader = ({ title, from, to, onClick, hideInfo }) => {
</p>
</div>
<div className={`timeline-info ${hideInfo ? "hidden" : ""}`}>
<p>{title}</p>
<p dangerouslySetInnerHTML={{ __html: title }} />
<p>
{d0} - {d1}
</p>

View File

@@ -33,7 +33,7 @@ const TimelineMarkers = ({
strokeLinejoin="round"
strokeDasharray={styles ? styles["stroke-dasharray"] : "2,2"}
style={{
transform: `translate(${getEventX(event)}px, ${y}px)`,
transform: `translate(${getEventX(event)}px, ${y + 40}px)`,
WebkitTransition: `transform ${transitionDuration / 1000}s ease`,
MozTransition: "none",
opacity: 1,

View File

@@ -23,7 +23,7 @@ const TimelineZoomControls = ({ extent, zoomLevels, dims, onApplyZoom }) => {
);
const isActive = zoomIsActive(zoom.duration, extent, max.duration);
return (
<text
<div
className={`zoom-level-button ${isActive ? "active" : ""}`}
x="60"
y={idx * 15 + 20}
@@ -31,7 +31,7 @@ const TimelineZoomControls = ({ extent, zoomLevels, dims, onApplyZoom }) => {
key={idx}
>
{zoom.label}
</text>
</div>
);
}
@@ -39,9 +39,9 @@ const TimelineZoomControls = ({ extent, zoomLevels, dims, onApplyZoom }) => {
zoomLevels = DEFAULT_ZOOM_LEVELS;
}
return (
<g transform={`translate(${dims.width - dims.width_controls}, 0)`}>
<div className="zoom-controls">
{zoomLevels.map((z, idx) => renderZoom(z, idx))}
</g>
</div>
);
};

View File

@@ -1,110 +1,46 @@
// Burger transition
.side-menu-burg {
position: absolute;
overflow: hidden;
float: right;
margin: 0;
padding: 0;
width: 20px;
height: 20px;
appearance: none;
box-shadow: none;
border-radius: none;
border: none;
border-radius: 0;
border: 0;
cursor: pointer;
background: none;
position: relative;
width: 18px;
height: 18px;
padding: 3px;
box-sizing: content-box;
&:before,
&:after {
content: " ";
position: absolute;
right: 50%;
right: calc(50% - 1px);
top: 3px;
width: 2px;
height: 18px;
background-color: $midwhite;
}
&:before {
transform: rotate(45deg);
}
&:after {
transform: rotate(-45deg);
}
&:hover:after,
&:hover:before {
background-color: #fff;
}
&.hidden {
display: none;
}
span {
display: block;
position: absolute;
top: 9px;
left: 0px;
right: 0px;
height: 2px;
background: $offwhite;
border-radius: 4px;
}
span::before,
span::after {
position: absolute;
display: block;
left: 0;
width: 100%;
height: 2px;
background: $offwhite;
border-radius: 4px;
content: "";
transition-duration: 0.2s, 0.2s;
transition-delay: 0.2s, 0s;
}
span::before {
transition-property: top, transform;
top: -8px;
}
span::after {
transition-property: bottom, transform;
bottom: -8px;
}
&:hover {
span::before {
top: -6px;
}
span::after {
bottom: -6px;
}
}
&.is-active {
span {
background: $midwhite;
transform: rotate(45deg);
transition-delay: 0s, 0.2s;
}
span::before,
span::after {
background: $midwhite;
transition-delay: 0s, 0.2s;
}
span::before {
top: 0;
transform: rotate(0deg);
-webkit-transform: rotate(0deg);
}
span::after {
bottom: 0;
transform: rotate(-90deg);
-webkit-transform: rotate(-90deg);
}
&:hover {
span,
span::before,
span::after {
transition: 0.2s ease;
background: $offwhite;
}
}
&.over-white:hover {
span,
span:before,
span:after {
transition: 0.2s ease;
background: $darkgrey;
}
}
}
}

View File

@@ -16,6 +16,7 @@ $green: rgb(61, 241, 79);
$midgrey: rgb(44, 44, 44);
$darkgrey: #232323;
$black: #000000;
$active: #7e56c2;
$black-transparent: rgba(0, 0, 0, 0.7);
// Category colors
@@ -37,7 +38,7 @@ $other: yellow;
background: $beta;
}
$mainfont: "GT-Zirkon", "Lato", Helvetica, sans-serif;
$mainfont: "Roboto", Helvetica, sans-serif;
// Font sizes
$xsmall: 10px; //0.7em;
@@ -52,7 +53,7 @@ $xxxlarge: 32px;
$final-level: 10000;
$loading-overlay: 500;
$overheader: 100;
$header: 10;
$header: 20;
$map-overlay: 2;
$map: 1;
$scene: 1;
@@ -67,8 +68,8 @@ $card-width: 500px;
$card-right: 2px;
$narrative-info-height: 205px;
$narrative-info-desc-height: 153px;
$timeline-height: 250px;
$toolbar-width: 110px;
$timeline-height: 130px;
$toolbar-width: 0px;
$panel-width: 1000px;
$panel-height: 1000px;

View File

@@ -1,21 +1,22 @@
.event-card {
box-sizing: border-box;
border: 1px solid black;
margin: 0;
padding: 15px;
transition: 0.2 ease;
background: $midwhite;
opacity: 0.92;
color: $darkgrey;
border: 0;
opacity: 1;
color: $black;
list-style-type: none;
font-size: $large;
line-height: $xxlarge;
transition: background-color 0.4s;
text-align: left;
overflow-y: scroll;
overflow-y: auto;
height: 100%;
max-width: $card-width;
& + .event-card {
border-top: 1px solid #dedede;
}
&:hover {
background: $lightwhite;
transition: background-color 0.4s;
@@ -26,9 +27,10 @@
margin-bottom: 0;
margin-right: 5px;
text-transform: uppercase;
font-size: $small;
color: $darkwhite;
font-weight: 800;
font-size: 0.875rem;
font-weight: 400;
color: $midwhite;
margin-bottom: 3px;
&:first-child {
margin-top: 0;
@@ -54,9 +56,18 @@
flex-direction: row;
justify-content: space-between;
& > span,
.card-cell {
flex: 1;
}
@media screen and (max-width: 600px) {
flex-wrap: wrap;
& > span {
display: block;
min-width: 50%;
}
}
}
.card-col {
@@ -104,6 +115,7 @@
}
.card-cell {
font-size: 16px;
a {
transition: color 0.2s;
}
@@ -260,10 +272,22 @@
.card-row {
border-color: darkgray;
@media screen and (max-width: 600px) {
& > span {
flex: 1;
}
}
}
.embedded {
width: calc(#{$card-width} - 50px) !important;
// width: calc(#{$card-width} - 50px) !important;
width: 100%;
max-width: 90vw;
.twitter-tweet {
max-width: 450px !important;
}
}
.source-hidden,
@@ -282,15 +306,60 @@
color: white;
}
}
details {
margin-top: 18px;
}
/* Styling the Disclosure Widgets */
details > summary {
cursor: pointer;
padding: 0;
display: flex;
align-items: center;
justify-content: space-between;
&:hover {
.summary-text {
background: rgba($active, 0.3);
}
}
.summary-hide {
display: none;
}
.summary-line {
height: 1px;
flex: 1;
background: #000;
}
.summary-text {
padding: 5px 9px;
border-radius: 6px;
margin: 0 6px;
transition: background 0.3s ease;
}
}
details[open] {
.summary-hide {
display: inline;
}
.summary-show {
display: none;
}
}
details > summary > * {
display: inline;
}
}
.media.source-graphic {
background-color: darken($red, 26%);
h4 {
color: white;
color: $midwhite;
transition: font-size 0.3s ease;
}
h4:hover {
font-size: 103%;
color: lighten(yellow, 20%);
color: $offwhite;
cursor: pointer;
}
}

View File

@@ -1,22 +1,31 @@
// @import 'burger';
@import "card";
$card-stack-header-height: 38px;
.card-stack {
display: flex;
flex-direction: column;
position: absolute;
top: #{$card-right};
padding-top: #{$card-stack-header-height};
right: $card-right;
max-height: calc(100% - #{$timeline-height} + 60px);
max-height: calc(100% - #{$timeline-height} - 35px);
height: auto;
width: $card-width;
overflow-y: scroll;
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22);
z-index: $header;
color: white;
overflow-x: hidden;
overflow-y: auto;
overflow: hidden;
max-width: 100vw;
background: $offwhite;
border-radius: 6px;
@media screen and (max-width: 600px) {
top: 0;
left: 0;
right: 0;
bottom: 0;
max-height: 100vh;
}
&.narrative-mode {
right: $card-right;
@@ -30,10 +39,8 @@ $card-stack-header-height: 38px;
}
.card-stack-header {
position: fixed;
position: initial;
top: $card-right;
min-height: $card-stack-header-height;
line-height: $card-stack-header-height;
width: 100%;
max-width: $card-width;
box-sizing: border-box;
@@ -77,8 +84,13 @@ $card-stack-header-height: 38px;
}
.card-stack-content {
width: 100%;
flex: 1;
max-width: $card-width;
overflow: auto;
padding-right: 10px;
display: block;
width: 100%;
box-sizing: border-box;
ul {
padding: 0;

View File

@@ -1,5 +1,14 @@
@import "variables";
html {
font-family: $mainfont;
font-size: 14px;
-webkit-font-smoothing: antialiased;
@media screen and (max-width: 600px) {
font-size: 16px;
}
}
body {
margin: 0;
overflow: hidden;
@@ -15,12 +24,17 @@ body {
}
h1 {
font-family: $mainfont;
}
h2 {
font-size: 1.3rem;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.1em;
}
p {
font-size: 1rem;
line-height: 1.5em;
}
.login-wrapper {
@@ -58,7 +72,6 @@ h2 {
height: 30px;
border: 1px solid $offwhite;
text-transform: uppercase;
letter-spacing: 0.1em;
cursor: pointer;
outline: none;
margin-top: 10px;
@@ -132,6 +145,13 @@ Scrollbar
background: $offwhite;
}
.scrollbar-black {
*::-webkit-scrollbar-thumb,
&::-webkit-scrollbar-thumb {
background: $black;
}
}
.hidden {
visibility: hidden;
}

View File

@@ -7,7 +7,7 @@
width: 100%;
opacity: 1;
transition: top 0.4s ease;
z-index: $loading-overlay + 1;
z-index: 2;
overflow-y: auto;
overflow-x: hidden;
color: $offwhite;
@@ -15,6 +15,7 @@
&.showing {
top: 0;
left: 0;
z-index: $loading-overlay + 1;
}
}
@@ -22,42 +23,39 @@
position: fixed;
bottom: 20px;
left: 0;
display: flex;
width: 100vw;
width: 64px;
right: 7px;
left: auto;
top: 7px;
bottom: auto;
border-radius: 6px;
overflow: hidden;
max-width: initial;
justify-content: center;
align-items: center;
flex-direction: column;
transition: all 0.6s ease;
transition-delay: 0.3s;
@media only screen and (max-width: 1200px) {
position: inherit;
&.minimized {
top: 78px;
right: 7px;
transition: all 0.6s ease;
transition-delay: 0s;
z-index: 10;
@media screen and (max-width: 600px) {
top: 145px;
}
}
.cover-logo-container {
padding: 20px 0 0 20px;
display: flex;
&.minimized {
}
.cover-logo {
transition: all 1s;
width: 60px;
height: 60px;
}
}
&.minimized {
bottom: 150px;
max-width: $toolbar-width;
max-height: 30px;
justify-content: center;
align-items: center;
flex-direction: column;
.cover-logo-container {
padding: 5px;
}
.cover-logo {
width: 60px;
height: 60px;
display: block;
padding: 0;
img {
display: block;
width: 100%;
height: auto;
}
}
}
@@ -152,26 +150,15 @@
.hero {
min-width: 100%;
min-height: 80px;
margin: auto;
margin: 20px 0 40px;
display: flex;
flex-direction: column;
margin-bottom: 20px;
margin-top: 60px;
@media only screen and (max-width: 1200px) {
min-height: 250px;
}
.row {
display: flex;
flex: 1;
flex-direction: row;
@media only screen and (max-width: 1200px) {
flex-direction: column;
}
justify-content: space-around;
&.vertical {
@@ -200,37 +187,30 @@
min-height: 10px;
background-color: black;
letter-spacing: 1px;
@media only screen and (max-width: 1200px) {
min-height: 100px;
}
}
&.yellow {
color: black !important;
color: $offwhite;
background-color: $yellow;
}
&:hover {
cursor: pointer;
background-color: $darkwhite;
color: white;
}
@media only screen and (max-width: 1200px) {
min-height: 100px;
background-color: $offwhite;
color: $yellow;
border-color: $yellow;
}
}
}
}
.cover-content {
display: flex;
flex-direction: column;
max-width: 600px;
margin: 0 auto;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 10em;
padding-bottom: 2em;
h1,
h2,
@@ -239,6 +219,9 @@
h5 {
text-align: center;
}
h2 {
margin: 75px 0 15px;
}
h1 {
margin-bottom: -15px;
@@ -264,6 +247,8 @@
p {
text-align: justify;
font-size: 1.2rem;
line-height: 1.65em;
}
}

View File

@@ -1,32 +1,93 @@
@import "burger";
.infopopup {
width: $infopopup-width;
box-shadow: 10px -10px 38px rgba(0, 0, 0, 0.3),
10px 15px 12px rgba(0, 0, 0, 0.22);
color: $darkgrey;
display: block;
position: absolute;
width: 600px;
max-width: calc(min(60vw, 100%));
color: $darkgrey;
background: $offwhite-transparent;
bottom: $timeline-height;
left: $toolbar-width;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 3px solid $offwhite;
border-radius: 1px;
padding: 20px;
padding: 20px 15px 15px;
box-sizing: border-box;
font-size: $large;
transition: opacity 0.5s ease 0.1s, z-index 0.1s ease 0s;
opacity: 1;
z-index: $overheader;
border: 2px solid $midwhite;
border-radius: 6px;
box-shadow: 10px -10px 38px rgba(0, 0, 0, 0.3),
10px 15px 12px rgba(0, 0, 0, 0.22);
&__bg {
background-color: rgba(0, 0, 0, 0.4);
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
cursor: pointer;
&.hidden {
display: none;
}
}
@media screen and (max-width: 600px) {
font-size: 18px;
width: 98vw;
max-width: none;
max-height: 95vh;
background: rgba(0, 0, 0, 0.95);
overflow: auto;
figcaption {
overflow-wrap: break-word;
font-size: 0.75rem;
}
}
p:nth-last-child(1) {
margin-bottom: 0;
}
&.hidden {
transition: 0.5s ease;
opacity: 0;
}
.two-columns {
display: flex;
flex-direction: row;
max-width: 100%;
overflow: hidden;
margin-top: 20px;
gap: 20px;
justify-content: space-between;
align-items: flex-start;
&_column {
flex: 1;
figure {
margin: 0;
}
figcaption {
margin-top: 6px;
text-align: left;
color: $midwhite;
}
img {
border-radius: 9px;
}
}
}
.side-menu-burg {
position: absolute;
right: 8px;
top: 10px;
right: 6px;
top: 6px;
&.light {
&.is-active span:after,
&.is-active span:before {
@@ -39,6 +100,9 @@
// background: $black-transparent;
background: rgba(0, 0, 0, 0.8);
color: white;
@media screen and (max-width: 600px) {
background: rgba(0, 0, 0, 0.9);
}
}
iframe {
@@ -67,14 +131,10 @@
}
.legend-header {
display: flex;
flex-direction: row;
justify-content: center;
h2 {
display: flex;
font-size: 12pt;
letter-spacing: 2px;
width: 100%;
margin: 0;
text-align: center;
}
}

View File

@@ -18,7 +18,7 @@
position: fixed;
top: 0px;
bottom: 0px;
left: 110px;
left: 0;
right: 0;
&.mobile {

View File

@@ -1,18 +1,22 @@
@import "variables";
.satellite-overlay-toggle {
background-color: rgb(53, 53, 53);
opacity: 0.9;
position: fixed;
top: 0.5em;
right: 0.5em;
z-index: $map-overlay;
cursor: pointer;
border-radius: 6px;
overflow: hidden;
@media screen and (max-width: 600px) {
top: 75px;
}
.satellite-overlay-toggle-button {
cursor: pointer;
width: 64px;
height: 64px;
opacity: 0.8;
opacity: 0.85;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
border: none;
color: white;

View File

@@ -46,11 +46,6 @@
}
}
.folded {
left: -400px;
transition: 0.2s ease;
}
.search-outer-container {
position: absolute;
left: 110px;

View File

@@ -1,7 +1,6 @@
.react-tabs {
padding-top: 0;
box-sizing: border-box;
height: 100%;
[role="tablist"] {
padding: 0;
@@ -40,6 +39,13 @@
.react-innertabpanel {
box-sizing: border-box;
padding-top: 20px;
padding-top: 0;
hr {
border-top: 0;
border-bottom: 1px solid $midwhite;
margin-block-start: 0.5em;
margin-block-end: 1.5em;
}
}
}

View File

@@ -1,19 +1,19 @@
.timeline-wrapper {
position: fixed;
box-sizing: border-box;
left: 110px;
right: 0px;
height: $timeline-height;
left: 0;
right: 0;
bottom: 0;
height: auto;
background: rgba($black, 0.8);
box-shadow: 0 -10px 38px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22);
color: white;
transition: left 0.2s ease, bottom 0.2s ease;
bottom: 0px;
transition: transform 0.3s ease;
z-index: $timeline;
border-top: 1px solid black;
&.folded {
transition: bottom 0.2s ease;
transform: translateY(100%);
.timeline-header .timeline-toggle p .arrow-down {
transform: translate(0, 0px) rotate(-135deg);
@@ -44,6 +44,7 @@
margin: 0 auto;
background: rgba($black, 0.8);
margin-top: -25px;
border-radius: 6px 6px 0 0;
cursor: pointer;
&:hover {
@@ -65,38 +66,59 @@
border-bottom: 2px solid $midwhite;
}
}
@media screen and (max-width: 1040px) {
.timeline-toggle p {
margin: -25px 10px 0 auto;
}
}
.timeline-info {
width: calc(#{$card-width} - 20px);
position: absolute;
bottom: 100%;
margin-bottom: 6px;
margin-left: 10px;
background: rgba($black, 0.8);
padding: 9px 15px 11px;
box-sizing: border-box;
min-height: 20px;
border-radius: 6px;
&.hidden {
display: none;
}
width: calc(#{$card-width} - 20px);
position: absolute;
margin-top: -70px;
margin-left: 10px;
background: rgba($black, 0.8);
padding: 10px;
min-height: 20px;
p {
margin: 0;
height: 20px;
text-transform: uppercase;
letter-spacing: 0.1em;
font-size: 1.15rem;
span {
color: $offwhite;
}
&:first-child {
text-transform: none;
font-size: $normal;
letter-spacing: 0.05em;
font-size: 1rem;
color: $midwhite;
font-weight: 400;
}
@media screen and (max-width: 600px) {
font-size: 1rem;
&:nth-child(1) {
font-size: 0.875rem;
}
}
}
// mobile styles
@media screen and (max-width: 600px) {
bottom: 115%;
bottom: calc(100% + 25px);
width: 96vw;
margin: 0 2vw 10px;
}
}
}
.timeline-content {
display: flex;
justify-content: center;
align-items: center;
.timeline-labels {
padding-top: 2px;
padding-left: 20px;
@@ -134,15 +156,11 @@
}
.timeline {
/*width: calc(100% - 200px);*/
width: calc(100% - 40px);
margin-left: 20px;
width: 100%;
box-sizing: border-box;
float: left;
svg {
display: inline-block;
float: left;
display: block;
}
.domain {
@@ -261,21 +279,45 @@
}
}
}
}
}
}
.zoom-level-button {
font-size: $xsmall;
cursor: pointer;
text-anchor: middle;
letter-spacing: 0.05em;
transition: 0.2s ease;
fill: $midwhite;
.zoom-controls {
display: flex;
padding: 6px 20px;
align-items: center;
justify-content: center;
grid-gap: 9px;
@media screen and (max-width: 600px) {
padding: 6px 3px;
}
&:hover,
&.active {
transition: 0.2s ease;
fill: $offwhite;
}
}
.zoom-level-button {
padding: 6px 9px;
font-size: 0.875rem;
cursor: pointer;
text-anchor: middle;
letter-spacing: 0.05em;
transition: 0.2s ease;
color: $midwhite;
border-radius: 3px;
border: 0;
background-color: transparent;
font-weight: 600;
text-align: center;
@media screen and (max-width: 600px) {
font-size: 0.65rem;
}
&:hover {
color: $offwhite;
background-color: rgba($active, 0.3);
}
&.active {
color: $offwhite;
background-color: $active;
}
}
}
@@ -302,6 +344,49 @@
cursor: pointer;
}
.handle {
fill: $offwhite;
/*
* Handles
*/
.timeline-bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
.timeline-handle {
width: 30px;
height: 30px;
text-align: right;
display: flex;
align-items: center;
justify-content: center;
margin-left: 5px;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: rgba($active, 0.4);
.timeline-handle__triangle {
border-color: transparent $offwhite transparent transparent;
}
}
&__triangle {
display: block;
width: 0;
height: 0;
border-style: solid;
border-width: 7px 10px 7px 0;
border-color: transparent $midwhite transparent transparent;
}
&.right {
margin-right: 5px;
&:hover {
background-color: rgba($active, 0.4);
.timeline-handle__triangle {
border-color: transparent transparent transparent $offwhite;
}
}
.timeline-handle__triangle {
border-width: 7px 0 7px 10px;
border-color: transparent transparent transparent $midwhite;
}
}
}

View File

@@ -5,52 +5,67 @@
position: fixed;
top: 0px;
left: 0px;
bottom: 0px;
z-index: $header;
background: $midgrey;
background: transparent;
&.narrative-mode {
left: -$toolbar-width;
left: -0;
}
.toolbar {
position: relative;
width: $toolbar-width;
height: 100%;
padding: 20px 0px 0px 0px;
display: flex;
width: auto;
height: 70px;
padding: 0;
margin: 0;
box-sizing: border-box;
color: $offwhite;
background: $darkgrey;
text-align: center;
font-size: $normal;
font-weight: 100;
transition: 0.2s ease;
font-weight: 400;
z-index: $header;
button {
background: #222222;
}
.react-tabs__tab-list {
margin: 0;
display: flex;
align-items: center;
justify-content: flex-start;
grid-gap: 15px;
margin-left: 10px;
height: 100%;
}
.toolbar-header {
margin: 0 15px 10px 15px;
padding: 10px 0 25px 0;
padding: 5px 15px 5px 10px;
border-radius: 0 0 6px 0;
background-color: $midgrey;
transition: 0.2s ease;
border-bottom: 2px solid $midwhite;
// border-bottom: 2px solid $midwhite;
text-transform: uppercase;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
p {
font-size: $normal;
white-space: pre-wrap;
font-size: 1.15rem;
line-height: 1.3em;
font-weight: 400;
text-transform: uppercase;
margin: 0;
}
p:first-child {
font-size: $xsmall;
}
}
&:hover {
transition: 0.2s ease;
border-bottom: 2px solid $offwhite;
@media screen and (max-width: 600px) {
height: 60px;
.toolbar-header p {
font-size: 1rem;
}
}
@@ -59,7 +74,7 @@
}
.bottom-actions {
position: absolute;
display: none;
width: $toolbar-width;
bottom: 10px;
box-sizing: border-box;
@@ -186,37 +201,57 @@
}
}
.download-row {
display: flex;
flex-direction: row;
.download-row + .download-row {
margin-top: 14px;
}
.download-button {
flex: 1 1 auto;
aspect-ratio: 1 / 1;
width: 50px;
height: auto;
flex-direction: column;
text-align: center;
display: inline-flex;
vertical-align: middle;
align-items: center;
justify-content: center;
color: $black;
border: 1px solid $offwhite;
background: $offwhite;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
transition: background 0.3s ease;
&:hover {
background: rgba(#fff, 0.6);
}
}
.download-description {
flex: 1 5 auto;
text-align: justify;
margin: auto;
display: inline-block;
width: calc(100% - 52px);
padding-left: 12px;
box-sizing: border-box;
vertical-align: middle;
}
.toolbar-tab,
.download-button {
.toolbar-tab {
position: relative;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 60px;
width: $toolbar-width;
padding: 5px 0;
font-weight: 400;
text-overflow: ellipsis;
overflow: hidden;
padding: 0;
height: auto;
width: 45px;
aspect-ratio: 1 / 1;
background: rgba(0, 0, 0, 0.8);
border-radius: 6px;
cursor: pointer;
transition: 0.2s ease;
color: $midwhite;
svg {
transform: scale(0.7);
// transform: scale(0.7);
path,
circle,
polygon,
@@ -238,15 +273,42 @@
}
}
&:hover {
.tab-caption {
transform: scale(1);
}
}
.tab-caption {
display: block;
text-align: center;
font-size: $xsmall;
letter-spacing: 0.05em;
font-size: 1rem;
position: absolute;
top: 100%;
top: calc(100% + 5px);
background: #000;
padding: 3px 6px;
font-size: 1rem;
color: #fff;
border-radius: 3px;
transform: scale(0);
transition: transform 0.15s ease;
user-select: none;
&:after {
content: "";
display: block;
position: absolute;
width: 6px;
height: 6px;
transform: rotate(45deg);
background-color: #000;
left: 50%;
left: calc(50% - 3px);
top: -3px;
}
}
&.active {
background: $black;
background: $active;
}
&:hover,
@@ -277,43 +339,72 @@
}
.toolbar-panels {
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: flex-start;
width: 440px;
top: 15px;
bottom: 0;
top: 75px;
left: 15px;
padding: 15px 15px 30px;
box-sizing: border-box;
padding: 30px 10px 10px 30px;
font-size: $normal;
background: $black;
color: $offwhite;
position: fixed;
transition: 0.2s ease;
left: $toolbar-width;
max-height: calc(100vh - #{$timeline-height});
max-height: calc(100vh - #{$timeline-height} - 50px);
box-shadow: 10px -10px 38px rgba(0, 0, 0, 0.3),
10px 15px 12px rgba(0, 0, 0, 0.22);
z-index: 20;
h2 {
font-size: $large;
text-transform: none;
letter-spacing: normal;
@media screen and (max-width: 600px) {
left: 3px;
right: 3px;
width: auto;
top: 65px;
max-height: calc(100vh - 65px - 5px);
}
p {
font-size: $normal;
line-height: 1.4em;
.sticky-header {
position: sticky;
top: 0;
background: #000;
z-index: 100;
padding: 0 0 10px;
h2 {
margin: 0;
}
}
.panel-description {
margin-bottom: 1.5rem;
.hint {
color: $midwhite;
}
p:nth-last-child(1) {
margin-bottom: 0;
}
p:nth-child(1) {
margin-top: 0;
}
}
h2 {
text-transform: none;
letter-spacing: normal;
&:nth-child(1) {
margin-top: 0;
}
}
.panel-header {
position: absolute;
display: inline-block;
width: 36px;
float: right;
margin-left: 20px;
margin-right: -45px;
height: 36px;
padding-top: 5px;
box-sizing: border-box;
margin-top: 10px;
border-radius: 3px;
top: 0;
left: 100%;
border-radius: 0 3px 3px 0;
background: $black;
padding: 8px 6px;
cursor: pointer;
@@ -347,7 +438,6 @@
}
.react-tabs__tab-panel--selected {
height: calc(100% - 40px);
overflow-y: auto;
margin-top: 0;
@@ -368,9 +458,14 @@
height: calc(100% - 310px);
}
transition: opacity 0.3s ease, margin 0.3s ease, left 0s linear 0s;
&.folded {
transition: 0.2s ease;
left: -440px;
transition: opacity 0.3s ease, margin 0.3s ease, left 0s linear 1s;
opacity: 0;
margin-top: 20px;
left: -110%;
right: auto;
max-width: 100vw;
ul {
height: 0;
@@ -410,22 +505,32 @@
.item {
width: 100%;
height: 36px;
line-height: 36px;
background: none;
font-size: $large;
font-size: 1rem;
padding: 3px 0;
margin: 0 0 3px;
&:hover {
opacity: 0.8;
cursor: pointer;
}
button,
label {
display: inline-block;
vertical-align: middle;
}
button {
height: 36px;
aspect-ratio: 1 / 1;
border: 1px transparent;
background: none;
cursor: pointer;
color: $offwhite;
outline: none;
transition: 0.2s ease;
padding: 0 10px;
text-align: left;
float: left;
padding: 4px;
margin-right: 3px;
.border {
width: 16px;
@@ -441,7 +546,6 @@
border: 1px solid $offwhite;
box-sizing: border-box;
background: none;
float: left;
position: absolute;
top: 2px;
left: 2px;
@@ -455,7 +559,6 @@
display: inline-block;
height: 36px;
line-height: 36px;
float: left;
color: $offwhite;
font-size: $normal;
overflow: hidden;
@@ -543,23 +646,48 @@
}
}
@media (max-height: 678px) {
.toolbar-wrapper {
.toolbar-tab {
height: 60px;
padding: 0;
&:hover {
.tab-caption {
transition: 0.2s ease;
opacity: 1;
}
}
}
.toolbar .bottom-actions {
.action-button {
margin-top: 5px;
}
}
/*
* Made with block
*/
#made-with {
position: fixed;
top: 75px;
background: rgba(0, 0, 0, 0.8);
left: 5px;
padding: 5px 12px;
margin-bottom: 6px;
border-radius: 4px;
width: 115px;
text-align: left;
font-size: 0.75rem;
opacity: 0.65;
color: $midwhite;
&:hover {
opacity: 1;
}
@media screen and (max-width: 600px) {
top: 65px;
opacity: 1;
}
}
// @media (max-height: 678px) {
// .toolbar-wrapper {
// .toolbar-tab {
// height: 60px;
// padding: 0;
// &:hover {
// .tab-caption {
// transition: 0.2s ease;
// opacity: 1;
// }
// }
// }
// .toolbar .bottom-actions {
// .action-button {
// margin-top: 5px;
// }
// }
// }
// }

View File

@@ -77,7 +77,7 @@ const initial = {
ticks: 15,
height: isSmallLaptop ? 170 : 250,
width: 0,
marginLeft: 70,
marginLeft: 20,
marginTop: isSmallLaptop ? 5 : 10, // the padding used for the day/month labels inside the timeline
marginBottom: 60,
contentHeight: isSmallLaptop ? 160 : 200,
@@ -159,7 +159,7 @@ const initial = {
tiles: {
current: "openstreetmap", // ['openstreetmap', 'streets', 'satellite']
default: "openstreetmap", // ['openstreetmap', 'streets', 'satellite']
satellite: "satellite"
satellite: "satellite",
},
style: {
categories: {