UI / Drag/Drop Palettes

Drag and Drop for Nodes

A common use case in the sorts of applications for which the Toolkit is useful is the requirement to be able to drag and drop new nodes onto the workspace. Surface widgets support this concept via the registerDroppableNodes method.
A quick example:

var surface = toolkit.render({
  container:$("someDiv"),
  ... other options ...
});

surface.registerDroppableNodes({
  droppables:$(".aSelector"),
  typeExtractor:function(el) {
    ...
  },
  dataGenerator:function(type) {
    ...
  }
});

There are three parameters shown here on the call to registerDroppableNodes:

  • droppables A selector, array, DOM element, or element id of element(s) to configure as being draggable and droppable onto the Surface's canvas.
  • typeExtractor A function used by the Surface to derive the type of some element that was dropped onto the canvas. See example below.
  • dataGenerator This optional function is used by the Renderer to get some initial data to represent a new node that has been dropped on the canvas.

Note Our React, Vue2 and Angular integrations all offer a wrapper around this functionality.

When you drag and drop a new Node onto the canvas, the Surface needs to be able to figure out the type of the new Node. This is what the typeExtractor function does. You can also, optionally, supply a function to be used to prepare some data for the new Node - which is what the dataGenerator function does. Consider this markup:

<div id="workspace">
  <div id="canvas"></div>
    <ul id="palette">
      <li data-type="table">Table</li>
      <li data-type="view">View</li>
    </ul>
</div>

Here we see a div that we're going to turn into a Surface, and a ul containing items we're going to drag and drop onto that Surface's canvas.

First, of course, get the Surface:

var surface = toolkit.render({
  container:$("#canvas")
});

Then register those li elements, providing a function that can tell the Surface what the type of a new Node is, and a function that can give the Surface some new data:

surface.registerDroppableNodes({
  droppables:$("#palette li"),
  typeExtractor:function(el) {
    return $(el).attr("data-type");
  },
  dataGenerator:function(type) {
    return {
      ... some appropriate data ...
    }
  }
});
A note on the dataGenerator function

Even if you supply a dataGenerator, the Toolkit will still call the current NodeFactory to get the data for a new Node, so it really is optional. It may just be the case that you wish to associate something with the Node that you derived from the dragged object.

A note on Node type

Every Node in a Toolkit instance is expected to be associated with some type. The default mechanism for making this association is to look for a type member in a Node's data. When you use the drag and drop functionality discussed here, the result of your typeGenerator function is automatically written as the type member of the data for the new Node. If you use some custom mechanism for marking a Node's type then you may need to take care of configuring that in your Node Factory. In that case, also, you may wish to delete the type member from the data.

This method wraps the underlying drag and drop functionality from Katavorio, inserting handlers where it needs to, so you can provide custom drag and drop options if you need to. We recommend you at least set clone:true, in order that the original element not be moved when dragging. We tend to use the drag options shown here:

surface.registerDroppableNodes({
  droppables:$("#palette li"),
  dragOptions: {
      zIndex: 50000,
      cursor: "move",
      clone: true
  },
  dropOptions:{ 
    drop:function() {
      console.log("something was dropped");
    }
  },
  typeExtractor:function(el) {
    return $(el).attr("data-type");
  },
  dataGenerator:function(type) {
    return {
      ... some appropriate data ...
    }
  }
});

Here we see the containment parameter in use in the dragOptions object. This example also shows some drop options - although this code does not do anything useful.