Skip to content

Commit

Permalink
add dragenter handler + preventDefaults + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Yvem committed Aug 6, 2015
1 parent 7fba77a commit 531ad60
Show file tree
Hide file tree
Showing 9 changed files with 481 additions and 172 deletions.
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org

root = true

[*]

indent_style = space
indent_size = 2

end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
53 changes: 53 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"bitwise": true,
"camelcase": true,
"curly": false,
"eqeqeq": true,
"es3": false,
"forin": true,
"immed": true,
"indent": 2,
"latedef": "nofunc",
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": false,
"plusplus": false,
"quotmark": true,
"regexp": false,
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"maxparams": 4,
"maxdepth": 2,
"maxstatements": 30,
"maxcomplexity": 10,
"maxlen": 110,

"asi": false,
"boss": false,
"debug": false,
"eqnull": true,
"esnext": false,
"evil": false,
"expr": false,
"funcscope": false,
"globalstrict": false,
"iterator": false,
"lastsemic": false,
"laxbreak": false,
"laxcomma": false,
"loopfunc": false,
"moz": false,
"multistr": true,
"proto": false,
"scripturl": false,
"smarttabs": false,
"shadow": false,
"sub": false,
"supernew": false,
"validthis": true,

"node": true
}
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
# angular-draganddrop
[![Build Status](https://travis-ci.org/lemonde/angular-draganddrop.svg?branch=master)](https://travis-ci.org/lemonde/angular-draganddrop)
[![Dependency Status](https://david-dm.org/neoziro/angular-draganddrop.svg?theme=shields.io)](https://david-dm.org/neoziro/angular-draganddrop)
[![devDependency Status](https://david-dm.org/neoziro/angular-draganddrop/dev-status.svg?theme=shields.io)](https://david-dm.org/neoziro/angular-draganddrop#info=devDependencies)
[![Dependency Status](https://david-dm.org/lemonde/angular-draganddrop.svg?theme=shields.io)](https://david-dm.org/lemonde/angular-draganddrop)
[![devDependency Status](https://david-dm.org/lemonde/angular-draganddrop/dev-status.svg?theme=shields.io)](https://david-dm.org/lemonde/angular-draganddrop#info=devDependencies)

Drag and drop directives for Angular using native HTML5 API.


## introduction
This module defines 2 directives :
* "draggable" for a draggable element
* "drop" for a drop zone
It handles data type control.

Note HTML5 drag&drop is not trivial. Recommended reads :
* http://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html
* https://html.spec.whatwg.org/multipage/interaction.html#dnd


## Install

### Using bower
Expand Down Expand Up @@ -65,22 +77,35 @@ angular.module('controllers.dragDrop', ['draganddrop'])

### "draggable" directive

Parameters :
- "draggable" Make the element draggable. Accepts a boolean.
- "effect-allowed" Allowed effects for the dragged element, see https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer#effectAllowed.28.29. Accepts a string.
- "draggable-type" Type of data object attached to the dragged element, this type is prefixed by "json/". Accepts a string.
- "draggable-data" Data attached to the dragged element, data are serialized in JSON. Accepts an Angular expression.
- "drag-start" (optional) an Angular expression to be evaluated on drag start ("dragstart" event).
- "drag-end" (optional) an Angular expression to be evaluated on drag end ("dragend" event).

The draggable directive serializes data as JSON and prefix the specified type with "json/".

### "drop" directive

The drop directive automatically :
- calls the event.preventDefault() for dragenter and dragleave, as asked in the spec
- unserializes data with the "json" format, other data are not formatted
- throttles the dragover event (200ms) to avoid burning your CPU during the drag&drop

Parameters :
- "drop" Drop handler, executed on drop. Accepts an Angular expression.
- "drop-effect" Drop effect to set, see https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer#dropEffect.28.29. Accepts a string.
- "drop-accept" Types accepted or function to prevent unauthorized drag and drop. Accepts a string, an array, a function or a boolean.
- "drag-over" Drag over handler, executed on drag over. Accepts an Angular expression.
- "drag-over-class" Class set on drag over, when the drag is authorized. Accepts a string.
- "drag-over" (optional) an Angular expression to be evaluated on drag over ("dragover" event).
- "drag-enter" (optional) an Angular expression to be evaluated on drag enter ("dragenter" event).
- "drag-leave" (optional) an Angular expression to be evaluated on drag leave ("dragleave" event).
- "drop" (optional) an Angular expression to be evaluated on drag over ("drop" event).

Handlers are : `(scope, { $data: data, $event: event })`

The drop directive automatically unserializes data with the "json" format, other data are not formatted.

## Browsers support

Expand Down
66 changes: 49 additions & 17 deletions angular-draganddrop.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/*! Angular draganddrop v0.2.1 | (c) 2013 Greg Bergé | License MIT */

angular
.module('draganddrop', [])
.directive('draggable', ['$parse', draggableDirective])
Expand Down Expand Up @@ -46,7 +44,7 @@ function draggableDirective($parse) {
var dragEndHandler = $parse(attrs.dragEnd);

attrs.$observe('draggable', function (draggable) {
domElement.draggable = draggable !== 'false';
domElement.draggable = (draggable !== 'false');
});

domElement.addEventListener('dragstart', dragStartListener);
Expand All @@ -57,15 +55,28 @@ function draggableDirective($parse) {
domElement.removeEventListener('dragend', dragEndListener);
});

// Convenience function to help the directive user.
// AngularJS default error is unclear.
function informativeDraggableDataEval() {
try {
return scope.$eval(draggableData);
}
catch(e) {
// throw a clearer error
throw new Error('draggable-data can\'t be parsed by Angular : ' +
'check your draggable directive invocation !');
}
}

function dragStartListener(event) {
// Restrict drag effect.
event.dataTransfer.effectAllowed = effectAllowed || event.dataTransfer.effectAllowed;

// Eval and serialize data.
var data = scope.$eval(draggableData);
var data = informativeDraggableDataEval();
var jsonData = angular.toJson(data);

// Call dragStartHandler
// Call custom handler
scope.$apply(function () {
dragStartHandler(scope, { $data: data, $event: event });
});
Expand All @@ -78,9 +89,9 @@ function draggableDirective($parse) {

function dragEndListener(event) {
// Eval and serialize data.
var data = scope.$eval(draggableData);
var data = informativeDraggableDataEval();

// Call dragEndHandler
// Call custom handler
scope.$apply(function () {
dragEndHandler(scope, { $data: data, $event: event });
});
Expand Down Expand Up @@ -118,15 +129,18 @@ function dropDirective($parse) {
var dragOverClass = attrs.dragOverClass;

var dragOverHandler = $parse(attrs.dragOver);
var dragEnterHandler = $parse(attrs.dragEnter);
var dragLeaveHandler = $parse(attrs.dragLeave);
var dropHandler = $parse(attrs.drop);

domElement.addEventListener('dragover', dragOverListener);
domElement.addEventListener('dragenter', dragEnterListener);
domElement.addEventListener('dragleave', dragLeaveListener);
domElement.addEventListener('drop', dropListener);

scope.$on('$destroy', function () {
domElement.removeEventListener('dragover', dragOverListener);
domElement.removeEventListener('dragenter', dragEnterListener);
domElement.removeEventListener('dragleave', dragLeaveListener);
domElement.removeEventListener('drop', dropListener);
});
Expand All @@ -137,53 +151,71 @@ function dropDirective($parse) {
// Check if type is accepted.
if (! accepts(scope.$eval(dropAccept), event)) return true;

if (dragOverClass) element.addClass(dragOverClass);

// Set up drop effect to link.
event.dataTransfer.dropEffect = dropEffect || event.dataTransfer.dropEffect;

// Prevent default to accept drag and drop.
event.preventDefault();

if (dragOverClass) element.addClass(dragOverClass);

var now = new Date().getTime();
if (now - throttledDragover < 200) return;
throttledDragover = now;

if (!attrs.dragOver) return;
if (! attrs.dragOver) return;

var data = getData(event);

// Call dragOverHandler
// Call custom handler
scope.$apply(function () {
dragOverHandler(scope, { $data: data, $event: event });
});
}

function dragLeaveListener(event) {
function dragEnterListener(event) {
// Check if type is accepted.
if (! accepts(scope.$eval(dropAccept), event)) return true;

removeDragOverClass();
// Prevent default to accept drag and drop.
event.preventDefault();

if (!attrs.dragLeave) return;
if (! attrs.dragEnter) return;

var data = getData(event);

// Call dragLeaveHandler
// Call custom handler
scope.$apply(function () {
dragLeaveHandler(scope, { $data: data, $event: event });
dragEnterHandler(scope, { $data: data, $event: event });
});
}


function dragLeaveListener(event) {
// Check if type is accepted.
if (! accepts(scope.$eval(dropAccept), event)) return true;

// Prevent default to accept drag and drop.
event.preventDefault();

removeDragOverClass();

if (! attrs.dragLeave) return;

var data = getData(event);

// Call custom handler
scope.$apply(function () {
dragLeaveHandler(scope, { $data: data, $event: event });
});
}

function dropListener(event) {
var data = getData(event);

removeDragOverClass();

// Call dropHandler
// Call custom handler
scope.$apply(function () {
dropHandler(scope, { $data: data, $event: event });
});
Expand Down
3 changes: 1 addition & 2 deletions angular-draganddrop.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 531ad60

Please sign in to comment.