|
import { Backdrop, CircularProgress, List, ListItem, Paper, Typography } from "@mui/material"; |
|
import * as d3 from "d3"; |
|
import React, { useCallback, useContext, useEffect, useRef, useState } from "react"; |
|
import { TopicsContext } from "./UploadFileContext"; |
|
|
|
const bunkaTopics = "bunka_topics.json"; |
|
const { REACT_APP_API_ENDPOINT } = process.env; |
|
|
|
function TreemapView() { |
|
const svgRef = useRef(null); |
|
const [selectedTopic, setSelectedTopic] = useState({ name: "", content: [] }); |
|
const { data: apiData, isLoading } = useContext(TopicsContext); |
|
|
|
const createTreemap = useCallback((data) => { |
|
const width = window.innerWidth * 0.6; |
|
const height = 800; |
|
|
|
const svg = d3.select(svgRef.current).attr("width", width).attr("height", height); |
|
|
|
const root = d3.hierarchy({ children: data }).sum((d) => d.size); |
|
|
|
const treemapLayout = d3.treemap().size([width, height]).padding(1).round(true); |
|
|
|
treemapLayout(root); |
|
|
|
const cell = svg |
|
.selectAll("g") |
|
.data(root.leaves()) |
|
.enter() |
|
.append("g") |
|
.attr("transform", (d) => `translate(${d.x0},${d.y0})`) |
|
.on("click", (event, d) => { |
|
const topicName = d.data.name; |
|
const topicContent = d.data.top_doc_content || []; |
|
|
|
setSelectedTopic({ name: topicName, content: topicContent }); |
|
}); |
|
|
|
cell |
|
.append("rect") |
|
.attr("width", (d) => d.x1 - d.x0) |
|
.attr("height", (d) => d.y1 - d.y0) |
|
.style("fill", "lightblue") |
|
.style("stroke", "blue"); |
|
|
|
cell |
|
.append("text") |
|
.selectAll("tspan") |
|
.data((d) => { |
|
const text = d.data.name.split(/(?=[A-Z][^A-Z])/g); |
|
return text; |
|
}) |
|
.enter() |
|
.append("tspan") |
|
.attr("x", 3) |
|
.attr("y", (d, i) => 13 + i * 10) |
|
.text((d) => d); |
|
|
|
svg.selectAll("text").attr("font-size", 13).attr("fill", "black"); |
|
}, []); |
|
|
|
useEffect(() => { |
|
if (REACT_APP_API_ENDPOINT === "local" || apiData === undefined) { |
|
|
|
fetch(`/${bunkaTopics}`) |
|
.then((response) => response.json()) |
|
.then((localData) => { |
|
createTreemap(localData); |
|
}) |
|
.catch((error) => { |
|
console.error("Error fetching JSON data:", error); |
|
}); |
|
} else { |
|
|
|
createTreemap(apiData.topics); |
|
} |
|
}, [apiData, createTreemap]); |
|
|
|
return ( |
|
<div> |
|
<h2>Treemap View</h2> |
|
{isLoading ? ( |
|
<Backdrop open={isLoading} style={{ zIndex: 9999 }}> |
|
<CircularProgress color="primary" /> |
|
</Backdrop> |
|
) : ( |
|
<div style={{ display: "flex" }}> |
|
<svg ref={svgRef} style={{ marginRight: "20px" }} /> |
|
<div |
|
style={{ |
|
width: window.innerWidth * 0.25, |
|
maxHeight: "800px", |
|
overflowY: "auto", |
|
}} |
|
> |
|
<Paper> |
|
<Typography |
|
variant="h4" |
|
style={{ |
|
position: "sticky", |
|
top: 0, |
|
backgroundColor: "white", |
|
color: "blue", |
|
}} |
|
> |
|
{selectedTopic.name} |
|
</Typography> |
|
{selectedTopic.content.map((doc, index) => ( |
|
<List key={doc.id}> |
|
<ListItem> |
|
<Typography variant="h5">{doc}</Typography> |
|
</ListItem> |
|
</List> |
|
))} |
|
{selectedTopic.content.length === 0 && <Typography variant="h4">Click on a Square.</Typography>} |
|
</Paper> |
|
</div> |
|
</div> |
|
)} |
|
</div> |
|
); |
|
} |
|
|
|
export default TreemapView; |
|
|