diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 59ac5877b7..bcd4bd6994 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -104,7 +104,6 @@ export { default as ArrowShape } from './view/geometry/edge/ArrowShape'; export { default as ArrowConnectorShape } from './view/geometry/edge/ArrowConnectorShape'; export { default as ConnectorShape } from './view/geometry/edge/ConnectorShape'; export { default as LineShape } from './view/geometry/edge/LineShape'; -export { default as MarkerShape } from './view/geometry/edge/MarkerShape'; export { default as PolylineShape } from './view/geometry/edge/PolylineShape'; export { default as CloudShape } from './view/geometry/node/CloudShape'; @@ -115,11 +114,13 @@ export { default as HexagonShape } from './view/geometry/node/HexagonShape'; export { default as ImageShape } from './view/geometry/node/ImageShape'; export { default as RectangleShape } from './view/geometry/node/RectangleShape'; export { default as RhombusShape } from './view/geometry/node/RhombusShape'; + +export { unregisterAllStencilShapes } from './view/geometry/stencil/register'; export { default as StencilShape, StencilShapeConfig, -} from './view/geometry/node/StencilShape'; -export { default as StencilShapeRegistry } from './view/geometry/node/StencilShapeRegistry'; +} from './view/geometry/stencil/StencilShape'; +export { default as StencilShapeRegistry } from './view/geometry/stencil/StencilShapeRegistry'; export * as constants from './util/Constants'; export { default as Guide } from './view/other/Guide'; @@ -157,6 +158,17 @@ export { default as Point } from './view/geometry/Point'; export { default as Rectangle } from './view/geometry/Rectangle'; export * from './view/style/config'; +export * from './view/style/register'; +export { default as MarkerShape } from './view/style/EdgeMarkerRegistry'; +/** + * Includes all builtins edge markers which can be registered in {@link MarkerShape}. + * + * They are registered by default when instantiating {@link Graph} or they can all be registered by calling {@link registerDefaultEdgeMarkers}. + * + * @since 0.18.0 + * @category Style + */ +export * as EdgeMarker from './view/style/edge-markers'; export { default as EdgeStyle } from './view/style/EdgeStyle'; export { default as Perimeter } from './view/style/Perimeter'; export { default as StyleRegistry } from './view/style/StyleRegistry'; @@ -194,6 +206,7 @@ export { default as Clipboard } from './util/Clipboard'; export { default as UndoableEdit } from './view/undoable_changes/UndoableEdit'; export { default as UndoManager } from './view/undoable_changes/UndoManager'; +export * from './view/cell/register-shapes'; export { Cell } from './view/cell/Cell'; export { default as CellOverlay } from './view/cell/CellOverlay'; export { default as CellPath } from './view/cell/CellPath'; diff --git a/packages/core/src/serialization/register-model-codecs.ts b/packages/core/src/serialization/register-model-codecs.ts index e0809d9634..a95066a56f 100644 --- a/packages/core/src/serialization/register-model-codecs.ts +++ b/packages/core/src/serialization/register-model-codecs.ts @@ -24,7 +24,7 @@ import { import Geometry from '../view/geometry/Geometry'; import Point from '../view/geometry/Point'; import ObjectCodec from './ObjectCodec'; -import { registerBaseCodecs } from './register-shared'; +import { CodecRegistrationStates, registerBaseCodecs } from './register-shared'; const createObjectCodec = (template: any, name: string): ObjectCodec => { const objectCodec = new ObjectCodec(template); @@ -32,7 +32,6 @@ const createObjectCodec = (template: any, name: string): ObjectCodec => { return objectCodec; }; -let isModelCodecsRegistered = false; /** * Register model codecs i.e. codecs used to import/export the Graph Model, see {@link GraphDataModel}. * @@ -43,7 +42,7 @@ let isModelCodecsRegistered = false; * @category Serialization with Codecs */ export const registerModelCodecs = (force = false) => { - if (!isModelCodecsRegistered || force) { + if (!CodecRegistrationStates.model || force) { CodecRegistry.register(new CellCodec()); CodecRegistry.register(new ModelCodec()); @@ -59,6 +58,6 @@ export const registerModelCodecs = (force = false) => { CodecRegistry.register(new mxCellCodec(), false); CodecRegistry.register(new mxGeometryCodec(), false); - isModelCodecsRegistered = true; + CodecRegistrationStates.model = true; } }; diff --git a/packages/core/src/serialization/register-other-codecs.ts b/packages/core/src/serialization/register-other-codecs.ts index 40f1e65d4e..fafcb5003d 100644 --- a/packages/core/src/serialization/register-other-codecs.ts +++ b/packages/core/src/serialization/register-other-codecs.ts @@ -34,7 +34,7 @@ import GeometryChange from '../view/undoable_changes/GeometryChange'; import StyleChange from '../view/undoable_changes/StyleChange'; import ValueChange from '../view/undoable_changes/ValueChange'; import VisibleChange from '../view/undoable_changes/VisibleChange'; -import { registerBaseCodecs } from './register-shared'; +import { CodecRegistrationStates, registerBaseCodecs } from './register-shared'; import { registerModelCodecs } from './register-model-codecs'; const registerGenericChangeCodecs = () => { @@ -59,7 +59,6 @@ const registerGenericChangeCodecs = () => { ); }; -let isCoreCodecsRegistered = false; /** * Register core codecs i.e. codecs that don't relate to editor. This includes model codecs that can be registered individually with {@link registerModelCodecs}. * @@ -70,7 +69,7 @@ let isCoreCodecsRegistered = false; * @category Serialization with Codecs */ export const registerCoreCodecs = (force = false) => { - if (!isCoreCodecsRegistered || force) { + if (!CodecRegistrationStates.core || force) { CodecRegistry.register(new ChildChangeCodec()); CodecRegistry.register(new GraphCodec()); CodecRegistry.register(new GraphViewCodec()); @@ -81,11 +80,10 @@ export const registerCoreCodecs = (force = false) => { registerModelCodecs(force); - isCoreCodecsRegistered = true; + CodecRegistrationStates.core = true; } }; -let isEditorCodecsRegistered = false; /** * Register only editor codecs. * @param force if `true` register the codecs even if they were already registered. If false, only register them @@ -95,7 +93,7 @@ let isEditorCodecsRegistered = false; * @category Serialization with Codecs */ export const registerEditorCodecs = (force = false) => { - if (!isEditorCodecsRegistered || force) { + if (!CodecRegistrationStates.editor || force) { registerBaseCodecs(force); CodecRegistry.register(new EditorCodec()); @@ -103,7 +101,7 @@ export const registerEditorCodecs = (force = false) => { CodecRegistry.register(new EditorPopupMenuCodec()); CodecRegistry.register(new EditorToolbarCodec()); - isEditorCodecsRegistered = true; + CodecRegistrationStates.editor = true; } }; @@ -120,3 +118,20 @@ export const registerAllCodecs = (force = false) => { registerCoreCodecs(force); registerEditorCodecs(force); }; + +/** + * Unregister all codecs from {@link CodecRegistry}. + * + * @since 0.18.0 + * @category Configuration + * @category Serialization with Codecs + */ +export const unregisterAllCodecs = () => { + CodecRegistry.codecs = {}; + CodecRegistry.aliases = {}; + + // reset the state to ensure that the codecs are registered again when the "register" functions are called + for (const key of Object.keys(CodecRegistrationStates)) { + CodecRegistrationStates[key] = false; + } +}; diff --git a/packages/core/src/serialization/register-shared.ts b/packages/core/src/serialization/register-shared.ts index a75817ea2e..d5bd528b21 100644 --- a/packages/core/src/serialization/register-shared.ts +++ b/packages/core/src/serialization/register-shared.ts @@ -14,17 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -// These functions are private and are not intended to be exported by the npm package +// The elements in this file are private and are not intended to be exported by the npm package import CodecRegistry from './CodecRegistry'; import ObjectCodec from './ObjectCodec'; -let isBaseCodecsRegistered = false; +/** + * @private + */ +export const CodecRegistrationStates: Record = { + base: false, + core: false, + editor: false, + model: false, +}; + +/** + * @private + */ export const registerBaseCodecs = (force = false) => { - if (!isBaseCodecsRegistered || force) { + if (!CodecRegistrationStates.base || force) { CodecRegistry.register(new ObjectCodec({})); // Object CodecRegistry.register(new ObjectCodec([])); // Array - isBaseCodecsRegistered = true; + CodecRegistrationStates.base = true; } }; diff --git a/packages/core/src/view/Graph.ts b/packages/core/src/view/Graph.ts index c8bd88cbe9..4ccc67450a 100644 --- a/packages/core/src/view/Graph.ts +++ b/packages/core/src/view/Graph.ts @@ -56,8 +56,11 @@ 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 { + registerDefaultEdgeMarkers, + registerDefaultEdgeStyles, + registerDefaultPerimeters, +} from './style/register'; import { applyGraphMixins } from './mixins/_graph-mixins-apply'; import { getDefaultPlugins } from './plugins'; import { isNullish } from '../internal/utils'; @@ -488,16 +491,17 @@ class Graph extends EventSource { // =================================================================================================================== protected registerDefaults(): void { - registerDefaultShapes(); - registerDefaultStyleElements(); registerDefaultEdgeMarkers(); + registerDefaultEdgeStyles(); + registerDefaultPerimeters(); + registerDefaultShapes(); } constructor( container?: HTMLElement, model?: GraphDataModel, plugins: GraphPluginConstructor[] = getDefaultPlugins(), - stylesheet: Stylesheet | null = null + stylesheet?: Stylesheet | null ) { super(); this.registerDefaults(); diff --git a/packages/core/src/view/cell/CellRenderer.ts b/packages/core/src/view/cell/CellRenderer.ts index 33037f6589..796d555327 100644 --- a/packages/core/src/view/cell/CellRenderer.ts +++ b/packages/core/src/view/cell/CellRenderer.ts @@ -33,7 +33,7 @@ import { getRotatedPoint, mod, toRadians } from '../../util/mathUtils'; import { convertPoint } from '../../util/styleUtils'; import { equalEntries, equalPoints } from '../../util/arrayUtils'; import Rectangle from '../geometry/Rectangle'; -import StencilShapeRegistry from '../geometry/node/StencilShapeRegistry'; +import StencilShapeRegistry from '../geometry/stencil/StencilShapeRegistry'; import InternalEvent from '../event/InternalEvent'; import Client from '../../Client'; import InternalMouseEvent from '../event/InternalMouseEvent'; diff --git a/packages/core/src/view/cell/register-shapes.ts b/packages/core/src/view/cell/register-shapes.ts index f73f1c4b94..2d576b2bb8 100644 --- a/packages/core/src/view/cell/register-shapes.ts +++ b/packages/core/src/view/cell/register-shapes.ts @@ -37,11 +37,11 @@ import { SHAPE } from '../../util/Constants'; let isDefaultElementsRegistered = false; /** - * Add default shapes into `CellRenderer` shapes. + * Register default builtin shapes into {@link CellRenderer}. * - * @private * @category Configuration * @category Style + * @since 0.18.0 */ export function registerDefaultShapes() { if (!isDefaultElementsRegistered) { @@ -70,3 +70,15 @@ export function registerDefaultShapes() { isDefaultElementsRegistered = true; } } + +/** + * Unregister all shapes from {@link CellRenderer}. + * + * @category Configuration + * @category Style + * @since 0.18.0 + */ +export function unregisterAllShapes() { + CellRenderer.defaultShapes = {}; + isDefaultElementsRegistered = false; +} diff --git a/packages/core/src/view/geometry/Shape.ts b/packages/core/src/view/geometry/Shape.ts index 9560a270f1..9d22df1df7 100644 --- a/packages/core/src/view/geometry/Shape.ts +++ b/packages/core/src/view/geometry/Shape.ts @@ -32,7 +32,7 @@ import SvgCanvas2D from '../canvas/SvgCanvas2D'; import InternalEvent from '../event/InternalEvent'; import Client from '../../Client'; import type CellState from '../cell/CellState'; -import type StencilShape from './node/StencilShape'; +import type StencilShape from './stencil/StencilShape'; import type CellOverlay from '../cell/CellOverlay'; import type ImageBox from '../image/ImageBox'; import type { diff --git a/packages/core/src/view/geometry/edge/ConnectorShape.ts b/packages/core/src/view/geometry/edge/ConnectorShape.ts index 57f5126668..de17118d6b 100644 --- a/packages/core/src/view/geometry/edge/ConnectorShape.ts +++ b/packages/core/src/view/geometry/edge/ConnectorShape.ts @@ -18,7 +18,7 @@ limitations under the License. import { DEFAULT_MARKERSIZE, NONE } from '../../../util/Constants'; import PolylineShape from './PolylineShape'; -import MarkerShape from './MarkerShape'; +import MarkerShape from '../../style/EdgeMarkerRegistry'; import Point from '../Point'; import AbstractCanvas2D from '../../canvas/AbstractCanvas2D'; import Rectangle from '../Rectangle'; diff --git a/packages/core/src/view/geometry/node/StencilShape.ts b/packages/core/src/view/geometry/stencil/StencilShape.ts similarity index 100% rename from packages/core/src/view/geometry/node/StencilShape.ts rename to packages/core/src/view/geometry/stencil/StencilShape.ts diff --git a/packages/core/src/view/geometry/node/StencilShapeRegistry.ts b/packages/core/src/view/geometry/stencil/StencilShapeRegistry.ts similarity index 100% rename from packages/core/src/view/geometry/node/StencilShapeRegistry.ts rename to packages/core/src/view/geometry/stencil/StencilShapeRegistry.ts diff --git a/packages/core/src/view/geometry/stencil/register.ts b/packages/core/src/view/geometry/stencil/register.ts new file mode 100644 index 0000000000..d01cc2aa50 --- /dev/null +++ b/packages/core/src/view/geometry/stencil/register.ts @@ -0,0 +1,28 @@ +/* +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 StencilShapeRegistry from './StencilShapeRegistry'; + +/** + * Unregister all {@link StencilShape}s from {@link StencilShapeRegistry}. + * + * @category Configuration + * @category Style + * @since 0.18.0 + */ +export function unregisterAllStencilShapes() { + StencilShapeRegistry.stencils = {}; +} diff --git a/packages/core/src/view/style/EdgeMarkerRegistry.ts b/packages/core/src/view/style/EdgeMarkerRegistry.ts new file mode 100644 index 0000000000..735584b04c --- /dev/null +++ b/packages/core/src/view/style/EdgeMarkerRegistry.ts @@ -0,0 +1,67 @@ +/* +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 type { MarkerFactoryFunction, StyleArrowValue } from '../../types'; +import type AbstractCanvas2D from '../canvas/AbstractCanvas2D'; +import type Point from '../geometry/Point'; +import type Shape from '../geometry/Shape'; + +/** + * A registry that stores the factory functions that create edge markers. + * + * NOTE: The signature and the name of in this class will change. + */ +class MarkerShape { + /** + * Maps from markers names to functions to paint the markers. + * + * Mapping: the attribute name on the object is the marker type, the associated value is the function to paint the marker + */ + static markers: Record = {}; + + /** + * Adds a factory method that updates a given endpoint and returns a + * function to paint the marker onto the given canvas. + */ + static addMarker(type: string, funct: MarkerFactoryFunction) { + MarkerShape.markers[type] = funct; + } + + /** + * Returns a function to paint the given marker. + */ + static createMarker( + canvas: AbstractCanvas2D, + shape: Shape, + type: StyleArrowValue, + pe: Point, + unitX: number, + unitY: number, + size: number, + source: boolean, + sw: number, + filled: boolean + ) { + const markerFunction = MarkerShape.markers[type]; + return markerFunction + ? markerFunction(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) + : null; + } +} + +export default MarkerShape; diff --git a/packages/core/src/view/geometry/edge/MarkerShape.ts b/packages/core/src/view/style/edge-markers.ts similarity index 67% rename from packages/core/src/view/geometry/edge/MarkerShape.ts rename to packages/core/src/view/style/edge-markers.ts index aba3eb0eb9..fb45bc076e 100644 --- a/packages/core/src/view/geometry/edge/MarkerShape.ts +++ b/packages/core/src/view/style/edge-markers.ts @@ -16,62 +16,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { MarkerFactoryFunction, StyleArrowValue } from '../../../types'; -import type AbstractCanvas2D from '../../canvas/AbstractCanvas2D'; -import { ARROW } from '../../../util/Constants'; -import type Point from '../Point'; -import type Shape from '../Shape'; +import type { StyleArrowValue } from '../../types'; +import type AbstractCanvas2D from '../canvas/AbstractCanvas2D'; +import type Shape from '../geometry/Shape'; +import type Point from '../geometry/Point'; +import { ARROW } from '../../util/Constants'; /** - * A registry that stores all edge markers using . + * Generally used to create the "classic" and "block" marker factory methods. * - * NOTE: The signatures in this class will change. - */ -class MarkerShape { - /** - * Maps from markers names to functions to paint the markers. - * - * Mapping: the attribute name on the object is the marker type, the associated value is the function to paint the marker - */ - static markers: Record = {}; - - /** - * Adds a factory method that updates a given endpoint and returns a - * function to paint the marker onto the given canvas. - */ - static addMarker(type: string, funct: MarkerFactoryFunction) { - MarkerShape.markers[type] = funct; - } - - /** - * Returns a function to paint the given marker. - */ - static createMarker( - canvas: AbstractCanvas2D, - shape: Shape, - type: StyleArrowValue, - pe: Point, - unitX: number, - unitY: number, - size: number, - source: boolean, - sw: number, - filled: boolean - ) { - const markerFunction = MarkerShape.markers[type]; - return markerFunction - ? markerFunction(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) - : null; - } -} - -export default MarkerShape; - -/** - * For the classic and block marker factory methods. + * Here is an example the registration of a factory edge marker function with `createArrow`: + * ```js + * MarkerShape.addMarker('classic', EdgeMarker.createArrow(2)); + * MarkerShape.addMarker('blockThin', EdgeMarker.createArrow(3)); + * ``` + * + * @since 0.18.0 */ -function createArrow(widthFactor: number) { - return ( +export const createArrow = + (widthFactor: number) => + ( canvas: AbstractCanvas2D, _shape: Shape, type: StyleArrowValue, @@ -125,10 +89,20 @@ function createArrow(widthFactor: number) { } }; }; -} -function createOpenArrow(widthFactor: number) { - return ( +/** + * Generally used to create the "open" and "open thin" marker factory methods. + * + * Here is an example the registration of a factory edge marker function with `createOpenArrow`: + * ```js + * MarkerShape.addMarker('open', createOpenArrow(2)); + * ``` + * + * @since 0.18.0 + */ +export const createOpenArrow = + (widthFactor: number) => + ( canvas: AbstractCanvas2D, _shape: Shape, _type: StyleArrowValue, @@ -170,9 +144,11 @@ function createOpenArrow(widthFactor: number) { canvas.stroke(); }; }; -} -const oval = ( +/** + * @since 0.18.0 + */ +export const oval = ( canvas: AbstractCanvas2D, _shape: Shape, _type: StyleArrowValue, @@ -201,7 +177,17 @@ const oval = ( }; }; -function diamond( +/** + * Generally used to create the "diamond" and "diamond thin" marker factory methods. + * + * ```js + * MarkerShape.addMarker('diamond', diamond); + * MarkerShape.addMarker('diamondThin', diamond); + * ``` + * + * @since 0.18.0 + */ +export const diamond = ( canvas: AbstractCanvas2D, _shape: Shape, type: StyleArrowValue, @@ -212,7 +198,7 @@ function diamond( _source: boolean, sw: number, filled: boolean -) { +) => { // The angle of the forward facing arrow sides against the x axis is // 45 degrees, 1/sin(45) = 1.4142 / 2 = 0.7071 ( / 2 allows for // only half the strokewidth is processed ). Or 0.9862 for thin diamond. @@ -249,29 +235,4 @@ function diamond( canvas.stroke(); } }; -} - -let isDefaultMarkersRegistered = false; -/** - * @private - * @category Configuration - * @category Style - */ -export const registerDefaultEdgeMarkers = (): void => { - if (!isDefaultMarkersRegistered) { - MarkerShape.addMarker('classic', createArrow(2)); - MarkerShape.addMarker('classicThin', createArrow(3)); - MarkerShape.addMarker('block', createArrow(2)); - MarkerShape.addMarker('blockThin', createArrow(3)); - - MarkerShape.addMarker('open', createOpenArrow(2)); - MarkerShape.addMarker('openThin', createOpenArrow(3)); - - MarkerShape.addMarker('oval', oval); - - MarkerShape.addMarker('diamond', diamond); - MarkerShape.addMarker('diamondThin', diamond); - - isDefaultMarkersRegistered = true; - } }; diff --git a/packages/core/src/view/style/register.ts b/packages/core/src/view/style/register.ts index 9ae164caff..77c9906017 100644 --- a/packages/core/src/view/style/register.ts +++ b/packages/core/src/view/style/register.ts @@ -18,19 +18,20 @@ import { EDGESTYLE, PERIMETER } from '../../util/Constants'; import EdgeStyle from './EdgeStyle'; import Perimeter from './Perimeter'; import StyleRegistry from './StyleRegistry'; +import MarkerShape from './EdgeMarkerRegistry'; +import { createArrow, createOpenArrow, diamond, oval } from './edge-markers'; -let isDefaultsRegistered = false; +let isDefaultEdgeStylesRegistered = false; /** - * Register style elements for "EdgeStyle" and "Perimeters". + * Register default builtin {@link EdgeStyle}s in {@link StyleRegistry}. * - * @private * @category Configuration * @category Style + * @since 0.18.0 */ -export const registerDefaultStyleElements = (): void => { - if (!isDefaultsRegistered) { - // Edge styles +export const registerDefaultEdgeStyles = (): void => { + if (!isDefaultEdgeStylesRegistered) { StyleRegistry.putValue(EDGESTYLE.ELBOW, EdgeStyle.ElbowConnector); StyleRegistry.putValue(EDGESTYLE.ENTITY_RELATION, EdgeStyle.EntityRelation); StyleRegistry.putValue(EDGESTYLE.LOOP, EdgeStyle.Loop); @@ -40,13 +41,82 @@ export const registerDefaultStyleElements = (): void => { StyleRegistry.putValue(EDGESTYLE.SIDETOSIDE, EdgeStyle.SideToSide); StyleRegistry.putValue(EDGESTYLE.TOPTOBOTTOM, EdgeStyle.TopToBottom); - // Perimeters + isDefaultEdgeStylesRegistered = true; + } +}; + +let isDefaultPerimetersRegistered = false; + +/** + * Register default builtin {@link Perimeter}s in {@link StyleRegistry}. + * + * @category Configuration + * @category Style + * @since 0.18.0 + */ +export const registerDefaultPerimeters = (): void => { + if (!isDefaultPerimetersRegistered) { StyleRegistry.putValue(PERIMETER.ELLIPSE, Perimeter.EllipsePerimeter); StyleRegistry.putValue(PERIMETER.HEXAGON, Perimeter.HexagonPerimeter); StyleRegistry.putValue(PERIMETER.RECTANGLE, Perimeter.RectanglePerimeter); StyleRegistry.putValue(PERIMETER.RHOMBUS, Perimeter.RhombusPerimeter); StyleRegistry.putValue(PERIMETER.TRIANGLE, Perimeter.TrianglePerimeter); - isDefaultsRegistered = true; + isDefaultPerimetersRegistered = true; + } +}; + +/** + * Unregister all {@link EdgeStyle}s and {@link Perimeter}s from {@link StyleRegistry}. + * + * **NOTE**: in the future, this function will be replaced by dedicated functions to remove `Perimeter` and `EdgeStyle` individually. + * For more details, see [Issue #767](https://github.com/maxGraph/maxGraph/issues/767). + * + * @category Configuration + * @category Style + * @since 0.18.0 + */ +export const unregisterAllEdgeStylesAndPerimeters = (): void => { + StyleRegistry.values = {}; + isDefaultEdgeStylesRegistered = false; + isDefaultPerimetersRegistered = false; +}; + +let isDefaultMarkersRegistered = false; +/** + * + * Register default builtin {@link MarkerFactoryFunction}s in {@link MarkerShape}. + * + * @category Configuration + * @category Style + * @since 0.18.0 + */ +export const registerDefaultEdgeMarkers = (): void => { + if (!isDefaultMarkersRegistered) { + MarkerShape.addMarker('classic', createArrow(2)); + MarkerShape.addMarker('classicThin', createArrow(3)); + MarkerShape.addMarker('block', createArrow(2)); + MarkerShape.addMarker('blockThin', createArrow(3)); + + MarkerShape.addMarker('open', createOpenArrow(2)); + MarkerShape.addMarker('openThin', createOpenArrow(3)); + + MarkerShape.addMarker('oval', oval); + + MarkerShape.addMarker('diamond', diamond); + MarkerShape.addMarker('diamondThin', diamond); + + isDefaultMarkersRegistered = true; } }; +/** + * Unregister all {@link MarkerFactoryFunction}s from {@link MarkerShape}. + * + * @category Configuration + * @category Style + * @since 0.18.0 + */ +export const unregisterAllEdgeMarkers = (): void => { + MarkerShape.markers = {}; + isDefaultMarkersRegistered = false; +}; diff --git a/packages/html/.storybook/main.ts b/packages/html/.storybook/main.ts index 42473b2140..9704176d91 100644 --- a/packages/html/.storybook/main.ts +++ b/packages/html/.storybook/main.ts @@ -1,3 +1,19 @@ +/* +Copyright 2023-present The maxGraph project Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + import type { StorybookConfig } from '@storybook/html-vite'; const config: StorybookConfig = { stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'], diff --git a/packages/html/.storybook/preview.ts b/packages/html/.storybook/preview.ts index 00857dd045..f481c34f39 100644 --- a/packages/html/.storybook/preview.ts +++ b/packages/html/.storybook/preview.ts @@ -1,11 +1,25 @@ +/* +Copyright 2023-present The maxGraph project Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + import type { Preview } from '@storybook/html'; import { Client, - CodecRegistry, GlobalConfig, NoOpLogger, ObjectCodec, - registerModelCodecs, resetEdgeHandlerConfig, resetEntityRelationConnectorConfig, resetHandleConfig, @@ -14,10 +28,14 @@ import { resetStyleDefaultsConfig, resetTranslationsConfig, resetVertexHandlerConfig, - StencilShapeRegistry, StylesheetCodec, Translations, TranslationsAsI18n, + unregisterAllCodecs, + unregisterAllEdgeMarkers, + unregisterAllEdgeStylesAndPerimeters, + unregisterAllShapes, + unregisterAllStencilShapes, } from '@maxgraph/core'; const defaultLogger = new NoOpLogger(); @@ -40,6 +58,7 @@ const originalAllowEvalConfig = { }; const resetMaxGraphConfigs = (): void => { + // Global configuration GlobalConfig.i18n = i18nProvider; GlobalConfig.logger = defaultLogger; @@ -52,21 +71,18 @@ const resetMaxGraphConfigs = (): void => { resetTranslationsConfig(); resetVertexHandlerConfig(); - // Reset registries to remove additional elements registered in a story - // The objects storing the registered elements are currently public, but they should not be part of the public API. - // They will be marked as private in the future and clear functions will probably provide instead. - - // Codec resets - CodecRegistry.aliases = {}; - CodecRegistry.codecs = {}; - // This is done automatically by ModelSerializer but only once, even if the codec registry is cleaned. So force reload manually here. - // This is a workaround. If we had a unregisteredCodecs function, we could reset the global codecs loading status and the codecs would be automatically registered again. - registerModelCodecs(true); + // Codecs + unregisterAllCodecs(); ObjectCodec.allowEval = originalAllowEvalConfig.objectCodec; StylesheetCodec.allowEval = originalAllowEvalConfig.stylesheetCodec; + // Style configuration + unregisterAllEdgeMarkers(); + unregisterAllEdgeStylesAndPerimeters(); + unregisterAllShapes(); + // The following registries are filled by stories only - StencilShapeRegistry.stencils = {}; + unregisterAllStencilShapes(); }; // This function is a workaround to destroy mxGraph elements that are not released by the previous story. diff --git a/packages/js-example-without-defaults/src/index.js b/packages/js-example-without-defaults/src/index.js index 2713c5a361..a1ab455da9 100644 --- a/packages/js-example-without-defaults/src/index.js +++ b/packages/js-example-without-defaults/src/index.js @@ -15,20 +15,28 @@ limitations under the License. */ import './style.css'; -import { - CellRenderer, - constants, - Graph, - InternalEvent, - MarkerShape, - StyleRegistry, -} from '@maxgraph/core'; +import { constants, Graph, InternalEvent } from '@maxgraph/core'; + +/** + * Create a custom implementation to not load all default built-in styles. This is because Graph registers them. + * + * In the future, we expect to have an implementation of Graph that does not do it. + * See https://github.com/maxGraph/maxGraph/issues/760 + */ +class CustomGraph extends Graph { + /** + * Only registers the elements required for this example. Do not let Graph load all default built-in styles. + */ + registerDefaults() { + // do nothing + } +} const initializeGraph = (container) => { // Disables the built-in context menu InternalEvent.disableContextMenu(container); - const graph = new Graph( + const graph = new CustomGraph( container, undefined, [] // override default plugins, use none @@ -42,14 +50,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], diff --git a/packages/js-example-without-defaults/src/style.css b/packages/js-example-without-defaults/src/style.css index 7cc31c51f3..81bef39fa0 100644 --- a/packages/js-example-without-defaults/src/style.css +++ b/packages/js-example-without-defaults/src/style.css @@ -36,11 +36,6 @@ footer { #graph-container { border: #B0B0B0 1px solid; - height: 70vh; + height: 75vh; overflow: hidden; } - -button { - margin-top: 1.5rem; - margin-left: .5rem; -} diff --git a/packages/ts-example-without-defaults/src/main.ts b/packages/ts-example-without-defaults/src/main.ts index 5b11ade44e..62adfaf187 100644 --- a/packages/ts-example-without-defaults/src/main.ts +++ b/packages/ts-example-without-defaults/src/main.ts @@ -15,20 +15,28 @@ limitations under the License. */ import './style.css'; -import { - CellRenderer, - constants, - Graph, - InternalEvent, - MarkerShape, - StyleRegistry, -} from '@maxgraph/core'; +import { constants, Graph, InternalEvent } from '@maxgraph/core'; + +/** + * Create a custom implementation to not load all default built-in styles. This is because Graph registers them. + * + * In the future, we expect to have an implementation of Graph that does not do it. + * See https://github.com/maxGraph/maxGraph/issues/760 + */ +class CustomGraph extends Graph { + /** + * Only registers the elements required for this example. Do not let Graph load all default built-in styles. + */ + protected override registerDefaults() { + // do nothing + } +} const initializeGraph = (container: HTMLElement) => { // Disables the built-in context menu InternalEvent.disableContextMenu(container); - const graph = new Graph( + const graph = new CustomGraph( container, undefined, [] // override default plugins, use none @@ -42,14 +50,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], diff --git a/packages/ts-example-without-defaults/src/style.css b/packages/ts-example-without-defaults/src/style.css index 627d006262..72f35e6af9 100644 --- a/packages/ts-example-without-defaults/src/style.css +++ b/packages/ts-example-without-defaults/src/style.css @@ -36,6 +36,6 @@ footer { #graph-container { border: #B0B0B0 1px solid; - height: 80vh; + height: 75vh; overflow: hidden; } diff --git a/packages/website/docs/usage/codecs.md b/packages/website/docs/usage/codecs.md index 6aa2667438..2cc155e746 100644 --- a/packages/website/docs/usage/codecs.md +++ b/packages/website/docs/usage/codecs.md @@ -39,6 +39,12 @@ From [version 0.6.0](https://github.com/maxGraph/maxGraph/releases/tag/v0.6.0) o Registering codecs has an impact on the tree-shaking, so, only register the codecs you need to reduce the size of the final bundle. ::: +It is possible to unregister all elements from `CodecRegistry` using the related `unregister` function: + +```javascript +unregisterAllCodecs(); +``` + ## Encode/Export and Decode/Import diff --git a/packages/website/docs/usage/global-configuration.md b/packages/website/docs/usage/global-configuration.md index 4bc0889dbc..7b20b0fb61 100644 --- a/packages/website/docs/usage/global-configuration.md +++ b/packages/website/docs/usage/global-configuration.md @@ -59,6 +59,24 @@ See also discussions in [issue #192](https://github.com/maxGraph/maxGraph/issues When instantiating a `Graph` object, the registries are filled with `maxGraph` default style configurations. There is no default stencil shapes registered by default. +To manually register default style configurations, you can use the following functions: + +```javascript +registerDefaultEdgeMarkers(); +registerDefaultEdgeStyles(); +registerDefaultPerimeters(); +registerDefaultShapes(); +``` + +It is possible to unregister all elements from a style registry using the related `unregister` function. For example: + +```javascript +unregisterAllEdgeMarkers(); +unregisterAllEdgeStylesAndPerimeters(); +unregisterAllShapes(); +unregisterAllStencilShapes(); +``` + ## Codecs and Serialization