-
-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathindex.ts
58 lines (47 loc) · 1.57 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import { DependencyList, EffectCallback } from 'react';
import * as React from 'react';
const { useState, useEffect, useLayoutEffect } = React;
type MediaQueryObject = { [key: string]: string | number | boolean };
const camelToHyphen = (str: string) =>
str.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`).toLowerCase();
const objectToString = (query: string | MediaQueryObject) => {
if (typeof query === 'string') return query;
return Object.entries(query)
.map(([feature, value]) => {
feature = camelToHyphen(feature);
if (typeof value === 'boolean') {
return value ? feature : `not ${feature}`;
}
if (typeof value === 'number' && /[height|width]$/.test(feature)) {
value = `${value}px`;
}
return `(${feature}: ${value})`;
})
.join(' and ');
};
type Effect = (effect: EffectCallback, deps?: DependencyList) => void;
const createUseMedia = (effect: Effect) => (
rawQuery: string | MediaQueryObject,
defaultState: boolean = false
) => {
const [state, setState] = useState(defaultState);
const query = objectToString(rawQuery);
effect(() => {
let mounted = true;
const mql = window.matchMedia(query);
const onChange = () => {
if (!mounted) return;
setState(!!mql.matches);
};
mql.addListener(onChange);
setState(mql.matches);
return () => {
mounted = false;
mql.removeListener(onChange);
};
}, [query]);
return state;
};
export const useMedia = createUseMedia(useEffect);
export const useMediaLayout = createUseMedia(useLayoutEffect);
export default useMedia;