mirror of
https://github.com/projectstorm/react-diagrams.git
synced 2026-03-13 09:50:09 +08:00
+ Cleaned up documentation
+ Added new events to the LinkModel for when ports change + Added properties allowing the canvas to lock its zoom and panning + Added option for disabling loose links + depreciated but did not remove the controlsUpdated event
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,6 +4,8 @@ demos/demo2/bundle.js
|
||||
demos/demo2/bundle.js.map
|
||||
demos/demo3/bundle.js
|
||||
demos/demo3/bundle.js.map
|
||||
demos/demo4/bundle.js
|
||||
demos/demo4/bundle.js.map
|
||||
dist/main.js
|
||||
dist/main.js.map
|
||||
|
||||
|
||||
35
README.md
35
README.md
@@ -59,40 +59,7 @@ a link can be connected to it.
|
||||
|
||||
## Events
|
||||
|
||||
Each model (DiagramModel, NodeModel etc..) are all built ontop of an event system. You can listen for most of these events by registering
|
||||
an event on the model itself. See below for some common events (I will add better documentation soon)
|
||||
|
||||
- entityRemoved (entity)
|
||||
- selectionChanged (entity, isSelected:Boolean)
|
||||
- nodeFactoriesUpdated
|
||||
- linkFactoriesUpdated
|
||||
- controlsUpdated
|
||||
- linksUpdated (entity, isAdded:Boolean)
|
||||
- nodesUpdated (entity, isAdded:Boolean)
|
||||
|
||||
|
||||
### Example of usage
|
||||
```javascript
|
||||
let model = new SRD.DiagramModel();
|
||||
let node1 = new SRD.DefaultNodeModel("default","rgb(0,192,255)");
|
||||
node1.addListener({
|
||||
entityRemoved: (node) => {
|
||||
console.log('Removed', node.id)
|
||||
},
|
||||
selectionChanged: (node, isSelected) => {
|
||||
console.log(isSelected?'Selected':'Unselected', node)
|
||||
}
|
||||
});
|
||||
model.addListener({
|
||||
linksUpdated:(entity, isAdded) => {
|
||||
console.log(isAdded?'added':'removed', entity)
|
||||
},
|
||||
nodesUpdated: (entity, isAdded) => {
|
||||
console.log(isAdded?'added':'removed', entity)
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
[Event System](docs/Events.md)
|
||||
|
||||
## DiagramWidget props
|
||||
|
||||
|
||||
15
demos/demo4/index.html
Normal file
15
demos/demo4/index.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>STORM React Diagrams Test 2</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://unpkg.com/react/dist/react.min.js"></script>
|
||||
<script src="https://unpkg.com/react-dom/dist/react-dom.min.js"></script>
|
||||
<script src="https://unpkg.com/lodash@4.17.4/lodash.min.js"></script>
|
||||
<script src="./bundle.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
63
demos/demo4/index.ts
Normal file
63
demos/demo4/index.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import * as SRD from "../../src/main";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
||||
declare var require: {
|
||||
<T>(path: string): T;
|
||||
(paths: string[], callback: (...modules: any[]) => void): void;
|
||||
ensure: (paths: string[], callback: (require: <T>(path: string) => T) => void) => void;
|
||||
};
|
||||
|
||||
require("../test.scss");
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Shows how you can lock down the ystem so that the entire scene cant be interacted with.
|
||||
*
|
||||
* @Author Dylan Vorster
|
||||
*/
|
||||
window.onload = () => {
|
||||
|
||||
//1) setup the diagram engine
|
||||
var engine = new SRD.DiagramEngine();
|
||||
engine.registerNodeFactory(new SRD.DefaultNodeFactory());
|
||||
engine.registerLinkFactory(new SRD.DefaultLinkFactory());
|
||||
|
||||
var model = new SRD.DiagramModel();
|
||||
|
||||
var node1 = new SRD.DefaultNodeModel("Node 1","rgb(0,192,255)");
|
||||
var port1 = node1.addPort(new SRD.DefaultPortModel(false,"out-1","Out"));
|
||||
node1.x = 100;
|
||||
node1.y = 100;
|
||||
|
||||
var node2 = new SRD.DefaultNodeModel("Node 2","rgb(192,255,0)");
|
||||
var port2 = node2.addPort(new SRD.DefaultPortModel(true,"in-1","IN"));
|
||||
node2.x = 400;
|
||||
node2.y = 100;
|
||||
|
||||
var link1 = new SRD.LinkModel();
|
||||
link1.setSourcePort(port1);
|
||||
link1.setTargetPort(port2);
|
||||
|
||||
model.addNode(node1);
|
||||
model.addNode(node2);
|
||||
model.addLink(link1);
|
||||
|
||||
engine.setDiagramModel(model);
|
||||
|
||||
//!========================================= <<<<<<<
|
||||
|
||||
model.setLocked(true);
|
||||
var props = {
|
||||
diagramEngine: engine,
|
||||
allowLooseLinks: false,
|
||||
allowCanvasTranslation: false,
|
||||
allowCanvasZoom: false
|
||||
} as SRD.DiagramProps;
|
||||
|
||||
//!========================================= <<<<<<<
|
||||
|
||||
ReactDOM.render(React.createElement(SRD.DiagramWidget,props), document.body);
|
||||
|
||||
}
|
||||
0
dist/demos/demo4/index.d.ts
vendored
Normal file
0
dist/demos/demo4/index.d.ts
vendored
Normal file
4
dist/src/BaseEntity.d.ts
vendored
4
dist/src/BaseEntity.d.ts
vendored
@@ -2,12 +2,14 @@
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export declare class BaseListener {
|
||||
lockChanged?(entity: BaseEntity<BaseListener>, locked: boolean): void;
|
||||
}
|
||||
export declare class BaseEntity<T extends BaseListener> {
|
||||
listeners: {
|
||||
[s: string]: T;
|
||||
};
|
||||
id: string;
|
||||
locked: boolean;
|
||||
constructor();
|
||||
getID(): string;
|
||||
clearListeners(): void;
|
||||
@@ -18,4 +20,6 @@ export declare class BaseEntity<T extends BaseListener> {
|
||||
itterateListeners(cb: (t: T) => any): void;
|
||||
removeListener(listener: string): boolean;
|
||||
addListener(listener: T): string;
|
||||
isLocked(): boolean;
|
||||
setLocked(locked?: boolean): void;
|
||||
}
|
||||
|
||||
20
dist/src/Common.d.ts
vendored
20
dist/src/Common.d.ts
vendored
@@ -1,12 +1,12 @@
|
||||
import { BaseEntity, BaseListener } from "./BaseEntity";
|
||||
export interface BaseModelListener extends BaseListener {
|
||||
selectionChanged?(item: any, isSelected: boolean): any;
|
||||
entityRemoved?(item: any): any;
|
||||
selectionChanged?(item: BaseModel<BaseModelListener>, isSelected: boolean): void;
|
||||
entityRemoved?(item: any): void;
|
||||
}
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export declare class BaseModel extends BaseEntity<BaseModelListener> {
|
||||
export declare class BaseModel<T extends BaseModelListener> extends BaseEntity<BaseModelListener> {
|
||||
selected: boolean;
|
||||
constructor();
|
||||
deSerialize(ob: any): void;
|
||||
@@ -18,10 +18,10 @@ export declare class BaseModel extends BaseEntity<BaseModelListener> {
|
||||
};
|
||||
getID(): string;
|
||||
isSelected(): boolean;
|
||||
setSelected(selected: boolean): void;
|
||||
setSelected(selected?: boolean): void;
|
||||
remove(): void;
|
||||
}
|
||||
export declare class PointModel extends BaseModel {
|
||||
export declare class PointModel extends BaseModel<BaseModelListener> {
|
||||
x: number;
|
||||
y: number;
|
||||
link: LinkModel;
|
||||
@@ -48,7 +48,11 @@ export declare class PointModel extends BaseModel {
|
||||
getY(): number;
|
||||
getLink(): LinkModel;
|
||||
}
|
||||
export declare class LinkModel extends BaseModel {
|
||||
export interface LinkModelListener extends BaseModelListener {
|
||||
sourcePortChanged?(item: LinkModel, target: null | PortModel): void;
|
||||
targetPortChanged?(item: LinkModel, target: null | PortModel): void;
|
||||
}
|
||||
export declare class LinkModel extends BaseModel<LinkModelListener> {
|
||||
linkType: string;
|
||||
sourcePort: PortModel | null;
|
||||
targetPort: PortModel | null;
|
||||
@@ -94,7 +98,7 @@ export declare class LinkModel extends BaseModel {
|
||||
addPoint(pointModel: PointModel, index?: number): void;
|
||||
getType(): string;
|
||||
}
|
||||
export declare class PortModel extends BaseModel {
|
||||
export declare class PortModel extends BaseModel<BaseModelListener> {
|
||||
name: string;
|
||||
parentNode: NodeModel;
|
||||
links: {
|
||||
@@ -121,7 +125,7 @@ export declare class PortModel extends BaseModel {
|
||||
[id: string]: LinkModel;
|
||||
};
|
||||
}
|
||||
export declare class NodeModel extends BaseModel {
|
||||
export declare class NodeModel extends BaseModel<BaseModelListener> {
|
||||
nodeType: string;
|
||||
x: number;
|
||||
y: number;
|
||||
|
||||
11
dist/src/DiagramEngine.d.ts
vendored
11
dist/src/DiagramEngine.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="react" />
|
||||
import { NodeWidgetFactory, LinkWidgetFactory } from "./WidgetFactories";
|
||||
import { LinkModel, NodeModel, BaseModel, PortModel } from "./Common";
|
||||
import { LinkModel, NodeModel, BaseModel, BaseModelListener, PortModel } from "./Common";
|
||||
import { BaseEntity, BaseListener } from "./BaseEntity";
|
||||
import { DiagramModel } from "./DiagramModel";
|
||||
import { AbstractInstanceFactory } from "./AbstractInstanceFactory";
|
||||
@@ -29,8 +29,13 @@ export declare class DiagramEngine extends BaseEntity<DiagramEngineListener> {
|
||||
paintableWidgets: {};
|
||||
constructor();
|
||||
clearRepaintEntities(): void;
|
||||
enableRepaintEntities(entities: BaseModel[]): void;
|
||||
canEntityRepaint(baseModel: BaseModel): boolean;
|
||||
enableRepaintEntities(entities: BaseModel<BaseModelListener>[]): void;
|
||||
/**
|
||||
* Checks to see if a model is locked by running through
|
||||
* its parents to see if they are locked first
|
||||
*/
|
||||
isModelLocked(model: BaseEntity<BaseListener>): boolean;
|
||||
canEntityRepaint(baseModel: BaseModel<BaseModelListener>): boolean;
|
||||
setCanvas(canvas: Element | null): void;
|
||||
setDiagramModel(model: DiagramModel): void;
|
||||
getDiagramModel(): DiagramModel;
|
||||
|
||||
17
dist/src/DiagramModel.d.ts
vendored
17
dist/src/DiagramModel.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
import { LinkModel, NodeModel, BaseModel } from "./Common";
|
||||
import { LinkModel, NodeModel, BaseModel, BaseModelListener } from "./Common";
|
||||
import { BaseListener, BaseEntity } from "./BaseEntity";
|
||||
import { DiagramEngine } from "./DiagramEngine";
|
||||
/**
|
||||
@@ -6,9 +6,14 @@ import { DiagramEngine } from "./DiagramEngine";
|
||||
*
|
||||
*/
|
||||
export interface DiagramListener extends BaseListener {
|
||||
nodesUpdated(node: any, isCreated: boolean): any;
|
||||
linksUpdated(link: any, isCreated: boolean): any;
|
||||
controlsUpdated(): any;
|
||||
nodesUpdated(node: any, isCreated: boolean): void;
|
||||
linksUpdated(link: any, isCreated: boolean): void;
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
controlsUpdated(): void;
|
||||
offsetUpdated(model: DiagramModel, offsetX: number, offsetY: number): void;
|
||||
zoomUpdated(model: DiagramModel, zoom: number): void;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -76,8 +81,8 @@ export declare class DiagramModel extends BaseEntity<DiagramListener> {
|
||||
})[];
|
||||
})[];
|
||||
};
|
||||
clearSelection(ignore?: BaseModel | null): void;
|
||||
getSelectedItems(): BaseModel[];
|
||||
clearSelection(ignore?: BaseModel<BaseModelListener> | null): void;
|
||||
getSelectedItems(): BaseModel<BaseModelListener>[];
|
||||
setZoomLevel(zoom: number): void;
|
||||
setOffset(offsetX: number, offsetY: number): void;
|
||||
setOffsetX(offsetX: number): void;
|
||||
|
||||
9
dist/src/widgets/DiagramWidget.d.ts
vendored
9
dist/src/widgets/DiagramWidget.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
/// <reference types="react" />
|
||||
import * as React from "react";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import { BaseModel } from "../Common";
|
||||
import { BaseModel, BaseModelListener } from "../Common";
|
||||
export declare class BaseAction {
|
||||
mouseX: number;
|
||||
mouseY: number;
|
||||
@@ -10,7 +10,9 @@ export declare class BaseAction {
|
||||
}
|
||||
export interface DiagramProps {
|
||||
diagramEngine: DiagramEngine;
|
||||
onLinkStateChanged?: any;
|
||||
allowLooseLinks?: boolean;
|
||||
allowCanvasTranslation?: boolean;
|
||||
allowCanvasZoom?: boolean;
|
||||
}
|
||||
export interface DiagramState {
|
||||
action: BaseAction | null;
|
||||
@@ -21,6 +23,7 @@ export interface DiagramState {
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export declare class DiagramWidget extends React.Component<DiagramProps, DiagramState> {
|
||||
static defaultProps: DiagramProps;
|
||||
constructor(props: DiagramProps);
|
||||
componentWillUnmount(): void;
|
||||
componentWillUpdate(nextProps: DiagramProps): void;
|
||||
@@ -30,7 +33,7 @@ export declare class DiagramWidget extends React.Component<DiagramProps, Diagram
|
||||
* Gets a model and element under the mouse cursor
|
||||
*/
|
||||
getMouseElement(event: any): {
|
||||
model: BaseModel;
|
||||
model: BaseModel<BaseModelListener>;
|
||||
element: Element;
|
||||
};
|
||||
render(): React.DOMElement<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
||||
|
||||
88
docs/Events.md
Normal file
88
docs/Events.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Events
|
||||
|
||||
Each model (DiagramModel, NodeModel etc..) are all built ontop of an event system. You can listen for most of these events by registering
|
||||
an event on the model itself.
|
||||
|
||||
## All models
|
||||
|
||||
All models will fire these events:
|
||||
|
||||
#### lockChanged?(entity: BaseEntity,locked: boolean)
|
||||
|
||||
Fires when the lock state of the entity changes. If an element is locked, it cannot be moved or deletes.
|
||||
|
||||
## All Base models excluding DiagramModel
|
||||
|
||||
|
||||
#### selectionChanged?(item: BaseModel, isSelected:boolean)
|
||||
|
||||
When the _selected_ property of a model changes
|
||||
|
||||
#### entityRemoved?(item:any)
|
||||
|
||||
When the entity is going to be deleted. The DiagramModel listenes for this event to when to remove the model from itself.
|
||||
|
||||
|
||||
## DiagramModel
|
||||
|
||||
#### nodesUpdated(node: any, isCreated:boolean)
|
||||
|
||||
When nodes are added or removed
|
||||
|
||||
#### linksUpdated(link: any, isCreated:boolean)
|
||||
|
||||
when links are added or removed
|
||||
|
||||
#### controlsUpdated() [DEPRECIATED]
|
||||
|
||||
_depreciated, use offsetUpdated and zoomUpdated instead_
|
||||
|
||||
#### offsetUpdated(model: DiagramModel,offsetX: number, offsetY: number)
|
||||
|
||||
to know when the canvas was translated in any direction
|
||||
|
||||
#### zoomUpdated(model: DiagramModel,zoom: number)
|
||||
|
||||
to know when the zoom level of the canvas was updated
|
||||
|
||||
## DiagramEngine
|
||||
|
||||
The diagram engine
|
||||
|
||||
#### nodeFactoriesUpdated
|
||||
|
||||
When node factories have been added or removed from the engine
|
||||
|
||||
#### linkFactoriesUpdated
|
||||
|
||||
When link factories have been added or removed from the engine
|
||||
|
||||
## LinkModel
|
||||
|
||||
#### sourcePortChanged?(item:LinkModel,target: null|PortModel)
|
||||
|
||||
#### targetPortChanged?(item:LinkModel,target: null|PortModel)
|
||||
|
||||
|
||||
# Example of usage
|
||||
|
||||
```javascript
|
||||
let model = new SRD.DiagramModel();
|
||||
let node1 = new SRD.DefaultNodeModel("default","rgb(0,192,255)");
|
||||
node1.addListener({
|
||||
entityRemoved: (node) => {
|
||||
console.log('Removed', node.id)
|
||||
},
|
||||
selectionChanged: (node, isSelected) => {
|
||||
console.log(isSelected?'Selected':'Unselected', node)
|
||||
}
|
||||
});
|
||||
model.addListener({
|
||||
linksUpdated:(entity, isAdded) => {
|
||||
console.log(isAdded?'added':'removed', entity)
|
||||
},
|
||||
nodesUpdated: (entity, isAdded) => {
|
||||
console.log(isAdded?'added':'removed', entity)
|
||||
}
|
||||
});
|
||||
```
|
||||
@@ -1,20 +1,21 @@
|
||||
import {Toolkit} from "./Toolkit";
|
||||
import {AbstractInstanceFactory} from "./AbstractInstanceFactory";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export class BaseListener{
|
||||
|
||||
lockChanged?(entity: BaseEntity<BaseListener>,locked: boolean): void;
|
||||
}
|
||||
|
||||
export class BaseEntity<T extends BaseListener>{
|
||||
|
||||
public listeners:{[s: string]: T};
|
||||
public id: string;
|
||||
public locked: boolean;
|
||||
|
||||
constructor(){
|
||||
this.listeners = {};
|
||||
this.id = Toolkit.UID();
|
||||
this.locked = false;
|
||||
}
|
||||
|
||||
getID(){
|
||||
@@ -54,4 +55,17 @@ export class BaseEntity<T extends BaseListener>{
|
||||
this.listeners[uid] = listener;
|
||||
return uid;
|
||||
}
|
||||
|
||||
public isLocked(): boolean{
|
||||
return this.locked;
|
||||
}
|
||||
|
||||
public setLocked(locked: boolean = true){
|
||||
this.locked = locked;
|
||||
this.itterateListeners((listener) => {
|
||||
if (listener.lockChanged){
|
||||
listener.lockChanged(this, locked);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
import {Toolkit} from "./Toolkit";
|
||||
import {BaseEntity, BaseListener} from "./BaseEntity";
|
||||
import * as _ from "lodash";
|
||||
|
||||
export interface BaseModelListener extends BaseListener{
|
||||
|
||||
selectionChanged?(item:any, isSelected:boolean);
|
||||
entityRemoved?(item:any);
|
||||
selectionChanged?(item: BaseModel<BaseModelListener>, isSelected:boolean): void;
|
||||
|
||||
entityRemoved?(item:any): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export class BaseModel extends BaseEntity<BaseModelListener>{
|
||||
export class BaseModel<T extends BaseModelListener> extends BaseEntity<BaseModelListener>{
|
||||
|
||||
selected: boolean;
|
||||
|
||||
@@ -40,7 +40,7 @@ export class BaseModel extends BaseEntity<BaseModelListener>{
|
||||
return this.selected;
|
||||
}
|
||||
|
||||
public setSelected(selected: boolean){
|
||||
public setSelected(selected: boolean = true){
|
||||
this.selected = selected;
|
||||
this.itterateListeners((listener) => {
|
||||
if(listener.selectionChanged){
|
||||
@@ -58,7 +58,7 @@ export class BaseModel extends BaseEntity<BaseModelListener>{
|
||||
}
|
||||
}
|
||||
|
||||
export class PointModel extends BaseModel{
|
||||
export class PointModel extends BaseModel<BaseModelListener>{
|
||||
|
||||
x:number;
|
||||
y:number;
|
||||
@@ -111,7 +111,14 @@ export class PointModel extends BaseModel{
|
||||
}
|
||||
}
|
||||
|
||||
export class LinkModel extends BaseModel{
|
||||
export interface LinkModelListener extends BaseModelListener{
|
||||
|
||||
sourcePortChanged?(item:LinkModel,target: null|PortModel): void;
|
||||
|
||||
targetPortChanged?(item:LinkModel,target: null|PortModel): void;
|
||||
}
|
||||
|
||||
export class LinkModel extends BaseModel<LinkModelListener>{
|
||||
|
||||
linkType: string;
|
||||
sourcePort: PortModel|null;
|
||||
@@ -194,6 +201,9 @@ export class LinkModel extends BaseModel{
|
||||
setSourcePort(port: PortModel){
|
||||
port.addLink(this);
|
||||
this.sourcePort = port;
|
||||
this.itterateListeners((listener: LinkModelListener) => {
|
||||
listener.sourcePortChanged && listener.sourcePortChanged(this, port);
|
||||
});
|
||||
}
|
||||
|
||||
getSourcePort(): PortModel{
|
||||
@@ -207,6 +217,9 @@ export class LinkModel extends BaseModel{
|
||||
setTargetPort(port: PortModel){
|
||||
port.addLink(this);
|
||||
this.targetPort = port;
|
||||
this.itterateListeners((listener: LinkModelListener) => {
|
||||
listener.targetPortChanged && listener.targetPortChanged(this, port);
|
||||
});
|
||||
}
|
||||
|
||||
getPoints(): PointModel[]{
|
||||
@@ -230,7 +243,7 @@ export class LinkModel extends BaseModel{
|
||||
}
|
||||
}
|
||||
|
||||
export class PortModel extends BaseModel{
|
||||
export class PortModel extends BaseModel<BaseModelListener>{
|
||||
name: string;
|
||||
parentNode: NodeModel;
|
||||
links: {[id: string]: LinkModel};
|
||||
@@ -282,7 +295,7 @@ export class PortModel extends BaseModel{
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeModel extends BaseModel{
|
||||
export class NodeModel extends BaseModel<BaseModelListener>{
|
||||
|
||||
nodeType: string;
|
||||
x: number;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {NodeWidgetFactory, LinkWidgetFactory} from "./WidgetFactories";
|
||||
import {LinkModel, NodeModel, BaseModel, PortModel, PointModel} from "./Common";
|
||||
import {LinkModel, NodeModel, BaseModel,BaseModelListener, PortModel, PointModel} from "./Common";
|
||||
import {BaseEntity, BaseListener} from "./BaseEntity";
|
||||
import {DiagramModel} from "./DiagramModel";
|
||||
import {AbstractInstanceFactory} from "./AbstractInstanceFactory";
|
||||
@@ -41,7 +41,7 @@ export class DiagramEngine extends BaseEntity<DiagramEngineListener>{
|
||||
this.paintableWidgets = null;
|
||||
}
|
||||
|
||||
enableRepaintEntities(entities: BaseModel[]){
|
||||
enableRepaintEntities(entities: BaseModel<BaseModelListener>[]){
|
||||
this.paintableWidgets = {};
|
||||
entities.forEach((entity) => {
|
||||
|
||||
@@ -62,7 +62,28 @@ export class DiagramEngine extends BaseEntity<DiagramEngineListener>{
|
||||
});
|
||||
}
|
||||
|
||||
canEntityRepaint(baseModel: BaseModel){
|
||||
/**
|
||||
* Checks to see if a model is locked by running through
|
||||
* its parents to see if they are locked first
|
||||
*/
|
||||
isModelLocked(model: BaseEntity<BaseListener>){
|
||||
|
||||
//always check the diagram model
|
||||
if (this.diagramModel.isLocked()){
|
||||
return true;
|
||||
}
|
||||
|
||||
//a point is locked, if its model is locked
|
||||
if (model instanceof PointModel){
|
||||
if (model.getLink().isLocked()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return model.isLocked();
|
||||
}
|
||||
|
||||
canEntityRepaint(baseModel: BaseModel<BaseModelListener>){
|
||||
//no rules applied, allow repaint
|
||||
if(this.paintableWidgets === null){
|
||||
return true;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {LinkModel, NodeModel, BaseModel, PortModel} from "./Common";
|
||||
import {LinkModel, NodeModel, BaseModel,BaseModelListener, PortModel} from "./Common";
|
||||
import {BaseListener, BaseEntity} from "./BaseEntity";
|
||||
import * as _ from "lodash";
|
||||
import {DiagramEngine} from "./DiagramEngine";
|
||||
@@ -8,11 +8,18 @@ import {DiagramEngine} from "./DiagramEngine";
|
||||
*/
|
||||
export interface DiagramListener extends BaseListener{
|
||||
|
||||
nodesUpdated(node: any, isCreated:boolean): any;
|
||||
nodesUpdated(node: any, isCreated:boolean): void;
|
||||
|
||||
linksUpdated(link: any, isCreated:boolean): any;
|
||||
linksUpdated(link: any, isCreated:boolean): void;
|
||||
|
||||
controlsUpdated(): any;
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
controlsUpdated(): void;
|
||||
|
||||
offsetUpdated(model: DiagramModel,offsetX: number, offsetY: number): void;
|
||||
|
||||
zoomUpdated(model: DiagramModel,zoom: number): void;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -93,7 +100,7 @@ export class DiagramModel extends BaseEntity<DiagramListener>{
|
||||
});
|
||||
}
|
||||
|
||||
clearSelection(ignore: BaseModel|null = null){
|
||||
clearSelection(ignore: BaseModel<BaseModelListener>|null = null){
|
||||
_.forEach(this.getSelectedItems(),(element) => {
|
||||
if (ignore && ignore.getID() === element.getID()){
|
||||
return;
|
||||
@@ -102,7 +109,7 @@ export class DiagramModel extends BaseEntity<DiagramListener>{
|
||||
});
|
||||
}
|
||||
|
||||
getSelectedItems(): BaseModel[]{
|
||||
getSelectedItems(): BaseModel<BaseModelListener>[]{
|
||||
var items = [];
|
||||
|
||||
//find all nodes
|
||||
@@ -128,6 +135,9 @@ export class DiagramModel extends BaseEntity<DiagramListener>{
|
||||
this.itterateListeners((listener) => {
|
||||
if(listener.controlsUpdated) listener.controlsUpdated();
|
||||
});
|
||||
this.itterateListeners((listener) => {
|
||||
listener.zoomUpdated && listener.zoomUpdated(this,this.zoom);
|
||||
});
|
||||
}
|
||||
|
||||
setOffset(offsetX: number, offsetY: number){
|
||||
@@ -136,6 +146,9 @@ export class DiagramModel extends BaseEntity<DiagramListener>{
|
||||
this.itterateListeners((listener) => {
|
||||
if(listener.controlsUpdated) listener.controlsUpdated();
|
||||
});
|
||||
this.itterateListeners((listener) => {
|
||||
listener.offsetUpdated && listener.offsetUpdated(this,this.offsetX, this.offsetY)
|
||||
});
|
||||
}
|
||||
|
||||
setOffsetX(offsetX: number){
|
||||
@@ -143,12 +156,18 @@ export class DiagramModel extends BaseEntity<DiagramListener>{
|
||||
this.itterateListeners((listener) => {
|
||||
if(listener.controlsUpdated) listener.controlsUpdated();
|
||||
});
|
||||
this.itterateListeners((listener) => {
|
||||
listener.offsetUpdated && listener.offsetUpdated(this,this.offsetX, this.offsetY)
|
||||
});
|
||||
}
|
||||
setOffsetY(offsetY: number){
|
||||
this.offsetX = offsetY;
|
||||
this.itterateListeners((listener) => {
|
||||
if(listener.controlsUpdated) listener.controlsUpdated();
|
||||
});
|
||||
this.itterateListeners((listener) => {
|
||||
listener.offsetUpdated && listener.offsetUpdated(this,this.offsetX, this.offsetY)
|
||||
});
|
||||
}
|
||||
|
||||
getOffsetY(){
|
||||
|
||||
@@ -55,11 +55,9 @@ export class DefaultLinkWidget extends React.Component<DefaultLinkProps, Default
|
||||
opacity: 0,
|
||||
onMouseLeave:() => {
|
||||
this.setState({selected: false});
|
||||
// this.props.link.setSelected(false);
|
||||
},
|
||||
onMouseEnter:() => {
|
||||
this.setState({selected: true});
|
||||
// this.props.link.setSelected(true);
|
||||
},
|
||||
})
|
||||
);
|
||||
@@ -122,7 +120,7 @@ export class DefaultLinkWidget extends React.Component<DefaultLinkProps, Default
|
||||
paths.push(this.generateLink({
|
||||
id: 0,
|
||||
onMouseDown: (event) =>{
|
||||
if (!event.shiftKey){
|
||||
if (!event.shiftKey && !this.props.diagramEngine.isModelLocked(this.props.link)){
|
||||
var point = new PointModel(this.props.link,this.props.diagramEngine.getRelativeMousePoint(event));
|
||||
point.setSelected(true);
|
||||
this.forceUpdate();
|
||||
|
||||
@@ -2,12 +2,12 @@ import * as React from "react";
|
||||
import {DiagramEngine} from "../DiagramEngine";
|
||||
import {DiagramModel} from "../DiagramModel";
|
||||
import * as _ from "lodash";
|
||||
import {PointModel, NodeModel, BaseModel, LinkModel,PortModel} from "../Common";
|
||||
import {PointModel, NodeModel, BaseModel, BaseModelListener, LinkModel,PortModel} from "../Common";
|
||||
import {LinkLayerWidget} from "./LinkLayerWidget";
|
||||
import {NodeLayerWidget} from "./NodeLayerWidget";
|
||||
|
||||
interface SelectionModel{
|
||||
model: BaseModel;
|
||||
model: BaseModel<BaseModelListener>;
|
||||
initialX: number;
|
||||
initialY: number;
|
||||
}
|
||||
@@ -63,7 +63,14 @@ class MoveItemsAction extends BaseAction{
|
||||
super(mouseX, mouseY);
|
||||
this.moved = false;
|
||||
diagramEngine.enableRepaintEntities(diagramEngine.getDiagramModel().getSelectedItems());
|
||||
this.selectionModels = diagramEngine.getDiagramModel().getSelectedItems().map((item: PointModel | NodeModel) => {
|
||||
var selectedItems = diagramEngine.getDiagramModel().getSelectedItems();
|
||||
|
||||
//dont allow items which are locked to move
|
||||
selectedItems = selectedItems.filter((item) => {
|
||||
return !diagramEngine.isModelLocked(item);
|
||||
})
|
||||
|
||||
this.selectionModels = selectedItems.map((item: PointModel | NodeModel) => {
|
||||
return {
|
||||
model: item,
|
||||
initialX: item.x,
|
||||
@@ -75,7 +82,9 @@ class MoveItemsAction extends BaseAction{
|
||||
|
||||
export interface DiagramProps {
|
||||
diagramEngine:DiagramEngine;
|
||||
onLinkStateChanged?: any;
|
||||
allowLooseLinks?: boolean;
|
||||
allowCanvasTranslation?: boolean;
|
||||
allowCanvasZoom?: boolean;
|
||||
}
|
||||
|
||||
export interface DiagramState {
|
||||
@@ -88,6 +97,13 @@ export interface DiagramState {
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export class DiagramWidget extends React.Component<DiagramProps, DiagramState> {
|
||||
|
||||
public static defaultProps: DiagramProps = {
|
||||
diagramEngine:null,
|
||||
allowLooseLinks: true,
|
||||
allowCanvasTranslations: true,
|
||||
allowCanvasZoom: true
|
||||
};
|
||||
|
||||
constructor(props: DiagramProps) {
|
||||
super(props);
|
||||
@@ -133,7 +149,11 @@ export class DiagramWidget extends React.Component<DiagramProps, DiagramState> {
|
||||
//delete all selected
|
||||
if(event.keyCode === 46 || event.keyCode === 8){
|
||||
_.forEach(this.props.diagramEngine.getDiagramModel().getSelectedItems(),(element) => {
|
||||
element.remove();
|
||||
|
||||
//only delete items which are not locked
|
||||
if (!this.props.diagramEngine.isModelLocked(element)){
|
||||
element.remove();
|
||||
}
|
||||
});
|
||||
this.forceUpdate();
|
||||
}
|
||||
@@ -145,7 +165,7 @@ export class DiagramWidget extends React.Component<DiagramProps, DiagramState> {
|
||||
/**
|
||||
* Gets a model and element under the mouse cursor
|
||||
*/
|
||||
getMouseElement(event): {model: BaseModel, element: Element}{
|
||||
getMouseElement(event): {model: BaseModel<BaseModelListener>, element: Element}{
|
||||
var target = event.target as Element;
|
||||
var diagramModel = this.props.diagramEngine.diagramModel;
|
||||
|
||||
@@ -199,11 +219,13 @@ export class DiagramWidget extends React.Component<DiagramProps, DiagramState> {
|
||||
ref:'canvas',
|
||||
className:'storm-diagrams-canvas',
|
||||
onWheel: (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
diagramModel.setZoomLevel(diagramModel.getZoomLevel()+(event.deltaY/60));
|
||||
diagramEngine.enableRepaintEntities([]);
|
||||
this.forceUpdate();
|
||||
if (this.props.allowCanvasZoom){
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
diagramModel.setZoomLevel(diagramModel.getZoomLevel()+(event.deltaY/60));
|
||||
diagramEngine.enableRepaintEntities([]);
|
||||
this.forceUpdate();
|
||||
}
|
||||
},
|
||||
onMouseMove: (event) => {
|
||||
|
||||
@@ -252,18 +274,21 @@ export class DiagramWidget extends React.Component<DiagramProps, DiagramState> {
|
||||
|
||||
//translate the actual canvas
|
||||
else if (this.state.action instanceof MoveCanvasAction){
|
||||
diagramModel.setOffset(
|
||||
this.state.action.initialOffsetX + ((event.pageX - this.state.action.mouseX) / (diagramModel.getZoomLevel()/100)),
|
||||
this.state.action.initialOffsetY+((event.pageY-this.state.action.mouseY)/(diagramModel.getZoomLevel()/100))
|
||||
);
|
||||
this.forceUpdate();
|
||||
if (this.props.allowCanvasTranslation){
|
||||
diagramModel.setOffset(
|
||||
this.state.action.initialOffsetX + ((event.pageX - this.state.action.mouseX) / (diagramModel.getZoomLevel()/100)),
|
||||
this.state.action.initialOffsetY+((event.pageY-this.state.action.mouseY)/(diagramModel.getZoomLevel()/100))
|
||||
);
|
||||
this.forceUpdate();
|
||||
}
|
||||
}
|
||||
},
|
||||
onMouseDown: (event) =>{
|
||||
diagramEngine.clearRepaintEntities();
|
||||
|
||||
var model = this.getMouseElement(event);
|
||||
//its the canvas
|
||||
|
||||
//the canvas was selected
|
||||
if(model === null){
|
||||
//is it a multiple selection
|
||||
if (event.shiftKey){
|
||||
@@ -320,7 +345,23 @@ export class DiagramWidget extends React.Component<DiagramProps, DiagramState> {
|
||||
//are we going to connect a link to something?
|
||||
if (this.state.action instanceof MoveItemsAction){
|
||||
var element = this.getMouseElement(event);
|
||||
if(element){
|
||||
var linkConnected = false;
|
||||
_.forEach(this.state.action.selectionModels,(model) => {
|
||||
|
||||
//only care about points connecting to things
|
||||
if (!(model.model instanceof PointModel)){
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.model instanceof PortModel){
|
||||
linkConnected = true;
|
||||
let link = model.model.getLink();
|
||||
link.setTargetPort(element.model);
|
||||
}
|
||||
});
|
||||
|
||||
//do we want to allow loose links on the diagram model or not
|
||||
if (!linkConnected && !this.props.allowLooseLinks){
|
||||
_.forEach(this.state.action.selectionModels,(model) => {
|
||||
|
||||
//only care about points connecting to things
|
||||
@@ -328,27 +369,11 @@ export class DiagramWidget extends React.Component<DiagramProps, DiagramState> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.model instanceof PortModel){
|
||||
let link = model.model.getLink();
|
||||
link.setTargetPort(element.model);
|
||||
if(this.props.onLinkStateChanged && typeof this.props.onLinkStateChanged === 'function'){
|
||||
this.props.onLinkStateChanged(link, true);
|
||||
}
|
||||
var link = model.model.getLink();
|
||||
if(link.isLastPoint(model.model)){
|
||||
link.remove();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('unwired');
|
||||
// it does not work :(
|
||||
// _.forEach(this.state.action.selectionModels,(model) => {
|
||||
// //only care about points connecting to things
|
||||
// if (!(model.model instanceof PointModel)){
|
||||
// return;
|
||||
// }
|
||||
// let link = model.model.getLink();
|
||||
// if (this.props.onLinkStateChanged && typeof this.props.onLinkStateChanged === 'function') {
|
||||
// this.props.onLinkStateChanged(link, false);
|
||||
// }
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ module.exports = [
|
||||
'demo1/bundle.js': './demos/demo1/index.ts',
|
||||
'demo2/bundle.js': './demos/demo2/index.ts',
|
||||
'demo3/bundle.js': './demos/demo3/index.ts',
|
||||
'demo4/bundle.js': './demos/demo4/index.ts',
|
||||
},
|
||||
output: {
|
||||
filename: '[name]',
|
||||
|
||||
Reference in New Issue
Block a user