Cleaning technical debt (#192)

* abstract Space component to switch out Map

* basic viewing possible

* restructure components dir

* all jsx --> js

* App.jsx --> App.js

* comment out 3d for now
This commit is contained in:
Lachlan Kermode
2021-01-19 22:22:12 +01:00
committed by GitHub
parent 745953a435
commit e99398ceab
75 changed files with 121 additions and 745 deletions

View File

@@ -404,7 +404,7 @@ module.exports = function (webpackEnv) {
},
],
],
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
@@ -449,7 +449,7 @@ module.exports = function (webpackEnv) {
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
// Babel sourcemaps are needed for debugging into node_modules
// code. Without the options below, debuggers like VSCode
// show incorrect code and set breakpoints on the wrong lines.

View File

@@ -1,158 +0,0 @@
import copy from "../common/data/copy.json";
import React from "react";
import CardCustomField from "./presentational/Card/CustomField";
import CardTime from "./presentational/Card/Time";
import CardLocation from "./presentational/Card/Location";
import CardCaret from "./presentational/Card/Caret";
import CardSummary from "./presentational/Card/Summary";
import CardSource from "./presentational/Card/Source";
import { makeNiceDate } from "../common/utilities";
class Card extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false,
};
}
toggle() {
this.setState({
isOpen: !this.state.isOpen,
});
}
makeTimelabel(datetime) {
return makeNiceDate(datetime);
}
handleCardSelect(e) {
if (!e.target.className.includes("arrow-down")) {
const selectedEventFormat =
this.props.idx > 0 ? [this.props.event] : this.props.event;
this.props.onSelect(selectedEventFormat, this.props.idx);
}
}
renderSummary() {
return (
<CardSummary
language={this.props.language}
description={this.props.event.description}
isOpen={this.state.isOpen}
/>
);
}
renderLocation() {
return (
<CardLocation
language={this.props.language}
location={this.props.event.location}
isPrecise={
!this.props.event.type || this.props.event.type === "Structure"
}
/>
);
}
renderSources() {
if (this.props.sourceError) {
return <div>ERROR: something went wrong loading sources, TODO:</div>;
}
const sourceLang = copy[this.props.language].cardstack.sources;
return (
<div className="card-col">
<h4>{sourceLang}: </h4>
{this.props.event.sources.map((source) => (
<CardSource
isLoading={this.props.isLoading}
source={source}
onClickHandler={(source) => this.props.onViewSource(source)}
/>
))}
</div>
);
}
// NB: should be internaionalized.
renderTime() {
const timelabel = this.makeTimelabel(this.props.event.datetime);
// let precision = this.props.event.time_display
// if (precision === '_date_only') {
// precision = ''
// timelabel = timelabel.substring(0, 11)
// } else if (precision === '_approximate_date_only') {
// precision = ' (Approximate date)'
// timelabel = timelabel.substring(0, 11)
// } else if (precision === '_approximate_datetime') {
// precision = ' (Approximate datetime)'
// } else {
// timelabel = timelabel.substring(0, 11)
// }
return (
<CardTime
makeTimelabel={timelabel}
language={this.props.language}
timelabel={timelabel}
/>
);
}
renderCustomFields() {
return this.props.features.CUSTOM_EVENT_FIELDS.map((field) => {
const value = this.props.event[field.key];
return value ? (
<CardCustomField field={field} value={this.props.event[field.key]} />
) : null;
});
}
renderMain() {
return (
<div className="card-container">
<div className="card-row details">
{this.renderTime()}
{this.renderLocation()}
</div>
{this.renderSummary()}
{this.renderCustomFields()}
</div>
);
}
renderExtra() {
return <div className="card-bottomhalf">{this.renderSources()}</div>;
}
renderCaret() {
return this.props.features.USE_SOURCES ? (
<CardCaret toggle={() => this.toggle()} isOpen={this.state.isOpen} />
) : null;
}
render() {
const { isSelected, idx } = this.props;
return (
<li
className={`event-card ${isSelected ? "selected" : ""}`}
id={`event-card-${idx}`}
ref={this.props.innerRef}
onClick={(e) => this.handleCardSelect(e)}
>
{this.renderMain()}
{this.state.isOpen ? this.renderExtra() : null}
{this.renderCaret()}
</li>
);
}
}
// The ref to each card will be used in CardStack for programmatic scrolling
export default React.forwardRef((props, ref) => (
<Card innerRef={ref} {...props} />
));

