layout.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /**
  2. * @license
  3. * Copyright Google LLC All Rights Reserved.
  4. *
  5. * Use of this source code is governed by an MIT-style license that can be
  6. * found in the LICENSE file at https://angular.io/license
  7. */
  8. import { NgModule, Injectable, NgZone, ɵɵdefineInjectable, ɵɵinject } from '@angular/core';
  9. import { Platform } from '@angular/cdk/platform';
  10. import { combineLatest, concat, Observable, Subject } from 'rxjs';
  11. import { debounceTime, map, skip, startWith, take, takeUntil } from 'rxjs/operators';
  12. import { coerceArray } from '@angular/cdk/coercion';
  13. /**
  14. * @fileoverview added by tsickle
  15. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  16. */
  17. class LayoutModule {
  18. }
  19. LayoutModule.decorators = [
  20. { type: NgModule, args: [{},] },
  21. ];
  22. /**
  23. * @fileoverview added by tsickle
  24. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  25. */
  26. /**
  27. * Global registry for all dynamically-created, injected media queries.
  28. * @type {?}
  29. */
  30. const mediaQueriesForWebkitCompatibility = new Set();
  31. /**
  32. * Style tag that holds all of the dynamically-created media queries.
  33. * @type {?}
  34. */
  35. let mediaQueryStyleNode;
  36. /**
  37. * A utility for calling matchMedia queries.
  38. */
  39. class MediaMatcher {
  40. /**
  41. * @param {?} _platform
  42. */
  43. constructor(_platform) {
  44. this._platform = _platform;
  45. this._matchMedia = this._platform.isBrowser && window.matchMedia ?
  46. // matchMedia is bound to the window scope intentionally as it is an illegal invocation to
  47. // call it from a different scope.
  48. window.matchMedia.bind(window) :
  49. noopMatchMedia;
  50. }
  51. /**
  52. * Evaluates the given media query and returns the native MediaQueryList from which results
  53. * can be retrieved.
  54. * Confirms the layout engine will trigger for the selector query provided and returns the
  55. * MediaQueryList for the query provided.
  56. * @param {?} query
  57. * @return {?}
  58. */
  59. matchMedia(query) {
  60. if (this._platform.WEBKIT) {
  61. createEmptyStyleRule(query);
  62. }
  63. return this._matchMedia(query);
  64. }
  65. }
  66. MediaMatcher.decorators = [
  67. { type: Injectable, args: [{ providedIn: 'root' },] },
  68. ];
  69. /** @nocollapse */
  70. MediaMatcher.ctorParameters = () => [
  71. { type: Platform }
  72. ];
  73. /** @nocollapse */ MediaMatcher.ngInjectableDef = ɵɵdefineInjectable({ factory: function MediaMatcher_Factory() { return new MediaMatcher(ɵɵinject(Platform)); }, token: MediaMatcher, providedIn: "root" });
  74. /**
  75. * For Webkit engines that only trigger the MediaQueryListListener when
  76. * there is at least one CSS selector for the respective media query.
  77. * @param {?} query
  78. * @return {?}
  79. */
  80. function createEmptyStyleRule(query) {
  81. if (mediaQueriesForWebkitCompatibility.has(query)) {
  82. return;
  83. }
  84. try {
  85. if (!mediaQueryStyleNode) {
  86. mediaQueryStyleNode = document.createElement('style');
  87. mediaQueryStyleNode.setAttribute('type', 'text/css');
  88. (/** @type {?} */ (document.head)).appendChild(mediaQueryStyleNode);
  89. }
  90. if (mediaQueryStyleNode.sheet) {
  91. ((/** @type {?} */ (mediaQueryStyleNode.sheet)))
  92. .insertRule(`@media ${query} {.fx-query-test{ }}`, 0);
  93. mediaQueriesForWebkitCompatibility.add(query);
  94. }
  95. }
  96. catch (e) {
  97. console.error(e);
  98. }
  99. }
  100. /**
  101. * No-op matchMedia replacement for non-browser platforms.
  102. * @param {?} query
  103. * @return {?}
  104. */
  105. function noopMatchMedia(query) {
  106. // Use `as any` here to avoid adding additional necessary properties for
  107. // the noop matcher.
  108. return (/** @type {?} */ ({
  109. matches: query === 'all' || query === '',
  110. media: query,
  111. addListener: (/**
  112. * @return {?}
  113. */
  114. () => { }),
  115. removeListener: (/**
  116. * @return {?}
  117. */
  118. () => { })
  119. }));
  120. }
  121. /**
  122. * @fileoverview added by tsickle
  123. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  124. */
  125. /**
  126. * Utility for checking the matching state of \@media queries.
  127. */
  128. class BreakpointObserver {
  129. /**
  130. * @param {?} _mediaMatcher
  131. * @param {?} _zone
  132. */
  133. constructor(_mediaMatcher, _zone) {
  134. this._mediaMatcher = _mediaMatcher;
  135. this._zone = _zone;
  136. /**
  137. * A map of all media queries currently being listened for.
  138. */
  139. this._queries = new Map();
  140. /**
  141. * A subject for all other observables to takeUntil based on.
  142. */
  143. this._destroySubject = new Subject();
  144. }
  145. /**
  146. * Completes the active subject, signalling to all other observables to complete.
  147. * @return {?}
  148. */
  149. ngOnDestroy() {
  150. this._destroySubject.next();
  151. this._destroySubject.complete();
  152. }
  153. /**
  154. * Whether one or more media queries match the current viewport size.
  155. * @param {?} value One or more media queries to check.
  156. * @return {?} Whether any of the media queries match.
  157. */
  158. isMatched(value) {
  159. /** @type {?} */
  160. const queries = splitQueries(coerceArray(value));
  161. return queries.some((/**
  162. * @param {?} mediaQuery
  163. * @return {?}
  164. */
  165. mediaQuery => this._registerQuery(mediaQuery).mql.matches));
  166. }
  167. /**
  168. * Gets an observable of results for the given queries that will emit new results for any changes
  169. * in matching of the given queries.
  170. * @param {?} value One or more media queries to check.
  171. * @return {?} A stream of matches for the given queries.
  172. */
  173. observe(value) {
  174. /** @type {?} */
  175. const queries = splitQueries(coerceArray(value));
  176. /** @type {?} */
  177. const observables = queries.map((/**
  178. * @param {?} query
  179. * @return {?}
  180. */
  181. query => this._registerQuery(query).observable));
  182. /** @type {?} */
  183. let stateObservable = combineLatest(observables);
  184. // Emit the first state immediately, and then debounce the subsequent emissions.
  185. stateObservable = concat(stateObservable.pipe(take(1)), stateObservable.pipe(skip(1), debounceTime(0)));
  186. return stateObservable.pipe(map((/**
  187. * @param {?} breakpointStates
  188. * @return {?}
  189. */
  190. (breakpointStates) => {
  191. /** @type {?} */
  192. const response = {
  193. matches: false,
  194. breakpoints: {},
  195. };
  196. breakpointStates.forEach((/**
  197. * @param {?} state
  198. * @return {?}
  199. */
  200. (state) => {
  201. response.matches = response.matches || state.matches;
  202. response.breakpoints[state.query] = state.matches;
  203. }));
  204. return response;
  205. })));
  206. }
  207. /**
  208. * Registers a specific query to be listened for.
  209. * @private
  210. * @param {?} query
  211. * @return {?}
  212. */
  213. _registerQuery(query) {
  214. // Only set up a new MediaQueryList if it is not already being listened for.
  215. if (this._queries.has(query)) {
  216. return (/** @type {?} */ (this._queries.get(query)));
  217. }
  218. /** @type {?} */
  219. const mql = this._mediaMatcher.matchMedia(query);
  220. // Create callback for match changes and add it is as a listener.
  221. /** @type {?} */
  222. const queryObservable = new Observable((/**
  223. * @param {?} observer
  224. * @return {?}
  225. */
  226. (observer) => {
  227. // Listener callback methods are wrapped to be placed back in ngZone. Callbacks must be placed
  228. // back into the zone because matchMedia is only included in Zone.js by loading the
  229. // webapis-media-query.js file alongside the zone.js file. Additionally, some browsers do not
  230. // have MediaQueryList inherit from EventTarget, which causes inconsistencies in how Zone.js
  231. // patches it.
  232. /** @type {?} */
  233. const handler = (/**
  234. * @param {?} e
  235. * @return {?}
  236. */
  237. (e) => this._zone.run((/**
  238. * @return {?}
  239. */
  240. () => observer.next(e))));
  241. mql.addListener(handler);
  242. return (/**
  243. * @return {?}
  244. */
  245. () => {
  246. mql.removeListener(handler);
  247. });
  248. })).pipe(startWith(mql), map((/**
  249. * @param {?} nextMql
  250. * @return {?}
  251. */
  252. (nextMql) => ({ query, matches: nextMql.matches }))), takeUntil(this._destroySubject));
  253. // Add the MediaQueryList to the set of queries.
  254. /** @type {?} */
  255. const output = { observable: queryObservable, mql };
  256. this._queries.set(query, output);
  257. return output;
  258. }
  259. }
  260. BreakpointObserver.decorators = [
  261. { type: Injectable, args: [{ providedIn: 'root' },] },
  262. ];
  263. /** @nocollapse */
  264. BreakpointObserver.ctorParameters = () => [
  265. { type: MediaMatcher },
  266. { type: NgZone }
  267. ];
  268. /** @nocollapse */ BreakpointObserver.ngInjectableDef = ɵɵdefineInjectable({ factory: function BreakpointObserver_Factory() { return new BreakpointObserver(ɵɵinject(MediaMatcher), ɵɵinject(NgZone)); }, token: BreakpointObserver, providedIn: "root" });
  269. /**
  270. * Split each query string into separate query strings if two queries are provided as comma
  271. * separated.
  272. * @param {?} queries
  273. * @return {?}
  274. */
  275. function splitQueries(queries) {
  276. return queries.map((/**
  277. * @param {?} query
  278. * @return {?}
  279. */
  280. (query) => query.split(',')))
  281. .reduce((/**
  282. * @param {?} a1
  283. * @param {?} a2
  284. * @return {?}
  285. */
  286. (a1, a2) => a1.concat(a2)))
  287. .map((/**
  288. * @param {?} query
  289. * @return {?}
  290. */
  291. query => query.trim()));
  292. }
  293. /**
  294. * @fileoverview added by tsickle
  295. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  296. */
  297. // PascalCase is being used as Breakpoints is used like an enum.
  298. // tslint:disable-next-line:variable-name
  299. /**
  300. * @license
  301. * Copyright Google LLC All Rights Reserved.
  302. *
  303. * Use of this source code is governed by an MIT-style license that can be
  304. * found in the LICENSE file at https://angular.io/license
  305. * @type {?}
  306. */
  307. const Breakpoints = {
  308. XSmall: '(max-width: 599.99px)',
  309. Small: '(min-width: 600px) and (max-width: 959.99px)',
  310. Medium: '(min-width: 960px) and (max-width: 1279.99px)',
  311. Large: '(min-width: 1280px) and (max-width: 1919.99px)',
  312. XLarge: '(min-width: 1920px)',
  313. Handset: '(max-width: 599.99px) and (orientation: portrait), ' +
  314. '(max-width: 959.99px) and (orientation: landscape)',
  315. Tablet: '(min-width: 600px) and (max-width: 839.99px) and (orientation: portrait), ' +
  316. '(min-width: 960px) and (max-width: 1279.99px) and (orientation: landscape)',
  317. Web: '(min-width: 840px) and (orientation: portrait), ' +
  318. '(min-width: 1280px) and (orientation: landscape)',
  319. HandsetPortrait: '(max-width: 599.99px) and (orientation: portrait)',
  320. TabletPortrait: '(min-width: 600px) and (max-width: 839.99px) and (orientation: portrait)',
  321. WebPortrait: '(min-width: 840px) and (orientation: portrait)',
  322. HandsetLandscape: '(max-width: 959.99px) and (orientation: landscape)',
  323. TabletLandscape: '(min-width: 960px) and (max-width: 1279.99px) and (orientation: landscape)',
  324. WebLandscape: '(min-width: 1280px) and (orientation: landscape)',
  325. };
  326. /**
  327. * @fileoverview added by tsickle
  328. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  329. */
  330. /**
  331. * @fileoverview added by tsickle
  332. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  333. */
  334. export { LayoutModule, BreakpointObserver, Breakpoints, MediaMatcher };
  335. //# sourceMappingURL=layout.js.map