summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--public/assets/css/css.css35
-rw-r--r--src/graph.js31
-rw-r--r--src/views/App.js26
-rw-r--r--src/views/Legend.js27
4 files changed, 113 insertions, 6 deletions
diff --git a/public/assets/css/css.css b/public/assets/css/css.css
index fd62f45..f976e8f 100644
--- a/public/assets/css/css.css
+++ b/public/assets/css/css.css
@@ -14,9 +14,38 @@ body {
padding: 4px;
max-width: 250px;
text-align: center;
- font-size: 18px !important;
- line-height: 1.3;
- font-family: serif !important;
+ font-size: 1rem !important;
+ line-height: 1.5;
+ font-family: "Merriweather", serif !important;
+}
+
+/** Legend */
+
+.legend {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ font-size: 1rem;
+ line-height: 1.25;
+ padding: 1rem;
+ margin: 1rem 2rem;
+ background: rgba(0, 0, 0, 0.5);
+ text-transform: capitalize;
+ font-variant: small-caps;
+ color: #fff;
+ transition: opacity 0.2s;
+}
+.legend div {
+ cursor: pointer;
+}
+.legend div:hover {
+ text-decoration: underline;
+}
+.legend .selected {
+ text-decoration: underline;
+}
+.legend .unselected {
+ opacity: 0.8;
}
/** Detail view */
diff --git a/src/graph.js b/src/graph.js
index 4ed953a..640b42e 100644
--- a/src/graph.js
+++ b/src/graph.js
@@ -102,6 +102,34 @@ export default function buildGraph(db, handlers) {
})
.onNodeClick(handlers.click);
+ const handleSelect = (category) => {
+ if (!category) {
+ graph.graphData(data);
+ return;
+ }
+ const { nodes, links } = data;
+ const visible = new Set();
+
+ const selectedData = {};
+ selectedData.nodes = nodes.filter((node) => {
+ for (let tagIndex = 0; tagIndex < 8; tagIndex += 1) {
+ const group = node.data["tag_" + tagIndex];
+ if (!group) continue;
+ if (group === category) {
+ visible.add(node.id);
+ return true;
+ }
+ }
+ return false;
+ });
+ selectedData.links = links.filter((link) => {
+ const { source, target } = link;
+ return visible.has(source.id) && visible.has(target.id);
+ });
+ console.log(selectedData);
+ graph.graphData(selectedData);
+ };
+
// graph.d3Force("charge").strength(-150);
// camera orbit
@@ -112,6 +140,9 @@ export default function buildGraph(db, handlers) {
z: distance * Math.cos(angle),
});
+ return {
+ onSelect: handleSelect,
+ };
// stars();
}
diff --git a/src/views/App.js b/src/views/App.js
index c5a7f83..561523d 100644
--- a/src/views/App.js
+++ b/src/views/App.js
@@ -5,19 +5,24 @@
import React, { useState, useEffect, useCallback } from "react";
import Detail from "./Detail.js";
+import Legend from "./Legend.js";
import buildGraph from "../graph.js";
export default function App() {
const [db, setDb] = useState(null);
const [node, setNode] = useState(null);
+ const [graph, setGraph] = useState(null);
+ const [selected, setSelected] = useState(null);
const [detailVisible, setDetailVisible] = useState(null);
useEffect(async () => {
const newDb = await loadDB();
setDb(newDb);
- buildGraph(newDb, {
- click: handleClick,
- });
+ setGraph(
+ buildGraph(newDb, {
+ click: handleClick,
+ })
+ );
}, []);
const handleClick = useCallback((node) => {
@@ -29,9 +34,24 @@ export default function App() {
setDetailVisible(false);
});
+ const handleSelect = useCallback((category) => {
+ if (category === selected) {
+ setSelected(null);
+ graph.onSelect(null);
+ } else {
+ setSelected(category);
+ graph.onSelect(category);
+ }
+ });
+
return (
<div>
<Detail node={node} visible={detailVisible} onClose={handleClose} />
+ <Legend
+ visible={!detailVisible}
+ selected={selected}
+ onSelect={handleSelect}
+ />
</div>
);
}
diff --git a/src/views/Legend.js b/src/views/Legend.js
new file mode 100644
index 0000000..eedcc35
--- /dev/null
+++ b/src/views/Legend.js
@@ -0,0 +1,27 @@
+/**
+ * Category list in the corner
+ */
+
+import React, { useState, useEffect } from "react";
+
+const categories = "No6092,1620s,painting,blunt,National Gallery of Canada,AGO,courtauld,intervensions,connsoeurship,double agent,forensics,black box,Stankievech".split(
+ ","
+);
+
+export default function Legend({ visible, selected, onSelect }) {
+ return (
+ <div className="legend" style={{ opacity: visible ? 1 : 0 }}>
+ {categories.map((category, index) => (
+ <div
+ key={category}
+ className={
+ selected ? (selected === index + 1 ? "selected" : "unselected") : ""
+ }
+ onClick={() => onSelect(index + 1)}
+ >
+ {category}
+ </div>
+ ))}
+ </div>
+ );
+}