Skip to content

Commit 06d222a

Browse files
committed
feat(drag-n-drop): fixed drag between grids
1 parent 505c326 commit 06d222a

File tree

2 files changed

+83
-103
lines changed

2 files changed

+83
-103
lines changed

projects/angular-grid-layout/src/lib/grid.component.ts

+24-94
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ export type KtdResizeEnd = KtdDragResizeEvent;
7777
interface KtdDroppedEvent<T> {
7878
event: PointingDeviceEvent;
7979
currentLayout: KtdGridLayout;
80-
previousLayoutItem: KtdGridLayoutItem<T> | null; // Previous layout is null only when dragging ktdDrag
8180
currentLayoutItem: KtdGridLayoutItem<T>;
8281
}
8382

@@ -367,7 +366,10 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
367366
private _gridItemsRenderData: KtdDictionary<KtdGridItemRenderData<number>>;
368367
private subscriptions: Subscription[];
369368

370-
private drag: KtdGridDrag | null = null;
369+
public get drag(): KtdGridDrag | null {
370+
return this._drag;
371+
}
372+
private _drag: KtdGridDrag | null = null;
371373

372374
private readonly gridElement: HTMLElement;
373375

@@ -551,14 +553,6 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
551553
this.dragExited.subscribe(({dragInfo}) => {
552554
this.pauseDragSequence(dragInfo);
553555
}),
554-
this.gridService.pointerBeforeEnd$.subscribe(({dragInfo}) => {
555-
if (this.drag !== null && dragInfo !== null && dragInfo.currentGrid === this) {
556-
console.log('Grid ', this.id);
557-
this.updateLayout(dragInfo);
558-
this.stopDragSequence(dragInfo);
559-
}
560-
this.drag = null;
561-
}),
562556
];
563557
}
564558

@@ -568,10 +562,16 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
568562
* @param dragInfo The drag info.
569563
*/
570564
private startRestoreDragSequence(event: PointingDeviceEvent, dragInfo: PointerEventInfo): void {
565+
// Drag sequence can be paused, but the resize sequence can't be paused.
571566
if (this.drag !== null && dragInfo.type === 'resize') {
572567
return;
573568
}
574569

570+
// Prevents the resize from starting if the resize already started on another grid.
571+
if (dragInfo.type === 'resize' && this.drag === null && dragInfo.fromGrid !== this) {
572+
return;
573+
}
574+
575575
const scrollableParent = typeof this.scrollableParent === 'string' ? document.getElementById(this.scrollableParent) : this.scrollableParent;
576576

577577
// TODO (enhancement): consider move this 'side effect' observable inside the main drag loop.
@@ -588,7 +588,7 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
588588
takeUntil(this.gridService.pointerEnd$),
589589
).subscribe());
590590

591-
this.drag = {
591+
this._drag = {
592592
dragSubscription: this.createDragResizeLoop(scrollableParent, dragInfo),
593593
scrollSubscription,
594594
startEvent: event,
@@ -740,7 +740,7 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
740740
);
741741
}
742742

743-
private stopDragSequence(dragInfo: PointerEventInfo): void {
743+
public stopDragSequence(dragInfo: PointerEventInfo): void {
744744
if (this.drag === null) {
745745
return;
746746
}
@@ -754,86 +754,18 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
754754
});
755755

756756
this.addGridItemAnimatingClass(dragInfo.dragRef).subscribe();
757-
// Consider destroying the placeholder after the animation has finished.
758-
this.destroyPlaceholder();
759-
this.drag.dragSubscription?.unsubscribe();
760-
this.drag.scrollSubscription?.unsubscribe();
761-
this.drag = null;
762-
}
763-
764-
public updateLayout(dragInfo: PointerEventInfo): void {
765-
if (this.drag != null && this.drag.newLayout) {
766-
const currentLayoutItem = dragInfo.fromGrid === null ? {...dragInfo.newLayoutItem, id: this.getNextId()} : dragInfo.newLayoutItem;
767-
const previousLayoutItem = this.layout.find(item => item.id === dragInfo.dragRef.id);
768-
769-
// Dragging from one grid to another
770-
if (dragInfo.fromGrid !== dragInfo.currentGrid) {
771-
// Add new item to the layout if it is being dragged from outside the grid.
772-
this.ngZone.run(() => {
773-
if (dragInfo.fromGrid !== null) {
774-
dragInfo.fromGrid.layoutUpdated.emit(dragInfo.fromGrid.layout.filter(item => item.id !== dragInfo.dragRef.id));
775-
}
776-
777-
// Do not emit when:
778-
// - Drag is a resize and bounds have not changed.
779-
// - Drag is a normal drag and the item is being dragged inside the same grid.
780-
// - We are dragging from outside the grid and the item was dragged into the grid, but then pointer was released outside the grid.
781-
if (dragInfo.type !== 'resize' && dragInfo.currentGrid === this) {
782-
this.dropped.emit({
783-
event: dragInfo.moveEvent,
784-
currentLayout: this.drag!.newLayout!.map(item => ({
785-
id: item.id,
786-
x: item.x,
787-
y: item.y,
788-
w: item.w,
789-
h: item.h,
790-
minW: item.minW,
791-
minH: item.minH,
792-
maxW: item.maxW,
793-
maxH: item.maxH,
794-
data: item.data,
795-
})) as KtdGridLayout,
796-
previousLayoutItem: previousLayoutItem !== undefined ? previousLayoutItem : null,
797-
currentLayoutItem: currentLayoutItem,
798-
});
799-
return;
800-
}
801-
802-
// Emit when we are not dragging or resizing items already inside the grid.
803-
// this.layoutUpdated.emit(this.drag!.newLayout!);
804-
});
805-
} else {
806-
// Add new item to the layout if it is being dragged from outside the grid.
807-
this.ngZone.run(() => {
808-
// Do not emit when:
809-
// - Drag is a resize and bounds have not changed.
810-
// - Drag is a normal drag and the item is being dragged inside the same grid.
811-
// - We are dragging from outside the grid and the item was dragged into the grid, but then pointer was released outside the grid.
812-
if (dragInfo.type !== 'resize' && dragInfo.fromGrid === null && dragInfo.currentGrid === this) {
813-
this.dropped.emit({
814-
event: dragInfo.moveEvent,
815-
currentLayout: this.drag!.newLayout!.map(item => ({
816-
id: item.id,
817-
x: item.x,
818-
y: item.y,
819-
w: item.w,
820-
h: item.h,
821-
minW: item.minW,
822-
minH: item.minH,
823-
maxW: item.maxW,
824-
maxH: item.maxH,
825-
data: item.data,
826-
})) as KtdGridLayout,
827-
previousLayoutItem: previousLayoutItem !== undefined ? previousLayoutItem : null,
828-
currentLayoutItem: currentLayoutItem,
829-
});
830-
return;
831-
}
757+
}
832758

833-
// Emit when we are not dragging or resizing items already inside the grid.
834-
this.layoutUpdated.emit(this.drag!.newLayout!);
835-
});
836-
}
759+
/**
760+
* Clears the drag sequence.
761+
* This is called from grid-service when drag/resize finishes.
762+
*/
763+
public clearDragSequence(): void {
764+
if (this.drag !== null) {
765+
this.destroyPlaceholder();
766+
this.drag?.dragSubscription?.unsubscribe();
767+
this.drag?.scrollSubscription?.unsubscribe();
768+
this._drag = null;
837769
}
838770
}
839771

@@ -844,7 +776,7 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
844776
return gridElemClientRect.left < pointerX && pointerX < gridElemClientRect.right && gridElemClientRect.top < pointerY && pointerY < gridElemClientRect.bottom;
845777
}
846778

