Skip to content

Commit

Permalink
fix(tm): swap positions only once on drag over
Browse files Browse the repository at this point in the history
  • Loading branch information
mtsvyatkova committed Jan 6, 2025
1 parent 3c8cc97 commit b98cbdc
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/components/common/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type IgcTileComponent from '../tile-manager/tile.js';
export type TileManagerContext = {
instance: IgcTileManagerComponent;
draggedItem: IgcTileComponent | null;
lastSwapTile: IgcTileComponent | null;
};

export type TileContext = {
Expand Down
3 changes: 2 additions & 1 deletion src/components/common/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,11 +347,12 @@ export function simulateDragStart(node: Element) {
);
}

export function simulateDragOver(node: Element) {
export function simulateDragOver(node: Element, options?: PointerEventInit) {
node.dispatchEvent(
new DragEvent('dragover', {
bubbles: true,
composed: true,
...options,
})
);
}
Expand Down
36 changes: 35 additions & 1 deletion src/components/tile-manager/tile-dnd.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
import { spy } from 'sinon';
import { spy, stub } from 'sinon';

import { range } from 'lit/directives/range.js';
import { defineComponents } from '../common/definitions/defineComponents.js';
Expand All @@ -9,7 +9,10 @@ import {
simulateDragOver,
simulateDragStart,
simulateDrop,
simulatePointerDown,
simulatePointerMove,
} from '../common/utils.spec.js';
import * as PositionUtils from './position.js';
import IgcTileManagerComponent from './tile-manager.js';
import type IgcTileComponent from './tile.js';

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

const getBoundingRect = (el: Element) => el.getBoundingClientRect();

let tileManager: IgcTileManagerComponent;

function getTiles() {
Expand Down Expand Up @@ -180,5 +185,34 @@ describe('Tile drag and drop', () => {
expect(tileManager.tiles[0].id).to.equal('tile0');
expect(tileManager.tiles[4].id).to.equal('tile4');
});

it('should swap positions only once while dragging smaller tile over bigger tile in slide mode', async () => {
tileManager.columnCount = 5;
const draggedTile = first(tileManager.tiles);
const dropTarget = tileManager.tiles[1];

draggedTile.rowSpan = 1;
draggedTile.colSpan = 1;

dropTarget.rowSpan = 3;
dropTarget.colSpan = 3;
await elementUpdated(tileManager);

const dropTargetRect = dropTarget.getBoundingClientRect();

simulateDragStart(draggedTile);
simulateDragOver(dropTarget);
await elementUpdated(tileManager);

// Simulate second dragover event (inside dropTarget bounds)
simulateDragOver(dropTarget, {
clientX: dropTargetRect.left + dropTargetRect.width / 2,
clientY: dropTargetRect.top + dropTargetRect.height / 3,
});
await elementUpdated(tileManager);

expect(draggedTile.position).to.equal(1);
expect(dropTarget.position).to.equal(0);
});
});
});
8 changes: 7 additions & 1 deletion src/components/tile-manager/tile-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
private _minColWidth?: string;
private _minRowHeight?: string;
private _draggedItem: IgcTileComponent | null = null;
private _lastSwapTile: IgcTileComponent | null = null;

private _serializer = createSerializer(this);
private _tilesState = createTilesState(this);
Expand All @@ -68,12 +69,17 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
initialValue: {
instance: this,
draggedItem: this._draggedItem,
lastSwapTile: this._lastSwapTile,
},
});

private _setManagerContext() {
this._context.setValue(
{ instance: this, draggedItem: this._draggedItem },
{
instance: this,
draggedItem: this._draggedItem,
lastSwapTile: this._lastSwapTile,
},
true
);
}
Expand Down
46 changes: 42 additions & 4 deletions src/components/tile-manager/tile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ export default class IgcTileComponent extends EventEmitterMixin<
this._hasDragOver = true;
}

private handleDragOver() {
private handleDragOver(event: DragEvent) {
if (!this._draggedItem) {
return;
}
Expand All @@ -363,16 +363,29 @@ export default class IgcTileComponent extends EventEmitterMixin<
visibility: 'visible',
});
}
if (this._managerContext) {
this._managerContext.lastSwapTile = null;
}
} else if (this._isSlideMode) {
swapTiles(this, this._draggedItem!);
if (
this._managerContext &&
(!this._managerContext.lastSwapTile ||
this.hasPointerLeftLastSwapTile(
event,
this._managerContext.lastSwapTile
))
) {
this._managerContext.lastSwapTile = this;
swapTiles(this, this._draggedItem!);
}
}
}

private handleDragLeave() {
this._dragCounter--;

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

private hasPointerLeftLastSwapTile(
event: DragEvent,
lastSwapTile: IgcTileComponent | null
) {
if (!lastSwapTile) return false;

// Check if the pointer is outside the boundaries of the last swapped tile

const rect = lastSwapTile.getBoundingClientRect();
const pointerX = event.clientX;
const pointerY = event.clientY;

const outsideBoundaries =
pointerX < rect.left ||
pointerX > rect.right ||
pointerY < rect.top ||
pointerY > rect.bottom;

if (outsideBoundaries && this._managerContext) {
this._managerContext.lastSwapTile = null;
}

return outsideBoundaries;
}

private cacheStyles() {
//use util
const computedStyle = getComputedStyle(this);
Expand Down Expand Up @@ -530,7 +568,7 @@ export default class IgcTileComponent extends EventEmitterMixin<
protected renderContent() {
const parts = partNameMap({
base: true,
'drag-over': this._hasDragOver,
'drag-over': this._hasDragOver && !this._isSlideMode,
fullscreen: this.fullscreen,
draggable: !this.disableDrag,
dragging: this._isDragging,
Expand Down

0 comments on commit b98cbdc

Please sign in to comment.