Skip to content

Commit 7b4307d

Browse files
Merge pull request marceljuenemann#99 from marceljuenemann/dev
Merge 1.3.0 into master
2 parents e2aa9d5 + f773ea0 commit 7b4307d

12 files changed

+182
-53
lines changed

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
# 1.3.0 (2015-08-20)
2+
3+
## Features
4+
5+
- **New callbacks**: `dnd-dragend`, `dnd-canceled` and `dnd-inserted`.
6+
- **Custom placeholder elements**: `dnd-list` elements can have custom elements by creating a child element with `dnd-placeholder` class. This is useful for cases where a simple `li` element is not sufficient.
7+
- **dnd-nodrag directive**: This directive can be used inside `dnd-draggable` to prevent dragging certain areas. This is useful for input elements inside the draggable or creating handle elements.
8+
9+
## Bug Fixes
10+
11+
- **Fix user selection inside dnd-draggable**: The `selectstart` event is no longer cancelled.
12+
- **Fix click handler compatibility**: Propagation of click events is now only stopped if the `dnd-selected` attribute is present.
13+
- **Fix IE9 glitch**: Double clicks in IE9 previously would trigger the `dnd-moved` callback, and therefore remove items accidentially. (issue #21)
14+
15+
## Tested browsers
16+
17+
- Chrome 43 (Win7)
18+
- Chrome 44 (Ubuntu)
19+
- Chrome 44 (Mac)
20+
- Firefox 40 (Win7)
21+
- Firefox 39 (Ubuntu)
22+
- Safari 8.0.8 (Mac)
23+
- Internet Explorer 11 (IE9 & 10 in compatibility mode)
24+
125
# 1.2.0 (2014-11-30)
226

327
## Bug Fixes

README.md

Lines changed: 34 additions & 28 deletions
Large diffs are not rendered by default.

angular-drag-and-drop-lists.js

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* angular-drag-and-drop-lists v1.2.0
2+
* angular-drag-and-drop-lists v1.3.0
33
*
44
* Copyright (c) 2014 Marcel Juenemann mail@marcel-juenemann.de
55
* Copyright (c) 2014-2015 Google Inc.
@@ -33,11 +33,16 @@ angular.module('dndLists', [])
3333
* remove your element from the original list in this callback, since the
3434
* directive is not doing that for you automatically. The original dragend
3535
* event will be provided in the local event variable.
36+
* - dnd-canceled Callback that is invoked if the element was dragged, but the operation was
37+
* canceled and the element was not dropped. The original dragend event will
38+
* be provided in the local event variable.
3639
* - dnd-copied Same as dnd-moved, just that it is called when the element was copied
3740
* instead of moved. The original dragend event will be provided in the local
3841
* event variable.
3942
* - dnd-dragstart Callback that is invoked when the element was dragged. The original
4043
* dragstart event will be provided in the local event variable.
44+
* - dnd-dragend Callback that is invoked when the drag operation ended. Available local
45+
* variables are event and dropEffect.
4146
* - dnd-type Use this attribute if you have different kinds of items in your
4247
* application and you want to limit which items can be dropped into which
4348
* lists. Combine with dnd-allowed-types on the dnd-list(s). This attribute
@@ -123,16 +128,19 @@ angular.module('dndLists', [])
123128
case "move":
124129
$parse(attr.dndMoved)(scope, {event: event});
125130
break;
126-
127131
case "copy":
128132
$parse(attr.dndCopied)(scope, {event: event});
129133
break;
134+
case "none":
135+
$parse(attr.dndCanceled)(scope, {event: event});
136+
break;
130137
}
138+
$parse(attr.dndDragend)(scope, {event: event, dropEffect: dropEffect});
131139
});
132140

133141
// Clean up
134142
element.removeClass("dndDragging");
135-
element.removeClass("dndDraggingSource");
143+
$timeout(function() { element.removeClass("dndDraggingSource"); }, 0);
136144
dndDragTypeWorkaround.isDragging = false;
137145
event.stopPropagation();
138146
});
@@ -142,12 +150,14 @@ angular.module('dndLists', [])
142150
* specified with the dnd-selected attribute.
143151
*/
144152
element.on('click', function(event) {
145-
event = event.originalEvent || event;
153+
if (!attr.dndSelected) return;
146154

155+
event = event.originalEvent || event;
147156
scope.$apply(function() {
148157
$parse(attr.dndSelected)(scope, {event: event});
149158
});
150159

160+
// Prevent triggering dndSelected in parant elements.
151161
event.stopPropagation();
152162
});
153163

@@ -156,7 +166,6 @@ angular.module('dndLists', [])
156166
*/
157167
element.on('selectstart', function() {
158168
if (this.dragDrop) this.dragDrop();
159-
return false;
160169
});
161170
};
162171
}])
@@ -194,6 +203,11 @@ angular.module('dndLists', [])
194203
* - index: The position in the list at which the element would be dropped.
195204
* - item: The transferred object.
196205
* - type: The dnd-type set on the dnd-draggable, or undefined if unset.
206+
* - dnd-inserted Optional expression that is invoked after a drop if the element was
207+
* actually inserted into the list. The same local variables as for
208+
* dnd-drop will be available. Note that for reorderings inside the same
209+
* list the old element will still be in the list due to the fact that
210+
* dnd-moved was not called yet.
197211
* - dnd-external-sources Optional boolean expression. When it evaluates to true, the list accepts
198212
* drops from sources outside of the current browser tab. This allows to
199213
* drag and drop accross different browser tabs. Note that this will allow
@@ -205,17 +219,19 @@ angular.module('dndLists', [])
205219
* CSS classes:
206220
* - dndPlaceholder When an element is dragged over the list, a new placeholder child
207221
* element will be added. This element is of type li and has the class
208-
* dndPlaceholder set.
222+
* dndPlaceholder set. Alternatively, you can define your own placeholder
223+
* by creating a child element with dndPlaceholder class.
209224
* - dndDragover Will be added to the list while an element is dragged over the list.
210225
*/
211226
.directive('dndList', ['$parse', '$timeout', 'dndDropEffectWorkaround', 'dndDragTypeWorkaround',
212227
function($parse, $timeout, dndDropEffectWorkaround, dndDragTypeWorkaround) {
213228
return function(scope, element, attr) {
214229
// While an element is dragged over the list, this placeholder element is inserted
215230
// at the location where the element would be inserted after dropping
216-
var placeholder = angular.element("<li class='dndPlaceholder'></li>");
231+
var placeholder = getPlaceholderElement();
217232
var placeholderNode = placeholder[0];
218233
var listNode = element[0];
234+
placeholder.remove();
219235

220236
var horizontal = attr.dndHorizontalList && scope.$eval(attr.dndHorizontalList);
221237
var externalSources = attr.dndExternalSources && scope.$eval(attr.dndExternalSources);
@@ -277,7 +293,7 @@ angular.module('dndLists', [])
277293

278294
// At this point we invoke the callback, which still can disallow the drop.
279295
// We can't do this earlier because we want to pass the index of the placeholder.
280-
if (attr.dndDragover && !invokeCallback(attr.dndDragover, event)) {
296+
if (attr.dndDragover && !invokeCallback(attr.dndDragover, event, getPlaceholderIndex())) {
281297
return stopDragover();
282298
}
283299

@@ -312,8 +328,9 @@ angular.module('dndLists', [])
312328
}
313329

314330
// Invoke the callback, which can transform the transferredObject and even abort the drop.
331+
var index = getPlaceholderIndex();
315332
if (attr.dndDrop) {
316-
transferredObject = invokeCallback(attr.dndDrop, event, transferredObject);
333+
transferredObject = invokeCallback(attr.dndDrop, event, index, transferredObject);
317334
if (!transferredObject) {
318335
return stopDragover();
319336
}
@@ -322,8 +339,9 @@ angular.module('dndLists', [])
322339
// Retrieve the JSON array and insert the transferred object into it.
323340
var targetArray = scope.$eval(attr.dndList);
324341
scope.$apply(function() {
325-
targetArray.splice(getPlaceholderIndex(), 0, transferredObject);
342+
targetArray.splice(index, 0, transferredObject);
326343
});
344+
invokeCallback(attr.dndInserted, event, index, transferredObject);
327345

328346
// In Chrome on Windows the dropEffect will always be none...
329347
// We have to determine the actual effect manually from the allowed effects
@@ -380,6 +398,21 @@ angular.module('dndLists', [])
380398
return mousePointer < targetPosition + targetSize / 2;
381399
}
382400

401+
/**
402+
* Tries to find a child element that has the dndPlaceholder class set. If none was found, a
403+
* new li element is created.
404+
*/
405+
function getPlaceholderElement() {
406+
var placeholder;
407+
angular.forEach(element.children(), function(childNode) {
408+
var child = angular.element(childNode);
409+
if (child.hasClass('dndPlaceholder')) {
410+
placeholder = child;
411+
}
412+
});
413+
return placeholder || angular.element("<li class='dndPlaceholder'></li>");
414+
}
415+
383416
/**
384417
* We use the position of the placeholder node to determine at which position of the array the
385418
* object needs to be inserted
@@ -426,10 +459,10 @@ angular.module('dndLists', [])
426459
/**
427460
* Invokes a callback with some interesting parameters and returns the callbacks return value.
428461
*/
429-
function invokeCallback(expression, event, item) {
462+
function invokeCallback(expression, event, index, item) {
430463
return $parse(expression)(scope, {
431464
event: event,
432-
index: getPlaceholderIndex(),
465+
index: index,
433466
item: item || undefined,
434467
external: !dndDragTypeWorkaround.isDragging,
435468
type: dndDragTypeWorkaround.isDragging ? dndDragTypeWorkaround.dragType : undefined
@@ -451,6 +484,42 @@ angular.module('dndLists', [])
451484
};
452485
}])
453486

487+
/**
488+
* Use the dnd-nodrag attribute inside of dnd-draggable elements to prevent them from starting
489+
* drag operations. This is especially useful if you want to use input elements inside of
490+
* dnd-draggable elements or create specific handle elements.
491+
*/
492+
.directive('dndNodrag', function() {
493+
return function(scope, element, attr) {
494+
// Set as draggable so that we can cancel the events explicitly
495+
element.attr("draggable", "true");
496+
497+
/**
498+
* Since the element is draggable, the browser's default operation is to drag it on dragstart.
499+
* We will prevent that and also stop the event from bubbling up.
500+
*/
501+
element.on('dragstart', function(event) {
502+
event = event.originalEvent || event;
503+
504+
// If a child element already reacted to dragstart and set a dataTransfer object, we will
505+
// allow that. For example, this is the case for user selections inside of input elements.
506+
if (!(event.dataTransfer.types && event.dataTransfer.types.length)) {
507+
event.preventDefault();
508+
}
509+
event.stopPropagation();
510+
});
511+
512+
/**
513+
* Stop propagation of dragend events, otherwise dnd-moved might be triggered and the element
514+
* would be removed.
515+
*/
516+
element.on('dragend', function(event) {
517+
event = event.originalEvent || event;
518+
event.stopPropagation();
519+
});
520+
};
521+
})
522+
454523
/**
455524
* This workaround handles the fact that Internet Explorer does not support drag types other than
456525
* "Text" and "URL". That means we can not know whether the data comes from one of our elements or

angular-drag-and-drop-lists.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "angular-drag-and-drop-lists",
33
"main": "angular-drag-and-drop-lists.js",
4-
"version": "1.2.0",
4+
"version": "1.3.0",
55
"homepage": "https://github.com/marceljuenemann/angular-drag-and-drop-lists",
66
"authors": [
77
"Marcel Juenemann <mail@marcel-juenemann.de>"

0 commit comments

Comments
 (0)