View File

@@ -1,145 +0,0 @@
import React from "react";
const Icon = ({ iconType }) => {
if (iconType === "personas") {
return (
<svg
x="0px"
y="0px"
width="40px"
height="40px"
viewBox="0 0 40 40"
enableBackground="new 0 0 40 40"
>
<path d="M15.464,17.713" />
<path d="M5.526,17.713c-1.537,0.595-3,1.472-4.314,2.637l1.114,17.081h16.338" />
<path d="M12.283,15.522c-1.707,0.661-3.332,1.636-4.792,2.93l1.238,18.979h18.153" />
<circle cx="27.432" cy="8.876" r="6.877" />
<path d="M21.297,13.088c-1.896,0.733-3.702,1.817-5.326,3.256l1.375,21.087h20.17l1.376-21.087c-1.624-1.438-3.43-2.522-5.326-3.256" />
<path d="M20.968,6.547c-0.926-0.554-2.006-0.877-3.163-0.877c-3.418,0-6.188,2.771-6.188,6.188c0,2.811,1.875,5.18,4.441,5.935" />
<path d="M12.38,8.881c-0.738-0.361-1.564-0.57-2.441-0.57c-3.076,0-5.57,2.494-5.57,5.57c0,1.983,1.04,3.72,2.601,4.707" />
</svg>
);
} else if (iconType === "tipos") {
return (
<svg
x="0px"
y="0px"
width="40px"
height="40px"
viewBox="0 0 40 40"
enableBackground="new 0 0 40 40"
>
<path
strokeDasharray="3, 4"
d="M22.326,5.346
c-2.154-2.081-5.082-3.367-8.314-3.367c-6.614,0-11.976,5.361-11.976,11.974c0,6.613,5.361,11.977,11.976,11.977
c0.228,0,0.449-0.021,0.674-0.034"
/>
<circle cx="23" cy="17.288" r="11.975" />
<circle strokeDasharray="3, 4" cx="25.987" cy="26.926" r="11.976" />
</svg>
);
} else if (iconType === "hardware") {
return (
<svg
x="0px"
y="0px"
width="40px"
height="40px"
viewBox="0 0 40 40"
enableBackground="new 0 0 40 40"
>
<path
d="M20,1.695C12.571,1.696,6.286,2.019,5.272,2.452C5.253,2.458,5.233,2.466,5.215,2.474
c-0.01,0.004-0.019,0.008-0.027,0.012C4.38,2.831,3.803,4.256,3.802,5.907v3.502H2.926H1.175c-0.241,0-0.438,0.196-0.438,0.438
v0.875v5.254c0,0.242,0.196,0.438,0.438,0.438h1.751c0.242,0,0.438-0.195,0.438-0.438V11.16h0.438v15.324h5.691
c0.242,0,0.438,0.195,0.438,0.438v1.751c0,0.241-0.195,0.438-0.438,0.438H3.802v3.063c0,0.626,0.167,1.203,0.438,1.515v3.74
c0,0.482,0.393,0.875,0.876,0.875h2.627c0.483,0,0.875-0.393,0.875-0.875v-2.627h22.765v2.627c0,0.482,0.393,0.875,0.877,0.875
h2.627c0.482,0,0.875-0.393,0.875-0.875v-3.74c0.271-0.312,0.438-0.889,0.438-1.515v-3.065h-5.691c-0.241,0-0.438-0.195-0.438-0.438
v-1.751c0-0.241,0.197-0.438,0.438-0.438H36.2V11.161h0.438v4.816c0,0.242,0.195,0.438,0.438,0.438h1.752
c0.24,0,0.438-0.195,0.438-0.438v-5.254V9.848c0-0.242-0.195-0.438-0.438-0.438h-1.752h-0.875V5.907
c-0.001-1.703-0.614-3.159-1.453-3.448C33.79,2.023,27.479,1.696,20,1.695z M5.429,3.28h29.144c0.483,0,0.875,0.98,0.875,2.189l0,0
V7.22c0,0.242-0.195,0.438-0.438,0.438H4.991c-0.242,0-0.438-0.196-0.438-0.438V5.469C4.553,4.261,4.945,3.28,5.429,3.28z
M5.553,8.534h28.895c0.483,0,0.876,0.392,0.876,0.875v13.134c0,0.484-0.393,0.876-0.876,0.876h-3.466c0,0-0.863,0.613-0.912,0.613
H9.931c-0.113,0-0.225-0.022-0.33-0.065l-0.778-0.548h-3.27c-0.483,0-0.875-0.392-0.875-0.876V9.409
C4.678,8.926,5.069,8.534,5.553,8.534L5.553,8.534z"
/>
</svg>
);
} else if (iconType === "escenas") {
return (
<svg
className="scenes"
x="0px"
y="0px"
width="40px"
height="40px"
viewBox="0 0 40 40"
enableBackground="new 0 0 40 40"
>
<path
d="M36.729,14.743v13.15l-14.225,6.693V21.438L36.729,14.743 M38.732,11.045L20.5,19.625v18.662l18.232-8.58V11.045
L38.732,11.045z"
/>
<path
d="M4.271,14.743l14.225,6.695v13.148L4.271,27.894V14.743 M2.268,11.045v18.662l18.232,8.58V19.625L2.268,11.045L2.268,11.045
z"
/>
<path
d="M20.5,4.844l13.289,6.202L20.5,17.247L7.209,11.046L20.5,4.844 M20.5,2.537L2.268,11.045L20.5,19.554l18.232-8.509
L20.5,2.537L20.5,2.537z"
/>
</svg>
);
} else if (iconType === "docs") {
return (
<svg
x="0px"
y="0px"
width="40px"
height="40px"
viewBox="0 0 40 40"
enableBackground="new 0 0 40 40"
>
<path
d="M31.543,5.987V3.158
c0-1.103-0.095-1.197-1.197-1.197H4.791c-1.103,0-1.198,0.095-1.198,1.197V32.84c0,1.103,0.095,1.197,1.198,1.197h2.829"
/>
<path
d="M35.57,36.866
c0,1.103-0.096,1.198-1.198,1.198H8.817c-1.103,0-1.198-0.096-1.198-1.198V7.185c0-1.103,0.095-1.197,1.198-1.197h25.555
c1.103,0,1.198,0.095,1.198,1.197V36.866z"
/>
<path d="M58.755,29.633" />
<path d="M21.86,40.072" />
<path d="M-22.755,58.555" />
<line x1="11.612" y1="11.977" x2="31.577" y2="11.977" />
<line x1="11.612" y1="17.966" x2="31.577" y2="17.966" />
<line x1="11.612" y1="29.945" x2="31.577" y2="29.945" />
<line x1="11.612" y1="23.955" x2="31.577" y2="23.955" />
</svg>
);
} else if (iconType === "search") {
return (
<svg
x="0px"
y="0px"
width="40px"
height="40px"
viewBox="0 0 40 40"
enableBackground="new 0 0 40 40"
>
<circle cx="18.306" cy="18.307" r="13.856" />
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M28.24,28.24
l8.346,8.346L28.24,28.24z"
/>
</svg>
);
}
};
export default Icon;

