Skip to content

Commit

Permalink
fix(grid): DragStarted event emitter to always emit inside the zone (#3
Browse files Browse the repository at this point in the history
…).

fix(grid-item): dragStart$ to be always outside the zone on mouse/touch move events.
  • Loading branch information
llorenspujol committed Feb 5, 2021
1 parent a069890 commit 51d7d76
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 39 deletions.
75 changes: 75 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
AfterContentInit, ChangeDetectionStrategy, Component, ContentChildren, ElementRef, Inject, Input, OnDestroy, OnInit, QueryList,
Renderer2, ViewChild
AfterContentInit, ChangeDetectionStrategy, Component, ContentChildren, ElementRef, Inject, Input, NgZone, OnDestroy, OnInit, QueryList, Renderer2,
ViewChild
} from '@angular/core';
import { BehaviorSubject, iif, merge, NEVER, Observable, Subject, Subscription } from 'rxjs';
import { exhaustMap, filter, map, startWith, switchMap, take, takeUntil } from 'rxjs/operators';
Expand All @@ -9,6 +9,7 @@ import { GRID_ITEM_GET_RENDER_DATA_TOKEN, KtdGridItemRenderDataTokenType } from
import { KTD_GRID_DRAG_HANDLE, KtdGridDragHandle } from '../directives/drag-handle';
import { KTD_GRID_RESIZE_HANDLE, KtdGridResizeHandle } from '../directives/resize-handle';
import { KtdGridService } from '../grid.service';
import { ktdOutsideZone } from '../utils/operators';

