123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- import * as i0 from '@angular/core';
- import { inject, CSP_NONCE, Injectable, NgZone } from '@angular/core';
- import { Subject, combineLatest, concat, Observable } from 'rxjs';
- import { take, skip, debounceTime, map, startWith, takeUntil } from 'rxjs/operators';
- import { P as Platform } from './platform-DmdVEw_C.mjs';
- import { c as coerceArray } from './array-I1yfCXUO.mjs';
- /** Global registry for all dynamically-created, injected media queries. */
- const mediaQueriesForWebkitCompatibility = new Set();
- /** Style tag that holds all of the dynamically-created media queries. */
- let mediaQueryStyleNode;
- /** A utility for calling matchMedia queries. */
- class MediaMatcher {
- _platform = inject(Platform);
- _nonce = inject(CSP_NONCE, { optional: true });
- /** The internal matchMedia method to return back a MediaQueryList like object. */
- _matchMedia;
- constructor() {
- this._matchMedia =
- this._platform.isBrowser && window.matchMedia
- ? // matchMedia is bound to the window scope intentionally as it is an illegal invocation to
- // call it from a different scope.
- window.matchMedia.bind(window)
- : noopMatchMedia;
- }
- /**
- * Evaluates the given media query and returns the native MediaQueryList from which results
- * can be retrieved.
- * Confirms the layout engine will trigger for the selector query provided and returns the
- * MediaQueryList for the query provided.
- */
- matchMedia(query) {
- if (this._platform.WEBKIT || this._platform.BLINK) {
- createEmptyStyleRule(query, this._nonce);
- }
- return this._matchMedia(query);
- }
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MediaMatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MediaMatcher, providedIn: 'root' });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MediaMatcher, decorators: [{
- type: Injectable,
- args: [{ providedIn: 'root' }]
- }], ctorParameters: () => [] });
- /**
- * Creates an empty stylesheet that is used to work around browser inconsistencies related to
- * `matchMedia`. At the time of writing, it handles the following cases:
- * 1. On WebKit browsers, a media query has to have at least one rule in order for `matchMedia`
- * to fire. We work around it by declaring a dummy stylesheet with a `@media` declaration.
- * 2. In some cases Blink browsers will stop firing the `matchMedia` listener if none of the rules
- * inside the `@media` match existing elements on the page. We work around it by having one rule
- * targeting the `body`. See https://github.com/angular/components/issues/23546.
- */
- function createEmptyStyleRule(query, nonce) {
- if (mediaQueriesForWebkitCompatibility.has(query)) {
- return;
- }
- try {
- if (!mediaQueryStyleNode) {
- mediaQueryStyleNode = document.createElement('style');
- if (nonce) {
- mediaQueryStyleNode.setAttribute('nonce', nonce);
- }
- mediaQueryStyleNode.setAttribute('type', 'text/css');
- document.head.appendChild(mediaQueryStyleNode);
- }
- if (mediaQueryStyleNode.sheet) {
- mediaQueryStyleNode.sheet.insertRule(`@media ${query} {body{ }}`, 0);
- mediaQueriesForWebkitCompatibility.add(query);
- }
- }
- catch (e) {
- console.error(e);
- }
- }
- /** No-op matchMedia replacement for non-browser platforms. */
- function noopMatchMedia(query) {
- // Use `as any` here to avoid adding additional necessary properties for
- // the noop matcher.
- return {
- matches: query === 'all' || query === '',
- media: query,
- addListener: () => { },
- removeListener: () => { },
- };
- }
- /** Utility for checking the matching state of `@media` queries. */
- class BreakpointObserver {
- _mediaMatcher = inject(MediaMatcher);
- _zone = inject(NgZone);
- /** A map of all media queries currently being listened for. */
- _queries = new Map();
- /** A subject for all other observables to takeUntil based on. */
- _destroySubject = new Subject();
- constructor() { }
- /** Completes the active subject, signalling to all other observables to complete. */
- ngOnDestroy() {
- this._destroySubject.next();
- this._destroySubject.complete();
- }
- /**
- * Whether one or more media queries match the current viewport size.
- * @param value One or more media queries to check.
- * @returns Whether any of the media queries match.
- */
- isMatched(value) {
- const queries = splitQueries(coerceArray(value));
- return queries.some(mediaQuery => this._registerQuery(mediaQuery).mql.matches);
- }
- /**
- * Gets an observable of results for the given queries that will emit new results for any changes
- * in matching of the given queries.
- * @param value One or more media queries to check.
- * @returns A stream of matches for the given queries.
- */
- observe(value) {
- const queries = splitQueries(coerceArray(value));
- const observables = queries.map(query => this._registerQuery(query).observable);
- let stateObservable = combineLatest(observables);
- // Emit the first state immediately, and then debounce the subsequent emissions.
- stateObservable = concat(stateObservable.pipe(take(1)), stateObservable.pipe(skip(1), debounceTime(0)));
- return stateObservable.pipe(map(breakpointStates => {
- const response = {
- matches: false,
- breakpoints: {},
- };
- breakpointStates.forEach(({ matches, query }) => {
- response.matches = response.matches || matches;
- response.breakpoints[query] = matches;
- });
- return response;
- }));
- }
- /** Registers a specific query to be listened for. */
- _registerQuery(query) {
- // Only set up a new MediaQueryList if it is not already being listened for.
- if (this._queries.has(query)) {
- return this._queries.get(query);
- }
- const mql = this._mediaMatcher.matchMedia(query);
- // Create callback for match changes and add it is as a listener.
- const queryObservable = new Observable((observer) => {
- // Listener callback methods are wrapped to be placed back in ngZone. Callbacks must be placed
- // back into the zone because matchMedia is only included in Zone.js by loading the
- // webapis-media-query.js file alongside the zone.js file. Additionally, some browsers do not
- // have MediaQueryList inherit from EventTarget, which causes inconsistencies in how Zone.js
- // patches it.
- const handler = (e) => this._zone.run(() => observer.next(e));
- mql.addListener(handler);
- return () => {
- mql.removeListener(handler);
- };
- }).pipe(startWith(mql), map(({ matches }) => ({ query, matches })), takeUntil(this._destroySubject));
- // Add the MediaQueryList to the set of queries.
- const output = { observable: queryObservable, mql };
- this._queries.set(query, output);
- return output;
- }
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BreakpointObserver, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BreakpointObserver, providedIn: 'root' });
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: BreakpointObserver, decorators: [{
- type: Injectable,
- args: [{ providedIn: 'root' }]
- }], ctorParameters: () => [] });
- /**
- * Split each query string into separate query strings if two queries are provided as comma
- * separated.
- */
- function splitQueries(queries) {
- return queries
- .map(query => query.split(','))
- .reduce((a1, a2) => a1.concat(a2))
- .map(query => query.trim());
- }
- export { BreakpointObserver as B, MediaMatcher as M };
- //# sourceMappingURL=breakpoints-observer-CljOfYGy.mjs.map
|