|
@@ -22,6 +22,7 @@ const edges = [
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
const zoomConstant = 0.01;
|
|
const zoomConstant = 0.01;
|
|
|
|
|
+const panConstant = 0.8;
|
|
|
|
|
|
|
|
type NodeType = {
|
|
type NodeType = {
|
|
|
id: number,
|
|
id: number,
|
|
@@ -47,48 +48,52 @@ type PropsType = {
|
|
|
type StateType = {
|
|
type StateType = {
|
|
|
nodes: NodeType[],
|
|
nodes: NodeType[],
|
|
|
edges: EdgeType[],
|
|
edges: EdgeType[],
|
|
|
|
|
+ activeIds: number[],
|
|
|
originX: number | null,
|
|
originX: number | null,
|
|
|
originY: number | null,
|
|
originY: number | null,
|
|
|
- activeIds: number[],
|
|
|
|
|
cursorX: number | null,
|
|
cursorX: number | null,
|
|
|
cursorY: number | null,
|
|
cursorY: number | null,
|
|
|
deltaX: number | null,
|
|
deltaX: number | null,
|
|
|
deltaY: number | null,
|
|
deltaY: number | null,
|
|
|
|
|
+ panX: number | null,
|
|
|
|
|
+ panY: number | null,
|
|
|
dragBg: boolean,
|
|
dragBg: boolean,
|
|
|
preventDrag: boolean,
|
|
preventDrag: boolean,
|
|
|
- scale: number
|
|
|
|
|
|
|
+ scale: number,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
export default class GraphDisplay extends Component<PropsType, StateType> {
|
|
export default class GraphDisplay extends Component<PropsType, StateType> {
|
|
|
state = {
|
|
state = {
|
|
|
nodes: [] as NodeType[],
|
|
nodes: [] as NodeType[],
|
|
|
edges: [] as EdgeType[],
|
|
edges: [] as EdgeType[],
|
|
|
|
|
+ activeIds: [] as number[],
|
|
|
originX: null as (number | null),
|
|
originX: null as (number | null),
|
|
|
originY: null as (number | null),
|
|
originY: null as (number | null),
|
|
|
- activeIds: [] as number[],
|
|
|
|
|
cursorX: null as (number | null),
|
|
cursorX: null as (number | null),
|
|
|
cursorY: null as (number | null),
|
|
cursorY: null as (number | null),
|
|
|
deltaX: null as (number | null),
|
|
deltaX: null as (number | null),
|
|
|
deltaY: null as (number | null),
|
|
deltaY: null as (number | null),
|
|
|
|
|
+ panX: null as (number | null),
|
|
|
|
|
+ panY: null as (number | null),
|
|
|
dragBg: false,
|
|
dragBg: false,
|
|
|
preventDrag: false,
|
|
preventDrag: false,
|
|
|
scale: 0.5
|
|
scale: 0.5
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- myRef: any = React.createRef();
|
|
|
|
|
|
|
+ spaceRef: any = React.createRef();
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
componentDidMount() {
|
|
|
let { components } = this.props;
|
|
let { components } = this.props;
|
|
|
- let height = this.myRef.offsetHeight;
|
|
|
|
|
- let width = this.myRef.offsetWidth;
|
|
|
|
|
|
|
+ let height = this.spaceRef.offsetHeight;
|
|
|
|
|
+ let width = this.spaceRef.offsetWidth;
|
|
|
this.setState({
|
|
this.setState({
|
|
|
originX: Math.round(width / 2),
|
|
originX: Math.round(width / 2),
|
|
|
originY: Math.round(height / 2)
|
|
originY: Math.round(height / 2)
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// Suppress trackpad gestures
|
|
// Suppress trackpad gestures
|
|
|
- this.myRef.addEventListener("touchmove", (e: any) => e.preventDefault());
|
|
|
|
|
- this.myRef.addEventListener("mousewheel", (e: any) => e.preventDefault());
|
|
|
|
|
|
|
+ this.spaceRef.addEventListener("touchmove", (e: any) => e.preventDefault());
|
|
|
|
|
+ this.spaceRef.addEventListener("mousewheel", (e: any) => e.preventDefault());
|
|
|
let nodes = components.map( (c: ResourceType) => {
|
|
let nodes = components.map( (c: ResourceType) => {
|
|
|
return {id: c.ID, name: c.Name, x:0, y:0, w:40, h:40}
|
|
return {id: c.ID, name: c.Name, x:0, y:0, w:40, h:40}
|
|
|
})
|
|
})
|
|
@@ -97,8 +102,8 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
componentWillUnmount() {
|
|
|
- this.myRef.removeEventListener("touchmove", (e: any) => e.preventDefault());
|
|
|
|
|
- this.myRef.removeEventListener("mousewheel", (e: any) => e.preventDefault());
|
|
|
|
|
|
|
+ this.spaceRef.removeEventListener("touchmove", (e: any) => e.preventDefault());
|
|
|
|
|
+ this.spaceRef.removeEventListener("mousewheel", (e: any) => e.preventDefault());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Push to activeIds if not already present
|
|
// Push to activeIds if not already present
|
|
@@ -134,11 +139,15 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
onMouseMove = (e: any) => {
|
|
onMouseMove = (e: any) => {
|
|
|
- let { originX, originY, dragBg, preventDrag } = this.state;
|
|
|
|
|
- this.setState({ scale: 1 });
|
|
|
|
|
|
|
+ let { originX, originY, dragBg, preventDrag, scale, panX, panY } = this.state;
|
|
|
|
|
+
|
|
|
|
|
+ // Suppress navigation gestures
|
|
|
|
|
+ if (scale !== 1 || panX !== 0 || panY !== 0) {
|
|
|
|
|
+ this.setState({ scale: 1, panX: 0, panY: 0 });
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Update origin-centered cursor coordinates
|
|
// Update origin-centered cursor coordinates
|
|
|
- let bounds = this.myRef.getBoundingClientRect();
|
|
|
|
|
|
|
+ let bounds = this.spaceRef.getBoundingClientRect();
|
|
|
let cursorX = e.clientX - bounds.left - originX;
|
|
let cursorX = e.clientX - bounds.left - originX;
|
|
|
let cursorY = -(e.clientY - bounds.top - originY);
|
|
let cursorY = -(e.clientY - bounds.top - originY);
|
|
|
this.setState({ cursorX, cursorY });
|
|
this.setState({ cursorX, cursorY });
|
|
@@ -149,16 +158,22 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Set zoom scale
|
|
|
|
|
|
|
+ // Handle pan XOR zoom (two-finger gestures count as onWheel)
|
|
|
handleOnWheel = (e: any) => {
|
|
handleOnWheel = (e: any) => {
|
|
|
- var scale = 1;
|
|
|
|
|
- scale -= e.deltaY * zoomConstant;
|
|
|
|
|
- this.setState({ scale });
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Pinch/zoom sets e.ctrlKey to true
|
|
|
|
|
+ if (e.ctrlKey) {
|
|
|
|
|
+ var scale = 1;
|
|
|
|
|
+ scale -= e.deltaY * zoomConstant;
|
|
|
|
|
+ this.setState({ scale, panX: 0, panY: 0 });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.setState({ panX: e.deltaX, panY: e.deltaY, scale: 1 });
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// Pass origin to node for offset
|
|
// Pass origin to node for offset
|
|
|
renderNodes = () => {
|
|
renderNodes = () => {
|
|
|
- let { activeIds, originX, originY, cursorX, cursorY, scale } = this.state;
|
|
|
|
|
|
|
+ let { activeIds, originX, originY, cursorX, cursorY, scale, panX, panY } = this.state;
|
|
|
|
|
|
|
|
return this.state.nodes.map((node: NodeType, i: number) => {
|
|
return this.state.nodes.map((node: NodeType, i: number) => {
|
|
|
|
|
|
|
@@ -179,6 +194,10 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
|
|
|
node.x = cursorX + scale * (node.x - cursorX);
|
|
node.x = cursorX + scale * (node.x - cursorX);
|
|
|
node.y = cursorY + scale * (node.y - cursorY);
|
|
node.y = cursorY + scale * (node.y - cursorY);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Apply pan
|
|
|
|
|
+ node.x -= panConstant * panX;
|
|
|
|
|
+ node.y += panConstant * panY;
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<Node
|
|
<Node
|
|
@@ -214,7 +233,7 @@ export default class GraphDisplay extends Component<PropsType, StateType> {
|
|
|
console.log('rendering graph display')
|
|
console.log('rendering graph display')
|
|
|
return (
|
|
return (
|
|
|
<StyledGraphDisplay
|
|
<StyledGraphDisplay
|
|
|
- ref={element => this.myRef = element}
|
|
|
|
|
|
|
+ ref={element => this.spaceRef = element}
|
|
|
onMouseMove={this.onMouseMove}
|
|
onMouseMove={this.onMouseMove}
|
|
|
onMouseDown={() => this.setState({ dragBg: true })}
|
|
onMouseDown={() => this.setState({ dragBg: true })}
|
|
|
onMouseUp={() => this.setState({ dragBg: false })}
|
|
onMouseUp={() => this.setState({ dragBg: false })}
|