Skip to content

Commit 448cbcd

Browse files
Some more code
1 parent e8d641c commit 448cbcd

File tree

4 files changed

+74
-66
lines changed

4 files changed

+74
-66
lines changed

angular-drag-and-drop-lists.js

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
// All valid HTML5 drop effects, in the order in which we prefer to use them.
2020
var ALL_EFFECTS = ['move', 'copy', 'link'];
21-
// TODO: Update description of advanced demo.
2221

2322
/**
2423
* Use the dnd-draggable attribute to make your element draggable
@@ -27,17 +26,24 @@
2726
* - dnd-draggable Required attribute. The value has to be an object that represents the data
2827
* of the element. In case of a drag and drop operation the object will be
2928
* serialized and unserialized on the receiving end.
30-
* - dnd-selected Callback that is invoked when the element was clicked but not dragged.
31-
* The original click event will be provided in the local event variable.
3229
* - dnd-effect-allowed Use this attribute to limit the operations that can be performed. Valid
3330
* options are "move", "copy" and "link", as well as "all", "copyMove",
3431
* "copyLink" and "linkMove". The semantics of these operations are up to you
3532
* and have to be implemented using the callbacks described below. If you
3633
* allow multiple options, the user can choose between them by using the
37-
* modifier keys and the cursor will be changed accordingly (expect for IE
38-
* and Edge, where this is not supported).
39-
TODO: list keys (Mac/Windows)
40-
TODO: link design doc
34+
* modifier keys (OS specific). The cursor will be changed accordingly,
35+
* expect for IE and Edge, where this is not supported.
36+
* - dnd-type Use this attribute if you have different kinds of items in your
37+
* application and you want to limit which items can be dropped into which
38+
* lists. Combine with dnd-allowed-types on the dnd-list(s). This attribute
39+
* must be a lower case string. Upper case characters can be used, but will
40+
* be converted to lower case automatically.
41+
* - dnd-disable-if You can use this attribute to dynamically disable the draggability of the
42+
* element. This is useful if you have certain list items that you don't want
43+
* to be draggable, or if you want to disable drag & drop completely without
44+
* having two different code branches (e.g. only allow for admins).
45+
*
46+
* Callbacks:
4147
* - dnd-dragstart Callback that is invoked when the element was dragged. The original
4248
* dragstart event will be provided in the local event variable.
4349
* - dnd-moved Callback that is invoked when the element was moved. Usually you will
@@ -53,15 +59,8 @@
5359
* be provided in the local event variable.
5460
* - dnd-dragend Callback that is invoked when the drag operation ended. Available local
5561
* variables are event and dropEffect.
56-
* - dnd-type Use this attribute if you have different kinds of items in your
57-
* application and you want to limit which items can be dropped into which
58-
* lists. Combine with dnd-allowed-types on the dnd-list(s). This attribute
59-
* must be a lower case string. Upper case characters can be used, but will
60-
* be converted to lower case automatically.
61-
* - dnd-disable-if You can use this attribute to dynamically disable the draggability of the
62-
* element. This is useful if you have certain list items that you don't want
63-
* to be draggable, or if you want to disable drag & drop completely without
64-
* having two different code branches (e.g. only allow for admins).
62+
* - dnd-selected Callback that is invoked when the element was clicked but not dragged.
63+
* The original click event will be provided in the local event variable.
6564
*
6665
* CSS classes:
6766
* - dndDragging This class will be added to the element while the element is being
@@ -96,12 +95,13 @@
9695
if (element.attr('draggable') == 'false') return true;
9796

9897
// Initialize global state.
99-
dndState.dropEffect = "none";
100-
dndState.effectAllowed = attr.dndEffectAllowed || ALL_EFFECTS[0];
10198
dndState.isDragging = true;
10299
dndState.itemType = attr.dndType && scope.$eval(attr.dndType).toLowerCase();
103100

104-
event.dataTransfer.effectAllowed = dndState.effectAllowed; // Special IE handling below.
101+
// Set the allowed drop effects. See below for special IE handling.
102+
dndState.dropEffect = "none";
103+
dndState.effectAllowed = attr.dndEffectAllowed || ALL_EFFECTS[0];
104+
event.dataTransfer.effectAllowed = dndState.effectAllowed;
105105

106106
// Internet Explorer and Microsoft Edge don't support custom mime types, see design doc:
107107
// https://github.com/marceljuenemann/angular-drag-and-drop-lists/wiki/Data-Transfer-Design
@@ -203,12 +203,21 @@
203203
* - dnd-allowed-types Optional array of allowed item types. When used, only items that had a
204204
* matching dnd-type attribute will be dropable. Upper case characters will
205205
* automatically be converted to lower case.
206+
* - dnd-effect-allowed Optional string expression that limits the drop effects that can be
207+
* performed in the list. See dnd-effect-allowed on dnd-draggable for more
208+
* details on allowed options. The default value is all.
206209
* - dnd-disable-if Optional boolean expresssion. When it evaluates to true, no dropping
207210
* into the list is possible. Note that this also disables rearranging
208211
* items inside the list.
209212
* - dnd-horizontal-list Optional boolean expresssion. When it evaluates to true, the positioning
210213
* algorithm will use the left and right halfs of the list items instead of
211214
* the upper and lower halfs.
215+
* - dnd-external-sources Optional boolean expression. When it evaluates to true, the list accepts
216+
* drops from sources outside of the current browser tab. This allows to
217+
* drag and drop accross different browser tabs. The only major browser
218+
* that does not support this is currently Microsoft Edge.
219+
*
220+
* Callbacks:
212221
* - dnd-dragover Optional expression that is invoked when an element is dragged over the
213222
* list. If the expression is set, but does not return true, the element is
214223
* not allowed to be dropped. The following variables will be available:
@@ -217,6 +226,7 @@
217226
* - type: The dnd-type set on the dnd-draggable, or undefined if non was
218227
* set. Will be null for drops from external sources in IE and Edge,
219228
* since we don't know the type in those cases.
229+
* - dropEffect: One of move, copy or link, see dnd-effect-allowed.
220230
* - external: Whether the element was dragged from an external source.
221231
* - dnd-drop Optional expression that is invoked when an element is dropped on the
222232
* list. The same variables as for dnd-dragover will be available, with the
@@ -234,10 +244,6 @@
234244
* dnd-drop will be available. Note that for reorderings inside the same
235245
* list the old element will still be in the list due to the fact that
236246
* dnd-moved was not called yet.
237-
* - dnd-external-sources Optional boolean expression. When it evaluates to true, the list accepts
238-
* drops from sources outside of the current browser tab. This allows to
239-
* drag and drop accross different browser tabs. The only major browser
240-
* that does not support this is currently Microsoft Edge.
241247
*
242248
* CSS classes:
243249
* - dndPlaceholder When an element is dragged over the list, a new placeholder child
@@ -322,23 +328,23 @@
322328
isFirstHalf ? listItemNode : listItemNode.nextSibling);
323329
}
324330
}
325-
// TODO: design doc
326331

327-
// TODO: documentation
332+
// In IE we set a fake effectAllowed in dragstart to get the correct cursor. For drops from
333+
// the same document we therefore ignore the effectAllowed passed in dataTransfer.
328334
var ignoreDataTransfer = mimeType == MSIE_MIME_TYPE && dndState.isDragging;
329335
var dropEffect = getDropEffect(event, ignoreDataTransfer);
330336
if (dropEffect == 'none') return stopDragover();
331-
// TODO: pass to callback
332337

333338
// At this point we invoke the callback, which still can disallow the drop.
334339
// We can't do this earlier because we want to pass the index of the placeholder.
335-
if (attr.dndDragover && !invokeCallback(attr.dndDragover, event, dropEfect, itemType)) {
340+
if (attr.dndDragover && !invokeCallback(attr.dndDragover, event, dropEffect, itemType)) {
336341
return stopDragover();
337342
}
338343

339-
// Set dropEffect to modify the cursor shown by the browser. Only works after preventDefault.
344+
// Set dropEffect to modify the cursor shown by the browser, unless we're in IE, where this
345+
// is not supported. This must be done after preventDefault in Firefox.
340346
event.preventDefault();
341-
if (!ignoreDataTransfer) { // Not for IE, because we might set the wrong dropEffect
347+
if (!ignoreDataTransfer) {
342348
event.dataTransfer.dropEffect = dropEffect;
343349
}
344350

@@ -378,15 +384,13 @@
378384
if (!isDropAllowed(itemType)) return stopDragover();
379385
}
380386

381-
// TODO: documentation
387+
// Special handling for internal IE drops, see dragover handler.
382388
var ignoreDataTransfer = mimeType == MSIE_MIME_TYPE && dndState.isDragging;
383389
var dropEffect = getDropEffect(event, ignoreDataTransfer);
384390
if (dropEffect == 'none') return stopDragover();
385-
if (!ignoreDataTransfer) { // Not for IE, because we might set the wrong dropEffect
391+
if (!ignoreDataTransfer) {
386392
event.dataTransfer.dropEffect = dropEffect;
387393
}
388-
// TODO: pass to callback documentaion
389-
390394

391395
// Invoke the callback, which can transform the transferredObject and even abort the drop.
392396
var index = getPlaceholderIndex();
@@ -463,22 +467,25 @@
463467
return itemType && listSettings.allowedTypes.indexOf(itemType) != -1;
464468
}
465469

466-
// TODO: docs
470+
/**
471+
* Determines which drop effect to use for the given event. In Internet Explorer we have to
472+
* ignore the effectAllowed field on dataTransfer, since we set a fake value in dragstart.
473+
* In those cases we rely on dndState to filter effects. Read the design doc for more details:
474+
* https://github.com/marceljuenemann/angular-drag-and-drop-lists/wiki/Data-Transfer-Design
475+
*/
467476
function getDropEffect(event, ignoreDataTransfer) {
468477
var effects = ALL_EFFECTS;
469-
// don't use effectAllowed for internet explorer, otherwise we'd remove the
470-
// wrong ones. For externals we still do it though, because the above filter did not work.
471478
if (!ignoreDataTransfer) {
472479
effects = filterEffects(effects, event.dataTransfer.effectAllowed);
473480
}
474-
// TODO: add lots of comments
475481
if (dndState.isDragging) {
476482
effects = filterEffects(effects, dndState.effectAllowed);
477483
}
478-
// TODO: add to dnd-list docs
479484
if (attr.dndEffectAllowed) {
480485
effects = filterEffects(effects, attr.dndEffectAllowed);
481486
}
487+
// MacOS automatically filters dataTransfer.effectAllowed depending on the modifier keys,
488+
// therefore the following modifier keys will only affect other operating systems.
482489
if (!effects.length) {
483490
return 'none';
484491
} else if (event.ctrlKey && effects.indexOf('copy') != -1) {
@@ -598,20 +605,23 @@
598605
};
599606
});
600607

