No provider for ....(popupMenu)

I am working on the context-pad feature and I keep on having the following error and I’m not sure on how to resolve this. Any help will be appreciated:

image

Here is my code:

import {
  assign,
  isArray,
} from 'min-dash';

import {
  hasPrimaryModifier
} from 'diagram-js/lib/util/Mouse';


/**
 * A provider for od elements context pad.
 */
export default function ContextPadProvider(
    config, injector, eventBus, connect, create,
    elementFactory, contextPad, modeling, rules,
    translate, popupMenu) {

  config = config || {};

  contextPad.registerProvider(this);

  this._connect = connect;
  this._create = create;
  this._elementFactory = elementFactory;
  this._contextPad = contextPad;

  this._modeling = modeling;

  this._rules = rules;
  this._translate = translate;

  this._popupMenu = popupMenu;

  if (config.autoPlace !== false) {
    this._autoPlace = injector.get('autoPlace', false);
  }

  eventBus.on('create.end', 250, function(event) {
    let context = event.context,
        shape = context.shape;

    if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) {
      return;
    }

    let entries = contextPad.getEntries(shape);

    if (entries.replace) {
      entries.replace.action.click(event, shape);
    }
  });
}

ContextPadProvider.$inject = [
  'config.contextPad',
  'injector',
  'eventBus',
  'connect',
  'create',
  'elementFactory',
  'contextPad',
  'modeling',
  'rules',
  'translate',
  'popupMenu',
];


ContextPadProvider.prototype.getContextPadEntries = function(element) {

  const {
    _rules: rules,
    _modeling: modeling,
    _translate: translate,
    _connect: connect,
    _elementFactory: elementFactory,
    _contextPad: contextPad,
    _autoPlace: autoPlace,
    _create: create,
    _popupMenu: popupMenu
  } = this;

  let actions = {};

  if (element.type === 'label') {
    return actions;
  }

  createDeleteEntry(actions);

  if (element.type === 'dcr:Object') {
    createReplaceMenu(actions);

  }

  return actions;

  function removeElement() {
    modeling.removeElements([ element ]);
  }

  function createDeleteEntry(actions) {

    // delete element entry, only show if allowed by rules
    let deleteAllowed = rules.allowed('elements.delete', { elements: [ element ] });

    if (isArray(deleteAllowed)) {

      // was the element returned as a deletion candidate?
      deleteAllowed = deleteAllowed[0] === element;
    }

    if (deleteAllowed) {
      assign(actions, {
        'delete': {
          group: 'edit',
          className: 'bpmn-icon-trash',
          title: translate('Remove'),
          action: {
            click: removeElement
          }
        }
      });
    }
  }



  function getReplaceMenuPosition(element) {
    var Y_OFFSET = 5;

    var pad = contextPad.getPad(element).html;

    var padRect = pad.getBoundingClientRect();

    var pos = {
      x: padRect.left,
      y: padRect.bottom + Y_OFFSET
    };

    return pos;
  }

 

  function createReplaceMenu(actions) {
    assign(actions, {
      'dcr-replace': {
        group: 'edit',
        className: 'bpmn-icon-screw-wrench',
        title: 'Change state of the selected object',   //'Link object to other objects',
        action: {
          click: function (event, element) {//appendDcrTaskStart

            var position = assign(getReplaceMenuPosition(element), {
              cursor: { x: event.x, y: event.y }
            });

            popupMenu.open(element, 'dcr-replace', position, {
              title: translate('Change element'),
              width: 300,
              search: true
            });
          }/**/
        },
      },
    });
  }
 


  function startConnect(event, element) {
    connect.start(event, element);
  }

  /**
   * Create an append action
   *
   * @param {string} type
   * @param {string} className
   * @param {string} [title]
   * @param {Object} [options]
   *
   * @return {Object} descriptor
   */
  function appendAction(type, className, title, options) {

    if (typeof title !== 'string') {
      options = title;
      title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') });
    }

    function appendStart(event, element) {

      var shape = elementFactory.createShape(assign({ type: type }, options));
      create.start(event, shape, {
        source: element
      });
    }


    var append = autoPlace ? function(event, element) {
      var shape = elementFactory.createShape(assign({ type: type }, options));

      autoPlace.append(element, shape);
    } : appendStart;


    return {
      group: 'model',
      className: className,
      title: title,
      action: {
        dragstart: appendStart,
        click: append
      }
    };
  }
};

You need a provider for popupMenu but non seems to be available. How did you configure the editor?

I made the provider below for the popup just so I can open a popup menu with no entries first before going ahead to add some entries:

import inherits from 'inherits';
import PopupMenuProvider from 'diagram-js/lib/features/popup-menu/PopupMenuProvider';



export default function DCRPopupProvider(
  injector, popupMenu, modeling
) {
  injector.invoke(PopupMenuProvider, this);
  this._popupMenu = popupMenu;
  this._modeling = modeling;
}

inherits(DCRPopupProvider, PopupMenuProvider);

DCRPopupProvider.$inject = [
  'injector',
  'popupMenu',
  'modeling'
];

DCRPopupProvider.prototype.getHeaderEntries = function(element) {
  return [];
};

DCRPopupProvider.prototype.getEntries = function(element) {
  return [];
};

DCRPopupProvider.prototype.register = function() {
  this._popupMenu.registerProvider('dcr-replace', this);
};

And I keep on getting this error:
image

Which I assume results from the fact that PopupMenuProvider is imported but not used in the injector.invoke() function (or vice versa) but I’m not too sure on why this error because the code seems correct to me:

As of diagram-js@12 PopupMenuProvider is an interface.

That means:

  • If you use TypeScript you can consume it and implement it
  • If you use JSDoc then you can declare your popup menu to implement it

In both cases you have to implement the actual methods yourself, you cannot do the following:

class MyPopupMenuProvider extends PopupMenuProvider { ... }
1 Like