From 0c7b8fb3ed13d077b6f68e8b0d86c0acabb85965 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Mon, 19 Dec 2016 15:11:50 +0200 Subject: [PATCH 1/4] feat: make hammerjs optional Makes HammerJS and logs a warning if it's missing, instead of crashing the app. --- package.json | 4 ++- src/lib/core/core.ts | 2 +- src/lib/core/gestures/gesture-config.ts | 42 +++++++++++++++++++------ src/lib/slide-toggle/slide-toggle.ts | 3 +- src/lib/slider/slider.ts | 7 ++--- src/lib/slider/test-gesture-config.ts | 2 +- 6 files changed, 43 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 1eeac95d1840..13e704dac5eb 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,13 @@ "@angular/platform-browser-dynamic": "^2.2.0", "@angular/router": "^3.2.0", "core-js": "^2.4.1", - "hammerjs": "^2.0.8", "rxjs": "5.0.0-beta.12", "systemjs": "0.19.38", "zone.js": "^0.6.23" }, + "optionalDependencies": { + "hammerjs": "^2.0.8" + }, "devDependencies": { "@angular/compiler-cli": "^2.2.0", "@angular/platform-server": "^2.2.0", diff --git a/src/lib/core/core.ts b/src/lib/core/core.ts index 2a314300e8d7..d1da5f13c4d9 100644 --- a/src/lib/core/core.ts +++ b/src/lib/core/core.ts @@ -49,7 +49,7 @@ export * from './overlay/position/connected-position-strategy'; export * from './overlay/position/connected-position'; // Gestures -export {GestureConfig} from './gestures/gesture-config'; +export {GestureConfig, MdHammerEvent} from './gestures/gesture-config'; // Ripple export {MdRipple, MdRippleModule} from './ripple/ripple'; diff --git a/src/lib/core/gestures/gesture-config.ts b/src/lib/core/gestures/gesture-config.ts index 63ad3fcd11d0..37857253002f 100644 --- a/src/lib/core/gestures/gesture-config.ts +++ b/src/lib/core/gestures/gesture-config.ts @@ -1,19 +1,28 @@ -import {Injectable} from '@angular/core'; +import {Injectable, isDevMode} from '@angular/core'; import {HammerGestureConfig} from '@angular/platform-browser'; /* Adjusts configuration of our gesture library, Hammer. */ @Injectable() export class GestureConfig extends HammerGestureConfig { + private _hammer = typeof window !== 'undefined' ? (window as any).Hammer : null; /* List of new event names to add to the gesture support list */ - events: string[] = [ + events: string[] = this._hammer ? [ 'longpress', 'slide', 'slidestart', 'slideend', 'slideright', 'slideleft' - ]; + ] : []; + + constructor() { + super(); + + if (!this._hammer && isDevMode()) { + console.warn('Could not find HammerJS. Certain Angular Material may not work correctly.'); + } + } /* * Builds Hammer instance manually to add custom recognizers that match the Material Design spec. @@ -28,12 +37,12 @@ export class GestureConfig extends HammerGestureConfig { * TODO: Confirm threshold numbers with Material Design UX Team * */ buildHammer(element: HTMLElement) { - const mc = new Hammer(element); + const mc: any = super.buildHammer(element); // Default Hammer Recognizers. - let pan = new Hammer.Pan(); - let swipe = new Hammer.Swipe(); - let press = new Hammer.Press(); + let pan = new this._hammer.Pan(); + let swipe = new this._hammer.Swipe(); + let press = new this._hammer.Press(); // Notice that a HammerJS recognizer can only depend on one other recognizer once. // Otherwise the previous `recognizeWith` will be dropped. @@ -50,8 +59,8 @@ export class GestureConfig extends HammerGestureConfig { } /** Creates a new recognizer, without affecting the default recognizers of HammerJS */ - private _createRecognizer(base: Recognizer, options: any, ...inheritances: Recognizer[]) { - let recognizer = new ( base.constructor)(options); + private _createRecognizer(base: any, options: any, ...inheritances: any[]) { + let recognizer = new base.constructor(options); inheritances.push(base); inheritances.forEach(item => recognizer.recognizeWith(item)); @@ -60,3 +69,18 @@ export class GestureConfig extends HammerGestureConfig { } } + +/** + * Stripped-down annotation to be used as alternative + * to the one from HammerJS. + * @docs-private + */ +export interface MdHammerEvent { + preventDefault: () => {}; + deltaX: number; + deltaY: number; + center: { + x: number; + y: number; + }; +} diff --git a/src/lib/slide-toggle/slide-toggle.ts b/src/lib/slide-toggle/slide-toggle.ts index 09674d2cfc6a..ef279f09410a 100644 --- a/src/lib/slide-toggle/slide-toggle.ts +++ b/src/lib/slide-toggle/slide-toggle.ts @@ -19,6 +19,7 @@ import { applyCssTransform, coerceBooleanProperty, GestureConfig, + MdHammerEvent, DefaultStyleCompatibilityModeModule, } from '../core'; import {Observable} from 'rxjs/Observable'; @@ -234,7 +235,7 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor { } } - _onDrag(event: HammerInput) { + _onDrag(event: MdHammerEvent) { if (this._slideRenderer.isDragging()) { this._slideRenderer.updateThumbPosition(event.deltaX); } diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index 3e539b71ad9c..6ae5d449130a 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -14,11 +14,11 @@ import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/for import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import { GestureConfig, + MdHammerEvent, coerceBooleanProperty, coerceNumberProperty, DefaultStyleCompatibilityModeModule, } from '../core'; -import {Input as HammerInput} from 'hammerjs'; import {Dir} from '../core/rtl/dir'; import {CommonModule} from '@angular/common'; import { @@ -32,7 +32,6 @@ import { DOWN_ARROW, } from '../core/keyboard/keycodes'; - /** * Visually, a 30px separation between tick marks looks best. This is very subjective but it is * the default separation we chose. @@ -324,7 +323,7 @@ export class MdSlider implements ControlValueAccessor { this._emitValueIfChanged(); } - _onSlide(event: HammerInput) { + _onSlide(event: MdHammerEvent) { if (this.disabled) { return; } @@ -334,7 +333,7 @@ export class MdSlider implements ControlValueAccessor { this._updateValueFromPosition({x: event.center.x, y: event.center.y}); } - _onSlideStart(event: HammerInput) { + _onSlideStart(event: MdHammerEvent) { if (this.disabled) { return; } diff --git a/src/lib/slider/test-gesture-config.ts b/src/lib/slider/test-gesture-config.ts index f4573b002035..849351c7c153 100644 --- a/src/lib/slider/test-gesture-config.ts +++ b/src/lib/slider/test-gesture-config.ts @@ -11,7 +11,7 @@ export class TestGestureConfig extends GestureConfig { * A map of Hammer instances to element. * Used to emit events over instances for an element. */ - hammerInstances: Map = new Map(); + hammerInstances: Map = new Map(); /** * Create a mapping of Hammer instances to element so that events can be emitted during testing. From 9faa28c9b673358267d86e48a900ddb01e5a482c Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 20 Dec 2016 11:11:15 +0200 Subject: [PATCH 2/4] Rename the MdHammerEvent interface. --- src/lib/core/core.ts | 2 +- src/lib/core/gestures/gesture-config.ts | 2 +- src/lib/slide-toggle/slide-toggle.ts | 4 ++-- src/lib/slider/slider.ts | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/core/core.ts b/src/lib/core/core.ts index d1da5f13c4d9..100debbdd995 100644 --- a/src/lib/core/core.ts +++ b/src/lib/core/core.ts @@ -49,7 +49,7 @@ export * from './overlay/position/connected-position-strategy'; export * from './overlay/position/connected-position'; // Gestures -export {GestureConfig, MdHammerEvent} from './gestures/gesture-config'; +export {GestureConfig, HammerEvent} from './gestures/gesture-config'; // Ripple export {MdRipple, MdRippleModule} from './ripple/ripple'; diff --git a/src/lib/core/gestures/gesture-config.ts b/src/lib/core/gestures/gesture-config.ts index 37857253002f..74eaac7cd72a 100644 --- a/src/lib/core/gestures/gesture-config.ts +++ b/src/lib/core/gestures/gesture-config.ts @@ -75,7 +75,7 @@ export class GestureConfig extends HammerGestureConfig { * to the one from HammerJS. * @docs-private */ -export interface MdHammerEvent { +export interface HammerEvent { preventDefault: () => {}; deltaX: number; deltaY: number; diff --git a/src/lib/slide-toggle/slide-toggle.ts b/src/lib/slide-toggle/slide-toggle.ts index ef279f09410a..c32499e7523e 100644 --- a/src/lib/slide-toggle/slide-toggle.ts +++ b/src/lib/slide-toggle/slide-toggle.ts @@ -19,7 +19,7 @@ import { applyCssTransform, coerceBooleanProperty, GestureConfig, - MdHammerEvent, + HammerEvent, DefaultStyleCompatibilityModeModule, } from '../core'; import {Observable} from 'rxjs/Observable'; @@ -235,7 +235,7 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor { } } - _onDrag(event: MdHammerEvent) { + _onDrag(event: HammerEvent) { if (this._slideRenderer.isDragging()) { this._slideRenderer.updateThumbPosition(event.deltaX); } diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index 6ae5d449130a..16c20a5c27d1 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -14,7 +14,7 @@ import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/for import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import { GestureConfig, - MdHammerEvent, + HammerEvent, coerceBooleanProperty, coerceNumberProperty, DefaultStyleCompatibilityModeModule, @@ -323,7 +323,7 @@ export class MdSlider implements ControlValueAccessor { this._emitValueIfChanged(); } - _onSlide(event: MdHammerEvent) { + _onSlide(event: HammerEvent) { if (this.disabled) { return; } @@ -333,7 +333,7 @@ export class MdSlider implements ControlValueAccessor { this._updateValueFromPosition({x: event.center.x, y: event.center.y}); } - _onSlideStart(event: MdHammerEvent) { + _onSlideStart(event: HammerEvent) { if (this.disabled) { return; } From 57340b68535452696286e9b3813f656272cd434d Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 20 Dec 2016 12:33:20 +0200 Subject: [PATCH 3/4] Replace the HammerJS annotations with stripped-down alternatives. --- src/lib/core/core.ts | 3 +- src/lib/core/gestures/gesture-annotations.ts | 48 ++++++++++++++++++++ src/lib/core/gestures/gesture-config.ts | 31 ++++--------- src/lib/slide-toggle/slide-toggle.ts | 4 +- src/lib/slider/slider.ts | 6 +-- 5 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 src/lib/core/gestures/gesture-annotations.ts diff --git a/src/lib/core/core.ts b/src/lib/core/core.ts index 100debbdd995..c9b0a6a5f20d 100644 --- a/src/lib/core/core.ts +++ b/src/lib/core/core.ts @@ -49,7 +49,8 @@ export * from './overlay/position/connected-position-strategy'; export * from './overlay/position/connected-position'; // Gestures -export {GestureConfig, HammerEvent} from './gestures/gesture-config'; +export {GestureConfig} from './gestures/gesture-config'; +export * from './gestures/gesture-annotations'; // Ripple export {MdRipple, MdRippleModule} from './ripple/ripple'; diff --git a/src/lib/core/gestures/gesture-annotations.ts b/src/lib/core/gestures/gesture-annotations.ts new file mode 100644 index 000000000000..480b0a0f4136 --- /dev/null +++ b/src/lib/core/gestures/gesture-annotations.ts @@ -0,0 +1,48 @@ +/** + * Stripped-down HammerJS annotations to be used within Material, which are necessary, + * because HammerJS is an optional dependency. For the full annotations see: + * https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/hammerjs + */ + +/** @docs-private */ +export interface HammerInput { + preventDefault: () => {}; + deltaX: number; + deltaY: number; + center: { x: number; y: number; }; +} + +/** @docs-private */ +export interface HammerStatic { + new(element: HTMLElement | SVGElement, options?: any): HammerManager; + + Pan: Recognizer; + Swipe: Recognizer; + Press: Recognizer; +} + +/** @docs-private */ +export interface Recognizer { + new(options?: any): Recognizer; + recognizeWith(otherRecognizer: Recognizer | string): Recognizer; +} + +/** @docs-private */ +export interface RecognizerStatic { + new(options?: any): Recognizer; +} + +/** @docs-private */ +export interface HammerInstance { + on(eventName: string, callback: Function): void; + off(eventName: string, callback: Function): void; +} + +/** @docs-private */ +export interface HammerManager { + add(recogniser: Recognizer | Recognizer[]): Recognizer; + set(options: any): HammerManager; + emit(event: string, data: any): void; + off(events: string, handler?: Function): void; + on(events: string, handler: Function): void; +} diff --git a/src/lib/core/gestures/gesture-config.ts b/src/lib/core/gestures/gesture-config.ts index 74eaac7cd72a..3ab4ec804ee5 100644 --- a/src/lib/core/gestures/gesture-config.ts +++ b/src/lib/core/gestures/gesture-config.ts @@ -1,10 +1,11 @@ import {Injectable, isDevMode} from '@angular/core'; import {HammerGestureConfig} from '@angular/platform-browser'; +import {HammerStatic, HammerInstance, Recognizer, RecognizerStatic} from './gesture-annotations'; /* Adjusts configuration of our gesture library, Hammer. */ @Injectable() export class GestureConfig extends HammerGestureConfig { - private _hammer = typeof window !== 'undefined' ? (window as any).Hammer : null; + private _hammer: HammerStatic = typeof window !== 'undefined' ? (window as any).Hammer : null; /* List of new event names to add to the gesture support list */ events: string[] = this._hammer ? [ @@ -20,7 +21,10 @@ export class GestureConfig extends HammerGestureConfig { super(); if (!this._hammer && isDevMode()) { - console.warn('Could not find HammerJS. Certain Angular Material may not work correctly.'); + console.warn( + 'Could not find HammerJS. Certain Angular Material ' + + 'components may not work correctly.' + ); } } @@ -37,7 +41,7 @@ export class GestureConfig extends HammerGestureConfig { * TODO: Confirm threshold numbers with Material Design UX Team * */ buildHammer(element: HTMLElement) { - const mc: any = super.buildHammer(element); + const mc = new this._hammer(element); // Default Hammer Recognizers. let pan = new this._hammer.Pan(); @@ -55,12 +59,12 @@ export class GestureConfig extends HammerGestureConfig { // Add customized gestures to Hammer manager mc.add([swipe, press, pan, slide, longpress]); - return mc; + return mc as HammerInstance; } /** Creates a new recognizer, without affecting the default recognizers of HammerJS */ - private _createRecognizer(base: any, options: any, ...inheritances: any[]) { - let recognizer = new base.constructor(options); + private _createRecognizer(base: Recognizer, options: any, ...inheritances: Recognizer[]) { + let recognizer = new (base.constructor as RecognizerStatic)(options); inheritances.push(base); inheritances.forEach(item => recognizer.recognizeWith(item)); @@ -69,18 +73,3 @@ export class GestureConfig extends HammerGestureConfig { } } - -/** - * Stripped-down annotation to be used as alternative - * to the one from HammerJS. - * @docs-private - */ -export interface HammerEvent { - preventDefault: () => {}; - deltaX: number; - deltaY: number; - center: { - x: number; - y: number; - }; -} diff --git a/src/lib/slide-toggle/slide-toggle.ts b/src/lib/slide-toggle/slide-toggle.ts index c32499e7523e..d015590fd92f 100644 --- a/src/lib/slide-toggle/slide-toggle.ts +++ b/src/lib/slide-toggle/slide-toggle.ts @@ -19,7 +19,7 @@ import { applyCssTransform, coerceBooleanProperty, GestureConfig, - HammerEvent, + HammerInput, DefaultStyleCompatibilityModeModule, } from '../core'; import {Observable} from 'rxjs/Observable'; @@ -235,7 +235,7 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor { } } - _onDrag(event: HammerEvent) { + _onDrag(event: HammerInput) { if (this._slideRenderer.isDragging()) { this._slideRenderer.updateThumbPosition(event.deltaX); } diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index 16c20a5c27d1..e685e2ac8eea 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -14,7 +14,7 @@ import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/for import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import { GestureConfig, - HammerEvent, + HammerInput, coerceBooleanProperty, coerceNumberProperty, DefaultStyleCompatibilityModeModule, @@ -323,7 +323,7 @@ export class MdSlider implements ControlValueAccessor { this._emitValueIfChanged(); } - _onSlide(event: HammerEvent) { + _onSlide(event: HammerInput) { if (this.disabled) { return; } @@ -333,7 +333,7 @@ export class MdSlider implements ControlValueAccessor { this._updateValueFromPosition({x: event.center.x, y: event.center.y}); } - _onSlideStart(event: HammerEvent) { + _onSlideStart(event: HammerInput) { if (this.disabled) { return; } From 22d7d60a00dbc3e95910e47c6b8121254267bf3c Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 20 Dec 2016 12:33:40 +0200 Subject: [PATCH 4/4] Remove "hammerjs" from the "types" config. --- src/demo-app/tsconfig.json | 5 +---- src/e2e-app/tsconfig.json | 5 +---- src/lib/slider/test-gesture-config.ts | 6 +++--- src/lib/tsconfig-srcs.json | 2 -- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/demo-app/tsconfig.json b/src/demo-app/tsconfig.json index 3a220f91e278..f8acc9304ae2 100644 --- a/src/demo-app/tsconfig.json +++ b/src/demo-app/tsconfig.json @@ -21,10 +21,7 @@ "@angular/material": [ "../../dist/@angular/material" ] - }, - "types": [ - "hammerjs" - ] + } }, "angularCompilerOptions": { "genDir": "../../dist", diff --git a/src/e2e-app/tsconfig.json b/src/e2e-app/tsconfig.json index 678e1786cccc..898fe4d4aceb 100644 --- a/src/e2e-app/tsconfig.json +++ b/src/e2e-app/tsconfig.json @@ -22,10 +22,7 @@ "@angular/material": [ "../../dist/@angular/material" ] - }, - "types": [ - "hammerjs" - ] + } }, "angularCompilerOptions": { "genDir": "../../dist", diff --git a/src/lib/slider/test-gesture-config.ts b/src/lib/slider/test-gesture-config.ts index 849351c7c153..4efd37318381 100644 --- a/src/lib/slider/test-gesture-config.ts +++ b/src/lib/slider/test-gesture-config.ts @@ -1,5 +1,5 @@ import {Injectable} from '@angular/core'; -import {GestureConfig} from '../core'; +import {GestureConfig, HammerManager} from '../core'; /** * An extension of GestureConfig that exposes the underlying HammerManager instances. @@ -11,13 +11,13 @@ export class TestGestureConfig extends GestureConfig { * A map of Hammer instances to element. * Used to emit events over instances for an element. */ - hammerInstances: Map = new Map(); + hammerInstances: Map = new Map(); /** * Create a mapping of Hammer instances to element so that events can be emitted during testing. */ buildHammer(element: HTMLElement) { - let mc = super.buildHammer(element); + let mc = super.buildHammer(element) as HammerManager; if (this.hammerInstances.get(element)) { this.hammerInstances.get(element).push(mc); diff --git a/src/lib/tsconfig-srcs.json b/src/lib/tsconfig-srcs.json index 948f8c15377e..b9f9eb382263 100644 --- a/src/lib/tsconfig-srcs.json +++ b/src/lib/tsconfig-srcs.json @@ -17,8 +17,6 @@ "stripInternal": false, "typeRoots": [ "../../node_modules/@types" - ], - "types": [ ] }, "exclude": [