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

Reworking select layer - WIP commit

parent 78639b94
No related branches found
No related tags found
No related merge requests found
Pipeline #56967 passed
import React from "react";
import { ReactNode } from "react";
import React, { useState } from "react";
import { Node } from "../../common/graph/node";
import "./selectlayer.css";
import { Coordinate2D } from "../../common/graph/graph";
type propTypes = {
children: any;
allNodes: Node[];
interface SelectLayerProps {
children: React.ReactNode | React.ReactNode[];
nodes: Node[];
isEnabled: boolean;
screen2GraphCoords: (x: number, y: number) => any;
screen2GraphCoords: (x: number, y: number) => Coordinate2D;
onBoxSelect: (nodes: Node[]) => void;
};
type layerCoordinates = {
x: number;
y: number;
};
export class SelectLayer extends React.Component<propTypes> {
private layerContainer: any;
private layerBox: any;
private initialSelectPoint: layerCoordinates = undefined;
constructor(props: propTypes) {
super(props);
this.isSelecting = this.isSelecting.bind(this);
this.onBoxSelect = this.onBoxSelect.bind(this);
this.boxSelectOnPointerDown = this.boxSelectOnPointerDown.bind(this);
this.boxSelectOnPointerMove = this.boxSelectOnPointerMove.bind(this);
this.boxSelectOnPointerUp = this.boxSelectOnPointerUp.bind(this);
this.layerContainer = React.createRef();
this.layerBox = React.createRef();
}
componentDidMount(): void {
this.setupBoxSelect();
}
setupBoxSelect() {
// Source: https://github.com/vasturiano/force-graph/issues/151#issuecomment-735850938
this.layerContainer.current.onpointerdown = this.boxSelectOnPointerDown;
this.layerContainer.current.onpointermove = this.boxSelectOnPointerMove;
this.layerContainer.current.onpointerup = this.boxSelectOnPointerUp;
}
}
private isSelecting(): boolean {
if (!this.initialSelectPoint) {
return false;
}
interface Box {
bottom: number;
top: number;
left: number;
right: number;
}
if (!this.props.isEnabled) {
this.initialSelectPoint = undefined;
this.layerBox.current.className = "";
return false;
}
function SelectLayer({
children,
nodes,
onBoxSelect,
isEnabled,
screen2GraphCoords,
}: SelectLayerProps) {
const [selectionStart, setSelectionStart] =
useState<Coordinate2D>(undefined);
const [selectionEnd, setSelectionEnd] = useState<Coordinate2D>(undefined);
return true;
if (!isEnabled) {
return <>{children}</>;
}
onBoxSelect(left: number, bottom: number, top: number, right: number) {
const makeSelection = (bb: Box) => {
// Filter out selected nodes
const hitNodes: Node[] = [];
const tl = this.props.screen2GraphCoords(left, top);
const br = this.props.screen2GraphCoords(right, bottom);
this.props.allNodes.forEach((node: any) => {
const tl = screen2GraphCoords(bb.left, bb.top);
const br = screen2GraphCoords(bb.right, bb.bottom);
nodes.forEach((node: Node) => {
if (
tl.x < node.x &&
node.x < br.x &&
......@@ -75,87 +49,216 @@ export class SelectLayer extends React.Component<propTypes> {
hitNodes.push(node);
}
});
onBoxSelect(hitNodes);
};
this.props.onBoxSelect(hitNodes);
}
boxSelectOnPointerDown(e: any) {
if (!this.props.isEnabled) {
return;
}
e.preventDefault();
this.layerBox.current.style.left = e.offsetX.toString() + "px";
this.layerBox.current.style.top = e.offsetY.toString() + "px";
this.layerBox.current.style.width = "0px";
this.layerBox.current.style.height = "0px";
this.initialSelectPoint = {
x: e.offsetX,
y: e.offsetY,
const getBoundingBox = (p1: Coordinate2D, p2: Coordinate2D): Box => {
return {
left: Math.min(p1.x, p2.x),
top: Math.min(p1.y, p2.y),
right: Math.max(p1.x, p2.x),
bottom: Math.max(p1.x, p2.x),
};
this.layerBox.current.className = "visible";
}
};
boxSelectOnPointerMove(e: any) {
if (!this.isSelecting()) {
return;
}
const handlePointerUp = (e: React.PointerEvent<HTMLDivElement>) => {
e.preventDefault();
makeSelection(getBoundingBox(selectionStart, selectionEnd));
setSelectionStart(undefined);
setSelectionEnd(undefined);
};
const handlePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
e.preventDefault();
if (e.offsetX < this.initialSelectPoint.x) {
this.layerBox.current.style.left = e.offsetX.toString() + "px";
this.layerBox.current.style.width =
(this.initialSelectPoint.x - e.offsetX).toString() + "px";
} else {
this.layerBox.current.style.left =
this.initialSelectPoint.x.toString() + "px";
this.layerBox.current.style.width =
(e.offsetX - this.initialSelectPoint.x).toString() + "px";
}
if (e.offsetY < this.initialSelectPoint.y) {
this.layerBox.current.style.top = e.offsetY.toString() + "px";
this.layerBox.current.style.height =
(this.initialSelectPoint.y - e.offsetY).toString() + "px";
} else {
this.layerBox.current.style.top =
this.initialSelectPoint.y.toString() + "px";
this.layerBox.current.style.height =
(e.offsetY - this.initialSelectPoint.y).toString() + "px";
}
}
setSelectionStart({ x: e.screenX, y: e.screenY });
setSelectionEnd({ x: e.screenX, y: e.screenY });
};
boxSelectOnPointerUp(e: any) {
if (!this.isSelecting()) {
return;
}
const handlePointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
setSelectionEnd({ x: e.screenX, y: e.screenY });
};
e.preventDefault();
let left, bottom, top, right;
if (e.offsetX < this.initialSelectPoint.x) {
left = e.offsetX;
right = this.initialSelectPoint.x;
} else {
left = this.initialSelectPoint.x;
right = e.offsetX;
}
if (e.offsetY < this.initialSelectPoint.y) {
top = e.offsetY;
bottom = this.initialSelectPoint.y;
} else {
top = this.initialSelectPoint.y;
bottom = e.offsetY;
}
this.initialSelectPoint = undefined;
this.layerBox.current.className = "";
this.onBoxSelect(left, bottom, top, right);
}
let width = 0;
let height = 0;
let left = 0;
let top = 0;
render(): ReactNode {
return (
<div ref={this.layerContainer} id="select-layer">
<div ref={this.layerBox} id="box-select"></div>
{this.props.children}
</div>
);
if (selectionStart && selectionEnd) {
const box = getBoundingBox(selectionStart, selectionEnd);
width = box.right - box.left;
height = box.bottom - box.top;
left = box.left;
top = box.top;
}
return (
<div
onPointerDown={handlePointerDown}
onPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
id="select-layer"
>
<div
id="box-select"
className={selectionStart ? "visible" : ""}
style={{ width: width, height: height, left: left, top: top }}
></div>
{children}
</div>
);
}
export default SelectLayer;
// export class SelectLayer extends React.Component<propTypes> {
// private layerContainer: React.RefObject<HTMLDivElement>;
// private layerBox: React.RefObject<HTMLDivElement>;
// private initialSelectPoint: layerCoordinates = undefined;
//
// constructor(props: propTypes) {
// super(props);
//
// this.isSelecting = this.isSelecting.bind(this);
// this.onBoxSelect = this.onBoxSelect.bind(this);
// this.boxSelectOnPointerDown = this.boxSelectOnPointerDown.bind(this);
// this.boxSelectOnPointerMove = this.boxSelectOnPointerMove.bind(this);
// this.boxSelectOnPointerUp = this.boxSelectOnPointerUp.bind(this);
//
// this.layerContainer = React.createRef();
// this.layerBox = React.createRef();
// }
//
// componentDidMount(): void {
// this.setupBoxSelect();
// }
//
// setupBoxSelect() {
// // Source: https://github.com/vasturiano/force-graph/issues/151#issuecomment-735850938
// this.layerContainer.current.addEventListener(
// "pointerdown",
// this.boxSelectOnPointerDown
// );
// this.layerContainer.current.addEventListener(
// "pointermove",
// this.boxSelectOnPointerMove
// );
// this.layerContainer.current.addEventListener(
// "pointerup",
// this.boxSelectOnPointerUp
// );
// }
//
// private isSelecting(): boolean {
// if (!this.initialSelectPoint) {
// return false;
// }
//
// if (!this.props.isEnabled) {
// this.initialSelectPoint = undefined;
// this.layerBox.current.className = "";
// return false;
// }
//
// return true;
// }
//
// onBoxSelect(left: number, bottom: number, top: number, right: number) {
// // Filter out selected nodes
// const hitNodes: Node[] = [];
// const tl = this.props.screen2GraphCoords(left, top);
// const br = this.props.screen2GraphCoords(right, bottom);
// this.props.allNodes.forEach((node: Node) => {
// if (
// tl.x < node.x &&
// node.x < br.x &&
// br.y > node.y &&
// node.y > tl.y
// ) {
// // Add node if in box area
// hitNodes.push(node);
// }
// });
//
// this.props.onBoxSelect(hitNodes);
// }
//
// boxSelectOnPointerDown(e: PointerEvent) {
// if (!this.props.isEnabled) {
// return;
// }
//
// e.preventDefault();
// this.layerBox.current.style.left = e.offsetX.toString() + "px";
// this.layerBox.current.style.top = e.offsetY.toString() + "px";
// this.layerBox.current.style.width = "0px";
// this.layerBox.current.style.height = "0px";
// this.initialSelectPoint = {
// x: e.offsetX,
// y: e.offsetY,
// };
// this.layerBox.current.className = "visible";
// }
//
// boxSelectOnPointerMove(e: PointerEvent) {
// if (!this.isSelecting()) {
// return;
// }
//
// e.preventDefault();
// if (e.offsetX < this.initialSelectPoint.x) {
// this.layerBox.current.style.left = e.offsetX.toString() + "px";
// this.layerBox.current.style.width =
// (this.initialSelectPoint.x - e.offsetX).toString() + "px";
// } else {
// this.layerBox.current.style.left =
// this.initialSelectPoint.x.toString() + "px";
// this.layerBox.current.style.width =
// (e.offsetX - this.initialSelectPoint.x).toString() + "px";
// }
// if (e.offsetY < this.initialSelectPoint.y) {
// this.layerBox.current.style.top = e.offsetY.toString() + "px";
// this.layerBox.current.style.height =
// (this.initialSelectPoint.y - e.offsetY).toString() + "px";
// } else {
// this.layerBox.current.style.top =
// this.initialSelectPoint.y.toString() + "px";
// this.layerBox.current.style.height =
// (e.offsetY - this.initialSelectPoint.y).toString() + "px";
// }
// }
//
// boxSelectOnPointerUp(e: PointerEvent) {
// if (!this.isSelecting()) {
// return;
// }
//
// e.preventDefault();
// let left, bottom, top, right;
// if (e.offsetX < this.initialSelectPoint.x) {
// left = e.offsetX;
// right = this.initialSelectPoint.x;
// } else {
// left = this.initialSelectPoint.x;
// right = e.offsetX;
// }
// if (e.offsetY < this.initialSelectPoint.y) {
// top = e.offsetY;
// bottom = this.initialSelectPoint.y;
// } else {
// top = this.initialSelectPoint.y;
// bottom = e.offsetY;
// }
// this.initialSelectPoint = undefined;
// this.layerBox.current.className = "";
// this.onBoxSelect(left, bottom, top, right);
// }
//
// render(): ReactNode {
// return (
// <div ref={this.layerContainer} id="select-layer">
// <div ref={this.layerBox} id="box-select"></div>
// {this.props.children}
// </div>
// );
// }
// }
......@@ -7,7 +7,7 @@ import * as Helpers from "../common/helpers";
import { Node, NodeProperties } from "../common/graph/node";
import { SpaceManager } from "./components/spacemanager";
import { SelectLayer } from "./components/selectlayer";
import SelectLayer from "./components/selectlayer";
import { GraphData } from "../common/graph/graph";
import { NodeType } from "../common/graph/nodetype";
import { GraphRenderer2D } from "./renderer";
......@@ -107,13 +107,13 @@ export class Editor extends React.PureComponent<any, stateTypes> {
keyPressed(key: string) {
const keys = this.state.keys;
keys[key] = true;
this.setState({ keys: keys });
this.setState({ keys: { ...keys } });
}
keyReleased(key: string) {
const keys = this.state.keys;
keys[key] = false;
this.setState({ keys: keys });
this.setState({ keys: { ...keys } });
}
/**
......@@ -230,11 +230,7 @@ export class Editor extends React.PureComponent<any, stateTypes> {
ref={this.graphContainer}
>
<SelectLayer
allNodes={
this.state.graph
? this.state.graph.nodes
: []
}
nodes={this.state.graph.nodes}
screen2GraphCoords={
this.rendererRef
? this.rendererRef.current
......
......@@ -108,10 +108,10 @@ export class GraphRenderer2D extends React.PureComponent<
if (this.keys["Control"]) {
// Connect to clicked node as parent while control is pressed
if (this.state.selectedNodes.length == 0) {
if (this.props.selectedNodes.length == 0) {
// Have no node connected, so select
this.props.onNodeSelectionChanged([node]);
} else if (!this.state.selectedNodes.includes(node)) {
} else if (!this.props.selectedNodes.includes(node)) {
// Already have *other* node/s selected, so connect
this.connectSelectionToNode(node);
}
......@@ -121,7 +121,6 @@ export class GraphRenderer2D extends React.PureComponent<
// By default, simply select node
this.props.onNodeSelectionChanged([node]);
}
//this.forceUpdate(); // TODO: Remove?
}
/**
......@@ -216,7 +215,7 @@ export class GraphRenderer2D extends React.PureComponent<
// Add node to selection
selection.push(node);
}
this.props.onNodeSelectionChanged(selection);
this.props.onNodeSelectionChanged([...selection]);
}
private handleEngineStop() {
......@@ -231,19 +230,16 @@ export class GraphRenderer2D extends React.PureComponent<
private handleNodeDrag(node: Node) {
this.graphInFocus = true;
if (
!this.state.selectedNodes ||
!this.state.selectedNodes.includes(node)
) {
this.props.onNodeSelectionChanged([node]);
}
// if (!this.props.selectedNodes.includes(node)) {
// this.props.onNodeSelectionChanged([...this.props.selectedNodes, node]);
// }
// Should run connect logic?
if (!this.state.connectOnDrag) {
if (!this.props.connectOnDrag) {
return;
}
const closest = this.state.graph.getClosestNode(node.x, node.y, node);
const closest = this.props.graph.getClosestNode(node.x, node.y, node);
// Is close enough for new link?
if (closest.distance > this.maxDistanceToConnect) {
......@@ -256,7 +252,7 @@ export class GraphRenderer2D extends React.PureComponent<
}
// Add link
node.connect(closest.node);
node.connect(closest.node); // TODO: Change must propagate
// this.forceUpdate(); TODO: Remove?
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment