/aurelia-dragula

An ES2015 port of the Dragula library, targeted at the Aurelia Framework

Primary LanguageJavaScriptMIT LicenseMIT

Aurelia-Dragula

aurelia-dragula is an Aurelia plugin which provides a simple (but not simplistic) library to add drag and drop functionality.

Because of the way Aurelia works, I have decided to fork Dragula and make it a bit more friendly to the framework.

To develop for the library, run npm install, jspm install, gulp build, gulp test

If installing in an aurelia application, jspm install npm:aurelia-dragula and remember to aurelia.use.plugin('aurelia-dragula') in your initialisation code. Aurelia-dragula is also webpack compatible.

As Aurelia doesn't support IE < 9, Aurelia-Dragula won't, either. Aurelia-dragula has zero external dependencies. None. Nada. Keine. Not a one.

Aurelia Dragula differs from the upstream library, in that it also passes the view-models for the item (and sibling on the drop event) if the item being dragged corresponds to an Aurelia Custom Element.

Usage

The element itself is called dragula-and-drop and you can bind all the options available for the main library (with camel-case converted to hyphenated attribute names in the standard way) to it as well as a couple of extras. The functions are short-hand for binding to the equivalent events and should be bound with .call and if you want to receive arguments, they should be named the same as in the Type column below:

AttributeTypeDefault Value
Description
target-classstring'drop-target'
The css class name describing any container element which can be a drop target.
source-classstring'drag-source'
The css class name describing any container element which can be a drag source.
drag-fnfunction(item, source, itemVM)null
A function to be called when dragging begins.
drop-fnfunction(item, target, source, sibling, itemVM, siblingVMnull
A function to be called when the item is dropped.
drag-end-fnfunction(item, itemVM)null
A function to be called when the drag operation completes.

E.g: viewmodel.html:

<template>
  <dragula-and-drop drop-fn.call="itemDropped(item, target, source, sibling, itemVM, siblingVM)"></dragula-and-drop>
  <div class="drag-source drop-target">
    <compose repeat.for="thing of things" view-model.bind="thing"></compose>
  </div>
</template>

viewmodel.js:

export class ViewModel {

  constructor() {
    this.things = [];
  }

  itemDropped(item, target, source, sibling, itemVM, siblingVM) {
    //do things in here
  }
}

As an extra helping hand, I have also exposed a helper function for moving an item before the sibling in an array (for such bindings as the previous ones): function moveBefore(array, itemMatcherFn, siblingMatcherFn) Where array is the array in which to move the objects, itemMatcherFn is a function which takes one of the items in array as an argument and returns a boolean, to say whether the js Object matches the item HTMLElement being dragged or not. siblingMatcherFn is a function which takes one of the items in array as an argument and returns a boolean, to say whether the js Object matches the sibling HTMLElement.

A more complete example is available here.

Options

import {Options} from 'aurelia-dragula;

The options can either be passed in as a parameter to the Dragula constructor for individual instances, or can be set globally during plugin configuration:

let options = new Options();
options.revertOnSpill = false;
let dragula = new Dragula(options);

or

aurelia.use
  .plugin('aurelia-dragula', (options) => {
    options.revertOnSpill = false;
  });

They can be used in conjunction with one another, with the individually set settings taking precedence over the global settings.

AttributeTypeDefault Value
Description
movesfunction(item, source, handle, sibling):booleanOptions.always
When the drag operation is set to begin, this function is called to see whether the item, just before sibling in the DOM, to be dragged from source, by clicking on handle is allowed to be dragged. Returning true begins the drag operation, returning false stops the drag from happening.
acceptsfunction(item, target, source, sibling):booleanOptions.always
When the item, just before sibling, being dragged from source is dropped, this function is called to see whether the target container is allowed to accept the drop. The resulting action depends on other options (revertOnSpill and removeOnSpill). Returning true allows the drop, returning false causes the Spill action to complete.
invalidfunction(item, handle):booleanOptions.invalidTarget
When starting to drag an item, by clicking on handle, this function can decide that it's not valid to drag that item after all.
containersArray<HTMLElement>[]
Can pre-load an array of HTMLElements which are considered to be containers. This list takes priority over the isContainer and accepts functions. Containers are used to determine whether elements can be dragged and can be dropped.
isContainerfunction(item)Options.never
This function returns whether the item can be considered a container. This is lower precedence than the containers and accepts options, but higher precendence than the moves function.
copybooleanfalse
If this is set to true, then dragging an item will cause it to be copied, rather than moved.
copySortSourcebooleanfalse
revertOnSpillbooleanfalse
If the item is dropped outside of a valid container, then the drag operation will be reverted.
removeOnSpillbooleanfalse
If the item is dropped outside of a valid container, then the item will be removed from the DOM.
directionDIRECTIONDIRECTION.VERTICAL
DIRECTION can be imported from aurelia-dragula and the other valid option is HORIZONTAL. This tells aurelia-dragula which direction the drag operations are expected to happen in.
ignoreInputTextSelectionbooleantrue
If set to true, if the user clicks inside an input element, then the drag operations won't be started, allowing the user to select text inside an input without causing the element to drag.
mirrorContainerNodedocument.body
This is the container to which the mirror elements are added. The mirror element is element being moved around with the mouse (not the semi-transparent placeholder element).

Events

Events can be subscribed to by calling dragula.on with the event name and a callback. They can be registered for multiple times, with different callbacks.

dragula.on('drag', (el, source) => {
  //do a thing here
});

Events can be unsubscribed from by calling the dragula.off function. If only an event type is passed, then all subscribers for that event will be unsubscribed. If a function is also given, then only the subscriber with the matching callback will be unsubscribed:

let fn = (el, source) => {
  //do a thing here
};
dragula.on('drag', fn);

dragula.off('drag', fn);
dragula.off('dragend');

If you only want the event to fire exactly once, then instead of registering and de-registering manually, the dragula.once registration function may be used:

dragula.once('drag', (item, source, itemVM) => {
  //do a thing here
});
Event NameListener Arguments
Event Description
dragitem, source, itemVM
item was lifted from source
dragenditem, itemVM
Dragging event for item ended with either cancel, remove, or drop
dropitem, target, source, sibling, itemVM, siblingVM
item was dropped into target, before a sibling element, and originall came from source
cancelitem, container, source, itemVM
item was being dragged but it got nowhere and went back into container, its last stable parent; item originally came from source
removeitem, container, source, itemVM
item was being dragged but it got nowhere and was removed from the DOM. Its last stable parent; item originally came from source
shadowitem, container, source, itemVM
item, the visual aid shadow, was moved into container. May trigger many times as the position of item changes, even within the same container. item originally came from source.
overitem, container, source, itemVM
item is over container, and originally came from source
outitem, container, source, itemVM
item was dragged out of container and originally came from source
clonedclone, original, type
DOM element original was cloned as clone, of type ('mirror' or 'copy'). Fired for mirror images and when copy: true