Polygon shapes abstracted to be types of checkboxes; abstracted rendering of panel into one component; importing shapes from separate endpoint

This commit is contained in:
efarooqui
2021-05-20 18:44:26 -07:00
parent e431422c19
commit 7156639ec3
23 changed files with 308 additions and 133 deletions

View File

@@ -327,6 +327,7 @@ class Dashboard extends React.Component {
actions.toggleAssociations("filters", filters),
onCategoryFilter: (categories) =>
actions.toggleAssociations("categories", categories),
onShapeFilter: actions.toggleShapes,
onSelectNarrative: this.setNarrative,
}}
/>

View File

@@ -7,6 +7,7 @@ import * as selectors from "../selectors";
import { Tabs, TabPanel } from "react-tabs";
import FilterListPanel from "./controls/FilterListPanel";
import CategoriesListPanel from "./controls/CategoriesListPanel";
import ShapesListPanel from "./controls/ShapesListPanel";
import BottomActions from "./controls/BottomActions";
import copy from "../common/data/copy.json";
import {
@@ -17,7 +18,6 @@ import {
addToColoringSet,
removeFromColoringSet,
} from "../common/utilities.js";
import { DEFAULT_TAB_ICONS } from "../common/constants";
class Toolbar extends React.Component {
constructor(props) {
@@ -85,17 +85,10 @@ class Toolbar extends React.Component {
renderToolbarNarrativePanel() {
const { panels } = this.props.toolbarCopy;
const panelTitle = panels.narratives.label
? panels.narratives.label
: copy[this.props.language].toolbar.narratives;
const panelDescription = panels.narratives.description
? panels.narratives.description
: copy[this.props.language].toolbar.explore_by_narrative__description;
return (
<TabPanel>
<h2>{panelTitle}</h2>
<p>{panelDescription}</p>
<h2>{panels.narratives.label}</h2>
<p>{panels.narratives.description}</p>
{this.props.narratives.map((narr) => {
return (
<div className="panel-action action">
@@ -118,13 +111,6 @@ class Toolbar extends React.Component {
renderToolbarCategoriesPanel() {
const { panels } = this.props.toolbarCopy;
const panelTitle = panels.categories.label
? panels.categories.label
: copy[this.props.language].toolbar.categories;
const panelDescription = panels.categories.description
? panels.categories.description
: copy[this.props.language].toolbar.explore_by_category__description;
if (this.props.features.USE_CATEGORIES) {
return (
<TabPanel>
@@ -133,8 +119,9 @@ class Toolbar extends React.Component {
activeCategories={this.props.activeCategories}
onCategoryFilter={this.props.methods.onCategoryFilter}
language={this.props.language}
title={panelTitle}
description={panelDescription}
title={panels.categories.label}
description={panels.categories.description}
checkboxColor={this.props.categoriesCheckboxColor}
/>
</TabPanel>
);
@@ -143,13 +130,6 @@ class Toolbar extends React.Component {
renderToolbarFilterPanel() {
const { panels } = this.props.toolbarCopy;
const panelTitle = panels.filters.label
? panels.filters.label
: copy[this.props.language].toolbar.filters;
const panelDescription = panels.filters.description
? panels.filters.description
: copy[this.props.language].toolbar.explore_by_filter__description;
return (
<TabPanel>
<FilterListPanel
@@ -159,13 +139,33 @@ class Toolbar extends React.Component {
language={this.props.language}
coloringSet={this.props.coloringSet}
filterColors={this.props.filterColors}
title={panelTitle}
description={panelDescription}
title={panels.filters.label}
description={panels.filters.description}
/>
</TabPanel>
);
}
renderToolbarShapePanel() {
const { panels } = this.props.toolbarCopy;
if (this.props.features.USE_SHAPES) {
return (
<TabPanel>
<ShapesListPanel
shapes={this.props.shapes}
activeShapes={this.props.activeShapes}
onShapeFilter={this.props.methods.onShapeFilter}
language={this.props.language}
title={panels.shapes.label}
description={panels.shapes.description}
checkboxColor={this.props.shapesCheckboxColor}
/>
</TabPanel>
);
}
}
renderToolbarTab(_selected, label, iconKey) {
const isActive = this.state._selected === _selected;
const classes = isActive ? "toolbar-tab active" : "toolbar-tab";
@@ -196,6 +196,7 @@ class Toolbar extends React.Component {
: null}
{features.USE_CATEGORIES ? this.renderToolbarCategoriesPanel() : null}
{features.USE_ASSOCIATIONS ? this.renderToolbarFilterPanel() : null}
{features.USE_SHAPES ? this.renderToolbarShapePanel() : null}
</Tabs>
</div>
);
@@ -228,31 +229,17 @@ class Toolbar extends React.Component {
const narrativesExist = narratives && narratives.length !== 0;
let title = copy[this.props.language].toolbar.title;
if (process.env.display_title) title = process.env.display_title;
const { panels } = toolbarCopy;
const narrativesLabel = copy[this.props.language].toolbar.narratives_label;
const filtersLabel = panels.filters.label
? panels.filters.label
: copy[this.props.language].toolbar.filters_label;
const categoriesLabel = panels.categories.label
? panels.categories.label
: copy[this.props.language].toolbar.categories_label;
const filterIcon = panels.filters.icon
? panels.filters.icon
: DEFAULT_TAB_ICONS.FILTER;
const categoriesIcon = panels.categories.icon
? panels.categories.icon
: DEFAULT_TAB_ICONS.CATEGORY;
const narrativesIdx = 0;
const categoriesIdx = narrativesExist ? 1 : 0;
const filtersIdx =
narrativesExist && features.CATEGORIES_AS_FILTERS
narrativesExist && features.USE_CATEGORIES
? 2
: narrativesExist || features.CATEGORIES_AS_FILTERS
: narrativesExist || features.USE_CATEGORIES
? 1
: 0;
const shapesIdx = filtersIdx + 1;
return (
<div className="toolbar">
<div className="toolbar-header" onClick={this.props.methods.onTitle}>
@@ -260,17 +247,32 @@ class Toolbar extends React.Component {
</div>
<div className="toolbar-tabs">
{narrativesExist
? this.renderToolbarTab(narrativesIdx, narrativesLabel, "timeline")
? this.renderToolbarTab(
narrativesIdx,
panels.narratives.label,
panels.narratives.icon
)
: null}
{features.CATEGORIES_AS_FILTERS
{features.USE_CATEGORIES
? this.renderToolbarTab(
categoriesIdx,
categoriesLabel,
categoriesIcon
panels.categories.label,
panels.categories.icon
)
: null}
{features.USE_ASSOCIATIONS
? this.renderToolbarTab(filtersIdx, filtersLabel, filterIcon)
? this.renderToolbarTab(
filtersIdx,
panels.filters.label,
panels.filters.icon
)
: null}
{features.USE_SHAPES
? this.renderToolbarTab(
shapesIdx,
panels.shapes.label,
panels.shapes.icon
)
: null}
</div>
<BottomActions
@@ -311,10 +313,12 @@ function mapStateToProps(state) {
filters: selectors.getFilters(state),
categories: selectors.getCategories(state),
narratives: selectors.selectNarratives(state),
shapes: selectors.getShapes(state),
language: state.app.language,
toolbarCopy: state.app.toolbar,
activeFilters: selectors.getActiveFilters(state),
activeCategories: selectors.getActiveCategories(state),
activeShapes: selectors.getActiveShapes(state),
viewFilters: state.app.associations.views,
narrative: state.app.associations.narrative,
sitesShowing: state.app.flags.isShowingSites,
@@ -322,6 +326,9 @@ function mapStateToProps(state) {
coloringSet: state.app.associations.coloringSet,
maxNumOfColors: state.ui.coloring.maxNumOfColors,
filterColors: state.ui.coloring.colors,
eventRadius: state.ui.eventRadius,
categoriesCheckboxColor: state.ui.style.categories.checkboxColor,
shapesCheckboxColor: state.ui.style.shapes.checkboxColor,
features: selectors.getFeatures(state),
};
}

View File

@@ -1,16 +1,13 @@
import React from "react";
const Checkbox = ({ label, isActive, onClickCheckbox, color }) => {
const styles = {
background: isActive ? color : "none",
border: `1px solid ${color}`,
};
const Checkbox = ({ label, isActive, onClickCheckbox, color, styleProps }) => {
return (
<div className={isActive ? "item active" : "item"}>
<span style={{ color: color }}>{label}</span>
<button onClick={onClickCheckbox}>
<div className="checkbox" style={styles} />
<div className="border">
<div className="checkbox" style={styleProps} />
</div>
</button>
</div>
);

View File

@@ -1,6 +1,6 @@
import React from "react";
import marked from "marked";
import Checkbox from "../atoms/Checkbox";
import PanelTree from "./atoms/PanelTree";
const CategoriesListPanel = ({
categories,
@@ -9,29 +9,8 @@ const CategoriesListPanel = ({
language,
title,
description,
checkboxColor,
}) => {
function renderCategoryTree() {
return (
<div>
{categories.map((cat) => {
return (
<li
key={cat.title.replace(/ /g, "_")}
className="filter-filter active"
style={{ marginLeft: "20px" }}
>
<Checkbox
label={cat.title}
isActive={activeCategories.includes(cat.title)}
onClickCheckbox={() => onCategoryFilter(cat.title)}
/>
</li>
);
})}
</div>
);
}
return (
<div className="react-innertabpanel">
<h2>{title}</h2>
@@ -40,7 +19,12 @@ const CategoriesListPanel = ({
__html: marked(description),
}}
/>
{renderCategoryTree()}
<PanelTree
data={categories}
activeValues={activeCategories}
onSelect={onCategoryFilter}
defaultCheckboxColor={checkboxColor}
/>
</div>
);
};

View File

@@ -0,0 +1,34 @@
import React from "react";
import marked from "marked";
import PanelTree from "./atoms/PanelTree";
import { mapStyleByShape } from "../../common/utilities";
const ShapesListPanel = ({
shapes,
activeShapes,
onShapeFilter,
language,
title,
description,
checkboxColor,
}) => {
const styledShapes = mapStyleByShape(shapes, activeShapes);
return (
<div className="react-innertabpanel">
<h2>{title}</h2>
<p
dangerouslySetInnerHTML={{
__html: marked(description),
}}
/>
<PanelTree
data={styledShapes}
activeValues={activeShapes}
onSelect={onShapeFilter}
defaultCheckboxColor={checkboxColor}
/>
</div>
);
};
export default ShapesListPanel;

View File

@@ -0,0 +1,32 @@
import React from "react";
import Checkbox from "../../atoms/Checkbox";
const PanelTree = ({ data, activeValues, onSelect, defaultCheckboxColor }) => {
return (
<div>
{data.map((val) => {
const isActive = activeValues.includes(val.title);
const baseStyles = {
background: isActive ? defaultCheckboxColor : "none",
border: `1px solid ${defaultCheckboxColor}`,
};
return (
<li
key={val.title.replace(/ /g, "_")}
className="filter-filter active"
style={{ marginLeft: "20px" }}
>
<Checkbox
label={val.title}
isActive={isActive}
onClickCheckbox={() => onSelect(val.title)}
styleProps={val.styles ? val.styles : baseStyles}
/>
</li>
);
})}
</div>
);
};
export default PanelTree;

View File

@@ -8,7 +8,7 @@ import { connect } from "react-redux";
import * as selectors from "../../../selectors";
import Sites from "./atoms/Sites";
import Shapes from "./atoms/Shapes";
import Regions from "./atoms/Regions";
import Events from "./atoms/Events";
import Clusters from "./atoms/Clusters";
import SelectedEvents from "./atoms/SelectedEvents";
@@ -324,13 +324,13 @@ class Map extends React.Component {
);
}
renderShapes() {
renderRegions() {
return (
<Shapes
<Regions
svg={this.svgRef.current}
shapes={this.props.domain.shapes}
regions={this.props.domain.regions}
projectPoint={this.projectPoint}
styles={this.props.ui.shapes}
styles={this.props.ui.regions}
/>
);
}
@@ -481,7 +481,7 @@ class Map extends React.Component {
{this.renderTiles()}
{this.renderMarkers()}
{isShowingSites ? this.renderSites() : null}
{this.renderShapes()}
{this.renderRegions()}
{this.renderNarratives()}
{this.renderEvents()}
{this.renderClusters()}
@@ -510,7 +510,7 @@ function mapStateToProps(state) {
narratives: selectors.selectNarratives(state),
categories: selectors.getCategories(state),
sites: selectors.selectSites(state),
shapes: selectors.selectShapes(state),
regions: selectors.selectRegions(state),
},
app: {
views: state.app.associations.views,
@@ -532,7 +532,7 @@ function mapStateToProps(state) {
dom: state.ui.dom,
narratives: state.ui.style.narratives,
mapSelectedEvents: state.ui.style.selectedEvents,
shapes: state.ui.style.shapes,
regions: state.ui.style.regions,
eventRadius: state.ui.eventRadius,
radial: state.ui.style.clusters.radial,
filterColors: state.ui.coloring.colors,

View File

@@ -1,13 +1,13 @@
import React from "react";
import { Portal } from "react-portal";
function MapShapes({ svg, shapes, projectPoint, styles }) {
function renderShape(shape) {
function MapRegions({ svg, regions, projectPoint, styles }) {
function renderRegion(region) {
const lineCoords = [];
const points = shape.points.map(projectPoint);
const points = region.points.map(projectPoint);
points.forEach((p1, idx) => {
if (idx < shape.points.length - 1) {
if (idx < region.points.length - 1) {
const p2 = points[idx + 1];
lineCoords.push({
x1: p1.x,
@@ -19,29 +19,29 @@ function MapShapes({ svg, shapes, projectPoint, styles }) {
});
return lineCoords.map((coords) => {
const shapeStyles =
shape.name in styles ? styles[shape.name] : styles.default;
const regionstyles =
region.name in styles ? styles[region.name] : styles.default;
return (
<line
id={`${shape.name}_style`}
id={`${region.name}_style`}
markerStart="none"
{...coords}
style={shapeStyles}
style={regionstyles}
/>
);
});
}
if (!shapes || !shapes.length) return null;
if (!regions || !regions.length) return null;
return (
<Portal node={svg}>
<g id="shapes-layer" className="narrative">
{shapes.map(renderShape)}
<g id="regions-layer" className="narrative">
{regions.map(renderRegion)}
</g>
</Portal>
);
}
export default MapShapes;
export default MapRegions;

View File

@@ -7,7 +7,7 @@ const DatetimePentagon = ({ x, y, r, transform, onSelect, styleProps }) => {
onClick={onSelect}
className="event"
x={x}
y={y - r}
y={y}
style={styleProps}
points={`${x},${y + s} ${x + s},${y} ${x + s},${y - s} ${x - s},${
y - s

View File

@@ -15,7 +15,7 @@ const DatetimeStar = ({
onClick={onSelect}
className="event"
x={x}
y={y - r}
y={y}
style={styleProps}
points={`${x + s},${y - s} ${x - r},${y} ${x + r},${y} ${x - s},${
y - s

View File

@@ -7,7 +7,7 @@ const DatetimeTriangle = ({ x, y, r, transform, onSelect, styleProps }) => {
onClick={onSelect}
className="event"
x={x}
y={y - r}
y={y}
style={styleProps}
points={`${x},${y + s} ${x + s},${y - s} ${x - s},${y - s}`}
transform={`rotate(180, ${x}, ${y})`}

View File

@@ -14,6 +14,7 @@ import {
isLatitude,
isLongitude,
} from "../../../common/utilities";
import { AVAILABLE_SHAPES } from "../../../common/constants";
function renderDot(event, styles, props) {
const colorPercentages = calculateColorPercentages(
@@ -158,17 +159,17 @@ const TimelineEvents = ({
let renderShape = isDot ? renderDot : renderBar;
if (event.shape) {
if (event.shape === "bar") {
if (event.shape === AVAILABLE_SHAPES.BAR) {
renderShape = renderBar;
} else if (event.shape === "diamond") {
} else if (event.shape === AVAILABLE_SHAPES.DIAMOND) {
renderShape = renderDiamond;
} else if (event.shape === "star") {
} else if (event.shape === AVAILABLE_SHAPES.STAR) {
renderShape = renderStar;
} else if (event.shape === "triangle") {
} else if (event.shape === AVAILABLE_SHAPES.TRIANGLE) {
renderShape = renderTriangle;
} else if (event.shape === "pentagon") {
} else if (event.shape === AVAILABLE_SHAPES.PENTAGON) {
renderShape = renderPentagon;
} else if (event.shape === "square") {
} else if (event.shape === AVAILABLE_SHAPES.SQUARE) {
renderShape = renderSquare;
} else {
renderShape = renderDot;

View File

@@ -5,6 +5,7 @@ import {
isLatitude,
isLongitude,
} from "../../../common/utilities";
import { AVAILABLE_SHAPES } from "../../../common/constants";
const TimelineMarkers = ({
styles,
@@ -72,11 +73,11 @@ const TimelineMarkers = ({
function renderMarkerForEvent(y) {
switch (event.shape) {
case "circle":
case "diamond":
case "star":
case AVAILABLE_SHAPES.DIAMOND:
case AVAILABLE_SHAPES.STAR:
acc.push(renderCircle(y));
break;
case "bar":
case AVAILABLE_SHAPES.BAR:
acc.push(renderBar(y));
break;
default: