Skip to content

Commit 44bcbec

Browse files
committed
feat: initial drag ghost functionality
1 parent f18d17b commit 44bcbec

File tree

7 files changed

+117
-48
lines changed

7 files changed

+117
-48
lines changed

src/components/common/utils.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,15 @@ export function simulateDragStart(node: Element) {
264264
);
265265
}
266266

267+
export function simulateDragOver(node: Element) {
268+
node.dispatchEvent(
269+
new DragEvent('dragover', {
270+
bubbles: true,
271+
composed: true,
272+
})
273+
);
274+
}
275+
267276
export function simulateDragEnd(node: Element) {
268277
node.dispatchEvent(
269278
new DragEvent('dragend', { bubbles: true, composed: true })

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,18 @@ type TileDragAndDropConfig = {
88
dragEnd: TileDragCallback;
99
dragEnter: TileDragCallback;
1010
dragLeave: TileDragCallback;
11+
dragOver: TileDragCallback;
1112
drop: TileDragCallback;
1213
};
1314

14-
const DragEvents = ['dragstart', 'dragend', 'dragenter', 'dragleave', 'drop'];
15+
const DragEvents = [
16+
'dragstart',
17+
'dragend',
18+
'dragenter',
19+
'dragleave',
20+
'dragover',
21+
'drop',
22+
];
1523

1624
export class TileDragAndDropController implements ReactiveController {
1725
public enabled = true;

src/components/tile-manager/themes/tile.base.scss

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
}
1515
}
1616

17+
[part~='tile-container'] {
18+
width: 100%;
19+
height: 100%;
20+
}
21+
1722
[part~='base'] {
1823
width: 100%;
1924
height: 100%;
@@ -46,6 +51,20 @@
4651
}
4752
}
4853