@Component({
selector: 'ktd-grid-item',
Expand Down Expand Up @@ -79,6 +80,7 @@ export class KtdGridItemComponent implements OnInit, OnDestroy, AfterContentInit
constructor(public elementRef: ElementRef,
private gridService: KtdGridService,
private renderer: Renderer2,
private ngZone: NgZone,
@Inject(GRID_ITEM_GET_RENDER_DATA_TOKEN) private getItemRenderData: KtdGridItemRenderDataTokenType) {
this.dragStart$ = this.dragStartSubject.asObservable();
this.resizeStart$ = this.resizeStartSubject.asObservable();
Expand Down Expand Up @@ -122,33 +124,36 @@ export class KtdGridItemComponent implements OnInit, OnDestroy, AfterContentInit
() => dragHandles.length > 0,
merge(...dragHandles.toArray().map(dragHandle => ktdMouseOrTouchDown(dragHandle.element.nativeElement, 1))),
ktdMouseOrTouchDown(this.elementRef.nativeElement, 1)
).pipe(exhaustMap((startEvent) => {
// If the event started from an element with the native HTML drag&drop, it'll interfere
// with our own dragging (e.g. `img` tags do it by default). Prevent the default action
// to stop it from happening. Note that preventing on `dragstart` also seems to work, but
// it's flaky and it fails if the user drags it away quickly. Also note that we only want
// to do this for `mousedown` since doing the same for `touchstart` will stop any `click`
// events from firing on touch devices.
if (startEvent.target && (startEvent.target as HTMLElement).draggable && startEvent.type === 'mousedown') {
startEvent.preventDefault();
}

const startPointer = ktdPointerClient(startEvent);
return this.gridService.mouseOrTouchMove$(document).pipe(
takeUntil(ktdMouseOrTouchEnd(document, 1)),
filter((moveEvent) => {
moveEvent.preventDefault();
const movePointer = ktdPointerClient(moveEvent);
const distanceX = Math.abs(startPointer.clientX - movePointer.clientX);
const distanceY = Math.abs(startPointer.clientY - movePointer.clientY);
// When this conditions returns true mean that we are over threshold.
return distanceX + distanceY >= this.dragStartThreshold;
}),
take(1),
// Return the original start event
map(() => startEvent)
);
}));
).pipe(
exhaustMap((startEvent) => {
// If the event started from an element with the native HTML drag&drop, it'll interfere
// with our own dragging (e.g. `img` tags do it by default). Prevent the default action
// to stop it from happening. Note that preventing on `dragstart` also seems to work, but
// it's flaky and it fails if the user drags it away quickly. Also note that we only want
// to do this for `mousedown` since doing the same for `touchstart` will stop any `click`
// events from firing on touch devices.
if (startEvent.target && (startEvent.target as HTMLElement).draggable && startEvent.type === 'mousedown') {
startEvent.preventDefault();
}

const startPointer = ktdPointerClient(startEvent);
return this.gridService.mouseOrTouchMove$(document).pipe(
takeUntil(ktdMouseOrTouchEnd(document, 1)),
ktdOutsideZone(this.ngZone),
filter((moveEvent) => {
moveEvent.preventDefault();
const movePointer = ktdPointerClient(moveEvent);
const distanceX = Math.abs(startPointer.clientX - movePointer.clientX);
const distanceY = Math.abs(startPointer.clientY - movePointer.clientY);
// When this conditions returns true mean that we are over threshold.
return distanceX + distanceY >= this.dragStartThreshold;
}),
take(1),
// Return the original start event
map(() => startEvent)
);
})
);
})
);
}
Expand Down
4 changes: 2 additions & 2 deletions projects/angular-grid-layout/src/lib/grid.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,8 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
...gridItems.map((gridItem) => gridItem.dragStart$.pipe(map((event) => ({event, gridItem, type: 'drag'})))),
...gridItems.map((gridItem) => gridItem.resizeStart$.pipe(map((event) => ({event, gridItem, type: 'resize'})))),
).pipe(exhaustMap(({event, gridItem, type}) => {
// Emit drag or resize start events
(type === 'drag' ? this.dragStarted : this.resizeStarted).emit(getDragResizeEventData(gridItem, this.layout));
// Emit drag or resize start events. Ensure that is start event is inside the zone.
this.ngZone.run(() => (type === 'drag' ? this.dragStarted : this.resizeStarted).emit(getDragResizeEventData(gridItem, this.layout)));
// Get the correct newStateFunc depending on if we are dragging or resizing
const calcNewStateFunc = type === 'drag' ? ktdGridItemDragging : ktdGridItemResizing;

Expand Down
20 changes: 20 additions & 0 deletions projects/angular-grid-layout/src/lib/utils/operators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NgZone } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';

/** Runs source observable outside the zone */
export function ktdOutsideZone<T>(zone: NgZone) {
return (source: Observable<T>) => {
return new Observable<T>(observer => {
return zone.runOutsideAngular<Subscription>(() => source.subscribe(observer));
});
};
}


/** Rxjs operator that makes source observable to no emit any data */
export function ktdNoEmit() {
return (source$: Observable<any>): Observable<any> => {
return source$.pipe(filter(() => false));
};
}
11 changes: 3 additions & 8 deletions projects/angular-grid-layout/src/lib/utils/scroll.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { animationFrameScheduler, fromEvent, interval, NEVER, Observable } from 'rxjs';
import { distinctUntilChanged, filter, map, switchMap, tap } from 'rxjs/operators';
import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
import { ktdNormalizePassiveListenerOptions } from './passive-listeners';
import { getMutableClientRect } from './client-rect';
import { ktdNoEmit } from './operators';

/**
* Proximity, as a ratio to width/height at which to start auto-scrolling.
Expand Down Expand Up @@ -86,12 +87,6 @@ function getHorizontalScrollDirection(clientRect: ClientRect, pointerX: number)
return AutoScrollHorizontalDirection.NONE;
}

/** Rxjs operator that makes source observable to no emit any data */
const noEmit = () =>
(source$: Observable<any>): Observable<any> => {
return source$.pipe(filter(() => false));
};

/**
* Returns an observable that schedules a loop and apply scroll on the scrollNode into the specified direction/s.
* This observable doesn't emit, it just performs the 'scroll' side effect.
Expand All @@ -116,7 +111,7 @@ function scrollToDirectionInterval$(scrollNode: HTMLElement | Window, verticalSc
incrementHorizontalScroll(scrollNode, scrollStep);
}
}),
noEmit()
ktdNoEmit()
);
}

Expand Down

0 comments on commit 51d7d76

Please sign in to comment.