From f73adef1b1ac24c5953ab76bafde1c6130763090 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:51:02 +0200 Subject: [PATCH 1/4] feat: let efficiently not load default plugins and style builtins [POC] --- .github/workflows/build.yml | 10 +- README.md | 1 + packages/core/__tests__/utils.ts | 2 + packages/core/src/editor/Editor.ts | 9 +- packages/core/src/editor/EditorToolbar.ts | 7 +- packages/core/src/i18n/Translations.ts | 2 +- packages/core/src/index.ts | 2 + packages/core/src/types.ts | 4 +- packages/core/src/util/Clipboard.ts | 10 +- packages/core/src/util/gestureUtils.ts | 6 +- packages/core/src/util/printUtils.ts | 14 +- packages/core/src/util/treeTraversal.ts | 5 +- packages/core/src/util/xmlUtils.ts | 4 +- packages/core/src/view/AbstractGraph.ts | 1476 +++++++++++++++++ packages/core/src/view/BaseGraph.ts | 31 + packages/core/src/view/Graph.ts | 1444 +--------------- packages/core/src/view/GraphSelectionModel.ts | 6 +- packages/core/src/view/GraphView.ts | 6 +- packages/core/src/view/animate/Effects.ts | 14 +- packages/core/src/view/animate/Morphing.ts | 11 +- packages/core/src/view/cell/CellHighlight.ts | 6 +- packages/core/src/view/cell/CellMarker.ts | 6 +- .../core/src/view/cell/CellStatePreview.ts | 11 +- packages/core/src/view/cell/CellTracker.ts | 4 +- packages/core/src/view/cell/VertexHandle.ts | 4 +- packages/core/src/view/event/InternalEvent.ts | 4 +- .../src/view/handler/ConstraintHandler.ts | 9 +- packages/core/src/view/handler/EdgeHandler.ts | 6 +- packages/core/src/view/handler/KeyHandler.ts | 10 +- .../core/src/view/handler/VertexHandler.ts | 4 +- packages/core/src/view/layout/CircleLayout.ts | 6 +- .../core/src/view/layout/CompactTreeLayout.ts | 4 +- .../core/src/view/layout/CompositeLayout.ts | 6 +- .../core/src/view/layout/EdgeLabelLayout.ts | 4 +- .../core/src/view/layout/FastOrganicLayout.ts | 4 +- packages/core/src/view/layout/GraphLayout.ts | 10 +- .../src/view/layout/HierarchicalLayout.ts | 6 +- .../core/src/view/layout/LayoutManager.ts | 10 +- .../src/view/layout/ParallelEdgeLayout.ts | 4 +- .../core/src/view/layout/PartitionLayout.ts | 4 +- .../core/src/view/layout/RadialTreeLayout.ts | 4 +- packages/core/src/view/layout/StackLayout.ts | 4 +- .../core/src/view/layout/SwimlaneLayout.ts | 8 +- .../core/src/view/layout/SwimlaneManager.ts | 15 +- .../hierarchical/CoordinateAssignment.ts | 12 +- packages/core/src/view/mixins/CellsMixin.ts | 6 +- .../core/src/view/mixins/CellsMixin.type.ts | 4 +- .../core/src/view/mixins/ConnectionsMixin.ts | 6 +- .../src/view/mixins/ConnectionsMixin.type.ts | 4 +- .../core/src/view/mixins/DragDropMixin.ts | 6 +- .../src/view/mixins/DragDropMixin.type.ts | 4 +- packages/core/src/view/mixins/EdgeMixin.ts | 6 +- .../core/src/view/mixins/EdgeMixin.type.ts | 4 +- packages/core/src/view/mixins/EditingMixin.ts | 6 +- .../core/src/view/mixins/EditingMixin.type.ts | 4 +- packages/core/src/view/mixins/EventsMixin.ts | 8 +- .../core/src/view/mixins/EventsMixin.type.ts | 4 +- packages/core/src/view/mixins/FoldingMixin.ts | 6 +- .../core/src/view/mixins/FoldingMixin.type.ts | 4 +- .../core/src/view/mixins/GroupingMixin.ts | 6 +- .../src/view/mixins/GroupingMixin.type.ts | 4 +- packages/core/src/view/mixins/ImageMixin.ts | 4 +- .../core/src/view/mixins/ImageMixin.type.ts | 4 +- packages/core/src/view/mixins/LabelMixin.ts | 6 +- .../core/src/view/mixins/LabelMixin.type.ts | 4 +- packages/core/src/view/mixins/OrderMixin.ts | 6 +- .../core/src/view/mixins/OrderMixin.type.ts | 4 +- .../core/src/view/mixins/OverlaysMixin.ts | 6 +- .../src/view/mixins/OverlaysMixin.type.ts | 4 +- .../core/src/view/mixins/PageBreaksMixin.ts | 6 +- .../src/view/mixins/PageBreaksMixin.type.ts | 4 +- packages/core/src/view/mixins/PanningMixin.ts | 9 +- .../core/src/view/mixins/PanningMixin.type.ts | 4 +- packages/core/src/view/mixins/PortsMixin.ts | 4 +- .../core/src/view/mixins/PortsMixin.type.ts | 4 +- .../core/src/view/mixins/SelectionMixin.ts | 6 +- .../src/view/mixins/SelectionMixin.type.ts | 4 +- packages/core/src/view/mixins/SnapMixin.ts | 6 +- .../core/src/view/mixins/SnapMixin.type.ts | 4 +- .../core/src/view/mixins/SwimlaneMixin.ts | 6 +- .../src/view/mixins/SwimlaneMixin.type.ts | 4 +- .../core/src/view/mixins/TerminalMixin.ts | 6 +- .../src/view/mixins/TerminalMixin.type.ts | 4 +- packages/core/src/view/mixins/TooltipMixin.ts | 9 +- .../core/src/view/mixins/TooltipMixin.type.ts | 4 +- .../core/src/view/mixins/ValidationMixin.ts | 8 +- .../src/view/mixins/ValidationMixin.type.ts | 4 +- packages/core/src/view/mixins/VertexMixin.ts | 6 +- .../core/src/view/mixins/VertexMixin.type.ts | 4 +- packages/core/src/view/mixins/ZoomMixin.ts | 6 +- .../core/src/view/mixins/ZoomMixin.type.ts | 4 +- .../src/view/mixins/_graph-mixins-apply.ts | 4 +- .../core/src/view/other/AutoSaveManager.ts | 10 +- packages/core/src/view/other/DragSource.ts | 32 +- packages/core/src/view/other/Guide.ts | 6 +- packages/core/src/view/other/Multiplicity.ts | 18 +- packages/core/src/view/other/Outline.ts | 41 +- .../core/src/view/other/PanningManager.ts | 4 +- packages/core/src/view/other/PrintPreview.ts | 10 +- .../src/view/plugins/CellEditorHandler.ts | 6 +- .../src/view/plugins/ConnectionHandler.ts | 8 +- packages/core/src/view/plugins/FitPlugin.ts | 4 +- .../core/src/view/plugins/PanningHandler.ts | 6 +- .../core/src/view/plugins/PopupMenuHandler.ts | 6 +- .../src/view/plugins/RubberBandHandler.ts | 6 +- .../src/view/plugins/SelectionCellsHandler.ts | 6 +- .../core/src/view/plugins/SelectionHandler.ts | 19 +- .../core/src/view/plugins/TooltipHandler.ts | 6 +- .../view/undoable_changes/SelectionChange.ts | 6 +- .../js-example-selected-features/.gitignore | 4 + .../js-example-selected-features/README.md | 29 + .../js-example-selected-features/favicon.svg | 1 + .../js-example-selected-features/index.html | 26 + .../js-example-selected-features/package.json | 20 + .../js-example-selected-features/src/index.js | 144 ++ .../src/style.css | 46 + .../webpack.config.js | 45 + .../js-example-without-defaults/src/index.js | 23 +- .../ts-example-selected-features/README.md | 25 + .../ts-example-selected-features/favicon.svg | 15 + .../ts-example-selected-features/index.html | 28 + .../ts-example-selected-features/package.json | 14 + .../ts-example-selected-features/src/main.ts | 144 ++ .../src/style.css | 51 + .../src/vite-env.d.ts | 1 + .../tsconfig.json | 14 + .../vite.config.js | 33 + .../ts-example-without-defaults/src/main.ts | 23 +- .../vite.config.js | 2 +- packages/website/docs/demo-and-examples.md | 1 + 130 files changed, 2539 insertions(+), 1813 deletions(-) create mode 100644 packages/core/src/view/AbstractGraph.ts create mode 100644 packages/core/src/view/BaseGraph.ts create mode 100644 packages/js-example-selected-features/.gitignore create mode 100644 packages/js-example-selected-features/README.md create mode 100644 packages/js-example-selected-features/favicon.svg create mode 100644 packages/js-example-selected-features/index.html create mode 100644 packages/js-example-selected-features/package.json create mode 100644 packages/js-example-selected-features/src/index.js create mode 100644 packages/js-example-selected-features/src/style.css create mode 100644 packages/js-example-selected-features/webpack.config.js create mode 100644 packages/ts-example-selected-features/README.md create mode 100644 packages/ts-example-selected-features/favicon.svg create mode 100644 packages/ts-example-selected-features/index.html create mode 100644 packages/ts-example-selected-features/package.json create mode 100644 packages/ts-example-selected-features/src/main.ts create mode 100644 packages/ts-example-selected-features/src/style.css create mode 100644 packages/ts-example-selected-features/src/vite-env.d.ts create mode 100644 packages/ts-example-selected-features/tsconfig.json create mode 100644 packages/ts-example-selected-features/vite.config.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f6cac5cb16..08a051d636 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,12 +57,16 @@ jobs: run: npm test -w packages/ts-support - name: Build ts-example (custom shapes) run: npm run build -w packages/ts-example + - name: Build ts-example (selected features) + run: npm run build -w packages/ts-example-selected-features - name: Build ts-example (without defaults) run: npm run build -w packages/ts-example-without-defaults - name: Build Storybook mini-site run: npm run build -w packages/html - name: Build js-example run: npm run build -w packages/js-example + - name: Build js-example (selected features) + run: npm run build -w packages/js-example-selected-features - name: Build js-example (without defaults) run: npm run build -w packages/js-example-without-defaults - name: Upload all examples as artifact @@ -71,10 +75,8 @@ jobs: name: examples-${{runner.os}}-${{github.sha}} path: | packages/html/dist/ - packages/js-example/dist/ - packages/js-example-without-defaults/dist/ - packages/ts-example/dist/ - packages/ts-example-without-defaults/dist/ + packages/js-example*/dist/ + packages/ts-example*/dist/ - name: Ensure no circular dependencies in the @maxgraph/core produced JS files run: npm run check:circular-dependencies -w packages/core - name: Ensure no lint errors diff --git a/README.md b/README.md index 538f7ef180..9bcd98b10a 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ Note that they are based on `maxGraph` features, which require the use of [CSS a - The stories are currently written in `JavaScript` and will be progressively migrated to `TypeScript`. - A live instance is available on the [maxGraph website](https://maxgraph.github.io/maxGraph/demo). - the [ts-example](packages/ts-example) project/application that demonstrates how to define and use custom `Shapes` with `maxGraph`. It is a vanilla TypeScript application built by [Vite](https://vitejs.dev/). +- the [ts-example-selected-features](packages/js-example-without-defaults) project/application that demonstrates how to use select some features (use related plugins only) and how to only register specific style defaults. It is a vanilla JavaScript application built by [Webpack](https://webpack.js.org/). - the [ts-example-without-defaults](packages/ts-example-without-defaults) project/application that demonstrates how to not use defaults plugins and style defaults (shapes, perimeters, ...). It is a vanilla TypeScript application built by [Vite](https://vitejs.dev/). - the [js-example](packages/js-example) project/application that demonstrates how to import and export the `maxGraph` model with XML data. It is a vanilla JavaScript application built by [Webpack](https://webpack.js.org/). - the [js-example-without-defaults](packages/js-example-without-defaults) project/application that demonstrates how to not use defaults plugins and style defaults (shapes, perimeters, ...). It is a vanilla JavaScript application built by [Webpack](https://webpack.js.org/). diff --git a/packages/core/__tests__/utils.ts b/packages/core/__tests__/utils.ts index 92079b8a9a..e37e4f9246 100644 --- a/packages/core/__tests__/utils.ts +++ b/packages/core/__tests__/utils.ts @@ -17,6 +17,8 @@ limitations under the License. import { Cell, type CellStateStyle, Graph } from '../src'; import { jest } from '@jest/globals'; +// TODO add tests that use BaseGraph to ensure everything works fine + // no need for a container, we don't check the view here export const createGraphWithoutContainer = (): Graph => new Graph(null!); diff --git a/packages/core/src/editor/Editor.ts b/packages/core/src/editor/Editor.ts index 411f63644a..7db884e1b0 100644 --- a/packages/core/src/editor/Editor.ts +++ b/packages/core/src/editor/Editor.ts @@ -1387,11 +1387,14 @@ export class Editor extends EventSource { } /** - * Creates the {@link graph} for the editor. The graph is created with no - * container and is initialized from {@link setGraphContainer}. - * @returns graph instance + * Creates the {@link graph} for the editor. + * + * The Graph is created with no container and is initialized from {@link setGraphContainer}. + * + * @returns the Graph instance used by the Editor */ createGraph(): Graph { + // TODO we may probably introduce a createGraphInstance method to let user choose which plugins they want to use const graph = new Graph(undefined!); // Enables rubberband, tooltips, panning diff --git a/packages/core/src/editor/EditorToolbar.ts b/packages/core/src/editor/EditorToolbar.ts index 322a306813..c6e11ee493 100644 --- a/packages/core/src/editor/EditorToolbar.ts +++ b/packages/core/src/editor/EditorToolbar.ts @@ -25,7 +25,7 @@ import { getClientX, getClientY } from '../util/EventUtils'; import { makeDraggable } from '../util/gestureUtils'; import Editor from './Editor'; import type Cell from '../view/cell/Cell'; -import type { Graph } from '../view/Graph'; +import type { AbstractGraph } from '../view/AbstractGraph'; import EventObject from '../view/event/EventObject'; import type { DropHandler } from '../view/other/DragSource'; @@ -300,10 +300,9 @@ export class EditorToolbar { toggle ); - // Creates a wrapper function that calls the click handler without - // the graph argument + // Creates a wrapper function that calls the click handler without the graph argument const dropHandler: DropHandler = ( - graph: Graph, + _graph: AbstractGraph, evt: MouseEvent, cell: Cell | null ) => { diff --git a/packages/core/src/i18n/Translations.ts b/packages/core/src/i18n/Translations.ts index 30a17aea16..43361128c7 100644 --- a/packages/core/src/i18n/Translations.ts +++ b/packages/core/src/i18n/Translations.ts @@ -73,7 +73,7 @@ import { I18nProvider } from '../types'; * * ## Loading default resources * - * Call {@link loadResources} to load the default resources file for both {@link Graph} and {@link Editor}. + * Call {@link loadResources} to load the default resources file for both {@link AbstractGraph} and {@link Editor}. * * @category I18n */ diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9d9dde96ba..c0c22d22b6 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -17,6 +17,8 @@ limitations under the License. // Contribution of Mixins to the Graph type (no side effects, types only) import './view/mixins/_graph-mixins-types'; +export { AbstractGraph } from './view/AbstractGraph'; +export { BaseGraph } from './view/BaseGraph'; export { Graph } from './view/Graph'; export * from './view/plugins'; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 5c4057dd9a..ade870916c 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -15,7 +15,6 @@ limitations under the License. */ import { IDENTITY_FIELD_NAME } from './util/Constants'; -import type { Graph } from './view/Graph'; import type AbstractCanvas2D from './view/canvas/AbstractCanvas2D'; import type Cell from './view/cell/Cell'; import type CellState from './view/cell/CellState'; @@ -26,6 +25,7 @@ import type Point from './view/geometry/Point'; import type Rectangle from './view/geometry/Rectangle'; import type Shape from './view/geometry/Shape'; import type ImageBox from './view/image/ImageBox'; +import { AbstractGraph } from './view/AbstractGraph'; export type FilterFunction = (cell: Cell) => boolean; @@ -1102,7 +1102,7 @@ export type VertexParameters = { /** @category Plugin */ export interface GraphPluginConstructor { pluginId: string; - new (graph: Graph): GraphPlugin; + new (graph: AbstractGraph): GraphPlugin; } /** @category Plugin */ diff --git a/packages/core/src/util/Clipboard.ts b/packages/core/src/util/Clipboard.ts index 38cae2741e..579ea7c21b 100644 --- a/packages/core/src/util/Clipboard.ts +++ b/packages/core/src/util/Clipboard.ts @@ -17,7 +17,7 @@ limitations under the License. */ import type Cell from '../view/cell/Cell'; -import type { Graph } from '../view/Graph'; +import type { AbstractGraph } from '../view/AbstractGraph'; import { getTopmostCells } from './cellArrayUtils'; /** @@ -123,7 +123,7 @@ class Clipboard { * @param cells - Optional array of {@link Cell} to be cut. * @returns Returns the cells that have been cut from the graph. */ - static cut(graph: Graph, cells: Cell[] = []): Cell[] { + static cut(graph: AbstractGraph, cells: Cell[] = []): Cell[] { cells = Clipboard.copy(graph, cells); Clipboard.insertCount = 0; Clipboard.removeCells(graph, cells); @@ -137,7 +137,7 @@ class Clipboard { * @param graph - {@link graph} that contains the cells to be cut. * @param cells - Array of {@link Cell} to be cut. */ - static removeCells(graph: Graph, cells: Cell[]): void { + static removeCells(graph: AbstractGraph, cells: Cell[]): void { graph.removeCells(cells); } @@ -149,7 +149,7 @@ class Clipboard { * @param graph - {@link graph} that contains the cells to be copied. * @param cells - Optional array of {@link Cell} to be copied. */ - static copy(graph: Graph, cells?: Cell[]): Cell[] { + static copy(graph: AbstractGraph, cells?: Cell[]): Cell[] { cells = cells || graph.getSelectionCells(); const result = getTopmostCells(graph.getExportableCells(cells)); Clipboard.insertCount = 1; @@ -164,7 +164,7 @@ class Clipboard { * * @param graph - {@link Graph} to paste the {@link Cell}s into. */ - static paste(graph: Graph): Cell[] | null { + static paste(graph: AbstractGraph): Cell[] | null { let cells = null; if (!Clipboard.isEmpty() && Clipboard.getCells()) { diff --git a/packages/core/src/util/gestureUtils.ts b/packages/core/src/util/gestureUtils.ts index d25e295262..f3a792f9e2 100644 --- a/packages/core/src/util/gestureUtils.ts +++ b/packages/core/src/util/gestureUtils.ts @@ -17,7 +17,7 @@ limitations under the License. import DragSource, { DropHandler } from '../view/other/DragSource'; import Point from '../view/geometry/Point'; import { TOOLTIP_VERTICAL_OFFSET } from './Constants'; -import type { Graph } from '../view/Graph'; +import type { AbstractGraph } from '../view/AbstractGraph'; import type Cell from '../view/cell/Cell'; /** @@ -86,7 +86,7 @@ import type Cell from '../view/cell/Cell'; */ export const makeDraggable = ( element: Element, - graphF: Graph | Function, + graphF: AbstractGraph | Function, funct: DropHandler, dragElement: Element | null = null, dx: number | null = null, @@ -95,7 +95,7 @@ export const makeDraggable = ( scalePreview = false, highlightDropTargets = true, getDropTarget: - | ((graph: Graph, x: number, y: number, evt: MouseEvent) => Cell) + | ((graph: AbstractGraph, x: number, y: number, evt: MouseEvent) => Cell) | null = null ) => { const dragSource = new DragSource(element, funct); diff --git a/packages/core/src/util/printUtils.ts b/packages/core/src/util/printUtils.ts index 48922c2856..e9081fc51b 100644 --- a/packages/core/src/util/printUtils.ts +++ b/packages/core/src/util/printUtils.ts @@ -20,7 +20,7 @@ import Client from '../Client'; import { PAGE_FORMAT_A4_PORTRAIT } from './Constants'; import Rectangle from '../view/geometry/Rectangle'; import { getOuterHtml } from './domUtils'; -import type { Graph } from '../view/Graph'; +import type { AbstractGraph } from '../view/AbstractGraph'; import { removeCursors } from './styleUtils'; /** @@ -30,14 +30,14 @@ import { removeCursors } from './styleUtils'; * pages in the print output. See {@link PrintPreview} for an example. * * @param pageCount Specifies the number of pages in the print output. - * @param graph {@link Graph} that should be printed. + * @param graph {@link AbstractGraph} that should be printed. * @param pageFormat Optional {@link Rectangle} that specifies the page format. * Default is . * @param border The border along each side of every page. */ export const getScaleForPageCount = ( pageCount: number, - graph: Graph, + graph: AbstractGraph, pageFormat?: Rectangle, border = 0 ) => { @@ -162,7 +162,7 @@ export const getScaleForPageCount = ( * If you experience problems with missing stylesheets in IE then try adding * the domain to the trusted sites. * - * @param graph {@link Graph} to be copied. + * @param graph {@link AbstractGraph} to be copied. * @param doc Document where the new graph is created. * @param x0 X-coordinate of the graph view origin. Default is 0. * @param y0 Y-coordinate of the graph view origin. Default is 0. @@ -170,7 +170,7 @@ export const getScaleForPageCount = ( * @param h Optional height of the graph view. */ export const show = ( - graph: Graph, + graph: AbstractGraph, doc: Document | null = null, x0 = 0, y0 = 0, @@ -279,9 +279,9 @@ export const show = ( * * This function should be called from within the document with the graph. * - * @param graph {@link Graph} to be printed. + * @param graph {@link AbstractGraph} to be printed. */ -export const printScreen = (graph: Graph) => { +export const printScreen = (graph: AbstractGraph) => { const wnd = window.open(); if (!wnd) return; diff --git a/packages/core/src/util/treeTraversal.ts b/packages/core/src/util/treeTraversal.ts index 98cd5a385d..b43f135c74 100644 --- a/packages/core/src/util/treeTraversal.ts +++ b/packages/core/src/util/treeTraversal.ts @@ -16,7 +16,7 @@ limitations under the License. import type Cell from '../view/cell/Cell'; import Dictionary from './Dictionary'; -import type { Graph } from '../view/Graph'; +import type { AbstractGraph } from '../view/AbstractGraph'; /***************************************************************************** * Group: Tree and traversal-related @@ -27,6 +27,7 @@ import type { Graph } from '../view/Graph'; * edges. If the result is empty then the with the greatest difference * between incoming and outgoing edges is returned. * + * @param graph the Graph to use for the traversal. * @param parent {@link Cell} whose children should be checked. * @param isolate Optional boolean that specifies if edges should be ignored if * the opposite end is not a child of the given parent cell. Default is @@ -36,7 +37,7 @@ import type { Graph } from '../view/Graph'; * counted. Default is `false`. */ export function findTreeRoots( - graph: Graph, + graph: AbstractGraph, parent: Cell, isolate = false, invert = false diff --git a/packages/core/src/util/xmlUtils.ts b/packages/core/src/util/xmlUtils.ts index 6240b3ac3d..a57ca31dbe 100644 --- a/packages/core/src/util/xmlUtils.ts +++ b/packages/core/src/util/xmlUtils.ts @@ -19,7 +19,7 @@ limitations under the License. import { DIALECT, NODETYPE, NS_SVG } from './Constants'; import Point from '../view/geometry/Point'; import type Cell from '../view/cell/Cell'; -import type { Graph } from '../view/Graph'; +import type { AbstractGraph } from '../view/AbstractGraph'; import { htmlEntities, trim } from './StringUtils'; import TemporaryCellStates from '../view/cell/TemporaryCellStates'; import type { StyleValue } from '../types'; @@ -39,7 +39,7 @@ export const parseXml = (xmlString: string): Document => { }; export const getViewXml = ( - graph: Graph, + graph: AbstractGraph, scale = 1, cells: Cell[] | null = null, x0 = 0, diff --git a/packages/core/src/view/AbstractGraph.ts b/packages/core/src/view/AbstractGraph.ts new file mode 100644 index 0000000000..f72279cfa0 --- /dev/null +++ b/packages/core/src/view/AbstractGraph.ts @@ -0,0 +1,1476 @@ +/* +Copyright 2021-present The maxGraph project Contributors +Copyright (c) 2006-2015, JGraph Ltd +Copyright (c) 2006-2015, Gaudenz Alder + +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 Image from './image/ImageBox'; +import EventObject from './event/EventObject'; +import EventSource from './event/EventSource'; +import InternalEvent from './event/InternalEvent'; +import Rectangle from './geometry/Rectangle'; +import Client from '../Client'; +import type PanningHandler from './plugins/PanningHandler'; +import GraphView from './GraphView'; +import CellRenderer from './cell/CellRenderer'; +import Point from './geometry/Point'; +import { getCurrentStyle, hasScrollbars, parseCssNumber } from '../util/styleUtils'; +import Cell from './cell/Cell'; +import GraphDataModel from './GraphDataModel'; +import { Stylesheet } from './style/Stylesheet'; +import { PAGE_FORMAT_A4_PORTRAIT } from '../util/Constants'; +import ChildChange from './undoable_changes/ChildChange'; +import GeometryChange from './undoable_changes/GeometryChange'; +import RootChange from './undoable_changes/RootChange'; +import StyleChange from './undoable_changes/StyleChange'; +import TerminalChange from './undoable_changes/TerminalChange'; +import ValueChange from './undoable_changes/ValueChange'; +import CellState from './cell/CellState'; +import { isNode } from '../util/domUtils'; +import EdgeStyle from './style/EdgeStyle'; +import EdgeHandler from './handler/EdgeHandler'; +import VertexHandler from './handler/VertexHandler'; +import EdgeSegmentHandler from './handler/EdgeSegmentHandler'; +import ElbowEdgeHandler from './handler/ElbowEdgeHandler'; +import type { + EdgeStyleFunction, + GraphFoldingOptions, + GraphPlugin, + GraphPluginConstructor, + MouseListenerSet, +} from '../types'; +import Multiplicity from './other/Multiplicity'; +import ImageBundle from './image/ImageBundle'; +import GraphSelectionModel from './GraphSelectionModel'; +import { applyGraphMixins } from './mixins/_graph-mixins-apply'; +import { isNullish } from '../internal/utils'; +import { isI18nEnabled } from '../internal/i18n-utils'; + +// TODO move types to /types.ts +// TODO review the name: GraphOptions for simplicity? +export type BaseGraphOptions = { + container: HTMLElement; // TODO should be optional in the future to manage https://github.com/maxGraph/maxGraph/issues/367 + plugins?: GraphPluginConstructor[]; +} & CollaboratorsGraphOptions; + +// TODO generalize usage of collaborators, see https://github.com/maxGraph/maxGraph/discussions/151 +export type CollaboratorsGraphOptions = { + cellRenderer?: CellRenderer; + model?: GraphDataModel; + // TODO do not allow null for simplification, in Graph constructor, set to undefined if null is passed + stylesheet?: Stylesheet | null; // allow null because the Graph constructor allows null values + // TODO potential cycle + view?: (graph: AbstractGraph) => GraphView; +}; + +// TODO in all JSDoc replace Graph by AbstractGraph + +// TODO review the JSDoc for the description of the class +/** + * 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 + * - selectionHandler: {@link SelectionHandler} for moving and cloning cells + * + * These listeners will be called in the above order if they are enabled. + */ +export abstract class AbstractGraph extends EventSource { + container: HTMLElement; + + destroyed = false; + + graphModelChangeListener: Function | null = null; + paintBackground: Function | null = null; + isConstrainedMoving = false; + + // =================================================================================================================== + // Group: Variables (that maybe should be in the mixins, but need to be created for each new class instance) + // =================================================================================================================== + + cells: Cell[] = []; + + imageBundles: ImageBundle[] = []; + + /** + * Holds the mouse event listeners. See {@link fireMouseEvent}. + */ + mouseListeners: MouseListenerSet[] = []; + + /** + * An array of {@link Multiplicity} describing the allowed connections in a graph. + */ + multiplicities: Multiplicity[] = []; + + /** + * Holds the {@link GraphDataModel} that contains the cells to be displayed. + */ + model!: GraphDataModel; // initialized in "initializeCollaborators" + + private plugins: Record = {}; + + /** + * Holds the {@link GraphView} that caches the {@link CellState}s for the cells. + */ + view!: GraphView; // initialized in "initializeCollaborators" + + /** + * Holds the {@link Stylesheet} that defines the appearance of the cells. + * + * Use the following code to read a stylesheet into an existing graph. + * + * @example + * ```javascript + * var req = mxUtils.load('stylesheet.xml'); + * var root = req.getDocumentElement(); + * var dec = new Codec(root.ownerDocument); + * dec.decode(root, graph.stylesheet); + * ``` + */ + stylesheet!: Stylesheet; // initialized in "initializeCollaborators" + + /** + * Holds the {@link CellRenderer} for rendering the cells in the graph. + */ + cellRenderer!: CellRenderer; // initialized in "initializeCollaborators" + + /** + * RenderHint as it was passed to the constructor. + */ + renderHint: string | null = null; + + /** + * Dialect to be used for drawing the graph. Possible values are all constants in {@link DIALECT}. + */ + dialect: 'svg' | 'mixedHtml' | 'preferHtml' | 'strictHtml' = 'svg'; + + /** + * Value returned by {@link getOverlap} if {@link isAllowOverlapParent} returns + * `true` for the given cell. {@link getOverlap} is used in {@link constrainChild} if + * {@link isConstrainChild} returns `true`. The value specifies the + * portion of the child which is allowed to overlap the parent. + */ + defaultOverlap = 0.5; + + /** + * Specifies the default parent to be used to insert new cells. + * This is used in {@link getDefaultParent}. + * @default null + */ + defaultParent: Cell | null = null; + + /** + * Specifies the {@link Image} to be returned by {@link getBackgroundImage}. + * @default null + * + * @example + * ```javascript + * var img = new mxImage('http://www.example.com/maps/examplemap.jpg', 1024, 768); + * graph.setBackgroundImage(img); + * graph.view.validate(); + * ``` + */ + backgroundImage: Image | null = null; + + /** + * Specifies if the background page should be visible. + * Not yet implemented. + * @default false + */ + pageVisible = false; + + /** + * Specifies if a dashed line should be drawn between multiple pages. + * If you change this value while a graph is being displayed then you + * should call {@link sizeDidChange} to force an update of the display. + * @default false + */ + pageBreaksVisible = false; + + /** + * Specifies the color for page breaks. + * @default gray + */ + pageBreakColor = 'gray'; + + /** + * Specifies the page breaks should be dashed. + * @default true + */ + pageBreakDashed = true; + + /** + * Specifies the minimum distance in pixels for page breaks to be visible. + * @default 20 + */ + minPageBreakDist = 20; + + /** + * 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 = false; + + /** + * Specifies the page format for the background page. + * This is used as the default in {@link PrintPreview} and for painting the background page + * if {@link pageVisible} is `true` and the page breaks if {@link pageBreaksVisible} is `true`. + * @default {@link mxConstants.PAGE_FORMAT_A4_PORTRAIT} + */ + pageFormat = new Rectangle(...PAGE_FORMAT_A4_PORTRAIT); + + /** + * Specifies the scale of the background page. + * Not yet implemented. + * @default 1.5 + */ + pageScale = 1.5; + + /** + * Specifies the return value for {@link isEnabled}. + * @default true + */ + enabled = true; + + /** + * Specifies the return value for {@link canExportCell}. + * @default true + */ + exportEnabled = true; + + /** + * Specifies the return value for {@link canImportCell}. + * @default true + */ + importEnabled = true; + + /** + * Specifies if the graph should automatically scroll regardless of the + * scrollbars. This will scroll the container using positive values for + * scroll positions (ie usually only rightwards and downwards). To avoid + * possible conflicts with panning, set {@link translateToScrollPosition} to `true`. + */ + ignoreScrollbars = false; + + /** + * Specifies if the graph should automatically convert the current scroll + * position to a translate in the graph view when a mouseUp event is received. + * This can be used to avoid conflicts when using {@link autoScroll} and + * {@link ignoreScrollbars} with no scrollbars in the container. + */ + translateToScrollPosition = false; + + /** + * {@link Rectangle} that specifies the area in which all cells in the diagram + * should be placed. Uses in {@link getMaximumGraphBounds}. Use a width or height of + * `0` if you only want to give a upper, left corner. + */ + maximumGraphBounds: Rectangle | null = null; + + /** + * {@link Rectangle} that specifies the minimum size of the graph. This is ignored + * if the graph container has no scrollbars. + * @default null + */ + minimumGraphSize: Rectangle | null = null; + + /** + * {@link Rectangle} that specifies the minimum size of the {@link container} if + * {@link resizeContainer} is `true`. + */ + minimumContainerSize: Rectangle | null = null; + + /** + * {@link Rectangle} that specifies the maximum size of the container if + * {@link resizeContainer} is `true`. + */ + maximumContainerSize: Rectangle | null = null; + + /** + * Specifies if the container should be resized to the graph size when + * the graph size has changed. + * @default false + */ + resizeContainer = false; + + /** + * Border to be added to the bottom and right side when the container is + * being resized after the graph has been changed. + * @default 0 + */ + border = 0; + + /** + * Specifies if edges should appear in the foreground regardless of their order + * in the model. If {@link keepEdgesInForeground} and {@link keepEdgesInBackground} are + * both `true` then the normal order is applied. + * @default false + */ + keepEdgesInForeground = false; + + /** + * Specifies if edges should appear in the background regardless of their order + * in the model. If {@link keepEdgesInForeground} and {@link keepEdgesInBackground} are + * both `true` then the normal order is applied. + * @default false + */ + keepEdgesInBackground = false; + + /** + * Specifies the return value for {@link isRecursiveResize}. + * @default false (for backwards compatibility) + */ + recursiveResize = false; + + /** + * Specifies if the scale and translate should be reset if the root changes in + * the model. + * @default true + */ + resetViewOnRootChange = true; + + /** + * Specifies if loops (aka self-references) are allowed. + * @default false + */ + allowLoops = false; + + /** + * {@link EdgeStyle} to be used for loops. This is a fallback for loops if the + * {@link CellStateStyle.loopStyle} is `undefined`. + * @default {@link EdgeStyle.Loop} + */ + defaultLoopStyle = EdgeStyle.Loop; + + /** + * Specifies if multiple edges in the same direction between the same pair of + * vertices are allowed. + * @default true + */ + multigraph = true; + + /** + * Specifies the minimum scale to be applied in {@link fit}. Set this to `null` to allow any value. + * @default 0.1 + */ + minFitScale: number | null = 0.1; + + /** + * Specifies the maximum scale to be applied in {@link fit}. Set this to `null` to allow any value. + * @default 8 + */ + maxFitScale: number | null = 8; + + /** + * Specifies the {@link Image} for the image to be used to display a warning + * overlay. See {@link setCellWarning}. Default value is Client.imageBasePath + + * '/warning'. The extension for the image depends on the platform. It is + * '.png' on the Mac and '.gif' on all other platforms. + */ + warningImage: Image = new Image( + `${Client.imageBasePath}/warning${Client.IS_MAC ? '.png' : '.gif'}`, + 16, + 16 + ); + + /** + * Specifies the resource key for the error message to be displayed in + * non-multigraphs when two vertices are already connected. If the resource + * for this key does not exist then the value is used as the error message. + * @default 'alreadyConnected' + */ + alreadyConnectedResource: string = isI18nEnabled() ? 'alreadyConnected' : ''; + + /** + * Specifies the resource key for the warning message to be displayed when + * a collapsed cell contains validation errors. If the resource for this + * key does not exist then the value is used as the warning message. + * @default 'containsValidationErrors' + */ + containsValidationErrorsResource: string = isI18nEnabled() + ? 'containsValidationErrors' + : ''; + + /** Folding options. */ + options: GraphFoldingOptions = { + foldingEnabled: true, + collapsedImage: new Image(`${Client.imageBasePath}/collapsed.gif`, 9, 9), + expandedImage: new Image(`${Client.imageBasePath}/expanded.gif`, 9, 9), + collapseToPreferredSize: true, + }; + + // =================================================================================================================== + // Group: "Create Class Instance" factory functions. + // These can be overridden in subclasses of Graph to allow the Graph to instantiate user-defined implementations with + // custom behavior. + // =================================================================================================================== + + /** + * Hooks to create a new {@link EdgeHandler} for the given {@link CellState}. + * + * @param state {@link CellState} to create the handler for. + */ + createEdgeHandlerInstance(state: CellState): EdgeHandler { + // Note this method not being called createEdgeHandler to keep compatibility + // with older code which overrides/calls createEdgeHandler + return new EdgeHandler(state); + } + + /** + * Hooks to create a new {@link EdgeSegmentHandler} for the given {@link CellState}. + * + * @param state {@link CellState} to create the handler for. + */ + createEdgeSegmentHandler(state: CellState) { + return new EdgeSegmentHandler(state); + } + + /** + * Hooks to create a new {@link ElbowEdgeHandler} for the given {@link CellState}. + * + * @param state {@link CellState} to create the handler for. + */ + createElbowEdgeHandler(state: CellState) { + return new ElbowEdgeHandler(state); + } + + /** + * Creates a new {@link GraphSelectionModel} to be used in this graph. + */ + createSelectionModel() { + return new GraphSelectionModel(this); + } + + /** + * Hooks to create a new {@link VertexHandler} for the given {@link CellState}. + * + * @param state {@link CellState} to create the handler for. + */ + createVertexHandler(state: CellState): VertexHandler { + return new VertexHandler(state); + } + + // =================================================================================================================== + // Group: Main graph constructor and functions + // =================================================================================================================== + + // TODO review the name of this method, explain purpose + // register defaults transparently, global defaults can also be registered outside of this class + protected registerDefaults(): void { + // do nothing, this is the purpose of this class not to load any defaults + } + + protected abstract initializeCollaborators(options: CollaboratorsGraphOptions): void; + + // TODO decide if we manage undefined options with "options: BaseGraphOptions = {}" + // This will be needed to manage https://github.com/maxGraph/maxGraph/issues/367 + constructor(options: BaseGraphOptions) { + super(); + this.registerDefaults(); + + this.container = options.container ?? document.createElement('div'); + + // collaborators + this.initializeCollaborators(options); + + // Adds a graph model listener to update the view + this.graphModelChangeListener = (sender: any, evt: EventObject) => { + this.graphModelChanged(evt.getProperty('edit').changes); + }; + this.getDataModel().addListener(InternalEvent.CHANGE, this.graphModelChangeListener); + + // Initializes the container using the view + this.view.init(); + + // Updates the size of the container for the current graph + this.sizeDidChange(); + + // Set the selection model + this.setSelectionModel(this.createSelectionModel()); + + // Initializes plugins + options.plugins?.forEach((p) => (this.plugins[p.pluginId] = new p(this))); + + this.view.revalidate(); + } + + getContainer = () => this.container; + getPlugin = (id: string): T => this.plugins[id] as T; + getCellRenderer = () => this.cellRenderer; + getDialect = () => this.dialect; + isPageVisible = () => this.pageVisible; + isPageBreaksVisible = () => this.pageBreaksVisible; + getPageBreakColor = () => this.pageBreakColor; + isPageBreakDashed = () => this.pageBreakDashed; + getMinPageBreakDist = () => this.minPageBreakDist; + isPreferPageSize = () => this.preferPageSize; + getPageFormat = () => this.pageFormat; + getPageScale = () => this.pageScale; + isExportEnabled = () => this.exportEnabled; + isImportEnabled = () => this.importEnabled; + isIgnoreScrollbars = () => this.ignoreScrollbars; + isTranslateToScrollPosition = () => this.translateToScrollPosition; + + getMinimumGraphSize = () => this.minimumGraphSize; + setMinimumGraphSize = (size: Rectangle | null) => (this.minimumGraphSize = size); + + getMinimumContainerSize = () => this.minimumContainerSize; + setMinimumContainerSize = (size: Rectangle | null) => + (this.minimumContainerSize = size); + + getWarningImage() { + return this.warningImage; + } + + getAlreadyConnectedResource = () => this.alreadyConnectedResource; + + getContainsValidationErrorsResource = () => this.containsValidationErrorsResource; + + /** + * Updates the model in a transaction. + * + * @param fn the update to be performed in the transaction. + * + * @see {@link GraphDataModel.batchUpdate} + */ + batchUpdate(fn: () => void) { + this.getDataModel().batchUpdate(fn); + } + + /** + * Returns the {@link GraphDataModel} that contains the cells. + */ + getDataModel() { + return this.model; + } + + /** + * Returns the {@link GraphView} that contains the {@link mxCellStates}. + */ + getView() { + return this.view; + } + + /** + * Returns the {@link Stylesheet} that defines the style. + */ + getStylesheet() { + return this.stylesheet; + } + + /** + * Sets the {@link Stylesheet} that defines the style. + */ + setStylesheet(stylesheet: Stylesheet) { + this.stylesheet = stylesheet; + } + + /** + * Called when the graph model changes. Invokes {@link processChange} on each + * item of the given array to update the view accordingly. + * + * @param changes Array that contains the individual changes. + */ + graphModelChanged(changes: any[]) { + for (const change of changes) { + this.processChange(change); + } + + this.updateSelection(); + this.view.validate(); + this.sizeDidChange(); + } + + /** + * Processes the given change and invalidates the respective cached data + * in {@link GraphView}. This fires a {@link root} event if the root has changed in the + * model. + * + * @param {(RootChange|ChildChange|TerminalChange|GeometryChange|ValueChange|StyleChange)} change - Object that represents the change on the model. + */ + processChange(change: any): void { + // Resets the view settings, removes all cells and clears + // the selection if the root changes. + if (change instanceof RootChange) { + this.clearSelection(); + this.setDefaultParent(null); + + if (change.previous) this.removeStateForCell(change.previous); + + if (this.resetViewOnRootChange) { + this.view.scale = 1; + this.view.translate.x = 0; + this.view.translate.y = 0; + } + + this.fireEvent(new EventObject(InternalEvent.ROOT)); + } + + // Adds or removes a child to the view by online invaliding + // the minimal required portions of the cache, namely, the + // old and new parent and the child. + else if (change instanceof ChildChange) { + const newParent = change.child.getParent(); + this.view.invalidate(change.child, true, true); + + if ( + !newParent || + !this.getDataModel().contains(newParent) || + newParent.isCollapsed() + ) { + this.view.invalidate(change.child, true, true); + this.removeStateForCell(change.child); + + // Handles special case of current root of view being removed + if (this.view.currentRoot == change.child) { + this.home(); + } + } + + if (newParent != change.previous) { + // Refreshes the collapse/expand icons on the parents + if (newParent != null) { + this.view.invalidate(newParent, false, false); + } + + if (change.previous != null) { + this.view.invalidate(change.previous, false, false); + } + } + } + + // Handles two special cases where the shape does not need to be + // recreated from scratch, it only needs to be invalidated. + else if (change instanceof TerminalChange || change instanceof GeometryChange) { + // Checks if the geometry has changed to avoid unnessecary revalidation + if ( + change instanceof TerminalChange || + (change.previous == null && change.geometry != null) || + (change.previous != null && !change.previous.equals(change.geometry)) + ) { + this.view.invalidate(change.cell); + } + } + + // Handles two special cases where only the shape, but no + // descendants need to be recreated + else if (change instanceof ValueChange) { + this.view.invalidate(change.cell, false, false); + } + + // Requires a new mxShape in JavaScript + else if (change instanceof StyleChange) { + this.view.invalidate(change.cell, true, true); + const state = this.view.getState(change.cell); + + if (state != null) { + state.invalidStyle = true; + } + } + + // Removes the state from the cache by default + else if (change.cell != null && change.cell instanceof Cell) { + this.removeStateForCell(change.cell); + } + } + + /** + * Scrolls the graph to the given point, extending the graph container if + * specified. + */ + scrollPointToVisible(x: number, y: number, extend = false, border = 20) { + const panningHandler = this.getPlugin('PanningHandler'); + + if ( + !this.isTimerAutoScroll() && + (this.ignoreScrollbars || hasScrollbars(this.container)) + ) { + const c = this.container; + + if ( + x >= c.scrollLeft && + y >= c.scrollTop && + x <= c.scrollLeft + c.clientWidth && + y <= c.scrollTop + c.clientHeight + ) { + let dx = c.scrollLeft + c.clientWidth - x; + + if (dx < border) { + const old = c.scrollLeft; + c.scrollLeft += border - dx; + + // Automatically extends the canvas size to the bottom, right + // if the event is outside of the canvas and the edge of the + // canvas has been reached. Notes: Needs fix for IE. + if (extend && old === c.scrollLeft) { + // @ts-ignore + const root = this.view.getDrawPane().ownerSVGElement; + const width = c.scrollWidth + border - dx; + + // Updates the clipping region. This is an expensive + // operation that should not be executed too often. + // @ts-ignore + root.style.width = `${width}px`; + + c.scrollLeft += border - dx; + } + } else { + dx = x - c.scrollLeft; + + if (dx < border) { + c.scrollLeft -= border - dx; + } + } + + let dy = c.scrollTop + c.clientHeight - y; + + if (dy < border) { + const old = c.scrollTop; + c.scrollTop += border - dy; + + if (old == c.scrollTop && extend) { + // @ts-ignore + const root = this.view.getDrawPane().ownerSVGElement; + const height = c.scrollHeight + border - dy; + + // Updates the clipping region. This is an expensive + // operation that should not be executed too often. + // @ts-ignore + root.style.height = `${height}px`; + + c.scrollTop += border - dy; + } + } else { + dy = y - c.scrollTop; + + if (dy < border) { + c.scrollTop -= border - dy; + } + } + } + } else if ( + this.isAllowAutoPanning() && + panningHandler && + !panningHandler.isActive() + ) { + panningHandler.getPanningManager().panTo(x + this.getPanDx(), y + this.getPanDy()); + } + } + + /** + * Returns the size of the border and padding on all four sides of the + * container. The left, top, right and bottom borders are stored in the x, y, + * width and height of the returned {@link Rectangle}, respectively. + */ + getBorderSizes(): Rectangle { + const css = getCurrentStyle(this.container); + + return new Rectangle( + parseCssNumber(css.paddingLeft) + + (css.borderLeftStyle != 'none' ? parseCssNumber(css.borderLeftWidth) : 0), + parseCssNumber(css.paddingTop) + + (css.borderTopStyle != 'none' ? parseCssNumber(css.borderTopWidth) : 0), + parseCssNumber(css.paddingRight) + + (css.borderRightStyle != 'none' ? parseCssNumber(css.borderRightWidth) : 0), + parseCssNumber(css.paddingBottom) + + (css.borderBottomStyle != 'none' ? parseCssNumber(css.borderBottomWidth) : 0) + ); + } + + /** + * Returns the preferred size of the background page if {@link preferPageSize} is true. + */ + getPreferredPageSize(bounds: Rectangle, width: number, height: number) { + const tr = this.view.translate; + const fmt = this.pageFormat; + const ps = this.pageScale; + const page = new Rectangle( + 0, + 0, + Math.ceil(fmt.width * ps), + Math.ceil(fmt.height * ps) + ); + + const hCount = this.pageBreaksVisible ? Math.ceil(width / page.width) : 1; + const vCount = this.pageBreaksVisible ? Math.ceil(height / page.height) : 1; + + return new Rectangle( + 0, + 0, + hCount * page.width + 2 + tr.x, + vCount * page.height + 2 + tr.y + ); + } + + /** + * Scales the graph such that the complete diagram fits into {@link Graph.container} and returns the current scale in the view. + * To fit an initial graph prior to rendering, set {@link GraphView.rendering} to `false` prior to changing the model + * and execute the following after changing the model. + * + * ```javascript + * graph.view.rendering = false; + * // here, change the model + * graph.fit(); + * graph.view.rendering = true; + * graph.refresh(); + * ``` + * + * To fit and center the graph, use {@link FitPlugin.fitCenter}. + * + * @param border Optional number that specifies the border. Default is {@link border}. + * @param keepOrigin Optional boolean that specifies if the translate should be changed. Default is `false`. + * @param margin Optional margin in pixels. Default is `0`. + * @param enabled Optional boolean that specifies if the scale should be set or just returned. Default is `true`. + * @param ignoreWidth Optional boolean that specifies if the width should be ignored. Default is `false`. + * @param ignoreHeight Optional boolean that specifies if the height should be ignored. Default is `false`. + * @param maxHeight Optional maximum height. + */ + fit( + border: number = this.getBorder(), + keepOrigin = false, + margin = 0, + enabled = true, + ignoreWidth = false, + ignoreHeight = false, + maxHeight: number | null = null + ): number { + const { container, view } = this; + if (container) { + // Adds spacing and border from css + const cssBorder = this.getBorderSizes(); + let w1: number = container.offsetWidth - cssBorder.x - cssBorder.width - 1; + let h1: number = + maxHeight != null + ? maxHeight + : container.offsetHeight - cssBorder.y - cssBorder.height - 1; + let bounds = view.getGraphBounds(); + + if (bounds.width > 0 && bounds.height > 0) { + if (keepOrigin && bounds.x != null && bounds.y != null) { + bounds = bounds.clone(); + bounds.width += bounds.x; + bounds.height += bounds.y; + bounds.x = 0; + bounds.y = 0; + } + + // LATER: Use unscaled bounding boxes to fix rounding errors + const originalScale = view.scale; + let w2 = bounds.width / originalScale; + let h2 = bounds.height / originalScale; + + // Fits to the size of the background image if required + if (this.backgroundImage) { + w2 = Math.max(w2, this.backgroundImage.width - bounds.x / originalScale); + h2 = Math.max(h2, this.backgroundImage.height - bounds.y / originalScale); + } + + const b: number = (keepOrigin ? border : 2 * border) + margin + 1; + + w1 -= b; + h1 -= b; + + let newScale = ignoreWidth + ? h1 / h2 + : ignoreHeight + ? w1 / w2 + : Math.min(w1 / w2, h1 / h2); + + if (this.minFitScale != null) { + newScale = Math.max(newScale, this.minFitScale); + } + + if (this.maxFitScale != null) { + newScale = Math.min(newScale, this.maxFitScale); + } + + if (enabled) { + if (!keepOrigin) { + if (!hasScrollbars(container)) { + const x0 = + bounds.x != null + ? Math.floor( + view.translate.x - + bounds.x / originalScale + + border / newScale + + margin / 2 + ) + : border; + const y0 = + bounds.y != null + ? Math.floor( + view.translate.y - + bounds.y / originalScale + + border / newScale + + margin / 2 + ) + : border; + + view.scaleAndTranslate(newScale, x0, y0); + } else { + view.setScale(newScale); + const newBounds = this.getGraphBounds(); + + if (newBounds.x != null) { + container.scrollLeft = newBounds.x; + } + + if (newBounds.y != null) { + container.scrollTop = newBounds.y; + } + } + } else if (view.scale != newScale) { + view.setScale(newScale); + } + } else { + return newScale; + } + } + } + return view.scale; + } + + /** + * Resizes the container for the given graph width and height. + */ + doResizeContainer(width: number, height: number): void { + if (this.maximumContainerSize != null) { + width = Math.min(this.maximumContainerSize.width, width); + height = Math.min(this.maximumContainerSize.height, height); + } + const container = this.container; + container.style.width = `${Math.ceil(width)}px`; + container.style.height = `${Math.ceil(height)}px`; + } + + /***************************************************************************** + * Group: UNCLASSIFIED + *****************************************************************************/ + + /** + * Creates a new handler for the given cell state. This implementation + * returns a new {@link EdgeHandler} of the corresponding cell is an edge, + * otherwise it returns an {@link VertexHandler}. + * + * @param state {@link CellState} whose handler should be created. + */ + createHandler(state: CellState) { + let result: EdgeHandler | VertexHandler | null = null; + + if (state.cell.isEdge()) { + const source = state.getVisibleTerminalState(true); + const target = state.getVisibleTerminalState(false); + const geo = state.cell.getGeometry(); + + const edgeStyle = this.getView().getEdgeStyle( + state, + geo ? geo.points || undefined : undefined, + source, + target + ); + result = this.createEdgeHandler(state, edgeStyle); + } else { + result = this.createVertexHandler(state); + } + return result; + } + + /** + * Hooks to create a new {@link EdgeHandler} for the given {@link CellState}. + * + * @param state {@link CellState} to create the handler for. + * @param edgeStyle the {@link EdgeStyleFunction} that let choose the actual edge handler. + */ + createEdgeHandler(state: CellState, edgeStyle: EdgeStyleFunction | null): EdgeHandler { + let result = null; + if ( + edgeStyle == EdgeStyle.ElbowConnector || + edgeStyle == EdgeStyle.Loop || + edgeStyle == EdgeStyle.SideToSide || + edgeStyle == EdgeStyle.TopToBottom + ) { + result = this.createElbowEdgeHandler(state); + } else if ( + edgeStyle == EdgeStyle.ManhattanConnector || + edgeStyle == EdgeStyle.OrthConnector || + edgeStyle == EdgeStyle.SegmentConnector + ) { + result = this.createEdgeSegmentHandler(state); + } else { + result = this.createEdgeHandlerInstance(state); + } + + return result; + } + + /***************************************************************************** + * Group: Drilldown + *****************************************************************************/ + + /** + * Returns the current root of the displayed cell hierarchy. This is a + * shortcut to {@link GraphView.currentRoot} in {@link GraphView}. + */ + getCurrentRoot() { + return this.view.currentRoot; + } + + /** + * Returns the translation to be used if the given cell is the root cell as + * an {@link Point}. This implementation returns null. + * + * To keep the children at their absolute position while stepping into groups, + * this function can be overridden as follows. + * + * @example + * ```javascript + * var offset = new mxPoint(0, 0); + * + * while (cell != null) + * { + * var geo = this.model.getGeometry(cell); + * + * if (geo != null) + * { + * offset.x -= geo.x; + * offset.y -= geo.y; + * } + * + * cell = this.model.getParent(cell); + * } + * + * return offset; + * ``` + * + * @param cell {@link Cell} that represents the root. + */ + getTranslateForRoot(cell: Cell | null): Point | null { + return null; + } + + /** + * Returns the offset to be used for the cells inside the given cell. The + * root and layer cells may be identified using {@link GraphDataModel.isRoot} and + * {@link GraphDataModel.isLayer}. For all other current roots, the + * {@link GraphView.currentRoot} field points to the respective cell, so that + * the following holds: cell == this.view.currentRoot. This implementation + * returns null. + * + * @param cell {@link Cell} whose offset should be returned. + */ + getChildOffsetForCell(cell: Cell): Point | null { + return null; + } + + /** + * Uses the root of the model as the root of the displayed cell hierarchy + * and selects the previous root. + */ + home() { + const current = this.getCurrentRoot(); + + if (current != null) { + this.view.setCurrentRoot(null); + const state = this.view.getState(current); + + if (state != null) { + this.setSelectionCell(current); + } + } + } + + /** + * Returns true if the given cell is a valid root for the cell display + * hierarchy. This implementation returns true for all non-null values. + * + * @param cell {@link Cell} which should be checked as a possible root. + */ + isValidRoot(cell: Cell) { + return !!cell; + } + + /***************************************************************************** + * Group: Graph display + *****************************************************************************/ + + /** + * Returns the bounds of the visible graph. Shortcut to + * {@link GraphView.getGraphBounds}. See also: {@link getBoundingBoxFromGeometry}. + */ + getGraphBounds(): Rectangle { + return this.view.getGraphBounds(); + } + + /** + * Returns the bounds inside which the diagram should be kept as an + * {@link Rectangle}. + */ + getMaximumGraphBounds(): Rectangle | null { + return this.maximumGraphBounds; + } + + /** + * Clears all cell states or the states for the hierarchy starting at the + * given cell and validates the graph. This fires a refresh event as the + * last step. + * + * @param cell Optional {@link Cell} for which the cell states should be cleared. + */ + refresh(cell: Cell | null = null): void { + if (cell) { + this.view.clear(cell, false); + } else { + this.view.clear(undefined, true); + } + this.view.validate(); + this.sizeDidChange(); + this.fireEvent(new EventObject(InternalEvent.REFRESH)); + } + + /** + * Centers the graph in the container. + * + * @param horizontal Optional boolean that specifies if the graph should be centered + * horizontally. Default is `true`. + * @param vertical Optional boolean that specifies if the graph should be centered + * vertically. Default is `true`. + * @param cx Optional float that specifies the horizontal center. Default is `0.5`. + * @param cy Optional float that specifies the vertical center. Default is `0.5`. + */ + center(horizontal = true, vertical = true, cx = 0.5, cy = 0.5): void { + const container = this.container; + const _hasScrollbars = hasScrollbars(this.container); + const padding = 2 * this.getBorder(); + const cw = container.clientWidth - padding; + const ch = container.clientHeight - padding; + const bounds = this.getGraphBounds(); + + const t = this.view.translate; + const s = this.view.scale; + + let dx = horizontal ? cw - bounds.width : 0; + let dy = vertical ? ch - bounds.height : 0; + + if (!_hasScrollbars) { + this.view.setTranslate( + horizontal ? Math.floor(t.x - bounds.x / s + (dx * cx) / s) : t.x, + vertical ? Math.floor(t.y - bounds.y / s + (dy * cy) / s) : t.y + ); + } else { + bounds.x -= t.x; + bounds.y -= t.y; + + const sw = container.scrollWidth; + const sh = container.scrollHeight; + + if (sw > cw) { + dx = 0; + } + + if (sh > ch) { + dy = 0; + } + + this.view.setTranslate( + Math.floor(dx / 2 - bounds.x), + Math.floor(dy / 2 - bounds.y) + ); + container.scrollLeft = (sw - cw) / 2; + container.scrollTop = (sh - ch) / 2; + } + } + + /** + * Returns `true` if perimeter points should be computed such that the resulting edge has only horizontal or vertical segments. + * + * @param edge {@link CellState} that represents the edge. + */ + isOrthogonal(edge: CellState): boolean { + const orthogonal = edge.style.orthogonal; + if (!isNullish(orthogonal)) { + return orthogonal; + } + + // fallback when the orthogonal style is not defined + const edgeStyle = this.view.getEdgeStyle(edge); + + return [ + EdgeStyle.EntityRelation, + EdgeStyle.ElbowConnector, + EdgeStyle.ManhattanConnector, + EdgeStyle.OrthConnector, + EdgeStyle.SegmentConnector, + EdgeStyle.SideToSide, + EdgeStyle.TopToBottom, + ].includes(edgeStyle!); + } + + /***************************************************************************** + * Group: Graph appearance + *****************************************************************************/ + + /** + * Returns the {@link backgroundImage} as an {@link Image}. + */ + getBackgroundImage(): Image | null { + return this.backgroundImage; + } + + /** + * Sets the new {@link backgroundImage}. + * + * @param image New {@link Image} to be used for the background. + */ + setBackgroundImage(image: Image | null): void { + this.backgroundImage = image; + } + + /** + * Returns the textual representation for the given cell. + * + * This implementation returns the node name or string-representation of the user object. + * + * + * The following returns the label attribute from the cells user object if it is an XML node. + * + * @example + * ```javascript + * graph.convertValueToString = function(cell) + * { + * return cell.getAttribute('label'); + * } + * ``` + * + * See also: {@link cellLabelChanged}. + * + * @param cell {@link Cell} whose textual representation should be returned. + */ + convertValueToString(cell: Cell): string { + const value = cell.getValue(); + + if (value != null) { + if (isNode(value)) { + return value.nodeName; + } + if (typeof value.toString === 'function') { + return value.toString(); + } + } + return ''; + } + + /** + * Returns the string to be used as the link for the given cell. + * + * This implementation returns null. + * + * @param cell {@link Cell} whose link should be returned. + */ + getLinkForCell(cell: Cell): string | null { + return null; + } + + /** + * Returns the value of {@link border}. + */ + getBorder(): number { + return this.border; + } + + /** + * Sets the value of {@link border}. + * + * @param value Positive integer that represents the border to be used. + */ + setBorder(value: number): void { + this.border = value; + } + + /***************************************************************************** + * Group: Graph behaviour + *****************************************************************************/ + + /** + * Returns {@link resizeContainer}. + */ + isResizeContainer() { + return this.resizeContainer; + } + + /** + * Sets {@link resizeContainer}. + * + * @param value Boolean indicating if the container should be resized. + */ + setResizeContainer(value: boolean) { + this.resizeContainer = value; + } + + /** + * Returns true if the graph is {@link enabled}. + */ + isEnabled() { + return this.enabled; + } + + /** + * Specifies if the graph should allow any interactions. This + * implementation updates {@link enabled}. + * + * @param value Boolean indicating if the graph should be enabled. + */ + setEnabled(value: boolean) { + this.enabled = value; + } + + /** + * Returns {@link multigraph} as a boolean. + */ + isMultigraph() { + return this.multigraph; + } + + /** + * Specifies if the graph should allow multiple connections between the + * same pair of vertices. + * + * @param value Boolean indicating if the graph allows multiple connections + * between the same pair of vertices. + */ + setMultigraph(value: boolean) { + this.multigraph = value; + } + + /** + * Returns {@link allowLoops} as a boolean. + */ + isAllowLoops() { + return this.allowLoops; + } + + /** + * Specifies if loops are allowed. + * + * @param value Boolean indicating if loops are allowed. + */ + setAllowLoops(value: boolean) { + this.allowLoops = value; + } + + /** + * Returns {@link recursiveResize}. + * + * @param state {@link CellState} that is being resized. + */ + isRecursiveResize(state: CellState | null = null) { + return this.recursiveResize; + } + + /** + * Sets {@link recursiveResize}. + * + * @param value New boolean value for {@link recursiveResize}. + */ + setRecursiveResize(value: boolean) { + this.recursiveResize = 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 + * means all children must stay inside the parent, 1 means the child is + * allowed to be placed outside of the parent such that it touches one of + * the parents sides. If {@link isAllowOverlapParent} returns false for the given + * cell, then this method returns 0. + * + * @param cell {@link Cell} for which the overlap ratio should be returned. + */ + getOverlap(cell: Cell) { + return this.isAllowOverlapParent(cell) ? this.defaultOverlap : 0; + } + + /** + * Returns true if the given cell is allowed to be placed outside of the + * parents area. + * + * @param cell {@link Cell} that represents the child to be checked. + */ + isAllowOverlapParent(cell: Cell): boolean { + return false; + } + + /***************************************************************************** + * Group: Cell retrieval + *****************************************************************************/ + + /** + * Returns {@link defaultParent} or {@link GraphView.currentRoot} or the first child + * of {@link GraphDataModel.root} if both are null. The value returned by + * this function should be used as the parent for new cells (aka default + * layer). + */ + getDefaultParent() { + let parent = this.getCurrentRoot(); + + if (!parent) { + parent = this.defaultParent; + + if (!parent) { + const root = this.getDataModel().getRoot(); + parent = root.getChildAt(0); + } + } + + return parent; + } + + /** + * Sets the {@link defaultParent} to the given cell. Set this to null to return + * the first child of the root in getDefaultParent. + */ + setDefaultParent(cell: Cell | null) { + this.defaultParent = cell; + } + + /** + * Destroys the graph and all its resources. + */ + destroy() { + if (!this.destroyed) { + this.destroyed = true; + + Object.values(this.plugins).forEach((p) => p.onDestroy()); + + this.view.destroy(); + + if (this.model && this.graphModelChangeListener) { + this.getDataModel().removeListener(this.graphModelChangeListener); + this.graphModelChangeListener = null; + } + } + } +} + +// This introduces a side effect, but it is necessary to ensure the Graph is enriched with all properties and methods defined in mixins. +// It is only called when Graph is imported, so the Graph definition is always consistent. +// And this doesn't impact the tree-shaking. +applyGraphMixins(AbstractGraph); diff --git a/packages/core/src/view/BaseGraph.ts b/packages/core/src/view/BaseGraph.ts new file mode 100644 index 0000000000..9a41c68c0e --- /dev/null +++ b/packages/core/src/view/BaseGraph.ts @@ -0,0 +1,31 @@ +/* +Copyright 2025-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 { AbstractGraph, CollaboratorsGraphOptions } from './AbstractGraph'; +import GraphDataModel from './GraphDataModel'; +import CellRenderer from './cell/CellRenderer'; +import { Stylesheet } from './style/Stylesheet'; +import GraphView from './GraphView'; + +// TODO JSDoc of BaseGraph +export class BaseGraph extends AbstractGraph { + protected override initializeCollaborators(options: CollaboratorsGraphOptions): void { + this.model = options.model ?? new GraphDataModel(); + this.cellRenderer = options.cellRenderer ?? new CellRenderer(); + this.setStylesheet(options.stylesheet ?? new Stylesheet()); + this.view = options.view?.(this) ?? new GraphView(this); + } +} diff --git a/packages/core/src/view/Graph.ts b/packages/core/src/view/Graph.ts index dea19688b0..b7816c9ee4 100644 --- a/packages/core/src/view/Graph.ts +++ b/packages/core/src/view/Graph.ts @@ -1,7 +1,5 @@ /* -Copyright 2021-present The maxGraph project Contributors -Copyright (c) 2006-2015, JGraph Ltd -Copyright (c) 2006-2015, Gaudenz Alder +Copyright 2025-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. @@ -16,400 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -import Image from './image/ImageBox'; -import EventObject from './event/EventObject'; -import EventSource from './event/EventSource'; -import InternalEvent from './event/InternalEvent'; -import Rectangle from './geometry/Rectangle'; -import Client from '../Client'; -import type PanningHandler from './plugins/PanningHandler'; -import GraphView from './GraphView'; +import type { GraphPluginConstructor } from '../types'; import CellRenderer from './cell/CellRenderer'; -import Point from './geometry/Point'; -import { getCurrentStyle, hasScrollbars, parseCssNumber } from '../util/styleUtils'; -import Cell from './cell/Cell'; import GraphDataModel from './GraphDataModel'; +import GraphView from './GraphView'; import { Stylesheet } from './style/Stylesheet'; -import { PAGE_FORMAT_A4_PORTRAIT } from '../util/Constants'; - -import ChildChange from './undoable_changes/ChildChange'; -import GeometryChange from './undoable_changes/GeometryChange'; -import RootChange from './undoable_changes/RootChange'; -import StyleChange from './undoable_changes/StyleChange'; -import TerminalChange from './undoable_changes/TerminalChange'; -import ValueChange from './undoable_changes/ValueChange'; -import CellState from './cell/CellState'; -import { isNode } from '../util/domUtils'; -import EdgeStyle from './style/EdgeStyle'; -import EdgeHandler from './handler/EdgeHandler'; -import VertexHandler from './handler/VertexHandler'; -import EdgeSegmentHandler from './handler/EdgeSegmentHandler'; -import ElbowEdgeHandler from './handler/ElbowEdgeHandler'; -import type { - EdgeStyleFunction, - GraphFoldingOptions, - GraphPlugin, - GraphPluginConstructor, - MouseListenerSet, -} from '../types'; -import Multiplicity from './other/Multiplicity'; -import ImageBundle from './image/ImageBundle'; -import GraphSelectionModel from './GraphSelectionModel'; import { registerDefaultShapes } from './cell/register-shapes'; import { registerDefaultEdgeMarkers } from './geometry/edge/MarkerShape'; import { registerDefaultStyleElements } from './style/register'; -import { applyGraphMixins } from './mixins/_graph-mixins-apply'; import { getDefaultPlugins } from './plugins'; -import { isNullish } from '../internal/utils'; -import { isI18nEnabled } from '../internal/i18n-utils'; +import { AbstractGraph, CollaboratorsGraphOptions } from './AbstractGraph'; +// TODO review the JSDoc for the description of the class /** - * 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}. + * Extends {@link AbstractGraph} to implement a graph component that automatically loads some default built-ins (plugins, style elements). * - * 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 - * - selectionHandler: {@link SelectionHandler} for moving and cloning cells - * - * These listeners will be called in the above order if they are enabled. + * Good for evaluation and prototyping, but not recommended for production use. Use {@link BaseGraph} instead. */ -class Graph extends EventSource { - container: HTMLElement; - - destroyed = false; - - graphModelChangeListener: Function | null = null; - paintBackground: Function | null = null; - isConstrainedMoving = false; - - // =================================================================================================================== - // Group: Variables (that maybe should be in the mixins, but need to be created for each new class instance) - // =================================================================================================================== - - cells: Cell[] = []; - - imageBundles: ImageBundle[] = []; - - /** - * Holds the mouse event listeners. See {@link fireMouseEvent}. - */ - mouseListeners: MouseListenerSet[] = []; - - /** - * An array of {@link Multiplicity} describing the allowed connections in a graph. - */ - multiplicities: Multiplicity[] = []; - - /** - * Holds the {@link GraphDataModel} that contains the cells to be displayed. - */ - model: GraphDataModel; - - private plugins: Record = {}; - - /** - * Holds the {@link GraphView} that caches the {@link CellState}s for the cells. - */ - view: GraphView; - - /** - * Holds the {@link Stylesheet} that defines the appearance of the cells. - * - * Use the following code to read a stylesheet into an existing graph. - * - * @example - * ```javascript - * var req = mxUtils.load('stylesheet.xml'); - * var root = req.getDocumentElement(); - * var dec = new Codec(root.ownerDocument); - * dec.decode(root, graph.stylesheet); - * ``` - */ - // @ts-ignore - stylesheet: Stylesheet; - - /** - * Holds the {@link CellRenderer} for rendering the cells in the graph. - */ - cellRenderer: CellRenderer; - - /** - * RenderHint as it was passed to the constructor. - */ - renderHint: string | null = null; - - /** - * Dialect to be used for drawing the graph. Possible values are all constants in {@link DIALECT}. - */ - dialect: 'svg' | 'mixedHtml' | 'preferHtml' | 'strictHtml' = 'svg'; - - /** - * Value returned by {@link getOverlap} if {@link isAllowOverlapParent} returns - * `true` for the given cell. {@link getOverlap} is used in {@link constrainChild} if - * {@link isConstrainChild} returns `true`. The value specifies the - * portion of the child which is allowed to overlap the parent. - */ - defaultOverlap = 0.5; - - /** - * Specifies the default parent to be used to insert new cells. - * This is used in {@link getDefaultParent}. - * @default null - */ - defaultParent: Cell | null = null; - - /** - * Specifies the {@link Image} to be returned by {@link getBackgroundImage}. - * @default null - * - * @example - * ```javascript - * var img = new mxImage('http://www.example.com/maps/examplemap.jpg', 1024, 768); - * graph.setBackgroundImage(img); - * graph.view.validate(); - * ``` - */ - backgroundImage: Image | null = null; - - /** - * Specifies if the background page should be visible. - * Not yet implemented. - * @default false - */ - pageVisible = false; - - /** - * Specifies if a dashed line should be drawn between multiple pages. - * If you change this value while a graph is being displayed then you - * should call {@link sizeDidChange} to force an update of the display. - * @default false - */ - pageBreaksVisible = false; - - /** - * Specifies the color for page breaks. - * @default gray - */ - pageBreakColor = 'gray'; - - /** - * Specifies the page breaks should be dashed. - * @default true - */ - pageBreakDashed = true; - - /** - * Specifies the minimum distance in pixels for page breaks to be visible. - * @default 20 - */ - minPageBreakDist = 20; - - /** - * 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 = false; - - /** - * Specifies the page format for the background page. - * This is used as the default in {@link PrintPreview} and for painting the background page - * if {@link pageVisible} is `true` and the page breaks if {@link pageBreaksVisible} is `true`. - * @default {@link mxConstants.PAGE_FORMAT_A4_PORTRAIT} - */ - pageFormat = new Rectangle(...PAGE_FORMAT_A4_PORTRAIT); - - /** - * Specifies the scale of the background page. - * Not yet implemented. - * @default 1.5 - */ - pageScale = 1.5; - - /** - * Specifies the return value for {@link isEnabled}. - * @default true - */ - enabled = true; - - /** - * Specifies the return value for {@link canExportCell}. - * @default true - */ - exportEnabled = true; - - /** - * Specifies the return value for {@link canImportCell}. - * @default true - */ - importEnabled = true; - - /** - * Specifies if the graph should automatically scroll regardless of the - * scrollbars. This will scroll the container using positive values for - * scroll positions (ie usually only rightwards and downwards). To avoid - * possible conflicts with panning, set {@link translateToScrollPosition} to `true`. - */ - ignoreScrollbars = false; - - /** - * Specifies if the graph should automatically convert the current scroll - * position to a translate in the graph view when a mouseUp event is received. - * This can be used to avoid conflicts when using {@link autoScroll} and - * {@link ignoreScrollbars} with no scrollbars in the container. - */ - translateToScrollPosition = false; - - /** - * {@link Rectangle} that specifies the area in which all cells in the diagram - * should be placed. Uses in {@link getMaximumGraphBounds}. Use a width or height of - * `0` if you only want to give a upper, left corner. - */ - maximumGraphBounds: Rectangle | null = null; - - /** - * {@link Rectangle} that specifies the minimum size of the graph. This is ignored - * if the graph container has no scrollbars. - * @default null - */ - minimumGraphSize: Rectangle | null = null; - - /** - * {@link Rectangle} that specifies the minimum size of the {@link container} if - * {@link resizeContainer} is `true`. - */ - minimumContainerSize: Rectangle | null = null; - - /** - * {@link Rectangle} that specifies the maximum size of the container if - * {@link resizeContainer} is `true`. - */ - maximumContainerSize: Rectangle | null = null; - - /** - * Specifies if the container should be resized to the graph size when - * the graph size has changed. - * @default false - */ - resizeContainer = false; - - /** - * Border to be added to the bottom and right side when the container is - * being resized after the graph has been changed. - * @default 0 - */ - border = 0; - - /** - * Specifies if edges should appear in the foreground regardless of their order - * in the model. If {@link keepEdgesInForeground} and {@link keepEdgesInBackground} are - * both `true` then the normal order is applied. - * @default false - */ - keepEdgesInForeground = false; - - /** - * Specifies if edges should appear in the background regardless of their order - * in the model. If {@link keepEdgesInForeground} and {@link keepEdgesInBackground} are - * both `true` then the normal order is applied. - * @default false - */ - keepEdgesInBackground = false; - - /** - * Specifies the return value for {@link isRecursiveResize}. - * @default false (for backwards compatibility) - */ - recursiveResize = false; - - /** - * Specifies if the scale and translate should be reset if the root changes in - * the model. - * @default true - */ - resetViewOnRootChange = true; - - /** - * Specifies if loops (aka self-references) are allowed. - * @default false - */ - allowLoops = false; - - /** - * {@link EdgeStyle} to be used for loops. This is a fallback for loops if the - * {@link CellStateStyle.loopStyle} is `undefined`. - * @default {@link EdgeStyle.Loop} - */ - defaultLoopStyle = EdgeStyle.Loop; - - /** - * Specifies if multiple edges in the same direction between the same pair of - * vertices are allowed. - * @default true - */ - multigraph = true; - - /** - * Specifies the minimum scale to be applied in {@link fit}. Set this to `null` to allow any value. - * @default 0.1 - */ - minFitScale: number | null = 0.1; - - /** - * Specifies the maximum scale to be applied in {@link fit}. Set this to `null` to allow any value. - * @default 8 - */ - maxFitScale: number | null = 8; - - /** - * Specifies the {@link Image} for the image to be used to display a warning - * overlay. See {@link setCellWarning}. Default value is Client.imageBasePath + - * '/warning'. The extension for the image depends on the platform. It is - * '.png' on the Mac and '.gif' on all other platforms. - */ - warningImage: Image = new Image( - `${Client.imageBasePath}/warning${Client.IS_MAC ? '.png' : '.gif'}`, - 16, - 16 - ); - - /** - * Specifies the resource key for the error message to be displayed in - * non-multigraphs when two vertices are already connected. If the resource - * for this key does not exist then the value is used as the error message. - * @default 'alreadyConnected' - */ - alreadyConnectedResource: string = isI18nEnabled() ? 'alreadyConnected' : ''; - - /** - * Specifies the resource key for the warning message to be displayed when - * a collapsed cell contains validation errors. If the resource for this - * key does not exist then the value is used as the warning message. - * @default 'containsValidationErrors' - */ - containsValidationErrorsResource: string = isI18nEnabled() - ? 'containsValidationErrors' - : ''; - - /** Folding options. */ - options: GraphFoldingOptions = { - foldingEnabled: true, - collapsedImage: new Image(`${Client.imageBasePath}/collapsed.gif`, 9, 9), - expandedImage: new Image(`${Client.imageBasePath}/expanded.gif`, 9, 9), - collapseToPreferredSize: true, - }; - - // =================================================================================================================== - // Group: "Create Class Instance" factory functions. - // These can be overridden in subclasses of Graph to allow the Graph to instantiate user-defined implementations with - // custom behavior. - // =================================================================================================================== - +export class Graph extends AbstractGraph { /** * Creates a new {@link CellRenderer} to be used in this graph. */ @@ -417,35 +39,6 @@ class Graph extends EventSource { return new CellRenderer(); } - /** - * Hooks to create a new {@link EdgeHandler} for the given {@link CellState}. - * - * @param state {@link CellState} to create the handler for. - */ - createEdgeHandlerInstance(state: CellState): EdgeHandler { - // Note this method not being called createEdgeHandler to keep compatibility - // with older code which overrides/calls createEdgeHandler - return new EdgeHandler(state); - } - - /** - * Hooks to create a new {@link EdgeSegmentHandler} for the given {@link CellState}. - * - * @param state {@link CellState} to create the handler for. - */ - createEdgeSegmentHandler(state: CellState) { - return new EdgeSegmentHandler(state); - } - - /** - * Hooks to create a new {@link ElbowEdgeHandler} for the given {@link CellState}. - * - * @param state {@link CellState} to create the handler for. - */ - createElbowEdgeHandler(state: CellState) { - return new ElbowEdgeHandler(state); - } - /** * Creates a new {@link GraphDataModel} to be used in this graph. */ @@ -460,13 +53,6 @@ class Graph extends EventSource { return new GraphView(this); } - /** - * Creates a new {@link GraphSelectionModel} to be used in this graph. - */ - createSelectionModel() { - return new GraphSelectionModel(this); - } - /** * Creates a new {@link Stylesheet} to be used in this graph. */ @@ -474,1021 +60,25 @@ class Graph extends EventSource { return new Stylesheet(); } - /** - * Hooks to create a new {@link VertexHandler} for the given {@link CellState}. - * - * @param state {@link CellState} to create the handler for. - */ - createVertexHandler(state: CellState): VertexHandler { - return new VertexHandler(state); - } - - // =================================================================================================================== - // Group: Main graph constructor and functions - // =================================================================================================================== - - protected registerDefaults(): void { + protected override registerDefaults(): void { registerDefaultShapes(); registerDefaultStyleElements(); registerDefaultEdgeMarkers(); } + protected override initializeCollaborators(options: CollaboratorsGraphOptions): void { + this.model = options.model ?? this.createGraphDataModel(); + this.cellRenderer = this.createCellRenderer(); + this.setStylesheet(options.stylesheet ?? this.createStylesheet()); + this.view = this.createGraphView(); + } + constructor( container: HTMLElement, model?: GraphDataModel, plugins: GraphPluginConstructor[] = getDefaultPlugins(), stylesheet: Stylesheet | null = null ) { - super(); - this.registerDefaults(); - - this.container = container ?? document.createElement('div'); - this.model = model ?? this.createGraphDataModel(); - this.cellRenderer = this.createCellRenderer(); - this.setStylesheet(stylesheet ?? this.createStylesheet()); - this.view = this.createGraphView(); - - // Adds a graph model listener to update the view - this.graphModelChangeListener = (sender: any, evt: EventObject) => { - this.graphModelChanged(evt.getProperty('edit').changes); - }; - this.getDataModel().addListener(InternalEvent.CHANGE, this.graphModelChangeListener); - - // Initializes the container using the view - this.view.init(); - - // Updates the size of the container for the current graph - this.sizeDidChange(); - - // Set the selection model - this.setSelectionModel(this.createSelectionModel()); - - // Initializes plugins - plugins.forEach((p) => (this.plugins[p.pluginId] = new p(this))); - - this.view.revalidate(); - } - - getContainer = () => this.container; - getPlugin = (id: string): T => this.plugins[id] as T; - getCellRenderer = () => this.cellRenderer; - getDialect = () => this.dialect; - isPageVisible = () => this.pageVisible; - isPageBreaksVisible = () => this.pageBreaksVisible; - getPageBreakColor = () => this.pageBreakColor; - isPageBreakDashed = () => this.pageBreakDashed; - getMinPageBreakDist = () => this.minPageBreakDist; - isPreferPageSize = () => this.preferPageSize; - getPageFormat = () => this.pageFormat; - getPageScale = () => this.pageScale; - isExportEnabled = () => this.exportEnabled; - isImportEnabled = () => this.importEnabled; - isIgnoreScrollbars = () => this.ignoreScrollbars; - isTranslateToScrollPosition = () => this.translateToScrollPosition; - - getMinimumGraphSize = () => this.minimumGraphSize; - setMinimumGraphSize = (size: Rectangle | null) => (this.minimumGraphSize = size); - - getMinimumContainerSize = () => this.minimumContainerSize; - setMinimumContainerSize = (size: Rectangle | null) => - (this.minimumContainerSize = size); - - getWarningImage() { - return this.warningImage; - } - - getAlreadyConnectedResource = () => this.alreadyConnectedResource; - - getContainsValidationErrorsResource = () => this.containsValidationErrorsResource; - - /** - * Updates the model in a transaction. - * - * @param fn the update to be performed in the transaction. - * - * @see {@link GraphDataModel.batchUpdate} - */ - batchUpdate(fn: () => void) { - this.getDataModel().batchUpdate(fn); - } - - /** - * Returns the {@link GraphDataModel} that contains the cells. - */ - getDataModel() { - return this.model; - } - - /** - * Returns the {@link GraphView} that contains the {@link mxCellStates}. - */ - getView() { - return this.view; - } - - /** - * Returns the {@link Stylesheet} that defines the style. - */ - getStylesheet() { - return this.stylesheet; - } - - /** - * Sets the {@link Stylesheet} that defines the style. - */ - setStylesheet(stylesheet: Stylesheet) { - this.stylesheet = stylesheet; - } - - /** - * Called when the graph model changes. Invokes {@link processChange} on each - * item of the given array to update the view accordingly. - * - * @param changes Array that contains the individual changes. - */ - graphModelChanged(changes: any[]) { - for (const change of changes) { - this.processChange(change); - } - - this.updateSelection(); - this.view.validate(); - this.sizeDidChange(); - } - - /** - * Processes the given change and invalidates the respective cached data - * in {@link GraphView}. This fires a {@link root} event if the root has changed in the - * model. - * - * @param {(RootChange|ChildChange|TerminalChange|GeometryChange|ValueChange|StyleChange)} change - Object that represents the change on the model. - */ - processChange(change: any): void { - // Resets the view settings, removes all cells and clears - // the selection if the root changes. - if (change instanceof RootChange) { - this.clearSelection(); - this.setDefaultParent(null); - - if (change.previous) this.removeStateForCell(change.previous); - - if (this.resetViewOnRootChange) { - this.view.scale = 1; - this.view.translate.x = 0; - this.view.translate.y = 0; - } - - this.fireEvent(new EventObject(InternalEvent.ROOT)); - } - - // Adds or removes a child to the view by online invaliding - // the minimal required portions of the cache, namely, the - // old and new parent and the child. - else if (change instanceof ChildChange) { - const newParent = change.child.getParent(); - this.view.invalidate(change.child, true, true); - - if ( - !newParent || - !this.getDataModel().contains(newParent) || - newParent.isCollapsed() - ) { - this.view.invalidate(change.child, true, true); - this.removeStateForCell(change.child); - - // Handles special case of current root of view being removed - if (this.view.currentRoot == change.child) { - this.home(); - } - } - - if (newParent != change.previous) { - // Refreshes the collapse/expand icons on the parents - if (newParent != null) { - this.view.invalidate(newParent, false, false); - } - - if (change.previous != null) { - this.view.invalidate(change.previous, false, false); - } - } - } - - // Handles two special cases where the shape does not need to be - // recreated from scratch, it only needs to be invalidated. - else if (change instanceof TerminalChange || change instanceof GeometryChange) { - // Checks if the geometry has changed to avoid unnessecary revalidation - if ( - change instanceof TerminalChange || - (change.previous == null && change.geometry != null) || - (change.previous != null && !change.previous.equals(change.geometry)) - ) { - this.view.invalidate(change.cell); - } - } - - // Handles two special cases where only the shape, but no - // descendants need to be recreated - else if (change instanceof ValueChange) { - this.view.invalidate(change.cell, false, false); - } - - // Requires a new mxShape in JavaScript - else if (change instanceof StyleChange) { - this.view.invalidate(change.cell, true, true); - const state = this.view.getState(change.cell); - - if (state != null) { - state.invalidStyle = true; - } - } - - // Removes the state from the cache by default - else if (change.cell != null && change.cell instanceof Cell) { - this.removeStateForCell(change.cell); - } - } - - /** - * Scrolls the graph to the given point, extending the graph container if - * specified. - */ - scrollPointToVisible(x: number, y: number, extend = false, border = 20) { - const panningHandler = this.getPlugin('PanningHandler'); - - if ( - !this.isTimerAutoScroll() && - (this.ignoreScrollbars || hasScrollbars(this.container)) - ) { - const c = this.container; - - if ( - x >= c.scrollLeft && - y >= c.scrollTop && - x <= c.scrollLeft + c.clientWidth && - y <= c.scrollTop + c.clientHeight - ) { - let dx = c.scrollLeft + c.clientWidth - x; - - if (dx < border) { - const old = c.scrollLeft; - c.scrollLeft += border - dx; - - // Automatically extends the canvas size to the bottom, right - // if the event is outside of the canvas and the edge of the - // canvas has been reached. Notes: Needs fix for IE. - if (extend && old === c.scrollLeft) { - // @ts-ignore - const root = this.view.getDrawPane().ownerSVGElement; - const width = c.scrollWidth + border - dx; - - // Updates the clipping region. This is an expensive - // operation that should not be executed too often. - // @ts-ignore - root.style.width = `${width}px`; - - c.scrollLeft += border - dx; - } - } else { - dx = x - c.scrollLeft; - - if (dx < border) { - c.scrollLeft -= border - dx; - } - } - - let dy = c.scrollTop + c.clientHeight - y; - - if (dy < border) { - const old = c.scrollTop; - c.scrollTop += border - dy; - - if (old == c.scrollTop && extend) { - // @ts-ignore - const root = this.view.getDrawPane().ownerSVGElement; - const height = c.scrollHeight + border - dy; - - // Updates the clipping region. This is an expensive - // operation that should not be executed too often. - // @ts-ignore - root.style.height = `${height}px`; - - c.scrollTop += border - dy; - } - } else { - dy = y - c.scrollTop; - - if (dy < border) { - c.scrollTop -= border - dy; - } - } - } - } else if ( - this.isAllowAutoPanning() && - panningHandler && - !panningHandler.isActive() - ) { - panningHandler.getPanningManager().panTo(x + this.getPanDx(), y + this.getPanDy()); - } - } - - /** - * Returns the size of the border and padding on all four sides of the - * container. The left, top, right and bottom borders are stored in the x, y, - * width and height of the returned {@link Rectangle}, respectively. - */ - getBorderSizes(): Rectangle { - const css = getCurrentStyle(this.container); - - return new Rectangle( - parseCssNumber(css.paddingLeft) + - (css.borderLeftStyle != 'none' ? parseCssNumber(css.borderLeftWidth) : 0), - parseCssNumber(css.paddingTop) + - (css.borderTopStyle != 'none' ? parseCssNumber(css.borderTopWidth) : 0), - parseCssNumber(css.paddingRight) + - (css.borderRightStyle != 'none' ? parseCssNumber(css.borderRightWidth) : 0), - parseCssNumber(css.paddingBottom) + - (css.borderBottomStyle != 'none' ? parseCssNumber(css.borderBottomWidth) : 0) - ); - } - - /** - * Returns the preferred size of the background page if {@link preferPageSize} is true. - */ - getPreferredPageSize(bounds: Rectangle, width: number, height: number) { - const tr = this.view.translate; - const fmt = this.pageFormat; - const ps = this.pageScale; - const page = new Rectangle( - 0, - 0, - Math.ceil(fmt.width * ps), - Math.ceil(fmt.height * ps) - ); - - const hCount = this.pageBreaksVisible ? Math.ceil(width / page.width) : 1; - const vCount = this.pageBreaksVisible ? Math.ceil(height / page.height) : 1; - - return new Rectangle( - 0, - 0, - hCount * page.width + 2 + tr.x, - vCount * page.height + 2 + tr.y - ); - } - - /** - * Scales the graph such that the complete diagram fits into {@link Graph.container} and returns the current scale in the view. - * To fit an initial graph prior to rendering, set {@link GraphView.rendering} to `false` prior to changing the model - * and execute the following after changing the model. - * - * ```javascript - * graph.view.rendering = false; - * // here, change the model - * graph.fit(); - * graph.view.rendering = true; - * graph.refresh(); - * ``` - * - * To fit and center the graph, use {@link FitPlugin.fitCenter}. - * - * @param border Optional number that specifies the border. Default is {@link border}. - * @param keepOrigin Optional boolean that specifies if the translate should be changed. Default is `false`. - * @param margin Optional margin in pixels. Default is `0`. - * @param enabled Optional boolean that specifies if the scale should be set or just returned. Default is `true`. - * @param ignoreWidth Optional boolean that specifies if the width should be ignored. Default is `false`. - * @param ignoreHeight Optional boolean that specifies if the height should be ignored. Default is `false`. - * @param maxHeight Optional maximum height. - */ - fit( - border: number = this.getBorder(), - keepOrigin = false, - margin = 0, - enabled = true, - ignoreWidth = false, - ignoreHeight = false, - maxHeight: number | null = null - ): number { - const { container, view } = this; - if (container) { - // Adds spacing and border from css - const cssBorder = this.getBorderSizes(); - let w1: number = container.offsetWidth - cssBorder.x - cssBorder.width - 1; - let h1: number = - maxHeight != null - ? maxHeight - : container.offsetHeight - cssBorder.y - cssBorder.height - 1; - let bounds = view.getGraphBounds(); - - if (bounds.width > 0 && bounds.height > 0) { - if (keepOrigin && bounds.x != null && bounds.y != null) { - bounds = bounds.clone(); - bounds.width += bounds.x; - bounds.height += bounds.y; - bounds.x = 0; - bounds.y = 0; - } - - // LATER: Use unscaled bounding boxes to fix rounding errors - const originalScale = view.scale; - let w2 = bounds.width / originalScale; - let h2 = bounds.height / originalScale; - - // Fits to the size of the background image if required - if (this.backgroundImage) { - w2 = Math.max(w2, this.backgroundImage.width - bounds.x / originalScale); - h2 = Math.max(h2, this.backgroundImage.height - bounds.y / originalScale); - } - - const b: number = (keepOrigin ? border : 2 * border) + margin + 1; - - w1 -= b; - h1 -= b; - - let newScale = ignoreWidth - ? h1 / h2 - : ignoreHeight - ? w1 / w2 - : Math.min(w1 / w2, h1 / h2); - - if (this.minFitScale != null) { - newScale = Math.max(newScale, this.minFitScale); - } - - if (this.maxFitScale != null) { - newScale = Math.min(newScale, this.maxFitScale); - } - - if (enabled) { - if (!keepOrigin) { - if (!hasScrollbars(container)) { - const x0 = - bounds.x != null - ? Math.floor( - view.translate.x - - bounds.x / originalScale + - border / newScale + - margin / 2 - ) - : border; - const y0 = - bounds.y != null - ? Math.floor( - view.translate.y - - bounds.y / originalScale + - border / newScale + - margin / 2 - ) - : border; - - view.scaleAndTranslate(newScale, x0, y0); - } else { - view.setScale(newScale); - const newBounds = this.getGraphBounds(); - - if (newBounds.x != null) { - container.scrollLeft = newBounds.x; - } - - if (newBounds.y != null) { - container.scrollTop = newBounds.y; - } - } - } else if (view.scale != newScale) { - view.setScale(newScale); - } - } else { - return newScale; - } - } - } - return view.scale; - } - - /** - * Resizes the container for the given graph width and height. - */ - doResizeContainer(width: number, height: number): void { - if (this.maximumContainerSize != null) { - width = Math.min(this.maximumContainerSize.width, width); - height = Math.min(this.maximumContainerSize.height, height); - } - const container = this.container; - container.style.width = `${Math.ceil(width)}px`; - container.style.height = `${Math.ceil(height)}px`; - } - - /***************************************************************************** - * Group: UNCLASSIFIED - *****************************************************************************/ - - /** - * Creates a new handler for the given cell state. This implementation - * returns a new {@link EdgeHandler} of the corresponding cell is an edge, - * otherwise it returns an {@link VertexHandler}. - * - * @param state {@link CellState} whose handler should be created. - */ - createHandler(state: CellState) { - let result: EdgeHandler | VertexHandler | null = null; - - if (state.cell.isEdge()) { - const source = state.getVisibleTerminalState(true); - const target = state.getVisibleTerminalState(false); - const geo = state.cell.getGeometry(); - - const edgeStyle = this.getView().getEdgeStyle( - state, - geo ? geo.points || undefined : undefined, - source, - target - ); - result = this.createEdgeHandler(state, edgeStyle); - } else { - result = this.createVertexHandler(state); - } - return result; - } - - /** - * Hooks to create a new {@link EdgeHandler} for the given {@link CellState}. - * - * @param state {@link CellState} to create the handler for. - * @param edgeStyle the {@link EdgeStyleFunction} that let choose the actual edge handler. - */ - createEdgeHandler(state: CellState, edgeStyle: EdgeStyleFunction | null): EdgeHandler { - let result = null; - if ( - edgeStyle == EdgeStyle.ElbowConnector || - edgeStyle == EdgeStyle.Loop || - edgeStyle == EdgeStyle.SideToSide || - edgeStyle == EdgeStyle.TopToBottom - ) { - result = this.createElbowEdgeHandler(state); - } else if ( - edgeStyle == EdgeStyle.ManhattanConnector || - edgeStyle == EdgeStyle.OrthConnector || - edgeStyle == EdgeStyle.SegmentConnector - ) { - result = this.createEdgeSegmentHandler(state); - } else { - result = this.createEdgeHandlerInstance(state); - } - - return result; - } - - /***************************************************************************** - * Group: Drilldown - *****************************************************************************/ - - /** - * Returns the current root of the displayed cell hierarchy. This is a - * shortcut to {@link GraphView.currentRoot} in {@link GraphView}. - */ - getCurrentRoot() { - return this.view.currentRoot; - } - - /** - * Returns the translation to be used if the given cell is the root cell as - * an {@link Point}. This implementation returns null. - * - * To keep the children at their absolute position while stepping into groups, - * this function can be overridden as follows. - * - * @example - * ```javascript - * var offset = new mxPoint(0, 0); - * - * while (cell != null) - * { - * var geo = this.model.getGeometry(cell); - * - * if (geo != null) - * { - * offset.x -= geo.x; - * offset.y -= geo.y; - * } - * - * cell = this.model.getParent(cell); - * } - * - * return offset; - * ``` - * - * @param cell {@link mxCell} that represents the root. - */ - getTranslateForRoot(cell: Cell | null): Point | null { - return null; - } - - /** - * Returns the offset to be used for the cells inside the given cell. The - * root and layer cells may be identified using {@link GraphDataModel.isRoot} and - * {@link GraphDataModel.isLayer}. For all other current roots, the - * {@link GraphView.currentRoot} field points to the respective cell, so that - * the following holds: cell == this.view.currentRoot. This implementation - * returns null. - * - * @param cell {@link mxCell} whose offset should be returned. - */ - getChildOffsetForCell(cell: Cell): Point | null { - return null; - } - - /** - * Uses the root of the model as the root of the displayed cell hierarchy - * and selects the previous root. - */ - home() { - const current = this.getCurrentRoot(); - - if (current != null) { - this.view.setCurrentRoot(null); - const state = this.view.getState(current); - - if (state != null) { - this.setSelectionCell(current); - } - } - } - - /** - * Returns true if the given cell is a valid root for the cell display - * hierarchy. This implementation returns true for all non-null values. - * - * @param cell {@link mxCell} which should be checked as a possible root. - */ - isValidRoot(cell: Cell) { - return !!cell; - } - - /***************************************************************************** - * Group: Graph display - *****************************************************************************/ - - /** - * Returns the bounds of the visible graph. Shortcut to - * {@link GraphView.getGraphBounds}. See also: {@link getBoundingBoxFromGeometry}. - */ - getGraphBounds(): Rectangle { - return this.view.getGraphBounds(); - } - - /** - * Returns the bounds inside which the diagram should be kept as an - * {@link Rectangle}. - */ - getMaximumGraphBounds(): Rectangle | null { - return this.maximumGraphBounds; - } - - /** - * Clears all cell states or the states for the hierarchy starting at the - * given cell and validates the graph. This fires a refresh event as the - * last step. - * - * @param cell Optional {@link Cell} for which the cell states should be cleared. - */ - refresh(cell: Cell | null = null): void { - if (cell) { - this.view.clear(cell, false); - } else { - this.view.clear(undefined, true); - } - this.view.validate(); - this.sizeDidChange(); - this.fireEvent(new EventObject(InternalEvent.REFRESH)); - } - - /** - * Centers the graph in the container. - * - * @param horizontal Optional boolean that specifies if the graph should be centered - * horizontally. Default is `true`. - * @param vertical Optional boolean that specifies if the graph should be centered - * vertically. Default is `true`. - * @param cx Optional float that specifies the horizontal center. Default is `0.5`. - * @param cy Optional float that specifies the vertical center. Default is `0.5`. - */ - center(horizontal = true, vertical = true, cx = 0.5, cy = 0.5): void { - const container = this.container; - const _hasScrollbars = hasScrollbars(this.container); - const padding = 2 * this.getBorder(); - const cw = container.clientWidth - padding; - const ch = container.clientHeight - padding; - const bounds = this.getGraphBounds(); - - const t = this.view.translate; - const s = this.view.scale; - - let dx = horizontal ? cw - bounds.width : 0; - let dy = vertical ? ch - bounds.height : 0; - - if (!_hasScrollbars) { - this.view.setTranslate( - horizontal ? Math.floor(t.x - bounds.x / s + (dx * cx) / s) : t.x, - vertical ? Math.floor(t.y - bounds.y / s + (dy * cy) / s) : t.y - ); - } else { - bounds.x -= t.x; - bounds.y -= t.y; - - const sw = container.scrollWidth; - const sh = container.scrollHeight; - - if (sw > cw) { - dx = 0; - } - - if (sh > ch) { - dy = 0; - } - - this.view.setTranslate( - Math.floor(dx / 2 - bounds.x), - Math.floor(dy / 2 - bounds.y) - ); - container.scrollLeft = (sw - cw) / 2; - container.scrollTop = (sh - ch) / 2; - } - } - - /** - * Returns `true` if perimeter points should be computed such that the resulting edge has only horizontal or vertical segments. - * - * @param edge {@link CellState} that represents the edge. - */ - isOrthogonal(edge: CellState): boolean { - const orthogonal = edge.style.orthogonal; - if (!isNullish(orthogonal)) { - return orthogonal; - } - - // fallback when the orthogonal style is not defined - const edgeStyle = this.view.getEdgeStyle(edge); - - return [ - EdgeStyle.EntityRelation, - EdgeStyle.ElbowConnector, - EdgeStyle.ManhattanConnector, - EdgeStyle.OrthConnector, - EdgeStyle.SegmentConnector, - EdgeStyle.SideToSide, - EdgeStyle.TopToBottom, - ].includes(edgeStyle!); - } - - /***************************************************************************** - * Group: Graph appearance - *****************************************************************************/ - - /** - * Returns the {@link backgroundImage} as an {@link Image}. - */ - getBackgroundImage(): Image | null { - return this.backgroundImage; - } - - /** - * Sets the new {@link backgroundImage}. - * - * @param image New {@link Image} to be used for the background. - */ - setBackgroundImage(image: Image | null): void { - this.backgroundImage = image; - } - - /** - * Returns the textual representation for the given cell. - * - * This implementation returns the node name or string-representation of the user object. - * - * - * The following returns the label attribute from the cells user object if it is an XML node. - * - * @example - * ```javascript - * graph.convertValueToString = function(cell) - * { - * return cell.getAttribute('label'); - * } - * ``` - * - * See also: {@link cellLabelChanged}. - * - * @param cell {@link Cell} whose textual representation should be returned. - */ - convertValueToString(cell: Cell): string { - const value = cell.getValue(); - - if (value != null) { - if (isNode(value)) { - return value.nodeName; - } - if (typeof value.toString === 'function') { - return value.toString(); - } - } - return ''; - } - - /** - * Returns the string to be used as the link for the given cell. - * - * This implementation returns null. - * - * @param cell {@link Cell} whose link should be returned. - */ - getLinkForCell(cell: Cell): string | null { - return null; - } - - /** - * Returns the value of {@link border}. - */ - getBorder(): number { - return this.border; - } - - /** - * Sets the value of {@link border}. - * - * @param value Positive integer that represents the border to be used. - */ - setBorder(value: number): void { - this.border = value; - } - - /***************************************************************************** - * Group: Graph behaviour - *****************************************************************************/ - - /** - * Returns {@link resizeContainer}. - */ - isResizeContainer() { - return this.resizeContainer; - } - - /** - * Sets {@link resizeContainer}. - * - * @param value Boolean indicating if the container should be resized. - */ - setResizeContainer(value: boolean) { - this.resizeContainer = value; - } - - /** - * Returns true if the graph is {@link enabled}. - */ - isEnabled() { - return this.enabled; - } - - /** - * Specifies if the graph should allow any interactions. This - * implementation updates {@link enabled}. - * - * @param value Boolean indicating if the graph should be enabled. - */ - setEnabled(value: boolean) { - this.enabled = value; - } - - /** - * Returns {@link multigraph} as a boolean. - */ - isMultigraph() { - return this.multigraph; - } - - /** - * Specifies if the graph should allow multiple connections between the - * same pair of vertices. - * - * @param value Boolean indicating if the graph allows multiple connections - * between the same pair of vertices. - */ - setMultigraph(value: boolean) { - this.multigraph = value; - } - - /** - * Returns {@link allowLoops} as a boolean. - */ - isAllowLoops() { - return this.allowLoops; - } - - /** - * Specifies if loops are allowed. - * - * @param value Boolean indicating if loops are allowed. - */ - setAllowLoops(value: boolean) { - this.allowLoops = value; - } - - /** - * Returns {@link recursiveResize}. - * - * @param state {@link CellState} that is being resized. - */ - isRecursiveResize(state: CellState | null = null) { - return this.recursiveResize; - } - - /** - * Sets {@link recursiveResize}. - * - * @param value New boolean value for {@link recursiveResize}. - */ - setRecursiveResize(value: boolean) { - this.recursiveResize = 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 - * means all children must stay inside the parent, 1 means the child is - * allowed to be placed outside of the parent such that it touches one of - * the parents sides. If {@link isAllowOverlapParent} returns false for the given - * cell, then this method returns 0. - * - * @param cell {@link mxCell} for which the overlap ratio should be returned. - */ - getOverlap(cell: Cell) { - return this.isAllowOverlapParent(cell) ? this.defaultOverlap : 0; - } - - /** - * Returns true if the given cell is allowed to be placed outside of the - * parents area. - * - * @param cell {@link mxCell} that represents the child to be checked. - */ - isAllowOverlapParent(cell: Cell): boolean { - return false; - } - - /***************************************************************************** - * Group: Cell retrieval - *****************************************************************************/ - - /** - * Returns {@link defaultParent} or {@link GraphView.currentRoot} or the first child - * child of {@link GraphDataModel.root} if both are null. The value returned by - * this function should be used as the parent for new cells (aka default - * layer). - */ - getDefaultParent() { - let parent = this.getCurrentRoot(); - - if (!parent) { - parent = this.defaultParent; - - if (!parent) { - const root = this.getDataModel().getRoot(); - parent = root.getChildAt(0); - } - } - - return parent; - } - - /** - * Sets the {@link defaultParent} to the given cell. Set this to null to return - * the first child of the root in getDefaultParent. - */ - setDefaultParent(cell: Cell | null) { - this.defaultParent = cell; - } - - /** - * Destroys the graph and all its resources. - */ - destroy() { - if (!this.destroyed) { - this.destroyed = true; - - Object.values(this.plugins).forEach((p) => p.onDestroy()); - - this.view.destroy(); - - if (this.model && this.graphModelChangeListener) { - this.getDataModel().removeListener(this.graphModelChangeListener); - this.graphModelChangeListener = null; - } - } + super({ container, model, plugins, stylesheet }); } } - -// This introduces a side effect, but it is necessary to ensure the Graph is enriched with all properties and methods defined in mixins. -// It is only called when Graph is imported, so the Graph definition is always consistent. -// And this doesn't impact the tree-shaking. -applyGraphMixins(Graph); - -export { Graph }; diff --git a/packages/core/src/view/GraphSelectionModel.ts b/packages/core/src/view/GraphSelectionModel.ts index 09245a8b51..0a341a0209 100644 --- a/packages/core/src/view/GraphSelectionModel.ts +++ b/packages/core/src/view/GraphSelectionModel.ts @@ -17,7 +17,7 @@ limitations under the License. */ import EventSource from '../view/event/EventSource'; -import type { Graph } from './Graph'; +import type { AbstractGraph } from './AbstractGraph'; import type Cell from './cell/Cell'; import SelectionChange from './undoable_changes/SelectionChange'; import UndoableEdit from './undoable_changes/UndoableEdit'; @@ -63,13 +63,13 @@ import { isI18nEnabled } from '../internal/i18n-utils'; * graph - Reference to the enclosing {@link Graph}. */ class GraphSelectionModel extends EventSource { - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(); this.graph = graph; this.cells = []; } - graph: Graph; + graph: AbstractGraph; cells: Cell[]; /** diff --git a/packages/core/src/view/GraphView.ts b/packages/core/src/view/GraphView.ts index f3767c4753..bbed8f0831 100644 --- a/packages/core/src/view/GraphView.ts +++ b/packages/core/src/view/GraphView.ts @@ -41,7 +41,7 @@ import ConnectionConstraint from './other/ConnectionConstraint'; import type PopupMenuHandler from './plugins/PopupMenuHandler'; import { getClientX, getClientY, getSource, isConsumed } from '../util/EventUtils'; import { clone } from '../util/cloneUtils'; -import type { Graph } from './Graph'; +import type { AbstractGraph } from './AbstractGraph'; import StyleRegistry from './style/StyleRegistry'; import type TooltipHandler from './plugins/TooltipHandler'; import type { EdgeStyleFunction, MouseEventListener } from '../types'; @@ -92,7 +92,7 @@ import { isI18nEnabled } from '../internal/i18n-utils'; * respectively. */ export class GraphView extends EventSource { - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(); this.graph = graph; @@ -158,7 +158,7 @@ export class GraphView extends EventSource { /** * Reference to the enclosing {@link graph}. */ - graph: Graph; + graph: AbstractGraph; /** * {@link Cell} that acts as the root of the displayed cell hierarchy. diff --git a/packages/core/src/view/animate/Effects.ts b/packages/core/src/view/animate/Effects.ts index 33ed3c862a..8eb005c665 100644 --- a/packages/core/src/view/animate/Effects.ts +++ b/packages/core/src/view/animate/Effects.ts @@ -22,7 +22,7 @@ import TerminalChange from '../undoable_changes/TerminalChange'; import ValueChange from '../undoable_changes/ValueChange'; import ChildChange from '../undoable_changes/ChildChange'; import StyleChange from '../undoable_changes/StyleChange'; -import type { Graph } from '../../view/Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../../view/cell/Cell'; import { UndoableChange } from '../../types'; import Geometry from '../geometry/Geometry'; @@ -48,12 +48,16 @@ class Effects { * }); * ``` * - * @param graph - {@link Graph} that received the changes. + * @param graph - {@link AbstractGraph} that received the changes. * @param changes - Array of changes to be animated. * @param done - Optional function argument that is invoked after the * last step of the animation. */ - static animateChanges(graph: Graph, changes: UndoableChange[], done?: Function): void { + static animateChanges( + graph: AbstractGraph, + changes: UndoableChange[], + done?: Function + ): void { const maxStep = 10; let step = 0; @@ -125,11 +129,11 @@ class Effects { /** * Sets the opacity on the given cell and its descendants. * - * @param graph - {@link Graph} that contains the cells. + * @param graph - {@link AbstractGraph} that contains the cells. * @param cell - {@link Cell} to set the opacity for. * @param opacity - New value for the opacity in %. */ - static cascadeOpacity(graph: Graph, cell: Cell, opacity: number): void { + static cascadeOpacity(graph: AbstractGraph, cell: Cell, opacity: number): void { // Fades all children const childCount = cell.getChildCount(); diff --git a/packages/core/src/view/animate/Morphing.ts b/packages/core/src/view/animate/Morphing.ts index aff0a6d256..f09fe1e548 100644 --- a/packages/core/src/view/animate/Morphing.ts +++ b/packages/core/src/view/animate/Morphing.ts @@ -21,7 +21,7 @@ import CellStatePreview from '../cell/CellStatePreview'; import Animation from './Animation'; import type CellState from '../cell/CellState'; import type Cell from '../cell/Cell'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; /** * Implements animation for morphing cells. Here is an example of @@ -50,23 +50,20 @@ import type { Graph } from '../Graph'; * * Constructs an animation. * - * @param graph Reference to the enclosing {@link Graph}. + * @param graph Reference to the enclosing {@link AbstractGraph}. * @param steps Optional number of steps in the morphing animation. Default is 6. * @param ease Optional easing constant for the animation. Default is 1.5. * @param delay Optional delay between the animation steps. Passed to . */ class Morphing extends Animation { - constructor(graph: Graph, steps = 6, ease = 1.5, delay?: number) { + constructor(graph: AbstractGraph, steps = 6, ease = 1.5, delay?: number) { super(delay); this.graph = graph; this.steps = steps; this.ease = ease; } - /** - * Specifies the delay between the animation steps. Defaul is 30ms. - */ - graph: Graph; + graph: AbstractGraph; /** * Specifies the maximum number of steps for the morphing. diff --git a/packages/core/src/view/cell/CellHighlight.ts b/packages/core/src/view/cell/CellHighlight.ts index c5c5a79de2..f3eb053f7b 100644 --- a/packages/core/src/view/cell/CellHighlight.ts +++ b/packages/core/src/view/cell/CellHighlight.ts @@ -25,7 +25,7 @@ import { import InternalEvent from '../event/InternalEvent'; import Rectangle from '../geometry/Rectangle'; import type CellState from './CellState'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import Shape from '../geometry/Shape'; import type { ColorValue } from '../../types'; @@ -62,7 +62,7 @@ class CellHighlight { * Reference to the enclosing {@link graph}. * @default true */ - graph: Graph; + graph: AbstractGraph; /** * Reference to the {@link CellState}. @@ -83,7 +83,7 @@ class CellHighlight { resetHandler: Function; constructor( - graph: Graph, + graph: AbstractGraph, highlightColor?: ColorValue, strokeWidth?: number, dashed?: boolean diff --git a/packages/core/src/view/cell/CellMarker.ts b/packages/core/src/view/cell/CellMarker.ts index fab20c2b5c..aabaf19b87 100644 --- a/packages/core/src/view/cell/CellMarker.ts +++ b/packages/core/src/view/cell/CellMarker.ts @@ -29,7 +29,7 @@ import CellHighlight from './CellHighlight'; import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; import { intersectsHotspot } from '../../util/mathUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import { ColorValue } from '../../types'; import type CellState from './CellState'; import InternalMouseEvent from '../event/InternalMouseEvent'; @@ -61,7 +61,7 @@ class CellMarker extends EventSource { /** * Reference to the enclosing {@link Graph}. */ - graph: Graph; + graph: AbstractGraph; /** * Specifies if the marker is enabled. @@ -119,7 +119,7 @@ class CellMarker extends EventSource { * @param hotspot Portion of the width and height where a state intersects a given coordinate pair. A value of 0 means always highlight. Default is {@link DEFAULT_HOTSPOT}. */ constructor( - graph: Graph, + graph: AbstractGraph, validColor: ColorValue = DEFAULT_VALID_COLOR, invalidColor: ColorValue = DEFAULT_INVALID_COLOR, hotspot: number = DEFAULT_HOTSPOT diff --git a/packages/core/src/view/cell/CellStatePreview.ts b/packages/core/src/view/cell/CellStatePreview.ts index 20d856582c..e3c8024ba7 100644 --- a/packages/core/src/view/cell/CellStatePreview.ts +++ b/packages/core/src/view/cell/CellStatePreview.ts @@ -20,26 +20,23 @@ import Point from '../geometry/Point'; import Dictionary from '../../util/Dictionary'; import type CellState from './CellState'; import type Cell from './Cell'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type GraphView from '../GraphView'; /** * Implements a live preview for moving cells. */ class CellStatePreview { - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { this.deltas = new Dictionary(); this.graph = graph; } /** - * Reference to the enclosing {@link Graph}. + * Reference to the enclosing {@link AbstractGraph}. */ - graph: Graph; + graph: AbstractGraph; - /** - * Reference to the enclosing {@link Graph}. - */ deltas: Dictionary; /** diff --git a/packages/core/src/view/cell/CellTracker.ts b/packages/core/src/view/cell/CellTracker.ts index 216fe409d3..029483c766 100644 --- a/packages/core/src/view/cell/CellTracker.ts +++ b/packages/core/src/view/cell/CellTracker.ts @@ -18,7 +18,7 @@ limitations under the License. import CellMarker from './CellMarker'; import InternalMouseEvent from '../event/InternalMouseEvent'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from './Cell'; import EventSource from '../event/EventSource'; @@ -79,7 +79,7 @@ import type { ColorValue } from '../../types'; */ class CellTracker extends CellMarker { constructor( - graph: Graph, + graph: AbstractGraph, color: ColorValue, funct: ((me: InternalMouseEvent) => Cell) | null = null ) { diff --git a/packages/core/src/view/cell/VertexHandle.ts b/packages/core/src/view/cell/VertexHandle.ts index 7f0b458a32..d48718896b 100644 --- a/packages/core/src/view/cell/VertexHandle.ts +++ b/packages/core/src/view/cell/VertexHandle.ts @@ -28,7 +28,7 @@ import InternalMouseEvent from '../event/InternalMouseEvent'; import ImageBox from '../image/ImageBox'; import CellState from './CellState'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type { CellHandle, CellStateStyle } from '../../types'; import { HandleConfig } from '../handler/config'; @@ -40,7 +40,7 @@ import { HandleConfig } from '../handler/config'; class VertexHandle implements CellHandle { dependencies = ['snap', 'cells']; - graph: Graph; + graph: AbstractGraph; state: CellState; shape: Shape | ImageShape | null; diff --git a/packages/core/src/view/event/InternalEvent.ts b/packages/core/src/view/event/InternalEvent.ts index 83c71cfc28..3212067f41 100644 --- a/packages/core/src/view/event/InternalEvent.ts +++ b/packages/core/src/view/event/InternalEvent.ts @@ -27,7 +27,7 @@ import type { Listenable, MouseEventListener, } from '../../types'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; // Checks if passive event listeners are supported // see https://github.com/Modernizr/Modernizr/issues/1894 @@ -238,7 +238,7 @@ class InternalEvent { */ static redirectMouseEvents( node: Listenable, - graph: Graph, + graph: AbstractGraph, state: CellState | ((evt: Event) => CellState | null) | null = null, down: MouseEventListener | null = null, move: MouseEventListener | null = null, diff --git a/packages/core/src/view/handler/ConstraintHandler.ts b/packages/core/src/view/handler/ConstraintHandler.ts index 4adce23d46..0746b2b85a 100644 --- a/packages/core/src/view/handler/ConstraintHandler.ts +++ b/packages/core/src/view/handler/ConstraintHandler.ts @@ -31,7 +31,7 @@ import Rectangle from '../geometry/Rectangle'; import ImageShape from '../geometry/node/ImageShape'; import RectangleShape from '../geometry/node/RectangleShape'; import { isShiftDown } from '../../util/EventUtils'; -import type { Graph } from '../Graph'; +import { AbstractGraph } from '../AbstractGraph'; import CellState from '../cell/CellState'; import InternalMouseEvent from '../event/InternalMouseEvent'; import ConnectionConstraint from '../other/ConnectionConstraint'; @@ -43,7 +43,6 @@ import type Cell from '../cell/Cell'; * showing fixed points when the mouse is over a vertex and handles constraints * to establish new connections. * - * @class ConstraintHandler */ class ConstraintHandler { /** @@ -52,9 +51,9 @@ class ConstraintHandler { pointImage = new Image(`${Client.imageBasePath}/point.gif`, 5, 5); /** - * Reference to the enclosing {@link mxGraph}. + * Reference to the enclosing {@link AbstractGraph}. */ - graph: Graph; + graph: AbstractGraph; resetHandler: () => void; @@ -86,7 +85,7 @@ class ConstraintHandler { mouseleaveHandler: (() => void) | null = null; - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { this.graph = graph; // Adds a graph model listener to update the current focus on changes diff --git a/packages/core/src/view/handler/EdgeHandler.ts b/packages/core/src/view/handler/EdgeHandler.ts index 0eab6cabcf..be7846a361 100644 --- a/packages/core/src/view/handler/EdgeHandler.ts +++ b/packages/core/src/view/handler/EdgeHandler.ts @@ -53,7 +53,7 @@ import { isMouseEvent, isShiftDown, } from '../../util/EventUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import CellState from '../cell/CellState'; import Shape from '../geometry/Shape'; import { CellHandle, ColorValue, Listenable } from '../../types'; @@ -78,7 +78,7 @@ class EdgeHandler { /** * Reference to the enclosing {@link Graph}. */ - graph: Graph; + graph: AbstractGraph; /** * Reference to the {@link CellState} being modified. @@ -2248,7 +2248,7 @@ class EdgeHandlerCellMarker extends CellMarker { edgeHandler: EdgeHandler; constructor( - graph: Graph, + graph: AbstractGraph, edgeHandler: EdgeHandler, validColor: ColorValue = DEFAULT_VALID_COLOR, invalidColor: ColorValue = DEFAULT_INVALID_COLOR, diff --git a/packages/core/src/view/handler/KeyHandler.ts b/packages/core/src/view/handler/KeyHandler.ts index 62c901b6b8..1ab589c4be 100644 --- a/packages/core/src/view/handler/KeyHandler.ts +++ b/packages/core/src/view/handler/KeyHandler.ts @@ -16,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import InternalEvent from '../event/InternalEvent'; import { isAncestorNode } from '../../util/domUtils'; import { @@ -74,11 +74,11 @@ class KeyHandler { /** * Constructs an event handler that executes functions bound to specific keystrokes. * - * @param graph Reference to the associated {@link Graph}. + * @param graph Reference to the associated {@link AbstractGraph}. * @param target Optional reference to the event target. * If `null`, the document element is used as the event target, that is, the object where the key event listener is installed. */ - constructor(graph: Graph, target: Element | null = null) { + constructor(graph: AbstractGraph, target: Element | null = null) { if (graph != null) { this.graph = graph; this.target = target || document.documentElement; @@ -95,9 +95,9 @@ class KeyHandler { keydownHandler: ((event: KeyboardEvent) => void) | null = null; /** - * Reference to the {@link Graph} associated with this handler. + * Reference to the {@link AbstractGraph} associated with this handler. */ - graph: Graph | null = null; + graph: AbstractGraph | null = null; /** * Reference to the target DOM, that is, the DOM node where the key event diff --git a/packages/core/src/view/handler/VertexHandler.ts b/packages/core/src/view/handler/VertexHandler.ts index 358e6cb691..4e73c873d9 100644 --- a/packages/core/src/view/handler/VertexHandler.ts +++ b/packages/core/src/view/handler/VertexHandler.ts @@ -26,7 +26,7 @@ import Point from '../geometry/Point'; import { getRotatedPoint, intersects, mod, toRadians } from '../../util/mathUtils'; import Client from '../../Client'; import { isMouseEvent, isShiftDown } from '../../util/EventUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import CellState from '../cell/CellState'; import Image from '../image/ImageBox'; import type Cell from '../cell/Cell'; @@ -55,7 +55,7 @@ class VertexHandler { /** * Reference to the enclosing {@link Graph}. */ - graph: Graph; + graph: AbstractGraph; /** * Reference to the {@link CellState} being modified. diff --git a/packages/core/src/view/layout/CircleLayout.ts b/packages/core/src/view/layout/CircleLayout.ts index 57ea1ccdbc..716b8c91c8 100644 --- a/packages/core/src/view/layout/CircleLayout.ts +++ b/packages/core/src/view/layout/CircleLayout.ts @@ -17,7 +17,7 @@ limitations under the License. */ import GraphLayout from './GraphLayout'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../cell/Cell'; /** @@ -38,10 +38,10 @@ class CircleLayout extends GraphLayout { /** * Constructs a new circular layout for the specified radius. * - * @param graph {@link Graph} that contains the cells. + * @param graph {@link AbstractGraph} that contains the cells. * @param radius Optional radius as an int. Default is 100. */ - constructor(graph: Graph, radius = 100) { + constructor(graph: AbstractGraph, radius = 100) { super(graph); this.radius = radius; } diff --git a/packages/core/src/view/layout/CompactTreeLayout.ts b/packages/core/src/view/layout/CompactTreeLayout.ts index 005d51ffa2..0e1dd5b9dd 100644 --- a/packages/core/src/view/layout/CompactTreeLayout.ts +++ b/packages/core/src/view/layout/CompactTreeLayout.ts @@ -24,7 +24,7 @@ import Rectangle from '../geometry/Rectangle'; import { sortCells } from '../../util/styleUtils'; import WeightedCellSorter from './util/WeightedCellSorter'; import type Cell from '../cell/Cell'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import { findTreeRoots } from '../../util/treeTraversal'; /** @@ -75,7 +75,7 @@ export interface _mxCompactTreeLayoutLine { * @category Layout */ export class CompactTreeLayout extends GraphLayout { - constructor(graph: Graph, horizontal = true, invert = false) { + constructor(graph: AbstractGraph, horizontal = true, invert = false) { super(graph); this.horizontal = horizontal; this.invert = invert; diff --git a/packages/core/src/view/layout/CompositeLayout.ts b/packages/core/src/view/layout/CompositeLayout.ts index ef8c89d6fa..4c0943c6b1 100644 --- a/packages/core/src/view/layout/CompositeLayout.ts +++ b/packages/core/src/view/layout/CompositeLayout.ts @@ -17,7 +17,7 @@ limitations under the License. */ import type Cell from '../cell/Cell'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import GraphLayout from './GraphLayout'; /** @@ -42,11 +42,11 @@ class CompositeLayout extends GraphLayout { * Constructs a new layout using the given layouts. The graph instance is * required for creating the transaction that contains all layouts. * - * @param graph Reference to the enclosing {@link Graph}. + * @param graph Reference to the enclosing {@link AbstractGraph}. * @param layouts Array of {@link GraphLayout}s. * @param master Optional layout that handles moves. If no layout is given, then the first layout of the above array is used to handle moves. */ - constructor(graph: Graph, layouts: GraphLayout[], master?: GraphLayout) { + constructor(graph: AbstractGraph, layouts: GraphLayout[], master?: GraphLayout) { super(graph); this.layouts = layouts; this.master = master; diff --git a/packages/core/src/view/layout/EdgeLabelLayout.ts b/packages/core/src/view/layout/EdgeLabelLayout.ts index fc63c48147..cd99a70efc 100644 --- a/packages/core/src/view/layout/EdgeLabelLayout.ts +++ b/packages/core/src/view/layout/EdgeLabelLayout.ts @@ -20,7 +20,7 @@ import Point from '../geometry/Point'; import GraphLayout from './GraphLayout'; import { intersects } from '../../util/mathUtils'; import type Cell from '../cell/Cell'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import CellState from '../cell/CellState'; import TextShape from '../geometry/node/TextShape'; import Rectangle from '../geometry/Rectangle'; @@ -39,7 +39,7 @@ import Rectangle from '../geometry/Rectangle'; * @category Layout */ class EdgeLabelLayout extends GraphLayout { - constructor(graph: Graph, radius: number) { + constructor(graph: AbstractGraph, radius: number) { super(graph); } diff --git a/packages/core/src/view/layout/FastOrganicLayout.ts b/packages/core/src/view/layout/FastOrganicLayout.ts index 6ef6697572..dd01d082a7 100644 --- a/packages/core/src/view/layout/FastOrganicLayout.ts +++ b/packages/core/src/view/layout/FastOrganicLayout.ts @@ -17,7 +17,7 @@ limitations under the License. */ import ObjectIdentity from '../../util/ObjectIdentity'; import GraphLayout from './GraphLayout'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../cell/Cell'; /** @@ -35,7 +35,7 @@ import type Cell from '../cell/Cell'; * @category Layout */ class MxFastOrganicLayout extends GraphLayout { - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(graph); } diff --git a/packages/core/src/view/layout/GraphLayout.ts b/packages/core/src/view/layout/GraphLayout.ts index d1a42f57a5..0cf9fbaa24 100644 --- a/packages/core/src/view/layout/GraphLayout.ts +++ b/packages/core/src/view/layout/GraphLayout.ts @@ -20,7 +20,7 @@ import Dictionary from '../../util/Dictionary'; import Rectangle from '../geometry/Rectangle'; import Geometry from '../geometry/Geometry'; import Point from '../geometry/Point'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../cell/Cell'; import { GraphLayoutTraverseArgs } from './types'; @@ -33,14 +33,14 @@ import { GraphLayoutTraverseArgs } from './types'; * @category Layout */ class GraphLayout { - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { this.graph = graph; } /** - * Reference to the enclosing {@link Graph}. + * Reference to the enclosing {@link AbstractGraph}. */ - graph: Graph; + graph: AbstractGraph; /** * Boolean indicating if the bounding box of the label should be used if it iss available. @@ -96,7 +96,7 @@ class GraphLayout { /** * Returns the graph that this layout operates on. */ - getGraph(): Graph { + getGraph(): AbstractGraph { return this.graph; } diff --git a/packages/core/src/view/layout/HierarchicalLayout.ts b/packages/core/src/view/layout/HierarchicalLayout.ts index 0a2e5e5334..8a12b348ed 100644 --- a/packages/core/src/view/layout/HierarchicalLayout.ts +++ b/packages/core/src/view/layout/HierarchicalLayout.ts @@ -25,7 +25,7 @@ import ObjectIdentity from '../../util/ObjectIdentity'; import MinimumCycleRemover from './hierarchical/MinimumCycleRemover'; import MedianHybridCrossingReduction from './hierarchical/MedianHybridCrossingReduction'; import CoordinateAssignment from './hierarchical/CoordinateAssignment'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../cell/Cell'; import { HierarchicalGraphLayoutTraverseArgs } from './types'; @@ -38,12 +38,12 @@ class HierarchicalLayout extends GraphLayout { /** * Constructs a new hierarchical layout algorithm. * - * @param graph Reference to the enclosing {@link Graph}. + * @param graph Reference to the enclosing {@link AbstractGraph}. * @param orientation Optional constant that defines the orientation of this layout. Default is {@link DIRECTION.NORTH}. * @param deterministic Optional boolean that specifies if this layout should be deterministic. Default is true. */ constructor( - graph: Graph, + graph: AbstractGraph, orientation: DIRECTION = DIRECTION.NORTH, deterministic = true ) { diff --git a/packages/core/src/view/layout/LayoutManager.ts b/packages/core/src/view/layout/LayoutManager.ts index 378eac76b5..32051c788f 100644 --- a/packages/core/src/view/layout/LayoutManager.ts +++ b/packages/core/src/view/layout/LayoutManager.ts @@ -29,7 +29,7 @@ import EventObject from '../event/EventObject'; import type Cell from '../cell/Cell'; import Rectangle from '../geometry/Rectangle'; import { getClientX, getClientY } from '../../util/EventUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import GraphLayout from './GraphLayout'; import UndoableEdit from '../undoable_changes/UndoableEdit'; @@ -57,9 +57,9 @@ import UndoableEdit from '../undoable_changes/UndoableEdit'; */ class LayoutManager extends EventSource { /** - * Reference to the enclosing {@link graph}. + * Reference to the enclosing {@link AbstractGraph}. */ - graph!: Graph; + graph!: AbstractGraph; /** * Specifies if the layout should bubble along @@ -89,7 +89,7 @@ class LayoutManager extends EventSource { */ resizeHandler: (...args: any[]) => any; - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(); // Executes the layout before the changes are dispatched @@ -164,7 +164,7 @@ class LayoutManager extends EventSource { /** * Sets the graph that the layouts operate on. */ - setGraph(graph: Graph | null) { + setGraph(graph: AbstractGraph | null) { if (this.graph) { const model = this.graph.getDataModel(); model.removeListener(this.undoHandler); diff --git a/packages/core/src/view/layout/ParallelEdgeLayout.ts b/packages/core/src/view/layout/ParallelEdgeLayout.ts index 7abc210b48..ea27477f2b 100644 --- a/packages/core/src/view/layout/ParallelEdgeLayout.ts +++ b/packages/core/src/view/layout/ParallelEdgeLayout.ts @@ -19,7 +19,7 @@ limitations under the License. import Point from '../geometry/Point'; import GraphLayout from './GraphLayout'; import ObjectIdentity from '../../util/ObjectIdentity'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../cell/Cell'; import Geometry from '../geometry/Geometry'; @@ -63,7 +63,7 @@ import Geometry from '../geometry/Geometry'; * @category Layout */ class ParallelEdgeLayout extends GraphLayout { - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(graph); } diff --git a/packages/core/src/view/layout/PartitionLayout.ts b/packages/core/src/view/layout/PartitionLayout.ts index 2b7a9b66f0..93e3b8be3e 100644 --- a/packages/core/src/view/layout/PartitionLayout.ts +++ b/packages/core/src/view/layout/PartitionLayout.ts @@ -18,7 +18,7 @@ limitations under the License. import Rectangle from '../geometry/Rectangle'; import GraphLayout from './GraphLayout'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../cell/Cell'; /** @@ -39,7 +39,7 @@ import type Cell from '../cell/Cell'; * @category Layout */ class PartitionLayout extends GraphLayout { - constructor(graph: Graph, horizontal = true, spacing = 0, border = 0) { + constructor(graph: AbstractGraph, horizontal = true, spacing = 0, border = 0) { super(graph); this.horizontal = horizontal != null ? horizontal : true; this.spacing = spacing || 0; diff --git a/packages/core/src/view/layout/RadialTreeLayout.ts b/packages/core/src/view/layout/RadialTreeLayout.ts index 231bf30658..f991e33bfe 100644 --- a/packages/core/src/view/layout/RadialTreeLayout.ts +++ b/packages/core/src/view/layout/RadialTreeLayout.ts @@ -22,7 +22,7 @@ import { _mxCompactTreeLayoutNode, } from './CompactTreeLayout'; import type Cell from '../cell/Cell'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; /** * Extends {@link CompactTreeLayout} to implement a radial tree algorithm. This @@ -37,7 +37,7 @@ import type { Graph } from '../Graph'; * @category Layout */ class RadialTreeLayout extends CompactTreeLayout { - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(graph, false); } diff --git a/packages/core/src/view/layout/StackLayout.ts b/packages/core/src/view/layout/StackLayout.ts index 1ec3bba0ed..c0a36d5691 100644 --- a/packages/core/src/view/layout/StackLayout.ts +++ b/packages/core/src/view/layout/StackLayout.ts @@ -18,7 +18,7 @@ limitations under the License. import GraphLayout from './GraphLayout'; import { DEFAULT_STARTSIZE } from '../../util/Constants'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../cell/Cell'; import Geometry from '../geometry/Geometry'; @@ -37,7 +37,7 @@ import Geometry from '../geometry/Geometry'; */ class StackLayout extends GraphLayout { constructor( - graph: Graph, + graph: AbstractGraph, horizontal: boolean | null = null, spacing: number | null = null, x0: number | null = null, diff --git a/packages/core/src/view/layout/SwimlaneLayout.ts b/packages/core/src/view/layout/SwimlaneLayout.ts index 6e817f48b5..773ab320df 100644 --- a/packages/core/src/view/layout/SwimlaneLayout.ts +++ b/packages/core/src/view/layout/SwimlaneLayout.ts @@ -26,7 +26,7 @@ import ObjectIdentity from '../../util/ObjectIdentity'; import SwimlaneOrdering from './hierarchical/SwimlaneOrdering'; import MedianHybridCrossingReduction from './hierarchical/MedianHybridCrossingReduction'; import CoordinateAssignment from './hierarchical/CoordinateAssignment'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../cell/Cell'; import Geometry from '../../view/geometry/Geometry'; import { SwimlaneGraphLayoutTraverseArgs } from './types'; @@ -40,11 +40,11 @@ class SwimlaneLayout extends GraphLayout { /** * Constructs a new hierarchical layout algorithm. * - * @param graph Reference to the enclosing {@link Graph}. - * @param orientation Optional constant that defines the orientation of this layout. Default is {@link DIRECTION_NORTH}. + * @param graph Reference to the enclosing {@link AbstractGraph}. + * @param orientation Optional constant that defines the orientation of this layout. Default is {@link DIRECTION.NORTH}. * @param deterministic Optional boolean that specifies if this layout should be deterministic. Default is true. */ - constructor(graph: Graph, orientation: DIRECTION | null, deterministic = true) { + constructor(graph: AbstractGraph, orientation: DIRECTION | null, deterministic = true) { super(graph); this.orientation = orientation != null ? orientation : DIRECTION.NORTH; this.deterministic = deterministic != null ? deterministic : true; diff --git a/packages/core/src/view/layout/SwimlaneManager.ts b/packages/core/src/view/layout/SwimlaneManager.ts index b9f7e9b1f3..e3d6989ec6 100644 --- a/packages/core/src/view/layout/SwimlaneManager.ts +++ b/packages/core/src/view/layout/SwimlaneManager.ts @@ -19,7 +19,7 @@ limitations under the License. import EventSource from '../event/EventSource'; import InternalEvent from '../event/InternalEvent'; import Rectangle from '../geometry/Rectangle'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import EventObject from '../event/EventObject'; import type Cell from '../cell/Cell'; @@ -32,7 +32,12 @@ import type Cell from '../cell/Cell'; * @category Layout */ class SwimlaneManager extends EventSource { - constructor(graph: Graph, horizontal = true, addEnabled = true, resizeEnabled = true) { + constructor( + graph: AbstractGraph, + horizontal = true, + addEnabled = true, + resizeEnabled = true + ) { super(); this.horizontal = horizontal; @@ -55,9 +60,9 @@ class SwimlaneManager extends EventSource { } /** - * Reference to the enclosing {@link graph}. + * Reference to the enclosing {@link AbstractGraph}. */ - graph!: Graph; + graph!: AbstractGraph; /** * Specifies if event handling is enabled. @@ -164,7 +169,7 @@ class SwimlaneManager extends EventSource { /** * Sets the graph that the manager operates on. */ - setGraph(graph: Graph | null) { + setGraph(graph: AbstractGraph | null) { if (this.graph) { this.graph.removeListener(this.addHandler); this.graph.removeListener(this.resizeHandler); diff --git a/packages/core/src/view/layout/hierarchical/CoordinateAssignment.ts b/packages/core/src/view/layout/hierarchical/CoordinateAssignment.ts index 16dde3347d..62feb72f53 100644 --- a/packages/core/src/view/layout/hierarchical/CoordinateAssignment.ts +++ b/packages/core/src/view/layout/hierarchical/CoordinateAssignment.ts @@ -28,7 +28,7 @@ import GraphHierarchyModel from './GraphHierarchyModel'; import Cell from '../../../view/cell/Cell'; import GraphHierarchyNode from '../datatypes/GraphHierarchyNode'; import GraphAbstractHierarchyCell from '../datatypes/GraphAbstractHierarchyCell'; -import type { Graph } from '../../../view/Graph'; +import type { AbstractGraph } from '../../AbstractGraph'; import Geometry from '../../../view/geometry/Geometry'; import GraphHierarchyEdge from '../datatypes/GraphHierarchyEdge'; import SwimlaneLayout from '../SwimlaneLayout'; @@ -664,7 +664,7 @@ class CoordinateAssignment extends HierarchicalLayoutStage { * @param facade the facade describing the input graph * @param model an internal model of the hierarchical layout */ - initialCoords(facade: Graph, model: GraphHierarchyModel) { + initialCoords(facade: AbstractGraph, model: GraphHierarchyModel) { this.calculateWidestRank(facade, model); // Sweep up and down from the widest rank @@ -691,7 +691,7 @@ class CoordinateAssignment extends HierarchicalLayoutStage { * @param graph the facade describing the input graph * @param model an internal model of the hierarchical layout */ - rankCoordinates(rankValue: number, graph: Graph, model: GraphHierarchyModel) { + rankCoordinates(rankValue: number, graph: AbstractGraph, model: GraphHierarchyModel) { const ranks = model.ranks; const rank = ranks[rankValue]; let maxY = 0.0; @@ -759,7 +759,7 @@ class CoordinateAssignment extends HierarchicalLayoutStage { * @param graph the facade describing the input graph * @param model an internal model of the hierarchical layout */ - calculateWidestRank(graph: Graph, model: GraphHierarchyModel) { + calculateWidestRank(graph: AbstractGraph, model: GraphHierarchyModel) { // Starting y co-ordinate let y = -this.interRankCellSpacing; @@ -860,7 +860,7 @@ class CoordinateAssignment extends HierarchicalLayoutStage { * @param graph the facade describing the input graph * @param model an internal model of the hierarchical layout */ - minPath(graph: Graph, model: GraphHierarchyModel) { + minPath(graph: AbstractGraph, model: GraphHierarchyModel) { // Work down and up each edge with at least 2 control points // trying to straighten each one out. If the same number of // straight segments are formed in both directions, the @@ -1030,7 +1030,7 @@ class CoordinateAssignment extends HierarchicalLayoutStage { * @param graph the input graph * @param model the layout model */ - setCellLocations(graph: Graph, model: GraphHierarchyModel) { + setCellLocations(graph: AbstractGraph, model: GraphHierarchyModel) { this.rankTopY = []; this.rankBottomY = []; const ranks = model.ranks; diff --git a/packages/core/src/view/mixins/CellsMixin.ts b/packages/core/src/view/mixins/CellsMixin.ts index 6c497b79e8..343220f15f 100644 --- a/packages/core/src/view/mixins/CellsMixin.ts +++ b/packages/core/src/view/mixins/CellsMixin.ts @@ -43,11 +43,11 @@ import Dictionary from '../../util/Dictionary'; import Point from '../geometry/Point'; import { htmlEntities } from '../../util/StringUtils'; import CellState from '../cell/CellState'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import { cloneCells, getTopmostCells } from '../../util/cellArrayUtils'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'getView' | 'getStylesheet' | 'batchUpdate' @@ -90,7 +90,7 @@ type PartialGraph = Pick< >; type PartialCells = Pick< - Graph, + AbstractGraph, | 'cellsResizable' | 'cellsBendable' | 'cellsSelectable' diff --git a/packages/core/src/view/mixins/CellsMixin.type.ts b/packages/core/src/view/mixins/CellsMixin.type.ts index 503d1d70c5..4c2fa2dc1f 100644 --- a/packages/core/src/view/mixins/CellsMixin.type.ts +++ b/packages/core/src/view/mixins/CellsMixin.type.ts @@ -20,8 +20,8 @@ import type { CellStateStyle, CellStyle, NumericCellStateStyleKeys } from '../.. import type Geometry from '../geometry/Geometry'; import type CellState from '../cell/CellState'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Specifies the return value for {@link isCellsResizable}. * @default true diff --git a/packages/core/src/view/mixins/ConnectionsMixin.ts b/packages/core/src/view/mixins/ConnectionsMixin.ts index ae51139a24..0f1f94a99c 100644 --- a/packages/core/src/view/mixins/ConnectionsMixin.ts +++ b/packages/core/src/view/mixins/ConnectionsMixin.ts @@ -22,12 +22,12 @@ import Cell from '../cell/Cell'; import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; import Dictionary from '../../util/Dictionary'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type ConnectionHandler from '../plugins/ConnectionHandler'; -type PartialGraph = Pick; +type PartialGraph = Pick; type PartialConnections = Pick< - Graph, + AbstractGraph, | 'constrainChildren' | 'constrainRelativeChildren' | 'disconnectOnMove' diff --git a/packages/core/src/view/mixins/ConnectionsMixin.type.ts b/packages/core/src/view/mixins/ConnectionsMixin.type.ts index c5af5e7670..c74d9de7f8 100644 --- a/packages/core/src/view/mixins/ConnectionsMixin.type.ts +++ b/packages/core/src/view/mixins/ConnectionsMixin.type.ts @@ -20,8 +20,8 @@ import type InternalMouseEvent from '../event/InternalMouseEvent'; import type ConnectionConstraint from '../other/ConnectionConstraint'; import type Cell from '../cell/Cell'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Specifies if a child should be constrained inside the parent bounds after a move or resize of the child. * @default true diff --git a/packages/core/src/view/mixins/DragDropMixin.ts b/packages/core/src/view/mixins/DragDropMixin.ts index eca4558cb4..62a13d3252 100644 --- a/packages/core/src/view/mixins/DragDropMixin.ts +++ b/packages/core/src/view/mixins/DragDropMixin.ts @@ -14,11 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; -type PartialGraph = Pick; +type PartialGraph = Pick; type PartialDragDrop = Pick< - Graph, + AbstractGraph, | 'dropEnabled' | 'splitEnabled' | 'autoScroll' diff --git a/packages/core/src/view/mixins/DragDropMixin.type.ts b/packages/core/src/view/mixins/DragDropMixin.type.ts index 06f01f7f7f..cd6056a371 100644 --- a/packages/core/src/view/mixins/DragDropMixin.type.ts +++ b/packages/core/src/view/mixins/DragDropMixin.type.ts @@ -16,8 +16,8 @@ limitations under the License. import type Cell from '../cell/Cell'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Specifies the return value for {@link isDropEnabled}. * @default false diff --git a/packages/core/src/view/mixins/EdgeMixin.ts b/packages/core/src/view/mixins/EdgeMixin.ts index d6fa6bd7c4..f13efda018 100644 --- a/packages/core/src/view/mixins/EdgeMixin.ts +++ b/packages/core/src/view/mixins/EdgeMixin.ts @@ -18,7 +18,7 @@ import type { CellStyle } from '../../types'; import Dictionary from '../../util/Dictionary'; import { removeDuplicates } from '../../util/arrayUtils'; import { findNearestSegment } from '../../util/mathUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import Cell from '../cell/Cell'; import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; @@ -26,7 +26,7 @@ import Geometry from '../geometry/Geometry'; import type Point from '../geometry/Point'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'batchUpdate' | 'fireEvent' | 'getDataModel' @@ -40,7 +40,7 @@ type PartialGraph = Pick< | 'cellConnected' >; type PartialEdge = Pick< - Graph, + AbstractGraph, | 'resetEdgesOnResize' | 'resetEdgesOnMove' | 'resetEdgesOnConnect' diff --git a/packages/core/src/view/mixins/EdgeMixin.type.ts b/packages/core/src/view/mixins/EdgeMixin.type.ts index d30beb970f..a7f96e3cea 100644 --- a/packages/core/src/view/mixins/EdgeMixin.type.ts +++ b/packages/core/src/view/mixins/EdgeMixin.type.ts @@ -17,8 +17,8 @@ limitations under the License. import type { CellStyle, EdgeParameters, EdgeParametersValue } from '../../types'; import type Cell from '../cell/Cell'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Specifies if edge control points should be reset after the resize of a connected cell. * @default false diff --git a/packages/core/src/view/mixins/EditingMixin.ts b/packages/core/src/view/mixins/EditingMixin.ts index 9e4772f196..6b6149a1d8 100644 --- a/packages/core/src/view/mixins/EditingMixin.ts +++ b/packages/core/src/view/mixins/EditingMixin.ts @@ -17,11 +17,11 @@ limitations under the License. import { isMultiTouchEvent } from '../../util/EventUtils'; import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type CellEditorHandler from '../plugins/CellEditorHandler'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'convertValueToString' | 'batchUpdate' | 'getDataModel' @@ -34,7 +34,7 @@ type PartialGraph = Pick< | 'getPlugin' >; type PartialEditing = Pick< - Graph, + AbstractGraph, | 'cellsEditable' | 'startEditing' | 'startEditingAtCell' diff --git a/packages/core/src/view/mixins/EditingMixin.type.ts b/packages/core/src/view/mixins/EditingMixin.type.ts index f310ccea07..31cb004e9a 100644 --- a/packages/core/src/view/mixins/EditingMixin.type.ts +++ b/packages/core/src/view/mixins/EditingMixin.type.ts @@ -18,8 +18,8 @@ import type Cell from '../cell/Cell'; import type InternalMouseEvent from '../event/InternalMouseEvent'; import type EventObject from '../event/EventObject'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * @default true */ diff --git a/packages/core/src/view/mixins/EventsMixin.ts b/packages/core/src/view/mixins/EventsMixin.ts index 7721e6a2ff..2c3e5189fe 100644 --- a/packages/core/src/view/mixins/EventsMixin.ts +++ b/packages/core/src/view/mixins/EventsMixin.ts @@ -41,11 +41,11 @@ import { convertPoint } from '../../util/styleUtils'; import { NONE } from '../../util/Constants'; import Client from '../../Client'; import type CellEditorHandler from '../plugins/CellEditorHandler'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type TooltipHandler from '../plugins/TooltipHandler'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'fireEvent' | 'isEnabled' | 'getCellAt' @@ -88,7 +88,7 @@ type PartialGraph = Pick< | 'isSwimlane' >; type PartialEvents = Pick< - Graph, + AbstractGraph, | 'mouseListeners' | 'lastTouchEvent' | 'doubleClickCounter' @@ -573,7 +573,7 @@ export const EventsMixin: PartialType = { }, fireMouseEvent(evtName, me, sender) { - sender = sender ?? (this as Graph); + sender = sender ?? (this as AbstractGraph); if (this.isEventSourceIgnored(evtName, me)) { const tooltipHandler = this.getPlugin('TooltipHandler'); diff --git a/packages/core/src/view/mixins/EventsMixin.type.ts b/packages/core/src/view/mixins/EventsMixin.type.ts index 6a0e2e868c..f7b98e2606 100644 --- a/packages/core/src/view/mixins/EventsMixin.type.ts +++ b/packages/core/src/view/mixins/EventsMixin.type.ts @@ -21,8 +21,8 @@ import type CellState from '../cell/CellState'; import type EventSource from '../event/EventSource'; import type Point from '../geometry/Point'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { mouseListeners: MouseListenerSet[]; lastTouchEvent: MouseEvent | null; doubleClickCounter: number; diff --git a/packages/core/src/view/mixins/FoldingMixin.ts b/packages/core/src/view/mixins/FoldingMixin.ts index 4cc84162fa..421d373974 100644 --- a/packages/core/src/view/mixins/FoldingMixin.ts +++ b/packages/core/src/view/mixins/FoldingMixin.ts @@ -20,11 +20,11 @@ import InternalEvent from '../event/InternalEvent'; import Geometry from '../geometry/Geometry'; import { toRadians } from '../../util/mathUtils'; import Rectangle from '../geometry/Rectangle'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import { isI18nEnabled } from '../../internal/i18n-utils'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'getDataModel' | 'fireEvent' | 'getCurrentCellStyle' @@ -38,7 +38,7 @@ type PartialGraph = Pick< | 'options' >; type PartialFolding = Pick< - Graph, + AbstractGraph, | 'collapseExpandResource' | 'getCollapseExpandResource' | 'isFoldingEnabled' diff --git a/packages/core/src/view/mixins/FoldingMixin.type.ts b/packages/core/src/view/mixins/FoldingMixin.type.ts index 35917111c7..bc1506a2b2 100644 --- a/packages/core/src/view/mixins/FoldingMixin.type.ts +++ b/packages/core/src/view/mixins/FoldingMixin.type.ts @@ -19,8 +19,8 @@ import type CellState from '../cell/CellState'; import type ImageBox from '../image/ImageBox'; import type Geometry from '../geometry/Geometry'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Specifies the resource key for the tooltip on the collapse/expand icon. * If the resource for this key does not exist then the value is used as diff --git a/packages/core/src/view/mixins/GroupingMixin.ts b/packages/core/src/view/mixins/GroupingMixin.ts index 79f43be2d4..f4eec9d6b8 100644 --- a/packages/core/src/view/mixins/GroupingMixin.ts +++ b/packages/core/src/view/mixins/GroupingMixin.ts @@ -21,10 +21,10 @@ import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; import Rectangle from '../geometry/Rectangle'; import type Point from '../geometry/Point'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'getDataModel' | 'fireEvent' | 'getView' @@ -49,7 +49,7 @@ type PartialGraph = Pick< | 'getActualStartSize' >; type PartialGrouping = Pick< - Graph, + AbstractGraph, | 'groupCells' | 'getCellsForGroup' | 'getBoundsForGroup' diff --git a/packages/core/src/view/mixins/GroupingMixin.type.ts b/packages/core/src/view/mixins/GroupingMixin.type.ts index 59a788ee01..163c9b04f2 100644 --- a/packages/core/src/view/mixins/GroupingMixin.type.ts +++ b/packages/core/src/view/mixins/GroupingMixin.type.ts @@ -17,8 +17,8 @@ limitations under the License. import type Cell from '../cell/Cell'; import type Rectangle from '../geometry/Rectangle'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Adds the cells into the given group. * The change is carried out using {@link cellsAdded}, {@link cellsMoved} and {@link cellsResized}. diff --git a/packages/core/src/view/mixins/ImageMixin.ts b/packages/core/src/view/mixins/ImageMixin.ts index 712be72cfe..e17c94362b 100644 --- a/packages/core/src/view/mixins/ImageMixin.ts +++ b/packages/core/src/view/mixins/ImageMixin.ts @@ -14,11 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type ImageBundle from '../image/ImageBundle'; type PartialImage = Pick< - Graph, + AbstractGraph, 'imageBundles' | 'addImageBundle' | 'removeImageBundle' | 'getImageFromBundles' >; type PartialType = PartialImage; diff --git a/packages/core/src/view/mixins/ImageMixin.type.ts b/packages/core/src/view/mixins/ImageMixin.type.ts index 30a0fbb292..eaab8f1b3b 100644 --- a/packages/core/src/view/mixins/ImageMixin.type.ts +++ b/packages/core/src/view/mixins/ImageMixin.type.ts @@ -16,8 +16,8 @@ limitations under the License. import type ImageBundle from '../image/ImageBundle'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { imageBundles: ImageBundle[]; /** diff --git a/packages/core/src/view/mixins/LabelMixin.ts b/packages/core/src/view/mixins/LabelMixin.ts index a819746d43..e40aa25694 100644 --- a/packages/core/src/view/mixins/LabelMixin.ts +++ b/packages/core/src/view/mixins/LabelMixin.ts @@ -14,10 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'convertValueToString' | 'getCurrentCellStyle' | 'isCellLocked' @@ -25,7 +25,7 @@ type PartialGraph = Pick< | 'isVertexLabelsMovable' >; type PartialLabel = Pick< - Graph, + AbstractGraph, | 'labelsVisible' | 'htmlLabels' | 'getLabel' diff --git a/packages/core/src/view/mixins/LabelMixin.type.ts b/packages/core/src/view/mixins/LabelMixin.type.ts index 02ba5645d4..bb86bd45e6 100644 --- a/packages/core/src/view/mixins/LabelMixin.type.ts +++ b/packages/core/src/view/mixins/LabelMixin.type.ts @@ -16,8 +16,8 @@ limitations under the License. import type Cell from '../cell/Cell'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Specifies if labels should be visible. This is used in {@link getLabel}. * @default true diff --git a/packages/core/src/view/mixins/OrderMixin.ts b/packages/core/src/view/mixins/OrderMixin.ts index cd2fc8b920..09eab6790e 100644 --- a/packages/core/src/view/mixins/OrderMixin.ts +++ b/packages/core/src/view/mixins/OrderMixin.ts @@ -17,14 +17,14 @@ limitations under the License. import { sortCells } from '../../util/styleUtils'; import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../cell/Cell'; type PartialGraph = Pick< - Graph, + AbstractGraph, 'fireEvent' | 'batchUpdate' | 'getDataModel' | 'getSelectionCells' >; -type PartialOrder = Pick; +type PartialOrder = Pick; type PartialType = PartialGraph & PartialOrder; // @ts-expect-error The properties of PartialGraph are defined elsewhere. diff --git a/packages/core/src/view/mixins/OrderMixin.type.ts b/packages/core/src/view/mixins/OrderMixin.type.ts index 933632c2ca..1fb7ecf79f 100644 --- a/packages/core/src/view/mixins/OrderMixin.type.ts +++ b/packages/core/src/view/mixins/OrderMixin.type.ts @@ -16,8 +16,8 @@ limitations under the License. import type Cell from '../cell/Cell'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Moves the given cells to the front or back. The change is carried out using {@link cellsOrdered}. * diff --git a/packages/core/src/view/mixins/OverlaysMixin.ts b/packages/core/src/view/mixins/OverlaysMixin.ts index 99daa63b1d..4d11227fd4 100644 --- a/packages/core/src/view/mixins/OverlaysMixin.ts +++ b/packages/core/src/view/mixins/OverlaysMixin.ts @@ -18,10 +18,10 @@ import CellOverlay from '../cell/CellOverlay'; import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; import type InternalMouseEvent from '../event/InternalMouseEvent'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'getView' | 'fireEvent' | 'getDataModel' @@ -31,7 +31,7 @@ type PartialGraph = Pick< | 'setSelectionCell' >; type PartialOverlays = Pick< - Graph, + AbstractGraph, | 'addCellOverlay' | 'getCellOverlays' | 'removeCellOverlay' diff --git a/packages/core/src/view/mixins/OverlaysMixin.type.ts b/packages/core/src/view/mixins/OverlaysMixin.type.ts index 0ff21e19e1..e5c5a265b4 100644 --- a/packages/core/src/view/mixins/OverlaysMixin.type.ts +++ b/packages/core/src/view/mixins/OverlaysMixin.type.ts @@ -18,8 +18,8 @@ import type Cell from '../cell/Cell'; import type CellOverlay from '../cell/CellOverlay'; import type Image from '../image/ImageBox'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Adds an {@link CellOverlay} for the specified cell. * diff --git a/packages/core/src/view/mixins/PageBreaksMixin.ts b/packages/core/src/view/mixins/PageBreaksMixin.ts index 3545dddcef..1877062bad 100644 --- a/packages/core/src/view/mixins/PageBreaksMixin.ts +++ b/packages/core/src/view/mixins/PageBreaksMixin.ts @@ -17,10 +17,10 @@ limitations under the License. import Rectangle from '../geometry/Rectangle'; import Point from '../geometry/Point'; import PolylineShape from '../geometry/edge/PolylineShape'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'getView' | 'getGraphBounds' | 'getPageFormat' @@ -31,7 +31,7 @@ type PartialGraph = Pick< | 'isPageBreakDashed' >; type PartialPageBreaks = Pick< - Graph, + AbstractGraph, 'horizontalPageBreaks' | 'verticalPageBreaks' | 'updatePageBreaks' >; type PartialType = PartialGraph & PartialPageBreaks; diff --git a/packages/core/src/view/mixins/PageBreaksMixin.type.ts b/packages/core/src/view/mixins/PageBreaksMixin.type.ts index f37b5faaaa..0c4dcce96e 100644 --- a/packages/core/src/view/mixins/PageBreaksMixin.type.ts +++ b/packages/core/src/view/mixins/PageBreaksMixin.type.ts @@ -18,8 +18,8 @@ limitations under the License. // TS2436: Ambient module declaration cannot specify relative module name. export {}; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** @default null */ horizontalPageBreaks: any[] | null; diff --git a/packages/core/src/view/mixins/PanningMixin.ts b/packages/core/src/view/mixins/PanningMixin.ts index 1ccb354c41..2b24926cb2 100644 --- a/packages/core/src/view/mixins/PanningMixin.ts +++ b/packages/core/src/view/mixins/PanningMixin.ts @@ -18,14 +18,17 @@ import { hasScrollbars } from '../../util/styleUtils'; import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; import type PanningHandler from '../plugins/PanningHandler'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import Rectangle from '../geometry/Rectangle'; import Point from '../geometry/Point'; import type SelectionCellsHandler from '../plugins/SelectionCellsHandler'; -type PartialGraph = Pick; +type PartialGraph = Pick< + AbstractGraph, + 'getContainer' | 'getView' | 'getPlugin' | 'fireEvent' +>; type PartialPanning = Pick< - Graph, + AbstractGraph, | 'shiftPreview1' | 'shiftPreview2' | 'useScrollbarsForPanning' diff --git a/packages/core/src/view/mixins/PanningMixin.type.ts b/packages/core/src/view/mixins/PanningMixin.type.ts index f16a28c648..8249d7aec1 100644 --- a/packages/core/src/view/mixins/PanningMixin.type.ts +++ b/packages/core/src/view/mixins/PanningMixin.type.ts @@ -17,8 +17,8 @@ limitations under the License. import type Cell from '../cell/Cell'; import type Rectangle from '../geometry/Rectangle'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** @default null */ shiftPreview1: HTMLElement | null; diff --git a/packages/core/src/view/mixins/PortsMixin.ts b/packages/core/src/view/mixins/PortsMixin.ts index 5b52fa49a5..b19fef9be8 100644 --- a/packages/core/src/view/mixins/PortsMixin.ts +++ b/packages/core/src/view/mixins/PortsMixin.ts @@ -14,10 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; type PartialPorts = Pick< - Graph, + AbstractGraph, 'portsEnabled' | 'isPort' | 'getTerminalForPort' | 'isPortsEnabled' | 'setPortsEnabled' >; type PartialType = PartialPorts; diff --git a/packages/core/src/view/mixins/PortsMixin.type.ts b/packages/core/src/view/mixins/PortsMixin.type.ts index 2e730248de..b39fbbe3b2 100644 --- a/packages/core/src/view/mixins/PortsMixin.type.ts +++ b/packages/core/src/view/mixins/PortsMixin.type.ts @@ -16,8 +16,8 @@ limitations under the License. import type Cell from '../cell/Cell'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Specifies if ports are enabled. This is used in {@link cellConnected} to update the respective style. * @default true diff --git a/packages/core/src/view/mixins/SelectionMixin.ts b/packages/core/src/view/mixins/SelectionMixin.ts index 3a55487cc2..70d8a39b1a 100644 --- a/packages/core/src/view/mixins/SelectionMixin.ts +++ b/packages/core/src/view/mixins/SelectionMixin.ts @@ -18,10 +18,10 @@ import Cell from '../cell/Cell'; import Dictionary from '../../util/Dictionary'; import RootChange from '../undoable_changes/RootChange'; import ChildChange from '../undoable_changes/ChildChange'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'getDataModel' | 'getView' | 'isCellSelectable' @@ -32,7 +32,7 @@ type PartialGraph = Pick< | 'isToggleEvent' >; type PartialCells = Pick< - Graph, + AbstractGraph, | 'singleSelection' | 'selectionModel' | 'getSelectionModel' diff --git a/packages/core/src/view/mixins/SelectionMixin.type.ts b/packages/core/src/view/mixins/SelectionMixin.type.ts index 5ab30cd565..651b591116 100644 --- a/packages/core/src/view/mixins/SelectionMixin.type.ts +++ b/packages/core/src/view/mixins/SelectionMixin.type.ts @@ -18,8 +18,8 @@ import type Cell from '../cell/Cell'; import type GraphSelectionModel from '../GraphSelectionModel'; import type Rectangle from '../geometry/Rectangle'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { cells: Cell[]; doneResource: string; updatingSelectionResource: string; diff --git a/packages/core/src/view/mixins/SnapMixin.ts b/packages/core/src/view/mixins/SnapMixin.ts index 80256edbda..a50983cbb0 100644 --- a/packages/core/src/view/mixins/SnapMixin.ts +++ b/packages/core/src/view/mixins/SnapMixin.ts @@ -14,11 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; -type PartialGraph = Pick; +type PartialGraph = Pick; type PartialSnap = Pick< - Graph, + AbstractGraph, | 'snapTolerance' | 'gridSize' | 'gridEnabled' diff --git a/packages/core/src/view/mixins/SnapMixin.type.ts b/packages/core/src/view/mixins/SnapMixin.type.ts index abbb73d4b7..92a440dc87 100644 --- a/packages/core/src/view/mixins/SnapMixin.type.ts +++ b/packages/core/src/view/mixins/SnapMixin.type.ts @@ -17,8 +17,8 @@ limitations under the License. import type Point from '../geometry/Point'; import type Rectangle from '../geometry/Rectangle'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * @default 0 */ diff --git a/packages/core/src/view/mixins/SwimlaneMixin.ts b/packages/core/src/view/mixins/SwimlaneMixin.ts index eeef275f5e..a1668c28c6 100644 --- a/packages/core/src/view/mixins/SwimlaneMixin.ts +++ b/packages/core/src/view/mixins/SwimlaneMixin.ts @@ -19,11 +19,11 @@ import { convertPoint } from '../../util/styleUtils'; import { mod } from '../../util/mathUtils'; import { DEFAULT_STARTSIZE, DIRECTION, SHAPE } from '../../util/Constants'; import { getClientX, getClientY } from '../../util/EventUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type { DirectionValue } from '../../types'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'getDefaultParent' | 'getCurrentRoot' | 'getDataModel' @@ -37,7 +37,7 @@ type PartialGraph = Pick< | 'getPanDy' >; type PartialSwimlane = Pick< - Graph, + AbstractGraph, | 'swimlaneSelectionEnabled' | 'swimlaneNesting' | 'swimlaneIndicatorColorAttribute' diff --git a/packages/core/src/view/mixins/SwimlaneMixin.type.ts b/packages/core/src/view/mixins/SwimlaneMixin.type.ts index 58ec1c539a..1418949a17 100644 --- a/packages/core/src/view/mixins/SwimlaneMixin.type.ts +++ b/packages/core/src/view/mixins/SwimlaneMixin.type.ts @@ -18,8 +18,8 @@ import type Cell from '../cell/Cell'; import type Rectangle from '../geometry/Rectangle'; import type { CellStateStyle, DirectionValue } from '../../types'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Specifies if swimlanes should be selectable via the content if the mouse is released. * @default true diff --git a/packages/core/src/view/mixins/TerminalMixin.ts b/packages/core/src/view/mixins/TerminalMixin.ts index 95d051b653..bff8a1c85c 100644 --- a/packages/core/src/view/mixins/TerminalMixin.ts +++ b/packages/core/src/view/mixins/TerminalMixin.ts @@ -16,10 +16,10 @@ limitations under the License. import type Cell from '../cell/Cell'; import Dictionary from '../../util/Dictionary'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; -type PartialGraph = Pick; -type PartialTerminal = Pick; +type PartialGraph = Pick; +type PartialTerminal = Pick; type PartialType = PartialGraph & PartialTerminal; // @ts-expect-error The properties of PartialGraph are defined elsewhere. diff --git a/packages/core/src/view/mixins/TerminalMixin.type.ts b/packages/core/src/view/mixins/TerminalMixin.type.ts index 195b397cce..e2af2ef62a 100644 --- a/packages/core/src/view/mixins/TerminalMixin.type.ts +++ b/packages/core/src/view/mixins/TerminalMixin.type.ts @@ -16,8 +16,8 @@ limitations under the License. import type Cell from '../cell/Cell'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Returns true if the given terminal point is movable. This is independent * from {@link isCellConnectable} and {@link isCellDisconnectable} and controls if terminal diff --git a/packages/core/src/view/mixins/TooltipMixin.ts b/packages/core/src/view/mixins/TooltipMixin.ts index 46b3b8760e..a01fa19504 100644 --- a/packages/core/src/view/mixins/TooltipMixin.ts +++ b/packages/core/src/view/mixins/TooltipMixin.ts @@ -17,16 +17,19 @@ limitations under the License. import { htmlEntities } from '../../util/StringUtils'; import type Shape from '../geometry/Shape'; import type Cell from '../cell/Cell'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type SelectionCellsHandler from '../plugins/SelectionCellsHandler'; import type TooltipHandler from '../plugins/TooltipHandler'; import { translate } from '../../internal/i18n-utils'; type PartialGraph = Pick< - Graph, + AbstractGraph, 'convertValueToString' | 'getPlugin' | 'getCollapseExpandResource' >; -type PartialTooltip = Pick; +type PartialTooltip = Pick< + AbstractGraph, + 'getTooltip' | 'getTooltipForCell' | 'setTooltips' +>; type PartialType = PartialGraph & PartialTooltip; // @ts-expect-error The properties of PartialGraph are defined elsewhere. diff --git a/packages/core/src/view/mixins/TooltipMixin.type.ts b/packages/core/src/view/mixins/TooltipMixin.type.ts index 4b140b3d6a..1cd5b1e4a2 100644 --- a/packages/core/src/view/mixins/TooltipMixin.type.ts +++ b/packages/core/src/view/mixins/TooltipMixin.type.ts @@ -17,8 +17,8 @@ limitations under the License. import type CellState from '../cell/CellState'; import type Cell from '../cell/Cell'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Returns the string or DOM node that represents the tooltip for the given * state, node and coordinate pair. This implementation checks if the given diff --git a/packages/core/src/view/mixins/ValidationMixin.ts b/packages/core/src/view/mixins/ValidationMixin.ts index e124b11354..457ec240bd 100644 --- a/packages/core/src/view/mixins/ValidationMixin.ts +++ b/packages/core/src/view/mixins/ValidationMixin.ts @@ -16,11 +16,11 @@ limitations under the License. import type Cell from '../cell/Cell'; import { isNode } from '../../util/domUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import { translate } from '../../internal/i18n-utils'; type PartialGraph = Pick< - Graph, + AbstractGraph, | 'getDataModel' | 'isAllowLoops' | 'isMultigraph' @@ -33,7 +33,7 @@ type PartialGraph = Pick< | 'setCellWarning' >; type PartialValidation = Pick< - Graph, + AbstractGraph, | 'multiplicities' | 'validationAlert' | 'isEdgeValid' @@ -104,7 +104,7 @@ export const ValidationMixin: PartialType = { // Checks the change against each multiplicity rule for (const multiplicity of this.multiplicities) { const err = multiplicity.check( - (this), // needs to cast to Graph + (this), // needs to cast to Graph edge, source, target, diff --git a/packages/core/src/view/mixins/ValidationMixin.type.ts b/packages/core/src/view/mixins/ValidationMixin.type.ts index 826e193aa0..6d11efda66 100644 --- a/packages/core/src/view/mixins/ValidationMixin.type.ts +++ b/packages/core/src/view/mixins/ValidationMixin.type.ts @@ -18,8 +18,8 @@ import type Multiplicity from '../other/Multiplicity'; import type Cell from '../cell/Cell'; import type CellState from '../cell/CellState'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { multiplicities: Multiplicity[]; /** diff --git a/packages/core/src/view/mixins/VertexMixin.ts b/packages/core/src/view/mixins/VertexMixin.ts index 3fba4e536c..57c4dccd1f 100644 --- a/packages/core/src/view/mixins/VertexMixin.ts +++ b/packages/core/src/view/mixins/VertexMixin.ts @@ -16,12 +16,12 @@ limitations under the License. import Cell from '../cell/Cell'; import Geometry from '../geometry/Geometry'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type { CellStyle } from '../../types'; -type PartialGraph = Pick; +type PartialGraph = Pick; type PartialVertex = Pick< - Graph, + AbstractGraph, | 'vertexLabelsMovable' | 'allowNegativeCoordinates' | 'isAllowNegativeCoordinates' diff --git a/packages/core/src/view/mixins/VertexMixin.type.ts b/packages/core/src/view/mixins/VertexMixin.type.ts index 0e7952ffdc..a8651a4846 100644 --- a/packages/core/src/view/mixins/VertexMixin.type.ts +++ b/packages/core/src/view/mixins/VertexMixin.type.ts @@ -18,8 +18,8 @@ import type Cell from '../cell/Cell'; import type { CellStyle, VertexParameters } from '../../types'; import type Geometry from '../geometry/Geometry'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Specifies the return value for vertices in {@link isLabelMovable}. * @default false diff --git a/packages/core/src/view/mixins/ZoomMixin.ts b/packages/core/src/view/mixins/ZoomMixin.ts index 7777eed9e7..c82634e00c 100644 --- a/packages/core/src/view/mixins/ZoomMixin.ts +++ b/packages/core/src/view/mixins/ZoomMixin.ts @@ -16,14 +16,14 @@ limitations under the License. import Rectangle from '../geometry/Rectangle'; import { hasScrollbars } from '../../util/styleUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; type PartialGraph = Pick< - Graph, + AbstractGraph, 'getView' | 'getSelectionCell' | 'getContainer' | 'scrollRectToVisible' >; type PartialZoom = Pick< - Graph, + AbstractGraph, | 'zoomFactor' | 'keepSelectionVisibleOnZoom' | 'centerZoom' diff --git a/packages/core/src/view/mixins/ZoomMixin.type.ts b/packages/core/src/view/mixins/ZoomMixin.type.ts index 4bf3436eec..0cc6e491f3 100644 --- a/packages/core/src/view/mixins/ZoomMixin.type.ts +++ b/packages/core/src/view/mixins/ZoomMixin.type.ts @@ -16,8 +16,8 @@ limitations under the License. import type Rectangle from '../geometry/Rectangle'; -declare module '../Graph' { - interface Graph { +declare module '../AbstractGraph' { + interface AbstractGraph { /** * Specifies the factor used for {@link zoomIn} and {@link zoomOut}. * @default 1.2 (120%) diff --git a/packages/core/src/view/mixins/_graph-mixins-apply.ts b/packages/core/src/view/mixins/_graph-mixins-apply.ts index 0de63c1595..a4bee47a13 100644 --- a/packages/core/src/view/mixins/_graph-mixins-apply.ts +++ b/packages/core/src/view/mixins/_graph-mixins-apply.ts @@ -15,7 +15,7 @@ limitations under the License. */ import { mixInto } from '../../internal/utils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import { CellsMixin } from './CellsMixin'; import { ConnectionsMixin } from './ConnectionsMixin'; import { DragDropMixin } from './DragDropMixin'; @@ -40,7 +40,7 @@ import { ValidationMixin } from './ValidationMixin'; import { VertexMixin } from './VertexMixin'; import { ZoomMixin } from './ZoomMixin'; -export const applyGraphMixins = (target: typeof Graph) => { +export const applyGraphMixins = (target: typeof AbstractGraph) => { const mixIntoGraph = mixInto(target); // Apply the mixins in alphabetic order to ease maintenance. diff --git a/packages/core/src/view/other/AutoSaveManager.ts b/packages/core/src/view/other/AutoSaveManager.ts index 548fdb7d47..54f42046b9 100644 --- a/packages/core/src/view/other/AutoSaveManager.ts +++ b/packages/core/src/view/other/AutoSaveManager.ts @@ -18,7 +18,7 @@ limitations under the License. import EventSource from '../event/EventSource'; import InternalEvent from '../event/InternalEvent'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; /** * Manager for automatically saving diagrams. The hook must be @@ -33,7 +33,7 @@ import type { Graph } from '../Graph'; * ``` */ class AutoSaveManager extends EventSource { - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(); // Notifies the manager of a change @@ -47,9 +47,9 @@ class AutoSaveManager extends EventSource { } /** - * Reference to the enclosing {@link Graph}. + * Reference to the enclosing {@link AbstractGraph}. */ - graph: Graph | null = null; + graph: AbstractGraph | null = null; /** * Minimum amount of seconds between two consecutive autosaves. Eg. a @@ -115,7 +115,7 @@ class AutoSaveManager extends EventSource { /** * Sets the graph that the layouts operate on. */ - setGraph(graph: Graph | null): void { + setGraph(graph: AbstractGraph | null): void { if (this.graph != null) { this.graph.getDataModel().removeListener(this.changeHandler); } diff --git a/packages/core/src/view/other/DragSource.ts b/packages/core/src/view/other/DragSource.ts index 8d2f085aef..e5a3442b46 100644 --- a/packages/core/src/view/other/DragSource.ts +++ b/packages/core/src/view/other/DragSource.ts @@ -40,12 +40,12 @@ import { } from '../../util/EventUtils'; import EventSource from '../event/EventSource'; import EventObject from '../event/EventObject'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type Cell from '../cell/Cell'; import type SelectionHandler from '../plugins/SelectionHandler'; export type DropHandler = ( - graph: Graph, + graph: AbstractGraph, evt: MouseEvent, cell: Cell | null, x?: number, @@ -128,9 +128,9 @@ class DragSource { enabled = true; /** - * Reference to the {@link mxGraph} that is the current drop target. + * Reference to the {@link AbstractGraph} that is the current drop target. */ - currentGraph: Graph | null = null; + currentGraph: AbstractGraph | null = null; /** * Holds the current drop target under the mouse. @@ -143,12 +143,12 @@ class DragSource { currentPoint: Point | null = null; /** - * Holds an {@link mxGuide} for the {@link currentGraph} if {@link dragPreview} is not null. + * Holds an {@link Guide} for the {@link currentGraph} if {@link dragPreview} is not null. */ currentGuide: Guide | null = null; /** - * Holds an {@link mxGuide} for the {@link currentGraph} if {@link dragPreview} is not null. + * Holds an {@link Guide} for the {@link currentGraph} if {@link dragPreview} is not null. * @note wrong doc */ currentHighlight: CellHighlight | null = null; @@ -159,7 +159,7 @@ class DragSource { autoscroll = true; /** - * Specifies if {@link mxGuide} should be enabled. Default is true. + * Specifies if {@link Guide} should be enabled. Default is true. */ guidesEnabled = true; @@ -246,9 +246,9 @@ class DragSource { /** * Returns the drop target for the given graph and coordinates. This - * implementation uses {@link mxGraph.getCellAt}. + * implementation uses {@link AbstractGraph.getCellAt}. */ - getDropTarget(graph: Graph, x: number, y: number, evt: MouseEvent) { + getDropTarget(graph: AbstractGraph, x: number, y: number, evt: MouseEvent) { return graph.getCellAt(x, y); } @@ -264,7 +264,7 @@ class DragSource { * Creates and returns an element which can be used as a preview in the given * graph. */ - createPreviewElement(graph: Graph): HTMLElement | null { + createPreviewElement(graph: AbstractGraph): HTMLElement | null { return null; } @@ -385,7 +385,7 @@ class DragSource { /** * Returns true if the given graph contains the given event. */ - graphContainsEvent(graph: Graph, evt: MouseEvent) { + graphContainsEvent(graph: AbstractGraph, evt: MouseEvent) { const x = getClientX(evt); const y = getClientY(evt); const offset = getOffset(graph.container); @@ -523,7 +523,7 @@ class DragSource { /** * Actives the given graph as a drop target. */ - dragEnter(graph: Graph, evt: MouseEvent) { + dragEnter(graph: AbstractGraph, evt: MouseEvent) { graph.isMouseDown = true; graph.isMouseTrigger = isMouseEvent(evt); this.previewElement = this.createPreviewElement(graph); @@ -549,7 +549,7 @@ class DragSource { /** * Deactivates the given graph as a drop target. */ - dragExit(graph: Graph, evt?: MouseEvent) { + dragExit(graph: AbstractGraph, evt?: MouseEvent) { this.currentDropTarget = null; this.currentPoint = null; graph.isMouseDown = false; @@ -580,7 +580,7 @@ class DragSource { * Implements autoscroll, updates the {@link currentPoint}, highlights any drop * targets and updates the preview. */ - dragOver(graph: Graph, evt: MouseEvent) { + dragOver(graph: AbstractGraph, evt: MouseEvent) { const offset = getOffset(graph.container); const origin = getScrollOrigin(graph.container); let x = getClientX(evt) - offset.x + origin.x - graph.getPanDx(); @@ -650,10 +650,10 @@ class DragSource { /** * Returns the drop target for the given graph and coordinates. This - * implementation uses {@link mxGraph.getCellAt}. + * implementation uses {@link AbstractGraph.getCellAt}. */ drop( - graph: Graph, + graph: AbstractGraph, evt: MouseEvent, dropTarget: Cell | null = null, x: number, diff --git a/packages/core/src/view/other/Guide.ts b/packages/core/src/view/other/Guide.ts index abbbcb5e7f..330087532d 100644 --- a/packages/core/src/view/other/Guide.ts +++ b/packages/core/src/view/other/Guide.ts @@ -22,7 +22,7 @@ import PolylineShape from '../geometry/edge/PolylineShape'; import type CellState from '../cell/CellState'; import Shape from '../geometry/Shape'; import Rectangle from '../geometry/Rectangle'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; /** * Implements the alignment of selection cells to other cells in the graph. @@ -32,7 +32,7 @@ import type { Graph } from '../Graph'; * Constructs a new guide object. */ class Guide { - constructor(graph: Graph, states: CellState[]) { + constructor(graph: AbstractGraph, states: CellState[]) { this.graph = graph; this.setStates(states); } @@ -40,7 +40,7 @@ class Guide { /** * Reference to the enclosing {@link Graph} instance. */ - graph: Graph; + graph: AbstractGraph; /** * Contains the {@link CellStates} that are used for alignment. diff --git a/packages/core/src/view/other/Multiplicity.ts b/packages/core/src/view/other/Multiplicity.ts index fd9fb6fd1c..b10fb806f5 100644 --- a/packages/core/src/view/other/Multiplicity.ts +++ b/packages/core/src/view/other/Multiplicity.ts @@ -18,7 +18,7 @@ limitations under the License. import { isNode } from '../../util/domUtils'; import type Cell from '../cell/Cell'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import { translate } from '../../internal/i18n-utils'; /** @@ -132,15 +132,15 @@ class Multiplicity { * Checks the multiplicity for the given arguments and returns the error * for the given connection or null if the multiplicity does not apply. * - * @param graph Reference to the enclosing {@link graph} instance. - * @param edge {@link mxCell} that represents the edge to validate. - * @param source {@link mxCell} that represents the source terminal. - * @param target {@link mxCell} that represents the target terminal. + * @param graph Reference to the enclosing {@link AbstractGraph} instance. + * @param edge {@link Cell} that represents the edge to validate. + * @param source {@link Cell} that represents the source terminal. + * @param target {@link Cell} that represents the target terminal. * @param sourceOut Number of outgoing edges from the source terminal. * @param targetIn Number of incoming edges for the target terminal. */ check( - graph: Graph, + graph: AbstractGraph, edge: Cell, source: Cell, target: Cell, @@ -181,7 +181,7 @@ class Multiplicity { * Checks if there are any valid neighbours in {@link validNeighbors}. This is only * called if {@link validNeighbors} is a non-empty array. */ - checkNeighbors(graph: Graph, edge: Cell, source: Cell, target: Cell): boolean { + checkNeighbors(graph: AbstractGraph, edge: Cell, source: Cell, target: Cell): boolean { const sourceValue = source.getValue(); const targetValue = target.getValue(); let isValid = !this.validNeighborsAllowed; @@ -205,7 +205,7 @@ class Multiplicity { * given cell is the source or target of the given edge, depending on * {@link source}. This implementation uses {@link checkType} on the terminal's value. */ - checkTerminal(graph: Graph, edge: Cell, terminal: Cell): boolean { + checkTerminal(graph: AbstractGraph, edge: Cell, terminal: Cell): boolean { const value = terminal?.getValue() ?? null; return this.checkType(graph, value, this.type, this.attr, this.value); @@ -215,7 +215,7 @@ class Multiplicity { * Checks the type of the given value. */ checkType( - graph: Graph, + graph: AbstractGraph, value: string | Element | Cell, type: string, attr?: string, diff --git a/packages/core/src/view/other/Outline.ts b/packages/core/src/view/other/Outline.ts index a170062389..74776a5b33 100644 --- a/packages/core/src/view/other/Outline.ts +++ b/packages/core/src/view/other/Outline.ts @@ -27,7 +27,8 @@ import { import Point from '../geometry/Point'; import Rectangle from '../geometry/Rectangle'; import RectangleShape from '../geometry/node/RectangleShape'; -import { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; +import { BaseGraph } from '../BaseGraph'; import ImageShape from '../geometry/node/ImageShape'; import InternalEvent from '../event/InternalEvent'; import Image from '../image/ImageBox'; @@ -36,7 +37,6 @@ import { getSource, isMouseEvent } from '../../util/EventUtils'; import EventSource from '../event/EventSource'; import { hasScrollbars } from '../../util/styleUtils'; import { Listenable } from '../../types'; -import { getDefaultPlugins } from '../plugins'; /** * Implements an outline (aka overview) for a graph. Set {@link updateOnPan} to true @@ -79,7 +79,7 @@ import { getDefaultPlugins } from '../plugins'; * ``` */ class Outline { - constructor(source: Graph, container: HTMLElement | null = null) { + constructor(source: AbstractGraph, container: HTMLElement | null = null) { this.source = source; if (container != null) { @@ -231,14 +231,14 @@ class Outline { index: number | null = null; /** - * Reference to the source {@link graph}. + * Reference to the source {@link AbstractGraph}. */ - source: Graph; + source: AbstractGraph; /** - * Reference to the {@link graph} that renders the outline. + * Reference to the {@link AbstractGraph} that renders the outline. */ - outline: Graph | null = null; + outline: AbstractGraph | null = null; /** * Renderhint to be used for the outline graph. @@ -315,23 +315,28 @@ class Outline { suspended = false; /** - * Creates the {@link graph} used in the outline. + * Creates the {@link AbstractGraph} used in the outline. */ - createGraph(container: HTMLElement): Graph { - const graph = new Graph( - container, - this.source.getDataModel(), - // TODO review the list of plugins for the Graph of an Outline - // we could pass an empty array or a selection of plugins - // it may be necessary to make the plugins array configurable to allow custom plugins and improve tree-shaking - getDefaultPlugins(), - this.source.getStylesheet() - ); + createGraph(container: HTMLElement): AbstractGraph { + const graph = this.createGraphInstance(container); graph.options.foldingEnabled = false; graph.autoScroll = false; return graph; } + // TODO decide if we keept this method or if only keep createGraph + protected createGraphInstance(container: HTMLElement): AbstractGraph { + // We don't need plugins here, and the Graph used here use the same globally registered style elements as the source Graph + // TODO review the list of plugins for the Graph of an Outline + // we could pass a selection of plugins if we see that Outline relies on some optional features (to validate with the OrgChart stories when it works) + // users that want to change the plugins list can override this method + return new BaseGraph({ + container, + model: this.source.getDataModel(), + stylesheet: this.source.getStylesheet(), + }); + } + /** * Returns true if events are handled. This implementation * returns {@link enabled}. diff --git a/packages/core/src/view/other/PanningManager.ts b/packages/core/src/view/other/PanningManager.ts index 4e514aefc9..252494b4c7 100644 --- a/packages/core/src/view/other/PanningManager.ts +++ b/packages/core/src/view/other/PanningManager.ts @@ -22,13 +22,13 @@ import EventObject from '../event/EventObject'; import EventSource from '../event/EventSource'; import InternalEvent from '../event/InternalEvent'; import InternalMouseEvent from '../event/InternalMouseEvent'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; /** * Implements a handler for panning. */ class PanningManager { - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { this.thread = null; this.active = false; this.tdx = 0; diff --git a/packages/core/src/view/other/PrintPreview.ts b/packages/core/src/view/other/PrintPreview.ts index 5cf5a58d31..08e4143837 100644 --- a/packages/core/src/view/other/PrintPreview.ts +++ b/packages/core/src/view/other/PrintPreview.ts @@ -24,7 +24,7 @@ import Client from '../../Client'; import { intersects } from '../../util/mathUtils'; import { DIALECT } from '../../util/Constants'; import { addLinkToHead, write } from '../../util/domUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type CellState from '../cell/CellState'; import type Cell from '../cell/Cell'; import { GlobalConfig } from '../../util/config'; @@ -147,7 +147,7 @@ import { GlobalConfig } from '../../util/config'; */ class PrintPreview { constructor( - graph: Graph, + graph: AbstractGraph, scale: number | null = null, pageFormat: Rectangle | null = null, border: number | null = null, @@ -171,9 +171,9 @@ class PrintPreview { } /** - * Reference to the {@link graph} that should be previewed. + * Reference to the {@link AbstractGraph} that should be previewed. */ - graph: Graph; + graph: AbstractGraph; /** * Holds the {@link Rectangle} that defines the page format. @@ -311,7 +311,7 @@ class PrintPreview { * this is specified then no HEAD tag, CSS and BODY tag will be written. */ appendGraph( - graph: Graph, + graph: AbstractGraph, scale: number, x0: number, y0: number, diff --git a/packages/core/src/view/plugins/CellEditorHandler.ts b/packages/core/src/view/plugins/CellEditorHandler.ts index 06e232f95e..bb0c5a7293 100644 --- a/packages/core/src/view/plugins/CellEditorHandler.ts +++ b/packages/core/src/view/plugins/CellEditorHandler.ts @@ -48,7 +48,7 @@ import { } from '../../util/EventUtils'; import EventSource from '../event/EventSource'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import type { GraphPlugin } from '../../types'; import type TooltipHandler from './TooltipHandler'; @@ -153,7 +153,7 @@ import type TooltipHandler from './TooltipHandler'; class CellEditorHandler implements GraphPlugin { static pluginId = 'CellEditorHandler'; - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { this.graph = graph; // Stops editing after zoom changes @@ -191,7 +191,7 @@ class CellEditorHandler implements GraphPlugin { /** * Reference to the enclosing {@link Graph}. */ - graph: Graph; + graph: AbstractGraph; /** * Holds the DIV that is used for text editing. Note that this may be null before the first diff --git a/packages/core/src/view/plugins/ConnectionHandler.ts b/packages/core/src/view/plugins/ConnectionHandler.ts index b000e85e5d..82874421b8 100644 --- a/packages/core/src/view/plugins/ConnectionHandler.ts +++ b/packages/core/src/view/plugins/ConnectionHandler.ts @@ -54,7 +54,7 @@ import { } from '../../util/EventUtils'; import Image from '../image/ImageBox'; import CellState from '../cell/CellState'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import ConnectionConstraint from '../other/ConnectionConstraint'; import Shape from '../geometry/Shape'; import type { CellStyle, ColorValue, GraphPlugin, Listenable } from '../../types'; @@ -217,7 +217,7 @@ class ConnectionHandler extends EventSource implements GraphPlugin { /** * Reference to the enclosing {@link Graph}. */ - graph: Graph; + graph: AbstractGraph; /** * Function that is used for creating new edges. The function takes the @@ -385,7 +385,7 @@ class ConnectionHandler extends EventSource implements GraphPlugin { * optional cell style from the preview as the third argument. It returns * the {@link Cell} that represents the new edge. */ - constructor(graph: Graph, factoryMethod: FactoryMethod | null = null) { + constructor(graph: AbstractGraph, factoryMethod: FactoryMethod | null = null) { super(); this.graph = graph; @@ -1988,7 +1988,7 @@ class ConnectionHandlerCellMarker extends CellMarker { hotspotEnabled = true; constructor( - graph: Graph, + graph: AbstractGraph, connectionHandler: ConnectionHandler, validColor: ColorValue = DEFAULT_VALID_COLOR, invalidColor: ColorValue = DEFAULT_INVALID_COLOR, diff --git a/packages/core/src/view/plugins/FitPlugin.ts b/packages/core/src/view/plugins/FitPlugin.ts index 6bf5a61201..a47e9f98b6 100644 --- a/packages/core/src/view/plugins/FitPlugin.ts +++ b/packages/core/src/view/plugins/FitPlugin.ts @@ -15,7 +15,7 @@ limitations under the License. */ import type { GraphPlugin } from '../../types'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; function keep2digits(value: number): number { return Number(value.toFixed(2)); @@ -54,7 +54,7 @@ export class FitPlugin implements GraphPlugin { * * @param graph Reference to the enclosing {@link Graph}. */ - constructor(private readonly graph: Graph) {} + constructor(private readonly graph: AbstractGraph) {} /** * Fit and center the graph within its container. diff --git a/packages/core/src/view/plugins/PanningHandler.ts b/packages/core/src/view/plugins/PanningHandler.ts index 09716350dc..e621da27bc 100644 --- a/packages/core/src/view/plugins/PanningHandler.ts +++ b/packages/core/src/view/plugins/PanningHandler.ts @@ -32,7 +32,7 @@ import PanningManager from '../other/PanningManager'; import InternalMouseEvent from '../event/InternalMouseEvent'; import type { GraphPlugin, MouseEventListener } from '../../types'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; /** * Event handler that pans and creates popupmenus. To use the left @@ -62,7 +62,7 @@ import type { Graph } from '../Graph'; class PanningHandler extends EventSource implements GraphPlugin { static pluginId = 'PanningHandler'; - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(); this.graph = graph; @@ -123,7 +123,7 @@ class PanningHandler extends EventSource implements GraphPlugin { /** * Reference to the enclosing {@link Graph}. */ - graph: Graph; + graph: AbstractGraph; panningManager: PanningManager; diff --git a/packages/core/src/view/plugins/PopupMenuHandler.ts b/packages/core/src/view/plugins/PopupMenuHandler.ts index 3fb1c4bcc5..4b2765b0fa 100644 --- a/packages/core/src/view/plugins/PopupMenuHandler.ts +++ b/packages/core/src/view/plugins/PopupMenuHandler.ts @@ -20,7 +20,7 @@ import MaxPopupMenu from '../../gui/MaxPopupMenu'; import InternalEvent from '../event/InternalEvent'; import { getScrollOrigin } from '../../util/styleUtils'; import { getMainEvent, isMultiTouchEvent } from '../../util/EventUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import InternalMouseEvent from '../event/InternalMouseEvent'; import type { GraphPlugin } from '../../types'; import type TooltipHandler from './TooltipHandler'; @@ -37,7 +37,7 @@ import EventObject from '../event/EventObject'; class PopupMenuHandler extends MaxPopupMenu implements GraphPlugin { static pluginId = 'PopupMenuHandler'; - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(); this.graph = graph; @@ -61,7 +61,7 @@ class PopupMenuHandler extends MaxPopupMenu implements GraphPlugin { /** * Reference to the enclosing {@link Graph}. */ - graph: Graph; + graph: AbstractGraph; /** * Specifies if cells should be selected if a popupmenu is displayed for diff --git a/packages/core/src/view/plugins/RubberBandHandler.ts b/packages/core/src/view/plugins/RubberBandHandler.ts index 71f28f733d..e0635b81a7 100644 --- a/packages/core/src/view/plugins/RubberBandHandler.ts +++ b/packages/core/src/view/plugins/RubberBandHandler.ts @@ -30,7 +30,7 @@ import Client from '../../Client'; import Rectangle from '../geometry/Rectangle'; import { isAltDown, isMultiTouchEvent } from '../../util/EventUtils'; import { clearSelection } from '../../util/domUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import { GraphPlugin } from '../../types'; import EventObject from '../event/EventObject'; import EventSource from '../event/EventSource'; @@ -60,7 +60,7 @@ import EventSource from '../event/EventSource'; class RubberBandHandler implements GraphPlugin { static pluginId = 'RubberBandHandler'; - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { this.graph = graph; this.graph.addMouseListener(this); @@ -101,7 +101,7 @@ class RubberBandHandler implements GraphPlugin { forceRubberbandHandler: Function; panHandler: Function; gestureHandler: Function; - graph: Graph; + graph: AbstractGraph; first: Point | null = null; destroyed = false; dragHandler: ((evt: MouseEvent) => void) | null = null; diff --git a/packages/core/src/view/plugins/SelectionCellsHandler.ts b/packages/core/src/view/plugins/SelectionCellsHandler.ts index 58096b7e46..86158992d6 100644 --- a/packages/core/src/view/plugins/SelectionCellsHandler.ts +++ b/packages/core/src/view/plugins/SelectionCellsHandler.ts @@ -21,7 +21,7 @@ import Dictionary from '../../util/Dictionary'; import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; import { sortCells } from '../../util/styleUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import Cell from '../cell/Cell'; import CellState from '../cell/CellState'; import type { GraphPlugin } from '../../types'; @@ -51,7 +51,7 @@ type Handler = EdgeHandler | VertexHandler; class SelectionCellsHandler extends EventSource implements GraphPlugin { static pluginId = 'SelectionCellsHandler'; - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(); this.graph = graph; @@ -78,7 +78,7 @@ class SelectionCellsHandler extends EventSource implements GraphPlugin { /** * Reference to the enclosing {@link Graph}. */ - graph: Graph; + graph: AbstractGraph; /** * Specifies if events are handled. Default is true. diff --git a/packages/core/src/view/plugins/SelectionHandler.ts b/packages/core/src/view/plugins/SelectionHandler.ts index c63b219f86..d5c45375f4 100644 --- a/packages/core/src/view/plugins/SelectionHandler.ts +++ b/packages/core/src/view/plugins/SelectionHandler.ts @@ -21,7 +21,7 @@ import InternalEvent from '../event/InternalEvent'; import { contains, getRotatedPoint, isNumeric, toRadians } from '../../util/mathUtils'; import { convertPoint } from '../../util/styleUtils'; import RectangleShape from '../geometry/node/RectangleShape'; -import mxGuide from '../other/Guide'; +import Guide from '../other/Guide'; import Point from '../geometry/Point'; import { CURSOR, @@ -40,8 +40,7 @@ import { isAltDown, isMultiTouchEvent, } from '../../util/EventUtils'; -import type { Graph } from '../Graph'; -import Guide from '../other/Guide'; +import type { AbstractGraph } from '../AbstractGraph'; import Shape from '../geometry/Shape'; import InternalMouseEvent from '../event/InternalMouseEvent'; import type SelectionCellsHandler from './SelectionCellsHandler'; @@ -56,10 +55,10 @@ import type CellEditorHandler from './CellEditorHandler'; import type { ColorValue, GraphPlugin } from '../../types'; /** - * Graph event handler that handles selection. Individual cells are handled - * separately using {@link VertexHandler} or one of the edge handlers. These - * handlers are created using {@link Graph#createHandler} in - * {@link GraphSelectionModel#cellAdded}. + * Graph event handler that handles selection. + * + * Individual cells are handled separately by {@link SelectionCellsHandler} using {@link VertexHandler} or one of the {@link EdgeHandler}s. + * When the {@link SelectionCellsHandler} plugin is registered in the {@link Graph}, {@link SelectionHandler} interacts with this plugin to propagate global selection events to individual cells. * * To avoid the container to scroll a moved cell into view, set {@link scrollOnMove} to `false`. * @@ -73,7 +72,7 @@ class SelectionHandler implements GraphPlugin { * * @param graph Reference to the enclosing {@link Graph}. */ - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { this.graph = graph; this.graph.addMouseListener(this); @@ -170,7 +169,7 @@ class SelectionHandler implements GraphPlugin { /** * Reference to the enclosing {@link Graph}. */ - graph: Graph; + graph: AbstractGraph; panHandler: () => void; escapeHandler: (sender: EventSource, evt: EventObject) => void; @@ -729,7 +728,7 @@ class SelectionHandler implements GraphPlugin { } createGuide() { - return new mxGuide(this.graph, this.getGuideStates()); + return new Guide(this.graph, this.getGuideStates()); } /** diff --git a/packages/core/src/view/plugins/TooltipHandler.ts b/packages/core/src/view/plugins/TooltipHandler.ts index 6faa27db65..a452793766 100644 --- a/packages/core/src/view/plugins/TooltipHandler.ts +++ b/packages/core/src/view/plugins/TooltipHandler.ts @@ -21,7 +21,7 @@ import { fit, getScrollOrigin } from '../../util/styleUtils'; import { TOOLTIP_VERTICAL_OFFSET } from '../../util/Constants'; import { getSource, isMouseEvent } from '../../util/EventUtils'; import { isNode } from '../../util/domUtils'; -import type { Graph } from '../Graph'; +import type { AbstractGraph } from '../AbstractGraph'; import CellState from '../cell/CellState'; import InternalMouseEvent from '../event/InternalMouseEvent'; import type PopupMenuHandler from './PopupMenuHandler'; @@ -78,7 +78,7 @@ class TooltipHandler implements GraphPlugin { * * @param graph Reference to the enclosing {@link Graph}. */ - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { this.graph = graph; this.graph.addMouseListener(this); } @@ -94,7 +94,7 @@ class TooltipHandler implements GraphPlugin { /** * Reference to the enclosing {@link Graph}. */ - graph: Graph; + graph: AbstractGraph; /** * Delay to show the tooltip in milliseconds. diff --git a/packages/core/src/view/undoable_changes/SelectionChange.ts b/packages/core/src/view/undoable_changes/SelectionChange.ts index 35c9131f50..8a11ec2531 100644 --- a/packages/core/src/view/undoable_changes/SelectionChange.ts +++ b/packages/core/src/view/undoable_changes/SelectionChange.ts @@ -18,20 +18,20 @@ import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; import type { UndoableChange } from '../../types'; -import type { Graph } from '../Graph'; import Cell from '../cell/Cell'; +import { AbstractGraph } from '../AbstractGraph'; /** * Action to change the current root in a view. */ class SelectionChange implements UndoableChange { - constructor(graph: Graph, added: Cell[] = [], removed: Cell[] = []) { + constructor(graph: AbstractGraph, added: Cell[] = [], removed: Cell[] = []) { this.graph = graph; this.added = added.slice(); this.removed = removed.slice(); } - graph: Graph; + graph: AbstractGraph; added: Cell[]; diff --git a/packages/js-example-selected-features/.gitignore b/packages/js-example-selected-features/.gitignore new file mode 100644 index 0000000000..72479eb485 --- /dev/null +++ b/packages/js-example-selected-features/.gitignore @@ -0,0 +1,4 @@ +# Build directories +/node_modules/ +/dist/ +package-lock.json diff --git a/packages/js-example-selected-features/README.md b/packages/js-example-selected-features/README.md new file mode 100644 index 0000000000..186f30f5bb --- /dev/null +++ b/packages/js-example-selected-features/README.md @@ -0,0 +1,29 @@ +# Integrate `maxGraph` without defaults in a vanilla JavaScript project built with Webpack + +Demonstrate how to use `BaseGraph` with a set of features to demonstrate how to perform effective tree-shaking when using `maxGraph`. +- use `BaseGraph` instead of `Graph` to not include some defaults: default plugins and default style registration +- features: Cells selection (with mouse click and RubberBand), panning, zooming and fit center using buttons +- styles: only register Shapes, Perimeters, EdgeMarkers and EdgeStyles used by the application + +Note: This example should be kept in sync with the [ts-example-selected-features](../ts-example-selected-features) example. + +## Setup + +From the repository root, run: +```bash +npm install +cd packages/core/ +npm run build +cd ../../packages/js-example-without-defaults/ +# For more details see 'Run' below +npm run dev +``` + +For more build information see: [@maxgraph/core](../../README.md#development). + + +## Run + +Run `npm run dev` from this directory and go to http://localhost:8080/ + +If you want to bundle the application, run `npm run build` and then run `npm run preview` to access to a preview of the bundle application. diff --git a/packages/js-example-selected-features/favicon.svg b/packages/js-example-selected-features/favicon.svg new file mode 100644 index 0000000000..900e14eb27 --- /dev/null +++ b/packages/js-example-selected-features/favicon.svg @@ -0,0 +1 @@ +icon-square-small \ No newline at end of file diff --git a/packages/js-example-selected-features/index.html b/packages/js-example-selected-features/index.html new file mode 100644 index 0000000000..1f891f8490 --- /dev/null +++ b/packages/js-example-selected-features/index.html @@ -0,0 +1,26 @@ + + + + + + + maxGraph JavaScript Integration with Webpack (selected features) + + +

maxGraph JavaScript Integration with Webpack (selected features)

+

Display a test graph. Activated behaviors:

+
    +
  • Panning: use mouse right button
  • +
  • Cells selection: with Rubberband (use mouse left button), Ctrl+Click to select multiple + cells +
  • +
+
+
+ + +
+
+ + + diff --git a/packages/js-example-selected-features/package.json b/packages/js-example-selected-features/package.json new file mode 100644 index 0000000000..b44c3deb2d --- /dev/null +++ b/packages/js-example-selected-features/package.json @@ -0,0 +1,20 @@ +{ + "name": "@maxgraph/js-example-webpack-selected-features", + "private": true, + "scripts": { + "dev": "webpack serve --open", + "build": "webpack --mode=production", + "preview": "http-server -c-1 dist" + }, + "devDependencies": { + "copy-webpack-plugin": "~13.0.0", + "css-loader": "~7.1.2", + "http-server": "~14.1.1", + "html-webpack-plugin": "~5.6.3", + "mini-css-extract-plugin": "~2.9.2", + "style-loader": "~4.0.0", + "webpack": "~5.98.0", + "webpack-cli": "~6.0.1", + "webpack-dev-server": "~5.2.0" + } +} diff --git a/packages/js-example-selected-features/src/index.js b/packages/js-example-selected-features/src/index.js new file mode 100644 index 0000000000..bc083e359e --- /dev/null +++ b/packages/js-example-selected-features/src/index.js @@ -0,0 +1,144 @@ +/* +Copyright 2025-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 '@maxgraph/core/css/common.css'; // required by RubberBandHandler +import './style.css'; +import { + CellRenderer, + constants, + FitPlugin, + BaseGraph, + InternalEvent, + EllipseShape, + PanningHandler, + Perimeter, + EdgeStyle, + RubberBandHandler, + SelectionCellsHandler, + SelectionHandler, + StyleRegistry, +} from '@maxgraph/core'; + +const initializeGraph = (container) => { + // Disables the built-in context menu + InternalEvent.disableContextMenu(container); + + const graph = new BaseGraph({ + container, + plugins: [ + FitPlugin, // Enables the fitCenter method + PanningHandler, // Enables panning with the mouse + RubberBandHandler, // Enables rubber band selection + SelectionCellsHandler, // Enables management of selected cells + SelectionHandler, // Enables selection with the mouse + ], + }); + graph.setPanning(true); // Use mouse right button for panning + + // Register shapes + // The RectangleShape not registered here because it is always available by default. It is the fallback shape when no shape is returned + // @ts-ignore TODO fix CellRenderer. Calls to this function are also marked as 'ts-ignore' in CellRenderer + CellRenderer.registerShape('ellipse', EllipseShape); + + // Register styles + StyleRegistry.putValue('ellipsePerimeter', Perimeter.EllipsePerimeter); + StyleRegistry.putValue('rectanglePerimeter', Perimeter.RectanglePerimeter); // default + StyleRegistry.putValue('orthogonalEdgeStyle', EdgeStyle.OrthConnector); + // TODO register edge markers when available in maxGraph + + // create a dedicated style for "ellipse" to share properties + graph.getStylesheet().putCellStyle('myEllipse', { + perimeter: 'ellipsePerimeter', + shape: 'ellipse', + verticalAlign: 'top', + verticalLabelPosition: 'bottom', + }); + + // Adds cells to the model in a single step + graph.batchUpdate(() => { + const vertex01 = graph.insertVertex({ + value: 'a regular rectangle', + position: [10, 10], + size: [100, 100], + }); + const vertex02 = graph.insertVertex({ + value: 'a regular ellipse', + position: [350, 90], + size: [50, 50], + style: { + baseStyleNames: ['myEllipse'], + fillColor: 'orange', + }, + }); + graph.insertEdge({ + value: 'an orthogonal style edge', + source: vertex01, + target: vertex02, + style: { + edgeStyle: 'orthogonalEdgeStyle', + rounded: true, + }, + }); + + const vertex11 = graph.insertVertex({ + value: 'a custom rectangle', + position: [20, 200], + size: [100, 100], + style: { + fillColor: 'Chartreuse', + rounded: true, + strokeColor: 'Black', + strokeWidth: 3, + }, + }); + const vertex12 = graph.insertVertex({ + value: 'a custom ellipse', + x: 150, + y: 350, + width: 70, + height: 70, + style: { + baseStyleNames: ['myEllipse'], + fillColor: 'Yellow', + strokeColor: 'Red', + strokeWidth: 5, + }, + }); + graph.insertEdge({ + value: 'another edge', + source: vertex11, + target: vertex12, + style: { endArrow: 'block' }, + }); + }); + + return graph; +}; + +// display the maxGraph version in the footer +const footer = document.querySelector('footer'); +footer.innerText = `Built with maxGraph ${constants.VERSION}`; + +// Creates the graph inside the given container +const graph = initializeGraph(document.querySelector('#graph-container')); + +// Control buttons +document.getElementById('reset-zoom').addEventListener('click', () => { + graph.zoomActual(); +}); +document.getElementById('fit-center').addEventListener('click', () => { + graph.getPlugin('fit')?.fitCenter({ margin: 20 }); +}); diff --git a/packages/js-example-selected-features/src/style.css b/packages/js-example-selected-features/src/style.css new file mode 100644 index 0000000000..7cc31c51f3 --- /dev/null +++ b/packages/js-example-selected-features/src/style.css @@ -0,0 +1,46 @@ +/* +Copyright 2022-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. +*/ + +body { + font-family: Avenir, Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #2c3e50; +} + +h1 { + text-align: center; + margin-top: 3rem; +} + +footer { + position: absolute; + bottom: 1rem; + right: 1rem; + z-index: 1; + font-weight: 800; +} + +#graph-container { + border: #B0B0B0 1px solid; + height: 70vh; + overflow: hidden; +} + +button { + margin-top: 1.5rem; + margin-left: .5rem; +} diff --git a/packages/js-example-selected-features/webpack.config.js b/packages/js-example-selected-features/webpack.config.js new file mode 100644 index 0000000000..30d9ddeddc --- /dev/null +++ b/packages/js-example-selected-features/webpack.config.js @@ -0,0 +1,45 @@ +// Generated using webpack-cli http://github.com/webpack-cli +const path = require('path'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); + +// hack to get the webpack mode +const isDevMode = !process.argv.includes('--mode=production'); + +module.exports = { + mode: 'development', + entry: './src/index.js', + output: { + filename: '[name].[contenthash].js', + path: path.resolve(__dirname, 'dist'), + clean: true, + }, + devServer: { + static: './dist', + }, + module: { + rules: [ + { + test: /\.css$/i, + use: [isDevMode ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader'], + }, + // @maxgraph/core is a dependency of this project but don't declare imports with js extension + // use this workaround to make webpack happy: https://github.com/webpack/webpack/issues/11467#issuecomment-691873586 + { + test: /\.m?js/, + resolve: { + fullySpecified: false, + }, + }, + ], + }, + plugins: [ + new CopyWebpackPlugin({ + patterns: [{ from: './favicon.svg', to: 'favicon.svg' }], + }), + new HtmlWebpackPlugin({ + template: 'index.html', + }), + ].concat(isDevMode ? [] : [new MiniCssExtractPlugin()]), +}; diff --git a/packages/js-example-without-defaults/src/index.js b/packages/js-example-without-defaults/src/index.js index 2713c5a361..80c5c1a605 100644 --- a/packages/js-example-without-defaults/src/index.js +++ b/packages/js-example-without-defaults/src/index.js @@ -15,24 +15,13 @@ limitations under the License. */ import './style.css'; -import { - CellRenderer, - constants, - Graph, - InternalEvent, - MarkerShape, - StyleRegistry, -} from '@maxgraph/core'; +import { BaseGraph, constants, InternalEvent } from '@maxgraph/core'; const initializeGraph = (container) => { // Disables the built-in context menu InternalEvent.disableContextMenu(container); - const graph = new Graph( - container, - undefined, - [] // override default plugins, use none - ); + const graph = new BaseGraph({ container }); // create a dedicated style for "ellipse" to share properties graph.getStylesheet().putCellStyle('myEllipse', { @@ -42,14 +31,8 @@ const initializeGraph = (container) => { verticalLabelPosition: 'bottom', }); - // Custom code to unregister maxGraph style defaults - CellRenderer.defaultShapes = {}; - MarkerShape.markers = {}; - StyleRegistry.values = {}; - // Adds cells to the model in a single step graph.batchUpdate(() => { - // use the legacy insertVertex method const vertex01 = graph.insertVertex({ value: 'a regular rectangle', position: [10, 10], @@ -68,7 +51,7 @@ const initializeGraph = (container) => { source: vertex01, target: vertex02, style: { - edgeStyle: constants.EDGESTYLE.ORTHOGONAL, + edgeStyle: 'orthogonalEdgeStyle', rounded: true, }, }); diff --git a/packages/ts-example-selected-features/README.md b/packages/ts-example-selected-features/README.md new file mode 100644 index 0000000000..86440bf1ae --- /dev/null +++ b/packages/ts-example-selected-features/README.md @@ -0,0 +1,25 @@ +# Integrate `maxGraph` in a vanilla TypeScript project built with Vite + +Demonstrate how to use `BaseGraph` with a set of features to demonstrate how to perform effective tree-shaking when using `maxGraph`. +- use `BaseGraph` instead of `Graph` to not include some defaults: default plugins and default style registration +- features: Cells selection (with mouse click and RubberBand), panning, zooming and fit center using buttons +- styles: only register Shapes, Perimeters, EdgeMarkers and EdgeStyles used by the application + +Note: This example should be kept in sync with the [js-example-selected-features](../js-example-selected-features) example. + +Initialized from https://github.com/vitejs/vite/tree/v2.9.8/packages/create-vite/template-vanilla-ts + +## Setup + +Initialize all packages +> From the repository root, run `npm install`. + +Build maxgraph@core +> From the `packages/core` directory, run `npm run build`. + +## Run + +Run `npm run dev` and go to http://localhost:5173/ + +If you want to bundle the application, run `npm run build` and then run `npm run preview` to access to a preview of the +bundle application. diff --git a/packages/ts-example-selected-features/favicon.svg b/packages/ts-example-selected-features/favicon.svg new file mode 100644 index 0000000000..de4aeddc12 --- /dev/null +++ b/packages/ts-example-selected-features/favicon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/ts-example-selected-features/index.html b/packages/ts-example-selected-features/index.html new file mode 100644 index 0000000000..d5ac9e9d2c --- /dev/null +++ b/packages/ts-example-selected-features/index.html @@ -0,0 +1,28 @@ + + + + + + + maxGraph TypeScript Integration with Vite (selected features) + + + + +

maxGraph TypeScript Integration with Vite, using selected features

+

Display a test graph. Activated behaviors:

+
    +
  • Panning: use mouse right button
  • +
  • Cells selection: with Rubberband (use mouse left button), Ctrl+Click to select multiple cells
  • +
+
+
+ + +
+
+ + + diff --git a/packages/ts-example-selected-features/package.json b/packages/ts-example-selected-features/package.json new file mode 100644 index 0000000000..a78d0b5661 --- /dev/null +++ b/packages/ts-example-selected-features/package.json @@ -0,0 +1,14 @@ +{ + "name": "@maxgraph/ts-example-vite-with-selected-features", + "license": "Apache-2.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc --version && tsc && vite build --base ./", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "~6.2.0" + } +} diff --git a/packages/ts-example-selected-features/src/main.ts b/packages/ts-example-selected-features/src/main.ts new file mode 100644 index 0000000000..b783c7e4c6 --- /dev/null +++ b/packages/ts-example-selected-features/src/main.ts @@ -0,0 +1,144 @@ +/* +Copyright 2025-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 '@maxgraph/core/css/common.css'; // required by RubberBandHandler +import './style.css'; +import { + CellRenderer, + constants, + FitPlugin, + BaseGraph, + InternalEvent, + EllipseShape, + PanningHandler, + Perimeter, + EdgeStyle, + RubberBandHandler, + SelectionCellsHandler, + SelectionHandler, + StyleRegistry, +} from '@maxgraph/core'; + +const initializeGraph = (container: HTMLElement) => { + // Disables the built-in context menu + InternalEvent.disableContextMenu(container); + + const graph = new BaseGraph({ + container, + plugins: [ + FitPlugin, // Enables the fitCenter method + PanningHandler, // Enables panning with the mouse + RubberBandHandler, // Enables rubber band selection + SelectionCellsHandler, // Enables management of selected cells + SelectionHandler, // Enables selection with the mouse + ], + }); + graph.setPanning(true); // Use mouse right button for panning + + // Register shapes + // The RectangleShape not registered here because it is always available by default. It is the fallback shape when no shape is returned + // @ts-ignore TODO fix CellRenderer. Calls to this function are also marked as 'ts-ignore' in CellRenderer + CellRenderer.registerShape('ellipse', EllipseShape); + + // Register styles + StyleRegistry.putValue('ellipsePerimeter', Perimeter.EllipsePerimeter); + StyleRegistry.putValue('rectanglePerimeter', Perimeter.RectanglePerimeter); // default + StyleRegistry.putValue('orthogonalEdgeStyle', EdgeStyle.OrthConnector); + // TODO register edge markers when available in maxGraph + + // create a dedicated style for "ellipse" to share properties + graph.getStylesheet().putCellStyle('myEllipse', { + perimeter: 'ellipsePerimeter', + shape: 'ellipse', + verticalAlign: 'top', + verticalLabelPosition: 'bottom', + }); + + // Adds cells to the model in a single step + graph.batchUpdate(() => { + const vertex01 = graph.insertVertex({ + value: 'a regular rectangle', + position: [10, 10], + size: [100, 100], + }); + const vertex02 = graph.insertVertex({ + value: 'a regular ellipse', + position: [350, 90], + size: [50, 50], + style: { + baseStyleNames: ['myEllipse'], + fillColor: 'orange', + }, + }); + graph.insertEdge({ + value: 'an orthogonal style edge', + source: vertex01, + target: vertex02, + style: { + edgeStyle: 'orthogonalEdgeStyle', + rounded: true, + }, + }); + + const vertex11 = graph.insertVertex({ + value: 'a custom rectangle', + position: [20, 200], + size: [100, 100], + style: { + fillColor: 'Chartreuse', + rounded: true, + strokeColor: 'Black', + strokeWidth: 3, + }, + }); + const vertex12 = graph.insertVertex({ + value: 'a custom ellipse', + x: 150, + y: 350, + width: 70, + height: 70, + style: { + baseStyleNames: ['myEllipse'], + fillColor: 'Yellow', + strokeColor: 'Red', + strokeWidth: 5, + }, + }); + graph.insertEdge({ + value: 'another edge', + source: vertex11, + target: vertex12, + style: { endArrow: 'block' }, + }); + }); + + return graph; +}; + +// display the maxGraph version in the footer +const footer = document.querySelector('footer')!; +footer.innerText = `Built with maxGraph ${constants.VERSION}`; + +// Creates the graph inside the given container +const graph = initializeGraph(document.querySelector('#graph-container')!); + +// Control buttons +document.getElementById('reset-zoom')!.addEventListener('click', () => { + graph.zoomActual(); +}); +document.getElementById('fit-center')!.addEventListener('click', () => { + graph.getPlugin('fit')?.fitCenter({ margin: 20 }); +}); diff --git a/packages/ts-example-selected-features/src/style.css b/packages/ts-example-selected-features/src/style.css new file mode 100644 index 0000000000..fb44dbb30c --- /dev/null +++ b/packages/ts-example-selected-features/src/style.css @@ -0,0 +1,51 @@ +/* +Copyright 2022-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. +*/ + +body { + font-family: Avenir, Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #2c3e50; +} + +h1 { + text-align: center; + margin-top: 3rem; +} + +footer { + position: absolute; + bottom: 1rem; + right: 1rem; + z-index: 1; + font-weight: 800; +} + +#graph-container { + border: #B0B0B0 1px solid; + height: 70vh; + overflow: hidden; +} + +.controls { + margin-top: .75rem; +} + +/* For rubber band selection, override maxGraph defaults */ +div.mxRubberband { + border-color: #b18426; + background: #db9b0b; +} diff --git a/packages/ts-example-selected-features/src/vite-env.d.ts b/packages/ts-example-selected-features/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/packages/ts-example-selected-features/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/ts-example-selected-features/tsconfig.json b/packages/ts-example-selected-features/tsconfig.json new file mode 100644 index 0000000000..2aa41a6d7a --- /dev/null +++ b/packages/ts-example-selected-features/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "useDefineForClassFields": true, + "sourceMap": true, + "moduleResolution": "bundler", + "resolveJsonModule": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true + }, + "include": ["src/*.ts"] +} diff --git a/packages/ts-example-selected-features/vite.config.js b/packages/ts-example-selected-features/vite.config.js new file mode 100644 index 0000000000..b64fc88af9 --- /dev/null +++ b/packages/ts-example-selected-features/vite.config.js @@ -0,0 +1,33 @@ +/* +Copyright 2022-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 { defineConfig } from 'vite'; + +export default defineConfig(({ mode }) => { + return { + build: { + rollupOptions: { + output: { + manualChunks: { + // put the maxgraph code in a dedicated file. It lets know the size the produced bundle in an external application and if tree shaking works + maxgraph: ['@maxgraph/core'], + }, + }, + }, + chunkSizeWarningLimit: 369, // @maxgraph/core + }, + }; +}); diff --git a/packages/ts-example-without-defaults/src/main.ts b/packages/ts-example-without-defaults/src/main.ts index 5b11ade44e..6ec3bf242e 100644 --- a/packages/ts-example-without-defaults/src/main.ts +++ b/packages/ts-example-without-defaults/src/main.ts @@ -15,24 +15,13 @@ limitations under the License. */ import './style.css'; -import { - CellRenderer, - constants, - Graph, - InternalEvent, - MarkerShape, - StyleRegistry, -} from '@maxgraph/core'; +import { BaseGraph, constants, InternalEvent } from '@maxgraph/core'; const initializeGraph = (container: HTMLElement) => { // Disables the built-in context menu InternalEvent.disableContextMenu(container); - const graph = new Graph( - container, - undefined, - [] // override default plugins, use none - ); + const graph = new BaseGraph({ container }); // create a dedicated style for "ellipse" to share properties graph.getStylesheet().putCellStyle('myEllipse', { @@ -42,14 +31,8 @@ const initializeGraph = (container: HTMLElement) => { verticalLabelPosition: 'bottom', }); - // Custom code to unregister maxGraph style defaults - CellRenderer.defaultShapes = {}; - MarkerShape.markers = {}; - StyleRegistry.values = {}; - // Adds cells to the model in a single step graph.batchUpdate(() => { - // use the legacy insertVertex method const vertex01 = graph.insertVertex({ value: 'a regular rectangle', position: [10, 10], @@ -68,7 +51,7 @@ const initializeGraph = (container: HTMLElement) => { source: vertex01, target: vertex02, style: { - edgeStyle: constants.EDGESTYLE.ORTHOGONAL, + edgeStyle: 'orthogonalEdgeStyle', rounded: true, }, }); diff --git a/packages/ts-example-without-defaults/vite.config.js b/packages/ts-example-without-defaults/vite.config.js index 7c956591dc..83aec18b62 100644 --- a/packages/ts-example-without-defaults/vite.config.js +++ b/packages/ts-example-without-defaults/vite.config.js @@ -27,7 +27,7 @@ export default defineConfig(({ mode }) => { }, }, }, - chunkSizeWarningLimit: 436, // @maxgraph/core + chunkSizeWarningLimit: 331, // @maxgraph/core }, }; }); diff --git a/packages/website/docs/demo-and-examples.md b/packages/website/docs/demo-and-examples.md index 6e97c7804a..4f3c94e681 100644 --- a/packages/website/docs/demo-and-examples.md +++ b/packages/website/docs/demo-and-examples.md @@ -26,6 +26,7 @@ Note that they are based on `maxGraph` features, which require the use of [CSS a ## TypeScript Examples - the [ts-example](https://github.com/maxGraph/maxGraph/tree/main/packages/ts-example) project/application demonstrates how to define and use custom `Shapes` with `maxGraph`. It is a vanilla TypeScript application built by [Vite](https://vitejs.dev/). +- TODO ts-example-selected-features (see README) - the [ts-example-without-defaults](https://github.com/maxGraph/maxGraph/tree/main/packages/ts-example-without-defaults) project/application demonstrates how to not use defaults plugins and style defaults (shapes, perimeters, ...). It is a vanilla TypeScript application built by [Vite](https://vitejs.dev/). ## JavaScript Examples From 9d1ae40f249a957e1fc7148c79c1ade79eed66e7 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Tue, 8 Apr 2025 06:53:57 +0200 Subject: [PATCH 2/4] update package-lock.json: add new examples --- package-lock.json | 822 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 822 insertions(+) diff --git a/package-lock.json b/package-lock.json index 499e072b21..f9328c1528 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6203,6 +6203,10 @@ "resolved": "packages/js-example", "link": true }, + "node_modules/@maxgraph/js-example-webpack-selected-features": { + "resolved": "packages/js-example-selected-features", + "link": true + }, "node_modules/@maxgraph/js-example-webpack-without-defaults": { "resolved": "packages/js-example-without-defaults", "link": true @@ -6211,6 +6215,10 @@ "resolved": "packages/ts-example", "link": true }, + "node_modules/@maxgraph/ts-example-vite-with-selected-features": { + "resolved": "packages/ts-example-selected-features", + "link": true + }, "node_modules/@maxgraph/ts-example-vite-without-defaults": { "resolved": "packages/ts-example-without-defaults", "link": true @@ -35447,6 +35455,260 @@ "webpack-dev-server": "~5.2.0" } }, + "packages/js-example-selected-features": { + "name": "@maxgraph/js-example-webpack-selected-features", + "devDependencies": { + "copy-webpack-plugin": "~13.0.0", + "css-loader": "~7.1.2", + "html-webpack-plugin": "~5.6.3", + "http-server": "~14.1.1", + "mini-css-extract-plugin": "~2.9.2", + "style-loader": "~4.0.0", + "webpack": "~5.98.0", + "webpack-cli": "~6.0.1", + "webpack-dev-server": "~5.2.0" + } + }, + "packages/js-example-selected-features/node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "dev": true, + "license": "MIT" + }, + "packages/js-example-selected-features/node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "packages/js-example-selected-features/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/js-example-selected-features/node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "packages/js-example-selected-features/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/js-example-selected-features/node_modules/memfs": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.0.tgz", + "integrity": "sha512-4eirfZ7thblFmqFjywlTmuWVSvccHAJbn1r8qQLzmTO11qcqpohOjmY2mFce6x7x7WtskzRqApPD0hv+Oa74jg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, + "packages/js-example-selected-features/node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/js-example-selected-features/node_modules/p-retry": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", + "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/js-example-selected-features/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "packages/js-example-selected-features/node_modules/webpack-dev-server": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.1.tgz", + "integrity": "sha512-ml/0HIj9NLpVKOMq+SuBPLHcmbG+TGIjXRHsYfZwocUBIqEvws8NnS/V9AFQ5FKP+tgn5adwVwRrTEpGL33QFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/express-serve-static-core": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "express": "^4.21.2", + "graceful-fs": "^4.2.6", + "http-proxy-middleware": "^2.0.7", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "packages/js-example-selected-features/node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", + "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^4.6.0", + "mime-types": "^2.1.31", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, "packages/js-example-without-defaults": { "name": "@maxgraph/js-example-webpack-without-defaults", "devDependencies": { @@ -35960,6 +36222,566 @@ "vite": "~6.2.0" } }, + "packages/ts-example-selected-features": { + "name": "@maxgraph/ts-example-vite-with-selected-features", + "license": "Apache-2.0", + "devDependencies": { + "vite": "~6.2.0" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "packages/ts-example-selected-features/node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, + "packages/ts-example-selected-features/node_modules/vite": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz", + "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "packages/ts-example-selected-features/node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "packages/ts-example-without-defaults": { "name": "@maxgraph/ts-example-vite-without-defaults", "license": "Apache-2.0", From 4bf1d07e05ef694d6a917fd723df5f281de64686 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Tue, 8 Apr 2025 07:12:54 +0200 Subject: [PATCH 3/4] Graph.ts: restore original file header [skip ci] --- packages/core/src/view/Graph.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/view/Graph.ts b/packages/core/src/view/Graph.ts index b7816c9ee4..4b2ebbdba6 100644 --- a/packages/core/src/view/Graph.ts +++ b/packages/core/src/view/Graph.ts @@ -1,5 +1,7 @@ /* -Copyright 2025-present The maxGraph project Contributors +Copyright 2021-present The maxGraph project Contributors +Copyright (c) 2006-2015, JGraph Ltd +Copyright (c) 2006-2015, Gaudenz Alder Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From ad54125a8d27428daabfc991a4d59b9295dc6362 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Tue, 8 Apr 2025 07:26:25 +0200 Subject: [PATCH 4/4] fix TSC errors in stories --- packages/html/stories/DynamicToolbar.stories.ts | 3 ++- packages/html/stories/MenuStyle.stories.ts | 3 ++- packages/html/stories/Toolbar.stories.ts | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/html/stories/DynamicToolbar.stories.ts b/packages/html/stories/DynamicToolbar.stories.ts index 1d611d9c07..3f75cffbdd 100644 --- a/packages/html/stories/DynamicToolbar.stories.ts +++ b/packages/html/stories/DynamicToolbar.stories.ts @@ -16,6 +16,7 @@ limitations under the License. */ import { + type AbstractGraph, Cell, cellArrayUtils, type CellStyle, @@ -146,7 +147,7 @@ const Template = ({ label, ...args }: Record) => { // Function that is executed when the image is dropped on the graph. // The cell argument points to the cell under the mouse pointer if there is one. const dropHandler = ( - graph: Graph, + graph: AbstractGraph, _evt: MouseEvent, _cell: Cell | null, x?: number, diff --git a/packages/html/stories/MenuStyle.stories.ts b/packages/html/stories/MenuStyle.stories.ts index 0bda78f07c..fcd56862cd 100644 --- a/packages/html/stories/MenuStyle.stories.ts +++ b/packages/html/stories/MenuStyle.stories.ts @@ -22,6 +22,7 @@ This example demonstrates using CSS to style the mxPopupMenu. */ import { + type AbstractGraph, Client, CellOverlay, CellRenderer, @@ -110,7 +111,7 @@ const Template = ({ label, ...args }: Record) => { } class MyCustomPopupMenuHandler extends PopupMenuHandler { - constructor(graph: Graph) { + constructor(graph: AbstractGraph) { super(graph); // Configures automatic expand on mouseover this.autoExpand = true; // TODO autoExpand is not working diff --git a/packages/html/stories/Toolbar.stories.ts b/packages/html/stories/Toolbar.stories.ts index c122dfa93e..7d422c8c30 100644 --- a/packages/html/stories/Toolbar.stories.ts +++ b/packages/html/stories/Toolbar.stories.ts @@ -16,6 +16,7 @@ limitations under the License. */ import { + AbstractGraph, Cell, type CellStyle, Client, @@ -177,7 +178,7 @@ const Template = ({ label, ...args }: { [p: string]: any }) => { // Function that is executed when the image is dropped on // the graph. The cell argument points to the cell under // the mousepointer if there is one. - const funct = (graph: Graph, _evt: MouseEvent, cell: Cell | null) => { + const funct = (graph: AbstractGraph, _evt: MouseEvent, cell: Cell | null) => { graph.stopEditing(false); const pt = graph.getPointForEvent(evt); @@ -207,7 +208,7 @@ const Template = ({ label, ...args }: { [p: string]: any }) => { // Function that is executed when the image is dropped on // the graph. The cell argument points to the cell under // the mousepointer if there is one. - const funct = (graph: Graph, evt: MouseEvent, cell: Cell | null) => { + const funct = (graph: AbstractGraph, evt: MouseEvent, cell: Cell | null) => { graph.stopEditing(false); const pt = graph.getPointForEvent(evt);