Skip to content
This repository has been archived by the owner on Mar 10, 2024. It is now read-only.

Commit

Permalink
Merge pull request #99 from marceljuenemann/dev
Browse files Browse the repository at this point in the history
Merge 1.3.0 into master
  • Loading branch information
marceljuenemann committed Aug 20, 2015
2 parents e2aa9d5 + f773ea0 commit 7b4307d
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 53 deletions.
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
# 1.3.0 (2015-08-20)

## Features

- **New callbacks**: `dnd-dragend`, `dnd-canceled` and `dnd-inserted`.
- **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.
- **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.

## Bug Fixes

- **Fix user selection inside dnd-draggable**: The `selectstart` event is no longer cancelled.
- **Fix click handler compatibility**: Propagation of click events is now only stopped if the `dnd-selected` attribute is present.
- **Fix IE9 glitch**: Double clicks in IE9 previously would trigger the `dnd-moved` callback, and therefore remove items accidentially. (issue #21)

## Tested browsers

- Chrome 43 (Win7)
- Chrome 44 (Ubuntu)
- Chrome 44 (Mac)
- Firefox 40 (Win7)
- Firefox 39 (Ubuntu)
- Safari 8.0.8 (Mac)
- Internet Explorer 11 (IE9 & 10 in compatibility mode)

# 1.2.0 (2014-11-30)

## Bug Fixes
Expand Down
62 changes: 34 additions & 28 deletions README.md

Large diffs are not rendered by default.

93 changes: 81 additions & 12 deletions angular-drag-and-drop-lists.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* angular-drag-and-drop-lists v1.2.0
* angular-drag-and-drop-lists v1.3.0
*
* Copyright (c) 2014 Marcel Juenemann [email protected]
* Copyright (c) 2014-2015 Google Inc.
Expand Down Expand Up @@ -33,11 +33,16 @@ angular.module('dndLists', [])
* remove your element from the original list in this callback, since the
* directive is not doing that for you automatically. The original dragend
* event will be provided in the local event variable.
* - dnd-canceled Callback that is invoked if the element was dragged, but the operation was
* canceled and the element was not dropped. The original dragend event will
* be provided in the local event variable.
* - dnd-copied Same as dnd-moved, just that it is called when the element was copied
* instead of moved. The original dragend event will be provided in the local
* event variable.
* - dnd-dragstart Callback that is invoked when the element was dragged. The original
* dragstart event will be provided in the local event variable.
* - dnd-dragend Callback that is invoked when the drag operation ended. Available local
* variables are event and dropEffect.
* - dnd-type Use this attribute if you have different kinds of items in your
* application and you want to limit which items can be dropped into which
* lists. Combine with dnd-allowed-types on the dnd-list(s). This attribute
Expand Down Expand Up @@ -123,16 +128,19 @@ angular.module('dndLists', [])
case "move":
$parse(attr.dndMoved)(scope, {event: event});
break;

case "copy":
$parse(attr.dndCopied)(scope, {event: event});
break;
case "none":
$parse(attr.dndCanceled)(scope, {event: event});
break;
}
$parse(attr.dndDragend)(scope, {event: event, dropEffect: dropEffect});
});

// Clean up
element.removeClass("dndDragging");
element.removeClass("dndDraggingSource");
$timeout(function() { element.removeClass("dndDraggingSource"); }, 0);
dndDragTypeWorkaround.isDragging = false;
event.stopPropagation();
});
Expand All @@ -142,12 +150,14 @@ angular.module('dndLists', [])
* specified with the dnd-selected attribute.
*/
element.on('click', function(event) {
event = event.originalEvent || event;
if (!attr.dndSelected) return;

event = event.originalEvent || event;
scope.$apply(function() {
$parse(attr.dndSelected)(scope, {event: event});
});

// Prevent triggering dndSelected in parant elements.
event.stopPropagation();
});

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

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

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

Expand Down Expand Up @@ -312,8 +328,9 @@ angular.module('dndLists', [])
}

// Invoke the callback, which can transform the transferredObject and even abort the drop.
var index = getPlaceholderIndex();
if (attr.dndDrop) {
transferredObject = invokeCallback(attr.dndDrop, event, transferredObject);
transferredObject = invokeCallback(attr.dndDrop, event, index, transferredObject);
if (!transferredObject) {
return stopDragover();
}
Expand All @@ -322,8 +339,9 @@ angular.module('dndLists', [])
// Retrieve the JSON array and insert the transferred object into it.
var targetArray = scope.$eval(attr.dndList);
scope.$apply(function() {
targetArray.splice(getPlaceholderIndex(), 0, transferredObject);
targetArray.splice(index, 0, transferredObject);
});
invokeCallback(attr.dndInserted, event, index, transferredObject);

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

/**
* Tries to find a child element that has the dndPlaceholder class set. If none was found, a
* new li element is created.
*/
function getPlaceholderElement() {
var placeholder;
angular.forEach(element.children(), function(childNode) {
var child = angular.element(childNode);
if (child.hasClass('dndPlaceholder')) {
placeholder = child;
}
});
return placeholder || angular.element("<li class='dndPlaceholder'></li>");
}

/**
* We use the position of the placeholder node to determine at which position of the array the
* object needs to be inserted
Expand Down Expand Up @@ -426,10 +459,10 @@ angular.module('dndLists', [])
/**
* Invokes a callback with some interesting parameters and returns the callbacks return value.
*/
function invokeCallback(expression, event, item) {
function invokeCallback(expression, event, index, item) {
return $parse(expression)(scope, {
event: event,
index: getPlaceholderIndex(),
index: index,
item: item || undefined,
external: !dndDragTypeWorkaround.isDragging,
type: dndDragTypeWorkaround.isDragging ? dndDragTypeWorkaround.dragType : undefined
Expand All @@ -451,6 +484,42 @@ angular.module('dndLists', [])
};
}])

/**
* Use the dnd-nodrag attribute inside of dnd-draggable elements to prevent them from starting
* drag operations. This is especially useful if you want to use input elements inside of
* dnd-draggable elements or create specific handle elements.
*/
.directive('dndNodrag', function() {
return function(scope, element, attr) {
// Set as draggable so that we can cancel the events explicitly
element.attr("draggable", "true");

/**
* Since the element is draggable, the browser's default operation is to drag it on dragstart.
* We will prevent that and also stop the event from bubbling up.
*/
element.on('dragstart', function(event) {
event = event.originalEvent || event;

// If a child element already reacted to dragstart and set a dataTransfer object, we will
// allow that. For example, this is the case for user selections inside of input elements.
if (!(event.dataTransfer.types && event.dataTransfer.types.length)) {
event.preventDefault();
}
event.stopPropagation();
});

/**
* Stop propagation of dragend events, otherwise dnd-moved might be triggered and the element
* would be removed.
*/
element.on('dragend', function(event) {
event = event.originalEvent || event;
event.stopPropagation();
});
};
})

/**
* This workaround handles the fact that Internet Explorer does not support drag types other than
* "Text" and "URL". That means we can not know whether the data comes from one of our elements or
Expand Down
2 changes: 1 addition & 1 deletion angular-drag-and-drop-lists.min.js

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

2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "angular-drag-and-drop-lists",
"main": "angular-drag-and-drop-lists.js",
"version": "1.2.0",
"version": "1.3.0",
"homepage": "https://github.com/marceljuenemann/angular-drag-and-drop-lists",
"authors": [
"Marcel Juenemann <[email protected]>"
Expand Down
Loading

0 comments on commit 7b4307d

Please sign in to comment.