Skip to content
Snippets Groups Projects
Commit 09f5d90f authored by Matthias Konitzny's avatar Matthias Konitzny :fire:
Browse files

Partially re-implemented filter menu

parent e5d5f2c3
No related branches found
No related tags found
1 merge request!3Master into new editor
.filter-class-label {
display: inline-block;
padding-right: 5px;
color: #fff;
font-size: 13px;
line-height: 20px;
font-family: CuratorRegular, Helvetica Neue, Helvetica, Arial, sans-serif;
text-transform: uppercase;
margin-bottom: 6px;
height: 17px;
z-index: 100;
cursor: pointer;
pointer-events: all;
opacity: 1;
}
.filter-class-label p {
margin: 0
}
.filter-class-label-color-strip {
display: block;
height: 2px;
width: 78px;
}
\ No newline at end of file
import React, { useState } from "react";
import "./classlabel.css";
interface ClassLabelProps {
type: string;
color: string;
width: number;
visible?: boolean;
onClick?: (type: string) => void;
}
function ClassLabel({
type,
color,
width,
onClick,
visible = true,
}: ClassLabelProps) {
const handleClick = () => {
if (onClick) {
onClick(type);
}
};
const opacity = visible ? 1.0 : 0.4;
return (
<div
className={"filter-class-label"}
style={{ opacity }}
onClick={handleClick}
>
<p>{type}</p>
<div
className={"filter-class-label-color-strip"}
style={{ width: width + "px", backgroundColor: color }}
></div>
</div>
);
}
export default ClassLabel;
.filter-menu {
position: absolute;
bottom: 0;
pointer-events: none;
width: 350px;
max-width: 400px;
padding: 10px;
left: 0;
display: block;
background-color: rgba(0, 0, 0, 0.6);
z-index: 1;
}
import React, { useState } from "react";
import "./filtermenu.css";
import ClassLabel from "./classlabel";
interface FilterMenuProps {
classes: Map<string, string>;
onVisibilityChange?: (visibility: Map<string, boolean>) => void;
}
function FilterMenu({ classes, onVisibilityChange }: FilterMenuProps) {
const classList = [...classes.keys()];
const chars = Math.max(
...classList.map(function (c: string) {
return c.length;
})
);
const [visibility, setVisibility] = useState(
new Array(classList.length).fill(true)
);
const handleClick = (idx: number) => {
const vis = [...visibility];
vis[idx] = !vis[idx];
setVisibility(vis);
if (onVisibilityChange !== undefined) {
onVisibilityChange(
new Map<string, boolean>(
classList.map((cls, idx) => [cls, vis[idx]])
)
);
}
};
return (
<div className={"filter-menu"}>
{classList.map((cls: string, idx) => (
<ClassLabel
key={cls}
type={cls}
color={classes.get(cls)}
width={10 * chars}
visible={visibility[idx]}
onClick={() => handleClick(idx)}
/>
))}
</div>
);
}
export default FilterMenu;
......@@ -153,45 +153,6 @@
width: 100%;
}
.link-overlay {
position: absolute;
bottom: 0;
pointer-events: none;
width: 350px;
max-width: 400px;
padding: 10px;
left: 0;
display: block;
background-color: rgba(0, 0, 0, 0.6);
z-index: 1;
}
.relation {
display: inline-block;
padding-right: 5px;
color: #fff;
font-size: 13px;
line-height: 20px;
font-family: CuratorRegular, Helvetica Neue, Helvetica, Arial, sans-serif;
text-transform: uppercase;
margin-bottom: 6px;
height: 17px;
z-index: 100;
cursor: pointer;
pointer-events: all;
opacity: 1;
}
.relation p {
margin: 0
}
.rel-container {
display: block;
height: 2px;
width: 78px;
}
/*New Section */
.neighbor-collapsible-title {
......
......@@ -7,6 +7,7 @@ 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";
class Display extends React.Component<
InferType<typeof Display.propTypes>,
......@@ -17,6 +18,7 @@ class Display extends React.Component<
fullscreenRef: React.RefObject<HTMLDivElement>;
rendererRef: React.RefObject<GraphRenderer>;
graph: Graph;
static propTypes = {
spaceId: PropTypes.string.isRequired,
......@@ -44,6 +46,7 @@ class Display extends React.Component<
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() {
......@@ -54,8 +57,8 @@ class Display extends React.Component<
const fetchGraph = async () => {
const graphData = await loadGraphJson(this.props.spaceId);
const graph = new Graph(graphData.nodes, graphData.links);
this.setState({ graph: graph });
this.graph = new Graph(graphData.nodes, graphData.links);
this.setState({ graph: this.graph });
};
fetchGraph();
}
......@@ -73,6 +76,11 @@ class Display extends React.Component<
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) {
......@@ -121,6 +129,13 @@ class Display extends React.Component<
></NodeInfoBar>
)}
{this.state.graph && (
<FilterMenu
classes={this.state.graph.nodeColors}
onVisibilityChange={this.handleNodeFilter}
/>
)}
<div id="3d-graph">
{this.state.graph && (
<GraphRenderer
......
......@@ -133,12 +133,16 @@ export default class Graph {
this.nodes.forEach((node) => nodeClasses.push(node.type));
return [...new Set(nodeClasses)].map((c) => String(c));
}
public view(
nodeTypes: Map<string, boolean>,
linkTypes: Map<string, boolean>
linkTypes?: Map<string, boolean>
): Graph {
const links = this.links.filter((l) => linkTypes.get(l.type));
let links;
if (linkTypes === undefined) {
links = this.links;
} else {
links = this.links.filter((l) => linkTypes.get(l.type));
}
return new Graph(
this.nodes.filter((l) => nodeTypes.get(l.type)),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment