Compare commits

..

63 Commits

Author SHA1 Message Date
Dylan Vorster
341e540af6 v6.0.1-beta.6 2019-11-23 23:56:32 +02:00
Dylan Vorster
9bd86480e9 Merge pull request #482 from antonioru/hotfix/wrong-link-appereance
Clicking on a source port causes a nonsense link segment, fixing #481
2019-11-23 23:48:21 +02:00
Dylan Vorster
5e0a75d4ed Merge pull request #484 from pierre-moire/master
Adds animation demo
2019-11-23 23:45:19 +02:00
Pierre Moiré
91789729c4 Adds animation demo 2019-11-23 20:08:08 +01:00
Antonio Russo
361fbe4ffe Removing unused imports 2019-11-23 17:49:45 +00:00
Antonio Russo
80e9bdcfc0 Clicking on a source port causes a nonsense link segment, fixing #481 2019-11-23 15:58:06 +00:00
Dylan Vorster
97c66b99ab Merge pull request #471 from mathiasbaldissera/master
Fixing #470
2019-11-21 01:30:52 +02:00
Mathias
220852697f Fixing #470
After the #469 PR, a bug when the zoom level is != from 1 make the link go far the mouse. Now it's considering the zoom level in the calculation
2019-11-20 16:38:24 -03:00
Dylan Vorster
8e05f28931 Merge pull request #465 from neolite/master
Remove error when creating a new canvas without node
2019-11-19 20:34:02 +02:00
Dylan Vorster
a450c9b5b5 Merge pull request #469 from antonioru/hotfix/new-link-far-end
link far end aligns with the mouse pointer position
2019-11-19 20:33:40 +02:00
Dylan Vorster
508fbf2777 Merge pull request #468 from mathiasbaldissera/master
Export DeleteItemsAction and function to deregister the default DeleteItemsAction
2019-11-19 20:30:25 +02:00
Mathias
11dc57a834 Fixing example 2019-11-19 15:04:19 -03:00
Antonio Russo
ed63bf151c keeping the engine offset into account when calculating a new link position 2019-11-19 15:39:13 +00:00
Antonio Russo
6dd0677b28 Using initialRelative coordinates when creating a new link makes it more precise 2019-11-19 14:03:24 +00:00
Antonio Russo
ea6130d1a9 getting the initial click position from the AbstractDisplacementState 2019-11-18 20:23:51 +00:00
Mathias
3c8839a1ec More bug fixes 2019-11-18 14:23:58 -03:00
Mathias
a7ab686463 Some BugFix 2019-11-18 14:07:53 -03:00
Mathias
11c2c70ec6 Options to NOT REGISTER default DeleteItemsAction and ZoomCanvasAction 2019-11-18 14:02:30 -03:00
Antonio Russo
a3ca75d039 link far end alignes with the mouse pointer position, fixes #467 2019-11-18 08:48:05 +00:00
Mathias
dedc627294 Export DeleteItemsAction and event to deregister the default DeleteI...
DeleteItemsAction and ZoomCanvasAction are now exported;
Now there is 2 functions to deregister the default ZoomCanvas and DeleteItems actions;
A exemple with how to deregister and register another DeleteItemsAction
2019-11-16 21:58:12 -03:00
Rafkat
c000ffc3cd Remove error when creating a new canvas without node 2019-11-06 18:36:09 +03:00
Dylan Vorster
37babb3bb2 Merge pull request #448 from christianbuehlmeyer/checkCanLinkToPort
Fix: Also check canLinkToPort in DragDiagramItemsState
2019-10-11 18:25:46 +02:00
Dylan Vorster
33651b22a1 Merge pull request #451 from matthax/master
Fix #450 by exporting SmartLayerWidget from react-canvas-core
2019-10-11 18:24:53 +02:00
Matt Bark
dfc7102b1c export SmartLayerWIdget from react-canvas-core
Fixes #450
2019-10-07 12:39:02 -04:00
Christian Bühlmeyer
a399f135ba Added the canLinkToPort check also to DragDiagramItemsState to prevent Port to Port connection that is not allowed 2019-10-04 21:40:03 +02:00
Dylan Vorster
36e097cb70 Merge pull request #443 from larshp/readme
README: fix links under "how to use"
2019-10-01 01:11:51 +02:00
Dylan Vorster
23e1742e99 Merge pull request #434 from DanieLazarLDAPPS/bugfix/#428-after-deserialize-fixes
bugfix after deserialize link has no source or target port and code i…
2019-10-01 01:11:29 +02:00
Lars Hvam
5d913f28ba README: fix links under "how to use"
firs one was broken, plus second was only to the readme for an old tree
2019-09-27 12:33:20 +02:00
Daniel Lazar
1c955b4d12 bugfix after deserialize link has no source or target port and code in body of condition should not be called. This is quick fix. Better will be handle it in fireMouseMoved of DragNewLinkState 2019-09-19 10:25:56 +02:00
Dylan Vorster
741851b855 v6.0.1-beta.5 2019-09-12 11:59:17 +02:00
Dylan Vorster
4729bbedc9 Merge pull request #429 from DanieLazarLDAPPS/bugfix/#428-after-deserialize-fixes
issue #428 fixing by moving entityRemoved registration to addModel
2019-09-12 11:57:04 +02:00
Daniel Lazar
994ff7c714 formatting fixes 2019-09-12 07:38:52 +02:00
Dylan Vorster
05f1345e62 Merge pull request #426 from vidjuheffex/patch-1
issue #407 update css to remove first-child unsafe warning
2019-09-12 00:36:44 +02:00
Dylan Vorster
5079188b99 Merge pull request #425 from duhruh/bug/unique-key
fix for unique key maps
2019-09-12 00:36:05 +02:00
Daniel Lazar
13dd53f185 after deserialize rightAngleLinkModel the first and last path must be set by points if doesnt links right angles are broken 2019-09-11 13:05:36 +02:00
Daniel Lazar
502faeef5a issue #428 fixing by moving entityRemoved registration to addModel 2019-09-11 12:17:01 +02:00
Julián Herrera
39c4f37d12 update css to remove first-of-type unsafe warning 2019-09-10 22:30:20 -05:00
David Rivera
1dccace7e2 fix for unique key maps 2019-09-10 16:49:13 -07:00
Dylan Vorster
85857afd31 Merge pull request #423 from mx2323/dagreFix2
use top left instead of center with dagre
2019-09-07 19:53:02 +02:00
Michael Xu
c875ce6e22 use top left instead of center with dagre 2019-09-06 17:27:29 +00:00
Dylan Vorster
422b967be7 Merge pull request #410 from ppoffice/patch-1
fix diagrams-demo-gallery generation issue on windows and use resize-observer-polyfill
2019-09-06 17:37:32 +02:00
Dylan Vorster
1a8b3caca4 Merge pull request #419 from sljuka/click-linking
Make custom state demo where linking is done by clicking ports
2019-09-06 17:36:25 +02:00
David Sljukic
116a296bf5 Clear state props before ejecting state 2019-09-05 22:38:31 +02:00
David Sljukic
57f4e2d832 Fix tabs with prettier 2019-09-03 23:30:29 +02:00
David Sljukic
b0089662aa Update demo name 2019-09-03 23:00:29 +02:00
David Sljukic
8ded9cd30e Make custom state demo where linking is done by clicking ports 2019-09-03 22:55:53 +02:00
ppoffice
14a76ffbf5 place resize observer polyfill in peer deps 2019-09-03 11:29:51 -04:00
Dylan Vorster
1a7acafdc8 v6.0.1-beta.4 2019-09-03 09:48:37 +02:00
Dylan Vorster
ad25420166 v6.0.1-beta.3 2019-09-03 09:46:24 +02:00
Dylan Vorster
1fb622366b Merge pull request #416 from DanieLazarLDAPPS/features/labview-style-of-routes
Features/labview style of routes
2019-09-03 09:39:59 +02:00
Daniel Lazar
761ac41901 improvment of handling first and last path direction 2019-09-02 11:05:18 +02:00
Daniel Lazar
c8aa7a67ac some after merge fixes and another fixes for linking 2019-09-02 09:44:43 +02:00
Daniel Lazar
e923245f6d better to keep boolean for last and first path direction private for widget 2019-09-02 09:29:32 +02:00
Daniel Lazar
0cb6c66b1f when node is moving better path from its port cannot change direction to keep 90 angles all the time 2019-09-02 09:29:32 +02:00
Daniel Lazar
f20bb0bd2e improved new links to have better experience 2019-09-02 09:27:57 +02:00
Daniel Lazar
0deaec246d added usefull variable that gives last known index of path 2019-09-02 09:26:31 +02:00
ppoffice
cb79d4e390 add resize-observer-polyfill 2019-08-21 14:57:58 -04:00
Ruipeng Zhang
35a53c8134 fix storybook failed to build issue on windows 2019-08-21 00:44:11 -04:00
Dylan Vorster
326567c290 v6.0.1-beta.2 2019-08-19 07:26:28 +02:00
Dylan Vorster
7fa51d5ea0 Merge branch 'master' of github.com:projectstorm/react-diagrams 2019-08-19 07:25:15 +02:00
Dylan Vorster
e72e7e999c fix relative import 2019-08-19 07:24:10 +02:00
Dylan Vorster
25fe2a8e64 Merge pull request #403 from kuldar/patch-1
Update README.md
2019-08-17 11:39:33 +02:00
Kuldar Kalvik
897237c9ca Update README.md 2019-08-17 11:07:58 +02:00
37 changed files with 540 additions and 110 deletions

View File

@@ -58,7 +58,7 @@ yarn add @projectstorm/react-diagrams-core@next
this is built ontop of the evolving **react-canvas-core** library
```text
yarn add @projectstorm/react-diagrams-core@next
yarn add @projectstorm/react-canvas-core@next
```
which makes use of
@@ -76,11 +76,11 @@ yarn add @projectstorm/react-diagrams-routing@next
## How to use
Take a look at the [diagram demos](https://github.com/projectstorm/react-diagrams/tree/8fbd87df8c074e0263fd86a64b05a56687a631cf/packages/diagrams-demo-gallery/demos/README.md)
Take a look at the [diagram demos](https://github.com/projectstorm/react-diagrams/tree/master/packages/diagrams-demo-gallery/demos)
**or**
Take a look at the [demo project](https://github.com/projectstorm/react-diagrams/tree/8fbd87df8c074e0263fd86a64b05a56687a631cf/packages/diagrams-demo-project/README.md) which contains an example for ES6 as well as Typescript
Take a look at the [demo project](https://github.com/projectstorm/react-diagrams/tree/master/packages/diagrams-demo-project) which contains an example for ES6 as well as Typescript
## Run the demos

View File

@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "6.0.1-beta.1"
"version": "6.0.1-beta.6"
}

View File

@@ -83,6 +83,7 @@
"react-dom": "^16.8.6",
"react-syntax-highlighter": "^11.0.2",
"react-test-renderer": "^16.8.6",
"resize-observer-polyfill": "^1.5.1",
"source-map-loader": "^0.2.4",
"storybook-host": "^5.1.0",
"storybook-readme": "^5.0.5",

View File

@@ -26,7 +26,7 @@ module.exports = async ({ config, mode }) => {
enforce: 'pre',
test: /\.js$/,
loader: 'source-map-loader',
exclude: [/node_modules\//]
exclude: [/node_modules/]
},
{
test: /\.tsx?$/,

View File

@@ -0,0 +1,89 @@
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<DiagramEngine> {
sourcePort: PortModel;
link: LinkModel;
constructor() {
super({ name: 'create-new-link' });
this.registerAction(
new Action({
type: InputType.MOUSE_UP,
fire: (actionEvent: ActionEvent<MouseEvent>) => {
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.clearState();
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<React.MouseEvent>) => {
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<KeyboardEvent>) => {
// on esc press remove any started link and pop back to default state
if (actionEvent.event.keyCode === 27) {
this.link.remove();
this.clearState();
this.eject();
this.engine.repaintCanvas();
}
}
})
);
}
clearState() {
this.link = undefined;
this.sourcePort = undefined;
}
}

View File

@@ -0,0 +1,59 @@
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<DiagramEngine> {
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<MouseEvent>) => {
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<MouseEvent>) => {
const element = this.engine.getActionEventBus().getModelForEvent(event);
if (element instanceof PortModel) this.transitionWithEvent(this.createLink, event);
}
})
);
}
}

