mirror of
https://github.com/projectstorm/react-diagrams.git
synced 2025-08-26 16:01:30 +08:00
Added a new demo showing off flow diagram usage:
- PortModel can now decide whether or not a link should be allowed (E.g. only allowing outputs to connect to inputs) - PortModel now has an optional maximum number of links - When set to 1 an existing link is returned by createLinkModel and when set to another finite number null will be returned when the maximum is reached - LinkModel has been updated to support the resetting of existing links (I.e. removing ports and removing mid-points) - DiagramWidget has been updated to handle null being returned by createLinkModel as well as an existing link (this also supports an existing link where the link's target port should now be the source port) - DiagramWidget has been updated to respect the PortModel's new canLinkToPort method - DiagramWidget has been updated to disallow duplicate links
This commit is contained in:
@ -99,12 +99,16 @@ export default () => {
|
|||||||
model.addNode(node4);
|
model.addNode(node4);
|
||||||
|
|
||||||
var link1 = node1.getOutPorts()[0].createLinkModel();
|
var link1 = node1.getOutPorts()[0].createLinkModel();
|
||||||
link1.setTargetPort(port3);
|
if (link1) {
|
||||||
model.addLink(link1);
|
link1.setTargetPort(port3);
|
||||||
|
model.addLink(link1);
|
||||||
|
}
|
||||||
|
|
||||||
var link2 = node1.getOutPorts()[1].createLinkModel();
|
var link2 = node1.getOutPorts()[1].createLinkModel();
|
||||||
link2.setTargetPort(port4);
|
if (link2) {
|
||||||
model.addLink(link2);
|
link2.setTargetPort(port4);
|
||||||
|
model.addLink(link2);
|
||||||
|
}
|
||||||
|
|
||||||
// load model into engine
|
// load model into engine
|
||||||
engine.setDiagramModel(model);
|
engine.setDiagramModel(model);
|
||||||
|
53
demos/demo-simple-flow/index.tsx
Normal file
53
demos/demo-simple-flow/index.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import {
|
||||||
|
DiagramEngine,
|
||||||
|
DiagramModel,
|
||||||
|
DefaultNodeModel,
|
||||||
|
LinkModel,
|
||||||
|
DefaultPortModel,
|
||||||
|
DiagramWidget
|
||||||
|
} from "../../src/main";
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
//1) setup the diagram engine
|
||||||
|
var engine = new DiagramEngine();
|
||||||
|
engine.installDefaultFactories();
|
||||||
|
|
||||||
|
//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.addPort(new DefaultPortModel(false, "out-1", "OUT"));
|
||||||
|
node1.x = 100;
|
||||||
|
node1.y = 100;
|
||||||
|
|
||||||
|
//3-B) create another default node
|
||||||
|
var node2 = new DefaultNodeModel("Node 2", "rgb(192,255,0)");
|
||||||
|
var port2 = node2.addPort(new DefaultPortModel(true, "in-1", "IN"));
|
||||||
|
node2.x = 400;
|
||||||
|
node2.y = 100;
|
||||||
|
|
||||||
|
//3-C) link the 2 nodes together
|
||||||
|
var link1 = new LinkModel();
|
||||||
|
link1.setSourcePort(port1);
|
||||||
|
link1.setTargetPort(port2);
|
||||||
|
|
||||||
|
//3-D) create an orphaned node
|
||||||
|
var node3 = new DefaultNodeModel("Node 3", "rgb(0,192,255)");
|
||||||
|
var port3 = node3.addPort(new DefaultPortModel(false, "out-1", "OUT"));
|
||||||
|
node3.x = 100;
|
||||||
|
node3.y = 200;
|
||||||
|
|
||||||
|
//4) add the models to the root graph
|
||||||
|
model.addNode(node1);
|
||||||
|
model.addNode(node2);
|
||||||
|
model.addNode(node3);
|
||||||
|
model.addLink(link1);
|
||||||
|
|
||||||
|
//5) load model into engine
|
||||||
|
engine.setDiagramModel(model);
|
||||||
|
|
||||||
|
//6) render the diagram!
|
||||||
|
return <DiagramWidget diagramEngine={engine} allowLooseLinks={false} />;
|
||||||
|
};
|
@ -35,6 +35,10 @@ storiesOf("Simple Usage", module)
|
|||||||
require("./demo-simple/docs.md")
|
require("./demo-simple/docs.md")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.add(
|
||||||
|
"Simple flow example",
|
||||||
|
Helper.makeDemo(require("./demo-simple-flow/index").default(), require("!!raw-loader!./demo-simple-flow/index"))
|
||||||
|
)
|
||||||
.add(
|
.add(
|
||||||
"Performance demo",
|
"Performance demo",
|
||||||
Helper.makeDemo(require("./demo-performance/index").default(), require("!!raw-loader!./demo-performance/index"))
|
Helper.makeDemo(require("./demo-performance/index").default(), require("!!raw-loader!./demo-performance/index"))
|
||||||
|
@ -9,7 +9,7 @@ export class DefaultPortModel extends PortModel {
|
|||||||
label: string;
|
label: string;
|
||||||
|
|
||||||
constructor(isInput: boolean, name: string, label: string = null, id?: string) {
|
constructor(isInput: boolean, name: string, label: string = null, id?: string) {
|
||||||
super(name, "default", id);
|
super(name, "default", id, isInput ? null : 1);
|
||||||
this.in = isInput;
|
this.in = isInput;
|
||||||
this.label = label || name;
|
this.label = label || name;
|
||||||
}
|
}
|
||||||
@ -26,4 +26,8 @@ export class DefaultPortModel extends PortModel {
|
|||||||
label: this.label
|
label: this.label
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canLinkToPort(port: PortModel): boolean {
|
||||||
|
return port instanceof DefaultPortModel && this.in !== port.in;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,13 @@ export class LinkModel extends BaseModel<LinkModelListener> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setSourcePort(port: PortModel) {
|
setSourcePort(port: PortModel) {
|
||||||
port.addLink(this);
|
if (port !== null) {
|
||||||
|
port.addLink(this);
|
||||||
|
} else if (this.sourcePort !== null) {
|
||||||
|
this.sourcePort.removeLink(this);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.sourcePort = port;
|
this.sourcePort = port;
|
||||||
this.iterateListeners((listener: LinkModelListener, event) => {
|
this.iterateListeners((listener: LinkModelListener, event) => {
|
||||||
listener.sourcePortChanged && listener.sourcePortChanged({ ...event, port: port });
|
listener.sourcePortChanged && listener.sourcePortChanged({ ...event, port: port });
|
||||||
@ -138,7 +144,13 @@ export class LinkModel extends BaseModel<LinkModelListener> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTargetPort(port: PortModel) {
|
setTargetPort(port: PortModel) {
|
||||||
port.addLink(this);
|
if (port !== null) {
|
||||||
|
port.addLink(this);
|
||||||
|
} else if (this.targetPort !== null) {
|
||||||
|
this.targetPort.removeLink(this);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.targetPort = port;
|
this.targetPort = port;
|
||||||
this.iterateListeners((listener: LinkModelListener, event) => {
|
this.iterateListeners((listener: LinkModelListener, event) => {
|
||||||
listener.targetPortChanged && listener.targetPortChanged({ ...event, port: port });
|
listener.targetPortChanged && listener.targetPortChanged({ ...event, port: port });
|
||||||
@ -176,6 +188,12 @@ export class LinkModel extends BaseModel<LinkModelListener> {
|
|||||||
this.points.splice(this.getPointIndex(pointModel) + 1);
|
this.points.splice(this.getPointIndex(pointModel) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeMiddlePoints() {
|
||||||
|
if (this.points.length > 2) {
|
||||||
|
this.points.splice(0, this.points.length - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addPoint(pointModel: PointModel, index = 1) {
|
addPoint(pointModel: PointModel, index = 1) {
|
||||||
pointModel.link = this;
|
pointModel.link = this;
|
||||||
this.points.splice(index, 0, pointModel);
|
this.points.splice(index, 0, pointModel);
|
||||||
|
@ -7,17 +7,20 @@ export class PortModel extends BaseModel<BaseModelListener> {
|
|||||||
name: string;
|
name: string;
|
||||||
parentNode: NodeModel;
|
parentNode: NodeModel;
|
||||||
links: { [id: string]: LinkModel };
|
links: { [id: string]: LinkModel };
|
||||||
|
maximumLinks: number;
|
||||||
|
|
||||||
constructor(name: string, type?: string, id?: string) {
|
constructor(name: string, type?: string, id?: string, maximumLinks?: number) {
|
||||||
super(type, id);
|
super(type, id);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.links = {};
|
this.links = {};
|
||||||
this.parentNode = null;
|
this.parentNode = null;
|
||||||
|
this.maximumLinks = maximumLinks;
|
||||||
}
|
}
|
||||||
|
|
||||||
deSerialize(ob) {
|
deSerialize(ob) {
|
||||||
super.deSerialize(ob);
|
super.deSerialize(ob);
|
||||||
this.name = ob.name;
|
this.name = ob.name;
|
||||||
|
this.maximumLinks = ob.maximumLinks;
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize() {
|
serialize() {
|
||||||
@ -26,7 +29,8 @@ export class PortModel extends BaseModel<BaseModelListener> {
|
|||||||
parentNode: this.parentNode.id,
|
parentNode: this.parentNode.id,
|
||||||
links: _.map(this.links, link => {
|
links: _.map(this.links, link => {
|
||||||
return link.id;
|
return link.id;
|
||||||
})
|
}),
|
||||||
|
maximumLinks: this.maximumLinks
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +51,14 @@ export class PortModel extends BaseModel<BaseModelListener> {
|
|||||||
this.parentNode = node;
|
this.parentNode = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMaximumLinks(): number {
|
||||||
|
return this.maximumLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMaximumLinks(maximumLinks: number) {
|
||||||
|
this.maximumLinks = maximumLinks;
|
||||||
|
}
|
||||||
|
|
||||||
removeLink(link: LinkModel) {
|
removeLink(link: LinkModel) {
|
||||||
delete this.links[link.getID()];
|
delete this.links[link.getID()];
|
||||||
}
|
}
|
||||||
@ -60,11 +72,24 @@ export class PortModel extends BaseModel<BaseModelListener> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createLinkModel(): LinkModel | null {
|
createLinkModel(): LinkModel | null {
|
||||||
|
if (_.isFinite(this.maximumLinks)) {
|
||||||
|
var numberOfLinks: number = _.size(this.links);
|
||||||
|
if (this.maximumLinks === 1 && numberOfLinks >= 1) {
|
||||||
|
return _.values(this.links)[0];
|
||||||
|
} else if (numberOfLinks >= this.maximumLinks) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var linkModel = new LinkModel();
|
var linkModel = new LinkModel();
|
||||||
linkModel.setSourcePort(this);
|
linkModel.setSourcePort(this);
|
||||||
return linkModel;
|
return linkModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canLinkToPort(port: PortModel): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
isLocked() {
|
isLocked() {
|
||||||
return super.isLocked() || this.getParent().isLocked();
|
return super.isLocked() || this.getParent().isLocked();
|
||||||
}
|
}
|
||||||
|
@ -348,6 +348,29 @@ export class DiagramWidget extends React.Component<DiagramProps, DiagramState> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//remove any invalid links
|
||||||
|
_.forEach(this.state.action.selectionModels, model => {
|
||||||
|
//only care about points connecting to things
|
||||||
|
if (!(model.model instanceof PointModel)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var link = model.model.getLink();
|
||||||
|
var sourcePort: PortModel = link.getSourcePort();
|
||||||
|
var targetPort: PortModel = link.getTargetPort();
|
||||||
|
if (sourcePort !== null && targetPort !== null) {
|
||||||
|
if (!sourcePort.canLinkToPort(targetPort)) {
|
||||||
|
//link not allowed
|
||||||
|
link.remove();
|
||||||
|
}
|
||||||
|
else if (_.some(_.values(targetPort.getLinks()), (l: LinkModel) => l !== link && (l.getSourcePort() === sourcePort || l.getTargetPort() === sourcePort))) {
|
||||||
|
//link is a duplicate
|
||||||
|
link.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
diagramEngine.clearRepaintEntities();
|
diagramEngine.clearRepaintEntities();
|
||||||
this.stopFiringAction(!this.state.wasMoved);
|
this.stopFiringAction(!this.state.wasMoved);
|
||||||
} else {
|
} else {
|
||||||
@ -454,14 +477,22 @@ export class DiagramWidget extends React.Component<DiagramProps, DiagramState> {
|
|||||||
var sourcePort = model.model;
|
var sourcePort = model.model;
|
||||||
var link = sourcePort.createLinkModel();
|
var link = sourcePort.createLinkModel();
|
||||||
|
|
||||||
link.getFirstPoint().updateLocation(relative);
|
if (link) {
|
||||||
link.getLastPoint().updateLocation(relative);
|
link.removeMiddlePoints();
|
||||||
|
if (link.getSourcePort() !== sourcePort) {
|
||||||
|
link.setSourcePort(sourcePort);
|
||||||
|
}
|
||||||
|
link.setTargetPort(null);
|
||||||
|
|
||||||
diagramModel.clearSelection();
|
link.getFirstPoint().updateLocation(relative);
|
||||||
link.getLastPoint().setSelected(true);
|
link.getLastPoint().updateLocation(relative);
|
||||||
diagramModel.addLink(link);
|
|
||||||
|
|
||||||
this.startFiringAction(new MoveItemsAction(event.clientX, event.clientY, diagramEngine));
|
diagramModel.clearSelection();
|
||||||
|
link.getLastPoint().setSelected(true);
|
||||||
|
diagramModel.addLink(link);
|
||||||
|
|
||||||
|
this.startFiringAction(new MoveItemsAction(event.clientX, event.clientY, diagramEngine));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
diagramModel.clearSelection();
|
diagramModel.clearSelection();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user