In this blog post, I’m going to show you how to enable custom drag and drop interactions in AEM Author mode. In my specific use case, I wanted to give authors the ability to drag assets and drop them into dialog field. Upon dropping, the asset path should be copied into the field.
This article assumes basic knowledge of AEM clientlibs, JavaScript, and jQuery.
First, let’s create a clientlib with appropriate categories and dependencies:
- categories: cq.authoring.dialog (this clientlib will be included as part of clientlibs for authoring dialog)
- dependencies: cq.authoring.editor (this client lib depends on editor clientlib, which has the interaction definition – we’ll get to that in a second)
Now that we have a clientlib, let’s define the interaction!
Granite.author.ui.Interaction is OOTB JavaScript prototype defined in the JCR path: /libs/cq/gui/components/authoring/clientlibs/editor/js/ui/ui.Interaction.js
Take a look at the prototype and its API.
Here is how to use Interaction to create a Drag and Drop experience:
(I’m attaching a working package to the end of this blog.)
/* JsLint ignore Granite object from validation */ | |
/* globals Granite*/ | |
//define new interaction for dropping assets into fields, this wil copy the Asset's path into the field | |
var customePathbrowserInteraction; | |
(function (Granite, $) { | |
if (Granite && Granite.author && Granite.author.ui && Granite.author.ui.Interaction) { | |
// selector of element we want to drag | |
var DRAGGABLE_SELECTOR = '.cq-draggable.card-asset', | |
// selecttor of target element | |
DROP_TARGET_SELECTOR = '.js-coral-pathbrowser-input', | |
// class to be added when dropzone is active, ie. we started dragging | |
// use this class to add some border color to indicate that element is droppable in this zone | |
DROPZONE_ACTIVE = 'custom-dropzone-active', | |
// class to be added when dragged element is over dropzone | |
// use this class to add some border color to indicate that element is droppable in this zone | |
DROPZONE_OVER = 'custom-dropzone-over', | |
// true to log all events | |
logMessages = true; | |
var log = function(msg){ | |
if(logMessages){ | |
console.log(msg); | |
} | |
}; | |
customePathbrowserInteraction = customePathbrowserInteraction || new Granite.author.ui.Interaction({ | |
// The selector of elements that can be dragged | |
dragOrigin: DRAGGABLE_SELECTOR, | |
// The drop target/ drop zone element selector | |
dropTarget: DROP_TARGET_SELECTOR, | |
// when entering dropTarget | |
enter: function (e) { | |
log("enter"); | |
if (e.target) { | |
$(e.target).addClass(DROPZONE_OVER); | |
} | |
}, | |
// when over dropTarget | |
over: function () { | |
log("over"); | |
}, | |
// when leaving dropTarget | |
leave: function (e) { | |
log("leave"); | |
if (e.target) { | |
$(e.target).removeClass(DROPZONE_OVER).addClass(DROPZONE_ACTIVE); | |
} | |
}, | |
// when dropping dragOrigin into dropTarget | |
drop: function (e) { | |
log("drop"); | |
if (e.target && this.path) { | |
var $target = $(e.target); | |
$target.val(this.path); | |
// optional to check if the input fiels is valid | |
$target.checkValidity(); | |
$target.updateErrorUI(); | |
} | |
}, | |
// when starting to drag dragOrigin | |
start: function (e) { | |
log("start"); | |
this.path = (e.target) ? $(e.target).data("path") : ""; | |
this.$dropTarget = $(DROP_TARGET_SELECTOR); | |
this.$dropTarget.addClass(DROPZONE_ACTIVE); | |
}, | |
// end drag or dropped | |
end: function () { | |
log("end"); | |
this.$dropTarget.removeClass(DROPZONE_ACTIVE).removeClass(DROPZONE_OVER); | |
} | |
}); | |
} | |
})(Granite, Granite.$); |
The above code is documented line by line. But let’s explore what is happening:
Line 10-19, we define some constants for classes we’d like use later on.
Line 29, we instantiate a new Interaction via its constructor. We pass an object to the constructor that has the following properties:
- dragOrigin:{String} a CSS selector (class/id/etc) of an element that can be dragged.
- dropTarget: {String} a CSS selector (class/id/etc) of the element we can drop onto.
- start {Function} called once when we start dragging dragOrigin
- end {Function} called once at the end of interaction
- enter {Function} called when dragging dragOrigin, then entering the dropTarget
- over {Function} called when dragOrigin is dragged over dropTarget
- leave {Function} called after enter if dragOrigin leaves dropTarget
- drop {Function} called if dragOrigin is dropped onto dropTarget
Additionally, on each of those functions above, we are adding CSS classes to dropTarget backed by CSS rules that will add border styling once a user starts the interaction (drags a dragOrigin).
Line 58, we are setting the dropzone’s val to the path of the dragged image. In this case, I know the dropzone is an input.
Here is a package that you can deploy on your AEM instance:
interactionSample.zip