import { Injectable, NgZone, OnDestroy, signal, Signal, } from '@angular/core'; import { log as defaultLog } from '../util/log'; const log = defaultLog.factory('media query'); export enum MediaQuerySettingType { Width, } let mediaQuerySettingId = 0; export interface MediaQuerySetting { name: string; min: number; max: number; type: MediaQuerySettingType; _id: number; } @Injectable() export class MediaQueryService implements OnDestroy { settings: MediaQuerySetting[] = []; lastResult: MediaQuerySetting[] = []; width: number; height: number; private readonly _state = signal([]); /** Signal-native state. Read inside an `effect()` to react. */ public readonly state: Signal = this._state.asReadonly(); onResize: EventListener; debounce: any; constructor( private ngZone: NgZone, ) { this.register([ { name: 'small', min: 0, max: 599, type: MediaQuerySettingType.Width, }, { name: 'large', min: 600, max: Infinity, type: MediaQuerySettingType.Width, }, ]); const debounceTime = 500; this.onResize = () => { clearTimeout(this.debounce); this.debounce = setTimeout(() => { this.ngZone.run(() => { if (typeof window !== 'undefined') { this.width = window.innerWidth; this.height = window.innerHeight; } this.findMediaQuery(); }); }, debounceTime); }; if (typeof window !== 'undefined') { this.debounce = setTimeout(() => { this.onResize(null); }, debounceTime); window.addEventListener('resize', this.onResize); } } public register(settings: MediaQuerySetting[]) { const unregisterIds: Array = []; for (let setting of settings) { let found = false; for (let useSetting of this.settings) { if (useSetting.name === setting.name && useSetting.type === setting.type) { found = true; console.warn(`corifeus-web media-query service has duplicate settings`); break; } } if (found === false) { mediaQuerySettingId++; unregisterIds.push(mediaQuerySettingId); setting._id = mediaQuerySettingId; this.settings.push(setting); } } this.findMediaQuery(); const self = this; return ((unregisterIds: Array) => { return function () { const newSettings: MediaQuerySetting[] = []; for (let setting of self.settings) { let keep = true; for (let unregisterId of unregisterIds) { if (setting._id === unregisterId) { keep = false; break; } } if (keep) newSettings.push(setting); } self.settings = newSettings; }; })(unregisterIds); } private findMediaQuery() { const results: MediaQuerySetting[] = []; this.settings.forEach((setting) => { switch (setting.type) { case MediaQuerySettingType.Width: { const minFound = this.width >= setting.min; const maxFound = this.width <= setting.max; if (minFound && maxFound) results.push(setting); break; } } }); if (JSON.stringify(results) !== JSON.stringify(this.lastResult)) { this.lastResult = results; this._state.set(results); } } ngOnDestroy() { if (typeof window !== 'undefined') { window.removeEventListener('resize', this.onResize); } clearTimeout(this.debounce); } }