fix(robustness): prevent errors when a plugin is not loaded (#272)
Add guards to prevent accessing properties and methods on undefined instances when a plugin is not loaded. Also fix and improve the JSDoc of the `EdgeHandler` and `Guide` classes.development
parent
8d6727a411
commit
ffd5036054
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Copyright 2023-present The maxGraph project Contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { expect, test } from '@jest/globals';
|
||||
import { createGraphWithoutPlugins } from './utils';
|
||||
|
||||
test('The "ConnectionHandler" plugin is not available', () => {
|
||||
const graph = createGraphWithoutPlugins();
|
||||
graph.setConnectable(true);
|
||||
graph.isConnectable();
|
||||
expect(graph.isConnectable()).toBe(false);
|
||||
});
|
|
@ -15,16 +15,12 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { describe, expect, test } from '@jest/globals';
|
||||
import { Cell, type CellStyle, Geometry, Graph } from '../../../src';
|
||||
|
||||
function createGraph(): Graph {
|
||||
// @ts-ignore - no need for a container, we don't check the view here
|
||||
return new Graph(null);
|
||||
}
|
||||
import { createGraphWithoutContainer } from './utils';
|
||||
import { Cell, type CellStyle, Geometry } from '../../../src';
|
||||
|
||||
describe('insertEdge', () => {
|
||||
test('with several parameters', () => {
|
||||
const graph = createGraph();
|
||||
const graph = createGraphWithoutContainer();
|
||||
const source = new Cell();
|
||||
const target = new Cell();
|
||||
|
||||
|
@ -55,7 +51,7 @@ describe('insertEdge', () => {
|
|||
});
|
||||
|
||||
test('with single parameter', () => {
|
||||
const graph = createGraph();
|
||||
const graph = createGraphWithoutContainer();
|
||||
const source = new Cell();
|
||||
const target = new Cell();
|
||||
|
||||
|
@ -91,7 +87,7 @@ describe('insertEdge', () => {
|
|||
});
|
||||
|
||||
test('with single parameter and non default parent', () => {
|
||||
const graph = createGraph();
|
||||
const graph = createGraphWithoutContainer();
|
||||
|
||||
const parentCell = graph.insertVertex({
|
||||
value: 'non default',
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
Copyright 2023-present The maxGraph project Contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { test } from '@jest/globals';
|
||||
import { createGraphWithoutPlugins } from './utils';
|
||||
|
||||
test('The "PanningHandler" plugin is not available', () => {
|
||||
const graph = createGraphWithoutPlugins();
|
||||
graph.setPanning(true);
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
Copyright 2023-present The maxGraph project Contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { test } from '@jest/globals';
|
||||
import { createGraphWithoutPlugins } from './utils';
|
||||
import { Cell, CellState } from '../../../src';
|
||||
|
||||
test('The "TooltipHandler" plugin is not available', () => {
|
||||
const graph = createGraphWithoutPlugins();
|
||||
graph.setTooltips(true);
|
||||
});
|
||||
|
||||
test('The "SelectionCellsHandler" plugin is not available', () => {
|
||||
const graph = createGraphWithoutPlugins();
|
||||
graph.getTooltip(new CellState(null, new Cell()), null!, 0, 0);
|
||||
});
|
|
@ -15,16 +15,12 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { describe, expect, test } from '@jest/globals';
|
||||
import { type CellStyle, Geometry, Graph } from '../../../src';
|
||||
|
||||
function createGraph(): Graph {
|
||||
// @ts-ignore - no need for a container, we don't check the view here
|
||||
return new Graph(null);
|
||||
}
|
||||
import { createGraphWithoutContainer } from './utils';
|
||||
import { type CellStyle, Geometry } from '../../../src';
|
||||
|
||||
describe('insertVertex', () => {
|
||||
test('with several parameters', () => {
|
||||
const graph = createGraph();
|
||||
const graph = createGraphWithoutContainer();
|
||||
const style: CellStyle = { rounded: true, shape: 'cloud' };
|
||||
const cell = graph.insertVertex(null, 'vertex_1', 'a value', 10, 20, 110, 120, style);
|
||||
expect(cell.getId()).toBe('vertex_1');
|
||||
|
@ -50,7 +46,7 @@ describe('insertVertex', () => {
|
|||
});
|
||||
|
||||
test('with single parameter', () => {
|
||||
const graph = createGraph();
|
||||
const graph = createGraphWithoutContainer();
|
||||
const style: CellStyle = { align: 'right', fillColor: 'red' };
|
||||
const cell = graph.insertVertex({
|
||||
value: 'a value',
|
||||
|
@ -82,7 +78,7 @@ describe('insertVertex', () => {
|
|||
});
|
||||
|
||||
test('with single parameter and non default parent', () => {
|
||||
const graph = createGraph();
|
||||
const graph = createGraphWithoutContainer();
|
||||
|
||||
const parentCell = graph.insertVertex({
|
||||
value: 'non default',
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
Copyright 2023-present The maxGraph project Contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { Graph } from '../../../src';
|
||||
|
||||
// no need for a container, we don't check the view here
|
||||
export const createGraphWithoutContainer = (): Graph => new Graph(null!);
|
||||
|
||||
export const createGraphWithoutPlugins = (): Graph => new Graph(null!, null!, []);
|
|
@ -3,4 +3,5 @@ module.exports = {
|
|||
// preset: 'ts-jest',
|
||||
preset: 'ts-jest/presets/default-esm',
|
||||
testEnvironment: 'jsdom', // need to access to the browser objects
|
||||
testMatch: ['**/__tests__/**/?(*.)+(spec|test).ts'],
|
||||
};
|
||||
|
|
|
@ -1437,24 +1437,24 @@ export class Editor extends EventSource {
|
|||
// event if an insert function is defined
|
||||
this.installInsertHandler(graph);
|
||||
|
||||
// Redirects the function for creating the
|
||||
// popupmenu items
|
||||
// Redirects the function for creating the popupmenu items
|
||||
const popupMenuHandler = <PopupMenuHandler>graph.getPlugin('PopupMenuHandler');
|
||||
if (popupMenuHandler) {
|
||||
popupMenuHandler.factoryMethod = (menu: any, cell: Cell | null, evt: any): void => {
|
||||
return this.createPopupMenu(menu, cell, evt);
|
||||
};
|
||||
}
|
||||
|
||||
popupMenuHandler.factoryMethod = (menu: any, cell: Cell | null, evt: any): void => {
|
||||
return this.createPopupMenu(menu, cell, evt);
|
||||
};
|
||||
|
||||
// Redirects the function for creating
|
||||
// new connections in the diagram
|
||||
// Redirects the function for creating new connections in the diagram
|
||||
const connectionHandler = <ConnectionHandler>graph.getPlugin('ConnectionHandler');
|
||||
|
||||
connectionHandler.factoryMethod = (
|
||||
source: Cell | null,
|
||||
target: Cell | null
|
||||
): Cell => {
|
||||
return this.createEdge(source, target);
|
||||
};
|
||||
if (connectionHandler) {
|
||||
connectionHandler.factoryMethod = (
|
||||
source: Cell | null,
|
||||
target: Cell | null
|
||||
): Cell => {
|
||||
return this.createEdge(source, target);
|
||||
};
|
||||
}
|
||||
|
||||
// Maintains swimlanes and installs autolayout
|
||||
this.createSwimlaneManager(graph);
|
||||
|
@ -2458,13 +2458,13 @@ export class Editor extends EventSource {
|
|||
);
|
||||
|
||||
if (modename === 'select') {
|
||||
panningHandler.useLeftButtonForPanning = false;
|
||||
panningHandler && (panningHandler.useLeftButtonForPanning = false);
|
||||
this.graph.setConnectable(false);
|
||||
} else if (modename === 'connect') {
|
||||
panningHandler.useLeftButtonForPanning = false;
|
||||
panningHandler && (panningHandler.useLeftButtonForPanning = false);
|
||||
this.graph.setConnectable(true);
|
||||
} else if (modename === 'pan') {
|
||||
panningHandler.useLeftButtonForPanning = true;
|
||||
panningHandler && (panningHandler.useLeftButtonForPanning = true);
|
||||
this.graph.setConnectable(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -262,14 +262,14 @@ export const VERTEX_SELECTION_DASHED = true;
|
|||
export const EDGE_SELECTION_DASHED = true;
|
||||
|
||||
/**
|
||||
* Defines the color to be used for the guidelines in mxGraphHandler.
|
||||
* Default is #FF0000.
|
||||
* Defines the color to be used for the guidelines in `Guide`.
|
||||
* @default #FF0000.
|
||||
*/
|
||||
export const GUIDE_COLOR = '#FF0000';
|
||||
|
||||
/**
|
||||
* Defines the strokewidth to be used for the guidelines in mxGraphHandler.
|
||||
* Default is 1.
|
||||
* Defines the strokewidth to be used for the guidelines in `Guide`.
|
||||
* @default 1.
|
||||
*/
|
||||
export const GUIDE_STROKEWIDTH = 1;
|
||||
|
||||
|
|
|
@ -68,21 +68,19 @@ export const defaultPlugins: GraphPluginConstructor[] = [
|
|||
];
|
||||
|
||||
/**
|
||||
* Extends {@link EventSource} to implement a graph component for
|
||||
* the browser. This is the main class of the package. To activate
|
||||
* panning and connections use {@link setPanning} and {@link setConnectable}.
|
||||
* For rubberband selection you must create a new instance of
|
||||
* {@link rubberband}. The following listeners are added to
|
||||
* {@link mouseListeners} by default:
|
||||
* Extends {@link EventSource} to implement a graph component for the browser. This is the main class of the package.
|
||||
*
|
||||
* To activate panning and connections use {@link setPanning} and {@link setConnectable}.
|
||||
* For rubberband selection you must create a new instance of {@link rubberband}.
|
||||
*
|
||||
* The following listeners are added to {@link mouseListeners} by default:
|
||||
*
|
||||
* - tooltipHandler: {@link TooltipHandler} that displays tooltips
|
||||
* - panningHandler: {@link PanningHandler} for panning and popup menus
|
||||
* - connectionHandler: {@link ConnectionHandler} for creating connections
|
||||
* - graphHandler: {@link SelectionHandler} for moving and cloning cells
|
||||
* - selectionHandler: {@link SelectionHandler} for moving and cloning cells
|
||||
*
|
||||
* These listeners will be called in the above order if they are enabled.
|
||||
* @class graph
|
||||
* @extends {EventSource}
|
||||
*/
|
||||
class Graph extends EventSource {
|
||||
container: HTMLElement;
|
||||
|
@ -723,7 +721,11 @@ class Graph extends EventSource {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (this.isAllowAutoPanning() && !panningHandler.isActive()) {
|
||||
} else if (
|
||||
this.isAllowAutoPanning() &&
|
||||
panningHandler &&
|
||||
!panningHandler.isActive()
|
||||
) {
|
||||
panningHandler.getPanningManager().panTo(x + this.getPanDx(), y + this.getPanDy());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2165,8 +2165,7 @@ export class GraphView extends EventSource {
|
|||
graph.addMouseListener({
|
||||
mouseDown: (sender: any, me: InternalMouseEvent) => {
|
||||
const popupMenuHandler = graph.getPlugin('PopupMenuHandler') as PopupMenuHandler;
|
||||
|
||||
if (popupMenuHandler) popupMenuHandler.hideMenu();
|
||||
popupMenuHandler?.hideMenu();
|
||||
},
|
||||
mouseMove: () => {
|
||||
return;
|
||||
|
|
|
@ -1398,7 +1398,7 @@ class CellRenderer {
|
|||
const selectionCellsHandler = graph.getPlugin(
|
||||
'SelectionCellsHandler'
|
||||
) as SelectionCellsHandler;
|
||||
selectionCellsHandler.updateHandler(state);
|
||||
selectionCellsHandler?.updateHandler(state);
|
||||
}
|
||||
} else if (
|
||||
!force &&
|
||||
|
@ -1412,7 +1412,7 @@ class CellRenderer {
|
|||
const selectionCellsHandler = graph.getPlugin(
|
||||
'SelectionCellsHandler'
|
||||
) as SelectionCellsHandler;
|
||||
selectionCellsHandler.updateHandler(state);
|
||||
selectionCellsHandler?.updateHandler(state);
|
||||
force = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -712,10 +712,7 @@ class CellEditorHandler implements GraphPlugin {
|
|||
}
|
||||
|
||||
const tooltipHandler = this.graph.getPlugin('TooltipHandler') as TooltipHandler;
|
||||
|
||||
if (tooltipHandler) {
|
||||
tooltipHandler.hideTooltip();
|
||||
}
|
||||
tooltipHandler?.hideTooltip();
|
||||
|
||||
const state = this.graph.getView().getState(cell);
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ type FactoryMethod = (
|
|||
* the port IDs, use <Transactions.getCell>.
|
||||
*
|
||||
* ```javascript
|
||||
* graph.getPlugin('ConnectionHandler').addListener(mxEvent.CONNECT, (sender, evt)=>
|
||||
* graph.getPlugin('ConnectionHandler')?.addListener(mxEvent.CONNECT, (sender, evt) =>
|
||||
* {
|
||||
* let edge = evt.getProperty('cell');
|
||||
* let source = graph.getDataModel().getTerminal(edge, true);
|
||||
|
|
|
@ -75,7 +75,7 @@ import { equalPoints } from '../../util/arrayUtils';
|
|||
/**
|
||||
* Graph event handler that reconnects edges and modifies control points and the edge
|
||||
* label location.
|
||||
* Uses {@link TerminalMarker} for finding and highlighting new source and target vertices.
|
||||
* Uses {@link CellMarker} for finding and highlighting new source and target vertices.
|
||||
* This handler is automatically created in mxGraph.createHandler for each selected edge.
|
||||
* **To enable adding/removing control points, the following code can be used**
|
||||
* @example
|
||||
|
@ -98,7 +98,7 @@ class EdgeHandler {
|
|||
state: CellState;
|
||||
|
||||
/**
|
||||
* Holds the {@link TerminalMarker} which is used for highlighting terminals.
|
||||
* Holds the {@link CellMarker} which is used for highlighting terminals.
|
||||
*/
|
||||
marker: CellMarker;
|
||||
|
||||
|
@ -311,13 +311,14 @@ class EdgeHandler {
|
|||
}
|
||||
}
|
||||
|
||||
const graphHandler = this.graph.getPlugin('SelectionHandler') as SelectionHandler;
|
||||
const selectionHandler = this.graph.getPlugin('SelectionHandler') as SelectionHandler;
|
||||
|
||||
// Creates bends for the non-routed absolute points
|
||||
// or bends that don't correspond to points
|
||||
if (
|
||||
this.graph.getSelectionCount() < graphHandler.maxCells ||
|
||||
graphHandler.maxCells <= 0
|
||||
selectionHandler &&
|
||||
(this.graph.getSelectionCount() < selectionHandler.maxCells ||
|
||||
selectionHandler.maxCells <= 0)
|
||||
) {
|
||||
this.bends = this.createBends();
|
||||
|
||||
|
@ -496,21 +497,21 @@ class EdgeHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Constants#EDGE_SELECTION_COLOR}.
|
||||
* Returns {@link EDGE_SELECTION_COLOR}.
|
||||
*/
|
||||
getSelectionColor() {
|
||||
return EDGE_SELECTION_COLOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Constants#EDGE_SELECTION_STROKEWIDTH}.
|
||||
* Returns {@link EDGE_SELECTION_STROKEWIDTH}.
|
||||
*/
|
||||
getSelectionStrokeWidth() {
|
||||
return EDGE_SELECTION_STROKEWIDTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link Constants#EDGE_SELECTION_DASHED}.
|
||||
* Returns {@link EDGE_SELECTION_DASHED}.
|
||||
*/
|
||||
isSelectionDashed() {
|
||||
return EDGE_SELECTION_DASHED;
|
||||
|
@ -525,14 +526,14 @@ class EdgeHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the {@link CellMarker} used in {@link arker}.
|
||||
* Creates and returns the {@link CellMarker} used in {@link marker}.
|
||||
*/
|
||||
getCellAt(x: number, y: number) {
|
||||
return !this.outlineConnect ? this.graph.getCellAt(x, y) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the {@link CellMarker} used in {@link arker}.
|
||||
* Creates and returns the {@link CellMarker} used in {@link marker}.
|
||||
*/
|
||||
createMarker() {
|
||||
return new EdgeHandlerCellMarker(this.graph, this);
|
||||
|
@ -552,7 +553,7 @@ class EdgeHandler {
|
|||
|
||||
/**
|
||||
* Creates and returns the bends used for modifying the edge. This is
|
||||
* typically an array of {@link RectangleShapes}.
|
||||
* typically an array of {@link RectangleShape}.
|
||||
*/
|
||||
createBends() {
|
||||
const { cell } = this.state;
|
||||
|
@ -593,7 +594,7 @@ class EdgeHandler {
|
|||
|
||||
/**
|
||||
* Creates and returns the bends used for modifying the edge. This is
|
||||
* typically an array of {@link RectangleShapes}.
|
||||
* typically an array of {@link RectangleShape}.
|
||||
*/
|
||||
// createVirtualBends(): mxRectangleShape[];
|
||||
createVirtualBends() {
|
||||
|
@ -1439,7 +1440,7 @@ class EdgeHandler {
|
|||
|
||||
/**
|
||||
* Handles the event to applying the previewed changes on the edge by
|
||||
* using {@link oveLabel}, <connect> or <changePoints>.
|
||||
* using {@link moveLabel}, <connect> or <changePoints>.
|
||||
*/
|
||||
mouseUp(sender: EventSource, me: InternalMouseEvent) {
|
||||
// Workaround for wrong event source in Webkit
|
||||
|
|
|
@ -242,15 +242,14 @@ class KeyHandler {
|
|||
isGraphEvent(evt: KeyboardEvent) {
|
||||
const source = <Element>getSource(evt);
|
||||
|
||||
// Accepts events from the target object or
|
||||
// in-place editing inside graph
|
||||
const cellEditor = this.graph?.getPlugin(
|
||||
// Accepts events from the target object or in-place editing inside graph
|
||||
const cellEditorHandler = this.graph?.getPlugin(
|
||||
'CellEditorHandler'
|
||||
) as CellEditorHandler | null;
|
||||
) as CellEditorHandler;
|
||||
if (
|
||||
source === this.target ||
|
||||
source.parentNode === this.target ||
|
||||
(cellEditor != null && cellEditor.isEventSource(evt))
|
||||
(cellEditorHandler && cellEditorHandler.isEventSource(evt))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -99,11 +99,10 @@ class PopupMenuHandler extends MaxPopupMenu implements GraphPlugin {
|
|||
* Initializes the shapes required for this vertex handler.
|
||||
*/
|
||||
init() {
|
||||
// Hides the tooltip if the mouse is over
|
||||
// the context menu
|
||||
// Hides the tooltip if the mouse is over the context menu
|
||||
InternalEvent.addGestureListeners(this.div, (evt) => {
|
||||
const tooltipHandler = this.graph.getPlugin('TooltipHandler') as TooltipHandler;
|
||||
tooltipHandler.hide();
|
||||
tooltipHandler?.hide();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -176,7 +175,7 @@ class PopupMenuHandler extends MaxPopupMenu implements GraphPlugin {
|
|||
|
||||
// Hides the tooltip if there is one
|
||||
const tooltipHandler = this.graph.getPlugin('TooltipHandler') as TooltipHandler;
|
||||
tooltipHandler.hide();
|
||||
tooltipHandler?.hide();
|
||||
|
||||
// Menu is shifted by 1 pixel so that the mouse up event
|
||||
// is routed via the underlying shape instead of the DIV
|
||||
|
|
|
@ -61,13 +61,9 @@ import type { ColorValue, GraphPlugin } from '../../types';
|
|||
* handlers are created using {@link Graph#createHandler} in
|
||||
* {@link GraphSelectionModel#cellAdded}.
|
||||
*
|
||||
* To avoid the container to scroll a moved cell into view, set
|
||||
* <scrollAfterMove> to false.
|
||||
* To avoid the container to scroll a moved cell into view, set {@link scrollOnMove} to `false`.
|
||||
*
|
||||
* Constructor: mxGraphHandler
|
||||
*
|
||||
* Constructs an event handler that creates handles for the
|
||||
* selection cells.
|
||||
* Constructs an event handler that creates handles for the selection cells.
|
||||
*
|
||||
* @param graph Reference to the enclosing {@link Graph}.
|
||||
*/
|
||||
|
@ -132,7 +128,7 @@ class SelectionHandler implements GraphPlugin {
|
|||
|
||||
// Forces update to ignore last visible state
|
||||
this.setHandlesVisibleForCells(
|
||||
selectionCellsHandler.getHandledSelectionCells(),
|
||||
selectionCellsHandler?.getHandledSelectionCells() ?? [],
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
@ -269,8 +265,8 @@ class SelectionHandler implements GraphPlugin {
|
|||
connectOnDrop = false;
|
||||
|
||||
/**
|
||||
* Specifies if the view should be scrolled so that a moved cell is
|
||||
* visible. Default is true.
|
||||
* Specifies if the view should be scrolled so that a moved cell is visible.
|
||||
* @default true
|
||||
*/
|
||||
scrollOnMove = true;
|
||||
|
||||
|
@ -479,11 +475,11 @@ class SelectionHandler implements GraphPlugin {
|
|||
|
||||
if (!this.graph.isToggleEvent(me.getEvent()) || !isAltDown(me.getEvent())) {
|
||||
while (c) {
|
||||
if (selectionCellsHandler.isHandled(c)) {
|
||||
const cellEditor = this.graph.getPlugin(
|
||||
if (selectionCellsHandler?.isHandled(c)) {
|
||||
const cellEditorHandler = this.graph.getPlugin(
|
||||
'CellEditorHandler'
|
||||
) as CellEditorHandler;
|
||||
return cellEditor.getEditingCell() !== c;
|
||||
return cellEditorHandler?.getEditingCell() !== c;
|
||||
}
|
||||
c = c.getParent();
|
||||
}
|
||||
|
@ -497,7 +493,7 @@ class SelectionHandler implements GraphPlugin {
|
|||
selectDelayed(me: InternalMouseEvent) {
|
||||
const popupMenuHandler = this.graph.getPlugin('PopupMenuHandler') as PopupMenuHandler;
|
||||
|
||||
if (!popupMenuHandler.isPopupTrigger(me)) {
|
||||
if (!popupMenuHandler || !popupMenuHandler.isPopupTrigger(me)) {
|
||||
let cell = me.getCell();
|
||||
if (cell === null) {
|
||||
cell = this.cell;
|
||||
|
@ -1064,7 +1060,7 @@ class SelectionHandler implements GraphPlugin {
|
|||
) as SelectionCellsHandler;
|
||||
|
||||
this.setHandlesVisibleForCells(
|
||||
selectionCellsHandler.getHandledSelectionCells(),
|
||||
selectionCellsHandler?.getHandledSelectionCells() ?? [],
|
||||
false
|
||||
);
|
||||
this.updateLivePreview(this.currentDx, this.currentDy);
|
||||
|
@ -1265,11 +1261,8 @@ class SelectionHandler implements GraphPlugin {
|
|||
) as SelectionCellsHandler;
|
||||
|
||||
for (let i = 0; i < states.length; i += 1) {
|
||||
const handler = selectionCellsHandler.getHandler(states[i][0].cell);
|
||||
|
||||
if (handler != null) {
|
||||
handler.redraw(true);
|
||||
}
|
||||
const handler = selectionCellsHandler?.getHandler(states[i][0].cell);
|
||||
handler?.redraw(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1383,11 +1376,9 @@ class SelectionHandler implements GraphPlugin {
|
|||
) as SelectionCellsHandler;
|
||||
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
const handler = selectionCellsHandler.getHandler(cells[i]);
|
||||
|
||||
if (handler != null) {
|
||||
const handler = selectionCellsHandler?.getHandler(cells[i]);
|
||||
if (handler) {
|
||||
handler.setHandlesVisible(visible);
|
||||
|
||||
if (visible) {
|
||||
handler.redraw();
|
||||
}
|
||||
|
@ -1438,7 +1429,7 @@ class SelectionHandler implements GraphPlugin {
|
|||
'ConnectionHandler'
|
||||
) as ConnectionHandler;
|
||||
|
||||
connectionHandler.connect(this.cell, cell, me.getEvent());
|
||||
connectionHandler?.connect(this.cell, cell, me.getEvent());
|
||||
} else {
|
||||
const clone =
|
||||
graph.isCloneEvent(me.getEvent()) &&
|
||||
|
@ -1493,7 +1484,7 @@ class SelectionHandler implements GraphPlugin {
|
|||
) as SelectionCellsHandler;
|
||||
|
||||
this.setHandlesVisibleForCells(
|
||||
selectionCellsHandler.getHandledSelectionCells(),
|
||||
selectionCellsHandler?.getHandledSelectionCells() ?? [],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
|
|
@ -233,12 +233,13 @@ class VertexHandler {
|
|||
this.selectionBorder.setCursor(CURSOR.MOVABLE_VERTEX);
|
||||
}
|
||||
|
||||
const graphHandler = this.graph.getPlugin('SelectionHandler') as SelectionHandler;
|
||||
const selectionHandler = this.graph.getPlugin('SelectionHandler') as SelectionHandler;
|
||||
|
||||
// Adds the sizer handles
|
||||
if (
|
||||
graphHandler.maxCells <= 0 ||
|
||||
this.graph.getSelectionCount() < graphHandler.maxCells
|
||||
selectionHandler &&
|
||||
(selectionHandler.maxCells <= 0 ||
|
||||
this.graph.getSelectionCount() < selectionHandler.maxCells)
|
||||
) {
|
||||
const resizable = this.graph.isCellResizable(this.state.cell);
|
||||
this.sizers = [];
|
||||
|
@ -338,14 +339,17 @@ class VertexHandler {
|
|||
* Returns true if the rotation handle should be showing.
|
||||
*/
|
||||
isRotationHandleVisible() {
|
||||
const graphHandler = this.graph.getPlugin('SelectionHandler') as SelectionHandler;
|
||||
const selectionHandler = this.graph.getPlugin('SelectionHandler') as SelectionHandler;
|
||||
const selectionHandlerCheck = selectionHandler
|
||||
? selectionHandler.maxCells <= 0 ||
|
||||
this.graph.getSelectionCount() < selectionHandler.maxCells
|
||||
: true;
|
||||
|
||||
return (
|
||||
this.graph.isEnabled() &&
|
||||
this.rotationEnabled &&
|
||||
this.graph.isCellRotatable(this.state.cell) &&
|
||||
(graphHandler.maxCells <= 0 ||
|
||||
this.graph.getSelectionCount() < graphHandler.maxCells)
|
||||
selectionHandlerCheck
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -724,8 +728,7 @@ class VertexHandler {
|
|||
) as SelectionCellsHandler;
|
||||
|
||||
for (let i = 0; i < edges.length; i += 1) {
|
||||
const handler = selectionCellsHandler.getHandler(edges[i]);
|
||||
|
||||
const handler = selectionCellsHandler?.getHandler(edges[i]);
|
||||
if (handler) {
|
||||
this.edgeHandlers.push(handler as EdgeHandler);
|
||||
}
|
||||
|
@ -1274,7 +1277,7 @@ class VertexHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the `recursiveResize` status of the given state.
|
||||
* Returns the `recursiveResize` status of the given state.
|
||||
* @param state the given {@link CellState}. This implementation takes the value of this state.
|
||||
* @param me the mouse event.
|
||||
*/
|
||||
|
|
|
@ -746,7 +746,7 @@ const ConnectionsMixin: PartialType = {
|
|||
*/
|
||||
setConnectable(connectable) {
|
||||
const connectionHandler = this.getPlugin('ConnectionHandler') as ConnectionHandler;
|
||||
connectionHandler.setEnabled(connectable);
|
||||
connectionHandler?.setEnabled(connectable);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -754,7 +754,7 @@ const ConnectionsMixin: PartialType = {
|
|||
*/
|
||||
isConnectable() {
|
||||
const connectionHandler = this.getPlugin('ConnectionHandler') as ConnectionHandler;
|
||||
return connectionHandler.isEnabled();
|
||||
return connectionHandler?.isEnabled() ?? false;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -92,8 +92,8 @@ const EditingMixin: PartialType = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Fires a {@link startEditing} event and invokes {@link CellEditorHandler.startEditing}
|
||||
* on {@link editor}. After editing was started, a {@link editingStarted} event is
|
||||
* Fires a {@link InternalEvent.START_EDITING} event and invokes {@link CellEditorHandler.startEditing}.
|
||||
* After editing was started, a {@link InternalEvent.EDITING_STARTED} event is
|
||||
* fired.
|
||||
*
|
||||
* @param cell {@link mxCell} to start the in-place editor for.
|
||||
|
@ -112,8 +112,10 @@ const EditingMixin: PartialType = {
|
|||
new EventObject(InternalEvent.START_EDITING, { cell, event: evt })
|
||||
);
|
||||
|
||||
const cellEditor = this.getPlugin('CellEditorHandler') as CellEditorHandler;
|
||||
cellEditor.startEditing(cell, evt);
|
||||
const cellEditorHandler = this.getPlugin(
|
||||
'CellEditorHandler'
|
||||
) as CellEditorHandler;
|
||||
cellEditorHandler?.startEditing(cell, evt);
|
||||
|
||||
this.fireEvent(
|
||||
new EventObject(InternalEvent.EDITING_STARTED, { cell, event: evt })
|
||||
|
@ -136,14 +138,14 @@ const EditingMixin: PartialType = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Stops the current editing and fires a {@link editingStopped} event.
|
||||
* Stops the current editing and fires a {@link InternalEvent.EDITING_STOPPED} event.
|
||||
*
|
||||
* @param cancel Boolean that specifies if the current editing value
|
||||
* should be stored.
|
||||
*/
|
||||
stopEditing(cancel = false) {
|
||||
const cellEditor = this.getPlugin('CellEditorHandler') as CellEditorHandler;
|
||||
cellEditor.stopEditing(cancel);
|
||||
const cellEditorHandler = this.getPlugin('CellEditorHandler') as CellEditorHandler;
|
||||
cellEditorHandler?.stopEditing(cancel);
|
||||
this.fireEvent(new EventObject(InternalEvent.EDITING_STOPPED, { cancel }));
|
||||
},
|
||||
|
||||
|
@ -218,11 +220,11 @@ const EditingMixin: PartialType = {
|
|||
* If no cell is specified then this returns true if any
|
||||
* cell is currently being edited.
|
||||
*
|
||||
* @param cell {@link mxCell} that should be checked.
|
||||
* @param cell {@link Cell} that should be checked.
|
||||
*/
|
||||
isEditing(cell = null) {
|
||||
const cellEditor = this.getPlugin('CellEditorHandler') as CellEditorHandler;
|
||||
const editingCell = cellEditor.getEditingCell();
|
||||
const cellEditorHandler = this.getPlugin('CellEditorHandler') as CellEditorHandler;
|
||||
const editingCell = cellEditorHandler?.getEditingCell();
|
||||
return !cell ? !!editingCell : cell === editingCell;
|
||||
},
|
||||
|
||||
|
|
|
@ -589,7 +589,7 @@ const EventsMixin: PartialType = {
|
|||
|
||||
if (mxe.isConsumed()) {
|
||||
// Resets the state of the panning handler
|
||||
panningHandler.panningTrigger = false;
|
||||
panningHandler && (panningHandler.panningTrigger = false);
|
||||
}
|
||||
|
||||
// Handles the event if it has not been consumed
|
||||
|
@ -597,6 +597,7 @@ const EventsMixin: PartialType = {
|
|||
this.isEnabled() &&
|
||||
!isConsumed(evt) &&
|
||||
!mxe.isConsumed() &&
|
||||
connectionHandler &&
|
||||
connectionHandler.isEnabled()
|
||||
) {
|
||||
const cell = connectionHandler.marker.getCell(me);
|
||||
|
@ -1057,13 +1058,13 @@ const EventsMixin: PartialType = {
|
|||
Math.abs(this.initialTouchY - me.getGraphY()) < this.tolerance;
|
||||
}
|
||||
|
||||
const cellEditor = this.getPlugin('CellEditorHandler') as CellEditorHandler;
|
||||
const cellEditorHandler = this.getPlugin('CellEditorHandler') as CellEditorHandler;
|
||||
|
||||
// Stops editing for all events other than from cellEditor
|
||||
// Stops editing for all events other than from cellEditorHandler
|
||||
if (
|
||||
evtName === InternalEvent.MOUSE_DOWN &&
|
||||
this.isEditing() &&
|
||||
!cellEditor.isEventSource(me.getEvent())
|
||||
!cellEditorHandler?.isEventSource(me.getEvent())
|
||||
) {
|
||||
this.stopEditing(!this.isInvokesStopCellEditing());
|
||||
}
|
||||
|
|
|
@ -395,15 +395,13 @@ const PanningMixin: PartialType = {
|
|||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Specifies if panning should be enabled. This implementation updates
|
||||
* {@link PanningHandler.panningEnabled} in {@link panningHandler}.
|
||||
* Specifies if panning should be enabled. This implementation updates {@link PanningHandler.panningEnabled}.
|
||||
*
|
||||
* @param enabled Boolean indicating if panning should be enabled.
|
||||
*/
|
||||
setPanning(enabled) {
|
||||
const panningHandler = this.getPlugin('PanningHandler') as PanningHandler;
|
||||
|
||||
if (panningHandler) panningHandler.panningEnabled = enabled;
|
||||
panningHandler && (panningHandler.panningEnabled = enabled);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ const TooltipMixin: PartialType = {
|
|||
'SelectionCellsHandler'
|
||||
) as SelectionCellsHandler;
|
||||
|
||||
const handler = selectionCellsHandler.getHandler(state.cell);
|
||||
const handler = selectionCellsHandler?.getHandler(state.cell);
|
||||
|
||||
// @ts-ignore Guarded against undefined error already.
|
||||
if (handler && typeof handler.getTooltipForNode === 'function') {
|
||||
|
@ -147,8 +147,7 @@ const TooltipMixin: PartialType = {
|
|||
*/
|
||||
setTooltips(enabled: boolean) {
|
||||
const tooltipHandler = this.getPlugin('TooltipHandler') as TooltipHandler;
|
||||
|
||||
tooltipHandler.setEnabled(enabled);
|
||||
tooltipHandler?.setEnabled(enabled);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -534,8 +534,8 @@ class DragSource {
|
|||
|
||||
// Guide is only needed if preview element is used
|
||||
if (this.isGuidesEnabled() && this.previewElement) {
|
||||
const graphHandler = graph.getPlugin('SelectionHandler') as SelectionHandler;
|
||||
this.currentGuide = new Guide(graph, graphHandler.getGuideStates());
|
||||
const selectionHandler = graph.getPlugin('SelectionHandler') as SelectionHandler;
|
||||
this.currentGuide = new Guide(graph, selectionHandler?.getGuideStates());
|
||||
}
|
||||
|
||||
if (this.highlightDropTargets) {
|
||||
|
|
|
@ -78,7 +78,7 @@ class Guide {
|
|||
tolerance = 2;
|
||||
|
||||
/**
|
||||
* Sets the {@link CellStates} that should be used for alignment.
|
||||
* Sets the {@link CellState}s that should be used for alignment.
|
||||
*/
|
||||
setStates(states: CellState[]) {
|
||||
this.states = states;
|
||||
|
@ -103,8 +103,8 @@ class Guide {
|
|||
|
||||
/**
|
||||
* Returns the mxShape to be used for painting the respective guide. This
|
||||
* implementation returns a new, dashed and crisp {@link Polyline} using
|
||||
* {@link Constants#GUIDE_COLOR} and {@link Constants#GUIDE_STROKEWIDTH} as the format.
|
||||
* implementation returns a new, dashed and crisp {@link PolylineShape} using
|
||||
* {@link GUIDE_COLOR} and {@link GUIDE_STROKEWIDTH} as the format.
|
||||
*
|
||||
* @param horizontal Boolean that specifies which guide should be created.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue