parent
648e324cc0
commit
61648e43ce
|
@ -1,5 +1,6 @@
|
|||
import type Cell from './view/cell/datatypes/Cell';
|
||||
import type CellState from './view/cell/datatypes/CellState';
|
||||
import EventSource from './view/event/EventSource';
|
||||
import type InternalMouseEvent from './view/event/InternalMouseEvent';
|
||||
import type Shape from './view/geometry/shape/Shape';
|
||||
import type { MaxGraph } from './view/Graph';
|
||||
|
@ -43,6 +44,7 @@ export type CellStateStyles = {
|
|||
direction: DirectionValue;
|
||||
edge: string;
|
||||
editable: boolean;
|
||||
elbow: string;
|
||||
endArrow: ArrowType;
|
||||
endFill: boolean;
|
||||
endSize: number;
|
||||
|
@ -93,6 +95,7 @@ export type CellStateStyles = {
|
|||
movable: boolean;
|
||||
noEdgeStyle: boolean;
|
||||
opacity: number;
|
||||
orthogonal: boolean | null;
|
||||
overflow: OverflowValue;
|
||||
perimeter: Function | string | null;
|
||||
perimeterSpacing: number;
|
||||
|
@ -226,7 +229,7 @@ export interface GraphPlugin {
|
|||
|
||||
export type Listener = {
|
||||
name: string;
|
||||
f: MouseEventListener;
|
||||
f: MouseEventListener | KeyboardEventListener;
|
||||
};
|
||||
|
||||
export type ListenerTarget = {
|
||||
|
@ -236,6 +239,7 @@ export type ListenerTarget = {
|
|||
export type Listenable = (EventSource | EventTarget) & ListenerTarget;
|
||||
|
||||
export type MouseEventListener = (me: MouseEvent) => void;
|
||||
export type KeyboardEventListener = (ke: KeyboardEvent) => void;
|
||||
|
||||
export type GestureEvent = Event &
|
||||
MouseEvent & {
|
||||
|
@ -265,3 +269,13 @@ export interface CellHandle {
|
|||
redraw: () => void;
|
||||
destroy: () => void;
|
||||
}
|
||||
|
||||
export interface PopupMenuItem extends HTMLElement {
|
||||
table: HTMLElement;
|
||||
tbody: HTMLElement;
|
||||
div: HTMLElement;
|
||||
willAddSeparator: boolean;
|
||||
containsItems: boolean;
|
||||
activeRow: PopupMenuItem | null;
|
||||
eventReceiver: HTMLElement | null;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ export const getSource = (evt: MouseEvent) => {
|
|||
/**
|
||||
* Returns true if the event has been consumed using {@link consume}.
|
||||
*/
|
||||
export const isConsumed = (evt: MouseEvent) => {
|
||||
export const isConsumed = (evt: MouseEvent | KeyboardEvent) => {
|
||||
const t = evt as any;
|
||||
return t.isConsumed !== undefined && t.isConsumed;
|
||||
};
|
||||
|
|
|
@ -11,9 +11,10 @@ import Polyline from '../view/geometry/shape/edge/Polyline';
|
|||
import CellState from '../view/cell/datatypes/CellState';
|
||||
import Shape from '../view/geometry/shape/Shape';
|
||||
import Rectangle from '../view/geometry/Rectangle';
|
||||
import graph from '../view/Graph';
|
||||
import { MaxGraph } from '../view/Graph';
|
||||
import EventObject from '../view/event/EventObject';
|
||||
import GraphView from '../view/view/GraphView';
|
||||
import { ColorValue } from '../types';
|
||||
|
||||
/**
|
||||
* Class: mxGuide
|
||||
|
@ -25,7 +26,7 @@ import GraphView from '../view/view/GraphView';
|
|||
* Constructs a new guide object.
|
||||
*/
|
||||
class Guide {
|
||||
constructor(graph: graph, states: CellState[]) {
|
||||
constructor(graph: MaxGraph, states: CellState[]) {
|
||||
this.graph = graph;
|
||||
this.setStates(states);
|
||||
}
|
||||
|
@ -35,39 +36,34 @@ class Guide {
|
|||
*
|
||||
* Reference to the enclosing <mxGraph> instance.
|
||||
*/
|
||||
// graph: mxGraph;
|
||||
graph: graph;
|
||||
graph: MaxGraph;
|
||||
|
||||
/**
|
||||
* Variable: states
|
||||
*
|
||||
* Contains the <mxCellStates> that are used for alignment.
|
||||
*/
|
||||
// states: mxCellState[];
|
||||
states: CellState[] | null = null;
|
||||
states: CellState[] = [];
|
||||
|
||||
/**
|
||||
* Variable: horizontal
|
||||
*
|
||||
* Specifies if horizontal guides are enabled. Default is true.
|
||||
*/
|
||||
// horizontal: boolean;
|
||||
horizontal: boolean = true;
|
||||
horizontal = true;
|
||||
|
||||
/**
|
||||
* Variable: vertical
|
||||
*
|
||||
* Specifies if vertical guides are enabled. Default is true.
|
||||
*/
|
||||
// vertical: boolean;
|
||||
vertical: boolean = true;
|
||||
vertical = true;
|
||||
|
||||
/**
|
||||
* Variable: vertical
|
||||
*
|
||||
* Holds the <mxShape> for the horizontal guide.
|
||||
*/
|
||||
// guideX: mxShape;
|
||||
guideX: Shape | null = null;
|
||||
|
||||
/**
|
||||
|
@ -75,7 +71,6 @@ class Guide {
|
|||
*
|
||||
* Holds the <mxShape> for the vertical guide.
|
||||
*/
|
||||
// guideY: mxShape;
|
||||
guideY: Shape | null = null;
|
||||
|
||||
/**
|
||||
|
@ -83,22 +78,21 @@ class Guide {
|
|||
*
|
||||
* Specifies if rounded coordinates should be used. Default is false.
|
||||
*/
|
||||
rounded: boolean = false;
|
||||
rounded = false;
|
||||
|
||||
/**
|
||||
* Variable: tolerance
|
||||
*
|
||||
* Default tolerance in px if grid is disabled. Default is 2.
|
||||
*/
|
||||
tolerance: number = 2;
|
||||
tolerance = 2;
|
||||
|
||||
/**
|
||||
* Function: setStates
|
||||
*
|
||||
* Sets the <mxCellStates> that should be used for alignment.
|
||||
*/
|
||||
// setStates(states: mxCellState[]): void;
|
||||
setStates(states: CellState[]): void {
|
||||
setStates(states: CellState[]) {
|
||||
this.states = states;
|
||||
}
|
||||
|
||||
|
@ -108,8 +102,7 @@ class Guide {
|
|||
* Returns true if the guide should be enabled for the given native event. This
|
||||
* implementation always returns true.
|
||||
*/
|
||||
// isEnabledForEvent(evt: Event): boolean;
|
||||
isEnabledForEvent(evt: EventObject | null = null): boolean {
|
||||
isEnabledForEvent(evt: MouseEvent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -118,9 +111,9 @@ class Guide {
|
|||
*
|
||||
* Returns the tolerance for the guides. Default value is gridSize / 2.
|
||||
*/
|
||||
getGuideTolerance(gridEnabled: boolean = false): number {
|
||||
return gridEnabled && this.graph.gridEnabled
|
||||
? this.graph.gridSize / 2
|
||||
getGuideTolerance(gridEnabled = false) {
|
||||
return gridEnabled && this.graph.isGridEnabled()
|
||||
? this.graph.getGridSize() / 2
|
||||
: this.tolerance;
|
||||
}
|
||||
|
||||
|
@ -135,7 +128,7 @@ class Guide {
|
|||
*
|
||||
* horizontal - Boolean that specifies which guide should be created.
|
||||
*/
|
||||
createGuideShape(horizontal: boolean = false): Polyline {
|
||||
createGuideShape(horizontal = false) {
|
||||
// TODO: Should vertical guides be supported here?? ============================
|
||||
const guide = new Polyline([], GUIDE_COLOR, GUIDE_STROKEWIDTH);
|
||||
guide.isDashed = true;
|
||||
|
@ -146,7 +139,7 @@ class Guide {
|
|||
* Returns true if the given state should be ignored.
|
||||
* @param state
|
||||
*/
|
||||
isStateIgnored(state: CellState | null = null): boolean {
|
||||
isStateIgnored(state: CellState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -158,15 +151,10 @@ class Guide {
|
|||
move(
|
||||
bounds: Rectangle | null = null,
|
||||
delta: Point,
|
||||
gridEnabled: boolean = false,
|
||||
clone: boolean = false
|
||||
): Point {
|
||||
if (
|
||||
this.states != null &&
|
||||
(this.horizontal || this.vertical) &&
|
||||
bounds != null &&
|
||||
delta != null
|
||||
) {
|
||||
gridEnabled = false,
|
||||
clone = false
|
||||
) {
|
||||
if ((this.horizontal || this.vertical) && bounds) {
|
||||
const { scale } = this.graph.getView();
|
||||
const tt = this.getGuideTolerance(gridEnabled) * scale;
|
||||
const b = bounds.clone();
|
||||
|
@ -174,10 +162,10 @@ class Guide {
|
|||
b.y += delta.y;
|
||||
let overrideX = false;
|
||||
let stateX: CellState | null = null;
|
||||
let valueX = null;
|
||||
let valueX: number | null = null;
|
||||
let overrideY = false;
|
||||
let stateY: CellState | null = null;
|
||||
let valueY = null;
|
||||
let valueY: number | null = null;
|
||||
let ttX = tt;
|
||||
let ttY = tt;
|
||||
const left = b.x;
|
||||
|
@ -211,7 +199,7 @@ class Guide {
|
|||
stateX = state;
|
||||
valueX = x;
|
||||
|
||||
if (this.guideX == null) {
|
||||
if (!this.guideX) {
|
||||
this.guideX = this.createGuideShape(true);
|
||||
|
||||
// Makes sure to use SVG shapes in order to implement
|
||||
|
@ -250,7 +238,7 @@ class Guide {
|
|||
stateY = state;
|
||||
valueY = y;
|
||||
|
||||
if (this.guideY == null) {
|
||||
if (!this.guideY) {
|
||||
this.guideY = this.createGuideShape(false);
|
||||
|
||||
// Makes sure to use SVG shapes in order to implement
|
||||
|
@ -268,7 +256,7 @@ class Guide {
|
|||
for (let i = 0; i < this.states.length; i += 1) {
|
||||
const state = this.states[i];
|
||||
|
||||
if (state != null && !this.isStateIgnored(state)) {
|
||||
if (state && !this.isStateIgnored(state)) {
|
||||
// Align x
|
||||
if (this.horizontal) {
|
||||
snapX(state.getCenterX(), state, true);
|
||||
|
@ -276,7 +264,7 @@ class Guide {
|
|||
snapX(state.x + state.width, state, false);
|
||||
|
||||
// Aligns left and right of shape to center of page
|
||||
if (state.cell == null) {
|
||||
if (!state.cell) {
|
||||
snapX(state.getCenterX(), state, false);
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +276,7 @@ class Guide {
|
|||
snapY(state.y + state.height, state, false);
|
||||
|
||||
// Aligns left and right of shape to center of page
|
||||
if (state.cell == null) {
|
||||
if (!state.cell) {
|
||||
snapY(state.getCenterY(), state, false);
|
||||
}
|
||||
}
|
||||
|
@ -300,69 +288,63 @@ class Guide {
|
|||
delta = this.getDelta(bounds, stateX, delta.x, stateY, delta.y);
|
||||
|
||||
// Redraws the guides
|
||||
const c = <HTMLElement>this.graph.container;
|
||||
const c = this.graph.container;
|
||||
|
||||
if (!overrideX && this.guideX != null) {
|
||||
(<SVGElement>this.guideX.node).style.visibility = 'hidden';
|
||||
} else if (this.guideX != null) {
|
||||
let minY = null;
|
||||
let maxY = null;
|
||||
if (!overrideX && this.guideX) {
|
||||
this.guideX.node.style.visibility = 'hidden';
|
||||
} else if (this.guideX) {
|
||||
let minY: number | null = null;
|
||||
let maxY: number | null = null;
|
||||
|
||||
if (stateX != null) {
|
||||
minY = Math.min(bounds.y + delta.y - this.graph.panDy, stateX.y);
|
||||
if (stateX) {
|
||||
minY = Math.min(bounds.y + delta.y - this.graph.getPanDy(), stateX!.y);
|
||||
maxY = Math.max(
|
||||
bounds.y + bounds.height + delta.y - this.graph.panDy,
|
||||
// @ts-ignore
|
||||
stateX.y + stateX.height
|
||||
bounds.y + bounds.height + delta.y - this.graph.getPanDy(),
|
||||
// @ts-ignore stateX! doesn't work for some reason...
|
||||
stateX!.y + stateX!.height
|
||||
);
|
||||
}
|
||||
|
||||
if (minY != null && maxY != null) {
|
||||
this.guideX.points = [
|
||||
new Point(valueX, minY),
|
||||
new Point(valueX, maxY),
|
||||
];
|
||||
if (minY !== null && maxY !== null) {
|
||||
this.guideX.points = [new Point(valueX!, minY), new Point(valueX!, maxY)];
|
||||
} else {
|
||||
this.guideX.points = [
|
||||
new Point(valueX, -this.graph.panDy),
|
||||
new Point(valueX, c.scrollHeight - 3 - this.graph.panDy),
|
||||
new Point(valueX!, -this.graph.getPanDy()),
|
||||
new Point(valueX!, c.scrollHeight - 3 - this.graph.getPanDy()),
|
||||
];
|
||||
}
|
||||
|
||||
this.guideX.stroke = this.getGuideColor(stateX, true);
|
||||
(<SVGElement>this.guideX.node).style.visibility = 'visible';
|
||||
this.guideX.stroke = this.getGuideColor(stateX!, true);
|
||||
this.guideX.node.style.visibility = 'visible';
|
||||
this.guideX.redraw();
|
||||
}
|
||||
|
||||
if (!overrideY && this.guideY != null) {
|
||||
(<SVGElement>this.guideY.node).style.visibility = 'hidden';
|
||||
this.guideY.node.style.visibility = 'hidden';
|
||||
} else if (this.guideY != null) {
|
||||
let minX = null;
|
||||
let maxX = null;
|
||||
|
||||
if (stateY != null && bounds != null) {
|
||||
minX = Math.min(bounds.x + delta.x - this.graph.panDx, stateY.x);
|
||||
minX = Math.min(bounds.x + delta.x - this.graph.getPanDx(), stateY!.x);
|
||||
maxX = Math.max(
|
||||
bounds.x + bounds.width + delta.x - this.graph.panDx,
|
||||
bounds.x + bounds.width + delta.x - this.graph.getPanDx(),
|
||||
// @ts-ignore
|
||||
stateY.x + stateY.width
|
||||
);
|
||||
}
|
||||
|
||||
if (minX != null && maxX != null) {
|
||||
if (minX != null && maxX != null && valueY !== null) {
|
||||
this.guideY.points = [new Point(minX, valueY), new Point(maxX, valueY)];
|
||||
} else if (valueY !== null) {
|
||||
this.guideY.points = [
|
||||
new Point(minX, valueY),
|
||||
new Point(maxX, valueY),
|
||||
];
|
||||
} else {
|
||||
this.guideY.points = [
|
||||
new Point(-this.graph.panDx, valueY),
|
||||
new Point(c.scrollWidth - 3 - this.graph.panDx, valueY),
|
||||
new Point(-this.graph.getPanDx(), valueY),
|
||||
new Point(c.scrollWidth - 3 - this.graph.getPanDx(), valueY),
|
||||
];
|
||||
}
|
||||
|
||||
this.guideY.stroke = this.getGuideColor(stateY, false);
|
||||
(<SVGElement>this.guideY.node).style.visibility = 'visible';
|
||||
this.guideY.stroke = this.getGuideColor(stateY!, false);
|
||||
this.guideY.node.style.visibility = 'visible';
|
||||
this.guideY.redraw();
|
||||
}
|
||||
}
|
||||
|
@ -382,7 +364,7 @@ class Guide {
|
|||
stateY: CellState | null = null,
|
||||
dy: number
|
||||
): Point {
|
||||
const s = (<GraphView>this.graph.view).scale;
|
||||
const s = this.graph.view.scale;
|
||||
if (this.rounded || (stateX != null && stateX.cell == null)) {
|
||||
dx = Math.round((bounds.x + dx) / s) * s - bounds.x;
|
||||
}
|
||||
|
@ -397,9 +379,7 @@ class Guide {
|
|||
*
|
||||
* Hides all current guides.
|
||||
*/
|
||||
// getGuideColor(state: mxCellState, horizontal: any): string;
|
||||
getGuideColor(state: CellState | null,
|
||||
horizontal: boolean | null): string {
|
||||
getGuideColor(state: CellState, horizontal: boolean) {
|
||||
return GUIDE_COLOR;
|
||||
}
|
||||
|
||||
|
@ -408,7 +388,7 @@ class Guide {
|
|||
*
|
||||
* Hides all current guides.
|
||||
*/
|
||||
hide(): void {
|
||||
hide() {
|
||||
this.setVisible(false);
|
||||
}
|
||||
|
||||
|
@ -417,16 +397,12 @@ class Guide {
|
|||
*
|
||||
* Shows or hides the current guides.
|
||||
*/
|
||||
setVisible(visible: boolean): void {
|
||||
if (this.guideX != null) {
|
||||
(<SVGElement>this.guideX.node).style.visibility = visible
|
||||
? 'visible'
|
||||
: 'hidden';
|
||||
setVisible(visible: boolean) {
|
||||
if (this.guideX) {
|
||||
this.guideX.node.style.visibility = visible ? 'visible' : 'hidden';
|
||||
}
|
||||
if (this.guideY != null) {
|
||||
(<SVGElement>this.guideY.node).style.visibility = visible
|
||||
? 'visible'
|
||||
: 'hidden';
|
||||
if (this.guideY) {
|
||||
this.guideY.node.style.visibility = visible ? 'visible' : 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -435,12 +411,12 @@ class Guide {
|
|||
*
|
||||
* Destroys all resources that this object uses.
|
||||
*/
|
||||
destroy(): void {
|
||||
if (this.guideX != null) {
|
||||
destroy() {
|
||||
if (this.guideX) {
|
||||
this.guideX.destroy();
|
||||
this.guideX = null;
|
||||
}
|
||||
if (this.guideY != null) {
|
||||
if (this.guideY) {
|
||||
this.guideY.destroy();
|
||||
this.guideY = null;
|
||||
}
|
||||
|
|
|
@ -172,11 +172,11 @@ export const setPrefixedStyle = (
|
|||
prefix = 'Moz';
|
||||
}
|
||||
|
||||
style[name] = value;
|
||||
style.setProperty(name, value);
|
||||
|
||||
if (prefix !== null && name.length > 0) {
|
||||
name = prefix + name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
style[name] = value;
|
||||
style.setProperty(name, value);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
* Type definitions from the typed-mxgraph project
|
||||
*/
|
||||
import EventSource from '../../view/event/EventSource';
|
||||
import utils, { fit, getDocumentScrollOrigin } from '../Utils';
|
||||
import { fit, getDocumentScrollOrigin } from '../Utils';
|
||||
import EventObject from '../../view/event/EventObject';
|
||||
import mxClient from '../../mxClient';
|
||||
import InternalEvent from '../../view/event/InternalEvent';
|
||||
import { write } from '../DomUtils';
|
||||
import { isLeftMouseButton } from '../EventUtils';
|
||||
import Cell from '../../view/cell/datatypes/Cell';
|
||||
import InternalMouseEvent from '../../view/event/InternalMouseEvent';
|
||||
import { PopupMenuItem } from '../../types';
|
||||
|
||||
/**
|
||||
* Class: mxPopupMenu
|
||||
|
@ -38,23 +41,14 @@ import { isLeftMouseButton } from '../EventUtils';
|
|||
*
|
||||
* Fires after the menu has been shown in <popup>.
|
||||
*/
|
||||
class mxPopupMenu extends EventSource {
|
||||
constructor(factoryMethod) {
|
||||
class PopupMenu extends EventSource implements Partial<PopupMenuItem> {
|
||||
constructor(
|
||||
factoryMethod?: (handler: PopupMenuItem, cell: Cell | null, me: MouseEvent) => void
|
||||
) {
|
||||
super();
|
||||
|
||||
this.factoryMethod = factoryMethod;
|
||||
|
||||
if (factoryMethod != null) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function: init
|
||||
*
|
||||
* Initializes the shapes required for this vertex handler.
|
||||
*/
|
||||
// init(): void;
|
||||
init() {
|
||||
// Adds the inner table
|
||||
this.table = document.createElement('table');
|
||||
this.table.className = 'mxPopupMenu';
|
||||
|
@ -66,19 +60,24 @@ class mxPopupMenu extends EventSource {
|
|||
this.div = document.createElement('div');
|
||||
this.div.className = 'mxPopupMenu';
|
||||
this.div.style.display = 'inline';
|
||||
this.div.style.zIndex = this.zIndex;
|
||||
this.div.style.zIndex = String(this.zIndex);
|
||||
this.div.appendChild(this.table);
|
||||
|
||||
// Disables the context menu on the outer div
|
||||
InternalEvent.disableContextMenu(this.div);
|
||||
}
|
||||
|
||||
div: HTMLElement;
|
||||
table: HTMLElement;
|
||||
tbody: HTMLElement;
|
||||
activeRow: PopupMenuItem | null = null;
|
||||
eventReceiver: HTMLElement | null = null;
|
||||
|
||||
/**
|
||||
* Variable: submenuImage
|
||||
*
|
||||
* URL of the image to be used for the submenu icon.
|
||||
*/
|
||||
// submenuImage: string;
|
||||
submenuImage = `${mxClient.imageBasePath}/submenu.gif`;
|
||||
|
||||
/**
|
||||
|
@ -86,7 +85,6 @@ class mxPopupMenu extends EventSource {
|
|||
*
|
||||
* Specifies the zIndex for the popupmenu and its shadow. Default is 1006.
|
||||
*/
|
||||
// zIndex: number;
|
||||
zIndex = 10006;
|
||||
|
||||
/**
|
||||
|
@ -96,8 +94,7 @@ class mxPopupMenu extends EventSource {
|
|||
* current panning handler, the <mxCell> under the mouse and the mouse
|
||||
* event that triggered the call as arguments.
|
||||
*/
|
||||
// factoryMethod: (handler: mxPopupMenuHandler, cell: mxCell, me: mxMouseEvent) => any;
|
||||
factoryMethod = null;
|
||||
factoryMethod?: (handler: PopupMenuItem, cell: Cell | null, me: MouseEvent) => void;
|
||||
|
||||
/**
|
||||
* Variable: useLeftButtonForPopup
|
||||
|
@ -105,7 +102,6 @@ class mxPopupMenu extends EventSource {
|
|||
* Specifies if popupmenus should be activated by clicking the left mouse
|
||||
* button. Default is false.
|
||||
*/
|
||||
// useLeftButtonForPopup: boolean;
|
||||
useLeftButtonForPopup = false;
|
||||
|
||||
/**
|
||||
|
@ -113,7 +109,6 @@ class mxPopupMenu extends EventSource {
|
|||
*
|
||||
* Specifies if events are handled. Default is true.
|
||||
*/
|
||||
// enabled: boolean;
|
||||
enabled = true;
|
||||
|
||||
/**
|
||||
|
@ -121,7 +116,6 @@ class mxPopupMenu extends EventSource {
|
|||
*
|
||||
* Contains the number of times <addItem> has been called for a new menu.
|
||||
*/
|
||||
// itemCount: number;
|
||||
itemCount = 0;
|
||||
|
||||
/**
|
||||
|
@ -129,7 +123,6 @@ class mxPopupMenu extends EventSource {
|
|||
*
|
||||
* Specifies if submenus should be expanded on mouseover. Default is false.
|
||||
*/
|
||||
// autoExpand: boolean;
|
||||
autoExpand = false;
|
||||
|
||||
/**
|
||||
|
@ -138,7 +131,6 @@ class mxPopupMenu extends EventSource {
|
|||
* Specifies if separators should only be added if a menu item follows them.
|
||||
* Default is false.
|
||||
*/
|
||||
// smartSeparators: boolean;
|
||||
smartSeparators = false;
|
||||
|
||||
/**
|
||||
|
@ -146,16 +138,17 @@ class mxPopupMenu extends EventSource {
|
|||
*
|
||||
* Specifies if any labels should be visible. Default is true.
|
||||
*/
|
||||
// labels: boolean;
|
||||
labels = true;
|
||||
|
||||
willAddSeparator = false;
|
||||
containsItems = false;
|
||||
|
||||
/**
|
||||
* Function: isEnabled
|
||||
*
|
||||
* Returns true if events are handled. This implementation
|
||||
* returns <enabled>.
|
||||
*/
|
||||
// isEnabled(): boolean;
|
||||
isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
@ -166,8 +159,7 @@ class mxPopupMenu extends EventSource {
|
|||
* Enables or disables event handling. This implementation
|
||||
* updates <enabled>.
|
||||
*/
|
||||
// setEnabled(enabled: boolean): void;
|
||||
setEnabled(enabled) {
|
||||
setEnabled(enabled: boolean) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
|
@ -181,8 +173,7 @@ class mxPopupMenu extends EventSource {
|
|||
*
|
||||
* me - <mxMouseEvent> that represents the mouse event.
|
||||
*/
|
||||
// isPopupTrigger(me: mxMouseEvent): boolean;
|
||||
isPopupTrigger(me) {
|
||||
isPopupTrigger(me: InternalMouseEvent) {
|
||||
return (
|
||||
me.isPopupTrigger() ||
|
||||
(this.useLeftButtonForPopup && isLeftMouseButton(me.getEvent()))
|
||||
|
@ -210,8 +201,17 @@ class mxPopupMenu extends EventSource {
|
|||
* Default is true.
|
||||
* noHover - Optional boolean to disable hover state.
|
||||
*/
|
||||
addItem(title, image, funct, parent, iconCls, enabled, active, noHover) {
|
||||
parent = parent || this;
|
||||
addItem(
|
||||
title: string,
|
||||
image: string | null,
|
||||
funct: Function,
|
||||
parent: PopupMenuItem | null = null,
|
||||
iconCls?: string,
|
||||
enabled?: boolean,
|
||||
active?: boolean,
|
||||
noHover?: boolean
|
||||
) {
|
||||
parent = (parent ?? this) as PopupMenuItem;
|
||||
this.itemCount++;
|
||||
|
||||
// Smart separators only added if element contains items
|
||||
|
@ -224,17 +224,18 @@ class mxPopupMenu extends EventSource {
|
|||
}
|
||||
|
||||
parent.containsItems = true;
|
||||
const tr = document.createElement('tr');
|
||||
|
||||
const tr = <PopupMenuItem>(<unknown>document.createElement('tr'));
|
||||
tr.className = 'mxPopupMenuItem';
|
||||
const col1 = document.createElement('td');
|
||||
col1.className = 'mxPopupMenuIcon';
|
||||
|
||||
// Adds the given image into the first column
|
||||
if (image != null) {
|
||||
if (image) {
|
||||
const img = document.createElement('img');
|
||||
img.src = image;
|
||||
col1.appendChild(img);
|
||||
} else if (iconCls != null) {
|
||||
} else if (iconCls) {
|
||||
const div = document.createElement('div');
|
||||
div.className = iconCls;
|
||||
col1.appendChild(div);
|
||||
|
@ -244,18 +245,14 @@ class mxPopupMenu extends EventSource {
|
|||
|
||||
if (this.labels) {
|
||||
const col2 = document.createElement('td');
|
||||
col2.className = `mxPopupMenuItem${
|
||||
enabled != null && !enabled ? ' mxDisabled' : ''
|
||||
}`;
|
||||
col2.className = `mxPopupMenuItem${!enabled ? ' mxDisabled' : ''}`;
|
||||
|
||||
write(col2, title);
|
||||
col2.align = 'left';
|
||||
tr.appendChild(col2);
|
||||
|
||||
const col3 = document.createElement('td');
|
||||
col3.className = `mxPopupMenuItem${
|
||||
enabled != null && !enabled ? ' mxDisabled' : ''
|
||||
}`;
|
||||
col3.className = `mxPopupMenuItem${!enabled ? ' mxDisabled' : ''}`;
|
||||
col3.style.paddingRight = '6px';
|
||||
col3.style.textAlign = 'right';
|
||||
|
||||
|
@ -266,21 +263,16 @@ class mxPopupMenu extends EventSource {
|
|||
}
|
||||
}
|
||||
|
||||
parent.tbody.appendChild(tr);
|
||||
|
||||
if (active != false && enabled != false) {
|
||||
let currentSelection = null;
|
||||
parent.tbody?.appendChild(tr);
|
||||
|
||||
if (active && enabled) {
|
||||
InternalEvent.addGestureListeners(
|
||||
tr,
|
||||
evt => {
|
||||
(evt) => {
|
||||
this.eventReceiver = tr;
|
||||
|
||||
if (parent.activeRow != tr && parent.activeRow != parent) {
|
||||
if (
|
||||
parent.activeRow != null &&
|
||||
parent.activeRow.div.parentNode != null
|
||||
) {
|
||||
if (parent && parent.activeRow != tr && parent.activeRow != parent) {
|
||||
if (parent.activeRow != null && parent.activeRow.div.parentNode != null) {
|
||||
this.hideSubmenu(parent);
|
||||
}
|
||||
|
||||
|
@ -292,12 +284,9 @@ class mxPopupMenu extends EventSource {
|
|||
|
||||
InternalEvent.consume(evt);
|
||||
},
|
||||
evt => {
|
||||
if (parent.activeRow != tr && parent.activeRow != parent) {
|
||||
if (
|
||||
parent.activeRow != null &&
|
||||
parent.activeRow.div.parentNode != null
|
||||
) {
|
||||
(evt) => {
|
||||
if (parent && parent.activeRow != tr && parent.activeRow != parent) {
|
||||
if (parent.activeRow != null && parent.activeRow.div.parentNode != null) {
|
||||
this.hideSubmenu(parent);
|
||||
}
|
||||
|
||||
|
@ -312,26 +301,14 @@ class mxPopupMenu extends EventSource {
|
|||
tr.className = 'mxPopupMenuItemHover';
|
||||
}
|
||||
},
|
||||
evt => {
|
||||
(evt) => {
|
||||
// EventReceiver avoids clicks on a submenu item
|
||||
// which has just been shown in the mousedown
|
||||
if (this.eventReceiver == tr) {
|
||||
if (parent.activeRow != tr) {
|
||||
if (parent && parent.activeRow != tr) {
|
||||
this.hideMenu();
|
||||
}
|
||||
|
||||
// Workaround for lost current selection in page because of focus in IE
|
||||
if (currentSelection != null) {
|
||||
// Workaround for "unspecified error" in IE8 standards
|
||||
try {
|
||||
currentSelection.select();
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
currentSelection = null;
|
||||
}
|
||||
|
||||
if (funct != null) {
|
||||
funct(evt);
|
||||
}
|
||||
|
@ -344,7 +321,7 @@ class mxPopupMenu extends EventSource {
|
|||
|
||||
// Resets hover style because TR in IE doesn't have hover
|
||||
if (!noHover) {
|
||||
InternalEvent.addListener(tr, 'mouseout', evt => {
|
||||
InternalEvent.addListener(tr, 'mouseout', (evt: MouseEvent) => {
|
||||
tr.className = 'mxPopupMenuItem';
|
||||
});
|
||||
}
|
||||
|
@ -356,12 +333,13 @@ class mxPopupMenu extends EventSource {
|
|||
/**
|
||||
* Adds a checkmark to the given menuitem.
|
||||
*/
|
||||
// addCheckmark(item: Element, img: string): void;
|
||||
addCheckmark(item, img) {
|
||||
const td = item.firstChild.nextSibling;
|
||||
td.style.backgroundImage = `url('${img}')`;
|
||||
td.style.backgroundRepeat = 'no-repeat';
|
||||
td.style.backgroundPosition = '2px 50%';
|
||||
addCheckmark(item: HTMLElement, img: string) {
|
||||
if (item.firstChild) {
|
||||
const td = item.firstChild.nextSibling as HTMLElement;
|
||||
td.style.backgroundImage = `url('${img}')`;
|
||||
td.style.backgroundRepeat = 'no-repeat';
|
||||
td.style.backgroundPosition = '2px 50%';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -375,8 +353,7 @@ class mxPopupMenu extends EventSource {
|
|||
*
|
||||
* parent - An item returned by <addItem>.
|
||||
*/
|
||||
// createSubmenu(parent: Element): void;
|
||||
createSubmenu(parent) {
|
||||
createSubmenu(parent: PopupMenuItem) {
|
||||
parent.table = document.createElement('table');
|
||||
parent.table.className = 'mxPopupMenu';
|
||||
|
||||
|
@ -388,7 +365,7 @@ class mxPopupMenu extends EventSource {
|
|||
|
||||
parent.div.style.position = 'absolute';
|
||||
parent.div.style.display = 'inline';
|
||||
parent.div.style.zIndex = this.zIndex;
|
||||
parent.div.style.zIndex = String(this.zIndex);
|
||||
|
||||
parent.div.appendChild(parent.table);
|
||||
|
||||
|
@ -396,8 +373,10 @@ class mxPopupMenu extends EventSource {
|
|||
img.setAttribute('src', this.submenuImage);
|
||||
|
||||
// Last column of the submenu item in the parent menu
|
||||
td = parent.firstChild.nextSibling.nextSibling;
|
||||
td.appendChild(img);
|
||||
if (parent.firstChild?.nextSibling?.nextSibling) {
|
||||
const td = parent.firstChild.nextSibling.nextSibling;
|
||||
td.appendChild(img);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -406,18 +385,17 @@ class mxPopupMenu extends EventSource {
|
|||
* Shows the submenu inside the given parent row.
|
||||
*/
|
||||
// showSubmenu(parent: Element, row: Element): void;
|
||||
showSubmenu(parent, row) {
|
||||
showSubmenu(parent: PopupMenuItem, row: PopupMenuItem) {
|
||||
if (row.div != null) {
|
||||
row.div.style.left = `${parent.div.offsetLeft +
|
||||
row.offsetLeft +
|
||||
row.offsetWidth -
|
||||
1}px`;
|
||||
row.div.style.left = `${
|
||||
parent.div.offsetLeft + row.offsetLeft + row.offsetWidth - 1
|
||||
}px`;
|
||||
row.div.style.top = `${parent.div.offsetTop + row.offsetTop}px`;
|
||||
document.body.appendChild(row.div);
|
||||
|
||||
// Moves the submenu to the left side if there is no space
|
||||
const left = parseInt(row.div.offsetLeft);
|
||||
const width = parseInt(row.div.offsetWidth);
|
||||
const left = row.div.offsetLeft;
|
||||
const width = row.div.offsetWidth;
|
||||
const offset = getDocumentScrollOrigin(document);
|
||||
|
||||
const b = document.body;
|
||||
|
@ -426,10 +404,7 @@ class mxPopupMenu extends EventSource {
|
|||
const right = offset.x + (b.clientWidth || d.clientWidth);
|
||||
|
||||
if (left + width > right) {
|
||||
row.div.style.left = `${Math.max(
|
||||
0,
|
||||
parent.div.offsetLeft - width - 6
|
||||
)}px`;
|
||||
row.div.style.left = `${Math.max(0, parent.div.offsetLeft - width - 6)}px`;
|
||||
}
|
||||
|
||||
fit(row.div);
|
||||
|
@ -447,13 +422,12 @@ class mxPopupMenu extends EventSource {
|
|||
* parent - Optional item returned by <addItem>.
|
||||
* force - Optional boolean to ignore <smartSeparators>. Default is false.
|
||||
*/
|
||||
// addSeparator(parent?: Element, force?: boolean): void;
|
||||
addSeparator(parent, force) {
|
||||
addSeparator(parent: PopupMenuItem, force = false) {
|
||||
parent = parent || this;
|
||||
|
||||
if (this.smartSeparators && !force) {
|
||||
parent.willAddSeparator = true;
|
||||
} else if (parent.tbody != null) {
|
||||
} else if (parent.tbody) {
|
||||
parent.willAddSeparator = false;
|
||||
const tr = document.createElement('tr');
|
||||
|
||||
|
@ -491,8 +465,7 @@ class mxPopupMenu extends EventSource {
|
|||
* }
|
||||
* (end)
|
||||
*/
|
||||
// popup(x: number, y: number, cell: mxCell, evt: Event): void;
|
||||
popup(x, y, cell, evt) {
|
||||
popup(x: number, y: number, cell: Cell | null, evt: MouseEvent) {
|
||||
if (this.div != null && this.tbody != null && this.factoryMethod != null) {
|
||||
this.div.style.left = `${x}px`;
|
||||
this.div.style.top = `${y}px`;
|
||||
|
@ -504,7 +477,7 @@ class mxPopupMenu extends EventSource {
|
|||
}
|
||||
|
||||
this.itemCount = 0;
|
||||
this.factoryMethod(this, cell, evt);
|
||||
this.factoryMethod(<PopupMenuItem>(<unknown>this), cell, evt);
|
||||
|
||||
if (this.itemCount > 0) {
|
||||
this.showMenu();
|
||||
|
@ -547,7 +520,7 @@ class mxPopupMenu extends EventSource {
|
|||
this.div.parentNode.removeChild(this.div);
|
||||
}
|
||||
|
||||
this.hideSubmenu(this);
|
||||
this.hideSubmenu(<PopupMenuItem>(<unknown>this));
|
||||
this.containsItems = false;
|
||||
this.fireEvent(new EventObject(InternalEvent.HIDE));
|
||||
}
|
||||
|
@ -562,8 +535,7 @@ class mxPopupMenu extends EventSource {
|
|||
*
|
||||
* parent - An item returned by <addItem>.
|
||||
*/
|
||||
// hideSubmenu(parent: Element): void;
|
||||
hideSubmenu(parent) {
|
||||
hideSubmenu(parent: PopupMenuItem) {
|
||||
if (parent.activeRow != null) {
|
||||
this.hideSubmenu(parent.activeRow);
|
||||
|
||||
|
@ -580,7 +552,6 @@ class mxPopupMenu extends EventSource {
|
|||
*
|
||||
* Destroys the handler and all its resources and DOM nodes.
|
||||
*/
|
||||
// destroy(): void;
|
||||
destroy() {
|
||||
if (this.div != null) {
|
||||
InternalEvent.release(this.div);
|
||||
|
@ -588,10 +559,8 @@ class mxPopupMenu extends EventSource {
|
|||
if (this.div.parentNode != null) {
|
||||
this.div.parentNode.removeChild(this.div);
|
||||
}
|
||||
|
||||
this.div = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default mxPopupMenu;
|
||||
export default PopupMenu;
|
|
@ -5,9 +5,8 @@
|
|||
* Type definitions from the typed-mxgraph project
|
||||
*/
|
||||
|
||||
import graph from '../../view/Graph';
|
||||
import Model from "../../view/model/Model";
|
||||
import CellArray from "../../view/cell/datatypes/CellArray";
|
||||
import { MaxGraph } from '../../view/Graph';
|
||||
import CellArray from '../../view/cell/datatypes/CellArray';
|
||||
|
||||
/**
|
||||
* @class
|
||||
|
@ -18,8 +17,8 @@ import CellArray from "../../view/cell/datatypes/CellArray";
|
|||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* mxClipboard.copy(graph);
|
||||
* mxClipboard.paste(graph2);
|
||||
* Clipboard.copy(graph);
|
||||
* Clipboard.paste(graph2);
|
||||
* ```
|
||||
*
|
||||
* This copies the selection cells from the graph to the clipboard and
|
||||
|
@ -33,30 +32,30 @@ import CellArray from "../../view/cell/datatypes/CellArray";
|
|||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* mxClipboard.copy = function(graph, cells)
|
||||
* Clipboard.copy = function(graph, cells)
|
||||
* {
|
||||
* cells = cells || graph.getSelectionCells();
|
||||
* var result = graph.getExportableCells(cells);
|
||||
*
|
||||
* mxClipboard.parents = new Object();
|
||||
* Clipboard.parents = new Object();
|
||||
*
|
||||
* for (var i = 0; i < result.length; i++)
|
||||
* {
|
||||
* mxClipboard.parents[i] = graph.model.getParent(cells[i]);
|
||||
* Clipboard.parents[i] = graph.model.getParent(cells[i]);
|
||||
* }
|
||||
*
|
||||
* mxClipboard.insertCount = 1;
|
||||
* mxClipboard.setCells(graph.cloneCells(result));
|
||||
* Clipboard.insertCount = 1;
|
||||
* Clipboard.setCells(graph.cloneCells(result));
|
||||
*
|
||||
* return result;
|
||||
* };
|
||||
*
|
||||
* mxClipboard.paste = function(graph)
|
||||
* Clipboard.paste = function(graph)
|
||||
* {
|
||||
* if (!mxClipboard.isEmpty())
|
||||
* if (!Clipboard.isEmpty())
|
||||
* {
|
||||
* var cells = graph.getImportableCells(mxClipboard.getCells());
|
||||
* var delta = mxClipboard.insertCount * mxClipboard.STEPSIZE;
|
||||
* var cells = graph.getImportableCells(Clipboard.getCells());
|
||||
* var delta = Clipboard.insertCount * Clipboard.STEPSIZE;
|
||||
* var parent = graph.getDefaultParent();
|
||||
*
|
||||
* graph.model.beginUpdate();
|
||||
|
@ -64,8 +63,8 @@ import CellArray from "../../view/cell/datatypes/CellArray";
|
|||
* {
|
||||
* for (var i = 0; i < cells.length; i++)
|
||||
* {
|
||||
* var tmp = (mxClipboard.parents != null && graph.model.contains(mxClipboard.parents[i])) ?
|
||||
* mxClipboard.parents[i] : parent;
|
||||
* var tmp = (Clipboard.parents != null && graph.model.contains(Clipboard.parents[i])) ?
|
||||
* Clipboard.parents[i] : parent;
|
||||
* cells[i] = graph.importCells([cells[i]], delta, delta, tmp)[0];
|
||||
* }
|
||||
* }
|
||||
|
@ -75,48 +74,48 @@ import CellArray from "../../view/cell/datatypes/CellArray";
|
|||
* }
|
||||
*
|
||||
* // Increments the counter and selects the inserted cells
|
||||
* mxClipboard.insertCount++;
|
||||
* Clipboard.insertCount++;
|
||||
* graph.setSelectionCells(cells);
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class mxClipboard {
|
||||
class Clipboard {
|
||||
/**
|
||||
* Defines the step size to offset the cells after each paste operation.
|
||||
* Default is 10.
|
||||
*/
|
||||
static STEPSIZE: number = 10;
|
||||
static STEPSIZE = 10;
|
||||
|
||||
/**
|
||||
* Counts the number of times the clipboard data has been inserted.
|
||||
*/
|
||||
static insertCount: number = 1;
|
||||
static insertCount = 1;
|
||||
|
||||
/**
|
||||
* Holds the array of {@link mxCell} currently in the clipboard.
|
||||
*/
|
||||
static cells: CellArray | null = null;
|
||||
static cells: CellArray;
|
||||
|
||||
/**
|
||||
* Sets the cells in the clipboard. Fires a {@link mxEvent.CHANGE} event.
|
||||
*/
|
||||
static setCells(cells: CellArray | null): void {
|
||||
mxClipboard.cells = cells;
|
||||
static setCells(cells: CellArray) {
|
||||
Clipboard.cells = cells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cells in the clipboard.
|
||||
*/
|
||||
static getCells(): CellArray | null {
|
||||
return mxClipboard.cells;
|
||||
static getCells() {
|
||||
return Clipboard.cells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the clipboard currently has not data stored.
|
||||
*/
|
||||
static isEmpty(): boolean {
|
||||
return mxClipboard.getCells() == null;
|
||||
static isEmpty() {
|
||||
return !Clipboard.getCells();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,10 +126,10 @@ class mxClipboard {
|
|||
* @param graph - {@link graph} that contains the cells to be cut.
|
||||
* @param cells - Optional array of {@link mxCell} to be cut.
|
||||
*/
|
||||
static cut(graph: graph, cells?: CellArray | null): CellArray | null {
|
||||
cells = mxClipboard.copy(graph, cells);
|
||||
mxClipboard.insertCount = 0;
|
||||
mxClipboard.removeCells(graph, cells);
|
||||
static cut(graph: MaxGraph, cells?: CellArray) {
|
||||
cells = Clipboard.copy(graph, cells);
|
||||
Clipboard.insertCount = 0;
|
||||
Clipboard.removeCells(graph, cells);
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
@ -142,7 +141,7 @@ class mxClipboard {
|
|||
* @param graph - {@link graph} that contains the cells to be cut.
|
||||
* @param cells - Array of {@link mxCell} to be cut.
|
||||
*/
|
||||
static removeCells(graph: graph, cells: CellArray | null): void {
|
||||
static removeCells(graph: MaxGraph, cells: CellArray) {
|
||||
graph.removeCells(cells);
|
||||
}
|
||||
|
||||
|
@ -154,11 +153,11 @@ class mxClipboard {
|
|||
* @param graph - {@link graph} that contains the cells to be copied.
|
||||
* @param cells - Optional array of {@link mxCell} to be copied.
|
||||
*/
|
||||
static copy(graph: graph, cells?: CellArray | null): CellArray | null {
|
||||
static copy(graph: MaxGraph, cells?: CellArray) {
|
||||
cells = cells || graph.getSelectionCells();
|
||||
const result = (<CellArray>graph.getExportableCells(cells)).getTopmostCells();
|
||||
mxClipboard.insertCount = 1;
|
||||
mxClipboard.setCells(graph.cloneCells(<CellArray>result));
|
||||
const result = graph.getExportableCells(cells).getTopmostCells();
|
||||
Clipboard.insertCount = 1;
|
||||
Clipboard.setCells(graph.cloneCells(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -174,18 +173,17 @@ class mxClipboard {
|
|||
*
|
||||
* @param graph - {@link graph} to paste the {@link cells} into.
|
||||
*/
|
||||
static paste(graph: graph): CellArray | null {
|
||||
static paste(graph: MaxGraph) {
|
||||
let cells = null;
|
||||
|
||||
if (!mxClipboard.isEmpty()) {
|
||||
// @ts-ignore
|
||||
cells = graph.getImportableCells(mxClipboard.getCells());
|
||||
const delta = mxClipboard.insertCount * mxClipboard.STEPSIZE;
|
||||
if (!Clipboard.isEmpty() && Clipboard.getCells()) {
|
||||
cells = graph.getImportableCells(Clipboard.getCells());
|
||||
const delta = Clipboard.insertCount * Clipboard.STEPSIZE;
|
||||
const parent = graph.getDefaultParent();
|
||||
cells = graph.importCells(cells, delta, delta, parent);
|
||||
|
||||
// Increments the counter and selects the inserted cells
|
||||
mxClipboard.insertCount++;
|
||||
Clipboard.insertCount++;
|
||||
graph.setSelectionCells(<CellArray>cells);
|
||||
}
|
||||
|
||||
|
@ -193,4 +191,4 @@ class mxClipboard {
|
|||
}
|
||||
}
|
||||
|
||||
export default mxClipboard;
|
||||
export default Clipboard;
|
|
@ -47,27 +47,29 @@ 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 Dictionary from '../util/Dictionary';
|
||||
|
||||
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';
|
||||
import GraphPorts from './ports/GraphPorts';
|
||||
import GraphPanning from './panning/GraphPanning';
|
||||
import GraphZoom from './zoom/GraphZoom';
|
||||
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 GraphTooltip from './tooltip/GraphTooltip';
|
||||
import GraphTerminal from './terminal/GraphTerminal';
|
||||
import GraphDragDrop from './drag_drop/GraphDragDrop';
|
||||
import GraphSwimlane from './swimlane/GraphSwimlane';
|
||||
|
||||
type PartialEvents = Pick<
|
||||
GraphEvents,
|
||||
|
@ -86,6 +88,10 @@ type PartialEvents = Pick<
|
|||
| 'getEventTolerance'
|
||||
| 'isInvokesStopCellEditing'
|
||||
| 'getPointForEvent'
|
||||
| 'isConstrainedEvent'
|
||||
| 'isMouseTrigger'
|
||||
| 'isEnterStopsCellEditing'
|
||||
| 'getCursorForMouseEvent'
|
||||
>;
|
||||
type PartialSelection = Pick<
|
||||
GraphSelection,
|
||||
|
@ -101,6 +107,8 @@ type PartialSelection = Pick<
|
|||
| 'cellRemoved'
|
||||
| 'getUpdatingSelectionResource'
|
||||
| 'getDoneResource'
|
||||
| 'isSiblingSelected'
|
||||
| 'setSelectionCells'
|
||||
>;
|
||||
type PartialCells = Pick<
|
||||
GraphCells,
|
||||
|
@ -119,10 +127,22 @@ type PartialCells = Pick<
|
|||
| 'getCurrentCellStyle'
|
||||
| 'resizeCell'
|
||||
| 'removeStateForCell'
|
||||
| 'getMovableCells'
|
||||
| 'getCloneableCells'
|
||||
| 'isCellLocked'
|
||||
| 'moveCells'
|
||||
| 'removeCells'
|
||||
| 'isCellDeletable'
|
||||
| 'addCell'
|
||||
| 'getExportableCells'
|
||||
| 'cloneCells'
|
||||
| 'importCells'
|
||||
| 'getImportableCells'
|
||||
>;
|
||||
type PartialConnections = Pick<
|
||||
GraphConnections,
|
||||
| 'getConnectionConstraint'
|
||||
| 'setConnectionConstraint'
|
||||
| 'getConnectionPoint'
|
||||
| 'isCellDisconnectable'
|
||||
| 'getOutlineConstraint'
|
||||
|
@ -130,25 +150,35 @@ type PartialConnections = Pick<
|
|||
| 'getConnections'
|
||||
| 'isConstrainChild'
|
||||
| 'isValidSource'
|
||||
| 'getAllConnectionConstraints'
|
||||
>;
|
||||
type PartialEditing = Pick<
|
||||
GraphEditing,
|
||||
'isEditing' | 'stopEditing' | 'labelChanged' | 'getEditingValue'
|
||||
>;
|
||||
type PartialEditing = Pick<GraphEditing, 'isEditing' | 'stopEditing'>;
|
||||
type PartialTooltip = Pick<GraphTooltip, 'getTooltip'>;
|
||||
type PartialValidation = Pick<
|
||||
GraphValidation,
|
||||
'getEdgeValidationError' | 'validationAlert'
|
||||
'getEdgeValidationError' | 'validationAlert' | 'isEdgeValid'
|
||||
>;
|
||||
type PartialLabel = Pick<
|
||||
GraphLabel,
|
||||
'isLabelMovable' | 'isHtmlLabel' | 'isWrapping' | 'isLabelClipped' | 'getLabel'
|
||||
>;
|
||||
type PartialTerminal = Pick<GraphTerminal, 'isTerminalPointMovable'>;
|
||||
type PartialTerminal = Pick<GraphTerminal, 'isTerminalPointMovable' | 'getOpposites'>;
|
||||
type PartialSnap = Pick<
|
||||
GraphSnap,
|
||||
'snap' | 'getGridSize' | 'isGridEnabled' | 'getSnapTolerance'
|
||||
'snap' | 'getGridSize' | 'isGridEnabled' | 'getSnapTolerance' | 'snapDelta'
|
||||
>;
|
||||
type PartialEdge = Pick<
|
||||
GraphEdge,
|
||||
'isAllowDanglingEdges' | 'isResetEdgesOnConnect' | 'getEdges' | 'insertEdge' | 'addEdge'
|
||||
| 'isAllowDanglingEdges'
|
||||
| 'isResetEdgesOnConnect'
|
||||
| 'getEdges'
|
||||
| 'insertEdge'
|
||||
| 'addEdge'
|
||||
| 'splitEdge'
|
||||
| 'flipEdge'
|
||||
>;
|
||||
type PartialOverlays = Pick<GraphOverlays, 'getCellOverlays'>;
|
||||
type PartialFolding = Pick<
|
||||
|
@ -163,8 +193,16 @@ type PartialPanning = Pick<
|
|||
| 'setPanDx'
|
||||
| 'getPanDy'
|
||||
| 'setPanDy'
|
||||
| 'isTimerAutoScroll'
|
||||
| 'isAllowAutoPanning'
|
||||
| 'scrollCellToVisible'
|
||||
>;
|
||||
type PartialZoom = Pick<GraphZoom, 'zoomTo'>;
|
||||
type PartialDragDrop = Pick<
|
||||
GraphDragDrop,
|
||||
'isDropEnabled' | 'isAutoScroll' | 'isAutoExtend' | 'isSplitEnabled' | 'isSplitTarget'
|
||||
>;
|
||||
type PartialSwimlane = Pick<GraphSwimlane, 'getDropTarget'>;
|
||||
type PartialClass = PartialEvents &
|
||||
PartialSelection &
|
||||
PartialCells &
|
||||
|
@ -180,16 +218,20 @@ type PartialClass = PartialEvents &
|
|||
PartialFolding &
|
||||
PartialPanning &
|
||||
PartialZoom &
|
||||
PartialDragDrop &
|
||||
PartialSwimlane &
|
||||
EventSource;
|
||||
|
||||
export type MaxGraph = Graph & PartialClass;
|
||||
|
||||
const defaultPlugins: GraphPluginConstructor[] = [
|
||||
CellEditor,
|
||||
TooltipHandler,
|
||||
SelectionCellsHandler,
|
||||
PopupMenuHandler,
|
||||
ConnectionHandler,
|
||||
GraphHandler,
|
||||
PanningHandler,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -233,9 +275,6 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
|
||||
this.getModel().addListener(InternalEvent.CHANGE, this.graphModelChangeListener);
|
||||
|
||||
// Initializes the in-place editor
|
||||
this.cellEditor = this.createCellEditor();
|
||||
|
||||
// Initializes the container using the view
|
||||
this.view.init();
|
||||
|
||||
|
@ -256,26 +295,8 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
|
||||
destroyed: boolean = false;
|
||||
|
||||
// Handlers
|
||||
// @ts-ignore Cannot be null.
|
||||
// tooltipHandler: TooltipHandler;
|
||||
// @ts-ignore Cannot be null.
|
||||
// selectionCellsHandler: SelectionCellsHandler;
|
||||
// @ts-ignore Cannot be null.
|
||||
// popupMenuHandler: PopupMenuHandler;
|
||||
// @ts-ignore Cannot be null.
|
||||
// connectionHandler: ConnectionHandler;
|
||||
// @ts-ignore Cannot be null.
|
||||
// graphHandler: 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;
|
||||
|
||||
|
@ -338,6 +359,8 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*/
|
||||
dialect: 'svg' | 'mixedHtml' | 'preferHtml' | 'strictHtml' = 'svg';
|
||||
|
||||
getDialect = () => this.dialect;
|
||||
|
||||
/**
|
||||
* Value returned by {@link getOverlap} if {@link isAllowOverlapParent} returns
|
||||
* `true` for the given cell. {@link getOverlap} is used in {@link constrainChild} if
|
||||
|
@ -371,7 +394,9 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
* Not yet implemented.
|
||||
* @default false
|
||||
*/
|
||||
pageVisible: boolean = false;
|
||||
pageVisible = false;
|
||||
|
||||
isPageVisible = () => this.pageVisible;
|
||||
|
||||
/**
|
||||
* Specifies if a dashed line should be drawn between multiple pages.
|
||||
|
@ -379,32 +404,42 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
* should call {@link sizeDidChange} to force an update of the display.
|
||||
* @default false
|
||||
*/
|
||||
pageBreaksVisible: boolean = false;
|
||||
pageBreaksVisible = false;
|
||||
|
||||
isPageBreaksVisible = () => this.pageBreaksVisible;
|
||||
|
||||
/**
|
||||
* Specifies the color for page breaks.
|
||||
* @default gray
|
||||
*/
|
||||
pageBreakColor: string = 'gray';
|
||||
pageBreakColor = 'gray';
|
||||
|
||||
getPageBreakColor = () => this.pageBreakColor;
|
||||
|
||||
/**
|
||||
* Specifies the page breaks should be dashed.
|
||||
* @default true
|
||||
*/
|
||||
pageBreakDashed: boolean = true;
|
||||
pageBreakDashed = true;
|
||||
|
||||
isPageBreakDashed = () => this.pageBreakDashed;
|
||||
|
||||
/**
|
||||
* Specifies the minimum distance in pixels for page breaks to be visible.
|
||||
* @default 20
|
||||
*/
|
||||
minPageBreakDist: number = 20;
|
||||
minPageBreakDist = 20;
|
||||
|
||||
getMinPageBreakDist = () => this.minPageBreakDist;
|
||||
|
||||
/**
|
||||
* Specifies if the graph size should be rounded to the next page number in
|
||||
* {@link sizeDidChange}. This is only used if the graph container has scrollbars.
|
||||
* @default false
|
||||
*/
|
||||
preferPageSize: boolean = false;
|
||||
preferPageSize = false;
|
||||
|
||||
isPreferPageSize = () => this.preferPageSize;
|
||||
|
||||
/**
|
||||
* Specifies the page format for the background page.
|
||||
|
@ -412,26 +447,30 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
* if {@link pageVisible} is `true` and the page breaks if {@link pageBreaksVisible} is `true`.
|
||||
* @default {@link mxConstants.PAGE_FORMAT_A4_PORTRAIT}
|
||||
*/
|
||||
pageFormat: Rectangle = new Rectangle(...PAGE_FORMAT_A4_PORTRAIT);
|
||||
pageFormat = new Rectangle(...PAGE_FORMAT_A4_PORTRAIT);
|
||||
|
||||
getPageFormat = () => this.pageFormat;
|
||||
|
||||
/**
|
||||
* Specifies the scale of the background page.
|
||||
* Not yet implemented.
|
||||
* @default 1.5
|
||||
*/
|
||||
pageScale: number = 1.5;
|
||||
pageScale = 1.5;
|
||||
|
||||
getPageScale = () => this.pageScale;
|
||||
|
||||
/**
|
||||
* Specifies the return value for {@link isEnabled}.
|
||||
* @default true
|
||||
*/
|
||||
enabled: boolean = true;
|
||||
enabled = true;
|
||||
|
||||
/**
|
||||
* Specifies the return value for {@link canExportCell}.
|
||||
* @default true
|
||||
*/
|
||||
exportEnabled: boolean = true;
|
||||
exportEnabled = true;
|
||||
|
||||
isExportEnabled = () => this.exportEnabled;
|
||||
|
||||
|
@ -439,7 +478,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
* Specifies the return value for {@link canImportCell}.
|
||||
* @default true
|
||||
*/
|
||||
importEnabled: boolean = true;
|
||||
importEnabled = true;
|
||||
|
||||
isImportEnabled = () => this.importEnabled;
|
||||
|
||||
|
@ -449,7 +488,9 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
* scroll positions (ie usually only rightwards and downwards). To avoid
|
||||
* possible conflicts with panning, set {@link translateToScrollPosition} to `true`.
|
||||
*/
|
||||
ignoreScrollbars: boolean = false;
|
||||
ignoreScrollbars = false;
|
||||
|
||||
isIgnoreScrollbars = () => this.ignoreScrollbars;
|
||||
|
||||
/**
|
||||
* Specifies if the graph should automatically convert the current scroll
|
||||
|
@ -457,7 +498,9 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
* This can be used to avoid conflicts when using {@link autoScroll} and
|
||||
* {@link ignoreScrollbars} with no scrollbars in the container.
|
||||
*/
|
||||
translateToScrollPosition: boolean = false;
|
||||
translateToScrollPosition = false;
|
||||
|
||||
isTranslateToScrollPosition = () => this.translateToScrollPosition;
|
||||
|
||||
/**
|
||||
* {@link Rectangle} that specifies the area in which all cells in the diagram
|
||||
|
@ -473,12 +516,19 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*/
|
||||
minimumGraphSize: Rectangle | null = null;
|
||||
|
||||
getMinimumGraphSize = () => this.minimumGraphSize;
|
||||
setMinimumGraphSize = (size: Rectangle | null) => (this.minimumGraphSize = size);
|
||||
|
||||
/**
|
||||
* {@link Rectangle} that specifies the minimum size of the {@link container} if
|
||||
* {@link resizeContainer} is `true`.
|
||||
*/
|
||||
minimumContainerSize: Rectangle | null = null;
|
||||
|
||||
getMinimumContainerSize = () => this.minimumContainerSize;
|
||||
setMinimumContainerSize = (size: Rectangle | null) =>
|
||||
(this.minimumContainerSize = size);
|
||||
|
||||
/**
|
||||
* {@link Rectangle} that specifies the maximum size of the container if
|
||||
* {@link resizeContainer} is `true`.
|
||||
|
@ -490,7 +540,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
* the graph size has changed.
|
||||
* @default false
|
||||
*/
|
||||
resizeContainer: boolean = false;
|
||||
resizeContainer = false;
|
||||
|
||||
/**
|
||||
* Border to be added to the bottom and right side when the container is
|
||||
|
@ -644,13 +694,6 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
return new CellRenderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link CellEditor} to be used in this graph.
|
||||
*/
|
||||
createCellEditor(): CellEditor {
|
||||
return new CellEditor(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Model} that contains the cells.
|
||||
*/
|
||||
|
@ -708,7 +751,8 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
if (change instanceof RootChange) {
|
||||
this.clearSelection();
|
||||
this.setDefaultParent(null);
|
||||
this.removeStateForCell(change.previous);
|
||||
|
||||
if (change.previous) this.removeStateForCell(change.previous);
|
||||
|
||||
if (this.resetViewOnRootChange) {
|
||||
this.view.scale = 1;
|
||||
|
@ -792,9 +836,11 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
y: number,
|
||||
extend: boolean = false,
|
||||
border: number = 20
|
||||
): void {
|
||||
) {
|
||||
const panningHandler = this.getPlugin('PanningHandler') as PanningHandler;
|
||||
|
||||
if (
|
||||
!this.timerAutoScroll &&
|
||||
!this.isTimerAutoScroll() &&
|
||||
(this.ignoreScrollbars || hasScrollbars(this.container))
|
||||
) {
|
||||
const c = <HTMLElement>this.container;
|
||||
|
@ -860,14 +906,8 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
this.allowAutoPanning &&
|
||||
!(<PanningHandler>this.panningHandler).isActive()
|
||||
) {
|
||||
if (this.panningManager == null) {
|
||||
this.panningManager = this.createPanningManager();
|
||||
}
|
||||
this.panningManager.panTo(x + this.panDx, y + this.panDy);
|
||||
} else if (this.isAllowAutoPanning() && !panningHandler.isActive()) {
|
||||
panningHandler.getPanningManager().panTo(x + this.getPanDx(), y + this.getPanDy());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -894,8 +934,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
/**
|
||||
* Returns the preferred size of the background page if {@link preferPageSize} is true.
|
||||
*/
|
||||
getPreferredPageSize(bounds: Rectangle, width: number, height: number): Rectangle {
|
||||
const { scale } = this.view;
|
||||
getPreferredPageSize(bounds: Rectangle, width: number, height: number) {
|
||||
const tr = this.view.translate;
|
||||
const fmt = this.pageFormat;
|
||||
const ps = this.pageScale;
|
||||
|
@ -1091,13 +1130,13 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
if (state.cell.isEdge()) {
|
||||
const source = state.getVisibleTerminalState(true);
|
||||
const target = state.getVisibleTerminalState(false);
|
||||
const geo = (<Cell>state.cell).getGeometry();
|
||||
const geo = state.cell.getGeometry();
|
||||
|
||||
const edgeStyle = this.getView().getEdgeStyle(
|
||||
state,
|
||||
geo != null ? geo.points : null,
|
||||
<CellState>source,
|
||||
<CellState>target
|
||||
geo ? geo.points : undefined,
|
||||
source,
|
||||
target
|
||||
);
|
||||
result = this.createEdgeHandler(state, edgeStyle);
|
||||
} else {
|
||||
|
@ -1120,7 +1159,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param state {@link mxCellState} to create the handler for.
|
||||
*/
|
||||
createEdgeHandler(state: CellState, edgeStyle: any): EdgeHandler {
|
||||
createEdgeHandler(state: CellState, edgeStyle: any) {
|
||||
let result = null;
|
||||
if (
|
||||
edgeStyle == EdgeStyle.Loop ||
|
||||
|
@ -1137,7 +1176,8 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
} else {
|
||||
result = new EdgeHandler(state);
|
||||
}
|
||||
return result;
|
||||
|
||||
return result as EdgeHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1145,8 +1185,8 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param state {@link mxCellState} to create the handler for.
|
||||
*/
|
||||
createEdgeSegmentHandler(state: CellState): mxEdgeSegmentHandler {
|
||||
return new mxEdgeSegmentHandler(state);
|
||||
createEdgeSegmentHandler(state: CellState) {
|
||||
return new EdgeSegmentHandler(state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1154,7 +1194,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param state {@link mxCellState} to create the handler for.
|
||||
*/
|
||||
createElbowEdgeHandler(state: CellState): ElbowEdgeHandler {
|
||||
createElbowEdgeHandler(state: CellState) {
|
||||
return new ElbowEdgeHandler(state);
|
||||
}
|
||||
|
||||
|
@ -1166,7 +1206,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
* Returns the current root of the displayed cell hierarchy. This is a
|
||||
* shortcut to {@link GraphView.currentRoot} in {@link GraphView}.
|
||||
*/
|
||||
getCurrentRoot(): Cell | null {
|
||||
getCurrentRoot() {
|
||||
return this.view.currentRoot;
|
||||
}
|
||||
|
||||
|
@ -1213,7 +1253,6 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param cell {@link mxCell} whose offset should be returned.
|
||||
*/
|
||||
// getChildOffsetForCell(cell: mxCell): number;
|
||||
getChildOffsetForCell(cell: Cell): Point | null {
|
||||
return null;
|
||||
}
|
||||
|
@ -1230,7 +1269,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
const state = this.view.getState(current);
|
||||
|
||||
if (state != null) {
|
||||
this.selection.setSelectionCell(current);
|
||||
this.setSelectionCell(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1242,7 +1281,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
* @param cell {@link mxCell} which should be checked as a possible root.
|
||||
*/
|
||||
isValidRoot(cell: Cell) {
|
||||
return cell != null;
|
||||
return !!cell;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
@ -1357,7 +1396,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*/
|
||||
const orthogonal = edge.style.orthogonal;
|
||||
|
||||
if (orthogonal != null) {
|
||||
if (orthogonal !== null) {
|
||||
return orthogonal;
|
||||
}
|
||||
|
||||
|
@ -1461,7 +1500,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
/**
|
||||
* Returns {@link resizeContainer}.
|
||||
*/
|
||||
isResizeContainer(): boolean {
|
||||
isResizeContainer() {
|
||||
return this.resizeContainer;
|
||||
}
|
||||
|
||||
|
@ -1477,7 +1516,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
/**
|
||||
* Returns true if the graph is {@link enabled}.
|
||||
*/
|
||||
isEnabled(): boolean {
|
||||
isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
|
@ -1487,14 +1526,14 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param value Boolean indicating if the graph should be enabled.
|
||||
*/
|
||||
setEnabled(value: boolean): void {
|
||||
setEnabled(value: boolean) {
|
||||
this.enabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link multigraph} as a boolean.
|
||||
*/
|
||||
isMultigraph(): boolean {
|
||||
isMultigraph() {
|
||||
return this.multigraph;
|
||||
}
|
||||
|
||||
|
@ -1505,14 +1544,14 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
* @param value Boolean indicating if the graph allows multiple connections
|
||||
* between the same pair of vertices.
|
||||
*/
|
||||
setMultigraph(value: boolean): void {
|
||||
setMultigraph(value: boolean) {
|
||||
this.multigraph = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link allowLoops} as a boolean.
|
||||
*/
|
||||
isAllowLoops(): boolean {
|
||||
isAllowLoops() {
|
||||
return this.allowLoops;
|
||||
}
|
||||
|
||||
|
@ -1521,7 +1560,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param value Boolean indicating if loops are allowed.
|
||||
*/
|
||||
setAllowLoops(value: boolean): void {
|
||||
setAllowLoops(value: boolean) {
|
||||
this.allowLoops = value;
|
||||
}
|
||||
|
||||
|
@ -1530,7 +1569,7 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param state {@link mxCellState} that is being resized.
|
||||
*/
|
||||
isRecursiveResize(state: CellState | null = null): boolean {
|
||||
isRecursiveResize(state: CellState | null = null) {
|
||||
return this.recursiveResize;
|
||||
}
|
||||
|
||||
|
@ -1539,24 +1578,10 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param value New boolean value for {@link recursiveResize}.
|
||||
*/
|
||||
setRecursiveResize(value: boolean): void {
|
||||
setRecursiveResize(value: boolean) {
|
||||
this.recursiveResize = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link allowNegativeCoordinates}.
|
||||
*/
|
||||
isAllowNegativeCoordinates(): boolean {
|
||||
return this.allowNegativeCoordinates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link allowNegativeCoordinates}.
|
||||
*/
|
||||
setAllowNegativeCoordinates(value: boolean): void {
|
||||
this.allowNegativeCoordinates = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a decimal number representing the amount of the width and height
|
||||
* of the given cell that is allowed to overlap its parent. A value of 0
|
||||
|
@ -1623,17 +1648,10 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
destroy(): void {
|
||||
if (!this.destroyed) {
|
||||
this.destroyed = true;
|
||||
// @ts-ignore
|
||||
this.container = null;
|
||||
|
||||
this.tooltipHandler?.destroy?.();
|
||||
this.selectionCellsHandler?.destroy?.();
|
||||
this.panningHandler?.destroy?.();
|
||||
this.popupMenuHandler?.destroy?.();
|
||||
this.connectionHandler?.destroy?.();
|
||||
this.graphHandler?.destroy?.();
|
||||
this.cellEditor?.destroy?.();
|
||||
this.view?.destroy?.();
|
||||
Object.values(this.pluginsMap).forEach((p) => p.onDestroy());
|
||||
|
||||
this.view.destroy();
|
||||
|
||||
if (this.model != null && this.graphModelChangeListener != null) {
|
||||
this.getModel().removeListener(this.graphModelChangeListener);
|
||||
|
@ -1644,19 +1662,20 @@ class Graph extends autoImplement<PartialClass>() {
|
|||
}
|
||||
|
||||
applyMixins(Graph, [
|
||||
GraphEvents,
|
||||
GraphImage,
|
||||
GraphCells,
|
||||
GraphSelection,
|
||||
GraphConnections,
|
||||
GraphEdge,
|
||||
GraphVertex,
|
||||
GraphOverlays,
|
||||
GraphEditing,
|
||||
GraphEvents,
|
||||
GraphFolding,
|
||||
GraphImage,
|
||||
GraphLabel,
|
||||
GraphValidation,
|
||||
GraphOverlays,
|
||||
GraphSelection,
|
||||
GraphSnap,
|
||||
GraphSwimlane,
|
||||
GraphValidation,
|
||||
GraphVertex,
|
||||
]);
|
||||
|
||||
export default Graph;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -129,7 +129,7 @@ class CellMarker extends EventSource {
|
|||
*
|
||||
* Holds the current marker color.
|
||||
*/
|
||||
currentColor: ColorValue | null = null;
|
||||
currentColor: ColorValue = NONE;
|
||||
|
||||
/**
|
||||
* Variable: validState
|
||||
|
|
|
@ -471,7 +471,7 @@ class CellRenderer {
|
|||
// getCellAt for the subsequent mouseMoves and the final mouseUp.
|
||||
let forceGetCell = false;
|
||||
|
||||
const getState = (evt: Event | InternalMouseEvent) => {
|
||||
const getState = (evt: MouseEvent) => {
|
||||
let result: CellState | null = state;
|
||||
|
||||
if (mxClient.IS_TOUCH || forceGetCell) {
|
||||
|
@ -489,29 +489,33 @@ class CellRenderer {
|
|||
// TODO: Add handling for special touch device gestures
|
||||
InternalEvent.addGestureListeners(
|
||||
state.text.node,
|
||||
(evt: Event) => {
|
||||
if (this.isLabelEvent(state, evt as MouseEvent)) {
|
||||
(evt: MouseEvent) => {
|
||||
if (this.isLabelEvent(state, evt)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_DOWN,
|
||||
new InternalMouseEvent(evt as MouseEvent, state)
|
||||
new InternalMouseEvent(evt, state)
|
||||
);
|
||||
|
||||
const source = getSource(evt);
|
||||
|
||||
forceGetCell =
|
||||
graph.dialect !== DIALECT_SVG && getSource(evt).nodeName === 'IMG';
|
||||
// @ts-ignore nodeName should exist.
|
||||
graph.dialect !== DIALECT_SVG && source.nodeName === 'IMG';
|
||||
}
|
||||
},
|
||||
(evt: Event) => {
|
||||
if (this.isLabelEvent(state, evt as MouseEvent)) {
|
||||
(evt: MouseEvent) => {
|
||||
if (this.isLabelEvent(state, evt)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_MOVE,
|
||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||
new InternalMouseEvent(evt, getState(evt))
|
||||
);
|
||||
}
|
||||
},
|
||||
(evt: Event) => {
|
||||
if (this.isLabelEvent(state, evt as MouseEvent)) {
|
||||
(evt: MouseEvent) => {
|
||||
if (this.isLabelEvent(state, evt)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_UP,
|
||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||
new InternalMouseEvent(evt, getState(evt))
|
||||
);
|
||||
forceGetCell = false;
|
||||
}
|
||||
|
@ -520,9 +524,9 @@ class CellRenderer {
|
|||
|
||||
// Uses double click timeout in mxGraph for quirks mode
|
||||
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.addListener(state.text.node, 'dblclick', (evt: MouseEvent) => {
|
||||
if (this.isLabelEvent(state, evt)) {
|
||||
graph.dblClick(evt, state.cell);
|
||||
InternalEvent.consume(evt);
|
||||
}
|
||||
});
|
||||
|
@ -749,24 +753,24 @@ class CellRenderer {
|
|||
|
||||
InternalEvent.addGestureListeners(
|
||||
node,
|
||||
(evt: Event) => {
|
||||
(evt: MouseEvent) => {
|
||||
first = new Point(getClientX(evt), getClientY(evt));
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_DOWN,
|
||||
new InternalMouseEvent(evt as MouseEvent, state)
|
||||
new InternalMouseEvent(evt, state)
|
||||
);
|
||||
InternalEvent.consume(evt);
|
||||
},
|
||||
(evt: Event) => {
|
||||
(evt: MouseEvent) => {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_MOVE,
|
||||
new InternalMouseEvent(evt as MouseEvent, state)
|
||||
new InternalMouseEvent(evt, state)
|
||||
);
|
||||
},
|
||||
(evt: Event) => {
|
||||
(evt: MouseEvent) => {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_UP,
|
||||
new InternalMouseEvent(evt as MouseEvent, state)
|
||||
new InternalMouseEvent(evt, state)
|
||||
);
|
||||
InternalEvent.consume(evt);
|
||||
}
|
||||
|
@ -776,13 +780,13 @@ class CellRenderer {
|
|||
if (clickHandler && mxClient.IS_IOS) {
|
||||
node.addEventListener(
|
||||
'touchend',
|
||||
(evt) => {
|
||||
(evt: Event) => {
|
||||
if (first) {
|
||||
const tol = graph.getEventTolerance();
|
||||
|
||||
if (
|
||||
Math.abs(first.x - getClientX(evt)) < tol &&
|
||||
Math.abs(first.y - getClientY(evt)) < tol
|
||||
Math.abs(first.x - getClientX(evt as MouseEvent)) < tol &&
|
||||
Math.abs(first.y - getClientY(evt as MouseEvent)) < tol
|
||||
) {
|
||||
clickHandler.call(clickHandler, evt);
|
||||
InternalEvent.consume(evt);
|
||||
|
@ -808,7 +812,7 @@ class CellRenderer {
|
|||
* state - <mxCellState> whose shape fired the event.
|
||||
* evt - Mouse event which was fired.
|
||||
*/
|
||||
isShapeEvent(state: CellState, evt: InternalMouseEvent | MouseEvent) {
|
||||
isShapeEvent(state: CellState, evt: MouseEvent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -823,7 +827,7 @@ class CellRenderer {
|
|||
* state - <mxCellState> whose label fired the event.
|
||||
* evt - Mouse event which was fired.
|
||||
*/
|
||||
isLabelEvent(state: CellState, evt: InternalMouseEvent | MouseEvent) {
|
||||
isLabelEvent(state: CellState, evt: MouseEvent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -842,11 +846,15 @@ class CellRenderer {
|
|||
// 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) => {
|
||||
const getState = (evt: MouseEvent) => {
|
||||
let result: CellState | null = state;
|
||||
const source = getSource(evt);
|
||||
|
||||
if (
|
||||
(graph.dialect !== DIALECT_SVG && getSource(evt).nodeName === 'IMG') ||
|
||||
(source &&
|
||||
graph.dialect !== DIALECT_SVG &&
|
||||
// @ts-ignore nodeName should exist
|
||||
source.nodeName === 'IMG') ||
|
||||
mxClient.IS_TOUCH
|
||||
) {
|
||||
const x = getClientX(evt);
|
||||
|
@ -855,50 +863,52 @@ class CellRenderer {
|
|||
// Dispatches the drop event to the graph which
|
||||
// consumes and executes the source function
|
||||
const pt = convertPoint(graph.container, x, y);
|
||||
result = graph.view.getState(graph.getCellAt(pt.x, pt.y) as Cell);
|
||||
const cell = graph.getCellAt(pt.x, pt.y);
|
||||
|
||||
result = cell ? graph.view.getState(cell) : null;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
InternalEvent.addGestureListeners(
|
||||
// @ts-ignore
|
||||
state.shape.node,
|
||||
(evt: Event) => {
|
||||
if (this.isShapeEvent(state, evt as MouseEvent)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_DOWN,
|
||||
new InternalMouseEvent(evt as MouseEvent, state)
|
||||
);
|
||||
if (state.shape) {
|
||||
InternalEvent.addGestureListeners(
|
||||
state.shape.node,
|
||||
(evt: MouseEvent) => {
|
||||
if (this.isShapeEvent(state, evt)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_DOWN,
|
||||
new InternalMouseEvent(evt, state)
|
||||
);
|
||||
}
|
||||
},
|
||||
(evt: MouseEvent) => {
|
||||
if (this.isShapeEvent(state, evt)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_MOVE,
|
||||
new InternalMouseEvent(evt, getState(evt))
|
||||
);
|
||||
}
|
||||
},
|
||||
(evt: MouseEvent) => {
|
||||
if (this.isShapeEvent(state, evt)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_UP,
|
||||
new InternalMouseEvent(evt, getState(evt))
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
(evt: Event) => {
|
||||
if (this.isShapeEvent(state, evt as MouseEvent)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_MOVE,
|
||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||
);
|
||||
}
|
||||
},
|
||||
(evt: Event) => {
|
||||
if (this.isShapeEvent(state, evt as MouseEvent)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_UP,
|
||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
// Uses double click timeout in mxGraph for quirks mode
|
||||
if (graph.isNativeDblClickEnabled()) {
|
||||
// @ts-ignore
|
||||
InternalEvent.addListener(state.shape.node, 'dblclick', (evt) => {
|
||||
if (this.isShapeEvent(state, evt as MouseEvent)) {
|
||||
graph.dblClick(evt as MouseEvent, state.cell);
|
||||
InternalEvent.consume(evt);
|
||||
}
|
||||
});
|
||||
// Uses double click timeout in mxGraph for quirks mode
|
||||
if (graph.isNativeDblClickEnabled()) {
|
||||
InternalEvent.addListener(state.shape.node, 'dblclick', (evt: MouseEvent) => {
|
||||
if (this.isShapeEvent(state, evt)) {
|
||||
graph.dblClick(evt, state.cell);
|
||||
InternalEvent.consume(evt);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ import type GraphLabel from '../label/GraphLabel';
|
|||
import type GraphSnap from '../snap/GraphSnap';
|
||||
|
||||
import type { CellStateStyles } from '../../types';
|
||||
import GraphVertex from './vertex/GraphVertex';
|
||||
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
|
@ -55,8 +56,6 @@ type PartialGraph = Pick<
|
|||
| 'fireEvent'
|
||||
| 'getDefaultParent'
|
||||
| 'getCurrentRoot'
|
||||
| 'isAllowNegativeCoordinates'
|
||||
| 'setAllowNegativeCoordinates'
|
||||
| 'getOverlap'
|
||||
| 'isRecursiveResize'
|
||||
| 'getCellRenderer'
|
||||
|
@ -91,6 +90,10 @@ type PartialSnap = Pick<
|
|||
GraphSnap,
|
||||
'isGridEnabled' | 'snap' | 'getGridSize' | 'getTolerance'
|
||||
>;
|
||||
type PartialVertex = Pick<
|
||||
GraphVertex,
|
||||
'isAllowNegativeCoordinates' | 'setAllowNegativeCoordinates'
|
||||
>;
|
||||
type PartialClass = PartialGraph &
|
||||
PartialImage &
|
||||
PartialSelection &
|
||||
|
@ -99,7 +102,8 @@ type PartialClass = PartialGraph &
|
|||
PartialValidation &
|
||||
PartialFolding &
|
||||
PartialLabel &
|
||||
PartialSnap;
|
||||
PartialSnap &
|
||||
PartialVertex;
|
||||
|
||||
// @ts-ignore recursive reference error
|
||||
class GraphCells extends autoImplement<PartialClass>() {
|
||||
|
@ -1575,7 +1579,7 @@ class GraphCells extends autoImplement<PartialClass>() {
|
|||
dx: number,
|
||||
dy: number,
|
||||
target: Cell | null = null,
|
||||
evt: InternalMouseEvent | null = null,
|
||||
evt: MouseEvent | null = null,
|
||||
mapping: any = {}
|
||||
) {
|
||||
return this.moveCells(cells, dx, dy, true, target, evt, mapping);
|
||||
|
@ -1612,7 +1616,7 @@ class GraphCells extends autoImplement<PartialClass>() {
|
|||
dy: number = 0,
|
||||
clone = false,
|
||||
target: Cell | null = null,
|
||||
evt: InternalMouseEvent | null = null,
|
||||
evt: MouseEvent | null = null,
|
||||
mapping: any = null
|
||||
) {
|
||||
if (dx !== 0 || dy !== 0 || clone || target) {
|
||||
|
|
|
@ -248,7 +248,7 @@ class CellState extends Rectangle {
|
|||
* isSource - Boolean that specifies if the first or last point should
|
||||
* be assigned.
|
||||
*/
|
||||
setAbsoluteTerminalPoint(point: Point, isSource = false) {
|
||||
setAbsoluteTerminalPoint(point: Point | null, isSource = false) {
|
||||
if (isSource) {
|
||||
if (this.absolutePoints.length === 0) {
|
||||
this.absolutePoints.push(point);
|
||||
|
|
|
@ -34,6 +34,7 @@ import {
|
|||
import utils, {
|
||||
contains,
|
||||
convertPoint,
|
||||
equalPoints,
|
||||
findNearestSegment,
|
||||
getOffset,
|
||||
intersects,
|
||||
|
@ -64,6 +65,7 @@ import Cell from '../datatypes/Cell';
|
|||
import ImageBox from '../../image/ImageBox';
|
||||
import Marker from '../../geometry/shape/edge/Marker';
|
||||
import EventSource from '../../event/EventSource';
|
||||
import GraphHandler from '../../GraphHandler';
|
||||
|
||||
/**
|
||||
* Graph event handler that reconnects edges and modifies control points and the edge
|
||||
|
@ -129,11 +131,13 @@ class EdgeHandler {
|
|||
}
|
||||
}
|
||||
|
||||
const graphHandler = this.graph.getPlugin('GraphHandler') as GraphHandler;
|
||||
|
||||
// Creates bends for the non-routed absolute points
|
||||
// or bends that don't correspond to points
|
||||
if (
|
||||
this.graph.getSelectionCount() < this.graph.graphHandler.maxCells ||
|
||||
this.graph.graphHandler.maxCells <= 0
|
||||
this.graph.getSelectionCount() < graphHandler.maxCells ||
|
||||
graphHandler.maxCells <= 0
|
||||
) {
|
||||
this.bends = this.createBends();
|
||||
|
||||
|
@ -193,7 +197,7 @@ class EdgeHandler {
|
|||
* Holds the <mxConstraintHandler> used for drawing and highlighting
|
||||
* constraints.
|
||||
*/
|
||||
constraintHandler: ConstraintHandler = null;
|
||||
constraintHandler: ConstraintHandler;
|
||||
|
||||
/**
|
||||
* Variable: error
|
||||
|
@ -207,7 +211,7 @@ class EdgeHandler {
|
|||
*
|
||||
* Holds the <mxShape> that represents the preview edge.
|
||||
*/
|
||||
shape: Shape | null = null;
|
||||
shape: Shape;
|
||||
|
||||
/**
|
||||
* Variable: bends
|
||||
|
@ -223,7 +227,7 @@ class EdgeHandler {
|
|||
*
|
||||
* Holds the <mxShape> that represents the label position.
|
||||
*/
|
||||
labelShape: Shape | null = null;
|
||||
labelShape: Shape;
|
||||
|
||||
/**
|
||||
* Variable: cloneEnabled
|
||||
|
@ -370,7 +374,7 @@ class EdgeHandler {
|
|||
|
||||
isTarget: boolean = false;
|
||||
|
||||
label: Point | null = null;
|
||||
label: Point;
|
||||
|
||||
isLabel = false;
|
||||
|
||||
|
@ -492,7 +496,7 @@ class EdgeHandler {
|
|||
* Returns true if the given event is a trigger to add a new Point. This
|
||||
* implementation returns true if shift is pressed.
|
||||
*/
|
||||
isAddPointEvent(evt: Event) {
|
||||
isAddPointEvent(evt: MouseEvent) {
|
||||
return isShiftDown(evt);
|
||||
}
|
||||
|
||||
|
@ -502,7 +506,7 @@ class EdgeHandler {
|
|||
* Returns true if the given event is a trigger to remove a point. This
|
||||
* implementation returns true if shift is pressed.
|
||||
*/
|
||||
isRemovePointEvent(evt: Event) {
|
||||
isRemovePointEvent(evt: MouseEvent) {
|
||||
return isShiftDown(evt);
|
||||
}
|
||||
|
||||
|
@ -851,7 +855,7 @@ class EdgeHandler {
|
|||
*
|
||||
* bend - <mxShape> that represents the bend to be initialized.
|
||||
*/
|
||||
initBend(bend: Shape, dblClick?: EventListener) {
|
||||
initBend(bend: Shape, dblClick?: (evt: MouseEvent) => void) {
|
||||
if (this.preferHtml) {
|
||||
bend.dialect = DIALECT_STRICTHTML;
|
||||
bend.init(this.graph.container);
|
||||
|
@ -1252,7 +1256,7 @@ class EdgeHandler {
|
|||
* pt - <mxPoint> that contains the current pointer position.
|
||||
* me - Optional <mxMouseEvent> that contains the current event.
|
||||
*/
|
||||
getPreviewPoints(pt: Point, me: InternalMouseEvent) {
|
||||
getPreviewPoints(pt: Point, me?: InternalMouseEvent) {
|
||||
const geometry = this.state.cell.getGeometry();
|
||||
|
||||
if (!geometry) return null;
|
||||
|
@ -1400,7 +1404,7 @@ class EdgeHandler {
|
|||
point: Point,
|
||||
terminalState: CellState,
|
||||
me: InternalMouseEvent,
|
||||
outline: boolean
|
||||
outline = false
|
||||
) {
|
||||
// Computes the points for the edge style and terminals
|
||||
const sourceState = this.isSource
|
||||
|
@ -1436,7 +1440,7 @@ class EdgeHandler {
|
|||
this.constraintHandler.currentConstraint = constraint;
|
||||
this.constraintHandler.currentPoint = point;
|
||||
} else {
|
||||
constraint = new ConnectionConstraint();
|
||||
constraint = new ConnectionConstraint(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1716,7 +1720,7 @@ class EdgeHandler {
|
|||
model.setGeometry(cloned, geo);
|
||||
}
|
||||
|
||||
const other = edge.getTerminal(!this.isSource);
|
||||
const other = edge.getTerminal(!this.isSource) as Cell;
|
||||
this.graph.connectCell(cloned, other, !this.isSource);
|
||||
|
||||
edge = cloned;
|
||||
|
@ -1744,8 +1748,8 @@ class EdgeHandler {
|
|||
pt.y -= pstate.origin.y;
|
||||
}
|
||||
|
||||
pt.x -= this.graph.panDx / this.graph.view.scale;
|
||||
pt.y -= this.graph.panDy / this.graph.view.scale;
|
||||
pt.x -= this.graph.getPanDx() / this.graph.view.scale;
|
||||
pt.y -= this.graph.getPanDy() / this.graph.view.scale;
|
||||
|
||||
// Destroys and recreates this handler
|
||||
edge = this.changeTerminalPoint(edge, pt, this.isSource, clone);
|
||||
|
@ -1780,7 +1784,6 @@ class EdgeHandler {
|
|||
*
|
||||
* Resets the state of this handler.
|
||||
*/
|
||||
// reset(): void;
|
||||
reset() {
|
||||
if (this.active) {
|
||||
this.refresh();
|
||||
|
@ -1788,34 +1791,21 @@ class EdgeHandler {
|
|||
|
||||
this.error = null;
|
||||
this.index = null;
|
||||
this.label = null;
|
||||
this.points = null;
|
||||
this.points = [];
|
||||
this.snapPoint = null;
|
||||
this.isLabel = false;
|
||||
this.isSource = false;
|
||||
this.isTarget = false;
|
||||
this.active = false;
|
||||
|
||||
if (this.livePreview && this.sizers != null) {
|
||||
for (let i = 0; i < this.sizers.length; i += 1) {
|
||||
if (this.sizers[i] != null) {
|
||||
this.sizers[i].node.style.display = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.marker != null) {
|
||||
if (this.marker) {
|
||||
this.marker.reset();
|
||||
}
|
||||
|
||||
if (this.constraintHandler != null) {
|
||||
this.constraintHandler.reset();
|
||||
}
|
||||
this.constraintHandler.reset();
|
||||
|
||||
if (this.customHandles != null) {
|
||||
for (let i = 0; i < this.customHandles.length; i += 1) {
|
||||
this.customHandles[i].reset();
|
||||
}
|
||||
for (let i = 0; i < this.customHandles.length; i += 1) {
|
||||
this.customHandles[i].reset();
|
||||
}
|
||||
|
||||
this.setPreviewColor(EDGE_SELECTION_COLOR);
|
||||
|
@ -1829,9 +1819,7 @@ class EdgeHandler {
|
|||
* Sets the color of the preview to the given value.
|
||||
*/
|
||||
setPreviewColor(color: ColorValue) {
|
||||
if (this.shape != null) {
|
||||
this.shape.stroke = color;
|
||||
}
|
||||
this.shape.stroke = color;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1860,7 +1848,7 @@ class EdgeHandler {
|
|||
|
||||
const pstate = this.graph.getView().getState(this.state.cell.getParent());
|
||||
|
||||
if (pstate != null) {
|
||||
if (pstate) {
|
||||
point.x -= pstate.origin.x;
|
||||
point.y -= pstate.origin.y;
|
||||
}
|
||||
|
@ -1954,7 +1942,7 @@ class EdgeHandler {
|
|||
let constraint = this.constraintHandler.currentConstraint;
|
||||
|
||||
if (constraint == null) {
|
||||
constraint = new ConnectionConstraint();
|
||||
constraint = new ConnectionConstraint(null);
|
||||
}
|
||||
|
||||
this.graph.connectCell(edge, terminal, isSource, constraint);
|
||||
|
@ -1989,7 +1977,7 @@ class EdgeHandler {
|
|||
geo = geo.clone();
|
||||
geo.setTerminalPoint(point, isSource);
|
||||
model.setGeometry(edge, geo);
|
||||
this.graph.connectCell(edge, null, isSource, new ConnectionConstraint());
|
||||
this.graph.connectCell(edge, null, isSource, new ConnectionConstraint(null));
|
||||
}
|
||||
} finally {
|
||||
model.endUpdate();
|
||||
|
@ -2064,7 +2052,8 @@ class EdgeHandler {
|
|||
|
||||
if (parent.isVertex()) {
|
||||
const pState = this.graph.view.getState(parent);
|
||||
offset = new Point(pState.x, pState.y);
|
||||
|
||||
if (pState) offset = new Point(pState.x, pState.y);
|
||||
}
|
||||
|
||||
const index = findNearestSegment(state, pt.x * s + offset.x, pt.y * s + offset.y);
|
||||
|
@ -2169,7 +2158,8 @@ class EdgeHandler {
|
|||
const { cell } = this.state;
|
||||
|
||||
// Updates the handle for the label position
|
||||
let b = this.labelShape.bounds;
|
||||
let b = this.labelShape.bounds as Rectangle;
|
||||
|
||||
this.label = new Point(this.state.absoluteOffset.x, this.state.absoluteOffset.y);
|
||||
this.labelShape.bounds = new Rectangle(
|
||||
Math.round(this.label.x - b.width / 2),
|
||||
|
@ -2186,11 +2176,12 @@ class EdgeHandler {
|
|||
if (this.bends != null && this.bends.length > 0) {
|
||||
const n = this.abspoints.length - 1;
|
||||
|
||||
const p0 = this.abspoints[0];
|
||||
const p0 = this.abspoints[0] as Point;
|
||||
const x0 = p0.x;
|
||||
const y0 = p0.y;
|
||||
|
||||
b = this.bends[0].bounds;
|
||||
b = this.bends[0].bounds as Rectangle;
|
||||
|
||||
this.bends[0].bounds = new Rectangle(
|
||||
Math.floor(x0 - b.width / 2),
|
||||
Math.floor(y0 - b.height / 2),
|
||||
|
@ -2204,12 +2195,12 @@ class EdgeHandler {
|
|||
this.checkLabelHandle(this.bends[0].bounds);
|
||||
}
|
||||
|
||||
const pe = this.abspoints[n];
|
||||
const pe = this.abspoints[n] as Point;
|
||||
const xn = pe.x;
|
||||
const yn = pe.y;
|
||||
|
||||
const bn = this.bends.length - 1;
|
||||
b = this.bends[bn].bounds;
|
||||
b = this.bends[bn].bounds as Rectangle;
|
||||
this.bends[bn].bounds = new Rectangle(
|
||||
Math.floor(xn - b.width / 2),
|
||||
Math.floor(yn - b.height / 2),
|
||||
|
@ -2220,56 +2211,54 @@ class EdgeHandler {
|
|||
this.bends[bn].redraw();
|
||||
|
||||
if (this.manageLabelHandle) {
|
||||
this.checkLabelHandle(this.bends[bn].bounds);
|
||||
this.checkLabelHandle(this.bends[bn].bounds as Rectangle);
|
||||
}
|
||||
|
||||
this.redrawInnerBends(p0, pe);
|
||||
}
|
||||
|
||||
if (
|
||||
this.abspoints != null &&
|
||||
this.virtualBends != null &&
|
||||
this.virtualBends.length > 0
|
||||
) {
|
||||
let last = this.abspoints[0];
|
||||
if (this.virtualBends.length > 0) {
|
||||
let last = this.abspoints[0] as Point;
|
||||
|
||||
for (let i = 0; i < this.virtualBends.length; i += 1) {
|
||||
if (this.virtualBends[i] != null && this.abspoints[i + 1] != null) {
|
||||
const pt = this.abspoints[i + 1];
|
||||
b = this.virtualBends[i];
|
||||
const pt = this.abspoints[i + 1] as Point;
|
||||
const b = this.virtualBends[i];
|
||||
const x = last.x + (pt.x - last.x) / 2;
|
||||
const y = last.y + (pt.y - last.y) / 2;
|
||||
b.bounds = new Rectangle(
|
||||
Math.floor(x - b.bounds.width / 2),
|
||||
Math.floor(y - b.bounds.height / 2),
|
||||
b.bounds.width,
|
||||
b.bounds.height
|
||||
);
|
||||
b.redraw();
|
||||
|
||||
if (b.bounds) {
|
||||
b.bounds = new Rectangle(
|
||||
Math.floor(x - b.bounds.width / 2),
|
||||
Math.floor(y - b.bounds.height / 2),
|
||||
b.bounds.width,
|
||||
b.bounds.height
|
||||
);
|
||||
b.redraw();
|
||||
}
|
||||
|
||||
setOpacity(b.node, this.virtualBendOpacity);
|
||||
last = pt;
|
||||
|
||||
if (this.manageLabelHandle) {
|
||||
this.checkLabelHandle(b.bounds);
|
||||
this.checkLabelHandle(b.bounds as Rectangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.labelShape != null) {
|
||||
this.labelShape.redraw();
|
||||
}
|
||||
this.labelShape.redraw();
|
||||
|
||||
if (this.customHandles != null) {
|
||||
for (let i = 0; i < this.customHandles.length; i += 1) {
|
||||
const temp = this.customHandles[i].shape.node.style.display;
|
||||
for (let i = 0; i < this.customHandles.length; i += 1) {
|
||||
const shape = this.customHandles[i].shape;
|
||||
|
||||
if (shape) {
|
||||
const temp = shape.node.style.display;
|
||||
this.customHandles[i].redraw();
|
||||
this.customHandles[i].shape.node.style.display = temp;
|
||||
shape.node.style.display = temp;
|
||||
|
||||
// Hides custom handles during text editing
|
||||
this.customHandles[i].shape.node.style.visibility = this.isCustomHandleVisible(
|
||||
this.customHandles[i]
|
||||
)
|
||||
shape.node.style.visibility = this.isCustomHandleVisible(this.customHandles[i])
|
||||
? ''
|
||||
: 'hidden';
|
||||
}
|
||||
|
@ -2291,26 +2280,18 @@ class EdgeHandler {
|
|||
* Shortcut to <hideSizers>.
|
||||
*/
|
||||
setHandlesVisible(visible: boolean) {
|
||||
if (this.bends != null) {
|
||||
for (let i = 0; i < this.bends.length; i += 1) {
|
||||
this.bends[i].node.style.display = visible ? '' : 'none';
|
||||
}
|
||||
for (let i = 0; i < this.bends.length; i += 1) {
|
||||
this.bends[i].node.style.display = visible ? '' : 'none';
|
||||
}
|
||||
|
||||
if (this.virtualBends != null) {
|
||||
for (let i = 0; i < this.virtualBends.length; i += 1) {
|
||||
this.virtualBends[i].node.style.display = visible ? '' : 'none';
|
||||
}
|
||||
for (let i = 0; i < this.virtualBends.length; i += 1) {
|
||||
this.virtualBends[i].node.style.display = visible ? '' : 'none';
|
||||
}
|
||||
|
||||
if (this.labelShape != null) {
|
||||
this.labelShape.node.style.display = visible ? '' : 'none';
|
||||
}
|
||||
this.labelShape.node.style.display = visible ? '' : 'none';
|
||||
|
||||
if (this.customHandles != null) {
|
||||
for (let i = 0; i < this.customHandles.length; i += 1) {
|
||||
this.customHandles[i].setVisible(visible);
|
||||
}
|
||||
for (let i = 0; i < this.customHandles.length; i += 1) {
|
||||
this.customHandles[i].setVisible(visible);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2328,10 +2309,10 @@ class EdgeHandler {
|
|||
for (let i = 1; i < this.bends.length - 1; i += 1) {
|
||||
if (this.bends[i] != null) {
|
||||
if (this.abspoints[i] != null) {
|
||||
const { x } = this.abspoints[i];
|
||||
const { y } = this.abspoints[i];
|
||||
const { x } = this.abspoints[i] as Point;
|
||||
const { y } = this.abspoints[i] as Point;
|
||||
|
||||
const b = this.bends[i].bounds;
|
||||
const b = this.bends[i].bounds as Rectangle;
|
||||
this.bends[i].node.style.visibility = 'visible';
|
||||
this.bends[i].bounds = new Rectangle(
|
||||
Math.round(x - b.width / 2),
|
||||
|
@ -2341,11 +2322,14 @@ class EdgeHandler {
|
|||
);
|
||||
|
||||
if (this.manageLabelHandle) {
|
||||
this.checkLabelHandle(this.bends[i].bounds);
|
||||
this.checkLabelHandle(this.bends[i].bounds as Rectangle);
|
||||
} else if (
|
||||
this.handleImage == null &&
|
||||
this.labelShape.visible &&
|
||||
intersects(this.bends[i].bounds, this.labelShape.bounds)
|
||||
intersects(
|
||||
this.bends[i].bounds as Rectangle,
|
||||
this.labelShape.bounds as Rectangle
|
||||
)
|
||||
) {
|
||||
const w = HANDLE_SIZE + 3;
|
||||
const h = HANDLE_SIZE + 3;
|
||||
|
@ -2360,7 +2344,6 @@ class EdgeHandler {
|
|||
this.bends[i].redraw();
|
||||
} else {
|
||||
this.bends[i].destroy();
|
||||
this.bends[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2373,15 +2356,13 @@ class EdgeHandler {
|
|||
* intersects.
|
||||
*/
|
||||
checkLabelHandle(b: Rectangle) {
|
||||
if (this.labelShape != null) {
|
||||
const b2 = this.labelShape.bounds;
|
||||
const b2 = this.labelShape.bounds as Rectangle;
|
||||
|
||||
if (intersects(b, b2)) {
|
||||
if (b.getCenterY() < b2.getCenterY()) {
|
||||
b2.y = b.y + b.height;
|
||||
} else {
|
||||
b2.y = b.y - b2.height;
|
||||
}
|
||||
if (intersects(b, b2)) {
|
||||
if (b.getCenterY() < b2.getCenterY()) {
|
||||
b2.y = b.y + b.height;
|
||||
} else {
|
||||
b2.y = b.y - b2.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2394,7 +2375,7 @@ class EdgeHandler {
|
|||
drawPreview() {
|
||||
try {
|
||||
if (this.isLabel) {
|
||||
const b = this.labelShape.bounds;
|
||||
const b = this.labelShape.bounds as Rectangle;
|
||||
const bounds = new Rectangle(
|
||||
Math.round(this.label.x - b.width / 2),
|
||||
Math.round(this.label.y - b.height / 2),
|
||||
|
@ -2402,7 +2383,7 @@ class EdgeHandler {
|
|||
b.height
|
||||
);
|
||||
|
||||
if (!this.labelShape.bounds.equals(bounds)) {
|
||||
if (!b.equals(bounds)) {
|
||||
this.labelShape.bounds = bounds;
|
||||
this.labelShape.redraw();
|
||||
}
|
||||
|
@ -2436,20 +2417,14 @@ class EdgeHandler {
|
|||
this.abspoints = this.getSelectionPoints(this.state);
|
||||
this.points = [];
|
||||
|
||||
if (this.bends != null) {
|
||||
this.destroyBends(this.bends);
|
||||
this.bends = this.createBends();
|
||||
}
|
||||
this.destroyBends(this.bends);
|
||||
this.bends = this.createBends();
|
||||
|
||||
if (this.virtualBends != null) {
|
||||
this.destroyBends(this.virtualBends);
|
||||
this.virtualBends = this.createVirtualBends();
|
||||
}
|
||||
this.destroyBends(this.virtualBends);
|
||||
this.virtualBends = this.createVirtualBends();
|
||||
|
||||
if (this.customHandles != null) {
|
||||
this.destroyBends(this.customHandles);
|
||||
this.customHandles = this.createCustomHandles();
|
||||
}
|
||||
this.destroyBends(this.customHandles);
|
||||
this.customHandles = this.createCustomHandles();
|
||||
|
||||
// Puts label node on top of bends
|
||||
if (
|
||||
|
@ -2476,7 +2451,7 @@ class EdgeHandler {
|
|||
*
|
||||
* Destroys all elements in <bends>.
|
||||
*/
|
||||
destroyBends(bends: Shape[]) {
|
||||
destroyBends(bends: Shape[] | CellHandle[]) {
|
||||
if (bends != null) {
|
||||
for (let i = 0; i < bends.length; i += 1) {
|
||||
if (bends[i] != null) {
|
||||
|
@ -2495,26 +2470,17 @@ class EdgeHandler {
|
|||
*/
|
||||
// destroy(): void;
|
||||
destroy() {
|
||||
if (this.escapeHandler != null) {
|
||||
this.state.view.graph.removeListener(this.escapeHandler);
|
||||
this.escapeHandler = null;
|
||||
}
|
||||
this.state.view.graph.removeListener(this.escapeHandler);
|
||||
|
||||
if (this.marker != null) {
|
||||
this.marker.destroy();
|
||||
this.marker = null;
|
||||
}
|
||||
this.marker.destroy();
|
||||
|
||||
if (this.shape != null) {
|
||||
this.shape.destroy();
|
||||
this.shape = null;
|
||||
}
|
||||
this.shape.destroy();
|
||||
|
||||
if (this.parentHighlight != null) {
|
||||
if (this.parentHighlight) {
|
||||
const parent = this.state.cell.getParent();
|
||||
const pstate = this.graph.view.getState(parent);
|
||||
|
||||
if (pstate != null && pstate.parentHighlight === this.parentHighlight) {
|
||||
if (pstate && pstate.parentHighlight === this.parentHighlight) {
|
||||
pstate.parentHighlight = null;
|
||||
}
|
||||
|
||||
|
@ -2522,21 +2488,15 @@ class EdgeHandler {
|
|||
this.parentHighlight = null;
|
||||
}
|
||||
|
||||
if (this.labelShape != null) {
|
||||
this.labelShape.destroy();
|
||||
this.labelShape = null;
|
||||
}
|
||||
this.labelShape.destroy();
|
||||
|
||||
if (this.constraintHandler != null) {
|
||||
this.constraintHandler.destroy();
|
||||
this.constraintHandler = null;
|
||||
}
|
||||
this.constraintHandler.destroy();
|
||||
|
||||
this.destroyBends(this.virtualBends);
|
||||
this.virtualBends = null;
|
||||
this.virtualBends = [];
|
||||
|
||||
this.destroyBends(this.customHandles);
|
||||
this.customHandles = null;
|
||||
this.customHandles = [];
|
||||
|
||||
this.destroyBends(this.bends);
|
||||
this.bends = [];
|
||||
|
|
|
@ -7,49 +7,43 @@
|
|||
import Point from '../../geometry/Point';
|
||||
import { CURSOR_TERMINAL_HANDLE } from '../../../util/Constants';
|
||||
import Rectangle from '../../geometry/Rectangle';
|
||||
import utils, { contains, setOpacity } from '../../../util/Utils';
|
||||
import { contains, setOpacity } from '../../../util/Utils';
|
||||
import ElbowEdgeHandler from './ElbowEdgeHandler';
|
||||
import CellState from '../datatypes/CellState';
|
||||
import Cell from '../datatypes/Cell';
|
||||
import InternalMouseEvent from '../../event/InternalMouseEvent';
|
||||
|
||||
class EdgeSegmentHandler extends ElbowEdgeHandler {
|
||||
constructor(state: CellState) {
|
||||
// WARNING: should be super of mxEdgeHandler!
|
||||
super(state);
|
||||
}
|
||||
|
||||
points: Point[] | null = null;
|
||||
points: Point[] = [];
|
||||
|
||||
/**
|
||||
* Function: getCurrentPoints
|
||||
*
|
||||
* Returns the current absolute points.
|
||||
*/
|
||||
getCurrentPoints(): Point[] {
|
||||
getCurrentPoints() {
|
||||
let pts = this.state.absolutePoints;
|
||||
|
||||
if (pts != null) {
|
||||
// Special case for straight edges where we add a virtual middle handle for moving the edge
|
||||
const tol = Math.max(1, this.graph.view.scale);
|
||||
// Special case for straight edges where we add a virtual middle handle for moving the edge
|
||||
const tol = Math.max(1, this.graph.view.scale);
|
||||
|
||||
if (
|
||||
pts.length === 2 ||
|
||||
(pts.length === 3 &&
|
||||
((Math.abs(pts[0].x - pts[1].x) < tol &&
|
||||
Math.abs(pts[1].x - pts[2].x) < tol) ||
|
||||
(Math.abs(pts[0].y - pts[1].y) < tol &&
|
||||
Math.abs(pts[1].y - pts[2].y) < tol)))
|
||||
) {
|
||||
const cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
|
||||
const cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
|
||||
if (
|
||||
(pts.length === 2 && pts[0] && pts[1]) ||
|
||||
(pts.length === 3 &&
|
||||
pts[0] &&
|
||||
pts[1] &&
|
||||
pts[2] &&
|
||||
((Math.abs(pts[0].x - pts[1].x) < tol && Math.abs(pts[1].x - pts[2].x) < tol) ||
|
||||
(Math.abs(pts[0].y - pts[1].y) < tol && Math.abs(pts[1].y - pts[2].y) < tol)))
|
||||
) {
|
||||
const cx = pts[0].x + (pts[pts.length - 1]!.x - pts[0].x) / 2;
|
||||
const cy = pts[0].y + (pts[pts.length - 1]!.y - pts[0].y) / 2;
|
||||
|
||||
pts = [
|
||||
pts[0],
|
||||
new Point(cx, cy),
|
||||
new Point(cx, cy),
|
||||
pts[pts.length - 1],
|
||||
];
|
||||
}
|
||||
pts = [pts[0], new Point(cx, cy), new Point(cx, cy), pts[pts.length - 1]];
|
||||
}
|
||||
|
||||
return pts;
|
||||
|
@ -60,17 +54,17 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
*
|
||||
* Updates the given preview state taking into account the state of the constraint handler.
|
||||
*/
|
||||
getPreviewPoints(point: Point): Point[] {
|
||||
getPreviewPoints(point: Point) {
|
||||
if (this.isSource || this.isTarget) {
|
||||
return super.getPreviewPoints(point);
|
||||
}
|
||||
const pts = this.getCurrentPoints();
|
||||
let last = this.convertPoint(pts[0].clone(), false);
|
||||
let last = this.convertPoint(pts[0]!.clone(), false);
|
||||
point = this.convertPoint(point.clone(), false);
|
||||
let result = [];
|
||||
let result: Point[] = [];
|
||||
|
||||
for (let i = 1; i < pts.length; i += 1) {
|
||||
const pt = this.convertPoint(pts[i].clone(), false);
|
||||
const pt = this.convertPoint(pts[i]!.clone(), false);
|
||||
|
||||
if (i === this.index) {
|
||||
if (Math.round(last.x - pt.x) === 0) {
|
||||
|
@ -117,29 +111,29 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
*
|
||||
* Overridden to perform optimization of the edge style result.
|
||||
*/
|
||||
updatePreviewState(edge: Cell,
|
||||
point: Point,
|
||||
terminalState: CellState,
|
||||
me: MouseEvent): void {
|
||||
|
||||
updatePreviewState(
|
||||
edge: CellState,
|
||||
point: Point,
|
||||
terminalState: CellState,
|
||||
me: InternalMouseEvent
|
||||
): void {
|
||||
super.updatePreviewState(edge, point, terminalState, me);
|
||||
|
||||
// Checks and corrects preview by running edge style again
|
||||
if (!this.isSource && !this.isTarget) {
|
||||
point = this.convertPoint(point.clone(), false);
|
||||
const pts = edge.absolutePoints;
|
||||
let pt0 = pts[0];
|
||||
let pt1 = pts[1];
|
||||
let pt0 = pts[0] as Point;
|
||||
let pt1 = pts[1] as Point;
|
||||
|
||||
let result = [];
|
||||
|
||||
for (let i = 2; i < pts.length; i += 1) {
|
||||
const pt2 = pts[i];
|
||||
const pt2 = pts[i] as Point;
|
||||
|
||||
// Merges adjacent segments only if more than 2 to allow for straight edges
|
||||
if (
|
||||
(Math.round(pt0.x - pt1.x) !== 0 ||
|
||||
Math.round(pt1.x - pt2.x) !== 0) &&
|
||||
(Math.round(pt0.x - pt1.x) !== 0 || Math.round(pt1.x - pt2.x) !== 0) &&
|
||||
(Math.round(pt0.y - pt1.y) !== 0 || Math.round(pt1.y - pt2.y) !== 0)
|
||||
) {
|
||||
result.push(this.convertPoint(pt1.clone(), false));
|
||||
|
@ -153,11 +147,14 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
const target = this.state.getVisibleTerminalState(false);
|
||||
const rpts = this.state.absolutePoints;
|
||||
|
||||
const end = pts[pts.length - 1];
|
||||
|
||||
// A straight line is represented by 3 handles
|
||||
if (
|
||||
result.length === 0 &&
|
||||
(Math.round(pts[0].x - pts[pts.length - 1].x) === 0 ||
|
||||
Math.round(pts[0].y - pts[pts.length - 1].y) === 0)
|
||||
pts[0] &&
|
||||
end &&
|
||||
(Math.round(pts[0].x - end.x) === 0 || Math.round(pts[0].y - end.y) === 0)
|
||||
) {
|
||||
result = [point, point];
|
||||
}
|
||||
|
@ -168,7 +165,7 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
source != null &&
|
||||
target != null &&
|
||||
rpts != null &&
|
||||
Math.round(rpts[0].x - rpts[rpts.length - 1].x) === 0
|
||||
Math.round(rpts[0]!.x - rpts[rpts.length - 1]!.x) === 0
|
||||
) {
|
||||
const view = this.graph.getView();
|
||||
const scale = view.getScale();
|
||||
|
@ -191,10 +188,10 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
let ye = view.getRoutingCenterY(target) / scale - tr.y;
|
||||
|
||||
// Use fixed connection point y-coordinate if one exists
|
||||
const tc = this.graph.connection.getConnectionConstraint(edge, target, false);
|
||||
const tc = this.graph.getConnectionConstraint(edge, target, false);
|
||||
|
||||
if (tc) {
|
||||
const pt = this.graph.connection.getConnectionPoint(target, tc);
|
||||
const pt = this.graph.getConnectionPoint(target, tc);
|
||||
|
||||
if (pt != null) {
|
||||
this.convertPoint(pt, false);
|
||||
|
@ -217,15 +214,16 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
/**
|
||||
* Overriden to merge edge segments.
|
||||
*/
|
||||
connect(edge: Cell,
|
||||
terminal: Cell,
|
||||
isSource: boolean,
|
||||
isClone: boolean,
|
||||
me: MouseEvent): Cell {
|
||||
|
||||
connect(
|
||||
edge: Cell,
|
||||
terminal: Cell,
|
||||
isSource: boolean,
|
||||
isClone: boolean,
|
||||
me: InternalMouseEvent
|
||||
) {
|
||||
const model = this.graph.getModel();
|
||||
let geo = edge.getGeometry();
|
||||
let result = null;
|
||||
let result: Point[] | null = null;
|
||||
|
||||
// Merges adjacent edge segments
|
||||
if (geo != null && geo.points != null && geo.points.length > 0) {
|
||||
|
@ -239,8 +237,10 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
|
||||
// Merges adjacent segments only if more than 2 to allow for straight edges
|
||||
if (
|
||||
(Math.round(pt0.x - pt1.x) !== 0 ||
|
||||
Math.round(pt1.x - pt2.x) !== 0) &&
|
||||
pt0 &&
|
||||
pt1 &&
|
||||
pt2 &&
|
||||
(Math.round(pt0.x - pt1.x) !== 0 || Math.round(pt1.x - pt2.x) !== 0) &&
|
||||
(Math.round(pt0.y - pt1.y) !== 0 || Math.round(pt1.y - pt2.y) !== 0)
|
||||
) {
|
||||
result.push(this.convertPoint(pt1.clone(), false));
|
||||
|
@ -273,7 +273,7 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
*
|
||||
* Returns no tooltips.
|
||||
*/
|
||||
getTooltipForNode(node: any): string {
|
||||
getTooltipForNode(node: Element): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -282,8 +282,7 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
*
|
||||
* Adds custom bends for the center of each segment.
|
||||
*/
|
||||
// start(x: number, y: number, index: number): void;
|
||||
start(x: number, y: number, index: number): void {
|
||||
start(x: number, y: number, index: number) {
|
||||
super.start(x, y, index);
|
||||
|
||||
if (
|
||||
|
@ -321,11 +320,11 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
for (let i = 0; i < pts.length - 1; i += 1) {
|
||||
bend = this.createVirtualBend();
|
||||
bends.push(bend);
|
||||
let horizontal = Math.round(pts[i].x - pts[i + 1].x) === 0;
|
||||
let horizontal = Math.round(pts[i]!.x - pts[i + 1]!.x) === 0;
|
||||
|
||||
// Special case where dy is 0 as well
|
||||
if (Math.round(pts[i].y - pts[i + 1].y) === 0 && i < pts.length - 2) {
|
||||
horizontal = Math.round(pts[i].x - pts[i + 2].x) === 0;
|
||||
if (Math.round(pts[i]!.y - pts[i + 1]!.y) === 0 && i < pts.length - 2) {
|
||||
horizontal = Math.round(pts[i]!.x - pts[i + 2]!.x) === 0;
|
||||
}
|
||||
|
||||
bend.setCursor(horizontal ? 'col-resize' : 'row-resize');
|
||||
|
@ -347,7 +346,7 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
*
|
||||
* Overridden to invoke <refresh> before the redraw.
|
||||
*/
|
||||
redraw(): void {
|
||||
redraw() {
|
||||
this.refresh();
|
||||
super.redraw();
|
||||
}
|
||||
|
@ -357,7 +356,7 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
*
|
||||
* Updates the position of the custom bends.
|
||||
*/
|
||||
redrawInnerBends(p0: Point, pe: Point): void {
|
||||
redrawInnerBends(p0: Point, pe: Point) {
|
||||
if (this.graph.isCellBendable(this.state.cell)) {
|
||||
const pts = this.getCurrentPoints();
|
||||
|
||||
|
@ -367,17 +366,21 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
// Puts handle in the center of straight edges
|
||||
if (
|
||||
pts.length === 4 &&
|
||||
pts[0] &&
|
||||
pts[1] &&
|
||||
pts[2] &&
|
||||
pts[3] &&
|
||||
Math.round(pts[1].x - pts[2].x) === 0 &&
|
||||
Math.round(pts[1].y - pts[2].y) === 0
|
||||
) {
|
||||
straight = true;
|
||||
|
||||
if (Math.round(pts[0].y - pts[pts.length - 1].y) === 0) {
|
||||
const cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
|
||||
if (Math.round(pts[0].y - pts[pts.length - 1]!.y) === 0) {
|
||||
const cx = pts[0].x + (pts[pts.length - 1]!.x - pts[0].x) / 2;
|
||||
pts[1] = new Point(cx, pts[1].y);
|
||||
pts[2] = new Point(cx, pts[2].y);
|
||||
} else {
|
||||
const cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
|
||||
const cy = pts[0].y + (pts[pts.length - 1]!.y - pts[0].y) / 2;
|
||||
pts[1] = new Point(pts[1].x, cy);
|
||||
pts[2] = new Point(pts[2].x, cy);
|
||||
}
|
||||
|
@ -385,13 +388,10 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
|
||||
for (let i = 0; i < pts.length - 1; i += 1) {
|
||||
if (this.bends[i + 1] != null) {
|
||||
p0 = pts[i];
|
||||
pe = pts[i + 1];
|
||||
const pt = new Point(
|
||||
p0.x + (pe.x - p0.x) / 2,
|
||||
p0.y + (pe.y - p0.y) / 2
|
||||
);
|
||||
const b = this.bends[i + 1].bounds;
|
||||
p0 = pts[i] as Point;
|
||||
pe = pts[i + 1] as Point;
|
||||
const pt = new Point(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2);
|
||||
const b = this.bends[i + 1].bounds as Rectangle;
|
||||
this.bends[i + 1].bounds = new Rectangle(
|
||||
Math.floor(pt.x - b.width / 2),
|
||||
Math.floor(pt.y - b.height / 2),
|
||||
|
@ -401,7 +401,7 @@ class EdgeSegmentHandler extends ElbowEdgeHandler {
|
|||
this.bends[i + 1].redraw();
|
||||
|
||||
if (this.manageLabelHandle) {
|
||||
this.checkLabelHandle(this.bends[i + 1].bounds);
|
||||
this.checkLabelHandle(this.bends[i + 1].bounds as Rectangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import Rectangle from '../../geometry/Rectangle';
|
|||
import utils, { intersects } from '../../../util/Utils';
|
||||
import mxClient from '../../../mxClient';
|
||||
import { isConsumed } from '../../../util/EventUtils';
|
||||
import CellState from '../datatypes/CellState';
|
||||
import InternalMouseEvent from '../../event/InternalMouseEvent';
|
||||
|
||||
/**
|
||||
* Class: mxElbowEdgeHandler
|
||||
|
@ -38,7 +40,7 @@ import { isConsumed } from '../../../util/EventUtils';
|
|||
* state - <mxCellState> of the cell to be modified.
|
||||
*/
|
||||
class ElbowEdgeHandler extends EdgeHandler {
|
||||
constructor(state) {
|
||||
constructor(state: CellState) {
|
||||
super(state);
|
||||
}
|
||||
|
||||
|
@ -46,7 +48,6 @@ class ElbowEdgeHandler extends EdgeHandler {
|
|||
* Specifies if a double click on the middle handle should call
|
||||
* <mxGraph.flipEdge>. Default is true.
|
||||
*/
|
||||
// flipEnabled: boolean;
|
||||
flipEnabled = true;
|
||||
|
||||
/**
|
||||
|
@ -77,9 +78,9 @@ class ElbowEdgeHandler extends EdgeHandler {
|
|||
|
||||
// Virtual
|
||||
bends.push(
|
||||
this.createVirtualBend((evt) => {
|
||||
this.createVirtualBend((evt: MouseEvent) => {
|
||||
if (!isConsumed(evt) && this.flipEnabled) {
|
||||
this.graph.flipEdge(this.state.cell, evt);
|
||||
this.graph.flipEdge(this.state.cell);
|
||||
InternalEvent.consume(evt);
|
||||
}
|
||||
})
|
||||
|
@ -102,8 +103,7 @@ class ElbowEdgeHandler extends EdgeHandler {
|
|||
* Creates a virtual bend that supports double clicking and calls
|
||||
* <mxGraph.flipEdge>.
|
||||
*/
|
||||
// createVirtualBend(dblClickHandler: (evt: Event) => void): mxRectangleShape;
|
||||
createVirtualBend(dblClickHandler) {
|
||||
createVirtualBend(dblClickHandler?: (evt: MouseEvent) => void) {
|
||||
const bend = this.createHandleShape();
|
||||
this.initBend(bend, dblClickHandler);
|
||||
|
||||
|
@ -121,12 +121,9 @@ class ElbowEdgeHandler extends EdgeHandler {
|
|||
*
|
||||
* Returns the cursor to be used for the bend.
|
||||
*/
|
||||
// getCursorForBend(): string;
|
||||
getCursorForBend() {
|
||||
return this.state.style.edge === mxEdgeStyle.TopToBottom ||
|
||||
this.state.style.edge === EDGESTYLE_TOPTOBOTTOM ||
|
||||
((this.state.style.edge === mxEdgeStyle.ElbowConnector ||
|
||||
this.state.style.edge === EDGESTYLE_ELBOW) &&
|
||||
return this.state.style.edge === EDGESTYLE_TOPTOBOTTOM ||
|
||||
(this.state.style.edge === EDGESTYLE_ELBOW &&
|
||||
this.state.style.elbow === ELBOW_VERTICAL)
|
||||
? 'row-resize'
|
||||
: 'col-resize';
|
||||
|
@ -137,8 +134,7 @@ class ElbowEdgeHandler extends EdgeHandler {
|
|||
*
|
||||
* Returns the tooltip for the given node.
|
||||
*/
|
||||
// getTooltipForNode(node: Element): string;
|
||||
getTooltipForNode(node) {
|
||||
getTooltipForNode(node: Element) {
|
||||
let tip = null;
|
||||
|
||||
if (
|
||||
|
@ -164,8 +160,7 @@ class ElbowEdgeHandler extends EdgeHandler {
|
|||
* point - <mxPoint> to be converted.
|
||||
* gridEnabled - Boolean that specifies if the grid should be applied.
|
||||
*/
|
||||
// convertPoint(point: mxPoint, gridEnabled: boolean): mxPoint;
|
||||
convertPoint(point, gridEnabled) {
|
||||
convertPoint(point: Point, gridEnabled: boolean) {
|
||||
const scale = this.graph.getView().getScale();
|
||||
const tr = this.graph.getView().getTranslate();
|
||||
const { origin } = this.state;
|
||||
|
@ -191,17 +186,16 @@ class ElbowEdgeHandler extends EdgeHandler {
|
|||
* p0 - <mxPoint> that represents the location of the first point.
|
||||
* pe - <mxPoint> that represents the location of the last point.
|
||||
*/
|
||||
// redrawInnerBends(p0: mxPoint, pe: mxPoint): void;
|
||||
redrawInnerBends(p0, pe) {
|
||||
redrawInnerBends(p0: Point, pe: Point) {
|
||||
const g = this.state.cell.getGeometry();
|
||||
const pts = this.state.absolutePoints;
|
||||
let pt = null;
|
||||
|
||||
// Keeps the virtual bend on the edge shape
|
||||
if (pts.length > 1) {
|
||||
p0 = pts[1];
|
||||
pe = pts[pts.length - 2];
|
||||
} else if (g.points != null && g.points.length > 0) {
|
||||
p0 = pts[1] as Point;
|
||||
pe = pts[pts.length - 2] as Point;
|
||||
} else if (g!.points != null && g!.points.length > 0) {
|
||||
pt = pts[0];
|
||||
}
|
||||
|
||||
|
@ -219,30 +213,21 @@ class ElbowEdgeHandler extends EdgeHandler {
|
|||
// Makes handle slightly bigger if the yellow label handle
|
||||
// exists and intersects this green handle
|
||||
const b = this.bends[1].bounds;
|
||||
let w = b.width;
|
||||
let h = b.height;
|
||||
let bounds = new Rectangle(
|
||||
Math.round(pt.x - w / 2),
|
||||
Math.round(pt.y - h / 2),
|
||||
w,
|
||||
h
|
||||
);
|
||||
let w = b!.width;
|
||||
let h = b!.height;
|
||||
let bounds = new Rectangle(Math.round(pt.x - w / 2), Math.round(pt.y - h / 2), w, h);
|
||||
|
||||
if (this.manageLabelHandle) {
|
||||
this.checkLabelHandle(bounds);
|
||||
} else if (
|
||||
this.handleImage == null &&
|
||||
this.labelShape.visible &&
|
||||
this.labelShape.bounds &&
|
||||
intersects(bounds, this.labelShape.bounds)
|
||||
) {
|
||||
w = HANDLE_SIZE + 3;
|
||||
h = HANDLE_SIZE + 3;
|
||||
bounds = new Rectangle(
|
||||
Math.floor(pt.x - w / 2),
|
||||
Math.floor(pt.y - h / 2),
|
||||
w,
|
||||
h
|
||||
);
|
||||
bounds = new Rectangle(Math.floor(pt.x - w / 2), Math.floor(pt.y - h / 2), w, h);
|
||||
}
|
||||
|
||||
this.bends[1].bounds = bounds;
|
||||
|
|
|
@ -232,7 +232,7 @@ class GraphEdge extends autoImplement<PartialClass>() {
|
|||
splitEdge(
|
||||
edge: Cell,
|
||||
cells: CellArray,
|
||||
newEdge: Cell,
|
||||
newEdge: Cell | null,
|
||||
dx = 0,
|
||||
dy = 0,
|
||||
x: number,
|
||||
|
@ -311,7 +311,7 @@ class GraphEdge extends autoImplement<PartialClass>() {
|
|||
let value: any; // note me - can be a string or a class instance!!!
|
||||
let source: Cell;
|
||||
let target: Cell;
|
||||
let style: string; // TODO: Also allow for an object or class instance??
|
||||
let style: string = ''; // TODO: Also allow for an object or class instance??
|
||||
|
||||
if (args.length === 1) {
|
||||
// If only a single parameter, treat as an object
|
||||
|
|
|
@ -13,13 +13,27 @@ class GraphVertex extends autoImplement<PartialClass>() {
|
|||
* Specifies the return value for vertices in {@link isLabelMovable}.
|
||||
* @default false
|
||||
*/
|
||||
vertexLabelsMovable: boolean = false;
|
||||
vertexLabelsMovable = false;
|
||||
|
||||
/**
|
||||
* Specifies if negative coordinates for vertices are allowed.
|
||||
* @default true
|
||||
*/
|
||||
allowNegativeCoordinates: boolean = true;
|
||||
allowNegativeCoordinates = true;
|
||||
|
||||
/**
|
||||
* Returns {@link allowNegativeCoordinates}.
|
||||
*/
|
||||
isAllowNegativeCoordinates() {
|
||||
return this.allowNegativeCoordinates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link allowNegativeCoordinates}.
|
||||
*/
|
||||
setAllowNegativeCoordinates(value: boolean) {
|
||||
this.allowNegativeCoordinates = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function: insertVertex
|
||||
|
@ -64,7 +78,7 @@ class GraphVertex extends autoImplement<PartialClass>() {
|
|||
* geometryClass - Optional class reference to a class derived from mxGeometry.
|
||||
* This can be useful for defining custom constraints.
|
||||
*/
|
||||
insertVertex = (...args: any[]): Cell => {
|
||||
insertVertex = (...args: any[]) => {
|
||||
let parent;
|
||||
let id;
|
||||
let value;
|
||||
|
@ -147,7 +161,7 @@ class GraphVertex extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param parent {@link mxCell} whose children should be returned.
|
||||
*/
|
||||
getChildVertices(parent: Cell): CellArray {
|
||||
getChildVertices(parent: Cell) {
|
||||
return this.getChildCells(parent, true, false);
|
||||
}
|
||||
|
||||
|
@ -158,14 +172,14 @@ class GraphVertex extends autoImplement<PartialClass>() {
|
|||
/**
|
||||
* Returns {@link vertexLabelsMovable}.
|
||||
*/
|
||||
isVertexLabelsMovable(): boolean {
|
||||
isVertexLabelsMovable() {
|
||||
return this.vertexLabelsMovable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link vertexLabelsMovable}.
|
||||
*/
|
||||
setVertexLabelsMovable(value: boolean): void {
|
||||
setVertexLabelsMovable(value: boolean) {
|
||||
this.vertexLabelsMovable = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ import CellArray from '../datatypes/CellArray';
|
|||
import EdgeHandler from '../edge/EdgeHandler';
|
||||
import CellHighlight from '../../selection/CellHighlight';
|
||||
import EventSource from '../../event/EventSource';
|
||||
import GraphHandler from '../../GraphHandler';
|
||||
import SelectionCellsHandler from '../../selection/SelectionCellsHandler';
|
||||
|
||||
/**
|
||||
* Class: mxVertexHandler
|
||||
|
@ -82,10 +84,12 @@ class VertexHandler {
|
|||
this.selectionBorder.setCursor(CURSOR_MOVABLE_VERTEX);
|
||||
}
|
||||
|
||||
const graphHandler = this.graph.getPlugin('GraphHandler') as GraphHandler;
|
||||
|
||||
// Adds the sizer handles
|
||||
if (
|
||||
this.graph.graphHandler.maxCells <= 0 ||
|
||||
this.graph.getSelectionCount() < this.graph.graphHandler.maxCells
|
||||
graphHandler.maxCells <= 0 ||
|
||||
this.graph.getSelectionCount() < graphHandler.maxCells
|
||||
) {
|
||||
const resizable = this.graph.isCellResizable(this.state.cell);
|
||||
this.sizers = [];
|
||||
|
@ -374,12 +378,14 @@ class VertexHandler {
|
|||
* Returns true if the rotation handle should be showing.
|
||||
*/
|
||||
isRotationHandleVisible() {
|
||||
const graphHandler = this.graph.getPlugin('GraphHandler') as GraphHandler;
|
||||
|
||||
return (
|
||||
this.graph.isEnabled() &&
|
||||
this.rotationEnabled &&
|
||||
this.graph.isCellRotatable(this.state.cell) &&
|
||||
(this.graph.graphHandler.maxCells <= 0 ||
|
||||
this.graph.getSelectionCount() < this.graph.graphHandler.maxCells)
|
||||
(graphHandler.maxCells <= 0 ||
|
||||
this.graph.getSelectionCount() < graphHandler.maxCells)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -790,11 +796,15 @@ class VertexHandler {
|
|||
const edges = this.graph.getEdges(this.state.cell);
|
||||
this.edgeHandlers = [];
|
||||
|
||||
for (let i = 0; i < edges.length; i += 1) {
|
||||
const handler = this.graph.selectionCellsHandler.getHandler(edges[i]);
|
||||
const selectionCellsHandler = this.graph.getPlugin(
|
||||
'SelectionCellsHandler'
|
||||
) as SelectionCellsHandler;
|
||||
|
||||
if (handler != null) {
|
||||
this.edgeHandlers.push(handler);
|
||||
for (let i = 0; i < edges.length; i += 1) {
|
||||
const handler = selectionCellsHandler.getHandler(edges[i]);
|
||||
|
||||
if (handler) {
|
||||
this.edgeHandlers.push(handler as EdgeHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,9 +48,10 @@ import CellState from '../cell/datatypes/CellState';
|
|||
import Graph from '../Graph';
|
||||
import ConnectionConstraint from './ConnectionConstraint';
|
||||
import Shape from '../geometry/shape/Shape';
|
||||
import { Listenable } from '../../types';
|
||||
import { GraphPlugin, Listenable } from '../../types';
|
||||
import CellArray from '../cell/datatypes/CellArray';
|
||||
|
||||
type FactoryMethod = (source: Cell, target: Cell, style?: string) => Cell;
|
||||
type FactoryMethod = (source: Cell | null, target: Cell | null, style?: string) => Cell;
|
||||
|
||||
/**
|
||||
* Class: mxConnectionHandler
|
||||
|
@ -208,7 +209,9 @@ type FactoryMethod = (source: Cell, target: Cell, style?: string) => Cell;
|
|||
* optional cell style from the preview as the third argument. It returns
|
||||
* the <mxCell> that represents the new edge.
|
||||
*/
|
||||
class ConnectionHandler extends EventSource {
|
||||
class ConnectionHandler extends EventSource implements GraphPlugin {
|
||||
static pluginId = 'ConnectionHandler';
|
||||
|
||||
constructor(graph: MaxGraph, factoryMethod: FactoryMethod | null = null) {
|
||||
super();
|
||||
|
||||
|
@ -216,7 +219,7 @@ class ConnectionHandler extends EventSource {
|
|||
this.factoryMethod = factoryMethod;
|
||||
|
||||
this.graph.addMouseListener(this);
|
||||
this.marker = <CellMarker>this.createMarker();
|
||||
this.marker = this.createMarker();
|
||||
this.constraintHandler = new ConstraintHandler(this.graph);
|
||||
|
||||
// Redraws the icons if the graph changes
|
||||
|
@ -269,7 +272,7 @@ class ConnectionHandler extends EventSource {
|
|||
originalPoint: Point | null = null;
|
||||
currentState: CellState | null = null;
|
||||
selectedIcon: ImageShape | null = null;
|
||||
waypoints: Point[] | null = null;
|
||||
waypoints: Point[] = [];
|
||||
|
||||
/**
|
||||
* Variable: graph
|
||||
|
@ -519,10 +522,10 @@ class ConnectionHandler extends EventSource {
|
|||
*/
|
||||
isInsertBefore(
|
||||
edge: Cell,
|
||||
source: Cell,
|
||||
target: Cell,
|
||||
source: Cell | null,
|
||||
target: Cell | null,
|
||||
evt: MouseEvent,
|
||||
dropTarget: Cell
|
||||
dropTarget: Cell | null
|
||||
) {
|
||||
return this.insertBeforeSource && source !== target;
|
||||
}
|
||||
|
@ -857,7 +860,7 @@ class ConnectionHandler extends EventSource {
|
|||
return icons;
|
||||
}
|
||||
|
||||
return null;
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -945,7 +948,7 @@ class ConnectionHandler extends EventSource {
|
|||
*
|
||||
* Handles the event by initiating a new connection.
|
||||
*/
|
||||
mouseDown(sender: Listenable, me: InternalMouseEvent) {
|
||||
mouseDown(sender: EventSource, me: InternalMouseEvent) {
|
||||
this.mouseDownCounter += 1;
|
||||
|
||||
if (
|
||||
|
@ -972,7 +975,7 @@ class ConnectionHandler extends EventSource {
|
|||
this.mouseDownCounter = 1;
|
||||
|
||||
if (this.waypointsEnabled && !this.shape) {
|
||||
this.waypoints = null;
|
||||
this.waypoints = [];
|
||||
this.shape = this.createShape();
|
||||
|
||||
if (this.edgeState) {
|
||||
|
@ -1034,6 +1037,8 @@ class ConnectionHandler extends EventSource {
|
|||
* or shift is pressed.
|
||||
*/
|
||||
isOutlineConnectEvent(me: InternalMouseEvent) {
|
||||
if (!this.currentPoint) return false;
|
||||
|
||||
const offset = getOffset(this.graph.container);
|
||||
const evt = me.getEvent();
|
||||
|
||||
|
@ -1068,9 +1073,9 @@ class ConnectionHandler extends EventSource {
|
|||
updateCurrentState(me: InternalMouseEvent, point: Point): void {
|
||||
this.constraintHandler.update(
|
||||
me,
|
||||
this.first == null,
|
||||
!this.first,
|
||||
false,
|
||||
this.first == null || me.isSource(this.marker.highlight.shape) ? null : point
|
||||
!this.first || me.isSource(this.marker.highlight.shape) ? null : point
|
||||
);
|
||||
|
||||
if (
|
||||
|
@ -1080,9 +1085,10 @@ class ConnectionHandler extends EventSource {
|
|||
// Handles special case where grid is large and connection point is at actual point in which
|
||||
// case the outline is not followed as long as we're < gridSize / 2 away from that point
|
||||
if (
|
||||
this.marker.highlight != null &&
|
||||
this.marker.highlight.state != null &&
|
||||
this.marker.highlight.state.cell === this.constraintHandler.currentFocus.cell
|
||||
this.marker.highlight &&
|
||||
this.marker.highlight.state &&
|
||||
this.marker.highlight.state.cell === this.constraintHandler.currentFocus.cell &&
|
||||
this.marker.highlight.shape
|
||||
) {
|
||||
// Direct repaint needed if cell already highlighted
|
||||
if (this.marker.highlight.shape.stroke !== 'transparent') {
|
||||
|
@ -1094,19 +1100,19 @@ class ConnectionHandler extends EventSource {
|
|||
}
|
||||
|
||||
// Updates validation state
|
||||
if (this.previous != null) {
|
||||
if (this.previous) {
|
||||
this.error = this.validateConnection(
|
||||
this.previous.cell,
|
||||
this.constraintHandler.currentFocus.cell
|
||||
);
|
||||
|
||||
if (this.error == null) {
|
||||
if (!this.error) {
|
||||
this.currentState = this.constraintHandler.currentFocus;
|
||||
}
|
||||
|
||||
if (
|
||||
this.error != null ||
|
||||
(this.currentState != null && !this.isCellEnabled(this.currentState.cell))
|
||||
this.error ||
|
||||
(this.currentState && !this.isCellEnabled(this.currentState.cell))
|
||||
) {
|
||||
this.constraintHandler.reset();
|
||||
}
|
||||
|
@ -1154,11 +1160,14 @@ class ConnectionHandler extends EventSource {
|
|||
OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s;
|
||||
this.marker.highlight.repaint();
|
||||
} else if (this.marker.hasValidState()) {
|
||||
const cell = me.getCell();
|
||||
|
||||
// Handles special case where actual end point of edge and current mouse point
|
||||
// are not equal (due to grid snapping) and there is no hit on shape or highlight
|
||||
// but ignores cases where parent is used for non-connectable child cells
|
||||
if (
|
||||
me.getCell().isConnectable() &&
|
||||
cell &&
|
||||
cell.isConnectable() &&
|
||||
this.marker.getValidState() !== me.getState()
|
||||
) {
|
||||
this.marker.highlight.shape.stroke = 'transparent';
|
||||
|
@ -1227,7 +1236,7 @@ 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) {
|
||||
mouseMove(sender: EventSource, me: InternalMouseEvent) {
|
||||
if (
|
||||
!me.isConsumed() &&
|
||||
(this.ignoreMouseDown || this.first || !this.graph.isMouseDown)
|
||||
|
@ -1266,7 +1275,7 @@ class ConnectionHandler extends EventSource {
|
|||
|
||||
if (this.first) {
|
||||
let constraint = null;
|
||||
let current = point;
|
||||
let current: Point | null = point;
|
||||
|
||||
// Uses the current point from the constraint handler if available
|
||||
if (
|
||||
|
@ -1291,7 +1300,7 @@ class ConnectionHandler extends EventSource {
|
|||
}
|
||||
}
|
||||
|
||||
let pt2 = this.first;
|
||||
let pt2: Point | null = this.first;
|
||||
|
||||
// Moves the connect icon with the mouse
|
||||
if (this.selectedIcon && this.selectedIcon.bounds) {
|
||||
|
@ -1323,8 +1332,8 @@ class ConnectionHandler extends EventSource {
|
|||
];
|
||||
pt2 = this.edgeState.absolutePoints[0];
|
||||
} else {
|
||||
if (this.currentState != null) {
|
||||
if (this.constraintHandler.currentConstraint == null) {
|
||||
if (this.currentState) {
|
||||
if (!this.constraintHandler.currentConstraint) {
|
||||
const tmp = this.getTargetPerimeterPoint(this.currentState, me);
|
||||
|
||||
if (tmp != null) {
|
||||
|
@ -1334,14 +1343,11 @@ class ConnectionHandler extends EventSource {
|
|||
}
|
||||
|
||||
// Computes the source perimeter point
|
||||
if (this.sourceConstraint == null && this.previous != null) {
|
||||
const next =
|
||||
this.waypoints != null && this.waypoints.length > 0
|
||||
? this.waypoints[0]
|
||||
: current;
|
||||
const tmp = this.getSourcePerimeterPoint(this.previous, next, me);
|
||||
if (!this.sourceConstraint && this.previous) {
|
||||
const next = this.waypoints.length > 0 ? this.waypoints[0] : current;
|
||||
const tmp = this.getSourcePerimeterPoint(this.previous, next as Point, me);
|
||||
|
||||
if (tmp != null) {
|
||||
if (tmp) {
|
||||
pt2 = tmp;
|
||||
}
|
||||
}
|
||||
|
@ -1351,38 +1357,40 @@ class ConnectionHandler extends EventSource {
|
|||
// by moving the preview shape away from the mouse. This
|
||||
// makes sure the preview shape does not prevent the detection
|
||||
// of the cell under the mousepointer even for slow gestures.
|
||||
if (this.currentState == null && this.movePreviewAway) {
|
||||
if (!this.currentState && this.movePreviewAway && current) {
|
||||
let tmp = pt2;
|
||||
|
||||
if (this.edgeState != null && this.edgeState.absolutePoints.length >= 2) {
|
||||
if (this.edgeState && this.edgeState.absolutePoints.length >= 2) {
|
||||
const tmp2 = this.edgeState.absolutePoints[
|
||||
this.edgeState.absolutePoints.length - 2
|
||||
];
|
||||
|
||||
if (tmp2 != null) {
|
||||
if (tmp2) {
|
||||
tmp = tmp2;
|
||||
}
|
||||
}
|
||||
|
||||
const dx = current.x - tmp.x;
|
||||
const dy = current.y - tmp.y;
|
||||
if (tmp) {
|
||||
const dx = current.x - tmp.x;
|
||||
const dy = current.y - tmp.y;
|
||||
|
||||
const len = Math.sqrt(dx * dx + dy * dy);
|
||||
const len = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (len === 0) {
|
||||
return;
|
||||
if (len === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stores old point to reuse when creating edge
|
||||
this.originalPoint = current.clone();
|
||||
current.x -= (dx * 4) / len;
|
||||
current.y -= (dy * 4) / len;
|
||||
}
|
||||
|
||||
// Stores old point to reuse when creating edge
|
||||
this.originalPoint = current.clone();
|
||||
current.x -= (dx * 4) / len;
|
||||
current.y -= (dy * 4) / len;
|
||||
} else {
|
||||
this.originalPoint = null;
|
||||
}
|
||||
|
||||
// Creates the preview shape (lazy)
|
||||
if (this.shape == null) {
|
||||
if (!this.shape) {
|
||||
const dx = Math.abs(me.getGraphX() - this.first.x);
|
||||
const dy = Math.abs(me.getGraphY() - this.first.y);
|
||||
|
||||
|
@ -1392,7 +1400,7 @@ class ConnectionHandler extends EventSource {
|
|||
) {
|
||||
this.shape = this.createShape();
|
||||
|
||||
if (this.edgeState != null) {
|
||||
if (this.edgeState) {
|
||||
this.shape.apply(this.edgeState);
|
||||
}
|
||||
|
||||
|
@ -1402,13 +1410,13 @@ class ConnectionHandler extends EventSource {
|
|||
}
|
||||
|
||||
// Updates the points in the preview edge
|
||||
if (this.shape != null) {
|
||||
if (this.edgeState != null) {
|
||||
if (this.shape) {
|
||||
if (this.edgeState) {
|
||||
this.shape.points = this.edgeState.absolutePoints;
|
||||
} else {
|
||||
let pts = [pt2];
|
||||
|
||||
if (this.waypoints != null) {
|
||||
if (this.waypoints.length > 0) {
|
||||
pts = pts.concat(this.waypoints);
|
||||
}
|
||||
|
||||
|
@ -1420,7 +1428,7 @@ class ConnectionHandler extends EventSource {
|
|||
}
|
||||
|
||||
// Makes sure endpoint of edge is visible during connect
|
||||
if (this.cursor != null) {
|
||||
if (this.cursor) {
|
||||
this.graph.container.style.cursor = this.cursor;
|
||||
}
|
||||
|
||||
|
@ -1428,14 +1436,14 @@ class ConnectionHandler extends EventSource {
|
|||
me.consume();
|
||||
} else if (!this.isEnabled() || !this.graph.isEnabled()) {
|
||||
this.constraintHandler.reset();
|
||||
} else if (this.previous !== this.currentState && this.edgeState == null) {
|
||||
} else if (this.previous !== this.currentState && !this.edgeState) {
|
||||
this.destroyIcons();
|
||||
|
||||
// Sets the cursor on the current shape
|
||||
if (
|
||||
this.currentState != null &&
|
||||
this.error == null &&
|
||||
this.constraintHandler.currentConstraint == null
|
||||
this.currentState &&
|
||||
!this.error &&
|
||||
!this.constraintHandler.currentConstraint
|
||||
) {
|
||||
this.icons = this.createIcons(this.currentState);
|
||||
|
||||
|
@ -1462,7 +1470,9 @@ class ConnectionHandler extends EventSource {
|
|||
|
||||
for (let i = 0; i < this.icons.length && !hitsIcon; i += 1) {
|
||||
hitsIcon =
|
||||
target === this.icons[i].node || target.parentNode === this.icons[i].node;
|
||||
target === this.icons[i].node ||
|
||||
// @ts-ignore parentNode should exist.
|
||||
(!!target && target.parentNode === this.icons[i].node);
|
||||
}
|
||||
|
||||
if (!hitsIcon) {
|
||||
|
@ -1479,7 +1489,7 @@ class ConnectionHandler extends EventSource {
|
|||
*
|
||||
* Updates <edgeState>.
|
||||
*/
|
||||
updateEdgeState(current: Point, constraint: ConnectionConstraint) {
|
||||
updateEdgeState(current: Point | null, constraint: ConnectionConstraint | null) {
|
||||
if (!this.edgeState) return;
|
||||
|
||||
// TODO: Use generic method for writing constraint to style
|
||||
|
@ -1526,16 +1536,12 @@ class ConnectionHandler extends EventSource {
|
|||
}
|
||||
|
||||
// Scales and translates the waypoints to the model
|
||||
let realPoints = null;
|
||||
let realPoints = [];
|
||||
|
||||
if (this.waypoints != null) {
|
||||
realPoints = [];
|
||||
|
||||
for (let i = 0; i < this.waypoints.length; i += 1) {
|
||||
const pt = this.waypoints[i].clone();
|
||||
this.convertWaypoint(pt);
|
||||
realPoints[i] = pt;
|
||||
}
|
||||
for (let i = 0; i < this.waypoints.length; i += 1) {
|
||||
const pt = this.waypoints[i].clone();
|
||||
this.convertWaypoint(pt);
|
||||
realPoints[i] = pt;
|
||||
}
|
||||
|
||||
this.graph.view.updatePoints(
|
||||
|
@ -1561,14 +1567,14 @@ class ConnectionHandler extends EventSource {
|
|||
* state - <mxCellState> that represents the target cell state.
|
||||
* me - <mxMouseEvent> that represents the mouse move.
|
||||
*/
|
||||
getTargetPerimeterPoint(state: CellState, me: MouseEvent): Point {
|
||||
let result = null;
|
||||
getTargetPerimeterPoint(state: CellState, me: InternalMouseEvent) {
|
||||
let result: Point | null = null;
|
||||
const { view } = state;
|
||||
const targetPerimeter = view.getPerimeterFunction(state);
|
||||
|
||||
if (targetPerimeter != null) {
|
||||
if (targetPerimeter && this.previous) {
|
||||
const next =
|
||||
this.waypoints != null && this.waypoints.length > 0
|
||||
this.waypoints.length > 0
|
||||
? this.waypoints[this.waypoints.length - 1]
|
||||
: new Point(this.previous.getCenterX(), this.previous.getCenterY());
|
||||
const tmp = targetPerimeter(
|
||||
|
@ -1578,7 +1584,7 @@ class ConnectionHandler extends EventSource {
|
|||
false
|
||||
);
|
||||
|
||||
if (tmp != null) {
|
||||
if (tmp) {
|
||||
result = tmp;
|
||||
}
|
||||
} else {
|
||||
|
@ -1600,13 +1606,13 @@ class ConnectionHandler extends EventSource {
|
|||
* next - <mxPoint> that represents the next point along the previewed edge.
|
||||
* me - <mxMouseEvent> that represents the mouse move.
|
||||
*/
|
||||
getSourcePerimeterPoint(state: CellState, next: Point, me: MouseEvent): Point {
|
||||
getSourcePerimeterPoint(state: CellState, next: Point, me: InternalMouseEvent) {
|
||||
let result = null;
|
||||
const { view } = state;
|
||||
const sourcePerimeter = view.getPerimeterFunction(state);
|
||||
const c = new Point(state.getCenterX(), state.getCenterY());
|
||||
|
||||
if (sourcePerimeter != null) {
|
||||
if (sourcePerimeter) {
|
||||
const theta = getValue(state.style, 'rotation', 0);
|
||||
const rad = -theta * (Math.PI / 180);
|
||||
|
||||
|
@ -1621,7 +1627,7 @@ class ConnectionHandler extends EventSource {
|
|||
|
||||
let tmp = sourcePerimeter(view.getPerimeterBounds(state), state, next, false);
|
||||
|
||||
if (tmp != null) {
|
||||
if (tmp) {
|
||||
if (theta !== 0) {
|
||||
tmp = getRotatedPoint(
|
||||
new Point(tmp.x, tmp.y),
|
||||
|
@ -1652,7 +1658,7 @@ class ConnectionHandler extends EventSource {
|
|||
* icons - Array of currently displayed icons.
|
||||
* me - <mxMouseEvent> that contains the mouse event.
|
||||
*/
|
||||
updateIcons(state: CellState, icons: string[], me: InternalMouseEvent): void {
|
||||
updateIcons(state: CellState, icons: ImageShape[], me: InternalMouseEvent) {
|
||||
// empty
|
||||
}
|
||||
|
||||
|
@ -1664,8 +1670,8 @@ class ConnectionHandler extends EventSource {
|
|||
* called if <waypointsEnabled> is true. This implemtation returns true
|
||||
* if there is a cell state in the given event.
|
||||
*/
|
||||
isStopEvent(me: InternalMouseEvent): boolean {
|
||||
return me.getState() != null;
|
||||
isStopEvent(me: InternalMouseEvent) {
|
||||
return !!me.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1673,20 +1679,18 @@ class ConnectionHandler extends EventSource {
|
|||
*
|
||||
* Adds the waypoint for the given event to <waypoints>.
|
||||
*/
|
||||
addWaypointForEvent(me: InternalMouseEvent): void {
|
||||
addWaypointForEvent(me: InternalMouseEvent) {
|
||||
if (!this.first) return;
|
||||
|
||||
let point = convertPoint(this.graph.container, me.getX(), me.getY());
|
||||
const dx = Math.abs(point.x - this.first.x);
|
||||
const dy = Math.abs(point.y - this.first.y);
|
||||
const addPoint =
|
||||
this.waypoints != null ||
|
||||
this.waypoints.length > 0 ||
|
||||
(this.mouseDownCounter > 1 &&
|
||||
(dx > this.graph.getEventTolerance() || dy > this.graph.getEventTolerance()));
|
||||
|
||||
if (addPoint) {
|
||||
if (this.waypoints == null) {
|
||||
this.waypoints = [];
|
||||
}
|
||||
|
||||
const { scale } = this.graph.view;
|
||||
point = new Point(
|
||||
this.graph.snap(me.getGraphX() / scale) * scale,
|
||||
|
@ -1703,12 +1707,12 @@ class ConnectionHandler extends EventSource {
|
|||
* implementation returns true if the constraints are not pointing to the
|
||||
* same fixed connection point.
|
||||
*/
|
||||
checkConstraints(c1, c2) {
|
||||
checkConstraints(c1: ConnectionConstraint | null, c2: ConnectionConstraint | null) {
|
||||
return (
|
||||
c1 == null ||
|
||||
c2 == null ||
|
||||
c1.point == null ||
|
||||
c2.point == null ||
|
||||
!c1 ||
|
||||
!c2 ||
|
||||
!c1.point ||
|
||||
!c2.point ||
|
||||
!c1.point.equals(c2.point) ||
|
||||
c1.dx !== c2.dx ||
|
||||
c1.dy !== c2.dy ||
|
||||
|
@ -1721,7 +1725,7 @@ class ConnectionHandler extends EventSource {
|
|||
*
|
||||
* Handles the event by inserting the new connection.
|
||||
*/
|
||||
mouseUp(sender: InternalMouseEvent, me: InternalMouseEvent): void {
|
||||
mouseUp(sender: EventSource, me: InternalMouseEvent) {
|
||||
if (!me.isConsumed() && this.isConnecting()) {
|
||||
if (this.waypointsEnabled && !this.isStopEvent(me)) {
|
||||
this.addWaypointForEvent(me);
|
||||
|
@ -1733,27 +1737,24 @@ class ConnectionHandler extends EventSource {
|
|||
const c1 = this.sourceConstraint;
|
||||
const c2 = this.constraintHandler.currentConstraint;
|
||||
|
||||
const source = this.previous != null ? this.previous.cell : null;
|
||||
const source = this.previous ? this.previous.cell : null;
|
||||
let target = null;
|
||||
|
||||
if (
|
||||
this.constraintHandler.currentConstraint != null &&
|
||||
this.constraintHandler.currentFocus != null
|
||||
this.constraintHandler.currentConstraint &&
|
||||
this.constraintHandler.currentFocus
|
||||
) {
|
||||
target = this.constraintHandler.currentFocus.cell;
|
||||
}
|
||||
|
||||
if (target == null && this.currentState != null) {
|
||||
if (!target && this.currentState) {
|
||||
target = this.currentState.cell;
|
||||
}
|
||||
|
||||
// Inserts the edge if no validation error exists and if constraints differ
|
||||
if (
|
||||
this.error == null &&
|
||||
(source == null ||
|
||||
target == null ||
|
||||
source !== target ||
|
||||
this.checkConstraints(c1, c2))
|
||||
!this.error &&
|
||||
(!source || !target || source !== target || this.checkConstraints(c1, c2))
|
||||
) {
|
||||
this.connect(source, target, me.getEvent(), me.getCell());
|
||||
} else {
|
||||
|
@ -1763,7 +1764,7 @@ class ConnectionHandler extends EventSource {
|
|||
this.marker.validState != null &&
|
||||
this.previous.cell === this.marker.validState.cell
|
||||
) {
|
||||
this.graph.selectCellForEvent(this.marker.source, me.getEvent());
|
||||
this.graph.selectCellForEvent(this.marker.validState.cell, me.getEvent());
|
||||
}
|
||||
|
||||
// Displays the error message if it is not an empty string,
|
||||
|
@ -1887,37 +1888,47 @@ class ConnectionHandler extends EventSource {
|
|||
* dropTarget - <mxCell> that represents the cell under the mouse when it was
|
||||
* released.
|
||||
*/
|
||||
connect(source: Cell, target: Cell, evt: MouseEvent, dropTarget: Cell): void {
|
||||
if (target != null || this.isCreateTarget(evt) || this.graph.isAllowDanglingEdges()) {
|
||||
connect(
|
||||
source: Cell | null,
|
||||
target: Cell | null,
|
||||
evt: MouseEvent,
|
||||
dropTarget: Cell | null = null
|
||||
) {
|
||||
if (target || 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();
|
||||
let terminalInserted = false;
|
||||
let edge = null;
|
||||
let edge: Cell | null = null;
|
||||
|
||||
model.beginUpdate();
|
||||
try {
|
||||
if (
|
||||
source != null &&
|
||||
target == null &&
|
||||
source &&
|
||||
!target &&
|
||||
!this.graph.isIgnoreTerminalEvent(evt) &&
|
||||
this.isCreateTarget(evt)
|
||||
) {
|
||||
target = this.createTargetVertex(evt, source);
|
||||
|
||||
if (target != null) {
|
||||
dropTarget = this.graph.getDropTarget([target], evt, dropTarget);
|
||||
if (target) {
|
||||
dropTarget = this.graph.getDropTarget(new CellArray(target), evt, dropTarget);
|
||||
terminalInserted = true;
|
||||
|
||||
// Disables edges as drop targets if the target cell was created
|
||||
// FIXME: Should not shift if vertex was aligned (same in Java)
|
||||
if (dropTarget == null || !dropTarget.isEdge()) {
|
||||
const pstate = this.graph.getView().getState(dropTarget);
|
||||
const pstate = dropTarget
|
||||
? this.graph.getView().getState(dropTarget)
|
||||
: null;
|
||||
|
||||
if (pstate != null) {
|
||||
if (pstate) {
|
||||
const tmp = target.getGeometry();
|
||||
tmp.x -= pstate.origin.x;
|
||||
tmp.y -= pstate.origin.y;
|
||||
|
||||
if (tmp) {
|
||||
tmp.x -= pstate.origin.x;
|
||||
tmp.y -= pstate.origin.y;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dropTarget = this.graph.getDefaultParent();
|
||||
|
@ -1930,17 +1941,17 @@ class ConnectionHandler extends EventSource {
|
|||
let parent = this.graph.getDefaultParent();
|
||||
|
||||
if (
|
||||
source != null &&
|
||||
target != null &&
|
||||
source &&
|
||||
target &&
|
||||
source.getParent() === target.getParent() &&
|
||||
source.getParent().getParent() !== model.getRoot()
|
||||
) {
|
||||
parent = source.getParent();
|
||||
|
||||
if (
|
||||
source.geometry != null &&
|
||||
source.geometry &&
|
||||
source.geometry.relative &&
|
||||
target.geometry != null &&
|
||||
target.geometry &&
|
||||
target.geometry.relative
|
||||
) {
|
||||
parent = parent.getParent();
|
||||
|
@ -1950,16 +1961,16 @@ class ConnectionHandler extends EventSource {
|
|||
// Uses the value of the preview edge state for inserting
|
||||
// the new edge into the graph
|
||||
let value = null;
|
||||
let style = null;
|
||||
let style = '';
|
||||
|
||||
if (this.edgeState != null) {
|
||||
if (this.edgeState) {
|
||||
value = this.edgeState.cell.value;
|
||||
style = this.edgeState.cell.style;
|
||||
style = this.edgeState.cell.style ?? '';
|
||||
}
|
||||
|
||||
edge = this.insertEdge(parent, null, value, source, target, style);
|
||||
edge = this.insertEdge(parent, '', value, source, target, style);
|
||||
|
||||
if (edge != null) {
|
||||
if (edge && source) {
|
||||
// Updates the connection constraints
|
||||
this.graph.setConnectionConstraint(edge, source, true, this.sourceConstraint);
|
||||
this.graph.setConnectionConstraint(
|
||||
|
@ -1970,7 +1981,7 @@ class ConnectionHandler extends EventSource {
|
|||
);
|
||||
|
||||
// Uses geometry of the preview edge state
|
||||
if (this.edgeState != null) {
|
||||
if (this.edgeState && this.edgeState.cell && this.edgeState.cell.geometry) {
|
||||
model.setGeometry(edge, this.edgeState.cell.geometry);
|
||||
}
|
||||
|
||||
|
@ -2006,7 +2017,7 @@ class ConnectionHandler extends EventSource {
|
|||
}
|
||||
|
||||
// Uses scaled waypoints in geometry
|
||||
if (this.waypoints != null && this.waypoints.length > 0) {
|
||||
if (this.waypoints.length > 0) {
|
||||
const s = this.graph.view.scale;
|
||||
const tr = this.graph.view.translate;
|
||||
geo.points = [];
|
||||
|
@ -2017,7 +2028,7 @@ class ConnectionHandler extends EventSource {
|
|||
}
|
||||
}
|
||||
|
||||
if (target == null) {
|
||||
if (!target && this.currentPoint) {
|
||||
const t = this.graph.view.translate;
|
||||
const s = this.graph.view.scale;
|
||||
const pt =
|
||||
|
@ -2027,8 +2038,8 @@ class ConnectionHandler extends EventSource {
|
|||
this.originalPoint.y / s - t.y
|
||||
)
|
||||
: new Point(this.currentPoint.x / s - t.x, this.currentPoint.y / s - t.y);
|
||||
pt.x -= this.graph.panDx / this.graph.view.scale;
|
||||
pt.y -= this.graph.panDy / this.graph.view.scale;
|
||||
pt.x -= this.graph.getPanDx() / this.graph.view.scale;
|
||||
pt.y -= this.graph.getPanDy() / this.graph.view.scale;
|
||||
geo.setTerminalPoint(pt, false);
|
||||
}
|
||||
|
||||
|
@ -2067,7 +2078,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) {
|
||||
selectCells(edge: Cell | null, target: Cell | null) {
|
||||
this.graph.setSelectionCell(edge);
|
||||
}
|
||||
|
||||
|
@ -2082,8 +2093,8 @@ class ConnectionHandler extends EventSource {
|
|||
parent: Cell,
|
||||
id: string,
|
||||
value: any,
|
||||
source: Cell,
|
||||
target: Cell,
|
||||
source: Cell | null,
|
||||
target: Cell | null,
|
||||
style: string
|
||||
): Cell {
|
||||
if (!this.factoryMethod) {
|
||||
|
@ -2107,11 +2118,11 @@ class ConnectionHandler extends EventSource {
|
|||
* evt - Mousedown event of the connect gesture.
|
||||
* source - <mxCell> that represents the source terminal.
|
||||
*/
|
||||
createTargetVertex(evt: MouseEvent, source: Cell): Cell {
|
||||
createTargetVertex(evt: MouseEvent, source: Cell) {
|
||||
// Uses the first non-relative source
|
||||
let geo = source.getGeometry();
|
||||
|
||||
while (geo != null && geo.relative) {
|
||||
while (geo && geo.relative) {
|
||||
source = source.getParent();
|
||||
geo = source.getGeometry();
|
||||
}
|
||||
|
@ -2119,15 +2130,15 @@ class ConnectionHandler extends EventSource {
|
|||
const clone = this.graph.cloneCell(source);
|
||||
geo = clone.getGeometry();
|
||||
|
||||
if (geo != null) {
|
||||
if (geo && this.currentPoint) {
|
||||
const t = this.graph.view.translate;
|
||||
const s = this.graph.view.scale;
|
||||
const point = new Point(
|
||||
this.currentPoint.x / s - t.x,
|
||||
this.currentPoint.y / s - t.y
|
||||
);
|
||||
geo.x = Math.round(point.x - geo.width / 2 - this.graph.panDx / s);
|
||||
geo.y = Math.round(point.y - geo.height / 2 - this.graph.panDy / s);
|
||||
geo.x = Math.round(point.x - geo.width / 2 - this.graph.getPanDx() / s);
|
||||
geo.y = Math.round(point.y - geo.height / 2 - this.graph.getPanDy() / s);
|
||||
|
||||
// Aligns with source if within certain tolerance
|
||||
const tol = this.getAlignmentTolerance();
|
||||
|
@ -2158,7 +2169,7 @@ class ConnectionHandler extends EventSource {
|
|||
*
|
||||
* Returns the tolerance for aligning new targets to sources. This returns the grid size / 2.
|
||||
*/
|
||||
getAlignmentTolerance(evt: MouseEvent): number {
|
||||
getAlignmentTolerance(evt?: MouseEvent): number {
|
||||
return this.graph.isGridEnabled()
|
||||
? this.graph.getGridSize() / 2
|
||||
: this.graph.getSnapTolerance();
|
||||
|
@ -2179,7 +2190,7 @@ class ConnectionHandler extends EventSource {
|
|||
* target - <mxCell> that represents the target terminal.
|
||||
* style - Optional style from the preview edge.
|
||||
*/
|
||||
createEdge(value?: any, source?: Cell, target?: Cell, style?: string): Cell {
|
||||
createEdge(value: any, source: Cell | null, target: Cell | null, style: string = '') {
|
||||
let edge = null;
|
||||
|
||||
// Creates a new edge using the factoryMethod
|
||||
|
@ -2207,7 +2218,7 @@ class ConnectionHandler extends EventSource {
|
|||
* called on all instances. It is called automatically for the built-in
|
||||
* instance created for each <mxGraph>.
|
||||
*/
|
||||
destroy(): void {
|
||||
onDestroy() {
|
||||
this.graph.removeMouseListener(this);
|
||||
|
||||
if (this.shape != null) {
|
||||
|
@ -2217,29 +2228,24 @@ class ConnectionHandler extends EventSource {
|
|||
|
||||
if (this.marker != null) {
|
||||
this.marker.destroy();
|
||||
this.marker = null;
|
||||
}
|
||||
|
||||
if (this.constraintHandler != null) {
|
||||
this.constraintHandler.destroy();
|
||||
this.constraintHandler = null;
|
||||
}
|
||||
|
||||
if (this.changeHandler != null) {
|
||||
this.graph.getModel().removeListener(this.changeHandler);
|
||||
this.graph.getView().removeListener(this.changeHandler);
|
||||
this.changeHandler = null;
|
||||
}
|
||||
|
||||
if (this.drillHandler != null) {
|
||||
this.graph.removeListener(this.drillHandler);
|
||||
this.graph.getView().removeListener(this.drillHandler);
|
||||
this.drillHandler = null;
|
||||
}
|
||||
|
||||
if (this.escapeHandler != null) {
|
||||
this.graph.removeListener(this.escapeHandler);
|
||||
this.escapeHandler = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,17 @@ import {
|
|||
HIGHLIGHT_STROKEWIDTH,
|
||||
} from '../../util/Constants';
|
||||
import InternalEvent from '../event/InternalEvent';
|
||||
import utils, { intersects } from '../../util/Utils';
|
||||
import { intersects } from '../../util/Utils';
|
||||
import Rectangle from '../geometry/Rectangle';
|
||||
import ImageShape from '../geometry/shape/node/ImageShape';
|
||||
import RectangleShape from '../geometry/shape/node/RectangleShape';
|
||||
import { isShiftDown } from '../../util/EventUtils';
|
||||
import { MaxGraph } from '../Graph';
|
||||
import CellState from '../cell/datatypes/CellState';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
import ConnectionConstraint from './ConnectionConstraint';
|
||||
import Point from '../geometry/Point';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
|
||||
/**
|
||||
* Handles constraints on connection targets. This class is in charge of
|
||||
|
@ -29,11 +35,11 @@ import { isShiftDown } from '../../util/EventUtils';
|
|||
* @class ConstraintHandler
|
||||
*/
|
||||
class ConstraintHandler {
|
||||
constructor(graph) {
|
||||
constructor(graph: MaxGraph) {
|
||||
this.graph = graph;
|
||||
|
||||
// Adds a graph model listener to update the current focus on changes
|
||||
this.resetHandler = (sender, evt) => {
|
||||
this.resetHandler = () => {
|
||||
if (
|
||||
this.currentFocus != null &&
|
||||
this.graph.view.getState(this.currentFocus.cell) == null
|
||||
|
@ -60,26 +66,42 @@ class ConstraintHandler {
|
|||
/**
|
||||
* Reference to the enclosing {@link mxGraph}.
|
||||
*/
|
||||
// graph: mxGraph;
|
||||
graph = null;
|
||||
graph: MaxGraph;
|
||||
|
||||
resetHandler: () => void;
|
||||
|
||||
currentFocus: CellState | null = null;
|
||||
|
||||
currentFocusArea: Rectangle | null = null;
|
||||
|
||||
focusIcons: ImageShape[] = [];
|
||||
|
||||
constraints: ConnectionConstraint[] = [];
|
||||
|
||||
currentConstraint: ConnectionConstraint | null = null;
|
||||
|
||||
focusHighlight: RectangleShape | null = null;
|
||||
|
||||
focusPoints: Point[] = [];
|
||||
|
||||
currentPoint: Point | null = null;
|
||||
|
||||
/**
|
||||
* Specifies if events are handled. Default is true.
|
||||
*/
|
||||
// enabled: boolean;
|
||||
enabled = true;
|
||||
|
||||
/**
|
||||
* Specifies the color for the highlight. Default is {@link DEFAULT_VALID_COLOR}.
|
||||
*/
|
||||
// highlightColor: string;
|
||||
highlightColor = DEFAULT_VALID_COLOR;
|
||||
|
||||
mouseleaveHandler: (() => void) | null = null;
|
||||
|
||||
/**
|
||||
* Returns true if events are handled. This implementation
|
||||
* returns {@link enabled}.
|
||||
*/
|
||||
// isEnabled(): boolean;
|
||||
isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
@ -92,22 +114,20 @@ class ConstraintHandler {
|
|||
*
|
||||
* @param {boolean} enabled - Boolean that specifies the new enabled state.
|
||||
*/
|
||||
// setEnabled(enabled: boolean): void;
|
||||
setEnabled(enabled) {
|
||||
setEnabled(enabled: boolean) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the state of this handler.
|
||||
*/
|
||||
// reset(): void;
|
||||
reset() {
|
||||
if (this.focusIcons != null) {
|
||||
for (let i = 0; i < this.focusIcons.length; i += 1) {
|
||||
this.focusIcons[i].destroy();
|
||||
}
|
||||
|
||||
this.focusIcons = null;
|
||||
this.focusIcons = [];
|
||||
}
|
||||
|
||||
if (this.focusHighlight != null) {
|
||||
|
@ -119,7 +139,7 @@ class ConstraintHandler {
|
|||
this.currentFocusArea = null;
|
||||
this.currentPoint = null;
|
||||
this.currentFocus = null;
|
||||
this.focusPoints = null;
|
||||
this.focusPoints = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,16 +150,18 @@ class ConstraintHandler {
|
|||
*
|
||||
* me - {@link mxMouseEvent} whose tolerance should be returned.
|
||||
*/
|
||||
// getTolerance(me: mxMouseEvent): number;
|
||||
getTolerance(me) {
|
||||
return this.graph.getTolerance();
|
||||
getTolerance(me: InternalMouseEvent) {
|
||||
return this.graph.getEventTolerance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tolerance to be used for intersecting connection points.
|
||||
*/
|
||||
// getImageForConstraint(state: mxCellState, constraint: mxConnectionConstraint, point: mxPoint): mxImage;
|
||||
getImageForConstraint(state, constraint, point) {
|
||||
getImageForConstraint(
|
||||
state: CellState,
|
||||
constraint: ConnectionConstraint,
|
||||
point: Point
|
||||
) {
|
||||
return this.pointImage;
|
||||
}
|
||||
|
||||
|
@ -147,16 +169,14 @@ class ConstraintHandler {
|
|||
* Returns true if the given {@link mxMouseEvent} should be ignored in {@link update}. This
|
||||
* implementation always returns false.
|
||||
*/
|
||||
// isEventIgnored(me: mxMouseEvent, source: boolean): boolean;
|
||||
isEventIgnored(me, source) {
|
||||
isEventIgnored(me: InternalMouseEvent, source = false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given state should be ignored. This always returns false.
|
||||
*/
|
||||
// isStateIgnored(state: mxCellState, source: boolean): boolean;
|
||||
isStateIgnored(state, source) {
|
||||
isStateIgnored(state: CellState, source = false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -170,8 +190,8 @@ class ConstraintHandler {
|
|||
this.focusIcons[i].destroy();
|
||||
}
|
||||
|
||||
this.focusIcons = null;
|
||||
this.focusPoints = null;
|
||||
this.focusIcons = [];
|
||||
this.focusPoints = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,16 +210,14 @@ class ConstraintHandler {
|
|||
* Returns true if the current focused state should not be changed for the given event.
|
||||
* This returns true if shift and alt are pressed.
|
||||
*/
|
||||
// isKeepFocusEvent(me: mxMouseEvent): boolean;
|
||||
isKeepFocusEvent(me) {
|
||||
isKeepFocusEvent(me: InternalMouseEvent) {
|
||||
return isShiftDown(me.getEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cell for the given event.
|
||||
*/
|
||||
// getCellForEvent(me: mxMouseEvent, point: mxPoint): mxCell;
|
||||
getCellForEvent(me, point) {
|
||||
getCellForEvent(me: InternalMouseEvent, point: Point | null) {
|
||||
let cell = me.getCell();
|
||||
|
||||
// Gets cell under actual point if different from event location
|
||||
|
@ -231,8 +249,12 @@ class ConstraintHandler {
|
|||
* Updates the state of this handler based on the given {@link mxMouseEvent}.
|
||||
* Source is a boolean indicating if the cell is a source or target.
|
||||
*/
|
||||
// update(me: mxMouseEvent, source: mxCell, existingEdge: mxCell, point: mxPoint): void;
|
||||
update(me, source, existingEdge, point) {
|
||||
update(
|
||||
me: InternalMouseEvent,
|
||||
source: boolean,
|
||||
existingEdge: boolean,
|
||||
point: Point | null
|
||||
) {
|
||||
if (this.isEnabled() && !this.isEventIgnored(me)) {
|
||||
// Lazy installation of mouseleave handler
|
||||
if (this.mouseleaveHandler == null && this.graph.container != null) {
|
||||
|
@ -253,21 +275,21 @@ class ConstraintHandler {
|
|||
2 * tol,
|
||||
2 * tol
|
||||
);
|
||||
const state = this.graph.view.getState(this.getCellForEvent(me, point));
|
||||
const state = this.graph.view.getState(this.getCellForEvent(me, point) as Cell);
|
||||
|
||||
// Keeps focus icons visible while over vertex bounds and no other cell under mouse or shift is pressed
|
||||
if (
|
||||
!this.isKeepFocusEvent(me) &&
|
||||
(this.currentFocusArea == null ||
|
||||
this.currentFocus == null ||
|
||||
state != null ||
|
||||
state ||
|
||||
!this.currentFocus.cell.isVertex() ||
|
||||
!intersects(this.currentFocusArea, mouse)) &&
|
||||
state !== this.currentFocus
|
||||
) {
|
||||
this.currentFocusArea = null;
|
||||
this.currentFocus = null;
|
||||
this.setFocus(me, state, source);
|
||||
this.setFocus(me, state!, source);
|
||||
}
|
||||
|
||||
this.currentConstraint = null;
|
||||
|
@ -285,8 +307,8 @@ class ConstraintHandler {
|
|||
const cy = mouse.getCenterY();
|
||||
|
||||
for (let i = 0; i < this.focusIcons.length; i += 1) {
|
||||
const dx = cx - this.focusIcons[i].bounds.getCenterX();
|
||||
const dy = cy - this.focusIcons[i].bounds.getCenterY();
|
||||
const dx = cx - this.focusIcons[i].bounds!.getCenterX();
|
||||
const dy = cy - this.focusIcons[i].bounds!.getCenterY();
|
||||
tmp = dx * dx + dy * dy;
|
||||
|
||||
if (
|
||||
|
@ -299,7 +321,7 @@ class ConstraintHandler {
|
|||
this.currentPoint = this.focusPoints[i];
|
||||
minDistSq = tmp;
|
||||
|
||||
tmp = this.focusIcons[i].bounds.clone();
|
||||
tmp = this.focusIcons[i].bounds!.clone();
|
||||
tmp.grow(HIGHLIGHT_SIZE + 1);
|
||||
tmp.width -= 1;
|
||||
tmp.height -= 1;
|
||||
|
@ -347,12 +369,12 @@ class ConstraintHandler {
|
|||
this.constraints != null &&
|
||||
this.focusIcons != null
|
||||
) {
|
||||
const state = this.graph.view.getState(this.currentFocus.cell);
|
||||
const state = this.graph.view.getState(this.currentFocus.cell) as CellState;
|
||||
this.currentFocus = state;
|
||||
this.currentFocusArea = new Rectangle(state.x, state.y, state.width, state.height);
|
||||
|
||||
for (let i = 0; i < this.constraints.length; i += 1) {
|
||||
const cp = this.graph.getConnectionPoint(state, this.constraints[i]);
|
||||
const cp = this.graph.getConnectionPoint(state, this.constraints[i]) as Point;
|
||||
const img = this.getImageForConstraint(state, this.constraints[i], cp);
|
||||
|
||||
const bounds = new Rectangle(
|
||||
|
@ -363,7 +385,7 @@ class ConstraintHandler {
|
|||
);
|
||||
this.focusIcons[i].bounds = bounds;
|
||||
this.focusIcons[i].redraw();
|
||||
this.currentFocusArea.add(this.focusIcons[i].bounds);
|
||||
this.currentFocusArea.add(this.focusIcons[i].bounds as Rectangle);
|
||||
this.focusPoints[i] = cp;
|
||||
}
|
||||
}
|
||||
|
@ -374,14 +396,13 @@ class ConstraintHandler {
|
|||
* the handler is not enabled then the outline is painted, but the constraints
|
||||
* are ignored.
|
||||
*/
|
||||
// setFocus(me: mxMouseEvent, state: mxCellState, source: mxCell): void;
|
||||
setFocus(me, state, source) {
|
||||
setFocus(me: InternalMouseEvent, state: CellState, source: boolean) {
|
||||
this.constraints =
|
||||
state != null && !this.isStateIgnored(state, source) && state.cell.isConnectable()
|
||||
? this.isEnabled()
|
||||
? this.graph.getAllConnectionConstraints(state, source) || []
|
||||
: []
|
||||
: null;
|
||||
: [];
|
||||
|
||||
// Only uses cells which have constraints
|
||||
if (this.constraints != null) {
|
||||
|
@ -393,15 +414,15 @@ class ConstraintHandler {
|
|||
this.focusIcons[i].destroy();
|
||||
}
|
||||
|
||||
this.focusIcons = null;
|
||||
this.focusPoints = null;
|
||||
this.focusIcons = [];
|
||||
this.focusPoints = [];
|
||||
}
|
||||
|
||||
this.focusPoints = [];
|
||||
this.focusIcons = [];
|
||||
|
||||
for (let i = 0; i < this.constraints.length; i += 1) {
|
||||
const cp = this.graph.getConnectionPoint(state, this.constraints[i]);
|
||||
const cp = this.graph.getConnectionPoint(state, this.constraints[i]) as Point;
|
||||
const img = this.getImageForConstraint(state, this.constraints[i], cp);
|
||||
|
||||
const { src } = img;
|
||||
|
@ -419,7 +440,7 @@ class ConstraintHandler {
|
|||
|
||||
// Move the icon behind all other overlays
|
||||
if (icon.node.previousSibling != null) {
|
||||
icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
|
||||
icon.node.parentNode?.insertBefore(icon.node, icon.node.parentNode.firstChild);
|
||||
}
|
||||
|
||||
const getState = () => {
|
||||
|
@ -429,7 +450,7 @@ class ConstraintHandler {
|
|||
icon.redraw();
|
||||
|
||||
InternalEvent.redirectMouseEvents(icon.node, this.graph, getState);
|
||||
this.currentFocusArea.add(icon.bounds);
|
||||
this.currentFocusArea.add(icon.bounds as Rectangle);
|
||||
this.focusIcons.push(icon);
|
||||
this.focusPoints.push(cp);
|
||||
}
|
||||
|
@ -446,10 +467,9 @@ class ConstraintHandler {
|
|||
*
|
||||
* Returns true if the given icon intersects the given point.
|
||||
*/
|
||||
// createHighlightShape(): mxShape;
|
||||
createHighlightShape() {
|
||||
const hl = new RectangleShape(
|
||||
null,
|
||||
new Rectangle(),
|
||||
this.highlightColor,
|
||||
this.highlightColor,
|
||||
HIGHLIGHT_STROKEWIDTH
|
||||
|
@ -462,9 +482,8 @@ class ConstraintHandler {
|
|||
/**
|
||||
* Returns true if the given icon intersects the given rectangle.
|
||||
*/
|
||||
// intersects(icon: mxShape, mouse: mxRectangle, source: mxCell, existingEdge: mxCell): boolean;
|
||||
intersects(icon, mouse, source, existingEdge) {
|
||||
return intersects(icon.bounds, mouse);
|
||||
intersects(icon: ImageShape, mouse: Rectangle, source: boolean, existingEdge: boolean) {
|
||||
return intersects(icon.bounds as Rectangle, mouse);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -474,12 +493,9 @@ class ConstraintHandler {
|
|||
destroy() {
|
||||
this.reset();
|
||||
|
||||
if (this.resetHandler != null) {
|
||||
this.graph.model.removeListener(this.resetHandler);
|
||||
this.graph.view.removeListener(this.resetHandler);
|
||||
this.graph.removeListener(this.resetHandler);
|
||||
this.resetHandler = null;
|
||||
}
|
||||
this.graph.model.removeListener(this.resetHandler);
|
||||
this.graph.view.removeListener(this.resetHandler);
|
||||
this.graph.removeListener(this.resetHandler);
|
||||
|
||||
if (this.mouseleaveHandler != null && this.graph.container != null) {
|
||||
InternalEvent.removeListener(
|
||||
|
@ -492,4 +508,4 @@ class ConstraintHandler {
|
|||
}
|
||||
}
|
||||
|
||||
export default mxConstraintHandler;
|
||||
export default ConstraintHandler;
|
||||
|
|
|
@ -202,7 +202,7 @@ class GraphConnections extends autoImplement<PartialClass>() {
|
|||
*/
|
||||
setConnectionConstraint(
|
||||
edge: Cell,
|
||||
terminal: Cell,
|
||||
terminal: Cell | null,
|
||||
source: boolean = false,
|
||||
constraint: ConnectionConstraint | null = null
|
||||
) {
|
||||
|
@ -380,7 +380,7 @@ class GraphConnections extends autoImplement<PartialClass>() {
|
|||
*/
|
||||
connectCell(
|
||||
edge: Cell,
|
||||
terminal: Cell,
|
||||
terminal: Cell | null = null,
|
||||
source: boolean = false,
|
||||
constraint: ConnectionConstraint | null = null
|
||||
) {
|
||||
|
@ -419,7 +419,7 @@ class GraphConnections extends autoImplement<PartialClass>() {
|
|||
*/
|
||||
cellConnected(
|
||||
edge: Cell,
|
||||
terminal: Cell,
|
||||
terminal: Cell | null,
|
||||
source: boolean = false,
|
||||
constraint: ConnectionConstraint | null = null
|
||||
) {
|
||||
|
@ -435,9 +435,9 @@ class GraphConnections extends autoImplement<PartialClass>() {
|
|||
if (this.isPortsEnabled()) {
|
||||
let id = null;
|
||||
|
||||
if (this.isPort(terminal)) {
|
||||
if (terminal && this.isPort(terminal)) {
|
||||
id = terminal.getId();
|
||||
terminal = <Cell>this.getTerminalForPort(terminal, source);
|
||||
terminal = this.getTerminalForPort(terminal, source);
|
||||
}
|
||||
|
||||
// Sets or resets all previous information for connecting to a child port
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
*/
|
||||
import Rectangle from '../geometry/Rectangle';
|
||||
import CellHighlight from '../selection/CellHighlight';
|
||||
import utils, { getDocumentScrollOrigin, getOffset, getScrollOrigin, setOpacity } from '../../util/Utils';
|
||||
import utils, {
|
||||
getDocumentScrollOrigin,
|
||||
getOffset,
|
||||
getScrollOrigin,
|
||||
setOpacity,
|
||||
} from '../../util/Utils';
|
||||
import InternalEvent from '../event/InternalEvent';
|
||||
import mxClient from '../../mxClient';
|
||||
import mxGuide from '../../util/Guide';
|
||||
|
@ -21,6 +26,22 @@ import {
|
|||
isPenEvent,
|
||||
isTouchEvent,
|
||||
} from '../../util/EventUtils';
|
||||
import EventSource from '../event/EventSource';
|
||||
import EventObject from '../event/EventObject';
|
||||
import { MaxGraph } from '../Graph';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
import Guide from '../../util/Guide';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import { GraphPlugin } from '../../types';
|
||||
import GraphHandler from '../GraphHandler';
|
||||
|
||||
type DropHandler = (
|
||||
graph: MaxGraph,
|
||||
evt: MouseEvent,
|
||||
cell: Cell | null,
|
||||
x?: number,
|
||||
y?: number
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* @class DragSource
|
||||
|
@ -33,7 +54,7 @@ import {
|
|||
*
|
||||
*/
|
||||
class DragSource {
|
||||
constructor(element, dropHandler) {
|
||||
constructor(element: EventTarget, dropHandler: DropHandler) {
|
||||
this.element = element;
|
||||
this.dropHandler = dropHandler;
|
||||
|
||||
|
@ -43,11 +64,11 @@ class DragSource {
|
|||
});
|
||||
|
||||
// Prevents native drag and drop
|
||||
InternalEvent.addListener(element, 'dragstart', (evt) => {
|
||||
InternalEvent.addListener(element, 'dragstart', (evt: MouseEvent) => {
|
||||
InternalEvent.consume(evt);
|
||||
});
|
||||
|
||||
this.eventConsumer = (sender, evt) => {
|
||||
this.eventConsumer = (sender: EventSource, evt: EventObject) => {
|
||||
const evtName = evt.getProperty('eventName');
|
||||
const me = evt.getProperty('event');
|
||||
|
||||
|
@ -60,126 +81,115 @@ class DragSource {
|
|||
/**
|
||||
* Reference to the DOM node which was made draggable.
|
||||
*/
|
||||
// element: HTMLElement;
|
||||
element = null;
|
||||
element: EventTarget;
|
||||
|
||||
/**
|
||||
* Holds the DOM node that is used to represent the drag preview. If this is
|
||||
* null then the source element will be cloned and used for the drag preview.
|
||||
*/
|
||||
// dropHandler: Function;
|
||||
dropHandler = null;
|
||||
dropHandler: DropHandler;
|
||||
|
||||
eventConsumer: (sender: EventSource, evt: EventObject) => void;
|
||||
|
||||
/**
|
||||
* {@link Point} that specifies the offset of the {@link dragElement}. Default is null.
|
||||
*/
|
||||
// dragOffset: mxPoint;
|
||||
dragOffset = null;
|
||||
dragOffset: Point | null = null;
|
||||
|
||||
/**
|
||||
* Holds the DOM node that is used to represent the drag preview. If this is
|
||||
* null then the source element will be cloned and used for the drag preview.
|
||||
*/
|
||||
// dragElement: HTMLElement;
|
||||
dragElement = null;
|
||||
dragElement: HTMLElement | null = null;
|
||||
|
||||
/**
|
||||
* TODO - wrong description
|
||||
* Optional {@link Rectangle} that specifies the unscaled size of the preview.
|
||||
*/
|
||||
// previewElement: mxRectangle;
|
||||
previewElement = null;
|
||||
previewElement: HTMLElement | null = null;
|
||||
|
||||
/**
|
||||
* Variable: previewOffset
|
||||
*
|
||||
* Optional <mxPoint> that specifies the offset of the preview in pixels.
|
||||
*/
|
||||
previewOffset = null;
|
||||
previewOffset: Point | null = null;
|
||||
|
||||
/**
|
||||
* Specifies if this drag source is enabled. Default is true.
|
||||
*/
|
||||
// enabled: boolean;
|
||||
enabled = true;
|
||||
|
||||
/**
|
||||
* Reference to the {@link mxGraph} that is the current drop target.
|
||||
*/
|
||||
// currentGraph: mxGraph;
|
||||
currentGraph = null;
|
||||
currentGraph: MaxGraph | null = null;
|
||||
|
||||
/**
|
||||
* Holds the current drop target under the mouse.
|
||||
*/
|
||||
// currentDropTarget: mxCell;
|
||||
currentDropTarget = null;
|
||||
currentDropTarget: Cell | null = null;
|
||||
|
||||
/**
|
||||
* Holds the current drop location.
|
||||
*/
|
||||
// currentPoint: mxPoint;
|
||||
currentPoint = null;
|
||||
currentPoint: Point | null = null;
|
||||
|
||||
/**
|
||||
* Holds an {@link mxGuide} for the {@link currentGraph} if {@link dragPreview} is not null.
|
||||
*/
|
||||
// currentGuide: mxGuide;
|
||||
currentGuide = null;
|
||||
currentGuide: Guide | null = null;
|
||||
|
||||
/**
|
||||
* Holds an {@link mxGuide} for the {@link currentGraph} if {@link dragPreview} is not null.
|
||||
* @note wrong doc
|
||||
*/
|
||||
// currentHighlight: mxCellHighlight;
|
||||
currentHighlight = null;
|
||||
currentHighlight: CellHighlight | null = null;
|
||||
|
||||
/**
|
||||
* Specifies if the graph should scroll automatically. Default is true.
|
||||
*/
|
||||
// autoscroll: boolean;
|
||||
autoscroll = true;
|
||||
|
||||
/**
|
||||
* Specifies if {@link mxGuide} should be enabled. Default is true.
|
||||
*/
|
||||
// guidesEnabled: boolean;
|
||||
guidesEnabled = true;
|
||||
|
||||
/**
|
||||
* Specifies if the grid should be allowed. Default is true.
|
||||
*/
|
||||
// gridEnabled: boolean;
|
||||
gridEnabled = true;
|
||||
|
||||
/**
|
||||
* Specifies if drop targets should be highlighted. Default is true.
|
||||
*/
|
||||
// highlightDropTargets: boolean;
|
||||
highlightDropTargets = true;
|
||||
|
||||
/**
|
||||
* ZIndex for the drag element. Default is 100.
|
||||
*/
|
||||
// dragElementZIndex: number;
|
||||
dragElementZIndex = 100;
|
||||
|
||||
/**
|
||||
* Opacity of the drag element in %. Default is 70.
|
||||
*/
|
||||
// dragElementOpacity: number;
|
||||
dragElementOpacity = 70;
|
||||
|
||||
/**
|
||||
* Whether the event source should be checked in {@link graphContainerEvent}. Default
|
||||
* is true.
|
||||
*/
|
||||
// checkEventSource: boolean;
|
||||
checkEventSource = true;
|
||||
|
||||
mouseMoveHandler: ((evt: MouseEvent) => void) | null = null;
|
||||
mouseUpHandler: ((evt: MouseEvent) => void) | null = null;
|
||||
|
||||
eventSource: EventTarget | null = null;
|
||||
|
||||
/**
|
||||
* Returns {@link enabled}.
|
||||
*/
|
||||
// isEnabled(): boolean;
|
||||
isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
@ -187,15 +197,13 @@ class DragSource {
|
|||
/**
|
||||
* Sets {@link enabled}.
|
||||
*/
|
||||
// setEnabled(value: boolean): void;
|
||||
setEnabled(value) {
|
||||
setEnabled(value: boolean) {
|
||||
this.enabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link guidesEnabled}.
|
||||
*/
|
||||
// isGuidesEnabled(): boolean;
|
||||
isGuidesEnabled() {
|
||||
return this.guidesEnabled;
|
||||
}
|
||||
|
@ -203,15 +211,13 @@ class DragSource {
|
|||
/**
|
||||
* Sets {@link guidesEnabled}.
|
||||
*/
|
||||
// setGuidesEnabled(value: boolean): void;
|
||||
setGuidesEnabled(value) {
|
||||
setGuidesEnabled(value: boolean) {
|
||||
this.guidesEnabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link gridEnabled}.
|
||||
*/
|
||||
// isGridEnabled(): boolean;
|
||||
isGridEnabled() {
|
||||
return this.gridEnabled;
|
||||
}
|
||||
|
@ -219,8 +225,7 @@ class DragSource {
|
|||
/**
|
||||
* Sets {@link gridEnabled}.
|
||||
*/
|
||||
// setGridEnabled(value: boolean): void;
|
||||
setGridEnabled(value) {
|
||||
setGridEnabled(value: boolean) {
|
||||
this.gridEnabled = value;
|
||||
}
|
||||
|
||||
|
@ -228,8 +233,7 @@ class DragSource {
|
|||
* Returns the graph for the given mouse event. This implementation returns
|
||||
* null.
|
||||
*/
|
||||
// getGraphForEvent(evt: MouseEvent): mxGraph;
|
||||
getGraphForEvent(evt) {
|
||||
getGraphForEvent(evt: MouseEvent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -237,8 +241,7 @@ class DragSource {
|
|||
* Returns the drop target for the given graph and coordinates. This
|
||||
* implementation uses {@link mxGraph.getCellAt}.
|
||||
*/
|
||||
// getDropTarget(graph: mxGraph, x: number, y: number, evt: PointerEvent): mxCell;
|
||||
getDropTarget(graph, x, y, evt) {
|
||||
getDropTarget(graph: MaxGraph, x: number, y: number, evt: MouseEvent) {
|
||||
return graph.getCellAt(x, y);
|
||||
}
|
||||
|
||||
|
@ -246,34 +249,30 @@ class DragSource {
|
|||
* Creates and returns a clone of the {@link dragElementPrototype} or the {@link element}
|
||||
* if the former is not defined.
|
||||
*/
|
||||
// createDragElement(evt: Event): Node;
|
||||
createDragElement(evt) {
|
||||
return this.element.cloneNode(true);
|
||||
createDragElement(evt: MouseEvent) {
|
||||
return (this.element as HTMLElement).cloneNode(true) as HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an element which can be used as a preview in the given
|
||||
* graph.
|
||||
*/
|
||||
// createPreviewElement(graph: mxGraph): HTMLElement;
|
||||
createPreviewElement(graph) {
|
||||
createPreviewElement(graph: MaxGraph): HTMLElement | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this drag source is active.
|
||||
*/
|
||||
// isActive(): boolean;
|
||||
isActive() {
|
||||
return this.mouseMoveHandler != null;
|
||||
return !!this.mouseMoveHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops and removes everything and restores the state of the object.
|
||||
*/
|
||||
// reset(): void;
|
||||
reset() {
|
||||
if (this.currentGraph != null) {
|
||||
if (this.currentGraph) {
|
||||
this.dragExit(this.currentGraph);
|
||||
this.currentGraph = null;
|
||||
}
|
||||
|
@ -303,8 +302,7 @@ class DragSource {
|
|||
* };
|
||||
* ```
|
||||
*/
|
||||
// mouseDown(evt: mxMouseEvent): void;
|
||||
mouseDown(evt) {
|
||||
mouseDown(evt: MouseEvent) {
|
||||
if (this.enabled && !isConsumed(evt) && this.mouseMoveHandler == null) {
|
||||
this.startDrag(evt);
|
||||
this.mouseMoveHandler = this.mouseMove.bind(this);
|
||||
|
@ -318,12 +316,15 @@ class DragSource {
|
|||
|
||||
if (mxClient.IS_TOUCH && !isMouseEvent(evt)) {
|
||||
this.eventSource = getSource(evt);
|
||||
InternalEvent.addGestureListeners(
|
||||
this.eventSource,
|
||||
null,
|
||||
this.mouseMoveHandler,
|
||||
this.mouseUpHandler
|
||||
);
|
||||
|
||||
if (this.eventSource) {
|
||||
InternalEvent.addGestureListeners(
|
||||
this.eventSource,
|
||||
null,
|
||||
this.mouseMoveHandler,
|
||||
this.mouseUpHandler
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -331,11 +332,10 @@ class DragSource {
|
|||
/**
|
||||
* Creates the {@link dragElement} using {@link createDragElement}.
|
||||
*/
|
||||
// startDrag(evt: mxMouseEvent): void;
|
||||
startDrag(evt) {
|
||||
startDrag(evt: MouseEvent) {
|
||||
this.dragElement = this.createDragElement(evt);
|
||||
this.dragElement.style.position = 'absolute';
|
||||
this.dragElement.style.zIndex = this.dragElementZIndex;
|
||||
this.dragElement.style.zIndex = String(this.dragElementZIndex);
|
||||
setOpacity(this.dragElement, this.dragElementOpacity);
|
||||
|
||||
if (this.checkEventSource && mxClient.IS_SVG) {
|
||||
|
@ -346,7 +346,6 @@ class DragSource {
|
|||
/**
|
||||
* Invokes {@link removeDragElement}.
|
||||
*/
|
||||
// stopDrag(): void;
|
||||
stopDrag() {
|
||||
// LATER: This used to have a mouse event. If that is still needed we need to add another
|
||||
// final call to the DnD protocol to add a cleanup step in the case of escape press, which
|
||||
|
@ -357,10 +356,9 @@ class DragSource {
|
|||
/**
|
||||
* Removes and destroys the {@link dragElement}.
|
||||
*/
|
||||
// removeDragElement(): void;
|
||||
removeDragElement() {
|
||||
if (this.dragElement != null) {
|
||||
if (this.dragElement.parentNode != null) {
|
||||
if (this.dragElement) {
|
||||
if (this.dragElement.parentNode) {
|
||||
this.dragElement.parentNode.removeChild(this.dragElement);
|
||||
}
|
||||
|
||||
|
@ -371,8 +369,7 @@ class DragSource {
|
|||
/**
|
||||
* Returns the topmost element under the given event.
|
||||
*/
|
||||
// getElementForEvent(evt: Event): Element;
|
||||
getElementForEvent(evt) {
|
||||
getElementForEvent(evt: MouseEvent) {
|
||||
return isTouchEvent(evt) || isPenEvent(evt)
|
||||
? document.elementFromPoint(getClientX(evt), getClientY(evt))
|
||||
: getSource(evt);
|
||||
|
@ -381,8 +378,7 @@ class DragSource {
|
|||
/**
|
||||
* Returns true if the given graph contains the given event.
|
||||
*/
|
||||
// graphContainsEvent(graph: mxGraph, evt: Event): boolean;
|
||||
graphContainsEvent(graph, evt) {
|
||||
graphContainsEvent(graph: MaxGraph, evt: MouseEvent) {
|
||||
const x = getClientX(evt);
|
||||
const y = getClientY(evt);
|
||||
const offset = getOffset(graph.container);
|
||||
|
@ -390,14 +386,15 @@ class DragSource {
|
|||
let elt = this.getElementForEvent(evt);
|
||||
|
||||
if (this.checkEventSource) {
|
||||
while (elt != null && elt !== graph.container) {
|
||||
while (elt && elt !== graph.container) {
|
||||
// @ts-ignore parentNode may exist
|
||||
elt = elt.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if event is inside the bounds of the graph container
|
||||
return (
|
||||
elt != null &&
|
||||
!!elt &&
|
||||
x >= offset.x - origin.x &&
|
||||
y >= offset.y - origin.y &&
|
||||
x <= offset.x - origin.x + graph.container.offsetWidth &&
|
||||
|
@ -410,35 +407,33 @@ class DragSource {
|
|||
* {@link currentGraph}, calling {@link dragEnter} and {@link dragExit} on the new and old graph,
|
||||
* respectively, and invokes {@link dragOver} if {@link currentGraph} is not null.
|
||||
*/
|
||||
// mouseMove(evt: MouseEvent): void;
|
||||
mouseMove(evt) {
|
||||
mouseMove(evt: MouseEvent) {
|
||||
let graph = this.getGraphForEvent(evt);
|
||||
|
||||
// Checks if event is inside the bounds of the graph container
|
||||
if (graph != null && !this.graphContainsEvent(graph, evt)) {
|
||||
if (graph && !this.graphContainsEvent(graph, evt)) {
|
||||
graph = null;
|
||||
}
|
||||
|
||||
if (graph !== this.currentGraph) {
|
||||
if (this.currentGraph != null) {
|
||||
if (this.currentGraph) {
|
||||
this.dragExit(this.currentGraph, evt);
|
||||
}
|
||||
|
||||
this.currentGraph = graph;
|
||||
|
||||
if (this.currentGraph != null) {
|
||||
if (this.currentGraph) {
|
||||
this.dragEnter(this.currentGraph, evt);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentGraph != null) {
|
||||
if (this.currentGraph) {
|
||||
this.dragOver(this.currentGraph, evt);
|
||||
}
|
||||
|
||||
if (
|
||||
this.dragElement != null &&
|
||||
(this.previewElement == null ||
|
||||
this.previewElement.style.visibility !== 'visible')
|
||||
this.dragElement &&
|
||||
(!this.previewElement || this.previewElement.style.visibility !== 'visible')
|
||||
) {
|
||||
let x = getClientX(evt);
|
||||
let y = getClientY(evt);
|
||||
|
@ -449,7 +444,7 @@ class DragSource {
|
|||
|
||||
this.dragElement.style.visibility = 'visible';
|
||||
|
||||
if (this.dragOffset != null) {
|
||||
if (this.dragOffset) {
|
||||
x += this.dragOffset.x;
|
||||
y += this.dragOffset.y;
|
||||
}
|
||||
|
@ -458,7 +453,7 @@ class DragSource {
|
|||
|
||||
this.dragElement.style.left = `${x + offset.x}px`;
|
||||
this.dragElement.style.top = `${y + offset.y}px`;
|
||||
} else if (this.dragElement != null) {
|
||||
} else if (this.dragElement) {
|
||||
this.dragElement.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
|
@ -469,13 +464,11 @@ class DragSource {
|
|||
* Processes the mouse up event and invokes {@link drop}, {@link dragExit} and {@link stopDrag}
|
||||
* as required.
|
||||
*/
|
||||
// mouseUp(evt: MouseEvent): void;
|
||||
mouseUp(evt) {
|
||||
if (this.currentGraph != null) {
|
||||
mouseUp(evt: MouseEvent) {
|
||||
if (this.currentGraph) {
|
||||
if (
|
||||
this.currentPoint != null &&
|
||||
(this.previewElement == null ||
|
||||
this.previewElement.style.visibility !== 'hidden')
|
||||
this.currentPoint &&
|
||||
(!this.previewElement || this.previewElement.style.visibility !== 'hidden')
|
||||
) {
|
||||
const { scale } = this.currentGraph.view;
|
||||
const tr = this.currentGraph.view.translate;
|
||||
|
@ -500,7 +493,7 @@ class DragSource {
|
|||
*/
|
||||
// removeListeners(): void;
|
||||
removeListeners() {
|
||||
if (this.eventSource != null) {
|
||||
if (this.eventSource) {
|
||||
InternalEvent.removeGestureListeners(
|
||||
this.eventSource,
|
||||
null,
|
||||
|
@ -523,26 +516,19 @@ class DragSource {
|
|||
/**
|
||||
* Actives the given graph as a drop target.
|
||||
*/
|
||||
// dragEnter(graph: mxGraph, evt: Event): void;
|
||||
dragEnter(graph, evt) {
|
||||
dragEnter(graph: MaxGraph, evt: MouseEvent) {
|
||||
graph.isMouseDown = true;
|
||||
graph.isMouseTrigger = isMouseEvent(evt);
|
||||
this.previewElement = this.createPreviewElement(graph);
|
||||
|
||||
if (
|
||||
this.previewElement != null &&
|
||||
this.checkEventSource &&
|
||||
mxClient.IS_SVG
|
||||
) {
|
||||
if (this.previewElement && this.checkEventSource && mxClient.IS_SVG) {
|
||||
this.previewElement.style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
// Guide is only needed if preview element is used
|
||||
if (this.isGuidesEnabled() && this.previewElement != null) {
|
||||
this.currentGuide = new mxGuide(
|
||||
graph,
|
||||
graph.graphHandler.getGuideStates()
|
||||
);
|
||||
if (this.isGuidesEnabled() && this.previewElement) {
|
||||
const graphHandler = graph.getPlugin('GraphHandler') as GraphHandler;
|
||||
this.currentGuide = new mxGuide(graph, graphHandler.getGuideStates());
|
||||
}
|
||||
|
||||
if (this.highlightDropTargets) {
|
||||
|
@ -556,8 +542,7 @@ class DragSource {
|
|||
/**
|
||||
* Deactivates the given graph as a drop target.
|
||||
*/
|
||||
// dragExit(graph: mxGraph, evt: Event): void;
|
||||
dragExit(graph, evt) {
|
||||
dragExit(graph: MaxGraph, evt?: MouseEvent) {
|
||||
this.currentDropTarget = null;
|
||||
this.currentPoint = null;
|
||||
graph.isMouseDown = false;
|
||||
|
@ -565,20 +550,20 @@ class DragSource {
|
|||
// Consumes all events in the current graph before they are fired
|
||||
graph.removeListener(this.eventConsumer);
|
||||
|
||||
if (this.previewElement != null) {
|
||||
if (this.previewElement.parentNode != null) {
|
||||
if (this.previewElement) {
|
||||
if (this.previewElement.parentNode) {
|
||||
this.previewElement.parentNode.removeChild(this.previewElement);
|
||||
}
|
||||
|
||||
this.previewElement = null;
|
||||
}
|
||||
|
||||
if (this.currentGuide != null) {
|
||||
if (this.currentGuide) {
|
||||
this.currentGuide.destroy();
|
||||
this.currentGuide = null;
|
||||
}
|
||||
|
||||
if (this.currentHighlight != null) {
|
||||
if (this.currentHighlight) {
|
||||
this.currentHighlight.destroy();
|
||||
this.currentHighlight = null;
|
||||
}
|
||||
|
@ -588,27 +573,29 @@ class DragSource {
|
|||
* Implements autoscroll, updates the {@link currentPoint}, highlights any drop
|
||||
* targets and updates the preview.
|
||||
*/
|
||||
// dragOver(graph: mxGraph, evt: Event): void;
|
||||
dragOver(graph, evt) {
|
||||
dragOver(graph: MaxGraph, evt: MouseEvent) {
|
||||
const offset = getOffset(graph.container);
|
||||
const origin = getScrollOrigin(graph.container);
|
||||
let x = getClientX(evt) - offset.x + origin.x - graph.panDx;
|
||||
let y = getClientY(evt) - offset.y + origin.y - graph.panDy;
|
||||
let x = getClientX(evt) - offset.x + origin.x - graph.getPanDx();
|
||||
let y = getClientY(evt) - offset.y + origin.y - graph.getPanDy();
|
||||
|
||||
if (graph.autoScroll && (this.autoscroll == null || this.autoscroll)) {
|
||||
graph.scrollPointToVisible(x, y, graph.autoExtend);
|
||||
if (graph.isAutoScroll() && (!this.autoscroll || this.autoscroll)) {
|
||||
graph.scrollPointToVisible(x, y, graph.isAutoExtend());
|
||||
}
|
||||
|
||||
// Highlights the drop target under the mouse
|
||||
if (this.currentHighlight != null && graph.isDropEnabled()) {
|
||||
if (this.currentHighlight && graph.isDropEnabled()) {
|
||||
this.currentDropTarget = this.getDropTarget(graph, x, y, evt);
|
||||
const state = graph.getView().getState(this.currentDropTarget);
|
||||
this.currentHighlight.highlight(state);
|
||||
|
||||
if (this.currentDropTarget) {
|
||||
const state = graph.getView().getState(this.currentDropTarget);
|
||||
this.currentHighlight.highlight(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the location of the preview
|
||||
if (this.previewElement != null) {
|
||||
if (this.previewElement.parentNode == null) {
|
||||
if (this.previewElement) {
|
||||
if (!this.previewElement.parentNode) {
|
||||
graph.container.appendChild(this.previewElement);
|
||||
|
||||
this.previewElement.style.zIndex = '3';
|
||||
|
@ -619,10 +606,7 @@ class DragSource {
|
|||
let hideGuide = true;
|
||||
|
||||
// Grid and guides
|
||||
if (
|
||||
this.currentGuide != null &&
|
||||
this.currentGuide.isEnabledForEvent(evt)
|
||||
) {
|
||||
if (this.currentGuide && this.currentGuide.isEnabledForEvent(evt)) {
|
||||
// LATER: HTML preview appears smaller than SVG preview
|
||||
const w = parseInt(this.previewElement.style.width);
|
||||
const h = parseInt(this.previewElement.style.height);
|
||||
|
@ -635,16 +619,16 @@ class DragSource {
|
|||
} else if (gridEnabled) {
|
||||
const { scale } = graph.view;
|
||||
const tr = graph.view.translate;
|
||||
const off = graph.gridSize / 2;
|
||||
const off = graph.getGridSize() / 2;
|
||||
x = (graph.snap(x / scale - tr.x - off) + tr.x) * scale;
|
||||
y = (graph.snap(y / scale - tr.y - off) + tr.y) * scale;
|
||||
}
|
||||
|
||||
if (this.currentGuide != null && hideGuide) {
|
||||
if (this.currentGuide && hideGuide) {
|
||||
this.currentGuide.hide();
|
||||
}
|
||||
|
||||
if (this.previewOffset != null) {
|
||||
if (this.previewOffset) {
|
||||
x += this.previewOffset.x;
|
||||
y += this.previewOffset.y;
|
||||
}
|
||||
|
@ -661,8 +645,13 @@ class DragSource {
|
|||
* Returns the drop target for the given graph and coordinates. This
|
||||
* implementation uses {@link mxGraph.getCellAt}.
|
||||
*/
|
||||
// drop(graph: mxGraph, evt: Event, dropTarget: mxCell, x: number, y: number): void;
|
||||
drop(graph, evt, dropTarget, x, y) {
|
||||
drop(
|
||||
graph: MaxGraph,
|
||||
evt: MouseEvent,
|
||||
dropTarget: Cell | null = null,
|
||||
x: number,
|
||||
y: number
|
||||
) {
|
||||
this.dropHandler(graph, evt, dropTarget, x, y);
|
||||
|
||||
// Had to move this to after the insert because it will
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import { autoImplement } from '../../util/Utils';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import CellArray from '../cell/datatypes/CellArray';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
|
||||
class GraphDragDrop {
|
||||
import type GraphValidation from '../validation/GraphValidation';
|
||||
|
||||
type PartialValidation = Pick<GraphValidation, 'getEdgeValidationError'>;
|
||||
type PartialClass = PartialValidation;
|
||||
|
||||
class GraphDragDrop extends autoImplement<PartialClass>() {
|
||||
/**
|
||||
* Specifies the return value for {@link isDropEnabled}.
|
||||
* @default false
|
||||
*/
|
||||
dropEnabled: boolean = false;
|
||||
dropEnabled = false;
|
||||
|
||||
/**
|
||||
* Specifies if dropping onto edges should be enabled. This is ignored if
|
||||
|
@ -15,7 +21,7 @@ class GraphDragDrop {
|
|||
* out the drop operation.
|
||||
* @default true
|
||||
*/
|
||||
splitEnabled: boolean = true;
|
||||
splitEnabled = true;
|
||||
|
||||
/**
|
||||
* Specifies if the graph should automatically scroll if the mouse goes near
|
||||
|
@ -27,7 +33,9 @@ class GraphDragDrop {
|
|||
* no scrollbars, the use of {@link allowAutoPanning} is recommended.
|
||||
* @default true
|
||||
*/
|
||||
autoScroll: boolean = true;
|
||||
autoScroll = true;
|
||||
|
||||
isAutoScroll = () => this.autoScroll;
|
||||
|
||||
/**
|
||||
* Specifies if the size of the graph should be automatically extended if the
|
||||
|
@ -35,8 +43,9 @@ class GraphDragDrop {
|
|||
* account if the container has scrollbars. See {@link autoScroll}.
|
||||
* @default true
|
||||
*/
|
||||
autoExtend: boolean = true;
|
||||
autoExtend = true;
|
||||
|
||||
isAutoExtend = () => this.autoExtend;
|
||||
|
||||
/*****************************************************************************
|
||||
* Group: Graph behaviour
|
||||
|
@ -45,7 +54,7 @@ class GraphDragDrop {
|
|||
/**
|
||||
* Returns {@link dropEnabled} as a boolean.
|
||||
*/
|
||||
isDropEnabled(): boolean {
|
||||
isDropEnabled() {
|
||||
return this.dropEnabled;
|
||||
}
|
||||
|
||||
|
@ -56,7 +65,7 @@ class GraphDragDrop {
|
|||
* @param dropEnabled Boolean indicating if the graph should allow dropping
|
||||
* of cells into other cells.
|
||||
*/
|
||||
setDropEnabled(value: boolean): void {
|
||||
setDropEnabled(value: boolean) {
|
||||
this.dropEnabled = value;
|
||||
}
|
||||
|
||||
|
@ -67,7 +76,7 @@ class GraphDragDrop {
|
|||
/**
|
||||
* Returns {@link splitEnabled} as a boolean.
|
||||
*/
|
||||
isSplitEnabled(): boolean {
|
||||
isSplitEnabled() {
|
||||
return this.splitEnabled;
|
||||
}
|
||||
|
||||
|
@ -78,7 +87,7 @@ class GraphDragDrop {
|
|||
* @param dropEnabled Boolean indicating if the graph should allow dropping
|
||||
* of cells into other cells.
|
||||
*/
|
||||
setSplitEnabled(value: boolean): void {
|
||||
setSplitEnabled(value: boolean) {
|
||||
this.splitEnabled = value;
|
||||
}
|
||||
|
||||
|
@ -90,23 +99,17 @@ class GraphDragDrop {
|
|||
* @param cells {@link mxCell} that should split the edge.
|
||||
* @param evt Mouseevent that triggered the invocation.
|
||||
*/
|
||||
// isSplitTarget(target: mxCell, cells: mxCellArray, evt: Event): boolean;
|
||||
isSplitTarget(target: Cell, cells: CellArray, evt: InternalMouseEvent): boolean {
|
||||
isSplitTarget(target: Cell, cells: CellArray, evt: MouseEvent) {
|
||||
if (
|
||||
target.isEdge() &&
|
||||
cells != null &&
|
||||
cells.length == 1 &&
|
||||
cells.length === 1 &&
|
||||
cells[0].isConnectable() &&
|
||||
this.getEdgeValidationError(target, target.getTerminal(true), cells[0]) ==
|
||||
null
|
||||
!this.getEdgeValidationError(target, target.getTerminal(true), cells[0])
|
||||
) {
|
||||
const src = <Cell>target.getTerminal(true);
|
||||
const trg = <Cell>target.getTerminal(false);
|
||||
const src = target.getTerminal(true);
|
||||
const trg = target.getTerminal(false);
|
||||
|
||||
return (
|
||||
!cells[0].isAncestor(src) &&
|
||||
!cells[0].isAncestor(trg)
|
||||
);
|
||||
return !cells[0].isAncestor(src) && !cells[0].isAncestor(trg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,12 @@
|
|||
* Type definitions from the typed-mxgraph project
|
||||
*/
|
||||
|
||||
import { getAlignmentAsPoint, getStringValue, getValue, setPrefixedStyle } from '../../util/Utils';
|
||||
import {
|
||||
getAlignmentAsPoint,
|
||||
getStringValue,
|
||||
getValue,
|
||||
setPrefixedStyle,
|
||||
} from '../../util/Utils';
|
||||
import Rectangle from '../geometry/Rectangle';
|
||||
import InternalEvent from '../event/InternalEvent';
|
||||
import mxClient from '../../mxClient';
|
||||
|
@ -23,20 +28,17 @@ import {
|
|||
FONT_STRIKETHROUGH,
|
||||
FONT_UNDERLINE,
|
||||
LINE_HEIGHT,
|
||||
NONE,
|
||||
WORD_WRAP,
|
||||
} from '../../util/Constants';
|
||||
import TextShape from '../geometry/shape/node/TextShape';
|
||||
import graph from '../Graph';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
import CellState from '../cell/datatypes/CellState';
|
||||
import Shape from '../geometry/shape/Shape';
|
||||
import EventObject from '../event/EventObject';
|
||||
import { extractTextWithWhitespace, isNode } from '../../util/DomUtils';
|
||||
import {
|
||||
htmlEntities,
|
||||
replaceTrailingNewlines,
|
||||
} from '../../util/StringUtils';
|
||||
import { htmlEntities, replaceTrailingNewlines } from '../../util/StringUtils';
|
||||
import {
|
||||
getSource,
|
||||
isConsumed,
|
||||
|
@ -44,6 +46,12 @@ import {
|
|||
isMetaDown,
|
||||
isShiftDown,
|
||||
} from '../../util/EventUtils';
|
||||
import EventSource from '../event/EventSource';
|
||||
|
||||
import type { MaxGraph } from '../Graph';
|
||||
import type { GraphPlugin } from '../../types';
|
||||
import CellArray from '../cell/datatypes/CellArray';
|
||||
import TooltipHandler from '../tooltip/TooltipHandler';
|
||||
|
||||
/**
|
||||
* Class: mxCellEditor
|
||||
|
@ -151,8 +159,10 @@ import {
|
|||
*
|
||||
* graph - Reference to the enclosing <mxGraph>.
|
||||
*/
|
||||
class CellEditor {
|
||||
constructor(graph: graph) {
|
||||
class CellEditor implements GraphPlugin {
|
||||
static pluginId = 'CellEditor';
|
||||
|
||||
constructor(graph: MaxGraph) {
|
||||
this.graph = graph;
|
||||
|
||||
// Stops editing after zoom changes
|
||||
|
@ -163,24 +173,21 @@ class CellEditor {
|
|||
};
|
||||
|
||||
// Handling of deleted cells while editing
|
||||
this.changeHandler = (sender: any) => {
|
||||
if (
|
||||
this.editingCell != null &&
|
||||
this.graph.getView().getState(this.editingCell, false) == null
|
||||
) {
|
||||
this.changeHandler = (sender: EventSource) => {
|
||||
if (this.editingCell && !this.graph.getView().getState(this.editingCell, false)) {
|
||||
this.stopEditing(true);
|
||||
}
|
||||
};
|
||||
|
||||
this.graph.view.addListener(InternalEvent.SCALE, this.zoomHandler);
|
||||
this.graph.view.addListener(InternalEvent.SCALE_AND_TRANSLATE, this.zoomHandler);
|
||||
this.graph.getView().addListener(InternalEvent.SCALE, this.zoomHandler);
|
||||
this.graph.getView().addListener(InternalEvent.SCALE_AND_TRANSLATE, this.zoomHandler);
|
||||
this.graph.getModel().addListener(InternalEvent.CHANGE, this.changeHandler);
|
||||
}
|
||||
|
||||
// TODO: Document me!
|
||||
changeHandler: Function | null;
|
||||
changeHandler: (sender: EventSource) => void;
|
||||
|
||||
zoomHandler: Function | null;
|
||||
zoomHandler: () => void;
|
||||
|
||||
clearOnChange: boolean = false;
|
||||
|
||||
|
@ -195,8 +202,7 @@ class CellEditor {
|
|||
*
|
||||
* Reference to the enclosing <mxGraph>.
|
||||
*/
|
||||
// graph: mxGraph;
|
||||
graph: graph;
|
||||
graph: MaxGraph;
|
||||
|
||||
/**
|
||||
* Variable: textarea
|
||||
|
@ -204,7 +210,6 @@ class CellEditor {
|
|||
* Holds the DIV that is used for text editing. Note that this may be null before the first
|
||||
* edit. Instantiated in <init>.
|
||||
*/
|
||||
// textarea: Element;
|
||||
textarea: HTMLElement | null = null;
|
||||
|
||||
/**
|
||||
|
@ -335,8 +340,7 @@ class CellEditor {
|
|||
* Creates the <textarea> and installs the event listeners. The key handler
|
||||
* updates the <modified> state.
|
||||
*/
|
||||
// init(): void;
|
||||
init(): void {
|
||||
init() {
|
||||
this.textarea = document.createElement('div');
|
||||
this.textarea.className = 'mxCellEditor mxPlainTextEditor';
|
||||
this.textarea.contentEditable = String(true);
|
||||
|
@ -356,7 +360,7 @@ class CellEditor {
|
|||
* Called in <stopEditing> if cancel is false to invoke <mxGraph.labelChanged>.
|
||||
*/
|
||||
// applyValue(state: mxCellState, value: string): void;
|
||||
applyValue(state: CellState, value: any): void {
|
||||
applyValue(state: CellState, value: any) {
|
||||
this.graph.labelChanged(state.cell, value, <InternalMouseEvent>this.trigger);
|
||||
}
|
||||
|
||||
|
@ -365,8 +369,8 @@ class CellEditor {
|
|||
*
|
||||
* Sets the temporary horizontal alignment for the current editing session.
|
||||
*/
|
||||
setAlign(align: string): void {
|
||||
if (this.textarea != null) {
|
||||
setAlign(align: string) {
|
||||
if (this.textarea) {
|
||||
this.textarea.style.textAlign = align;
|
||||
}
|
||||
|
||||
|
@ -379,12 +383,8 @@ class CellEditor {
|
|||
*
|
||||
* Gets the initial editing value for the given cell.
|
||||
*/
|
||||
// getInitialValue(state: mxCellState, trigger: Event): string;
|
||||
getInitialValue(state: CellState, trigger: EventObject | InternalMouseEvent) {
|
||||
let result = htmlEntities(
|
||||
<string>this.graph.getEditingValue(state.cell, trigger),
|
||||
false
|
||||
);
|
||||
getInitialValue(state: CellState, trigger: MouseEvent | null) {
|
||||
let result = htmlEntities(this.graph.getEditingValue(state.cell, trigger), false);
|
||||
result = replaceTrailingNewlines(result, '<div><br></div>');
|
||||
return result.replace(/\n/g, '<br>');
|
||||
}
|
||||
|
@ -394,9 +394,9 @@ class CellEditor {
|
|||
*
|
||||
* Returns the current editing value.
|
||||
*/
|
||||
// getCurrentValue(state: mxCellState): string;
|
||||
getCurrentValue(state: CellState) {
|
||||
// @ts-ignore
|
||||
if (!this.textarea) return null;
|
||||
|
||||
return extractTextWithWhitespace(this.textarea.childNodes);
|
||||
}
|
||||
|
||||
|
@ -407,12 +407,12 @@ class CellEditor {
|
|||
* are not pressed.
|
||||
*/
|
||||
// isCancelEditingKeyEvent(evt: Event): boolean;
|
||||
isCancelEditingKeyEvent(evt: KeyboardEvent) {
|
||||
isCancelEditingKeyEvent(evt: MouseEvent | KeyboardEvent) {
|
||||
return (
|
||||
this.escapeCancelsEditing ||
|
||||
isShiftDown(evt) ||
|
||||
isControlDown(evt) ||
|
||||
isMetaDown(evt)
|
||||
isShiftDown(<MouseEvent>(<unknown>evt)) ||
|
||||
isControlDown(<MouseEvent>(<unknown>evt)) ||
|
||||
isMetaDown(<MouseEvent>(<unknown>evt))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -459,8 +459,7 @@ class CellEditor {
|
|||
this.clearOnChange &&
|
||||
elt.innerHTML === this.getEmptyLabelText() &&
|
||||
(!mxClient.IS_FF ||
|
||||
(evt.keyCode !== 8 /* Backspace */ &&
|
||||
evt.keyCode !== 46)) /* Delete */
|
||||
(evt.keyCode !== 8 /* Backspace */ && evt.keyCode !== 46)) /* Delete */
|
||||
) {
|
||||
this.clearOnChange = false;
|
||||
elt.innerHTML = '';
|
||||
|
@ -496,7 +495,7 @@ class CellEditor {
|
|||
// Adds automatic resizing of the textbox while typing using input, keyup and/or DOM change events
|
||||
const evtName = 'input';
|
||||
|
||||
const resizeHandler = (evt: Event) => {
|
||||
const resizeHandler = (evt: MouseEvent) => {
|
||||
if (this.editingCell != null && this.autoSize && !isConsumed(evt)) {
|
||||
// Asynchronous is needed for keydown and shows better results for input events overall
|
||||
// (ie non-blocking and cases where the offsetWidth/-Height was wrong at this time)
|
||||
|
@ -524,14 +523,13 @@ class CellEditor {
|
|||
* returns true if F2 is pressed of if <mxGraph.enterStopsCellEditing> is true
|
||||
* and enter is pressed without control or shift.
|
||||
*/
|
||||
// isStopEditingEvent(evt: Event): boolean;
|
||||
isStopEditingEvent(evt: KeyboardEvent) {
|
||||
return (
|
||||
evt.keyCode === 113 /* F2 */ ||
|
||||
(this.graph.isEnterStopsCellEditing() &&
|
||||
evt.keyCode === 13 /* Enter */ &&
|
||||
!isControlDown(evt) &&
|
||||
!isShiftDown(evt))
|
||||
!isControlDown(<MouseEvent>(<unknown>evt)) &&
|
||||
!isShiftDown(<MouseEvent>(<unknown>evt)))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -540,7 +538,7 @@ class CellEditor {
|
|||
*
|
||||
* Returns true if this editor is the source for the given native event.
|
||||
*/
|
||||
isEventSource(evt: Event): boolean {
|
||||
isEventSource(evt: MouseEvent) {
|
||||
return getSource(evt) === this.textarea;
|
||||
}
|
||||
|
||||
|
@ -549,10 +547,12 @@ class CellEditor {
|
|||
*
|
||||
* Returns <modified>.
|
||||
*/
|
||||
resize(): void {
|
||||
const state = this.graph.getView().getState(this.editingCell);
|
||||
resize() {
|
||||
const state = this.editingCell
|
||||
? this.graph.getView().getState(this.editingCell)
|
||||
: null;
|
||||
|
||||
if (state == null) {
|
||||
if (!state) {
|
||||
this.stopEditing(true);
|
||||
} else if (this.textarea != null) {
|
||||
const isEdge = state.cell.isEdge();
|
||||
|
@ -562,22 +562,12 @@ class CellEditor {
|
|||
if (!this.autoSize || state.style.overflow === 'fill') {
|
||||
// Specifies the bounds of the editor box
|
||||
this.bounds = <Rectangle>this.getEditorBounds(state);
|
||||
this.textarea.style.width = `${Math.round(
|
||||
this.bounds.width / scale
|
||||
)}px`;
|
||||
this.textarea.style.height = `${Math.round(
|
||||
this.bounds.height / scale
|
||||
)}px`;
|
||||
this.textarea.style.width = `${Math.round(this.bounds.width / scale)}px`;
|
||||
this.textarea.style.height = `${Math.round(this.bounds.height / scale)}px`;
|
||||
|
||||
// FIXME: Offset when scaled
|
||||
this.textarea.style.left = `${Math.max(
|
||||
0,
|
||||
Math.round(this.bounds.x + 1)
|
||||
)}px`;
|
||||
this.textarea.style.top = `${Math.max(
|
||||
0,
|
||||
Math.round(this.bounds.y + 1)
|
||||
)}px`;
|
||||
this.textarea.style.left = `${Math.max(0, Math.round(this.bounds.x + 1))}px`;
|
||||
this.textarea.style.top = `${Math.max(0, Math.round(this.bounds.y + 1))}px`;
|
||||
|
||||
// Installs native word wrapping and avoids word wrap for empty label placeholder
|
||||
if (
|
||||
|
@ -606,8 +596,7 @@ class CellEditor {
|
|||
|
||||
if (m == null) {
|
||||
m = getAlignmentAsPoint(
|
||||
this.align ||
|
||||
getValue(state.style, 'align', ALIGN_CENTER),
|
||||
this.align || getValue(state.style, 'align', ALIGN_CENTER),
|
||||
getValue(state.style, 'verticalAlign', ALIGN_MIDDLE)
|
||||
);
|
||||
}
|
||||
|
@ -627,16 +616,8 @@ class CellEditor {
|
|||
}
|
||||
} else {
|
||||
let bounds = Rectangle.fromRectangle(state);
|
||||
let hpos = getValue(
|
||||
state.style,
|
||||
'labelPosition',
|
||||
ALIGN_CENTER
|
||||
);
|
||||
let vpos = getValue(
|
||||
state.style,
|
||||
'verticalLabelPosition',
|
||||
ALIGN_MIDDLE
|
||||
);
|
||||
let hpos = getValue(state.style, 'labelPosition', ALIGN_CENTER);
|
||||
let vpos = getValue(state.style, 'verticalLabelPosition', ALIGN_MIDDLE);
|
||||
|
||||
bounds =
|
||||
state.shape != null && hpos === 'center' && vpos === 'middle'
|
||||
|
@ -653,30 +634,20 @@ class CellEditor {
|
|||
) {
|
||||
// @ts-ignore
|
||||
const dummy = new TextShape(); // FIXME!!!! ===================================================================================================
|
||||
const spacing = parseInt(state.style.spacing || 2) * scale;
|
||||
const spacing = (state.style.spacing ?? 2) * scale;
|
||||
const spacingTop =
|
||||
(parseInt(state.style.spacingTop || 0) + dummy.baseSpacingTop) *
|
||||
scale +
|
||||
spacing;
|
||||
((state.style.spacingTop ?? 0) + dummy.baseSpacingTop) * scale + spacing;
|
||||
const spacingRight =
|
||||
(parseInt(state.style.spacingRight || 0) +
|
||||
dummy.baseSpacingRight) *
|
||||
scale +
|
||||
((state.style.spacingRight ?? 0) + dummy.baseSpacingRight) * scale +
|
||||
spacing;
|
||||
const spacingBottom =
|
||||
(parseInt(state.style.spacingBottom || 0) +
|
||||
dummy.baseSpacingBottom) *
|
||||
scale +
|
||||
((state.style.spacingBottom ?? 0) + dummy.baseSpacingBottom) * scale +
|
||||
spacing;
|
||||
const spacingLeft =
|
||||
(parseInt(state.style.spacingLeft || 0) + dummy.baseSpacingLeft) *
|
||||
scale +
|
||||
spacing;
|
||||
((state.style.spacingLeft ?? 0) + dummy.baseSpacingLeft) * scale + spacing;
|
||||
|
||||
hpos =
|
||||
state.style.labelPosition != null
|
||||
? state.style.labelPosition
|
||||
: 'center';
|
||||
state.style.labelPosition != null ? state.style.labelPosition : 'center';
|
||||
vpos =
|
||||
state.style.verticalLabelPosition != null
|
||||
? state.style.verticalLabelPosition
|
||||
|
@ -686,11 +657,8 @@ class CellEditor {
|
|||
bounds.x + spacingLeft,
|
||||
bounds.y + spacingTop,
|
||||
bounds.width -
|
||||
(hpos === ALIGN_CENTER && lw == null
|
||||
? spacingLeft + spacingRight
|
||||
: 0),
|
||||
bounds.height -
|
||||
(vpos === ALIGN_MIDDLE ? spacingTop + spacingBottom : 0)
|
||||
(hpos === ALIGN_CENTER && lw == null ? spacingLeft + spacingRight : 0),
|
||||
bounds.height - (vpos === ALIGN_MIDDLE ? spacingTop + spacingBottom : 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -714,8 +682,7 @@ class CellEditor {
|
|||
this.textarea.style.whiteSpace = 'normal';
|
||||
|
||||
// Forces automatic reflow if text is removed from an oversize label and normal word wrap
|
||||
const tmp =
|
||||
Math.round(this.bounds.width / scale) + this.wordWrapPadding;
|
||||
const tmp = Math.round(this.bounds.width / scale) + this.wordWrapPadding;
|
||||
|
||||
if (this.textarea.style.position !== 'relative') {
|
||||
this.textarea.style.width = `${tmp}px`;
|
||||
|
@ -750,18 +717,12 @@ class CellEditor {
|
|||
this.textarea.style.top = `${Math.max(
|
||||
0,
|
||||
Math.round(
|
||||
this.bounds.y -
|
||||
m.y * (this.bounds.height - 4) +
|
||||
(m.y === -1 ? 3 : 0)
|
||||
this.bounds.y - m.y * (this.bounds.height - 4) + (m.y === -1 ? 3 : 0)
|
||||
) + 1
|
||||
)}px`;
|
||||
}
|
||||
|
||||
setPrefixedStyle(
|
||||
this.textarea.style,
|
||||
'transformOrigin',
|
||||
'0px 0px'
|
||||
);
|
||||
setPrefixedStyle(this.textarea.style, 'transformOrigin', '0px 0px');
|
||||
setPrefixedStyle(
|
||||
this.textarea.style,
|
||||
'transform',
|
||||
|
@ -777,8 +738,7 @@ class CellEditor {
|
|||
*
|
||||
* Called if the textarea has lost focus.
|
||||
*/
|
||||
// focusLost(): void;
|
||||
focusLost(): void {
|
||||
focusLost() {
|
||||
this.stopEditing(!this.graph.isInvokesStopCellEditing());
|
||||
}
|
||||
|
||||
|
@ -786,11 +746,10 @@ class CellEditor {
|
|||
* Function: getBackgroundColor
|
||||
*
|
||||
* Returns the background color for the in-place editor. This implementation
|
||||
* always returns null.
|
||||
* always returns NONE.
|
||||
*/
|
||||
// getBackgroundColor(state: mxCellState): string;
|
||||
getBackgroundColor(state: CellState): string | null {
|
||||
return null;
|
||||
getBackgroundColor(state: CellState) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -803,11 +762,7 @@ class CellEditor {
|
|||
* cell - <mxCell> to start editing.
|
||||
* trigger - Optional mouse event that triggered the editor.
|
||||
*/
|
||||
// startEditing(cell: mxCell, trigger?: MouseEvent): void;
|
||||
startEditing(
|
||||
cell: Cell,
|
||||
trigger: InternalMouseEvent | MouseEvent | null = null
|
||||
): void {
|
||||
startEditing(cell: Cell, trigger: MouseEvent | null = null) {
|
||||
this.stopEditing(true);
|
||||
this.align = null;
|
||||
|
||||
|
@ -816,23 +771,21 @@ class CellEditor {
|
|||
this.init();
|
||||
}
|
||||
|
||||
if (this.graph.tooltipHandler != null) {
|
||||
this.graph.tooltipHandler.hideTooltip();
|
||||
const tooltipHandler = this.graph.getPlugin('TooltipHandler') as TooltipHandler;
|
||||
|
||||
if (tooltipHandler) {
|
||||
tooltipHandler.hideTooltip();
|
||||
}
|
||||
|
||||
const state = this.graph.getView().getState(cell);
|
||||
|
||||
if (state != null) {
|
||||
if (state) {
|
||||
// Configures the style of the in-place editor
|
||||
const { scale } = this.graph.getView();
|
||||
const size =
|
||||
state.style.fontSize != null ? state.style.fontSize : DEFAULT_FONTSIZE;
|
||||
const family =
|
||||
state.style.fontFamily != null
|
||||
? state.style.fontFamily
|
||||
: DEFAULT_FONTFAMILY;
|
||||
const color = getValue(state.style, 'fontColor', 'black');
|
||||
const align = getValue(state.style, 'align', ALIGN_LEFT);
|
||||
const size = state.style.fontSize ?? DEFAULT_FONTSIZE;
|
||||
const family = state.style.fontFamily ?? DEFAULT_FONTFAMILY;
|
||||
const color = state.style.fontColor ?? 'black';
|
||||
const align = state.style.align ?? ALIGN_LEFT;
|
||||
const bold = (state.style.fontStyle || 0) & FONT_BOLD;
|
||||
const italic = (state.style.fontStyle || 0) & FONT_ITALIC;
|
||||
|
||||
|
@ -848,8 +801,7 @@ class CellEditor {
|
|||
textarea.style.lineHeight = ABSOLUTE_LINE_HEIGHT
|
||||
? `${Math.round(size * LINE_HEIGHT)}px`
|
||||
: String(LINE_HEIGHT);
|
||||
textarea.style.backgroundColor =
|
||||
this.getBackgroundColor(state) || 'transparent';
|
||||
textarea.style.backgroundColor = this.getBackgroundColor(state) || 'transparent';
|
||||
textarea.style.textDecoration = txtDecor.join(' ');
|
||||
textarea.style.fontWeight = bold ? 'bold' : 'normal';
|
||||
textarea.style.fontStyle = italic ? 'italic' : '';
|
||||
|
@ -861,13 +813,11 @@ class CellEditor {
|
|||
textarea.style.color = color;
|
||||
|
||||
let dir = (this.textDirection =
|
||||
state.style.textDirection != null
|
||||
? state.style.textDirection
|
||||
: DEFAULT_TEXT_DIRECTION);
|
||||
state.style.textDirection ?? DEFAULT_TEXT_DIRECTION);
|
||||
|
||||
if (dir === 'auto') {
|
||||
if (
|
||||
state.text != null &&
|
||||
state.text !== null &&
|
||||
state.text.dialect !== DIALECT_STRICTHTML &&
|
||||
!isNode(state.text.value)
|
||||
) {
|
||||
|
@ -882,8 +832,7 @@ class CellEditor {
|
|||
}
|
||||
|
||||
// Sets the initial editing value
|
||||
textarea.innerHTML =
|
||||
this.getInitialValue(state, <InternalMouseEvent>trigger) || '';
|
||||
textarea.innerHTML = this.getInitialValue(state, trigger) || '';
|
||||
this.initialValue = textarea.innerHTML;
|
||||
|
||||
// Uses an optional text value for empty labels which is cleared
|
||||
|
@ -904,7 +853,7 @@ class CellEditor {
|
|||
this.trigger = trigger;
|
||||
this.textNode = null;
|
||||
|
||||
if (state.text != null && this.isHideLabel(state)) {
|
||||
if (state.text !== null && this.isHideLabel(state)) {
|
||||
this.textNode = <SVGGElement>state.text.node;
|
||||
this.textNode.style.visibility = 'hidden';
|
||||
}
|
||||
|
@ -913,8 +862,7 @@ class CellEditor {
|
|||
if (
|
||||
this.autoSize &&
|
||||
// @ts-ignore
|
||||
(this.graph.model.isEdge(state.cell) ||
|
||||
state.style.overflow !== 'fill')
|
||||
(this.graph.model.isEdge(state.cell) || state.style.overflow !== 'fill')
|
||||
) {
|
||||
window.setTimeout(() => {
|
||||
this.resize();
|
||||
|
@ -931,8 +879,7 @@ class CellEditor {
|
|||
if (
|
||||
this.isSelectText() &&
|
||||
textarea.innerHTML.length > 0 &&
|
||||
(textarea.innerHTML !== this.getEmptyLabelText() ||
|
||||
!this.clearOnChange)
|
||||
(textarea.innerHTML !== this.getEmptyLabelText() || !this.clearOnChange)
|
||||
) {
|
||||
document.execCommand('selectAll', false);
|
||||
}
|
||||
|
@ -947,7 +894,6 @@ class CellEditor {
|
|||
*
|
||||
* Returns <selectText>.
|
||||
*/
|
||||
// isSelectText(): boolean;
|
||||
isSelectText() {
|
||||
return this.selectText;
|
||||
}
|
||||
|
@ -955,11 +901,10 @@ class CellEditor {
|
|||
/**
|
||||
* Function: clearSelection
|
||||
*/
|
||||
// clearSelection(): void;
|
||||
clearSelection() {
|
||||
const selection = window.getSelection();
|
||||
|
||||
if (selection != null) {
|
||||
if (selection) {
|
||||
if (selection.empty) {
|
||||
selection.empty();
|
||||
} else if (selection.removeAllRanges) {
|
||||
|
@ -973,10 +918,9 @@ class CellEditor {
|
|||
*
|
||||
* Stops the editor and applies the value if cancel is false.
|
||||
*/
|
||||
// stopEditing(cancel: boolean): void;
|
||||
stopEditing(cancel: boolean = false) {
|
||||
if (this.editingCell != null) {
|
||||
if (this.textNode != null) {
|
||||
if (this.editingCell) {
|
||||
if (this.textNode) {
|
||||
this.textNode.style.visibility = 'visible';
|
||||
this.textNode = null;
|
||||
}
|
||||
|
@ -992,33 +936,27 @@ class CellEditor {
|
|||
textarea.blur();
|
||||
this.clearSelection();
|
||||
|
||||
if (textarea.parentNode != null) {
|
||||
if (textarea.parentNode) {
|
||||
textarea.parentNode.removeChild(textarea);
|
||||
}
|
||||
|
||||
if (
|
||||
this.clearOnChange &&
|
||||
textarea.innerHTML === this.getEmptyLabelText()
|
||||
) {
|
||||
if (this.clearOnChange && textarea.innerHTML === this.getEmptyLabelText()) {
|
||||
textarea.innerHTML = '';
|
||||
this.clearOnChange = false;
|
||||
}
|
||||
|
||||
if (
|
||||
state != null &&
|
||||
(textarea.innerHTML !== initial || this.align != null)
|
||||
) {
|
||||
if (state && (textarea.innerHTML !== initial || this.align !== null)) {
|
||||
this.prepareTextarea();
|
||||
const value = this.getCurrentValue(state);
|
||||
|
||||
this.graph.getModel().beginUpdate();
|
||||
try {
|
||||
if (value != null) {
|
||||
if (value !== null) {
|
||||
this.applyValue(state, value);
|
||||
}
|
||||
|
||||
if (this.align != null) {
|
||||
this.graph.setCellStyles('align', this.align, [state.cell]);
|
||||
if (this.align !== null) {
|
||||
this.graph.setCellStyles('align', this.align, new CellArray(state.cell));
|
||||
}
|
||||
} finally {
|
||||
this.graph.getModel().endUpdate();
|
||||
|
@ -1026,7 +964,8 @@ class CellEditor {
|
|||
}
|
||||
|
||||
// Forces new instance on next edit for undo history reset
|
||||
InternalEvent.release(this.textarea);
|
||||
if (this.textarea) InternalEvent.release(this.textarea);
|
||||
|
||||
this.textarea = null;
|
||||
this.align = null;
|
||||
}
|
||||
|
@ -1038,10 +977,9 @@ class CellEditor {
|
|||
* Prepares the textarea for getting its value in <stopEditing>.
|
||||
* This implementation removes the extra trailing linefeed in Firefox.
|
||||
*/
|
||||
// prepareTextarea(): void;
|
||||
prepareTextarea(): void {
|
||||
prepareTextarea() {
|
||||
const textarea = <HTMLElement>this.textarea;
|
||||
if (textarea.lastChild != null && textarea.lastChild.nodeName === 'BR') {
|
||||
if (textarea.lastChild && textarea.lastChild.nodeName === 'BR') {
|
||||
textarea.removeChild(textarea.lastChild);
|
||||
}
|
||||
}
|
||||
|
@ -1052,8 +990,7 @@ class CellEditor {
|
|||
* Returns true if the label should be hidden while the cell is being
|
||||
* edited.
|
||||
*/
|
||||
// isHideLabel(state: mxCellState): boolean;
|
||||
isHideLabel(state: CellState | null = null): boolean {
|
||||
isHideLabel(state: CellState | null = null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1062,15 +999,14 @@ class CellEditor {
|
|||
*
|
||||
* Returns the minimum width and height for editing the given state.
|
||||
*/
|
||||
// getMinimumSize(state: mxCellState): mxRectangle;
|
||||
getMinimumSize(state: CellState): Rectangle {
|
||||
getMinimumSize(state: CellState) {
|
||||
const { scale } = this.graph.getView();
|
||||
const textarea = <HTMLElement>this.textarea;
|
||||
|
||||
return new Rectangle(
|
||||
0,
|
||||
0,
|
||||
state.text == null ? 30 : state.text.size * scale + 20,
|
||||
state.text === null ? 30 : state.text.size * scale + 20,
|
||||
textarea.style.textAlign === 'left' ? 120 : 40
|
||||
);
|
||||
}
|
||||
|
@ -1080,8 +1016,7 @@ class CellEditor {
|
|||
*
|
||||
* Returns the <mxRectangle> that defines the bounds of the editor.
|
||||
*/
|
||||
// getEditorBounds(state: mxCellState): mxRectangle;
|
||||
getEditorBounds(state: CellState): Rectangle | null {
|
||||
getEditorBounds(state: CellState) {
|
||||
const isEdge = state.cell.isEdge();
|
||||
const { scale } = this.graph.getView();
|
||||
const minSize = this.getMinimumSize(state);
|
||||
|
@ -1094,28 +1029,19 @@ class CellEditor {
|
|||
state.view.graph.cellRenderer.legacySpacing &&
|
||||
state.style.overflow === 'fill'
|
||||
) {
|
||||
result = (<Shape>state.shape).getLabelBounds(
|
||||
Rectangle.fromRectangle(state)
|
||||
);
|
||||
result = (<Shape>state.shape).getLabelBounds(Rectangle.fromRectangle(state));
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const dummy = new TextShape(); // FIXME!!!! ===================================================================================================
|
||||
const spacing: number = parseInt(state.style.spacing || 0) * scale;
|
||||
const spacing: number = (state.style.spacing ?? 0) * scale;
|
||||
const spacingTop: number =
|
||||
(parseInt(state.style.spacingTop || 0) + dummy.baseSpacingTop) * scale +
|
||||
spacing;
|
||||
((state.style.spacingTop ?? 0) + dummy.baseSpacingTop) * scale + spacing;
|
||||
const spacingRight: number =
|
||||
(parseInt(state.style.spacingRight || 0) + dummy.baseSpacingRight) *
|
||||
scale +
|
||||
spacing;
|
||||
((state.style.spacingRight ?? 0) + dummy.baseSpacingRight) * scale + spacing;
|
||||
const spacingBottom: number =
|
||||
(parseInt(state.style.spacingBottom || 0) + dummy.baseSpacingBottom) *
|
||||
scale +
|
||||
spacing;
|
||||
((state.style.spacingBottom ?? 0) + dummy.baseSpacingBottom) * scale + spacing;
|
||||
const spacingLeft: number =
|
||||
(parseInt(state.style.spacingLeft || 0) + dummy.baseSpacingLeft) *
|
||||
scale +
|
||||
spacing;
|
||||
((state.style.spacingLeft ?? 0) + dummy.baseSpacingLeft) * scale + spacing;
|
||||
|
||||
result = new Rectangle(
|
||||
state.x,
|
||||
|
@ -1124,9 +1050,7 @@ class CellEditor {
|
|||
Math.max(minHeight, state.height - spacingTop - spacingBottom)
|
||||
);
|
||||
const hpos: string =
|
||||
state.style.labelPosition != null
|
||||
? state.style.labelPosition
|
||||
: 'center';
|
||||
state.style.labelPosition != null ? state.style.labelPosition : 'center';
|
||||
const vpos: string =
|
||||
state.style.verticalLabelPosition != null
|
||||
? state.style.verticalLabelPosition
|
||||
|
@ -1163,10 +1087,7 @@ class CellEditor {
|
|||
if (state.text != null && state.text.boundingBox != null) {
|
||||
if (!isEdge) {
|
||||
result.width = Math.max(result.width, state.text.boundingBox.width);
|
||||
result.height = Math.max(
|
||||
result.height,
|
||||
state.text.boundingBox.height
|
||||
);
|
||||
result.height = Math.max(result.height, state.text.boundingBox.height);
|
||||
} else {
|
||||
result.width = Math.max(minWidth, state.text.boundingBox.width);
|
||||
result.height = Math.max(minHeight, state.text.boundingBox.height);
|
||||
|
@ -1176,11 +1097,7 @@ class CellEditor {
|
|||
// Applies the horizontal and vertical label positions
|
||||
if (state.cell.isVertex()) {
|
||||
const horizontal: string = <string>(
|
||||
getStringValue(
|
||||
state.style,
|
||||
'labelPosition',
|
||||
ALIGN_CENTER
|
||||
)
|
||||
getStringValue(state.style, 'labelPosition', ALIGN_CENTER)
|
||||
);
|
||||
|
||||
if (horizontal === 'left') {
|
||||
|
@ -1222,9 +1139,8 @@ class CellEditor {
|
|||
* cell - <mxCell> for which a text for an empty editing box should be
|
||||
* returned.
|
||||
*/
|
||||
// getEmptyLabelText(cell: mxCell): string;
|
||||
getEmptyLabelText(cell: Cell | null = null): string {
|
||||
return this.emptyLabelText || '';
|
||||
getEmptyLabelText(cell: Cell | null = null) {
|
||||
return this.emptyLabelText ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1233,8 +1149,7 @@ class CellEditor {
|
|||
* Returns the cell that is currently being edited or null if no cell is
|
||||
* being edited.
|
||||
*/
|
||||
// getEditingCell(): mxCell;
|
||||
getEditingCell(): Cell | null {
|
||||
getEditingCell() {
|
||||
return this.editingCell;
|
||||
}
|
||||
|
||||
|
@ -1243,25 +1158,17 @@ class CellEditor {
|
|||
*
|
||||
* Destroys the editor and removes all associated resources.
|
||||
*/
|
||||
// destroy(): void;
|
||||
destroy(): void {
|
||||
if (this.textarea != null) {
|
||||
onDestroy() {
|
||||
if (this.textarea) {
|
||||
InternalEvent.release(this.textarea);
|
||||
if (this.textarea.parentNode != null) {
|
||||
if (this.textarea.parentNode) {
|
||||
this.textarea.parentNode.removeChild(this.textarea);
|
||||
}
|
||||
this.textarea = null;
|
||||
}
|
||||
|
||||
if (this.changeHandler != null) {
|
||||
this.graph.getModel().removeListener(this.changeHandler);
|
||||
this.changeHandler = null;
|
||||
}
|
||||
|
||||
if (this.zoomHandler) {
|
||||
this.graph.view.removeListener(this.zoomHandler);
|
||||
this.zoomHandler = null;
|
||||
}
|
||||
this.graph.getModel().removeListener(this.changeHandler);
|
||||
this.graph.getView().removeListener(this.zoomHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ class GraphEditing extends autoImplement<PartialClass>() {
|
|||
* @param cell {@link mxCell} for which the initial editing value should be returned.
|
||||
* @param evt Optional mouse event that triggered the editor.
|
||||
*/
|
||||
getEditingValue(cell: Cell, evt: EventObject) {
|
||||
getEditingValue(cell: Cell, evt: MouseEvent | null) {
|
||||
return this.convertValueToString(cell);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
*/
|
||||
import CellMarker from '../cell/CellMarker';
|
||||
import InternalMouseEvent from './InternalMouseEvent';
|
||||
import Graph from '../Graph';
|
||||
import Graph, { MaxGraph } from '../Graph';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import EventSource from './EventSource';
|
||||
|
||||
/**
|
||||
* Event handler that highlights cells
|
||||
|
@ -66,31 +67,31 @@ import Cell from '../cell/datatypes/Cell';
|
|||
*/
|
||||
class CellTracker extends CellMarker {
|
||||
constructor(
|
||||
graph: Graph,
|
||||
graph: MaxGraph,
|
||||
color: string,
|
||||
funct: null | ((me: InternalMouseEvent) => Cell)=null
|
||||
funct: ((me: InternalMouseEvent) => Cell) | null = null
|
||||
) {
|
||||
super(graph, color);
|
||||
|
||||
this.graph.event.addMouseListener(this);
|
||||
this.graph.addMouseListener(this);
|
||||
|
||||
if (funct != null) {
|
||||
if (funct) {
|
||||
this.getCell = funct;
|
||||
}
|
||||
}
|
||||
|
||||
destroyed: boolean = false;
|
||||
destroyed = false;
|
||||
|
||||
/**
|
||||
* Ignores the event. The event is not consumed.
|
||||
*/
|
||||
mouseDown(sender: any, me: InternalMouseEvent): void {}
|
||||
mouseDown(sender: EventSource, me: InternalMouseEvent) {}
|
||||
|
||||
/**
|
||||
* Handles the event by highlighting the cell under the mousepointer if it
|
||||
* is over the hotspot region of the cell.
|
||||
*/
|
||||
mouseMove(sender: any, me: InternalMouseEvent): void {
|
||||
mouseMove(sender: EventSource, me: InternalMouseEvent) {
|
||||
if (this.isEnabled()) {
|
||||
this.process(me);
|
||||
}
|
||||
|
@ -99,7 +100,7 @@ class CellTracker extends CellMarker {
|
|||
/**
|
||||
* Handles the event by resetting the highlight.
|
||||
*/
|
||||
mouseUp(sender: any, me: InternalMouseEvent): void {}
|
||||
mouseUp(sender: EventSource, me: InternalMouseEvent) {}
|
||||
|
||||
/**
|
||||
* Function: destroy
|
||||
|
@ -108,11 +109,11 @@ class CellTracker extends CellMarker {
|
|||
* normally need to be called. It is called automatically when the window
|
||||
* unloads.
|
||||
*/
|
||||
destroy(): void {
|
||||
destroy() {
|
||||
if (!this.destroyed) {
|
||||
this.destroyed = true;
|
||||
|
||||
this.graph.event.removeMouseListener(this);
|
||||
this.graph.removeMouseListener(this);
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,9 @@ type EventListenerObject = {
|
|||
*
|
||||
* Constructs a new event source.
|
||||
*/
|
||||
class EventSource {
|
||||
class EventSource extends EventTarget {
|
||||
constructor(eventSource: EventSource | null = null) {
|
||||
super();
|
||||
this.eventSource = eventSource;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,9 +30,12 @@ import CellEditor from '../editing/CellEditor';
|
|||
import type Graph from '../Graph';
|
||||
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';
|
||||
import type GraphEditing from '../editing/GraphEditing';
|
||||
import type GraphSnap from '../snap/GraphSnap';
|
||||
import { MouseEventListener, MouseListenerSet } from '../../types';
|
||||
import TooltipHandler from '../tooltip/TooltipHandler';
|
||||
import GraphDragDrop from '../drag_drop/GraphDragDrop';
|
||||
import GraphPageBreaks from '../page_breaks/GraphPageBreaks';
|
||||
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
|
@ -42,6 +45,21 @@ type PartialGraph = Pick<
|
|||
| 'getGraphBounds'
|
||||
| 'getContainer'
|
||||
| 'paintBackground'
|
||||
| 'getPlugin'
|
||||
| 'getPanDx'
|
||||
| 'getPanDy'
|
||||
| 'getMinimumContainerSize'
|
||||
| 'scrollPointToVisible'
|
||||
| 'isIgnoreScrollbars'
|
||||
| 'isTranslateToScrollPosition'
|
||||
| 'getBorder'
|
||||
| 'isResizeContainer'
|
||||
| 'doResizeContainer'
|
||||
| 'isPreferPageSize'
|
||||
| 'isPageVisible'
|
||||
| 'getPreferredPageSize'
|
||||
| 'getMinimumGraphSize'
|
||||
| 'isPageBreaksVisible'
|
||||
>;
|
||||
type PartialCells = Pick<GraphCells, 'getCellAt' | 'getCursorForCell'>;
|
||||
type PartialSelection = Pick<
|
||||
|
@ -53,11 +71,15 @@ type PartialEditing = Pick<
|
|||
'isCellEditable' | 'isEditing' | 'startEditingAtCell' | 'stopEditing'
|
||||
>;
|
||||
type PartialSnap = Pick<GraphSnap, 'getGridSize' | 'snap'>;
|
||||
type PartialDragDrop = Pick<GraphDragDrop, 'isAutoScroll' | 'isAutoExtend'>;
|
||||
type PartialPageBreaks = Pick<GraphPageBreaks, 'updatePageBreaks'>;
|
||||
type PartialClass = PartialGraph &
|
||||
PartialCells &
|
||||
PartialSelection &
|
||||
PartialEditing &
|
||||
PartialSnap &
|
||||
PartialDragDrop &
|
||||
PartialPageBreaks &
|
||||
EventSource;
|
||||
|
||||
// @ts-ignore recursive reference error
|
||||
|
@ -86,7 +108,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
* is pressed.
|
||||
* @default true
|
||||
*/
|
||||
escapeEnabled: boolean = true;
|
||||
escapeEnabled = true;
|
||||
|
||||
/**
|
||||
* If `true`, when editing is to be stopped by way of selection changing,
|
||||
|
@ -95,7 +117,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
* {@link CellEditor}.
|
||||
* @default true
|
||||
*/
|
||||
invokesStopCellEditing: boolean = true;
|
||||
invokesStopCellEditing = true;
|
||||
|
||||
/**
|
||||
* If `true`, pressing the enter key without pressing control or shift will stop
|
||||
|
@ -103,18 +125,18 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
* cell editing. Note: You can always use F2 and escape to stop editing.
|
||||
* @default false
|
||||
*/
|
||||
enterStopsCellEditing: boolean = false;
|
||||
enterStopsCellEditing = false;
|
||||
|
||||
/**
|
||||
* Holds the state of the mouse button.
|
||||
*/
|
||||
isMouseDown: boolean = false;
|
||||
isMouseDown = false;
|
||||
|
||||
/**
|
||||
* Specifies if native double click events should be detected.
|
||||
* @default true
|
||||
*/
|
||||
nativeDblClickEnabled: boolean = true;
|
||||
nativeDblClickEnabled = true;
|
||||
|
||||
isNativeDblClickEnabled = () => this.nativeDblClickEnabled;
|
||||
|
||||
|
@ -123,76 +145,76 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
* double click.
|
||||
* @default true
|
||||
*/
|
||||
doubleTapEnabled: boolean = true;
|
||||
doubleTapEnabled = true;
|
||||
|
||||
/**
|
||||
* Specifies the timeout in milliseconds for double taps and non-native double clicks.
|
||||
* @default 500
|
||||
*/
|
||||
doubleTapTimeout: number = 500;
|
||||
doubleTapTimeout = 500;
|
||||
|
||||
/**
|
||||
* Specifies the tolerance in pixels for double taps and double clicks in quirks mode.
|
||||
* @default 25
|
||||
*/
|
||||
doubleTapTolerance: number = 25;
|
||||
doubleTapTolerance = 25;
|
||||
|
||||
/**
|
||||
* Variable: lastTouchX
|
||||
*
|
||||
* Holds the x-coordinate of the last touch event for double tap detection.
|
||||
*/
|
||||
lastTouchX: number = 0;
|
||||
lastTouchX = 0;
|
||||
|
||||
/**
|
||||
* Holds the x-coordinate of the last touch event for double tap detection.
|
||||
*/
|
||||
lastTouchY: number = 0;
|
||||
lastTouchY = 0;
|
||||
|
||||
/**
|
||||
* Holds the time of the last touch event for double click detection.
|
||||
*/
|
||||
lastTouchTime: number = 0;
|
||||
lastTouchTime = 0;
|
||||
|
||||
/**
|
||||
* Specifies if tap and hold should be used for starting connections on touch-based
|
||||
* devices.
|
||||
* @default true
|
||||
*/
|
||||
tapAndHoldEnabled: boolean = true;
|
||||
tapAndHoldEnabled = true;
|
||||
|
||||
/**
|
||||
* Specifies the time in milliseconds for a tap and hold.
|
||||
* @default 500
|
||||
*/
|
||||
tapAndHoldDelay: number = 500;
|
||||
tapAndHoldDelay = 500;
|
||||
|
||||
/**
|
||||
* `True` if the timer for tap and hold events is running.
|
||||
*/
|
||||
tapAndHoldInProgress: boolean = false;
|
||||
tapAndHoldInProgress = false;
|
||||
|
||||
/**
|
||||
* `True` as long as the timer is running and the touch events
|
||||
* stay within the given {@link tapAndHoldTolerance}.
|
||||
*/
|
||||
tapAndHoldValid: boolean = false;
|
||||
tapAndHoldValid = false;
|
||||
|
||||
/**
|
||||
* Holds the x-coordinate of the initial touch event for tap and hold.
|
||||
*/
|
||||
initialTouchX: number = 0;
|
||||
initialTouchX = 0;
|
||||
|
||||
/**
|
||||
* Holds the y-coordinate of the initial touch event for tap and hold.
|
||||
*/
|
||||
initialTouchY: number = 0;
|
||||
initialTouchY = 0;
|
||||
|
||||
/**
|
||||
* Tolerance in pixels for a move to be handled as a single click.
|
||||
* @default 4
|
||||
*/
|
||||
tolerance: number = 4;
|
||||
tolerance = 4;
|
||||
|
||||
getEventTolerance = () => this.tolerance;
|
||||
|
||||
|
@ -350,7 +372,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) {
|
||||
dblClick(evt: MouseEvent, cell: Cell | null = null) {
|
||||
const mxe = new EventObject(InternalEvent.DOUBLE_CLICK, { event: evt, cell: cell });
|
||||
this.fireEvent(mxe);
|
||||
|
||||
|
@ -383,8 +405,9 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
'cell',
|
||||
me.getCell()
|
||||
);
|
||||
const panningHandler = <PanningHandler>this.panning.panningHandler;
|
||||
const connectionHandler = <ConnectionHandler>this.connectionHandler;
|
||||
|
||||
const panningHandler = this.getPlugin('PanningHandler') as PanningHandler;
|
||||
const connectionHandler = this.getPlugin('ConnectionHandler') as ConnectionHandler;
|
||||
|
||||
// LATER: Check if event should be consumed if me is consumed
|
||||
this.fireEvent(mxe);
|
||||
|
@ -401,19 +424,23 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
!mxe.isConsumed() &&
|
||||
connectionHandler.isEnabled()
|
||||
) {
|
||||
const state = this.getView().getState(connectionHandler.marker.getCell(me));
|
||||
const cell = connectionHandler.marker.getCell(me);
|
||||
|
||||
if (state != null) {
|
||||
connectionHandler.marker.currentColor = connectionHandler.marker.validColor;
|
||||
connectionHandler.marker.markedState = state;
|
||||
connectionHandler.marker.mark();
|
||||
if (cell) {
|
||||
const state = this.getView().getState(cell);
|
||||
|
||||
connectionHandler.first = new Point(me.getGraphX(), me.getGraphY());
|
||||
connectionHandler.edgeState = connectionHandler.createEdgeState(me);
|
||||
connectionHandler.previous = state;
|
||||
connectionHandler.fireEvent(
|
||||
new EventObject(InternalEvent.START, 'state', connectionHandler.previous)
|
||||
);
|
||||
if (state) {
|
||||
connectionHandler.marker.currentColor = connectionHandler.marker.validColor;
|
||||
connectionHandler.marker.markedState = state;
|
||||
connectionHandler.marker.mark();
|
||||
|
||||
connectionHandler.first = new Point(me.getGraphX(), me.getGraphY());
|
||||
connectionHandler.edgeState = connectionHandler.createEdgeState(me);
|
||||
connectionHandler.previous = state;
|
||||
connectionHandler.fireEvent(
|
||||
new EventObject(InternalEvent.START, 'state', connectionHandler.previous)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -457,25 +484,21 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
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.getPanDx();
|
||||
me.graphY = pt.y - this.getPanDy();
|
||||
|
||||
// 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)
|
||||
);
|
||||
})
|
||||
);
|
||||
if (!me.getCell() && this.isMouseDown && evtName === InternalEvent.MOUSE_MOVE) {
|
||||
const cell = this.getCellAt(pt.x, pt.y, null, true, true, (state: CellState) => {
|
||||
return (
|
||||
!state.shape ||
|
||||
state.shape.paintBackground !== this.paintBackground ||
|
||||
state.style.pointerEvents ||
|
||||
state.shape.fill !== NONE
|
||||
);
|
||||
});
|
||||
|
||||
me.state = cell ? this.getView().getState(cell) : null;
|
||||
}
|
||||
|
||||
return me;
|
||||
|
@ -491,8 +514,9 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
// Dispatches the drop event to the graph which
|
||||
// consumes and executes the source function
|
||||
const pt = convertPoint(this.getContainer(), x, y);
|
||||
const cell = this.getCellAt(pt.x, pt.y);
|
||||
|
||||
return this.getView().getState(this.getCellAt(pt.x, pt.y));
|
||||
return cell ? this.getView().getState(cell) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -527,6 +551,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
} else if (!mxClient.IS_GC && eventSource && me.getSource() !== eventSource) {
|
||||
result = true;
|
||||
} else if (
|
||||
eventSource &&
|
||||
mxClient.IS_TOUCH &&
|
||||
evtName === InternalEvent.MOUSE_DOWN &&
|
||||
!mouseEvent &&
|
||||
|
@ -625,9 +650,13 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
* @param evtName The name of the event.
|
||||
* @param me {@link mxMouseEvent} that should be ignored.
|
||||
*/
|
||||
isEventSourceIgnored(evtName: string, me: InternalMouseEvent): boolean {
|
||||
isEventSourceIgnored(evtName: string, me: InternalMouseEvent) {
|
||||
const source = me.getSource();
|
||||
const name = source.nodeName != null ? source.nodeName.toLowerCase() : '';
|
||||
|
||||
if (!source) return true;
|
||||
|
||||
// @ts-ignore nodeName could exist
|
||||
const name = source.nodeName ? source.nodeName.toLowerCase() : '';
|
||||
const candidate = !isMouseEvent(me.getEvent()) || isLeftMouseButton(me.getEvent());
|
||||
|
||||
return (
|
||||
|
@ -636,10 +665,15 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
(name === 'select' ||
|
||||
name === 'option' ||
|
||||
(name === 'input' &&
|
||||
// @ts-ignore type could exist
|
||||
source.type !== 'checkbox' &&
|
||||
// @ts-ignore type could exist
|
||||
source.type !== 'radio' &&
|
||||
// @ts-ignore type could exist
|
||||
source.type !== 'button' &&
|
||||
// @ts-ignore type could exist
|
||||
source.type !== 'submit' &&
|
||||
// @ts-ignore type could exist
|
||||
source.type !== 'file'))
|
||||
);
|
||||
}
|
||||
|
@ -650,7 +684,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* {@link CellState} - State whose event source should be returned.
|
||||
*/
|
||||
getEventState(state: CellState): CellState {
|
||||
getEventState(state: CellState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -666,9 +700,12 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
*/
|
||||
fireMouseEvent(evtName: string, me: InternalMouseEvent, sender: EventSource = this) {
|
||||
if (this.isEventSourceIgnored(evtName, me)) {
|
||||
if (this.tooltipHandler != null) {
|
||||
this.tooltipHandler.hide();
|
||||
const tooltipHandler = this.getPlugin('TooltipHandler') as TooltipHandler;
|
||||
|
||||
if (tooltipHandler) {
|
||||
tooltipHandler.hide();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -690,7 +727,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
|
||||
if (evtName === InternalEvent.MOUSE_DOWN) {
|
||||
if (
|
||||
this.lastTouchEvent != null &&
|
||||
this.lastTouchEvent &&
|
||||
this.lastTouchEvent !== me.getEvent() &&
|
||||
currentTime - this.lastTouchTime < this.doubleTapTimeout &&
|
||||
Math.abs(this.lastTouchX - me.getX()) < this.doubleTapTolerance &&
|
||||
|
@ -701,7 +738,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
let doubleClickFired = false;
|
||||
|
||||
if (evtName === InternalEvent.MOUSE_UP) {
|
||||
if (me.getCell() === this.lastTouchCell && this.lastTouchCell !== null) {
|
||||
if (me.getCell() === this.lastTouchCell && this.lastTouchCell) {
|
||||
this.lastTouchTime = 0;
|
||||
const cell = this.lastTouchCell;
|
||||
this.lastTouchCell = null;
|
||||
|
@ -718,7 +755,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
InternalEvent.consume(me.getEvent());
|
||||
return;
|
||||
}
|
||||
} else if (this.lastTouchEvent == null || this.lastTouchEvent !== me.getEvent()) {
|
||||
} else if (!this.lastTouchEvent || this.lastTouchEvent !== me.getEvent()) {
|
||||
this.lastTouchCell = me.getCell();
|
||||
this.lastTouchX = me.getX();
|
||||
this.lastTouchY = me.getY();
|
||||
|
@ -737,7 +774,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
|
||||
// Workaround for Chrome/Safari not firing native double click events for double touch on background
|
||||
const valid =
|
||||
cell != null ||
|
||||
cell ||
|
||||
((isTouchEvent(me.getEvent()) || isPenEvent(me.getEvent())) &&
|
||||
(mxClient.IS_GC || mxClient.IS_SF));
|
||||
|
||||
|
@ -756,26 +793,32 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
}
|
||||
|
||||
if (!this.isEventIgnored(evtName, me, sender)) {
|
||||
const state = me.getState();
|
||||
|
||||
// Updates the event state via getEventState
|
||||
me.state = this.getEventState(me.getState());
|
||||
me.state = state ? this.getEventState(state) : null;
|
||||
this.fireEvent(
|
||||
new EventObject(InternalEvent.FIRE_MOUSE_EVENT, 'eventName', evtName, 'event', me)
|
||||
);
|
||||
|
||||
if (mxClient.IS_SF || mxClient.IS_GC || me.getEvent().target !== this.container) {
|
||||
const container = <HTMLElement>this.container;
|
||||
if (
|
||||
mxClient.IS_SF ||
|
||||
mxClient.IS_GC ||
|
||||
me.getEvent().target !== this.getContainer()
|
||||
) {
|
||||
const container = this.getContainer();
|
||||
|
||||
if (
|
||||
evtName === InternalEvent.MOUSE_MOVE &&
|
||||
this.isMouseDown &&
|
||||
this.autoScroll &&
|
||||
!isMultiTouchEvent(me.getEvent)
|
||||
this.isAutoScroll() &&
|
||||
!isMultiTouchEvent(me.getEvent())
|
||||
) {
|
||||
this.scrollPointToVisible(me.getGraphX(), me.getGraphY(), this.autoExtend);
|
||||
this.scrollPointToVisible(me.getGraphX(), me.getGraphY(), this.isAutoExtend());
|
||||
} else if (
|
||||
evtName === InternalEvent.MOUSE_UP &&
|
||||
this.ignoreScrollbars &&
|
||||
this.translateToScrollPosition &&
|
||||
this.isIgnoreScrollbars() &&
|
||||
this.isTranslateToScrollPosition() &&
|
||||
(container.scrollLeft !== 0 || container.scrollTop !== 0)
|
||||
) {
|
||||
const s = this.getView().scale;
|
||||
|
@ -788,7 +831,6 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
container.scrollTop = 0;
|
||||
}
|
||||
|
||||
const args = [sender, me];
|
||||
const mouseListeners = this.mouseListeners;
|
||||
|
||||
// Does not change returnValue in Opera
|
||||
|
@ -798,11 +840,11 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
|
||||
for (const l of mouseListeners) {
|
||||
if (evtName === InternalEvent.MOUSE_DOWN) {
|
||||
l.mouseDown.apply(l, args);
|
||||
l.mouseDown(sender, me);
|
||||
} else if (evtName === InternalEvent.MOUSE_MOVE) {
|
||||
l.mouseMove.apply(l, args);
|
||||
l.mouseMove(sender, me);
|
||||
} else if (evtName === InternalEvent.MOUSE_UP) {
|
||||
l.mouseUp.apply(l, args);
|
||||
l.mouseUp(sender, me);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -847,11 +889,13 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
Math.abs(this.initialTouchY - me.getGraphY()) < this.tolerance;
|
||||
}
|
||||
|
||||
const cellEditor = this.getPlugin('CellEditor') as CellEditor;
|
||||
|
||||
// Stops editing for all events other than from cellEditor
|
||||
if (
|
||||
evtName === InternalEvent.MOUSE_DOWN &&
|
||||
this.isEditing() &&
|
||||
!(<CellEditor>this.cellEditor).isEventSource(me.getEvent())
|
||||
!cellEditor.isEventSource(me.getEvent())
|
||||
) {
|
||||
this.stopEditing(!this.isInvokesStopCellEditing());
|
||||
}
|
||||
|
@ -863,7 +907,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
/**
|
||||
* Consumes the given {@link InternalMouseEvent} if it's a touchStart event.
|
||||
*/
|
||||
consumeMouseEvent(evtName: string, me: InternalMouseEvent, sender: any = this): void {
|
||||
consumeMouseEvent(evtName: string, me: InternalMouseEvent, sender: EventSource = this) {
|
||||
// Workaround for duplicate click in Windows 8 with Chrome/FF/Opera with touch
|
||||
if (evtName === InternalEvent.MOUSE_DOWN && isTouchEvent(me.getEvent())) {
|
||||
me.consume(false);
|
||||
|
@ -912,7 +956,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
* a {@link size} event after updating the clipping region of the SVG element in
|
||||
* SVG-bases browsers.
|
||||
*/
|
||||
sizeDidChange(): void {
|
||||
sizeDidChange() {
|
||||
const bounds = this.getGraphBounds();
|
||||
|
||||
const border = this.getBorder();
|
||||
|
@ -920,31 +964,33 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
let width = Math.max(0, bounds.x) + bounds.width + 2 * border;
|
||||
let height = Math.max(0, bounds.y) + bounds.height + 2 * border;
|
||||
|
||||
if (this.minimumContainerSize != null) {
|
||||
width = Math.max(width, this.minimumContainerSize.width);
|
||||
height = Math.max(height, this.minimumContainerSize.height);
|
||||
const minimumContainerSize = this.getMinimumContainerSize();
|
||||
|
||||
if (minimumContainerSize) {
|
||||
width = Math.max(width, minimumContainerSize.width);
|
||||
height = Math.max(height, minimumContainerSize.height);
|
||||
}
|
||||
|
||||
if (this.resizeContainer) {
|
||||
if (this.isResizeContainer()) {
|
||||
this.doResizeContainer(width, height);
|
||||
}
|
||||
|
||||
if (this.preferPageSize || this.pageVisible) {
|
||||
if (this.isPreferPageSize() || this.isPageVisible()) {
|
||||
const size = this.getPreferredPageSize(
|
||||
bounds,
|
||||
Math.max(1, width),
|
||||
Math.max(1, height)
|
||||
);
|
||||
|
||||
if (size != null) {
|
||||
width = size.width * this.getView().scale;
|
||||
height = size.height * this.getView().scale;
|
||||
}
|
||||
width = size.width * this.getView().scale;
|
||||
height = size.height * this.getView().scale;
|
||||
}
|
||||
|
||||
if (this.minimumGraphSize != null) {
|
||||
width = Math.max(width, this.minimumGraphSize.width * this.getView().scale);
|
||||
height = Math.max(height, this.minimumGraphSize.height * this.getView().scale);
|
||||
const minimumGraphSize = this.getMinimumGraphSize();
|
||||
|
||||
if (minimumGraphSize) {
|
||||
width = Math.max(width, minimumGraphSize.width * this.getView().scale);
|
||||
height = Math.max(height, minimumGraphSize.height * this.getView().scale);
|
||||
}
|
||||
|
||||
width = Math.ceil(width);
|
||||
|
@ -953,14 +999,14 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
// @ts-ignore
|
||||
const root = this.getView().getDrawPane().ownerSVGElement;
|
||||
|
||||
if (root != null) {
|
||||
if (root) {
|
||||
root.style.minWidth = `${Math.max(1, width)}px`;
|
||||
root.style.minHeight = `${Math.max(1, height)}px`;
|
||||
root.style.width = '100%';
|
||||
root.style.height = '100%';
|
||||
}
|
||||
|
||||
this.pageBreaks.updatePageBreaks(this.pageBreaksVisible, width, height);
|
||||
this.updatePageBreaks(this.isPageBreaksVisible(), width, height);
|
||||
|
||||
this.fireEvent(new EventObject(InternalEvent.SIZE, 'bounds', bounds));
|
||||
}
|
||||
|
@ -1025,7 +1071,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: MouseEvent, addOffset: boolean = true) {
|
||||
getPointForEvent(evt: MouseEvent, addOffset = true) {
|
||||
const p = convertPoint(this.getContainer(), getClientX(evt), getClientY(evt));
|
||||
const s = this.getView().scale;
|
||||
const tr = this.getView().translate;
|
||||
|
@ -1044,7 +1090,7 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
/**
|
||||
* Returns {@link escapeEnabled}.
|
||||
*/
|
||||
isEscapeEnabled(): boolean {
|
||||
isEscapeEnabled() {
|
||||
return this.escapeEnabled;
|
||||
}
|
||||
|
||||
|
@ -1053,35 +1099,35 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param enabled Boolean indicating if escape should be enabled.
|
||||
*/
|
||||
setEscapeEnabled(value: boolean): void {
|
||||
setEscapeEnabled(value: boolean) {
|
||||
this.escapeEnabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link invokesStopCellEditing}.
|
||||
*/
|
||||
isInvokesStopCellEditing(): boolean {
|
||||
isInvokesStopCellEditing() {
|
||||
return this.invokesStopCellEditing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link invokesStopCellEditing}.
|
||||
*/
|
||||
setInvokesStopCellEditing(value: boolean): void {
|
||||
setInvokesStopCellEditing(value: boolean) {
|
||||
this.invokesStopCellEditing = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link enterStopsCellEditing}.
|
||||
*/
|
||||
isEnterStopsCellEditing(): boolean {
|
||||
isEnterStopsCellEditing() {
|
||||
return this.enterStopsCellEditing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link enterStopsCellEditing}.
|
||||
*/
|
||||
setEnterStopsCellEditing(value: boolean): void {
|
||||
setEnterStopsCellEditing(value: boolean) {
|
||||
this.enterStopsCellEditing = value;
|
||||
}
|
||||
|
||||
|
@ -1095,8 +1141,9 @@ class GraphEvents extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param me {@link mxMouseEvent} whose cursor should be returned.
|
||||
*/
|
||||
getCursorForMouseEvent(me: InternalMouseEvent): string | null {
|
||||
return this.getCursorForCell(me.getCell());
|
||||
getCursorForMouseEvent(me: InternalMouseEvent) {
|
||||
const cell = me.getCell();
|
||||
return cell ? this.getCursorForCell(cell) : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,13 @@ import mxClient from '../../mxClient';
|
|||
import { isConsumed, isMouseEvent } from '../../util/EventUtils';
|
||||
import graph from '../Graph';
|
||||
import CellState from '../cell/datatypes/CellState';
|
||||
import { EventCache, GestureEvent, Listenable, MouseEventListener } from '../../types';
|
||||
import {
|
||||
EventCache,
|
||||
GestureEvent,
|
||||
KeyboardEventListener,
|
||||
Listenable,
|
||||
MouseEventListener,
|
||||
} from '../../types';
|
||||
|
||||
// Checks if passive event listeners are supported
|
||||
// see https://github.com/Modernizr/Modernizr/issues/1894
|
||||
|
@ -50,7 +56,11 @@ class InternalEvent {
|
|||
* {@link mxUtils.bind} in order to bind the "this" keyword inside the function
|
||||
* to a given execution scope.
|
||||
*/
|
||||
static addListener(element: Listenable, eventName: string, funct: MouseEventListener) {
|
||||
static addListener(
|
||||
element: Listenable,
|
||||
eventName: string,
|
||||
funct: MouseEventListener | KeyboardEventListener
|
||||
) {
|
||||
element.addEventListener(
|
||||
eventName,
|
||||
funct as EventListener,
|
||||
|
@ -71,7 +81,7 @@ class InternalEvent {
|
|||
static removeListener(
|
||||
element: Listenable,
|
||||
eventName: string,
|
||||
funct: MouseEventListener
|
||||
funct: MouseEventListener | KeyboardEventListener
|
||||
) {
|
||||
element.removeEventListener(eventName, funct as EventListener, false);
|
||||
|
||||
|
@ -221,7 +231,7 @@ class InternalEvent {
|
|||
static redirectMouseEvents(
|
||||
node: Listenable,
|
||||
graph: graph,
|
||||
state: CellState | ((evt: Event) => CellState) | null = null,
|
||||
state: CellState | ((evt: Event) => CellState | null) | null = null,
|
||||
down: MouseEventListener | null = null,
|
||||
move: MouseEventListener | null = null,
|
||||
up: MouseEventListener | null = null,
|
||||
|
@ -265,7 +275,7 @@ class InternalEvent {
|
|||
}
|
||||
);
|
||||
|
||||
InternalEvent.addListener(node, 'dblclick', (evt) => {
|
||||
InternalEvent.addListener(node, 'dblclick', (evt: MouseEvent) => {
|
||||
if (dblClick) {
|
||||
dblClick(evt);
|
||||
} else if (!isConsumed(evt)) {
|
||||
|
@ -344,7 +354,7 @@ class InternalEvent {
|
|||
if (mxClient.IS_SF && !mxClient.IS_TOUCH) {
|
||||
let scale = 1;
|
||||
|
||||
InternalEvent.addListener(target, 'gesturestart', (evt) => {
|
||||
InternalEvent.addListener(target, 'gesturestart', (evt: GestureEvent) => {
|
||||
InternalEvent.consume(evt);
|
||||
scale = 1;
|
||||
});
|
||||
|
@ -362,7 +372,7 @@ class InternalEvent {
|
|||
}
|
||||
}) as EventListener);
|
||||
|
||||
InternalEvent.addListener(target, 'gestureend', (evt) => {
|
||||
InternalEvent.addListener(target, 'gestureend', (evt: GestureEvent) => {
|
||||
InternalEvent.consume(evt);
|
||||
});
|
||||
} else {
|
||||
|
@ -427,7 +437,7 @@ class InternalEvent {
|
|||
* Disables the context menu for the given element.
|
||||
*/
|
||||
static disableContextMenu(element: Listenable) {
|
||||
InternalEvent.addListener(element, 'contextmenu', (evt) => {
|
||||
InternalEvent.addListener(element, 'contextmenu', (evt: MouseEvent) => {
|
||||
if (evt.preventDefault) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
|
|
@ -276,6 +276,8 @@ class Shape {
|
|||
*/
|
||||
pointerEvents = true;
|
||||
|
||||
originalPointerEvents: boolean | null = null;
|
||||
|
||||
/**
|
||||
* Variable: svgPointerEvents
|
||||
*
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
import Rectangle from "../geometry/Rectangle";
|
||||
import Point from "../geometry/Point";
|
||||
import Polyline from "../geometry/shape/edge/Polyline";
|
||||
import Rectangle from '../geometry/Rectangle';
|
||||
import Point from '../geometry/Point';
|
||||
import Polyline from '../geometry/shape/edge/Polyline';
|
||||
import { autoImplement } from '../../util/Utils';
|
||||
|
||||
class GraphPageBreaks {
|
||||
import type Graph from '../Graph';
|
||||
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
| 'getView'
|
||||
| 'getGraphBounds'
|
||||
| 'getPageFormat'
|
||||
| 'getPageScale'
|
||||
| 'getMinPageBreakDist'
|
||||
| 'getPageBreakColor'
|
||||
| 'getDialect'
|
||||
| 'isPageBreakDashed'
|
||||
>;
|
||||
type PartialClass = PartialGraph;
|
||||
|
||||
class GraphPageBreaks extends autoImplement<PartialClass>() {
|
||||
horizontalPageBreaks: any[] | null = null;
|
||||
verticalPageBreaks: any[] | null = null;
|
||||
|
||||
|
@ -14,10 +30,9 @@ class GraphPageBreaks {
|
|||
* @param height Specifies the height of the container in pixels.
|
||||
*/
|
||||
updatePageBreaks(visible: boolean, width: number, height: number): void {
|
||||
const { scale } = this.view;
|
||||
const tr = this.getView().translate;
|
||||
const fmt = this.pageFormat;
|
||||
const ps = scale * this.pageScale;
|
||||
const { scale, translate: tr } = this.getView();
|
||||
const fmt = this.getPageFormat();
|
||||
const ps = scale * this.getPageScale();
|
||||
const bounds = new Rectangle(0, 0, fmt.width * ps, fmt.height * ps);
|
||||
|
||||
const gb = Rectangle.fromRectangle(this.getGraphBounds());
|
||||
|
@ -25,25 +40,19 @@ class GraphPageBreaks {
|
|||
gb.height = Math.max(1, gb.height);
|
||||
|
||||
bounds.x =
|
||||
Math.floor((gb.x - tr.x * scale) / bounds.width) * bounds.width +
|
||||
tr.x * scale;
|
||||
Math.floor((gb.x - tr.x * scale) / bounds.width) * bounds.width + tr.x * scale;
|
||||
bounds.y =
|
||||
Math.floor((gb.y - tr.y * scale) / bounds.height) * bounds.height +
|
||||
tr.y * scale;
|
||||
Math.floor((gb.y - tr.y * scale) / bounds.height) * bounds.height + tr.y * scale;
|
||||
|
||||
gb.width =
|
||||
Math.ceil((gb.width + (gb.x - bounds.x)) / bounds.width) * bounds.width;
|
||||
gb.width = Math.ceil((gb.width + (gb.x - bounds.x)) / bounds.width) * bounds.width;
|
||||
gb.height =
|
||||
Math.ceil((gb.height + (gb.y - bounds.y)) / bounds.height) *
|
||||
bounds.height;
|
||||
Math.ceil((gb.height + (gb.y - bounds.y)) / bounds.height) * bounds.height;
|
||||
|
||||
// Does not show page breaks if the scale is too small
|
||||
visible =
|
||||
visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist;
|
||||
visible && Math.min(bounds.width, bounds.height) > this.getMinPageBreakDist();
|
||||
|
||||
const horizontalCount = visible
|
||||
? Math.ceil(gb.height / bounds.height) + 1
|
||||
: 0;
|
||||
const horizontalCount = visible ? Math.ceil(gb.height / bounds.height) + 1 : 0;
|
||||
const verticalCount = visible ? Math.ceil(gb.width / bounds.width) + 1 : 0;
|
||||
const right = (verticalCount - 1) * bounds.width;
|
||||
const bottom = (horizontalCount - 1) * bounds.height;
|
||||
|
@ -59,42 +68,40 @@ class GraphPageBreaks {
|
|||
const drawPageBreaks = (breaks: any) => {
|
||||
if (breaks != null) {
|
||||
const count =
|
||||
breaks === this.horizontalPageBreaks
|
||||
? horizontalCount
|
||||
: verticalCount;
|
||||
breaks === this.horizontalPageBreaks ? horizontalCount : verticalCount;
|
||||
|
||||
for (let i = 0; i <= count; i += 1) {
|
||||
const pts =
|
||||
breaks === this.horizontalPageBreaks
|
||||
? [
|
||||
new Point(
|
||||
Math.round(bounds.x),
|
||||
Math.round(bounds.y + i * bounds.height)
|
||||
),
|
||||
new Point(
|
||||
Math.round(bounds.x + right),
|
||||
Math.round(bounds.y + i * bounds.height)
|
||||
),
|
||||
]
|
||||
new Point(
|
||||
Math.round(bounds.x),
|
||||
Math.round(bounds.y + i * bounds.height)
|
||||
),
|
||||
new Point(
|
||||
Math.round(bounds.x + right),
|
||||
Math.round(bounds.y + i * bounds.height)
|
||||
),
|
||||
]
|
||||
: [
|
||||
new Point(
|
||||
Math.round(bounds.x + i * bounds.width),
|
||||
Math.round(bounds.y)
|
||||
),
|
||||
new Point(
|
||||
Math.round(bounds.x + i * bounds.width),
|
||||
Math.round(bounds.y + bottom)
|
||||
),
|
||||
];
|
||||
new Point(
|
||||
Math.round(bounds.x + i * bounds.width),
|
||||
Math.round(bounds.y)
|
||||
),
|
||||
new Point(
|
||||
Math.round(bounds.x + i * bounds.width),
|
||||
Math.round(bounds.y + bottom)
|
||||
),
|
||||
];
|
||||
|
||||
if (breaks[i] != null) {
|
||||
breaks[i].points = pts;
|
||||
breaks[i].redraw();
|
||||
} else {
|
||||
const pageBreak = new Polyline(pts, this.pageBreakColor);
|
||||
pageBreak.dialect = this.dialect;
|
||||
const pageBreak = new Polyline(pts, this.getPageBreakColor());
|
||||
pageBreak.dialect = this.getDialect();
|
||||
pageBreak.pointerEvents = false;
|
||||
pageBreak.isDashed = this.pageBreakDashed;
|
||||
pageBreak.isDashed = this.isPageBreakDashed();
|
||||
pageBreak.init(this.getView().backgroundPane);
|
||||
pageBreak.redraw();
|
||||
|
||||
|
@ -116,4 +123,3 @@ class GraphPageBreaks {
|
|||
}
|
||||
|
||||
export default GraphPageBreaks;
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ class GraphPanning extends autoImplement<PartialClass>() {
|
|||
*/
|
||||
timerAutoScroll = false;
|
||||
|
||||
isTimerAutoScroll = () => this.timerAutoScroll;
|
||||
|
||||
/**
|
||||
* Specifies if panning via {@link panGraph} should be allowed to implement autoscroll
|
||||
* if no scrollbars are available in {@link scrollPointToVisible}. To enable panning
|
||||
|
@ -47,6 +49,8 @@ class GraphPanning extends autoImplement<PartialClass>() {
|
|||
*/
|
||||
allowAutoPanning = false;
|
||||
|
||||
isAllowAutoPanning = () => this.allowAutoPanning;
|
||||
|
||||
/**
|
||||
* Current horizontal panning value.
|
||||
* @default 0
|
||||
|
|
|
@ -121,6 +121,8 @@ class PanningHandler extends EventSource implements GraphPlugin {
|
|||
|
||||
panningManager: PanningManager;
|
||||
|
||||
getPanningManager = () => this.panningManager;
|
||||
|
||||
/**
|
||||
* Variable: useLeftButtonForPanning
|
||||
*
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
import { MouseEventListener, MouseListenerSet } from '../../types';
|
||||
import { hasScrollbars } from '../../util/Utils';
|
||||
import EventObject from '../event/EventObject';
|
||||
import EventSource from '../event/EventSource';
|
||||
import InternalEvent from '../event/InternalEvent';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
import { MaxGraph } from '../Graph';
|
||||
|
||||
/**
|
||||
|
@ -31,9 +33,9 @@ class PanningManager {
|
|||
this.scrollTop = 0;
|
||||
|
||||
this.mouseListener = {
|
||||
mouseDown: (sender, me) => {},
|
||||
mouseMove: (sender, me) => {},
|
||||
mouseUp: (sender, me) => {
|
||||
mouseDown: (sender: EventSource, me: InternalMouseEvent) => {},
|
||||
mouseMove: (sender: EventSource, me: InternalMouseEvent) => {},
|
||||
mouseUp: (sender: EventSource, me: InternalMouseEvent) => {
|
||||
if (this.active) {
|
||||
this.stop();
|
||||
}
|
||||
|
@ -92,7 +94,7 @@ class PanningManager {
|
|||
this.active = true;
|
||||
};
|
||||
|
||||
this.panTo = (x, y, w, h) => {
|
||||
this.panTo = (x, y, w = 0, h = 0) => {
|
||||
if (!this.active) {
|
||||
this.start();
|
||||
}
|
||||
|
@ -100,9 +102,6 @@ class PanningManager {
|
|||
this.scrollLeft = graph.container.scrollLeft;
|
||||
this.scrollTop = graph.container.scrollTop;
|
||||
|
||||
w = w != null ? w : 0;
|
||||
h = h != null ? h : 0;
|
||||
|
||||
const c = graph.container;
|
||||
this.dx = x + w - c.scrollLeft - c.clientWidth;
|
||||
|
||||
|
@ -249,7 +248,7 @@ class PanningManager {
|
|||
getDx: () => number;
|
||||
getDy: () => number;
|
||||
start: () => void;
|
||||
panTo: (x: number, y: number, w: number, h: number) => void;
|
||||
panTo: (x: number, y: number, w?: number, h?: number) => void;
|
||||
destroy: () => void;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,16 @@
|
|||
* Updated to ES9 syntax by David Morrissey 2021
|
||||
* Type definitions from the typed-mxgraph project
|
||||
*/
|
||||
import mxPopupMenu from '../../util/gui/mxPopupMenu';
|
||||
import PopupMenu from '../../util/gui/PopupMenu';
|
||||
import InternalEvent from '../event/InternalEvent';
|
||||
import utils, { getScrollOrigin } from '../../util/Utils';
|
||||
import { getScrollOrigin } from '../../util/Utils';
|
||||
import { getMainEvent, isMultiTouchEvent } from '../../util/EventUtils';
|
||||
import Graph from '../Graph';
|
||||
import { MaxGraph } from '../Graph';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import { GraphPlugin } from '../../types';
|
||||
import { GraphPlugin, PopupMenuItem } from '../../types';
|
||||
import TooltipHandler from '../tooltip/TooltipHandler';
|
||||
import EventSource from '../event/EventSource';
|
||||
import EventObject from '../event/EventObject';
|
||||
|
||||
/**
|
||||
* Class: mxPopupMenuHandler
|
||||
|
@ -22,40 +24,36 @@ import { GraphPlugin } from '../../types';
|
|||
*
|
||||
* Constructs an event handler that creates a <mxPopupMenu>.
|
||||
*/
|
||||
class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
||||
constructor(graph: Graph, factoryMethod: any) {
|
||||
class PopupMenuHandler extends PopupMenu implements GraphPlugin {
|
||||
static pluginId = 'PopupMenuHandler';
|
||||
|
||||
constructor(graph: MaxGraph) {
|
||||
super();
|
||||
|
||||
if (graph != null) {
|
||||
this.graph = graph;
|
||||
this.factoryMethod = factoryMethod;
|
||||
this.graph.event.addMouseListener(this);
|
||||
this.graph = graph;
|
||||
this.graph.addMouseListener(this);
|
||||
|
||||
// Does not show menu if any touch gestures take place after the trigger
|
||||
this.gestureHandler = (sender, eo) => {
|
||||
this.inTolerance = false;
|
||||
};
|
||||
// Does not show menu if any touch gestures take place after the trigger
|
||||
this.gestureHandler = (sender: EventSource, eo: EventObject) => {
|
||||
this.inTolerance = false;
|
||||
};
|
||||
|
||||
this.graph.addListener(InternalEvent.GESTURE, this.gestureHandler);
|
||||
this.graph.addListener(InternalEvent.GESTURE, this.gestureHandler);
|
||||
|
||||
this.init();
|
||||
}
|
||||
this.init();
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
gestureHandler: (...args: any[]) => any;
|
||||
// @ts-ignore
|
||||
inTolerance: boolean;
|
||||
// @ts-ignore
|
||||
popupTrigger: boolean;
|
||||
gestureHandler: (sender: EventSource, eo: EventObject) => void;
|
||||
|
||||
inTolerance = false;
|
||||
popupTrigger = false;
|
||||
|
||||
/**
|
||||
* Variable: graph
|
||||
*
|
||||
* Reference to the enclosing <mxGraph>.
|
||||
*/
|
||||
// @ts-ignore
|
||||
graph: Graph;
|
||||
graph: MaxGraph;
|
||||
|
||||
/**
|
||||
* Variable: selectOnPopup
|
||||
|
@ -63,7 +61,7 @@ class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
|||
* Specifies if cells should be selected if a popupmenu is displayed for
|
||||
* them. Default is true.
|
||||
*/
|
||||
selectOnPopup: boolean = true;
|
||||
selectOnPopup = true;
|
||||
|
||||
/**
|
||||
* Variable: clearSelectionOnBackground
|
||||
|
@ -71,7 +69,7 @@ class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
|||
* Specifies if cells should be deselected if a popupmenu is displayed for
|
||||
* the diagram background. Default is true.
|
||||
*/
|
||||
clearSelectionOnBackground: boolean = true;
|
||||
clearSelectionOnBackground = true;
|
||||
|
||||
/**
|
||||
* Variable: triggerX
|
||||
|
@ -106,14 +104,12 @@ class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
|||
*
|
||||
* Initializes the shapes required for this vertex handler.
|
||||
*/
|
||||
init(): void {
|
||||
// Supercall
|
||||
super.init();
|
||||
|
||||
init() {
|
||||
// Hides the tooltip if the mouse is over
|
||||
// the context menu
|
||||
InternalEvent.addGestureListeners(this.div, (evt) => {
|
||||
this.graph.tooltipHandler.hide();
|
||||
const tooltipHandler = this.graph.getPlugin('TooltipHandler') as TooltipHandler;
|
||||
tooltipHandler.hide();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -133,7 +129,7 @@ class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
|||
* 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: InternalMouseEvent): void {
|
||||
mouseDown(sender: EventSource, me: InternalMouseEvent) {
|
||||
if (this.isEnabled() && !isMultiTouchEvent(me.getEvent())) {
|
||||
// Hides the popupmenu if is is being displayed
|
||||
this.hideMenu();
|
||||
|
@ -151,14 +147,14 @@ class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
|||
*
|
||||
* Handles the event by updating the panning on the graph.
|
||||
*/
|
||||
mouseMove(sender: any, me: InternalMouseEvent): void {
|
||||
mouseMove(sender: EventSource, me: InternalMouseEvent) {
|
||||
// Popup trigger may change on mouseUp so ignore it
|
||||
if (this.inTolerance && this.screenX != null && this.screenY != null) {
|
||||
if (
|
||||
Math.abs(getMainEvent(me.getEvent()).screenX - this.screenX) >
|
||||
this.graph.tolerance ||
|
||||
this.graph.getEventTolerance() ||
|
||||
Math.abs(getMainEvent(me.getEvent()).screenY - this.screenY) >
|
||||
this.graph.tolerance
|
||||
this.graph.getEventTolerance()
|
||||
) {
|
||||
this.inTolerance = false;
|
||||
}
|
||||
|
@ -171,7 +167,7 @@ class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
|||
* Handles the event by setting the translation on the view or showing the
|
||||
* popupmenu.
|
||||
*/
|
||||
mouseUp(sender: any, me: InternalMouseEvent): void {
|
||||
mouseUp(sender: EventSource, me: InternalMouseEvent) {
|
||||
if (
|
||||
this.popupTrigger &&
|
||||
this.inTolerance &&
|
||||
|
@ -185,15 +181,16 @@ class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
|||
this.graph.isEnabled() &&
|
||||
this.isSelectOnPopup(me) &&
|
||||
cell != null &&
|
||||
!this.graph.selection.isCellSelected(cell)
|
||||
!this.graph.isCellSelected(cell)
|
||||
) {
|
||||
this.graph.selection.setSelectionCell(cell);
|
||||
this.graph.setSelectionCell(cell);
|
||||
} else if (this.clearSelectionOnBackground && cell == null) {
|
||||
this.graph.selection.clearSelection();
|
||||
this.graph.clearSelection();
|
||||
}
|
||||
|
||||
// Hides the tooltip if there is one
|
||||
this.graph.tooltipHandler.hide();
|
||||
const tooltipHandler = this.graph.getPlugin('TooltipHandler') as TooltipHandler;
|
||||
tooltipHandler.hide();
|
||||
|
||||
// Menu is shifted by 1 pixel so that the mouse up event
|
||||
// is routed via the underlying shape instead of the DIV
|
||||
|
@ -211,7 +208,7 @@ class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
|||
*
|
||||
* Hook to return the cell for the mouse up popup trigger handling.
|
||||
*/
|
||||
getCellForPopupEvent(me: InternalMouseEvent): Cell {
|
||||
getCellForPopupEvent(me: InternalMouseEvent) {
|
||||
return me.getCell();
|
||||
}
|
||||
|
||||
|
@ -220,9 +217,9 @@ class PopupMenuHandler extends mxPopupMenu implements GraphPlugin {
|
|||
*
|
||||
* Destroys the handler and all its resources and DOM nodes.
|
||||
*/
|
||||
destroy(): void {
|
||||
this.graph.event.removeMouseListener(this);
|
||||
this.graph.event.removeListener(this.gestureHandler);
|
||||
onDestroy() {
|
||||
this.graph.removeMouseListener(this);
|
||||
this.graph.removeListener(this.gestureHandler);
|
||||
|
||||
// Supercall
|
||||
super.destroy();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import Cell from "../cell/datatypes/Cell";
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import Graph from '../Graph';
|
||||
|
||||
class GraphPorts {
|
||||
|
@ -40,7 +40,7 @@ class GraphPorts {
|
|||
*
|
||||
* @param cell {@link mxCell} that represents the port.
|
||||
*/
|
||||
isPort(cell: Cell): boolean {
|
||||
isPort(cell: Cell | null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,8 +116,8 @@ class GraphSelection extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param cell {@link mxCell} to be selected.
|
||||
*/
|
||||
setCell(cell: Cell) {
|
||||
this.setCells(new CellArray(cell));
|
||||
setCell(cell: Cell | null) {
|
||||
this.setCells(cell ? new CellArray(cell) : new CellArray());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -318,7 +318,7 @@ class GraphSelection extends autoImplement<PartialClass>() {
|
|||
*
|
||||
* @param cell {@link mxCell} to be selected.
|
||||
*/
|
||||
setSelectionCell(cell: Cell) {
|
||||
setSelectionCell(cell: Cell | null) {
|
||||
this.setCell(cell);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Cell from '../cell/datatypes/Cell';
|
||||
import Rectangle from '../geometry/Rectangle';
|
||||
import utils, { convertPoint, getValue, mod } from '../../util/Utils';
|
||||
import utils, { autoImplement, convertPoint, getValue, mod } from '../../util/Utils';
|
||||
import {
|
||||
DEFAULT_STARTSIZE,
|
||||
DIRECTION_EAST,
|
||||
|
@ -12,34 +12,42 @@ import {
|
|||
import CellArray from '../cell/datatypes/CellArray';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
import { getClientX, getClientY } from '../../util/EventUtils';
|
||||
import Graph from '../Graph';
|
||||
|
||||
class GraphSwimlane {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
import type Graph from '../Graph';
|
||||
import GraphCells from '../cell/GraphCells';
|
||||
import { CellStateStyles } from '../../types';
|
||||
import GraphDragDrop from '../drag_drop/GraphDragDrop';
|
||||
import GraphPanning from '../panning/GraphPanning';
|
||||
|
||||
graph: Graph;
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
'getDefaultParent' | 'getCurrentRoot' | 'getModel' | 'getView' | 'getContainer'
|
||||
>;
|
||||
type PartialCells = Pick<GraphCells, 'getCurrentCellStyle' | 'intersects'>;
|
||||
type PartialDragDrop = Pick<GraphDragDrop, 'isSplitEnabled' | 'isSplitTarget'>;
|
||||
type PartialPanning = Pick<GraphPanning, 'getPanDx' | 'getPanDy'>;
|
||||
type PartialClass = PartialGraph & PartialCells & PartialDragDrop & PartialPanning;
|
||||
|
||||
class GraphSwimlane extends autoImplement<PartialClass>() {
|
||||
/**
|
||||
* Specifies if swimlanes should be selectable via the content if the
|
||||
* mouse is released.
|
||||
* @default true
|
||||
*/
|
||||
swimlaneSelectionEnabled: boolean = true;
|
||||
swimlaneSelectionEnabled = true;
|
||||
|
||||
/**
|
||||
* Specifies if nesting of swimlanes is allowed.
|
||||
* @default true
|
||||
*/
|
||||
swimlaneNesting: boolean = true;
|
||||
swimlaneNesting = true;
|
||||
|
||||
/**
|
||||
* The attribute used to find the color for the indicator if the indicator
|
||||
* color is set to 'swimlane'.
|
||||
* @default {@link 'fillColor'}
|
||||
*/
|
||||
swimlaneIndicatorColorAttribute: string = 'fillColor';
|
||||
swimlaneIndicatorColorAttribute = 'fillColor';
|
||||
|
||||
/**
|
||||
* Returns the nearest ancestor of the given cell which is a swimlane, or
|
||||
|
@ -47,10 +55,9 @@ class GraphSwimlane {
|
|||
*
|
||||
* @param cell {@link mxCell} for which the ancestor swimlane should be returned.
|
||||
*/
|
||||
// getSwimlane(cell: mxCell): mxCell;
|
||||
getSwimlane(cell: Cell | null = null): Cell | null {
|
||||
while (cell != null && !this.isSwimlane(cell)) {
|
||||
cell = <Cell>cell.getParent();
|
||||
getSwimlane(cell: Cell | null = null) {
|
||||
while (cell && !this.isSwimlane(cell)) {
|
||||
cell = cell.getParent();
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
@ -64,26 +71,22 @@ class GraphSwimlane {
|
|||
* @param parent {@link mxCell} that should be used as the root of the recursion.
|
||||
* Default is {@link defaultParent}.
|
||||
*/
|
||||
getSwimlaneAt(
|
||||
x: number,
|
||||
y: number,
|
||||
parent: Cell = this.getDefaultParent()
|
||||
): Cell | null {
|
||||
if (parent == null) {
|
||||
parent = <Cell>this.getCurrentRoot();
|
||||
getSwimlaneAt(x: number, y: number, parent?: Cell | null): Cell | null {
|
||||
if (!parent) {
|
||||
parent = this.getCurrentRoot();
|
||||
|
||||
if (parent == null) {
|
||||
parent = <Cell>this.getModel().getRoot();
|
||||
if (!parent) {
|
||||
parent = this.getModel().getRoot();
|
||||
}
|
||||
}
|
||||
|
||||
if (parent != null) {
|
||||
if (parent) {
|
||||
const childCount = parent.getChildCount();
|
||||
|
||||
for (let i = 0; i < childCount; i += 1) {
|
||||
const child = parent.getChildAt(i);
|
||||
|
||||
if (child != null) {
|
||||
if (child) {
|
||||
const result = this.getSwimlaneAt(x, y, child);
|
||||
|
||||
if (result != null) {
|
||||
|
@ -92,7 +95,7 @@ class GraphSwimlane {
|
|||
if (child.isVisible() && this.isSwimlane(child)) {
|
||||
const state = this.getView().getState(child);
|
||||
|
||||
if (this.intersects(state, x, y)) {
|
||||
if (state && this.intersects(state, x, y)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
@ -110,12 +113,12 @@ class GraphSwimlane {
|
|||
* @param x X-coordinate of the mouse event.
|
||||
* @param y Y-coordinate of the mouse event.
|
||||
*/
|
||||
hitsSwimlaneContent(swimlane: Cell, x: number, y: number): boolean {
|
||||
const state = this.graph.view.getState(swimlane);
|
||||
hitsSwimlaneContent(swimlane: Cell, x: number, y: number) {
|
||||
const state = this.getView().getState(swimlane);
|
||||
const size = this.getStartSize(swimlane);
|
||||
|
||||
if (state != null) {
|
||||
const scale = this.graph.view.getScale();
|
||||
if (state) {
|
||||
const scale = this.getView().getScale();
|
||||
x -= state.x;
|
||||
y -= state.y;
|
||||
|
||||
|
@ -142,12 +145,12 @@ class GraphSwimlane {
|
|||
* @param swimlane {@link mxCell} whose start size should be returned.
|
||||
* @param ignoreState Optional boolean that specifies if cell state should be ignored.
|
||||
*/
|
||||
getStartSize(swimlane: Cell, ignoreState: boolean = false): Rectangle {
|
||||
getStartSize(swimlane: Cell, ignoreState = false) {
|
||||
const result = new Rectangle();
|
||||
const style = this.graph.cell.getCurrentCellStyle(swimlane, ignoreState);
|
||||
const style = this.getCurrentCellStyle(swimlane, ignoreState);
|
||||
const size = parseInt(getValue(style, 'startSize', DEFAULT_STARTSIZE));
|
||||
|
||||
if (getValue(style, 'horizontal', true)) {
|
||||
if (style.horizontal === true) {
|
||||
result.height = size;
|
||||
} else {
|
||||
result.width = size;
|
||||
|
@ -158,11 +161,11 @@ class GraphSwimlane {
|
|||
/**
|
||||
* Returns the direction for the given swimlane style.
|
||||
*/
|
||||
getSwimlaneDirection(style: any): string {
|
||||
const dir = getValue(style, 'direction', DIRECTION_EAST);
|
||||
const flipH = getValue(style, 'flipH', 0) == 1;
|
||||
const flipV = getValue(style, 'flipV', 0) == 1;
|
||||
const h = getValue(style, 'horizontal', true);
|
||||
getSwimlaneDirection(style: CellStateStyles) {
|
||||
const dir = style.direction ?? DIRECTION_EAST;
|
||||
const flipH = style.flipH;
|
||||
const flipV = style.flipV;
|
||||
const h = style.horizontal;
|
||||
let n = h ? 0 : 3;
|
||||
|
||||
if (dir === DIRECTION_NORTH) {
|
||||
|
@ -195,11 +198,11 @@ class GraphSwimlane {
|
|||
* @param swimlane {@link mxCell} whose start size should be returned.
|
||||
* @param ignoreState Optional boolean that specifies if cell state should be ignored.
|
||||
*/
|
||||
getActualStartSize(swimlane: Cell, ignoreState: boolean = false): Rectangle {
|
||||
getActualStartSize(swimlane: Cell, ignoreState = false) {
|
||||
const result = new Rectangle();
|
||||
|
||||
if (this.isSwimlane(swimlane, ignoreState)) {
|
||||
const style = this.graph.cell.getCurrentCellStyle(swimlane, ignoreState);
|
||||
const style = this.getCurrentCellStyle(swimlane, ignoreState);
|
||||
const size = parseInt(getValue(style, 'startSize', DEFAULT_STARTSIZE));
|
||||
const dir = this.getSwimlaneDirection(style);
|
||||
|
||||
|
@ -224,13 +227,9 @@ class GraphSwimlane {
|
|||
* @param cell {@link mxCell} to be checked.
|
||||
* @param ignoreState Optional boolean that specifies if the cell state should be ignored.
|
||||
*/
|
||||
isSwimlane(cell: Cell, ignoreState: boolean = false): boolean {
|
||||
if (
|
||||
cell != null &&
|
||||
cell.getParent() !== this.graph.model.getRoot() &&
|
||||
!cell.isEdge()
|
||||
) {
|
||||
return this.graph.cell.getCurrentCellStyle(cell, ignoreState).shape === SHAPE_SWIMLANE;
|
||||
isSwimlane(cell: Cell, ignoreState = false) {
|
||||
if (cell && cell.getParent() !== this.getModel().getRoot() && !cell.isEdge()) {
|
||||
return this.getCurrentCellStyle(cell, ignoreState).shape === SHAPE_SWIMLANE;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -249,10 +248,10 @@ class GraphSwimlane {
|
|||
* @param cells {@link mxCell} that should be dropped into the target.
|
||||
* @param evt Mouseevent that triggered the invocation.
|
||||
*/
|
||||
isValidDropTarget(cell: Cell, cells: CellArray, evt: InternalMouseEvent): boolean {
|
||||
isValidDropTarget(cell: Cell, cells: CellArray, evt: MouseEvent) {
|
||||
return (
|
||||
cell != null &&
|
||||
((this.graph.isSplitEnabled() && this.graph.isSplitTarget(cell, cells, evt)) ||
|
||||
cell &&
|
||||
((this.isSplitEnabled() && this.isSplitTarget(cell, cells, evt)) ||
|
||||
(!cell.isEdge() &&
|
||||
(this.isSwimlane(cell) || (cell.getChildCount() > 0 && !cell.isCollapsed()))))
|
||||
);
|
||||
|
@ -274,10 +273,10 @@ class GraphSwimlane {
|
|||
*/
|
||||
getDropTarget(
|
||||
cells: CellArray,
|
||||
evt: InternalMouseEvent,
|
||||
evt: MouseEvent,
|
||||
cell: Cell | null = null,
|
||||
clone: boolean = false
|
||||
): Cell | null {
|
||||
clone = false
|
||||
) {
|
||||
if (!this.isSwimlaneNesting()) {
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
if (this.isSwimlane(cells[i])) {
|
||||
|
@ -286,31 +285,31 @@ class GraphSwimlane {
|
|||
}
|
||||
}
|
||||
|
||||
const pt = convertPoint(this.graph.container, getClientX(evt), getClientY(evt));
|
||||
pt.x -= this.graph.panning.panDx;
|
||||
pt.y -= this.graph.panning.panDy;
|
||||
const pt = convertPoint(this.getContainer(), getClientX(evt), getClientY(evt));
|
||||
pt.x -= this.getPanDx();
|
||||
pt.y -= this.getPanDy();
|
||||
const swimlane = this.getSwimlaneAt(pt.x, pt.y);
|
||||
|
||||
if (cell == null) {
|
||||
if (!cell) {
|
||||
cell = swimlane;
|
||||
} else if (swimlane != null) {
|
||||
} else if (swimlane) {
|
||||
// Checks if the cell is an ancestor of the swimlane
|
||||
// under the mouse and uses the swimlane in that case
|
||||
let tmp = swimlane.getParent();
|
||||
|
||||
while (tmp != null && this.isSwimlane(tmp) && tmp != cell) {
|
||||
while (tmp && this.isSwimlane(tmp) && tmp !== cell) {
|
||||
tmp = tmp.getParent();
|
||||
}
|
||||
|
||||
if (tmp == cell) {
|
||||
if (tmp === cell) {
|
||||
cell = swimlane;
|
||||
}
|
||||
}
|
||||
|
||||
while (
|
||||
cell != null &&
|
||||
cell &&
|
||||
!this.isValidDropTarget(cell, cells, evt) &&
|
||||
!this.graph.model.isLayer(cell)
|
||||
!this.getModel().isLayer(cell)
|
||||
) {
|
||||
cell = cell.getParent();
|
||||
}
|
||||
|
@ -318,18 +317,18 @@ class GraphSwimlane {
|
|||
// Checks if parent is dropped into child if not cloning
|
||||
if (!clone) {
|
||||
let parent = cell;
|
||||
while (parent != null && cells.indexOf(parent) < 0) {
|
||||
while (parent && cells.indexOf(parent) < 0) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
return !this.graph.model.isLayer(<Cell>cell) && parent == null ? cell : null;
|
||||
return !this.getModel().isLayer(<Cell>cell) && !parent ? cell : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link swimlaneNesting} as a boolean.
|
||||
*/
|
||||
isSwimlaneNesting(): boolean {
|
||||
isSwimlaneNesting() {
|
||||
return this.swimlaneNesting;
|
||||
}
|
||||
|
||||
|
@ -339,14 +338,14 @@ class GraphSwimlane {
|
|||
*
|
||||
* @param value Boolean indicating if swimlanes can be nested.
|
||||
*/
|
||||
setSwimlaneNesting(value: boolean): void {
|
||||
setSwimlaneNesting(value: boolean) {
|
||||
this.swimlaneNesting = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link swimlaneSelectionEnabled} as a boolean.
|
||||
*/
|
||||
isSwimlaneSelectionEnabled(): boolean {
|
||||
isSwimlaneSelectionEnabled() {
|
||||
return this.swimlaneSelectionEnabled;
|
||||
}
|
||||
|
||||
|
@ -357,7 +356,7 @@ class GraphSwimlane {
|
|||
* @param value Boolean indicating if swimlanes content areas
|
||||
* should be selected when the mouse is released over them.
|
||||
*/
|
||||
setSwimlaneSelectionEnabled(value: boolean): void {
|
||||
setSwimlaneSelectionEnabled(value: boolean) {
|
||||
this.swimlaneSelectionEnabled = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,10 @@ import { autoImplement } from '../../util/Utils';
|
|||
|
||||
import type Graph from '../Graph';
|
||||
import type GraphFolding from '../folding/GraphFolding';
|
||||
import SelectionCellsHandler from '../selection/SelectionCellsHandler';
|
||||
import TooltipHandler from './TooltipHandler';
|
||||
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
'getSelectionCellsHandler' | 'convertValueToString' | 'getTooltipHandler'
|
||||
>;
|
||||
type PartialGraph = Pick<Graph, 'convertValueToString' | 'getPlugin'>;
|
||||
type PartialFolding = Pick<GraphFolding, 'getCollapseExpandResource'>;
|
||||
type PartialClass = PartialGraph & PartialFolding;
|
||||
|
||||
|
@ -53,8 +52,15 @@ class GraphTooltip extends autoImplement<PartialClass>() {
|
|||
}
|
||||
|
||||
if (!tip) {
|
||||
const handler = this.getSelectionCellsHandler().getHandler(state.cell);
|
||||
const selectionCellsHandler = this.getPlugin(
|
||||
'SelectionCellsHandler'
|
||||
) as SelectionCellsHandler;
|
||||
|
||||
const handler = selectionCellsHandler.getHandler(state.cell);
|
||||
|
||||
// @ts-ignore Guarded against undefined error already.
|
||||
if (handler && typeof handler.getTooltipForNode === 'function') {
|
||||
// @ts-ignore Guarded against undefined error already.
|
||||
tip = handler.getTooltipForNode(node);
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +114,9 @@ class GraphTooltip extends autoImplement<PartialClass>() {
|
|||
* @param enabled Boolean indicating if tooltips should be enabled.
|
||||
*/
|
||||
setTooltips(enabled: boolean) {
|
||||
this.getTooltipHandler().setEnabled(enabled);
|
||||
const tooltipHandler = this.getPlugin('TooltipHandler') as TooltipHandler;
|
||||
|
||||
tooltipHandler.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import InternalMouseEvent from '../event/InternalMouseEvent';
|
|||
import PopupMenuHandler from '../popups_menus/PopupMenuHandler';
|
||||
|
||||
import type { GraphPlugin } from '../../types';
|
||||
import EventSource from '../event/EventSource';
|
||||
|
||||
/**
|
||||
* Class: mxTooltipHandler
|
||||
|
|
|
@ -52,7 +52,7 @@ class GraphValidation extends autoImplement<PartialClass>() {
|
|||
* @param source {@link mxCell} that represents the source terminal.
|
||||
* @param target {@link mxCell} that represents the target terminal.
|
||||
*/
|
||||
isEdgeValid(edge: Cell, source: Cell, target: Cell) {
|
||||
isEdgeValid(edge: Cell | null, source: Cell, target: Cell) {
|
||||
return !this.getEdgeValidationError(edge, source, target);
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ class GraphValidation extends autoImplement<PartialClass>() {
|
|||
edge: Cell | null = null,
|
||||
source: Cell | null = null,
|
||||
target: Cell | null = null
|
||||
): string | null {
|
||||
) {
|
||||
if (edge && !this.isAllowDanglingEdges() && (!source || !target)) {
|
||||
return '';
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue