diff options
| -rw-r--r-- | public/assets/css/css.css | 35 | ||||
| -rw-r--r-- | src/graph.js | 31 | ||||
| -rw-r--r-- | src/views/App.js | 26 | ||||
| -rw-r--r-- | src/views/Legend.js | 27 |
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> + ); +} |