847-
private getNextId(): string {
779+
public getNextId(): string {
848780
return this._gridItems.toArray().reduce((acc, cur) => acc > parseInt(cur.id) ? acc : parseInt(cur.id), 0) + 1 + '';
849781
}
850782

@@ -854,9 +786,7 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
854786
* @param dragRef that has been dragged
855787
*/
856788
private addGridItemAnimatingClass(dragRef: DragRef): Observable<undefined> {
857-
858789
return new Observable(observer => {
859-
860790
const duration = getTransformTransitionDurationInMs(dragRef.elementRef.nativeElement);
861791

862792
if (duration === 0) {

projects/angular-grid-layout/src/lib/grid.service.ts

+59-9
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ export class KtdGridService {
3232
private pointerEndSubject: Subject<MouseEvent | TouchEvent> = new Subject<MouseEvent | TouchEvent>();
3333
private pointerEndSubscription: Subscription;
3434

35-
pointerBeforeEnd$: Observable<{event: MouseEvent | TouchEvent, dragInfo: PointerEventInfo | null}>;
36-
private pointerBeforeEndSubject: Subject<{event: MouseEvent | TouchEvent, dragInfo: PointerEventInfo | null}> = new Subject<{event: MouseEvent | TouchEvent, dragInfo: PointerEventInfo | null}>();
37-
3835
private drag: PointerEventInfo | null = null;
3936

4037
constructor(
@@ -43,7 +40,6 @@ export class KtdGridService {
4340
) {
4441
this.pointerMove$ = this.pointerMoveSubject.asObservable();
4542
this.pointerEnd$ = this.pointerEndSubject.asObservable();
46-
this.pointerBeforeEnd$ = this.pointerBeforeEndSubject.asObservable();
4743
this.initSubscriptions();
4844
}
4945

@@ -56,12 +52,11 @@ export class KtdGridService {
5652
this.pointerEndSubscription = this.ngZone.runOutsideAngular(() =>
5753
ktdPointerUp(document)
5854
.subscribe((mouseEvent: MouseEvent | TouchEvent) => {
59-
this.pointerBeforeEndSubject.next({
60-
event: mouseEvent,
61-
dragInfo: this.drag,
62-
});
63-
this.drag = null;
6455
this.pointerEndSubject.next(mouseEvent);
56+
if (this.drag !== null) {
57+
this.updateGrids(this.drag);
58+
}
59+
this.drag = null;
6560
})
6661
);
6762
}
@@ -72,6 +67,7 @@ export class KtdGridService {
7267
* @param dragRef The dragRef that started the drag sequence.
7368
* @param type The type of drag sequence.
7469
* @param grid The grid where the drag sequence started. It can be null if the drag sequence started outside a grid.
70+
* @param gridItem The grid item that is being dragged. It can be null if the drag sequence started from outside a grid.
7571
*/
7672
public startDrag(event: MouseEvent | TouchEvent | PointerEvent, dragRef: DragRef, type: DragActionType, grid: KtdGridComponent | null = null, gridItem: {layoutItem: LayoutItem, renderData: KtdGridItemRenderData<number>} | null = null): void {
7773
// Make sure, this function is only being called once
@@ -163,6 +159,60 @@ export class KtdGridService {
163159
this.drag!.currentGrid = grid;
164160
}
165161

162+
private updateGrids(drag: PointerEventInfo): void {
163+
// If the drag ended outside a grid, we don't need to do anything
164+
if (drag.currentGrid === null) {
165+
/*
166+
* This emit is not required, but when it is not here, it cases a bug where the grid-element,
167+
* does not return to its original position when the drag ends outside the grid.
168+
* The same thing happens when the resize ends outside the grid.
169+
* */
170+
drag.fromGrid?.layoutUpdated.emit(drag.fromGrid!.layout);
171+
drag.fromGrid?.stopDragSequence(drag);
172+
return;
173+
}
174+
175+
if (drag.type === 'resize') {
176+
if (drag.fromGrid === drag.currentGrid) {
177+
drag.currentGrid.layoutUpdated.emit(drag.currentGrid.drag!.newLayout!);
178+
} else {
179+
/*
180+
* This emit is not required, but when it is not here, it cases a bug where the grid-element,
181+
* does not return to its original position when the resize ends on another grid than the one it started.
182+
*/
183+
if (drag.fromGrid !== null && drag.fromGrid.drag !== null) {
184+
drag.fromGrid.layoutUpdated.emit(drag.fromGrid.drag.newLayout!);
185+
}
186+
}
187+
} else {
188+
const currentLayoutItem = drag.fromGrid === null ? {
189+
...drag.newLayoutItem,
190+
id: drag.currentGrid.getNextId()
191+
} : drag.newLayoutItem;
192+
193+
// Dragging between two distinct grids
194+
if (drag.fromGrid !== drag.currentGrid) {
195+
// Notify the previous grid that the item has left it
196+
drag.fromGrid?.layoutUpdated.emit(drag.fromGrid!.layout.filter(item => item.id !== drag.dragRef.id));
197+
198+
// Notify the new grid that we dropped new item that was not in any grid
199+
drag.currentGrid?.dropped.emit({
200+
event: drag.moveEvent,
201+
currentLayout: drag.currentGrid.drag!.newLayout!.map(item => ({...item})),
202+
currentLayoutItem: currentLayoutItem,
203+
});
204+
} else {
205+
// Update the new grid layout
206+
drag.currentGrid.layoutUpdated.emit(drag.currentGrid.drag!.newLayout!);
207+
}
208+
}
209+
210+
// Clean up
211+
drag.fromGrid?.stopDragSequence(drag);
212+
drag.currentGrid?.stopDragSequence(drag);
213+
this.registryService._ktgGrids.forEach(grid => grid.clearDragSequence());
214+
}
215+
166216
dispose() {
167217
this.pointerMoveSubscription.unsubscribe();
168218
this.pointerEndSubscription.unsubscribe();

0 commit comments

Comments
 (0)