Files
gesara-entity-viz/src/views/Root.tsx

134 lines
4.7 KiB
TypeScript

import React, { FC, useEffect, useState } from "react";
import { SigmaContainer, ZoomControl, FullScreenControl } from "react-sigma-v2";
import { omit, mapValues, keyBy, constant } from "lodash";
import getNodeProgramImage from "sigma/rendering/webgl/programs/node.image";
import GraphSettingsController from "./GraphSettingsController";
import GraphEventsController from "./GraphEventsController";
import GraphDataController from "./GraphDataController";
import DescriptionPanel from "./DescriptionPanel";
import { Dataset, FiltersState } from "../types";
import ClustersPanel from "./ClustersPanel";
import SearchField from "./SearchField";
import drawLabel from "../canvas-utils";
import GraphTitle from "./GraphTitle";
import "react-sigma-v2/lib/react-sigma-v2.css";
import { GrClose } from "react-icons/gr";
import { BiRadioCircleMarked, BiBookContent } from "react-icons/bi";
import { BsArrowsFullscreen, BsFullscreenExit, BsZoomIn, BsZoomOut } from "react-icons/bs";
const Root: FC = () => {
const [showContents, setShowContents] = useState(false);
const [dataReady, setDataReady] = useState(false);
const [dataset, setDataset] = useState<Dataset | null>(null);
const [filtersState, setFiltersState] = useState<FiltersState>({
clusters: {},
});
const [hoveredNode, setHoveredNode] = useState<string | null>(null);
// Load data on mount:
useEffect(() => {
fetch(`${process.env.PUBLIC_URL}/dataset_entities.json`)
.then((res) => res.json())
.then((dataset: Dataset) => {
setDataset(dataset);
setFiltersState({
clusters: mapValues(keyBy(dataset.clusters, "key"), constant(true)),
});
requestAnimationFrame(() => setDataReady(true));
});
}, []);
if (!dataset) return null;
return (
<div id="app-root" className={showContents ? "show-contents" : ""}>
<SigmaContainer
graphOptions={{ type: "undirected" }}
initialSettings={{
nodeProgramClasses: { image: getNodeProgramImage() },
labelRenderer: drawLabel,
defaultNodeType: "image",
labelDensity: 0.07,
labelGridCellSize: 60,
labelRenderedSizeThreshold: dataset.labelThreshold,
labelFont: "Lato, sans-serif",
zIndex: true,
}}
className="react-sigma"
>
<GraphSettingsController hoveredNode={hoveredNode} />
<GraphEventsController setHoveredNode={setHoveredNode} />
<GraphDataController dataset={dataset} filters={filtersState} />
{dataReady && (
<>
<div className="controls">
<div className="ico">
<button
type="button"
className="show-contents"
onClick={() => setShowContents(true)}
title="Show caption and description"
>
<BiBookContent />
</button>
</div>
<FullScreenControl
className="ico"
customEnterFullScreen={<BsArrowsFullscreen />}
customExitFullScreen={<BsFullscreenExit />}
/>
<ZoomControl
className="ico"
customZoomIn={<BsZoomIn />}
customZoomOut={<BsZoomOut />}
customZoomCenter={<BiRadioCircleMarked />}
/>
</div>
<div className="contents">
<div className="ico">
<button
type="button"
className="ico hide-contents"
onClick={() => setShowContents(false)}
title="Show caption and description"
>
<GrClose />
</button>
</div>
<GraphTitle filters={filtersState} />
<div className="panels">
<SearchField filters={filtersState} />
<DescriptionPanel />
<ClustersPanel
clusters={dataset.clusters}
filters={filtersState}
setClusters={(clusters) =>
setFiltersState((filters) => ({
...filters,
clusters,
}))
}
toggleCluster={(cluster) => {
setFiltersState((filters) => ({
...filters,
clusters: filters.clusters[cluster]
? omit(filters.clusters, cluster)
: { ...filters.clusters, [cluster]: true },
}));
}}
/>
</div>
</div>
</>
)}
</SigmaContainer>
</div>
);
};
export default Root;