Skip to content

Commit b98cbdc

Browse files
committed
fix(tm): swap positions only once on drag over
1 parent 3c8cc97 commit b98cbdc

File tree

5 files changed

+87
-7
lines changed

5 files changed

+87
-7
lines changed

src/components/common/context.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type IgcTileComponent from '../tile-manager/tile.js';
66
export type TileManagerContext = {
77
instance: IgcTileManagerComponent;
88
draggedItem: IgcTileComponent | null;
9+
lastSwapTile: IgcTileComponent | null;
910
};
1011

1112
export type TileContext = {

src/components/common/utils.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,11 +347,12 @@ export function simulateDragStart(node: Element) {
347347
);
348348
}
349349

350-
export function simulateDragOver(node: Element) {
350+
export function simulateDragOver(node: Element, options?: PointerEventInit) {
351351
node.dispatchEvent(
352352
new DragEvent('dragover', {
353353
bubbles: true,
354354
composed: true,
355+
...options,
355356
})
356357
);
357358
}

src/components/tile-manager/tile-dnd.spec.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
2-
import { spy } from 'sinon';
2+
import { spy, stub } from 'sinon';
33

44
import { range } from 'lit/directives/range.js';
55
import { defineComponents } from '../common/definitions/defineComponents.js';
@@ -9,7 +9,10 @@ import {
99
simulateDragOver,
1010
simulateDragStart,
1111
simulateDrop,
12+
simulatePointerDown,
13+
simulatePointerMove,
1214
} from '../common/utils.spec.js';
15+
import * as PositionUtils from './position.js';
1316
import IgcTileManagerComponent from './tile-manager.js';
1417
import type IgcTileComponent from './tile.js';
1518

@@ -18,6 +21,8 @@ describe('Tile drag and drop', () => {
1821
defineComponents(IgcTileManagerComponent);
1922
});
2023

24+
const getBoundingRect = (el: Element) => el.getBoundingClientRect();
25+
2126
let tileManager: IgcTileManagerComponent;
2227

2328
function getTiles() {
@@ -180,5 +185,34 @@ describe('Tile drag and drop', () => {
180185
expect(tileManager.tiles[0].id).to.equal('tile0');
181186
expect(tileManager.tiles[4].id).to.equal('tile4');
182187
});
188+
189+
it('should swap positions only once while dragging smaller tile over bigger tile in slide mode', async () => {
190+
tileManager.columnCount = 5;
191+
const draggedTile = first(tileManager.tiles);
192+
const dropTarget = tileManager.tiles[1];
193+
194+
draggedTile.rowSpan = 1;
195+
draggedTile.colSpan = 1;
196+
197+
dropTarget.rowSpan = 3;
198+
dropTarget.colSpan = 3;
199+
await elementUpdated(tileManager);
200+
201+
const dropTargetRect = dropTarget.getBoundingClientRect();
202+
203+
simulateDragStart(draggedTile);
204+
simulateDragOver(dropTarget);
205+
await elementUpdated(tileManager);
206+
207+
// Simulate second dragover event (inside dropTarget bounds)
208+
simulateDragOver(dropTarget, {
209+
clientX: dropTargetRect.left + dropTargetRect.width / 2,
210+
clientY: dropTargetRect.top + dropTargetRect.height / 3,
211+
});
212+
await elementUpdated(tileManager);
213+
214+
expect(draggedTile.position).to.equal(1);
215+
expect(dropTarget.position).to.equal(0);
216+
});
183217
});
184218
});

src/components/tile-manager/tile-manager.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
5959
private _minColWidth?: string;
6060
private _minRowHeight?: string;
6161
private _draggedItem: IgcTileComponent | null = null;
62+
private _lastSwapTile: IgcTileComponent | null = null;
6263

6364
private _serializer = createSerializer(this);
6465
private _tilesState = createTilesState(this);
@@ -68,12 +69,17 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
6869
initialValue: {
6970
instance: this,
7071
draggedItem: this._draggedItem,
72+
lastSwapTile: this._lastSwapTile,
7173
},
7274
});
7375

7476
private _setManagerContext() {
7577
this._context.setValue(
76-
{ instance: this, draggedItem: this._draggedItem },
78+
{
79+
instance: this,
80+
draggedItem: this._draggedItem,
81+
lastSwapTile: this._lastSwapTile,
82+
},
7783
true
7884
);
7985
}

src/components/tile-manager/tile.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ export default class IgcTileComponent extends EventEmitterMixin<
351351
this._hasDragOver = true;
352352
}
353353

354-
private handleDragOver() {
354+
private handleDragOver(event: DragEvent) {
355355
if (!this._draggedItem) {
356356
return;
357357
}
@@ -363,16 +363,29 @@ export default class IgcTileComponent extends EventEmitterMixin<
363363
visibility: 'visible',
364364
});
365365
}
366+
if (this._managerContext) {
367+
this._managerContext.lastSwapTile = null;
368+
}
366369
} else if (this._isSlideMode) {
367-
swapTiles(this, this._draggedItem!);
370+
if (
371+
this._managerContext &&
372+
(!this._managerContext.lastSwapTile ||
373+
this.hasPointerLeftLastSwapTile(
374+
event,
375+
this._managerContext.lastSwapTile
376+
))
377+
) {
378+
this._managerContext.lastSwapTile = this;
379+
swapTiles(this, this._draggedItem!);
380+
}
368381
}
369382
}
370383

371384
private handleDragLeave() {
372385
this._dragCounter--;
373386

374387
// The drag leave is fired on entering a child element
375-
// so we need to check if the dragged item is actually leaving the tile
388+
// so we check if the dragged item is actually leaving the tile
376389
if (this._dragCounter === 0) {
377390
this._hasDragOver = false;
378391
}
@@ -395,6 +408,31 @@ export default class IgcTileComponent extends EventEmitterMixin<
395408
this._tileContent.style.visibility = 'visible';
396409
}
397410

411+
private hasPointerLeftLastSwapTile(
412+
event: DragEvent,
413+
lastSwapTile: IgcTileComponent | null
414+
) {
415+
if (!lastSwapTile) return false;
416+
417+
// Check if the pointer is outside the boundaries of the last swapped tile
418+
419+
const rect = lastSwapTile.getBoundingClientRect();
420+
const pointerX = event.clientX;
421+
const pointerY = event.clientY;
422+
423+
const outsideBoundaries =
424+
pointerX < rect.left ||
425+
pointerX > rect.right ||
426+
pointerY < rect.top ||
427+
pointerY > rect.bottom;
428+
429+
if (outsideBoundaries && this._managerContext) {
430+
this._managerContext.lastSwapTile = null;
431+
}
432+
433+
return outsideBoundaries;
434+
}
435+
398436
private cacheStyles() {
399437
//use util
400438
const computedStyle = getComputedStyle(this);
@@ -530,7 +568,7 @@ export default class IgcTileComponent extends EventEmitterMixin<
530568
protected renderContent() {
531569
const parts = partNameMap({
532570
base: true,
533-
'drag-over': this._hasDragOver,
571+
'drag-over': this._hasDragOver && !this._isSlideMode,
534572
fullscreen: this.fullscreen,
535573
draggable: !this.disableDrag,
536574
dragging: this._isDragging,

0 commit comments

Comments
 (0)