Skip to content

Commit

Permalink
feat: make hammerjs optional (#2280)
Browse files Browse the repository at this point in the history
* feat: make hammerjs optional

Makes HammerJS and logs a warning if it's missing, instead of crashing the app.

* Rename the MdHammerEvent interface.

* Replace the HammerJS annotations with stripped-down alternatives.

* Remove "hammerjs" from the "types" config.
  • Loading branch information
crisbeto authored and jelbourn committed Dec 20, 2016
1 parent d076bd3 commit 28691ca
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 24 deletions.
4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -32,11 +32,13 @@
"@angular/http": "^2.2.0",
"@angular/platform-browser": "^2.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-browser-dynamic": "^2.2.0",
Expand Down
5 changes: 1 addition & 4 deletions src/demo-app/tsconfig.json
Expand Up @@ -21,10 +21,7 @@
"@angular/material": [
"../../dist/@angular/material"
]
},
"types": [
"hammerjs"
]
}
},
"angularCompilerOptions": {
"genDir": "../../dist",
Expand Down
5 changes: 1 addition & 4 deletions src/e2e-app/tsconfig.json
Expand Up @@ -22,10 +22,7 @@
"@angular/material": [
"../../dist/@angular/material"
]
},
"types": [
"hammerjs"
]
}
},
"angularCompilerOptions": {
"genDir": "../../dist",
Expand Down
1 change: 1 addition & 0 deletions src/lib/core/core.ts
Expand Up @@ -50,6 +50,7 @@ export * from './overlay/position/connected-position';

// Gestures
export {GestureConfig} from './gestures/gesture-config';
export * from './gestures/gesture-annotations';

// Ripple
export {MdRipple, MdRippleModule} from './ripple/ripple';
Expand Down
48 changes: 48 additions & 0 deletions 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;
}
31 changes: 22 additions & 9 deletions src/lib/core/gestures/gesture-config.ts
@@ -1,19 +1,32 @@
import {Injectable} from '@angular/core';
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: HammerStatic = 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 ' +
'components may not work correctly.'
);
}
}

/*
* Builds Hammer instance manually to add custom recognizers that match the Material Design spec.
Expand All @@ -28,12 +41,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 = new this._hammer(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.
Expand All @@ -46,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: Recognizer, options: any, ...inheritances: Recognizer[]) {
let recognizer = new (<RecognizerStatic> base.constructor)(options);
let recognizer = new (base.constructor as RecognizerStatic)(options);

inheritances.push(base);
inheritances.forEach(item => recognizer.recognizeWith(item));
Expand Down
1 change: 1 addition & 0 deletions src/lib/slide-toggle/slide-toggle.ts
Expand Up @@ -19,6 +19,7 @@ import {
applyCssTransform,
coerceBooleanProperty,
GestureConfig,
HammerInput,
DefaultStyleCompatibilityModeModule,
} from '../core';
import {Observable} from 'rxjs/Observable';
Expand Down
3 changes: 1 addition & 2 deletions src/lib/slider/slider.ts
Expand Up @@ -14,11 +14,11 @@ import {NG_VALUE_ACCESSOR, ControlValueAccessor, FormsModule} from '@angular/for
import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
import {
GestureConfig,
HammerInput,
coerceBooleanProperty,
coerceNumberProperty,
DefaultStyleCompatibilityModeModule,
} from '../core';
import {Input as HammerInput} from 'hammerjs';
import {Dir} from '../core/rtl/dir';
import {CommonModule} from '@angular/common';
import {
Expand All @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions 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.
Expand All @@ -17,7 +17,7 @@ export class TestGestureConfig extends GestureConfig {
* 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);
Expand Down
2 changes: 0 additions & 2 deletions src/lib/tsconfig-srcs.json
Expand Up @@ -17,8 +17,6 @@
"stripInternal": false,
"typeRoots": [
"../../node_modules/@types"
],
"types": [
]
},
"exclude": [
Expand Down

0 comments on commit 28691ca

Please sign in to comment.