From 79cfda2a318f7ed813d642dfa530031b20899ade Mon Sep 17 00:00:00 2001 From: Toma <847775+redfish4ktc@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:35:35 +0200 Subject: [PATCH 1/2] fix: keep the custom shape defaults across style changes `CustomRectangleShape` and `CustomEllipseShape` set `strokeWidth` and `isRounded` in a constructor, but both fields are derived from the style, so `resetStyles()` wipes them as soon as a style change reaches the shape: `initStyles()` puts `strokeWidth` back to 1 and `resetStyles()` puts `isRounded` back to false. Nothing restores them afterwards, neither `strokeWidth` nor `rounded` being set in the style of the two cells that use these shapes. Declare the values as class fields, which covers the first render, and reassert them in a `resetStyles()` override, which covers every later style change. Dropping the constructor also removes a misleading signature: the shape registry builds a registered shape with no argument, so its `bounds`, `fill` and `stroke` parameters were typed as required while always being `undefined`. The defect is latent here, since these examples never change a style, but the shapes are what a reader copies. The two equivalent shapes of `packages/ts-example` in the maxGraph repository are fixed the same way. --- projects/_shared/src/custom-shapes.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/projects/_shared/src/custom-shapes.ts b/projects/_shared/src/custom-shapes.ts index c353041..f36e62e 100644 --- a/projects/_shared/src/custom-shapes.ts +++ b/projects/_shared/src/custom-shapes.ts @@ -1,4 +1,4 @@ -import {AbstractCanvas2D, ColorValue, EllipseShape, Rectangle, ShapeRegistry, RectangleShape} from '@maxgraph/core'; +import {AbstractCanvas2D, EllipseShape, ShapeRegistry, RectangleShape} from '@maxgraph/core'; export const registerCustomShapes = (): void => { console.info('Registering custom shapes...'); @@ -8,9 +8,15 @@ export const registerCustomShapes = (): void => { }; class CustomRectangleShape extends RectangleShape { - constructor(bounds: Rectangle, fill: ColorValue, stroke: ColorValue) { - super(bounds, fill, stroke, 3); - this.isRounded = true; // force rounded shape + // The renderer builds a registered shape with no argument, so the defaults are declared as class fields. + override strokeWidth = 3; + override isRounded = true; // force rounded shape + + // Both fields are derived from the style, so they are wiped on every style change and have to be reasserted. + override resetStyles(): void { + super.resetStyles(); + this.strokeWidth = 3; + this.isRounded = true; } override paintBackground( @@ -37,8 +43,11 @@ class CustomRectangleShape extends RectangleShape { } class CustomEllipseShape extends EllipseShape { - constructor(bounds: Rectangle, fill: string, stroke: string) { - super(bounds, fill, stroke, 5); + override strokeWidth = 5; + + override resetStyles(): void { + super.resetStyles(); + this.strokeWidth = 5; } override paintVertexShape( From 66ae666dde826921f5f41e831f9da884f920e08e Mon Sep 17 00:00:00 2001 From: Toma <847775+redfish4ktc@users.noreply.github.com> Date: Wed, 30 Sep 2026 06:37:54 +0200 Subject: [PATCH 2/2] refactor: name the duplicated defaults of the custom shapes Each default of the two shapes has to be written twice, as a class field for the first render and in the `resetStyles()` override for every later style change, so the same literal lived in two places that must stay in step. Extract one constant per value. The two comments explaining that duplication are merged into a single block above the constants. Each stated only half of the reason, one naming the construction with no argument and the other the style reset, while a reader needs both to understand why the value appears twice. --- projects/_shared/src/custom-shapes.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/projects/_shared/src/custom-shapes.ts b/projects/_shared/src/custom-shapes.ts index f36e62e..de34d5e 100644 --- a/projects/_shared/src/custom-shapes.ts +++ b/projects/_shared/src/custom-shapes.ts @@ -1,5 +1,13 @@ import {AbstractCanvas2D, EllipseShape, ShapeRegistry, RectangleShape} from '@maxgraph/core'; +// The renderer builds a registered shape with no argument, so these defaults cannot come from a constructor. They are +// also derived from the style, so `resetStyles()` wipes them on every style change. Each one is therefore declared +// twice, as a class field for the first render and in a `resetStyles()` override for every later change, which is why +// it is a named constant rather than a literal. +const RECTANGLE_STROKE_WIDTH = 3; +const RECTANGLE_IS_ROUNDED = true; // force rounded shape +const ELLIPSE_STROKE_WIDTH = 5; + export const registerCustomShapes = (): void => { console.info('Registering custom shapes...'); ShapeRegistry.add('customRectangle', CustomRectangleShape); @@ -8,15 +16,13 @@ export const registerCustomShapes = (): void => { }; class CustomRectangleShape extends RectangleShape { - // The renderer builds a registered shape with no argument, so the defaults are declared as class fields. - override strokeWidth = 3; - override isRounded = true; // force rounded shape + override strokeWidth = RECTANGLE_STROKE_WIDTH; + override isRounded = RECTANGLE_IS_ROUNDED; - // Both fields are derived from the style, so they are wiped on every style change and have to be reasserted. override resetStyles(): void { super.resetStyles(); - this.strokeWidth = 3; - this.isRounded = true; + this.strokeWidth = RECTANGLE_STROKE_WIDTH; + this.isRounded = RECTANGLE_IS_ROUNDED; } override paintBackground( @@ -43,11 +49,11 @@ class CustomRectangleShape extends RectangleShape { } class CustomEllipseShape extends EllipseShape { - override strokeWidth = 5; + override strokeWidth = ELLIPSE_STROKE_WIDTH; override resetStyles(): void { super.resetStyles(); - this.strokeWidth = 5; + this.strokeWidth = ELLIPSE_STROKE_WIDTH; } override paintVertexShape(