Skip to content

Commit 2e1fa81

Browse files
authored
Merge pull request #34 from trurl-master/refactor-matchMedia
Refactor match media
2 parents 17c4b40 + 1215151 commit 2e1fa81

File tree

10 files changed

+470
-41
lines changed

10 files changed

+470
-41
lines changed

example/App.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import GlobalObserver from './intersection-observer/global-observer/GlobalObserv
77
import MeasureParent from './resize-observer/measure-parent/MeasureParent';
88
import PrintMySize from './resize-observer/print-my-size/PrintMySize';
99
import CustomUseMedia from './viewport/custom-use-media/CustomUseMedia';
10+
import DeprecatedCustomUseMedia from './viewport/deprecated-use-media/DeprecatedUseMedia';
1011

1112
export default function App() {
1213
return (
@@ -16,6 +17,10 @@ export default function App() {
1617
<Switch>
1718
<Route path="/intersection-observer" component={GlobalObserver} />
1819
<Route path="/viewport" component={CustomUseMedia} />
20+
<Route
21+
path="/viewport-deprecated"
22+
component={DeprecatedCustomUseMedia}
23+
/>
1924
<Route path="/resize-observer/do-i-fit" component={MeasureParent} />
2025
<Route
2126
path="/resize-observer/print-my-size"

example/Nav.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ const Nav = (): React.ReactElement => (
2121
Resize Observer: print my size
2222
</Link>
2323
</li>
24-
<li>
24+
<li style={{ marginRight: '2em' }}>
2525
<Link to="/viewport">Viewport</Link>
2626
</li>
27+
<li>
28+
<Link to="/viewport-deprecated">Viewport (old)</Link>
29+
</li>
2730
</ul>
2831
</nav>
2932
);

example/viewport/custom-use-media/CustomUseMedia.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,27 @@ import * as React from 'react';
22

33
import useMedia from './useMedia';
44

5-
const CustomUseMedia = () => {
6-
const isDesktop = useMedia('(min-width: 640px)');
5+
const CustomUseMedia = ({
6+
query = '(min-width: 640px)',
7+
callback,
8+
asObject = false,
9+
messages: { ok = 'desktop', ko = 'not desktop' } = {
10+
ok: 'desktop',
11+
ko: 'not desktop',
12+
},
13+
}: {
14+
query?: string;
15+
callback?: () => void;
16+
asObject?: boolean;
17+
messages?: { ok: string; ko: string };
18+
}) => {
19+
const doesMatch = useMedia(query, null, { callback, asObject });
720

8-
if (isDesktop === null) {
21+
if (doesMatch === null) {
922
return <div>server</div>;
1023
}
1124

12-
return <div>{isDesktop ? 'desktop' : 'not desktop'}</div>;
25+
return <div>{doesMatch ? ok : ko}</div>;
1326
};
1427

1528
export default CustomUseMedia;

example/viewport/custom-use-media/useMedia.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
import { useState, useEffect } from 'react';
22

3-
function useMedia(query: string, defaultValue: any | null = null) {
3+
function useMedia(
4+
query: string,
5+
defaultValue: any | null = null,
6+
options?:
7+
| {
8+
callback?: (this: MediaQueryList, ev: MediaQueryListEvent) => any;
9+
asObject: false;
10+
}
11+
| {
12+
callback?: (ev: MediaQueryListEvent) => any;
13+
asObject: true;
14+
}
15+
) {
416
const isInBrowser = typeof window !== 'undefined' && window.matchMedia;
517

618
const mq = isInBrowser ? window.matchMedia(query) : null;
@@ -14,7 +26,25 @@ function useMedia(query: string, defaultValue: any | null = null) {
1426
return;
1527
}
1628

17-
const handler = () => setValue(getValue);
29+
if (options?.asObject) {
30+
const handler = {
31+
handleEvent: (ev: MediaQueryListEvent) => {
32+
setValue(getValue);
33+
34+
options?.callback?.(ev);
35+
},
36+
};
37+
38+
mq.addEventListener('change', handler);
39+
40+
return () => mq.removeEventListener('change', handler);
41+
}
42+
43+
function handler(this: MediaQueryList, ev: MediaQueryListEvent) {
44+
setValue(getValue);
45+
46+
options?.callback?.call(this, ev);
47+
}
1848

1949
mq.addEventListener('change', handler);
2050

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import * as React from 'react';
2+
import { useState, useEffect } from 'react';
3+
4+
function useMedia(
5+
query: string,
6+
defaultValue: any | null = null,
7+
callback?: (this: MediaQueryList, ev: MediaQueryListEvent) => any
8+
) {
9+
const isInBrowser = typeof window !== 'undefined' && window.matchMedia;
10+
11+
const mq = isInBrowser ? window.matchMedia(query) : null;
12+
13+
const getValue = () => mq?.matches;
14+
15+
const [value, setValue] = useState(isInBrowser ? getValue : defaultValue);
16+
17+
useEffect(() => {
18+
if (mq === null) {
19+
return;
20+
}
21+
22+
function handler(this: MediaQueryList, ev: MediaQueryListEvent) {
23+
setValue(getValue);
24+
25+
callback?.call(this, ev);
26+
}
27+
28+
mq.addListener(handler);
29+
30+
return () => mq.removeListener(handler);
31+
}, []);
32+
33+
return value;
34+
}
35+
36+
const DeprecatedCustomUseMedia = ({
37+
query = '(min-width: 640px)',
38+
callback,
39+
messages: { ok = 'desktop', ko = 'not desktop' } = {
40+
ok: 'desktop',
41+
ko: 'not desktop',
42+
},
43+
}: {
44+
query?: string;
45+
callback?: () => void;
46+
messages?: { ok: string; ko: string };
47+
}) => {
48+
const doesMatch = useMedia(query, null, callback);
49+
50+
if (doesMatch === null) {
51+
return <div>server</div>;
52+
}
53+
54+
return <div>{doesMatch ? ok : ko}</div>;
55+
};
56+
57+
export default DeprecatedCustomUseMedia;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"scripts": {
2727
"start": "tsdx watch",
2828
"build": "tsdx build",
29-
"test": "tsdx test --passWithNoTests",
29+
"test": "tsdx test",
3030
"lint": "tsdx lint",
3131
"prepare": "tsdx build",
3232
"size": "size-limit",

src/mocks/MediaQueryListEvent.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export class MockedMediaQueryListEvent extends Event {
2+
readonly matches: boolean;
3+
readonly media: string;
4+
5+
constructor(type: 'change', eventInitDict: MediaQueryListEventInit = {}) {
6+
super(type);
7+
8+
this.media = eventInitDict.media ?? '';
9+
this.matches = eventInitDict.matches ?? false;
10+
}
11+
}
12+
13+
if (typeof MediaQueryListEvent === 'undefined') {
14+
Object.defineProperty(window, 'MediaQueryListEvent', {
15+
writable: true,
16+
configurable: true,
17+
value: MockedMediaQueryListEvent,
18+
});
19+
}

src/mocks/resize-observer.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const state: State = {
88
nodeObservers: new Map(),
99
};
1010

11-
class MockedResizeObserver {
11+
class MockedResizeObserver implements ResizeObserver {
1212
nodes: HTMLElement[] = [];
1313
callback: ResizeObserverCallback;
1414

@@ -64,9 +64,12 @@ class MockedResizeObserver {
6464
};
6565
}
6666

67-
function elementToEntry(element: HTMLElement) {
67+
function elementToEntry(element: HTMLElement): ResizeObserverEntry {
6868
const boundingClientRect = element.getBoundingClientRect();
6969

70+
// @ts-ignore
71+
// for some reason, the typescript type definition for ResizeObserverEntry
72+
// is mismatched between tsdx typecheck and vscode typecheck
7073
return {
7174
borderBoxSize: [
7275
{
@@ -81,14 +84,25 @@ function elementToEntry(element: HTMLElement) {
8184
},
8285
],
8386
contentRect: boundingClientRect,
87+
// devicePixelContentBoxSize: [
88+
// // assume device pixel ratio of 1
89+
// {
90+
// blockSize: boundingClientRect.width,
91+
// inlineSize: boundingClientRect.height,
92+
// },
93+
// ],
8494
target: element,
8595
};
8696
}
8797

8898
function mockResizeObserver() {
8999
const savedImplementation = window.ResizeObserver;
90100

91-
window.ResizeObserver = MockedResizeObserver;
101+
Object.defineProperty(window, 'ResizeObserver', {
102+
writable: true,
103+
configurable: true,
104+
value: MockedResizeObserver,
105+
});
92106

93107
afterAll(() => {
94108
window.ResizeObserver = savedImplementation;

0 commit comments

Comments
 (0)