parent
c89ce4cea4
commit
648e324cc0
|
@ -1,9 +1,9 @@
|
||||||
import type Cell from './view/cell/datatypes/Cell';
|
import type Cell from './view/cell/datatypes/Cell';
|
||||||
import CellState from './view/cell/datatypes/CellState';
|
import type CellState from './view/cell/datatypes/CellState';
|
||||||
import RectangleShape from './view/geometry/shape/node/RectangleShape';
|
import type InternalMouseEvent from './view/event/InternalMouseEvent';
|
||||||
import type Shape from './view/geometry/shape/Shape';
|
import type Shape from './view/geometry/shape/Shape';
|
||||||
import type Graph from './view/Graph';
|
import type { MaxGraph } from './view/Graph';
|
||||||
import ImageBox from './view/image/ImageBox';
|
import type ImageBox from './view/image/ImageBox';
|
||||||
|
|
||||||
export type CellMap = {
|
export type CellMap = {
|
||||||
[id: string]: Cell;
|
[id: string]: Cell;
|
||||||
|
@ -19,10 +19,6 @@ export type UndoableChange = {
|
||||||
|
|
||||||
export type StyleValue = string | number;
|
export type StyleValue = string | number;
|
||||||
|
|
||||||
export type StyleProperties = {
|
|
||||||
[k: string]: StyleValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Properties = {
|
export type Properties = {
|
||||||
[k: string]: any;
|
[k: string]: any;
|
||||||
};
|
};
|
||||||
|
@ -30,6 +26,7 @@ export type Properties = {
|
||||||
export type CellStateStyles = {
|
export type CellStateStyles = {
|
||||||
absoluteArcSize: number;
|
absoluteArcSize: number;
|
||||||
align: AlignValue;
|
align: AlignValue;
|
||||||
|
anchorPointDirection: boolean;
|
||||||
arcSize: number;
|
arcSize: number;
|
||||||
aspect: string;
|
aspect: string;
|
||||||
autosize: boolean;
|
autosize: boolean;
|
||||||
|
@ -49,8 +46,14 @@ export type CellStateStyles = {
|
||||||
endArrow: ArrowType;
|
endArrow: ArrowType;
|
||||||
endFill: boolean;
|
endFill: boolean;
|
||||||
endSize: number;
|
endSize: number;
|
||||||
|
entryDx: number;
|
||||||
|
entryDy: number;
|
||||||
|
entryPerimeter: boolean;
|
||||||
entryX: number;
|
entryX: number;
|
||||||
entryY: number;
|
entryY: number;
|
||||||
|
exitDx: number;
|
||||||
|
exitDy: number;
|
||||||
|
exitPerimeter: boolean;
|
||||||
exitX: number;
|
exitX: number;
|
||||||
exitY: number;
|
exitY: number;
|
||||||
fillColor: ColorValue;
|
fillColor: ColorValue;
|
||||||
|
@ -75,11 +78,15 @@ export type CellStateStyles = {
|
||||||
imageHeight: number;
|
imageHeight: number;
|
||||||
imageWidth: number;
|
imageWidth: number;
|
||||||
indicatorColor: ColorValue;
|
indicatorColor: ColorValue;
|
||||||
|
indicatorDirection: DirectionValue;
|
||||||
indicatorHeight: number;
|
indicatorHeight: number;
|
||||||
indicatorImage: string;
|
indicatorImage: string;
|
||||||
indicatorShape: Shape;
|
indicatorShape: string;
|
||||||
|
indicatorStrokeColor: ColorValue;
|
||||||
indicatorWidth: number;
|
indicatorWidth: number;
|
||||||
|
labelBackgroundColor: ColorValue;
|
||||||
labelBorderColor: ColorValue;
|
labelBorderColor: ColorValue;
|
||||||
|
labelPadding: number;
|
||||||
labelPosition: AlignValue;
|
labelPosition: AlignValue;
|
||||||
loop: Function;
|
loop: Function;
|
||||||
margin: number;
|
margin: number;
|
||||||
|
@ -206,8 +213,12 @@ export type GradientMap = {
|
||||||
[k: string]: Gradient;
|
[k: string]: Gradient;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface GraphPluginConstructor {
|
||||||
|
new (graph: MaxGraph): GraphPlugin;
|
||||||
|
pluginId: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GraphPlugin {
|
export interface GraphPlugin {
|
||||||
onInit: (graph: Graph) => void;
|
|
||||||
onDestroy: () => void;
|
onDestroy: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,14 +226,16 @@ export interface GraphPlugin {
|
||||||
|
|
||||||
export type Listener = {
|
export type Listener = {
|
||||||
name: string;
|
name: string;
|
||||||
f: EventListener;
|
f: MouseEventListener;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ListenerTarget = {
|
export type ListenerTarget = {
|
||||||
mxListenerList?: Listener[];
|
mxListenerList?: Listener[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Listenable = (Node | Window) & ListenerTarget;
|
export type Listenable = (EventSource | EventTarget) & ListenerTarget;
|
||||||
|
|
||||||
|
export type MouseEventListener = (me: MouseEvent) => void;
|
||||||
|
|
||||||
export type GestureEvent = Event &
|
export type GestureEvent = Event &
|
||||||
MouseEvent & {
|
MouseEvent & {
|
||||||
|
@ -230,6 +243,12 @@ export type GestureEvent = Event &
|
||||||
pointerId?: number;
|
pointerId?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MouseListenerSet = {
|
||||||
|
mouseDown: (sender: EventSource, me: InternalMouseEvent) => void;
|
||||||
|
mouseMove: (sender: EventSource, me: InternalMouseEvent) => void;
|
||||||
|
mouseUp: (sender: EventSource, me: InternalMouseEvent) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export type EventCache = GestureEvent[];
|
export type EventCache = GestureEvent[];
|
||||||
|
|
||||||
export interface CellHandle {
|
export interface CellHandle {
|
||||||
|
@ -237,5 +256,12 @@ export interface CellHandle {
|
||||||
cursor: string;
|
cursor: string;
|
||||||
image: ImageBox | null;
|
image: ImageBox | null;
|
||||||
shape: Shape | null;
|
shape: Shape | null;
|
||||||
|
active: boolean;
|
||||||
setVisible: (v: boolean) => void;
|
setVisible: (v: boolean) => void;
|
||||||
|
processEvent: (me: InternalMouseEvent) => void;
|
||||||
|
positionChanged: () => void;
|
||||||
|
execute: (me: InternalMouseEvent) => void;
|
||||||
|
reset: () => void;
|
||||||
|
redraw: () => void;
|
||||||
|
destroy: () => void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,204 +0,0 @@
|
||||||
/**
|
|
||||||
* Returns the touch or mouse event that contains the mouse coordinates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import mxClient from "../mxClient";
|
|
||||||
|
|
||||||
// static getMainEvent(e: MouseEvent): MouseEvent;
|
|
||||||
export const getMainEvent = (e) => {
|
|
||||||
if (
|
|
||||||
(e.type == 'touchstart' || e.type == 'touchmove') &&
|
|
||||||
e.touches != null &&
|
|
||||||
e.touches[0] != null
|
|
||||||
) {
|
|
||||||
e = e.touches[0];
|
|
||||||
} else if (
|
|
||||||
e.type == 'touchend' &&
|
|
||||||
e.changedTouches != null &&
|
|
||||||
e.changedTouches[0] != null
|
|
||||||
) {
|
|
||||||
e = e.changedTouches[0];
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the meta key is pressed for the given event.
|
|
||||||
*/
|
|
||||||
// static getClientX(e: TouchEvent | MouseEvent): number;
|
|
||||||
export const getClientX = (e) => {
|
|
||||||
return getMainEvent(e).clientX;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the meta key is pressed for the given event.
|
|
||||||
*/
|
|
||||||
// static getClientY(e: TouchEvent | MouseEvent): number;
|
|
||||||
export const getClientY = (e) => {
|
|
||||||
return getMainEvent(e).clientY;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: getSource
|
|
||||||
*
|
|
||||||
* Returns the event's target or srcElement depending on the browser.
|
|
||||||
*/
|
|
||||||
export const getSource = (evt) => {
|
|
||||||
return evt.srcElement != null ? evt.srcElement : evt.target;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the event has been consumed using {@link consume}.
|
|
||||||
*/
|
|
||||||
// static isConsumed(evt: mxEventObject | mxMouseEvent | Event): boolean;
|
|
||||||
export const isConsumed = (evt) => {
|
|
||||||
return evt.isConsumed != null && evt.isConsumed;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the event was generated using a touch device (not a pen or mouse).
|
|
||||||
*/
|
|
||||||
// static isTouchEvent(evt: Event): boolean;
|
|
||||||
export const isTouchEvent = (evt) => {
|
|
||||||
return evt.pointerType != null
|
|
||||||
? evt.pointerType == 'touch' ||
|
|
||||||
evt.pointerType === evt.MSPOINTER_TYPE_TOUCH
|
|
||||||
: evt.mozInputSource != null
|
|
||||||
? evt.mozInputSource == 5
|
|
||||||
: evt.type.indexOf('touch') == 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the event was generated using a pen (not a touch device or mouse).
|
|
||||||
*/
|
|
||||||
// static isPenEvent(evt: Event): boolean;
|
|
||||||
export const isPenEvent = (evt) => {
|
|
||||||
return evt.pointerType != null
|
|
||||||
? evt.pointerType == 'pen' || evt.pointerType === evt.MSPOINTER_TYPE_PEN
|
|
||||||
: evt.mozInputSource != null
|
|
||||||
? evt.mozInputSource == 2
|
|
||||||
: evt.type.indexOf('pen') == 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the event was generated using a touch device (not a pen or mouse).
|
|
||||||
*/
|
|
||||||
// static isMultiTouchEvent(evt: Event): boolean;
|
|
||||||
export const isMultiTouchEvent = (evt) => {
|
|
||||||
return (
|
|
||||||
evt.type != null &&
|
|
||||||
evt.type.indexOf('touch') == 0 &&
|
|
||||||
evt.touches != null &&
|
|
||||||
evt.touches.length > 1
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the event was generated using a mouse (not a pen or touch device).
|
|
||||||
*/
|
|
||||||
// static isMouseEvent(evt: Event): boolean;
|
|
||||||
export const isMouseEvent = (evt) => {
|
|
||||||
return evt.pointerType != null
|
|
||||||
? evt.pointerType == 'mouse' ||
|
|
||||||
evt.pointerType === evt.MSPOINTER_TYPE_MOUSE
|
|
||||||
: evt.mozInputSource != null
|
|
||||||
? evt.mozInputSource == 1
|
|
||||||
: evt.type.indexOf('mouse') == 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the left mouse button is pressed for the given event.
|
|
||||||
* To check if a button is pressed during a mouseMove you should use the
|
|
||||||
* {@link mxGraph.isMouseDown} property. Note that this returns true in Firefox
|
|
||||||
* for control+left-click on the Mac.
|
|
||||||
*/
|
|
||||||
// static isLeftMouseButton(evt: MouseEvent): boolean;
|
|
||||||
export const isLeftMouseButton = (evt) => {
|
|
||||||
// Special case for mousemove and mousedown we check the buttons
|
|
||||||
// if it exists because which is 0 even if no button is pressed
|
|
||||||
if (
|
|
||||||
'buttons' in evt &&
|
|
||||||
(evt.type == 'mousedown' || evt.type == 'mousemove')
|
|
||||||
) {
|
|
||||||
return evt.buttons == 1;
|
|
||||||
}
|
|
||||||
if ('which' in evt) {
|
|
||||||
return evt.which === 1;
|
|
||||||
}
|
|
||||||
return evt.button === 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the middle mouse button is pressed for the given event.
|
|
||||||
* To check if a button is pressed during a mouseMove you should use the
|
|
||||||
* {@link mxGraph.isMouseDown} property.
|
|
||||||
*/
|
|
||||||
// static isMiddleMouseButton(evt: MouseEvent): boolean;
|
|
||||||
export const isMiddleMouseButton = (evt) => {
|
|
||||||
if ('which' in evt) {
|
|
||||||
return evt.which === 2;
|
|
||||||
}
|
|
||||||
return evt.button === 4;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the right mouse button was pressed. Note that this
|
|
||||||
* button might not be available on some systems. For handling a popup
|
|
||||||
* trigger {@link isPopupTrigger} should be used.
|
|
||||||
*/
|
|
||||||
// static isRightMouseButton(evt: MouseEvent): boolean;
|
|
||||||
export const isRightMouseButton = (evt) => {
|
|
||||||
if ('which' in evt) {
|
|
||||||
return evt.which === 3;
|
|
||||||
}
|
|
||||||
return evt.button === 2;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the event is a popup trigger. This implementation
|
|
||||||
* returns true if the right button or the left button and control was
|
|
||||||
* pressed on a Mac.
|
|
||||||
*/
|
|
||||||
// static isPopupTrigger(evt: Event): boolean;
|
|
||||||
export const isPopupTrigger = (evt) => {
|
|
||||||
return (
|
|
||||||
isRightMouseButton(evt) ||
|
|
||||||
(mxClient.IS_MAC &&
|
|
||||||
isControlDown(evt) &&
|
|
||||||
!isShiftDown(evt) &&
|
|
||||||
!isMetaDown(evt) &&
|
|
||||||
!isAltDown(evt))
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the shift key is pressed for the given event.
|
|
||||||
*/
|
|
||||||
// static isShiftDown(evt: MouseEvent): boolean;
|
|
||||||
export const isShiftDown = (evt) => {
|
|
||||||
return evt != null ? evt.shiftKey : false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the alt key is pressed for the given event.
|
|
||||||
*/
|
|
||||||
// static isAltDown(evt: MouseEvent): boolean;
|
|
||||||
export const isAltDown = (evt) => {
|
|
||||||
return evt != null ? evt.altKey : false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the control key is pressed for the given event.
|
|
||||||
*/
|
|
||||||
// static isControlDown(evt: MouseEvent): boolean;
|
|
||||||
export const isControlDown = (evt) => {
|
|
||||||
return evt != null ? evt.ctrlKey : false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the meta key is pressed for the given event.
|
|
||||||
*/
|
|
||||||
// static isMetaDown(evt: MouseEvent): boolean;
|
|
||||||
export const isMetaDown = (evt) => {
|
|
||||||
return evt != null ? evt.metaKey : false;
|
|
||||||
};
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
/**
|
||||||
|
* Returns the touch or mouse event that contains the mouse coordinates.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import mxClient from '../mxClient';
|
||||||
|
|
||||||
|
export const getMainEvent = (evt: MouseEvent) => {
|
||||||
|
let t = evt as any;
|
||||||
|
|
||||||
|
if ((t.type === 'touchstart' || t.type === 'touchmove') && t.touches && t.touches[0]) {
|
||||||
|
t = t.touches[0];
|
||||||
|
} else if (t.type === 'touchend' && t.changedTouches && t.changedTouches[0]) {
|
||||||
|
t = t.changedTouches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return t as MouseEvent;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the meta key is pressed for the given event.
|
||||||
|
*/
|
||||||
|
export const getClientX = (evt: MouseEvent) => {
|
||||||
|
return getMainEvent(evt).clientX;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the meta key is pressed for the given event.
|
||||||
|
*/
|
||||||
|
// static getClientY(e: TouchEvent | MouseEvent): number;
|
||||||
|
export const getClientY = (evt: MouseEvent) => {
|
||||||
|
return getMainEvent(evt).clientY;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function: getSource
|
||||||
|
*
|
||||||
|
* Returns the event's target or srcElement depending on the browser.
|
||||||
|
*/
|
||||||
|
export const getSource = (evt: MouseEvent) => {
|
||||||
|
return evt.srcElement !== undefined ? evt.srcElement : evt.target;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the event has been consumed using {@link consume}.
|
||||||
|
*/
|
||||||
|
export const isConsumed = (evt: MouseEvent) => {
|
||||||
|
const t = evt as any;
|
||||||
|
return t.isConsumed !== undefined && t.isConsumed;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the event was generated using a touch device (not a pen or mouse).
|
||||||
|
*/
|
||||||
|
export const isTouchEvent = (evt: MouseEvent) => {
|
||||||
|
const t = evt as any;
|
||||||
|
|
||||||
|
return t.pointerType
|
||||||
|
? t.pointerType === 'touch' || t.pointerType === t.MSPOINTER_TYPE_TOUCH
|
||||||
|
: t.mozInputSource !== undefined
|
||||||
|
? t.mozInputSource === 5
|
||||||
|
: t.type.indexOf('touch') === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the event was generated using a pen (not a touch device or mouse).
|
||||||
|
*/
|
||||||
|
export const isPenEvent = (evt: MouseEvent) => {
|
||||||
|
const t = evt as any;
|
||||||
|
|
||||||
|
return t.pointerType
|
||||||
|
? t.pointerType == 'pen' || t.pointerType === t.MSPOINTER_TYPE_PEN
|
||||||
|
: t.mozInputSource !== undefined
|
||||||
|
? t.mozInputSource === 2
|
||||||
|
: t.type.indexOf('pen') === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the event was generated using a touch device (not a pen or mouse).
|
||||||
|
*/
|
||||||
|
export const isMultiTouchEvent = (evt: MouseEvent) => {
|
||||||
|
const t = evt as any;
|
||||||
|
|
||||||
|
return (
|
||||||
|
t.type &&
|
||||||
|
t.type.indexOf('touch') == 0 &&
|
||||||
|
t.touches !== undefined &&
|
||||||
|
t.touches.length > 1
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the event was generated using a mouse (not a pen or touch device).
|
||||||
|
*/
|
||||||
|
export const isMouseEvent = (evt: Event) => {
|
||||||
|
const t = evt as any;
|
||||||
|
|
||||||
|
return t.pointerType
|
||||||
|
? t.pointerType == 'mouse' || t.pointerType === t.MSPOINTER_TYPE_MOUSE
|
||||||
|
: t.mozInputSource !== undefined
|
||||||
|
? t.mozInputSource === 1
|
||||||
|
: t.type.indexOf('mouse') === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the left mouse button is pressed for the given event.
|
||||||
|
* To check if a button is pressed during a mouseMove you should use the
|
||||||
|
* {@link mxGraph.isMouseDown} property. Note that this returns true in Firefox
|
||||||
|
* for control+left-click on the Mac.
|
||||||
|
*/
|
||||||
|
// static isLeftMouseButton(evt: MouseEvent): boolean;
|
||||||
|
export const isLeftMouseButton = (evt: MouseEvent) => {
|
||||||
|
// Special case for mousemove and mousedown we check the buttons
|
||||||
|
// if it exists because which is 0 even if no button is pressed
|
||||||
|
if ('buttons' in evt && (evt.type === 'mousedown' || evt.type === 'mousemove')) {
|
||||||
|
return evt.buttons === 1;
|
||||||
|
}
|
||||||
|
if ('which' in evt) {
|
||||||
|
return evt.which === 1;
|
||||||
|
}
|
||||||
|
return evt.button === 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the middle mouse button is pressed for the given event.
|
||||||
|
* To check if a button is pressed during a mouseMove you should use the
|
||||||
|
* {@link mxGraph.isMouseDown} property.
|
||||||
|
*/
|
||||||
|
export const isMiddleMouseButton = (evt: MouseEvent) => {
|
||||||
|
if ('which' in evt) {
|
||||||
|
return evt.which === 2;
|
||||||
|
}
|
||||||
|
return evt.button === 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the right mouse button was pressed. Note that this
|
||||||
|
* button might not be available on some systems. For handling a popup
|
||||||
|
* trigger {@link isPopupTrigger} should be used.
|
||||||
|
*/
|
||||||
|
export const isRightMouseButton = (evt: MouseEvent) => {
|
||||||
|
if ('which' in evt) {
|
||||||
|
return evt.which === 3;
|
||||||
|
}
|
||||||
|
return evt.button === 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the event is a popup trigger. This implementation
|
||||||
|
* returns true if the right button or the left button and control was
|
||||||
|
* pressed on a Mac.
|
||||||
|
*/
|
||||||
|
export const isPopupTrigger = (evt: MouseEvent) => {
|
||||||
|
return (
|
||||||
|
isRightMouseButton(evt) ||
|
||||||
|
(mxClient.IS_MAC &&
|
||||||
|
isControlDown(evt) &&
|
||||||
|
!isShiftDown(evt) &&
|
||||||
|
!isMetaDown(evt) &&
|
||||||
|
!isAltDown(evt))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the shift key is pressed for the given event.
|
||||||
|
*/
|
||||||
|
export const isShiftDown = (evt: MouseEvent) => {
|
||||||
|
return evt.shiftKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the alt key is pressed for the given event.
|
||||||
|
*/
|
||||||
|
export const isAltDown = (evt: MouseEvent) => {
|
||||||
|
return evt.altKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the control key is pressed for the given event.
|
||||||
|
*/
|
||||||
|
export const isControlDown = (evt: MouseEvent) => {
|
||||||
|
return evt.ctrlKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the meta key is pressed for the given event.
|
||||||
|
*/
|
||||||
|
export const isMetaDown = (evt: MouseEvent) => {
|
||||||
|
return evt.metaKey;
|
||||||
|
};
|
|
@ -41,7 +41,7 @@ import Cell from '../view/cell/datatypes/Cell';
|
||||||
import Model from '../view/model/Model';
|
import Model from '../view/model/Model';
|
||||||
import graph from '../view/Graph';
|
import graph from '../view/Graph';
|
||||||
|
|
||||||
import type { CellStateStyles, Properties, StyleProperties, StyleValue } from '../types';
|
import type { CellStateStyles, Properties, StyleValue } from '../types';
|
||||||
import CellArray from '../view/cell/datatypes/CellArray';
|
import CellArray from '../view/cell/datatypes/CellArray';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -159,7 +159,11 @@ export const parseCssNumber = (value: string) => {
|
||||||
* mxUtils.setPrefixedStyle(node.style, 'transformOrigin', '0% 0%');
|
* mxUtils.setPrefixedStyle(node.style, 'transformOrigin', '0% 0%');
|
||||||
* (end)
|
* (end)
|
||||||
*/
|
*/
|
||||||
export const setPrefixedStyle = (style: StyleProperties, name: string, value: string) => {
|
export const setPrefixedStyle = (
|
||||||
|
style: CSSStyleDeclaration,
|
||||||
|
name: string,
|
||||||
|
value: string
|
||||||
|
) => {
|
||||||
let prefix = null;
|
let prefix = null;
|
||||||
|
|
||||||
if (mxClient.IS_SF || mxClient.IS_GC) {
|
if (mxClient.IS_SF || mxClient.IS_GC) {
|
||||||
|
@ -189,7 +193,7 @@ export const setPrefixedStyle = (style: StyleProperties, name: string, value: st
|
||||||
export const hasScrollbars = (node: HTMLElement) => {
|
export const hasScrollbars = (node: HTMLElement) => {
|
||||||
const style = getCurrentStyle(node);
|
const style = getCurrentStyle(node);
|
||||||
|
|
||||||
return style && (style.overflow === 'scroll' || style.overflow === 'auto');
|
return !!style && (style.overflow === 'scroll' || style.overflow === 'auto');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -402,14 +406,16 @@ export const getColor = (array: any, key: string, defaultValue: any) => {
|
||||||
* a - Array of <mxPoints> to be compared.
|
* a - Array of <mxPoints> to be compared.
|
||||||
* b - Array of <mxPoints> to be compared.
|
* b - Array of <mxPoints> to be compared.
|
||||||
*/
|
*/
|
||||||
export const equalPoints = (a: Point[] | null, b: Point[] | null) => {
|
export const equalPoints = (a: (Point | null)[] | null, b: (Point | null)[] | null) => {
|
||||||
if ((!a && b) || (a && !b) || (a && b && a.length != b.length)) {
|
if ((!a && b) || (a && !b) || (a && b && a.length != b.length)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a && b) {
|
if (a && b) {
|
||||||
for (let i = 0; i < a.length; i += 1) {
|
for (let i = 0; i < a.length; i += 1) {
|
||||||
if (!a[i] || !a[i].equals(b[i])) return false;
|
const p = a[i];
|
||||||
|
|
||||||
|
if (!p || (p && !p.equals(b[i]))) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,16 +24,14 @@ import Point from './geometry/Point';
|
||||||
import {
|
import {
|
||||||
applyMixins,
|
applyMixins,
|
||||||
autoImplement,
|
autoImplement,
|
||||||
getBoundingBox,
|
|
||||||
getCurrentStyle,
|
getCurrentStyle,
|
||||||
getValue,
|
|
||||||
hasScrollbars,
|
hasScrollbars,
|
||||||
parseCssNumber,
|
parseCssNumber,
|
||||||
} from '../util/Utils';
|
} from '../util/Utils';
|
||||||
import Cell from './cell/datatypes/Cell';
|
import Cell from './cell/datatypes/Cell';
|
||||||
import Model from './model/Model';
|
import Model from './model/Model';
|
||||||
import Stylesheet from './style/Stylesheet';
|
import Stylesheet from './style/Stylesheet';
|
||||||
import { DIALECT_SVG, PAGE_FORMAT_A4_PORTRAIT } from '../util/Constants';
|
import { PAGE_FORMAT_A4_PORTRAIT } from '../util/Constants';
|
||||||
|
|
||||||
import ChildChange from './model/ChildChange';
|
import ChildChange from './model/ChildChange';
|
||||||
import GeometryChange from './geometry/GeometryChange';
|
import GeometryChange from './geometry/GeometryChange';
|
||||||
|
@ -49,23 +47,27 @@ import EdgeHandler from './cell/edge/EdgeHandler';
|
||||||
import VertexHandler from './cell/vertex/VertexHandler';
|
import VertexHandler from './cell/vertex/VertexHandler';
|
||||||
import EdgeSegmentHandler from './cell/edge/EdgeSegmentHandler';
|
import EdgeSegmentHandler from './cell/edge/EdgeSegmentHandler';
|
||||||
import ElbowEdgeHandler from './cell/edge/ElbowEdgeHandler';
|
import ElbowEdgeHandler from './cell/edge/ElbowEdgeHandler';
|
||||||
import GraphEvents from './event/GraphEvents';
|
|
||||||
import GraphImage from './image/GraphImage';
|
|
||||||
import GraphCells from './cell/GraphCells';
|
|
||||||
import GraphSelection from './selection/GraphSelection';
|
|
||||||
import GraphConnections from './connection/GraphConnections';
|
|
||||||
import GraphEdge from './cell/edge/GraphEdge';
|
|
||||||
import GraphVertex from './cell/vertex/GraphVertex';
|
|
||||||
import GraphOverlays from './layout/GraphOverlays';
|
|
||||||
import GraphEditing from './editing/GraphEditing';
|
|
||||||
import GraphFolding from './folding/GraphFolding';
|
|
||||||
import GraphLabel from './label/GraphLabel';
|
|
||||||
import GraphValidation from './validation/GraphValidation';
|
|
||||||
import GraphSnap from './snap/GraphSnap';
|
|
||||||
|
|
||||||
import type { GraphPlugin } from '../types';
|
import type { GraphPlugin, GraphPluginConstructor } from '../types';
|
||||||
import GraphTooltip from './tooltip/GraphTooltip';
|
import type GraphPorts from './ports/GraphPorts';
|
||||||
import GraphTerminal from './terminal/GraphTerminal';
|
import type Dictionary from '../util/Dictionary';
|
||||||
|
import type GraphPanning from './panning/GraphPanning';
|
||||||
|
import type GraphZoom from './zoom/GraphZoom';
|
||||||
|
import type GraphEvents from './event/GraphEvents';
|
||||||
|
import type GraphImage from './image/GraphImage';
|
||||||
|
import type GraphCells from './cell/GraphCells';
|
||||||
|
import type GraphSelection from './selection/GraphSelection';
|
||||||
|
import type GraphConnections from './connection/GraphConnections';
|
||||||
|
import type GraphEdge from './cell/edge/GraphEdge';
|
||||||
|
import type GraphVertex from './cell/vertex/GraphVertex';
|
||||||
|
import type GraphOverlays from './layout/GraphOverlays';
|
||||||
|
import type GraphEditing from './editing/GraphEditing';
|
||||||
|
import type GraphFolding from './folding/GraphFolding';
|
||||||
|
import type GraphLabel from './label/GraphLabel';
|
||||||
|
import type GraphValidation from './validation/GraphValidation';
|
||||||
|
import type GraphSnap from './snap/GraphSnap';
|
||||||
|
import type GraphTooltip from './tooltip/GraphTooltip';
|
||||||
|
import type GraphTerminal from './terminal/GraphTerminal';
|
||||||
|
|
||||||
type PartialEvents = Pick<
|
type PartialEvents = Pick<
|
||||||
GraphEvents,
|
GraphEvents,
|
||||||
|
@ -81,7 +83,9 @@ type PartialEvents = Pick<
|
||||||
| 'isIgnoreTerminalEvent'
|
| 'isIgnoreTerminalEvent'
|
||||||
| 'isCloneEvent'
|
| 'isCloneEvent'
|
||||||
| 'isToggleEvent'
|
| 'isToggleEvent'
|
||||||
| 'getClickTolerance'
|
| 'getEventTolerance'
|
||||||
|
| 'isInvokesStopCellEditing'
|
||||||
|
| 'getPointForEvent'
|
||||||
>;
|
>;
|
||||||
type PartialSelection = Pick<
|
type PartialSelection = Pick<
|
||||||
GraphSelection,
|
GraphSelection,
|
||||||
|
@ -90,6 +94,13 @@ type PartialSelection = Pick<
|
||||||
| 'getSelectionCount'
|
| 'getSelectionCount'
|
||||||
| 'selectCellForEvent'
|
| 'selectCellForEvent'
|
||||||
| 'setSelectionCell'
|
| 'setSelectionCell'
|
||||||
|
| 'getSelectionCells'
|
||||||
|
| 'updateSelection'
|
||||||
|
| 'selectRegion'
|
||||||
|
| 'cellAdded'
|
||||||
|
| 'cellRemoved'
|
||||||
|
| 'getUpdatingSelectionResource'
|
||||||
|
| 'getDoneResource'
|
||||||
>;
|
>;
|
||||||
type PartialCells = Pick<
|
type PartialCells = Pick<
|
||||||
GraphCells,
|
GraphCells,
|
||||||
|
@ -100,6 +111,14 @@ type PartialCells = Pick<
|
||||||
| 'isCellsCloneable'
|
| 'isCellsCloneable'
|
||||||
| 'cloneCell'
|
| 'cloneCell'
|
||||||
| 'setCellStyles'
|
| 'setCellStyles'
|
||||||
|
| 'isCellMovable'
|
||||||
|
| 'isCellResizable'
|
||||||
|
| 'getChildCells'
|
||||||
|
| 'isCellRotatable'
|
||||||
|
| 'getCellContainmentArea'
|
||||||
|
| 'getCurrentCellStyle'
|
||||||
|
| 'resizeCell'
|
||||||
|
| 'removeStateForCell'
|
||||||
>;
|
>;
|
||||||
type PartialConnections = Pick<
|
type PartialConnections = Pick<
|
||||||
GraphConnections,
|
GraphConnections,
|
||||||
|
@ -108,17 +127,44 @@ type PartialConnections = Pick<
|
||||||
| 'isCellDisconnectable'
|
| 'isCellDisconnectable'
|
||||||
| 'getOutlineConstraint'
|
| 'getOutlineConstraint'
|
||||||
| 'connectCell'
|
| 'connectCell'
|
||||||
|
| 'getConnections'
|
||||||
|
| 'isConstrainChild'
|
||||||
|
| 'isValidSource'
|
||||||
>;
|
>;
|
||||||
type PartialEditing = Pick<GraphEditing, 'isEditing'>;
|
type PartialEditing = Pick<GraphEditing, 'isEditing' | 'stopEditing'>;
|
||||||
type PartialTooltip = Pick<GraphTooltip, 'getTooltip'>;
|
type PartialTooltip = Pick<GraphTooltip, 'getTooltip'>;
|
||||||
type PartialValidation = Pick<
|
type PartialValidation = Pick<
|
||||||
GraphValidation,
|
GraphValidation,
|
||||||
'getEdgeValidationError' | 'validationAlert'
|
'getEdgeValidationError' | 'validationAlert'
|
||||||
>;
|
>;
|
||||||
type PartialLabel = Pick<GraphLabel, 'isLabelMovable'>;
|
type PartialLabel = Pick<
|
||||||
|
GraphLabel,
|
||||||
|
'isLabelMovable' | 'isHtmlLabel' | 'isWrapping' | 'isLabelClipped' | 'getLabel'
|
||||||
|
>;
|
||||||
type PartialTerminal = Pick<GraphTerminal, 'isTerminalPointMovable'>;
|
type PartialTerminal = Pick<GraphTerminal, 'isTerminalPointMovable'>;
|
||||||
type PartialSnap = Pick<GraphSnap, 'snap' | 'getGridSize'>;
|
type PartialSnap = Pick<
|
||||||
type PartialEdge = Pick<GraphEdge, 'isAllowDanglingEdges' | 'isResetEdgesOnConnect'>;
|
GraphSnap,
|
||||||
|
'snap' | 'getGridSize' | 'isGridEnabled' | 'getSnapTolerance'
|
||||||
|
>;
|
||||||
|
type PartialEdge = Pick<
|
||||||
|
GraphEdge,
|
||||||
|
'isAllowDanglingEdges' | 'isResetEdgesOnConnect' | 'getEdges' | 'insertEdge' | 'addEdge'
|
||||||
|
>;
|
||||||
|
type PartialOverlays = Pick<GraphOverlays, 'getCellOverlays'>;
|
||||||
|
type PartialFolding = Pick<
|
||||||
|
GraphFolding,
|
||||||
|
'getFoldingImage' | 'isFoldingEnabled' | 'foldCells'
|
||||||
|
>;
|
||||||
|
type PartialPanning = Pick<
|
||||||
|
GraphPanning,
|
||||||
|
| 'panGraph'
|
||||||
|
| 'isUseScrollbarsForPanning'
|
||||||
|
| 'getPanDx'
|
||||||
|
| 'setPanDx'
|
||||||
|
| 'getPanDy'
|
||||||
|
| 'setPanDy'
|
||||||
|
>;
|
||||||
|
type PartialZoom = Pick<GraphZoom, 'zoomTo'>;
|
||||||
type PartialClass = PartialEvents &
|
type PartialClass = PartialEvents &
|
||||||
PartialSelection &
|
PartialSelection &
|
||||||
PartialCells &
|
PartialCells &
|
||||||
|
@ -130,10 +176,22 @@ type PartialClass = PartialEvents &
|
||||||
PartialTerminal &
|
PartialTerminal &
|
||||||
PartialSnap &
|
PartialSnap &
|
||||||
PartialEdge &
|
PartialEdge &
|
||||||
|
PartialOverlays &
|
||||||
|
PartialFolding &
|
||||||
|
PartialPanning &
|
||||||
|
PartialZoom &
|
||||||
EventSource;
|
EventSource;
|
||||||
|
|
||||||
export type MaxGraph = Graph & PartialClass;
|
export type MaxGraph = Graph & PartialClass;
|
||||||
|
|
||||||
|
const defaultPlugins: GraphPluginConstructor[] = [
|
||||||
|
TooltipHandler,
|
||||||
|
SelectionCellsHandler,
|
||||||
|
PopupMenuHandler,
|
||||||
|
ConnectionHandler,
|
||||||
|
GraphHandler,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends {@link EventSource} to implement a graph component for
|
* Extends {@link EventSource} to implement a graph component for
|
||||||
* the browser. This is the main class of the package. To activate
|
* the browser. This is the main class of the package. To activate
|
||||||
|
@ -151,12 +209,12 @@ export type MaxGraph = Graph & PartialClass;
|
||||||
* @class graph
|
* @class graph
|
||||||
* @extends {EventSource}
|
* @extends {EventSource}
|
||||||
*/
|
*/
|
||||||
// @ts-ignore
|
// @ts-ignore recursive reference error
|
||||||
class Graph extends autoImplement<PartialClass>() {
|
class Graph extends autoImplement<PartialClass>() {
|
||||||
constructor(
|
constructor(
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
model: Model,
|
model: Model,
|
||||||
plugins: GraphPlugin[] = [],
|
plugins: GraphPluginConstructor[] = defaultPlugins,
|
||||||
stylesheet: Stylesheet | null = null
|
stylesheet: Stylesheet | null = null
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
@ -165,7 +223,6 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.plugins = plugins;
|
this.plugins = plugins;
|
||||||
this.cellRenderer = this.createCellRenderer();
|
this.cellRenderer = this.createCellRenderer();
|
||||||
this.setSelectionModel(this.createSelectionModel());
|
|
||||||
this.setStylesheet(stylesheet != null ? stylesheet : this.createStylesheet());
|
this.setStylesheet(stylesheet != null ? stylesheet : this.createStylesheet());
|
||||||
this.view = this.createGraphView();
|
this.view = this.createGraphView();
|
||||||
|
|
||||||
|
@ -176,21 +233,6 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
|
|
||||||
this.getModel().addListener(InternalEvent.CHANGE, this.graphModelChangeListener);
|
this.getModel().addListener(InternalEvent.CHANGE, this.graphModelChangeListener);
|
||||||
|
|
||||||
// Installs basic event handlers with disabled default settings.
|
|
||||||
this.createHandlers();
|
|
||||||
|
|
||||||
// Initializes the display if a container was specified
|
|
||||||
this.init();
|
|
||||||
|
|
||||||
this.view.revalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the {@link container} and creates the respective datastructures.
|
|
||||||
*
|
|
||||||
* @param container DOM node that will contain the graph display.
|
|
||||||
*/
|
|
||||||
init() {
|
|
||||||
// Initializes the in-place editor
|
// Initializes the in-place editor
|
||||||
this.cellEditor = this.createCellEditor();
|
this.cellEditor = this.createCellEditor();
|
||||||
|
|
||||||
|
@ -200,22 +242,14 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
// Updates the size of the container for the current graph
|
// Updates the size of the container for the current graph
|
||||||
this.sizeDidChange();
|
this.sizeDidChange();
|
||||||
|
|
||||||
// Hides tooltips and resets tooltip timer if mouse leaves container
|
// Initiailzes plugins
|
||||||
InternalEvent.addListener(this.container, 'mouseleave', (evt: Event) => {
|
this.plugins.forEach((p: GraphPluginConstructor) => {
|
||||||
if (
|
this.pluginsMap.put(p.pluginId, new p(this));
|
||||||
this.tooltipHandler.div &&
|
|
||||||
this.tooltipHandler.div !== (<MouseEvent>evt).relatedTarget
|
|
||||||
) {
|
|
||||||
this.tooltipHandler.hide();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initiailzes plugins
|
this.view.revalidate();
|
||||||
this.plugins.forEach((p) => p.onInit(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Document me!
|
|
||||||
|
|
||||||
container: HTMLElement;
|
container: HTMLElement;
|
||||||
|
|
||||||
getContainer = () => this.container;
|
getContainer = () => this.container;
|
||||||
|
@ -224,21 +258,23 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
// @ts-ignore Cannot be null.
|
// @ts-ignore Cannot be null.
|
||||||
tooltipHandler: TooltipHandler;
|
// tooltipHandler: TooltipHandler;
|
||||||
// @ts-ignore Cannot be null.
|
// @ts-ignore Cannot be null.
|
||||||
selectionCellsHandler: SelectionCellsHandler;
|
// selectionCellsHandler: SelectionCellsHandler;
|
||||||
// @ts-ignore Cannot be null.
|
// @ts-ignore Cannot be null.
|
||||||
popupMenuHandler: PopupMenuHandler;
|
// popupMenuHandler: PopupMenuHandler;
|
||||||
// @ts-ignore Cannot be null.
|
// @ts-ignore Cannot be null.
|
||||||
connectionHandler: ConnectionHandler;
|
// connectionHandler: ConnectionHandler;
|
||||||
// @ts-ignore Cannot be null.
|
// @ts-ignore Cannot be null.
|
||||||
graphHandler: GraphHandler;
|
// graphHandler: GraphHandler;
|
||||||
|
|
||||||
getTooltipHandler = () => this.tooltipHandler;
|
getPlugin = (id: string) => this.pluginsMap.get(id) as unknown;
|
||||||
getSelectionCellsHandler = () => this.selectionCellsHandler;
|
|
||||||
getPopupMenuHandler = () => this.popupMenuHandler;
|
// getTooltipHandler = () => this.pluginsMap.get('TooltipHandler');
|
||||||
getConnectionHandler = () => this.connectionHandler;
|
// getSelectionCellsHandler = () => this.selectionCellsHandler;
|
||||||
getGraphHandler = () => this.graphHandler;
|
// getPopupMenuHandler = () => this.popupMenuHandler;
|
||||||
|
// getConnectionHandler = () => this.connectionHandler;
|
||||||
|
// getGraphHandler = () => this.graphHandler;
|
||||||
|
|
||||||
graphModelChangeListener: Function | null = null;
|
graphModelChangeListener: Function | null = null;
|
||||||
paintBackground: Function | null = null;
|
paintBackground: Function | null = null;
|
||||||
|
@ -252,7 +288,8 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
*/
|
*/
|
||||||
model: Model;
|
model: Model;
|
||||||
|
|
||||||
plugins: GraphPlugin[];
|
plugins: GraphPluginConstructor[];
|
||||||
|
pluginsMap: Dictionary<string, GraphPlugin> = new Dictionary();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the {@link GraphView} that caches the {@link CellState}s for the cells.
|
* Holds the {@link GraphView} that caches the {@link CellState}s for the cells.
|
||||||
|
@ -275,11 +312,6 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
stylesheet: Stylesheet;
|
stylesheet: Stylesheet;
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the {@link mxGraphSelectionModel} that models the current selection.
|
|
||||||
*/
|
|
||||||
selectionModel: mxGraphSelectionModel | null = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the {@link CellEditor} that is used as the in-place editing.
|
* Holds the {@link CellEditor} that is used as the in-place editing.
|
||||||
*/
|
*/
|
||||||
|
@ -591,62 +623,6 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the tooltip-, panning-, connection- and graph-handler (in this
|
|
||||||
* order). This is called in the constructor before {@link init} is called.
|
|
||||||
*/
|
|
||||||
createHandlers(): void {
|
|
||||||
this.tooltipHandler = this.createTooltipHandler();
|
|
||||||
this.tooltipHandler.setEnabled(false);
|
|
||||||
this.selectionCellsHandler = this.createSelectionCellsHandler();
|
|
||||||
this.connectionHandler = this.createConnectionHandler();
|
|
||||||
this.connectionHandler.setEnabled(false);
|
|
||||||
this.graphHandler = this.createGraphHandler();
|
|
||||||
this.popupMenuHandler = this.createPopupMenuHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and returns a new {@link TooltipHandler} to be used in this graph.
|
|
||||||
*/
|
|
||||||
createTooltipHandler() {
|
|
||||||
return new TooltipHandler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and returns a new {@link TooltipHandler} to be used in this graph.
|
|
||||||
*/
|
|
||||||
createSelectionCellsHandler(): SelectionCellsHandler {
|
|
||||||
return new SelectionCellsHandler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and returns a new {@link ConnectionHandler} to be used in this graph.
|
|
||||||
*/
|
|
||||||
createConnectionHandler(): ConnectionHandler {
|
|
||||||
return new ConnectionHandler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and returns a new {@link GraphHandler} to be used in this graph.
|
|
||||||
*/
|
|
||||||
createGraphHandler(): GraphHandler {
|
|
||||||
return new GraphHandler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and returns a new {@link PopupMenuHandler} to be used in this graph.
|
|
||||||
*/
|
|
||||||
createPopupMenuHandler(): PopupMenuHandler {
|
|
||||||
return new PopupMenuHandler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link mxGraphSelectionModel} to be used in this graph.
|
|
||||||
*/
|
|
||||||
createSelectionModel(): mxGraphSelectionModel {
|
|
||||||
return new mxGraphSelectionModel(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link mxGraphSelectionModel} to be used in this graph.
|
* Creates a new {@link mxGraphSelectionModel} to be used in this graph.
|
||||||
*/
|
*/
|
||||||
|
@ -732,7 +708,7 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
if (change instanceof RootChange) {
|
if (change instanceof RootChange) {
|
||||||
this.clearSelection();
|
this.clearSelection();
|
||||||
this.setDefaultParent(null);
|
this.setDefaultParent(null);
|
||||||
this.cells.removeStateForCell(change.previous);
|
this.removeStateForCell(change.previous);
|
||||||
|
|
||||||
if (this.resetViewOnRootChange) {
|
if (this.resetViewOnRootChange) {
|
||||||
this.view.scale = 1;
|
this.view.scale = 1;
|
||||||
|
@ -752,7 +728,7 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
|
|
||||||
if (!this.getModel().contains(newParent) || newParent.isCollapsed()) {
|
if (!this.getModel().contains(newParent) || newParent.isCollapsed()) {
|
||||||
this.view.invalidate(change.child, true, true);
|
this.view.invalidate(change.child, true, true);
|
||||||
this.cells.removeStateForCell(change.child);
|
this.removeStateForCell(change.child);
|
||||||
|
|
||||||
// Handles special case of current root of view being removed
|
// Handles special case of current root of view being removed
|
||||||
if (this.view.currentRoot == change.child) {
|
if (this.view.currentRoot == change.child) {
|
||||||
|
@ -803,7 +779,7 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
|
|
||||||
// Removes the state from the cache by default
|
// Removes the state from the cache by default
|
||||||
else if (change.cell != null && change.cell instanceof Cell) {
|
else if (change.cell != null && change.cell instanceof Cell) {
|
||||||
this.cells.removeStateForCell(change.cell);
|
this.removeStateForCell(change.cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1109,7 +1085,7 @@ class Graph extends autoImplement<PartialClass>() {
|
||||||
*
|
*
|
||||||
* @param state {@link mxCellState} whose handler should be created.
|
* @param state {@link mxCellState} whose handler should be created.
|
||||||
*/
|
*/
|
||||||
createHandler(state: CellState): EdgeHandler | VertexHandler | null {
|
createHandler(state: CellState) {
|
||||||
let result: EdgeHandler | VertexHandler | null = null;
|
let result: EdgeHandler | VertexHandler | null = null;
|
||||||
|
|
||||||
if (state.cell.isEdge()) {
|
if (state.cell.isEdge()) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ import CellHighlight from './selection/CellHighlight';
|
||||||
import Rectangle from './geometry/Rectangle';
|
import Rectangle from './geometry/Rectangle';
|
||||||
import { getClientX, getClientY, isAltDown, isMultiTouchEvent } from '../util/EventUtils';
|
import { getClientX, getClientY, isAltDown, isMultiTouchEvent } from '../util/EventUtils';
|
||||||
import { MaxGraph } from './Graph';
|
import { MaxGraph } from './Graph';
|
||||||
|
import { GraphPlugin, GraphPluginConstructor } from '../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class: mxGraphHandler
|
* Class: mxGraphHandler
|
||||||
|
@ -52,7 +53,9 @@ import { MaxGraph } from './Graph';
|
||||||
*
|
*
|
||||||
* graph - Reference to the enclosing <mxGraph>.
|
* graph - Reference to the enclosing <mxGraph>.
|
||||||
*/
|
*/
|
||||||
class GraphHandler {
|
class GraphHandler implements GraphPlugin {
|
||||||
|
static pluginId = 'GraphHandler';
|
||||||
|
|
||||||
constructor(graph: MaxGraph) {
|
constructor(graph: MaxGraph) {
|
||||||
this.graph = graph;
|
this.graph = graph;
|
||||||
this.graph.addMouseListener(this);
|
this.graph.addMouseListener(this);
|
||||||
|
@ -1798,7 +1801,7 @@ class GraphHandler {
|
||||||
* Destroys the handler and all its resources and DOM nodes.
|
* Destroys the handler and all its resources and DOM nodes.
|
||||||
*/
|
*/
|
||||||
// destroy(): void;
|
// destroy(): void;
|
||||||
destroy() {
|
onDestroy() {
|
||||||
this.graph.removeMouseListener(this);
|
this.graph.removeMouseListener(this);
|
||||||
this.graph.removeListener(this.panHandler);
|
this.graph.removeListener(this.panHandler);
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
DEFAULT_VALID_COLOR,
|
DEFAULT_VALID_COLOR,
|
||||||
MAX_HOTSPOT_SIZE,
|
MAX_HOTSPOT_SIZE,
|
||||||
MIN_HOTSPOT_SIZE,
|
MIN_HOTSPOT_SIZE,
|
||||||
|
NONE,
|
||||||
} from '../../util/Constants';
|
} from '../../util/Constants';
|
||||||
import CellHighlight from '../selection/CellHighlight';
|
import CellHighlight from '../selection/CellHighlight';
|
||||||
import EventObject from '../event/EventObject';
|
import EventObject from '../event/EventObject';
|
||||||
|
@ -273,11 +274,7 @@ class CellMarker extends EventSource {
|
||||||
*
|
*
|
||||||
* Sets and marks the current valid state.
|
* Sets and marks the current valid state.
|
||||||
*/
|
*/
|
||||||
setCurrentState(
|
setCurrentState(state: CellState | null, me: InternalMouseEvent, color?: ColorValue) {
|
||||||
state: CellState | null,
|
|
||||||
me: InternalMouseEvent,
|
|
||||||
color: ColorValue | null = null
|
|
||||||
) {
|
|
||||||
const isValid = state ? this.isValidState(state) : false;
|
const isValid = state ? this.isValidState(state) : false;
|
||||||
color = color ?? this.getMarkerColor(me.getEvent(), state, isValid);
|
color = color ?? this.getMarkerColor(me.getEvent(), state, isValid);
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,8 @@ import Model from '../model/Model';
|
||||||
import CellOverlay from './CellOverlay';
|
import CellOverlay from './CellOverlay';
|
||||||
import { getClientX, getClientY, getSource } from '../../util/EventUtils';
|
import { getClientX, getClientY, getSource } from '../../util/EventUtils';
|
||||||
import { isNode } from '../../util/DomUtils';
|
import { isNode } from '../../util/DomUtils';
|
||||||
|
import { CellStateStyles } from '../../types';
|
||||||
|
import CellArray from './datatypes/CellArray';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class: mxCellRenderer
|
* Class: mxCellRenderer
|
||||||
|
@ -119,6 +121,7 @@ class CellRenderer {
|
||||||
*
|
*
|
||||||
* Defines the default shape for edges. Default is <mxConnector>.
|
* Defines the default shape for edges. Default is <mxConnector>.
|
||||||
*/
|
*/
|
||||||
|
// @ts-expect-error The constructors for Shape and Connector are different.
|
||||||
defaultEdgeShape: typeof Shape = Connector;
|
defaultEdgeShape: typeof Shape = Connector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -264,12 +267,11 @@ class CellRenderer {
|
||||||
let ctor = this.getShape(state.style.shape);
|
let ctor = this.getShape(state.style.shape);
|
||||||
|
|
||||||
if (!ctor) {
|
if (!ctor) {
|
||||||
ctor = <typeof Shape>(
|
// @ts-expect-error The various Shape constructors are not compatible.
|
||||||
(state.cell.isEdge() ? this.defaultEdgeShape : this.defaultVertexShape)
|
ctor = state.cell.isEdge() ? this.defaultEdgeShape : this.defaultVertexShape;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctor;
|
return ctor as typeof Shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -286,12 +288,12 @@ class CellRenderer {
|
||||||
|
|
||||||
if (shape) {
|
if (shape) {
|
||||||
shape.apply(state);
|
shape.apply(state);
|
||||||
shape.imageSrc = state.getImage();
|
shape.imageSrc = state.getImageSrc();
|
||||||
shape.indicatorColor = state.getIndicatorColor();
|
shape.indicatorColor = state.getIndicatorColor();
|
||||||
shape.indicatorStrokeColor = state.style.indicatorStrokeColor;
|
shape.indicatorStrokeColor = state.style.indicatorStrokeColor;
|
||||||
shape.indicatorGradientColor = state.getIndicatorGradientColor();
|
shape.indicatorGradientColor = state.getIndicatorGradientColor();
|
||||||
shape.indicatorDirection = state.style.indicatorDirection;
|
shape.indicatorDirection = state.style.indicatorDirection;
|
||||||
shape.indicatorImage = state.getIndicatorImage();
|
shape.indicatorImageSrc = state.getIndicatorImageSrc();
|
||||||
this.postConfigureShape(state);
|
this.postConfigureShape(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,8 +306,8 @@ class CellRenderer {
|
||||||
* This implementation resolves these keywords on the fill, stroke
|
* This implementation resolves these keywords on the fill, stroke
|
||||||
* and gradient color keys.
|
* and gradient color keys.
|
||||||
*/
|
*/
|
||||||
postConfigureShape(state: CellState): void {
|
postConfigureShape(state: CellState) {
|
||||||
if (state.shape != null) {
|
if (state.shape) {
|
||||||
this.resolveColor(state, 'indicatorGradientColor', 'gradientColor');
|
this.resolveColor(state, 'indicatorGradientColor', 'gradientColor');
|
||||||
this.resolveColor(state, 'indicatorColor', 'fillColor');
|
this.resolveColor(state, 'indicatorColor', 'fillColor');
|
||||||
this.resolveColor(state, 'gradient', 'gradientColor');
|
this.resolveColor(state, 'gradient', 'gradientColor');
|
||||||
|
@ -320,14 +322,19 @@ class CellRenderer {
|
||||||
* Resolves special keywords 'inherit', 'indicated' and 'swimlane' and sets
|
* Resolves special keywords 'inherit', 'indicated' and 'swimlane' and sets
|
||||||
* the respective color on the shape.
|
* the respective color on the shape.
|
||||||
*/
|
*/
|
||||||
checkPlaceholderStyles(state: CellState): boolean {
|
checkPlaceholderStyles(state: CellState) {
|
||||||
// LATER: Check if the color has actually changed
|
// LATER: Check if the color has actually changed
|
||||||
if (state.style != null) {
|
if (state.style) {
|
||||||
const values = ['inherit', 'swimlane', 'indicated'];
|
const values = ['inherit', 'swimlane', 'indicated'];
|
||||||
const styles = ['fillColor', 'strokeColor', 'gradientColor', 'fontColor'];
|
const styles: (keyof CellStateStyles)[] = [
|
||||||
|
'fillColor',
|
||||||
|
'strokeColor',
|
||||||
|
'gradientColor',
|
||||||
|
'fontColor',
|
||||||
|
];
|
||||||
|
|
||||||
for (let i = 0; i < styles.length; i += 1) {
|
for (let i = 0; i < styles.length; i += 1) {
|
||||||
if (values.indexOf(state.style[styles[i]]) >= 0) {
|
if (values.indexOf(state.style[styles[i]] as string) >= 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,26 +348,25 @@ class CellRenderer {
|
||||||
* Resolves special keywords 'inherit', 'indicated' and 'swimlane' and sets
|
* Resolves special keywords 'inherit', 'indicated' and 'swimlane' and sets
|
||||||
* the respective color on the shape.
|
* the respective color on the shape.
|
||||||
*/
|
*/
|
||||||
resolveColor(state: CellState, field: string, key: string): void {
|
resolveColor(state: CellState, field: string, key: string) {
|
||||||
const shape = key === 'fontColor' ? state.text : state.shape;
|
const shape: Shape | null = key === 'fontColor' ? state.text : state.shape;
|
||||||
|
|
||||||
if (shape != null) {
|
if (shape) {
|
||||||
const { graph } = state.view;
|
const { graph } = state.view;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const value = shape[field];
|
const value = shape[field];
|
||||||
let referenced = null;
|
let referenced = null;
|
||||||
|
|
||||||
if (value === 'inherit') {
|
if (value === 'inherit') {
|
||||||
// @ts-ignore
|
|
||||||
referenced = state.cell.getParent();
|
referenced = state.cell.getParent();
|
||||||
|
/* disable swimlane for now
|
||||||
} else if (value === 'swimlane') {
|
} else if (value === 'swimlane') {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
shape[field] =
|
shape[field] =
|
||||||
key === 'strokeColor' || key === 'fontColor' ? '#000000' : '#ffffff';
|
key === 'strokeColor' || key === 'fontColor' ? '#000000' : '#ffffff';
|
||||||
|
|
||||||
// @ts-ignore
|
if (state.cell.getTerminal(false)) {
|
||||||
if (state.cell.getTerminal(false) != null) {
|
|
||||||
// @ts-ignore
|
|
||||||
referenced = state.cell.getTerminal(false);
|
referenced = state.cell.getTerminal(false);
|
||||||
} else {
|
} else {
|
||||||
referenced = state.cell;
|
referenced = state.cell;
|
||||||
|
@ -368,30 +374,27 @@ class CellRenderer {
|
||||||
|
|
||||||
referenced = graph.swimlane.getSwimlane(<Cell>referenced);
|
referenced = graph.swimlane.getSwimlane(<Cell>referenced);
|
||||||
key = graph.swimlane.swimlaneIndicatorColorAttribute;
|
key = graph.swimlane.swimlaneIndicatorColorAttribute;
|
||||||
} else if (value === 'indicated' && state.shape != null) {
|
*/
|
||||||
|
} else if (value === 'indicated' && state.shape) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
shape[field] = state.shape.indicatorColor;
|
shape[field] = state.shape.indicatorColor;
|
||||||
} else if (key !== 'fillColor' && value === 'fillColor' && state.shape != null) {
|
} else if (key !== 'fillColor' && value === 'fillColor' && state.shape) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
shape[field] = state.style.fillColor;
|
shape[field] = state.style.fillColor;
|
||||||
} else if (
|
} else if (key !== 'strokeColor' && value === 'strokeColor' && state.shape) {
|
||||||
key !== 'strokeColor' &&
|
|
||||||
value === 'strokeColor' &&
|
|
||||||
state.shape != null
|
|
||||||
) {
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
shape[field] = state.style.strokeColor;
|
shape[field] = state.style.strokeColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (referenced != null) {
|
if (referenced) {
|
||||||
const rstate = graph.getView().getState(referenced);
|
const rstate = graph.getView().getState(referenced);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
shape[field] = null;
|
shape[field] = null;
|
||||||
|
|
||||||
if (rstate != null) {
|
if (rstate) {
|
||||||
const rshape = key === 'fontColor' ? rstate.text : rstate.shape;
|
const rshape = key === 'fontColor' ? rstate.text : rstate.shape;
|
||||||
|
|
||||||
if (rshape != null && field !== 'indicatorColor') {
|
if (rshape && field !== 'indicatorColor') {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
shape[field] = rshape[field];
|
shape[field] = rshape[field];
|
||||||
} else {
|
} else {
|
||||||
|
@ -412,7 +415,6 @@ class CellRenderer {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> for which the label should be created.
|
* state - <mxCellState> for which the label should be created.
|
||||||
*/
|
*/
|
||||||
// getLabelValue(state: mxCellState): string;
|
|
||||||
getLabelValue(state: CellState) {
|
getLabelValue(state: CellState) {
|
||||||
return state.view.graph.getLabel(state.cell);
|
return state.view.graph.getLabel(state.cell);
|
||||||
}
|
}
|
||||||
|
@ -426,15 +428,12 @@ class CellRenderer {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> for which the label should be created.
|
* state - <mxCellState> for which the label should be created.
|
||||||
*/
|
*/
|
||||||
// createLabel(state: mxCellState, value: string): void;
|
createLabel(state: CellState, value: string) {
|
||||||
createLabel(state: CellState, value: any) {
|
|
||||||
const { graph } = state.view;
|
const { graph } = state.view;
|
||||||
const isEdge = state.cell.isEdge();
|
|
||||||
|
|
||||||
if (state.style.fontSize > 0 || state.style.fontSize == null) {
|
if (state.style.fontSize > 0 || state.style.fontSize == null) {
|
||||||
// Avoids using DOM node for empty labels
|
// Avoids using DOM node for empty labels
|
||||||
const isForceHtml =
|
const isForceHtml = graph.isHtmlLabel(state.cell) || isNode(value);
|
||||||
graph.isHtmlLabel(state.cell) || (value != null && isNode(value));
|
|
||||||
|
|
||||||
state.text = new this.defaultTextShape(
|
state.text = new this.defaultTextShape(
|
||||||
value,
|
value,
|
||||||
|
@ -459,8 +458,7 @@ class CellRenderer {
|
||||||
state.style.labelPadding,
|
state.style.labelPadding,
|
||||||
state.style.textDirection || DEFAULT_TEXT_DIRECTION
|
state.style.textDirection || DEFAULT_TEXT_DIRECTION
|
||||||
);
|
);
|
||||||
state.text.opacity =
|
state.text.opacity = state.style.textOpacity;
|
||||||
state.style.textOpacity == null ? 100 : state.style.textOpacity;
|
|
||||||
state.text.dialect = isForceHtml ? DIALECT_STRICTHTML : state.view.graph.dialect;
|
state.text.dialect = isForceHtml ? DIALECT_STRICTHTML : state.view.graph.dialect;
|
||||||
state.text.style = state.style;
|
state.text.style = state.style;
|
||||||
state.text.state = state;
|
state.text.state = state;
|
||||||
|
@ -474,7 +472,7 @@ class CellRenderer {
|
||||||
let forceGetCell = false;
|
let forceGetCell = false;
|
||||||
|
|
||||||
const getState = (evt: Event | InternalMouseEvent) => {
|
const getState = (evt: Event | InternalMouseEvent) => {
|
||||||
let result = state;
|
let result: CellState | null = state;
|
||||||
|
|
||||||
if (mxClient.IS_TOUCH || forceGetCell) {
|
if (mxClient.IS_TOUCH || forceGetCell) {
|
||||||
const x = getClientX(evt);
|
const x = getClientX(evt);
|
||||||
|
@ -483,7 +481,7 @@ class CellRenderer {
|
||||||
// Dispatches the drop event to the graph which
|
// Dispatches the drop event to the graph which
|
||||||
// consumes and executes the source function
|
// consumes and executes the source function
|
||||||
const pt = convertPoint(graph.container, x, y);
|
const pt = convertPoint(graph.container, x, y);
|
||||||
result = <CellState>graph.view.getState(graph.getCellAt(pt.x, pt.y));
|
result = graph.view.getState(graph.getCellAt(pt.x, pt.y) as Cell);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@ -491,29 +489,29 @@ class CellRenderer {
|
||||||
// TODO: Add handling for special touch device gestures
|
// TODO: Add handling for special touch device gestures
|
||||||
InternalEvent.addGestureListeners(
|
InternalEvent.addGestureListeners(
|
||||||
state.text.node,
|
state.text.node,
|
||||||
(evt: MouseEvent) => {
|
(evt: Event) => {
|
||||||
if (this.isLabelEvent(state, evt)) {
|
if (this.isLabelEvent(state, evt as MouseEvent)) {
|
||||||
graph.event.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_DOWN,
|
InternalEvent.MOUSE_DOWN,
|
||||||
new InternalMouseEvent(evt, state)
|
new InternalMouseEvent(evt as MouseEvent, state)
|
||||||
);
|
);
|
||||||
forceGetCell =
|
forceGetCell =
|
||||||
graph.dialect !== DIALECT_SVG && getSource(evt).nodeName === 'IMG';
|
graph.dialect !== DIALECT_SVG && getSource(evt).nodeName === 'IMG';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(evt: MouseEvent) => {
|
(evt: Event) => {
|
||||||
if (this.isLabelEvent(state, evt)) {
|
if (this.isLabelEvent(state, evt as MouseEvent)) {
|
||||||
graph.event.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_MOVE,
|
InternalEvent.MOUSE_MOVE,
|
||||||
new InternalMouseEvent(evt, getState(evt))
|
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(evt: MouseEvent) => {
|
(evt: Event) => {
|
||||||
if (this.isLabelEvent(state, evt)) {
|
if (this.isLabelEvent(state, evt as MouseEvent)) {
|
||||||
graph.event.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_UP,
|
InternalEvent.MOUSE_UP,
|
||||||
new InternalMouseEvent(evt, getState(evt))
|
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||||
);
|
);
|
||||||
forceGetCell = false;
|
forceGetCell = false;
|
||||||
}
|
}
|
||||||
|
@ -521,10 +519,10 @@ class CellRenderer {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Uses double click timeout in mxGraph for quirks mode
|
// Uses double click timeout in mxGraph for quirks mode
|
||||||
if (graph.event.nativeDblClickEnabled) {
|
if (graph.isNativeDblClickEnabled()) {
|
||||||
InternalEvent.addListener(state.text.node, 'dblclick', (evt: MouseEvent) => {
|
InternalEvent.addListener(state.text.node, 'dblclick', (evt: Event) => {
|
||||||
if (this.isLabelEvent(state, evt)) {
|
if (this.isLabelEvent(state, evt as MouseEvent)) {
|
||||||
graph.event.dblClick(evt, state.cell);
|
graph.dblClick(evt as MouseEvent, state.cell);
|
||||||
InternalEvent.consume(evt);
|
InternalEvent.consume(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -558,47 +556,37 @@ class CellRenderer {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> for which the overlay should be created.
|
* state - <mxCellState> for which the overlay should be created.
|
||||||
*/
|
*/
|
||||||
createCellOverlays(state: CellState): void {
|
createCellOverlays(state: CellState) {
|
||||||
const { graph } = state.view;
|
const { graph } = state.view;
|
||||||
const overlays = graph.getCellOverlays(state.cell);
|
const overlays = graph.getCellOverlays(state.cell);
|
||||||
let dict = null;
|
const dict = new Dictionary<CellOverlay, Shape>();
|
||||||
|
|
||||||
if (overlays != null) {
|
for (let i = 0; i < overlays.length; i += 1) {
|
||||||
dict = new Dictionary();
|
const shape = state.overlays.remove(overlays[i]);
|
||||||
|
|
||||||
for (let i = 0; i < overlays.length; i += 1) {
|
if (!shape) {
|
||||||
const shape = state.overlays != null ? state.overlays.remove(overlays[i]) : null;
|
const tmp = new ImageShape(new Rectangle(), overlays[i].image.src);
|
||||||
|
tmp.dialect = state.view.graph.dialect;
|
||||||
|
tmp.preserveImageAspect = false;
|
||||||
|
tmp.overlay = overlays[i];
|
||||||
|
this.initializeOverlay(state, tmp);
|
||||||
|
this.installCellOverlayListeners(state, overlays[i], tmp);
|
||||||
|
|
||||||
if (shape == null) {
|
if (overlays[i].cursor) {
|
||||||
const tmp = new ImageShape(
|
tmp.node.style.cursor = overlays[i].cursor;
|
||||||
new Rectangle(),
|
|
||||||
// @ts-ignore
|
|
||||||
overlays[i].image.src
|
|
||||||
);
|
|
||||||
tmp.dialect = state.view.graph.dialect;
|
|
||||||
tmp.preserveImageAspect = false;
|
|
||||||
tmp.overlay = overlays[i];
|
|
||||||
this.initializeOverlay(state, tmp);
|
|
||||||
this.installCellOverlayListeners(state, overlays[i], tmp);
|
|
||||||
|
|
||||||
if (overlays[i].cursor != null) {
|
|
||||||
// @ts-ignore
|
|
||||||
tmp.node.style.cursor = overlays[i].cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
dict.put(overlays[i], tmp);
|
|
||||||
} else {
|
|
||||||
dict.put(overlays[i], shape);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dict.put(overlays[i], tmp);
|
||||||
|
} else {
|
||||||
|
dict.put(overlays[i], shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes unused
|
// Removes unused
|
||||||
if (state.overlays != null) {
|
state.overlays.visit((id: any, shape: { destroy: () => void }) => {
|
||||||
state.overlays.visit((id: any, shape: { destroy: () => void }) => {
|
shape.destroy();
|
||||||
shape.destroy();
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
state.overlays = dict;
|
state.overlays = dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,7 +600,7 @@ class CellRenderer {
|
||||||
* state - <mxCellState> for which the overlay should be created.
|
* state - <mxCellState> for which the overlay should be created.
|
||||||
* overlay - <mxImageShape> that represents the overlay.
|
* overlay - <mxImageShape> that represents the overlay.
|
||||||
*/
|
*/
|
||||||
initializeOverlay(state: CellState, overlay: ImageShape): void {
|
initializeOverlay(state: CellState, overlay: ImageShape) {
|
||||||
overlay.init(state.view.getOverlayPane());
|
overlay.init(state.view.getOverlayPane());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,16 +610,12 @@ class CellRenderer {
|
||||||
* Installs the listeners for the given <mxCellState>, <mxCellOverlay> and
|
* Installs the listeners for the given <mxCellState>, <mxCellOverlay> and
|
||||||
* <mxShape> that represents the overlay.
|
* <mxShape> that represents the overlay.
|
||||||
*/
|
*/
|
||||||
installCellOverlayListeners(
|
installCellOverlayListeners(state: CellState, overlay: CellOverlay, shape: Shape) {
|
||||||
state: CellState,
|
|
||||||
overlay: CellOverlay,
|
|
||||||
shape: Shape
|
|
||||||
): void {
|
|
||||||
const { graph } = state.view;
|
const { graph } = state.view;
|
||||||
|
|
||||||
InternalEvent.addListener(shape.node, 'click', (evt: Event) => {
|
InternalEvent.addListener(shape.node, 'click', (evt: Event) => {
|
||||||
if (graph.editing.isEditing()) {
|
if (graph.isEditing()) {
|
||||||
graph.editing.stopEditing(!graph.editing.isInvokesStopCellEditing());
|
graph.stopEditing(!graph.isInvokesStopCellEditing());
|
||||||
}
|
}
|
||||||
|
|
||||||
overlay.fireEvent(
|
overlay.fireEvent(
|
||||||
|
@ -645,9 +629,9 @@ class CellRenderer {
|
||||||
InternalEvent.consume(evt);
|
InternalEvent.consume(evt);
|
||||||
},
|
},
|
||||||
(evt: Event) => {
|
(evt: Event) => {
|
||||||
graph.event.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_MOVE,
|
InternalEvent.MOUSE_MOVE,
|
||||||
new InternalMouseEvent(evt, state)
|
new InternalMouseEvent(evt as MouseEvent, state)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -670,12 +654,12 @@ class CellRenderer {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> for which the control should be created.
|
* state - <mxCellState> for which the control should be created.
|
||||||
*/
|
*/
|
||||||
createControl(state: CellState): void {
|
createControl(state: CellState) {
|
||||||
const { graph } = state.view;
|
const { graph } = state.view;
|
||||||
const image = graph.getFoldingImage(state);
|
const image = graph.getFoldingImage(state);
|
||||||
|
|
||||||
if (graph.foldingEnabled && image != null) {
|
if (graph.isFoldingEnabled() && image) {
|
||||||
if (state.control == null) {
|
if (!state.control) {
|
||||||
const b = new Rectangle(0, 0, image.width, image.height);
|
const b = new Rectangle(0, 0, image.width, image.height);
|
||||||
state.control = new ImageShape(b, image.src);
|
state.control = new ImageShape(b, image.src);
|
||||||
state.control.preserveImageAspect = false;
|
state.control.preserveImageAspect = false;
|
||||||
|
@ -688,7 +672,7 @@ class CellRenderer {
|
||||||
this.createControlClickHandler(state)
|
this.createControlClickHandler(state)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (state.control != null) {
|
} else if (state.control) {
|
||||||
state.control.destroy();
|
state.control.destroy();
|
||||||
state.control = null;
|
state.control = null;
|
||||||
}
|
}
|
||||||
|
@ -703,13 +687,13 @@ class CellRenderer {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> whose control click handler should be returned.
|
* state - <mxCellState> whose control click handler should be returned.
|
||||||
*/
|
*/
|
||||||
createControlClickHandler(state: CellState): Function {
|
createControlClickHandler(state: CellState) {
|
||||||
const { graph } = state.view;
|
const { graph } = state.view;
|
||||||
|
|
||||||
return (evt: EventObject) => {
|
return (evt: Event) => {
|
||||||
if (this.forceControlClickHandler || graph.isEnabled()) {
|
if (this.forceControlClickHandler || graph.isEnabled()) {
|
||||||
const collapse = !state.cell.isCollapsed();
|
const collapse = !state.cell.isCollapsed();
|
||||||
graph.foldCells(collapse, false, [state.cell], false, evt);
|
graph.foldCells(collapse, false, new CellArray(state.cell), false, evt);
|
||||||
InternalEvent.consume(evt);
|
InternalEvent.consume(evt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -731,7 +715,7 @@ class CellRenderer {
|
||||||
state: CellState,
|
state: CellState,
|
||||||
control: Shape,
|
control: Shape,
|
||||||
handleEvents: boolean,
|
handleEvents: boolean,
|
||||||
clickHandler: Function
|
clickHandler: EventListener
|
||||||
): Element {
|
): Element {
|
||||||
const { graph } = state.view;
|
const { graph } = state.view;
|
||||||
|
|
||||||
|
@ -744,8 +728,7 @@ class CellRenderer {
|
||||||
if (isForceHtml) {
|
if (isForceHtml) {
|
||||||
control.dialect = DIALECT_PREFERHTML;
|
control.dialect = DIALECT_PREFERHTML;
|
||||||
control.init(graph.container);
|
control.init(graph.container);
|
||||||
// @ts-ignore
|
control.node.style.zIndex = String(1);
|
||||||
control.node.style.zIndex = 1;
|
|
||||||
} else {
|
} else {
|
||||||
control.init(state.view.getOverlayPane());
|
control.init(state.view.getOverlayPane());
|
||||||
}
|
}
|
||||||
|
@ -753,9 +736,8 @@ class CellRenderer {
|
||||||
const node = control.node;
|
const node = control.node;
|
||||||
|
|
||||||
// Workaround for missing click event on iOS is to check tolerance below
|
// Workaround for missing click event on iOS is to check tolerance below
|
||||||
if (clickHandler != null && !mxClient.IS_IOS) {
|
if (clickHandler && !mxClient.IS_IOS) {
|
||||||
if (graph.isEnabled()) {
|
if (graph.isEnabled()) {
|
||||||
// @ts-ignore
|
|
||||||
node.style.cursor = 'pointer';
|
node.style.cursor = 'pointer';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -769,35 +751,34 @@ class CellRenderer {
|
||||||
node,
|
node,
|
||||||
(evt: Event) => {
|
(evt: Event) => {
|
||||||
first = new Point(getClientX(evt), getClientY(evt));
|
first = new Point(getClientX(evt), getClientY(evt));
|
||||||
graph.event.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_DOWN,
|
InternalEvent.MOUSE_DOWN,
|
||||||
new InternalMouseEvent(evt, state)
|
new InternalMouseEvent(evt as MouseEvent, state)
|
||||||
);
|
);
|
||||||
InternalEvent.consume(evt);
|
InternalEvent.consume(evt);
|
||||||
},
|
},
|
||||||
(evt: Event) => {
|
(evt: Event) => {
|
||||||
graph.event.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_MOVE,
|
InternalEvent.MOUSE_MOVE,
|
||||||
new InternalMouseEvent(evt, state)
|
new InternalMouseEvent(evt as MouseEvent, state)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(evt: Event) => {
|
(evt: Event) => {
|
||||||
graph.event.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_UP,
|
InternalEvent.MOUSE_UP,
|
||||||
new InternalMouseEvent(evt, state)
|
new InternalMouseEvent(evt as MouseEvent, state)
|
||||||
);
|
);
|
||||||
InternalEvent.consume(evt);
|
InternalEvent.consume(evt);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Uses capture phase for event interception to stop bubble phase
|
// Uses capture phase for event interception to stop bubble phase
|
||||||
if (clickHandler != null && mxClient.IS_IOS) {
|
if (clickHandler && mxClient.IS_IOS) {
|
||||||
// @ts-ignore
|
|
||||||
node.addEventListener(
|
node.addEventListener(
|
||||||
'touchend',
|
'touchend',
|
||||||
(evt) => {
|
(evt) => {
|
||||||
if (first != null) {
|
if (first) {
|
||||||
const tol = graph.tolerance;
|
const tol = graph.getEventTolerance();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
Math.abs(first.x - getClientX(evt)) < tol &&
|
Math.abs(first.x - getClientX(evt)) < tol &&
|
||||||
|
@ -827,7 +808,7 @@ class CellRenderer {
|
||||||
* state - <mxCellState> whose shape fired the event.
|
* state - <mxCellState> whose shape fired the event.
|
||||||
* evt - Mouse event which was fired.
|
* evt - Mouse event which was fired.
|
||||||
*/
|
*/
|
||||||
isShapeEvent(state: CellState, evt: InternalMouseEvent | MouseEvent): boolean {
|
isShapeEvent(state: CellState, evt: InternalMouseEvent | MouseEvent) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -842,7 +823,7 @@ class CellRenderer {
|
||||||
* state - <mxCellState> whose label fired the event.
|
* state - <mxCellState> whose label fired the event.
|
||||||
* evt - Mouse event which was fired.
|
* evt - Mouse event which was fired.
|
||||||
*/
|
*/
|
||||||
isLabelEvent(state: CellState, evt: InternalMouseEvent | MouseEvent): boolean {
|
isLabelEvent(state: CellState, evt: InternalMouseEvent | MouseEvent) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -855,14 +836,14 @@ class CellRenderer {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> for which the event listeners should be isntalled.
|
* state - <mxCellState> for which the event listeners should be isntalled.
|
||||||
*/
|
*/
|
||||||
installListeners(state: CellState): void {
|
installListeners(state: CellState) {
|
||||||
const { graph } = state.view;
|
const { graph } = state.view;
|
||||||
|
|
||||||
// Workaround for touch devices routing all events for a mouse
|
// Workaround for touch devices routing all events for a mouse
|
||||||
// gesture (down, move, up) via the initial DOM node. Same for
|
// gesture (down, move, up) via the initial DOM node. Same for
|
||||||
// HTML images in all IE versions (VML images are working).
|
// HTML images in all IE versions (VML images are working).
|
||||||
const getState = (evt: Event) => {
|
const getState = (evt: Event) => {
|
||||||
let result = state;
|
let result: CellState | null = state;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(graph.dialect !== DIALECT_SVG && getSource(evt).nodeName === 'IMG') ||
|
(graph.dialect !== DIALECT_SVG && getSource(evt).nodeName === 'IMG') ||
|
||||||
|
@ -874,7 +855,7 @@ class CellRenderer {
|
||||||
// Dispatches the drop event to the graph which
|
// Dispatches the drop event to the graph which
|
||||||
// consumes and executes the source function
|
// consumes and executes the source function
|
||||||
const pt = convertPoint(graph.container, x, y);
|
const pt = convertPoint(graph.container, x, y);
|
||||||
result = <CellState>graph.view.getState(graph.getCellAt(pt.x, pt.y));
|
result = graph.view.getState(graph.getCellAt(pt.x, pt.y) as Cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -883,38 +864,38 @@ class CellRenderer {
|
||||||
InternalEvent.addGestureListeners(
|
InternalEvent.addGestureListeners(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
state.shape.node,
|
state.shape.node,
|
||||||
(evt: MouseEvent) => {
|
(evt: Event) => {
|
||||||
if (this.isShapeEvent(state, evt)) {
|
if (this.isShapeEvent(state, evt as MouseEvent)) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_DOWN,
|
InternalEvent.MOUSE_DOWN,
|
||||||
new InternalMouseEvent(evt, state)
|
new InternalMouseEvent(evt as MouseEvent, state)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(evt: MouseEvent) => {
|
(evt: Event) => {
|
||||||
if (this.isShapeEvent(state, evt)) {
|
if (this.isShapeEvent(state, evt as MouseEvent)) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_MOVE,
|
InternalEvent.MOUSE_MOVE,
|
||||||
new InternalMouseEvent(evt, getState(evt))
|
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(evt: MouseEvent) => {
|
(evt: Event) => {
|
||||||
if (this.isShapeEvent(state, evt)) {
|
if (this.isShapeEvent(state, evt as MouseEvent)) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_UP,
|
InternalEvent.MOUSE_UP,
|
||||||
new InternalMouseEvent(evt, getState(evt))
|
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Uses double click timeout in mxGraph for quirks mode
|
// Uses double click timeout in mxGraph for quirks mode
|
||||||
if (graph.nativeDblClickEnabled) {
|
if (graph.isNativeDblClickEnabled()) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
InternalEvent.addListener(state.shape.node, 'dblclick', (evt) => {
|
InternalEvent.addListener(state.shape.node, 'dblclick', (evt) => {
|
||||||
if (this.isShapeEvent(state, evt)) {
|
if (this.isShapeEvent(state, evt as MouseEvent)) {
|
||||||
graph.dblClick(evt, state.cell);
|
graph.dblClick(evt as MouseEvent, state.cell);
|
||||||
InternalEvent.consume(evt);
|
InternalEvent.consume(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -930,22 +911,22 @@ class CellRenderer {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> whose label should be redrawn.
|
* state - <mxCellState> whose label should be redrawn.
|
||||||
*/
|
*/
|
||||||
redrawLabel(state: CellState, forced: boolean): void {
|
redrawLabel(state: CellState, forced: boolean) {
|
||||||
const { graph } = state.view;
|
const { graph } = state.view;
|
||||||
const value = this.getLabelValue(state);
|
const value = this.getLabelValue(state);
|
||||||
const wrapping = graph.isWrapping(state.cell);
|
const wrapping = graph.isWrapping(state.cell);
|
||||||
const clipping = graph.isLabelClipped(state.cell);
|
const clipping = graph.isLabelClipped(state.cell);
|
||||||
const isForceHtml =
|
const isForceHtml =
|
||||||
state.view.graph.isHtmlLabel(state.cell) || (value != null && isNode(value));
|
state.view.graph.isHtmlLabel(state.cell) || (value && isNode(value));
|
||||||
const dialect = isForceHtml ? DIALECT_STRICTHTML : state.view.graph.dialect;
|
const dialect = isForceHtml ? DIALECT_STRICTHTML : state.view.graph.dialect;
|
||||||
const overflow = state.style.overflow || 'visible';
|
const overflow = state.style.overflow || 'visible';
|
||||||
|
|
||||||
if (
|
if (
|
||||||
state.text != null &&
|
state.text &&
|
||||||
(state.text.wrap != wrapping ||
|
(state.text.wrap !== wrapping ||
|
||||||
state.text.clipped != clipping ||
|
state.text.clipped !== clipping ||
|
||||||
state.text.overflow != overflow ||
|
state.text.overflow !== overflow ||
|
||||||
state.text.dialect != dialect)
|
state.text.dialect !== dialect)
|
||||||
) {
|
) {
|
||||||
state.text.destroy();
|
state.text.destroy();
|
||||||
state.text = null;
|
state.text = null;
|
||||||
|
@ -972,7 +953,7 @@ class CellRenderer {
|
||||||
state.text.apply(state);
|
state.text.apply(state);
|
||||||
|
|
||||||
// Special case where value is obtained via hook in graph
|
// Special case where value is obtained via hook in graph
|
||||||
state.text.valign = <string>state.getVerticalAlign();
|
state.text.valign = state.getVerticalAlign();
|
||||||
}
|
}
|
||||||
|
|
||||||
const bounds = this.getLabelBounds(state);
|
const bounds = this.getLabelBounds(state);
|
||||||
|
@ -1597,23 +1578,23 @@ class CellRenderer {
|
||||||
if (state.shape.indicatorShape != null) {
|
if (state.shape.indicatorShape != null) {
|
||||||
state.shape.indicator = new state.shape.indicatorShape();
|
state.shape.indicator = new state.shape.indicatorShape();
|
||||||
state.shape.indicator.dialect = state.shape.dialect;
|
state.shape.indicator.dialect = state.shape.dialect;
|
||||||
state.shape.indicator.init(state.node);
|
state.shape.indicator.init(state.node as HTMLElement);
|
||||||
force = true;
|
force = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.shape != null) {
|
if (state.shape) {
|
||||||
// Handles changes of the collapse icon
|
// Handles changes of the collapse icon
|
||||||
this.createControl(state);
|
this.createControl(state);
|
||||||
|
|
||||||
// Redraws the cell if required, ignores changes to bounds if points are
|
// Redraws the cell if required, ignores changes to bounds if points are
|
||||||
// defined as the bounds are updated for the given points inside the shape
|
// defined as the bounds are updated for the given points inside the shape
|
||||||
if (force || this.isShapeInvalid(state, state.shape)) {
|
if (force || this.isShapeInvalid(state, state.shape)) {
|
||||||
if (state.absolutePoints != null) {
|
if (state.absolutePoints.length > 0) {
|
||||||
state.shape.points = state.absolutePoints.slice();
|
state.shape.points = state.absolutePoints.slice();
|
||||||
state.shape.bounds = null;
|
state.shape.bounds = null;
|
||||||
} else {
|
} else {
|
||||||
state.shape.points = null;
|
state.shape.points = [];
|
||||||
state.shape.bounds = new Rectangle(state.x, state.y, state.width, state.height);
|
state.shape.bounds = new Rectangle(state.x, state.y, state.width, state.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1664,22 +1645,20 @@ class CellRenderer {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> for which the shapes should be destroyed.
|
* state - <mxCellState> for which the shapes should be destroyed.
|
||||||
*/
|
*/
|
||||||
destroy(state: CellState): void {
|
destroy(state: CellState) {
|
||||||
if (state.shape != null) {
|
if (state.shape) {
|
||||||
if (state.text != null) {
|
if (state.text) {
|
||||||
state.text.destroy();
|
state.text.destroy();
|
||||||
state.text = null;
|
state.text = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.overlays != null) {
|
state.overlays.visit((id: string, shape: Shape) => {
|
||||||
state.overlays.visit((id: string, shape: Shape) => {
|
shape.destroy();
|
||||||
shape.destroy();
|
});
|
||||||
});
|
|
||||||
|
|
||||||
state.overlays = null;
|
state.overlays = new Dictionary();
|
||||||
}
|
|
||||||
|
|
||||||
if (state.control != null) {
|
if (state.control) {
|
||||||
state.control.destroy();
|
state.control.destroy();
|
||||||
state.control = null;
|
state.control = null;
|
||||||
}
|
}
|
||||||
|
@ -1699,6 +1678,7 @@ CellRenderer.registerShape(SHAPE_ELLIPSE, EllipseShape);
|
||||||
CellRenderer.registerShape(SHAPE_RHOMBUS, RhombusShape);
|
CellRenderer.registerShape(SHAPE_RHOMBUS, RhombusShape);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_CYLINDER, CylinderShape);
|
CellRenderer.registerShape(SHAPE_CYLINDER, CylinderShape);
|
||||||
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_CONNECTOR, Connector);
|
CellRenderer.registerShape(SHAPE_CONNECTOR, Connector);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_ACTOR, Actor);
|
CellRenderer.registerShape(SHAPE_ACTOR, Actor);
|
||||||
|
@ -1706,14 +1686,19 @@ CellRenderer.registerShape(SHAPE_TRIANGLE, TriangleShape);
|
||||||
CellRenderer.registerShape(SHAPE_HEXAGON, HexagonShape);
|
CellRenderer.registerShape(SHAPE_HEXAGON, HexagonShape);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_CLOUD, CloudShape);
|
CellRenderer.registerShape(SHAPE_CLOUD, CloudShape);
|
||||||
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_LINE, Line);
|
CellRenderer.registerShape(SHAPE_LINE, Line);
|
||||||
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_ARROW, Arrow);
|
CellRenderer.registerShape(SHAPE_ARROW, Arrow);
|
||||||
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_ARROW_CONNECTOR, ArrowConnector);
|
CellRenderer.registerShape(SHAPE_ARROW_CONNECTOR, ArrowConnector);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_DOUBLE_ELLIPSE, DoubleEllipseShape);
|
CellRenderer.registerShape(SHAPE_DOUBLE_ELLIPSE, DoubleEllipseShape);
|
||||||
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_SWIMLANE, SwimlaneShape);
|
CellRenderer.registerShape(SHAPE_SWIMLANE, SwimlaneShape);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_IMAGE, ImageShape);
|
CellRenderer.registerShape(SHAPE_IMAGE, ImageShape);
|
||||||
|
// @ts-ignore
|
||||||
CellRenderer.registerShape(SHAPE_LABEL, Label);
|
CellRenderer.registerShape(SHAPE_LABEL, Label);
|
||||||
|
|
||||||
export default CellRenderer;
|
export default CellRenderer;
|
||||||
|
|
|
@ -97,7 +97,7 @@ class TemporaryCellStates {
|
||||||
/**
|
/**
|
||||||
* Holds the states of the rectangle.
|
* Holds the states of the rectangle.
|
||||||
*/
|
*/
|
||||||
oldStates: Dictionary<string, CellState>;
|
oldStates: Dictionary<Cell, CellState>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the bounds of the rectangle.
|
* Holds the bounds of the rectangle.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Cell from "./datatypes/Cell";
|
import Cell from './datatypes/Cell';
|
||||||
import CellArray from "./datatypes/CellArray";
|
import CellArray from './datatypes/CellArray';
|
||||||
import Dictionary from "../../util/Dictionary";
|
import Dictionary from '../../util/Dictionary';
|
||||||
import Graph from "../Graph";
|
import Graph from '../Graph';
|
||||||
|
|
||||||
class TreeTraversal {
|
class TreeTraversal {
|
||||||
dependencies = ['connections'];
|
dependencies = ['connections'];
|
||||||
|
@ -42,7 +42,7 @@ class TreeTraversal {
|
||||||
|
|
||||||
for (const cell of parent.getChildren()) {
|
for (const cell of parent.getChildren()) {
|
||||||
if (cell.isVertex() && cell.isVisible()) {
|
if (cell.isVertex() && cell.isVisible()) {
|
||||||
const conns = this.graph.connection.getConnections(cell, isolate ? parent : null);
|
const conns = this.graph.getConnections(cell, isolate ? parent : null);
|
||||||
let fanOut = 0;
|
let fanOut = 0;
|
||||||
let fanIn = 0;
|
let fanIn = 0;
|
||||||
|
|
||||||
|
@ -117,13 +117,13 @@ class TreeTraversal {
|
||||||
directed: boolean = true,
|
directed: boolean = true,
|
||||||
func: Function | null = null,
|
func: Function | null = null,
|
||||||
edge: Cell | null = null,
|
edge: Cell | null = null,
|
||||||
visited: Dictionary | null = null,
|
visited: Dictionary<Cell, boolean> | null = null,
|
||||||
inverse: boolean = false
|
inverse: boolean = false
|
||||||
): void {
|
): void {
|
||||||
if (func != null && vertex != null) {
|
if (func != null && vertex != null) {
|
||||||
directed = directed != null ? directed : true;
|
directed = directed != null ? directed : true;
|
||||||
inverse = inverse != null ? inverse : false;
|
inverse = inverse != null ? inverse : false;
|
||||||
visited = visited || new Dictionary();
|
visited = visited || new Dictionary<Cell, boolean>();
|
||||||
|
|
||||||
if (!visited.get(vertex)) {
|
if (!visited.get(vertex)) {
|
||||||
visited.put(vertex, true);
|
visited.put(vertex, true);
|
||||||
|
|
|
@ -15,6 +15,7 @@ import Dictionary from '../../../util/Dictionary';
|
||||||
import { NONE } from '../../../util/Constants';
|
import { NONE } from '../../../util/Constants';
|
||||||
import { CellStateStyles } from 'packages/core/src/types';
|
import { CellStateStyles } from 'packages/core/src/types';
|
||||||
import RectangleShape from '../../geometry/shape/node/RectangleShape';
|
import RectangleShape from '../../geometry/shape/node/RectangleShape';
|
||||||
|
import CellOverlay from '../CellOverlay';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class: mxCellState
|
* Class: mxCellState
|
||||||
|
@ -67,7 +68,7 @@ class CellState extends Rectangle {
|
||||||
control: Shape | null = null;
|
control: Shape | null = null;
|
||||||
|
|
||||||
// Used by mxCellRenderer's createCellOverlays()
|
// Used by mxCellRenderer's createCellOverlays()
|
||||||
overlays: Dictionary<Cell, Shape> | null = null;
|
overlays: Dictionary<CellOverlay, Shape> = new Dictionary();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: view
|
* Variable: view
|
||||||
|
@ -197,6 +198,8 @@ class CellState extends Rectangle {
|
||||||
|
|
||||||
parentHighlight: RectangleShape | null = null;
|
parentHighlight: RectangleShape | null = null;
|
||||||
|
|
||||||
|
point: Point | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function: getPerimeterBounds
|
* Function: getPerimeterBounds
|
||||||
*
|
*
|
||||||
|
|
|
@ -63,6 +63,7 @@ import InternalMouseEvent from '../../event/InternalMouseEvent';
|
||||||
import Cell from '../datatypes/Cell';
|
import Cell from '../datatypes/Cell';
|
||||||
import ImageBox from '../../image/ImageBox';
|
import ImageBox from '../../image/ImageBox';
|
||||||
import Marker from '../../geometry/shape/edge/Marker';
|
import Marker from '../../geometry/shape/edge/Marker';
|
||||||
|
import EventSource from '../../event/EventSource';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Graph event handler that reconnects edges and modifies control points and the edge
|
* Graph event handler that reconnects edges and modifies control points and the edge
|
||||||
|
@ -381,8 +382,12 @@ class EdgeHandler {
|
||||||
|
|
||||||
customHandles: CellHandle[] = [];
|
customHandles: CellHandle[] = [];
|
||||||
|
|
||||||
startX: number = 0;
|
startX = 0;
|
||||||
startY: number = 0;
|
startY = 0;
|
||||||
|
|
||||||
|
outline = true;
|
||||||
|
|
||||||
|
active = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function: isParentHighlightVisible
|
* Function: isParentHighlightVisible
|
||||||
|
@ -461,7 +466,7 @@ class EdgeHandler {
|
||||||
* <virtualBendsEnabled> is true and the current style allows and
|
* <virtualBendsEnabled> is true and the current style allows and
|
||||||
* renders custom waypoints.
|
* renders custom waypoints.
|
||||||
*/
|
*/
|
||||||
isVirtualBendsEnabled(evt: Event) {
|
isVirtualBendsEnabled(evt?: Event) {
|
||||||
return (
|
return (
|
||||||
this.virtualBendsEnabled &&
|
this.virtualBendsEnabled &&
|
||||||
(this.state.style.edge == null ||
|
(this.state.style.edge == null ||
|
||||||
|
@ -970,7 +975,7 @@ class EdgeHandler {
|
||||||
* control point. The source and target points are used for reconnecting
|
* control point. The source and target points are used for reconnecting
|
||||||
* the edge.
|
* the edge.
|
||||||
*/
|
*/
|
||||||
mouseDown(sender: Listenable, me: InternalMouseEvent) {
|
mouseDown(sender: EventSource, me: InternalMouseEvent) {
|
||||||
const handle = this.getHandleForEvent(me);
|
const handle = this.getHandleForEvent(me);
|
||||||
|
|
||||||
if (handle !== null && this.bends[handle]) {
|
if (handle !== null && this.bends[handle]) {
|
||||||
|
@ -1284,7 +1289,7 @@ class EdgeHandler {
|
||||||
|
|
||||||
// Removes point if user tries to straighten a segment
|
// Removes point if user tries to straighten a segment
|
||||||
if (!result && this.straightRemoveEnabled && (!me || !isAltDown(me.getEvent()))) {
|
if (!result && this.straightRemoveEnabled && (!me || !isAltDown(me.getEvent()))) {
|
||||||
const tol = this.graph.getClickTolerance() * this.graph.getClickTolerance();
|
const tol = this.graph.getEventTolerance() * this.graph.getEventTolerance();
|
||||||
const abs = this.state.absolutePoints.slice();
|
const abs = this.state.absolutePoints.slice();
|
||||||
abs[this.index] = pt;
|
abs[this.index] = pt;
|
||||||
|
|
||||||
|
@ -1517,8 +1522,7 @@ class EdgeHandler {
|
||||||
*
|
*
|
||||||
* Handles the event by updating the preview.
|
* Handles the event by updating the preview.
|
||||||
*/
|
*/
|
||||||
// mouseMove(sender: any, me: mxMouseEvent): void;
|
mouseMove(sender: EventSource, me: InternalMouseEvent) {
|
||||||
mouseMove(sender: Listenable, me: InternalMouseEvent) {
|
|
||||||
if (this.index != null && this.marker != null) {
|
if (this.index != null && this.marker != null) {
|
||||||
this.currentPoint = this.getPointForEvent(me);
|
this.currentPoint = this.getPointForEvent(me);
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
@ -1628,7 +1632,7 @@ class EdgeHandler {
|
||||||
* Handles the event to applying the previewed changes on the edge by
|
* Handles the event to applying the previewed changes on the edge by
|
||||||
* using <moveLabel>, <connect> or <changePoints>.
|
* using <moveLabel>, <connect> or <changePoints>.
|
||||||
*/
|
*/
|
||||||
mouseUp(sender: Listenable, me: InternalMouseEvent) {
|
mouseUp(sender: EventSource, me: InternalMouseEvent) {
|
||||||
// Workaround for wrong event source in Webkit
|
// Workaround for wrong event source in Webkit
|
||||||
if (this.index != null && this.marker != null) {
|
if (this.index != null && this.marker != null) {
|
||||||
if (this.shape != null && this.shape.node != null) {
|
if (this.shape != null && this.shape.node != null) {
|
||||||
|
@ -1675,7 +1679,7 @@ class EdgeHandler {
|
||||||
} else if (this.isLabel && this.label) {
|
} else if (this.isLabel && this.label) {
|
||||||
this.moveLabel(this.state, this.label.x, this.label.y);
|
this.moveLabel(this.state, this.label.x, this.label.y);
|
||||||
} else if (this.isSource || this.isTarget) {
|
} else if (this.isSource || this.isTarget) {
|
||||||
let terminal = null;
|
let terminal: Cell | null = null;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.constraintHandler.currentConstraint != null &&
|
this.constraintHandler.currentConstraint != null &&
|
||||||
|
@ -1685,17 +1689,17 @@ class EdgeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
terminal == null &&
|
!terminal &&
|
||||||
this.marker.hasValidState() &&
|
this.marker.hasValidState() &&
|
||||||
this.marker.highlight != null &&
|
this.marker.highlight != null &&
|
||||||
this.marker.highlight.shape != null &&
|
this.marker.highlight.shape != null &&
|
||||||
this.marker.highlight.shape.stroke !== 'transparent' &&
|
this.marker.highlight.shape.stroke !== 'transparent' &&
|
||||||
this.marker.highlight.shape.stroke !== 'white'
|
this.marker.highlight.shape.stroke !== 'white'
|
||||||
) {
|
) {
|
||||||
terminal = this.marker.validState.cell;
|
terminal = this.marker.validState!.cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (terminal != null) {
|
if (terminal) {
|
||||||
const model = this.graph.getModel();
|
const model = this.graph.getModel();
|
||||||
const parent = edge.getParent();
|
const parent = edge.getParent();
|
||||||
|
|
||||||
|
@ -1704,18 +1708,18 @@ class EdgeHandler {
|
||||||
// Clones and adds the cell
|
// Clones and adds the cell
|
||||||
if (clone) {
|
if (clone) {
|
||||||
let geo = edge.getGeometry();
|
let geo = edge.getGeometry();
|
||||||
clone = this.graph.cloneCell(edge);
|
const cloned = this.graph.cloneCell(edge);
|
||||||
model.add(parent, clone, parent.getChildCount());
|
model.add(parent, cloned, parent.getChildCount());
|
||||||
|
|
||||||
if (geo != null) {
|
if (geo != null) {
|
||||||
geo = geo.clone();
|
geo = geo.clone();
|
||||||
model.setGeometry(clone, geo);
|
model.setGeometry(cloned, geo);
|
||||||
}
|
}
|
||||||
|
|
||||||
const other = edge.getTerminal(!this.isSource);
|
const other = edge.getTerminal(!this.isSource);
|
||||||
this.graph.connectCell(clone, other, !this.isSource);
|
this.graph.connectCell(cloned, other, !this.isSource);
|
||||||
|
|
||||||
edge = clone;
|
edge = cloned;
|
||||||
}
|
}
|
||||||
|
|
||||||
edge = this.connect(edge, terminal, this.isSource, clone, me);
|
edge = this.connect(edge, terminal, this.isSource, clone, me);
|
||||||
|
@ -2127,7 +2131,7 @@ class EdgeHandler {
|
||||||
*
|
*
|
||||||
* Redraws the preview, and the bends- and label control points.
|
* Redraws the preview, and the bends- and label control points.
|
||||||
*/
|
*/
|
||||||
redraw(ignoreHandles: boolean) {
|
redraw(ignoreHandles?: boolean) {
|
||||||
this.abspoints = this.state.absolutePoints.slice();
|
this.abspoints = this.state.absolutePoints.slice();
|
||||||
const g = this.state.cell.getGeometry();
|
const g = this.state.cell.getGeometry();
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,8 @@ class VertexHandle implements CellHandle {
|
||||||
*/
|
*/
|
||||||
ignoreGrid = false;
|
ignoreGrid = false;
|
||||||
|
|
||||||
|
active = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook for subclassers to return the current position of the handle.
|
* Hook for subclassers to return the current position of the handle.
|
||||||
*/
|
*/
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,17 +13,17 @@ import Point from '../geometry/Point';
|
||||||
*/
|
*/
|
||||||
class ConnectionConstraint {
|
class ConnectionConstraint {
|
||||||
constructor(
|
constructor(
|
||||||
point: Point | null = null,
|
point: Point | null,
|
||||||
perimeter: boolean = true,
|
perimeter = true,
|
||||||
name: string | null = null,
|
name: string | null = null,
|
||||||
dx: number | null = null,
|
dx = 0,
|
||||||
dy: number | null = null
|
dy = 0
|
||||||
) {
|
) {
|
||||||
this.point = point;
|
this.point = point;
|
||||||
this.perimeter = perimeter != null ? perimeter : true;
|
this.perimeter = perimeter;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.dx = dx || 0;
|
this.dx = dx;
|
||||||
this.dy = dy || 0;
|
this.dy = dy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,7 @@ class ConnectionConstraint {
|
||||||
*
|
*
|
||||||
* <mxPoint> that specifies the fixed location of the connection point.
|
* <mxPoint> that specifies the fixed location of the connection point.
|
||||||
*/
|
*/
|
||||||
point: Point | null = null;
|
point: Point | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: perimeter
|
* Variable: perimeter
|
||||||
|
@ -39,7 +39,7 @@ class ConnectionConstraint {
|
||||||
* Boolean that specifies if the point should be projected onto the perimeter
|
* Boolean that specifies if the point should be projected onto the perimeter
|
||||||
* of the terminal.
|
* of the terminal.
|
||||||
*/
|
*/
|
||||||
perimeter: boolean = true;
|
perimeter = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: name
|
* Variable: name
|
||||||
|
@ -53,14 +53,14 @@ class ConnectionConstraint {
|
||||||
*
|
*
|
||||||
* Optional float that specifies the horizontal offset of the constraint.
|
* Optional float that specifies the horizontal offset of the constraint.
|
||||||
*/
|
*/
|
||||||
dx: number | null = null;
|
dx = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: dy
|
* Variable: dy
|
||||||
*
|
*
|
||||||
* Optional float that specifies the vertical offset of the constraint.
|
* Optional float that specifies the vertical offset of the constraint.
|
||||||
*/
|
*/
|
||||||
dy: number | null = null;
|
dy = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ConnectionConstraint;
|
export default ConnectionConstraint;
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
DIALECT_SVG,
|
DIALECT_SVG,
|
||||||
HIGHLIGHT_STROKEWIDTH,
|
HIGHLIGHT_STROKEWIDTH,
|
||||||
INVALID_COLOR,
|
INVALID_COLOR,
|
||||||
|
NONE,
|
||||||
OUTLINE_HIGHLIGHT_COLOR,
|
OUTLINE_HIGHLIGHT_COLOR,
|
||||||
OUTLINE_HIGHLIGHT_STROKEWIDTH,
|
OUTLINE_HIGHLIGHT_STROKEWIDTH,
|
||||||
TOOLTIP_VERTICAL_OFFSET,
|
TOOLTIP_VERTICAL_OFFSET,
|
||||||
|
@ -41,12 +42,13 @@ import {
|
||||||
isConsumed,
|
isConsumed,
|
||||||
isShiftDown,
|
isShiftDown,
|
||||||
} from '../../util/EventUtils';
|
} from '../../util/EventUtils';
|
||||||
import graph from '../Graph';
|
import graph, { MaxGraph } from '../Graph';
|
||||||
import Image from '../image/ImageBox';
|
import Image from '../image/ImageBox';
|
||||||
import CellState from '../cell/datatypes/CellState';
|
import CellState from '../cell/datatypes/CellState';
|
||||||
import Graph from '../Graph';
|
import Graph from '../Graph';
|
||||||
import ConnectionConstraint from './ConnectionConstraint';
|
import ConnectionConstraint from './ConnectionConstraint';
|
||||||
import Shape from '../geometry/shape/Shape';
|
import Shape from '../geometry/shape/Shape';
|
||||||
|
import { Listenable } from '../../types';
|
||||||
|
|
||||||
type FactoryMethod = (source: Cell, target: Cell, style?: string) => Cell;
|
type FactoryMethod = (source: Cell, target: Cell, style?: string) => Cell;
|
||||||
|
|
||||||
|
@ -207,12 +209,45 @@ type FactoryMethod = (source: Cell, target: Cell, style?: string) => Cell;
|
||||||
* the <mxCell> that represents the new edge.
|
* the <mxCell> that represents the new edge.
|
||||||
*/
|
*/
|
||||||
class ConnectionHandler extends EventSource {
|
class ConnectionHandler extends EventSource {
|
||||||
constructor(graph: Graph, factoryMethod: FactoryMethod | null = null) {
|
constructor(graph: MaxGraph, factoryMethod: FactoryMethod | null = null) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.graph = graph;
|
this.graph = graph;
|
||||||
this.factoryMethod = factoryMethod;
|
this.factoryMethod = factoryMethod;
|
||||||
this.init();
|
|
||||||
|
this.graph.addMouseListener(this);
|
||||||
|
this.marker = <CellMarker>this.createMarker();
|
||||||
|
this.constraintHandler = new ConstraintHandler(this.graph);
|
||||||
|
|
||||||
|
// Redraws the icons if the graph changes
|
||||||
|
this.changeHandler = (sender: Listenable) => {
|
||||||
|
if (this.iconState) {
|
||||||
|
this.iconState = this.graph.getView().getState(this.iconState.cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.iconState) {
|
||||||
|
this.redrawIcons(this.icons, this.iconState);
|
||||||
|
this.constraintHandler.reset();
|
||||||
|
} else if (this.previous && !this.graph.view.getState(this.previous.cell)) {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.graph.getModel().addListener(InternalEvent.CHANGE, this.changeHandler);
|
||||||
|
this.graph.getView().addListener(InternalEvent.SCALE, this.changeHandler);
|
||||||
|
this.graph.getView().addListener(InternalEvent.TRANSLATE, this.changeHandler);
|
||||||
|
this.graph
|
||||||
|
.getView()
|
||||||
|
.addListener(InternalEvent.SCALE_AND_TRANSLATE, this.changeHandler);
|
||||||
|
|
||||||
|
// Removes the icon if we step into/up or start editing
|
||||||
|
this.drillHandler = (sender: Listenable) => {
|
||||||
|
this.reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.graph.addListener(InternalEvent.START_EDITING, this.drillHandler);
|
||||||
|
this.graph.getView().addListener(InternalEvent.DOWN, this.drillHandler);
|
||||||
|
this.graph.getView().addListener(InternalEvent.UP, this.drillHandler);
|
||||||
|
|
||||||
// Handles escape keystrokes
|
// Handles escape keystrokes
|
||||||
this.escapeHandler = () => {
|
this.escapeHandler = () => {
|
||||||
|
@ -225,7 +260,7 @@ class ConnectionHandler extends EventSource {
|
||||||
// TODO: Document me!
|
// TODO: Document me!
|
||||||
previous: CellState | null = null;
|
previous: CellState | null = null;
|
||||||
iconState: CellState | null = null;
|
iconState: CellState | null = null;
|
||||||
icons: ImageShape[] | null = null;
|
icons: ImageShape[] = [];
|
||||||
cell: Cell | null = null;
|
cell: Cell | null = null;
|
||||||
currentPoint: Point | null = null;
|
currentPoint: Point | null = null;
|
||||||
sourceConstraint: ConnectionConstraint | null = null;
|
sourceConstraint: ConnectionConstraint | null = null;
|
||||||
|
@ -241,7 +276,7 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Reference to the enclosing <mxGraph>.
|
* Reference to the enclosing <mxGraph>.
|
||||||
*/
|
*/
|
||||||
graph: Graph;
|
graph: MaxGraph;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: factoryMethod
|
* Variable: factoryMethod
|
||||||
|
@ -319,7 +354,6 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Holds the <mxTerminalMarker> used for finding source and target cells.
|
* Holds the <mxTerminalMarker> used for finding source and target cells.
|
||||||
*/
|
*/
|
||||||
// @ts-ignore
|
|
||||||
marker: CellMarker;
|
marker: CellMarker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -328,14 +362,14 @@ class ConnectionHandler extends EventSource {
|
||||||
* Holds the <mxConstraintHandler> used for drawing and highlighting
|
* Holds the <mxConstraintHandler> used for drawing and highlighting
|
||||||
* constraints.
|
* constraints.
|
||||||
*/
|
*/
|
||||||
constraintHandler: ConstraintHandler | null = null;
|
constraintHandler: ConstraintHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: error
|
* Variable: error
|
||||||
*
|
*
|
||||||
* Holds the current validation error while connections are being created.
|
* Holds the current validation error while connections are being created.
|
||||||
*/
|
*/
|
||||||
error: any = null;
|
error: string | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: waypointsEnabled
|
* Variable: waypointsEnabled
|
||||||
|
@ -385,14 +419,14 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Holds the change event listener for later removal.
|
* Holds the change event listener for later removal.
|
||||||
*/
|
*/
|
||||||
changeHandler: any = null;
|
changeHandler: (sender: Listenable) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: drillHandler
|
* Variable: drillHandler
|
||||||
*
|
*
|
||||||
* Holds the drill event listener for later removal.
|
* Holds the drill event listener for later removal.
|
||||||
*/
|
*/
|
||||||
drillHandler: any = null;
|
drillHandler: (sender: Listenable) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: mouseDownCounter
|
* Variable: mouseDownCounter
|
||||||
|
@ -539,52 +573,6 @@ class ConnectionHandler extends EventSource {
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: init
|
|
||||||
*
|
|
||||||
* Initializes the shapes required for this connection handler. This should
|
|
||||||
* be invoked if <mxGraph.container> is assigned after the connection
|
|
||||||
* handler has been created.
|
|
||||||
*/
|
|
||||||
init(): void {
|
|
||||||
this.graph.event.addMouseListener(this);
|
|
||||||
this.marker = <CellMarker>this.createMarker();
|
|
||||||
this.constraintHandler = new ConstraintHandler(this.graph);
|
|
||||||
|
|
||||||
// Redraws the icons if the graph changes
|
|
||||||
this.changeHandler = (sender) => {
|
|
||||||
if (this.iconState != null) {
|
|
||||||
this.iconState = this.graph.getView().getState(this.iconState.cell);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.iconState != null) {
|
|
||||||
this.redrawIcons(this.icons, this.iconState);
|
|
||||||
this.constraintHandler.reset();
|
|
||||||
} else if (
|
|
||||||
this.previous != null &&
|
|
||||||
this.graph.view.getState(this.previous.cell) == null
|
|
||||||
) {
|
|
||||||
this.reset();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.graph.getModel().addListener(InternalEvent.CHANGE, this.changeHandler);
|
|
||||||
this.graph.getView().addListener(InternalEvent.SCALE, this.changeHandler);
|
|
||||||
this.graph.getView().addListener(InternalEvent.TRANSLATE, this.changeHandler);
|
|
||||||
this.graph
|
|
||||||
.getView()
|
|
||||||
.addListener(InternalEvent.SCALE_AND_TRANSLATE, this.changeHandler);
|
|
||||||
|
|
||||||
// Removes the icon if we step into/up or start editing
|
|
||||||
this.drillHandler = (sender) => {
|
|
||||||
this.reset();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.graph.addListener(InternalEvent.START_EDITING, this.drillHandler);
|
|
||||||
this.graph.getView().addListener(InternalEvent.DOWN, this.drillHandler);
|
|
||||||
this.graph.getView().addListener(InternalEvent.UP, this.drillHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function: isConnectableCell
|
* Function: isConnectableCell
|
||||||
*
|
*
|
||||||
|
@ -600,7 +588,7 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Creates and returns the <mxCellMarker> used in <marker>.
|
* Creates and returns the <mxCellMarker> used in <marker>.
|
||||||
*/
|
*/
|
||||||
createMarker(): CellMarker {
|
createMarker() {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
class MyCellMarker extends CellMarker {
|
class MyCellMarker extends CellMarker {
|
||||||
|
@ -613,12 +601,12 @@ class ConnectionHandler extends EventSource {
|
||||||
self.error = null;
|
self.error = null;
|
||||||
|
|
||||||
// Checks for cell at preview point (with grid)
|
// Checks for cell at preview point (with grid)
|
||||||
if (cell == null && self.currentPoint != null) {
|
if (!cell && self.currentPoint) {
|
||||||
cell = self.graph.getCellAt(self.currentPoint.x, self.currentPoint.y);
|
cell = self.graph.getCellAt(self.currentPoint.x, self.currentPoint.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses connectable parent vertex if one exists
|
// Uses connectable parent vertex if one exists
|
||||||
if (cell != null && !cell.isConnectable()) {
|
if (cell && !cell.isConnectable() && self.cell) {
|
||||||
const parent = self.cell.getParent();
|
const parent = self.cell.getParent();
|
||||||
|
|
||||||
if (parent.isVertex() && parent.isConnectable()) {
|
if (parent.isVertex() && parent.isConnectable()) {
|
||||||
|
@ -626,6 +614,7 @@ class ConnectionHandler extends EventSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* disable swimlane for now
|
||||||
if (
|
if (
|
||||||
(self.graph.swimlane.isSwimlane(cell) &&
|
(self.graph.swimlane.isSwimlane(cell) &&
|
||||||
self.currentPoint != null &&
|
self.currentPoint != null &&
|
||||||
|
@ -638,13 +627,14 @@ class ConnectionHandler extends EventSource {
|
||||||
) {
|
) {
|
||||||
cell = null;
|
cell = null;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if (cell != null) {
|
if (cell) {
|
||||||
if (self.isConnecting()) {
|
if (self.isConnecting()) {
|
||||||
if (self.previous != null) {
|
if (self.previous) {
|
||||||
self.error = self.validateConnection(self.previous.cell, cell);
|
self.error = self.validateConnection(self.previous.cell, cell);
|
||||||
|
|
||||||
if (self.error != null && self.error.length === 0) {
|
if (self.error && self.error.length === 0) {
|
||||||
cell = null;
|
cell = null;
|
||||||
|
|
||||||
// Enables create target inside groups
|
// Enables create target inside groups
|
||||||
|
@ -659,7 +649,7 @@ class ConnectionHandler extends EventSource {
|
||||||
} else if (
|
} else if (
|
||||||
self.isConnecting() &&
|
self.isConnecting() &&
|
||||||
!self.isCreateTarget(me.getEvent()) &&
|
!self.isCreateTarget(me.getEvent()) &&
|
||||||
!self.graph.allowDanglingEdges
|
!self.graph.isAllowDanglingEdges()
|
||||||
) {
|
) {
|
||||||
self.error = '';
|
self.error = '';
|
||||||
}
|
}
|
||||||
|
@ -670,30 +660,30 @@ class ConnectionHandler extends EventSource {
|
||||||
// Sets the highlight color according to validateConnection
|
// Sets the highlight color according to validateConnection
|
||||||
isValidState(state: CellState) {
|
isValidState(state: CellState) {
|
||||||
if (self.isConnecting()) {
|
if (self.isConnecting()) {
|
||||||
return self.error == null;
|
return !self.error;
|
||||||
}
|
}
|
||||||
return super.isValidState(state);
|
return super.isValidState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overrides to use marker color only in highlight mode or for
|
// Overrides to use marker color only in highlight mode or for
|
||||||
// target selection
|
// target selection
|
||||||
getMarkerColor(evt: Event, state: CellState, isValid: boolean): string | null {
|
getMarkerColor(evt: Event, state: CellState, isValid: boolean) {
|
||||||
return self.connectImage == null || self.isConnecting()
|
return !self.connectImage || self.isConnecting()
|
||||||
? super.getMarkerColor(evt, state, isValid)
|
? super.getMarkerColor(evt, state, isValid)
|
||||||
: null;
|
: NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overrides to use hotspot only for source selection otherwise
|
// Overrides to use hotspot only for source selection otherwise
|
||||||
// intersects always returns true when over a cell
|
// intersects always returns true when over a cell
|
||||||
intersects(state: CellState, evt: InternalMouseEvent) {
|
intersects(state: CellState, evt: InternalMouseEvent) {
|
||||||
if (self.connectImage != null || self.isConnecting()) {
|
if (self.connectImage || self.isConnecting()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.intersects(state, evt);
|
return super.intersects(state, evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <CellMarker>new MyCellMarker(this.graph);
|
return new MyCellMarker(this.graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -701,10 +691,10 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Starts a new connection for the given state and coordinates.
|
* Starts a new connection for the given state and coordinates.
|
||||||
*/
|
*/
|
||||||
start(state: CellState, x: number, y: number, edgeState: CellState): void {
|
start(state: CellState, x: number, y: number, edgeState: CellState) {
|
||||||
this.previous = state;
|
this.previous = state;
|
||||||
this.first = new Point(x, y);
|
this.first = new Point(x, y);
|
||||||
this.edgeState = edgeState != null ? edgeState : this.createEdgeState(null);
|
this.edgeState = edgeState ?? this.createEdgeState();
|
||||||
|
|
||||||
// Marks the source state
|
// Marks the source state
|
||||||
this.marker.currentColor = this.marker.validColor;
|
this.marker.currentColor = this.marker.validColor;
|
||||||
|
@ -720,8 +710,8 @@ class ConnectionHandler extends EventSource {
|
||||||
* Returns true if the source terminal has been clicked and a new
|
* Returns true if the source terminal has been clicked and a new
|
||||||
* connection is currently being previewed.
|
* connection is currently being previewed.
|
||||||
*/
|
*/
|
||||||
isConnecting(): boolean {
|
isConnecting() {
|
||||||
return this.first != null && this.shape != null;
|
return !!this.first && !!this.shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -734,7 +724,7 @@ class ConnectionHandler extends EventSource {
|
||||||
* cell - <mxCell> that represents the source terminal.
|
* cell - <mxCell> that represents the source terminal.
|
||||||
* me - <mxMouseEvent> that is associated with this call.
|
* me - <mxMouseEvent> that is associated with this call.
|
||||||
*/
|
*/
|
||||||
isValidSource(cell: Cell, me: InternalMouseEvent): boolean {
|
isValidSource(cell: Cell, me: InternalMouseEvent) {
|
||||||
return this.graph.isValidSource(cell);
|
return this.graph.isValidSource(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,7 +739,7 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* cell - <mxCell> that represents the target terminal.
|
* cell - <mxCell> that represents the target terminal.
|
||||||
*/
|
*/
|
||||||
isValidTarget(cell: Cell): boolean {
|
isValidTarget(cell: Cell) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,7 +755,7 @@ class ConnectionHandler extends EventSource {
|
||||||
* source - <mxCell> that represents the source terminal.
|
* source - <mxCell> that represents the source terminal.
|
||||||
* target - <mxCell> that represents the target terminal.
|
* target - <mxCell> that represents the target terminal.
|
||||||
*/
|
*/
|
||||||
validateConnection(source: Cell, target: Cell): string {
|
validateConnection(source: Cell, target: Cell) {
|
||||||
if (!this.isValidTarget(target)) {
|
if (!this.isValidTarget(target)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -782,7 +772,7 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> whose connect image should be returned.
|
* state - <mxCellState> whose connect image should be returned.
|
||||||
*/
|
*/
|
||||||
getConnectImage(state: CellState): Image | null {
|
getConnectImage(state: CellState) {
|
||||||
return this.connectImage;
|
return this.connectImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -796,8 +786,8 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> whose connect icons should be returned.
|
* state - <mxCellState> whose connect icons should be returned.
|
||||||
*/
|
*/
|
||||||
isMoveIconToFrontForState(state: CellState): boolean {
|
isMoveIconToFrontForState(state: CellState) {
|
||||||
if (state.text != null && state.text.node.parentNode === this.graph.container) {
|
if (state.text && state.text.node.parentNode === this.graph.container) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return this.moveIconFront;
|
return this.moveIconFront;
|
||||||
|
@ -813,10 +803,10 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* state - <mxCellState> whose connect icons should be returned.
|
* state - <mxCellState> whose connect icons should be returned.
|
||||||
*/
|
*/
|
||||||
createIcons(state: CellState): ImageShape[] | null {
|
createIcons(state: CellState) {
|
||||||
const image = this.getConnectImage(state);
|
const image = this.getConnectImage(state);
|
||||||
|
|
||||||
if (image != null && state != null) {
|
if (image) {
|
||||||
this.iconState = state;
|
this.iconState = state;
|
||||||
const icons = [];
|
const icons = [];
|
||||||
|
|
||||||
|
@ -825,7 +815,7 @@ class ConnectionHandler extends EventSource {
|
||||||
// connect-icon appears behind the selection border and the selection
|
// connect-icon appears behind the selection border and the selection
|
||||||
// border consumes the events before the icon gets a chance
|
// border consumes the events before the icon gets a chance
|
||||||
const bounds = new Rectangle(0, 0, image.width, image.height);
|
const bounds = new Rectangle(0, 0, image.width, image.height);
|
||||||
const icon = new ImageShape(bounds, image.src, null, null, 0);
|
const icon = new ImageShape(bounds, image.src, undefined, undefined, 0);
|
||||||
icon.preserveImageAspect = false;
|
icon.preserveImageAspect = false;
|
||||||
|
|
||||||
if (this.isMoveIconToFrontForState(state)) {
|
if (this.isMoveIconToFrontForState(state)) {
|
||||||
|
@ -836,7 +826,7 @@ class ConnectionHandler extends EventSource {
|
||||||
icon.init(this.graph.getView().getOverlayPane());
|
icon.init(this.graph.getView().getOverlayPane());
|
||||||
|
|
||||||
// Move the icon back in the overlay pane
|
// Move the icon back in the overlay pane
|
||||||
if (this.moveIconBack && icon.node.previousSibling != null) {
|
if (this.moveIconBack && icon.node.parentNode && icon.node.previousSibling) {
|
||||||
icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
|
icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -845,11 +835,11 @@ class ConnectionHandler extends EventSource {
|
||||||
|
|
||||||
// Events transparency
|
// Events transparency
|
||||||
const getState = () => {
|
const getState = () => {
|
||||||
return this.currentState != null ? this.currentState : state;
|
return this.currentState ?? state;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Updates the local icon before firing the mouse down event.
|
// Updates the local icon before firing the mouse down event.
|
||||||
const mouseDown = (evt) => {
|
const mouseDown = (evt: MouseEvent) => {
|
||||||
if (!isConsumed(evt)) {
|
if (!isConsumed(evt)) {
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.graph.fireMouseEvent(
|
this.graph.fireMouseEvent(
|
||||||
|
@ -877,10 +867,10 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
*
|
*
|
||||||
* icons - Optional array of <mxImageShapes> to be redrawn.
|
* icons - Array of <mxImageShapes> to be redrawn.
|
||||||
*/
|
*/
|
||||||
redrawIcons(icons?: ImageShape[] | null, state?: CellState): void {
|
redrawIcons(icons: ImageShape[], state: CellState) {
|
||||||
if (icons != null && icons[0] != null && state != null) {
|
if (icons[0] && icons[0].bounds) {
|
||||||
const pos = this.getIconPosition(icons[0], state);
|
const pos = this.getIconPosition(icons[0], state);
|
||||||
icons[0].bounds.x = pos.x;
|
icons[0].bounds.x = pos.x;
|
||||||
icons[0].bounds.y = pos.y;
|
icons[0].bounds.y = pos.y;
|
||||||
|
@ -889,11 +879,12 @@ class ConnectionHandler extends EventSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Document me! ===========================================================================================================
|
// TODO: Document me! ===========================================================================================================
|
||||||
getIconPosition(icon: ImageShape, state: CellState): Point {
|
getIconPosition(icon: ImageShape, state: CellState) {
|
||||||
const { scale } = this.graph.getView();
|
// const { scale } = this.graph.getView();
|
||||||
let cx = state.getCenterX();
|
let cx = state.getCenterX();
|
||||||
let cy = state.getCenterY();
|
let cy = state.getCenterY();
|
||||||
|
|
||||||
|
/* disable swimlane for now
|
||||||
if (this.graph.isSwimlane(state.cell)) {
|
if (this.graph.isSwimlane(state.cell)) {
|
||||||
const size = this.graph.getStartSize(state.cell);
|
const size = this.graph.getStartSize(state.cell);
|
||||||
|
|
||||||
|
@ -910,8 +901,9 @@ class ConnectionHandler extends EventSource {
|
||||||
cx = pt.x;
|
cx = pt.x;
|
||||||
cy = pt.y;
|
cy = pt.y;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
return new Point(cx - icon.bounds.width / 2, cy - icon.bounds.height / 2);
|
|
||||||
|
return new Point(cx - icon.bounds!.width / 2, cy - icon.bounds!.height / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -919,17 +911,14 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Destroys the connect icons and resets the respective state.
|
* Destroys the connect icons and resets the respective state.
|
||||||
*/
|
*/
|
||||||
destroyIcons(): void {
|
destroyIcons() {
|
||||||
if (this.icons != null) {
|
for (let i = 0; i < this.icons.length; i += 1) {
|
||||||
for (let i = 0; i < this.icons.length; i += 1) {
|
this.icons[i].destroy();
|
||||||
this.icons[i].destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.icons = null;
|
|
||||||
this.icon = null;
|
|
||||||
this.selectedIcon = null;
|
|
||||||
this.iconState = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.icon = null;
|
||||||
|
this.selectedIcon = null;
|
||||||
|
this.iconState = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -941,7 +930,7 @@ class ConnectionHandler extends EventSource {
|
||||||
* <constraintHandler> are not null, or <previous> and <error> are not null and
|
* <constraintHandler> are not null, or <previous> and <error> are not null and
|
||||||
* <icons> is null or <icons> and <icon> are not null.
|
* <icons> is null or <icons> and <icon> are not null.
|
||||||
*/
|
*/
|
||||||
isStartEvent(me: InternalMouseEvent): boolean {
|
isStartEvent(me: InternalMouseEvent) {
|
||||||
return (
|
return (
|
||||||
(this.constraintHandler.currentFocus !== null &&
|
(this.constraintHandler.currentFocus !== null &&
|
||||||
this.constraintHandler.currentConstraint !== null) ||
|
this.constraintHandler.currentConstraint !== null) ||
|
||||||
|
@ -956,7 +945,7 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Handles the event by initiating a new connection.
|
* Handles the event by initiating a new connection.
|
||||||
*/
|
*/
|
||||||
mouseDown(sender: any, me: InternalMouseEvent): void {
|
mouseDown(sender: Listenable, me: InternalMouseEvent) {
|
||||||
this.mouseDownCounter += 1;
|
this.mouseDownCounter += 1;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -967,9 +956,9 @@ class ConnectionHandler extends EventSource {
|
||||||
this.isStartEvent(me)
|
this.isStartEvent(me)
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
this.constraintHandler.currentConstraint != null &&
|
this.constraintHandler.currentConstraint &&
|
||||||
this.constraintHandler.currentFocus != null &&
|
this.constraintHandler.currentFocus &&
|
||||||
this.constraintHandler.currentPoint != null
|
this.constraintHandler.currentPoint
|
||||||
) {
|
) {
|
||||||
this.sourceConstraint = this.constraintHandler.currentConstraint;
|
this.sourceConstraint = this.constraintHandler.currentConstraint;
|
||||||
this.previous = this.constraintHandler.currentFocus;
|
this.previous = this.constraintHandler.currentFocus;
|
||||||
|
@ -982,17 +971,17 @@ class ConnectionHandler extends EventSource {
|
||||||
this.edgeState = this.createEdgeState(me);
|
this.edgeState = this.createEdgeState(me);
|
||||||
this.mouseDownCounter = 1;
|
this.mouseDownCounter = 1;
|
||||||
|
|
||||||
if (this.waypointsEnabled && this.shape == null) {
|
if (this.waypointsEnabled && !this.shape) {
|
||||||
this.waypoints = null;
|
this.waypoints = null;
|
||||||
this.shape = this.createShape();
|
this.shape = this.createShape();
|
||||||
|
|
||||||
if (this.edgeState != null) {
|
if (this.edgeState) {
|
||||||
this.shape.apply(this.edgeState);
|
this.shape.apply(this.edgeState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stores the starting point in the geometry of the preview
|
// Stores the starting point in the geometry of the preview
|
||||||
if (this.previous == null && this.edgeState != null) {
|
if (!this.previous && this.edgeState && this.edgeState.cell.geometry) {
|
||||||
const pt = this.graph.getPointForEvent(me.getEvent());
|
const pt = this.graph.getPointForEvent(me.getEvent());
|
||||||
this.edgeState.cell.geometry.setTerminalPoint(pt, true);
|
this.edgeState.cell.geometry.setTerminalPoint(pt, true);
|
||||||
}
|
}
|
||||||
|
@ -1013,7 +1002,7 @@ class ConnectionHandler extends EventSource {
|
||||||
* connecting. This implementation returns true if the state is not movable
|
* connecting. This implementation returns true if the state is not movable
|
||||||
* in the graph.
|
* in the graph.
|
||||||
*/
|
*/
|
||||||
isImmediateConnectSource(state: CellState): boolean {
|
isImmediateConnectSource(state: CellState) {
|
||||||
return !this.graph.isCellMovable(state.cell);
|
return !this.graph.isCellMovable(state.cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1034,7 +1023,7 @@ class ConnectionHandler extends EventSource {
|
||||||
* };
|
* };
|
||||||
* (end)
|
* (end)
|
||||||
*/
|
*/
|
||||||
createEdgeState(me: InternalMouseEvent): CellState | null {
|
createEdgeState(me?: InternalMouseEvent): CellState | null {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1044,7 +1033,7 @@ class ConnectionHandler extends EventSource {
|
||||||
* Returns true if <outlineConnect> is true and the source of the event is the outline shape
|
* Returns true if <outlineConnect> is true and the source of the event is the outline shape
|
||||||
* or shift is pressed.
|
* or shift is pressed.
|
||||||
*/
|
*/
|
||||||
isOutlineConnectEvent(me: InternalMouseEvent): boolean {
|
isOutlineConnectEvent(me: InternalMouseEvent) {
|
||||||
const offset = getOffset(this.graph.container);
|
const offset = getOffset(this.graph.container);
|
||||||
const evt = me.getEvent();
|
const evt = me.getEvent();
|
||||||
|
|
||||||
|
@ -1143,7 +1132,7 @@ class ConnectionHandler extends EventSource {
|
||||||
// Handles special case where mouse is on outline away from actual end point
|
// Handles special case where mouse is on outline away from actual end point
|
||||||
// in which case the grid is ignored and mouse point is used instead
|
// in which case the grid is ignored and mouse point is used instead
|
||||||
if (me.isSource(this.marker.highlight.shape)) {
|
if (me.isSource(this.marker.highlight.shape)) {
|
||||||
point = new point(me.getGraphX(), me.getGraphY());
|
point = new Point(me.getGraphX(), me.getGraphY());
|
||||||
}
|
}
|
||||||
|
|
||||||
const constraint = this.graph.getOutlineConstraint(point, this.currentState, me);
|
const constraint = this.graph.getOutlineConstraint(point, this.currentState, me);
|
||||||
|
@ -1191,7 +1180,7 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Returns true if the given cell does not allow new connections to be created.
|
* Returns true if the given cell does not allow new connections to be created.
|
||||||
*/
|
*/
|
||||||
isCellEnabled(cell: Cell): boolean {
|
isCellEnabled(cell: Cell) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1200,7 +1189,7 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Converts the given point from screen coordinates to model coordinates.
|
* Converts the given point from screen coordinates to model coordinates.
|
||||||
*/
|
*/
|
||||||
convertWaypoint(point: Point): void {
|
convertWaypoint(point: Point) {
|
||||||
const scale = this.graph.getView().getScale();
|
const scale = this.graph.getView().getScale();
|
||||||
const tr = this.graph.getView().getTranslate();
|
const tr = this.graph.getView().getTranslate();
|
||||||
|
|
||||||
|
@ -1214,13 +1203,13 @@ class ConnectionHandler extends EventSource {
|
||||||
* Called to snap the given point to the current preview. This snaps to the
|
* Called to snap the given point to the current preview. This snaps to the
|
||||||
* first point of the preview if alt is not pressed.
|
* first point of the preview if alt is not pressed.
|
||||||
*/
|
*/
|
||||||
snapToPreview(me: MouseEvent, point: Point): void {
|
snapToPreview(me: InternalMouseEvent, point: Point) {
|
||||||
if (!isAltDown(me.getEvent()) && this.previous != null) {
|
if (!isAltDown(me.getEvent()) && this.previous) {
|
||||||
const tol = (this.graph.gridSize * this.graph.view.scale) / 2;
|
const tol = (this.graph.getGridSize() * this.graph.view.scale) / 2;
|
||||||
const tmp =
|
const tmp =
|
||||||
this.sourceConstraint != null
|
this.sourceConstraint && this.first
|
||||||
? this.first
|
? this.first
|
||||||
: new point(this.previous.getCenterX(), this.previous.getCenterY());
|
: new Point(this.previous.getCenterX(), this.previous.getCenterY());
|
||||||
|
|
||||||
if (Math.abs(tmp.x - me.getGraphX()) < tol) {
|
if (Math.abs(tmp.x - me.getGraphX()) < tol) {
|
||||||
point.x = tmp.x;
|
point.x = tmp.x;
|
||||||
|
@ -1238,13 +1227,13 @@ class ConnectionHandler extends EventSource {
|
||||||
* Handles the event by updating the preview edge or by highlighting
|
* Handles the event by updating the preview edge or by highlighting
|
||||||
* a possible source or target terminal.
|
* a possible source or target terminal.
|
||||||
*/
|
*/
|
||||||
mouseMove(sender: MouseEvent, me: InternalMouseEvent): void {
|
mouseMove(sender: MouseEvent, me: InternalMouseEvent) {
|
||||||
if (
|
if (
|
||||||
!me.isConsumed() &&
|
!me.isConsumed() &&
|
||||||
(this.ignoreMouseDown || this.first != null || !this.graph.isMouseDown)
|
(this.ignoreMouseDown || this.first || !this.graph.isMouseDown)
|
||||||
) {
|
) {
|
||||||
// Handles special case when handler is disabled during highlight
|
// Handles special case when handler is disabled during highlight
|
||||||
if (!this.isEnabled() && this.currentState != null) {
|
if (!this.isEnabled() && this.currentState) {
|
||||||
this.destroyIcons();
|
this.destroyIcons();
|
||||||
this.currentState = null;
|
this.currentState = null;
|
||||||
}
|
}
|
||||||
|
@ -1255,10 +1244,10 @@ class ConnectionHandler extends EventSource {
|
||||||
let point = new Point(me.getGraphX(), me.getGraphY());
|
let point = new Point(me.getGraphX(), me.getGraphY());
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
|
||||||
if (this.graph.grid.isGridEnabledEvent(me.getEvent())) {
|
if (this.graph.isGridEnabledEvent(me.getEvent())) {
|
||||||
point = new point(
|
point = new Point(
|
||||||
(this.graph.grid.snap(point.x / scale - tr.x) + tr.x) * scale,
|
(this.graph.snap(point.x / scale - tr.x) + tr.x) * scale,
|
||||||
(this.graph.grid.snap(point.y / scale - tr.y) + tr.y) * scale
|
(this.graph.snap(point.y / scale - tr.y) + tr.y) * scale
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1266,29 +1255,29 @@ class ConnectionHandler extends EventSource {
|
||||||
this.currentPoint = point;
|
this.currentPoint = point;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(this.first != null || (this.isEnabled() && this.graph.isEnabled())) &&
|
(this.first || (this.isEnabled() && this.graph.isEnabled())) &&
|
||||||
(this.shape != null ||
|
(this.shape ||
|
||||||
this.first == null ||
|
!this.first ||
|
||||||
Math.abs(me.getGraphX() - this.first.x) > this.graph.tolerance ||
|
Math.abs(me.getGraphX() - this.first.x) > this.graph.getEventTolerance() ||
|
||||||
Math.abs(me.getGraphY() - this.first.y) > this.graph.tolerance)
|
Math.abs(me.getGraphY() - this.first.y) > this.graph.getEventTolerance())
|
||||||
) {
|
) {
|
||||||
this.updateCurrentState(me, point);
|
this.updateCurrentState(me, point);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.first != null) {
|
if (this.first) {
|
||||||
let constraint = null;
|
let constraint = null;
|
||||||
let current = point;
|
let current = point;
|
||||||
|
|
||||||
// Uses the current point from the constraint handler if available
|
// Uses the current point from the constraint handler if available
|
||||||
if (
|
if (
|
||||||
this.constraintHandler.currentConstraint != null &&
|
this.constraintHandler.currentConstraint &&
|
||||||
this.constraintHandler.currentFocus != null &&
|
this.constraintHandler.currentFocus &&
|
||||||
this.constraintHandler.currentPoint != null
|
this.constraintHandler.currentPoint
|
||||||
) {
|
) {
|
||||||
constraint = this.constraintHandler.currentConstraint;
|
constraint = this.constraintHandler.currentConstraint;
|
||||||
current = this.constraintHandler.currentPoint.clone();
|
current = this.constraintHandler.currentPoint.clone();
|
||||||
} else if (
|
} else if (
|
||||||
this.previous != null &&
|
this.previous &&
|
||||||
!this.graph.isIgnoreTerminalEvent(me.getEvent()) &&
|
!this.graph.isIgnoreTerminalEvent(me.getEvent()) &&
|
||||||
isShiftDown(me.getEvent())
|
isShiftDown(me.getEvent())
|
||||||
) {
|
) {
|
||||||
|
@ -1305,11 +1294,11 @@ class ConnectionHandler extends EventSource {
|
||||||
let pt2 = this.first;
|
let pt2 = this.first;
|
||||||
|
|
||||||
// Moves the connect icon with the mouse
|
// Moves the connect icon with the mouse
|
||||||
if (this.selectedIcon != null) {
|
if (this.selectedIcon && this.selectedIcon.bounds) {
|
||||||
const w = this.selectedIcon.bounds.width;
|
const w = this.selectedIcon.bounds.width;
|
||||||
const h = this.selectedIcon.bounds.height;
|
const h = this.selectedIcon.bounds.height;
|
||||||
|
|
||||||
if (this.currentState != null && this.targetConnectImage) {
|
if (this.currentState && this.targetConnectImage) {
|
||||||
const pos = this.getIconPosition(this.selectedIcon, this.currentState);
|
const pos = this.getIconPosition(this.selectedIcon, this.currentState);
|
||||||
this.selectedIcon.bounds.x = pos.x;
|
this.selectedIcon.bounds.x = pos.x;
|
||||||
this.selectedIcon.bounds.y = pos.y;
|
this.selectedIcon.bounds.y = pos.y;
|
||||||
|
@ -1327,7 +1316,7 @@ class ConnectionHandler extends EventSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses edge state to compute the terminal points
|
// Uses edge state to compute the terminal points
|
||||||
if (this.edgeState != null) {
|
if (this.edgeState) {
|
||||||
this.updateEdgeState(current, constraint);
|
this.updateEdgeState(current, constraint);
|
||||||
current = this.edgeState.absolutePoints[
|
current = this.edgeState.absolutePoints[
|
||||||
this.edgeState.absolutePoints.length - 1
|
this.edgeState.absolutePoints.length - 1
|
||||||
|
@ -1397,7 +1386,10 @@ class ConnectionHandler extends EventSource {
|
||||||
const dx = Math.abs(me.getGraphX() - this.first.x);
|
const dx = Math.abs(me.getGraphX() - this.first.x);
|
||||||
const dy = Math.abs(me.getGraphY() - this.first.y);
|
const dy = Math.abs(me.getGraphY() - this.first.y);
|
||||||
|
|
||||||
if (dx > this.graph.tolerance || dy > this.graph.tolerance) {
|
if (
|
||||||
|
dx > this.graph.getEventTolerance() ||
|
||||||
|
dy > this.graph.getEventTolerance()
|
||||||
|
) {
|
||||||
this.shape = this.createShape();
|
this.shape = this.createShape();
|
||||||
|
|
||||||
if (this.edgeState != null) {
|
if (this.edgeState != null) {
|
||||||
|
@ -1487,28 +1479,33 @@ class ConnectionHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Updates <edgeState>.
|
* Updates <edgeState>.
|
||||||
*/
|
*/
|
||||||
updateEdgeState(current: CellState, constraint: CellState): void {
|
updateEdgeState(current: Point, constraint: ConnectionConstraint) {
|
||||||
|
if (!this.edgeState) return;
|
||||||
|
|
||||||
// TODO: Use generic method for writing constraint to style
|
// TODO: Use generic method for writing constraint to style
|
||||||
if (this.sourceConstraint != null && this.sourceConstraint.point != null) {
|
if (this.sourceConstraint && this.sourceConstraint.point) {
|
||||||
this.edgeState.style.exitX = this.sourceConstraint.point.x;
|
this.edgeState.style.exitX = this.sourceConstraint.point.x;
|
||||||
this.edgeState.style.exitY = this.sourceConstraint.point.y;
|
this.edgeState.style.exitY = this.sourceConstraint.point.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (constraint != null && constraint.point != null) {
|
if (constraint && constraint.point) {
|
||||||
this.edgeState.style.entryX = constraint.point.x;
|
this.edgeState.style.entryX = constraint.point.x;
|
||||||
this.edgeState.style.entryY = constraint.point.y;
|
this.edgeState.style.entryY = constraint.point.y;
|
||||||
} else {
|
} else {
|
||||||
delete this.edgeState.style.entryX;
|
this.edgeState.style.entryX = 0;
|
||||||
delete this.edgeState.style.entryY;
|
this.edgeState.style.entryY = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.edgeState.absolutePoints = [null, this.currentState != null ? null : current];
|
this.edgeState.absolutePoints = [null, this.currentState != null ? null : current];
|
||||||
this.graph.view.updateFixedTerminalPoint(
|
|
||||||
this.edgeState,
|
if (this.sourceConstraint) {
|
||||||
this.previous,
|
this.graph.view.updateFixedTerminalPoint(
|
||||||
true,
|
this.edgeState,
|
||||||
this.sourceConstraint
|
this.previous,
|
||||||
);
|
true,
|
||||||
|
this.sourceConstraint
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.currentState != null) {
|
if (this.currentState != null) {
|
||||||
if (constraint == null) {
|
if (constraint == null) {
|
||||||
|
@ -1683,7 +1680,7 @@ class ConnectionHandler extends EventSource {
|
||||||
const addPoint =
|
const addPoint =
|
||||||
this.waypoints != null ||
|
this.waypoints != null ||
|
||||||
(this.mouseDownCounter > 1 &&
|
(this.mouseDownCounter > 1 &&
|
||||||
(dx > this.graph.tolerance || dy > this.graph.tolerance));
|
(dx > this.graph.getEventTolerance() || dy > this.graph.getEventTolerance()));
|
||||||
|
|
||||||
if (addPoint) {
|
if (addPoint) {
|
||||||
if (this.waypoints == null) {
|
if (this.waypoints == null) {
|
||||||
|
@ -1691,7 +1688,7 @@ class ConnectionHandler extends EventSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { scale } = this.graph.view;
|
const { scale } = this.graph.view;
|
||||||
point = new point(
|
point = new Point(
|
||||||
this.graph.snap(me.getGraphX() / scale) * scale,
|
this.graph.snap(me.getGraphX() / scale) * scale,
|
||||||
this.graph.snap(me.getGraphY() / scale) * scale
|
this.graph.snap(me.getGraphY() / scale) * scale
|
||||||
);
|
);
|
||||||
|
@ -1823,9 +1820,9 @@ class ConnectionHandler extends EventSource {
|
||||||
* Redraws the preview edge using the color and width returned by
|
* Redraws the preview edge using the color and width returned by
|
||||||
* <getEdgeColor> and <getEdgeWidth>.
|
* <getEdgeColor> and <getEdgeWidth>.
|
||||||
*/
|
*/
|
||||||
drawPreview(): void {
|
drawPreview() {
|
||||||
this.updatePreview(this.error == null);
|
this.updatePreview(this.error === null);
|
||||||
this.shape.redraw();
|
if (this.shape) this.shape.redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1839,9 +1836,11 @@ class ConnectionHandler extends EventSource {
|
||||||
* valid - Boolean indicating if the color for a valid edge should be
|
* valid - Boolean indicating if the color for a valid edge should be
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
updatePreview(valid: boolean): void {
|
updatePreview(valid: boolean) {
|
||||||
this.shape.strokeWidth = this.getEdgeWidth(valid);
|
if (this.shape) {
|
||||||
this.shape.stroke = this.getEdgeColor(valid);
|
this.shape.strokeWidth = this.getEdgeWidth(valid);
|
||||||
|
this.shape.stroke = this.getEdgeColor(valid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1855,7 +1854,7 @@ class ConnectionHandler extends EventSource {
|
||||||
* valid - Boolean indicating if the color for a valid edge should be
|
* valid - Boolean indicating if the color for a valid edge should be
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
getEdgeColor(valid: boolean): string {
|
getEdgeColor(valid: boolean) {
|
||||||
return valid ? VALID_COLOR : INVALID_COLOR;
|
return valid ? VALID_COLOR : INVALID_COLOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1889,7 +1888,7 @@ class ConnectionHandler extends EventSource {
|
||||||
* released.
|
* released.
|
||||||
*/
|
*/
|
||||||
connect(source: Cell, target: Cell, evt: MouseEvent, dropTarget: Cell): void {
|
connect(source: Cell, target: Cell, evt: MouseEvent, dropTarget: Cell): void {
|
||||||
if (target != null || this.isCreateTarget(evt) || this.graph.allowDanglingEdges) {
|
if (target != null || this.isCreateTarget(evt) || this.graph.isAllowDanglingEdges()) {
|
||||||
// Uses the common parent of source and target or
|
// Uses the common parent of source and target or
|
||||||
// the default parent to insert the edge
|
// the default parent to insert the edge
|
||||||
const model = this.graph.getModel();
|
const model = this.graph.getModel();
|
||||||
|
@ -2068,7 +2067,7 @@ class ConnectionHandler extends EventSource {
|
||||||
* Selects the given edge after adding a new connection. The target argument
|
* Selects the given edge after adding a new connection. The target argument
|
||||||
* contains the target vertex if one has been inserted.
|
* contains the target vertex if one has been inserted.
|
||||||
*/
|
*/
|
||||||
selectCells(edge: Cell, target: Cell): void {
|
selectCells(edge: Cell, target: Cell) {
|
||||||
this.graph.setSelectionCell(edge);
|
this.graph.setSelectionCell(edge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2087,7 +2086,7 @@ class ConnectionHandler extends EventSource {
|
||||||
target: Cell,
|
target: Cell,
|
||||||
style: string
|
style: string
|
||||||
): Cell {
|
): Cell {
|
||||||
if (this.factoryMethod == null) {
|
if (!this.factoryMethod) {
|
||||||
return this.graph.insertEdge(parent, id, value, source, target, style);
|
return this.graph.insertEdge(parent, id, value, source, target, style);
|
||||||
}
|
}
|
||||||
let edge = this.createEdge(value, source, target, style);
|
let edge = this.createEdge(value, source, target, style);
|
||||||
|
@ -2160,9 +2159,9 @@ class ConnectionHandler extends EventSource {
|
||||||
* Returns the tolerance for aligning new targets to sources. This returns the grid size / 2.
|
* Returns the tolerance for aligning new targets to sources. This returns the grid size / 2.
|
||||||
*/
|
*/
|
||||||
getAlignmentTolerance(evt: MouseEvent): number {
|
getAlignmentTolerance(evt: MouseEvent): number {
|
||||||
return this.graph.grid.isGridEnabled()
|
return this.graph.isGridEnabled()
|
||||||
? this.graph.grid.gridSize / 2
|
? this.graph.getGridSize() / 2
|
||||||
: this.graph.grid.tolerance;
|
: this.graph.getSnapTolerance();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,7 +4,12 @@ import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||||
import ConnectionConstraint from './ConnectionConstraint';
|
import ConnectionConstraint from './ConnectionConstraint';
|
||||||
import Rectangle from '../geometry/Rectangle';
|
import Rectangle from '../geometry/Rectangle';
|
||||||
import { DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_WEST } from '../../util/Constants';
|
import { DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_WEST } from '../../util/Constants';
|
||||||
import utils, { getRotatedPoint, getValue, toRadians } from '../../util/Utils';
|
import utils, {
|
||||||
|
autoImplement,
|
||||||
|
getRotatedPoint,
|
||||||
|
getValue,
|
||||||
|
toRadians,
|
||||||
|
} from '../../util/Utils';
|
||||||
import Cell from '../cell/datatypes/Cell';
|
import Cell from '../cell/datatypes/Cell';
|
||||||
import CellArray from '../cell/datatypes/CellArray';
|
import CellArray from '../cell/datatypes/CellArray';
|
||||||
import EventObject from '../event/EventObject';
|
import EventObject from '../event/EventObject';
|
||||||
|
@ -13,28 +18,53 @@ import Dictionary from '../../util/Dictionary';
|
||||||
import Geometry from '../geometry/Geometry';
|
import Geometry from '../geometry/Geometry';
|
||||||
import Graph from '../Graph';
|
import Graph from '../Graph';
|
||||||
import ConnectionHandler from './ConnectionHandler';
|
import ConnectionHandler from './ConnectionHandler';
|
||||||
|
import GraphCells from '../cell/GraphCells';
|
||||||
|
import GraphPorts from '../ports/GraphPorts';
|
||||||
|
import GraphEdge from '../cell/edge/GraphEdge';
|
||||||
|
|
||||||
class GraphConnections {
|
type PartialGraph = Pick<Graph, 'getView' | 'getModel' | 'fireEvent'>;
|
||||||
constructor(graph: Graph) {
|
type PartialCells = Pick<GraphCells, 'setCellStyles' | 'isCellLocked'>;
|
||||||
this.graph = graph;
|
type PartialPorts = Pick<GraphPorts, 'isPortsEnabled' | 'isPort' | 'getTerminalForPort'>;
|
||||||
}
|
type PartialEdge = Pick<
|
||||||
|
GraphEdge,
|
||||||
graph: Graph;
|
| 'isResetEdgesOnConnect'
|
||||||
|
| 'resetEdge'
|
||||||
|
| 'getEdges'
|
||||||
|
| 'isAllowDanglingEdges'
|
||||||
|
| 'isConnectableEdges'
|
||||||
|
>;
|
||||||
|
type PartialClass = PartialGraph & PartialCells & PartialPorts & PartialEdge;
|
||||||
|
|
||||||
|
// @ts-ignore recursive reference error
|
||||||
|
class GraphConnections extends autoImplement<PartialClass>() {
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Group: Cell connecting and connection constraints
|
* Group: Cell connecting and connection constraints
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
connectionHandler: ConnectionHandler | null = null;
|
||||||
|
|
||||||
|
getConnectionHandler() {
|
||||||
|
return this.connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
setConnectionHandler(connectionHandler: ConnectionHandler) {
|
||||||
|
this.connectionHandler = connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
constrainChildren = false;
|
||||||
|
|
||||||
|
constrainRelativeChildren = false;
|
||||||
|
|
||||||
|
disconnectOnMove = false;
|
||||||
|
|
||||||
|
cellsDisconnectable = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the constraint used to connect to the outline of the given state.
|
* Returns the constraint used to connect to the outline of the given state.
|
||||||
*/
|
*/
|
||||||
getOutlineConstraint(
|
getOutlineConstraint(point: Point, terminalState: CellState, me: InternalMouseEvent) {
|
||||||
point: Point,
|
if (terminalState.shape) {
|
||||||
terminalState: CellState,
|
const bounds = <Rectangle>this.getView().getPerimeterBounds(terminalState);
|
||||||
me: InternalMouseEvent
|
|
||||||
): ConnectionConstraint | null {
|
|
||||||
if (terminalState.shape != null) {
|
|
||||||
const bounds = <Rectangle>this.graph.view.getPerimeterBounds(terminalState);
|
|
||||||
const direction = terminalState.style.direction;
|
const direction = terminalState.style.direction;
|
||||||
|
|
||||||
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
|
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
|
||||||
|
@ -51,7 +81,7 @@ class GraphConnections {
|
||||||
const cos = Math.cos(-alpha);
|
const cos = Math.cos(-alpha);
|
||||||
const sin = Math.sin(-alpha);
|
const sin = Math.sin(-alpha);
|
||||||
|
|
||||||
const ct = new point(bounds.getCenterX(), bounds.getCenterY());
|
const ct = new Point(bounds.getCenterX(), bounds.getCenterY());
|
||||||
point = getRotatedPoint(point, cos, sin, ct);
|
point = getRotatedPoint(point, cos, sin, ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,16 +91,10 @@ class GraphConnections {
|
||||||
let dy = 0;
|
let dy = 0;
|
||||||
|
|
||||||
// LATER: Add flipping support for image shapes
|
// LATER: Add flipping support for image shapes
|
||||||
if ((<Cell>terminalState.cell).isVertex()) {
|
if (terminalState.cell.isVertex()) {
|
||||||
let flipH = terminalState.style.flipH;
|
let flipH = terminalState.style.flipH;
|
||||||
let flipV = terminalState.style.flipV;
|
let flipV = terminalState.style.flipV;
|
||||||
|
|
||||||
// Legacy support for stencilFlipH/V
|
|
||||||
if (terminalState.shape != null && terminalState.shape.stencil != null) {
|
|
||||||
flipH = getValue(terminalState.style, 'stencilFlipH', 0) == 1 || flipH;
|
|
||||||
flipV = getValue(terminalState.style, 'stencilFlipV', 0) == 1 || flipV;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
|
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
|
||||||
const tmp = flipH;
|
const tmp = flipH;
|
||||||
flipH = flipV;
|
flipH = flipV;
|
||||||
|
@ -88,7 +112,7 @@ class GraphConnections {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
point = new point(
|
point = new Point(
|
||||||
(point.x - bounds.x) * sx - dx + bounds.x,
|
(point.x - bounds.x) * sx - dx + bounds.x,
|
||||||
(point.y - bounds.y) * sy - dy + bounds.y
|
(point.y - bounds.y) * sy - dy + bounds.y
|
||||||
);
|
);
|
||||||
|
@ -102,7 +126,7 @@ class GraphConnections {
|
||||||
? 0
|
? 0
|
||||||
: Math.round(((point.y - bounds.y) * 1000) / bounds.height) / 1000;
|
: Math.round(((point.y - bounds.y) * 1000) / bounds.height) / 1000;
|
||||||
|
|
||||||
return new ConnectionConstraint(new point(x, y), false);
|
return new ConnectionConstraint(new Point(x, y), false);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -115,11 +139,8 @@ class GraphConnections {
|
||||||
* @param terminal {@link mxCellState} that represents the terminal.
|
* @param terminal {@link mxCellState} that represents the terminal.
|
||||||
* @param source Boolean that specifies if the terminal is the source or target.
|
* @param source Boolean that specifies if the terminal is the source or target.
|
||||||
*/
|
*/
|
||||||
getAllConnectionConstraints(
|
getAllConnectionConstraints(terminal: CellState | null, source: boolean) {
|
||||||
terminal: CellState,
|
if (terminal && terminal.shape && terminal.shape.stencil) {
|
||||||
source: boolean
|
|
||||||
): ConnectionConstraint[] | null {
|
|
||||||
if (terminal != null && terminal.shape != null && terminal.shape.stencil != null) {
|
|
||||||
return terminal.shape.stencil.constraints;
|
return terminal.shape.stencil.constraints;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -135,19 +156,18 @@ class GraphConnections {
|
||||||
*/
|
*/
|
||||||
getConnectionConstraint(
|
getConnectionConstraint(
|
||||||
edge: CellState,
|
edge: CellState,
|
||||||
terminal: CellState | null = null,
|
terminal: CellState | null,
|
||||||
source: boolean = false
|
source: boolean = false
|
||||||
): ConnectionConstraint {
|
) {
|
||||||
let point = null;
|
let point: Point | null = null;
|
||||||
// @ts-ignore
|
|
||||||
const x = <string>edge.style[source ? 'exitX' : 'entryX'];
|
|
||||||
|
|
||||||
if (x != null) {
|
const x = edge.style[source ? 'exitX' : 'entryX'];
|
||||||
// @ts-ignore
|
|
||||||
const y = <string>edge.style[source ? 'exitY' : 'entryY'];
|
|
||||||
|
|
||||||
if (y != null) {
|
if (x !== undefined) {
|
||||||
point = new point(parseFloat(x), parseFloat(y));
|
const y = edge.style[source ? 'exitY' : 'entryY'];
|
||||||
|
|
||||||
|
if (y !== undefined) {
|
||||||
|
point = new Point(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,14 +175,12 @@ class GraphConnections {
|
||||||
let dx = 0;
|
let dx = 0;
|
||||||
let dy = 0;
|
let dy = 0;
|
||||||
|
|
||||||
if (point != null) {
|
if (point) {
|
||||||
perimeter = getValue(edge.style, source ? 'exitPerimeter' : 'entryPerimeter', true);
|
perimeter = edge.style[source ? 'exitPerimeter' : 'entryPerimeter'];
|
||||||
|
|
||||||
// Add entry/exit offset
|
// Add entry/exit offset
|
||||||
// @ts-ignore
|
dx = edge.style[source ? 'exitDx' : 'entryDx'];
|
||||||
dx = parseFloat(<string>edge.style[source ? 'exitDx' : 'entryDx']);
|
dy = edge.style[source ? 'exitDy' : 'entryDy'];
|
||||||
// @ts-ignore
|
|
||||||
dy = parseFloat(<string>edge.style[source ? 'exitDy' : 'entryDy']);
|
|
||||||
|
|
||||||
dx = Number.isFinite(dx) ? dx : 0;
|
dx = Number.isFinite(dx) ? dx : 0;
|
||||||
dy = Number.isFinite(dy) ? dy : 0;
|
dy = Number.isFinite(dy) ? dy : 0;
|
||||||
|
@ -187,12 +205,12 @@ class GraphConnections {
|
||||||
terminal: Cell,
|
terminal: Cell,
|
||||||
source: boolean = false,
|
source: boolean = false,
|
||||||
constraint: ConnectionConstraint | null = null
|
constraint: ConnectionConstraint | null = null
|
||||||
): void {
|
) {
|
||||||
if (constraint != null) {
|
if (constraint) {
|
||||||
this.getModel().beginUpdate();
|
this.getModel().beginUpdate();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (constraint == null || constraint.point == null) {
|
if (!constraint || !constraint.point) {
|
||||||
this.setCellStyles(source ? 'exitX' : 'entryX', null, new CellArray(edge));
|
this.setCellStyles(source ? 'exitX' : 'entryX', null, new CellArray(edge));
|
||||||
this.setCellStyles(source ? 'exitY' : 'entryY', null, new CellArray(edge));
|
this.setCellStyles(source ? 'exitY' : 'entryY', null, new CellArray(edge));
|
||||||
this.setCellStyles(source ? 'exitDx' : 'entryDx', null, new CellArray(edge));
|
this.setCellStyles(source ? 'exitDx' : 'entryDx', null, new CellArray(edge));
|
||||||
|
@ -202,7 +220,7 @@ class GraphConnections {
|
||||||
null,
|
null,
|
||||||
new CellArray(edge)
|
new CellArray(edge)
|
||||||
);
|
);
|
||||||
} else if (constraint.point != null) {
|
} else if (constraint.point) {
|
||||||
this.setCellStyles(
|
this.setCellStyles(
|
||||||
source ? 'exitX' : 'entryX',
|
source ? 'exitX' : 'entryX',
|
||||||
constraint.point.x,
|
constraint.point.x,
|
||||||
|
@ -257,17 +275,17 @@ class GraphConnections {
|
||||||
vertex: CellState,
|
vertex: CellState,
|
||||||
constraint: ConnectionConstraint,
|
constraint: ConnectionConstraint,
|
||||||
round: boolean = true
|
round: boolean = true
|
||||||
): Point {
|
) {
|
||||||
let point = null;
|
let point: Point | null = null;
|
||||||
|
|
||||||
if (vertex != null && constraint.point != null) {
|
if (constraint.point) {
|
||||||
const bounds = <Rectangle>this.graph.view.getPerimeterBounds(vertex);
|
const bounds = <Rectangle>this.getView().getPerimeterBounds(vertex);
|
||||||
const cx = new point(bounds.getCenterX(), bounds.getCenterY());
|
const cx = new Point(bounds.getCenterX(), bounds.getCenterY());
|
||||||
const direction = vertex.style.direction;
|
const direction = vertex.style.direction;
|
||||||
let r1 = 0;
|
let r1 = 0;
|
||||||
|
|
||||||
// Bounds need to be rotated by 90 degrees for further computation
|
// Bounds need to be rotated by 90 degrees for further computation
|
||||||
if (direction != null && getValue(vertex.style, 'anchorPointDirection', 1) == 1) {
|
if (vertex.style.anchorPointDirection) {
|
||||||
if (direction === DIRECTION_NORTH) {
|
if (direction === DIRECTION_NORTH) {
|
||||||
r1 += 270;
|
r1 += 270;
|
||||||
} else if (direction === DIRECTION_WEST) {
|
} else if (direction === DIRECTION_WEST) {
|
||||||
|
@ -282,8 +300,8 @@ class GraphConnections {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { scale } = this.view;
|
const { scale } = this.getView();
|
||||||
point = new point(
|
point = new Point(
|
||||||
bounds.x + constraint.point.x * bounds.width + <number>constraint.dx * scale,
|
bounds.x + constraint.point.x * bounds.width + <number>constraint.dx * scale,
|
||||||
bounds.y + constraint.point.y * bounds.height + <number>constraint.dy * scale
|
bounds.y + constraint.point.y * bounds.height + <number>constraint.dy * scale
|
||||||
);
|
);
|
||||||
|
@ -308,19 +326,13 @@ class GraphConnections {
|
||||||
point = getRotatedPoint(point, cos, sin, cx);
|
point = getRotatedPoint(point, cos, sin, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
point = this.graph.view.getPerimeterPoint(vertex, point, false);
|
point = this.getView().getPerimeterPoint(vertex, point, false);
|
||||||
} else {
|
} else {
|
||||||
r2 += r1;
|
r2 += r1;
|
||||||
|
|
||||||
if ((<Cell>vertex.cell).isVertex()) {
|
if (vertex.cell.isVertex()) {
|
||||||
let flipH = vertex.style.flipH == 1;
|
let flipH = vertex.style.flipH;
|
||||||
let flipV = vertex.style.flipV == 1;
|
let flipV = vertex.style.flipV;
|
||||||
|
|
||||||
// Legacy support for stencilFlipH/V
|
|
||||||
if (vertex.shape != null && vertex.shape.stencil != null) {
|
|
||||||
flipH = getValue(vertex.style, 'stencilFlipH', 0) == 1 || flipH;
|
|
||||||
flipV = getValue(vertex.style, 'stencilFlipV', 0) == 1 || flipV;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
|
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
|
||||||
const temp = flipH;
|
const temp = flipH;
|
||||||
|
@ -339,7 +351,7 @@ class GraphConnections {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic rotation after projection on perimeter
|
// Generic rotation after projection on perimeter
|
||||||
if (r2 !== 0 && point != null) {
|
if (r2 !== 0 && point) {
|
||||||
const rad = toRadians(r2);
|
const rad = toRadians(r2);
|
||||||
const cos = Math.cos(rad);
|
const cos = Math.cos(rad);
|
||||||
const sin = Math.sin(rad);
|
const sin = Math.sin(rad);
|
||||||
|
@ -348,7 +360,7 @@ class GraphConnections {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (round && point != null) {
|
if (round && point) {
|
||||||
point.x = Math.round(point.x);
|
point.x = Math.round(point.x);
|
||||||
point.y = Math.round(point.y);
|
point.y = Math.round(point.y);
|
||||||
}
|
}
|
||||||
|
@ -371,7 +383,7 @@ class GraphConnections {
|
||||||
terminal: Cell,
|
terminal: Cell,
|
||||||
source: boolean = false,
|
source: boolean = false,
|
||||||
constraint: ConnectionConstraint | null = null
|
constraint: ConnectionConstraint | null = null
|
||||||
): Cell {
|
) {
|
||||||
this.getModel().beginUpdate();
|
this.getModel().beginUpdate();
|
||||||
try {
|
try {
|
||||||
const previous = edge.getTerminal(source);
|
const previous = edge.getTerminal(source);
|
||||||
|
@ -410,52 +422,50 @@ class GraphConnections {
|
||||||
terminal: Cell,
|
terminal: Cell,
|
||||||
source: boolean = false,
|
source: boolean = false,
|
||||||
constraint: ConnectionConstraint | null = null
|
constraint: ConnectionConstraint | null = null
|
||||||
): void {
|
) {
|
||||||
if (edge != null) {
|
this.getModel().beginUpdate();
|
||||||
this.getModel().beginUpdate();
|
try {
|
||||||
try {
|
const previous = edge.getTerminal(source);
|
||||||
const previous = edge.getTerminal(source);
|
|
||||||
|
|
||||||
// Updates the constraint
|
// Updates the constraint
|
||||||
this.setConnectionConstraint(edge, terminal, source, constraint);
|
this.setConnectionConstraint(edge, terminal, source, constraint);
|
||||||
|
|
||||||
// Checks if the new terminal is a port, uses the ID of the port in the
|
// Checks if the new terminal is a port, uses the ID of the port in the
|
||||||
// style and the parent of the port as the actual terminal of the edge.
|
// style and the parent of the port as the actual terminal of the edge.
|
||||||
if (this.isPortsEnabled()) {
|
if (this.isPortsEnabled()) {
|
||||||
let id = null;
|
let id = null;
|
||||||
|
|
||||||
if (this.isPort(terminal)) {
|
if (this.isPort(terminal)) {
|
||||||
id = terminal.getId();
|
id = terminal.getId();
|
||||||
terminal = <Cell>this.getTerminalForPort(terminal, source);
|
terminal = <Cell>this.getTerminalForPort(terminal, source);
|
||||||
}
|
|
||||||
|
|
||||||
// Sets or resets all previous information for connecting to a child port
|
|
||||||
const key = source ? 'sourcePort' : 'targetPort';
|
|
||||||
this.setCellStyles(key, id, new CellArray(edge));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getModel().setTerminal(edge, terminal, source);
|
// Sets or resets all previous information for connecting to a child port
|
||||||
|
const key = source ? 'sourcePort' : 'targetPort';
|
||||||
if (this.resetEdgesOnConnect) {
|
this.setCellStyles(key, id, new CellArray(edge));
|
||||||
this.resetEdge(edge);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fireEvent(
|
|
||||||
new EventObject(
|
|
||||||
InternalEvent.CELL_CONNECTED,
|
|
||||||
'edge',
|
|
||||||
edge,
|
|
||||||
'terminal',
|
|
||||||
terminal,
|
|
||||||
'source',
|
|
||||||
source,
|
|
||||||
'previous',
|
|
||||||
previous
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
this.getModel().endUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.getModel().setTerminal(edge, terminal, source);
|
||||||
|
|
||||||
|
if (this.isResetEdgesOnConnect()) {
|
||||||
|
this.resetEdge(edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fireEvent(
|
||||||
|
new EventObject(
|
||||||
|
InternalEvent.CELL_CONNECTED,
|
||||||
|
'edge',
|
||||||
|
edge,
|
||||||
|
'terminal',
|
||||||
|
terminal,
|
||||||
|
'source',
|
||||||
|
source,
|
||||||
|
'previous',
|
||||||
|
previous
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
this.getModel().endUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,84 +475,77 @@ class GraphConnections {
|
||||||
*
|
*
|
||||||
* @param cells Array of {@link Cell} to be disconnected.
|
* @param cells Array of {@link Cell} to be disconnected.
|
||||||
*/
|
*/
|
||||||
disconnectGraph(cells: CellArray | null) {
|
disconnectGraph(cells: CellArray) {
|
||||||
if (cells != null) {
|
this.getModel().beginUpdate();
|
||||||
this.getModel().beginUpdate();
|
try {
|
||||||
try {
|
const { scale, translate: tr } = this.getView();
|
||||||
const { scale } = this.view;
|
|
||||||
const tr = this.graph.view.translate;
|
|
||||||
|
|
||||||
// Fast lookup for finding cells in array
|
// Fast lookup for finding cells in array
|
||||||
const dict = new Dictionary();
|
const dict = new Dictionary<Cell, boolean>();
|
||||||
|
|
||||||
for (let i = 0; i < cells.length; i += 1) {
|
for (let i = 0; i < cells.length; i += 1) {
|
||||||
dict.put(cells[i], true);
|
dict.put(cells[i], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const cell of cells) {
|
for (const cell of cells) {
|
||||||
if (cell.isEdge()) {
|
if (cell.isEdge()) {
|
||||||
let geo = <Geometry>cell.getGeometry();
|
let geo = cell.getGeometry();
|
||||||
|
|
||||||
if (geo != null) {
|
if (geo) {
|
||||||
const state = this.graph.view.getState(cell);
|
const state = this.getView().getState(cell);
|
||||||
const pstate = <CellState>this.graph.view.getState(cell.getParent());
|
const pstate = <CellState>this.getView().getState(cell.getParent());
|
||||||
|
|
||||||
if (state != null && pstate != null) {
|
if (state && pstate) {
|
||||||
geo = geo.clone();
|
geo = geo.clone();
|
||||||
|
|
||||||
// @ts-ignore
|
const dx = -pstate.origin.x;
|
||||||
const dx = -pstate.origin.x;
|
const dy = -pstate.origin.y;
|
||||||
// @ts-ignore
|
const pts = state.absolutePoints;
|
||||||
const dy = -pstate.origin.y;
|
|
||||||
const pts = <Point[]>state.absolutePoints;
|
|
||||||
|
|
||||||
let src = cell.getTerminal(true);
|
let src = cell.getTerminal(true);
|
||||||
|
|
||||||
if (src != null && this.isCellDisconnectable(cell, src, true)) {
|
if (src && this.isCellDisconnectable(cell, src, true)) {
|
||||||
while (src != null && !dict.get(src)) {
|
while (src && !dict.get(src)) {
|
||||||
src = src.getParent();
|
src = src.getParent();
|
||||||
}
|
|
||||||
|
|
||||||
if (src == null) {
|
|
||||||
geo.setTerminalPoint(
|
|
||||||
new Point(
|
|
||||||
pts[0].x / scale - tr.x + dx,
|
|
||||||
pts[0].y / scale - tr.y + dy
|
|
||||||
),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
this.getModel().setTerminal(cell, null, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let trg = cell.getTerminal(false);
|
if (!src && pts[0]) {
|
||||||
|
geo.setTerminalPoint(
|
||||||
|
new Point(pts[0].x / scale - tr.x + dx, pts[0].y / scale - tr.y + dy),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
this.getModel().setTerminal(cell, null, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (trg != null && this.isCellDisconnectable(cell, trg, false)) {
|
let trg = cell.getTerminal(false);
|
||||||
while (trg != null && !dict.get(trg)) {
|
|
||||||
trg = trg.getParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trg == null) {
|
if (trg && this.isCellDisconnectable(cell, trg, false)) {
|
||||||
const n = pts.length - 1;
|
while (trg && !dict.get(trg)) {
|
||||||
|
trg = trg.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trg) {
|
||||||
|
const n = pts.length - 1;
|
||||||
|
const p = pts[n];
|
||||||
|
|
||||||
|
if (p) {
|
||||||
geo.setTerminalPoint(
|
geo.setTerminalPoint(
|
||||||
new Point(
|
new Point(p.x / scale - tr.x + dx, p.y / scale - tr.y + dy),
|
||||||
<number>(<Point>pts[n]).x / scale - tr.x + dx,
|
|
||||||
<number>(<Point>pts[n]).y / scale - tr.y + dy
|
|
||||||
),
|
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
this.getModel().setTerminal(cell, null, false);
|
this.getModel().setTerminal(cell, null, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getModel().setGeometry(cell, geo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.getModel().setGeometry(cell, geo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
this.getModel().endUpdate();
|
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
this.getModel().endUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,7 +556,7 @@ class GraphConnections {
|
||||||
* @param parent Optional parent of the opposite end for a connection to be
|
* @param parent Optional parent of the opposite end for a connection to be
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
getConnections(cell: Cell, parent: Cell | null = null): CellArray {
|
getConnections(cell: Cell, parent: Cell | null = null) {
|
||||||
return this.getEdges(cell, parent, true, true, false);
|
return this.getEdges(cell, parent, true, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,7 +568,7 @@ class GraphConnections {
|
||||||
*
|
*
|
||||||
* @param cell {@link mxCell} that should be constrained.
|
* @param cell {@link mxCell} that should be constrained.
|
||||||
*/
|
*/
|
||||||
isConstrainChild(cell: Cell): boolean {
|
isConstrainChild(cell: Cell) {
|
||||||
return (
|
return (
|
||||||
this.isConstrainChildren() &&
|
this.isConstrainChildren() &&
|
||||||
!!cell.getParent() &&
|
!!cell.getParent() &&
|
||||||
|
@ -576,28 +579,28 @@ class GraphConnections {
|
||||||
/**
|
/**
|
||||||
* Returns {@link constrainChildren}.
|
* Returns {@link constrainChildren}.
|
||||||
*/
|
*/
|
||||||
isConstrainChildren(): boolean {
|
isConstrainChildren() {
|
||||||
return this.constrainChildren;
|
return this.constrainChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets {@link constrainChildren}.
|
* Sets {@link constrainChildren}.
|
||||||
*/
|
*/
|
||||||
setConstrainChildren(value: boolean): void {
|
setConstrainChildren(value: boolean) {
|
||||||
this.constrainChildren = value;
|
this.constrainChildren = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@link constrainRelativeChildren}.
|
* Returns {@link constrainRelativeChildren}.
|
||||||
*/
|
*/
|
||||||
isConstrainRelativeChildren(): boolean {
|
isConstrainRelativeChildren() {
|
||||||
return this.constrainRelativeChildren;
|
return this.constrainRelativeChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets {@link constrainRelativeChildren}.
|
* Sets {@link constrainRelativeChildren}.
|
||||||
*/
|
*/
|
||||||
setConstrainRelativeChildren(value: boolean): void {
|
setConstrainRelativeChildren(value: boolean) {
|
||||||
this.constrainRelativeChildren = value;
|
this.constrainRelativeChildren = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,7 +611,7 @@ class GraphConnections {
|
||||||
/**
|
/**
|
||||||
* Returns {@link disconnectOnMove} as a boolean.
|
* Returns {@link disconnectOnMove} as a boolean.
|
||||||
*/
|
*/
|
||||||
isDisconnectOnMove(): boolean {
|
isDisconnectOnMove() {
|
||||||
return this.disconnectOnMove;
|
return this.disconnectOnMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,7 +622,7 @@ class GraphConnections {
|
||||||
* @param value Boolean indicating if edges should be disconnected
|
* @param value Boolean indicating if edges should be disconnected
|
||||||
* when moved.
|
* when moved.
|
||||||
*/
|
*/
|
||||||
setDisconnectOnMove(value: boolean): void {
|
setDisconnectOnMove(value: boolean) {
|
||||||
this.disconnectOnMove = value;
|
this.disconnectOnMove = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,21 +640,21 @@ class GraphConnections {
|
||||||
cell: Cell,
|
cell: Cell,
|
||||||
terminal: Cell | null = null,
|
terminal: Cell | null = null,
|
||||||
source: boolean = false
|
source: boolean = false
|
||||||
): boolean {
|
) {
|
||||||
return this.isCellsDisconnectable() && !this.isCellLocked(cell);
|
return this.isCellsDisconnectable() && !this.isCellLocked(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@link cellsDisconnectable}.
|
* Returns {@link cellsDisconnectable}.
|
||||||
*/
|
*/
|
||||||
isCellsDisconnectable(): boolean {
|
isCellsDisconnectable() {
|
||||||
return this.cellsDisconnectable;
|
return this.cellsDisconnectable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets {@link cellsDisconnectable}.
|
* Sets {@link cellsDisconnectable}.
|
||||||
*/
|
*/
|
||||||
setCellsDisconnectable(value: boolean): void {
|
setCellsDisconnectable(value: boolean) {
|
||||||
this.cellsDisconnectable = value;
|
this.cellsDisconnectable = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,10 +665,12 @@ class GraphConnections {
|
||||||
*
|
*
|
||||||
* @param cell {@link mxCell} that represents a possible source or null.
|
* @param cell {@link mxCell} that represents a possible source or null.
|
||||||
*/
|
*/
|
||||||
isValidSource(cell: Cell | null): boolean {
|
isValidSource(cell: Cell | null) {
|
||||||
return (
|
return (
|
||||||
(cell == null && this.allowDanglingEdges) ||
|
(cell == null && this.isAllowDanglingEdges()) ||
|
||||||
(cell != null && (!cell.isEdge() || this.connectableEdges) && cell.isConnectable())
|
(cell != null &&
|
||||||
|
(!cell.isEdge() || this.isConnectableEdges()) &&
|
||||||
|
cell.isConnectable())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -699,14 +704,14 @@ class GraphConnections {
|
||||||
*
|
*
|
||||||
* @param connectable Boolean indicating if new connections should be allowed.
|
* @param connectable Boolean indicating if new connections should be allowed.
|
||||||
*/
|
*/
|
||||||
setConnectable(connectable: boolean): void {
|
setConnectable(connectable: boolean) {
|
||||||
(<ConnectionHandler>this.connectionHandler).setEnabled(connectable);
|
(<ConnectionHandler>this.connectionHandler).setEnabled(connectable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the {@link connectionHandler} is enabled.
|
* Returns true if the {@link connectionHandler} is enabled.
|
||||||
*/
|
*/
|
||||||
isConnectable(): boolean {
|
isConnectable() {
|
||||||
return (<ConnectionHandler>this.connectionHandler).isEnabled();
|
return (<ConnectionHandler>this.connectionHandler).isEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ type PartialCells = Pick<
|
||||||
>;
|
>;
|
||||||
type PartialClass = PartialGraph & PartialSelection & PartialEvents & PartialCells;
|
type PartialClass = PartialGraph & PartialSelection & PartialEvents & PartialCells;
|
||||||
|
|
||||||
|
// @ts-ignore recursive reference error
|
||||||
class GraphEditing extends autoImplement<PartialClass>() {
|
class GraphEditing extends autoImplement<PartialClass>() {
|
||||||
/**
|
/**
|
||||||
* Specifies the return value for {@link isCellEditable}.
|
* Specifies the return value for {@link isCellEditable}.
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import EventObject from './EventObject';
|
import EventObject from './EventObject';
|
||||||
|
|
||||||
type EventListener = {
|
type EventListenerObject = {
|
||||||
funct: Function;
|
funct: Function;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
@ -46,7 +46,7 @@ class EventSource {
|
||||||
* contains the event name followed by the respective listener for each
|
* contains the event name followed by the respective listener for each
|
||||||
* registered listener.
|
* registered listener.
|
||||||
*/
|
*/
|
||||||
eventListeners: EventListener[] = [];
|
eventListeners: EventListenerObject[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: eventsEnabled
|
* Variable: eventsEnabled
|
||||||
|
@ -60,7 +60,7 @@ class EventSource {
|
||||||
*
|
*
|
||||||
* Optional source for events. Default is null.
|
* Optional source for events. Default is null.
|
||||||
*/
|
*/
|
||||||
eventSource: EventSource | null;
|
eventSource: EventSource | EventTarget | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function: isEventsEnabled
|
* Function: isEventsEnabled
|
||||||
|
@ -94,7 +94,7 @@ class EventSource {
|
||||||
*
|
*
|
||||||
* Sets <eventSource>.
|
* Sets <eventSource>.
|
||||||
*/
|
*/
|
||||||
setEventSource(value: EventSource | null) {
|
setEventSource(value: EventSource | EventTarget | null) {
|
||||||
this.eventSource = value;
|
this.eventSource = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ class EventSource {
|
||||||
*
|
*
|
||||||
* The parameters of the listener are the sender and an <mxEventObject>.
|
* The parameters of the listener are the sender and an <mxEventObject>.
|
||||||
*/
|
*/
|
||||||
addListener(name: string, funct: (...args: any[]) => any) {
|
addListener(name: string, funct: Function) {
|
||||||
this.eventListeners.push({ name, funct });
|
this.eventListeners.push({ name, funct });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ class EventSource {
|
||||||
*
|
*
|
||||||
* Removes all occurrences of the given listener from <eventListeners>.
|
* Removes all occurrences of the given listener from <eventListeners>.
|
||||||
*/
|
*/
|
||||||
removeListener(funct: (...args: any[]) => any) {
|
removeListener(funct: Function) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
while (i < this.eventListeners.length) {
|
while (i < this.eventListeners.length) {
|
||||||
|
@ -146,7 +146,7 @@ class EventSource {
|
||||||
* sender - Optional sender to be passed to the listener. Default value is
|
* sender - Optional sender to be passed to the listener. Default value is
|
||||||
* the return value of <getEventSource>.
|
* the return value of <getEventSource>.
|
||||||
*/
|
*/
|
||||||
fireEvent(evt: EventObject, sender: any = null) {
|
fireEvent(evt: EventObject, sender: EventSource | EventTarget | null = null) {
|
||||||
if (this.isEventsEnabled()) {
|
if (this.isEventsEnabled()) {
|
||||||
if (!evt) {
|
if (!evt) {
|
||||||
evt = new EventObject('');
|
evt = new EventObject('');
|
||||||
|
|
|
@ -32,6 +32,7 @@ import type GraphCells from '../cell/GraphCells';
|
||||||
import type GraphSelection from '../selection/GraphSelection';
|
import type GraphSelection from '../selection/GraphSelection';
|
||||||
import GraphEditing from '../editing/GraphEditing';
|
import GraphEditing from '../editing/GraphEditing';
|
||||||
import GraphSnap from '../snap/GraphSnap';
|
import GraphSnap from '../snap/GraphSnap';
|
||||||
|
import { MouseEventListener } from '../../types';
|
||||||
|
|
||||||
type PartialGraph = Pick<
|
type PartialGraph = Pick<
|
||||||
Graph,
|
Graph,
|
||||||
|
@ -59,21 +60,15 @@ type PartialClass = PartialGraph &
|
||||||
PartialSnap &
|
PartialSnap &
|
||||||
EventSource;
|
EventSource;
|
||||||
|
|
||||||
type MouseListener = {
|
|
||||||
mouseDown: Function;
|
|
||||||
mouseMove: Function;
|
|
||||||
mouseUp: Function;
|
|
||||||
};
|
|
||||||
|
|
||||||
// @ts-ignore recursive reference error
|
// @ts-ignore recursive reference error
|
||||||
class GraphEvents extends autoImplement<PartialClass>() {
|
class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
/**
|
/**
|
||||||
* Holds the mouse event listeners. See {@link fireMouseEvent}.
|
* Holds the mouse event listeners. See {@link fireMouseEvent}.
|
||||||
*/
|
*/
|
||||||
mouseListeners: MouseListener[] = [];
|
mouseListeners: MouseListenerSet[] = [];
|
||||||
|
|
||||||
// TODO: Document me!
|
// TODO: Document me!
|
||||||
lastTouchEvent: InternalMouseEvent | null = null;
|
lastTouchEvent: MouseEvent | null = null;
|
||||||
doubleClickCounter: number = 0;
|
doubleClickCounter: number = 0;
|
||||||
lastTouchCell: Cell | null = null;
|
lastTouchCell: Cell | null = null;
|
||||||
fireDoubleClick: boolean | null = null;
|
fireDoubleClick: boolean | null = null;
|
||||||
|
@ -82,8 +77,8 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
lastMouseY: number | null = null;
|
lastMouseY: number | null = null;
|
||||||
isMouseTrigger: boolean | null = null;
|
isMouseTrigger: boolean | null = null;
|
||||||
ignoreMouseEvents: boolean | null = null;
|
ignoreMouseEvents: boolean | null = null;
|
||||||
mouseMoveRedirect: EventListener | null = null;
|
mouseMoveRedirect: MouseEventListener | null = null;
|
||||||
mouseUpRedirect: EventListener | null = null;
|
mouseUpRedirect: MouseEventListener | null = null;
|
||||||
lastEvent: any; // FIXME: Check if this can be more specific - DOM events or mxEventObjects!
|
lastEvent: any; // FIXME: Check if this can be more specific - DOM events or mxEventObjects!
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -199,7 +194,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
*/
|
*/
|
||||||
tolerance: number = 4;
|
tolerance: number = 4;
|
||||||
|
|
||||||
getClickTolerance = () => this.tolerance;
|
getEventTolerance = () => this.tolerance;
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Group: Event processing
|
* Group: Event processing
|
||||||
|
@ -210,7 +205,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
*
|
*
|
||||||
* @param evt Mouseevent that represents the keystroke.
|
* @param evt Mouseevent that represents the keystroke.
|
||||||
*/
|
*/
|
||||||
escape(evt: InternalMouseEvent): void {
|
escape(evt: Event) {
|
||||||
this.fireEvent(new EventObject(InternalEvent.ESCAPE, 'event', evt));
|
this.fireEvent(new EventObject(InternalEvent.ESCAPE, 'event', evt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,7 +350,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
* @param evt Mouseevent that represents the doubleclick.
|
* @param evt Mouseevent that represents the doubleclick.
|
||||||
* @param cell Optional {@link Cell} under the mousepointer.
|
* @param cell Optional {@link Cell} under the mousepointer.
|
||||||
*/
|
*/
|
||||||
dblClick(evt: MouseEvent, cell?: Cell): void {
|
dblClick(evt: MouseEvent, cell?: Cell) {
|
||||||
const mxe = new EventObject(InternalEvent.DOUBLE_CLICK, { event: evt, cell: cell });
|
const mxe = new EventObject(InternalEvent.DOUBLE_CLICK, { event: evt, cell: cell });
|
||||||
this.fireEvent(mxe);
|
this.fireEvent(mxe);
|
||||||
|
|
||||||
|
@ -364,7 +359,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
this.isEnabled() &&
|
this.isEnabled() &&
|
||||||
!isConsumed(evt) &&
|
!isConsumed(evt) &&
|
||||||
!mxe.isConsumed() &&
|
!mxe.isConsumed() &&
|
||||||
cell != null &&
|
cell &&
|
||||||
this.isCellEditable(cell) &&
|
this.isCellEditable(cell) &&
|
||||||
!this.isEditing(cell)
|
!this.isEditing(cell)
|
||||||
) {
|
) {
|
||||||
|
@ -379,7 +374,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
* @param me {@link mxMouseEvent} that represents the touch event.
|
* @param me {@link mxMouseEvent} that represents the touch event.
|
||||||
* @param state Optional {@link CellState} that is associated with the event.
|
* @param state Optional {@link CellState} that is associated with the event.
|
||||||
*/
|
*/
|
||||||
tapAndHold(me: InternalMouseEvent): void {
|
tapAndHold(me: InternalMouseEvent) {
|
||||||
const evt = me.getEvent();
|
const evt = me.getEvent();
|
||||||
const mxe = new EventObject(
|
const mxe = new EventObject(
|
||||||
InternalEvent.TAP_AND_HOLD,
|
InternalEvent.TAP_AND_HOLD,
|
||||||
|
@ -434,8 +429,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
*
|
*
|
||||||
* @param listener Listener to be added to the graph event listeners.
|
* @param listener Listener to be added to the graph event listeners.
|
||||||
*/
|
*/
|
||||||
// addMouseListener(listener: { [key: string]: (sender: mxEventSource, me: mxMouseEvent) => void }): void;
|
addMouseListener(listener: MouseListenerSet) {
|
||||||
addMouseListener(listener: MouseListener): void {
|
|
||||||
this.mouseListeners.push(listener);
|
this.mouseListeners.push(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,8 +438,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
*
|
*
|
||||||
* @param listener Listener to be removed from the graph event listeners.
|
* @param listener Listener to be removed from the graph event listeners.
|
||||||
*/
|
*/
|
||||||
// removeMouseListener(listener: { [key: string]: (sender: mxEventSource, me: mxMouseEvent) => void }): void;
|
removeMouseListener(listener: MouseListenerSet) {
|
||||||
removeMouseListener(listener: MouseListener) {
|
|
||||||
for (let i = 0; i < this.mouseListeners.length; i += 1) {
|
for (let i = 0; i < this.mouseListeners.length; i += 1) {
|
||||||
if (this.mouseListeners[i] === listener) {
|
if (this.mouseListeners[i] === listener) {
|
||||||
this.mouseListeners.splice(i, 1);
|
this.mouseListeners.splice(i, 1);
|
||||||
|
@ -461,30 +454,28 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
* @param me {@link mxMouseEvent} to be updated.
|
* @param me {@link mxMouseEvent} to be updated.
|
||||||
* @param evtName Name of the mouse event.
|
* @param evtName Name of the mouse event.
|
||||||
*/
|
*/
|
||||||
updateMouseEvent(me: InternalMouseEvent, evtName: string): InternalMouseEvent {
|
updateMouseEvent(me: InternalMouseEvent, evtName: string) {
|
||||||
if (me.graphX == null || me.graphY == null) {
|
const pt = convertPoint(this.getContainer(), me.getX(), me.getY());
|
||||||
const pt = convertPoint(this.getContainer(), me.getX(), me.getY());
|
|
||||||
|
|
||||||
me.graphX = pt.x - this.panning.panDx;
|
me.graphX = pt.x - this.panning.panDx;
|
||||||
me.graphY = pt.y - this.panning.panDy;
|
me.graphY = pt.y - this.panning.panDy;
|
||||||
|
|
||||||
// Searches for rectangles using method if native hit detection is disabled on shape
|
// Searches for rectangles using method if native hit detection is disabled on shape
|
||||||
if (
|
if (
|
||||||
me.getCell() == null &&
|
me.getCell() == null &&
|
||||||
this.isMouseDown &&
|
this.isMouseDown &&
|
||||||
evtName === InternalEvent.MOUSE_MOVE
|
evtName === InternalEvent.MOUSE_MOVE
|
||||||
) {
|
) {
|
||||||
me.state = this.getView().getState(
|
me.state = this.getView().getState(
|
||||||
this.getCellAt(pt.x, pt.y, null, true, true, (state: CellState) => {
|
this.getCellAt(pt.x, pt.y, null, true, true, (state: CellState) => {
|
||||||
return (
|
return (
|
||||||
state.shape == null ||
|
state.shape == null ||
|
||||||
state.shape.paintBackground !== this.paintBackground ||
|
state.shape.paintBackground !== this.paintBackground ||
|
||||||
getValue(state.style, 'pointerEvents', '1') == '1' ||
|
getValue(state.style, 'pointerEvents', '1') == '1' ||
|
||||||
(state.shape.fill != null && state.shape.fill !== NONE)
|
(state.shape.fill != null && state.shape.fill !== NONE)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
|
@ -493,8 +484,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
/**
|
/**
|
||||||
* Returns the state for the given touch event.
|
* Returns the state for the given touch event.
|
||||||
*/
|
*/
|
||||||
// getStateForTouchEvent(evt: MouseEvent | TouchEvent): mxCellState;
|
getStateForTouchEvent(evt: MouseEvent) {
|
||||||
getStateForTouchEvent(evt: InternalMouseEvent) {
|
|
||||||
const x = getClientX(evt);
|
const x = getClientX(evt);
|
||||||
const y = getClientY(evt);
|
const y = getClientY(evt);
|
||||||
|
|
||||||
|
@ -508,8 +498,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
/**
|
/**
|
||||||
* Returns true if the event should be ignored in {@link fireMouseEvent}.
|
* Returns true if the event should be ignored in {@link fireMouseEvent}.
|
||||||
*/
|
*/
|
||||||
// isEventIgnored(evtName: string, me: mxMouseEvent, sender: mxEventSource): boolean;
|
isEventIgnored(evtName: string, me: InternalMouseEvent, sender: EventSource) {
|
||||||
isEventIgnored(evtName: string, me: InternalMouseEvent, sender: any): boolean {
|
|
||||||
const mouseEvent = isMouseEvent(me.getEvent());
|
const mouseEvent = isMouseEvent(me.getEvent());
|
||||||
let result = false;
|
let result = false;
|
||||||
|
|
||||||
|
@ -545,13 +534,13 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
) {
|
) {
|
||||||
this.setEventSource(me.getSource());
|
this.setEventSource(me.getSource());
|
||||||
|
|
||||||
this.mouseMoveRedirect = (evt: InternalMouseEvent) => {
|
this.mouseMoveRedirect = (evt: MouseEvent) => {
|
||||||
this.fireMouseEvent(
|
this.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_MOVE,
|
InternalEvent.MOUSE_MOVE,
|
||||||
new InternalMouseEvent(evt, this.getStateForTouchEvent(evt))
|
new InternalMouseEvent(evt, this.getStateForTouchEvent(evt))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
this.mouseUpRedirect = (evt: InternalMouseEvent) => {
|
this.mouseUpRedirect = (evt: MouseEvent) => {
|
||||||
this.fireMouseEvent(
|
this.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_UP,
|
InternalEvent.MOUSE_UP,
|
||||||
new InternalMouseEvent(evt, this.getStateForTouchEvent(evt))
|
new InternalMouseEvent(evt, this.getStateForTouchEvent(evt))
|
||||||
|
@ -675,11 +664,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
* @param me {@link mxMouseEvent} to be fired.
|
* @param me {@link mxMouseEvent} to be fired.
|
||||||
* @param sender Optional sender argument. Default is `this`.
|
* @param sender Optional sender argument. Default is `this`.
|
||||||
*/
|
*/
|
||||||
fireMouseEvent(
|
fireMouseEvent(evtName: string, me: InternalMouseEvent, sender: EventSource = this) {
|
||||||
evtName: string,
|
|
||||||
me: InternalMouseEvent,
|
|
||||||
sender: EventSource = this
|
|
||||||
): void {
|
|
||||||
if (this.isEventSourceIgnored(evtName, me)) {
|
if (this.isEventSourceIgnored(evtName, me)) {
|
||||||
if (this.tooltipHandler != null) {
|
if (this.tooltipHandler != null) {
|
||||||
this.tooltipHandler.hide();
|
this.tooltipHandler.hide();
|
||||||
|
@ -687,10 +672,6 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sender == null) {
|
|
||||||
sender = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the graph coordinates in the event
|
// Updates the graph coordinates in the event
|
||||||
me = this.updateMouseEvent(me, evtName);
|
me = this.updateMouseEvent(me, evtName);
|
||||||
|
|
||||||
|
@ -992,7 +973,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
* Returns true if the given event is a clone event. This implementation
|
* Returns true if the given event is a clone event. This implementation
|
||||||
* returns true if control is pressed.
|
* returns true if control is pressed.
|
||||||
*/
|
*/
|
||||||
isCloneEvent(evt: MouseEvent): boolean {
|
isCloneEvent(evt: MouseEvent) {
|
||||||
return isControlDown(evt);
|
return isControlDown(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1001,7 +982,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
* returns true the cell behind the selected cell will be selected. This
|
* returns true the cell behind the selected cell will be selected. This
|
||||||
* implementation returns false;
|
* implementation returns false;
|
||||||
*/
|
*/
|
||||||
isTransparentClickEvent(evt: MouseEvent): boolean {
|
isTransparentClickEvent(evt: MouseEvent) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1010,21 +991,21 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
* returns true if the meta key (Cmd) is pressed on Macs or if control is
|
* returns true if the meta key (Cmd) is pressed on Macs or if control is
|
||||||
* pressed on any other platform.
|
* pressed on any other platform.
|
||||||
*/
|
*/
|
||||||
isToggleEvent(evt: MouseEvent): boolean {
|
isToggleEvent(evt: MouseEvent) {
|
||||||
return mxClient.IS_MAC ? isMetaDown(evt) : isControlDown(evt);
|
return mxClient.IS_MAC ? isMetaDown(evt) : isControlDown(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the given mouse event should be aligned to the grid.
|
* Returns true if the given mouse event should be aligned to the grid.
|
||||||
*/
|
*/
|
||||||
isGridEnabledEvent(evt: MouseEvent): boolean {
|
isGridEnabledEvent(evt: MouseEvent) {
|
||||||
return evt != null && !isAltDown(evt);
|
return !isAltDown(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the given mouse event should be aligned to the grid.
|
* Returns true if the given mouse event should be aligned to the grid.
|
||||||
*/
|
*/
|
||||||
isConstrainedEvent(evt: MouseEvent): boolean {
|
isConstrainedEvent(evt: MouseEvent) {
|
||||||
return isShiftDown(evt);
|
return isShiftDown(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1032,7 +1013,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
* Returns true if the given mouse event should not allow any connections to be
|
* Returns true if the given mouse event should not allow any connections to be
|
||||||
* made. This implementation returns false.
|
* made. This implementation returns false.
|
||||||
*/
|
*/
|
||||||
isIgnoreTerminalEvent(evt: MouseEvent): boolean {
|
isIgnoreTerminalEvent(evt: MouseEvent) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1044,7 +1025,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
* @param addOffset Optional boolean that specifies if the position should be
|
* @param addOffset Optional boolean that specifies if the position should be
|
||||||
* offset by half of the {@link gridSize}. Default is `true`.
|
* offset by half of the {@link gridSize}. Default is `true`.
|
||||||
*/
|
*/
|
||||||
getPointForEvent(evt: InternalMouseEvent, addOffset: boolean = true): Point {
|
getPointForEvent(evt: MouseEvent, addOffset: boolean = true) {
|
||||||
const p = convertPoint(this.getContainer(), getClientX(evt), getClientY(evt));
|
const p = convertPoint(this.getContainer(), getClientX(evt), getClientY(evt));
|
||||||
const s = this.getView().scale;
|
const s = this.getView().scale;
|
||||||
const tr = this.getView().translate;
|
const tr = this.getView().translate;
|
||||||
|
@ -1052,6 +1033,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
||||||
|
|
||||||
p.x = this.snap(p.x / s - tr.x - off);
|
p.x = this.snap(p.x / s - tr.x - off);
|
||||||
p.y = this.snap(p.y / s - tr.y - off);
|
p.y = this.snap(p.y / s - tr.y - off);
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import mxClient from '../../mxClient';
|
||||||
import { isConsumed, isMouseEvent } from '../../util/EventUtils';
|
import { isConsumed, isMouseEvent } from '../../util/EventUtils';
|
||||||
import graph from '../Graph';
|
import graph from '../Graph';
|
||||||
import CellState from '../cell/datatypes/CellState';
|
import CellState from '../cell/datatypes/CellState';
|
||||||
import { EventCache, GestureEvent, Listenable } from '../../types';
|
import { EventCache, GestureEvent, Listenable, MouseEventListener } from '../../types';
|
||||||
|
|
||||||
// Checks if passive event listeners are supported
|
// Checks if passive event listeners are supported
|
||||||
// see https://github.com/Modernizr/Modernizr/issues/1894
|
// see https://github.com/Modernizr/Modernizr/issues/1894
|
||||||
|
@ -50,11 +50,10 @@ class InternalEvent {
|
||||||
* {@link mxUtils.bind} in order to bind the "this" keyword inside the function
|
* {@link mxUtils.bind} in order to bind the "this" keyword inside the function
|
||||||
* to a given execution scope.
|
* to a given execution scope.
|
||||||
*/
|
*/
|
||||||
// static addListener(element: Node | Window, eventName: string, funct: Function): void;
|
static addListener(element: Listenable, eventName: string, funct: MouseEventListener) {
|
||||||
static addListener(element: Listenable, eventName: string, funct: EventListener) {
|
|
||||||
element.addEventListener(
|
element.addEventListener(
|
||||||
eventName,
|
eventName,
|
||||||
funct,
|
funct as EventListener,
|
||||||
supportsPassive ? { passive: false } : false
|
supportsPassive ? { passive: false } : false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -69,9 +68,12 @@ class InternalEvent {
|
||||||
/**
|
/**
|
||||||
* Removes the specified listener from the given element.
|
* Removes the specified listener from the given element.
|
||||||
*/
|
*/
|
||||||
// static removeListener(element: Node | Window, eventName: string, funct: Function): void;
|
static removeListener(
|
||||||
static removeListener(element: Listenable, eventName: string, funct: EventListener) {
|
element: Listenable,
|
||||||
element.removeEventListener(eventName, funct, false);
|
eventName: string,
|
||||||
|
funct: MouseEventListener
|
||||||
|
) {
|
||||||
|
element.removeEventListener(eventName, funct as EventListener, false);
|
||||||
|
|
||||||
if (element.mxListenerList) {
|
if (element.mxListenerList) {
|
||||||
const listenerCount = element.mxListenerList.length;
|
const listenerCount = element.mxListenerList.length;
|
||||||
|
@ -90,7 +92,6 @@ class InternalEvent {
|
||||||
/**
|
/**
|
||||||
* Removes all listeners from the given element.
|
* Removes all listeners from the given element.
|
||||||
*/
|
*/
|
||||||
// static removeAllListeners(element: Node | Window): void;
|
|
||||||
static removeAllListeners(element: Listenable) {
|
static removeAllListeners(element: Listenable) {
|
||||||
const list = element.mxListenerList;
|
const list = element.mxListenerList;
|
||||||
|
|
||||||
|
@ -112,10 +113,10 @@ class InternalEvent {
|
||||||
* will be registered as well as the mouse events.
|
* will be registered as well as the mouse events.
|
||||||
*/
|
*/
|
||||||
static addGestureListeners(
|
static addGestureListeners(
|
||||||
node: Listenable,
|
node: EventSource | EventTarget,
|
||||||
startListener: EventListener | null = null,
|
startListener: MouseEventListener | null = null,
|
||||||
moveListener: EventListener | null = null,
|
moveListener: MouseEventListener | null = null,
|
||||||
endListener: EventListener | null = null
|
endListener: MouseEventListener | null = null
|
||||||
) {
|
) {
|
||||||
if (startListener) {
|
if (startListener) {
|
||||||
InternalEvent.addListener(
|
InternalEvent.addListener(
|
||||||
|
@ -164,9 +165,9 @@ class InternalEvent {
|
||||||
*/
|
*/
|
||||||
static removeGestureListeners(
|
static removeGestureListeners(
|
||||||
node: Listenable,
|
node: Listenable,
|
||||||
startListener: EventListener | null,
|
startListener: MouseEventListener | null,
|
||||||
moveListener: EventListener | null,
|
moveListener: MouseEventListener | null,
|
||||||
endListener: EventListener | null
|
endListener: MouseEventListener | null
|
||||||
) {
|
) {
|
||||||
if (startListener) {
|
if (startListener) {
|
||||||
InternalEvent.removeListener(
|
InternalEvent.removeListener(
|
||||||
|
@ -221,10 +222,10 @@ class InternalEvent {
|
||||||
node: Listenable,
|
node: Listenable,
|
||||||
graph: graph,
|
graph: graph,
|
||||||
state: CellState | ((evt: Event) => CellState) | null = null,
|
state: CellState | ((evt: Event) => CellState) | null = null,
|
||||||
down: EventListener | null = null,
|
down: MouseEventListener | null = null,
|
||||||
move: EventListener | null = null,
|
move: MouseEventListener | null = null,
|
||||||
up: EventListener | null = null,
|
up: MouseEventListener | null = null,
|
||||||
dblClick: EventListener | null = null
|
dblClick: MouseEventListener | null = null
|
||||||
) {
|
) {
|
||||||
const getState = (evt: Event) => {
|
const getState = (evt: Event) => {
|
||||||
return typeof state === 'function' ? state(evt) : state;
|
return typeof state === 'function' ? state(evt) : state;
|
||||||
|
@ -238,7 +239,7 @@ class InternalEvent {
|
||||||
} else if (!isConsumed(evt)) {
|
} else if (!isConsumed(evt)) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_DOWN,
|
InternalEvent.MOUSE_DOWN,
|
||||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
new InternalMouseEvent(evt, getState(evt))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -248,7 +249,7 @@ class InternalEvent {
|
||||||
} else if (!isConsumed(evt)) {
|
} else if (!isConsumed(evt)) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_MOVE,
|
InternalEvent.MOUSE_MOVE,
|
||||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
new InternalMouseEvent(evt, getState(evt))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -258,7 +259,7 @@ class InternalEvent {
|
||||||
} else if (!isConsumed(evt)) {
|
} else if (!isConsumed(evt)) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_UP,
|
InternalEvent.MOUSE_UP,
|
||||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
new InternalMouseEvent(evt, getState(evt))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,7 +270,7 @@ class InternalEvent {
|
||||||
dblClick(evt);
|
dblClick(evt);
|
||||||
} else if (!isConsumed(evt)) {
|
} else if (!isConsumed(evt)) {
|
||||||
const tmp = getState(evt);
|
const tmp = getState(evt);
|
||||||
graph.dblClick(evt as MouseEvent, tmp?.cell);
|
graph.dblClick(evt, tmp?.cell);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -279,17 +280,17 @@ class InternalEvent {
|
||||||
*
|
*
|
||||||
* @param element DOM node to remove the listeners from.
|
* @param element DOM node to remove the listeners from.
|
||||||
*/
|
*/
|
||||||
static release(element: Listenable | null) {
|
static release(element: Listenable) {
|
||||||
try {
|
try {
|
||||||
if (element) {
|
InternalEvent.removeAllListeners(element);
|
||||||
InternalEvent.removeAllListeners(element);
|
|
||||||
|
|
||||||
if ('childNodes' in element) {
|
// @ts-ignore
|
||||||
const children = element.childNodes;
|
const children = element.childNodes;
|
||||||
const childCount = children.length;
|
|
||||||
for (let i = 0; i < childCount; i += 1) {
|
if (children !== undefined) {
|
||||||
InternalEvent.release(children[i]);
|
const childCount = children.length;
|
||||||
}
|
for (let i = 0; i < childCount; i += 1) {
|
||||||
|
InternalEvent.release(children[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -66,6 +66,8 @@ class GraphFolding extends autoImplement<PartialClass>() {
|
||||||
|
|
||||||
getCollapseExpandResource = () => this.collapseExpandResource;
|
getCollapseExpandResource = () => this.collapseExpandResource;
|
||||||
|
|
||||||
|
isFoldingEnabled = () => this.options.foldingEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @default true
|
* @default true
|
||||||
|
@ -133,7 +135,7 @@ class GraphFolding extends autoImplement<PartialClass>() {
|
||||||
recurse: boolean = false,
|
recurse: boolean = false,
|
||||||
cells: CellArray | null = null,
|
cells: CellArray | null = null,
|
||||||
checkFoldable: boolean = false,
|
checkFoldable: boolean = false,
|
||||||
evt: EventObject | null = null
|
evt: Event | null = null
|
||||||
): CellArray | null {
|
): CellArray | null {
|
||||||
if (cells == null) {
|
if (cells == null) {
|
||||||
cells = this.getFoldableCells(this.getSelectionCells(), collapse);
|
cells = this.getFoldableCells(this.getSelectionCells(), collapse);
|
||||||
|
|
|
@ -326,6 +326,8 @@ class Shape {
|
||||||
|
|
||||||
image: ImageBox | null = null;
|
image: ImageBox | null = null;
|
||||||
|
|
||||||
|
imageSrc: string | null = null;
|
||||||
|
|
||||||
indicatorColor: ColorValue = NONE;
|
indicatorColor: ColorValue = NONE;
|
||||||
|
|
||||||
indicatorStrokeColor: ColorValue = NONE;
|
indicatorStrokeColor: ColorValue = NONE;
|
||||||
|
@ -334,6 +336,8 @@ class Shape {
|
||||||
|
|
||||||
indicatorDirection: DirectionValue = DIRECTION_EAST;
|
indicatorDirection: DirectionValue = DIRECTION_EAST;
|
||||||
|
|
||||||
|
indicatorImageSrc: string | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function: isHtmlAllowed
|
* Function: isHtmlAllowed
|
||||||
*
|
*
|
||||||
|
|
|
@ -63,7 +63,7 @@ import SvgCanvas2D from 'packages/core/src/util/canvas/SvgCanvas2D';
|
||||||
*/
|
*/
|
||||||
class TextShape extends Shape {
|
class TextShape extends Shape {
|
||||||
constructor(
|
constructor(
|
||||||
value: string,
|
value: string | HTMLElement | SVGGElement,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
align: AlignValue = ALIGN_CENTER,
|
align: AlignValue = ALIGN_CENTER,
|
||||||
valign: VAlignValue = ALIGN_MIDDLE,
|
valign: VAlignValue = ALIGN_MIDDLE,
|
||||||
|
@ -112,8 +112,7 @@ class TextShape extends Shape {
|
||||||
this.updateMargin();
|
this.updateMargin();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Document me!
|
value: string | HTMLElement | SVGGElement;
|
||||||
value: string | HTMLElement | SVGGElement | null;
|
|
||||||
bounds: Rectangle;
|
bounds: Rectangle;
|
||||||
align: AlignValue;
|
align: AlignValue;
|
||||||
valign: VAlignValue;
|
valign: VAlignValue;
|
||||||
|
|
|
@ -61,7 +61,7 @@ class GraphLabel extends autoImplement<PartialClass>() {
|
||||||
*
|
*
|
||||||
* @param cell {@link mxCell} whose label should be returned.
|
* @param cell {@link mxCell} whose label should be returned.
|
||||||
*/
|
*/
|
||||||
getLabel(cell: Cell): string | Node | null {
|
getLabel(cell: Cell) {
|
||||||
let result: string | null = '';
|
let result: string | null = '';
|
||||||
|
|
||||||
if (this.isLabelsVisible() && cell != null) {
|
if (this.isLabelsVisible() && cell != null) {
|
||||||
|
@ -71,6 +71,7 @@ class GraphLabel extends autoImplement<PartialClass>() {
|
||||||
result = this.convertValueToString(cell);
|
result = this.convertValueToString(cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
import {hasScrollbars} from "../../util/Utils";
|
import { autoImplement, hasScrollbars } from '../../util/Utils';
|
||||||
import EventObject from "../event/EventObject";
|
import EventObject from '../event/EventObject';
|
||||||
import InternalEvent from "../event/InternalEvent";
|
import InternalEvent from '../event/InternalEvent';
|
||||||
import PanningManager from './PanningManager';
|
import PanningHandler from './PanningHandler';
|
||||||
import PanningHandler from "./PanningHandler";
|
import Graph from '../Graph';
|
||||||
import Graph from "../Graph";
|
import Cell from '../cell/datatypes/Cell';
|
||||||
import Cell from "../cell/datatypes/Cell";
|
import Rectangle from '../geometry/Rectangle';
|
||||||
import Rectangle from "../geometry/Rectangle";
|
import Point from '../geometry/Point';
|
||||||
import Point from "../geometry/Point";
|
import GraphEvents from '../event/GraphEvents';
|
||||||
|
import SelectionCellsHandler from '../selection/SelectionCellsHandler';
|
||||||
|
|
||||||
class GraphPanning {
|
type PartialGraph = Pick<Graph, 'getContainer' | 'getView' | 'getPlugin'>;
|
||||||
constructor(graph: Graph) {
|
type PartialEvents = Pick<GraphEvents, 'fireEvent'>;
|
||||||
this.graph = graph;
|
type PartialClass = PartialGraph & PartialEvents;
|
||||||
this.createHandlers()
|
|
||||||
}
|
|
||||||
|
|
||||||
graph: Graph;
|
class GraphPanning extends autoImplement<PartialClass>() {
|
||||||
|
|
||||||
panningHandler: PanningHandler | null = null;
|
|
||||||
panningManager: PanningManager | null = null;
|
|
||||||
shiftPreview1: HTMLElement | null = null;
|
shiftPreview1: HTMLElement | null = null;
|
||||||
shiftPreview2: HTMLElement | null = null;
|
shiftPreview2: HTMLElement | null = null;
|
||||||
|
|
||||||
|
@ -28,7 +24,9 @@ class GraphPanning {
|
||||||
* then no panning occurs if this is `true`.
|
* then no panning occurs if this is `true`.
|
||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
useScrollbarsForPanning: boolean = true;
|
useScrollbarsForPanning = true;
|
||||||
|
|
||||||
|
isUseScrollbarsForPanning = () => this.useScrollbarsForPanning;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies if autoscrolling should be carried out via mxPanningManager even
|
* Specifies if autoscrolling should be carried out via mxPanningManager even
|
||||||
|
@ -38,7 +36,7 @@ class GraphPanning {
|
||||||
* are visible and scrollable in all directions.
|
* are visible and scrollable in all directions.
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
timerAutoScroll: boolean = false;
|
timerAutoScroll = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies if panning via {@link panGraph} should be allowed to implement autoscroll
|
* Specifies if panning via {@link panGraph} should be allowed to implement autoscroll
|
||||||
|
@ -47,38 +45,25 @@ class GraphPanning {
|
||||||
* positive value.
|
* positive value.
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
allowAutoPanning: boolean = false;
|
allowAutoPanning = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current horizontal panning value.
|
* Current horizontal panning value.
|
||||||
* @default 0
|
* @default 0
|
||||||
*/
|
*/
|
||||||
panDx: number = 0;
|
panDx = 0;
|
||||||
|
|
||||||
|
getPanDx = () => this.panDx;
|
||||||
|
setPanDx = (dx: number) => (this.panDx = dx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current vertical panning value.
|
* Current vertical panning value.
|
||||||
* @default 0
|
* @default 0
|
||||||
*/
|
*/
|
||||||
panDy: number = 0;
|
panDy = 0;
|
||||||
|
|
||||||
createHandlers() {
|
getPanDy = () => this.panDy;
|
||||||
this.panningHandler = this.createPanningHandler();
|
setPanDy = (dy: number) => (this.panDy = dy);
|
||||||
this.panningHandler.panningEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and returns a new {@link PanningHandler} to be used in this graph.
|
|
||||||
*/
|
|
||||||
createPanningHandler(): PanningHandler {
|
|
||||||
return new PanningHandler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and returns an {@link PanningManager}.
|
|
||||||
*/
|
|
||||||
createPanningManager(): PanningManager {
|
|
||||||
return new PanningManager(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shifts the graph display by the given amount. This is used to preview
|
* Shifts the graph display by the given amount. This is used to preview
|
||||||
|
@ -88,30 +73,30 @@ class GraphPanning {
|
||||||
* @param dx Amount to shift the graph along the x-axis.
|
* @param dx Amount to shift the graph along the x-axis.
|
||||||
* @param dy Amount to shift the graph along the y-axis.
|
* @param dy Amount to shift the graph along the y-axis.
|
||||||
*/
|
*/
|
||||||
panGraph(dx: number, dy: number): void {
|
panGraph(dx: number, dy: number) {
|
||||||
const container = <HTMLElement>this.graph.container;
|
const container = this.getContainer();
|
||||||
|
|
||||||
if (this.useScrollbarsForPanning && hasScrollbars(container)) {
|
if (this.useScrollbarsForPanning && hasScrollbars(container)) {
|
||||||
container.scrollLeft = -dx;
|
container.scrollLeft = -dx;
|
||||||
container.scrollTop = -dy;
|
container.scrollTop = -dy;
|
||||||
} else {
|
} else {
|
||||||
const canvas = <SVGElement>this.graph.view.getCanvas();
|
const canvas = this.getView().getCanvas();
|
||||||
|
|
||||||
// Puts everything inside the container in a DIV so that it
|
// Puts everything inside the container in a DIV so that it
|
||||||
// can be moved without changing the state of the container
|
// can be moved without changing the state of the container
|
||||||
if (dx === 0 && dy === 0) {
|
if (dx === 0 && dy === 0) {
|
||||||
canvas.removeAttribute('transform');
|
canvas.removeAttribute('transform');
|
||||||
|
|
||||||
if (this.shiftPreview1 != null) {
|
if (this.shiftPreview1) {
|
||||||
let child = this.shiftPreview1.firstChild;
|
let child = this.shiftPreview1.firstChild;
|
||||||
|
|
||||||
while (child != null) {
|
while (child) {
|
||||||
const next = child.nextSibling;
|
const next = child.nextSibling;
|
||||||
container.appendChild(child);
|
container.appendChild(child);
|
||||||
child = next;
|
child = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.shiftPreview1.parentNode != null) {
|
if (this.shiftPreview1.parentNode) {
|
||||||
this.shiftPreview1.parentNode.removeChild(this.shiftPreview1);
|
this.shiftPreview1.parentNode.removeChild(this.shiftPreview1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,13 +106,13 @@ class GraphPanning {
|
||||||
const shiftPreview2 = <HTMLElement>this.shiftPreview2;
|
const shiftPreview2 = <HTMLElement>this.shiftPreview2;
|
||||||
child = shiftPreview2.firstChild;
|
child = shiftPreview2.firstChild;
|
||||||
|
|
||||||
while (child != null) {
|
while (child) {
|
||||||
const next = child.nextSibling;
|
const next = child.nextSibling;
|
||||||
container.appendChild(child);
|
container.appendChild(child);
|
||||||
child = next;
|
child = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shiftPreview2.parentNode != null) {
|
if (shiftPreview2.parentNode) {
|
||||||
shiftPreview2.parentNode.removeChild(shiftPreview2);
|
shiftPreview2.parentNode.removeChild(shiftPreview2);
|
||||||
}
|
}
|
||||||
this.shiftPreview2 = null;
|
this.shiftPreview2 = null;
|
||||||
|
@ -135,7 +120,7 @@ class GraphPanning {
|
||||||
} else {
|
} else {
|
||||||
canvas.setAttribute('transform', `translate(${dx},${dy})`);
|
canvas.setAttribute('transform', `translate(${dx},${dy})`);
|
||||||
|
|
||||||
if (this.shiftPreview1 == null) {
|
if (!this.shiftPreview1) {
|
||||||
// Needs two divs for stuff before and after the SVG element
|
// Needs two divs for stuff before and after the SVG element
|
||||||
this.shiftPreview1 = document.createElement('div');
|
this.shiftPreview1 = document.createElement('div');
|
||||||
this.shiftPreview1.style.position = 'absolute';
|
this.shiftPreview1.style.position = 'absolute';
|
||||||
|
@ -148,7 +133,7 @@ class GraphPanning {
|
||||||
let current = this.shiftPreview1;
|
let current = this.shiftPreview1;
|
||||||
let child = container.firstChild;
|
let child = container.firstChild;
|
||||||
|
|
||||||
while (child != null) {
|
while (child) {
|
||||||
const next = child.nextSibling;
|
const next = child.nextSibling;
|
||||||
|
|
||||||
// SVG element is moved via transform attribute
|
// SVG element is moved via transform attribute
|
||||||
|
@ -163,11 +148,11 @@ class GraphPanning {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inserts elements only if not empty
|
// Inserts elements only if not empty
|
||||||
if (this.shiftPreview1.firstChild != null) {
|
if (this.shiftPreview1.firstChild) {
|
||||||
container.insertBefore(this.shiftPreview1, canvas.parentNode);
|
container.insertBefore(this.shiftPreview1, canvas.parentNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.shiftPreview2.firstChild != null) {
|
if (this.shiftPreview2.firstChild) {
|
||||||
container.appendChild(this.shiftPreview2);
|
container.appendChild(this.shiftPreview2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +169,7 @@ class GraphPanning {
|
||||||
this.panDx = dx;
|
this.panDx = dx;
|
||||||
this.panDy = dy;
|
this.panDy = dy;
|
||||||
|
|
||||||
this.graph.fireEvent(new EventObject(InternalEvent.PAN));
|
this.fireEvent(new EventObject(InternalEvent.PAN));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,23 +188,18 @@ class GraphPanning {
|
||||||
* @param cell {@link mxCell} to be made visible.
|
* @param cell {@link mxCell} to be made visible.
|
||||||
* @param center Optional boolean flag. Default is `false`.
|
* @param center Optional boolean flag. Default is `false`.
|
||||||
*/
|
*/
|
||||||
scrollCellToVisible(cell: Cell, center: boolean = false): void {
|
scrollCellToVisible(cell: Cell, center = false) {
|
||||||
const x = -this.graph.view.translate.x;
|
const x = -this.getView().translate.x;
|
||||||
const y = -this.graph.view.translate.y;
|
const y = -this.getView().translate.y;
|
||||||
|
|
||||||
const state = this.graph.view.getState(cell);
|
const state = this.getView().getState(cell);
|
||||||
|
|
||||||
if (state != null) {
|
if (state) {
|
||||||
const bounds = new Rectangle(
|
const bounds = new Rectangle(x + state.x, y + state.y, state.width, state.height);
|
||||||
x + state.x,
|
|
||||||
y + state.y,
|
|
||||||
state.width,
|
|
||||||
state.height
|
|
||||||
);
|
|
||||||
|
|
||||||
if (center && this.graph.container != null) {
|
if (center && this.getContainer()) {
|
||||||
const w = this.graph.container.clientWidth;
|
const w = this.getContainer().clientWidth;
|
||||||
const h = this.graph.container.clientHeight;
|
const h = this.getContainer().clientHeight;
|
||||||
|
|
||||||
bounds.x = bounds.getCenterX() - w / 2;
|
bounds.x = bounds.getCenterX() - w / 2;
|
||||||
bounds.width = w;
|
bounds.width = w;
|
||||||
|
@ -227,20 +207,14 @@ class GraphPanning {
|
||||||
bounds.height = h;
|
bounds.height = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tr = new Point(
|
const tr = new Point(this.getView().translate.x, this.getView().translate.y);
|
||||||
this.graph.view.translate.x,
|
|
||||||
this.graph.view.translate.y
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.scrollRectToVisible(bounds)) {
|
if (this.scrollRectToVisible(bounds)) {
|
||||||
// Triggers an update via the view's event source
|
// Triggers an update via the view's event source
|
||||||
const tr2 = new Point(
|
const tr2 = new Point(this.getView().translate.x, this.getView().translate.y);
|
||||||
this.graph.view.translate.x,
|
this.getView().translate.x = tr.x;
|
||||||
this.graph.view.translate.y
|
this.getView().translate.y = tr.y;
|
||||||
);
|
this.getView().setTranslate(tr2.x, tr2.y);
|
||||||
this.graph.view.translate.x = tr.x;
|
|
||||||
this.graph.view.translate.y = tr.y;
|
|
||||||
this.graph.view.setTranslate(tr2.x, tr2.y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,84 +224,84 @@ class GraphPanning {
|
||||||
*
|
*
|
||||||
* @param rect {@link mxRectangle} to be made visible.
|
* @param rect {@link mxRectangle} to be made visible.
|
||||||
*/
|
*/
|
||||||
scrollRectToVisible(rect: Rectangle): boolean {
|
scrollRectToVisible(rect: Rectangle) {
|
||||||
let isChanged = false;
|
let isChanged = false;
|
||||||
|
|
||||||
if (rect != null) {
|
const container = <HTMLElement>this.getContainer();
|
||||||
const container = <HTMLElement>this.graph.container;
|
const w = container.offsetWidth;
|
||||||
const w = container.offsetWidth;
|
const h = container.offsetHeight;
|
||||||
const h = container.offsetHeight;
|
|
||||||
|
|
||||||
const widthLimit = Math.min(w, rect.width);
|
const widthLimit = Math.min(w, rect.width);
|
||||||
const heightLimit = Math.min(h, rect.height);
|
const heightLimit = Math.min(h, rect.height);
|
||||||
|
|
||||||
if (hasScrollbars(container)) {
|
if (hasScrollbars(container)) {
|
||||||
rect.x += this.graph.view.translate.x;
|
rect.x += this.getView().translate.x;
|
||||||
rect.y += this.graph.view.translate.y;
|
rect.y += this.getView().translate.y;
|
||||||
let dx = container.scrollLeft - rect.x;
|
let dx = container.scrollLeft - rect.x;
|
||||||
const ddx = Math.max(dx - container.scrollLeft, 0);
|
const ddx = Math.max(dx - container.scrollLeft, 0);
|
||||||
|
|
||||||
|
if (dx > 0) {
|
||||||
|
container.scrollLeft -= dx + 2;
|
||||||
|
} else {
|
||||||
|
dx = rect.x + widthLimit - container.scrollLeft - container.clientWidth;
|
||||||
|
|
||||||
if (dx > 0) {
|
if (dx > 0) {
|
||||||
container.scrollLeft -= dx + 2;
|
container.scrollLeft += dx + 2;
|
||||||
} else {
|
|
||||||
dx =
|
|
||||||
rect.x + widthLimit - container.scrollLeft - container.clientWidth;
|
|
||||||
|
|
||||||
if (dx > 0) {
|
|
||||||
container.scrollLeft += dx + 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let dy = container.scrollTop - rect.y;
|
let dy = container.scrollTop - rect.y;
|
||||||
const ddy = Math.max(0, dy - container.scrollTop);
|
const ddy = Math.max(0, dy - container.scrollTop);
|
||||||
|
|
||||||
|
if (dy > 0) {
|
||||||
|
container.scrollTop -= dy + 2;
|
||||||
|
} else {
|
||||||
|
dy = rect.y + heightLimit - container.scrollTop - container.clientHeight;
|
||||||
|
|
||||||
if (dy > 0) {
|
if (dy > 0) {
|
||||||
container.scrollTop -= dy + 2;
|
container.scrollTop += dy + 2;
|
||||||
} else {
|
|
||||||
dy =
|
|
||||||
rect.y + heightLimit - container.scrollTop - container.clientHeight;
|
|
||||||
|
|
||||||
if (dy > 0) {
|
|
||||||
container.scrollTop += dy + 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.useScrollbarsForPanning && (ddx != 0 || ddy != 0)) {
|
if (!this.useScrollbarsForPanning && (ddx != 0 || ddy != 0)) {
|
||||||
this.graph.view.setTranslate(ddx, ddy);
|
this.getView().setTranslate(ddx, ddy);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const x = -this.graph.view.translate.x;
|
const x = -this.getView().translate.x;
|
||||||
const y = -this.graph.view.translate.y;
|
const y = -this.getView().translate.y;
|
||||||
|
|
||||||
const s = this.graph.view.scale;
|
const s = this.getView().scale;
|
||||||
|
|
||||||
if (rect.x + widthLimit > x + w) {
|
if (rect.x + widthLimit > x + w) {
|
||||||
this.graph.view.translate.x -= (rect.x + widthLimit - w - x) / s;
|
this.getView().translate.x -= (rect.x + widthLimit - w - x) / s;
|
||||||
isChanged = true;
|
isChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rect.y + heightLimit > y + h) {
|
if (rect.y + heightLimit > y + h) {
|
||||||
this.graph.view.translate.y -= (rect.y + heightLimit - h - y) / s;
|
this.getView().translate.y -= (rect.y + heightLimit - h - y) / s;
|
||||||
isChanged = true;
|
isChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rect.x < x) {
|
if (rect.x < x) {
|
||||||
this.graph.view.translate.x += (x - rect.x) / s;
|
this.getView().translate.x += (x - rect.x) / s;
|
||||||
isChanged = true;
|
isChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rect.y < y) {
|
if (rect.y < y) {
|
||||||
this.graph.view.translate.y += (y - rect.y) / s;
|
this.getView().translate.y += (y - rect.y) / s;
|
||||||
isChanged = true;
|
isChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isChanged) {
|
if (isChanged) {
|
||||||
this.graph.view.refresh();
|
this.getView().refresh();
|
||||||
|
|
||||||
// Repaints selection marker (ticket 18)
|
const selectionCellsHandler = this.getPlugin(
|
||||||
if (this.selectionCellsHandler != null) {
|
'SelectionCellsHandler'
|
||||||
this.selectionCellsHandler.refresh();
|
) as SelectionCellsHandler;
|
||||||
}
|
|
||||||
|
// Repaints selection marker (ticket 18)
|
||||||
|
if (selectionCellsHandler) {
|
||||||
|
selectionCellsHandler.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,10 +319,11 @@ class GraphPanning {
|
||||||
*
|
*
|
||||||
* @param enabled Boolean indicating if panning should be enabled.
|
* @param enabled Boolean indicating if panning should be enabled.
|
||||||
*/
|
*/
|
||||||
setPanning(enabled: boolean): void {
|
setPanning(enabled: boolean) {
|
||||||
(<PanningHandler>this.panningHandler).panningEnabled = enabled;
|
const panningHandler = this.getPlugin('PanningHandler') as PanningHandler;
|
||||||
}
|
|
||||||
|
|
||||||
|
if (panningHandler) panningHandler.panningEnabled = enabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GraphPanning;
|
export default GraphPanning;
|
||||||
|
|
|
@ -5,10 +5,22 @@
|
||||||
* Type definitions from the typed-mxgraph project
|
* Type definitions from the typed-mxgraph project
|
||||||
*/
|
*/
|
||||||
import EventSource from '../event/EventSource';
|
import EventSource from '../event/EventSource';
|
||||||
import utils, { hasScrollbars } from '../../util/Utils';
|
import { hasScrollbars } from '../../util/Utils';
|
||||||
import EventObject from '../event/EventObject';
|
import EventObject from '../event/EventObject';
|
||||||
import InternalEvent from '../event/InternalEvent';
|
import InternalEvent from '../event/InternalEvent';
|
||||||
import { isConsumed, isControlDown, isLeftMouseButton, isMultiTouchEvent, isPopupTrigger, isShiftDown } from '../../util/EventUtils';
|
import {
|
||||||
|
isConsumed,
|
||||||
|
isControlDown,
|
||||||
|
isLeftMouseButton,
|
||||||
|
isMultiTouchEvent,
|
||||||
|
isPopupTrigger,
|
||||||
|
isShiftDown,
|
||||||
|
} from '../../util/EventUtils';
|
||||||
|
import PanningManager from './PanningManager';
|
||||||
|
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||||
|
|
||||||
|
import type { GraphPlugin, MouseEventListener } from '../../types';
|
||||||
|
import type { MaxGraph } from '../Graph';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class: mxPanningHandler
|
* Class: mxPanningHandler
|
||||||
|
@ -39,66 +51,65 @@ import { isConsumed, isControlDown, isLeftMouseButton, isMultiTouchEvent, isPopu
|
||||||
* Fires when the panning handler changes its <active> state to false. The
|
* Fires when the panning handler changes its <active> state to false. The
|
||||||
* <code>event</code> property contains the corresponding <mxMouseEvent>.
|
* <code>event</code> property contains the corresponding <mxMouseEvent>.
|
||||||
*/
|
*/
|
||||||
class PanningHandler extends EventSource {
|
class PanningHandler extends EventSource implements GraphPlugin {
|
||||||
constructor(graph) {
|
static pluginId = 'PanningHandler';
|
||||||
|
|
||||||
|
constructor(graph: MaxGraph) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (graph != null) {
|
this.graph = graph;
|
||||||
this.graph = graph;
|
this.graph.addMouseListener(this);
|
||||||
this.graph.addMouseListener(this);
|
|
||||||
|
|
||||||
// Handles force panning event
|
// Handles force panning event
|
||||||
this.forcePanningHandler = (sender, evt) => {
|
this.forcePanningHandler = (sender: EventSource, eo: EventObject) => {
|
||||||
const evtName = evt.getProperty('eventName');
|
const evtName = eo.getProperty('eventName');
|
||||||
const me = evt.getProperty('event');
|
const me = eo.getProperty('event');
|
||||||
|
|
||||||
if (evtName === InternalEvent.MOUSE_DOWN && this.isForcePanningEvent(me)) {
|
if (evtName === InternalEvent.MOUSE_DOWN && this.isForcePanningEvent(me)) {
|
||||||
this.start(me);
|
this.start(me);
|
||||||
this.active = true;
|
this.active = true;
|
||||||
this.fireEvent(new EventObject(InternalEvent.PAN_START, 'event', me));
|
this.fireEvent(new EventObject(InternalEvent.PAN_START, 'event', me));
|
||||||
me.consume();
|
me.consume();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.graph.addListener(
|
this.graph.addListener(InternalEvent.FIRE_MOUSE_EVENT, this.forcePanningHandler);
|
||||||
InternalEvent.FIRE_MOUSE_EVENT,
|
|
||||||
this.forcePanningHandler
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handles pinch gestures
|
// Handles pinch gestures
|
||||||
this.gestureHandler = (sender, eo) => {
|
this.gestureHandler = (sender: EventSource, eo: EventObject) => {
|
||||||
if (this.isPinchEnabled()) {
|
if (this.isPinchEnabled()) {
|
||||||
const evt = eo.getProperty('event');
|
const evt = eo.getProperty('event');
|
||||||
|
|
||||||
if (!isConsumed(evt) && evt.type === 'gesturestart') {
|
if (!isConsumed(evt) && evt.type === 'gesturestart') {
|
||||||
this.initialScale = this.graph.view.scale;
|
this.initialScale = this.graph.view.scale;
|
||||||
|
|
||||||
// Forces start of panning when pinch gesture starts
|
// Forces start of panning when pinch gesture starts
|
||||||
if (!this.active && this.mouseDownEvent != null) {
|
if (!this.active && this.mouseDownEvent) {
|
||||||
this.start(this.mouseDownEvent);
|
this.start(this.mouseDownEvent);
|
||||||
this.mouseDownEvent = null;
|
this.mouseDownEvent = null;
|
||||||
}
|
|
||||||
} else if (evt.type === 'gestureend' && this.initialScale != null) {
|
|
||||||
this.initialScale = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.initialScale != null) {
|
|
||||||
this.zoomGraph(evt);
|
|
||||||
}
|
}
|
||||||
|
} else if (evt.type === 'gestureend' && this.initialScale !== 0) {
|
||||||
|
this.initialScale = 0;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
this.graph.addListener(InternalEvent.GESTURE, this.gestureHandler);
|
if (this.initialScale !== 0) {
|
||||||
|
this.zoomGraph(evt);
|
||||||
this.mouseUpListener = () => {
|
|
||||||
if (this.active) {
|
|
||||||
this.reset();
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Stops scrolling on every mouseup anywhere in the document
|
this.graph.addListener(InternalEvent.GESTURE, this.gestureHandler);
|
||||||
InternalEvent.addListener(document, 'mouseup', this.mouseUpListener);
|
|
||||||
}
|
this.mouseUpListener = () => {
|
||||||
|
if (this.active) {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stops scrolling on every mouseup anywhere in the document
|
||||||
|
InternalEvent.addListener(document, 'mouseup', this.mouseUpListener);
|
||||||
|
|
||||||
|
this.panningManager = new PanningManager(graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -106,8 +117,9 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Reference to the enclosing <mxGraph>.
|
* Reference to the enclosing <mxGraph>.
|
||||||
*/
|
*/
|
||||||
// graph: mxGraph;
|
graph: MaxGraph;
|
||||||
graph = null;
|
|
||||||
|
panningManager: PanningManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: useLeftButtonForPanning
|
* Variable: useLeftButtonForPanning
|
||||||
|
@ -115,7 +127,6 @@ class PanningHandler extends EventSource {
|
||||||
* Specifies if panning should be active for the left mouse button.
|
* Specifies if panning should be active for the left mouse button.
|
||||||
* Setting this to true may conflict with <mxRubberband>. Default is false.
|
* Setting this to true may conflict with <mxRubberband>. Default is false.
|
||||||
*/
|
*/
|
||||||
// useLeftButtonForPanning: boolean;
|
|
||||||
useLeftButtonForPanning = false;
|
useLeftButtonForPanning = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,7 +134,6 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Specifies if <mxEvent.isPopupTrigger> should also be used for panning.
|
* Specifies if <mxEvent.isPopupTrigger> should also be used for panning.
|
||||||
*/
|
*/
|
||||||
// usePopupTrigger: boolean;
|
|
||||||
usePopupTrigger = true;
|
usePopupTrigger = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,7 +142,6 @@ class PanningHandler extends EventSource {
|
||||||
* Specifies if panning should be active even if there is a cell under the
|
* Specifies if panning should be active even if there is a cell under the
|
||||||
* mousepointer. Default is false.
|
* mousepointer. Default is false.
|
||||||
*/
|
*/
|
||||||
// ignoreCell: boolean;
|
|
||||||
ignoreCell = false;
|
ignoreCell = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,7 +149,6 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Specifies if the panning should be previewed. Default is true.
|
* Specifies if the panning should be previewed. Default is true.
|
||||||
*/
|
*/
|
||||||
// previewEnabled: boolean;
|
|
||||||
previewEnabled = true;
|
previewEnabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,7 +157,6 @@ class PanningHandler extends EventSource {
|
||||||
* Specifies if the panning steps should be aligned to the grid size.
|
* Specifies if the panning steps should be aligned to the grid size.
|
||||||
* Default is false.
|
* Default is false.
|
||||||
*/
|
*/
|
||||||
// useGrid: boolean;
|
|
||||||
useGrid = false;
|
useGrid = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,7 +164,6 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Specifies if panning should be enabled. Default is true.
|
* Specifies if panning should be enabled. Default is true.
|
||||||
*/
|
*/
|
||||||
// panningEnabled: boolean;
|
|
||||||
panningEnabled = true;
|
panningEnabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,15 +171,15 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Specifies if pinch gestures should be handled as zoom. Default is true.
|
* Specifies if pinch gestures should be handled as zoom. Default is true.
|
||||||
*/
|
*/
|
||||||
// pinchEnabled: boolean;
|
|
||||||
pinchEnabled = true;
|
pinchEnabled = true;
|
||||||
|
|
||||||
|
initialScale = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: maxScale
|
* Variable: maxScale
|
||||||
*
|
*
|
||||||
* Specifies the maximum scale. Default is 8.
|
* Specifies the maximum scale. Default is 8.
|
||||||
*/
|
*/
|
||||||
// maxScale: number;
|
|
||||||
maxScale = 8;
|
maxScale = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,7 +187,6 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Specifies the minimum scale. Default is 0.01.
|
* Specifies the minimum scale. Default is 0.01.
|
||||||
*/
|
*/
|
||||||
// minScale: number;
|
|
||||||
minScale = 0.01;
|
minScale = 0.01;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -189,23 +194,20 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Holds the current horizontal offset.
|
* Holds the current horizontal offset.
|
||||||
*/
|
*/
|
||||||
// dx: number;
|
dx = 0;
|
||||||
dx = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: dy
|
* Variable: dy
|
||||||
*
|
*
|
||||||
* Holds the current vertical offset.
|
* Holds the current vertical offset.
|
||||||
*/
|
*/
|
||||||
// dy: number;
|
dy = 0;
|
||||||
dy = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: startX
|
* Variable: startX
|
||||||
*
|
*
|
||||||
* Holds the x-coordinate of the start point.
|
* Holds the x-coordinate of the start point.
|
||||||
*/
|
*/
|
||||||
// startX: number;
|
|
||||||
startX = 0;
|
startX = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -213,17 +215,29 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Holds the y-coordinate of the start point.
|
* Holds the y-coordinate of the start point.
|
||||||
*/
|
*/
|
||||||
// startY: number;
|
|
||||||
startY = 0;
|
startY = 0;
|
||||||
|
|
||||||
|
dx0 = 0;
|
||||||
|
dy0 = 0;
|
||||||
|
|
||||||
|
panningTrigger = false;
|
||||||
|
|
||||||
|
active = false;
|
||||||
|
|
||||||
|
forcePanningHandler: (sender: EventSource, evt: EventObject) => void;
|
||||||
|
gestureHandler: (sender: EventSource, evt: EventObject) => void;
|
||||||
|
|
||||||
|
mouseUpListener: MouseEventListener;
|
||||||
|
|
||||||
|
mouseDownEvent: InternalMouseEvent | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function: isActive
|
* Function: isActive
|
||||||
*
|
*
|
||||||
* Returns true if the handler is currently active.
|
* Returns true if the handler is currently active.
|
||||||
*/
|
*/
|
||||||
// isActive(): boolean;
|
|
||||||
isActive() {
|
isActive() {
|
||||||
return this.active || this.initialScale != null;
|
return this.active || this.initialScale !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -231,7 +245,6 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Returns <panningEnabled>.
|
* Returns <panningEnabled>.
|
||||||
*/
|
*/
|
||||||
// isPanningEnabled(): boolean;
|
|
||||||
isPanningEnabled() {
|
isPanningEnabled() {
|
||||||
return this.panningEnabled;
|
return this.panningEnabled;
|
||||||
}
|
}
|
||||||
|
@ -241,8 +254,7 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Sets <panningEnabled>.
|
* Sets <panningEnabled>.
|
||||||
*/
|
*/
|
||||||
// setPanningEnabled(value: boolean): void;
|
setPanningEnabled(value: boolean) {
|
||||||
setPanningEnabled(value) {
|
|
||||||
this.panningEnabled = value;
|
this.panningEnabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +263,6 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Returns <pinchEnabled>.
|
* Returns <pinchEnabled>.
|
||||||
*/
|
*/
|
||||||
// isPinchEnabled(): boolean;
|
|
||||||
isPinchEnabled() {
|
isPinchEnabled() {
|
||||||
return this.pinchEnabled;
|
return this.pinchEnabled;
|
||||||
}
|
}
|
||||||
|
@ -261,8 +272,7 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Sets <pinchEnabled>.
|
* Sets <pinchEnabled>.
|
||||||
*/
|
*/
|
||||||
// setPinchEnabled(value: boolean): void;
|
setPinchEnabled(value: boolean) {
|
||||||
setPinchEnabled(value) {
|
|
||||||
this.pinchEnabled = value;
|
this.pinchEnabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,14 +283,11 @@ class PanningHandler extends EventSource {
|
||||||
* given cell. This returns true if control-shift is pressed or if
|
* given cell. This returns true if control-shift is pressed or if
|
||||||
* <usePopupTrigger> is true and the event is a popup trigger.
|
* <usePopupTrigger> is true and the event is a popup trigger.
|
||||||
*/
|
*/
|
||||||
// isPanningTrigger(me: mxMouseEvent): boolean;
|
isPanningTrigger(me: InternalMouseEvent) {
|
||||||
isPanningTrigger(me) {
|
|
||||||
const evt = me.getEvent();
|
const evt = me.getEvent();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(this.useLeftButtonForPanning &&
|
(this.useLeftButtonForPanning && !me.getState() && isLeftMouseButton(evt)) ||
|
||||||
me.getState() == null &&
|
|
||||||
isLeftMouseButton(evt)) ||
|
|
||||||
(isControlDown(evt) && isShiftDown(evt)) ||
|
(isControlDown(evt) && isShiftDown(evt)) ||
|
||||||
(this.usePopupTrigger && isPopupTrigger(evt))
|
(this.usePopupTrigger && isPopupTrigger(evt))
|
||||||
);
|
);
|
||||||
|
@ -293,8 +300,7 @@ class PanningHandler extends EventSource {
|
||||||
* implementation always returns true if <ignoreCell> is true or for
|
* implementation always returns true if <ignoreCell> is true or for
|
||||||
* multi touch events.
|
* multi touch events.
|
||||||
*/
|
*/
|
||||||
// isForcePanningEvent(me: mxMouseEvent): boolean;
|
isForcePanningEvent(me: InternalMouseEvent) {
|
||||||
isForcePanningEvent(me) {
|
|
||||||
return this.ignoreCell || isMultiTouchEvent(me.getEvent());
|
return this.ignoreCell || isMultiTouchEvent(me.getEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,8 +310,7 @@ class PanningHandler extends EventSource {
|
||||||
* Handles the event by initiating the panning. By consuming the event all
|
* Handles the event by initiating the panning. By consuming the event all
|
||||||
* subsequent events of the gesture are redirected to this handler.
|
* subsequent events of the gesture are redirected to this handler.
|
||||||
*/
|
*/
|
||||||
// mouseDown(sender: any, me: mxMouseEvent): void;
|
mouseDown(sender: EventSource, me: InternalMouseEvent) {
|
||||||
mouseDown(sender, me) {
|
|
||||||
this.mouseDownEvent = me;
|
this.mouseDownEvent = me;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -324,16 +329,15 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Starts panning at the given event.
|
* Starts panning at the given event.
|
||||||
*/
|
*/
|
||||||
// start(me: mxMouseEvent): void;
|
start(me: InternalMouseEvent) {
|
||||||
start(me) {
|
|
||||||
this.dx0 = -this.graph.container.scrollLeft;
|
this.dx0 = -this.graph.container.scrollLeft;
|
||||||
this.dy0 = -this.graph.container.scrollTop;
|
this.dy0 = -this.graph.container.scrollTop;
|
||||||
|
|
||||||
// Stores the location of the trigger event
|
// Stores the location of the trigger event
|
||||||
this.startX = me.getX();
|
this.startX = me.getX();
|
||||||
this.startY = me.getY();
|
this.startY = me.getY();
|
||||||
this.dx = null;
|
this.dx = 0;
|
||||||
this.dy = null;
|
this.dy = 0;
|
||||||
|
|
||||||
this.panningTrigger = true;
|
this.panningTrigger = true;
|
||||||
}
|
}
|
||||||
|
@ -366,8 +370,7 @@ class PanningHandler extends EventSource {
|
||||||
* };
|
* };
|
||||||
* (end)
|
* (end)
|
||||||
*/
|
*/
|
||||||
// consumePanningTrigger(me: mxMouseEvent): void;
|
consumePanningTrigger(me: InternalMouseEvent) {
|
||||||
consumePanningTrigger(me) {
|
|
||||||
me.consume();
|
me.consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,8 +379,7 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Handles the event by updating the panning on the graph.
|
* Handles the event by updating the panning on the graph.
|
||||||
*/
|
*/
|
||||||
// mouseMove(sender: any, me: mxMouseEvent): void;
|
mouseMove(sender: EventSource, me: InternalMouseEvent) {
|
||||||
mouseMove(sender, me) {
|
|
||||||
this.dx = me.getX() - this.startX;
|
this.dx = me.getX() - this.startX;
|
||||||
this.dy = me.getY() - this.startY;
|
this.dy = me.getY() - this.startY;
|
||||||
|
|
||||||
|
@ -399,8 +401,8 @@ class PanningHandler extends EventSource {
|
||||||
// Panning is activated only if the mouse is moved
|
// Panning is activated only if the mouse is moved
|
||||||
// beyond the graph tolerance
|
// beyond the graph tolerance
|
||||||
this.active =
|
this.active =
|
||||||
Math.abs(this.dx) > this.graph.tolerance ||
|
Math.abs(this.dx) > this.graph.getSnapTolerance() ||
|
||||||
Math.abs(this.dy) > this.graph.tolerance;
|
Math.abs(this.dy) > this.graph.getSnapTolerance();
|
||||||
|
|
||||||
if (!tmp && this.active) {
|
if (!tmp && this.active) {
|
||||||
this.fireEvent(new EventObject(InternalEvent.PAN_START, 'event', me));
|
this.fireEvent(new EventObject(InternalEvent.PAN_START, 'event', me));
|
||||||
|
@ -418,13 +420,12 @@ class PanningHandler extends EventSource {
|
||||||
* Handles the event by setting the translation on the view or showing the
|
* Handles the event by setting the translation on the view or showing the
|
||||||
* popupmenu.
|
* popupmenu.
|
||||||
*/
|
*/
|
||||||
// mouseUp(sender: any, me: mxMouseEvent): void;
|
mouseUp(sender: EventSource, me: InternalMouseEvent) {
|
||||||
mouseUp(sender, me) {
|
|
||||||
if (this.active) {
|
if (this.active) {
|
||||||
if (this.dx != null && this.dy != null) {
|
if (this.dx !== 0 && this.dy !== 0) {
|
||||||
// Ignores if scrollbars have been used for panning
|
// Ignores if scrollbars have been used for panning
|
||||||
if (
|
if (
|
||||||
!this.graph.useScrollbarsForPanning ||
|
!this.graph.isUseScrollbarsForPanning() ||
|
||||||
!hasScrollbars(this.graph.container)
|
!hasScrollbars(this.graph.container)
|
||||||
) {
|
) {
|
||||||
const { scale } = this.graph.getView();
|
const { scale } = this.graph.getView();
|
||||||
|
@ -447,16 +448,11 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Zooms the graph to the given value and consumed the event if needed.
|
* Zooms the graph to the given value and consumed the event if needed.
|
||||||
*/
|
*/
|
||||||
zoomGraph(evt) {
|
zoomGraph(evt: Event) {
|
||||||
|
// @ts-ignore evt may have scale property
|
||||||
let value = Math.round(this.initialScale * evt.scale * 100) / 100;
|
let value = Math.round(this.initialScale * evt.scale * 100) / 100;
|
||||||
|
value = Math.max(this.minScale, value);
|
||||||
if (this.minScale != null) {
|
value = Math.min(this.maxScale, value);
|
||||||
value = Math.max(this.minScale, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.maxScale != null) {
|
|
||||||
value = Math.min(this.maxScale, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.graph.view.scale !== value) {
|
if (this.graph.view.scale !== value) {
|
||||||
this.graph.zoomTo(value);
|
this.graph.zoomTo(value);
|
||||||
|
@ -470,13 +466,12 @@ class PanningHandler extends EventSource {
|
||||||
* Handles the event by setting the translation on the view or showing the
|
* Handles the event by setting the translation on the view or showing the
|
||||||
* popupmenu.
|
* popupmenu.
|
||||||
*/
|
*/
|
||||||
// reset(): void;
|
|
||||||
reset() {
|
reset() {
|
||||||
this.panningTrigger = false;
|
this.panningTrigger = false;
|
||||||
this.mouseDownEvent = null;
|
this.mouseDownEvent = null;
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.dx = null;
|
this.dx = 0;
|
||||||
this.dy = null;
|
this.dy = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -484,8 +479,7 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Pans <graph> by the given amount.
|
* Pans <graph> by the given amount.
|
||||||
*/
|
*/
|
||||||
// panGraph(dx: number, dy: number): void;
|
panGraph(dx: number, dy: number) {
|
||||||
panGraph(dx, dy) {
|
|
||||||
this.graph.getView().setTranslate(dx, dy);
|
this.graph.getView().setTranslate(dx, dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,8 +488,7 @@ class PanningHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Destroys the handler and all its resources and DOM nodes.
|
* Destroys the handler and all its resources and DOM nodes.
|
||||||
*/
|
*/
|
||||||
// destroy(): void;
|
onDestroy() {
|
||||||
destroy() {
|
|
||||||
this.graph.removeMouseListener(this);
|
this.graph.removeMouseListener(this);
|
||||||
this.graph.removeListener(this.forcePanningHandler);
|
this.graph.removeListener(this.forcePanningHandler);
|
||||||
this.graph.removeListener(this.gestureHandler);
|
this.graph.removeListener(this.gestureHandler);
|
||||||
|
|
|
@ -5,8 +5,11 @@
|
||||||
* Type definitions from the typed-mxgraph project
|
* Type definitions from the typed-mxgraph project
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { MouseEventListener, MouseListenerSet } from '../../types';
|
||||||
import { hasScrollbars } from '../../util/Utils';
|
import { hasScrollbars } from '../../util/Utils';
|
||||||
import EventObject from '../event/EventObject';
|
import EventObject from '../event/EventObject';
|
||||||
|
import InternalEvent from '../event/InternalEvent';
|
||||||
|
import { MaxGraph } from '../Graph';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class: mxPanningManager
|
* Class: mxPanningManager
|
||||||
|
@ -14,7 +17,7 @@ import EventObject from '../event/EventObject';
|
||||||
* Implements a handler for panning.
|
* Implements a handler for panning.
|
||||||
*/
|
*/
|
||||||
class PanningManager {
|
class PanningManager {
|
||||||
constructor(graph) {
|
constructor(graph: MaxGraph) {
|
||||||
this.thread = null;
|
this.thread = null;
|
||||||
this.active = false;
|
this.active = false;
|
||||||
this.tdx = 0;
|
this.tdx = 0;
|
||||||
|
@ -46,7 +49,7 @@ class PanningManager {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Stops scrolling on every mouseup anywhere in the document
|
// Stops scrolling on every mouseup anywhere in the document
|
||||||
mxEvent.addListener(document, 'mouseup', this.mouseUpListener);
|
InternalEvent.addListener(document, 'mouseup', this.mouseUpListener);
|
||||||
|
|
||||||
const createThread = () => {
|
const createThread = () => {
|
||||||
this.scrollbars = hasScrollbars(graph.container);
|
this.scrollbars = hasScrollbars(graph.container);
|
||||||
|
@ -61,9 +64,9 @@ class PanningManager {
|
||||||
const left = -graph.container.scrollLeft - Math.ceil(this.dx);
|
const left = -graph.container.scrollLeft - Math.ceil(this.dx);
|
||||||
const top = -graph.container.scrollTop - Math.ceil(this.dy);
|
const top = -graph.container.scrollTop - Math.ceil(this.dy);
|
||||||
graph.panGraph(left, top);
|
graph.panGraph(left, top);
|
||||||
graph.panDx = this.scrollLeft - graph.container.scrollLeft;
|
graph.setPanDx(this.scrollLeft - graph.container.scrollLeft);
|
||||||
graph.panDy = this.scrollTop - graph.container.scrollTop;
|
graph.setPanDy(this.scrollTop - graph.container.scrollTop);
|
||||||
graph.fireEvent(new EventObject(mxEvent.PAN));
|
graph.fireEvent(new EventObject(InternalEvent.PAN));
|
||||||
// TODO: Implement graph.autoExtend
|
// TODO: Implement graph.autoExtend
|
||||||
} else {
|
} else {
|
||||||
graph.panGraph(this.getDx(), this.getDy());
|
graph.panGraph(this.getDx(), this.getDy());
|
||||||
|
@ -171,8 +174,8 @@ class PanningManager {
|
||||||
this.tdy = 0;
|
this.tdy = 0;
|
||||||
|
|
||||||
if (!this.scrollbars) {
|
if (!this.scrollbars) {
|
||||||
const px = graph.panDx;
|
const px = graph.getPanDx();
|
||||||
const py = graph.panDy;
|
const py = graph.getPanDy();
|
||||||
|
|
||||||
if (px != 0 || py != 0) {
|
if (px != 0 || py != 0) {
|
||||||
graph.panGraph(0, 0);
|
graph.panGraph(0, 0);
|
||||||
|
@ -182,16 +185,16 @@ class PanningManager {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
graph.panDx = 0;
|
graph.setPanDx(0);
|
||||||
graph.panDy = 0;
|
graph.setPanDy(0);
|
||||||
graph.fireEvent(new EventObject(mxEvent.PAN));
|
graph.fireEvent(new EventObject(InternalEvent.PAN));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.destroy = () => {
|
this.destroy = () => {
|
||||||
graph.removeMouseListener(this.mouseListener);
|
graph.removeMouseListener(this.mouseListener);
|
||||||
mxEvent.removeListener(document, 'mouseup', this.mouseUpListener);
|
InternalEvent.removeListener(document, 'mouseup', this.mouseUpListener);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +203,6 @@ class PanningManager {
|
||||||
*
|
*
|
||||||
* Damper value for the panning. Default is 1/6.
|
* Damper value for the panning. Default is 1/6.
|
||||||
*/
|
*/
|
||||||
// damper: number;
|
|
||||||
damper = 1 / 6;
|
damper = 1 / 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -208,7 +210,6 @@ class PanningManager {
|
||||||
*
|
*
|
||||||
* Delay in milliseconds for the panning. Default is 10.
|
* Delay in milliseconds for the panning. Default is 10.
|
||||||
*/
|
*/
|
||||||
// delay: number;
|
|
||||||
delay = 10;
|
delay = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -216,7 +217,6 @@ class PanningManager {
|
||||||
*
|
*
|
||||||
* Specifies if mouse events outside of the component should be handled. Default is true.
|
* Specifies if mouse events outside of the component should be handled. Default is true.
|
||||||
*/
|
*/
|
||||||
// handleMouseOut: boolean;
|
|
||||||
handleMouseOut = true;
|
handleMouseOut = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -224,8 +224,33 @@ class PanningManager {
|
||||||
*
|
*
|
||||||
* Border to handle automatic panning inside the component. Default is 0 (disabled).
|
* Border to handle automatic panning inside the component. Default is 0 (disabled).
|
||||||
*/
|
*/
|
||||||
// border: number;
|
|
||||||
border = 0;
|
border = 0;
|
||||||
|
|
||||||
|
thread: number | null = null;
|
||||||
|
|
||||||
|
active = false;
|
||||||
|
|
||||||
|
tdx = 0;
|
||||||
|
tdy = 0;
|
||||||
|
t0x = 0;
|
||||||
|
t0y = 0;
|
||||||
|
dx = 0;
|
||||||
|
dy = 0;
|
||||||
|
scrollbars = false;
|
||||||
|
scrollLeft = 0;
|
||||||
|
scrollTop = 0;
|
||||||
|
|
||||||
|
mouseListener: MouseListenerSet;
|
||||||
|
|
||||||
|
mouseUpListener: MouseEventListener;
|
||||||
|
|
||||||
|
stop: () => void;
|
||||||
|
isActive: () => boolean;
|
||||||
|
getDx: () => number;
|
||||||
|
getDy: () => number;
|
||||||
|
start: () => void;
|
||||||
|
panTo: (x: number, y: number, w: number, h: number) => void;
|
||||||
|
destroy: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PanningManager;
|
export default PanningManager;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { getMainEvent, isMultiTouchEvent } from '../../util/EventUtils';
|
||||||
import Graph from '../Graph';
|
import Graph from '../Graph';
|
||||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||||
import Cell from '../cell/datatypes/Cell';
|
import Cell from '../cell/datatypes/Cell';
|
||||||
|
import { GraphPlugin } from '../../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class: mxPopupMenuHandler
|
* Class: mxPopupMenuHandler
|
||||||
|
@ -21,7 +22,7 @@ import Cell from '../cell/datatypes/Cell';
|
||||||
*
|
*
|
||||||
* Constructs an event handler that creates a <mxPopupMenu>.
|
* Constructs an event handler that creates a <mxPopupMenu>.
|
||||||
*/
|
*/
|
||||||
class PopupMenuHandler extends mxPopupMenu {
|
class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
||||||
constructor(graph: Graph, factoryMethod: any) {
|
constructor(graph: Graph, factoryMethod: any) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ class CellHighlight {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Document me!!
|
// TODO: Document me!!
|
||||||
highlightColor: ColorValue | null = null;
|
highlightColor: ColorValue;
|
||||||
|
|
||||||
strokeWidth: number = 0;
|
strokeWidth: number = 0;
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ class CellHighlight {
|
||||||
|
|
||||||
opacity = 100;
|
opacity = 100;
|
||||||
|
|
||||||
repaintHandler: Function | null = null;
|
repaintHandler: Function;
|
||||||
|
|
||||||
shape: Shape | null = null;
|
shape: Shape | null = null;
|
||||||
|
|
||||||
|
@ -112,14 +112,14 @@ class CellHighlight {
|
||||||
* Holds the handler that automatically invokes reset if the highlight should be hidden.
|
* Holds the handler that automatically invokes reset if the highlight should be hidden.
|
||||||
* @default null
|
* @default null
|
||||||
*/
|
*/
|
||||||
resetHandler: Function | null = null;
|
resetHandler: Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the color of the rectangle used to highlight drop targets.
|
* Sets the color of the rectangle used to highlight drop targets.
|
||||||
*
|
*
|
||||||
* @param {string} color - String that represents the new highlight color.
|
* @param {string} color - String that represents the new highlight color.
|
||||||
*/
|
*/
|
||||||
setHighlightColor(color: ColorValue | null) {
|
setHighlightColor(color: ColorValue) {
|
||||||
this.highlightColor = color;
|
this.highlightColor = color;
|
||||||
|
|
||||||
if (this.shape) {
|
if (this.shape) {
|
||||||
|
@ -134,10 +134,12 @@ class CellHighlight {
|
||||||
this.shape = this.createShape();
|
this.shape = this.createShape();
|
||||||
this.repaint();
|
this.repaint();
|
||||||
|
|
||||||
const node = this.shape.node;
|
if (this.shape) {
|
||||||
|
const node = this.shape.node;
|
||||||
|
|
||||||
if (!this.keepOnTop && node?.parentNode?.firstChild !== node) {
|
if (!this.keepOnTop && node?.parentNode?.firstChild !== node && node.parentNode) {
|
||||||
node.parentNode.insertBefore(node, node.parentNode.firstChild);
|
node.parentNode.insertBefore(node, node.parentNode.firstChild);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,11 +147,11 @@ class CellHighlight {
|
||||||
* Creates and returns the highlight shape for the given state.
|
* Creates and returns the highlight shape for the given state.
|
||||||
*/
|
*/
|
||||||
createShape() {
|
createShape() {
|
||||||
if (!this.state) return;
|
if (!this.state) return null;
|
||||||
|
|
||||||
const shape = this.graph.cellRenderer.createShape(this.state);
|
const shape = this.graph.cellRenderer.createShape(this.state);
|
||||||
|
|
||||||
shape.svgStrokeTolerance = this.graph.tolerance;
|
shape.svgStrokeTolerance = this.graph.getEventTolerance();
|
||||||
shape.points = this.state.absolutePoints;
|
shape.points = this.state.absolutePoints;
|
||||||
shape.apply(this.state);
|
shape.apply(this.state);
|
||||||
shape.stroke = this.highlightColor;
|
shape.stroke = this.highlightColor;
|
||||||
|
@ -173,7 +175,7 @@ class CellHighlight {
|
||||||
/**
|
/**
|
||||||
* Updates the highlight after a change of the model or view.
|
* Updates the highlight after a change of the model or view.
|
||||||
*/
|
*/
|
||||||
getStrokeWidth(state: CellState | null = null): number | null {
|
getStrokeWidth(state: CellState | null = null) {
|
||||||
return this.strokeWidth;
|
return this.strokeWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,13 +258,13 @@ class CellHighlight {
|
||||||
/**
|
/**
|
||||||
* Destroys the handler and all its resources and DOM nodes.
|
* Destroys the handler and all its resources and DOM nodes.
|
||||||
*/
|
*/
|
||||||
destroy(): void {
|
destroy() {
|
||||||
const graph = <graph>this.graph;
|
const graph = this.graph;
|
||||||
graph.getView().removeListener(this.resetHandler);
|
graph.getView().removeListener(this.resetHandler);
|
||||||
graph.getView().removeListener(this.repaintHandler);
|
graph.getView().removeListener(this.repaintHandler);
|
||||||
graph.getModel().removeListener(this.repaintHandler);
|
graph.getModel().removeListener(this.repaintHandler);
|
||||||
|
|
||||||
if (this.shape != null) {
|
if (this.shape) {
|
||||||
this.shape.destroy();
|
this.shape.destroy();
|
||||||
this.shape = null;
|
this.shape = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import type GraphCells from '../cell/GraphCells';
|
||||||
import type Graph from '../Graph';
|
import type Graph from '../Graph';
|
||||||
import type GraphEvents from '../event/GraphEvents';
|
import type GraphEvents from '../event/GraphEvents';
|
||||||
import type EventSource from '../event/EventSource';
|
import type EventSource from '../event/EventSource';
|
||||||
|
import { MaxGraph } from '../Graph';
|
||||||
|
|
||||||
type PartialGraph = Pick<
|
type PartialGraph = Pick<
|
||||||
Graph,
|
Graph,
|
||||||
|
@ -36,6 +37,8 @@ class GraphSelection extends autoImplement<PartialClass>() {
|
||||||
*/
|
*/
|
||||||
doneResource: string = mxClient.language !== 'none' ? 'done' : '';
|
doneResource: string = mxClient.language !== 'none' ? 'done' : '';
|
||||||
|
|
||||||
|
getDoneResource = () => this.doneResource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the resource key for the status message while the selection is
|
* Specifies the resource key for the status message while the selection is
|
||||||
* being updated. If the resource for this key does not exist then the
|
* being updated. If the resource for this key does not exist then the
|
||||||
|
@ -44,6 +47,8 @@ class GraphSelection extends autoImplement<PartialClass>() {
|
||||||
updatingSelectionResource: string =
|
updatingSelectionResource: string =
|
||||||
mxClient.language !== 'none' ? 'updatingSelection' : '';
|
mxClient.language !== 'none' ? 'updatingSelection' : '';
|
||||||
|
|
||||||
|
getUpdatingSelectionResource = () => this.updatingSelectionResource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies if only one selected item at a time is allowed.
|
* Specifies if only one selected item at a time is allowed.
|
||||||
* Default is false.
|
* Default is false.
|
||||||
|
@ -222,7 +227,7 @@ class GraphSelection extends autoImplement<PartialClass>() {
|
||||||
(removed && removed.length > 0 && removed[0])
|
(removed && removed.length > 0 && removed[0])
|
||||||
) {
|
) {
|
||||||
const change = new SelectionChange(
|
const change = new SelectionChange(
|
||||||
this,
|
this as MaxGraph,
|
||||||
added || new CellArray(),
|
added || new CellArray(),
|
||||||
removed || new CellArray()
|
removed || new CellArray()
|
||||||
);
|
);
|
||||||
|
@ -512,7 +517,7 @@ class GraphSelection extends autoImplement<PartialClass>() {
|
||||||
) {
|
) {
|
||||||
const filter = (cell: Cell) => {
|
const filter = (cell: Cell) => {
|
||||||
return (
|
return (
|
||||||
this.getView().getState(cell) &&
|
!!this.getView().getState(cell) &&
|
||||||
(((selectGroups || cell.getChildCount() === 0) &&
|
(((selectGroups || cell.getChildCount() === 0) &&
|
||||||
cell.isVertex() &&
|
cell.isVertex() &&
|
||||||
vertices &&
|
vertices &&
|
||||||
|
|
|
@ -17,9 +17,10 @@ import mxClient from '../../mxClient';
|
||||||
import Rectangle from '../geometry/Rectangle';
|
import Rectangle from '../geometry/Rectangle';
|
||||||
import { isAltDown, isMultiTouchEvent } from '../../util/EventUtils';
|
import { isAltDown, isMultiTouchEvent } from '../../util/EventUtils';
|
||||||
import { clearSelection } from '../../util/DomUtils';
|
import { clearSelection } from '../../util/DomUtils';
|
||||||
import Graph from '../Graph';
|
import { MaxGraph } from '../Graph';
|
||||||
import { GraphPlugin } from '../../types';
|
import { GraphPlugin } from '../../types';
|
||||||
import EventObject from '../event/EventObject';
|
import EventObject from '../event/EventObject';
|
||||||
|
import EventSource from '../event/EventSource';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event handler that selects rectangular regions.
|
* Event handler that selects rectangular regions.
|
||||||
|
@ -27,23 +28,14 @@ import EventObject from '../event/EventObject';
|
||||||
* To enable rubberband selection in a graph, use the following code.
|
* To enable rubberband selection in a graph, use the following code.
|
||||||
*/
|
*/
|
||||||
class RubberBand implements GraphPlugin {
|
class RubberBand implements GraphPlugin {
|
||||||
forceRubberbandHandler?: Function;
|
static pluginId = 'RubberBand';
|
||||||
panHandler?: Function;
|
|
||||||
gestureHandler?: Function;
|
|
||||||
graph?: Graph;
|
|
||||||
first: Point | null = null;
|
|
||||||
destroyed: boolean = false;
|
|
||||||
dragHandler?: Function;
|
|
||||||
dropHandler?: Function;
|
|
||||||
|
|
||||||
constructor() {}
|
constructor(graph: MaxGraph) {
|
||||||
|
|
||||||
onInit(graph: Graph) {
|
|
||||||
this.graph = graph;
|
this.graph = graph;
|
||||||
this.graph.addMouseListener(this);
|
this.graph.addMouseListener(this);
|
||||||
|
|
||||||
// Handles force rubberband event
|
// Handles force rubberband event
|
||||||
this.forceRubberbandHandler = (sender: any, evt: EventObject) => {
|
this.forceRubberbandHandler = (sender: EventSource, evt: EventObject) => {
|
||||||
const evtName = evt.getProperty('eventName');
|
const evtName = evt.getProperty('eventName');
|
||||||
const me = evt.getProperty('event');
|
const me = evt.getProperty('event');
|
||||||
|
|
||||||
|
@ -67,8 +59,8 @@ class RubberBand implements GraphPlugin {
|
||||||
this.graph.addListener(InternalEvent.PAN, this.panHandler);
|
this.graph.addListener(InternalEvent.PAN, this.panHandler);
|
||||||
|
|
||||||
// Does not show menu if any touch gestures take place after the trigger
|
// Does not show menu if any touch gestures take place after the trigger
|
||||||
this.gestureHandler = (sender, eo) => {
|
this.gestureHandler = (sender: EventSource, eo: EventObject) => {
|
||||||
if (this.first != null) {
|
if (this.first) {
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -76,6 +68,20 @@ class RubberBand implements GraphPlugin {
|
||||||
this.graph.addListener(InternalEvent.GESTURE, this.gestureHandler);
|
this.graph.addListener(InternalEvent.GESTURE, this.gestureHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forceRubberbandHandler: Function;
|
||||||
|
panHandler: Function;
|
||||||
|
gestureHandler: Function;
|
||||||
|
graph: MaxGraph;
|
||||||
|
first: Point | null = null;
|
||||||
|
destroyed: boolean = false;
|
||||||
|
dragHandler: ((evt: MouseEvent) => void) | null = null;
|
||||||
|
dropHandler: ((evt: MouseEvent) => void) | null = null;
|
||||||
|
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
width = 0;
|
||||||
|
height = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the default opacity to be used for the rubberband div. Default is 20.
|
* Specifies the default opacity to be used for the rubberband div. Default is 20.
|
||||||
*/
|
*/
|
||||||
|
@ -93,14 +99,14 @@ class RubberBand implements GraphPlugin {
|
||||||
*
|
*
|
||||||
* Holds the DIV element which is currently visible.
|
* Holds the DIV element which is currently visible.
|
||||||
*/
|
*/
|
||||||
div = null;
|
div: HTMLElement | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: sharedDiv
|
* Variable: sharedDiv
|
||||||
*
|
*
|
||||||
* Holds the DIV element which is used to display the rubberband.
|
* Holds the DIV element which is used to display the rubberband.
|
||||||
*/
|
*/
|
||||||
sharedDiv = null;
|
sharedDiv: HTMLElement | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: currentX
|
* Variable: currentX
|
||||||
|
@ -119,12 +125,12 @@ class RubberBand implements GraphPlugin {
|
||||||
/**
|
/**
|
||||||
* Optional fade out effect. Default is false.
|
* Optional fade out effect. Default is false.
|
||||||
*/
|
*/
|
||||||
fadeOut: boolean = false;
|
fadeOut = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the rubberband selection shape.
|
* Creates the rubberband selection shape.
|
||||||
*/
|
*/
|
||||||
isEnabled(): boolean {
|
isEnabled() {
|
||||||
return this.enabled;
|
return this.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,12 +161,12 @@ class RubberBand implements GraphPlugin {
|
||||||
* event all subsequent events of the gesture are redirected to this
|
* event all subsequent events of the gesture are redirected to this
|
||||||
* handler.
|
* handler.
|
||||||
*/
|
*/
|
||||||
mouseDown(sender: any, me: InternalMouseEvent): void {
|
mouseDown(sender: EventSource, me: InternalMouseEvent) {
|
||||||
if (
|
if (
|
||||||
!me.isConsumed() &&
|
!me.isConsumed() &&
|
||||||
this.isEnabled() &&
|
this.isEnabled() &&
|
||||||
this.graph.isEnabled() &&
|
this.graph.isEnabled() &&
|
||||||
me.getState() == null &&
|
!me.getState() &&
|
||||||
!isMultiTouchEvent(me.getEvent())
|
!isMultiTouchEvent(me.getEvent())
|
||||||
) {
|
) {
|
||||||
const offset = getOffset(this.graph.container);
|
const offset = getOffset(this.graph.container);
|
||||||
|
@ -181,12 +187,12 @@ class RubberBand implements GraphPlugin {
|
||||||
/**
|
/**
|
||||||
* Creates the rubberband selection shape.
|
* Creates the rubberband selection shape.
|
||||||
*/
|
*/
|
||||||
start(x: number, y: number): void {
|
start(x: number, y: number) {
|
||||||
this.first = new Point(x, y);
|
this.first = new Point(x, y);
|
||||||
|
|
||||||
const { container } = this.graph;
|
const { container } = this.graph;
|
||||||
|
|
||||||
function createMouseEvent(evt) {
|
function createMouseEvent(evt: MouseEvent) {
|
||||||
const me = new InternalMouseEvent(evt);
|
const me = new InternalMouseEvent(evt);
|
||||||
const pt = convertPoint(container, me.getX(), me.getY());
|
const pt = convertPoint(container, me.getX(), me.getY());
|
||||||
|
|
||||||
|
@ -196,11 +202,11 @@ class RubberBand implements GraphPlugin {
|
||||||
return me;
|
return me;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dragHandler = (evt) => {
|
this.dragHandler = (evt: MouseEvent) => {
|
||||||
this.mouseMove(this.graph, createMouseEvent(evt));
|
this.mouseMove(this.graph, createMouseEvent(evt));
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dropHandler = (evt) => {
|
this.dropHandler = (evt: MouseEvent) => {
|
||||||
this.mouseUp(this.graph, createMouseEvent(evt));
|
this.mouseUp(this.graph, createMouseEvent(evt));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -220,8 +226,8 @@ class RubberBand implements GraphPlugin {
|
||||||
*
|
*
|
||||||
* Handles the event by updating therubberband selection.
|
* Handles the event by updating therubberband selection.
|
||||||
*/
|
*/
|
||||||
mouseMove(sender: any, me: InternalMouseEvent): void {
|
mouseMove(sender: EventSource, me: InternalMouseEvent) {
|
||||||
if (!me.isConsumed() && this.first != null) {
|
if (!me.isConsumed() && this.first) {
|
||||||
const origin = getScrollOrigin(this.graph.container);
|
const origin = getScrollOrigin(this.graph.container);
|
||||||
const offset = getOffset(this.graph.container);
|
const offset = getOffset(this.graph.container);
|
||||||
origin.x -= offset.x;
|
origin.x -= offset.x;
|
||||||
|
@ -230,10 +236,10 @@ class RubberBand implements GraphPlugin {
|
||||||
const y = me.getY() + origin.y;
|
const y = me.getY() + origin.y;
|
||||||
const dx = this.first.x - x;
|
const dx = this.first.x - x;
|
||||||
const dy = this.first.y - y;
|
const dy = this.first.y - y;
|
||||||
const tol = this.graph.tolerance;
|
const tol = this.graph.getEventTolerance();
|
||||||
|
|
||||||
if (this.div != null || Math.abs(dx) > tol || Math.abs(dy) > tol) {
|
if (this.div || Math.abs(dx) > tol || Math.abs(dy) > tol) {
|
||||||
if (this.div == null) {
|
if (!this.div) {
|
||||||
this.div = this.createShape();
|
this.div = this.createShape();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,8 +256,8 @@ class RubberBand implements GraphPlugin {
|
||||||
/**
|
/**
|
||||||
* Creates the rubberband selection shape.
|
* Creates the rubberband selection shape.
|
||||||
*/
|
*/
|
||||||
createShape(): HTMLElement | null {
|
createShape() {
|
||||||
if (this.sharedDiv == null) {
|
if (!this.sharedDiv) {
|
||||||
this.sharedDiv = document.createElement('div');
|
this.sharedDiv = document.createElement('div');
|
||||||
this.sharedDiv.className = 'mxRubberband';
|
this.sharedDiv.className = 'mxRubberband';
|
||||||
setOpacity(this.sharedDiv, this.defaultOpacity);
|
setOpacity(this.sharedDiv, this.defaultOpacity);
|
||||||
|
@ -272,8 +278,8 @@ class RubberBand implements GraphPlugin {
|
||||||
*
|
*
|
||||||
* Returns true if this handler is active.
|
* Returns true if this handler is active.
|
||||||
*/
|
*/
|
||||||
isActive(sender: any, me: InternalMouseEvent): boolean {
|
isActive(sender?: EventSource, me?: InternalMouseEvent) {
|
||||||
return this.div != null && this.div.style.display !== 'none';
|
return this.div && this.div.style.display !== 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -282,7 +288,7 @@ class RubberBand implements GraphPlugin {
|
||||||
* Handles the event by selecting the region of the rubberband using
|
* Handles the event by selecting the region of the rubberband using
|
||||||
* <mxGraph.selectRegion>.
|
* <mxGraph.selectRegion>.
|
||||||
*/
|
*/
|
||||||
mouseUp(sender: any, me: InternalMouseEvent): void {
|
mouseUp(sender: EventSource, me: InternalMouseEvent) {
|
||||||
const active = this.isActive();
|
const active = this.isActive();
|
||||||
this.reset();
|
this.reset();
|
||||||
|
|
||||||
|
@ -298,7 +304,7 @@ class RubberBand implements GraphPlugin {
|
||||||
* Resets the state of this handler and selects the current region
|
* Resets the state of this handler and selects the current region
|
||||||
* for the given event.
|
* for the given event.
|
||||||
*/
|
*/
|
||||||
execute(evt) {
|
execute(evt: MouseEvent) {
|
||||||
const rect = new Rectangle(this.x, this.y, this.width, this.height);
|
const rect = new Rectangle(this.x, this.y, this.width, this.height);
|
||||||
this.graph.selectRegion(rect, evt);
|
this.graph.selectRegion(rect, evt);
|
||||||
}
|
}
|
||||||
|
@ -309,18 +315,18 @@ class RubberBand implements GraphPlugin {
|
||||||
* Resets the state of the rubberband selection.
|
* Resets the state of the rubberband selection.
|
||||||
*/
|
*/
|
||||||
reset() {
|
reset() {
|
||||||
if (this.div != null) {
|
if (this.div) {
|
||||||
if (mxClient.IS_SVG && this.fadeOut) {
|
if (mxClient.IS_SVG && this.fadeOut) {
|
||||||
const temp = this.div;
|
const temp = this.div;
|
||||||
setPrefixedStyle(temp.style, 'transition', 'all 0.2s linear');
|
setPrefixedStyle(temp.style, 'transition', 'all 0.2s linear');
|
||||||
temp.style.pointerEvents = 'none';
|
temp.style.pointerEvents = 'none';
|
||||||
temp.style.opacity = 0;
|
temp.style.opacity = String(0);
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
temp.parentNode.removeChild(temp);
|
if (temp.parentNode) temp.parentNode.removeChild(temp);
|
||||||
}, 200);
|
}, 200);
|
||||||
} else {
|
} else {
|
||||||
this.div.parentNode.removeChild(this.div);
|
if (this.div.parentNode) this.div.parentNode.removeChild(this.div);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +350,7 @@ class RubberBand implements GraphPlugin {
|
||||||
*
|
*
|
||||||
* Sets <currentX> and <currentY> and calls <repaint>.
|
* Sets <currentX> and <currentY> and calls <repaint>.
|
||||||
*/
|
*/
|
||||||
update(x, y) {
|
update(x: number, y: number) {
|
||||||
this.currentX = x;
|
this.currentX = x;
|
||||||
this.currentY = y;
|
this.currentY = y;
|
||||||
|
|
||||||
|
@ -357,9 +363,9 @@ class RubberBand implements GraphPlugin {
|
||||||
* Computes the bounding box and updates the style of the <div>.
|
* Computes the bounding box and updates the style of the <div>.
|
||||||
*/
|
*/
|
||||||
repaint() {
|
repaint() {
|
||||||
if (this.div != null) {
|
if (this.div && this.first) {
|
||||||
const x = this.currentX - this.graph.panDx;
|
const x = this.currentX - this.graph.getPanDx();
|
||||||
const y = this.currentY - this.graph.panDy;
|
const y = this.currentY - this.graph.getPanDy();
|
||||||
|
|
||||||
this.x = Math.min(this.first.x, x);
|
this.x = Math.min(this.first.x, x);
|
||||||
this.y = Math.min(this.first.y, y);
|
this.y = Math.min(this.first.y, y);
|
||||||
|
@ -391,7 +397,7 @@ class RubberBand implements GraphPlugin {
|
||||||
this.graph.removeListener(this.panHandler);
|
this.graph.removeListener(this.panHandler);
|
||||||
this.reset();
|
this.reset();
|
||||||
|
|
||||||
if (this.sharedDiv != null) {
|
if (this.sharedDiv) {
|
||||||
this.sharedDiv = null;
|
this.sharedDiv = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,14 @@ import EventSource from '../event/EventSource';
|
||||||
import Dictionary from '../../util/Dictionary';
|
import Dictionary from '../../util/Dictionary';
|
||||||
import EventObject from '../event/EventObject';
|
import EventObject from '../event/EventObject';
|
||||||
import InternalEvent from '../event/InternalEvent';
|
import InternalEvent from '../event/InternalEvent';
|
||||||
import utils, { sortCells } from '../../util/Utils';
|
import { sortCells } from '../../util/Utils';
|
||||||
import Graph from '../Graph';
|
import { MaxGraph } from '../Graph';
|
||||||
import Cell from '../cell/datatypes/Cell';
|
import Cell from '../cell/datatypes/Cell';
|
||||||
import CellArray from '../cell/datatypes/CellArray';
|
|
||||||
import CellState from '../cell/datatypes/CellState';
|
import CellState from '../cell/datatypes/CellState';
|
||||||
|
import { GraphPlugin } from '../../types';
|
||||||
|
import EdgeHandler from '../cell/edge/EdgeHandler';
|
||||||
|
import VertexHandler from '../cell/vertex/VertexHandler';
|
||||||
|
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class: mxSelectionCellsHandler
|
* Class: mxSelectionCellsHandler
|
||||||
|
@ -36,21 +39,23 @@ import CellState from '../cell/datatypes/CellState';
|
||||||
*
|
*
|
||||||
* graph - Reference to the enclosing <mxGraph>.
|
* graph - Reference to the enclosing <mxGraph>.
|
||||||
*/
|
*/
|
||||||
class SelectionCellsHandler extends EventSource {
|
class SelectionCellsHandler extends EventSource implements GraphPlugin {
|
||||||
constructor(graph: Graph) {
|
static pluginId = 'SelectionCellsHandler';
|
||||||
|
|
||||||
|
constructor(graph: MaxGraph) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.graph = graph;
|
this.graph = graph;
|
||||||
this.handlers = new Dictionary();
|
this.handlers = new Dictionary();
|
||||||
this.graph.addMouseListener(this);
|
this.graph.addMouseListener(this);
|
||||||
|
|
||||||
this.refreshHandler = (sender, evt) => {
|
this.refreshHandler = (sender: EventSource, evt: EventObject) => {
|
||||||
if (this.isEnabled()) {
|
if (this.isEnabled()) {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.graph.getSelectionModel().addListener(InternalEvent.CHANGE, this.refreshHandler);
|
this.graph.addListener(InternalEvent.CHANGE, this.refreshHandler);
|
||||||
this.graph.getModel().addListener(InternalEvent.CHANGE, this.refreshHandler);
|
this.graph.getModel().addListener(InternalEvent.CHANGE, this.refreshHandler);
|
||||||
this.graph.getView().addListener(InternalEvent.SCALE, this.refreshHandler);
|
this.graph.getView().addListener(InternalEvent.SCALE, this.refreshHandler);
|
||||||
this.graph.getView().addListener(InternalEvent.TRANSLATE, this.refreshHandler);
|
this.graph.getView().addListener(InternalEvent.TRANSLATE, this.refreshHandler);
|
||||||
|
@ -66,42 +71,42 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Reference to the enclosing <mxGraph>.
|
* Reference to the enclosing <mxGraph>.
|
||||||
*/
|
*/
|
||||||
graph: Graph;
|
graph: MaxGraph;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: enabled
|
* Variable: enabled
|
||||||
*
|
*
|
||||||
* Specifies if events are handled. Default is true.
|
* Specifies if events are handled. Default is true.
|
||||||
*/
|
*/
|
||||||
enabled: boolean = true;
|
enabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: refreshHandler
|
* Variable: refreshHandler
|
||||||
*
|
*
|
||||||
* Keeps a reference to an event listener for later removal.
|
* Keeps a reference to an event listener for later removal.
|
||||||
*/
|
*/
|
||||||
refreshHandler: any = null;
|
refreshHandler: (sender: EventSource, evt: EventObject) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: maxHandlers
|
* Variable: maxHandlers
|
||||||
*
|
*
|
||||||
* Defines the maximum number of handlers to paint individually. Default is 100.
|
* Defines the maximum number of handlers to paint individually. Default is 100.
|
||||||
*/
|
*/
|
||||||
maxHandlers: number = 100;
|
maxHandlers = 100;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: handlers
|
* Variable: handlers
|
||||||
*
|
*
|
||||||
* <mxDictionary> that maps from cells to handlers.
|
* <mxDictionary> that maps from cells to handlers.
|
||||||
*/
|
*/
|
||||||
handlers: Dictionary<string, any>;
|
handlers: Dictionary<Cell, EdgeHandler | VertexHandler>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function: isEnabled
|
* Function: isEnabled
|
||||||
*
|
*
|
||||||
* Returns <enabled>.
|
* Returns <enabled>.
|
||||||
*/
|
*/
|
||||||
isEnabled(): boolean {
|
isEnabled() {
|
||||||
return this.enabled;
|
return this.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +115,7 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Sets <enabled>.
|
* Sets <enabled>.
|
||||||
*/
|
*/
|
||||||
setEnabled(value: boolean): void {
|
setEnabled(value: boolean) {
|
||||||
this.enabled = value;
|
this.enabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +124,7 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Returns the handler for the given cell.
|
* Returns the handler for the given cell.
|
||||||
*/
|
*/
|
||||||
getHandler(cell: Cell): any {
|
getHandler(cell: Cell) {
|
||||||
return this.handlers.get(cell);
|
return this.handlers.get(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,8 +133,8 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Returns true if the given cell has a handler.
|
* Returns true if the given cell has a handler.
|
||||||
*/
|
*/
|
||||||
isHandled(cell: Cell): boolean {
|
isHandled(cell: Cell) {
|
||||||
return this.getHandler(cell) != null;
|
return !!this.getHandler(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,7 +142,7 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Resets all handlers.
|
* Resets all handlers.
|
||||||
*/
|
*/
|
||||||
reset(): void {
|
reset() {
|
||||||
this.handlers.visit((key, handler) => {
|
this.handlers.visit((key, handler) => {
|
||||||
handler.reset.apply(handler);
|
handler.reset.apply(handler);
|
||||||
});
|
});
|
||||||
|
@ -148,8 +153,8 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Reloads or updates all handlers.
|
* Reloads or updates all handlers.
|
||||||
*/
|
*/
|
||||||
getHandledSelectionCells(): CellArray {
|
getHandledSelectionCells() {
|
||||||
return this.graph.selection.getSelectionCells();
|
return this.graph.getSelectionCells();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,7 +162,7 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Reloads or updates all handlers.
|
* Reloads or updates all handlers.
|
||||||
*/
|
*/
|
||||||
refresh(): void {
|
refresh() {
|
||||||
// Removes all existing handlers
|
// Removes all existing handlers
|
||||||
const oldHandlers = this.handlers;
|
const oldHandlers = this.handlers;
|
||||||
this.handlers = new Dictionary();
|
this.handlers = new Dictionary();
|
||||||
|
@ -169,23 +174,22 @@ class SelectionCellsHandler extends EventSource {
|
||||||
for (let i = 0; i < tmp.length; i += 1) {
|
for (let i = 0; i < tmp.length; i += 1) {
|
||||||
const state = this.graph.view.getState(tmp[i]);
|
const state = this.graph.view.getState(tmp[i]);
|
||||||
|
|
||||||
if (state != null) {
|
if (state) {
|
||||||
let handler = oldHandlers.remove(tmp[i]);
|
let handler = oldHandlers.remove(tmp[i]);
|
||||||
|
|
||||||
if (handler != null) {
|
if (handler) {
|
||||||
if (handler.state !== state) {
|
if (handler.state !== state) {
|
||||||
handler.destroy();
|
handler.destroy();
|
||||||
handler = null;
|
handler = null;
|
||||||
} else if (!this.isHandlerActive(handler)) {
|
} else if (!this.isHandlerActive(handler)) {
|
||||||
if (handler.refresh != null) {
|
// @ts-ignore refresh may exist
|
||||||
handler.refresh();
|
if (handler.refresh) handler.refresh();
|
||||||
}
|
|
||||||
|
|
||||||
handler.redraw();
|
handler.redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handler != null) {
|
if (handler) {
|
||||||
this.handlers.put(tmp[i], handler);
|
this.handlers.put(tmp[i], handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,10 +205,10 @@ class SelectionCellsHandler extends EventSource {
|
||||||
for (let i = 0; i < tmp.length; i += 1) {
|
for (let i = 0; i < tmp.length; i += 1) {
|
||||||
const state = this.graph.view.getState(tmp[i]);
|
const state = this.graph.view.getState(tmp[i]);
|
||||||
|
|
||||||
if (state != null) {
|
if (state) {
|
||||||
let handler = this.handlers.get(tmp[i]);
|
let handler = this.handlers.get(tmp[i]);
|
||||||
|
|
||||||
if (handler == null) {
|
if (!handler) {
|
||||||
handler = this.graph.createHandler(state);
|
handler = this.graph.createHandler(state);
|
||||||
this.fireEvent(new EventObject(InternalEvent.ADD, 'state', state));
|
this.fireEvent(new EventObject(InternalEvent.ADD, 'state', state));
|
||||||
this.handlers.put(tmp[i], handler);
|
this.handlers.put(tmp[i], handler);
|
||||||
|
@ -220,8 +224,8 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Returns true if the given handler is active and should not be redrawn.
|
* Returns true if the given handler is active and should not be redrawn.
|
||||||
*/
|
*/
|
||||||
isHandlerActive(handler: any): boolean {
|
isHandlerActive(handler: EdgeHandler | VertexHandler) {
|
||||||
return handler.index != null;
|
return handler.index !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -229,10 +233,10 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Updates the handler for the given shape if one exists.
|
* Updates the handler for the given shape if one exists.
|
||||||
*/
|
*/
|
||||||
updateHandler(state: CellState): void {
|
updateHandler(state: CellState) {
|
||||||
let handler = this.handlers.remove(state.cell);
|
let handler = this.handlers.remove(state.cell);
|
||||||
|
|
||||||
if (handler != null) {
|
if (handler) {
|
||||||
// Transfers the current state to the new handler
|
// Transfers the current state to the new handler
|
||||||
const { index } = handler;
|
const { index } = handler;
|
||||||
const x = handler.startX;
|
const x = handler.startX;
|
||||||
|
@ -241,10 +245,10 @@ class SelectionCellsHandler extends EventSource {
|
||||||
handler.destroy();
|
handler.destroy();
|
||||||
handler = this.graph.createHandler(state);
|
handler = this.graph.createHandler(state);
|
||||||
|
|
||||||
if (handler != null) {
|
if (handler) {
|
||||||
this.handlers.put(state.cell, handler);
|
this.handlers.put(state.cell, handler);
|
||||||
|
|
||||||
if (index != null && x != null && y != null) {
|
if (index !== null) {
|
||||||
handler.start(x, y, index);
|
handler.start(x, y, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,12 +260,10 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Redirects the given event to the handlers.
|
* Redirects the given event to the handlers.
|
||||||
*/
|
*/
|
||||||
mouseDown(sender: Event, me: Event): void {
|
mouseDown(sender: EventSource, me: InternalMouseEvent) {
|
||||||
if (this.graph.isEnabled() && this.isEnabled()) {
|
if (this.graph.isEnabled() && this.isEnabled()) {
|
||||||
const args = [sender, me];
|
|
||||||
|
|
||||||
this.handlers.visit((key, handler) => {
|
this.handlers.visit((key, handler) => {
|
||||||
handler.mouseDown.apply(handler, args);
|
handler.mouseDown(sender, me);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,12 +273,10 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Redirects the given event to the handlers.
|
* Redirects the given event to the handlers.
|
||||||
*/
|
*/
|
||||||
mouseMove(sender: Event, me: Event): void {
|
mouseMove(sender: EventSource, me: InternalMouseEvent) {
|
||||||
if (this.graph.isEnabled() && this.isEnabled()) {
|
if (this.graph.isEnabled() && this.isEnabled()) {
|
||||||
const args = [sender, me];
|
|
||||||
|
|
||||||
this.handlers.visit((key, handler) => {
|
this.handlers.visit((key, handler) => {
|
||||||
handler.mouseMove.apply(handler, args);
|
handler.mouseMove(sender, me);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,12 +286,10 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Redirects the given event to the handlers.
|
* Redirects the given event to the handlers.
|
||||||
*/
|
*/
|
||||||
mouseUp(sender: Event, me: Event): void {
|
mouseUp(sender: EventSource, me: InternalMouseEvent) {
|
||||||
if (this.graph.isEnabled() && this.isEnabled()) {
|
if (this.graph.isEnabled() && this.isEnabled()) {
|
||||||
const args = [sender, me];
|
|
||||||
|
|
||||||
this.handlers.visit((key, handler) => {
|
this.handlers.visit((key, handler) => {
|
||||||
handler.mouseUp.apply(handler, args);
|
handler.mouseUp(sender, me);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,15 +299,11 @@ class SelectionCellsHandler extends EventSource {
|
||||||
*
|
*
|
||||||
* Destroys the handler and all its resources and DOM nodes.
|
* Destroys the handler and all its resources and DOM nodes.
|
||||||
*/
|
*/
|
||||||
destroy(): void {
|
onDestroy() {
|
||||||
this.graph.removeMouseListener(this);
|
this.graph.removeMouseListener(this);
|
||||||
|
this.graph.removeListener(this.refreshHandler);
|
||||||
if (this.refreshHandler != null) {
|
this.graph.getModel().removeListener(this.refreshHandler);
|
||||||
this.graph.selection.getSelectionModel().removeListener(this.refreshHandler);
|
this.graph.getView().removeListener(this.refreshHandler);
|
||||||
this.graph.getModel().removeListener(this.refreshHandler);
|
|
||||||
this.graph.getView().removeListener(this.refreshHandler);
|
|
||||||
this.refreshHandler = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import EventObject from '../event/EventObject';
|
import EventObject from '../event/EventObject';
|
||||||
import Resources from '../../util/Resources';
|
import Resources from '../../util/Resources';
|
||||||
import mxLog from '../../util/gui/mxLog';
|
|
||||||
import InternalEvent from '../event/InternalEvent';
|
import InternalEvent from '../event/InternalEvent';
|
||||||
import mxGraphSelectionModel from '../view/selection/mxGraphSelectionModel';
|
import CellArray from '../cell/datatypes/CellArray';
|
||||||
import Cell from '../cell/datatypes/Cell';
|
|
||||||
import CellArray from "../cell/datatypes/CellArray";
|
|
||||||
|
|
||||||
import type { UndoableChange } from '../../types';
|
import type { UndoableChange } from '../../types';
|
||||||
|
import type { MaxGraph } from '../Graph';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class SelectionChange
|
* @class SelectionChange
|
||||||
|
@ -14,16 +12,16 @@ import type { UndoableChange } from '../../types';
|
||||||
*/
|
*/
|
||||||
class SelectionChange implements UndoableChange {
|
class SelectionChange implements UndoableChange {
|
||||||
constructor(
|
constructor(
|
||||||
selectionModel: mxGraphSelectionModel,
|
graph: MaxGraph,
|
||||||
added: CellArray = new CellArray(),
|
added: CellArray = new CellArray(),
|
||||||
removed: CellArray = new CellArray()
|
removed: CellArray = new CellArray()
|
||||||
) {
|
) {
|
||||||
this.selectionModel = selectionModel;
|
this.graph = graph;
|
||||||
this.added = added.slice();
|
this.added = added.slice();
|
||||||
this.removed = removed.slice();
|
this.removed = removed.slice();
|
||||||
}
|
}
|
||||||
|
|
||||||
selectionModel: mxGraphSelectionModel;
|
graph: MaxGraph;
|
||||||
|
|
||||||
added: CellArray;
|
added: CellArray;
|
||||||
|
|
||||||
|
@ -33,35 +31,25 @@ class SelectionChange implements UndoableChange {
|
||||||
* Changes the current root of the view.
|
* Changes the current root of the view.
|
||||||
*/
|
*/
|
||||||
execute() {
|
execute() {
|
||||||
const t0: any = mxLog.enter('mxSelectionChange.execute');
|
|
||||||
|
|
||||||
window.status =
|
window.status =
|
||||||
Resources.get(this.selectionModel.updatingSelectionResource) ||
|
Resources.get(this.graph.getUpdatingSelectionResource()) ||
|
||||||
this.selectionModel.updatingSelectionResource;
|
this.graph.getUpdatingSelectionResource();
|
||||||
|
|
||||||
for (const removed of this.removed) {
|
for (const removed of this.removed) {
|
||||||
this.selectionModel.cellRemoved(removed);
|
this.graph.cellRemoved(removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const added of this.added) {
|
for (const added of this.added) {
|
||||||
this.selectionModel.cellAdded(added);
|
this.graph.cellAdded(added);
|
||||||
}
|
}
|
||||||
|
|
||||||
[this.added, this.removed] = [this.removed, this.added];
|
[this.added, this.removed] = [this.removed, this.added];
|
||||||
|
|
||||||
window.status =
|
window.status =
|
||||||
Resources.get(this.selectionModel.doneResource) ||
|
Resources.get(this.graph.getDoneResource()) || this.graph.getDoneResource();
|
||||||
this.selectionModel.doneResource;
|
|
||||||
mxLog.leave('mxSelectionChange.execute', t0);
|
|
||||||
|
|
||||||
this.selectionModel.fireEvent(
|
this.graph.fireEvent(
|
||||||
new EventObject(
|
new EventObject(InternalEvent.CHANGE, 'added', this.added, 'removed', this.removed)
|
||||||
InternalEvent.CHANGE,
|
|
||||||
'added',
|
|
||||||
this.added,
|
|
||||||
'removed',
|
|
||||||
this.removed
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ class GraphSnap extends autoImplement<PartialClass>() {
|
||||||
// TODO: Document me!
|
// TODO: Document me!
|
||||||
tolerance: number = 0;
|
tolerance: number = 0;
|
||||||
|
|
||||||
|
getSnapTolerance = () => this.tolerance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the grid size.
|
* Specifies the grid size.
|
||||||
* @default 10
|
* @default 10
|
||||||
|
|
|
@ -9,10 +9,12 @@ import { fit, getScrollOrigin } from '../../util/Utils';
|
||||||
import { TOOLTIP_VERTICAL_OFFSET } from '../../util/Constants';
|
import { TOOLTIP_VERTICAL_OFFSET } from '../../util/Constants';
|
||||||
import { getSource, isMouseEvent } from '../../util/EventUtils';
|
import { getSource, isMouseEvent } from '../../util/EventUtils';
|
||||||
import { isNode } from '../../util/DomUtils';
|
import { isNode } from '../../util/DomUtils';
|
||||||
import Graph, { MaxGraph } from '../Graph';
|
import { MaxGraph } from '../Graph';
|
||||||
import CellState from '../cell/datatypes/CellState';
|
import CellState from '../cell/datatypes/CellState';
|
||||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||||
import { Listenable } from '../../types';
|
import PopupMenuHandler from '../popups_menus/PopupMenuHandler';
|
||||||
|
|
||||||
|
import type { GraphPlugin } from '../../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class: mxTooltipHandler
|
* Class: mxTooltipHandler
|
||||||
|
@ -38,14 +40,41 @@ import { Listenable } from '../../types';
|
||||||
* graph - Reference to the enclosing <mxGraph>.
|
* graph - Reference to the enclosing <mxGraph>.
|
||||||
* delay - Optional delay in milliseconds.
|
* delay - Optional delay in milliseconds.
|
||||||
*/
|
*/
|
||||||
class TooltipHandler {
|
class TooltipHandler implements GraphPlugin {
|
||||||
constructor(graph: MaxGraph, delay: number = 500) {
|
static pluginId = 'TooltipHandler';
|
||||||
|
|
||||||
|
constructor(graph: MaxGraph) {
|
||||||
this.graph = graph;
|
this.graph = graph;
|
||||||
this.delay = delay;
|
this.delay = 500;
|
||||||
this.graph.addMouseListener(this);
|
this.graph.addMouseListener(this);
|
||||||
|
|
||||||
|
this.div = document.createElement('div');
|
||||||
|
this.div.className = 'mxTooltip';
|
||||||
|
this.div.style.visibility = 'hidden';
|
||||||
|
|
||||||
|
document.body.appendChild(this.div);
|
||||||
|
|
||||||
|
InternalEvent.addGestureListeners(this.div, (evt) => {
|
||||||
|
const source = getSource(evt);
|
||||||
|
|
||||||
|
// @ts-ignore nodeName may exist
|
||||||
|
if (source && source.nodeName !== 'A') {
|
||||||
|
this.hideTooltip();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hides tooltips and resets tooltip timer if mouse leaves container
|
||||||
|
InternalEvent.addListener(
|
||||||
|
this.graph.getContainer(),
|
||||||
|
'mouseleave',
|
||||||
|
(evt: MouseEvent) => {
|
||||||
|
if (this.div !== evt.relatedTarget) {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore Cannot be null.
|
|
||||||
div: HTMLElement;
|
div: HTMLElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,7 +89,7 @@ class TooltipHandler {
|
||||||
*
|
*
|
||||||
* Reference to the enclosing <mxGraph>.
|
* Reference to the enclosing <mxGraph>.
|
||||||
*/
|
*/
|
||||||
graph: Graph;
|
graph: MaxGraph;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variable: delay
|
* Variable: delay
|
||||||
|
@ -91,10 +120,10 @@ class TooltipHandler {
|
||||||
*/
|
*/
|
||||||
destroyed = false;
|
destroyed = false;
|
||||||
|
|
||||||
lastX: number = 0;
|
lastX = 0;
|
||||||
lastY: number = 0;
|
lastY = 0;
|
||||||
state: CellState | null = null;
|
state: CellState | null = null;
|
||||||
stateSource: boolean = false;
|
stateSource = false;
|
||||||
node: any;
|
node: any;
|
||||||
thread: number | null = null;
|
thread: number | null = null;
|
||||||
|
|
||||||
|
@ -143,27 +172,6 @@ class TooltipHandler {
|
||||||
this.hideOnHover = value;
|
this.hideOnHover = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: init
|
|
||||||
*
|
|
||||||
* Initializes the DOM nodes required for this tooltip handler.
|
|
||||||
*/
|
|
||||||
init() {
|
|
||||||
this.div = document.createElement('div');
|
|
||||||
this.div.className = 'mxTooltip';
|
|
||||||
this.div.style.visibility = 'hidden';
|
|
||||||
|
|
||||||
document.body.appendChild(this.div);
|
|
||||||
|
|
||||||
InternalEvent.addGestureListeners(this.div, (evt) => {
|
|
||||||
const source = getSource(evt);
|
|
||||||
|
|
||||||
if (source.nodeName !== 'A') {
|
|
||||||
this.hideTooltip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function: getStateForEvent
|
* Function: getStateForEvent
|
||||||
*
|
*
|
||||||
|
@ -180,7 +188,7 @@ class TooltipHandler {
|
||||||
* event all subsequent events of the gesture are redirected to this
|
* event all subsequent events of the gesture are redirected to this
|
||||||
* handler.
|
* handler.
|
||||||
*/
|
*/
|
||||||
mouseDown(sender: any, me: InternalMouseEvent) {
|
mouseDown(sender: EventSource, me: InternalMouseEvent) {
|
||||||
this.reset(me, false);
|
this.reset(me, false);
|
||||||
this.hideTooltip();
|
this.hideTooltip();
|
||||||
}
|
}
|
||||||
|
@ -190,7 +198,7 @@ class TooltipHandler {
|
||||||
*
|
*
|
||||||
* Handles the event by updating the rubberband selection.
|
* Handles the event by updating the rubberband selection.
|
||||||
*/
|
*/
|
||||||
mouseMove(sender: Listenable, me: InternalMouseEvent) {
|
mouseMove(sender: EventSource, me: InternalMouseEvent) {
|
||||||
if (me.getX() !== this.lastX || me.getY() !== this.lastY) {
|
if (me.getX() !== this.lastX || me.getY() !== this.lastY) {
|
||||||
this.reset(me, true);
|
this.reset(me, true);
|
||||||
const state = this.getStateForEvent(me);
|
const state = this.getStateForEvent(me);
|
||||||
|
@ -218,7 +226,7 @@ class TooltipHandler {
|
||||||
* Handles the event by resetting the tooltip timer or hiding the existing
|
* Handles the event by resetting the tooltip timer or hiding the existing
|
||||||
* tooltip.
|
* tooltip.
|
||||||
*/
|
*/
|
||||||
mouseUp(sender: any, me: InternalMouseEvent): void {
|
mouseUp(sender: EventSource, me: InternalMouseEvent) {
|
||||||
this.reset(me, true);
|
this.reset(me, true);
|
||||||
this.hideTooltip();
|
this.hideTooltip();
|
||||||
}
|
}
|
||||||
|
@ -228,8 +236,8 @@ class TooltipHandler {
|
||||||
*
|
*
|
||||||
* Resets the timer.
|
* Resets the timer.
|
||||||
*/
|
*/
|
||||||
resetTimer(): void {
|
resetTimer() {
|
||||||
if (this.thread !== null) {
|
if (this.thread) {
|
||||||
window.clearTimeout(this.thread);
|
window.clearTimeout(this.thread);
|
||||||
this.thread = null;
|
this.thread = null;
|
||||||
}
|
}
|
||||||
|
@ -255,18 +263,28 @@ class TooltipHandler {
|
||||||
const x = me.getX();
|
const x = me.getX();
|
||||||
const y = me.getY();
|
const y = me.getY();
|
||||||
const stateSource = me.isSource(state.shape) || me.isSource(state.text);
|
const stateSource = me.isSource(state.shape) || me.isSource(state.text);
|
||||||
|
const popupMenuHandler = this.graph.getPlugin(
|
||||||
|
'PopupMenuHandler'
|
||||||
|
) as PopupMenuHandler;
|
||||||
|
|
||||||
this.thread = window.setTimeout(() => {
|
this.thread = window.setTimeout(() => {
|
||||||
if (
|
if (
|
||||||
state &&
|
state &&
|
||||||
|
node &&
|
||||||
!this.graph.isEditing() &&
|
!this.graph.isEditing() &&
|
||||||
!this.graph.popupMenuHandler.isMenuShowing() &&
|
popupMenuHandler &&
|
||||||
|
!popupMenuHandler.isMenuShowing() &&
|
||||||
!this.graph.isMouseDown
|
!this.graph.isMouseDown
|
||||||
) {
|
) {
|
||||||
// Uses information from inside event cause using the event at
|
// Uses information from inside event cause using the event at
|
||||||
// this (delayed) point in time is not possible in IE as it no
|
// this (delayed) point in time is not possible in IE as it no
|
||||||
// longer contains the required information (member not found)
|
// longer contains the required information (member not found)
|
||||||
const tip = this.graph.getTooltip(state, node, x, y);
|
const tip = this.graph.getTooltip(
|
||||||
|
state,
|
||||||
|
node as HTMLElement | SVGElement,
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
);
|
||||||
this.show(tip, x, y);
|
this.show(tip, x, y);
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.node = node;
|
this.node = node;
|
||||||
|
@ -328,12 +346,12 @@ class TooltipHandler {
|
||||||
*
|
*
|
||||||
* Destroys the handler and all its resources and DOM nodes.
|
* Destroys the handler and all its resources and DOM nodes.
|
||||||
*/
|
*/
|
||||||
destroy() {
|
onDestroy() {
|
||||||
if (!this.destroyed) {
|
if (!this.destroyed) {
|
||||||
this.graph.removeMouseListener(this);
|
this.graph.removeMouseListener(this);
|
||||||
InternalEvent.release(this.div);
|
InternalEvent.release(this.div);
|
||||||
|
|
||||||
if (this.div.parentNode != null) {
|
if (this.div.parentNode) {
|
||||||
this.div.parentNode.removeChild(this.div);
|
this.div.parentNode.removeChild(this.div);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,8 @@ import CellArray from '../cell/datatypes/CellArray';
|
||||||
|
|
||||||
import type { MaxGraph } from '../Graph';
|
import type { MaxGraph } from '../Graph';
|
||||||
import StyleRegistry from '../style/StyleRegistry';
|
import StyleRegistry from '../style/StyleRegistry';
|
||||||
|
import TooltipHandler from '../tooltip/TooltipHandler';
|
||||||
|
import { MouseEventListener } from '../../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class GraphView
|
* @class GraphView
|
||||||
|
@ -687,30 +689,26 @@ class GraphView extends EventSource {
|
||||||
// container and finishing the handling of a single gesture
|
// container and finishing the handling of a single gesture
|
||||||
InternalEvent.addGestureListeners(
|
InternalEvent.addGestureListeners(
|
||||||
this.backgroundPageShape.node,
|
this.backgroundPageShape.node,
|
||||||
(evt: Event) => {
|
(evt: MouseEvent) => {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(InternalEvent.MOUSE_DOWN, new InternalMouseEvent(evt));
|
||||||
InternalEvent.MOUSE_DOWN,
|
|
||||||
new InternalMouseEvent(evt as MouseEvent)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
(evt: Event) => {
|
(evt: MouseEvent) => {
|
||||||
|
const tooltipHandler = graph.getPlugin('TooltipHandler') as TooltipHandler;
|
||||||
|
|
||||||
// Hides the tooltip if mouse is outside container
|
// Hides the tooltip if mouse is outside container
|
||||||
if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) {
|
if (tooltipHandler && tooltipHandler.isHideOnHover()) {
|
||||||
graph.tooltipHandler.hide();
|
tooltipHandler.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (graph.isMouseDown && !isConsumed(evt)) {
|
if (graph.isMouseDown && !isConsumed(evt)) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_MOVE,
|
InternalEvent.MOUSE_MOVE,
|
||||||
new InternalMouseEvent(evt as MouseEvent)
|
new InternalMouseEvent(evt)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(evt: Event) => {
|
(evt: MouseEvent) => {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(InternalEvent.MOUSE_UP, new InternalMouseEvent(evt));
|
||||||
InternalEvent.MOUSE_UP,
|
|
||||||
new InternalMouseEvent(evt as MouseEvent)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2034,19 +2032,22 @@ class GraphView extends EventSource {
|
||||||
* Returns true if the event origin is one of the drawing panes or
|
* Returns true if the event origin is one of the drawing panes or
|
||||||
* containers of the view.
|
* containers of the view.
|
||||||
*/
|
*/
|
||||||
isContainerEvent(evt: Event | MouseEvent) {
|
isContainerEvent(evt: MouseEvent) {
|
||||||
const source = getSource(evt);
|
const source = getSource(evt);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
source === this.graph.container ||
|
source &&
|
||||||
source.parentNode === this.backgroundPane ||
|
(source === this.graph.container ||
|
||||||
(source.parentNode && source.parentNode.parentNode === this.backgroundPane) ||
|
// @ts-ignore parentNode may exist
|
||||||
source === this.canvas.parentNode ||
|
source.parentNode === this.backgroundPane ||
|
||||||
source === this.canvas ||
|
// @ts-ignore parentNode may exist
|
||||||
source === this.backgroundPane ||
|
(source.parentNode && source.parentNode.parentNode === this.backgroundPane) ||
|
||||||
source === this.drawPane ||
|
source === this.canvas.parentNode ||
|
||||||
source === this.overlayPane ||
|
source === this.canvas ||
|
||||||
source === this.decoratorPane
|
source === this.backgroundPane ||
|
||||||
|
source === this.drawPane ||
|
||||||
|
source === this.overlayPane ||
|
||||||
|
source === this.decoratorPane)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2125,24 +2126,18 @@ class GraphView extends EventSource {
|
||||||
pointerId = evt.pointerId;
|
pointerId = evt.pointerId;
|
||||||
}
|
}
|
||||||
}) as EventListener,
|
}) as EventListener,
|
||||||
(evt: Event) => {
|
(evt: MouseEvent) => {
|
||||||
if (
|
if (
|
||||||
this.isContainerEvent(evt) &&
|
this.isContainerEvent(evt) &&
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
(pointerId === null || evt.pointerId === pointerId)
|
(pointerId === null || evt.pointerId === pointerId)
|
||||||
) {
|
) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(InternalEvent.MOUSE_MOVE, new InternalMouseEvent(evt));
|
||||||
InternalEvent.MOUSE_MOVE,
|
|
||||||
new InternalMouseEvent(evt as MouseEvent)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(evt: Event) => {
|
(evt: MouseEvent) => {
|
||||||
if (this.isContainerEvent(evt)) {
|
if (this.isContainerEvent(evt)) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(InternalEvent.MOUSE_UP, new InternalMouseEvent(evt));
|
||||||
InternalEvent.MOUSE_UP,
|
|
||||||
new InternalMouseEvent(evt as MouseEvent)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pointerId = null;
|
pointerId = null;
|
||||||
|
@ -2161,7 +2156,7 @@ class GraphView extends EventSource {
|
||||||
// Workaround for touch events which started on some DOM node
|
// Workaround for touch events which started on some DOM node
|
||||||
// on top of the container, in which case the cells under the
|
// on top of the container, in which case the cells under the
|
||||||
// mouse for the move and up events are not detected.
|
// mouse for the move and up events are not detected.
|
||||||
const getState = (evt: Event) => {
|
const getState = (evt: MouseEvent) => {
|
||||||
let state = null;
|
let state = null;
|
||||||
|
|
||||||
// Workaround for touch events which started on some DOM node
|
// Workaround for touch events which started on some DOM node
|
||||||
|
@ -2188,16 +2183,20 @@ class GraphView extends EventSource {
|
||||||
// in Firefox and Chrome
|
// in Firefox and Chrome
|
||||||
graph.addMouseListener({
|
graph.addMouseListener({
|
||||||
mouseDown: (sender: any, me: InternalMouseEvent) => {
|
mouseDown: (sender: any, me: InternalMouseEvent) => {
|
||||||
(<PopupMenuHandler>graph.popupMenuHandler).hideMenu();
|
const popupMenuHandler = graph.getPlugin('PopupMenuHandler') as PopupMenuHandler;
|
||||||
|
|
||||||
|
if (popupMenuHandler) popupMenuHandler.hideMenu();
|
||||||
},
|
},
|
||||||
mouseMove: () => {},
|
mouseMove: () => {},
|
||||||
mouseUp: () => {},
|
mouseUp: () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.moveHandler = (evt: Event) => {
|
this.moveHandler = (evt: MouseEvent) => {
|
||||||
|
const tooltipHandler = graph.getPlugin('TooltipHandler') as TooltipHandler;
|
||||||
|
|
||||||
// Hides the tooltip if mouse is outside container
|
// Hides the tooltip if mouse is outside container
|
||||||
if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) {
|
if (tooltipHandler && tooltipHandler.isHideOnHover()) {
|
||||||
graph.tooltipHandler.hide();
|
tooltipHandler.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -2211,12 +2210,12 @@ class GraphView extends EventSource {
|
||||||
) {
|
) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(
|
||||||
InternalEvent.MOUSE_MOVE,
|
InternalEvent.MOUSE_MOVE,
|
||||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
new InternalMouseEvent(evt, getState(evt))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.endHandler = (evt: Event) => {
|
this.endHandler = (evt: MouseEvent) => {
|
||||||
if (
|
if (
|
||||||
this.captureDocumentGesture &&
|
this.captureDocumentGesture &&
|
||||||
graph.isMouseDown &&
|
graph.isMouseDown &&
|
||||||
|
@ -2225,10 +2224,7 @@ class GraphView extends EventSource {
|
||||||
graph.container.style.display !== 'none' &&
|
graph.container.style.display !== 'none' &&
|
||||||
graph.container.style.visibility !== 'hidden'
|
graph.container.style.visibility !== 'hidden'
|
||||||
) {
|
) {
|
||||||
graph.fireMouseEvent(
|
graph.fireMouseEvent(InternalEvent.MOUSE_UP, new InternalMouseEvent(evt));
|
||||||
InternalEvent.MOUSE_UP,
|
|
||||||
new InternalMouseEvent(evt as MouseEvent)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2320,8 +2316,8 @@ class GraphView extends EventSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
endHandler: EventListener | null = null;
|
endHandler: MouseEventListener | null = null;
|
||||||
moveHandler: EventListener | null = null;
|
moveHandler: MouseEventListener | null = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GraphView;
|
export default GraphView;
|
||||||
|
|
Loading…
Reference in New Issue