54+
[part="ghost"] {
55+
position: absolute;
56+
top: 0;
57+
left: 0;
58+
width: 100%;
59+
height: 100%;
60+
background-color: rgba(137 196 244);
61+
border: 2px dashed #007bff;
62+
transform: scale(0);
63+
64+
// transition: transform 0.5s ease-in-out;
65+
z-index: 10;
66+
}
67+
4968
// [part~='drag-over'] {
5069
// opacity: 0.5;
5170
// pointer-events: none;

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { defineComponents } from '../common/definitions/defineComponents.js';
66
import { first, last } from '../common/util.js';
77
import {
88
simulateDragEnd,
9+
simulateDragOver,
910
simulateDragStart,
1011
simulateDrop,
1112
} from '../common/utils.spec.js';
@@ -60,6 +61,7 @@ describe('Tile drag and drop', () => {
6061

6162
const dragAndDrop = async (draggedTile: Element, dropTarget: Element) => {
6263
simulateDragStart(draggedTile);
64+
simulateDragOver(dropTarget);
6365
simulateDrop(dropTarget);
6466
simulateDragEnd(draggedTile);
6567
await elementUpdated(tileManager);
@@ -83,29 +85,29 @@ describe('Tile drag and drop', () => {
8385
it('should adjust reflected tiles positions in slide mode', async () => {
8486
const tiles = getTiles();
8587
const draggedTile = first(tiles);
86-
const dropTarget = tiles[2];
88+
const dropTarget = tiles[1];
8789

8890
expect(tileManager.dragMode).to.equal('slide');
8991
tileManager.tiles.forEach((tile, index) => {
9092
expect(tile.id).to.equal(`tile${index}`);
9193
});
9294
expect(draggedTile.position).to.equal(0);
93-
expect(dropTarget.position).to.equal(2);
95+
expect(dropTarget.position).to.equal(1);
9496

9597
await dragAndDrop(draggedTile, dropTarget);
9698

9799
const expectedIdsAfterDrag = [
98100
'tile1',
99-
'tile2',
100101
'tile0',
102+
'tile2',
101103
'tile3',
102104
'tile4',
103105
];
104106
tileManager.tiles.forEach((tile, index) => {
105107
expect(tile.id).to.equal(expectedIdsAfterDrag[index]);
106108
});
107-
expect(draggedTile.position).to.equal(2);
108-
expect(dropTarget.position).to.equal(1);
109+
expect(draggedTile.position).to.equal(1);
110+
expect(dropTarget.position).to.equal(0);
109111
});
110112

111113
it('should not change order when dragging a tile onto itself in slide mode', async () => {

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,16 @@ describe('Tile Manager component', () => {
144144
part="resize"
145145
>
146146
<div
147-
part="base draggable resizable"
147+
part="tile-container"
148148
style="--ig-col-span:1;--ig-row-span:1;--ig-col-start:null;--ig-row-start:null;"
149149
>
150-
<slot name="header"></slot>
151-
<div part="content-container">
152-
<slot></slot>
150+
<div part="ghost">
151+
</div>
152+
<div part="base draggable resizable">
153+
<slot name="header"></slot>
154+
<div part="content-container">
155+
<slot></slot>
156+
</div>
153157
</div>
154158
</div>
155159
</igc-resize>`

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

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
4545
registerComponent(IgcTileManagerComponent, IgcTileComponent);
4646
}
4747

48-
private draggedItem: IgcTileComponent | null = null;
4948
private positionedTiles: IgcTileComponent[] = [];
5049
private _columnCount = 10;
5150

51+
/** @private @hidden @internal */
52+
public draggedItem: IgcTileComponent | null = null;
53+
5254
// @query('[part="base"]', true)
5355
// private _baseWrapper!: HTMLDivElement;
5456

@@ -200,7 +202,6 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
200202

201203
private handleTileDragEnd() {
202204
if (this.draggedItem) {
203-
this.draggedItem.style.transform = ''; // Reset transformation
204205
this.draggedItem = null;
205206
}
206207
}
@@ -221,28 +222,6 @@ export default class IgcTileManagerComponent extends EventEmitterMixin<
221222
const tempPosition = draggedItem.position;
222223
draggedItem.position = target.position;
223224
target.position = tempPosition;
224-
} else if (this.dragMode === 'slide') {
225-
// Move dragged tile and adjust positions of affected tiles
226-
const draggedPos = draggedItem.position;
227-
const targetPos = target.position;
228-
229-
this.tiles.forEach((tile) => {
230-
if (
231-
draggedPos < targetPos &&
232-
tile.position > draggedPos &&
233-
tile.position <= targetPos
234-
) {
235-
tile.position -= 1;
236-
} else if (
237-
draggedPos > targetPos &&
238-
tile.position >= targetPos &&
239-
tile.position < draggedPos
240-
) {
241-
tile.position += 1;
242-
}
243-
});
244-
245-
draggedItem.position = targetPos;
246225
}
247226
}
248227
}

src/components/tile-manager/tile.ts

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ContextProvider } from '@lit/context';
22
import { LitElement, html } from 'lit';
3-
import { property, state } from 'lit/decorators.js';
3+
import { property, query, state } from 'lit/decorators.js';
44
import { styleMap } from 'lit/directives/style-map.js';
55
import { tileContext } from '../common/context.js';
66
import { registerComponent } from '../common/definitions/register.js';
@@ -16,6 +16,7 @@ import IgcResizeComponent from './resize-element.js';
1616
import { styles as shared } from './themes/shared/tile/tile.common.css.js';
1717
import { styles } from './themes/tile.base.css.js';
1818
import IgcTileHeaderComponent from './tile-header.js';
19+
import type IgcTileManagerComponent from './tile-manager.js';
1920

2021
type IgcTileChangeState = {
2122
tile: IgcTileComponent;
@@ -60,6 +61,7 @@ export default class IgcTileComponent extends EventEmitterMixin<
6061

6162
private static readonly increment = createCounter();
6263
private _dragController: TileDragAndDropController;
64+
private _tileManager?: IgcTileManagerComponent;
6365
private _colSpan = 1;
6466
private _rowSpan = 1;
6567
private _colStart: number | null = null;
@@ -83,6 +85,9 @@ export default class IgcTileComponent extends EventEmitterMixin<
8385
initialValue: this,
8486
});
8587

88+
@query('[part="ghost"]', true)
89+
public _ghost!: HTMLElement;
90+
8691
@state()
8792
private _isDragging = false;
8893

@@ -254,6 +259,7 @@ export default class IgcTileComponent extends EventEmitterMixin<
254259
dragEnd: this.handleDragEnd,
255260
dragEnter: this.handleDragEnter,
256261
dragLeave: this.handleDragLeave,
262+
dragOver: this.handleDragOver,
257263
drop: this.handleDragLeave,
258264
});
259265

@@ -266,6 +272,10 @@ export default class IgcTileComponent extends EventEmitterMixin<
266272
public override connectedCallback() {
267273
super.connectedCallback();
268274
this.tileId = this.tileId || `tile-${IgcTileComponent.increment()}`;
275+
// RIVIEW: Should we use lit context instead?
276+
this._tileManager = this.closest(
277+
'igc-tile-manager'
278+
) as IgcTileManagerComponent;
269279
}
270280

271281
public toggleFullscreen() {
@@ -305,14 +315,6 @@ export default class IgcTileComponent extends EventEmitterMixin<
305315
}
306316
}
307317

308-
private handleDragEnter() {
309-
this._hasDragOver = true;
310-
}
311-
312-
private handleDragLeave() {
313-
this._hasDragOver = false;
314-
}
315-
316318
private handleDragStart(e: DragEvent) {
317319
const event = new CustomEvent('tileDragStart', {
318320
detail: { tile: this },
@@ -330,17 +332,56 @@ export default class IgcTileComponent extends EventEmitterMixin<
330332
this._isDragging = true;
331333

332334
requestAnimationFrame(() => {
333-
this.style.transform = 'scale(0)';
335+
this._ghost.style.transform = 'scale(1)';
334336
});
335337
}
336338

339+
private handleDragEnter() {
340+
const draggedId = this._tileManager?.draggedItem?.tileId;
341+
if (this._tileManager && this.tileId !== draggedId) {
342+
const draggedItem = this._tileManager.draggedItem;
343+
if (this._tileManager?.dragMode === 'slide' && draggedItem) {
344+
requestAnimationFrame(() => {
345+
this._ghost.style.transform = 'scale(0)';
346+
});
347+
} else {
348+
this._hasDragOver = true;
349+
}
350+
}
351+
}
352+
353+
private handleDragOver() {
354+
const draggedId = this._tileManager?.draggedItem?.tileId;
355+
if (
356+
this._tileManager &&
357+
this.tileId !== draggedId &&
358+
this._tileManager?.dragMode === 'slide'
359+
) {
360+
const draggedItem = this._tileManager?.draggedItem;
361+
const draggedPosition = draggedItem ? draggedItem.position : -1;
362+
if (draggedPosition >= 0) {
363+
const tempPosition = this._tileManager?.tiles[draggedPosition].position;
364+
this._tileManager.tiles[draggedPosition].position = this.position;
365+
this.position = tempPosition;
366+
}
367+
}
368+
}
369+
370+
private handleDragLeave() {
371+
this._hasDragOver = false;
372+
}
373+
337374
private handleDragEnd() {
338375
const event = new CustomEvent('tileDragEnd', {
339376
detail: { tile: this },
340377
bubbles: true,
341378
});
342379
this.dispatchEvent(event);
343380
this._isDragging = false;
381+
382+
requestAnimationFrame(() => {
383+
this._ghost.style.transform = 'scale(0)';
384+
});
344385
}
345386

346387
private cacheStyles() {
@@ -533,10 +574,17 @@ export default class IgcTileComponent extends EventEmitterMixin<
533574
};
534575

535576
return html`
536-
<div part=${parts} .inert=${this._hasDragOver} style=${styleMap(styles)}>
537-
<slot name="header"></slot>
538-
<div part="content-container">
539-
<slot></slot>
577+
<div
578+
part="tile-container"
579+
.inert=${this._hasDragOver}
580+
style=${styleMap(styles)}
581+
>
582+
<div part="ghost"></div>
583+
<div part=${parts}>
584+
<slot name="header"></slot>
585+
<div part="content-container">
586+
<slot></slot>
587+
</div>
540588
</div>
541589
</div>
542590
`;

0 commit comments

Comments
 (0)