View File

@@ -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 (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -0,0 +1,87 @@
import createEngine, { DiagramModel, DefaultNodeModel, NodeModel } from '@projectstorm/react-diagrams';
import * as React from 'react';
import * as _ from 'lodash';
import { DemoButton, DemoWorkspaceWidget } from '../helpers/DemoWorkspaceWidget';
import { CanvasWidget } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
import gsap from 'gsap';
/**
* Tests the grid size
*/
class NodeDelayedPosition extends React.Component<any, any> {
constructor(props) {
super(props);
}
render() {
const { engine } = this.props;
return (
<DemoWorkspaceWidget>
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
</DemoWorkspaceWidget>
);
}
}
export default () => {
//1) setup the diagram engine
var engine = createEngine();
//2) setup the diagram model
var model = new DiagramModel();
//3-A) create a default node
var node1 = new DefaultNodeModel('Node 1', 'rgb(0,192,255)');
var port1 = node1.addOutPort('Out');
node1.setPosition(100, 100);
//3-B) create another default node
var node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
var port2 = node2.addInPort('In');
node2.setPosition(400, 100);
//3-C) create another default node
var node3 = new DefaultNodeModel('Node 3', 'rgb(192,255,0)');
node2.setPosition(200, 300);
//3-D) create another default node
var node4 = new DefaultNodeModel('Node 4', 'rgb(192,255,0)');
node2.setPosition(400, 400);
//3-C) link the 2 nodes together
var link1 = port1.link(port2);
//4) add the models to the root graph
model.addAll(node1, node2, link1, node3, node4);
//5) load model into engine
engine.setModel(model);
var interval = setInterval(() => {
[node1, node2, node3, node4].map(node => {
var obj = { x: 0, y: 0 };
gsap.fromTo(
obj,
{
x: node.getPosition().x,
y: node.getPosition().y
},
{
x: Math.floor(Math.random() * 500),
y: Math.floor(Math.random() * 500),
duration: 0.8,
onUpdate: () => {
node.setPosition(obj.x, obj.y);
engine.repaintCanvas();
}
}
);
});
}, 2000);
//6) render the diagram!
return <NodeDelayedPosition engine={engine} model={model} />;
};

View File

@@ -0,0 +1,35 @@
import * as React from 'react';
import createEngine, { DiagramModel, DefaultNodeModel, DefaultLinkModel } from '@projectstorm/react-diagrams';
import { CanvasWidget, DeleteItemsAction } from '@projectstorm/react-canvas-core';
import { DemoCanvasWidget } from '../helpers/DemoCanvasWidget';
export default () => {
// create an engine without registering DeleteItemsAction
const engine = createEngine({ registerDefaultDeleteItemsAction: false });
const model = new DiagramModel();
const node1 = new DefaultNodeModel({ name: 'Node 1', color: 'rgb(0,192,255)' });
node1.setPosition(100, 100);
const port1 = node1.addOutPort('Out');
const node2 = new DefaultNodeModel('Node 2', 'rgb(192,255,0)');
const port2 = node2.addInPort('In');
node2.setPosition(400, 100);
const link1 = port1.link<DefaultLinkModel>(port2);
link1.getOptions().testName = 'Test';
link1.addLabel('Hello World!');
model.addAll(node1, node2, link1);
engine.setModel(model);
// register an DeleteItemsAction with custom keyCodes (in this case, only Delete key)
engine.getActionEventBus().registerAction(new DeleteItemsAction({ keyCodes: [46] }));
return (
<DemoCanvasWidget>
<CanvasWidget engine={engine} />
</DemoCanvasWidget>
);
};

View File

@@ -41,7 +41,9 @@ class DemoWidget extends React.Component<{ model: DiagramModel; engine: DiagramE
this.engine = new DagreEngine({
graph: {
rankdir: 'RL',
ranker: 'longest-path'
ranker: 'longest-path',
marginx: 25,
marginy: 25
},
includeLinks: true
});

View File

@@ -33,6 +33,8 @@ 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';
import demo_custom_delete_keys from './demos/demo-custom_delete_keys';
storiesOf('Simple Usage', module)
.add('Simple example', demo_simple)
@@ -58,7 +60,9 @@ 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('Linking by clicking instead of dragging', demo_alternative_linking)
.add('Setting custom delete keys', demo_custom_delete_keys);
import demo_cust_nodes from './demos/demo-custom-node1';
import demo_cust_links from './demos/demo-custom-link1';
@@ -68,5 +72,8 @@ storiesOf('Custom Models', module)
.add('Custom animated links', demo_cust_links);
import demo_3rd_dagre from './demos/demo-dagre';
import demo_gsap from './demos/demo-animation';
storiesOf('3rd party libraries', module).add('Auto Distribute (Dagre)', demo_3rd_dagre);
storiesOf('3rd party libraries', module)
.add('Auto Distribute (Dagre)', demo_3rd_dagre)
.add('Animated node moves (GSAP)', demo_gsap);

View File

@@ -1,6 +1,6 @@
{
"name": "@projectstorm/react-diagrams-gallery",
"version": "6.0.1-beta.1",
"version": "6.0.1-beta.6",
"author": "dylanvorster",
"license": "MIT",
"private": true,
@@ -25,5 +25,8 @@
"simple",
"links",
"nodes"
]
],
"dependencies": {
"gsap": "^3.0.1"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@projectstorm/react-diagrams-demo",
"version": "6.0.1-beta.1",
"version": "6.0.1-beta.6",
"author": "dylanvorster",
"license": "MIT",
"private": true,
@@ -25,7 +25,7 @@
"main": "./dist/index.js",
"typings": "./dist/@types/index",
"dependencies": {
"@projectstorm/react-diagrams": "^6.0.1-beta.1"
"@projectstorm/react-diagrams": "^6.0.1-beta.6"
},
"gitHead": "bb878657ba0c2f81764f32901fd96158a0f8352e"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@projectstorm/geometry",
"version": "6.0.1-beta.1",
"version": "6.0.1-beta.6",
"author": "dylanvorster",
"license": "MIT",
"repository": {

View File

@@ -21,6 +21,7 @@ export * from './src/entities/canvas/CanvasWidget';
export * from './src/entities/layer/LayerModel';
export * from './src/entities/layer/TransformLayerWidget';
export * from './src/entities/layer/SmartLayerWidget';
export * from './src/entities/selection/SelectionBoxLayerFactory';
export * from './src/entities/selection/SelectionBoxWidget';
@@ -37,3 +38,6 @@ export * from './src/states/DragCanvasState';
export * from './src/states/SelectingState';
export * from './src/states/SelectionBoxState';
export * from './src/states/MoveItemsState';
export * from './src/actions/DeleteItemsAction';
export * from './src/actions/ZoomCanvasAction';

View File

@@ -1,6 +1,6 @@
{
"name": "@projectstorm/react-canvas-core",
"version": "6.0.1-beta.1",
"version": "6.0.1-beta.6",
"author": "dylanvorster",
"license": "MIT",
"repository": {
@@ -29,7 +29,7 @@
"main": "./dist/index.js",
"typings": "./dist/@types/index",
"dependencies": {
"@projectstorm/geometry": "^6.0.1-beta.1"
"@projectstorm/geometry": "^6.0.1-beta.6"
},
"peerDependencies": {
"closest": "^0.0.1",

View File

@@ -19,6 +19,11 @@ export interface CanvasEngineListener extends BaseListener {
rendered?(): void;
}
export interface CanvasEngineOptions {
registerDefaultDeleteItemsAction?: boolean;
registerDefaultZoomCanvasAction?: boolean;
}
export class CanvasEngine<
L extends CanvasEngineListener = CanvasEngineListener,
M extends CanvasModel = CanvasModel
@@ -28,16 +33,26 @@ export class CanvasEngine<
protected canvas: HTMLDivElement;
protected eventBus: ActionEventBus;
protected stateMachine: StateMachine;
protected options: CanvasEngineOptions;
constructor() {
constructor(options: CanvasEngineOptions = {}) {
super();
this.model = null;
this.eventBus = new ActionEventBus(this);
this.stateMachine = new StateMachine(this);
this.layerFactories = new FactoryBank();
this.registerFactoryBank(this.layerFactories);
this.eventBus.registerAction(new ZoomCanvasAction());
this.eventBus.registerAction(new DeleteItemsAction());
this.options = {
registerDefaultDeleteItemsAction: true,
registerDefaultZoomCanvasAction: true,
...options
};
if (this.options.registerDefaultZoomCanvasAction === true) {
this.eventBus.registerAction(new ZoomCanvasAction());
}
if (this.options.registerDefaultDeleteItemsAction === true) {
this.eventBus.registerAction(new DeleteItemsAction());
}
}
getStateMachine() {

View File

@@ -93,7 +93,7 @@ export class CanvasWidget extends React.Component<DiagramProps> {
}}>
{model.getLayers().map(layer => {
return (
<TransformLayerWidget layer={layer}>
<TransformLayerWidget layer={layer} key={layer.getID()}>
<SmartLayerWidget layer={layer} engine={this.props.engine} key={layer.getID()} />
</TransformLayerWidget>
);

View File

@@ -1,6 +1,6 @@
{
"name": "@projectstorm/react-diagrams-core",
"version": "6.0.1-beta.1",
"version": "6.0.1-beta.6",
"author": "dylanvorster",
"license": "MIT",
"repository": {
@@ -29,13 +29,14 @@
"main": "./dist/index.js",
"typings": "./dist/@types/index",
"dependencies": {
"@projectstorm/geometry": "^6.0.1-beta.1",
"@projectstorm/react-canvas-core": "^6.0.1-beta.1"
"@projectstorm/geometry": "^6.0.1-beta.6",
"@projectstorm/react-canvas-core": "^6.0.1-beta.6"
},
"peerDependencies": {
"closest": "^0.0.1",
"lodash": "4.*",
"react": "16.*"
"react": "16.*",
"resize-observer-polyfill": "^1.5.1"
},
"gitHead": "bb878657ba0c2f81764f32901fd96158a0f8352e"
}

View File

@@ -12,7 +12,7 @@ import {
FactoryBank,
Toolkit
} from '@projectstorm/react-canvas-core';
import { CanvasEngineListener } from '@projectstorm/react-canvas-core';
import { CanvasEngineListener, CanvasEngineOptions } from '@projectstorm/react-canvas-core';
import { DiagramModel } from './models/DiagramModel';
/**
@@ -26,8 +26,8 @@ export class DiagramEngine extends CanvasEngine<CanvasEngineListener, DiagramMod
maxNumberPointsPerLink: number;
constructor() {
super();
constructor(options: CanvasEngineOptions = {}) {
super(options);
this.maxNumberPointsPerLink = 1000;
// create banks for the different factory types

View File

@@ -1,6 +1,7 @@
import { LayerModel, LayerModelGenerics } from '@projectstorm/react-canvas-core';
import { LinkModel } from '../link/LinkModel';
import { DiagramEngine } from '../../DiagramEngine';
import { DiagramModel } from '../../models/DiagramModel';
export interface LinkLayerModelGenerics extends LayerModelGenerics {
CHILDREN: LinkModel;
@@ -20,6 +21,11 @@ export class LinkLayerModel<G extends LinkLayerModelGenerics = LinkLayerModelGen
if (!(model instanceof LinkModel)) {
throw new Error('Can only add links to this layer');
}
model.registerListener({
entityRemoved: () => {
(this.getParent() as DiagramModel).removeLink(model);
}
});
super.addModel(model);
}

View File

@@ -10,9 +10,9 @@ import {
BaseModel,
BaseModelGenerics,
BaseModelListener,
DeserializeEvent
DeserializeEvent,
ModelGeometryInterface
} from '@projectstorm/react-canvas-core';
import { ModelGeometryInterface } from '@projectstorm/react-canvas-core/src/core/ModelGeometryInterface';
export interface LinkModelListener extends BaseModelListener {
sourcePortChanged?(event: BaseEntityEvent<LinkModel> & { port: null | PortModel }): void;
@@ -244,8 +244,8 @@ export class LinkModel<G extends LinkModelGenerics = LinkModelGenerics> 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) {

View File

@@ -1,6 +1,7 @@
import { LayerModel, LayerModelGenerics } from '@projectstorm/react-canvas-core';
import { NodeModel } from '../node/NodeModel';
import { DiagramEngine } from '../../DiagramEngine';
import { DiagramModel } from '../../models/DiagramModel';
export interface NodeLayerModelGenerics extends LayerModelGenerics {
CHILDREN: NodeModel;
@@ -20,6 +21,11 @@ export class NodeLayerModel<G extends NodeLayerModelGenerics = NodeLayerModelGen
if (!(model instanceof NodeModel)) {
throw new Error('Can only add nodes to this layer');
}
model.registerListener({
entityRemoved: () => {
(this.getParent() as DiagramModel).removeNode(model);
}
});
super.addModel(model);
}

View File

@@ -4,6 +4,7 @@ import { DiagramEngine } from '../../DiagramEngine';
import { NodeModel } from './NodeModel';
import { BaseEntityEvent, BaseModel, ListenerHandle, PeformanceWidget } from '@projectstorm/react-canvas-core';
import styled from '@emotion/styled';
import ResizeObserver from 'resize-observer-polyfill';
export interface NodeProps {
node: NodeModel;

View File

@@ -114,11 +114,6 @@ export class DiagramModel<G extends DiagramModelGenerics = DiagramModelGenerics>
}
addLink(link: LinkModel): LinkModel {
link.registerListener({
entityRemoved: () => {
this.removeLink(link);
}
});
this.getActiveLinkLayer().addModel(link);
this.fireEvent(
{
@@ -131,11 +126,6 @@ export class DiagramModel<G extends DiagramModelGenerics = DiagramModelGenerics>
}
addNode(node: NodeModel): NodeModel {
node.registerListener({
entityRemoved: () => {
this.removeNode(node);
}
});
this.getActiveNodeLayer().addModel(node);
this.fireEvent({ node, isCreated: true }, 'nodesUpdated');
return node;

View File

@@ -23,9 +23,11 @@ export class DragDiagramItemsState extends MoveItemsState<DiagramEngine> {
if (link.getLastPoint() !== position.item) {
return;
}
link.setTargetPort(item);
item.reportPosition();
this.engine.repaintCanvas();
if (link.getSourcePort().canLinkToPort(item)) {
link.setTargetPort(item);
item.reportPosition();
this.engine.repaintCanvas();
}
}
});
}

View File

@@ -28,14 +28,14 @@ export class DragNewLinkState extends AbstractDisplacementState<DiagramEngine> {
config: DragNewLinkStateOptions;
constructor(options: DragNewLinkStateOptions = {}) {
super({
name: 'drag-new-link'
});
super({ name: 'drag-new-link' });
this.config = {
allowLooseLinks: true,
allowLinksFromLockedPorts: false,
...options
};
this.registerAction(
new Action({
type: InputType.MOUSE_DOWN,
@@ -59,6 +59,7 @@ export class DragNewLinkState extends AbstractDisplacementState<DiagramEngine> {
}
})
);
this.registerAction(
new Action({
type: InputType.MOUSE_UP,
@@ -75,7 +76,7 @@ export class DragNewLinkState extends AbstractDisplacementState<DiagramEngine> {
}
}
if (!this.config.allowLooseLinks) {
if (this.isNearbySourcePort(event.event) || !this.config.allowLooseLinks) {
this.link.remove();
this.engine.repaintCanvas();
}
@@ -84,9 +85,37 @@ export class DragNewLinkState extends AbstractDisplacementState<DiagramEngine> {
);
}
/**
* Checks whether the mouse event appears to happen in proximity of the link's source port
* @param event
*/
isNearbySourcePort({ clientX, clientY }: MouseEvent): boolean {
const sourcePort = this.link.getSourcePort();
const sourcePortPosition = this.link.getSourcePort().getPosition();
return (
clientX >= sourcePortPosition.x &&
clientX <= sourcePortPosition.x + sourcePort.width &&
(clientY >= sourcePortPosition.y && clientY <= sourcePortPosition.y + sourcePort.height)
);
}
/**
* Calculates the link's far-end point position on mouse move.
* In order to be as precise as possible the mouse initialXRelative & initialYRelative are taken into account as well
* as the possible engine offset
*/
fireMouseMoved(event: AbstractDisplacementStateEvent): any {
const pos = this.port.getPosition();
this.link.getLastPoint().setPosition(pos.x + event.virtualDisplacementX, pos.y + event.virtualDisplacementY);
const portPos = this.port.getPosition();
const zoomLevelPercentage = this.engine.getModel().getZoomLevel() / 100;
const engineOffsetX = this.engine.getModel().getOffsetX() / zoomLevelPercentage;
const engineOffsetY = this.engine.getModel().getOffsetY() / zoomLevelPercentage;
const initialXRelative = this.initialXRelative / zoomLevelPercentage;
const initialYRelative = this.initialYRelative / zoomLevelPercentage;
const linkNextX = portPos.x - engineOffsetX + (initialXRelative - portPos.x) + event.virtualDisplacementX;
const linkNextY = portPos.y - engineOffsetY + (initialYRelative - portPos.y) + event.virtualDisplacementY;
this.link.getLastPoint().setPosition(linkNextX, linkNextY);
this.engine.repaintCanvas();
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@projectstorm/react-diagrams-defaults",
"version": "6.0.1-beta.1",
"version": "6.0.1-beta.6",
"author": "dylanvorster",
"license": "MIT",
"repository": {
@@ -29,7 +29,7 @@
"main": "./dist/index.js",
"typings": "./dist/@types/index",
"dependencies": {
"@projectstorm/react-diagrams-core": "^6.0.1-beta.1"
"@projectstorm/react-diagrams-core": "^6.0.1-beta.6"
},
"peerDependencies": {
"@emotion/core": "^10.*",

View File

@@ -28,7 +28,6 @@ export class DefaultLinkSegmentWidget extends React.Component<DefaultLinkSegment
);
const Top = React.cloneElement(Bottom, {
...this.props.extras,
strokeLinecap: 'round',
onMouseLeave: () => {
this.props.onSelection(false);
@@ -36,6 +35,7 @@ export class DefaultLinkSegmentWidget extends React.Component<DefaultLinkSegment
onMouseEnter: () => {
this.props.onSelection(true);
},
...this.props.extras,
ref: null,
'data-linkid': this.props.link.getID(),
strokeOpacity: this.props.selected ? 0.1 : 0,

View File

@@ -40,7 +40,7 @@ namespace S {
display: flex;
flex-direction: column;
&:first-child {
&:first-of-type {
margin-right: 10px;
}
@@ -61,7 +61,7 @@ export interface DefaultNodeProps {
*/
export class DefaultNodeWidget extends React.Component<DefaultNodeProps> {
generatePort = port => {
return <DefaultPortLabel engine={this.props.engine} port={port} key={port.id} />;
return <DefaultPortLabel engine={this.props.engine} port={port} key={port.getID()} />;
};
render() {

View File

@@ -1,6 +1,6 @@
{
"name": "@projectstorm/react-diagrams-routing",
"version": "6.0.1-beta.1",
"version": "6.0.1-beta.6",
"author": "dylanvorster",
"license": "MIT",
"repository": {
@@ -30,9 +30,9 @@
"main": "./dist/index.js",
"typings": "./dist/@types/index",
"dependencies": {
"@projectstorm/geometry": "^6.0.1-beta.1",
"@projectstorm/react-diagrams-core": "^6.0.1-beta.1",
"@projectstorm/react-diagrams-defaults": "^6.0.1-beta.1"
"@projectstorm/geometry": "^6.0.1-beta.6",
"@projectstorm/react-diagrams-core": "^6.0.1-beta.6",
"@projectstorm/react-diagrams-defaults": "^6.0.1-beta.6"
},
"peerDependencies": {
"dagre": "^0.8.4",

View File

@@ -59,7 +59,7 @@ export class DagreEngine {
g.nodes().forEach(v => {
const node = g.node(v);
model.getNode(v).setPosition(node.x, node.y);
model.getNode(v).setPosition(node.x - node.width / 2, node.y - node.height / 2);
});
// also include links?

View File

@@ -195,25 +195,20 @@ export class PathFindingLinkFactory extends DefaultLinkFactory<PathFindingLinkMo
height: 0
}));
const canvas = this.engine.getCanvas() as HTMLDivElement;
const minX =
Math.floor(
Math.min(_.minBy(_.concat(allNodesCoords, allPortsCoords, allPointsCoords), item => item.x).x, 0) /
this.ROUTING_SCALING_FACTOR
) * this.ROUTING_SCALING_FACTOR;
const maxXElement = _.maxBy(_.concat(allNodesCoords, allPortsCoords, allPointsCoords), item => item.x + item.width);
const maxX = Math.max(maxXElement.x + maxXElement.width, canvas.offsetWidth);
const sumProps = (object, props) => _.reduce(props, (acc, prop) => acc + _.get(object, prop, 0), 0);
const canvas = this.engine.getCanvas() as HTMLDivElement;
const concatedCoords = _.concat(allNodesCoords, allPortsCoords, allPointsCoords);
const minX =
Math.floor(Math.min(_.get(_.minBy(concatedCoords, 'x'), 'x', 0), 0) / this.ROUTING_SCALING_FACTOR) *
this.ROUTING_SCALING_FACTOR;
const maxXElement = _.maxBy(concatedCoords, item => sumProps(item, ['x', 'width']));
const maxX = Math.max(sumProps(maxXElement, ['x', 'width']), canvas.offsetWidth);
const minYCoords = _.minBy(concatedCoords, 'y');
const minY =
Math.floor(
Math.min(_.minBy(_.concat(allNodesCoords, allPortsCoords, allPointsCoords), item => item.y).y, 0) /
this.ROUTING_SCALING_FACTOR
) * this.ROUTING_SCALING_FACTOR;
const maxYElement = _.maxBy(
_.concat(allNodesCoords, allPortsCoords, allPointsCoords),
item => item.y + item.height
);
const maxY = Math.max(maxYElement.y + maxYElement.height, canvas.offsetHeight);
Math.floor(Math.min(_.get(minYCoords, 'y', 0), 0) / this.ROUTING_SCALING_FACTOR) * this.ROUTING_SCALING_FACTOR;
const maxYElement = _.maxBy(concatedCoords, item => sumProps(item, ['y', 'height']));
const maxY = Math.max(sumProps(maxYElement, ['y', 'height']), canvas.offsetHeight);
return {
width: Math.ceil(Math.abs(minX) + maxX),

View File

@@ -1,15 +1,67 @@
import { DefaultLinkModel, DefaultLinkModelOptions } from '@projectstorm/react-diagrams-defaults';
import { RightAngleLinkFactory } from './RightAngleLinkFactory';
import { PointModel } from '@projectstorm/react-diagrams-core';
import { DeserializeEvent } from '@projectstorm/react-canvas-core';
export class RightAngleLinkModel extends DefaultLinkModel {
lastHoverIndexOfPath: number;
private _lastPathXdirection: boolean;
private _firstPathXdirection: boolean;
constructor(options: DefaultLinkModelOptions = {}) {
super({
type: RightAngleLinkFactory.NAME,
...options
});
this.lastHoverIndexOfPath = 0;
this._lastPathXdirection = false;
this._firstPathXdirection = false;
}
performanceTune() {
return false;
setFirstAndLastPathsDirection() {
let points = this.getPoints();
for (let i = 1; i < points.length; i += points.length - 2) {
let dx = Math.abs(points[i].getX() - points[i - 1].getX());
let dy = Math.abs(points[i].getY() - points[i - 1].getY());
if (i - 1 === 0) {
this._firstPathXdirection = dx > dy;
} else {
this._lastPathXdirection = dx > dy;
}
}
}
// @ts-ignore
addPoint<P extends PointModel>(pointModel: P, index: number = 1): P {
// @ts-ignore
super.addPoint(pointModel, index);
this.setFirstAndLastPathsDirection();
return pointModel;
}
deserialize(event: DeserializeEvent<this>) {
super.deserialize(event);
this.setFirstAndLastPathsDirection();
}
setManuallyFirstAndLastPathsDirection(first, last) {
this._firstPathXdirection = first;
this._lastPathXdirection = last;
}
getLastPathXdirection(): boolean {
return this._lastPathXdirection;
}
getFirstPathXdirection(): boolean {
return this._firstPathXdirection;
}
setWidth(width: number) {
this.options.width = width;
this.fireEvent({ width }, 'widthChanged');
}
setColor(color: string) {
this.options.color = color;
this.fireEvent({ color }, 'colorChanged');
}
}

View File

@@ -170,6 +170,7 @@ export class RightAngleLinkWidget extends React.Component<RightAngleLinkProps, R
} else if (dy === 0) {
this.calculatePositions(points, event, index, 'y');
}
this.props.link.setFirstAndLastPathsDirection();
}
handleMove = function(event: MouseEvent) {
@@ -191,46 +192,57 @@ export class RightAngleLinkWidget extends React.Component<RightAngleLinkProps, R
// Get points based on link orientation
let pointLeft = points[0];
let pointRight = points[points.length - 1];
let hadToSwitch = false;
if (pointLeft.getX() > pointRight.getX()) {
pointLeft = points[points.length - 1];
pointRight = points[0];
hadToSwitch = true;
}
let dy = Math.abs(points[0].getY() - points[points.length - 1].getY());
// When new link add one middle point to get everywhere 90° angle
if (this.props.link.getTargetPort() === null && points.length === 2) {
this.props.link.addPoint(
new PointModel({
link: this.props.link,
position: new Point(pointLeft.getX(), pointRight.getY())
}),
1
);
[...Array(2)].forEach(item => {
this.props.link.addPoint(
new PointModel({
link: this.props.link,
position: new Point(pointLeft.getX(), pointRight.getY())
}),
1
);
});
this.props.link.setManuallyFirstAndLastPathsDirection(true, true);
}
// When new link is moving and not connected to target port move with middle point
else if (this.props.link.getTargetPort() === null) {
points[1].setPosition(pointLeft.getX(), pointRight.getY());
// TODO: @DanielLazarLDAPPS This will be better to update in DragNewLinkState
// in function fireMouseMoved to avoid calling this unexpectedly e.g. after Deserialize
else if (this.props.link.getTargetPort() === null && this.props.link.getSourcePort() !== null) {
points[1].setPosition(
pointRight.getX() + (pointLeft.getX() - pointRight.getX()) / 2,
!hadToSwitch ? pointLeft.getY() : pointRight.getY()
);
points[2].setPosition(
pointRight.getX() + (pointLeft.getX() - pointRight.getX()) / 2,
!hadToSwitch ? pointRight.getY() : pointLeft.getY()
);
}
// Render was called but link is not moved but user.
// Node is moved and in this case fix coordinates to get 90° angle.
// For loop just for first and last path
else if (!this.state.canDrag && points.length > 2) {
// Those points and its position only will be moved
for (let i = 1; i < points.length; i += points.length - 2) {
let dx = Math.abs(points[i].getX() - points[i - 1].getX());
let dy = Math.abs(points[i].getY() - points[i - 1].getY());
if (dx !== 0 || dy !== 0) {
if (dx < dy) {
if (i - 1 === 0) {
points[i].setPosition(points[i - 1].getX(), points[i].getY());
} else if (i === points.length - 1) {
points[i - 1].setPosition(points[i].getX(), points[i - 1].getY());
}
if (i - 1 === 0) {
if (this.props.link.getFirstPathXdirection()) {
points[i].setPosition(points[i].getX(), points[i - 1].getY());
} else {
if (i - 1 === 0) {
points[i].setPosition(points[i].getX(), points[i - 1].getY());
} else if (i === points.length - 1) {
points[i - 1].setPosition(points[i - 1].getX(), points[i].getY());
}
points[i].setPosition(points[i - 1].getX(), points[i].getY());
}
} else {
if (this.props.link.getLastPathXdirection()) {
points[i - 1].setPosition(points[i - 1].getX(), points[i].getY());
} else {
points[i - 1].setPosition(points[i].getX(), points[i - 1].getY());
}
}
}
@@ -243,8 +255,7 @@ export class RightAngleLinkWidget extends React.Component<RightAngleLinkProps, R
new PointModel({
link: this.props.link,
position: new Point(pointLeft.getX(), pointRight.getY())
}),
1
})
);
}
@@ -264,6 +275,10 @@ export class RightAngleLinkWidget extends React.Component<RightAngleLinkProps, R
window.addEventListener('mousemove', this.handleMove);
window.addEventListener('mouseup', this.handleUp);
}
},
onMouseEnter: (event: MouseEvent) => {
this.setState({ selected: true });
this.props.link.lastHoverIndexOfPath = j;
}
},
j

View File

@@ -11,7 +11,7 @@ import {
DefaultPortFactory
} from '@projectstorm/react-diagrams-defaults';
import { PathFindingLinkFactory } from '@projectstorm/react-diagrams-routing';
import { SelectionBoxLayerFactory } from '@projectstorm/react-canvas-core';
import { SelectionBoxLayerFactory, CanvasEngineOptions } from '@projectstorm/react-canvas-core';
export * from '@projectstorm/react-diagrams-core';
export * from '@projectstorm/react-diagrams-defaults';
@@ -20,8 +20,8 @@ export * from '@projectstorm/react-diagrams-routing';
/**
* Construct an engine with the defaults installed
*/
export default (): DiagramEngine => {
const engine = new DiagramEngine();
export default (options: CanvasEngineOptions = {}): DiagramEngine => {
const engine = new DiagramEngine(options);
// register model factories
engine.getLayerFactories().registerFactory(new NodeLayerFactory());

View File

@@ -1,6 +1,6 @@
{
"name": "@projectstorm/react-diagrams",
"version": "6.0.1-beta.1",
"version": "6.0.1-beta.6",
"author": "dylanvorster",
"license": "MIT",
"repository": {
@@ -29,9 +29,9 @@
"main": "./dist/index.js",
"typings": "./dist/@types/index",
"dependencies": {
"@projectstorm/react-diagrams-core": "^6.0.1-beta.1",
"@projectstorm/react-diagrams-defaults": "^6.0.1-beta.1",
"@projectstorm/react-diagrams-routing": "^6.0.1-beta.1"
"@projectstorm/react-diagrams-core": "^6.0.1-beta.6",
"@projectstorm/react-diagrams-defaults": "^6.0.1-beta.6",
"@projectstorm/react-diagrams-routing": "^6.0.1-beta.6"
},
"gitHead": "bb878657ba0c2f81764f32901fd96158a0f8352e"
}