-
Matthias Konitzny authoredMatthias Konitzny authored
display.tsx 5.05 KiB
import React from "react";
import screenfull from "screenfull";
import PropTypes, { InferType } from "prop-types";
import "./display.css";
import { GraphNode, GraphRenderer } from "./renderer";
import * as Helpers from "./helpers";
import Graph, { NodeData } from "./graph";
import { loadGraphJson } from "../datasets";
import NodeInfoBar from "./components/nodeinfo/nodeinfobar";
import FilterMenu from "./components/nodefilter/filtermenu";
/**
* This component manages and renders a 3d-force-graph with additional menus to navigate, filter and view information on nodes.
*/
class Display extends React.Component<
InferType<typeof Display.propTypes>,
InferType<typeof Display.stateTypes>
> {
props: InferType<typeof Display.propTypes>;
state: InferType<typeof Display.stateTypes>;
fullscreenRef: React.RefObject<HTMLDivElement>;
rendererRef: React.RefObject<GraphRenderer>;
graph: Graph;
static propTypes = {
spaceId: PropTypes.string.isRequired,
};
static stateTypes = {
graph: PropTypes.instanceOf(Graph),
currentNode: PropTypes.object,
nodeActive: PropTypes.bool,
width: PropTypes.number,
height: PropTypes.number,
};
constructor(props: InferType<typeof Display.propTypes>) {
super(props);
this.fullscreenRef = React.createRef();
this.rendererRef = React.createRef();
this.state = {
graph: null,
currentNode: null,
nodeActive: false,
width: 0,
height: 0,
};
this.handleNodeClicked = this.handleNodeClicked.bind(this);
this.handleNodeClose = this.handleNodeClose.bind(this);
this.handleNodeChangeRequest = this.handleNodeChangeRequest.bind(this);
this.handleNodeFilter = this.handleNodeFilter.bind(this);
}
componentDidMount() {
this.setState({
width: Helpers.getWidth(),
height: Helpers.getHeight(),
});
const fetchGraph = async () => {
const graphData = await loadGraphJson(this.props.spaceId);
this.graph = new Graph(graphData.nodes, graphData.links);
this.setState({ graph: this.graph });
};
fetchGraph();
}
handleNodeClicked(node: NodeData) {
this.setState({ currentNode: node, nodeActive: true });
}
handleNodeChangeRequest(node: NodeData) {
this.rendererRef.current.focusOnNode(node as GraphNode);
this.handleNodeClicked(node);
}
handleNodeClose() {
this.setState({ nodeActive: false });
}
handleNodeFilter(visibility: Map<string, boolean>) {
const graph = this.graph.view(visibility);
this.setState({ graph: graph });
}
toggleFullscreen() {
if (screenfull.isEnabled) {
if (!screenfull.isFullscreen) {
this.setState({
width: screen.width,
height: screen.height,
});
} else {
this.setState({
width: Helpers.getWidth(),
height: Helpers.getHeight(),
});
}
screenfull.toggle(this.fullscreenRef.current);
} else {
console.log("No fullscreen mode available :(");
}
}
render() {
return (
<div
id="kg-display"
style={{ position: "relative" }}
ref={this.fullscreenRef}
>
<div
className={"display-fullscreen-button no-select"}
title={"Vollbild"}
onClick={this.toggleFullscreen.bind(this)}
>
<p>⤢</p>
</div>
{/*{this.graph && (*/}
{/* <FilterOverlay graph={this.graph} type={"node"} />*/}
{/*)}*/}
{this.state.currentNode && (
<NodeInfoBar
node={this.state.currentNode}
nodeColors={this.state.graph.nodeColors}
height={this.state.nodeActive ? this.state.height : 0}
onClose={this.handleNodeClose}
nodeClickedCallback={this.handleNodeChangeRequest}
></NodeInfoBar>
)}
{this.state.graph && (
<FilterMenu
classes={this.graph.nodeColors}
onVisibilityChange={this.handleNodeFilter}
/>
)}
<div id="3d-graph">
{this.state.graph && (
<GraphRenderer
ref={this.rendererRef}
graph={this.state.graph}
width={this.state.width}
height={this.state.height}
onNodeClicked={this.handleNodeClicked}
/>
)}
</div>
</div>
);
}
}
export default Display;