From 8ded9cd30e2d605c31f24270d38b1053dbda64dc Mon Sep 17 00:00:00 2001 From: David Sljukic Date: Tue, 3 Sep 2019 22:55:53 +0200 Subject: [PATCH] Make custom state demo where linking is done by clicking ports --- .../CreateLinkState.ts | 87 +++++++++++++++++++ .../demo-alternative-linking/DefaultState.ts | 64 ++++++++++++++ .../demos/demo-alternative-linking/index.tsx | 31 +++++++ packages/diagrams-demo-gallery/index.tsx | 4 +- .../src/entities/link/LinkModel.ts | 4 +- 5 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 packages/diagrams-demo-gallery/demos/demo-alternative-linking/CreateLinkState.ts create mode 100644 packages/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts create mode 100644 packages/diagrams-demo-gallery/demos/demo-alternative-linking/index.tsx diff --git a/packages/diagrams-demo-gallery/demos/demo-alternative-linking/CreateLinkState.ts b/packages/diagrams-demo-gallery/demos/demo-alternative-linking/CreateLinkState.ts new file mode 100644 index 0000000..00b5568 --- /dev/null +++ b/packages/diagrams-demo-gallery/demos/demo-alternative-linking/CreateLinkState.ts @@ -0,0 +1,87 @@ +import { + Action, + ActionEvent, + InputType, + State +} from '@projectstorm/react-canvas-core'; +import { PortModel, LinkModel, DiagramEngine } from '@projectstorm/react-diagrams-core'; +import { MouseEvent, KeyboardEvent } from 'react'; + +/** + * This state is controlling the creation of a link. + */ +export class CreateLinkState extends State { + sourcePort: PortModel; + link: LinkModel; + + constructor() { + super({ name: 'create-new-link' }); + + this.registerAction( + new Action({ + type: InputType.MOUSE_UP, + fire: (actionEvent: ActionEvent) => { + const element = this.engine.getActionEventBus().getModelForEvent(actionEvent); + const { event: { clientX, clientY } } = actionEvent; + const ox = this.engine.getModel().getOffsetX(); + const oy = this.engine.getModel().getOffsetY(); + + if (element instanceof PortModel && !this.sourcePort) { + this.sourcePort = element; + + /* would be cool if link creating could be done somewhat like + const link = createLink({ + sourcePort: this.sourcePort, + points: [{ x: clientX, y: clientY }, { x: clientX, y: clientY }] + }) + */ + const link = this.sourcePort.createLinkModel(); + link.setSourcePort(this.sourcePort); + link.getFirstPoint().setPosition(clientX - ox, clientY - oy); + link.getLastPoint().setPosition(clientX - ox + 20, clientY - oy + 20); + + this.link = this.engine.getModel().addLink(link); + } else if (element instanceof PortModel && this.sourcePort && element != this.sourcePort) { + if (this.sourcePort.canLinkToPort(element)) { + this.link.setTargetPort(element); + element.reportPosition(); + this.eject(); + } + } else if (element === this.link.getLastPoint()) { + this.link.point(clientX - ox, clientY - oy, -1); + } + + this.engine.repaintCanvas(); + } + }) + ); + + this.registerAction( + new Action({ + type: InputType.MOUSE_MOVE, + fire: (actionEvent: ActionEvent) => { + if (!this.link) return; + const { event } = actionEvent; + this.link.getLastPoint().setPosition(event.clientX, event.clientY); + this.engine.repaintCanvas(); + } + }) + ); + + this.registerAction( + new Action({ + type: InputType.KEY_UP, + fire: (actionEvent: ActionEvent) => { + // on esc press remove any started link and pop back to default state + if (actionEvent.event.keyCode === 27) { + this.link.remove(); + this.link = undefined; + this.sourcePort = undefined; + this.eject(); + this.engine.repaintCanvas(); + } + } + }) + ); + } +} diff --git a/packages/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts b/packages/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts new file mode 100644 index 0000000..84c88be --- /dev/null +++ b/packages/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts @@ -0,0 +1,64 @@ +import { MouseEvent } from 'react'; +import { + SelectingState, + State, + Action, + InputType, + ActionEvent, + DragCanvasState +} from '@projectstorm/react-canvas-core'; +import { + PortModel, + DiagramEngine, + DragDiagramItemsState +} from '@projectstorm/react-diagrams-core'; +import { CreateLinkState } from './CreateLinkState'; + +export class DefaultState extends State { + dragCanvas: DragCanvasState; + createLink: CreateLinkState; + dragItems: DragDiagramItemsState; + + constructor() { + super({ name: 'starting-state' }); + this.childStates = [new SelectingState()]; + this.dragCanvas = new DragCanvasState(); + this.createLink = new CreateLinkState(); + this.dragItems = new DragDiagramItemsState(); + + // determine what was clicked on + this.registerAction( + new Action({ + type: InputType.MOUSE_DOWN, + fire: (event: ActionEvent) => { + const element = this.engine.getActionEventBus().getModelForEvent(event); + + // the canvas was clicked on, transition to the dragging canvas state + if (!element) { + this.transitionWithEvent(this.dragCanvas, event); + } + // initiate dragging a new link + else if (element instanceof PortModel) { + return; + } + // move the items (and potentially link points) + else { + this.transitionWithEvent(this.dragItems, event); + } + } + }) + ); + + this.registerAction( + new Action({ + type: InputType.MOUSE_UP, + fire: (event: ActionEvent) => { + const element = this.engine.getActionEventBus().getModelForEvent(event); + + if (element instanceof PortModel) + this.transitionWithEvent(this.createLink, event); + } + }) + ); + } +} diff --git a/packages/diagrams-demo-gallery/demos/demo-alternative-linking/index.tsx b/packages/diagrams-demo-gallery/demos/demo-alternative-linking/index.tsx new file mode 100644 index 0000000..79487af --- /dev/null +++ b/packages/diagrams-demo-gallery/demos/demo-alternative-linking/index.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import createEngine, { DiagramModel, DefaultNodeModel } from '@projectstorm/react-diagrams'; +import { CanvasWidget } from '@projectstorm/react-canvas-core'; +import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget'; +import { DefaultState } from './DefaultState'; + +export default () => { + const engine = createEngine(); + const model = new DiagramModel(); + + const node1 = new DefaultNodeModel('Node 1', 'rgb(0,192,255)'); + node1.addOutPort('Out'); + node1.setPosition(100, 100); + + const node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)'); + node2.addInPort('In'); + node2.setPosition(400, 100); + + model.addAll(node1, node2); + + engine.setModel(model); + + // Use this custom "DefaultState" instead of the actual default state we get with the engine + engine.getStateMachine().pushState(new DefaultState()); + + return ( + + + + ); +}; diff --git a/packages/diagrams-demo-gallery/index.tsx b/packages/diagrams-demo-gallery/index.tsx index d2a2dbe..c74db6c 100644 --- a/packages/diagrams-demo-gallery/index.tsx +++ b/packages/diagrams-demo-gallery/index.tsx @@ -33,6 +33,7 @@ import demo_listeners from './demos/demo-listeners'; import demo_zoom from './demos/demo-zoom-to-fit'; import demo_labels from './demos/demo-labelled-links'; import demo_dynamic_ports from './demos/demo-dynamic-ports'; +import demo_alternative_linking from './demos/demo-alternative-linking'; storiesOf('Simple Usage', module) .add('Simple example', demo_simple) @@ -58,7 +59,8 @@ storiesOf('Advanced Techniques', module) .add('Programatically modifying graph', demo_adv_prog) .add('Drag and drop', demo_adv_dnd) .add('Smart routing', demo_smart_routing) - .add('Right angles routing', demo_right_angles_routing); + .add('Right angles routing', demo_right_angles_routing) + .add('Create link by clicking instead of dragging', demo_alternative_linking); import demo_cust_nodes from './demos/demo-custom-node1'; import demo_cust_links from './demos/demo-custom-link1'; diff --git a/packages/react-diagrams-core/src/entities/link/LinkModel.ts b/packages/react-diagrams-core/src/entities/link/LinkModel.ts index 39ff8ed..150d9e9 100644 --- a/packages/react-diagrams-core/src/entities/link/LinkModel.ts +++ b/packages/react-diagrams-core/src/entities/link/LinkModel.ts @@ -244,8 +244,8 @@ export class LinkModel extends this.fireEvent({ port }, 'targetPortChanged'); } - point(x: number, y: number): PointModel { - return this.addPoint(this.generatePoint(x, y)); + point(x: number, y: number, index: number = 1): PointModel { + return this.addPoint(this.generatePoint(x, y), index); } addLabel(label: LabelModel) {