601-
// TODO: jsdoc
608+
/**
609+
* Filters an array of drop effects using a HTML5 effectAllowed string.
610+
*/
602611
function filterEffects(effects, effectAllowed) {
603612
if (effectAllowed == 'all') return effects;
604613
return effects.filter(function(effect) {
605614
return effectAllowed.toLowerCase().indexOf(effect) != -1;
606615
});
607616
}
608617

609-
// TODO: effectAllowed
610618
/**
611619
* For some features we need to maintain global state. This is done here, with these fields:
612620
* - dropEffect: Set in dragstart to "none" and to the actual value in the drop handler. We don't
613621
* rely on the dropEffect passed by the browser, since there are various bugs in Chrome and
614622
* Safari, and Internet Explorer defaults to copy if effectAllowed is copyMove.
623+
* - effectAllowed: Set in dragstart based on dnd-effect-allowed. This is needed for IE because
624+
* setting effectAllowed on dataTransfer might result in an undesired cursor.
615625
* - isDragging: True between dragstart and dragend. Falsy for drops from external sources.
616626
* - itemType: The item type of the dragged element set via dnd-type. This is needed because IE
617627
* and Edge don't support custom mime types that we can use to transfer this information.

demo/advanced/advanced-frame.html

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
<h1>Demo: Advanced Features</h1>
22

33
<ul>
4-
<li><strong>Callbacks:</strong> The directives offer various callbacks, which in this example will log the events to the console.
5-
Additionally, the callbacks on the dnd-list can prevent an element from being dropped. In this example <strong>you can't drop elements
6-
after the 10th position</strong>, because we are preventing that in the dnd-dragover callback.</li>
4+
<li><strong>dnd-effect-allowed:</strong> This demo shows how to use dnd-effect-allowed to control which drop effects are allowed.
5+
If the source and target elements have no drop effect that is allowed on both, then a drop is not possible. If there are multiple
6+
possible drop effects, then the user can control the drop effect using modifier keys (Ctrl and Alt).</li>
7+
<li><strong>dnd-external-sources:</strong> Allows to drag and drop elements accross browser windows, which you can test in this
8+
example. The downside to this is that the lists will accept arbitrary text to be dropped. To prevent that, the dnd-drop callback
9+
verifies that the dropped element is of the desired format.</li>
710
<li><strong>dnd-allowed-types in nested lists:</strong> We are using the dnd-allowed-types attribute to ensure that Containers
811
only accept items, but not other containers.</li>
912
<li><strong>dnd-horizontal-list:</strong> This attribute tells the positioning algorithm to drop incoming elements left or right
1013
of the existing elements, instead of above or below.</li>
11-
<li><strong>dnd-external-sources:</strong> Allows to drag and drop elements accross browser windows, which you can test in this
12-
example. The downside to this is that the lists will accept arbitrary text to be dropped. To prevent that, the dnd-drop callback
13-
verifies that the dropped element is of the desired format.</li>
14-
<li><strong>dnd-effect-allowed:</strong> This attribute is set to 'copyMove' in this example, which means that the user can choose
15-
whether to copy or move an element by holding down the Ctrl key. Note that this doesn't work in Chrome very well.</li>
14+
<li><strong>Callbacks:</strong> The directives offer various callbacks, which in this example will log the events to the console.
15+
Additionally, the callbacks on the dnd-list can prevent an element from being dropped. In this example <strong>you can't drop elements
16+
after the 10th position</strong>, because we are preventing that in the dnd-dragover callback.</li>
1617
</ul>
1718

1819
<div class="advancedDemo row">

demo/advanced/advanced.html

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ <h3>Dropzone {{$index + 1}}</h3>
22
<ul dnd-list="containers"
33
dnd-allowed-types="['containerType']"
44
dnd-external-sources="true"
5-
dnd-dragover="dragoverCallback(event, index, external, type)"
6-
dnd-drop="dropCallback(event, index, item, external, type)">
5+
dnd-dragover="dragoverCallback(index, external, type)"
6+
dnd-drop="dropCallback(index, item, external, type)">
77
<li ng-repeat="container in containers"
88
dnd-draggable="container"
99
dnd-type="'containerType'"
@@ -16,20 +16,17 @@ <h3>Container (effects allowed: {{container.effectAllowed}})</h3>
1616
dnd-horizontal-list="true"
1717
dnd-external-sources="true"
1818
dnd-effect-allowed="{{container.effectAllowed}}"
19-
dnd-dragover="dragoverCallback(event, index, external, type)"
20-
dnd-drop="dropCallback(event, index, item, external, type)"
21-
dnd-inserted="logListEvent('inserted at', event, index, external, type)"
19+
dnd-dragover="dragoverCallback(index, external, type)"
20+
dnd-drop="dropCallback(index, item, external, type)"
21+
dnd-inserted="logListEvent('inserted at', index, external, type)"
2222
class="itemlist">
2323
<li ng-repeat="item in container.items"
2424
dnd-draggable="item"
2525
dnd-type="'itemType'"
2626
dnd-effect-allowed="{{item.effectAllowed}}"
27-
dnd-dragstart="logEvent('Started to drag an item', event)"
28-
dnd-moved="container.items.splice($index, 1); logEvent('Item moved', event)"
29-
dnd-copied="logEvent('Item copied', event)"
30-
dnd-linked="logEvent('Item linked', event)"
31-
dnd-canceled="logEvent('Drag operation canceled', event)"
32-
dnd-dragend="logEvent('Drag operation ended', event)">
27+
dnd-dragstart="logEvent('Started to drag an item')"
28+
dnd-moved="container.items.splice($index, 1)"
29+
dnd-dragend="logEvent('Drag operation ended. Drop effect: ' + dropEffect)">
3330
{{item.label}}
3431
</li>
3532
</ul>

demo/advanced/advanced.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
angular.module("demo").controller("AdvancedDemoController", function($scope) {
22

3-
$scope.dragoverCallback = function(event, index, external, type) {
4-
$scope.logListEvent('dragged over', event, index, external, type);
3+
$scope.dragoverCallback = function(index, external, type) {
4+
$scope.logListEvent('dragged over', index, external, type);
55
return index < 10; // Disallow dropping in the third row.
66
};
77

8-
$scope.dropCallback = function(event, index, item, external, type) {
9-
$scope.logListEvent('dropped at', event, index, external, type);
8+
$scope.dropCallback = function(index, item, external, type) {
9+
$scope.logListEvent('dropped at', index, external, type);
1010
// Return false here to cancel drop. Return true if you insert the item yourself.
1111
return item;
1212
};
1313

14-
$scope.logEvent = function(message, event) {
14+
$scope.logEvent = function(message) {
1515
console.log(message);
1616
};
1717

18-
$scope.logListEvent = function(action, event, index, external, type) {
18+
$scope.logListEvent = function(action, index, external, type) {
1919
var message = external ? 'External ' : '';
2020
message += type + ' element was ' + action + ' position ' + index;
21-
$scope.logEvent(message, event);
21+
$scope.logEvent(message);
2222
};
2323

2424
// Initialize model

0 commit comments

Comments
 (0)