UNPKG

6.14 kBJavaScriptView Raw
1import { Observable } from '../data/observable';
2import { Screen } from '../platform';
3import { Application } from '../application';
4import { matchQuery, MediaQueryType } from '../css-mediaquery';
5import { Trace } from '../trace';
6const mediaQueryLists = [];
7const applicationEvents = [Application.orientationChangedEvent, Application.systemAppearanceChangedEvent];
8// In browser, developers cannot create MediaQueryList instances without calling matchMedia
9let isMediaInitializationEnabled = false;
10function toggleApplicationEventListeners(toAdd) {
11 for (const eventName of applicationEvents) {
12 if (toAdd) {
13 Application.on(eventName, onDeviceChange);
14 }
15 else {
16 Application.off(eventName, onDeviceChange);
17 }
18 }
19}
20function onDeviceChange(args) {
21 for (const mql of mediaQueryLists) {
22 const matches = checkIfMediaQueryMatches(mql.media);
23 if (mql.matches !== matches) {
24 mql._matches = matches;
25 mql.notify({
26 eventName: MediaQueryListImpl.changeEvent,
27 object: mql,
28 matches: mql.matches,
29 media: mql.media,
30 });
31 }
32 }
33}
34function checkIfMediaQueryMatches(mediaQueryString) {
35 const { widthPixels, heightPixels } = Screen.mainScreen;
36 let matches;
37 try {
38 matches = matchQuery(mediaQueryString, {
39 type: MediaQueryType.screen,
40 width: widthPixels,
41 height: heightPixels,
42 'device-width': widthPixels,
43 'device-height': heightPixels,
44 orientation: Application.orientation(),
45 'prefers-color-scheme': Application.systemAppearance(),
46 });
47 }
48 catch (err) {
49 matches = false;
50 Trace.write(err, Trace.categories.MediaQuery, Trace.messageType.error);
51 }
52 return matches;
53}
54function matchMedia(mediaQueryString) {
55 isMediaInitializationEnabled = true;
56 const mediaQueryList = new MediaQueryListImpl();
57 isMediaInitializationEnabled = false;
58 mediaQueryList._media = mediaQueryString;
59 mediaQueryList._matches = checkIfMediaQueryMatches(mediaQueryString);
60 return mediaQueryList;
61}
62class MediaQueryListImpl extends Observable {
63 constructor() {
64 super();
65 if (!isMediaInitializationEnabled) {
66 throw new TypeError('Illegal constructor');
67 }
68 Object.defineProperties(this, {
69 _media: {
70 writable: true,
71 },
72 _matches: {
73 writable: true,
74 },
75 _onChange: {
76 writable: true,
77 value: null,
78 },
79 mediaQueryChangeListeners: {
80 value: new Map(),
81 },
82 _throwInvocationError: {
83 value: null,
84 },
85 });
86 }
87 get media() {
88 this._throwInvocationError?.();
89 return this._media;
90 }
91 get matches() {
92 this._throwInvocationError?.();
93 return this._matches;
94 }
95 // @ts-ignore
96 addEventListener(eventName, callback, thisArg) {
97 this._throwInvocationError?.();
98 const hasChangeListeners = this.hasListeners(MediaQueryListImpl.changeEvent);
99 // Call super method first since it throws in the case of bad parameters
100 super.addEventListener(eventName, callback, thisArg);
101 if (eventName === MediaQueryListImpl.changeEvent && !hasChangeListeners) {
102 mediaQueryLists.push(this);
103 if (mediaQueryLists.length === 1) {
104 toggleApplicationEventListeners(true);
105 }
106 }
107 }
108 // @ts-ignore
109 removeEventListener(eventName, callback, thisArg) {
110 this._throwInvocationError?.();
111 // Call super method first since it throws in the case of bad parameters
112 super.removeEventListener(eventName, callback, thisArg);
113 if (eventName === MediaQueryListImpl.changeEvent) {
114 const hasChangeListeners = this.hasListeners(MediaQueryListImpl.changeEvent);
115 if (!hasChangeListeners) {
116 const index = mediaQueryLists.indexOf(this);
117 if (index >= 0) {
118 mediaQueryLists.splice(index, 1);
119 if (!mediaQueryLists.length) {
120 toggleApplicationEventListeners(false);
121 }
122 }
123 }
124 }
125 }
126 addListener(callback) {
127 this._throwInvocationError?.();
128 // This kind of implementation helps maintain listener registration order
129 // regardless of using the deprecated methods or property onchange
130 const wrapperCallback = (data) => {
131 callback.call(this, {
132 matches: this.matches,
133 media: this.media,
134 });
135 };
136 // Call this method first since it throws in the case of bad parameters
137 this.addEventListener(MediaQueryListImpl.changeEvent, wrapperCallback);
138 this.mediaQueryChangeListeners.set(callback, wrapperCallback);
139 }
140 removeListener(callback) {
141 this._throwInvocationError?.();
142 if (this.mediaQueryChangeListeners.has(callback)) {
143 // Call this method first since it throws in the case of bad parameters
144 this.removeEventListener(MediaQueryListImpl.changeEvent, this.mediaQueryChangeListeners.get(callback));
145 this.mediaQueryChangeListeners.delete(callback);
146 }
147 }
148 get onchange() {
149 this._throwInvocationError?.();
150 return this._onChange;
151 }
152 set onchange(callback) {
153 this._throwInvocationError?.();
154 // Remove old listener if any
155 if (this._onChange) {
156 this.removeListener(this._onChange);
157 }
158 if (callback) {
159 this.addListener(callback);
160 }
161 this._onChange = callback;
162 }
163 _throwInvocationError() {
164 throw new TypeError('Illegal invocation');
165 }
166}
167MediaQueryListImpl.changeEvent = 'change';
168export { matchMedia, MediaQueryListImpl as MediaQueryList, checkIfMediaQueryMatches };
169//# sourceMappingURL=index.js.map
\No newline at end of file