- Converting *Handlers into plugins.

- Keep resolving errors.
development
Junsik Shim 2021-08-09 09:34:19 +09:00
parent c89ce4cea4
commit 648e324cc0
37 changed files with 1945 additions and 1973 deletions

View File

@ -1,9 +1,9 @@
import type Cell from './view/cell/datatypes/Cell';
import CellState from './view/cell/datatypes/CellState';
import RectangleShape from './view/geometry/shape/node/RectangleShape';
import type CellState from './view/cell/datatypes/CellState';
import type InternalMouseEvent from './view/event/InternalMouseEvent';
import type Shape from './view/geometry/shape/Shape';
import type Graph from './view/Graph';
import ImageBox from './view/image/ImageBox';
import type { MaxGraph } from './view/Graph';
import type ImageBox from './view/image/ImageBox';
export type CellMap = {
[id: string]: Cell;
@ -19,10 +19,6 @@ export type UndoableChange = {
export type StyleValue = string | number;
export type StyleProperties = {
[k: string]: StyleValue;
};
export type Properties = {
[k: string]: any;
};
@ -30,6 +26,7 @@ export type Properties = {
export type CellStateStyles = {
absoluteArcSize: number;
align: AlignValue;
anchorPointDirection: boolean;
arcSize: number;
aspect: string;
autosize: boolean;
@ -49,8 +46,14 @@ export type CellStateStyles = {
endArrow: ArrowType;
endFill: boolean;
endSize: number;
entryDx: number;
entryDy: number;
entryPerimeter: boolean;
entryX: number;
entryY: number;
exitDx: number;
exitDy: number;
exitPerimeter: boolean;
exitX: number;
exitY: number;
fillColor: ColorValue;
@ -75,11 +78,15 @@ export type CellStateStyles = {
imageHeight: number;
imageWidth: number;
indicatorColor: ColorValue;
indicatorDirection: DirectionValue;
indicatorHeight: number;
indicatorImage: string;
indicatorShape: Shape;
indicatorShape: string;
indicatorStrokeColor: ColorValue;
indicatorWidth: number;
labelBackgroundColor: ColorValue;
labelBorderColor: ColorValue;
labelPadding: number;
labelPosition: AlignValue;
loop: Function;
margin: number;
@ -206,8 +213,12 @@ export type GradientMap = {
[k: string]: Gradient;
};
export interface GraphPluginConstructor {
new (graph: MaxGraph): GraphPlugin;
pluginId: string;
}
export interface GraphPlugin {
onInit: (graph: Graph) => void;
onDestroy: () => void;
}
@ -215,14 +226,16 @@ export interface GraphPlugin {
export type Listener = {
name: string;
f: EventListener;
f: MouseEventListener;
};
export type ListenerTarget = {
mxListenerList?: Listener[];
};
export type Listenable = (Node | Window) & ListenerTarget;
export type Listenable = (EventSource | EventTarget) & ListenerTarget;
export type MouseEventListener = (me: MouseEvent) => void;
export type GestureEvent = Event &
MouseEvent & {
@ -230,6 +243,12 @@ export type GestureEvent = Event &
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 interface CellHandle {
@ -237,5 +256,12 @@ export interface CellHandle {
cursor: string;
image: ImageBox | null;
shape: Shape | null;
active: boolean;
setVisible: (v: boolean) => void;
processEvent: (me: InternalMouseEvent) => void;
positionChanged: () => void;
execute: (me: InternalMouseEvent) => void;
reset: () => void;
redraw: () => void;
destroy: () => void;
}

View File

@ -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;
};

View File

@ -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;
};

View File

@ -41,7 +41,7 @@ import Cell from '../view/cell/datatypes/Cell';
import Model from '../view/model/Model';
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';
/**
@ -159,7 +159,11 @@ export const parseCssNumber = (value: string) => {
* mxUtils.setPrefixedStyle(node.style, 'transformOrigin', '0% 0%');
* (end)
*/
export const setPrefixedStyle = (style: StyleProperties, name: string, value: string) => {
export const setPrefixedStyle = (
style: CSSStyleDeclaration,
name: string,
value: string
) => {
let prefix = null;
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) => {
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.
* 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)) {
return false;
}
if (a && b) {
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;
}
}

View File

@ -24,16 +24,14 @@ import Point from './geometry/Point';
import {
applyMixins,
autoImplement,
getBoundingBox,
getCurrentStyle,
getValue,
hasScrollbars,
parseCssNumber,
} from '../util/Utils';
import Cell from './cell/datatypes/Cell';
import Model from './model/Model';
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 GeometryChange from './geometry/GeometryChange';
@ -49,23 +47,27 @@ import EdgeHandler from './cell/edge/EdgeHandler';
import VertexHandler from './cell/vertex/VertexHandler';
import EdgeSegmentHandler from './cell/edge/EdgeSegmentHandler';
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 GraphTooltip from './tooltip/GraphTooltip';
import GraphTerminal from './terminal/GraphTerminal';
import type { GraphPlugin, GraphPluginConstructor } from '../types';
import type GraphPorts from './ports/GraphPorts';
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<
GraphEvents,
@ -81,7 +83,9 @@ type PartialEvents = Pick<
| 'isIgnoreTerminalEvent'
| 'isCloneEvent'
| 'isToggleEvent'
| 'getClickTolerance'
| 'getEventTolerance'
| 'isInvokesStopCellEditing'
| 'getPointForEvent'
>;
type PartialSelection = Pick<
GraphSelection,
@ -90,6 +94,13 @@ type PartialSelection = Pick<
| 'getSelectionCount'
| 'selectCellForEvent'
| 'setSelectionCell'
| 'getSelectionCells'
| 'updateSelection'
| 'selectRegion'
| 'cellAdded'
| 'cellRemoved'
| 'getUpdatingSelectionResource'
| 'getDoneResource'
>;
type PartialCells = Pick<
GraphCells,
@ -100,6 +111,14 @@ type PartialCells = Pick<
| 'isCellsCloneable'
| 'cloneCell'
| 'setCellStyles'
| 'isCellMovable'
| 'isCellResizable'
| 'getChildCells'
| 'isCellRotatable'
| 'getCellContainmentArea'
| 'getCurrentCellStyle'
| 'resizeCell'
| 'removeStateForCell'
>;
type PartialConnections = Pick<
GraphConnections,
@ -108,17 +127,44 @@ type PartialConnections = Pick<
| 'isCellDisconnectable'
| 'getOutlineConstraint'
| 'connectCell'
| 'getConnections'
| 'isConstrainChild'
| 'isValidSource'
>;
type PartialEditing = Pick<GraphEditing, 'isEditing'>;
type PartialEditing = Pick<GraphEditing, 'isEditing' | 'stopEditing'>;
type PartialTooltip = Pick<GraphTooltip, 'getTooltip'>;
type PartialValidation = Pick<
GraphValidation,
'getEdgeValidationError' | 'validationAlert'
>;
type PartialLabel = Pick<GraphLabel, 'isLabelMovable'>;
type PartialLabel = Pick<
GraphLabel,
'isLabelMovable' | 'isHtmlLabel' | 'isWrapping' | 'isLabelClipped' | 'getLabel'
>;
type PartialTerminal = Pick<GraphTerminal, 'isTerminalPointMovable'>;
type PartialSnap = Pick<GraphSnap, 'snap' | 'getGridSize'>;
type PartialEdge = Pick<GraphEdge, 'isAllowDanglingEdges' | 'isResetEdgesOnConnect'>;
type PartialSnap = Pick<
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 &
PartialSelection &
PartialCells &
@ -130,10 +176,22 @@ type PartialClass = PartialEvents &
PartialTerminal &
PartialSnap &
PartialEdge &
PartialOverlays &
PartialFolding &
PartialPanning &
PartialZoom &
EventSource;
export type MaxGraph = Graph & PartialClass;
const defaultPlugins: GraphPluginConstructor[] = [
TooltipHandler,
SelectionCellsHandler,
PopupMenuHandler,
ConnectionHandler,
GraphHandler,
];
/**
* Extends {@link EventSource} to implement a graph component for
* the browser. This is the main class of the package. To activate
@ -151,12 +209,12 @@ export type MaxGraph = Graph & PartialClass;
* @class graph
* @extends {EventSource}
*/
// @ts-ignore
// @ts-ignore recursive reference error
class Graph extends autoImplement<PartialClass>() {
constructor(
container: HTMLElement,
model: Model,
plugins: GraphPlugin[] = [],
plugins: GraphPluginConstructor[] = defaultPlugins,
stylesheet: Stylesheet | null = null
) {
super();
@ -165,7 +223,6 @@ class Graph extends autoImplement<PartialClass>() {
this.model = model;
this.plugins = plugins;
this.cellRenderer = this.createCellRenderer();
this.setSelectionModel(this.createSelectionModel());
this.setStylesheet(stylesheet != null ? stylesheet : this.createStylesheet());
this.view = this.createGraphView();
@ -176,21 +233,6 @@ class Graph extends autoImplement<PartialClass>() {
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
this.cellEditor = this.createCellEditor();
@ -200,22 +242,14 @@ class Graph extends autoImplement<PartialClass>() {
// Updates the size of the container for the current graph
this.sizeDidChange();
// Hides tooltips and resets tooltip timer if mouse leaves container
InternalEvent.addListener(this.container, 'mouseleave', (evt: Event) => {
if (
this.tooltipHandler.div &&
this.tooltipHandler.div !== (<MouseEvent>evt).relatedTarget
) {
this.tooltipHandler.hide();
}
// Initiailzes plugins
this.plugins.forEach((p: GraphPluginConstructor) => {
this.pluginsMap.put(p.pluginId, new p(this));
});
// Initiailzes plugins
this.plugins.forEach((p) => p.onInit(this));
this.view.revalidate();
}
// TODO: Document me!
container: HTMLElement;
getContainer = () => this.container;
@ -224,21 +258,23 @@ class Graph extends autoImplement<PartialClass>() {
// Handlers
// @ts-ignore Cannot be null.
tooltipHandler: TooltipHandler;
// tooltipHandler: TooltipHandler;
// @ts-ignore Cannot be null.
selectionCellsHandler: SelectionCellsHandler;
// selectionCellsHandler: SelectionCellsHandler;
// @ts-ignore Cannot be null.
popupMenuHandler: PopupMenuHandler;
// popupMenuHandler: PopupMenuHandler;
// @ts-ignore Cannot be null.
connectionHandler: ConnectionHandler;
// connectionHandler: ConnectionHandler;
// @ts-ignore Cannot be null.
graphHandler: GraphHandler;
// graphHandler: GraphHandler;
getTooltipHandler = () => this.tooltipHandler;
getSelectionCellsHandler = () => this.selectionCellsHandler;
getPopupMenuHandler = () => this.popupMenuHandler;
getConnectionHandler = () => this.connectionHandler;
getGraphHandler = () => this.graphHandler;
getPlugin = (id: string) => this.pluginsMap.get(id) as unknown;
// getTooltipHandler = () => this.pluginsMap.get('TooltipHandler');
// getSelectionCellsHandler = () => this.selectionCellsHandler;
// getPopupMenuHandler = () => this.popupMenuHandler;
// getConnectionHandler = () => this.connectionHandler;
// getGraphHandler = () => this.graphHandler;
graphModelChangeListener: Function | null = null;
paintBackground: Function | null = null;
@ -252,7 +288,8 @@ class Graph extends autoImplement<PartialClass>() {
*/
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.
@ -275,11 +312,6 @@ class Graph extends autoImplement<PartialClass>() {
// @ts-ignore
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.
*/
@ -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.
*/
@ -732,7 +708,7 @@ class Graph extends autoImplement<PartialClass>() {
if (change instanceof RootChange) {
this.clearSelection();
this.setDefaultParent(null);
this.cells.removeStateForCell(change.previous);
this.removeStateForCell(change.previous);
if (this.resetViewOnRootChange) {
this.view.scale = 1;
@ -752,7 +728,7 @@ class Graph extends autoImplement<PartialClass>() {
if (!this.getModel().contains(newParent) || newParent.isCollapsed()) {
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
if (this.view.currentRoot == change.child) {
@ -803,7 +779,7 @@ class Graph extends autoImplement<PartialClass>() {
// Removes the state from the cache by default
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.
*/
createHandler(state: CellState): EdgeHandler | VertexHandler | null {
createHandler(state: CellState) {
let result: EdgeHandler | VertexHandler | null = null;
if (state.cell.isEdge()) {

View File

@ -31,6 +31,7 @@ import CellHighlight from './selection/CellHighlight';
import Rectangle from './geometry/Rectangle';
import { getClientX, getClientY, isAltDown, isMultiTouchEvent } from '../util/EventUtils';
import { MaxGraph } from './Graph';
import { GraphPlugin, GraphPluginConstructor } from '../types';
/**
* Class: mxGraphHandler
@ -52,7 +53,9 @@ import { MaxGraph } from './Graph';
*
* graph - Reference to the enclosing <mxGraph>.
*/
class GraphHandler {
class GraphHandler implements GraphPlugin {
static pluginId = 'GraphHandler';
constructor(graph: MaxGraph) {
this.graph = graph;
this.graph.addMouseListener(this);
@ -1798,7 +1801,7 @@ class GraphHandler {
* Destroys the handler and all its resources and DOM nodes.
*/
// destroy(): void;
destroy() {
onDestroy() {
this.graph.removeMouseListener(this);
this.graph.removeListener(this.panHandler);

View File

@ -11,6 +11,7 @@ import {
DEFAULT_VALID_COLOR,
MAX_HOTSPOT_SIZE,
MIN_HOTSPOT_SIZE,
NONE,
} from '../../util/Constants';
import CellHighlight from '../selection/CellHighlight';
import EventObject from '../event/EventObject';
@ -273,11 +274,7 @@ class CellMarker extends EventSource {
*
* Sets and marks the current valid state.
*/
setCurrentState(
state: CellState | null,
me: InternalMouseEvent,
color: ColorValue | null = null
) {
setCurrentState(state: CellState | null, me: InternalMouseEvent, color?: ColorValue) {
const isValid = state ? this.isValidState(state) : false;
color = color ?? this.getMarkerColor(me.getEvent(), state, isValid);

View File

@ -71,6 +71,8 @@ import Model from '../model/Model';
import CellOverlay from './CellOverlay';
import { getClientX, getClientY, getSource } from '../../util/EventUtils';
import { isNode } from '../../util/DomUtils';
import { CellStateStyles } from '../../types';
import CellArray from './datatypes/CellArray';
/**
* Class: mxCellRenderer
@ -119,6 +121,7 @@ class CellRenderer {
*
* Defines the default shape for edges. Default is <mxConnector>.
*/
// @ts-expect-error The constructors for Shape and Connector are different.
defaultEdgeShape: typeof Shape = Connector;
/**
@ -264,12 +267,11 @@ class CellRenderer {
let ctor = this.getShape(state.style.shape);
if (!ctor) {
ctor = <typeof Shape>(
(state.cell.isEdge() ? this.defaultEdgeShape : this.defaultVertexShape)
);
// @ts-expect-error The various Shape constructors are not compatible.
ctor = state.cell.isEdge() ? this.defaultEdgeShape : this.defaultVertexShape;
}
return ctor;
return ctor as typeof Shape;
}
/**
@ -286,12 +288,12 @@ class CellRenderer {
if (shape) {
shape.apply(state);
shape.imageSrc = state.getImage();
shape.imageSrc = state.getImageSrc();
shape.indicatorColor = state.getIndicatorColor();
shape.indicatorStrokeColor = state.style.indicatorStrokeColor;
shape.indicatorGradientColor = state.getIndicatorGradientColor();
shape.indicatorDirection = state.style.indicatorDirection;
shape.indicatorImage = state.getIndicatorImage();
shape.indicatorImageSrc = state.getIndicatorImageSrc();
this.postConfigureShape(state);
}
}
@ -304,8 +306,8 @@ class CellRenderer {
* This implementation resolves these keywords on the fill, stroke
* and gradient color keys.
*/
postConfigureShape(state: CellState): void {
if (state.shape != null) {
postConfigureShape(state: CellState) {
if (state.shape) {
this.resolveColor(state, 'indicatorGradientColor', 'gradientColor');
this.resolveColor(state, 'indicatorColor', 'fillColor');
this.resolveColor(state, 'gradient', 'gradientColor');
@ -320,14 +322,19 @@ class CellRenderer {
* Resolves special keywords 'inherit', 'indicated' and 'swimlane' and sets
* the respective color on the shape.
*/
checkPlaceholderStyles(state: CellState): boolean {
checkPlaceholderStyles(state: CellState) {
// LATER: Check if the color has actually changed
if (state.style != null) {
if (state.style) {
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) {
if (values.indexOf(state.style[styles[i]]) >= 0) {
if (values.indexOf(state.style[styles[i]] as string) >= 0) {
return true;
}
}
@ -341,26 +348,25 @@ class CellRenderer {
* Resolves special keywords 'inherit', 'indicated' and 'swimlane' and sets
* the respective color on the shape.
*/
resolveColor(state: CellState, field: string, key: string): void {
const shape = key === 'fontColor' ? state.text : state.shape;
resolveColor(state: CellState, field: string, key: string) {
const shape: Shape | null = key === 'fontColor' ? state.text : state.shape;
if (shape != null) {
if (shape) {
const { graph } = state.view;
// @ts-ignore
const value = shape[field];
let referenced = null;
if (value === 'inherit') {
// @ts-ignore
referenced = state.cell.getParent();
/* disable swimlane for now
} else if (value === 'swimlane') {
// @ts-ignore
shape[field] =
key === 'strokeColor' || key === 'fontColor' ? '#000000' : '#ffffff';
// @ts-ignore
if (state.cell.getTerminal(false) != null) {
// @ts-ignore
if (state.cell.getTerminal(false)) {
referenced = state.cell.getTerminal(false);
} else {
referenced = state.cell;
@ -368,30 +374,27 @@ class CellRenderer {
referenced = graph.swimlane.getSwimlane(<Cell>referenced);
key = graph.swimlane.swimlaneIndicatorColorAttribute;
} else if (value === 'indicated' && state.shape != null) {
*/
} else if (value === 'indicated' && state.shape) {
// @ts-ignore
shape[field] = state.shape.indicatorColor;
} else if (key !== 'fillColor' && value === 'fillColor' && state.shape != null) {
} else if (key !== 'fillColor' && value === 'fillColor' && state.shape) {
// @ts-ignore
shape[field] = state.style.fillColor;
} else if (
key !== 'strokeColor' &&
value === 'strokeColor' &&
state.shape != null
) {
} else if (key !== 'strokeColor' && value === 'strokeColor' && state.shape) {
// @ts-ignore
shape[field] = state.style.strokeColor;
}
if (referenced != null) {
if (referenced) {
const rstate = graph.getView().getState(referenced);
// @ts-ignore
shape[field] = null;
if (rstate != null) {
if (rstate) {
const rshape = key === 'fontColor' ? rstate.text : rstate.shape;
if (rshape != null && field !== 'indicatorColor') {
if (rshape && field !== 'indicatorColor') {
// @ts-ignore
shape[field] = rshape[field];
} else {
@ -412,7 +415,6 @@ class CellRenderer {
*
* state - <mxCellState> for which the label should be created.
*/
// getLabelValue(state: mxCellState): string;
getLabelValue(state: CellState) {
return state.view.graph.getLabel(state.cell);
}
@ -426,15 +428,12 @@ class CellRenderer {
*
* state - <mxCellState> for which the label should be created.
*/
// createLabel(state: mxCellState, value: string): void;
createLabel(state: CellState, value: any) {
createLabel(state: CellState, value: string) {
const { graph } = state.view;
const isEdge = state.cell.isEdge();
if (state.style.fontSize > 0 || state.style.fontSize == null) {
// Avoids using DOM node for empty labels
const isForceHtml =
graph.isHtmlLabel(state.cell) || (value != null && isNode(value));
const isForceHtml = graph.isHtmlLabel(state.cell) || isNode(value);
state.text = new this.defaultTextShape(
value,
@ -459,8 +458,7 @@ class CellRenderer {
state.style.labelPadding,
state.style.textDirection || DEFAULT_TEXT_DIRECTION
);
state.text.opacity =
state.style.textOpacity == null ? 100 : state.style.textOpacity;
state.text.opacity = state.style.textOpacity;
state.text.dialect = isForceHtml ? DIALECT_STRICTHTML : state.view.graph.dialect;
state.text.style = state.style;
state.text.state = state;
@ -474,7 +472,7 @@ class CellRenderer {
let forceGetCell = false;
const getState = (evt: Event | InternalMouseEvent) => {
let result = state;
let result: CellState | null = state;
if (mxClient.IS_TOUCH || forceGetCell) {
const x = getClientX(evt);
@ -483,7 +481,7 @@ class CellRenderer {
// Dispatches the drop event to the graph which
// consumes and executes the source function
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;
};
@ -491,29 +489,29 @@ class CellRenderer {
// TODO: Add handling for special touch device gestures
InternalEvent.addGestureListeners(
state.text.node,
(evt: MouseEvent) => {
if (this.isLabelEvent(state, evt)) {
graph.event.fireMouseEvent(
(evt: Event) => {
if (this.isLabelEvent(state, evt as MouseEvent)) {
graph.fireMouseEvent(
InternalEvent.MOUSE_DOWN,
new InternalMouseEvent(evt, state)
new InternalMouseEvent(evt as MouseEvent, state)
);
forceGetCell =
graph.dialect !== DIALECT_SVG && getSource(evt).nodeName === 'IMG';
}
},
(evt: MouseEvent) => {
if (this.isLabelEvent(state, evt)) {
graph.event.fireMouseEvent(
(evt: Event) => {
if (this.isLabelEvent(state, evt as MouseEvent)) {
graph.fireMouseEvent(
InternalEvent.MOUSE_MOVE,
new InternalMouseEvent(evt, getState(evt))
new InternalMouseEvent(evt as MouseEvent, getState(evt))
);
}
},
(evt: MouseEvent) => {
if (this.isLabelEvent(state, evt)) {
graph.event.fireMouseEvent(
(evt: Event) => {
if (this.isLabelEvent(state, evt as MouseEvent)) {
graph.fireMouseEvent(
InternalEvent.MOUSE_UP,
new InternalMouseEvent(evt, getState(evt))
new InternalMouseEvent(evt as MouseEvent, getState(evt))
);
forceGetCell = false;
}
@ -521,10 +519,10 @@ class CellRenderer {
);
// Uses double click timeout in mxGraph for quirks mode
if (graph.event.nativeDblClickEnabled) {
InternalEvent.addListener(state.text.node, 'dblclick', (evt: MouseEvent) => {
if (this.isLabelEvent(state, evt)) {
graph.event.dblClick(evt, state.cell);
if (graph.isNativeDblClickEnabled()) {
InternalEvent.addListener(state.text.node, 'dblclick', (evt: Event) => {
if (this.isLabelEvent(state, evt as MouseEvent)) {
graph.dblClick(evt as MouseEvent, state.cell);
InternalEvent.consume(evt);
}
});
@ -558,47 +556,37 @@ class CellRenderer {
*
* state - <mxCellState> for which the overlay should be created.
*/
createCellOverlays(state: CellState): void {
createCellOverlays(state: CellState) {
const { graph } = state.view;
const overlays = graph.getCellOverlays(state.cell);
let dict = null;
const dict = new Dictionary<CellOverlay, Shape>();
if (overlays != null) {
dict = new Dictionary();
for (let i = 0; i < overlays.length; i += 1) {
const shape = state.overlays.remove(overlays[i]);
for (let i = 0; i < overlays.length; i += 1) {
const shape = state.overlays != null ? state.overlays.remove(overlays[i]) : null;
if (!shape) {
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) {
const tmp = new ImageShape(
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);
if (overlays[i].cursor) {
tmp.node.style.cursor = overlays[i].cursor;
}
dict.put(overlays[i], tmp);
} else {
dict.put(overlays[i], shape);
}
}
// Removes unused
if (state.overlays != null) {
state.overlays.visit((id: any, shape: { destroy: () => void }) => {
shape.destroy();
});
}
state.overlays.visit((id: any, shape: { destroy: () => void }) => {
shape.destroy();
});
state.overlays = dict;
}
@ -612,7 +600,7 @@ class CellRenderer {
* state - <mxCellState> for which the overlay should be created.
* overlay - <mxImageShape> that represents the overlay.
*/
initializeOverlay(state: CellState, overlay: ImageShape): void {
initializeOverlay(state: CellState, overlay: ImageShape) {
overlay.init(state.view.getOverlayPane());
}
@ -622,16 +610,12 @@ class CellRenderer {
* Installs the listeners for the given <mxCellState>, <mxCellOverlay> and
* <mxShape> that represents the overlay.
*/
installCellOverlayListeners(
state: CellState,
overlay: CellOverlay,
shape: Shape
): void {
installCellOverlayListeners(state: CellState, overlay: CellOverlay, shape: Shape) {
const { graph } = state.view;
InternalEvent.addListener(shape.node, 'click', (evt: Event) => {
if (graph.editing.isEditing()) {
graph.editing.stopEditing(!graph.editing.isInvokesStopCellEditing());
if (graph.isEditing()) {
graph.stopEditing(!graph.isInvokesStopCellEditing());
}
overlay.fireEvent(
@ -645,9 +629,9 @@ class CellRenderer {
InternalEvent.consume(evt);
},
(evt: Event) => {
graph.event.fireMouseEvent(
graph.fireMouseEvent(
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.
*/
createControl(state: CellState): void {
createControl(state: CellState) {
const { graph } = state.view;
const image = graph.getFoldingImage(state);
if (graph.foldingEnabled && image != null) {
if (state.control == null) {
if (graph.isFoldingEnabled() && image) {
if (!state.control) {
const b = new Rectangle(0, 0, image.width, image.height);
state.control = new ImageShape(b, image.src);
state.control.preserveImageAspect = false;
@ -688,7 +672,7 @@ class CellRenderer {
this.createControlClickHandler(state)
);
}
} else if (state.control != null) {
} else if (state.control) {
state.control.destroy();
state.control = null;
}
@ -703,13 +687,13 @@ class CellRenderer {
*
* state - <mxCellState> whose control click handler should be returned.
*/
createControlClickHandler(state: CellState): Function {
createControlClickHandler(state: CellState) {
const { graph } = state.view;
return (evt: EventObject) => {
return (evt: Event) => {
if (this.forceControlClickHandler || graph.isEnabled()) {
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);
}
};
@ -731,7 +715,7 @@ class CellRenderer {
state: CellState,
control: Shape,
handleEvents: boolean,
clickHandler: Function
clickHandler: EventListener
): Element {
const { graph } = state.view;
@ -744,8 +728,7 @@ class CellRenderer {
if (isForceHtml) {
control.dialect = DIALECT_PREFERHTML;
control.init(graph.container);
// @ts-ignore
control.node.style.zIndex = 1;
control.node.style.zIndex = String(1);
} else {
control.init(state.view.getOverlayPane());
}
@ -753,9 +736,8 @@ class CellRenderer {
const node = control.node;
// 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()) {
// @ts-ignore
node.style.cursor = 'pointer';
}
@ -769,35 +751,34 @@ class CellRenderer {
node,
(evt: Event) => {
first = new Point(getClientX(evt), getClientY(evt));
graph.event.fireMouseEvent(
graph.fireMouseEvent(
InternalEvent.MOUSE_DOWN,
new InternalMouseEvent(evt, state)
new InternalMouseEvent(evt as MouseEvent, state)
);
InternalEvent.consume(evt);
},
(evt: Event) => {
graph.event.fireMouseEvent(
graph.fireMouseEvent(
InternalEvent.MOUSE_MOVE,
new InternalMouseEvent(evt, state)
new InternalMouseEvent(evt as MouseEvent, state)
);
},
(evt: Event) => {
graph.event.fireMouseEvent(
graph.fireMouseEvent(
InternalEvent.MOUSE_UP,
new InternalMouseEvent(evt, state)
new InternalMouseEvent(evt as MouseEvent, state)
);
InternalEvent.consume(evt);
}
);
// Uses capture phase for event interception to stop bubble phase
if (clickHandler != null && mxClient.IS_IOS) {
// @ts-ignore
if (clickHandler && mxClient.IS_IOS) {
node.addEventListener(
'touchend',
(evt) => {
if (first != null) {
const tol = graph.tolerance;
if (first) {
const tol = graph.getEventTolerance();
if (
Math.abs(first.x - getClientX(evt)) < tol &&
@ -827,7 +808,7 @@ class CellRenderer {
* state - <mxCellState> whose shape fired the event.
* evt - Mouse event which was fired.
*/
isShapeEvent(state: CellState, evt: InternalMouseEvent | MouseEvent): boolean {
isShapeEvent(state: CellState, evt: InternalMouseEvent | MouseEvent) {
return true;
}
@ -842,7 +823,7 @@ class CellRenderer {
* state - <mxCellState> whose label fired the event.
* evt - Mouse event which was fired.
*/
isLabelEvent(state: CellState, evt: InternalMouseEvent | MouseEvent): boolean {
isLabelEvent(state: CellState, evt: InternalMouseEvent | MouseEvent) {
return true;
}
@ -855,14 +836,14 @@ class CellRenderer {
*
* state - <mxCellState> for which the event listeners should be isntalled.
*/
installListeners(state: CellState): void {
installListeners(state: CellState) {
const { graph } = state.view;
// Workaround for touch devices routing all events for a mouse
// gesture (down, move, up) via the initial DOM node. Same for
// HTML images in all IE versions (VML images are working).
const getState = (evt: Event) => {
let result = state;
let result: CellState | null = state;
if (
(graph.dialect !== DIALECT_SVG && getSource(evt).nodeName === 'IMG') ||
@ -874,7 +855,7 @@ class CellRenderer {
// Dispatches the drop event to the graph which
// consumes and executes the source function
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;
@ -883,38 +864,38 @@ class CellRenderer {
InternalEvent.addGestureListeners(
// @ts-ignore
state.shape.node,
(evt: MouseEvent) => {
if (this.isShapeEvent(state, evt)) {
(evt: Event) => {
if (this.isShapeEvent(state, evt as MouseEvent)) {
graph.fireMouseEvent(
InternalEvent.MOUSE_DOWN,
new InternalMouseEvent(evt, state)
new InternalMouseEvent(evt as MouseEvent, state)
);
}
},
(evt: MouseEvent) => {
if (this.isShapeEvent(state, evt)) {
(evt: Event) => {
if (this.isShapeEvent(state, evt as MouseEvent)) {
graph.fireMouseEvent(
InternalEvent.MOUSE_MOVE,
new InternalMouseEvent(evt, getState(evt))
new InternalMouseEvent(evt as MouseEvent, getState(evt))
);
}
},
(evt: MouseEvent) => {
if (this.isShapeEvent(state, evt)) {
(evt: Event) => {
if (this.isShapeEvent(state, evt as MouseEvent)) {
graph.fireMouseEvent(
InternalEvent.MOUSE_UP,
new InternalMouseEvent(evt, getState(evt))
new InternalMouseEvent(evt as MouseEvent, getState(evt))
);
}
}
);
// Uses double click timeout in mxGraph for quirks mode
if (graph.nativeDblClickEnabled) {
if (graph.isNativeDblClickEnabled()) {
// @ts-ignore
InternalEvent.addListener(state.shape.node, 'dblclick', (evt) => {
if (this.isShapeEvent(state, evt)) {
graph.dblClick(evt, state.cell);
if (this.isShapeEvent(state, evt as MouseEvent)) {
graph.dblClick(evt as MouseEvent, state.cell);
InternalEvent.consume(evt);
}
});
@ -930,22 +911,22 @@ class CellRenderer {
*
* state - <mxCellState> whose label should be redrawn.
*/
redrawLabel(state: CellState, forced: boolean): void {
redrawLabel(state: CellState, forced: boolean) {
const { graph } = state.view;
const value = this.getLabelValue(state);
const wrapping = graph.isWrapping(state.cell);
const clipping = graph.isLabelClipped(state.cell);
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 overflow = state.style.overflow || 'visible';
if (
state.text != null &&
(state.text.wrap != wrapping ||
state.text.clipped != clipping ||
state.text.overflow != overflow ||
state.text.dialect != dialect)
state.text &&
(state.text.wrap !== wrapping ||
state.text.clipped !== clipping ||
state.text.overflow !== overflow ||
state.text.dialect !== dialect)
) {
state.text.destroy();
state.text = null;
@ -972,7 +953,7 @@ class CellRenderer {
state.text.apply(state);
// 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);
@ -1597,23 +1578,23 @@ class CellRenderer {
if (state.shape.indicatorShape != null) {
state.shape.indicator = new state.shape.indicatorShape();
state.shape.indicator.dialect = state.shape.dialect;
state.shape.indicator.init(state.node);
state.shape.indicator.init(state.node as HTMLElement);
force = true;
}
}
if (state.shape != null) {
if (state.shape) {
// Handles changes of the collapse icon
this.createControl(state);
// 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
if (force || this.isShapeInvalid(state, state.shape)) {
if (state.absolutePoints != null) {
if (state.absolutePoints.length > 0) {
state.shape.points = state.absolutePoints.slice();
state.shape.bounds = null;
} else {
state.shape.points = null;
state.shape.points = [];
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.
*/
destroy(state: CellState): void {
if (state.shape != null) {
if (state.text != null) {
destroy(state: CellState) {
if (state.shape) {
if (state.text) {
state.text.destroy();
state.text = null;
}
if (state.overlays != null) {
state.overlays.visit((id: string, shape: Shape) => {
shape.destroy();
});
state.overlays.visit((id: string, shape: Shape) => {
shape.destroy();
});
state.overlays = null;
}
state.overlays = new Dictionary();
if (state.control != null) {
if (state.control) {
state.control.destroy();
state.control = null;
}
@ -1699,6 +1678,7 @@ CellRenderer.registerShape(SHAPE_ELLIPSE, EllipseShape);
CellRenderer.registerShape(SHAPE_RHOMBUS, RhombusShape);
// @ts-ignore
CellRenderer.registerShape(SHAPE_CYLINDER, CylinderShape);
// @ts-ignore
CellRenderer.registerShape(SHAPE_CONNECTOR, Connector);
// @ts-ignore
CellRenderer.registerShape(SHAPE_ACTOR, Actor);
@ -1706,14 +1686,19 @@ CellRenderer.registerShape(SHAPE_TRIANGLE, TriangleShape);
CellRenderer.registerShape(SHAPE_HEXAGON, HexagonShape);
// @ts-ignore
CellRenderer.registerShape(SHAPE_CLOUD, CloudShape);
// @ts-ignore
CellRenderer.registerShape(SHAPE_LINE, Line);
// @ts-ignore
CellRenderer.registerShape(SHAPE_ARROW, Arrow);
// @ts-ignore
CellRenderer.registerShape(SHAPE_ARROW_CONNECTOR, ArrowConnector);
// @ts-ignore
CellRenderer.registerShape(SHAPE_DOUBLE_ELLIPSE, DoubleEllipseShape);
// @ts-ignore
CellRenderer.registerShape(SHAPE_SWIMLANE, SwimlaneShape);
// @ts-ignore
CellRenderer.registerShape(SHAPE_IMAGE, ImageShape);
// @ts-ignore
CellRenderer.registerShape(SHAPE_LABEL, Label);
export default CellRenderer;

View File

@ -97,7 +97,7 @@ class TemporaryCellStates {
/**
* Holds the states of the rectangle.
*/
oldStates: Dictionary<string, CellState>;
oldStates: Dictionary<Cell, CellState>;
/**
* Holds the bounds of the rectangle.

View File

@ -1,7 +1,7 @@
import Cell from "./datatypes/Cell";
import CellArray from "./datatypes/CellArray";
import Dictionary from "../../util/Dictionary";
import Graph from "../Graph";
import Cell from './datatypes/Cell';
import CellArray from './datatypes/CellArray';
import Dictionary from '../../util/Dictionary';
import Graph from '../Graph';
class TreeTraversal {
dependencies = ['connections'];
@ -42,7 +42,7 @@ class TreeTraversal {
for (const cell of parent.getChildren()) {
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 fanIn = 0;
@ -117,13 +117,13 @@ class TreeTraversal {
directed: boolean = true,
func: Function | null = null,
edge: Cell | null = null,
visited: Dictionary | null = null,
visited: Dictionary<Cell, boolean> | null = null,
inverse: boolean = false
): void {
if (func != null && vertex != null) {
directed = directed != null ? directed : true;
inverse = inverse != null ? inverse : false;
visited = visited || new Dictionary();
visited = visited || new Dictionary<Cell, boolean>();
if (!visited.get(vertex)) {
visited.put(vertex, true);

View File

@ -15,6 +15,7 @@ import Dictionary from '../../../util/Dictionary';
import { NONE } from '../../../util/Constants';
import { CellStateStyles } from 'packages/core/src/types';
import RectangleShape from '../../geometry/shape/node/RectangleShape';
import CellOverlay from '../CellOverlay';
/**
* Class: mxCellState
@ -67,7 +68,7 @@ class CellState extends Rectangle {
control: Shape | null = null;
// Used by mxCellRenderer's createCellOverlays()
overlays: Dictionary<Cell, Shape> | null = null;
overlays: Dictionary<CellOverlay, Shape> = new Dictionary();
/**
* Variable: view
@ -197,6 +198,8 @@ class CellState extends Rectangle {
parentHighlight: RectangleShape | null = null;
point: Point | null = null;
/**
* Function: getPerimeterBounds
*

View File

@ -63,6 +63,7 @@ import InternalMouseEvent from '../../event/InternalMouseEvent';
import Cell from '../datatypes/Cell';
import ImageBox from '../../image/ImageBox';
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
@ -381,8 +382,12 @@ class EdgeHandler {
customHandles: CellHandle[] = [];
startX: number = 0;
startY: number = 0;
startX = 0;
startY = 0;
outline = true;
active = true;
/**
* Function: isParentHighlightVisible
@ -461,7 +466,7 @@ class EdgeHandler {
* <virtualBendsEnabled> is true and the current style allows and
* renders custom waypoints.
*/
isVirtualBendsEnabled(evt: Event) {
isVirtualBendsEnabled(evt?: Event) {
return (
this.virtualBendsEnabled &&
(this.state.style.edge == null ||
@ -970,7 +975,7 @@ class EdgeHandler {
* control point. The source and target points are used for reconnecting
* the edge.
*/
mouseDown(sender: Listenable, me: InternalMouseEvent) {
mouseDown(sender: EventSource, me: InternalMouseEvent) {
const handle = this.getHandleForEvent(me);
if (handle !== null && this.bends[handle]) {
@ -1284,7 +1289,7 @@ class EdgeHandler {
// Removes point if user tries to straighten a segment
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();
abs[this.index] = pt;
@ -1517,8 +1522,7 @@ class EdgeHandler {
*
* Handles the event by updating the preview.
*/
// mouseMove(sender: any, me: mxMouseEvent): void;
mouseMove(sender: Listenable, me: InternalMouseEvent) {
mouseMove(sender: EventSource, me: InternalMouseEvent) {
if (this.index != null && this.marker != null) {
this.currentPoint = this.getPointForEvent(me);
this.error = null;
@ -1628,7 +1632,7 @@ class EdgeHandler {
* Handles the event to applying the previewed changes on the edge by
* using <moveLabel>, <connect> or <changePoints>.
*/
mouseUp(sender: Listenable, me: InternalMouseEvent) {
mouseUp(sender: EventSource, me: InternalMouseEvent) {
// Workaround for wrong event source in Webkit
if (this.index != null && this.marker != null) {
if (this.shape != null && this.shape.node != null) {
@ -1675,7 +1679,7 @@ class EdgeHandler {
} else if (this.isLabel && this.label) {
this.moveLabel(this.state, this.label.x, this.label.y);
} else if (this.isSource || this.isTarget) {
let terminal = null;
let terminal: Cell | null = null;
if (
this.constraintHandler.currentConstraint != null &&
@ -1685,17 +1689,17 @@ class EdgeHandler {
}
if (
terminal == null &&
!terminal &&
this.marker.hasValidState() &&
this.marker.highlight != null &&
this.marker.highlight.shape != null &&
this.marker.highlight.shape.stroke !== 'transparent' &&
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 parent = edge.getParent();
@ -1704,18 +1708,18 @@ class EdgeHandler {
// Clones and adds the cell
if (clone) {
let geo = edge.getGeometry();
clone = this.graph.cloneCell(edge);
model.add(parent, clone, parent.getChildCount());
const cloned = this.graph.cloneCell(edge);
model.add(parent, cloned, parent.getChildCount());
if (geo != null) {
geo = geo.clone();
model.setGeometry(clone, geo);
model.setGeometry(cloned, geo);
}
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);
@ -2127,7 +2131,7 @@ class EdgeHandler {
*
* Redraws the preview, and the bends- and label control points.
*/
redraw(ignoreHandles: boolean) {
redraw(ignoreHandles?: boolean) {
this.abspoints = this.state.absolutePoints.slice();
const g = this.state.cell.getGeometry();

View File

@ -69,6 +69,8 @@ class VertexHandle implements CellHandle {
*/
ignoreGrid = false;
active = true;
/**
* Hook for subclassers to return the current position of the handle.
*/

File diff suppressed because it is too large Load Diff

View File

@ -13,17 +13,17 @@ import Point from '../geometry/Point';
*/
class ConnectionConstraint {
constructor(
point: Point | null = null,
perimeter: boolean = true,
point: Point | null,
perimeter = true,
name: string | null = null,
dx: number | null = null,
dy: number | null = null
dx = 0,
dy = 0
) {
this.point = point;
this.perimeter = perimeter != null ? perimeter : true;
this.perimeter = perimeter;
this.name = name;
this.dx = dx || 0;
this.dy = dy || 0;
this.dx = dx;
this.dy = dy;
}
/**
@ -31,7 +31,7 @@ class ConnectionConstraint {
*
* <mxPoint> that specifies the fixed location of the connection point.
*/
point: Point | null = null;
point: Point | null;
/**
* Variable: perimeter
@ -39,7 +39,7 @@ class ConnectionConstraint {
* Boolean that specifies if the point should be projected onto the perimeter
* of the terminal.
*/
perimeter: boolean = true;
perimeter = true;
/**
* Variable: name
@ -53,14 +53,14 @@ class ConnectionConstraint {
*
* Optional float that specifies the horizontal offset of the constraint.
*/
dx: number | null = null;
dx = 0;
/**
* Variable: dy
*
* Optional float that specifies the vertical offset of the constraint.
*/
dy: number | null = null;
dy = 0;
}
export default ConnectionConstraint;

View File

@ -14,6 +14,7 @@ import {
DIALECT_SVG,
HIGHLIGHT_STROKEWIDTH,
INVALID_COLOR,
NONE,
OUTLINE_HIGHLIGHT_COLOR,
OUTLINE_HIGHLIGHT_STROKEWIDTH,
TOOLTIP_VERTICAL_OFFSET,
@ -41,12 +42,13 @@ import {
isConsumed,
isShiftDown,
} from '../../util/EventUtils';
import graph from '../Graph';
import graph, { MaxGraph } from '../Graph';
import Image from '../image/ImageBox';
import CellState from '../cell/datatypes/CellState';
import Graph from '../Graph';
import ConnectionConstraint from './ConnectionConstraint';
import Shape from '../geometry/shape/Shape';
import { Listenable } from '../../types';
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.
*/
class ConnectionHandler extends EventSource {
constructor(graph: Graph, factoryMethod: FactoryMethod | null = null) {
constructor(graph: MaxGraph, factoryMethod: FactoryMethod | null = null) {
super();
this.graph = graph;
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
this.escapeHandler = () => {
@ -225,7 +260,7 @@ class ConnectionHandler extends EventSource {
// TODO: Document me!
previous: CellState | null = null;
iconState: CellState | null = null;
icons: ImageShape[] | null = null;
icons: ImageShape[] = [];
cell: Cell | null = null;
currentPoint: Point | null = null;
sourceConstraint: ConnectionConstraint | null = null;
@ -241,7 +276,7 @@ class ConnectionHandler extends EventSource {
*
* Reference to the enclosing <mxGraph>.
*/
graph: Graph;
graph: MaxGraph;
/**
* Variable: factoryMethod
@ -319,7 +354,6 @@ class ConnectionHandler extends EventSource {
*
* Holds the <mxTerminalMarker> used for finding source and target cells.
*/
// @ts-ignore
marker: CellMarker;
/**
@ -328,14 +362,14 @@ class ConnectionHandler extends EventSource {
* Holds the <mxConstraintHandler> used for drawing and highlighting
* constraints.
*/
constraintHandler: ConstraintHandler | null = null;
constraintHandler: ConstraintHandler;
/**
* Variable: error
*
* Holds the current validation error while connections are being created.
*/
error: any = null;
error: string | null = null;
/**
* Variable: waypointsEnabled
@ -385,14 +419,14 @@ class ConnectionHandler extends EventSource {
*
* Holds the change event listener for later removal.
*/
changeHandler: any = null;
changeHandler: (sender: Listenable) => void;
/**
* Variable: drillHandler
*
* Holds the drill event listener for later removal.
*/
drillHandler: any = null;
drillHandler: (sender: Listenable) => void;
/**
* Variable: mouseDownCounter
@ -539,52 +573,6 @@ class ConnectionHandler extends EventSource {
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
*
@ -600,7 +588,7 @@ class ConnectionHandler extends EventSource {
*
* Creates and returns the <mxCellMarker> used in <marker>.
*/
createMarker(): CellMarker {
createMarker() {
const self = this;
class MyCellMarker extends CellMarker {
@ -613,12 +601,12 @@ class ConnectionHandler extends EventSource {
self.error = null;
// 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);
}
// Uses connectable parent vertex if one exists
if (cell != null && !cell.isConnectable()) {
if (cell && !cell.isConnectable() && self.cell) {
const parent = self.cell.getParent();
if (parent.isVertex() && parent.isConnectable()) {
@ -626,6 +614,7 @@ class ConnectionHandler extends EventSource {
}
}
/* disable swimlane for now
if (
(self.graph.swimlane.isSwimlane(cell) &&
self.currentPoint != null &&
@ -638,13 +627,14 @@ class ConnectionHandler extends EventSource {
) {
cell = null;
}
*/
if (cell != null) {
if (cell) {
if (self.isConnecting()) {
if (self.previous != null) {
if (self.previous) {
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;
// Enables create target inside groups
@ -659,7 +649,7 @@ class ConnectionHandler extends EventSource {
} else if (
self.isConnecting() &&
!self.isCreateTarget(me.getEvent()) &&
!self.graph.allowDanglingEdges
!self.graph.isAllowDanglingEdges()
) {
self.error = '';
}
@ -670,30 +660,30 @@ class ConnectionHandler extends EventSource {
// Sets the highlight color according to validateConnection
isValidState(state: CellState) {
if (self.isConnecting()) {
return self.error == null;
return !self.error;
}
return super.isValidState(state);
}
// Overrides to use marker color only in highlight mode or for
// target selection
getMarkerColor(evt: Event, state: CellState, isValid: boolean): string | null {
return self.connectImage == null || self.isConnecting()
getMarkerColor(evt: Event, state: CellState, isValid: boolean) {
return !self.connectImage || self.isConnecting()
? super.getMarkerColor(evt, state, isValid)
: null;
: NONE;
}
// Overrides to use hotspot only for source selection otherwise
// intersects always returns true when over a cell
intersects(state: CellState, evt: InternalMouseEvent) {
if (self.connectImage != null || self.isConnecting()) {
if (self.connectImage || self.isConnecting()) {
return true;
}
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.
*/
start(state: CellState, x: number, y: number, edgeState: CellState): void {
start(state: CellState, x: number, y: number, edgeState: CellState) {
this.previous = state;
this.first = new Point(x, y);
this.edgeState = edgeState != null ? edgeState : this.createEdgeState(null);
this.edgeState = edgeState ?? this.createEdgeState();
// Marks the source state
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
* connection is currently being previewed.
*/
isConnecting(): boolean {
return this.first != null && this.shape != null;
isConnecting() {
return !!this.first && !!this.shape;
}
/**
@ -734,7 +724,7 @@ class ConnectionHandler extends EventSource {
* cell - <mxCell> that represents the source terminal.
* me - <mxMouseEvent> that is associated with this call.
*/
isValidSource(cell: Cell, me: InternalMouseEvent): boolean {
isValidSource(cell: Cell, me: InternalMouseEvent) {
return this.graph.isValidSource(cell);
}
@ -749,7 +739,7 @@ class ConnectionHandler extends EventSource {
*
* cell - <mxCell> that represents the target terminal.
*/
isValidTarget(cell: Cell): boolean {
isValidTarget(cell: Cell) {
return true;
}
@ -765,7 +755,7 @@ class ConnectionHandler extends EventSource {
* source - <mxCell> that represents the source terminal.
* target - <mxCell> that represents the target terminal.
*/
validateConnection(source: Cell, target: Cell): string {
validateConnection(source: Cell, target: Cell) {
if (!this.isValidTarget(target)) {
return '';
}
@ -782,7 +772,7 @@ class ConnectionHandler extends EventSource {
*
* state - <mxCellState> whose connect image should be returned.
*/
getConnectImage(state: CellState): Image | null {
getConnectImage(state: CellState) {
return this.connectImage;
}
@ -796,8 +786,8 @@ class ConnectionHandler extends EventSource {
*
* state - <mxCellState> whose connect icons should be returned.
*/
isMoveIconToFrontForState(state: CellState): boolean {
if (state.text != null && state.text.node.parentNode === this.graph.container) {
isMoveIconToFrontForState(state: CellState) {
if (state.text && state.text.node.parentNode === this.graph.container) {
return true;
}
return this.moveIconFront;
@ -813,10 +803,10 @@ class ConnectionHandler extends EventSource {
*
* state - <mxCellState> whose connect icons should be returned.
*/
createIcons(state: CellState): ImageShape[] | null {
createIcons(state: CellState) {
const image = this.getConnectImage(state);
if (image != null && state != null) {
if (image) {
this.iconState = state;
const icons = [];
@ -825,7 +815,7 @@ class ConnectionHandler extends EventSource {
// connect-icon appears behind the selection border and the selection
// border consumes the events before the icon gets a chance
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;
if (this.isMoveIconToFrontForState(state)) {
@ -836,7 +826,7 @@ class ConnectionHandler extends EventSource {
icon.init(this.graph.getView().getOverlayPane());
// 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);
}
}
@ -845,11 +835,11 @@ class ConnectionHandler extends EventSource {
// Events transparency
const getState = () => {
return this.currentState != null ? this.currentState : state;
return this.currentState ?? state;
};
// Updates the local icon before firing the mouse down event.
const mouseDown = (evt) => {
const mouseDown = (evt: MouseEvent) => {
if (!isConsumed(evt)) {
this.icon = icon;
this.graph.fireMouseEvent(
@ -877,10 +867,10 @@ class ConnectionHandler extends EventSource {
*
* Parameters:
*
* icons - Optional array of <mxImageShapes> to be redrawn.
* icons - Array of <mxImageShapes> to be redrawn.
*/
redrawIcons(icons?: ImageShape[] | null, state?: CellState): void {
if (icons != null && icons[0] != null && state != null) {
redrawIcons(icons: ImageShape[], state: CellState) {
if (icons[0] && icons[0].bounds) {
const pos = this.getIconPosition(icons[0], state);
icons[0].bounds.x = pos.x;
icons[0].bounds.y = pos.y;
@ -889,11 +879,12 @@ class ConnectionHandler extends EventSource {
}
// TODO: Document me! ===========================================================================================================
getIconPosition(icon: ImageShape, state: CellState): Point {
const { scale } = this.graph.getView();
getIconPosition(icon: ImageShape, state: CellState) {
// const { scale } = this.graph.getView();
let cx = state.getCenterX();
let cy = state.getCenterY();
/* disable swimlane for now
if (this.graph.isSwimlane(state.cell)) {
const size = this.graph.getStartSize(state.cell);
@ -910,8 +901,9 @@ class ConnectionHandler extends EventSource {
cx = pt.x;
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.
*/
destroyIcons(): void {
if (this.icons != null) {
for (let i = 0; i < this.icons.length; i += 1) {
this.icons[i].destroy();
}
this.icons = null;
this.icon = null;
this.selectedIcon = null;
this.iconState = null;
destroyIcons() {
for (let i = 0; i < this.icons.length; i += 1) {
this.icons[i].destroy();
}
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
* <icons> is null or <icons> and <icon> are not null.
*/
isStartEvent(me: InternalMouseEvent): boolean {
isStartEvent(me: InternalMouseEvent) {
return (
(this.constraintHandler.currentFocus !== null &&
this.constraintHandler.currentConstraint !== null) ||
@ -956,7 +945,7 @@ class ConnectionHandler extends EventSource {
*
* Handles the event by initiating a new connection.
*/
mouseDown(sender: any, me: InternalMouseEvent): void {
mouseDown(sender: Listenable, me: InternalMouseEvent) {
this.mouseDownCounter += 1;
if (
@ -967,9 +956,9 @@ class ConnectionHandler extends EventSource {
this.isStartEvent(me)
) {
if (
this.constraintHandler.currentConstraint != null &&
this.constraintHandler.currentFocus != null &&
this.constraintHandler.currentPoint != null
this.constraintHandler.currentConstraint &&
this.constraintHandler.currentFocus &&
this.constraintHandler.currentPoint
) {
this.sourceConstraint = this.constraintHandler.currentConstraint;
this.previous = this.constraintHandler.currentFocus;
@ -982,17 +971,17 @@ class ConnectionHandler extends EventSource {
this.edgeState = this.createEdgeState(me);
this.mouseDownCounter = 1;
if (this.waypointsEnabled && this.shape == null) {
if (this.waypointsEnabled && !this.shape) {
this.waypoints = null;
this.shape = this.createShape();
if (this.edgeState != null) {
if (this.edgeState) {
this.shape.apply(this.edgeState);
}
}
// 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());
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
* in the graph.
*/
isImmediateConnectSource(state: CellState): boolean {
isImmediateConnectSource(state: CellState) {
return !this.graph.isCellMovable(state.cell);
}
@ -1034,7 +1023,7 @@ class ConnectionHandler extends EventSource {
* };
* (end)
*/
createEdgeState(me: InternalMouseEvent): CellState | null {
createEdgeState(me?: InternalMouseEvent): CellState | 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
* or shift is pressed.
*/
isOutlineConnectEvent(me: InternalMouseEvent): boolean {
isOutlineConnectEvent(me: InternalMouseEvent) {
const offset = getOffset(this.graph.container);
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
// in which case the grid is ignored and mouse point is used instead
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);
@ -1191,7 +1180,7 @@ class ConnectionHandler extends EventSource {
*
* Returns true if the given cell does not allow new connections to be created.
*/
isCellEnabled(cell: Cell): boolean {
isCellEnabled(cell: Cell) {
return true;
}
@ -1200,7 +1189,7 @@ class ConnectionHandler extends EventSource {
*
* Converts the given point from screen coordinates to model coordinates.
*/
convertWaypoint(point: Point): void {
convertWaypoint(point: Point) {
const scale = this.graph.getView().getScale();
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
* first point of the preview if alt is not pressed.
*/
snapToPreview(me: MouseEvent, point: Point): void {
if (!isAltDown(me.getEvent()) && this.previous != null) {
const tol = (this.graph.gridSize * this.graph.view.scale) / 2;
snapToPreview(me: InternalMouseEvent, point: Point) {
if (!isAltDown(me.getEvent()) && this.previous) {
const tol = (this.graph.getGridSize() * this.graph.view.scale) / 2;
const tmp =
this.sourceConstraint != null
this.sourceConstraint && 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) {
point.x = tmp.x;
@ -1238,13 +1227,13 @@ class ConnectionHandler extends EventSource {
* Handles the event by updating the preview edge or by highlighting
* a possible source or target terminal.
*/
mouseMove(sender: MouseEvent, me: InternalMouseEvent): void {
mouseMove(sender: MouseEvent, me: InternalMouseEvent) {
if (
!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
if (!this.isEnabled() && this.currentState != null) {
if (!this.isEnabled() && this.currentState) {
this.destroyIcons();
this.currentState = null;
}
@ -1255,10 +1244,10 @@ class ConnectionHandler extends EventSource {
let point = new Point(me.getGraphX(), me.getGraphY());
this.error = null;
if (this.graph.grid.isGridEnabledEvent(me.getEvent())) {
point = new point(
(this.graph.grid.snap(point.x / scale - tr.x) + tr.x) * scale,
(this.graph.grid.snap(point.y / scale - tr.y) + tr.y) * scale
if (this.graph.isGridEnabledEvent(me.getEvent())) {
point = new Point(
(this.graph.snap(point.x / scale - tr.x) + tr.x) * scale,
(this.graph.snap(point.y / scale - tr.y) + tr.y) * scale
);
}
@ -1266,29 +1255,29 @@ class ConnectionHandler extends EventSource {
this.currentPoint = point;
if (
(this.first != null || (this.isEnabled() && this.graph.isEnabled())) &&
(this.shape != null ||
this.first == null ||
Math.abs(me.getGraphX() - this.first.x) > this.graph.tolerance ||
Math.abs(me.getGraphY() - this.first.y) > this.graph.tolerance)
(this.first || (this.isEnabled() && this.graph.isEnabled())) &&
(this.shape ||
!this.first ||
Math.abs(me.getGraphX() - this.first.x) > this.graph.getEventTolerance() ||
Math.abs(me.getGraphY() - this.first.y) > this.graph.getEventTolerance())
) {
this.updateCurrentState(me, point);
}
if (this.first != null) {
if (this.first) {
let constraint = null;
let current = point;
// Uses the current point from the constraint handler if available
if (
this.constraintHandler.currentConstraint != null &&
this.constraintHandler.currentFocus != null &&
this.constraintHandler.currentPoint != null
this.constraintHandler.currentConstraint &&
this.constraintHandler.currentFocus &&
this.constraintHandler.currentPoint
) {
constraint = this.constraintHandler.currentConstraint;
current = this.constraintHandler.currentPoint.clone();
} else if (
this.previous != null &&
this.previous &&
!this.graph.isIgnoreTerminalEvent(me.getEvent()) &&
isShiftDown(me.getEvent())
) {
@ -1305,11 +1294,11 @@ class ConnectionHandler extends EventSource {
let pt2 = this.first;
// Moves the connect icon with the mouse
if (this.selectedIcon != null) {
if (this.selectedIcon && this.selectedIcon.bounds) {
const w = this.selectedIcon.bounds.width;
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);
this.selectedIcon.bounds.x = pos.x;
this.selectedIcon.bounds.y = pos.y;
@ -1327,7 +1316,7 @@ class ConnectionHandler extends EventSource {
}
// Uses edge state to compute the terminal points
if (this.edgeState != null) {
if (this.edgeState) {
this.updateEdgeState(current, constraint);
current = this.edgeState.absolutePoints[
this.edgeState.absolutePoints.length - 1
@ -1397,7 +1386,10 @@ class ConnectionHandler extends EventSource {
const dx = Math.abs(me.getGraphX() - this.first.x);
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();
if (this.edgeState != null) {
@ -1487,28 +1479,33 @@ class ConnectionHandler extends EventSource {
*
* 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
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.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.entryY = constraint.point.y;
} else {
delete this.edgeState.style.entryX;
delete this.edgeState.style.entryY;
this.edgeState.style.entryX = 0;
this.edgeState.style.entryY = 0;
}
this.edgeState.absolutePoints = [null, this.currentState != null ? null : current];
this.graph.view.updateFixedTerminalPoint(
this.edgeState,
this.previous,
true,
this.sourceConstraint
);
if (this.sourceConstraint) {
this.graph.view.updateFixedTerminalPoint(
this.edgeState,
this.previous,
true,
this.sourceConstraint
);
}
if (this.currentState != null) {
if (constraint == null) {
@ -1683,7 +1680,7 @@ class ConnectionHandler extends EventSource {
const addPoint =
this.waypoints != null ||
(this.mouseDownCounter > 1 &&
(dx > this.graph.tolerance || dy > this.graph.tolerance));
(dx > this.graph.getEventTolerance() || dy > this.graph.getEventTolerance()));
if (addPoint) {
if (this.waypoints == null) {
@ -1691,7 +1688,7 @@ class ConnectionHandler extends EventSource {
}
const { scale } = this.graph.view;
point = new point(
point = new Point(
this.graph.snap(me.getGraphX() / 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
* <getEdgeColor> and <getEdgeWidth>.
*/
drawPreview(): void {
this.updatePreview(this.error == null);
this.shape.redraw();
drawPreview() {
this.updatePreview(this.error === null);
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
* returned.
*/
updatePreview(valid: boolean): void {
this.shape.strokeWidth = this.getEdgeWidth(valid);
this.shape.stroke = this.getEdgeColor(valid);
updatePreview(valid: boolean) {
if (this.shape) {
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
* returned.
*/
getEdgeColor(valid: boolean): string {
getEdgeColor(valid: boolean) {
return valid ? VALID_COLOR : INVALID_COLOR;
}
@ -1889,7 +1888,7 @@ class ConnectionHandler extends EventSource {
* released.
*/
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
// the default parent to insert the edge
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
* contains the target vertex if one has been inserted.
*/
selectCells(edge: Cell, target: Cell): void {
selectCells(edge: Cell, target: Cell) {
this.graph.setSelectionCell(edge);
}
@ -2087,7 +2086,7 @@ class ConnectionHandler extends EventSource {
target: Cell,
style: string
): Cell {
if (this.factoryMethod == null) {
if (!this.factoryMethod) {
return this.graph.insertEdge(parent, id, 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.
*/
getAlignmentTolerance(evt: MouseEvent): number {
return this.graph.grid.isGridEnabled()
? this.graph.grid.gridSize / 2
: this.graph.grid.tolerance;
return this.graph.isGridEnabled()
? this.graph.getGridSize() / 2
: this.graph.getSnapTolerance();
}
/**

View File

@ -4,7 +4,12 @@ import InternalMouseEvent from '../event/InternalMouseEvent';
import ConnectionConstraint from './ConnectionConstraint';
import Rectangle from '../geometry/Rectangle';
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 CellArray from '../cell/datatypes/CellArray';
import EventObject from '../event/EventObject';
@ -13,28 +18,53 @@ import Dictionary from '../../util/Dictionary';
import Geometry from '../geometry/Geometry';
import Graph from '../Graph';
import ConnectionHandler from './ConnectionHandler';
import GraphCells from '../cell/GraphCells';
import GraphPorts from '../ports/GraphPorts';
import GraphEdge from '../cell/edge/GraphEdge';
class GraphConnections {
constructor(graph: Graph) {
this.graph = graph;
}
graph: Graph;
type PartialGraph = Pick<Graph, 'getView' | 'getModel' | 'fireEvent'>;
type PartialCells = Pick<GraphCells, 'setCellStyles' | 'isCellLocked'>;
type PartialPorts = Pick<GraphPorts, 'isPortsEnabled' | 'isPort' | 'getTerminalForPort'>;
type PartialEdge = Pick<
GraphEdge,
| '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
*****************************************************************************/
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.
*/
getOutlineConstraint(
point: Point,
terminalState: CellState,
me: InternalMouseEvent
): ConnectionConstraint | null {
if (terminalState.shape != null) {
const bounds = <Rectangle>this.graph.view.getPerimeterBounds(terminalState);
getOutlineConstraint(point: Point, terminalState: CellState, me: InternalMouseEvent) {
if (terminalState.shape) {
const bounds = <Rectangle>this.getView().getPerimeterBounds(terminalState);
const direction = terminalState.style.direction;
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
@ -51,7 +81,7 @@ class GraphConnections {
const cos = Math.cos(-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);
}
@ -61,16 +91,10 @@ class GraphConnections {
let dy = 0;
// LATER: Add flipping support for image shapes
if ((<Cell>terminalState.cell).isVertex()) {
if (terminalState.cell.isVertex()) {
let flipH = terminalState.style.flipH;
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) {
const tmp = flipH;
flipH = flipV;
@ -88,7 +112,7 @@ class GraphConnections {
}
}
point = new point(
point = new Point(
(point.x - bounds.x) * sx - dx + bounds.x,
(point.y - bounds.y) * sy - dy + bounds.y
);
@ -102,7 +126,7 @@ class GraphConnections {
? 0
: 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;
}
@ -115,11 +139,8 @@ class GraphConnections {
* @param terminal {@link mxCellState} that represents the terminal.
* @param source Boolean that specifies if the terminal is the source or target.
*/
getAllConnectionConstraints(
terminal: CellState,
source: boolean
): ConnectionConstraint[] | null {
if (terminal != null && terminal.shape != null && terminal.shape.stencil != null) {
getAllConnectionConstraints(terminal: CellState | null, source: boolean) {
if (terminal && terminal.shape && terminal.shape.stencil) {
return terminal.shape.stencil.constraints;
}
return null;
@ -135,19 +156,18 @@ class GraphConnections {
*/
getConnectionConstraint(
edge: CellState,
terminal: CellState | null = null,
terminal: CellState | null,
source: boolean = false
): ConnectionConstraint {
let point = null;
// @ts-ignore
const x = <string>edge.style[source ? 'exitX' : 'entryX'];
) {
let point: Point | null = null;
if (x != null) {
// @ts-ignore
const y = <string>edge.style[source ? 'exitY' : 'entryY'];
const x = edge.style[source ? 'exitX' : 'entryX'];
if (y != null) {
point = new point(parseFloat(x), parseFloat(y));
if (x !== undefined) {
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 dy = 0;
if (point != null) {
perimeter = getValue(edge.style, source ? 'exitPerimeter' : 'entryPerimeter', true);
if (point) {
perimeter = edge.style[source ? 'exitPerimeter' : 'entryPerimeter'];
// Add entry/exit offset
// @ts-ignore
dx = parseFloat(<string>edge.style[source ? 'exitDx' : 'entryDx']);
// @ts-ignore
dy = parseFloat(<string>edge.style[source ? 'exitDy' : 'entryDy']);
dx = edge.style[source ? 'exitDx' : 'entryDx'];
dy = edge.style[source ? 'exitDy' : 'entryDy'];
dx = Number.isFinite(dx) ? dx : 0;
dy = Number.isFinite(dy) ? dy : 0;
@ -187,12 +205,12 @@ class GraphConnections {
terminal: Cell,
source: boolean = false,
constraint: ConnectionConstraint | null = null
): void {
if (constraint != null) {
) {
if (constraint) {
this.getModel().beginUpdate();
try {
if (constraint == null || constraint.point == null) {
if (!constraint || !constraint.point) {
this.setCellStyles(source ? 'exitX' : 'entryX', null, new CellArray(edge));
this.setCellStyles(source ? 'exitY' : 'entryY', null, new CellArray(edge));
this.setCellStyles(source ? 'exitDx' : 'entryDx', null, new CellArray(edge));
@ -202,7 +220,7 @@ class GraphConnections {
null,
new CellArray(edge)
);
} else if (constraint.point != null) {
} else if (constraint.point) {
this.setCellStyles(
source ? 'exitX' : 'entryX',
constraint.point.x,
@ -257,17 +275,17 @@ class GraphConnections {
vertex: CellState,
constraint: ConnectionConstraint,
round: boolean = true
): Point {
let point = null;
) {
let point: Point | null = null;
if (vertex != null && constraint.point != null) {
const bounds = <Rectangle>this.graph.view.getPerimeterBounds(vertex);
const cx = new point(bounds.getCenterX(), bounds.getCenterY());
if (constraint.point) {
const bounds = <Rectangle>this.getView().getPerimeterBounds(vertex);
const cx = new Point(bounds.getCenterX(), bounds.getCenterY());
const direction = vertex.style.direction;
let r1 = 0;
// 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) {
r1 += 270;
} else if (direction === DIRECTION_WEST) {
@ -282,8 +300,8 @@ class GraphConnections {
}
}
const { scale } = this.view;
point = new point(
const { scale } = this.getView();
point = new Point(
bounds.x + constraint.point.x * bounds.width + <number>constraint.dx * 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 = this.graph.view.getPerimeterPoint(vertex, point, false);
point = this.getView().getPerimeterPoint(vertex, point, false);
} else {
r2 += r1;
if ((<Cell>vertex.cell).isVertex()) {
let flipH = vertex.style.flipH == 1;
let flipV = vertex.style.flipV == 1;
// 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 (vertex.cell.isVertex()) {
let flipH = vertex.style.flipH;
let flipV = vertex.style.flipV;
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
const temp = flipH;
@ -339,7 +351,7 @@ class GraphConnections {
}
// Generic rotation after projection on perimeter
if (r2 !== 0 && point != null) {
if (r2 !== 0 && point) {
const rad = toRadians(r2);
const cos = Math.cos(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.y = Math.round(point.y);
}
@ -371,7 +383,7 @@ class GraphConnections {
terminal: Cell,
source: boolean = false,
constraint: ConnectionConstraint | null = null
): Cell {
) {
this.getModel().beginUpdate();
try {
const previous = edge.getTerminal(source);
@ -410,52 +422,50 @@ class GraphConnections {
terminal: Cell,
source: boolean = false,
constraint: ConnectionConstraint | null = null
): void {
if (edge != null) {
this.getModel().beginUpdate();
try {
const previous = edge.getTerminal(source);
) {
this.getModel().beginUpdate();
try {
const previous = edge.getTerminal(source);
// Updates the constraint
this.setConnectionConstraint(edge, terminal, source, constraint);
// Updates the constraint
this.setConnectionConstraint(edge, terminal, source, constraint);
// 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.
if (this.isPortsEnabled()) {
let id = null;
// 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.
if (this.isPortsEnabled()) {
let id = null;
if (this.isPort(terminal)) {
id = terminal.getId();
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));
if (this.isPort(terminal)) {
id = terminal.getId();
terminal = <Cell>this.getTerminalForPort(terminal, source);
}
this.getModel().setTerminal(edge, terminal, source);
if (this.resetEdgesOnConnect) {
this.resetEdge(edge);
}
this.fireEvent(
new EventObject(
InternalEvent.CELL_CONNECTED,
'edge',
edge,
'terminal',
terminal,
'source',
source,
'previous',
previous
)
);
} finally {
this.getModel().endUpdate();
// 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);
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.
*/
disconnectGraph(cells: CellArray | null) {
if (cells != null) {
this.getModel().beginUpdate();
try {
const { scale } = this.view;
const tr = this.graph.view.translate;
disconnectGraph(cells: CellArray) {
this.getModel().beginUpdate();
try {
const { scale, translate: tr } = this.getView();
// Fast lookup for finding cells in array
const dict = new Dictionary();
// Fast lookup for finding cells in array
const dict = new Dictionary<Cell, boolean>();
for (let i = 0; i < cells.length; i += 1) {
dict.put(cells[i], true);
}
for (let i = 0; i < cells.length; i += 1) {
dict.put(cells[i], true);
}
for (const cell of cells) {
if (cell.isEdge()) {
let geo = <Geometry>cell.getGeometry();
for (const cell of cells) {
if (cell.isEdge()) {
let geo = cell.getGeometry();
if (geo != null) {
const state = this.graph.view.getState(cell);
const pstate = <CellState>this.graph.view.getState(cell.getParent());
if (geo) {
const state = this.getView().getState(cell);
const pstate = <CellState>this.getView().getState(cell.getParent());
if (state != null && pstate != null) {
geo = geo.clone();
if (state && pstate) {
geo = geo.clone();
// @ts-ignore
const dx = -pstate.origin.x;
// @ts-ignore
const dy = -pstate.origin.y;
const pts = <Point[]>state.absolutePoints;
const dx = -pstate.origin.x;
const dy = -pstate.origin.y;
const pts = state.absolutePoints;
let src = cell.getTerminal(true);
let src = cell.getTerminal(true);
if (src != null && this.isCellDisconnectable(cell, src, true)) {
while (src != null && !dict.get(src)) {
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);
}
if (src && this.isCellDisconnectable(cell, src, true)) {
while (src && !dict.get(src)) {
src = src.getParent();
}
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)) {
while (trg != null && !dict.get(trg)) {
trg = trg.getParent();
}
let trg = cell.getTerminal(false);
if (trg == null) {
const n = pts.length - 1;
if (trg && this.isCellDisconnectable(cell, trg, false)) {
while (trg && !dict.get(trg)) {
trg = trg.getParent();
}
if (!trg) {
const n = pts.length - 1;
const p = pts[n];
if (p) {
geo.setTerminalPoint(
new Point(
<number>(<Point>pts[n]).x / scale - tr.x + dx,
<number>(<Point>pts[n]).y / scale - tr.y + dy
),
new Point(p.x / scale - tr.x + dx, p.y / scale - tr.y + dy),
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
* returned.
*/
getConnections(cell: Cell, parent: Cell | null = null): CellArray {
getConnections(cell: Cell, parent: Cell | null = null) {
return this.getEdges(cell, parent, true, true, false);
}
@ -565,7 +568,7 @@ class GraphConnections {
*
* @param cell {@link mxCell} that should be constrained.
*/
isConstrainChild(cell: Cell): boolean {
isConstrainChild(cell: Cell) {
return (
this.isConstrainChildren() &&
!!cell.getParent() &&
@ -576,28 +579,28 @@ class GraphConnections {
/**
* Returns {@link constrainChildren}.
*/
isConstrainChildren(): boolean {
isConstrainChildren() {
return this.constrainChildren;
}
/**
* Sets {@link constrainChildren}.
*/
setConstrainChildren(value: boolean): void {
setConstrainChildren(value: boolean) {
this.constrainChildren = value;
}
/**
* Returns {@link constrainRelativeChildren}.
*/
isConstrainRelativeChildren(): boolean {
isConstrainRelativeChildren() {
return this.constrainRelativeChildren;
}
/**
* Sets {@link constrainRelativeChildren}.
*/
setConstrainRelativeChildren(value: boolean): void {
setConstrainRelativeChildren(value: boolean) {
this.constrainRelativeChildren = value;
}
@ -608,7 +611,7 @@ class GraphConnections {
/**
* Returns {@link disconnectOnMove} as a boolean.
*/
isDisconnectOnMove(): boolean {
isDisconnectOnMove() {
return this.disconnectOnMove;
}
@ -619,7 +622,7 @@ class GraphConnections {
* @param value Boolean indicating if edges should be disconnected
* when moved.
*/
setDisconnectOnMove(value: boolean): void {
setDisconnectOnMove(value: boolean) {
this.disconnectOnMove = value;
}
@ -637,21 +640,21 @@ class GraphConnections {
cell: Cell,
terminal: Cell | null = null,
source: boolean = false
): boolean {
) {
return this.isCellsDisconnectable() && !this.isCellLocked(cell);
}
/**
* Returns {@link cellsDisconnectable}.
*/
isCellsDisconnectable(): boolean {
isCellsDisconnectable() {
return this.cellsDisconnectable;
}
/**
* Sets {@link cellsDisconnectable}.
*/
setCellsDisconnectable(value: boolean): void {
setCellsDisconnectable(value: boolean) {
this.cellsDisconnectable = value;
}
@ -662,10 +665,12 @@ class GraphConnections {
*
* @param cell {@link mxCell} that represents a possible source or null.
*/
isValidSource(cell: Cell | null): boolean {
isValidSource(cell: Cell | null) {
return (
(cell == null && this.allowDanglingEdges) ||
(cell != null && (!cell.isEdge() || this.connectableEdges) && cell.isConnectable())
(cell == null && this.isAllowDanglingEdges()) ||
(cell != null &&
(!cell.isEdge() || this.isConnectableEdges()) &&
cell.isConnectable())
);
}
@ -699,14 +704,14 @@ class GraphConnections {
*
* @param connectable Boolean indicating if new connections should be allowed.
*/
setConnectable(connectable: boolean): void {
setConnectable(connectable: boolean) {
(<ConnectionHandler>this.connectionHandler).setEnabled(connectable);
}
/**
* Returns true if the {@link connectionHandler} is enabled.
*/
isConnectable(): boolean {
isConnectable() {
return (<ConnectionHandler>this.connectionHandler).isEnabled();
}
}

View File

@ -22,6 +22,7 @@ type PartialCells = Pick<
>;
type PartialClass = PartialGraph & PartialSelection & PartialEvents & PartialCells;
// @ts-ignore recursive reference error
class GraphEditing extends autoImplement<PartialClass>() {
/**
* Specifies the return value for {@link isCellEditable}.

View File

@ -7,7 +7,7 @@
import EventObject from './EventObject';
type EventListener = {
type EventListenerObject = {
funct: Function;
name: string;
};
@ -46,7 +46,7 @@ class EventSource {
* contains the event name followed by the respective listener for each
* registered listener.
*/
eventListeners: EventListener[] = [];
eventListeners: EventListenerObject[] = [];
/**
* Variable: eventsEnabled
@ -60,7 +60,7 @@ class EventSource {
*
* Optional source for events. Default is null.
*/
eventSource: EventSource | null;
eventSource: EventSource | EventTarget | null;
/**
* Function: isEventsEnabled
@ -94,7 +94,7 @@ class EventSource {
*
* Sets <eventSource>.
*/
setEventSource(value: EventSource | null) {
setEventSource(value: EventSource | EventTarget | null) {
this.eventSource = value;
}
@ -106,7 +106,7 @@ class EventSource {
*
* 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 });
}
@ -115,7 +115,7 @@ class EventSource {
*
* Removes all occurrences of the given listener from <eventListeners>.
*/
removeListener(funct: (...args: any[]) => any) {
removeListener(funct: Function) {
let i = 0;
while (i < this.eventListeners.length) {
@ -146,7 +146,7 @@ class EventSource {
* sender - Optional sender to be passed to the listener. Default value is
* the return value of <getEventSource>.
*/
fireEvent(evt: EventObject, sender: any = null) {
fireEvent(evt: EventObject, sender: EventSource | EventTarget | null = null) {
if (this.isEventsEnabled()) {
if (!evt) {
evt = new EventObject('');

View File

@ -32,6 +32,7 @@ import type GraphCells from '../cell/GraphCells';
import type GraphSelection from '../selection/GraphSelection';
import GraphEditing from '../editing/GraphEditing';
import GraphSnap from '../snap/GraphSnap';
import { MouseEventListener } from '../../types';
type PartialGraph = Pick<
Graph,
@ -59,21 +60,15 @@ type PartialClass = PartialGraph &
PartialSnap &
EventSource;
type MouseListener = {
mouseDown: Function;
mouseMove: Function;
mouseUp: Function;
};
// @ts-ignore recursive reference error
class GraphEvents extends autoImplement<PartialClass>() {
/**
* Holds the mouse event listeners. See {@link fireMouseEvent}.
*/
mouseListeners: MouseListener[] = [];
mouseListeners: MouseListenerSet[] = [];
// TODO: Document me!
lastTouchEvent: InternalMouseEvent | null = null;
lastTouchEvent: MouseEvent | null = null;
doubleClickCounter: number = 0;
lastTouchCell: Cell | null = null;
fireDoubleClick: boolean | null = null;
@ -82,8 +77,8 @@ class GraphEvents extends autoImplement<PartialClass>() {
lastMouseY: number | null = null;
isMouseTrigger: boolean | null = null;
ignoreMouseEvents: boolean | null = null;
mouseMoveRedirect: EventListener | null = null;
mouseUpRedirect: EventListener | null = null;
mouseMoveRedirect: MouseEventListener | null = null;
mouseUpRedirect: MouseEventListener | null = null;
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;
getClickTolerance = () => this.tolerance;
getEventTolerance = () => this.tolerance;
/*****************************************************************************
* Group: Event processing
@ -210,7 +205,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
*
* @param evt Mouseevent that represents the keystroke.
*/
escape(evt: InternalMouseEvent): void {
escape(evt: Event) {
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 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 });
this.fireEvent(mxe);
@ -364,7 +359,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
this.isEnabled() &&
!isConsumed(evt) &&
!mxe.isConsumed() &&
cell != null &&
cell &&
this.isCellEditable(cell) &&
!this.isEditing(cell)
) {
@ -379,7 +374,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
* @param me {@link mxMouseEvent} that represents the touch event.
* @param state Optional {@link CellState} that is associated with the event.
*/
tapAndHold(me: InternalMouseEvent): void {
tapAndHold(me: InternalMouseEvent) {
const evt = me.getEvent();
const mxe = new EventObject(
InternalEvent.TAP_AND_HOLD,
@ -434,8 +429,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
*
* @param listener Listener to be added to the graph event listeners.
*/
// addMouseListener(listener: { [key: string]: (sender: mxEventSource, me: mxMouseEvent) => void }): void;
addMouseListener(listener: MouseListener): void {
addMouseListener(listener: MouseListenerSet) {
this.mouseListeners.push(listener);
}
@ -444,8 +438,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
*
* @param listener Listener to be removed from the graph event listeners.
*/
// removeMouseListener(listener: { [key: string]: (sender: mxEventSource, me: mxMouseEvent) => void }): void;
removeMouseListener(listener: MouseListener) {
removeMouseListener(listener: MouseListenerSet) {
for (let i = 0; i < this.mouseListeners.length; i += 1) {
if (this.mouseListeners[i] === listener) {
this.mouseListeners.splice(i, 1);
@ -461,30 +454,28 @@ class GraphEvents extends autoImplement<PartialClass>() {
* @param me {@link mxMouseEvent} to be updated.
* @param evtName Name of the mouse event.
*/
updateMouseEvent(me: InternalMouseEvent, evtName: string): InternalMouseEvent {
if (me.graphX == null || me.graphY == null) {
const pt = convertPoint(this.getContainer(), me.getX(), me.getY());
updateMouseEvent(me: InternalMouseEvent, evtName: string) {
const pt = convertPoint(this.getContainer(), me.getX(), me.getY());
me.graphX = pt.x - this.panning.panDx;
me.graphY = pt.y - this.panning.panDy;
me.graphX = pt.x - this.panning.panDx;
me.graphY = pt.y - this.panning.panDy;
// Searches for rectangles using method if native hit detection is disabled on shape
if (
me.getCell() == null &&
this.isMouseDown &&
evtName === InternalEvent.MOUSE_MOVE
) {
me.state = this.getView().getState(
this.getCellAt(pt.x, pt.y, null, true, true, (state: CellState) => {
return (
state.shape == null ||
state.shape.paintBackground !== this.paintBackground ||
getValue(state.style, 'pointerEvents', '1') == '1' ||
(state.shape.fill != null && state.shape.fill !== NONE)
);
})
);
}
// Searches for rectangles using method if native hit detection is disabled on shape
if (
me.getCell() == null &&
this.isMouseDown &&
evtName === InternalEvent.MOUSE_MOVE
) {
me.state = this.getView().getState(
this.getCellAt(pt.x, pt.y, null, true, true, (state: CellState) => {
return (
state.shape == null ||
state.shape.paintBackground !== this.paintBackground ||
getValue(state.style, 'pointerEvents', '1') == '1' ||
(state.shape.fill != null && state.shape.fill !== NONE)
);
})
);
}
return me;
@ -493,8 +484,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
/**
* Returns the state for the given touch event.
*/
// getStateForTouchEvent(evt: MouseEvent | TouchEvent): mxCellState;
getStateForTouchEvent(evt: InternalMouseEvent) {
getStateForTouchEvent(evt: MouseEvent) {
const x = getClientX(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}.
*/
// isEventIgnored(evtName: string, me: mxMouseEvent, sender: mxEventSource): boolean;
isEventIgnored(evtName: string, me: InternalMouseEvent, sender: any): boolean {
isEventIgnored(evtName: string, me: InternalMouseEvent, sender: EventSource) {
const mouseEvent = isMouseEvent(me.getEvent());
let result = false;
@ -545,13 +534,13 @@ class GraphEvents extends autoImplement<PartialClass>() {
) {
this.setEventSource(me.getSource());
this.mouseMoveRedirect = (evt: InternalMouseEvent) => {
this.mouseMoveRedirect = (evt: MouseEvent) => {
this.fireMouseEvent(
InternalEvent.MOUSE_MOVE,
new InternalMouseEvent(evt, this.getStateForTouchEvent(evt))
);
};
this.mouseUpRedirect = (evt: InternalMouseEvent) => {
this.mouseUpRedirect = (evt: MouseEvent) => {
this.fireMouseEvent(
InternalEvent.MOUSE_UP,
new InternalMouseEvent(evt, this.getStateForTouchEvent(evt))
@ -675,11 +664,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
* @param me {@link mxMouseEvent} to be fired.
* @param sender Optional sender argument. Default is `this`.
*/
fireMouseEvent(
evtName: string,
me: InternalMouseEvent,
sender: EventSource = this
): void {
fireMouseEvent(evtName: string, me: InternalMouseEvent, sender: EventSource = this) {
if (this.isEventSourceIgnored(evtName, me)) {
if (this.tooltipHandler != null) {
this.tooltipHandler.hide();
@ -687,10 +672,6 @@ class GraphEvents extends autoImplement<PartialClass>() {
return;
}
if (sender == null) {
sender = this;
}
// Updates the graph coordinates in the event
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 control is pressed.
*/
isCloneEvent(evt: MouseEvent): boolean {
isCloneEvent(evt: MouseEvent) {
return isControlDown(evt);
}
@ -1001,7 +982,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
* returns true the cell behind the selected cell will be selected. This
* implementation returns false;
*/
isTransparentClickEvent(evt: MouseEvent): boolean {
isTransparentClickEvent(evt: MouseEvent) {
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
* pressed on any other platform.
*/
isToggleEvent(evt: MouseEvent): boolean {
isToggleEvent(evt: MouseEvent) {
return mxClient.IS_MAC ? isMetaDown(evt) : isControlDown(evt);
}
/**
* Returns true if the given mouse event should be aligned to the grid.
*/
isGridEnabledEvent(evt: MouseEvent): boolean {
return evt != null && !isAltDown(evt);
isGridEnabledEvent(evt: MouseEvent) {
return !isAltDown(evt);
}
/**
* Returns true if the given mouse event should be aligned to the grid.
*/
isConstrainedEvent(evt: MouseEvent): boolean {
isConstrainedEvent(evt: MouseEvent) {
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
* made. This implementation returns false.
*/
isIgnoreTerminalEvent(evt: MouseEvent): boolean {
isIgnoreTerminalEvent(evt: MouseEvent) {
return false;
}
@ -1044,7 +1025,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
* @param addOffset Optional boolean that specifies if the position should be
* 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 s = this.getView().scale;
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.y = this.snap(p.y / s - tr.y - off);
return p;
}

View File

@ -9,7 +9,7 @@ import mxClient from '../../mxClient';
import { isConsumed, isMouseEvent } from '../../util/EventUtils';
import graph from '../Graph';
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
// 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
* to a given execution scope.
*/
// static addListener(element: Node | Window, eventName: string, funct: Function): void;
static addListener(element: Listenable, eventName: string, funct: EventListener) {
static addListener(element: Listenable, eventName: string, funct: MouseEventListener) {
element.addEventListener(
eventName,
funct,
funct as EventListener,
supportsPassive ? { passive: false } : false
);
@ -69,9 +68,12 @@ class InternalEvent {
/**
* Removes the specified listener from the given element.
*/
// static removeListener(element: Node | Window, eventName: string, funct: Function): void;
static removeListener(element: Listenable, eventName: string, funct: EventListener) {
element.removeEventListener(eventName, funct, false);
static removeListener(
element: Listenable,
eventName: string,
funct: MouseEventListener
) {
element.removeEventListener(eventName, funct as EventListener, false);
if (element.mxListenerList) {
const listenerCount = element.mxListenerList.length;
@ -90,7 +92,6 @@ class InternalEvent {
/**
* Removes all listeners from the given element.
*/
// static removeAllListeners(element: Node | Window): void;
static removeAllListeners(element: Listenable) {
const list = element.mxListenerList;
@ -112,10 +113,10 @@ class InternalEvent {
* will be registered as well as the mouse events.
*/
static addGestureListeners(
node: Listenable,
startListener: EventListener | null = null,
moveListener: EventListener | null = null,
endListener: EventListener | null = null
node: EventSource | EventTarget,
startListener: MouseEventListener | null = null,
moveListener: MouseEventListener | null = null,
endListener: MouseEventListener | null = null
) {
if (startListener) {
InternalEvent.addListener(
@ -164,9 +165,9 @@ class InternalEvent {
*/
static removeGestureListeners(
node: Listenable,
startListener: EventListener | null,
moveListener: EventListener | null,
endListener: EventListener | null
startListener: MouseEventListener | null,
moveListener: MouseEventListener | null,
endListener: MouseEventListener | null
) {
if (startListener) {
InternalEvent.removeListener(
@ -221,10 +222,10 @@ class InternalEvent {
node: Listenable,
graph: graph,
state: CellState | ((evt: Event) => CellState) | null = null,
down: EventListener | null = null,
move: EventListener | null = null,
up: EventListener | null = null,
dblClick: EventListener | null = null
down: MouseEventListener | null = null,
move: MouseEventListener | null = null,
up: MouseEventListener | null = null,
dblClick: MouseEventListener | null = null
) {
const getState = (evt: Event) => {
return typeof state === 'function' ? state(evt) : state;
@ -238,7 +239,7 @@ class InternalEvent {
} else if (!isConsumed(evt)) {
graph.fireMouseEvent(
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)) {
graph.fireMouseEvent(
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)) {
graph.fireMouseEvent(
InternalEvent.MOUSE_UP,
new InternalMouseEvent(evt as MouseEvent, getState(evt))
new InternalMouseEvent(evt, getState(evt))
);
}
}
@ -269,7 +270,7 @@ class InternalEvent {
dblClick(evt);
} else if (!isConsumed(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.
*/
static release(element: Listenable | null) {
static release(element: Listenable) {
try {
if (element) {
InternalEvent.removeAllListeners(element);
InternalEvent.removeAllListeners(element);
if ('childNodes' in element) {
const children = element.childNodes;
const childCount = children.length;
for (let i = 0; i < childCount; i += 1) {
InternalEvent.release(children[i]);
}
// @ts-ignore
const children = element.childNodes;
if (children !== undefined) {
const childCount = children.length;
for (let i = 0; i < childCount; i += 1) {
InternalEvent.release(children[i]);
}
}
} catch (e) {

View File

@ -66,6 +66,8 @@ class GraphFolding extends autoImplement<PartialClass>() {
getCollapseExpandResource = () => this.collapseExpandResource;
isFoldingEnabled = () => this.options.foldingEnabled;
/**
*
* @default true
@ -133,7 +135,7 @@ class GraphFolding extends autoImplement<PartialClass>() {
recurse: boolean = false,
cells: CellArray | null = null,
checkFoldable: boolean = false,
evt: EventObject | null = null
evt: Event | null = null
): CellArray | null {
if (cells == null) {
cells = this.getFoldableCells(this.getSelectionCells(), collapse);

View File

@ -326,6 +326,8 @@ class Shape {
image: ImageBox | null = null;
imageSrc: string | null = null;
indicatorColor: ColorValue = NONE;
indicatorStrokeColor: ColorValue = NONE;
@ -334,6 +336,8 @@ class Shape {
indicatorDirection: DirectionValue = DIRECTION_EAST;
indicatorImageSrc: string | null = null;
/**
* Function: isHtmlAllowed
*

View File

@ -63,7 +63,7 @@ import SvgCanvas2D from 'packages/core/src/util/canvas/SvgCanvas2D';
*/
class TextShape extends Shape {
constructor(
value: string,
value: string | HTMLElement | SVGGElement,
bounds: Rectangle,
align: AlignValue = ALIGN_CENTER,
valign: VAlignValue = ALIGN_MIDDLE,
@ -112,8 +112,7 @@ class TextShape extends Shape {
this.updateMargin();
}
// TODO: Document me!
value: string | HTMLElement | SVGGElement | null;
value: string | HTMLElement | SVGGElement;
bounds: Rectangle;
align: AlignValue;
valign: VAlignValue;

View File

@ -61,7 +61,7 @@ class GraphLabel extends autoImplement<PartialClass>() {
*
* @param cell {@link mxCell} whose label should be returned.
*/
getLabel(cell: Cell): string | Node | null {
getLabel(cell: Cell) {
let result: string | null = '';
if (this.isLabelsVisible() && cell != null) {
@ -71,6 +71,7 @@ class GraphLabel extends autoImplement<PartialClass>() {
result = this.convertValueToString(cell);
}
}
return result;
}

View File

@ -1,23 +1,19 @@
import {hasScrollbars} from "../../util/Utils";
import EventObject from "../event/EventObject";
import InternalEvent from "../event/InternalEvent";
import PanningManager from './PanningManager';
import PanningHandler from "./PanningHandler";
import Graph from "../Graph";
import Cell from "../cell/datatypes/Cell";
import Rectangle from "../geometry/Rectangle";
import Point from "../geometry/Point";
import { autoImplement, hasScrollbars } from '../../util/Utils';
import EventObject from '../event/EventObject';
import InternalEvent from '../event/InternalEvent';
import PanningHandler from './PanningHandler';
import Graph from '../Graph';
import Cell from '../cell/datatypes/Cell';
import Rectangle from '../geometry/Rectangle';
import Point from '../geometry/Point';
import GraphEvents from '../event/GraphEvents';
import SelectionCellsHandler from '../selection/SelectionCellsHandler';
class GraphPanning {
constructor(graph: Graph) {
this.graph = graph;
this.createHandlers()
}
type PartialGraph = Pick<Graph, 'getContainer' | 'getView' | 'getPlugin'>;
type PartialEvents = Pick<GraphEvents, 'fireEvent'>;
type PartialClass = PartialGraph & PartialEvents;
graph: Graph;
panningHandler: PanningHandler | null = null;
panningManager: PanningManager | null = null;
class GraphPanning extends autoImplement<PartialClass>() {
shiftPreview1: HTMLElement | null = null;
shiftPreview2: HTMLElement | null = null;
@ -28,7 +24,9 @@ class GraphPanning {
* then no panning occurs if this is `true`.
* @default true
*/
useScrollbarsForPanning: boolean = true;
useScrollbarsForPanning = true;
isUseScrollbarsForPanning = () => this.useScrollbarsForPanning;
/**
* Specifies if autoscrolling should be carried out via mxPanningManager even
@ -38,7 +36,7 @@ class GraphPanning {
* are visible and scrollable in all directions.
* @default false
*/
timerAutoScroll: boolean = false;
timerAutoScroll = false;
/**
* Specifies if panning via {@link panGraph} should be allowed to implement autoscroll
@ -47,38 +45,25 @@ class GraphPanning {
* positive value.
* @default false
*/
allowAutoPanning: boolean = false;
allowAutoPanning = false;
/**
* Current horizontal panning value.
* @default 0
*/
panDx: number = 0;
panDx = 0;
getPanDx = () => this.panDx;
setPanDx = (dx: number) => (this.panDx = dx);
/**
* Current vertical panning value.
* @default 0
*/
panDy: number = 0;
panDy = 0;
createHandlers() {
this.panningHandler = this.createPanningHandler();
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);
}
getPanDy = () => this.panDy;
setPanDy = (dy: number) => (this.panDy = dy);
/**
* 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 dy Amount to shift the graph along the y-axis.
*/
panGraph(dx: number, dy: number): void {
const container = <HTMLElement>this.graph.container;
panGraph(dx: number, dy: number) {
const container = this.getContainer();
if (this.useScrollbarsForPanning && hasScrollbars(container)) {
container.scrollLeft = -dx;
container.scrollTop = -dy;
} else {
const canvas = <SVGElement>this.graph.view.getCanvas();
const canvas = this.getView().getCanvas();
// Puts everything inside the container in a DIV so that it
// can be moved without changing the state of the container
if (dx === 0 && dy === 0) {
canvas.removeAttribute('transform');
if (this.shiftPreview1 != null) {
if (this.shiftPreview1) {
let child = this.shiftPreview1.firstChild;
while (child != null) {
while (child) {
const next = child.nextSibling;
container.appendChild(child);
child = next;
}
if (this.shiftPreview1.parentNode != null) {
if (this.shiftPreview1.parentNode) {
this.shiftPreview1.parentNode.removeChild(this.shiftPreview1);
}
@ -121,13 +106,13 @@ class GraphPanning {
const shiftPreview2 = <HTMLElement>this.shiftPreview2;
child = shiftPreview2.firstChild;
while (child != null) {
while (child) {
const next = child.nextSibling;
container.appendChild(child);
child = next;
}
if (shiftPreview2.parentNode != null) {
if (shiftPreview2.parentNode) {
shiftPreview2.parentNode.removeChild(shiftPreview2);
}
this.shiftPreview2 = null;
@ -135,7 +120,7 @@ class GraphPanning {
} else {
canvas.setAttribute('transform', `translate(${dx},${dy})`);
if (this.shiftPreview1 == null) {
if (!this.shiftPreview1) {
// Needs two divs for stuff before and after the SVG element
this.shiftPreview1 = document.createElement('div');
this.shiftPreview1.style.position = 'absolute';
@ -148,7 +133,7 @@ class GraphPanning {
let current = this.shiftPreview1;
let child = container.firstChild;
while (child != null) {
while (child) {
const next = child.nextSibling;
// SVG element is moved via transform attribute
@ -163,11 +148,11 @@ class GraphPanning {
}
// Inserts elements only if not empty
if (this.shiftPreview1.firstChild != null) {
if (this.shiftPreview1.firstChild) {
container.insertBefore(this.shiftPreview1, canvas.parentNode);
}
if (this.shiftPreview2.firstChild != null) {
if (this.shiftPreview2.firstChild) {
container.appendChild(this.shiftPreview2);
}
}
@ -184,7 +169,7 @@ class GraphPanning {
this.panDx = dx;
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 center Optional boolean flag. Default is `false`.
*/
scrollCellToVisible(cell: Cell, center: boolean = false): void {
const x = -this.graph.view.translate.x;
const y = -this.graph.view.translate.y;
scrollCellToVisible(cell: Cell, center = false) {
const x = -this.getView().translate.x;
const y = -this.getView().translate.y;
const state = this.graph.view.getState(cell);
const state = this.getView().getState(cell);
if (state != null) {
const bounds = new Rectangle(
x + state.x,
y + state.y,
state.width,
state.height
);
if (state) {
const bounds = new Rectangle(x + state.x, y + state.y, state.width, state.height);
if (center && this.graph.container != null) {
const w = this.graph.container.clientWidth;
const h = this.graph.container.clientHeight;
if (center && this.getContainer()) {
const w = this.getContainer().clientWidth;
const h = this.getContainer().clientHeight;
bounds.x = bounds.getCenterX() - w / 2;
bounds.width = w;
@ -227,20 +207,14 @@ class GraphPanning {
bounds.height = h;
}
const tr = new Point(
this.graph.view.translate.x,
this.graph.view.translate.y
);
const tr = new Point(this.getView().translate.x, this.getView().translate.y);
if (this.scrollRectToVisible(bounds)) {
// Triggers an update via the view's event source
const tr2 = new Point(
this.graph.view.translate.x,
this.graph.view.translate.y
);
this.graph.view.translate.x = tr.x;
this.graph.view.translate.y = tr.y;
this.graph.view.setTranslate(tr2.x, tr2.y);
const tr2 = new Point(this.getView().translate.x, this.getView().translate.y);
this.getView().translate.x = tr.x;
this.getView().translate.y = tr.y;
this.getView().setTranslate(tr2.x, tr2.y);
}
}
}
@ -250,84 +224,84 @@ class GraphPanning {
*
* @param rect {@link mxRectangle} to be made visible.
*/
scrollRectToVisible(rect: Rectangle): boolean {
scrollRectToVisible(rect: Rectangle) {
let isChanged = false;
if (rect != null) {
const container = <HTMLElement>this.graph.container;
const w = container.offsetWidth;
const h = container.offsetHeight;
const container = <HTMLElement>this.getContainer();
const w = container.offsetWidth;
const h = container.offsetHeight;
const widthLimit = Math.min(w, rect.width);
const heightLimit = Math.min(h, rect.height);
const widthLimit = Math.min(w, rect.width);
const heightLimit = Math.min(h, rect.height);
if (hasScrollbars(container)) {
rect.x += this.graph.view.translate.x;
rect.y += this.graph.view.translate.y;
let dx = container.scrollLeft - rect.x;
const ddx = Math.max(dx - container.scrollLeft, 0);
if (hasScrollbars(container)) {
rect.x += this.getView().translate.x;
rect.y += this.getView().translate.y;
let dx = container.scrollLeft - rect.x;
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) {
container.scrollLeft -= dx + 2;
} else {
dx =
rect.x + widthLimit - container.scrollLeft - container.clientWidth;
if (dx > 0) {
container.scrollLeft += dx + 2;
}
container.scrollLeft += dx + 2;
}
}
let dy = container.scrollTop - rect.y;
const ddy = Math.max(0, dy - container.scrollTop);
let dy = container.scrollTop - rect.y;
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) {
container.scrollTop -= dy + 2;
} else {
dy =
rect.y + heightLimit - container.scrollTop - container.clientHeight;
if (dy > 0) {
container.scrollTop += dy + 2;
}
container.scrollTop += dy + 2;
}
}
if (!this.useScrollbarsForPanning && (ddx != 0 || ddy != 0)) {
this.graph.view.setTranslate(ddx, ddy);
}
} else {
const x = -this.graph.view.translate.x;
const y = -this.graph.view.translate.y;
if (!this.useScrollbarsForPanning && (ddx != 0 || ddy != 0)) {
this.getView().setTranslate(ddx, ddy);
}
} else {
const x = -this.getView().translate.x;
const y = -this.getView().translate.y;
const s = this.graph.view.scale;
const s = this.getView().scale;
if (rect.x + widthLimit > x + w) {
this.graph.view.translate.x -= (rect.x + widthLimit - w - x) / s;
isChanged = true;
}
if (rect.x + widthLimit > x + w) {
this.getView().translate.x -= (rect.x + widthLimit - w - x) / s;
isChanged = true;
}
if (rect.y + heightLimit > y + h) {
this.graph.view.translate.y -= (rect.y + heightLimit - h - y) / s;
isChanged = true;
}
if (rect.y + heightLimit > y + h) {
this.getView().translate.y -= (rect.y + heightLimit - h - y) / s;
isChanged = true;
}
if (rect.x < x) {
this.graph.view.translate.x += (x - rect.x) / s;
isChanged = true;
}
if (rect.x < x) {
this.getView().translate.x += (x - rect.x) / s;
isChanged = true;
}
if (rect.y < y) {
this.graph.view.translate.y += (y - rect.y) / s;
isChanged = true;
}
if (rect.y < y) {
this.getView().translate.y += (y - rect.y) / s;
isChanged = true;
}
if (isChanged) {
this.graph.view.refresh();
if (isChanged) {
this.getView().refresh();
// Repaints selection marker (ticket 18)
if (this.selectionCellsHandler != null) {
this.selectionCellsHandler.refresh();
}
const selectionCellsHandler = this.getPlugin(
'SelectionCellsHandler'
) 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.
*/
setPanning(enabled: boolean): void {
(<PanningHandler>this.panningHandler).panningEnabled = enabled;
}
setPanning(enabled: boolean) {
const panningHandler = this.getPlugin('PanningHandler') as PanningHandler;
if (panningHandler) panningHandler.panningEnabled = enabled;
}
}
export default GraphPanning;

View File

@ -5,10 +5,22 @@
* Type definitions from the typed-mxgraph project
*/
import EventSource from '../event/EventSource';
import utils, { hasScrollbars } from '../../util/Utils';
import { hasScrollbars } from '../../util/Utils';
import EventObject from '../event/EventObject';
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
@ -39,66 +51,65 @@ import { isConsumed, isControlDown, isLeftMouseButton, isMultiTouchEvent, isPopu
* Fires when the panning handler changes its <active> state to false. The
* <code>event</code> property contains the corresponding <mxMouseEvent>.
*/
class PanningHandler extends EventSource {
constructor(graph) {
class PanningHandler extends EventSource implements GraphPlugin {
static pluginId = 'PanningHandler';
constructor(graph: MaxGraph) {
super();
if (graph != null) {
this.graph = graph;
this.graph.addMouseListener(this);
this.graph = graph;
this.graph.addMouseListener(this);
// Handles force panning event
this.forcePanningHandler = (sender, evt) => {
const evtName = evt.getProperty('eventName');
const me = evt.getProperty('event');
// Handles force panning event
this.forcePanningHandler = (sender: EventSource, eo: EventObject) => {
const evtName = eo.getProperty('eventName');
const me = eo.getProperty('event');
if (evtName === InternalEvent.MOUSE_DOWN && this.isForcePanningEvent(me)) {
this.start(me);
this.active = true;
this.fireEvent(new EventObject(InternalEvent.PAN_START, 'event', me));
me.consume();
}
};
if (evtName === InternalEvent.MOUSE_DOWN && this.isForcePanningEvent(me)) {
this.start(me);
this.active = true;
this.fireEvent(new EventObject(InternalEvent.PAN_START, 'event', me));
me.consume();
}
};
this.graph.addListener(
InternalEvent.FIRE_MOUSE_EVENT,
this.forcePanningHandler
);
this.graph.addListener(InternalEvent.FIRE_MOUSE_EVENT, this.forcePanningHandler);
// Handles pinch gestures
this.gestureHandler = (sender, eo) => {
if (this.isPinchEnabled()) {
const evt = eo.getProperty('event');
// Handles pinch gestures
this.gestureHandler = (sender: EventSource, eo: EventObject) => {
if (this.isPinchEnabled()) {
const evt = eo.getProperty('event');
if (!isConsumed(evt) && evt.type === 'gesturestart') {
this.initialScale = this.graph.view.scale;
if (!isConsumed(evt) && evt.type === 'gesturestart') {
this.initialScale = this.graph.view.scale;
// Forces start of panning when pinch gesture starts
if (!this.active && this.mouseDownEvent != null) {
this.start(this.mouseDownEvent);
this.mouseDownEvent = null;
}
} else if (evt.type === 'gestureend' && this.initialScale != null) {
this.initialScale = null;
}
if (this.initialScale != null) {
this.zoomGraph(evt);
// Forces start of panning when pinch gesture starts
if (!this.active && this.mouseDownEvent) {
this.start(this.mouseDownEvent);
this.mouseDownEvent = null;
}
} else if (evt.type === 'gestureend' && this.initialScale !== 0) {
this.initialScale = 0;
}
};
this.graph.addListener(InternalEvent.GESTURE, this.gestureHandler);
this.mouseUpListener = () => {
if (this.active) {
this.reset();
if (this.initialScale !== 0) {
this.zoomGraph(evt);
}
};
}
};
// Stops scrolling on every mouseup anywhere in the document
InternalEvent.addListener(document, 'mouseup', this.mouseUpListener);
}
this.graph.addListener(InternalEvent.GESTURE, this.gestureHandler);
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>.
*/
// graph: mxGraph;
graph = null;
graph: MaxGraph;
panningManager: PanningManager;
/**
* Variable: useLeftButtonForPanning
@ -115,7 +127,6 @@ class PanningHandler extends EventSource {
* Specifies if panning should be active for the left mouse button.
* Setting this to true may conflict with <mxRubberband>. Default is false.
*/
// useLeftButtonForPanning: boolean;
useLeftButtonForPanning = false;
/**
@ -123,7 +134,6 @@ class PanningHandler extends EventSource {
*
* Specifies if <mxEvent.isPopupTrigger> should also be used for panning.
*/
// usePopupTrigger: boolean;
usePopupTrigger = true;
/**
@ -132,7 +142,6 @@ class PanningHandler extends EventSource {
* Specifies if panning should be active even if there is a cell under the
* mousepointer. Default is false.
*/
// ignoreCell: boolean;
ignoreCell = false;
/**
@ -140,7 +149,6 @@ class PanningHandler extends EventSource {
*
* Specifies if the panning should be previewed. Default is true.
*/
// previewEnabled: boolean;
previewEnabled = true;
/**
@ -149,7 +157,6 @@ class PanningHandler extends EventSource {
* Specifies if the panning steps should be aligned to the grid size.
* Default is false.
*/
// useGrid: boolean;
useGrid = false;
/**
@ -157,7 +164,6 @@ class PanningHandler extends EventSource {
*
* Specifies if panning should be enabled. Default is true.
*/
// panningEnabled: boolean;
panningEnabled = true;
/**
@ -165,15 +171,15 @@ class PanningHandler extends EventSource {
*
* Specifies if pinch gestures should be handled as zoom. Default is true.
*/
// pinchEnabled: boolean;
pinchEnabled = true;
initialScale = 0;
/**
* Variable: maxScale
*
* Specifies the maximum scale. Default is 8.
*/
// maxScale: number;
maxScale = 8;
/**
@ -181,7 +187,6 @@ class PanningHandler extends EventSource {
*
* Specifies the minimum scale. Default is 0.01.
*/
// minScale: number;
minScale = 0.01;
/**
@ -189,23 +194,20 @@ class PanningHandler extends EventSource {
*
* Holds the current horizontal offset.
*/
// dx: number;
dx = null;
dx = 0;
/**
* Variable: dy
*
* Holds the current vertical offset.
*/
// dy: number;
dy = null;
dy = 0;
/**
* Variable: startX
*
* Holds the x-coordinate of the start point.
*/
// startX: number;
startX = 0;
/**
@ -213,17 +215,29 @@ class PanningHandler extends EventSource {
*
* Holds the y-coordinate of the start point.
*/
// startY: number;
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
*
* Returns true if the handler is currently active.
*/
// isActive(): boolean;
isActive() {
return this.active || this.initialScale != null;
return this.active || this.initialScale !== null;
}
/**
@ -231,7 +245,6 @@ class PanningHandler extends EventSource {
*
* Returns <panningEnabled>.
*/
// isPanningEnabled(): boolean;
isPanningEnabled() {
return this.panningEnabled;
}
@ -241,8 +254,7 @@ class PanningHandler extends EventSource {
*
* Sets <panningEnabled>.
*/
// setPanningEnabled(value: boolean): void;
setPanningEnabled(value) {
setPanningEnabled(value: boolean) {
this.panningEnabled = value;
}
@ -251,7 +263,6 @@ class PanningHandler extends EventSource {
*
* Returns <pinchEnabled>.
*/
// isPinchEnabled(): boolean;
isPinchEnabled() {
return this.pinchEnabled;
}
@ -261,8 +272,7 @@ class PanningHandler extends EventSource {
*
* Sets <pinchEnabled>.
*/
// setPinchEnabled(value: boolean): void;
setPinchEnabled(value) {
setPinchEnabled(value: boolean) {
this.pinchEnabled = value;
}
@ -273,14 +283,11 @@ class PanningHandler extends EventSource {
* given cell. This returns true if control-shift is pressed or if
* <usePopupTrigger> is true and the event is a popup trigger.
*/
// isPanningTrigger(me: mxMouseEvent): boolean;
isPanningTrigger(me) {
isPanningTrigger(me: InternalMouseEvent) {
const evt = me.getEvent();
return (
(this.useLeftButtonForPanning &&
me.getState() == null &&
isLeftMouseButton(evt)) ||
(this.useLeftButtonForPanning && !me.getState() && isLeftMouseButton(evt)) ||
(isControlDown(evt) && isShiftDown(evt)) ||
(this.usePopupTrigger && isPopupTrigger(evt))
);
@ -293,8 +300,7 @@ class PanningHandler extends EventSource {
* implementation always returns true if <ignoreCell> is true or for
* multi touch events.
*/
// isForcePanningEvent(me: mxMouseEvent): boolean;
isForcePanningEvent(me) {
isForcePanningEvent(me: InternalMouseEvent) {
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
* subsequent events of the gesture are redirected to this handler.
*/
// mouseDown(sender: any, me: mxMouseEvent): void;
mouseDown(sender, me) {
mouseDown(sender: EventSource, me: InternalMouseEvent) {
this.mouseDownEvent = me;
if (
@ -324,16 +329,15 @@ class PanningHandler extends EventSource {
*
* Starts panning at the given event.
*/
// start(me: mxMouseEvent): void;
start(me) {
start(me: InternalMouseEvent) {
this.dx0 = -this.graph.container.scrollLeft;
this.dy0 = -this.graph.container.scrollTop;
// Stores the location of the trigger event
this.startX = me.getX();
this.startY = me.getY();
this.dx = null;
this.dy = null;
this.dx = 0;
this.dy = 0;
this.panningTrigger = true;
}
@ -366,8 +370,7 @@ class PanningHandler extends EventSource {
* };
* (end)
*/
// consumePanningTrigger(me: mxMouseEvent): void;
consumePanningTrigger(me) {
consumePanningTrigger(me: InternalMouseEvent) {
me.consume();
}
@ -376,8 +379,7 @@ class PanningHandler extends EventSource {
*
* Handles the event by updating the panning on the graph.
*/
// mouseMove(sender: any, me: mxMouseEvent): void;
mouseMove(sender, me) {
mouseMove(sender: EventSource, me: InternalMouseEvent) {
this.dx = me.getX() - this.startX;
this.dy = me.getY() - this.startY;
@ -399,8 +401,8 @@ class PanningHandler extends EventSource {
// Panning is activated only if the mouse is moved
// beyond the graph tolerance
this.active =
Math.abs(this.dx) > this.graph.tolerance ||
Math.abs(this.dy) > this.graph.tolerance;
Math.abs(this.dx) > this.graph.getSnapTolerance() ||
Math.abs(this.dy) > this.graph.getSnapTolerance();
if (!tmp && this.active) {
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
* popupmenu.
*/
// mouseUp(sender: any, me: mxMouseEvent): void;
mouseUp(sender, me) {
mouseUp(sender: EventSource, me: InternalMouseEvent) {
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
if (
!this.graph.useScrollbarsForPanning ||
!this.graph.isUseScrollbarsForPanning() ||
!hasScrollbars(this.graph.container)
) {
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.
*/
zoomGraph(evt) {
zoomGraph(evt: Event) {
// @ts-ignore evt may have scale property
let value = Math.round(this.initialScale * evt.scale * 100) / 100;
if (this.minScale != null) {
value = Math.max(this.minScale, value);
}
if (this.maxScale != null) {
value = Math.min(this.maxScale, value);
}
value = Math.max(this.minScale, value);
value = Math.min(this.maxScale, value);
if (this.graph.view.scale !== 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
* popupmenu.
*/
// reset(): void;
reset() {
this.panningTrigger = false;
this.mouseDownEvent = null;
this.active = false;
this.dx = null;
this.dy = null;
this.dx = 0;
this.dy = 0;
}
/**
@ -484,8 +479,7 @@ class PanningHandler extends EventSource {
*
* Pans <graph> by the given amount.
*/
// panGraph(dx: number, dy: number): void;
panGraph(dx, dy) {
panGraph(dx: number, dy: number) {
this.graph.getView().setTranslate(dx, dy);
}
@ -494,8 +488,7 @@ class PanningHandler extends EventSource {
*
* Destroys the handler and all its resources and DOM nodes.
*/
// destroy(): void;
destroy() {
onDestroy() {
this.graph.removeMouseListener(this);
this.graph.removeListener(this.forcePanningHandler);
this.graph.removeListener(this.gestureHandler);

View File

@ -5,8 +5,11 @@
* Type definitions from the typed-mxgraph project
*/
import { MouseEventListener, MouseListenerSet } from '../../types';
import { hasScrollbars } from '../../util/Utils';
import EventObject from '../event/EventObject';
import InternalEvent from '../event/InternalEvent';
import { MaxGraph } from '../Graph';
/**
* Class: mxPanningManager
@ -14,7 +17,7 @@ import EventObject from '../event/EventObject';
* Implements a handler for panning.
*/
class PanningManager {
constructor(graph) {
constructor(graph: MaxGraph) {
this.thread = null;
this.active = false;
this.tdx = 0;
@ -46,7 +49,7 @@ class PanningManager {
};
// Stops scrolling on every mouseup anywhere in the document
mxEvent.addListener(document, 'mouseup', this.mouseUpListener);
InternalEvent.addListener(document, 'mouseup', this.mouseUpListener);
const createThread = () => {
this.scrollbars = hasScrollbars(graph.container);
@ -61,9 +64,9 @@ class PanningManager {
const left = -graph.container.scrollLeft - Math.ceil(this.dx);
const top = -graph.container.scrollTop - Math.ceil(this.dy);
graph.panGraph(left, top);
graph.panDx = this.scrollLeft - graph.container.scrollLeft;
graph.panDy = this.scrollTop - graph.container.scrollTop;
graph.fireEvent(new EventObject(mxEvent.PAN));
graph.setPanDx(this.scrollLeft - graph.container.scrollLeft);
graph.setPanDy(this.scrollTop - graph.container.scrollTop);
graph.fireEvent(new EventObject(InternalEvent.PAN));
// TODO: Implement graph.autoExtend
} else {
graph.panGraph(this.getDx(), this.getDy());
@ -171,8 +174,8 @@ class PanningManager {
this.tdy = 0;
if (!this.scrollbars) {
const px = graph.panDx;
const py = graph.panDy;
const px = graph.getPanDx();
const py = graph.getPanDy();
if (px != 0 || py != 0) {
graph.panGraph(0, 0);
@ -182,16 +185,16 @@ class PanningManager {
);
}
} else {
graph.panDx = 0;
graph.panDy = 0;
graph.fireEvent(new EventObject(mxEvent.PAN));
graph.setPanDx(0);
graph.setPanDy(0);
graph.fireEvent(new EventObject(InternalEvent.PAN));
}
}
};
this.destroy = () => {
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: number;
damper = 1 / 6;
/**
@ -208,7 +210,6 @@ class PanningManager {
*
* Delay in milliseconds for the panning. Default is 10.
*/
// delay: number;
delay = 10;
/**
@ -216,7 +217,6 @@ class PanningManager {
*
* Specifies if mouse events outside of the component should be handled. Default is true.
*/
// handleMouseOut: boolean;
handleMouseOut = true;
/**
@ -224,8 +224,33 @@ class PanningManager {
*
* Border to handle automatic panning inside the component. Default is 0 (disabled).
*/
// border: number;
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;

View File

@ -11,6 +11,7 @@ import { getMainEvent, isMultiTouchEvent } from '../../util/EventUtils';
import Graph from '../Graph';
import InternalMouseEvent from '../event/InternalMouseEvent';
import Cell from '../cell/datatypes/Cell';
import { GraphPlugin } from '../../types';
/**
* Class: mxPopupMenuHandler
@ -21,7 +22,7 @@ import Cell from '../cell/datatypes/Cell';
*
* Constructs an event handler that creates a <mxPopupMenu>.
*/
class PopupMenuHandler extends mxPopupMenu {
class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
constructor(graph: Graph, factoryMethod: any) {
super();

View File

@ -72,7 +72,7 @@ class CellHighlight {
}
// TODO: Document me!!
highlightColor: ColorValue | null = null;
highlightColor: ColorValue;
strokeWidth: number = 0;
@ -80,7 +80,7 @@ class CellHighlight {
opacity = 100;
repaintHandler: Function | null = null;
repaintHandler: Function;
shape: Shape | null = null;
@ -112,14 +112,14 @@ class CellHighlight {
* Holds the handler that automatically invokes reset if the highlight should be hidden.
* @default null
*/
resetHandler: Function | null = null;
resetHandler: Function;
/**
* Sets the color of the rectangle used to highlight drop targets.
*
* @param {string} color - String that represents the new highlight color.
*/
setHighlightColor(color: ColorValue | null) {
setHighlightColor(color: ColorValue) {
this.highlightColor = color;
if (this.shape) {
@ -134,10 +134,12 @@ class CellHighlight {
this.shape = this.createShape();
this.repaint();
const node = this.shape.node;
if (this.shape) {
const node = this.shape.node;
if (!this.keepOnTop && node?.parentNode?.firstChild !== node) {
node.parentNode.insertBefore(node, node.parentNode.firstChild);
if (!this.keepOnTop && node?.parentNode?.firstChild !== node && node.parentNode) {
node.parentNode.insertBefore(node, node.parentNode.firstChild);
}
}
}
@ -145,11 +147,11 @@ class CellHighlight {
* Creates and returns the highlight shape for the given state.
*/
createShape() {
if (!this.state) return;
if (!this.state) return null;
const shape = this.graph.cellRenderer.createShape(this.state);
shape.svgStrokeTolerance = this.graph.tolerance;
shape.svgStrokeTolerance = this.graph.getEventTolerance();
shape.points = this.state.absolutePoints;
shape.apply(this.state);
shape.stroke = this.highlightColor;
@ -173,7 +175,7 @@ class CellHighlight {
/**
* 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;
}
@ -256,13 +258,13 @@ class CellHighlight {
/**
* Destroys the handler and all its resources and DOM nodes.
*/
destroy(): void {
const graph = <graph>this.graph;
destroy() {
const graph = this.graph;
graph.getView().removeListener(this.resetHandler);
graph.getView().removeListener(this.repaintHandler);
graph.getModel().removeListener(this.repaintHandler);
if (this.shape != null) {
if (this.shape) {
this.shape.destroy();
this.shape = null;
}

View File

@ -15,6 +15,7 @@ import type GraphCells from '../cell/GraphCells';
import type Graph from '../Graph';
import type GraphEvents from '../event/GraphEvents';
import type EventSource from '../event/EventSource';
import { MaxGraph } from '../Graph';
type PartialGraph = Pick<
Graph,
@ -36,6 +37,8 @@ class GraphSelection extends autoImplement<PartialClass>() {
*/
doneResource: string = mxClient.language !== 'none' ? 'done' : '';
getDoneResource = () => this.doneResource;
/**
* 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
@ -44,6 +47,8 @@ class GraphSelection extends autoImplement<PartialClass>() {
updatingSelectionResource: string =
mxClient.language !== 'none' ? 'updatingSelection' : '';
getUpdatingSelectionResource = () => this.updatingSelectionResource;
/**
* Specifies if only one selected item at a time is allowed.
* Default is false.
@ -222,7 +227,7 @@ class GraphSelection extends autoImplement<PartialClass>() {
(removed && removed.length > 0 && removed[0])
) {
const change = new SelectionChange(
this,
this as MaxGraph,
added || new CellArray(),
removed || new CellArray()
);
@ -512,7 +517,7 @@ class GraphSelection extends autoImplement<PartialClass>() {
) {
const filter = (cell: Cell) => {
return (
this.getView().getState(cell) &&
!!this.getView().getState(cell) &&
(((selectGroups || cell.getChildCount() === 0) &&
cell.isVertex() &&
vertices &&

View File

@ -17,9 +17,10 @@ import mxClient from '../../mxClient';
import Rectangle from '../geometry/Rectangle';
import { isAltDown, isMultiTouchEvent } from '../../util/EventUtils';
import { clearSelection } from '../../util/DomUtils';
import Graph from '../Graph';
import { MaxGraph } from '../Graph';
import { GraphPlugin } from '../../types';
import EventObject from '../event/EventObject';
import EventSource from '../event/EventSource';
/**
* 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.
*/
class RubberBand implements GraphPlugin {
forceRubberbandHandler?: Function;
panHandler?: Function;
gestureHandler?: Function;
graph?: Graph;
first: Point | null = null;
destroyed: boolean = false;
dragHandler?: Function;
dropHandler?: Function;
static pluginId = 'RubberBand';
constructor() {}
onInit(graph: Graph) {
constructor(graph: MaxGraph) {
this.graph = graph;
this.graph.addMouseListener(this);
// Handles force rubberband event
this.forceRubberbandHandler = (sender: any, evt: EventObject) => {
this.forceRubberbandHandler = (sender: EventSource, evt: EventObject) => {
const evtName = evt.getProperty('eventName');
const me = evt.getProperty('event');
@ -67,8 +59,8 @@ class RubberBand implements GraphPlugin {
this.graph.addListener(InternalEvent.PAN, this.panHandler);
// Does not show menu if any touch gestures take place after the trigger
this.gestureHandler = (sender, eo) => {
if (this.first != null) {
this.gestureHandler = (sender: EventSource, eo: EventObject) => {
if (this.first) {
this.reset();
}
};
@ -76,6 +68,20 @@ class RubberBand implements GraphPlugin {
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.
*/
@ -93,14 +99,14 @@ class RubberBand implements GraphPlugin {
*
* Holds the DIV element which is currently visible.
*/
div = null;
div: HTMLElement | null = null;
/**
* Variable: sharedDiv
*
* Holds the DIV element which is used to display the rubberband.
*/
sharedDiv = null;
sharedDiv: HTMLElement | null = null;
/**
* Variable: currentX
@ -119,12 +125,12 @@ class RubberBand implements GraphPlugin {
/**
* Optional fade out effect. Default is false.
*/
fadeOut: boolean = false;
fadeOut = false;
/**
* Creates the rubberband selection shape.
*/
isEnabled(): boolean {
isEnabled() {
return this.enabled;
}
@ -155,12 +161,12 @@ class RubberBand implements GraphPlugin {
* event all subsequent events of the gesture are redirected to this
* handler.
*/
mouseDown(sender: any, me: InternalMouseEvent): void {
mouseDown(sender: EventSource, me: InternalMouseEvent) {
if (
!me.isConsumed() &&
this.isEnabled() &&
this.graph.isEnabled() &&
me.getState() == null &&
!me.getState() &&
!isMultiTouchEvent(me.getEvent())
) {
const offset = getOffset(this.graph.container);
@ -181,12 +187,12 @@ class RubberBand implements GraphPlugin {
/**
* Creates the rubberband selection shape.
*/
start(x: number, y: number): void {
start(x: number, y: number) {
this.first = new Point(x, y);
const { container } = this.graph;
function createMouseEvent(evt) {
function createMouseEvent(evt: MouseEvent) {
const me = new InternalMouseEvent(evt);
const pt = convertPoint(container, me.getX(), me.getY());
@ -196,11 +202,11 @@ class RubberBand implements GraphPlugin {
return me;
}
this.dragHandler = (evt) => {
this.dragHandler = (evt: MouseEvent) => {
this.mouseMove(this.graph, createMouseEvent(evt));
};
this.dropHandler = (evt) => {
this.dropHandler = (evt: MouseEvent) => {
this.mouseUp(this.graph, createMouseEvent(evt));
};
@ -220,8 +226,8 @@ class RubberBand implements GraphPlugin {
*
* Handles the event by updating therubberband selection.
*/
mouseMove(sender: any, me: InternalMouseEvent): void {
if (!me.isConsumed() && this.first != null) {
mouseMove(sender: EventSource, me: InternalMouseEvent) {
if (!me.isConsumed() && this.first) {
const origin = getScrollOrigin(this.graph.container);
const offset = getOffset(this.graph.container);
origin.x -= offset.x;
@ -230,10 +236,10 @@ class RubberBand implements GraphPlugin {
const y = me.getY() + origin.y;
const dx = this.first.x - x;
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 == null) {
if (this.div || Math.abs(dx) > tol || Math.abs(dy) > tol) {
if (!this.div) {
this.div = this.createShape();
}
@ -250,8 +256,8 @@ class RubberBand implements GraphPlugin {
/**
* Creates the rubberband selection shape.
*/
createShape(): HTMLElement | null {
if (this.sharedDiv == null) {
createShape() {
if (!this.sharedDiv) {
this.sharedDiv = document.createElement('div');
this.sharedDiv.className = 'mxRubberband';
setOpacity(this.sharedDiv, this.defaultOpacity);
@ -272,8 +278,8 @@ class RubberBand implements GraphPlugin {
*
* Returns true if this handler is active.
*/
isActive(sender: any, me: InternalMouseEvent): boolean {
return this.div != null && this.div.style.display !== 'none';
isActive(sender?: EventSource, me?: InternalMouseEvent) {
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
* <mxGraph.selectRegion>.
*/
mouseUp(sender: any, me: InternalMouseEvent): void {
mouseUp(sender: EventSource, me: InternalMouseEvent) {
const active = this.isActive();
this.reset();
@ -298,7 +304,7 @@ class RubberBand implements GraphPlugin {
* Resets the state of this handler and selects the current region
* for the given event.
*/
execute(evt) {
execute(evt: MouseEvent) {
const rect = new Rectangle(this.x, this.y, this.width, this.height);
this.graph.selectRegion(rect, evt);
}
@ -309,18 +315,18 @@ class RubberBand implements GraphPlugin {
* Resets the state of the rubberband selection.
*/
reset() {
if (this.div != null) {
if (this.div) {
if (mxClient.IS_SVG && this.fadeOut) {
const temp = this.div;
setPrefixedStyle(temp.style, 'transition', 'all 0.2s linear');
temp.style.pointerEvents = 'none';
temp.style.opacity = 0;
temp.style.opacity = String(0);
window.setTimeout(() => {
temp.parentNode.removeChild(temp);
if (temp.parentNode) temp.parentNode.removeChild(temp);
}, 200);
} 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>.
*/
update(x, y) {
update(x: number, y: number) {
this.currentX = x;
this.currentY = y;
@ -357,9 +363,9 @@ class RubberBand implements GraphPlugin {
* Computes the bounding box and updates the style of the <div>.
*/
repaint() {
if (this.div != null) {
const x = this.currentX - this.graph.panDx;
const y = this.currentY - this.graph.panDy;
if (this.div && this.first) {
const x = this.currentX - this.graph.getPanDx();
const y = this.currentY - this.graph.getPanDy();
this.x = Math.min(this.first.x, x);
this.y = Math.min(this.first.y, y);
@ -391,7 +397,7 @@ class RubberBand implements GraphPlugin {
this.graph.removeListener(this.panHandler);
this.reset();
if (this.sharedDiv != null) {
if (this.sharedDiv) {
this.sharedDiv = null;
}
}

View File

@ -8,11 +8,14 @@ import EventSource from '../event/EventSource';
import Dictionary from '../../util/Dictionary';
import EventObject from '../event/EventObject';
import InternalEvent from '../event/InternalEvent';
import utils, { sortCells } from '../../util/Utils';
import Graph from '../Graph';
import { sortCells } from '../../util/Utils';
import { MaxGraph } from '../Graph';
import Cell from '../cell/datatypes/Cell';
import CellArray from '../cell/datatypes/CellArray';
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
@ -36,21 +39,23 @@ import CellState from '../cell/datatypes/CellState';
*
* graph - Reference to the enclosing <mxGraph>.
*/
class SelectionCellsHandler extends EventSource {
constructor(graph: Graph) {
class SelectionCellsHandler extends EventSource implements GraphPlugin {
static pluginId = 'SelectionCellsHandler';
constructor(graph: MaxGraph) {
super();
this.graph = graph;
this.handlers = new Dictionary();
this.graph.addMouseListener(this);
this.refreshHandler = (sender, evt) => {
this.refreshHandler = (sender: EventSource, evt: EventObject) => {
if (this.isEnabled()) {
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.getView().addListener(InternalEvent.SCALE, this.refreshHandler);
this.graph.getView().addListener(InternalEvent.TRANSLATE, this.refreshHandler);
@ -66,42 +71,42 @@ class SelectionCellsHandler extends EventSource {
*
* Reference to the enclosing <mxGraph>.
*/
graph: Graph;
graph: MaxGraph;
/**
* Variable: enabled
*
* Specifies if events are handled. Default is true.
*/
enabled: boolean = true;
enabled = true;
/**
* Variable: refreshHandler
*
* Keeps a reference to an event listener for later removal.
*/
refreshHandler: any = null;
refreshHandler: (sender: EventSource, evt: EventObject) => void;
/**
* Variable: maxHandlers
*
* Defines the maximum number of handlers to paint individually. Default is 100.
*/
maxHandlers: number = 100;
maxHandlers = 100;
/**
* Variable: handlers
*
* <mxDictionary> that maps from cells to handlers.
*/
handlers: Dictionary<string, any>;
handlers: Dictionary<Cell, EdgeHandler | VertexHandler>;
/**
* Function: isEnabled
*
* Returns <enabled>.
*/
isEnabled(): boolean {
isEnabled() {
return this.enabled;
}
@ -110,7 +115,7 @@ class SelectionCellsHandler extends EventSource {
*
* Sets <enabled>.
*/
setEnabled(value: boolean): void {
setEnabled(value: boolean) {
this.enabled = value;
}
@ -119,7 +124,7 @@ class SelectionCellsHandler extends EventSource {
*
* Returns the handler for the given cell.
*/
getHandler(cell: Cell): any {
getHandler(cell: Cell) {
return this.handlers.get(cell);
}
@ -128,8 +133,8 @@ class SelectionCellsHandler extends EventSource {
*
* Returns true if the given cell has a handler.
*/
isHandled(cell: Cell): boolean {
return this.getHandler(cell) != null;
isHandled(cell: Cell) {
return !!this.getHandler(cell);
}
/**
@ -137,7 +142,7 @@ class SelectionCellsHandler extends EventSource {
*
* Resets all handlers.
*/
reset(): void {
reset() {
this.handlers.visit((key, handler) => {
handler.reset.apply(handler);
});
@ -148,8 +153,8 @@ class SelectionCellsHandler extends EventSource {
*
* Reloads or updates all handlers.
*/
getHandledSelectionCells(): CellArray {
return this.graph.selection.getSelectionCells();
getHandledSelectionCells() {
return this.graph.getSelectionCells();
}
/**
@ -157,7 +162,7 @@ class SelectionCellsHandler extends EventSource {
*
* Reloads or updates all handlers.
*/
refresh(): void {
refresh() {
// Removes all existing handlers
const oldHandlers = this.handlers;
this.handlers = new Dictionary();
@ -169,23 +174,22 @@ class SelectionCellsHandler extends EventSource {
for (let i = 0; i < tmp.length; i += 1) {
const state = this.graph.view.getState(tmp[i]);
if (state != null) {
if (state) {
let handler = oldHandlers.remove(tmp[i]);
if (handler != null) {
if (handler) {
if (handler.state !== state) {
handler.destroy();
handler = null;
} else if (!this.isHandlerActive(handler)) {
if (handler.refresh != null) {
handler.refresh();
}
// @ts-ignore refresh may exist
if (handler.refresh) handler.refresh();
handler.redraw();
}
}
if (handler != null) {
if (handler) {
this.handlers.put(tmp[i], handler);
}
}
@ -201,10 +205,10 @@ class SelectionCellsHandler extends EventSource {
for (let i = 0; i < tmp.length; i += 1) {
const state = this.graph.view.getState(tmp[i]);
if (state != null) {
if (state) {
let handler = this.handlers.get(tmp[i]);
if (handler == null) {
if (!handler) {
handler = this.graph.createHandler(state);
this.fireEvent(new EventObject(InternalEvent.ADD, 'state', state));
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.
*/
isHandlerActive(handler: any): boolean {
return handler.index != null;
isHandlerActive(handler: EdgeHandler | VertexHandler) {
return handler.index !== null;
}
/**
@ -229,10 +233,10 @@ class SelectionCellsHandler extends EventSource {
*
* Updates the handler for the given shape if one exists.
*/
updateHandler(state: CellState): void {
updateHandler(state: CellState) {
let handler = this.handlers.remove(state.cell);
if (handler != null) {
if (handler) {
// Transfers the current state to the new handler
const { index } = handler;
const x = handler.startX;
@ -241,10 +245,10 @@ class SelectionCellsHandler extends EventSource {
handler.destroy();
handler = this.graph.createHandler(state);
if (handler != null) {
if (handler) {
this.handlers.put(state.cell, handler);
if (index != null && x != null && y != null) {
if (index !== null) {
handler.start(x, y, index);
}
}
@ -256,12 +260,10 @@ class SelectionCellsHandler extends EventSource {
*
* Redirects the given event to the handlers.
*/
mouseDown(sender: Event, me: Event): void {
mouseDown(sender: EventSource, me: InternalMouseEvent) {
if (this.graph.isEnabled() && this.isEnabled()) {
const args = [sender, me];
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.
*/
mouseMove(sender: Event, me: Event): void {
mouseMove(sender: EventSource, me: InternalMouseEvent) {
if (this.graph.isEnabled() && this.isEnabled()) {
const args = [sender, me];
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.
*/
mouseUp(sender: Event, me: Event): void {
mouseUp(sender: EventSource, me: InternalMouseEvent) {
if (this.graph.isEnabled() && this.isEnabled()) {
const args = [sender, me];
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.
*/
destroy(): void {
onDestroy() {
this.graph.removeMouseListener(this);
if (this.refreshHandler != null) {
this.graph.selection.getSelectionModel().removeListener(this.refreshHandler);
this.graph.getModel().removeListener(this.refreshHandler);
this.graph.getView().removeListener(this.refreshHandler);
this.refreshHandler = null;
}
this.graph.removeListener(this.refreshHandler);
this.graph.getModel().removeListener(this.refreshHandler);
this.graph.getView().removeListener(this.refreshHandler);
}
}

View File

@ -1,12 +1,10 @@
import EventObject from '../event/EventObject';
import Resources from '../../util/Resources';
import mxLog from '../../util/gui/mxLog';
import InternalEvent from '../event/InternalEvent';
import mxGraphSelectionModel from '../view/selection/mxGraphSelectionModel';
import Cell from '../cell/datatypes/Cell';
import CellArray from "../cell/datatypes/CellArray";
import CellArray from '../cell/datatypes/CellArray';
import type { UndoableChange } from '../../types';
import type { MaxGraph } from '../Graph';
/**
* @class SelectionChange
@ -14,16 +12,16 @@ import type { UndoableChange } from '../../types';
*/
class SelectionChange implements UndoableChange {
constructor(
selectionModel: mxGraphSelectionModel,
graph: MaxGraph,
added: CellArray = new CellArray(),
removed: CellArray = new CellArray()
) {
this.selectionModel = selectionModel;
this.graph = graph;
this.added = added.slice();
this.removed = removed.slice();
}
selectionModel: mxGraphSelectionModel;
graph: MaxGraph;
added: CellArray;
@ -33,35 +31,25 @@ class SelectionChange implements UndoableChange {
* Changes the current root of the view.
*/
execute() {
const t0: any = mxLog.enter('mxSelectionChange.execute');
window.status =
Resources.get(this.selectionModel.updatingSelectionResource) ||
this.selectionModel.updatingSelectionResource;
Resources.get(this.graph.getUpdatingSelectionResource()) ||
this.graph.getUpdatingSelectionResource();
for (const removed of this.removed) {
this.selectionModel.cellRemoved(removed);
this.graph.cellRemoved(removed);
}
for (const added of this.added) {
this.selectionModel.cellAdded(added);
this.graph.cellAdded(added);
}
[this.added, this.removed] = [this.removed, this.added];
window.status =
Resources.get(this.selectionModel.doneResource) ||
this.selectionModel.doneResource;
mxLog.leave('mxSelectionChange.execute', t0);
Resources.get(this.graph.getDoneResource()) || this.graph.getDoneResource();
this.selectionModel.fireEvent(
new EventObject(
InternalEvent.CHANGE,
'added',
this.added,
'removed',
this.removed
)
this.graph.fireEvent(
new EventObject(InternalEvent.CHANGE, 'added', this.added, 'removed', this.removed)
);
}
}

View File

@ -11,6 +11,8 @@ class GraphSnap extends autoImplement<PartialClass>() {
// TODO: Document me!
tolerance: number = 0;
getSnapTolerance = () => this.tolerance;
/**
* Specifies the grid size.
* @default 10

View File

@ -9,10 +9,12 @@ import { fit, getScrollOrigin } from '../../util/Utils';
import { TOOLTIP_VERTICAL_OFFSET } from '../../util/Constants';
import { getSource, isMouseEvent } from '../../util/EventUtils';
import { isNode } from '../../util/DomUtils';
import Graph, { MaxGraph } from '../Graph';
import { MaxGraph } from '../Graph';
import CellState from '../cell/datatypes/CellState';
import InternalMouseEvent from '../event/InternalMouseEvent';
import { Listenable } from '../../types';
import PopupMenuHandler from '../popups_menus/PopupMenuHandler';
import type { GraphPlugin } from '../../types';
/**
* Class: mxTooltipHandler
@ -38,14 +40,41 @@ import { Listenable } from '../../types';
* graph - Reference to the enclosing <mxGraph>.
* delay - Optional delay in milliseconds.
*/
class TooltipHandler {
constructor(graph: MaxGraph, delay: number = 500) {
class TooltipHandler implements GraphPlugin {
static pluginId = 'TooltipHandler';
constructor(graph: MaxGraph) {
this.graph = graph;
this.delay = delay;
this.delay = 500;
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;
/**
@ -60,7 +89,7 @@ class TooltipHandler {
*
* Reference to the enclosing <mxGraph>.
*/
graph: Graph;
graph: MaxGraph;
/**
* Variable: delay
@ -91,10 +120,10 @@ class TooltipHandler {
*/
destroyed = false;
lastX: number = 0;
lastY: number = 0;
lastX = 0;
lastY = 0;
state: CellState | null = null;
stateSource: boolean = false;
stateSource = false;
node: any;
thread: number | null = null;
@ -143,27 +172,6 @@ class TooltipHandler {
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
*
@ -180,7 +188,7 @@ class TooltipHandler {
* event all subsequent events of the gesture are redirected to this
* handler.
*/
mouseDown(sender: any, me: InternalMouseEvent) {
mouseDown(sender: EventSource, me: InternalMouseEvent) {
this.reset(me, false);
this.hideTooltip();
}
@ -190,7 +198,7 @@ class TooltipHandler {
*
* 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) {
this.reset(me, true);
const state = this.getStateForEvent(me);
@ -218,7 +226,7 @@ class TooltipHandler {
* Handles the event by resetting the tooltip timer or hiding the existing
* tooltip.
*/
mouseUp(sender: any, me: InternalMouseEvent): void {
mouseUp(sender: EventSource, me: InternalMouseEvent) {
this.reset(me, true);
this.hideTooltip();
}
@ -228,8 +236,8 @@ class TooltipHandler {
*
* Resets the timer.
*/
resetTimer(): void {
if (this.thread !== null) {
resetTimer() {
if (this.thread) {
window.clearTimeout(this.thread);
this.thread = null;
}
@ -255,18 +263,28 @@ class TooltipHandler {
const x = me.getX();
const y = me.getY();
const stateSource = me.isSource(state.shape) || me.isSource(state.text);
const popupMenuHandler = this.graph.getPlugin(
'PopupMenuHandler'
) as PopupMenuHandler;
this.thread = window.setTimeout(() => {
if (
state &&
node &&
!this.graph.isEditing() &&
!this.graph.popupMenuHandler.isMenuShowing() &&
popupMenuHandler &&
!popupMenuHandler.isMenuShowing() &&
!this.graph.isMouseDown
) {
// Uses information from inside event cause using the event at
// this (delayed) point in time is not possible in IE as it no
// 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.state = state;
this.node = node;
@ -328,12 +346,12 @@ class TooltipHandler {
*
* Destroys the handler and all its resources and DOM nodes.
*/
destroy() {
onDestroy() {
if (!this.destroyed) {
this.graph.removeMouseListener(this);
InternalEvent.release(this.div);
if (this.div.parentNode != null) {
if (this.div.parentNode) {
this.div.parentNode.removeChild(this.div);
}

View File

@ -51,6 +51,8 @@ import CellArray from '../cell/datatypes/CellArray';
import type { MaxGraph } from '../Graph';
import StyleRegistry from '../style/StyleRegistry';
import TooltipHandler from '../tooltip/TooltipHandler';
import { MouseEventListener } from '../../types';
/**
* @class GraphView
@ -687,30 +689,26 @@ class GraphView extends EventSource {
// container and finishing the handling of a single gesture
InternalEvent.addGestureListeners(
this.backgroundPageShape.node,
(evt: Event) => {
graph.fireMouseEvent(
InternalEvent.MOUSE_DOWN,
new InternalMouseEvent(evt as MouseEvent)
);
(evt: MouseEvent) => {
graph.fireMouseEvent(InternalEvent.MOUSE_DOWN, new InternalMouseEvent(evt));
},
(evt: Event) => {
(evt: MouseEvent) => {
const tooltipHandler = graph.getPlugin('TooltipHandler') as TooltipHandler;
// Hides the tooltip if mouse is outside container
if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) {
graph.tooltipHandler.hide();
if (tooltipHandler && tooltipHandler.isHideOnHover()) {
tooltipHandler.hide();
}
if (graph.isMouseDown && !isConsumed(evt)) {
graph.fireMouseEvent(
InternalEvent.MOUSE_MOVE,
new InternalMouseEvent(evt as MouseEvent)
new InternalMouseEvent(evt)
);
}
},
(evt: Event) => {
graph.fireMouseEvent(
InternalEvent.MOUSE_UP,
new InternalMouseEvent(evt as MouseEvent)
);
(evt: MouseEvent) => {
graph.fireMouseEvent(InternalEvent.MOUSE_UP, new InternalMouseEvent(evt));
}
);
}
@ -2034,19 +2032,22 @@ class GraphView extends EventSource {
* Returns true if the event origin is one of the drawing panes or
* containers of the view.
*/
isContainerEvent(evt: Event | MouseEvent) {
isContainerEvent(evt: MouseEvent) {
const source = getSource(evt);
return (
source === this.graph.container ||
source.parentNode === this.backgroundPane ||
(source.parentNode && source.parentNode.parentNode === this.backgroundPane) ||
source === this.canvas.parentNode ||
source === this.canvas ||
source === this.backgroundPane ||
source === this.drawPane ||
source === this.overlayPane ||
source === this.decoratorPane
source &&
(source === this.graph.container ||
// @ts-ignore parentNode may exist
source.parentNode === this.backgroundPane ||
// @ts-ignore parentNode may exist
(source.parentNode && source.parentNode.parentNode === this.backgroundPane) ||
source === this.canvas.parentNode ||
source === this.canvas ||
source === this.backgroundPane ||
source === this.drawPane ||
source === this.overlayPane ||
source === this.decoratorPane)
);
}
@ -2125,24 +2126,18 @@ class GraphView extends EventSource {
pointerId = evt.pointerId;
}
}) as EventListener,
(evt: Event) => {
(evt: MouseEvent) => {
if (
this.isContainerEvent(evt) &&
// @ts-ignore
(pointerId === null || evt.pointerId === pointerId)
) {
graph.fireMouseEvent(
InternalEvent.MOUSE_MOVE,
new InternalMouseEvent(evt as MouseEvent)
);
graph.fireMouseEvent(InternalEvent.MOUSE_MOVE, new InternalMouseEvent(evt));
}
},
(evt: Event) => {
(evt: MouseEvent) => {
if (this.isContainerEvent(evt)) {
graph.fireMouseEvent(
InternalEvent.MOUSE_UP,
new InternalMouseEvent(evt as MouseEvent)
);
graph.fireMouseEvent(InternalEvent.MOUSE_UP, new InternalMouseEvent(evt));
}
pointerId = null;
@ -2161,7 +2156,7 @@ class GraphView extends EventSource {
// Workaround for touch events which started on some DOM node
// on top of the container, in which case the cells under the
// mouse for the move and up events are not detected.
const getState = (evt: Event) => {
const getState = (evt: MouseEvent) => {
let state = null;
// Workaround for touch events which started on some DOM node
@ -2188,16 +2183,20 @@ class GraphView extends EventSource {
// in Firefox and Chrome
graph.addMouseListener({
mouseDown: (sender: any, me: InternalMouseEvent) => {
(<PopupMenuHandler>graph.popupMenuHandler).hideMenu();
const popupMenuHandler = graph.getPlugin('PopupMenuHandler') as PopupMenuHandler;
if (popupMenuHandler) popupMenuHandler.hideMenu();
},
mouseMove: () => {},
mouseUp: () => {},
});
this.moveHandler = (evt: Event) => {
this.moveHandler = (evt: MouseEvent) => {
const tooltipHandler = graph.getPlugin('TooltipHandler') as TooltipHandler;
// Hides the tooltip if mouse is outside container
if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) {
graph.tooltipHandler.hide();
if (tooltipHandler && tooltipHandler.isHideOnHover()) {
tooltipHandler.hide();
}
if (
@ -2211,12 +2210,12 @@ class GraphView extends EventSource {
) {
graph.fireMouseEvent(
InternalEvent.MOUSE_MOVE,
new InternalMouseEvent(evt as MouseEvent, getState(evt))
new InternalMouseEvent(evt, getState(evt))
);
}
};
this.endHandler = (evt: Event) => {
this.endHandler = (evt: MouseEvent) => {
if (
this.captureDocumentGesture &&
graph.isMouseDown &&
@ -2225,10 +2224,7 @@ class GraphView extends EventSource {
graph.container.style.display !== 'none' &&
graph.container.style.visibility !== 'hidden'
) {
graph.fireMouseEvent(
InternalEvent.MOUSE_UP,
new InternalMouseEvent(evt as MouseEvent)
);
graph.fireMouseEvent(InternalEvent.MOUSE_UP, new InternalMouseEvent(evt));
}
};
@ -2320,8 +2316,8 @@ class GraphView extends EventSource {
}
}
endHandler: EventListener | null = null;
moveHandler: EventListener | null = null;
endHandler: MouseEventListener | null = null;
moveHandler: MouseEventListener | null = null;
}
export default GraphView;