123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- import * as React from 'react';
- import {d3Types, Graph, GraphProps, Forces} from "./graph";
- import {
- NodeCreation,
- NodeDeletion,
- EdgeCreation,
- EdgeUpdate,
- PrimitiveDelta,
- } from "../onion/primitive_delta";
- import {NodeState, ValueState, GraphDeltaExecutor} from "../onion/delta_executor";
- import {
- CompositeLevel,
- } from "../onion/composite_delta";
- import {PrimitiveValue, UUID} from "../onion/types";
- import {
- Version,
- initialVersion,
- VersionRegistry,
- } from "../onion/version";
- export type NodeType = d3Types.d3Node<NodeState|ValueState>;
- export type LinkType = d3Types.d3Link<null>;
- export type GraphType = d3Types.d3Graph<NodeState|ValueState,null>;
- interface EditableGraphProps {
- graph: GraphType;
- graphDeltaExecutor: GraphDeltaExecutor;
- forces: Forces;
- version: Version;
- generateUUID: () => UUID;
- newVersionHandler: (Version) => void;
- newDeltaHandler: (CompositeDelta) => void;
- setNextNodePosition: (x:number, y:number) => void;
- }
- interface EditableGraphState {
- }
- export class EditableGraph extends React.Component<EditableGraphProps, EditableGraphState> {
- mouseDownNode: NodeType | null = null; // only while mouse button is down, does this record the d3Node that was under the cursor when the mouse went down.
- readonly compositeLvl: CompositeLevel = new CompositeLevel();
- readonly versionRegistry: VersionRegistry = new VersionRegistry();
- constructor(props) {
- super(props);
- this.state = {showCrossHair: false};
- }
- mouseDownHandler = (event, {x,y}, mouseDownNode: NodeType | undefined) => {
- event.stopPropagation();
- if (mouseDownNode) {
- this.mouseDownNode = mouseDownNode;
- }
- }
- mouseUpHandler = (event, {x,y}, mouseUpNode: NodeType | undefined) => {
- event.stopPropagation();
- // Construct the delta(s) that capture the user's change:
- const deltas: PrimitiveDelta[] = (() => {
- if (event.button === 2) { // right mouse button
- if (this.mouseDownNode !== null && this.mouseDownNode.obj instanceof NodeState) {
- // create outgoing edge...
- if (mouseUpNode !== undefined) {
- // right mouse button was dragged from one node to another -> create/update edge from one node to the other
- const label = prompt("Edge label (ESC to cancel):", "label");
- if (label !== null) {
- return this.mouseDownNode.obj.getDeltasForSetEdge(label, mouseUpNode.obj);
- }
- }
- else {
- // right mouse button was dragged from a node to empty space -> create/update edge from node to a value
- const label = prompt("Edge label (ESC to cancel):", "label");
- if (label !== null) {
- let enteredValue: string|null = "\"42\"";
- while (true) {
- enteredValue = prompt("Target value (enter a JSON-parsable(!) string (with quotes), number or boolean):\n(ESC to cancel)", enteredValue);
- if (enteredValue === null) {
- break;
- }
- let parsedValue;
- try {
- parsedValue = JSON.parse(enteredValue);
- } catch (err) {
- alert("Invalid JSON!");
- continue;
- }
- const typeofParsedValue = typeof parsedValue;
- if (typeofParsedValue !== "string" && typeofParsedValue !== "number" && typeofParsedValue !== "boolean") {
- alert("Expected string, number or boolean. Got: " + typeofParsedValue);
- continue;
- }
- return this.mouseDownNode.obj.getDeltasForSetEdge(label, this.props.graphDeltaExecutor.getValueState(parsedValue));
- }
- }
- }
- }
- else {
- // right mouse button clicked -> create node
- const uuid = this.props.generateUUID();
- this.props.setNextNodePosition(x,y);
- return [new NodeCreation(uuid)];
- }
- }
- else if (event.button === 1) { // middle mouse button
- if (mouseUpNode !== undefined) {
- // middle mouse button click on node -> delete node (and incoming/outgoing edges)
- return mouseUpNode.obj.getDeltasForDelete();
- }
- }
- return [];
- })();
- if (deltas.length > 0) {
- // Let the world know that there is a new (composite) delta, and a new version:
- const composite = this.compositeLvl.createComposite(deltas);
- const version = this.versionRegistry.createVersion(this.props.version, composite);
- this.props.newDeltaHandler(composite);
- this.props.newVersionHandler(version);
- // Actually update the graph state:
- deltas.forEach(d => d.exec(this.props.graphDeltaExecutor));
- }
- this.mouseDownNode = null;
- }
- render() {
- return (
- <Graph
- graph={this.props.graph}
- forces={this.props.forces}
- mouseDownHandler={this.mouseDownHandler}
- mouseUpHandler={this.mouseUpHandler}
- />
- );
- }
- }
|