Adding extra information in search list when CTRL + F is pressed

We have used BPMN in our Javascript (MERN) based project, and applied certain customizations in components and design.
When I press ctrl + F then I get list of components matching the entered value, but I also want to show the name of subprocess which the element is part of in the search list. How can I customize the search functionality of BPMN?

image

In the attached screenshot, it shows id of the element below the process name, but I also want subprocess name as well. Can someone please assist?

@barmac @philippfromme @Martin please help!!

The search UI itself is not customizable. You could implement a custom search provider and change what the primary and secondary tokens are but that’s a little hacky. Maybe, building your own search UI would be easier.

Please don’t do that, @shaansrivastava!

Ok, but you said custom search UI can be made, so can you please tell how to disable the BPMN searchPad?
We need this because we do not get to know that the component is part of which subprocess!!
@philippfromme

I tried implementing custom search provider. But when I search, the element is not being highlighted, and also as I hover on the search results list, it gives error:

Cannot read properties of undefined (reading 'id')
TypeError: Cannot read properties of undefined (reading 'id')
    at http://localhost:3005/static/js/bundle.js:65545:77
    at Array.filter (<anonymous>)

Code for:

  1. CustomSearchProvider:
export default class CustomSearchPadProvider {
  constructor(searchPad, eventBus, elementRegistry ) {
    this.searchPad = searchPad;
    this.eventBus = eventBus;
    this.elementRegistry = elementRegistry;
    
    // Register the provider
    searchPad.registerProvider(this);
  }

  /**
   * Define how search entries are filtered or modified.
   * @param {String} pattern - The search pattern entered by the user.
   * @returns {Array} List of elements matching the pattern.
   */
  find(pattern) {
    console.log("Searching for:", pattern);

    if (!pattern) return [];
    const allElements = this.elementRegistry.getAll();

    // Filter elements based on search pattern
    const matchingElements = allElements
  .filter((element) => {
    return (
      element.businessObject &&
      element.businessObject.name &&
      element.businessObject.name.toLowerCase().includes(pattern.toLowerCase())
    );
  })
  .map((element) => {
    const name = element.businessObject.name;
    const id = element.id;
    const type = element.type;

    // Preserve all original properties
    const newElement = {
      id,
      type,
      businessObject: element.businessObject, // Ensure businessObject is not lost
    };

    // Highlight matched part in primaryTokens
    const primaryTokens = [];
    const lowerName = name.toLowerCase();
    const lowerPattern = pattern.toLowerCase();

    let startIndex = lowerName.indexOf(lowerPattern);
    if (startIndex !== -1) {
      if (startIndex > 0) {
        primaryTokens.push({ normal: name.substring(0, startIndex) });
      }
      primaryTokens.push({ matched: name.substring(startIndex, startIndex + pattern.length) });
      if (startIndex + pattern.length < name.length) {
        primaryTokens.push({ normal: name.substring(startIndex + pattern.length) });
      }
    } else {
      primaryTokens.push({ normal: name });
    }

    return {
      element: newElement, // Store the new element object separately
      primaryTokens, // Properly formatted primaryTokens
      secondaryTokens: [{ normal: id }], // Ensure secondaryTokens follow the correct format
    };
  });
    return matchingElements; 
  }
}

CustomSearchPadProvider.$inject = ["searchPad", "eventBus", "elementRegistry"];

This is registered as additional module in modeler while loading flow modeler.

  1. CustomSearchPad:
export default class CustomSearchPad {
    constructor(eventBus, elementRegistry, canvas, overlays) {
      this.eventBus = eventBus;
      this.elementRegistry = elementRegistry;
      this.canvas = canvas;
      this.overlays = overlays;
      this.highlightElements = this.highlightElements.bind(this);
  
      console.log("CustomSearchPadProvider initialized"); // Debugging log

    }
  

    find(pattern) {
        if (!pattern) return [];
    
        // Get all elements from the registry
        const allElements = this.elementRegistry.getAll();
    
        // Filter elements based on search pattern
        const matchingElements = allElements.filter(element => {
          return element.businessObject &&
                 element.businessObject.name &&
                 element.businessObject.name.toLowerCase().includes(pattern.toLowerCase());
        });
    
        return matchingElements;
      }
  }
  
  CustomSearchPad.$inject = ['eventBus', 'elementRegistry', 'canvas', 'overlays'];
  

This file is passed into CustomModule, which has following:

const CustomModule = {
  __init__: ['customContextPad', 'customSearchPad'],
  customContextPad: ['type', CustomContextPad],
  customSearchPad: ['type', CustomSearchPad]
};

@philippfromme can you please help!!

@philippfromme @barmac can you please tell how to disable the default BPMN search functionality?

You can disable built-in search by passing an empty object with the same name as the module. Cf. Deactivation and activation of modules - #4 by Jonas