Skip to content

Commit

Permalink
perf(click): lazily initialise all click handlers
Browse files Browse the repository at this point in the history
Fixes #942
  • Loading branch information
mattlewis92 committed Apr 11, 2019
1 parent 31fda3b commit 823ee4d
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 13 deletions.
41 changes: 28 additions & 13 deletions projects/angular-calendar/src/modules/common/click.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,24 @@ import {
Inject
} from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { Observable, Subject } from 'rxjs';
import { requestIdleCallbackObservable } from './request-idle-callback';
import { switchMapTo, takeUntil } from 'rxjs/operators';

const clickElements = new Set<HTMLElement>();

const eventName: string =
typeof window !== 'undefined' && typeof window['Hammer'] !== 'undefined'
? 'tap'
: 'click';

@Directive({
selector: '[mwlClick]'
})
export class ClickDirective implements OnInit, OnDestroy {
@Output('mwlClick') click = new EventEmitter<MouseEvent>(); // tslint:disable-line

private removeListener: () => void;
private destroy$ = new Subject();

constructor(
private renderer: Renderer2,
Expand All @@ -33,16 +41,16 @@ export class ClickDirective implements OnInit, OnDestroy {
'true'
);
clickElements.add(this.elm.nativeElement);
const eventName: string =
typeof window !== 'undefined' && typeof window['Hammer'] !== 'undefined'
? 'tap'
: 'click';
this.removeListener = this.renderer.listen(
this.elm.nativeElement,
eventName,
event => {

// issue #942 - lazily initialise all click handlers after initial render as hammerjs is slow
requestIdleCallbackObservable()
.pipe(
switchMapTo(this.listen()),
takeUntil(this.destroy$)
)
.subscribe(event => {
// prevent child click events from firing on parent elements that also have click events
let nearestClickableParent: HTMLElement = event.target;
let nearestClickableParent = event.target as HTMLElement;
while (
!clickElements.has(nearestClickableParent) &&
nearestClickableParent !== this.document.body
Expand All @@ -54,12 +62,19 @@ export class ClickDirective implements OnInit, OnDestroy {
if (isThisClickableElement) {
this.click.next(event);
}
}
);
});
}

ngOnDestroy(): void {
this.removeListener();
this.destroy$.next();
clickElements.delete(this.elm.nativeElement);
}

private listen() {
return new Observable<MouseEvent>(observer => {
return this.renderer.listen(this.elm.nativeElement, eventName, event => {
observer.next(event);
});
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Observable } from 'rxjs';

const isSupported =
typeof window !== 'undefined' &&
typeof window['requestIdleCallback'] !== 'undefined';

export function requestIdleCallbackObservable() {
return new Observable(observer => {
/* istanbul ignore else */
if (isSupported) {
const id = window['requestIdleCallback'](() => {
observer.next();
observer.complete();
});
return () => {
window['cancelIdleCallback'](id);
};
} else {
const timeoutId = setTimeout(() => {
observer.next();
observer.complete();
}, 1);
return () => {
clearTimeout(timeoutId);
};
}
});
}
5 changes: 5 additions & 0 deletions projects/angular-calendar/test/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@ import * as sinon from 'sinon';
use(sinonChai);

let rafStub: sinon.SinonStub;
let requestIdleCallbackStub: sinon.SinonStub;
beforeEach(() => {
rafStub = sinon.stub(window, 'requestAnimationFrame').callsArg(0);
requestIdleCallbackStub = sinon
.stub(window, 'requestIdleCallback' as any)
.callsArg(0);
});

afterEach(() => {
rafStub.restore();
requestIdleCallbackStub.restore();
});

// First, initialize the Angular testing environment.
Expand Down

0 comments on commit 823ee4d

Please sign in to comment.