-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
index.js
128 lines (118 loc) · 3.1 KB
/
index.js
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
* External dependencies
*/
import { filter, get } from 'lodash';
import { match } from 'css-mediaquery';
/**
* WordPress dependencies
*/
import { useEffect } from '@wordpress/element';
const ENABLED_MEDIA_QUERY = '(min-width:0px)';
const DISABLED_MEDIA_QUERY = '(min-width:999999px)';
const VALID_MEDIA_QUERY_REGEX = /\((min|max)-width:[^\(]*?\)/g;
function getStyleSheetsThatMatchHostname() {
if ( ! window ) {
return;
}
return filter(
get( window, [ 'document', 'styleSheets' ], [] ),
( styleSheet ) => {
return (
styleSheet.href &&
styleSheet.href.includes( window.location.hostname )
);
}
);
}
function isReplaceableMediaRule( rule ) {
if ( ! rule.media ) {
return false;
}
// Need to use "media.mediaText" instead of "conditionText" for IE support.
return !! rule.media.mediaText.match( VALID_MEDIA_QUERY_REGEX );
}
function replaceRule( styleSheet, newRuleText, index ) {
styleSheet.deleteRule( index );
styleSheet.insertRule( newRuleText, index );
}
function replaceMediaQueryWithWidthEvaluation( ruleText, widthValue ) {
return ruleText.replace( VALID_MEDIA_QUERY_REGEX, ( matchedSubstring ) => {
if (
match( matchedSubstring, {
type: 'screen',
width: widthValue,
} )
) {
return ENABLED_MEDIA_QUERY;
}
return DISABLED_MEDIA_QUERY;
} );
}
/**
* Function that manipulates media queries from stylesheets to simulate a given viewport width.
*
* @param {string} marker CSS selector string defining start and end of manipulable styles.
* @param {number} width Viewport width to simulate.
*/
export default function useSimulatedMediaQuery( marker, width ) {
useEffect( () => {
const styleSheets = getStyleSheetsThatMatchHostname();
const originalStyles = [];
styleSheets.forEach( ( styleSheet, styleSheetIndex ) => {
let relevantSection = false;
for (
let ruleIndex = 0;
ruleIndex < styleSheet.cssRules.length;
++ruleIndex
) {
const rule = styleSheet.cssRules[ ruleIndex ];
if (
! relevantSection &&
!! rule.cssText.match( new RegExp( `#start-${ marker }` ) )
) {
relevantSection = true;
}
if (
relevantSection &&
!! rule.cssText.match( new RegExp( `#end-${ marker }` ) )
) {
relevantSection = false;
}
if ( ! relevantSection || ! isReplaceableMediaRule( rule ) ) {
continue;
}
const ruleText = rule.cssText;
if ( ! originalStyles[ styleSheetIndex ] ) {
originalStyles[ styleSheetIndex ] = [];
}
originalStyles[ styleSheetIndex ][ ruleIndex ] = ruleText;
replaceRule(
styleSheet,
replaceMediaQueryWithWidthEvaluation( ruleText, width ),
ruleIndex
);
}
} );
return () => {
originalStyles.forEach( ( rulesCollection, styleSheetIndex ) => {
if ( ! rulesCollection ) {
return;
}
for (
let ruleIndex = 0;
ruleIndex < rulesCollection.length;
++ruleIndex
) {
const originalRuleText = rulesCollection[ ruleIndex ];
if ( originalRuleText ) {
replaceRule(
styleSheets[ styleSheetIndex ],
originalRuleText,
ruleIndex
);
}
}
} );
};
}, [ width ] );
}