Skip to content

Commit d2a44a1

Browse files
committed
chore: improve type safety in breakpoint observer
* Fixes a workaround where we cast the MediaQueryList event listener to any because TypeScript didn't have proper types before TS 3.1.
1 parent 99f3c2d commit d2a44a1

File tree

2 files changed

+38
-39
lines changed

2 files changed

+38
-39
lines changed

src/cdk/layout/breakpoints-observer.ts

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {coerceArray} from '@angular/cdk/coercion';
910
import {Injectable, NgZone, OnDestroy} from '@angular/core';
10-
import {MediaMatcher} from './media-matcher';
11-
import {asapScheduler, combineLatest, Observable, Subject, Observer} from 'rxjs';
11+
import {asapScheduler, combineLatest, Observable, Observer, Subject} from 'rxjs';
1212
import {debounceTime, map, startWith, takeUntil} from 'rxjs/operators';
13-
import {coerceArray} from '@angular/cdk/coercion';
13+
14+
import {MediaMatcher} from './media-matcher';
1415

1516

1617
/** The current state of a layout breakpoint. */
@@ -60,7 +61,7 @@ export class BreakpointObserver implements OnDestroy {
6061
* @param value One or more media queries to check.
6162
* @returns Whether any of the media queries match.
6263
*/
63-
isMatched(value: string | string[]): boolean {
64+
isMatched(value: string|string[]): boolean {
6465
const queries = splitQueries(coerceArray(value));
6566
return queries.some(mediaQuery => this._registerQuery(mediaQuery).mql.matches);
6667
}
@@ -71,23 +72,22 @@ export class BreakpointObserver implements OnDestroy {
7172
* @param value One or more media queries to check.
7273
* @returns A stream of matches for the given queries.
7374
*/
74-
observe(value: string | string[]): Observable<BreakpointState> {
75+
observe(value: string|string[]): Observable<BreakpointState> {
7576
const queries = splitQueries(coerceArray(value));
7677
const observables = queries.map(query => this._registerQuery(query).observable);
7778

78-
return combineLatest(observables).pipe(
79-
debounceTime(0, asapScheduler),
80-
map((breakpointStates: InternalBreakpointState[]) => {
81-
const response: BreakpointState = {
82-
matches: false,
83-
breakpoints: {},
84-
};
85-
breakpointStates.forEach((state: InternalBreakpointState) => {
86-
response.matches = response.matches || state.matches;
87-
response.breakpoints[state.query] = state.matches;
88-
});
89-
return response;
90-
}));
79+
return combineLatest(observables)
80+
.pipe(debounceTime(0, asapScheduler), map((breakpointStates: InternalBreakpointState[]) => {
81+
const response: BreakpointState = {
82+
matches: false,
83+
breakpoints: {},
84+
};
85+
breakpointStates.forEach((state: InternalBreakpointState) => {
86+
response.matches = response.matches || state.matches;
87+
response.breakpoints[state.query] = state.matches;
88+
});
89+
return response;
90+
}));
9191
}
9292

9393
/** Registers a specific query to be listened for. */
@@ -100,23 +100,24 @@ export class BreakpointObserver implements OnDestroy {
100100
const mql: MediaQueryList = this.mediaMatcher.matchMedia(query);
101101

102102
// Create callback for match changes and add it is as a listener.
103-
const queryObservable = new Observable<MediaQueryList>((observer: Observer<MediaQueryList>) => {
104-
// Listener callback methods are wrapped to be placed back in ngZone. Callbacks must be placed
105-
// back into the zone because matchMedia is only included in Zone.js by loading the
106-
// webapis-media-query.js file alongside the zone.js file. Additionally, some browsers do not
107-
// have MediaQueryList inherit from EventTarget, which causes inconsistencies in how Zone.js
108-
// patches it.
109-
const handler = (e: any) => this.zone.run(() => observer.next(e));
110-
mql.addListener(handler);
111-
112-
return () => {
113-
mql.removeListener(handler);
114-
};
115-
}).pipe(
116-
startWith(mql),
117-
map((nextMql: MediaQueryList) => ({query, matches: nextMql.matches})),
118-
takeUntil(this._destroySubject)
119-
);
103+
const queryObservable =
104+
new Observable<MediaQueryListEvent>((observer: Observer<MediaQueryListEvent>) => {
105+
// Listener callback methods are wrapped to be placed back in ngZone. Callbacks must be
106+
// placed back into the zone because matchMedia is only included in Zone.js by loading the
107+
// webapis-media-query.js file alongside the zone.js file. Additionally, some browsers do
108+
// not have MediaQueryList inherit from EventTarget, which causes inconsistencies in how
109+
// Zone.js patches it.
110+
const handler: (event: MediaQueryListEvent) => void = e =>
111+
this.zone.run(() => observer.next(e));
112+
mql.addListener(handler);
113+
114+
return () => {
115+
mql.removeListener(handler);
116+
};
117+
})
118+
.pipe(
119+
map((nextMql: MediaQueryListEvent) => ({query, matches: nextMql.matches})),
120+
startWith({query, matches: mql.matches}), takeUntil(this._destroySubject));
120121

121122
// Add the MediaQueryList to the set of queries.
122123
const output = {observable: queryObservable, mql};
@@ -131,6 +132,6 @@ export class BreakpointObserver implements OnDestroy {
131132
*/
132133
function splitQueries(queries: string[]): string[] {
133134
return queries.map((query: string) => query.split(','))
134-
.reduce((a1: string[], a2: string[]) => a1.concat(a2))
135-
.map(query => query.trim());
135+
.reduce((a1: string[], a2: string[]) => a1.concat(a2))
136+
.map(query => query.trim());
136137
}

test/browser-providers.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
* - local: Launches the browser locally on the current operating system.
77
* - BS: Launches the browser within BrowserStack
88
* - SL: Launches the browser within Saucelabs
9-
*
10-
* TODO(devversion): rename this to "browserstack" and "saucelabs".
119
*/
1210
const browserConfig = {
1311
'ChromeHeadlessCI': { unitTest: {target: 'local', }},

0 commit comments

Comments
 (0)