make all the things pluggable

This commit is contained in:
Dylan Vorster
2019-07-28 18:13:03 +02:00
parent f4aa1852ed
commit 3a2d3ccb73
23 changed files with 296 additions and 179 deletions

View File

@@ -1,4 +1,9 @@
import { DiagramEngine } from '@projectstorm/react-diagrams-core';
import {
DiagramEngine,
MoveCanvasActionFactory,
MoveItemsActionFactory,
SelectingItemsActionFactory
} from '@projectstorm/react-diagrams-core';
import {
DefaultLabelFactory,
DefaultLinkFactory,
@@ -16,10 +21,17 @@ export * from '@projectstorm/react-diagrams-routing';
*/
export default (): DiagramEngine => {
const engine = new DiagramEngine();
// register model factories
engine.getLabelFactories().registerFactory(new DefaultLabelFactory());
engine.getNodeFactories().registerFactory(new DefaultNodeFactory());
engine.getNodeFactories().registerFactory(new DefaultNodeFactory() as any); // i cant figure out why
engine.getLinkFactories().registerFactory(new DefaultLinkFactory());
engine.getLinkFactories().registerFactory(new PathFindingLinkFactory());
engine.getPortFactories().registerFactory(new DefaultPortFactory());
// register the default interaction behaviours
engine.getActionFactories().registerFactory(new MoveCanvasActionFactory());
engine.getActionFactories().registerFactory(new SelectingItemsActionFactory());
engine.getActionFactories().registerFactory(new MoveItemsActionFactory());
return engine;
};

View File

@@ -1,13 +1,22 @@
import './sass/main.scss';
export * from './src/actions/BaseMouseAction';
export * from './src/actions/MoveCanvasAction';
export * from './src/actions/SelectingAction';
export * from './src/actions/MoveItemsAction';
export * from './src/core-actions/AbstractAction';
export * from './src/core-actions/AbstractActionFactory';
export * from './src/core-actions/AbstractMouseAction';
export * from './src/actions/move-canvas/MoveCanvasActionFactory';
export * from './src/actions/move-canvas/MoveCanvasAction';
export * from './src/actions/selecting-items/SelectingAction';
export * from './src/actions/selecting-items/SelectingItemsActionFactory';
export * from './src/actions/move-items/MoveItemsAction';
export * from './src/actions/move-items/MoveItemsActionFactory';
export * from './src/core/BaseObserver';
export * from './src/core/FactoryBank';
export * from './src/core/AbstractFactory';
export * from './src/core/AbstractModelFactory';
export * from './src/core/AbstractReactFactory';
export * from './src/core-models/BaseModel';

View File

@@ -8,12 +8,13 @@ import { PortModel } from './models/PortModel';
import { LinkModel } from './models/LinkModel';
import { LabelModel } from './models/LabelModel';
import { FactoryBank } from './core/FactoryBank';
import { AbstractFactory } from './core/AbstractFactory';
import { AbstractReactFactory } from './core/AbstractReactFactory';
import { BaseListener, BaseObserver } from './core/BaseObserver';
import { Point } from '@projectstorm/react-diagrams-geometry';
import { Toolkit } from './Toolkit';
import { MouseEvent } from 'react';
import { AbstractActionFactory } from './core-actions/AbstractActionFactory';
import { AbstractModelFactory } from './core/AbstractModelFactory';
export interface DiagramEngineListener extends BaseListener {
canvasReady?(): void;
@@ -29,8 +30,9 @@ export interface DiagramEngineListener extends BaseListener {
export class DiagramEngine extends BaseObserver<DiagramEngineListener> {
protected nodeFactories: FactoryBank<AbstractReactFactory<NodeModel>>;
protected linkFactories: FactoryBank<AbstractReactFactory<LinkModel>>;
protected portFactories: FactoryBank<AbstractFactory<PortModel>>;
protected portFactories: FactoryBank<AbstractModelFactory<PortModel>>;
protected labelFactories: FactoryBank<AbstractReactFactory<LabelModel>>;
protected actionFactories: FactoryBank<AbstractActionFactory>;
diagramModel: DiagramModel;
canvas: Element;
@@ -47,6 +49,7 @@ export class DiagramEngine extends BaseObserver<DiagramEngineListener> {
this.linkFactories = new FactoryBank();
this.portFactories = new FactoryBank();
this.labelFactories = new FactoryBank();
this.actionFactories = new FactoryBank();
const setup = (factory: FactoryBank) => {
factory.registerListener({
@@ -63,6 +66,7 @@ export class DiagramEngine extends BaseObserver<DiagramEngineListener> {
setup(this.linkFactories);
setup(this.portFactories);
setup(this.labelFactories);
setup(this.actionFactories);
this.canvas = null;
this.paintableWidgets = null;
@@ -194,22 +198,26 @@ export class DiagramEngine extends BaseObserver<DiagramEngineListener> {
//!-------------- FACTORIES ------------
getNodeFactories(): FactoryBank {
getNodeFactories() {
return this.nodeFactories;
}
getLinkFactories(): FactoryBank {
getLinkFactories() {
return this.linkFactories;
}
getLabelFactories(): FactoryBank {
getLabelFactories() {
return this.labelFactories;
}
getPortFactories(): FactoryBank {
getPortFactories() {
return this.portFactories;
}
getActionFactories() {
return this.actionFactories;
}
getFactoryForNode(node: NodeModel | string) {
if (typeof node === 'string') {
return this.nodeFactories.getFactory(node);

View File

@@ -1,22 +0,0 @@
import { DiagramModel } from '../models/DiagramModel';
import { MouseEvent } from 'react';
export abstract class BaseMouseAction {
mouseX: number;
mouseY: number;
ms: number;
model: DiagramModel;
constructor(mouseX: number, mouseY: number, model: DiagramModel) {
this.mouseX = mouseX;
this.mouseY = mouseY;
this.model = model;
this.ms = new Date().getTime();
}
abstract fireMouseDown(event: MouseEvent);
abstract fireMouseMove(event: MouseEvent);
abstract fireMouseUp(event: MouseEvent);
}

View File

@@ -1,15 +1,15 @@
import { BaseMouseAction } from './BaseMouseAction';
import { DiagramModel } from '../models/DiagramModel';
import { AbstractMouseAction } from '../../core-actions/AbstractMouseAction';
import { MouseEvent } from 'react';
import { DiagramEngine } from '../../DiagramEngine';
export class MoveCanvasAction extends BaseMouseAction {
export class MoveCanvasAction extends AbstractMouseAction {
initialOffsetX: number;
initialOffsetY: number;
constructor(mouseX: number, mouseY: number, diagramModel: DiagramModel) {
super(mouseX, mouseY, diagramModel);
this.initialOffsetX = diagramModel.getOffsetX();
this.initialOffsetY = diagramModel.getOffsetY();
constructor(mouseX: number, mouseY: number, engine: DiagramEngine) {
super(mouseX, mouseY, engine);
this.initialOffsetX = this.model.getOffsetX();
this.initialOffsetY = this.model.getOffsetY();
}
fireMouseMove(event: MouseEvent) {

View File

@@ -0,0 +1,17 @@
import { AbstractActionFactory, ActionFactoryActivationEvent } from '../../core-actions/AbstractActionFactory';
import { MoveCanvasAction } from './MoveCanvasAction';
import { MouseEvent } from 'react';
export class MoveCanvasActionFactory extends AbstractActionFactory<MoveCanvasAction> {
constructor() {
super('move-canvas');
}
generateAction(event: MouseEvent): MoveCanvasAction {
return new MoveCanvasAction(event.clientX, event.clientY, this.engine);
}
activate(event: ActionFactoryActivationEvent): boolean {
return !event.selectedEntity && !event.mouseEvent.shiftKey;
}
}

View File

@@ -1,24 +1,22 @@
import { BaseMouseAction } from './BaseMouseAction';
import { SelectionModel } from '../models/SelectionModel';
import { PointModel } from '../models/PointModel';
import { NodeModel } from '../models/NodeModel';
import { DiagramEngine } from '../DiagramEngine';
import { BasePositionModel } from '../core-models/BasePositionModel';
import { AbstractMouseAction } from '../../core-actions/AbstractMouseAction';
import { SelectionModel } from '../../models/SelectionModel';
import { PointModel } from '../../models/PointModel';
import { NodeModel } from '../../models/NodeModel';
import { DiagramEngine } from '../../DiagramEngine';
import { BasePositionModel } from '../../core-models/BasePositionModel';
import * as _ from 'lodash';
import { PortModel } from '../models/PortModel';
import { LinkModel } from '../models/LinkModel';
import { PortModel } from '../../models/PortModel';
import { LinkModel } from '../../models/LinkModel';
import { MouseEvent } from 'react';
export class MoveItemsAction extends BaseMouseAction {
export class MoveItemsAction extends AbstractMouseAction {
selectionModels: SelectionModel[];
moved: boolean;
diagramEngine: DiagramEngine;
allowLooseLinks: boolean;
constructor(mouseX: number, mouseY: number, diagramEngine: DiagramEngine, allowLooseLinks: boolean) {
super(mouseX, mouseY, diagramEngine.getDiagramModel());
super(mouseX, mouseY, diagramEngine);
this.allowLooseLinks = allowLooseLinks;
this.diagramEngine = diagramEngine;
this.moved = false;
}
@@ -38,7 +36,7 @@ export class MoveItemsAction extends BaseMouseAction {
if (model.model instanceof NodeModel) {
// update port coordinates as well
_.forEach(model.model.getPorts(), port => {
const portCoords = this.diagramEngine.getPortCoords(port);
const portCoords = this.engine.getPortCoords(port);
port.updateCoords(portCoords);
});
}
@@ -55,36 +53,42 @@ export class MoveItemsAction extends BaseMouseAction {
}
fireMouseUp(event: MouseEvent) {
const element = this.diagramEngine.getMouseElement(event);
const element = this.engine.getMouseElement(event);
_.forEach(this.selectionModels, model => {
//only care about points connecting to things
if (!(model.model instanceof PointModel)) {
return;
}
if (element && element.model instanceof PortModel && !this.diagramEngine.isModelLocked(element.model)) {
if (element && element.model instanceof PortModel && !this.engine.isModelLocked(element.model)) {
let link = model.model.getLink();
//if this was a valid link already and we are adding a node in the middle, create 2 links from the original
if (link.getTargetPort()) {
//if this was a valid link already and we are adding a node in the middle, create 2 links from the original
if (link.getTargetPort() !== element.model && link.getSourcePort() !== element.model) {
const targetPort = link.getTargetPort();
let newLink = link.clone({});
newLink.setSourcePort(element.model);
newLink.setTargetPort(targetPort);
link.setTargetPort(element.model);
link.getLastPoint().setPosition(this.engine.getPortCenter(element.model));
targetPort.removeLink(link);
newLink.removePointsBefore(newLink.getPoints()[link.getPointIndex(model.model)]);
link.removePointsAfter(model.model);
this.diagramEngine.getDiagramModel().addLink(newLink);
this.engine.getDiagramModel().addLink(newLink);
//if we are connecting to the same target or source, remove tweener points
} else if (link.getTargetPort() === element.model) {
link.removePointsAfter(model.model);
} else if (link.getSourcePort() === element.model) {
link.removePointsBefore(model.model);
}
} else {
link.setTargetPort(element.model);
}
delete this.diagramEngine.linksThatHaveInitiallyRendered[link.getID()];
// set the target port
else {
link.setTargetPort(element.model);
link.getLastPoint().setPosition(this.engine.getPortCenter(element.model));
}
delete this.engine.linksThatHaveInitiallyRendered[link.getID()];
}
});
@@ -130,15 +134,21 @@ export class MoveItemsAction extends BaseMouseAction {
}
});
this.diagramEngine.clearRepaintEntities();
this.engine.clearRepaintEntities();
}
fireMouseDown(event: React.MouseEvent) {
const selectedElement = this.diagramEngine.getMouseElement(event);
const selectedElement = this.engine.getMouseElement(event);
// clear selection first?
if (!selectedElement.model.isSelected()) {
this.model.clearSelection();
}
if (selectedElement.model instanceof PortModel) {
//its a port element, we want to drag a link
if (!this.diagramEngine.isModelLocked(selectedElement.model)) {
const portCenter = this.diagramEngine.getPortCenter(selectedElement.model);
if (!this.engine.isModelLocked(selectedElement.model)) {
const portCenter = this.engine.getPortCenter(selectedElement.model);
const sourcePort = selectedElement.model;
const link = sourcePort.createLinkModel();
link.setSourcePort(sourcePort);
@@ -165,7 +175,7 @@ export class MoveItemsAction extends BaseMouseAction {
if (!(item instanceof BasePositionModel)) {
return false;
}
return !this.diagramEngine.isModelLocked(item);
return !this.engine.isModelLocked(item);
});
this.selectionModels = selectedItems.map((item: PointModel | NodeModel) => {
@@ -175,6 +185,6 @@ export class MoveItemsAction extends BaseMouseAction {
initialY: item.getY()
};
});
this.diagramEngine.enableRepaintEntities(this.model.getSelectedItems());
this.engine.enableRepaintEntities(this.model.getSelectedItems());
}
}

View File

@@ -0,0 +1,32 @@
import { AbstractActionFactory, ActionFactoryActivationEvent } from '../../core-actions/AbstractActionFactory';
import { MouseEvent } from 'react';
import { MoveItemsAction } from './MoveItemsAction';
export interface MoveItemsActionFactoryOptions {
allowLooseLinks?: boolean;
}
export class MoveItemsActionFactory extends AbstractActionFactory<MoveItemsAction> {
options: MoveItemsActionFactoryOptions;
static NAME = 'move-items';
constructor(options: MoveItemsActionFactoryOptions = {}) {
super(MoveItemsActionFactory.NAME);
this.options = {
...options,
allowLooseLinks: options.allowLooseLinks == null ? true : options.allowLooseLinks
};
}
generateAction(event: MouseEvent): MoveItemsAction {
return new MoveItemsAction(event.clientX, event.clientY, this.engine, this.options.allowLooseLinks);
}
activate(event: ActionFactoryActivationEvent): boolean {
if (event.selectedModel) {
return !this.engine.isModelLocked(event.selectedModel);
}
return false;
}
}

View File

@@ -1,17 +1,15 @@
import { BaseMouseAction } from './BaseMouseAction';
import { DiagramModel } from '../models/DiagramModel';
import { AbstractMouseAction } from '../../core-actions/AbstractMouseAction';
import { DiagramModel } from '../../models/DiagramModel';
import * as _ from 'lodash';
import { DiagramEngine } from '../DiagramEngine';
import { DiagramEngine } from '../../DiagramEngine';
import { MouseEvent } from 'react';
export class SelectingAction extends BaseMouseAction {
export class SelectingAction extends AbstractMouseAction {
mouseX2: number;
mouseY2: number;
engine: DiagramEngine;
constructor(mouseX: number, mouseY: number, engine: DiagramEngine) {
super(mouseX, mouseY, engine.getDiagramModel());
this.engine = engine;
super(mouseX, mouseY, engine);
this.mouseX2 = mouseX;
this.mouseY2 = mouseY;
}

View File

@@ -0,0 +1,17 @@
import { AbstractActionFactory, ActionFactoryActivationEvent } from '../../core-actions/AbstractActionFactory';
import { MouseEvent } from 'react';
import { SelectingAction } from './SelectingAction';
export class SelectingItemsActionFactory extends AbstractActionFactory<SelectingAction> {
constructor() {
super('select-items');
}
generateAction(event: MouseEvent): SelectingAction {
return new SelectingAction(event.clientX, event.clientY, this.engine);
}
activate(event: ActionFactoryActivationEvent): boolean {
return !event.selectedEntity && event.mouseEvent.shiftKey;
}
}

View File

@@ -0,0 +1,12 @@
import { DiagramEngine } from '../DiagramEngine';
import { DiagramModel } from '../models/DiagramModel';
export class AbstractAction {
engine: DiagramEngine;
model: DiagramModel;
constructor(engine: DiagramEngine) {
this.engine = engine;
this.model = engine.getDiagramModel();
}
}

View File

@@ -0,0 +1,16 @@
import { AbstractAction } from './AbstractAction';
import { AbstractFactory } from '../core/AbstractFactory';
import { MouseEvent } from 'react';
import { BaseModel } from '../core-models/BaseModel';
export interface ActionFactoryActivationEvent {
selectedModel: BaseModel;
selectedEntity: HTMLElement;
mouseEvent: MouseEvent;
}
export abstract class AbstractActionFactory<T extends AbstractAction = AbstractAction> extends AbstractFactory {
abstract activate(event: ActionFactoryActivationEvent): boolean;
abstract generateAction(event: MouseEvent): T;
}

View File

@@ -0,0 +1,20 @@
import { MouseEvent } from 'react';
import { AbstractAction } from './AbstractAction';
import { DiagramEngine } from '../DiagramEngine';
export abstract class AbstractMouseAction extends AbstractAction {
protected mouseX: number;
protected mouseY: number;
constructor(mouseX: number, mouseY: number, engine: DiagramEngine) {
super(engine);
this.mouseX = mouseX;
this.mouseY = mouseY;
}
abstract fireMouseDown(event: MouseEvent);
abstract fireMouseMove(event: MouseEvent);
abstract fireMouseUp(event: MouseEvent);
}

View File

@@ -79,7 +79,7 @@ export class BaseEntity<T extends BaseEntityGenerics = BaseEntityGenerics> exten
};
}
fireEvent(event: Partial<BaseEntityEvent> & object, k: keyof T['LISTENER']) {
fireEvent<L extends Partial<BaseEntityEvent> & object>(event: L, k: keyof T['LISTENER']) {
super.fireEvent(
{
entity: this,

View File

@@ -1,15 +1,10 @@
import { BaseModel } from '../core-models/BaseModel';
import { DiagramEngine } from '../DiagramEngine';
export interface GenerateModelEvent {
initialConfig?: any;
}
/**
* Base factory for all the different types of entities.
* Gets registered with the engine, and is used to generate models
*/
export abstract class AbstractFactory<T extends BaseModel = BaseModel> {
export abstract class AbstractFactory<T = any> {
/**
* Couples the factory with the models it generates
*/
@@ -30,9 +25,4 @@ export abstract class AbstractFactory<T extends BaseModel = BaseModel> {
getType(): string {
return this.type;
}
/**
* Generates new models (the core factory pattern)
*/
abstract generateModel(event: GenerateModelEvent): T;
}

View File

@@ -0,0 +1,13 @@
import { AbstractFactory } from './AbstractFactory';
import { BaseModel } from '../core-models/BaseModel';
export interface GenerateModelEvent {
initialConfig?: any;
}
export abstract class AbstractModelFactory<T extends BaseModel> extends AbstractFactory<T> {
/**
* Generates new models (the core factory pattern)
*/
abstract generateModel(event: GenerateModelEvent): T;
}

View File

@@ -1,5 +1,5 @@
import { AbstractFactory } from './AbstractFactory';
import { BaseModel } from '../core-models/BaseModel';
import { AbstractModelFactory } from './AbstractModelFactory';
export interface GenerateWidgetEvent<T extends BaseModel> {
model: T;
@@ -8,7 +8,7 @@ export interface GenerateWidgetEvent<T extends BaseModel> {
/**
* Further extends the AbstractFactory to add widget generation capability.
*/
export abstract class AbstractReactFactory<T extends BaseModel> extends AbstractFactory<T> {
export abstract class AbstractReactFactory<T extends BaseModel> extends AbstractModelFactory<T> {
/**
* Generates React widgets from the model contained in the event object
*/

View File

@@ -1,5 +1,6 @@
import { BaseEvent, BaseListener, BaseObserver } from './BaseObserver';
import { AbstractFactory } from './AbstractFactory';
import * as _ from 'lodash';
export interface FactoryBankListener<F extends AbstractFactory> extends BaseListener {
/**
@@ -24,6 +25,10 @@ export class FactoryBank<F extends AbstractFactory = AbstractFactory> extends Ba
this.factories = {};
}
getFactories(): F[] {
return _.values(this.factories);
}
clearFactories() {
for (let factory in this.factories) {
this.deregisterFactory(factory);

View File

@@ -3,68 +3,46 @@ import { DiagramEngine } from '../DiagramEngine';
import * as _ from 'lodash';
import { LinkLayerWidget } from './layers/LinkLayerWidget';
import { NodeLayerWidget } from './layers/NodeLayerWidget';
import { BaseMouseAction } from '../actions/BaseMouseAction';
import { MoveCanvasAction } from '../actions/MoveCanvasAction';
import { MoveItemsAction } from '../actions/MoveItemsAction';
import { SelectingAction } from '../actions/SelectingAction';
import { AbstractMouseAction } from '../core-actions/AbstractMouseAction';
import { MoveItemsAction } from '../actions/move-items/MoveItemsAction';
import { SelectingAction } from '../actions/selecting-items/SelectingAction';
import { PointModel } from '../models/PointModel';
import { PortModel } from '../models/PortModel';
import { BaseWidget, BaseWidgetProps } from './BaseWidget';
import { MouseEvent } from 'react';
import { ActionFactoryActivationEvent } from '../core-actions/AbstractActionFactory';
import { AbstractAction } from '../core-actions/AbstractAction';
import { MoveItemsActionFactory } from '../actions/move-items/MoveItemsActionFactory';
export interface DiagramProps extends BaseWidgetProps {
diagramEngine: DiagramEngine;
allowLooseLinks?: boolean;
allowCanvasTranslation?: boolean;
// zoom
allowCanvasZoom?: boolean;
inverseZoom?: boolean;
maxNumberPointsPerLink?: number;
smartRouting?: boolean;
actionStartedFiring?: (action: BaseMouseAction) => boolean;
actionStillFiring?: (action: BaseMouseAction) => void;
actionStoppedFiring?: (action: BaseMouseAction) => void;
actionStartedFiring?: (action: AbstractAction) => boolean;
actionStillFiring?: (action: AbstractAction) => void;
actionStoppedFiring?: (action: AbstractAction) => void;
deleteKeys?: number[];
}
export interface DiagramState {
action: BaseMouseAction | null;
windowListener: any;
action: AbstractAction;
diagramEngineListener: any;
}
/**
* @author Dylan Vorster
*/
export class DiagramWidget extends BaseWidget<DiagramProps, DiagramState> {
public static defaultProps: DiagramProps = {
diagramEngine: null,
allowLooseLinks: true,
allowCanvasTranslation: true,
allowCanvasZoom: true,
inverseZoom: false,
maxNumberPointsPerLink: Infinity, // backwards compatible default
smartRouting: false,
deleteKeys: [46, 8]
};
onKeyUpPointer: (this: Window, ev: KeyboardEvent) => void = null;
ref: React.RefObject<HTMLDivElement>;
constructor(props: DiagramProps) {
super('srd-diagram', props);
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.ref = React.createRef();
this.state = {
action: null,
wasMoved: false,
renderedNodes: false,
windowListener: null,
diagramEngineListener: null,
document: null
diagramEngineListener: null
};
}
@@ -132,7 +110,7 @@ export class DiagramWidget extends BaseWidget<DiagramProps, DiagramState> {
this.setState({ action: null });
}
startFiringAction(action: BaseMouseAction) {
startFiringAction(action: AbstractAction) {
var setState = true;
if (this.props.actionStartedFiring) {
setState = this.props.actionStartedFiring(action);
@@ -142,18 +120,30 @@ export class DiagramWidget extends BaseWidget<DiagramProps, DiagramState> {
}
}
onMouseMove(event) {
onMouseUp = event => {
if (this.state.action && this.state.action instanceof AbstractMouseAction) {
this.state.action.fireMouseUp(event);
}
this.props.diagramEngine.clearRepaintEntities();
this.stopFiringAction();
document.removeEventListener('mousemove', this.onMouseMove);
document.removeEventListener('mouseup', this.onMouseUp);
};
onMouseMove = event => {
//select items so draw a bounding box
if (this.state.action) {
this.state.action.fireMouseMove(event);
if (this.state.action && this.state.action instanceof AbstractMouseAction) {
this.state.action.fireMouseMove(event);
}
this.fireAction();
this.forceUpdate();
}
}
};
onKeyUp(event) {
onKeyUp = event => {
//delete all selected
if (this.props.deleteKeys.indexOf(event.keyCode) !== -1) {
if ((this.props.deleteKeys || [46, 8]).indexOf(event.keyCode) !== -1) {
_.forEach(this.props.diagramEngine.getDiagramModel().getSelectedItems(), element => {
//only delete items which are not locked
if (!this.props.diagramEngine.isModelLocked(element)) {
@@ -162,17 +152,7 @@ export class DiagramWidget extends BaseWidget<DiagramProps, DiagramState> {
});
this.forceUpdate();
}
}
onMouseUp(event) {
if (this.state.action) {
this.state.action.fireMouseUp(event);
}
this.props.diagramEngine.clearRepaintEntities();
this.stopFiringAction();
document.removeEventListener('mousemove', this.onMouseMove);
document.removeEventListener('mouseup', this.onMouseUp);
}
};
drawSelectionBox() {
let dimensions = (this.state.action as SelectingAction).getBoxDimensions();
@@ -189,41 +169,29 @@ export class DiagramWidget extends BaseWidget<DiagramProps, DiagramState> {
);
}
getActionForEvent(event: MouseEvent): BaseMouseAction {
getActionForEvent(event: MouseEvent): AbstractAction {
event.persist();
this.props.diagramEngine.clearRepaintEntities();
const { diagramEngine } = this.props;
const model = diagramEngine.getMouseElement(event);
const relative = diagramEngine.getRelativePoint(event.clientX, event.clientY);
// the canvas was selected
if (model === null) {
// is it a multiple selection
if (event.shiftKey) {
return new SelectingAction(relative.x, relative.y, diagramEngine);
} else {
// its a drag the canvas event
return new MoveCanvasAction(event.clientX, event.clientY, diagramEngine.getDiagramModel());
const activateEvent: ActionFactoryActivationEvent = {
selectedModel: model && model.model,
selectedEntity: model && (model.element as HTMLElement),
mouseEvent: event
};
for (let factory of diagramEngine.getActionFactories().getFactories()) {
if (factory.activate(activateEvent)) {
return factory.generateAction(event);
}
}
// its a port element, we want to drag a link
else if (model.model instanceof PortModel) {
if (!this.props.diagramEngine.isModelLocked(model.model)) {
return new MoveItemsAction(event.clientX, event.clientY, diagramEngine, this.props.allowLooseLinks);
} else {
diagramEngine.getDiagramModel().clearSelection();
}
}
// its some or other element, probably want to move it
if (!event.shiftKey && !model.model.isSelected()) {
diagramEngine.getDiagramModel().clearSelection();
}
return new MoveItemsAction(event.clientX, event.clientY, diagramEngine, this.props.allowLooseLinks);
return null;
}
render() {
var diagramEngine = this.props.diagramEngine;
diagramEngine.setMaxNumberPointsPerLink(this.props.maxNumberPointsPerLink);
var diagramModel = diagramEngine.getDiagramModel();
const diagramEngine = this.props.diagramEngine;
const diagramModel = diagramEngine.getDiagramModel();
return (
<div
@@ -280,7 +248,9 @@ export class DiagramWidget extends BaseWidget<DiagramProps, DiagramState> {
// try and get an action for this event
const action = this.getActionForEvent(event);
if (action) {
action.fireMouseDown(event);
if (action instanceof AbstractMouseAction) {
action.fireMouseDown(event);
}
this.startFiringAction(action);
}
document.addEventListener('mousemove', this.onMouseMove);
@@ -293,9 +263,18 @@ export class DiagramWidget extends BaseWidget<DiagramProps, DiagramState> {
document.addEventListener('mouseup', this.onMouseUp);
event.stopPropagation();
diagramModel.clearSelection(point);
const action = new MoveItemsAction(event.clientX, event.clientY, diagramEngine, this.props.allowLooseLinks);
action.fireMouseDown(event);
this.startFiringAction(action);
// TODO implement this better and more generic
let action: MoveItemsAction = null;
let fac: MoveItemsActionFactory = null;
try {
fac = diagramEngine.getActionFactories().getFactory<MoveItemsActionFactory>(MoveItemsActionFactory.NAME);
} catch (e) {}
if (fac) {
action = fac.generateAction(event);
action.fireMouseDown(event);
this.startFiringAction(action);
}
}}
/>
<NodeLayerWidget diagramEngine={diagramEngine} />

View File

@@ -6,6 +6,7 @@ import { BaseEntityEvent } from '../core-models/BaseEntity';
import { BasePositionModel } from '../core-models/BasePositionModel';
import { PointModel } from '../models/PointModel';
import { PortModel } from '../models/PortModel';
import { MouseEvent } from 'react';
export interface LinkProps {
link: LinkModel;

View File

@@ -47,7 +47,7 @@ export class DefaultNodeModel extends NodeModel<DefaultNodeModelGenerics & NodeM
super.doClone(lookupTable, clone);
}
removePort(port: (DefaultNodeModelGenerics & NodeModelGenerics)['PORT']): void {
removePort(port: DefaultPortModel): void {
super.removePort(port);
if (port.getOptions().in) {
this.portsIn.splice(this.portsIn.indexOf(port));
@@ -56,7 +56,7 @@ export class DefaultNodeModel extends NodeModel<DefaultNodeModelGenerics & NodeM
}
}
addPort<T extends (DefaultNodeModelGenerics & NodeModelGenerics)['PORT']>(port: T): T {
addPort<T extends DefaultPortModel>(port: T): T {
super.addPort(port);
if (port.getOptions().in) {
if (this.portsIn.indexOf(port) === -1) {

View File

@@ -1,7 +1,7 @@
import { AbstractFactory } from '@projectstorm/react-diagrams-core';
import { AbstractModelFactory } from '@projectstorm/react-diagrams-core';
import { DefaultPortModel } from './DefaultPortModel';
export class DefaultPortFactory extends AbstractFactory<DefaultPortModel> {
export class DefaultPortFactory extends AbstractModelFactory<DefaultPortModel> {
constructor() {
super('default');
}

View File

@@ -1,12 +1,12 @@
import * as _ from 'lodash';
import {
AbstractFactory,
DiagramEngine,
LinkModel,
PortModel,
PortModelAlignment,
PortModelGenerics,
PortModelOptions
PortModelOptions,
AbstractModelFactory
} from '@projectstorm/react-diagrams-core';
import { DefaultLinkModel } from '../link/DefaultLinkModel';
import { DefaultNodeModel } from '../node/DefaultNodeModel';
@@ -21,7 +21,7 @@ export interface DefaultPortModelGenerics {
PARENT: DefaultNodeModel;
}
export class DefaultPortModel extends PortModel<PortModelGenerics & DefaultPortModelGenerics> {
export class DefaultPortModel extends PortModel<DefaultPortModelGenerics & PortModelGenerics> {
constructor(isIn: boolean, name?: string, label?: string);
constructor(options: DefaultPortModelOptions);
constructor(options: DefaultPortModelOptions | boolean, name?: string, label?: string) {
@@ -54,7 +54,7 @@ export class DefaultPortModel extends PortModel<PortModelGenerics & DefaultPortM
});
}
link(port: PortModel, factory?: AbstractFactory<LinkModel>): LinkModel {
link(port: PortModel, factory?: AbstractModelFactory<LinkModel>): LinkModel {
let link = this.createLinkModel(factory);
link.setSourcePort(this);
link.setTargetPort(port);
@@ -68,7 +68,7 @@ export class DefaultPortModel extends PortModel<PortModelGenerics & DefaultPortM
return true;
}
createLinkModel(factory?: AbstractFactory<LinkModel>): LinkModel {
createLinkModel(factory?: AbstractModelFactory<LinkModel>): LinkModel {
let link = super.createLinkModel();
if (!link && factory) {
return factory.generateModel({});