mirror of
https://github.com/projectstorm/react-diagrams.git
synced 2025-08-14 16:51:29 +08:00
oh boy what have I done :O
This commit is contained in:
@ -1,12 +1,24 @@
|
||||
import { Toolkit } from "./Toolkit";
|
||||
|
||||
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export class BaseListener {
|
||||
lockChanged?(entity: BaseEntity<BaseListener>, locked: boolean): void;
|
||||
export interface BaseEvent<T extends BaseEntity = any> {
|
||||
entity: BaseEntity<BaseListener>;
|
||||
stopPropagation: () => any;
|
||||
firing: boolean;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export class BaseEntity<T extends BaseListener> {
|
||||
|
||||
export interface BaseListener<T extends BaseEntity = any> {
|
||||
|
||||
lockChanged?(event: BaseEvent<T> & {locked: boolean} ): void;
|
||||
|
||||
}
|
||||
|
||||
export class BaseEntity<T extends BaseListener = {}> {
|
||||
public listeners: { [s: string]: T };
|
||||
public id: string;
|
||||
public locked: boolean;
|
||||
@ -35,9 +47,21 @@ export class BaseEntity<T extends BaseListener> {
|
||||
};
|
||||
}
|
||||
|
||||
public iterateListeners(cb: (t: T) => any) {
|
||||
public iterateListeners(cb: (t: T, event: BaseEvent) => any) {
|
||||
let event: BaseEvent = {
|
||||
id: Toolkit.UID(),
|
||||
firing: true,
|
||||
entity: this,
|
||||
stopPropagation: () => {
|
||||
event.firing = false;
|
||||
}
|
||||
};
|
||||
for (var i in this.listeners) {
|
||||
cb(this.listeners[i]);
|
||||
// propagation stopped
|
||||
if(!event.firing){
|
||||
return;
|
||||
}
|
||||
cb(this.listeners[i], event);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,9 +85,9 @@ export class BaseEntity<T extends BaseListener> {
|
||||
|
||||
public setLocked(locked: boolean = true) {
|
||||
this.locked = locked;
|
||||
this.iterateListeners(listener => {
|
||||
this.iterateListeners((listener, event) => {
|
||||
if (listener.lockChanged) {
|
||||
listener.lockChanged(this, locked);
|
||||
listener.lockChanged({...event, locked: locked});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { DiagramModel } from "./DiagramModel";
|
||||
import { DiagramModel } from "./models/DiagramModel";
|
||||
import { DiagramEngine } from "./DiagramEngine";
|
||||
import { NodeModel, PointModel } from "./Common";
|
||||
import { SelectionModel } from "./widgets/DiagramWidget";
|
||||
import {PointModel} from "./models/PointModel";
|
||||
import {NodeModel} from "./models/NodeModel";
|
||||
|
||||
export class BaseAction {
|
||||
mouseX: number;
|
||||
|
441
src/Common.ts
441
src/Common.ts
@ -1,441 +0,0 @@
|
||||
import { BaseEntity, BaseListener } from "./BaseEntity";
|
||||
import * as _ from "lodash";
|
||||
import { port } from "_debugger";
|
||||
|
||||
export interface BaseModelListener extends BaseListener {
|
||||
selectionChanged?(item: BaseModel<BaseModelListener>, isSelected: boolean): void;
|
||||
|
||||
entityRemoved?(item: any): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export class BaseModel<T extends BaseModelListener> extends BaseEntity<BaseModelListener> {
|
||||
selected: boolean;
|
||||
|
||||
constructor(id?: string) {
|
||||
super(id);
|
||||
this.selected = false;
|
||||
}
|
||||
|
||||
getSelectedEntities(): BaseModel<T>[] {
|
||||
if (this.isSelected()) {
|
||||
return [this];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
deSerialize(ob) {
|
||||
super.deSerialize(ob);
|
||||
this.selected = ob.selected;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return _.merge(super.serialize(), {
|
||||
_class: this.constructor.name,
|
||||
selected: this.selected
|
||||
});
|
||||
}
|
||||
|
||||
public getID(): string {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public isSelected(): boolean {
|
||||
return this.selected;
|
||||
}
|
||||
|
||||
public setSelected(selected: boolean = true) {
|
||||
this.selected = selected;
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.selectionChanged) {
|
||||
listener.selectionChanged(this, selected);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.entityRemoved) {
|
||||
listener.entityRemoved(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class PointModel extends BaseModel<BaseModelListener> {
|
||||
x: number;
|
||||
y: number;
|
||||
link: LinkModel;
|
||||
|
||||
constructor(link: LinkModel, points: { x: number; y: number }) {
|
||||
super();
|
||||
this.x = points.x;
|
||||
this.y = points.y;
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
getSelectedEntities() {
|
||||
if (super.isSelected() && !this.isConnectedToPort()) {
|
||||
return [this];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
isConnectedToPort(): boolean {
|
||||
return this.link.getPortForPoint(this) !== null;
|
||||
}
|
||||
|
||||
deSerialize(ob) {
|
||||
super.deSerialize(ob);
|
||||
this.x = ob.x;
|
||||
this.y = ob.y;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return _.merge(super.serialize(), {
|
||||
x: this.x,
|
||||
y: this.y
|
||||
});
|
||||
}
|
||||
|
||||
remove() {
|
||||
//clear references
|
||||
if (this.link) {
|
||||
this.link.removePoint(this);
|
||||
}
|
||||
super.remove();
|
||||
}
|
||||
|
||||
updateLocation(points: { x: number; y: number }) {
|
||||
this.x = points.x;
|
||||
this.y = points.y;
|
||||
}
|
||||
|
||||
getX(): number {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
getY(): number {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
getLink(): LinkModel {
|
||||
return this.link;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
targetPort: PortModel | null;
|
||||
points: PointModel[];
|
||||
extras: {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.linkType = "default";
|
||||
this.points = [new PointModel(this, { x: 0, y: 0 }), new PointModel(this, { x: 0, y: 0 })];
|
||||
this.extras = {};
|
||||
this.sourcePort = null;
|
||||
this.targetPort = null;
|
||||
}
|
||||
|
||||
deSerialize(ob) {
|
||||
super.deSerialize(ob);
|
||||
this.linkType = ob.type;
|
||||
this.extras = ob.extras;
|
||||
this.points = _.map(ob.points, (point: { x; y }) => {
|
||||
var p = new PointModel(this, { x: point.x, y: point.y });
|
||||
p.deSerialize(point);
|
||||
return p;
|
||||
});
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return _.merge(super.serialize(), {
|
||||
type: this.linkType,
|
||||
source: this.sourcePort ? this.sourcePort.getParent().id : null,
|
||||
sourcePort: this.sourcePort ? this.sourcePort.id : null,
|
||||
target: this.targetPort ? this.targetPort.getParent().id : null,
|
||||
targetPort: this.targetPort ? this.targetPort.id : null,
|
||||
points: _.map(this.points, point => {
|
||||
return point.serialize();
|
||||
}),
|
||||
extras: this.extras
|
||||
});
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this.sourcePort) {
|
||||
this.sourcePort.removeLink(this);
|
||||
}
|
||||
if (this.targetPort) {
|
||||
this.targetPort.removeLink(this);
|
||||
}
|
||||
super.remove();
|
||||
}
|
||||
|
||||
isLastPoint(point: PointModel) {
|
||||
var index = this.getPointIndex(point);
|
||||
return index === this.points.length - 1;
|
||||
}
|
||||
|
||||
getPointIndex(point: PointModel) {
|
||||
return this.points.indexOf(point);
|
||||
}
|
||||
|
||||
getPointModel(id: string): PointModel | null {
|
||||
for (var i = 0; i < this.points.length; i++) {
|
||||
if (this.points[i].id === id) {
|
||||
return this.points[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getPortForPoint(point: PointModel): PortModel {
|
||||
if (this.sourcePort !== null && this.getFirstPoint().getID() === point.getID()) {
|
||||
return this.sourcePort;
|
||||
}
|
||||
if (this.targetPort !== null && this.getLastPoint().getID() === point.getID()) {
|
||||
return this.targetPort;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getPointForPort(port: PortModel): PointModel {
|
||||
if (this.sourcePort !== null && this.sourcePort.getID() === port.getID()) {
|
||||
return this.getFirstPoint();
|
||||
}
|
||||
if (this.targetPort !== null && this.targetPort.getID() === port.getID()) {
|
||||
return this.getLastPoint();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getFirstPoint(): PointModel {
|
||||
return this.points[0];
|
||||
}
|
||||
|
||||
getLastPoint(): PointModel {
|
||||
return this.points[this.points.length - 1];
|
||||
}
|
||||
|
||||
setSourcePort(port: PortModel) {
|
||||
port.addLink(this);
|
||||
this.sourcePort = port;
|
||||
this.iterateListeners((listener: LinkModelListener) => {
|
||||
listener.sourcePortChanged && listener.sourcePortChanged(this, port);
|
||||
});
|
||||
}
|
||||
|
||||
getSourcePort(): PortModel {
|
||||
return this.sourcePort;
|
||||
}
|
||||
|
||||
getTargetPort(): PortModel {
|
||||
return this.targetPort;
|
||||
}
|
||||
|
||||
setTargetPort(port: PortModel) {
|
||||
port.addLink(this);
|
||||
this.targetPort = port;
|
||||
this.iterateListeners((listener: LinkModelListener) => {
|
||||
listener.targetPortChanged && listener.targetPortChanged(this, port);
|
||||
});
|
||||
}
|
||||
|
||||
getPoints(): PointModel[] {
|
||||
return this.points;
|
||||
}
|
||||
|
||||
setPoints(points: PointModel[]) {
|
||||
this.points = points;
|
||||
}
|
||||
|
||||
removePoint(pointModel: PointModel) {
|
||||
this.points.splice(this.getPointIndex(pointModel), 1);
|
||||
}
|
||||
|
||||
addPoint(pointModel: PointModel, index = 1) {
|
||||
this.points.splice(index, 0, pointModel);
|
||||
}
|
||||
|
||||
getType(): string {
|
||||
return this.linkType;
|
||||
}
|
||||
}
|
||||
|
||||
export class PortModel extends BaseModel<BaseModelListener> {
|
||||
name: string;
|
||||
parentNode: NodeModel;
|
||||
links: { [id: string]: LinkModel };
|
||||
|
||||
deSerialize(ob) {
|
||||
super.deSerialize(ob);
|
||||
this.name = ob.name;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return _.merge(super.serialize(), {
|
||||
name: this.name,
|
||||
parentNode: this.parentNode.id,
|
||||
links: _.map(this.links, link => {
|
||||
return link.id;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
constructor(name: string, id?: string) {
|
||||
super(id);
|
||||
this.name = name;
|
||||
this.links = {};
|
||||
this.parentNode = null;
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
getParent(): NodeModel {
|
||||
return this.parentNode;
|
||||
}
|
||||
|
||||
setParentNode(node: NodeModel) {
|
||||
this.parentNode = node;
|
||||
}
|
||||
|
||||
removeLink(link: LinkModel) {
|
||||
delete this.links[link.getID()];
|
||||
}
|
||||
|
||||
addLink(link: LinkModel) {
|
||||
this.links[link.getID()] = link;
|
||||
}
|
||||
|
||||
getLinks(): { [id: string]: LinkModel } {
|
||||
return this.links;
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeModel extends BaseModel<BaseModelListener> {
|
||||
nodeType: string;
|
||||
x: number;
|
||||
y: number;
|
||||
extras: {};
|
||||
ports: { [s: string]: PortModel };
|
||||
|
||||
constructor(nodeType: string = "default", id?: string) {
|
||||
super(id);
|
||||
this.nodeType = nodeType;
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.extras = {};
|
||||
this.ports = {};
|
||||
}
|
||||
|
||||
setPosition(x, y){
|
||||
//store position
|
||||
let oldX = this.x;
|
||||
let oldY = this.y;
|
||||
|
||||
for(let port in this.ports){
|
||||
_.forEach(this.ports[port].getLinks(), (link) => {
|
||||
let point = link.getPointForPort(this.ports[port]);
|
||||
point.x = point.x + x - oldX;
|
||||
point.y = point.y + y - oldY;
|
||||
})
|
||||
}
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
getSelectedEntities() {
|
||||
let entities = super.getSelectedEntities();
|
||||
|
||||
// add the points of each link that are selected here
|
||||
if (this.isSelected()) {
|
||||
for (let portName in this.ports) {
|
||||
entities = entities.concat(
|
||||
_.map(this.ports[portName].getLinks(), link => {
|
||||
return link.getPointForPort(this.ports[portName]);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
deSerialize(ob) {
|
||||
super.deSerialize(ob);
|
||||
this.nodeType = ob.type;
|
||||
this.x = ob.x;
|
||||
this.y = ob.y;
|
||||
this.extras = ob.extras;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return _.merge(super.serialize(), {
|
||||
type: this.nodeType,
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
extras: this.extras,
|
||||
ports: _.map(this.ports, port => {
|
||||
return port.serialize();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
remove() {
|
||||
super.remove();
|
||||
for (var i in this.ports) {
|
||||
_.forEach(this.ports[i].getLinks(), link => {
|
||||
link.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getPortFromID(id): PortModel | null {
|
||||
for (var i in this.ports) {
|
||||
if (this.ports[i].id === id) {
|
||||
return this.ports[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getPort(name: string): PortModel | null {
|
||||
return this.ports[name];
|
||||
}
|
||||
|
||||
getPorts(): { [s: string]: PortModel } {
|
||||
return this.ports;
|
||||
}
|
||||
|
||||
removePort(port: PortModel) {
|
||||
//clear the parent node reference
|
||||
if (this.ports[port.name]) {
|
||||
this.ports[port.name].setParentNode(null);
|
||||
delete this.ports[port.name];
|
||||
}
|
||||
}
|
||||
|
||||
addPort(port: PortModel): PortModel {
|
||||
port.setParentNode(this);
|
||||
this.ports[port.name] = port;
|
||||
return port;
|
||||
}
|
||||
|
||||
getType(): string {
|
||||
return this.nodeType;
|
||||
}
|
||||
}
|
@ -1,9 +1,13 @@
|
||||
import { NodeWidgetFactory, LinkWidgetFactory } from "./WidgetFactories";
|
||||
import { LinkModel, NodeModel, BaseModel, BaseModelListener, PortModel, PointModel } from "./Common";
|
||||
import { BaseEntity, BaseListener } from "./BaseEntity";
|
||||
import { DiagramModel } from "./DiagramModel";
|
||||
import { DiagramModel } from "./models/DiagramModel";
|
||||
import { AbstractInstanceFactory } from "./AbstractInstanceFactory";
|
||||
import * as _ from "lodash";
|
||||
import {BaseModel, BaseModelListener} from "./models/BaseModel";
|
||||
import {NodeModel} from "./models/NodeModel";
|
||||
import {PointModel} from "./models/PointModel";
|
||||
import {PortModel} from "./models/PortModel";
|
||||
import {LinkModel} from "./models/LinkModel";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
|
3
src/Function.d.ts
vendored
3
src/Function.d.ts
vendored
@ -1,3 +0,0 @@
|
||||
interface Function {
|
||||
name: string;
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import { LinkModel } from "./Common";
|
||||
import * as _ from "lodash";
|
||||
import { AbstractInstanceFactory } from "./AbstractInstanceFactory";
|
||||
import {LinkModel} from "./models/LinkModel";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { NodeModel, LinkModel } from "./Common";
|
||||
import { DiagramEngine } from "./DiagramEngine";
|
||||
import {NodeModel} from "./models/NodeModel";
|
||||
import {LinkModel} from "./models/LinkModel";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { LinkWidgetFactory } from "../WidgetFactories";
|
||||
import { LinkModel } from "../Common";
|
||||
import * as React from "react";
|
||||
import { DefaultLinkWidget } from "./DefaultLinkWidget";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import {LinkModel} from "../models/LinkModel";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import { LinkModel, PointModel } from "../Common";
|
||||
import * as _ from "lodash";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import {LinkModel} from "../models/LinkModel";
|
||||
import {PointModel} from "../models/PointModel";
|
||||
|
||||
export interface DefaultLinkProps {
|
||||
color?: string;
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { NodeModel } from "../Common";
|
||||
import { DefaultPortModel } from "./DefaultPortModel";
|
||||
import * as _ from "lodash";
|
||||
|
||||
import { AbstractInstanceFactory } from "../AbstractInstanceFactory";
|
||||
import {NodeModel} from "../models/NodeModel";
|
||||
|
||||
export class DefaultNodeInstanceFactory extends AbstractInstanceFactory<DefaultNodeModel> {
|
||||
constructor() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { PortModel } from "../Common";
|
||||
import * as _ from "lodash";
|
||||
import { AbstractInstanceFactory } from "../AbstractInstanceFactory";
|
||||
import {PortModel} from "../models/PortModel";
|
||||
|
||||
export class DefaultPortInstanceFactory extends AbstractInstanceFactory<DefaultPortModel> {
|
||||
constructor() {
|
||||
|
10
src/main.ts
10
src/main.ts
@ -15,13 +15,19 @@ export * from "./WidgetFactories";
|
||||
export * from "./Toolkit";
|
||||
|
||||
export * from "./DiagramEngine";
|
||||
export * from "./DiagramModel";
|
||||
export * from "./models/DiagramModel";
|
||||
export * from "./BaseEntity";
|
||||
export * from "./CanvasActions";
|
||||
export * from "./Common";
|
||||
export * from "./AbstractInstanceFactory";
|
||||
export * from "./LinkInstanceFactory";
|
||||
|
||||
export * from "./models/BaseModel";
|
||||
export * from "./models/DiagramModel";
|
||||
export * from "./models/LinkModel";
|
||||
export * from "./models/NodeModel";
|
||||
export * from "./models/PointModel";
|
||||
export * from "./models/PortModel";
|
||||
|
||||
export * from "./widgets/DiagramWidget";
|
||||
export * from "./widgets/LinkLayerWidget";
|
||||
export * from "./widgets/LinkWidget";
|
||||
|
69
src/models/BaseModel.ts
Normal file
69
src/models/BaseModel.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { BaseEntity, BaseListener } from "../BaseEntity";
|
||||
import * as _ from "lodash";
|
||||
import {BaseEvent} from "../BaseEntity";
|
||||
|
||||
export interface BaseModelListener extends BaseListener {
|
||||
|
||||
selectionChanged?(event: BaseEvent<BaseModel> & {isSelected: boolean}): void;
|
||||
|
||||
entityRemoved?(event: BaseEvent<BaseModel>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*/
|
||||
export class BaseModel<T extends BaseModelListener = BaseModelListener> extends BaseEntity<BaseModelListener> {
|
||||
|
||||
selected: boolean;
|
||||
class: string;
|
||||
|
||||
constructor(id?: string) {
|
||||
super(id);
|
||||
this.selected = false;
|
||||
this.class = this.constructor.name;
|
||||
}
|
||||
|
||||
public getSelectedEntities(): BaseModel<T>[] {
|
||||
if (this.isSelected()) {
|
||||
return [this];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public deSerialize(ob) {
|
||||
super.deSerialize(ob);
|
||||
this.selected = ob.selected;
|
||||
}
|
||||
|
||||
public serialize() {
|
||||
return _.merge(super.serialize(), {
|
||||
_class: this.class,
|
||||
selected: this.selected
|
||||
});
|
||||
}
|
||||
|
||||
public getID(): string {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public isSelected(): boolean {
|
||||
return this.selected;
|
||||
}
|
||||
|
||||
public setSelected(selected: boolean = true) {
|
||||
this.selected = selected;
|
||||
this.iterateListeners((listener, event) => {
|
||||
if (listener.selectionChanged) {
|
||||
listener.selectionChanged({...event, isSelected: selected});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public remove() {
|
||||
this.iterateListeners((listener, event) => {
|
||||
if (listener.entityRemoved) {
|
||||
listener.entityRemoved(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,26 +1,27 @@
|
||||
import { LinkModel, NodeModel, BaseModel, BaseModelListener, PortModel } from "./Common";
|
||||
import { BaseListener, BaseEntity } from "./BaseEntity";
|
||||
import {BaseListener, BaseEntity, BaseEvent} from "../BaseEntity";
|
||||
import * as _ from "lodash";
|
||||
import { DiagramEngine } from "./DiagramEngine";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import {LinkModel} from "./LinkModel";
|
||||
import {NodeModel} from "./NodeModel";
|
||||
import {PortModel} from "./PortModel";
|
||||
import {BaseModel, BaseModelListener} from "./BaseModel";
|
||||
/**
|
||||
* @author Dylan Vorster
|
||||
*
|
||||
*/
|
||||
export interface DiagramListener extends BaseListener {
|
||||
nodesUpdated?(node: any, isCreated: boolean): void;
|
||||
|
||||
linksUpdated?(link: any, isCreated: boolean): void;
|
||||
nodesUpdated?(event: BaseEvent & { node: NodeModel, isCreated: boolean }): void;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
controlsUpdated?(): void;
|
||||
linksUpdated?(event: BaseEvent & { link: LinkModel, isCreated: boolean }): void;
|
||||
|
||||
offsetUpdated?(model: DiagramModel, offsetX: number, offsetY: number): void;
|
||||
offsetUpdated?(event: BaseEvent<DiagramModel> & { offsetX: number, offsetY: number}): void;
|
||||
|
||||
zoomUpdated?(event: BaseEvent<DiagramModel> & { zoom: number }): void;
|
||||
|
||||
gridUpdated?(event: BaseEvent<DiagramModel> & { size: number }): void;
|
||||
|
||||
zoomUpdated?(model: DiagramModel, zoom: number): void;
|
||||
|
||||
gridUpdated?(model: DiagramModel, size: number): void;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@ -52,8 +53,8 @@ export class DiagramModel extends BaseEntity<DiagramListener> {
|
||||
|
||||
setGridSize(size: number = 0) {
|
||||
this.gridSize = size;
|
||||
this.iterateListeners(listener => {
|
||||
listener.gridUpdated && listener.gridUpdated(this, size);
|
||||
this.iterateListeners((listener, event) => {
|
||||
listener.gridUpdated && listener.gridUpdated({...event, size: size});
|
||||
});
|
||||
}
|
||||
|
||||
@ -157,41 +158,31 @@ export class DiagramModel extends BaseEntity<DiagramListener> {
|
||||
|
||||
setZoomLevel(zoom: number) {
|
||||
this.zoom = zoom;
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.controlsUpdated) listener.controlsUpdated();
|
||||
});
|
||||
this.iterateListeners(listener => {
|
||||
listener.zoomUpdated && listener.zoomUpdated(this, this.zoom);
|
||||
|
||||
this.iterateListeners((listener, event) => {
|
||||
listener.zoomUpdated && listener.zoomUpdated({...event, zoom: zoom});
|
||||
});
|
||||
}
|
||||
|
||||
setOffset(offsetX: number, offsetY: number) {
|
||||
this.offsetX = offsetX;
|
||||
this.offsetY = offsetY;
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.controlsUpdated) listener.controlsUpdated();
|
||||
});
|
||||
this.iterateListeners(listener => {
|
||||
listener.offsetUpdated && listener.offsetUpdated(this, this.offsetX, this.offsetY);
|
||||
this.iterateListeners((listener, event) => {
|
||||
listener.offsetUpdated && listener.offsetUpdated({...event, offsetX: offsetX, offsetY: offsetY});
|
||||
});
|
||||
}
|
||||
|
||||
setOffsetX(offsetX: number) {
|
||||
this.offsetX = offsetX;
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.controlsUpdated) listener.controlsUpdated();
|
||||
});
|
||||
this.iterateListeners(listener => {
|
||||
listener.offsetUpdated && listener.offsetUpdated(this, this.offsetX, this.offsetY);
|
||||
this.iterateListeners((listener, event) => {
|
||||
listener.offsetUpdated && listener.offsetUpdated({...event, offsetX: offsetX, offsetY: this.offsetY});
|
||||
});
|
||||
}
|
||||
setOffsetY(offsetY: number) {
|
||||
this.offsetY = offsetY;
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.controlsUpdated) listener.controlsUpdated();
|
||||
});
|
||||
this.iterateListeners(listener => {
|
||||
listener.offsetUpdated && listener.offsetUpdated(this, this.offsetX, this.offsetY);
|
||||
|
||||
this.iterateListeners((listener, event) => {
|
||||
listener.offsetUpdated && listener.offsetUpdated({...event, offsetX: this.offsetX, offsetY: this.offsetY});
|
||||
});
|
||||
}
|
||||
|
||||
@ -234,8 +225,8 @@ export class DiagramModel extends BaseEntity<DiagramListener> {
|
||||
}
|
||||
});
|
||||
this.links[link.getID()] = link;
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.linksUpdated) listener.linksUpdated(link, true);
|
||||
this.iterateListeners((listener, event) => {
|
||||
listener.linksUpdated && listener.linksUpdated({...event,link: link, isCreated: true});
|
||||
});
|
||||
return link;
|
||||
}
|
||||
@ -247,37 +238,25 @@ export class DiagramModel extends BaseEntity<DiagramListener> {
|
||||
}
|
||||
});
|
||||
this.nodes[node.getID()] = node;
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.nodesUpdated) listener.nodesUpdated(node, true);
|
||||
this.iterateListeners((listener, event) => {
|
||||
listener.nodesUpdated && listener.nodesUpdated({...event,node: node, isCreated: true});
|
||||
});
|
||||
return node;
|
||||
}
|
||||
|
||||
removeLink(link: LinkModel | string) {
|
||||
if (link instanceof LinkModel) {
|
||||
delete this.links[link.getID()];
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.linksUpdated) listener.linksUpdated(link, false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
delete this.links["" + link];
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.linksUpdated) listener.linksUpdated(link, false);
|
||||
link = this.getLink(link);
|
||||
delete this.links[link.getID()];
|
||||
this.iterateListeners((listener, event) => {
|
||||
listener.linksUpdated && listener.linksUpdated({...event, link: link as LinkModel, isCreated: false});
|
||||
});
|
||||
}
|
||||
removeNode(node: NodeModel | string) {
|
||||
if (node instanceof NodeModel) {
|
||||
delete this.nodes[node.getID()];
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.nodesUpdated) listener.nodesUpdated(node, false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
delete this.nodes["" + node];
|
||||
this.iterateListeners(listener => {
|
||||
if (listener.nodesUpdated) listener.nodesUpdated(node, false);
|
||||
removeNode(node: NodeModel | string) {
|
||||
node = this.getNode(node);
|
||||
delete this.nodes[node.getID()];
|
||||
this.iterateListeners((listener, event) => {
|
||||
listener.nodesUpdated && listener.nodesUpdated({...event, node: node as NodeModel, isCreated: false});
|
||||
});
|
||||
}
|
||||
|
154
src/models/LinkModel.ts
Normal file
154
src/models/LinkModel.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import {BaseModel, BaseModelListener} from "./BaseModel";
|
||||
import {PortModel} from "./PortModel";
|
||||
import {PointModel} from "./PointModel";
|
||||
import * as _ from "lodash";
|
||||
import {BaseEvent} from "../BaseEntity";
|
||||
|
||||
export interface LinkModelListener extends BaseModelListener {
|
||||
|
||||
sourcePortChanged?(event: BaseEvent<LinkModel> & {port: null | PortModel}): void;
|
||||
|
||||
targetPortChanged?(event: BaseEvent<LinkModel> & {port: null | PortModel}): void;
|
||||
}
|
||||
|
||||
export class LinkModel extends BaseModel<LinkModelListener> {
|
||||
linkType: string;
|
||||
sourcePort: PortModel | null;
|
||||
targetPort: PortModel | null;
|
||||
points: PointModel[];
|
||||
extras: {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.linkType = "default";
|
||||
this.points = [new PointModel(this, { x: 0, y: 0 }), new PointModel(this, { x: 0, y: 0 })];
|
||||
this.extras = {};
|
||||
this.sourcePort = null;
|
||||
this.targetPort = null;
|
||||
}
|
||||
|
||||
deSerialize(ob) {
|
||||
super.deSerialize(ob);
|
||||
this.linkType = ob.type;
|
||||
this.extras = ob.extras;
|
||||
this.points = _.map(ob.points, (point: { x; y }) => {
|
||||
var p = new PointModel(this, { x: point.x, y: point.y });
|
||||
p.deSerialize(point);
|
||||
return p;
|
||||
});
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return _.merge(super.serialize(), {
|
||||
type: this.linkType,
|
||||
source: this.sourcePort ? this.sourcePort.getParent().id : null,
|
||||
sourcePort: this.sourcePort ? this.sourcePort.id : null,
|
||||
target: this.targetPort ? this.targetPort.getParent().id : null,
|
||||
targetPort: this.targetPort ? this.targetPort.id : null,
|
||||
points: _.map(this.points, point => {
|
||||
return point.serialize();
|
||||
}),
|
||||
extras: this.extras
|
||||
});
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this.sourcePort) {
|
||||
this.sourcePort.removeLink(this);
|
||||
}
|
||||
if (this.targetPort) {
|
||||
this.targetPort.removeLink(this);
|
||||
}
|
||||
super.remove();
|
||||
}
|
||||
|
||||
isLastPoint(point: PointModel) {
|
||||
var index = this.getPointIndex(point);
|
||||
return index === this.points.length - 1;
|
||||
}
|
||||
|
||||
getPointIndex(point: PointModel) {
|
||||
return this.points.indexOf(point);
|
||||
}
|
||||
|
||||
getPointModel(id: string): PointModel | null {
|
||||
for (var i = 0; i < this.points.length; i++) {
|
||||
if (this.points[i].id === id) {
|
||||
return this.points[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getPortForPoint(point: PointModel): PortModel {
|
||||
if (this.sourcePort !== null && this.getFirstPoint().getID() === point.getID()) {
|
||||
return this.sourcePort;
|
||||
}
|
||||
if (this.targetPort !== null && this.getLastPoint().getID() === point.getID()) {
|
||||
return this.targetPort;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getPointForPort(port: PortModel): PointModel {
|
||||
if (this.sourcePort !== null && this.sourcePort.getID() === port.getID()) {
|
||||
return this.getFirstPoint();
|
||||
}
|
||||
if (this.targetPort !== null && this.targetPort.getID() === port.getID()) {
|
||||
return this.getLastPoint();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getFirstPoint(): PointModel {
|
||||
return this.points[0];
|
||||
}
|
||||
|
||||
getLastPoint(): PointModel {
|
||||
return this.points[this.points.length - 1];
|
||||
}
|
||||
|
||||
setSourcePort(port: PortModel) {
|
||||
port.addLink(this);
|
||||
this.sourcePort = port;
|
||||
this.iterateListeners((listener: LinkModelListener, event) => {
|
||||
listener.sourcePortChanged && listener.sourcePortChanged({...event, port: port});
|
||||
});
|
||||
}
|
||||
|
||||
getSourcePort(): PortModel {
|
||||
return this.sourcePort;
|
||||
}
|
||||
|
||||
getTargetPort(): PortModel {
|
||||
return this.targetPort;
|
||||
}
|
||||
|
||||
setTargetPort(port: PortModel) {
|
||||
port.addLink(this);
|
||||
this.targetPort = port;
|
||||
this.iterateListeners((listener: LinkModelListener, event) => {
|
||||
listener.targetPortChanged && listener.targetPortChanged({...event, port: port});
|
||||
});
|
||||
}
|
||||
|
||||
getPoints(): PointModel[] {
|
||||
return this.points;
|
||||
}
|
||||
|
||||
setPoints(points: PointModel[]) {
|
||||
this.points = points;
|
||||
}
|
||||
|
||||
removePoint(pointModel: PointModel) {
|
||||
this.points.splice(this.getPointIndex(pointModel), 1);
|
||||
}
|
||||
|
||||
addPoint(pointModel: PointModel, index = 1) {
|
||||
this.points.splice(index, 0, pointModel);
|
||||
}
|
||||
|
||||
getType(): string {
|
||||
return this.linkType;
|
||||
}
|
||||
}
|
117
src/models/NodeModel.ts
Normal file
117
src/models/NodeModel.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import {BaseModel, BaseModelListener} from "./BaseModel";
|
||||
import {PortModel} from "./PortModel";
|
||||
import * as _ from "lodash";
|
||||
|
||||
export class NodeModel extends BaseModel<BaseModelListener> {
|
||||
nodeType: string;
|
||||
x: number;
|
||||
y: number;
|
||||
extras: {};
|
||||
ports: { [s: string]: PortModel };
|
||||
|
||||
constructor(nodeType: string = "default", id?: string) {
|
||||
super(id);
|
||||
this.nodeType = nodeType;
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.extras = {};
|
||||
this.ports = {};
|
||||
}
|
||||
|
||||
setPosition(x, y){
|
||||
//store position
|
||||
let oldX = this.x;
|
||||
let oldY = this.y;
|
||||
|
||||
for(let port in this.ports){
|
||||
_.forEach(this.ports[port].getLinks(), (link) => {
|
||||
let point = link.getPointForPort(this.ports[port]);
|
||||
point.x = point.x + x - oldX;
|
||||
point.y = point.y + y - oldY;
|
||||
})
|
||||
}
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
getSelectedEntities() {
|
||||
let entities = super.getSelectedEntities();
|
||||
|
||||
// add the points of each link that are selected here
|
||||
if (this.isSelected()) {
|
||||
for (let portName in this.ports) {
|
||||
entities = entities.concat(
|
||||
_.map(this.ports[portName].getLinks(), link => {
|
||||
return link.getPointForPort(this.ports[portName]);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
deSerialize(ob) {
|
||||
super.deSerialize(ob);
|
||||
this.nodeType = ob.type;
|
||||
this.x = ob.x;
|
||||
this.y = ob.y;
|
||||
this.extras = ob.extras;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return _.merge(super.serialize(), {
|
||||
type: this.nodeType,
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
extras: this.extras,
|
||||
ports: _.map(this.ports, port => {
|
||||
return port.serialize();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
remove() {
|
||||
super.remove();
|
||||
for (var i in this.ports) {
|
||||
_.forEach(this.ports[i].getLinks(), link => {
|
||||
link.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getPortFromID(id): PortModel | null {
|
||||
for (var i in this.ports) {
|
||||
if (this.ports[i].id === id) {
|
||||
return this.ports[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getPort(name: string): PortModel | null {
|
||||
return this.ports[name];
|
||||
}
|
||||
|
||||
getPorts(): { [s: string]: PortModel } {
|
||||
return this.ports;
|
||||
}
|
||||
|
||||
removePort(port: PortModel) {
|
||||
//clear the parent node reference
|
||||
if (this.ports[port.name]) {
|
||||
this.ports[port.name].setParentNode(null);
|
||||
delete this.ports[port.name];
|
||||
}
|
||||
}
|
||||
|
||||
addPort(port: PortModel): PortModel {
|
||||
port.setParentNode(this);
|
||||
this.ports[port.name] = port;
|
||||
return port;
|
||||
}
|
||||
|
||||
getType(): string {
|
||||
return this.nodeType;
|
||||
}
|
||||
}
|
65
src/models/PointModel.ts
Normal file
65
src/models/PointModel.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import {BaseModel, BaseModelListener} from "./BaseModel";
|
||||
import {LinkModel} from "./LinkModel";
|
||||
import * as _ from "lodash";
|
||||
|
||||
export class PointModel extends BaseModel<BaseModelListener> {
|
||||
x: number;
|
||||
y: number;
|
||||
link: LinkModel;
|
||||
|
||||
constructor(link: LinkModel, points: { x: number; y: number }) {
|
||||
super();
|
||||
this.x = points.x;
|
||||
this.y = points.y;
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
getSelectedEntities() {
|
||||
if (super.isSelected() && !this.isConnectedToPort()) {
|
||||
return [this];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
isConnectedToPort(): boolean {
|
||||
return this.link.getPortForPoint(this) !== null;
|
||||
}
|
||||
|
||||
deSerialize(ob) {
|
||||
super.deSerialize(ob);
|
||||
this.x = ob.x;
|
||||
this.y = ob.y;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return _.merge(super.serialize(), {
|
||||
x: this.x,
|
||||
y: this.y
|
||||
});
|
||||
}
|
||||
|
||||
remove() {
|
||||
//clear references
|
||||
if (this.link) {
|
||||
this.link.removePoint(this);
|
||||
}
|
||||
super.remove();
|
||||
}
|
||||
|
||||
updateLocation(points: { x: number; y: number }) {
|
||||
this.x = points.x;
|
||||
this.y = points.y;
|
||||
}
|
||||
|
||||
getX(): number {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
getY(): number {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
getLink(): LinkModel {
|
||||
return this.link;
|
||||
}
|
||||
}
|
56
src/models/PortModel.ts
Normal file
56
src/models/PortModel.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import {BaseModel, BaseModelListener} from "./BaseModel";
|
||||
import {NodeModel} from "./NodeModel";
|
||||
import {LinkModel} from "./LinkModel";
|
||||
import * as _ from "lodash";
|
||||
|
||||
export class PortModel extends BaseModel<BaseModelListener> {
|
||||
name: string;
|
||||
parentNode: NodeModel;
|
||||
links: { [id: string]: LinkModel };
|
||||
|
||||
deSerialize(ob) {
|
||||
super.deSerialize(ob);
|
||||
this.name = ob.name;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return _.merge(super.serialize(), {
|
||||
name: this.name,
|
||||
parentNode: this.parentNode.id,
|
||||
links: _.map(this.links, link => {
|
||||
return link.id;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
constructor(name: string, id?: string) {
|
||||
super(id);
|
||||
this.name = name;
|
||||
this.links = {};
|
||||
this.parentNode = null;
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
getParent(): NodeModel {
|
||||
return this.parentNode;
|
||||
}
|
||||
|
||||
setParentNode(node: NodeModel) {
|
||||
this.parentNode = node;
|
||||
}
|
||||
|
||||
removeLink(link: LinkModel) {
|
||||
delete this.links[link.getID()];
|
||||
}
|
||||
|
||||
addLink(link: LinkModel) {
|
||||
this.links[link.getID()] = link;
|
||||
}
|
||||
|
||||
getLinks(): { [id: string]: LinkModel } {
|
||||
return this.links;
|
||||
}
|
||||
}
|
@ -1,11 +1,15 @@
|
||||
import * as React from "react";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import * as _ from "lodash";
|
||||
import { PointModel, NodeModel, BaseModel, BaseModelListener, LinkModel, PortModel } from "../Common";
|
||||
import { LinkLayerWidget } from "./LinkLayerWidget";
|
||||
import { NodeLayerWidget } from "./NodeLayerWidget";
|
||||
import { Toolkit } from "../Toolkit";
|
||||
import { BaseAction, MoveCanvasAction, MoveItemsAction, SelectingAction } from "../CanvasActions";
|
||||
import {NodeModel} from "../models/NodeModel";
|
||||
import {PointModel} from "../models/PointModel";
|
||||
import {PortModel} from "../models/PortModel";
|
||||
import {LinkModel} from "../models/LinkModel";
|
||||
import {BaseModel, BaseModelListener} from "../models/BaseModel";
|
||||
|
||||
export interface SelectionModel {
|
||||
model: BaseModel<BaseModelListener>;
|
||||
|
@ -1,9 +1,8 @@
|
||||
import * as React from "react";
|
||||
import { DiagramModel } from "../DiagramModel";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import { PointModel } from "../Common";
|
||||
import { LinkWidget } from "./LinkWidget";
|
||||
import * as _ from "lodash";
|
||||
import {PointModel} from "../models/PointModel";
|
||||
|
||||
export interface LinkLayerProps {
|
||||
diagramEngine: DiagramEngine;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { LinkModel } from "../Common";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import {LinkModel} from "../models/LinkModel";
|
||||
|
||||
export interface LinkProps {
|
||||
link: LinkModel;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { DiagramModel } from "../DiagramModel";
|
||||
import { DiagramModel } from "../models/DiagramModel";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import * as _ from "lodash";
|
||||
import { NodeWidget } from "./NodeWidget";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { NodeModel } from "../Common";
|
||||
import { DiagramEngine } from "../DiagramEngine";
|
||||
import {NodeModel} from "../models/NodeModel";
|
||||
|
||||
export interface NodeProps {
|
||||
node: NodeModel;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { NodeModel } from "../Common";
|
||||
import {NodeModel} from "../models/NodeModel";
|
||||
|
||||
export interface PortProps {
|
||||
name: string;
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user