mirror of
https://github.com/bellingcat/gesara-entity-viz.git
synced 2026-06-08 03:28:33 +03:00
134 lines
4.7 KiB
TypeScript
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;
|