From f19ce42ff315359330cf37077ad0c6cef3c48ab7 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Thu, 20 Nov 2025 01:18:19 +0100 Subject: [PATCH 1/9] feat!: configure IconPainter via constructor options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove IconPainterProvider singleton. Users must now pass custom IconPainter through renderer options in BpmnVisualization constructor. BREAKING CHANGE: IconPainterProvider removed. Use renderer.iconPainter option instead. Before: IconPainterProvider.set(new CustomIconPainter()); new BpmnVisualization({ container: 'bpmn-container' }); After: new BpmnVisualization({ container: 'bpmn-container', renderer: { iconPainter: new CustomIconPainter() } }); Changes: - Add iconPainter property to RendererOptions with JSDoc - Thread iconPainter: BpmnVisualization → GraphConfigurator → BpmnGraph → BpmnCellRenderer - Remove IconPainterProvider class and exports - Add integration test verifying custom IconPainter usage - Add test fixture: simple-start-userTask-end.bpmn - Use module-level pendingIconPainter variable to work around mxGraph factory method pattern --- CLAUDE.md | 47 +++++++++++++++++ dev/ts/shared/main.ts | 4 +- src/component/BpmnVisualization.ts | 4 +- src/component/mxgraph/BpmnGraph.ts | 52 +++++++++++++++++-- src/component/mxgraph/GraphConfigurator.ts | 18 ++++--- .../mxgraph/shape/render/icon-painter.ts | 19 ------- src/component/mxgraph/shape/render/index.ts | 2 +- src/component/options.ts | 29 +++++++++++ .../bpmn/simple-start-userTask-end.bpmn | 43 +++++++++++++++ test/integration/BpmnVisualization.test.ts | 17 ++++++ 10 files changed, 201 insertions(+), 34 deletions(-) create mode 100644 test/fixtures/bpmn/simple-start-userTask-end.bpmn diff --git a/CLAUDE.md b/CLAUDE.md index 8a039a8e16..57b50312c3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,6 +53,53 @@ npm run lint # Auto-fix linting issues npm run lint-check # Check linting without fixing ``` +### JSDoc Guidelines + +When writing or updating code, follow these JSDoc practices: + +**Public API Documentation:** +- **REQUIRED**: All public classes, methods, interfaces, and types must have JSDoc comments +- **REQUIRED**: All new public API elements must include the `@since` tag with the version number + - Infer the next version from `package.json` (current: `0.47.0-post` → next: `0.48.0`) + - Always confirm the target version with the user once per session before using it +- Include description, `@param` for parameters, `@return` for return values +- Document exceptions with `@throws` when applicable +- Use `@example` for complex APIs to show usage patterns +- Mark experimental APIs with `@experimental` tag +- Mark deprecated APIs with `@deprecated` and provide migration guidance + +**Internal Code Documentation:** +- **OPTIONAL**: Internal/private code may have JSDoc but it's not required +- Prefer self-documenting code with clear naming over excessive comments +- Add JSDoc for complex internal logic where the "why" isn't obvious +- Use inline comments for non-trivial implementation details + +**JSDoc Best Practices:** +- Keep descriptions concise but complete +- Use TypeScript types; avoid redundant type information in JSDoc (rely on `@param {Type}` when TypeScript inference isn't sufficient) +- Link to related types/methods using `{@link ClassName}` or `{@link methodName}` +- Document parameter constraints and valid ranges +- Specify units for numeric parameters (e.g., "duration in milliseconds") + +**Example:** +```typescript +/** + * Loads and renders a BPMN diagram from XML. + * + * @param xml The BPMN 2.0 XML content to parse and render + * @param options Optional configuration for rendering behavior + * @returns The loaded model information + * @throws {Error} If the XML is invalid or cannot be parsed + * @since 0.48.0 + * @example + * const bpmnVisualization = new BpmnVisualization({ container: 'diagram' }); + * bpmnVisualization.load('...'); + */ +public load(xml: string, options?: LoadOptions): LoadResult { + // implementation +} +``` + ### Documentation ```bash npm run docs # Generate all documentation diff --git a/dev/ts/shared/main.ts b/dev/ts/shared/main.ts index 32da21fb3b..48883bc6e1 100644 --- a/dev/ts/shared/main.ts +++ b/dev/ts/shared/main.ts @@ -240,8 +240,8 @@ function getFitOptionsFromParameters(config: BpmnVisualizationDemoConfiguration, function getRendererOptionsFromParameters(config: BpmnVisualizationDemoConfiguration, parameters: URLSearchParams): RendererOptions { const rendererOptions: RendererOptions = config.globalOptions.renderer ?? {}; - // Mapping between query parameter names and RendererOptions properties - const rendererParameterMappings: Record = { + // Mapping between query parameter names and RendererOptions boolean properties + const rendererParameterMappings: Record> = { 'renderer.ignore.bpmn.colors': 'ignoreBpmnColors', 'renderer.ignore.label.style': 'ignoreBpmnLabelStyles', 'renderer.ignore.activity.label.bounds': 'ignoreBpmnActivityLabelBounds', diff --git a/src/component/BpmnVisualization.ts b/src/component/BpmnVisualization.ts index 704e006ac5..ba253f419e 100644 --- a/src/component/BpmnVisualization.ts +++ b/src/component/BpmnVisualization.ts @@ -22,7 +22,7 @@ import type { Disposable } from './types'; import { htmlElement } from './helpers/dom-utils'; import { newBpmnRenderer } from './mxgraph/BpmnRenderer'; -import GraphConfigurator from './mxgraph/GraphConfigurator'; +import { newGraphConfigurator } from './mxgraph/GraphConfigurator'; import { createNewNavigation } from './navigation'; import { newBpmnParser } from './parser/BpmnParser'; import { createNewBpmnElementsRegistry } from './registry/bpmn-elements-registry'; @@ -78,7 +78,7 @@ export class BpmnVisualization implements Disposable { constructor(options: GlobalOptions) { this.rendererOptions = options?.renderer; // graph configuration - const configurator = new GraphConfigurator(htmlElement(options?.container)); + const configurator = newGraphConfigurator(htmlElement(options?.container), this.rendererOptions); this.graph = configurator.configure(); // other configurations this.navigation = createNewNavigation(this.graph, options?.navigation); diff --git a/src/component/mxgraph/BpmnGraph.ts b/src/component/mxgraph/BpmnGraph.ts index 610ff6a473..dcb2d9bac3 100644 --- a/src/component/mxgraph/BpmnGraph.ts +++ b/src/component/mxgraph/BpmnGraph.ts @@ -14,22 +14,59 @@ See the License for the specific language governing permissions and limitations under the License. */ +import type { IconPainter } from './shape/render'; import type { mxCellRenderer, mxCellState, mxGraphView, mxPoint } from 'mxgraph'; import { BpmnCellRenderer } from './BpmnCellRenderer'; import { mxgraph } from './initializer'; -import { IconPainterProvider } from './shape/render'; + +/** + * Temporary storage for iconPainter during BpmnGraph construction. + * + * **Problem**: The mxGraph super constructor calls `createCellRenderer()` to set the cellRenderer property + * (via createGraphView at https://github.com/jgraph/mxgraph/blob/v4.2.2/javascript/src/js/view/mxGraph.js#L672). + * However, in JavaScript/TypeScript, instance fields (including constructor parameters declared as fields) + * are initialized AFTER the super() call completes. This means `this.iconPainter` is undefined when + * `createCellRenderer()` is called during super() construction. + * + * **Root cause**: mxGraph uses the **factory method pattern** for initialization. The mxGraph class is in + * charge of its own initialization by calling factory methods (`createCellRenderer()`, `createGraphView()`) + * instead of relying on injected collaborators. This makes it impossible to inject dependencies cleanly. + * + * **Why we can't use other approaches**: + * - Can't use `this` as WeakMap key: TypeScript/JavaScript doesn't allow accessing `this` before `super()` call + * - Can't use `this.container` as key: It's set AFTER createGraphView completes + * (https://github.com/jgraph/mxgraph/blob/v4.2.2/javascript/src/js/view/mxGraph.js#L691) + * - Can't use constructor parameter: Not accessible from `createCellRenderer()` method + * + * **Solution**: Use a module-level temporary variable. This is safe because JavaScript is single-threaded, + * so only one constructor can be executing at a time. The variable is set before `super()`, used during + * `createCellRenderer()`, then immediately cleaned up after construction. + * + * **Future**: If we migrate to maxGraph, this problem won't exist. maxGraph's BaseGraph accepts the + * CellRenderer via constructor options (dependency injection), eliminating the need for this workaround. + * See https://github.com/maxGraph/maxGraph/blob/cb16ce46d5b33df1ea7b2fbc8815c23420d3e658/packages/core/src/view/BaseGraph.ts#L36 + */ +let pendingIconPainter: IconPainter | undefined; export class BpmnGraph extends mxgraph.mxGraph { /** * @internal */ - constructor(container: HTMLElement) { + constructor(container: HTMLElement, iconPainter: IconPainter) { + // Store iconPainter in temporary variable BEFORE super() call + // This makes it available in createCellRenderer() which is called during super() construction + pendingIconPainter = iconPainter; + super(container); + if (this.container) { // ensure we don't have a select text cursor on label hover, see #294 this.container.style.cursor = 'default'; } + + // Clean up the temporary variable now that super() is complete + pendingIconPainter = undefined; } /** @@ -39,9 +76,16 @@ export class BpmnGraph extends mxgraph.mxGraph { return new BpmnGraphView(this); } + /** + * Called by mxGraph super constructor to create the cell renderer. + * + * This method is only called once during construction (by the mxGraph super constructor), + * so we retrieve the iconPainter from the module-level temporary variable. + * + * @internal + */ override createCellRenderer(): mxCellRenderer { - // in the future, the IconPainter could be configured at library initialization and the provider could be removed - return new BpmnCellRenderer(IconPainterProvider.get()); + return new BpmnCellRenderer(pendingIconPainter!); } /** diff --git a/src/component/mxgraph/GraphConfigurator.ts b/src/component/mxgraph/GraphConfigurator.ts index 9e82a94c74..8081c0fe6b 100644 --- a/src/component/mxgraph/GraphConfigurator.ts +++ b/src/component/mxgraph/GraphConfigurator.ts @@ -14,9 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ +import type { RendererOptions } from '../options'; + import { BpmnGraph } from './BpmnGraph'; import { registerEdgeMarkers, registerShapes } from './config/register-style-definitions'; import { StyleConfigurator } from './config/StyleConfigurator'; +import { IconPainter } from './shape/render'; + +/** + * @internal + */ +export function newGraphConfigurator(container: HTMLElement, rendererOptions?: RendererOptions): GraphConfigurator { + return new GraphConfigurator(new BpmnGraph(container, rendererOptions?.iconPainter ?? new IconPainter())); +} /** * Configure the {@link BpmnGraph} graph that can be used by the lib @@ -26,12 +36,8 @@ import { StyleConfigurator } from './config/StyleConfigurator'; *
  • markers * @internal */ -export default class GraphConfigurator { - private readonly graph: BpmnGraph; - - constructor(container: HTMLElement) { - this.graph = new BpmnGraph(container); - } +export class GraphConfigurator { + constructor(private readonly graph: BpmnGraph) {} configure(): BpmnGraph { this.configureGraph(); diff --git a/src/component/mxgraph/shape/render/icon-painter.ts b/src/component/mxgraph/shape/render/icon-painter.ts index 3790bbb6cc..b7f65f215a 100644 --- a/src/component/mxgraph/shape/render/icon-painter.ts +++ b/src/component/mxgraph/shape/render/icon-painter.ts @@ -940,22 +940,3 @@ function paintGearInnerCircle(canvas: BpmnCanvas, arcStartX: number, arcStartY: canvas.close(); canvas.fillAndStroke(); } - -/** - * Hold the instance of {@link IconPainter} used by the BPMN Theme. - * - * **WARN**: You may use it to customize the BPMN Theme as suggested in the examples. But be aware that the way the default BPMN theme can be modified is subject to change. - * - * @category BPMN Theme - * @experimental - */ -export class IconPainterProvider { - private static instance = new IconPainter(); - - static get(): IconPainter { - return this.instance; - } - static set(painter: IconPainter): void { - this.instance = painter; - } -} diff --git a/src/component/mxgraph/shape/render/index.ts b/src/component/mxgraph/shape/render/index.ts index 1b84e1a8cf..ee3d5db5e9 100644 --- a/src/component/mxgraph/shape/render/index.ts +++ b/src/component/mxgraph/shape/render/index.ts @@ -17,4 +17,4 @@ limitations under the License. // export types first, otherwise typedoc doesn't generate the subsequent doc correctly (no category and uses the file header instead of the actual TSDoc) export type * from './render-types'; export { BpmnCanvas, type BpmnCanvasConfiguration } from './BpmnCanvas'; -export { IconPainter, IconPainterProvider, type PaintParameter } from './icon-painter'; +export { IconPainter, type PaintParameter } from './icon-painter'; diff --git a/src/component/options.ts b/src/component/options.ts index 0f6f0af3d1..8d231f847d 100644 --- a/src/component/options.ts +++ b/src/component/options.ts @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import type { IconPainter } from './mxgraph/shape/render'; + /** * Options to configure the `bpmn-visualization` initialization. * @category Initialization & Configuration @@ -197,6 +199,33 @@ export type ParserOptions = { * @since 0.35.0 */ export type RendererOptions = { + /** + * Custom {@link IconPainter} instance to use for rendering BPMN element icons. + * This allows you to customize how icons are rendered on tasks, events, and other BPMN elements. + * + * If not provided, a default {@link IconPainter} instance will be created automatically. + * + * @example + * ```typescript + * import { BpmnVisualization, IconPainter } from 'bpmn-visualization'; + * + * class CustomIconPainter extends IconPainter { + * paintPersonIcon(paintParameter) { + * // Custom rendering logic for user task icon + * } + * } + * + * const bpmnVisualization = new BpmnVisualization({ + * container: 'bpmn-container', + * renderer: { + * iconPainter: new CustomIconPainter() + * } + * }); + * ``` + * + * @since 0.48.0 + */ + iconPainter?: IconPainter; /** * If set to `true`, ignore the label bounds configuration defined in the BPMN diagram for all activities. * This forces the use of default label positioning instead of the bounds specified in the BPMN source. diff --git a/test/fixtures/bpmn/simple-start-userTask-end.bpmn b/test/fixtures/bpmn/simple-start-userTask-end.bpmn new file mode 100644 index 0000000000..bb75a77bc3 --- /dev/null +++ b/test/fixtures/bpmn/simple-start-userTask-end.bpmn @@ -0,0 +1,43 @@ + + + + Flow_1 + + + + Flow_1 + Flow_2 + + + Flow_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/integration/BpmnVisualization.test.ts b/test/integration/BpmnVisualization.test.ts index 720b9183ca..f5cf96d3f4 100644 --- a/test/integration/BpmnVisualization.test.ts +++ b/test/integration/BpmnVisualization.test.ts @@ -24,6 +24,7 @@ import { } from './helpers/bpmn-visualization-initialization'; import { allTestedFitTypes } from './helpers/fit-utils'; +import { IconPainter } from '@lib/component/mxgraph/shape/render'; import { ShapeBpmnElementKind } from '@lib/model/bpmn/internal'; import { readFileSync } from '@test/shared/file-helper'; @@ -39,6 +40,22 @@ describe('BpmnVisualization initialization', () => { }); }); +describe('BpmnVisualization constructor parameters', () => { + describe('Custom IconPainter', () => { + it('Custom IconPainter is called when rendering userTask', () => { + const customIconPainter = new IconPainter(); + const paintPersonIconSpy = jest.spyOn(customIconPainter, 'paintPersonIcon'); + + const bv = initializeBpmnVisualization('bpmn-custom-icon-painter', { + renderer: { iconPainter: customIconPainter }, + }); + bv.load(readFileSync('../fixtures/bpmn/simple-start-userTask-end.bpmn')); + + expect(paintPersonIconSpy).toHaveBeenCalled(); + }); + }); +}); + describe('BpmnVisualization API', () => { const bpmnVisualization = initializeBpmnVisualizationWithHtmlElement('bpmn-container', true); From e7d26c77b6074a171521e2ffbe6f6bfef519888d Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:46:21 +0100 Subject: [PATCH 2/9] remove extra exclamation mark not needed in BpmnGraph, tsc pass without it --- src/component/mxgraph/BpmnGraph.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component/mxgraph/BpmnGraph.ts b/src/component/mxgraph/BpmnGraph.ts index dcb2d9bac3..692e802490 100644 --- a/src/component/mxgraph/BpmnGraph.ts +++ b/src/component/mxgraph/BpmnGraph.ts @@ -85,7 +85,7 @@ export class BpmnGraph extends mxgraph.mxGraph { * @internal */ override createCellRenderer(): mxCellRenderer { - return new BpmnCellRenderer(pendingIconPainter!); + return new BpmnCellRenderer(pendingIconPainter); } /** From 34b07a9212ef66f8d1e35658dd54730339787cd7 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:51:06 +0100 Subject: [PATCH 3/9] simplify graph instantiation --- src/component/BpmnVisualization.ts | 5 ++--- src/component/mxgraph/GraphConfigurator.ts | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/component/BpmnVisualization.ts b/src/component/BpmnVisualization.ts index ba253f419e..983a664f71 100644 --- a/src/component/BpmnVisualization.ts +++ b/src/component/BpmnVisualization.ts @@ -22,7 +22,7 @@ import type { Disposable } from './types'; import { htmlElement } from './helpers/dom-utils'; import { newBpmnRenderer } from './mxgraph/BpmnRenderer'; -import { newGraphConfigurator } from './mxgraph/GraphConfigurator'; +import { createNewBpmnGraph } from './mxgraph/GraphConfigurator'; import { createNewNavigation } from './navigation'; import { newBpmnParser } from './parser/BpmnParser'; import { createNewBpmnElementsRegistry } from './registry/bpmn-elements-registry'; @@ -78,8 +78,7 @@ export class BpmnVisualization implements Disposable { constructor(options: GlobalOptions) { this.rendererOptions = options?.renderer; // graph configuration - const configurator = newGraphConfigurator(htmlElement(options?.container), this.rendererOptions); - this.graph = configurator.configure(); + this.graph = createNewBpmnGraph(htmlElement(options?.container), this.rendererOptions); // other configurations this.navigation = createNewNavigation(this.graph, options?.navigation); this.bpmnModelRegistry = new BpmnModelRegistry(); diff --git a/src/component/mxgraph/GraphConfigurator.ts b/src/component/mxgraph/GraphConfigurator.ts index 8081c0fe6b..7ce86acce6 100644 --- a/src/component/mxgraph/GraphConfigurator.ts +++ b/src/component/mxgraph/GraphConfigurator.ts @@ -24,8 +24,8 @@ import { IconPainter } from './shape/render'; /** * @internal */ -export function newGraphConfigurator(container: HTMLElement, rendererOptions?: RendererOptions): GraphConfigurator { - return new GraphConfigurator(new BpmnGraph(container, rendererOptions?.iconPainter ?? new IconPainter())); +export function createNewBpmnGraph(container: HTMLElement, rendererOptions?: RendererOptions): BpmnGraph { + return new GraphConfigurator(new BpmnGraph(container, rendererOptions?.iconPainter ?? new IconPainter())).configure(); } /** From 166eb47af91cd6917727c828a9d5cd8b4ee37ea4 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Thu, 20 Nov 2025 16:24:44 +0100 Subject: [PATCH 4/9] demo: let set a custom IconPainter by using a query parameter --- dev/ts/component/CustomIconPainter.ts | 47 +++++++++++++++++++++++++++ dev/ts/shared/main.ts | 7 ++++ 2 files changed, 54 insertions(+) create mode 100644 dev/ts/component/CustomIconPainter.ts diff --git a/dev/ts/component/CustomIconPainter.ts b/dev/ts/component/CustomIconPainter.ts new file mode 100644 index 0000000000..7b3b517d7b --- /dev/null +++ b/dev/ts/component/CustomIconPainter.ts @@ -0,0 +1,47 @@ +/* +Copyright 2025 Bonitasoft S.A. + +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 { PaintParameter } from '../../../src/component/mxgraph/shape/render'; + +import { IconPainter } from '../../../src/component/mxgraph/shape/render'; + +// Taken from https://github.com/process-analytics/bpmn-visualization-examples/blob/v0.47.0/examples/custom-bpmn-theme/custom-user-task-icon/index.js#L9 +export class CustomIconPainter extends IconPainter { + // adapted from https://github.com/primer/octicons/blob/638c6683c96ec4b357576c7897be8f19c933c052/icons/person.svg + // use mxgraph-svg2shape to generate the code from the svg + override paintPersonIcon(paintParameter: PaintParameter): void { + const canvas = this.newBpmnCanvas(paintParameter, { height: 13, width: 12 }); + // this way of doing subject to change in the future (probably by setting the fillColor in the icon style configuration) + paintParameter.canvas.setFillColor('orange'); + + canvas.begin(); + canvas.moveTo(12, 13); + canvas.arcTo(1, 1, 0, 0, 1, 11, 14); + canvas.lineTo(1, 14); + canvas.arcTo(1, 1, 0, 0, 1, 0, 13); + canvas.lineTo(0, 12); + canvas.curveTo(0, 9.37, 4, 8, 4, 8); + canvas.curveTo(4, 8, 4.23, 8, 4, 8); + canvas.curveTo(3.16, 6.38, 3.06, 5.41, 3, 3); + canvas.curveTo(3.17, 0.59, 4.87, 0, 6, 0); + canvas.curveTo(7.13, 0, 8.83, 0.59, 9, 3); + canvas.curveTo(8.94, 5.41, 8.84, 6.38, 8, 8); + canvas.curveTo(8, 8, 12, 9.37, 12, 12); + canvas.lineTo(12, 13); + canvas.close(); + canvas.fill(); + } +} diff --git a/dev/ts/shared/main.ts b/dev/ts/shared/main.ts index 48883bc6e1..8af52ecff2 100644 --- a/dev/ts/shared/main.ts +++ b/dev/ts/shared/main.ts @@ -33,6 +33,7 @@ import type { import type { mxCell } from 'mxgraph'; import { FlowKind, ShapeBpmnElementKind } from '../../../src/bpmn-visualization'; +import { CustomIconPainter } from '../component/CustomIconPainter'; import { downloadAsPng, downloadAsSvg } from '../component/download'; import { DropFileUserInterface } from '../component/DropFileUserInterface'; import { SvgExporter } from '../component/SvgExporter'; @@ -258,6 +259,12 @@ function getRendererOptionsFromParameters(config: BpmnVisualizationDemoConfigura } } + // Special handling for iconPainter as it requires an instance + if (parameters.get('renderer.iconPainter.use.custom') === 'true') { + rendererOptions.iconPainter = new CustomIconPainter(); + logStartup(`Setting renderer option 'iconPainter' with a CustomIconPainter instance`); + } + return rendererOptions; } From 9dc1ba1af63a3fe51f865a8201278d7a183e2b2d Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Tue, 25 Nov 2025 10:07:47 +0100 Subject: [PATCH 5/9] demo - custom iconPainter: dynamic fill color, based on stroke color --- dev/ts/component/CustomIconPainter.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev/ts/component/CustomIconPainter.ts b/dev/ts/component/CustomIconPainter.ts index 7b3b517d7b..6d9c5b0674 100644 --- a/dev/ts/component/CustomIconPainter.ts +++ b/dev/ts/component/CustomIconPainter.ts @@ -19,13 +19,14 @@ import type { PaintParameter } from '../../../src/component/mxgraph/shape/render import { IconPainter } from '../../../src/component/mxgraph/shape/render'; // Taken from https://github.com/process-analytics/bpmn-visualization-examples/blob/v0.47.0/examples/custom-bpmn-theme/custom-user-task-icon/index.js#L9 +// The only difference is the fill color of the icon, which is not set here, to let the theme define it. export class CustomIconPainter extends IconPainter { // adapted from https://github.com/primer/octicons/blob/638c6683c96ec4b357576c7897be8f19c933c052/icons/person.svg // use mxgraph-svg2shape to generate the code from the svg override paintPersonIcon(paintParameter: PaintParameter): void { const canvas = this.newBpmnCanvas(paintParameter, { height: 13, width: 12 }); - // this way of doing subject to change in the future (probably by setting the fillColor in the icon style configuration) - paintParameter.canvas.setFillColor('orange'); + // respect the color in the current theme + canvas.setFillColor(paintParameter.iconStyleConfig.strokeColor); canvas.begin(); canvas.moveTo(12, 13); From 2920339f106cc3a81b35dd61a00964bdc0526a5d Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Wed, 26 Nov 2025 10:45:18 +0100 Subject: [PATCH 6/9] EXTRA add missing method returned type --- src/component/navigation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/component/navigation.ts b/src/component/navigation.ts index 7d934f5db9..088537ebba 100644 --- a/src/component/navigation.ts +++ b/src/component/navigation.ts @@ -218,8 +218,8 @@ class ZoomSupport implements Disposable { this.mouseWheelListeners = []; } - private createMouseWheelZoomListener(performScaling: boolean) { - return (event: Event, up: boolean) => { + private createMouseWheelZoomListener(performScaling: boolean): MouseWheelListener { + return (event: Event, up: boolean): void => { if (mxEvent.isConsumed(event) || !(event instanceof MouseEvent)) { return; } From b76dcfdc271eb71c157630014ee0e556dca5c687 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Wed, 26 Nov 2025 12:26:54 +0100 Subject: [PATCH 7/9] README: document some query parameter to tune the demo --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9a0f5a04a8..83c26048cf 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,6 @@ Please check the [__⏩ live environment__](https://cdn.statically.io/gh/process You will find their basic usage as well as detailed examples showing possible rendering customizations. -## 📂 Repository Structure - -The [dev](./dev) directory contains the source code for the **Load and Navigation demo** showcased on the [example site](https://cdn.statically.io/gh/process-analytics/bpmn-visualization-examples/master/examples/index.html). \ -This demo is also used for the PR previews of this repository. - - ## 🔆 Project Status `bpmn-visualization` is actively developed and maintained. @@ -184,6 +178,15 @@ In the HTML page: The User documentation (with the feature list & the public API) is available in the [documentation site](https://process-analytics.github.io/bpmn-visualization-js/). +## 🚀 Repository Demo + +This repository includes a **Load and Navigation demo** located in the [dev](./dev) directory, which is also featured on the [example site](https://cdn.statically.io/gh/process-analytics/bpmn-visualization-examples/master/examples/index.html) and used for PR previews. + +The demo supports customization through URL query parameters. For instance, you can set a specific theme by passing `style.theme=light-blue`, or enable a custom icon painter with `renderer.iconPainter.use.custom=true`. + +For a complete list of configuration options, refer to the [source code](./dev/ts/shared/main.ts). + + ### ⚒️ More 💡 Want to know more about `bpmn-visualization` usage and extensibility? Have a look at the From 7553ccc5026395316671adb04c1cf42c4d235ae3 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Wed, 26 Nov 2025 12:28:33 +0100 Subject: [PATCH 8/9] improve jsdoc of RenderOptions.iconPainter --- src/component/options.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/component/options.ts b/src/component/options.ts index 8d231f847d..254ac94318 100644 --- a/src/component/options.ts +++ b/src/component/options.ts @@ -224,6 +224,7 @@ export type RendererOptions = { * ``` * * @since 0.48.0 + * @default an instance of the default {@link IconPainter} class */ iconPainter?: IconPainter; /** From 0d50ec2be7c2a60a371b4d2f279e0cbb784c8ce5 Mon Sep 17 00:00:00 2001 From: Thomas Bouffard <27200110+tbouffard@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:59:30 +0100 Subject: [PATCH 9/9] integrate review feedback --- README.md | 16 ++++++++-------- dev/ts/component/CustomIconPainter.ts | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 83c26048cf..361e544c85 100644 --- a/README.md +++ b/README.md @@ -178,21 +178,21 @@ In the HTML page: The User documentation (with the feature list & the public API) is available in the [documentation site](https://process-analytics.github.io/bpmn-visualization-js/). -## 🚀 Repository Demo +### ⚒️ More -This repository includes a **Load and Navigation demo** located in the [dev](./dev) directory, which is also featured on the [example site](https://cdn.statically.io/gh/process-analytics/bpmn-visualization-examples/master/examples/index.html) and used for PR previews. +💡 Want to know more about `bpmn-visualization` usage and extensibility? Have a look at the +[__⏩ live examples site__](https://cdn.statically.io/gh/process-analytics/bpmn-visualization-examples/master/examples/index.html). -The demo supports customization through URL query parameters. For instance, you can set a specific theme by passing `style.theme=light-blue`, or enable a custom icon painter with `renderer.iconPainter.use.custom=true`. +For more technical details and how-to, go to the `bpmn-visualization-examples` [repository](https://github.com/process-analytics/bpmn-visualization-examples/). -For a complete list of configuration options, refer to the [source code](./dev/ts/shared/main.ts). +## 🚀 Repository Demo -### ⚒️ More +This repository includes a **Load and Navigation demo** located in the [dev](./dev) directory, which is also featured on the [example site](https://cdn.statically.io/gh/process-analytics/bpmn-visualization-examples/master/examples/index.html) and used for PR previews. -💡 Want to know more about `bpmn-visualization` usage and extensibility? Have a look at the -[__⏩ live examples site__](https://cdn.statically.io/gh/process-analytics/bpmn-visualization-examples/master/examples/index.html). +The demo supports customization through URL query parameters. For instance, you can set a specific theme by passing `style.theme=light-blue`, or enable a custom icon painter with `renderer.iconPainter.use.custom=true`. -For more technical details and how-to, go to the `bpmn-visualization-examples` [repository](https://github.com/process-analytics/bpmn-visualization-examples/). +For a complete list of configuration options, refer to the [source code](./dev/ts/shared/main.ts). ## 🔧 Contributing diff --git a/dev/ts/component/CustomIconPainter.ts b/dev/ts/component/CustomIconPainter.ts index 6d9c5b0674..0514c1046a 100644 --- a/dev/ts/component/CustomIconPainter.ts +++ b/dev/ts/component/CustomIconPainter.ts @@ -25,7 +25,7 @@ export class CustomIconPainter extends IconPainter { // use mxgraph-svg2shape to generate the code from the svg override paintPersonIcon(paintParameter: PaintParameter): void { const canvas = this.newBpmnCanvas(paintParameter, { height: 13, width: 12 }); - // respect the color in the current theme + // respect the color of the current theme canvas.setFillColor(paintParameter.iconStyleConfig.strokeColor); canvas.begin();