View File

@@ -1,5 +1,5 @@
import React from "react";
import Popup from "./presentational/Popup";
import Popup from "./atoms/Popup";
import copy from "../common/data/copy.json";
const Infopopup = ({ isOpen, onClose, language, styles }) => (

View File

@@ -5,24 +5,25 @@ import { connect } from "react-redux";
import * as actions from "../actions";
import * as selectors from "../selectors";
import MediaOverlay from "./Overlay/Media";
import LoadingOverlay from "./Overlay/Loading";
import Map from "./Map.jsx";
import Toolbar from "./Toolbar/Layout";
import CardStack from "./CardStack.jsx";
import NarrativeControls from "./presentational/Narrative/Controls.js";
import InfoPopup from "./InfoPopup.jsx";
import Popup from "./presentational/Popup";
import Timeline from "./Timeline.jsx";
import Notification from "./Notification.jsx";
import StateOptions from "./StateOptions.jsx";
import StaticPage from "./StaticPage";
import Toolbar from "./Toolbar";
import InfoPopup from "./InfoPopup";
import Notification from "./Notification";
import TemplateCover from "./TemplateCover";
import Popup from "./atoms/Popup";
import StaticPage from "./atoms/StaticPage";
import MediaOverlay from "./atoms/Media";
import LoadingOverlay from "./atoms/Loading";
import Timeline from "./time/Timeline";
import Space from "./space/Space";
import Search from "./controls/Search";
import CardStack from "./controls/CardStack";
import NarrativeControls from "./controls/NarrativeControls.js";
import colors from "../common/global";
import { binarySearch, insetSourceFrom } from "../common/utilities";
import { isMobileOnly } from "react-device-detect";
import Search from "./Search.jsx";
class Dashboard extends React.Component {
constructor(props) {
@@ -329,7 +330,8 @@ class Dashboard extends React.Component {
onSelectNarrative: this.setNarrative,
}}
/>
<Map
<Space
kind={"map" in app ? "map" : "space3d"}
onKeyDown={this.onKeyDown}
methods={{
onSelectNarrative: this.setNarrative,
@@ -359,16 +361,6 @@ class Dashboard extends React.Component {
onToggleCardstack={() => actions.updateSelected([])}
getCategoryColor={this.getCategoryColor}
/>
<StateOptions
showing={
this.props.narratives &&
this.props.narratives.length !== 0 &&
!app.associations.narrative &&
app.associations.filters.length > 0
}
timelineDims={app.timeline.dimensions}
onClickHandler={this.setNarrativeFromFilters}
/>
<NarrativeControls
narrative={
app.associations.narrative

View File

@@ -1,30 +0,0 @@
import React, { useState } from "react";
const StateOptions = ({ showing, onClickHandler, timelineDims }) => {
const [checked, setChecked] = useState(false);
const handleCheck = () => setChecked(!checked);
const onNarrativise = () => onClickHandler(checked);
if (!showing) {
return null;
}
return (
<div className="stateoptions-panel" style={{ bottom: timelineDims.height }}>
<div>
<div className="button" onClick={onNarrativise}>
Narrativise
</div>
<label for="withlines">Connect by lines</label>
<input
name="withlines"
onClick={handleCheck}
checked={checked}
type="checkbox"
/>
</div>
</div>
);
};
export default StateOptions;

View File

@@ -2,7 +2,7 @@ import React from "react";
import { connect } from "react-redux";
import { Player } from "video-react";
import marked from "marked";
import MediaOverlay from "./Overlay/Media";
import MediaOverlay from "./atoms/Media";
import falogo from "../assets/fa-logo.png";
import bcatlogo from "../assets/bellingcat-logo.png";
const MEDIA_HIDDEN = -2;

View File

@@ -1,20 +1,20 @@
import React from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import * as actions from "../../actions";
import * as selectors from "../../selectors";
import * as actions from "../actions";
import * as selectors from "../selectors";
import { Tabs, TabPanel } from "react-tabs";
import FilterListPanel from "./FilterListPanel";
import CategoriesListPanel from "./CategoriesListPanel";
import BottomActions from "./BottomActions";
import copy from "../../common/data/copy.json";
import FilterListPanel from "./controls/FilterListPanel";
import CategoriesListPanel from "./controls/CategoriesListPanel";
import BottomActions from "./controls/BottomActions";
import copy from "../common/data/copy.json";
import {
trimAndEllipse,
getImmediateFilterParent,
getFilterSiblings,
getFilterParents,
} from "../../common/utilities.js";
} from "../common/utilities.js";
class Toolbar extends React.Component {
constructor(props) {

View File

@@ -1,80 +0,0 @@
import React from "react";
import Checkbox from "../presentational/Checkbox";
function SelectFilter(props) {
function isActive() {
if (props.isCategory) {
return props.categoryFilters.includes(props.filter.id);
}
return props.filterFilters.includes(props.filter.id);
}
function onClickFilter() {
if (isActive()) {
props.filter({
filters: props.filterFilters.filter(
(element) => element !== props.filter.id
),
});
} else {
props.filter({
filters: props.filterFilters.concat(props.filter.id),
});
}
}
function onClickCategory() {
if (isActive()) {
props.filter({
categories: props.categoryFilters.filter(
(element) => element !== props.filter.id
),
});
} else {
props.filter({
categories: props.categoryFilters.concat(props.filter.id),
});
}
}
function renderFilter() {
const filter = props.filter;
const classes = isActive() ? "filter-filter active" : "filter-filter";
let label = `${filter.name} ( ${filter.mentions} )`;
if (props.isShowTree) {
label = `${filter.group} > ${filter.subgroup} > ${filter.name} ( ${filter.mentions} )`;
}
return (
<li key={props.filter.id} className={classes}>
<Checkbox
isActive={isActive()}
label={label}
onClickCheckbox={() => onClickFilter()}
/>
</li>
);
}
function renderCategory() {
const category = props.categories[props.filter.id];
const classes = isActive() ? "filter-filter active" : "filter-filter";
if (category) {
return (
<li key={props.filter.id} className={classes}>
<Checkbox
isActive={isActive()}
label={`${category.name} ( ${category.counts} )`}
onClickCheckbox={onClickCategory}
/>
</li>
);
}
return <div />;
}
if (props.isCategory) return renderCategory();
return renderFilter();
}
export default SelectFilter;

View File

@@ -1,5 +1,5 @@
import React from "react";
import { getCoordinatesForPercent } from "../../../common/utilities";
import { getCoordinatesForPercent } from "../../common/utilities";
function ColoredMarkers({ radius, colorPercentMap, styles, className }) {
let cumulativeAngleSweep = 0;

View File

@@ -2,8 +2,8 @@ import React from "react";
import { Player } from "video-react";
import Img from "react-image";
import Md from "./Md";
import Spinner from "../presentational/Spinner";
import NoSource from "../presentational/NoSource";
import Spinner from "../atoms/Spinner";
import NoSource from "../atoms/NoSource";
const Content = ({ media, viewIdx, translations, switchLanguage, langIdx }) => {
const el = document.querySelector(".source-media-gallery");

View File

@@ -1,8 +1,8 @@
import React from "react";
import SitesIcon from "../presentational/Icons/Sites";
import CoverIcon from "../presentational/Icons/Cover";
import InfoIcon from "../presentational/Icons/Info";
import SitesIcon from "../atoms/SitesIcon";
import CoverIcon from "../atoms/CoverIcon";
import InfoIcon from "../atoms/InfoIcon";
function BottomActions(props) {
function renderToggles() {

View File

@@ -5,10 +5,9 @@ import {
generateCardLayout,
} from "@forensic-architecture/design-system/dist/react";
import * as selectors from "../selectors";
import { getFilterIdxFromColorSet } from "../common/utilities";
// import Card from './Card.jsx'
import copy from "../common/data/copy.json";
import * as selectors from "../../selectors";
import { getFilterIdxFromColorSet } from "../../common/utilities";
import copy from "../../common/data/copy.json";
class CardStack extends React.Component {
constructor() {

View File

@@ -1,6 +1,6 @@
import React from "react";
import marked from "marked";
import Checkbox from "../presentational/Checkbox";
import Checkbox from "../atoms/Checkbox";
import copy from "../../common/data/copy.json";
const CategoriesListPanel = ({

View File

@@ -1,5 +1,5 @@
import React from "react";
import Checkbox from "../presentational/Checkbox";
import Checkbox from "../atoms/Checkbox";
import marked from "marked";
import copy from "../../common/data/copy.json";
import { getFilterIdxFromColorSet } from "../../common/utilities";

View File

@@ -1,7 +1,7 @@
import React from "react";
import Card from "./Card";
import Adjust from "./Adjust";
import Close from "./Close";
import Card from "./atoms/NarrativeCard";
import Adjust from "./atoms/NarrativeAdjust";
import Close from "./atoms/NarrativeClose";
const NarrativeControls = ({ narrative, methods }) => {
if (!narrative) return null;

View File

@@ -2,11 +2,9 @@ import React from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import * as actions from "../actions";
import * as actions from "../../actions";
import "../scss/search.scss";
import SearchRow from "./SearchRow.jsx";
import SearchRow from "./atoms/SearchRow.jsx";
class Search extends React.Component {
constructor(props) {

Binary file not shown.

View File

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

View File

@@ -1,15 +0,0 @@
import React from "react";
import { capitalize } from "../../../common/utilities.js";
const CardCategory = ({ categoryTitle, categoryLabel, color }) => (
<div className="card-row card-cell category">
<h4>{categoryTitle}</h4>
<p>
{capitalize(categoryLabel)}
<span className="color-category" style={{ background: color }} />
</p>
</div>
);
export default CardCategory;

View File

@@ -1,14 +0,0 @@
import React from "react";
import marked from "marked";
const CardCustomField = ({ field, value }) => (
<div className="card-cell">
<p>
<i className="material-icons left">{field.icon}</i>
<b>{field.title ? `${field.title}: ` : "- "}</b>
{field.kind === "text" ? value : marked(`[${value}](${field.value})`)}
</p>
</div>
);
export default CardCustomField;

View File

@@ -1,28 +0,0 @@
import React from "react";
import copy from "../../../common/data/copy.json";
const CardLocation = ({ language, location, isPrecise }) => {
if (location !== "") {
return (
<div className="card-cell location">
<p>
<i className="material-icons left">location_on</i>
{`${location}${isPrecise ? "" : " (Approximated)"}`}
</p>
</div>
);
} else {
const unknown = copy[language].cardstack.unknown_location;
return (
<div className="card-cell location">
<p>
<i className="material-icons left">location_on</i>
{unknown}
</p>
</div>
);
}
};
export default CardLocation;

View File

@@ -1,79 +0,0 @@
import React from "react";
import Img from "react-image";
import Spinner from "../Spinner";
import { typeForPath } from "../../../common/utilities";
const CardSource = ({ source, isLoading, onClickHandler }) => {
function renderIconText(type) {
switch (type) {
case "Eyewitness Testimony":
return "visibility";
case "Government Data":
return "public";
case "Satellite Imagery":
return "satellite";
case "Second-Hand Testimony":
return "visibility_off";
case "Video":
return "videocam";
case "Photo":
return "photo";
case "Photobook":
return "photo_album";
case "Document":
return "picture_as_pdf";
default:
return "help";
}
}
if (!source) {
return (
<div className="card-source">
<div>Error: this source was not found</div>
</div>
);
}
const isImgUrl = /\.(jpg|png)$/;
let thumbnail = source.thumbnail;
if (!thumbnail || thumbnail === "" || !thumbnail.match(isImgUrl)) {
// default to first image in paths, null if no images
const imgs = source.paths.filter((p) => p.match(isImgUrl));
thumbnail = imgs.length > 0 ? imgs[0] : null;
}
if (source.type === "" && source.paths.length >= 1) {
source.type = typeForPath(source.paths[0]);
}
const fallbackIcon = (
<i className="material-icons source-icon">{renderIconText(source.type)}</i>
);
return (
<div className="card-source">
{isLoading ? (
<Spinner />
) : (
<div className="source-row" onClick={() => onClickHandler(source)}>
{thumbnail ? (
<Img
className="source-icon"
src={thumbnail}
loader={<Spinner small />}
unloader={fallbackIcon}
width={30}
height={30}
/>
) : (
fallbackIcon
)}
<p>{source.title ? source.title : source.id}</p>
</div>
)}
</div>
);
};
export default CardSource;

View File

@@ -1,18 +0,0 @@
import React from "react";
import copy from "../../../common/data/copy.json";
const CardSummary = ({ language, description, isHighlighted }) => {
const summary = copy[language].cardstack.description;
return (
<div className="card-row summary">
<div className="card-cell">
<h4>{summary}</h4>
<p>{description}</p>
</div>
</div>
);
};
export default CardSummary;

View File

@@ -1,33 +0,0 @@
import React from "react";
import copy from "../../../common/data/copy.json";
import { isNotNullNorUndefined } from "../../../common/utilities";
const CardTime = ({ timelabel, language, precision }) => {
// const daytimeLang = copy[language].cardstack.timestamp
// const estimatedLang = copy[language].cardstack.estimated
const unknownLang = copy[language].cardstack.unknown_time;
if (isNotNullNorUndefined(timelabel)) {
return (
<div className="card-cell timestamp">
<p>
<i className="material-icons left">today</i>
{timelabel}
{precision && precision !== "" ? ` - ${precision}` : ""}
</p>
</div>
);
} else {
return (
<div className="card-cell timestamp">
<p>
<i className="material-icons left">today</i>
{unknownLang}
</p>
</div>
);
}
};
export default CardTime;

View File

@@ -0,0 +1,14 @@
import React from "react";
import MapCarto from "./carto/Map";
// import Map3d from "./3d/Map";
const Space = (props) => {
switch (props.kind) {
// case "3d":
// return <Map3d {...props} />;
default:
return <MapCarto {...props} />;
}
};
export default Space;

View File

@@ -1,21 +1,20 @@
/* global L */
import "leaflet";
import React from "react";
import { Portal } from "react-portal";
import Supercluster from "supercluster";
import { connect } from "react-redux";
import * as selectors from "../selectors";
import * as selectors from "../../../selectors";
import "leaflet";
import Sites from "./presentational/Map/Sites.jsx";
import Shapes from "./presentational/Map/Shapes.jsx";
import Events from "./presentational/Map/Events.jsx";
import Clusters from "./presentational/Map/Clusters.jsx";
import SelectedEvents from "./presentational/Map/SelectedEvents.jsx";
import Narratives from "./presentational/Map/Narratives";
import DefsMarkers from "./presentational/Map/DefsMarkers.jsx";
import LoadingOverlay from "../components/Overlay/Loading";
import Sites from "./atoms/Sites";
import Shapes from "./atoms/Shapes";
import Events from "./atoms/Events";
import Clusters from "./atoms/Clusters";
import SelectedEvents from "./atoms/SelectedEvents";
import Narratives from "./atoms/Narratives";
import DefsMarkers from "./atoms/DefsMarkers";
import LoadingOverlay from "../../atoms/Loading";
import {
mapClustersToLocations,
@@ -24,7 +23,7 @@ import {
isLongitude,
calculateTotalClusterPoints,
calcClusterSize,
} from "../common/utilities";
} from "../../../common/utilities";
// NB: important constants for map, TODO: make statics
const supportedMapboxMap = ["streets", "satellite"];

View File

@@ -1,7 +1,7 @@
import React, { useState } from "react";
import { Portal } from "react-portal";
import colors from "../../../common/global.js";
import ColoredMarkers from "./ColoredMarkers.jsx";
import colors from "../../../../common/global";
import ColoredMarkers from "../../../atoms/ColoredMarkers";
import {
calcClusterOpacity,
calcClusterSize,
@@ -10,7 +10,7 @@ import {
calculateColorPercentages,
zipColorsToPercentages,
calculateTotalClusterPoints,
} from "../../../common/utilities";
} from "../../../../common/utilities";
const DefsClusters = () => (
<defs>

View File

@@ -1,13 +1,13 @@
import React from "react";
import { Portal } from "react-portal";
import colors from "../../../common/global.js";
import ColoredMarkers from "./ColoredMarkers.jsx";
import colors from "../../../../common/global";
import ColoredMarkers from "../../../atoms/ColoredMarkers";
import {
calcOpacity,
getCoordinatesForPercent,
calculateColorPercentages,
zipColorsToPercentages,
} from "../../../common/utilities";
} from "../../../../common/utilities";
function MapEvents({
getCategoryColor,

View File

@@ -1,6 +1,6 @@
import React from "react";
import { Portal } from "react-portal";
import colors from "../../../common/global.js";
import colors from "../../../../common/global";
class MapSelectedEvents extends React.Component {
renderMarker(marker) {

View File

@@ -1,6 +1,6 @@
import React from "react";
import * as d3 from "d3";
import { setD3Locale } from "../common/utilities";
import { setD3Locale } from "../../common/utilities";
const TEXT_HEIGHT = 15;
setD3Locale(d3);

View File

@@ -2,19 +2,20 @@ import React from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import * as d3 from "d3";
import * as selectors from "../selectors";
import { setLoading, setNotLoading } from "../actions";
import hash from "object-hash";
import copy from "../common/data/copy.json";
import Header from "./presentational/Timeline/Header";
import Axis from "./TimelineAxis.jsx";
import Clip from "./presentational/Timeline/Clip";
import Handles from "./presentational/Timeline/Handles.js";
import ZoomControls from "./presentational/Timeline/ZoomControls.js";
import Markers from "./presentational/Timeline/Markers.js";
import Events from "./presentational/Timeline/Events.js";
import Categories from "./TimelineCategories.jsx";
import { setLoading, setNotLoading } from "../../actions";
import * as selectors from "../../selectors";
import copy from "../../common/data/copy.json";
import Header from "./atoms/Header";
import Axis from "./Axis";
import Clip from "./atoms/Clip";
import Handles from "./atoms/Handles.js";
import ZoomControls from "./atoms/ZoomControls.js";
import Markers from "./atoms/Markers.js";
import Events from "./atoms/Events.js";
import Categories from "./Categories";
class Timeline extends React.Component {
constructor(props) {

View File

@@ -3,7 +3,7 @@ import DatetimeBar from "./DatetimeBar";
import DatetimeSquare from "./DatetimeSquare";
import DatetimeStar from "./DatetimeStar";
import Project from "./Project";
import ColoredMarkers from "../Map/ColoredMarkers.jsx";
import ColoredMarkers from "../../atoms/ColoredMarkers";
import {
calcOpacity,
getEventCategories,

View File

@@ -1,8 +1,8 @@
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./store/index.js";
import App from "./components/App.jsx";
import store from "./store";
import App from "./components/App";
ReactDOM.render(
<Provider store={store}>

View File

@@ -21,13 +21,20 @@ function createEventSchema(custom) {
date: Joi.string().allow(""),
time: Joi.string().allow(""),
time_precision: Joi.string().allow(""),
/* map */
location: Joi.string().allow(""),
latitude: Joi.string().allow(""),
longitude: Joi.string().allow(""),
/* space */
x: Joi.string().allow(""),
y: Joi.string().allow(""),
z: Joi.string().allow(""),
type: Joi.string().allow(""),
category: Joi.string().allow(""),
category_full: Joi.string().allow(""),
associations: Joi.array().required().default([]),
associations: Joi.array().default([]),
sources: Joi.array(),
comments: Joi.string().allow(""),
time_display: Joi.string().allow(""),

View File

@@ -12,4 +12,4 @@
@import "notification";
@import "mediaplayer";
@import "cover";
@import "stateoptions";
@import "search";

View File

@@ -1,28 +0,0 @@
.stateoptions-panel {
position: absolute;
right: 0;
box-sizing: border-box;
margin: 1px 0 0 0;
padding: 15px;
border: 1px solid $black;
transition: 0.2 ease;
background: $midwhite;
color: $darkgrey;
box-shadow: 0 19px 19px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22);
font-size: $large;
line-height: $xxlarge;
height: auto;
opacity: 0.9;
transition: background-color 0.4s;
.button {
border: 1px solid black;
padding: 0.3em;
transition: all 0.3s ease;
&:hover {
background-color: black;
color: white;
cursor: pointer;
}
}
}

View File

@@ -5,6 +5,19 @@ import copy from "../common/data/copy.json";
import { language } from "../common/utilities";
const isSmallLaptop = window.innerHeight < 800;
const mapIniital = {
anchor: [31.356397, 34.784818],
startZoom: 11,
minZoom: 2,
maxZoom: 16,
bounds: null,
maxBounds: [
[180, -180],
[-180, 180],
],
};
const space3dInitial = {};
const initial = {
/*
* The Domain or 'domain' of this state refers to the tree of data
@@ -51,17 +64,6 @@ const initial = {
},
isMobile: /Mobi/.test(navigator.userAgent),
language: "en-US",
map: {
anchor: [31.356397, 34.784818],
startZoom: 11,
minZoom: 2,
maxZoom: 16,
bounds: null,
maxBounds: [
[180, -180],
[-180, 180],
],
},
cluster: {
radius: 30,
minZoom: 2,
@@ -173,7 +175,14 @@ if (process.env.store) {
// NB: config.js dates get implicitly converted to strings in mergeDeepLeft
appStore.app.timeline.range[0] = new Date(appStore.app.timeline.range[0]);
appStore.app.timeline.range[1] = new Date(appStore.app.timeline.range[1]);
appStore.app.flags.isIntropopup = !!appStore.app.intro;
if ("map" in appStore.app) {
appStore.app.map = mergeDeepLeft(appStore.app.map, mapIniital);
}
if ("space3d" in appStore.app) {
appStore.app.space3d = mergeDeepLeft(appStore.app.space3d, space3dInitial);
}
export default appStore;