Skip to content

Commit 639e98c

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. There is still some signature issue with our current TypeScript version, but at least we can now type all other declarations, and just have a missing type for `removeListener`. This has been fixed with microsoft/TypeScript@40bd7c8
1 parent 99f3c2d commit 639e98c

File tree

2 files changed

+39
-42
lines changed

2 files changed

+39
-42
lines changed

src/cdk/layout/breakpoints-observer.ts

Lines changed: 39 additions & 40 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. */
@@ -21,9 +22,7 @@ export interface BreakpointState {
2122
* A key boolean pair for each query provided to the observe method,
2223
* with its current matched state.
2324
*/
24-
breakpoints: {
25-
[key: string]: boolean;
26-
};
25+
breakpoints: {[key: string]: boolean;};
2726
}
2827

2928
/** The current state of a layout breakpoint. */
@@ -60,7 +59,7 @@ export class BreakpointObserver implements OnDestroy {
6059
* @param value One or more media queries to check.
6160
* @returns Whether any of the media queries match.
6261
*/
63-
isMatched(value: string | string[]): boolean {
62+
isMatched(value: string|string[]): boolean {
6463
const queries = splitQueries(coerceArray(value));
6564
return queries.some(mediaQuery => this._registerQuery(mediaQuery).mql.matches);
6665
}
@@ -71,23 +70,22 @@ export class BreakpointObserver implements OnDestroy {
7170
* @param value One or more media queries to check.
7271
* @returns A stream of matches for the given queries.
7372
*/
74-
observe(value: string | string[]): Observable<BreakpointState> {
73+
observe(value: string|string[]): Observable<BreakpointState> {
7574
const queries = splitQueries(coerceArray(value));
7675
const observables = queries.map(query => this._registerQuery(query).observable);
7776

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-
}));
77+
return combineLatest(observables)
78+
.pipe(debounceTime(0, asapScheduler), map((breakpointStates: InternalBreakpointState[]) => {
79+
const response: BreakpointState = {
80+
matches: false,
81+
breakpoints: {},
82+
};
83+
breakpointStates.forEach((state: InternalBreakpointState) => {
84+
response.matches = response.matches || state.matches;
85+
response.breakpoints[state.query] = state.matches;
86+
});
87+
return response;
88+
}));
9189
}
9290

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

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

121120
// Add the MediaQueryList to the set of queries.
122121
const output = {observable: queryObservable, mql};
@@ -131,6 +130,6 @@ export class BreakpointObserver implements OnDestroy {
131130
*/
132131
function splitQueries(queries: string[]): string[] {
133132
return queries.map((query: string) => query.split(','))
134-
.reduce((a1: string[], a2: string[]) => a1.concat(a2))
135-
.map(query => query.trim());
133+
.reduce((a1: string[], a2: string[]) => a1.concat(a2))
134+
.map(query => query.trim());
136135
}

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)