How can I fire the 'create.task' action when user drops a custom element in the canvas?

Hi, my goal is to implement a tool which allows to build flow charts dynamically as the user selects tasks and in/out files associated. So I do not need the palette but I need a way to fire a create.task event each time the user drops a custom element in the bpmnjs dropping zone.
Any help would be very appreciated…

What do you want to do when you detect such an event? Please share more details so I know what solution suits best your use case.

I made a schema so it is easier to explain what I’m looking for.
The user can choose among a number of available tasks (each one running different operations in the back-end).

1- the user drags and drops a task in the bpmn-jd dropping zone

2 - the drop event is fired and a new bpmn-Task is drawn

3 - firing the drop event also redirect the user to a webpage where it is possible to set input/output files.

4 - the tool reads out the in/out infos and dinamically adds as many DataStoreReference (DSR) objs as the in/out files reqiured by the Task selected. The input DSRs connect to bpmn-task with a → arrow, the output DSR with ← arrow.

5- a new Task is dropped and the user is redirected to set in/out files for the current Task (n)

6 - the tool adds a bpmn-task just under the (n-1) DSR output, if one of the (n) input files is chosen among the (n-1) outputs.

And so on……
my_tool
Hope this is clear enough…
Thank you!!

And how do you define the drop zone? Is it a subprocess or what?

You can have a look at the command interceptor codesandbox.

I define the drop-zone as follows:

<main>
   <form>
       <h5>
           Available Tasks
       </h5>
       <div class="col s12 ">
            <ul id="tasksList" class></ul>
       </div>
   </form>
   <div class="content droppable" id="js-drop-zone" style="width: 500px; height: 600px; border-style: solid;">
       <div class="message intro">
            <div class="note">
                <p id="drop-it-here">Drop a module here to get started. </p>
             </div>
       </div>
       <div class="canvas " id="js-canvas"></div>
     </div>
</main>

and the js is almost as the original bpmn-js-example/modeler/app/app.js

import $ from 'jquery';

import BpmnModeler from 'bpmn-js/lib/Modeler';

import diagramXML from '../resources/newDiagram.bpmn';

var container = $('#js-drop-zone');

var modeler = new BpmnModeler({
  container: '#js-canvas'
});

function createNewDiagram() {
  // console.log('openDiagram xml');
  openDiagram(diagramXML);
}

async function openDiagram(xml) {
  try {

    await modeler.importXML(xml);

    container
      .removeClass('with-error')
      .addClass('with-diagram');
  } catch (err) {

    container
      .removeClass('with-diagram')
      .addClass('with-error');

    container.find('.error pre').text(err.message);

    console.error(err);
  }
}

function registerFileDrop(container, callback) {
  function handleDroppedModule(e) {
    e.stopPropagation();
    e.preventDefault();
    $('#drop-it-here').remove();
    createNewDiagram();
    var activeTask = JSON.parse(localStorage.getItem('moduleObj'));

    var taskName = activeTask.name;
    //  gotoTaskSetup(taskName);

	// Here I need to create the shapes 
    // var taskshape = elementFactory.createShape(assign({ type: 'bpmn: Task' }));
    // create.start(event, taskshape);

    if (localStorage.getItem('inputs_array') != undefined) {
      var inputs_array = JSON.parse(localStorage.getItem('inputs_array') || '{}');
    } else {
      console.log('***UNDEFINED inputs_array ***' + JSON.stringify(inputs_array));
    }
    if (localStorage.getItem('outputs_array') != undefined) {
      var outputs_array = JSON.parse(localStorage.getItem('outputs_array') || '{}');
    } else {
      console.log('***UNDEFINED outputs_array ***' + JSON.stringify(outputs_array));
    }
   [...]
  }
  function handleDragOver(e) {
    e.stopPropagation();
    e.preventDefault();

    e.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
  }
  // dragover and drop listeners
  container.get(0).addEventListener('dragover', handleDragOver, false);
  container.get(0).addEventListener('drop', handleDroppedModule, false);
}


// file drag / drop ///////////////////////
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
  // Great success! All the File APIs are supported.
} else {
  alert('The File APIs are not fully supported in this browser.');
}
// check file api availability
if (!window.FileList || !window.FileReader) {
  window.alert(
    'Looks like you use an older browser that does not support drag and drop. ' +
    'Try using Chrome, Firefox or the Internet Explorer > 10.');
} else {
  console.log('register openDiagram');
  registerFileDrop(container, openDiagram);
}

// bootstrap diagram functions
// click handler
$(function() {

  $('#js-create-diagram').click(function(e) {
    e.stopPropagation();
    e.preventDefault();

    createNewDiagram();
  });

  var downloadLink = $('#js-download-diagram');
  var downloadSvgLink = $('#js-download-svg');

  $('.buttons a').click(function(e) {
    if (!$(this).is('.active')) {
      e.preventDefault();
      e.stopPropagation();
    }
  });
  // the following is the function called when the user request the download of the svg/bpmn diagram
  // non serve nel nostro caso o magari si
  function setEncoded(link, name, data) {
    var encodedData = encodeURIComponent(data);

    if (data) {
      link.addClass('active').attr({
        'href': 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData,
        'download': name
      });
    } else {
      link.removeClass('active');
    }
  }

  var exportArtifacts = debounce(async function() {

    try {

      const { svg } = await modeler.saveSVG();

      setEncoded(downloadSvgLink, 'diagram.svg', svg);
    } catch (err) {

      console.error('Error happened saving svg: ', err);
      setEncoded(downloadSvgLink, 'diagram.svg', null);
    }

    try {

      const { xml } = await modeler.saveXML({ format: true });
      setEncoded(downloadLink, 'diagram.bpmn', xml);
    } catch (err) {

      console.error('Error happened saving XML: ', err);
      setEncoded(downloadLink, 'diagram.bpmn', null);
    }
  }, 500);

  modeler.on('commandStack.changed', exportArtifacts);
});

// helpers //////////////////////

function debounce(fn, timeout) {

  var timer;

  return function() {
    if (timer) {
      clearTimeout(timer);
    }

    timer = setTimeout(fn, timeout);
  };
}

In the drop event listener function:

function handleDroppedModule(e)

I’d need to create the requested shapes

    // var taskshape = elementFactory.createShape(assign({ type: 'bpmn: Task' }));
    // create.start(event, taskshape);

but it is not clear to me how to correctly do it…

Can you share a CodeSandbox that reproduces your issue in a way that we can inspect it?

Maybe I scared you with my schema and my posts but I just would like to know how to draw shapes for Task and DSSR objects without using the event-driven palette.

I’ve created an example where you can click a button and this creates a task: bpmn-js Sandbox (forked) - CodeSandbox

3 Likes

thank you, this is a perfect starting point. Now I have to make the click event to be a drop event, and implement all the steps in my schema.
I will be back :wink:

1 Like