1
1
/**
2
- * angular-drag-and-drop-lists v1.2 .0
2
+ * angular-drag-and-drop-lists v1.3 .0
3
3
*
4
4
* Copyright (c) 2014 Marcel Juenemann mail@marcel-juenemann.de
5
5
* Copyright (c) 2014-2015 Google Inc.
@@ -33,11 +33,16 @@ angular.module('dndLists', [])
33
33
* remove your element from the original list in this callback, since the
34
34
* directive is not doing that for you automatically. The original dragend
35
35
* 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.
36
39
* - dnd-copied Same as dnd-moved, just that it is called when the element was copied
37
40
* instead of moved. The original dragend event will be provided in the local
38
41
* event variable.
39
42
* - dnd-dragstart Callback that is invoked when the element was dragged. The original
40
43
* 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.
41
46
* - dnd-type Use this attribute if you have different kinds of items in your
42
47
* application and you want to limit which items can be dropped into which
43
48
* lists. Combine with dnd-allowed-types on the dnd-list(s). This attribute
@@ -123,16 +128,19 @@ angular.module('dndLists', [])
123
128
case "move" :
124
129
$parse ( attr . dndMoved ) ( scope , { event : event } ) ;
125
130
break ;
126
-
127
131
case "copy" :
128
132
$parse ( attr . dndCopied ) ( scope , { event : event } ) ;
129
133
break ;
134
+ case "none" :
135
+ $parse ( attr . dndCanceled ) ( scope , { event : event } ) ;
136
+ break ;
130
137
}
138
+ $parse ( attr . dndDragend ) ( scope , { event : event , dropEffect : dropEffect } ) ;
131
139
} ) ;
132
140
133
141
// Clean up
134
142
element . removeClass ( "dndDragging" ) ;
135
- element . removeClass ( "dndDraggingSource" ) ;
143
+ $timeout ( function ( ) { element . removeClass ( "dndDraggingSource" ) ; } , 0 ) ;
136
144
dndDragTypeWorkaround . isDragging = false ;
137
145
event . stopPropagation ( ) ;
138
146
} ) ;
@@ -142,12 +150,14 @@ angular.module('dndLists', [])
142
150
* specified with the dnd-selected attribute.
143
151
*/
144
152
element . on ( 'click' , function ( event ) {
145
- event = event . originalEvent || event ;
153
+ if ( ! attr . dndSelected ) return ;
146
154
155
+ event = event . originalEvent || event ;
147
156
scope . $apply ( function ( ) {
148
157
$parse ( attr . dndSelected ) ( scope , { event : event } ) ;
149
158
} ) ;
150
159
160
+ // Prevent triggering dndSelected in parant elements.
151
161
event . stopPropagation ( ) ;
152
162
} ) ;
153
163
@@ -156,7 +166,6 @@ angular.module('dndLists', [])
156
166
*/
157
167
element . on ( 'selectstart' , function ( ) {
158
168
if ( this . dragDrop ) this . dragDrop ( ) ;
159
- return false ;
160
169
} ) ;
161
170
} ;
162
171
} ] )
@@ -194,6 +203,11 @@ angular.module('dndLists', [])
194
203
* - index: The position in the list at which the element would be dropped.
195
204
* - item: The transferred object.
196
205
* - 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.
197
211
* - dnd-external-sources Optional boolean expression. When it evaluates to true, the list accepts
198
212
* drops from sources outside of the current browser tab. This allows to
199
213
* drag and drop accross different browser tabs. Note that this will allow
@@ -205,17 +219,19 @@ angular.module('dndLists', [])
205
219
* CSS classes:
206
220
* - dndPlaceholder When an element is dragged over the list, a new placeholder child
207
221
* 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.
209
224
* - dndDragover Will be added to the list while an element is dragged over the list.
210
225
*/
211
226
. directive ( 'dndList' , [ '$parse' , '$timeout' , 'dndDropEffectWorkaround' , 'dndDragTypeWorkaround' ,
212
227
function ( $parse , $timeout , dndDropEffectWorkaround , dndDragTypeWorkaround ) {
213
228
return function ( scope , element , attr ) {
214
229
// While an element is dragged over the list, this placeholder element is inserted
215
230
// at the location where the element would be inserted after dropping
216
- var placeholder = angular . element ( "<li class='dndPlaceholder'></li>" ) ;
231
+ var placeholder = getPlaceholderElement ( ) ;
217
232
var placeholderNode = placeholder [ 0 ] ;
218
233
var listNode = element [ 0 ] ;
234
+ placeholder . remove ( ) ;
219
235
220
236
var horizontal = attr . dndHorizontalList && scope . $eval ( attr . dndHorizontalList ) ;
221
237
var externalSources = attr . dndExternalSources && scope . $eval ( attr . dndExternalSources ) ;
@@ -277,7 +293,7 @@ angular.module('dndLists', [])
277
293
278
294
// At this point we invoke the callback, which still can disallow the drop.
279
295
// 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 ( ) ) ) {
281
297
return stopDragover ( ) ;
282
298
}
283
299
@@ -312,8 +328,9 @@ angular.module('dndLists', [])
312
328
}
313
329
314
330
// Invoke the callback, which can transform the transferredObject and even abort the drop.
331
+ var index = getPlaceholderIndex ( ) ;
315
332
if ( attr . dndDrop ) {
316
- transferredObject = invokeCallback ( attr . dndDrop , event , transferredObject ) ;
333
+ transferredObject = invokeCallback ( attr . dndDrop , event , index , transferredObject ) ;
317
334
if ( ! transferredObject ) {
318
335
return stopDragover ( ) ;
319
336
}
@@ -322,8 +339,9 @@ angular.module('dndLists', [])
322
339
// Retrieve the JSON array and insert the transferred object into it.
323
340
var targetArray = scope . $eval ( attr . dndList ) ;
324
341
scope . $apply ( function ( ) {
325
- targetArray . splice ( getPlaceholderIndex ( ) , 0 , transferredObject ) ;
342
+ targetArray . splice ( index , 0 , transferredObject ) ;
326
343
} ) ;
344
+ invokeCallback ( attr . dndInserted , event , index , transferredObject ) ;
327
345
328
346
// In Chrome on Windows the dropEffect will always be none...
329
347
// We have to determine the actual effect manually from the allowed effects
@@ -380,6 +398,21 @@ angular.module('dndLists', [])
380
398
return mousePointer < targetPosition + targetSize / 2 ;
381
399
}
382
400
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
+
383
416
/**
384
417
* We use the position of the placeholder node to determine at which position of the array the
385
418
* object needs to be inserted
@@ -426,10 +459,10 @@ angular.module('dndLists', [])
426
459
/**
427
460
* Invokes a callback with some interesting parameters and returns the callbacks return value.
428
461
*/
429
- function invokeCallback ( expression , event , item ) {
462
+ function invokeCallback ( expression , event , index , item ) {
430
463
return $parse ( expression ) ( scope , {
431
464
event : event ,
432
- index : getPlaceholderIndex ( ) ,
465
+ index : index ,
433
466
item : item || undefined ,
434
467
external : ! dndDragTypeWorkaround . isDragging ,
435
468
type : dndDragTypeWorkaround . isDragging ? dndDragTypeWorkaround . dragType : undefined
@@ -451,6 +484,42 @@ angular.module('dndLists', [])
451
484
} ;
452
485
} ] )
453
486
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
+
454
523
/**
455
524
* This workaround handles the fact that Internet Explorer does not support drag types other than
456
525
* "Text" and "URL". That means we can not know whether the data comes from one of our elements or
0 commit comments