Skip to content

Commit

Permalink
fix: do not mark listeners as passive if they are not supported and…
Browse files Browse the repository at this point in the history
… reduce DOM lookups
  • Loading branch information
arturovt committed Mar 25, 2022
1 parent 7f5b036 commit 09e39bb
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ jobs:
paths:
- 'node_modules'
- run: npm run lint -- --quiet
- run: npm run test -- --no-watch --no-progress --browsers=ChromeHeadlessCI
- run: npm run test -- --no-watch --no-progress --browsers=ChromeHeadlessCI --source-map=false
- run: npm run report-coverage
- run: npm run build
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"rules": {
"rxjs/no-unsafe-takeuntil": "error",
"rxjs/no-subject-unsubscribe": "error",
"rxjs/no-unsafe-subject-next": "error"
"rxjs/no-unsafe-subject-next": "error",
"@angular-eslint/no-host-metadata-property": "off"
}
},
{
Expand Down
33 changes: 21 additions & 12 deletions packages/gantt/src/components/bar/bar-drag.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable, ElementRef, OnDestroy } from '@angular/core';
import { Injectable, ElementRef, OnDestroy, SkipSelf } from '@angular/core';
import { DragRef, DragDrop } from '@angular/cdk/drag-drop';
import { GanttDomService } from '../../gantt-dom.service';
import { GanttDragContainer, InBarPosition } from '../../gantt-drag-container';
Expand All @@ -8,6 +8,8 @@ import { fromEvent, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { GanttUpper } from '../../gantt-upper';
import { GanttLinkType } from '../../class/link';
import { NgxGanttRootComponent } from '../../root.component';
import { passiveListenerOptions } from '../../utils/passive-listeners';

const dragMinWidth = 10;
const activeClass = 'gantt-bar-active';
Expand Down Expand Up @@ -44,7 +46,12 @@ export class GanttBarDrag implements OnDestroy {

private destroy$ = new Subject();

constructor(private dragDrop: DragDrop, private dom: GanttDomService, private dragContainer: GanttDragContainer) {}
constructor(
private dragDrop: DragDrop,
private dom: GanttDomService,
private dragContainer: GanttDragContainer,
@SkipSelf() private root: NgxGanttRootComponent
) {}

private createMouseEvents() {
const dropClass =
Expand All @@ -53,9 +60,9 @@ export class GanttBarDrag implements OnDestroy {
? singleDropActiveClass
: dropActiveClass;

fromEvent(this.barElement, 'mouseenter')
fromEvent(this.barElement, 'mouseenter', passiveListenerOptions)
.pipe(takeUntil(this.destroy$))
.subscribe((event: MouseEvent) => {
.subscribe(() => {
if (this.dragContainer.linkDraggingId && this.dragContainer.linkDraggingId !== this.item.id) {
if (this.item.linkable) {
this.barElement.classList.add(dropClass);
Expand All @@ -69,9 +76,9 @@ export class GanttBarDrag implements OnDestroy {
}
});

fromEvent(this.barElement, 'mouseleave')
fromEvent(this.barElement, 'mouseleave', passiveListenerOptions)
.pipe(takeUntil(this.destroy$))
.subscribe((event: MouseEvent) => {
.subscribe(() => {
if (!this.dragContainer.linkDraggingId) {
this.barElement.classList.remove(activeClass);
} else {
Expand Down Expand Up @@ -244,23 +251,25 @@ export class GanttBarDrag implements OnDestroy {
}

private openDragBackdrop(dragElement: HTMLElement, start: GanttDate, end: GanttDate) {
const dragMaskElement = this.dom.root.querySelector('.gantt-drag-mask') as HTMLElement;
const dragBackdropElement = this.dom.root.querySelector('.gantt-drag-backdrop') as HTMLElement;
const dragBackdropElement = this.root.backdrop.nativeElement;
const dragMaskElement = dragBackdropElement.querySelector('.gantt-drag-mask') as HTMLElement;
const rootRect = this.dom.root.getBoundingClientRect();
const dragRect = dragElement.getBoundingClientRect();
const left = dragRect.left - rootRect.left - this.dom.side.clientWidth;
const width = dragRect.right - dragRect.left;
// Note: updating styles will cause re-layout so we have to place them consistently one by one.
dragMaskElement.style.left = left + 'px';
dragMaskElement.style.width = width + 'px';
dragMaskElement.querySelector('.start').innerHTML = start.format('MM-dd');
dragMaskElement.querySelector('.end').innerHTML = end.format('MM-dd');
dragMaskElement.style.display = 'block';
dragBackdropElement.style.display = 'block';
// This will invalidate the layout, but we won't need re-layout, because we set styles previously.
dragMaskElement.querySelector('.start').innerHTML = start.format('MM-dd');
dragMaskElement.querySelector('.end').innerHTML = end.format('MM-dd');
}

private closeDragBackdrop() {
const dragMaskElement = this.dom.root.querySelector('.gantt-drag-mask') as HTMLElement;
const dragBackdropElement = this.dom.root.querySelector('.gantt-drag-backdrop') as HTMLElement;
const dragBackdropElement = this.root.backdrop.nativeElement;
const dragMaskElement = dragBackdropElement.querySelector('.gantt-drag-mask') as HTMLElement;
dragMaskElement.style.display = 'none';
dragBackdropElement.style.display = 'none';
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Component, HostBinding } from '@angular/core';
import { Component } from '@angular/core';

@Component({
selector: 'gantt-drag-backdrop',
templateUrl: `./drag-backdrop.component.html`
templateUrl: `./drag-backdrop.component.html`,
host: {
class: 'gantt-drag-backdrop'
}
})
export class GanttDragBackdropComponent {
@HostBinding('class.gantt-drag-backdrop') backdropClass = true;
}
export class GanttDragBackdropComponent {}
6 changes: 5 additions & 1 deletion packages/gantt/src/gantt-dom.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Injectable, ElementRef, OnDestroy, Inject, PLATFORM_ID, NgZone } from '
import { fromEvent, Subject, merge, EMPTY, Observable } from 'rxjs';
import { pairwise, map, auditTime, takeUntil } from 'rxjs/operators';
import { isNumber } from './utils/helpers';
import { passiveListenerOptions } from './utils/passive-listeners';

const scrollThreshold = 50;

Expand Down Expand Up @@ -39,7 +40,10 @@ export class GanttDomService implements OnDestroy {

private monitorScrollChange() {
this.ngZone.runOutsideAngular(() =>
merge(fromEvent(this.mainContainer, 'scroll', { passive: true }), fromEvent(this.sideContainer, 'scroll', { passive: true }))
merge(
fromEvent(this.mainContainer, 'scroll', passiveListenerOptions),
fromEvent(this.sideContainer, 'scroll', passiveListenerOptions)
)
.pipe(takeUntil(this.unsubscribe$))
.subscribe((event) => {
this.syncScroll(event);
Expand Down
18 changes: 12 additions & 6 deletions packages/gantt/src/root.component.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,43 @@
import {
Component,
OnInit,
HostBinding,
NgZone,
ElementRef,
Inject,
ContentChild,
TemplateRef,
Input,
Optional,
OnDestroy
OnDestroy,
ViewChild
} from '@angular/core';
import { GanttDomService, ScrollDirection } from './gantt-dom.service';
import { GanttDragContainer } from './gantt-drag-container';
import { take, takeUntil, startWith } from 'rxjs/operators';
import { from, Subject } from 'rxjs';
import { GanttUpper, GANTT_UPPER_TOKEN } from './gantt-upper';
import { GanttPrintService } from './gantt-print.service';
import { passiveListenerOptions } from './utils/passive-listeners';
import { GanttDragBackdropComponent } from './components/drag-backdrop/drag-backdrop.component';

@Component({
selector: 'ngx-gantt-root',
templateUrl: './root.component.html',
providers: [GanttDomService, GanttDragContainer]
providers: [GanttDomService, GanttDragContainer],
host: {
class: 'gantt'
}
})
export class NgxGanttRootComponent implements OnInit, OnDestroy {
@Input() sideWidth: number;

@HostBinding('class.gantt') ganttClass = true;

@ContentChild('sideTemplate', { static: true }) sideTemplate: TemplateRef<any>;

@ContentChild('mainTemplate', { static: true }) mainTemplate: TemplateRef<any>;

/** The native `<gantt-drag-backdrop></gantt-drag-backdrop>` element. */
@ViewChild(GanttDragBackdropComponent, { static: true, read: ElementRef }) backdrop: ElementRef<HTMLElement>;

private unsubscribe$ = new Subject<void>();

private get view() {
Expand Down Expand Up @@ -82,7 +88,7 @@ export class NgxGanttRootComponent implements OnInit, OnDestroy {
return;
}
this.dom
.getViewerScroll({ passive: true })
.getViewerScroll(passiveListenerOptions)
.pipe(takeUntil(this.unsubscribe$))
.subscribe((event) => {
if (event.direction === ScrollDirection.LEFT) {
Expand Down
36 changes: 36 additions & 0 deletions packages/gantt/src/utils/passive-listeners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/** Cached result of whether the user's browser supports passive event listeners. */
let supportsPassiveEvents: boolean;

/**
* Checks whether the user's browser supports passive event listeners.
* See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
*/
export function supportsPassiveEventListeners(): boolean {
if (supportsPassiveEvents == null && typeof window !== 'undefined') {
try {
window.addEventListener(
'test',
null!,
Object.defineProperty({}, 'passive', {
get: () => (supportsPassiveEvents = true)
})
);
} finally {
supportsPassiveEvents = supportsPassiveEvents || false;
}
}

return supportsPassiveEvents;
}

/**
* Normalizes an `AddEventListener` object to something that can be passed
* to `addEventListener` on any browser, no matter whether it supports the
* `options` parameter.
*/
export function normalizePassiveListenerOptions(options: AddEventListenerOptions): AddEventListenerOptions | boolean {
return supportsPassiveEventListeners() ? options : !!options.capture;
}

/** Options used to bind passive event listeners. */
export const passiveListenerOptions = <AddEventListenerOptions>normalizePassiveListenerOptions({ passive: true });

0 comments on commit 09e39bb

Please sign in to comment.