Help Needed: Creating a Custom OutlineProvider in BPMN.js for Custom Shapes

Hi everyone,

I’m trying to extend the OutlineProvider class in BPMN.js to create custom outlines for my custom shapes, but I’m facing challenges and haven’t found a solution yet. The default implementation always renders a rectangle, and I need to customize it for specific shapes.

I followed the guidance in this forum thread, but extending OutlineProvider directly doesn’t seem to work in my case.

Here’s the code I’m working with:

import { create as svgCreate, attr as svgAttr } from "tiny-svg";
import OutlineProvider from "diagram-js/lib/features/outline";

export default class CustomOutlineProvider extends OutlineProvider {
  constructor(styles) {
    super(styles);
    this.styles = styles;
  }

  getOutline(element) {
    if (element.type === "custom:Terminal") {
      const outline = svgCreate("rect");

      const width = element.width || 160;
      const height = element.height || 40;
      const rx = Math.min(width, height) / 4;

      const style = this.styles.cls("djs-outline", ["no-fill"]);

      svgAttr(outline, {
        x: -5,
        y: -5,
        width: width + 10,
        height: height + 10,
        rx: rx,
        ry: rx,
        ...style,
      });

      return outline;
    }

    return super.getOutline(element);
  }
}

CustomOutlineProvider.$inject = ["styles"];

The problem is that I cannot extend OutlineProvider properly. It throws an error or doesn’t work as expected during runtime. I suspect it might be related to how OutlineProvider is structured or exported in the library.

Has anyone successfully implemented a custom OutlineProvider or found a workaround to achieve this? Any guidance, code examples, or pointers would be greatly appreciated.

Thanks in advance! :blush:

i found the solution

export default class CustomOutlineProvider {
  constructor(styles) {
    this.styles = styles;
  }


  getOutline(element) {
    if (element.type === "custom:Terminal") {
      
      const outline = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "rect"
      );

      const width = element.width || 160;
      const height = element.height / 2 || 40;
      const rx = Math.min(width, height) / 4;
      console.log("getOutline==>", element.height);
    
      outline.setAttribute("x", -5);
      outline.setAttribute("y", -5);
      outline.setAttribute("width", width + 10);
      outline.setAttribute("height", height + 10);
      outline.setAttribute("rx", rx);
      outline.setAttribute("class", "djs-outline no-fill");

      return outline;
    }

    return null; 
  }

  updateOutline(element, outline) {
    console.log("update OUTLINE ");
    if (element.type === "custom:Terminal") {
      console.log("UPDATE==>", element.height);
      outline.setAttribute("width", element.width + 10);
      outline.setAttribute("height", element.height / 2 + 10);
      return true;
    }

    return false;
  }
}

Index file :

import CustomOutlineProvider from "./CustomOutlineProvider";

export default {
  __init__: ["outlineProvider"],
  outlineProvider: [
    "type",
    function (outline, styles) {
      const provider = new CustomOutlineProvider(styles);
      outline.registerProvider(provider);
    },
  ],
};