@@ -13,9 +13,12 @@ import {
13
13
} from './ManifestConstants' ;
14
14
import {
15
15
CSS_STYLE_CLASSES ,
16
+ DRAGGED_FROM_ZONE_ATTRIBUTE ,
17
+ DRAG_INVALID ,
16
18
ORIGINAL_POSITION_ATTRIBUTE ,
17
19
ORIGINAL_ZONE_ATTRIBUTE ,
18
20
RECORD_ID_ATTRIBUTE ,
21
+ RENDER_VERSION_ATTRIBUTE ,
19
22
ROTATION_CLASSES ,
20
23
} from './Styles' ;
21
24
@@ -40,7 +43,7 @@ const defaultSortableOptions: Sortable.Options = {
40
43
scrollSpeed : 10 ,
41
44
forceFallback : true ,
42
45
fallbackOnBody : true ,
43
- removeCloneOnHide : false ,
46
+ removeCloneOnHide : true ,
44
47
ghostClass : CSS_STYLE_CLASSES . Ghost ,
45
48
chosenClass : CSS_STYLE_CLASSES . Chosen ,
46
49
dataIdAttr : RECORD_ID_ATTRIBUTE ,
@@ -128,22 +131,13 @@ export class PowerDragDrop implements ComponentFramework.StandardControl<IInputs
128
131
129
132
// Event if this is not a master zone, the reset event triggers a re-render to enable items
130
133
// to be re-created after drop
134
+ const renderTriggerProperties = this . hasPropertyChanged ( RENDER_TRIGGER_PROPERTIES ) ;
131
135
const updateItems =
132
- ! this . itemRenderer . rendered ||
133
- resetDatasetTriggered ||
134
- datasetChanged ||
135
- this . hasPropertyChanged ( RENDER_TRIGGER_PROPERTIES ) ;
136
+ ! this . itemRenderer . rendered || resetDatasetTriggered || datasetChanged || renderTriggerProperties ;
136
137
137
138
if ( ! parameters . items . loading && updateItems ) {
138
- const renderResult = this . itemRenderer . renderItems ( context ) ;
139
- if ( renderResult . itemsRendered && renderResult . sortOrder ) {
140
- this . currentItems = renderResult . itemsRendered ;
141
- this . originalOrder = renderResult . sortOrder ;
142
-
143
- if ( isMasterZone ) {
144
- this . notifyOutputChanged ( ) ;
145
- }
146
- }
139
+ this . trace ( 'renderItems' , { resetDatasetTriggered, datasetChanged, renderTriggerProperties } ) ;
140
+ this . renderItems ( ) ;
147
141
}
148
142
149
143
if ( clearChangesTriggered ) {
@@ -179,6 +173,18 @@ export class PowerDragDrop implements ComponentFramework.StandardControl<IInputs
179
173
}
180
174
}
181
175
176
+ private renderItems ( ) : void {
177
+ const renderResult = this . itemRenderer . renderItems ( this . context ) ;
178
+ if ( renderResult . itemsRendered && renderResult . sortOrder ) {
179
+ this . currentItems = renderResult . itemsRendered ;
180
+ this . originalOrder = renderResult . sortOrder ;
181
+
182
+ if ( this . isMasterZone ( ) ) {
183
+ this . notifyOutputChanged ( ) ;
184
+ }
185
+ }
186
+ }
187
+
182
188
private getActionFromClass ( target : HTMLElement ) {
183
189
return target . className . split ( ' ' ) . find ( ( c : string ) => c . startsWith ( CSS_STYLE_CLASSES . ActionClassPrefix ) ) ;
184
190
}
@@ -329,6 +335,7 @@ export class PowerDragDrop implements ComponentFramework.StandardControl<IInputs
329
335
onUnchoose : this . onUnChoose ,
330
336
onEnd : this . onEnd ,
331
337
onMove : this . onMove ,
338
+ onClone : this . onClone ,
332
339
filter : this . actionFilter ,
333
340
} ) ;
334
341
const zoneRegistration = {
@@ -436,20 +443,54 @@ export class PowerDragDrop implements ComponentFramework.StandardControl<IInputs
436
443
// Check if we have reached the maximum items for the drop zone
437
444
if ( event . to ) {
438
445
const targetZoneId = this . getZoneId ( event . to as HTMLElement ) ;
439
- const zone = this . zonesRegistered [ targetZoneId ] ;
440
- if ( zone && zone . maximumItems && zone . maximumItems > 0 ) {
441
- const currentItemCount = zone . sortable . toArray ( ) . length ;
442
- return currentItemCount < zone . maximumItems ;
446
+ const sourceZoneId = this . getZoneId ( event . from as HTMLElement ) ;
447
+ const targetZone = this . zonesRegistered [ targetZoneId ] ;
448
+ const sourceZone = this . zonesRegistered [ sourceZoneId ] ;
449
+ // Check if the source zone has re-rendered - if so this item is invalid
450
+ const sourceZoneRenderVersion = sourceZone . sortable . el . getAttribute ( RENDER_VERSION_ATTRIBUTE ) ;
451
+ const draggedRenderVersion = event . dragged . getAttribute ( RENDER_VERSION_ATTRIBUTE ) ;
452
+ const originalZone = event . dragged . getAttribute ( ORIGINAL_ZONE_ATTRIBUTE ) ;
453
+ const invalidDrag = Sortable . utils . is ( event . dragged , '.' + DRAG_INVALID ) ;
454
+ if ( invalidDrag ) {
455
+ this . trace ( 'onMove - Invalid drag item' ) ;
456
+ return false ;
457
+ }
458
+ if ( sourceZoneRenderVersion !== draggedRenderVersion && originalZone === sourceZoneId ) {
459
+ this . trace ( 'onMove - Render version mismatch' ) ;
460
+ return false ;
461
+ }
462
+ if ( targetZone && targetZone . maximumItems && targetZone . maximumItems > 0 ) {
463
+ const currentItemCount = targetZone . sortable . toArray ( ) . length ;
464
+ return currentItemCount < targetZone . maximumItems ;
443
465
}
444
466
}
445
467
} ;
446
468
469
+ onClone = ( event : SortableEvent ) : void => {
470
+ const origEl = event . item ;
471
+ const invalidDragItem = ! origEl . parentElement ;
472
+ Sortable . utils . toggleClass ( origEl , DRAG_INVALID , invalidDragItem ) ;
473
+ if ( invalidDragItem ) {
474
+ // The item being dragged has no parent container since the zone has been removed
475
+ // or the items have been re-rendered. This makes the drag item invalid
476
+ origEl . style . display = 'none' ;
477
+ this . trace ( 'onClone -Invalid drag' ) ;
478
+ }
479
+ } ;
480
+
447
481
onEnd = ( event : SortableEvent ) : void => {
448
482
try {
449
483
const draggedElement = event . item ; // dragged HTMLElement
450
484
const targetZone = event . to ; // target list
451
485
const sourceZone = event . from ; // previous list
452
486
487
+ // Check if there is an invalid item being dragged
488
+ // and prevent it being dropped anywhere
489
+ if ( Sortable . utils . is ( draggedElement , '.' + DRAG_INVALID ) ) {
490
+ this . trace ( 'onEnd - Invalid drop' ) ;
491
+ return ;
492
+ }
493
+
453
494
const newPosition = event . newDraggableIndex ; // element's new index within new parent, only counting draggable elements
454
495
const itemId = draggedElement . getAttribute ( RECORD_ID_ATTRIBUTE ) as string ;
455
496
const targetZoneId = this . getZoneId ( targetZone ) ;
@@ -476,12 +517,16 @@ export class PowerDragDrop implements ComponentFramework.StandardControl<IInputs
476
517
}
477
518
} ;
478
519
479
- onUnChoose = ( ) : void => {
520
+ onUnChoose = ( event : SortableEvent ) : void => {
480
521
this . currentItemZone = null ;
522
+ this . trace ( 'onUnChoose' , this . context . parameters . DropZoneID . raw , event . item . innerText ) ;
523
+ event . item . removeAttribute ( DRAGGED_FROM_ZONE_ATTRIBUTE ) ;
481
524
} ;
482
525
483
526
onChoose = ( event : SortableEvent ) : void => {
484
527
this . currentItemZone = this . getZoneId ( event . from ) ;
528
+ this . trace ( 'onChoose' , this . context . parameters . DropZoneID . raw , event . item . innerText ) ;
529
+ event . item . setAttribute ( DRAGGED_FROM_ZONE_ATTRIBUTE , this . currentItemZone ) ;
485
530
} ;
486
531
487
532
actionFilter = ( event : Event | TouchEvent ) : boolean => {
0 commit comments