Extending Color Picker for Custom Shapes

I am creating a web-app based on bpmn-js-example-custom-shapes. So I have created a new shape that is drag and droppable to the canvas. I also imported bpmn-js-color-picker to be able to color the elements. I actually extended it “ExtendedColorPickerModule” with some colors I need. My problem is that I don’t know how to be able to color my custom shape (called ocbpmn:hexagon).
color_clip

I made my repository public if you want to have a look at. And yes, the Hexagon has a fillPathData and a strokePathData, so it should easily be possible to change those colors through the Color Picker. Any concrete ideas?

Hi,

The colors in bpmn-js rely on DI (diagram interchange) elements: BPMNShapes and BPMNEdges. In case of your custom elements, the DI elements are missing. When the SetColorHandler tries to add the color information, it does not find the DI element, and ends execution: https://github.com/bpmn-io/bpmn-js/blob/develop/lib/features/modeling/cmd/SetColorHandler.js#L84

There are two ways you could solve this:

  1. Add DI elements for your custom shapes. You can check how the di property is added on diagram import at https://github.com/bpmn-io/bpmn-js/blob/develop/lib/import/Importer.js#L51
  2. Override the SetColorHandler to handle colors assignment in a special, your modeler-specific way.
1 Like

To add to @barmac’s answer the canonical way to inject your custom color setting would be to override Modeling#setColor. This is the API that is used by the color picker extension. You’d want to detect your custom elements, and set colors differently for them.

2 Likes

@barmac @nikku

I would like to implement the coloring for my hexagon the way the color picker does it for normal BPMN elements. It changes the fill color and the stroke color. That’s why I tried to add DI elements to my hexagon & I think that’s the best approach. Unfortunately my attempts did not work. I would be super grateful for any guidance or other help with adding DI elements. What steps do I need to take?

While we’re on the subject of bpmn-js features for custom shapes. Maybe you also have a tip for me for the following problem:
You can’t give custom shapes a label. I have tried different approaches to make ‘label-editing’ for custom shapes possible (ensured my custom shape has a businessObject with a name property and added an SVG text element to my custom shape etc.). When that didn’t work, I created an OcbpmnDirectEditingProvider that uses the directEditing service, also made some css changes but… As you can see in the GIF, it looks different than it should. It should just work like for the rest of the BPMN elements (e.g. events):
labeling
I think the easiest way is to enable the label editing implemented in bpmn-js for custom shapes. But how? Where is the problem? Thank you very much for your help!

If you want to have a look at my code:
Here is my code with my implementation of the buggy label editing (I should probably discard this approach).
Here is my code without the implementation of the buggy label editing where I would like to add the normal label editing for custom shapes.

To render the updated label, you need to implement that in your renderer. Currently, the name property is not used in the renderer: https://github.com/rwth-pads/oc-bpmn/blob/feature/rules/app/ocbpmn-modeler/ocbpmn/ocbpmnRenderer.js#L60
See how labels are rendered in bpmn-js: https://github.com/bpmn-io/bpmn-js/blob/develop/lib/draw/BpmnRenderer.js#L2130

I’d suggest to implement the option 2. You can update your renderer to use the properties that you will set in your custom setColor handler.

Isn’t there an easier option that the behavior of adding a label for event happens exactly the same for my hexagon? I like the behavior and don’t want to implement a new label.

Hmm you could potentially use the BpmnRenderer to render a label: https://github.com/bpmn-io/bpmn-js/blob/develop/lib/draw/BpmnRenderer.js#L2236

Thank you! I solved it by creating a new file ‘ChangeColor.js’ that costumizes the color setting functionality in bpmn-js for my specific element type. It overrides the standard ‘setColor’ function to apply custom color logic (directly updating the SVG attribute for fill and stroke colors) to ‘ocbpmn:hexagon’ elements while retaining default behaviour for all other elements:

export default function ChangeColor(modeler) {
    const modeling = modeler.get('modeling');
    const elementRegistry = modeler.get('elementRegistry');

    // Backup the original setColor function
    const originalSetColor = modeling.setColor;

    // Override the setColor function
    modeling.setColor = function(elements, colors) {
        // Ensure elements is an array
        elements = Array.isArray(elements) ? elements : [elements];

        elements.forEach(element => {
            if (element.type === 'ocbpmn:hexagon') {
                // Store the colors in the element's business object
                element.businessObject.customColors = colors;

                // Get the graphical representation of the element
                const gfx = elementRegistry.getGraphics(element);

                // Update the fill and stroke colors of the hexagon
                const svgPath = gfx.querySelector('.fill-path');
                const svgStrokePath = gfx.querySelector('.stroke-path');
                if (svgPath) {
                    svgPath.setAttribute('fill', colors.fill);
                }
                if (svgStrokePath) {
                    svgStrokePath.setAttribute('stroke', colors.stroke);
                }
            } else {
                // For all other elements, use the original behavior
                originalSetColor.call(this, [element], colors);
            }
        });
    };
}

Even if you can now color the elements, the coloring disappears again as soon as you move the element. This also applies to the Sequence Flow (see GIF). I guess that bpmn-js redraws the element when it is moved with its original properties, not accounting for any runtime changes like the custom color attributes added dynamically to the SVG. Do you have any idea what I need to change such that color and Sequence Flow stays?
See good behavior at the top (classical bpmn elements)
See bad behavior at the bottom (custom elements)
coloring

@Jean-Christophe-S What is important to understand is that the renderer shall only account for what is in the model, and that there exists a simple update loop that you want to implement:

  • In your custom modeling#setColor, ensure that:
    • You persist the color in the custom element model, i.e. element.businessObject :white_check_mark:
    • Ensure you do that inside a command, this ensures:
      • Your changes do not break the do/undo chain
      • The diagram element is re-drawn :arrow_down:
  • In your renderer, ensure that it picks up the color definitions, and colors the element respectively

What you’ve showcased is a violation of this loop, as you perform the re-render within the model update loop :slight_smile:.