@@ -3,39 +3,46 @@ import PropTypes from "prop-types";
3
3
import invariant from "invariant" ;
4
4
import json2mq from "json2mq" ;
5
5
6
+ const queryType = PropTypes . oneOfType ( [
7
+ PropTypes . string ,
8
+ PropTypes . object ,
9
+ PropTypes . arrayOf ( PropTypes . object . isRequired )
10
+ ] ) ;
11
+
6
12
/**
7
13
* Conditionally renders based on whether or not a media query matches.
8
14
*/
9
15
class Media extends React . Component {
10
16
static propTypes = {
11
- defaultMatches : PropTypes . bool ,
12
- query : PropTypes . oneOfType ( [
13
- PropTypes . string ,
14
- PropTypes . object ,
15
- PropTypes . arrayOf ( PropTypes . object . isRequired )
16
- ] ) . isRequired ,
17
+ defaultMatches : PropTypes . objectOf ( PropTypes . bool ) ,
18
+ queries : PropTypes . objectOf ( queryType ) . isRequired ,
17
19
render : PropTypes . func ,
18
20
children : PropTypes . oneOfType ( [ PropTypes . node , PropTypes . func ] ) ,
19
21
targetWindow : PropTypes . object ,
20
22
onChange : PropTypes . func
21
23
} ;
22
24
23
- static defaultProps = {
24
- defaultMatches : true
25
- } ;
25
+ queries = [ ] ;
26
26
27
27
state = {
28
- matches : this . props . defaultMatches
28
+ matches :
29
+ this . props . defaultMatches ||
30
+ Object . keys ( this . props . queries ) . reduce (
31
+ ( acc , key ) => ( { ...acc , [ key ] : true } ) ,
32
+ { }
33
+ )
29
34
} ;
30
35
31
36
updateMatches = ( ) => {
32
- const { matches } = this . mediaQueryList ;
33
-
34
- this . setState ( { matches } ) ;
37
+ const newMatches = this . queries . reduce (
38
+ ( acc , { name, mqList } ) => ( { ...acc , [ name ] : mqList . matches } ) ,
39
+ { }
40
+ ) ;
41
+ this . setState ( { matches : newMatches } ) ;
35
42
36
43
const { onChange } = this . props ;
37
44
if ( onChange ) {
38
- onChange ( matches ) ;
45
+ onChange ( newMatches ) ;
39
46
}
40
47
} ;
41
48
@@ -49,32 +56,49 @@ class Media extends React.Component {
49
56
"<Media targetWindow> does not support `matchMedia`."
50
57
) ;
51
58
52
- let { query } = this . props ;
53
- if ( typeof query !== "string" ) query = json2mq ( query ) ;
59
+ const { queries } = this . props ;
60
+
61
+ this . queries = Object . keys ( queries ) . map ( name => {
62
+ const query = queries [ name ] ;
63
+ const qs = typeof query !== "string" ? json2mq ( query ) : query ;
64
+ const mqList = targetWindow . matchMedia ( qs ) ;
65
+
66
+ mqList . addListener ( this . updateMatches ) ;
67
+
68
+ return { name, qs, mqList } ;
69
+ } ) ;
54
70
55
- this . mediaQueryList = targetWindow . matchMedia ( query ) ;
56
- this . mediaQueryList . addListener ( this . updateMatches ) ;
57
71
this . updateMatches ( ) ;
58
72
}
59
73
60
74
componentWillUnmount ( ) {
61
- this . mediaQueryList . removeListener ( this . updateMatches ) ;
75
+ this . queries . forEach ( ( { mqList } ) =>
76
+ mqList . removeListener ( this . updateMatches )
77
+ ) ;
62
78
}
63
79
64
80
render ( ) {
65
81
const { children, render } = this . props ;
66
82
const { matches } = this . state ;
67
83
84
+ const isAnyMatches = Object . keys ( matches ) . some ( key => matches [ key ] ) ;
85
+
68
86
return render
69
- ? matches
70
- ? render ( )
87
+ ? isAnyMatches
88
+ ? render ( matches )
71
89
: null
72
90
: children
73
91
? typeof children === "function"
74
92
? children ( matches )
75
- : ! Array . isArray ( children ) || children . length // Preact defaults to empty children array
76
- ? matches
77
- ? React . Children . only ( children )
93
+ : // Preact defaults to empty children array
94
+ ! Array . isArray ( children ) || children . length
95
+ ? isAnyMatches
96
+ ? // We have to check whether child is a composite component or not to decide should we
97
+ // provide `matches` as a prop or not
98
+ React . Children . only ( children ) &&
99
+ typeof React . Children . only ( children ) . type === "string"
100
+ ? React . Children . only ( children )
101
+ : React . cloneElement ( React . Children . only ( children ) , { matches } )
78
102
: null
79
103
: null
80
104
: null ;
0 commit comments