This repository has been archived by the owner on Feb 2, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 162
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(util): add Media injectable for media query checks and events
- Loading branch information
1 parent
6916e46
commit edc437e
Showing
3 changed files
with
130 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import {CONST} from "angular2/src/facade/lang"; | ||
import {Injectable} from "angular2/core"; | ||
import {Output} from "angular2/core"; | ||
import {EventEmitter} from "angular2/core"; | ||
|
||
|
||
/** | ||
* As defined in core/style/variables.scss | ||
* | ||
* $layout-breakpoint-xs: 600px !default; | ||
* $layout-breakpoint-sm: 960px !default; | ||
* $layout-breakpoint-md: 1280px !default; | ||
* $layout-breakpoint-lg: 1920px !default; | ||
* | ||
*/ | ||
export const MEDIA: any = { | ||
'xs': '(max-width: 599px)', | ||
'gt-xs': '(min-width: 600px)', | ||
'sm': '(min-width: 600px) and (max-width: 959px)', | ||
'gt-sm': '(min-width: 960px)', | ||
'md': '(min-width: 960px) and (max-width: 1279px)', | ||
'gt-md': '(min-width: 1280px)', | ||
'lg': '(min-width: 1280px) and (max-width: 1919px)', | ||
'gt-lg': '(min-width: 1920px)', | ||
'xl': '(min-width: 1920px)' | ||
}; | ||
|
||
export const MEDIA_PRIORITY: any = [ | ||
'xl', | ||
'gt-lg', | ||
'lg', | ||
'gt-md', | ||
'md', | ||
'gt-sm', | ||
'sm', | ||
'gt-xs', | ||
'xs' | ||
]; | ||
|
||
/** | ||
* Reference to a Media query listener. When you are done with it, call the `destroy` method | ||
* to release its reference. | ||
*/ | ||
export class MediaListener { | ||
|
||
/** | ||
* Emits when the query that this is listening for changes. | ||
*/ | ||
@Output() onMatched: EventEmitter<string> = new EventEmitter<string>(); | ||
|
||
/** | ||
* Determine if this query is currently matched by the viewport. | ||
* @returns {boolean} True if the query is matched. | ||
*/ | ||
get matches(): boolean { | ||
return !this._destroyed && this._mql.matches; | ||
} | ||
|
||
private _destroyed: boolean = false; | ||
|
||
private _listener: MediaQueryListListener; | ||
|
||
constructor(public query: string, | ||
private _mql: MediaQueryList, | ||
private _media: Media) { | ||
this._listener = (mql: MediaQueryList) => this.onMatched.emit(query); | ||
this._mql.addListener(this._listener); | ||
} | ||
|
||
/** | ||
* Destroy and unhook this listener. | ||
*/ | ||
destroy() { | ||
if (!this._destroyed) { | ||
this._mql.removeListener(this._listener); | ||
this._media.unregisterListener(this); | ||
this._destroyed = true; | ||
this._listener = null; | ||
this._mql = null; | ||
} | ||
} | ||
|
||
} | ||
|
||
interface IMediaQueryCache { | ||
references:number; | ||
mql:MediaQueryList; | ||
} | ||
|
||
/** | ||
* Injectable class for being notified of changes to viewport media queries. | ||
*/ | ||
@Injectable() | ||
export class Media { | ||
private _cache: {[query:string]:IMediaQueryCache} = {}; | ||
|
||
listen(query: string): MediaListener { | ||
let listener = this._cache[query]; | ||
if (!listener) { | ||
listener = this._cache[query] = { | ||
mql: window.matchMedia(query), | ||
references: 0 | ||
}; | ||
} | ||
listener.references++; | ||
return new MediaListener(query, listener.mql, this); | ||
} | ||
|
||
unregisterListener(listener: MediaListener): void { | ||
let cached = this._cache[listener.query]; | ||
if (cached) { | ||
cached.references--; | ||
delete this._cache[listener.query]; | ||
} | ||
} | ||
|
||
static hasMedia(size: string): boolean { | ||
let query = MEDIA[size]; | ||
if (!query) { | ||
console.warn(`unknown media query size ${size}. Expected one of [${MEDIA_PRIORITY.join(',')}]`); | ||
return false; | ||
} | ||
return window.matchMedia(query).matches; | ||
} | ||
|
||
} |