Browse Source

Merge pull request #56 from ShaMan123/master

Adding AutoWidth capability
iou90 6 years ago
parent
commit
c3140b353e
No account linked to committer's email address
3 changed files with 186 additions and 95 deletions
  1. 36
    9
      autoHeightWebView/common.js
  2. 84
    47
      autoHeightWebView/index.android.js
  3. 66
    39
      autoHeightWebView/index.ios.js

+ 36
- 9
autoHeightWebView/common.js View File

5
     return script;
5
     return script;
6
   }
6
   }
7
   return files.reduceRight((file, combinedScript) => {
7
   return files.reduceRight((file, combinedScript) => {
8
-    const { rel, type, href } = file;
8
+      const { rel, type, href } = file;
9
     return `
9
     return `
10
             var link  = document.createElement('link');
10
             var link  = document.createElement('link');
11
             link.rel  = '${rel}';
11
             link.rel  = '${rel}';
17
   }, script);
17
   }, script);
18
 }
18
 }
19
 
19
 
20
-function appendStylesToHead(styles, script) {
21
-  if (!styles) {
22
-    return script;
23
-  }
20
+function appendStylesToHead(styles, script, shouldResizeWidth) {
21
+    var bodyStyle;
22
+    if (shouldResizeWidth) {
23
+        bodyStyle = `
24
+            body {
25
+                display: flex;
26
+                justify-content: center;
27
+                align-items: center;
28
+            }`;
29
+    }
30
+    else {
31
+        bodyStyle = '';
32
+    }
33
+    
34
+    if (!styles) {
35
+        styles = bodyStyle;
36
+    }
37
+    else {
38
+        styles += bodyStyle;
39
+    }
24
   // Escape any single quotes or newlines in the CSS with .replace()
40
   // Escape any single quotes or newlines in the CSS with .replace()
25
-  const escaped = styles.replace(/\'/g, "\\'").replace(/\n/g, '\\n');
41
+    const escaped = styles.replace(/\'/g, "\\'").replace(/\n/g, '\\n');
42
+    
26
   return `
43
   return `
27
           var styleElement = document.createElement('style');
44
           var styleElement = document.createElement('style');
28
           var styleText = document.createTextNode('${escaped}');
45
           var styleText = document.createTextNode('${escaped}');
33
 }
50
 }
34
 
51
 
35
 function getScript(props, baseScript, iframeBaseScript) {
52
 function getScript(props, baseScript, iframeBaseScript) {
36
-  const { hasIframe, files, customStyle } = props;
53
+    const { hasIframe, files, customStyle, shouldResizeWidth } = props;
37
   let script = hasIframe ? iframeBaseScript : baseScript;
54
   let script = hasIframe ? iframeBaseScript : baseScript;
38
   script = files ? appendFilesToHead(files, baseScript) : baseScript;
55
   script = files ? appendFilesToHead(files, baseScript) : baseScript;
39
-  script = customStyle ? appendStylesToHead(customStyle, script) : script;
56
+    script = appendStylesToHead(customStyle, script, shouldResizeWidth);
40
   return script;
57
   return script;
41
 }
58
 }
42
 
59
 
44
   props.onHeightUpdated && props.onHeightUpdated(height);
61
   props.onHeightUpdated && props.onHeightUpdated(height);
45
 }
62
 }
46
 
63
 
64
+function onWidthUpdated(width, props) {
65
+    props.onWidthUpdated && props.onWidthUpdated(width);
66
+}
67
+
68
+function onHeightWidthUpdated(height, width, props) {
69
+    onHeightUpdated(height, props);
70
+    onWidthUpdated(width, props);
71
+    props.onHeightWidthUpdated && props.onHeightWidthUpdated(height, width);
72
+}
73
+
47
 const domMutationObserveScript = 
74
 const domMutationObserveScript = 
48
 `
75
 `
49
 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
76
 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
54
 });
81
 });
55
 `;
82
 `;
56
 
83
 
57
-export { getScript, onHeightUpdated, domMutationObserveScript };
84
+export { getScript, onHeightUpdated, onWidthUpdated, onHeightWidthUpdated, domMutationObserveScript };

+ 84
- 47
autoHeightWebView/index.android.js View File

19
 
19
 
20
 import Immutable from 'immutable';
20
 import Immutable from 'immutable';
21
 
21
 
22
-import { getScript, onHeightUpdated, domMutationObserveScript, getHeight } from './common.js';
22
+import { getScript, onHeightUpdated, onWidthUpdated, onHeightWidthUpdated, domMutationObserveScript } from './common.js';
23
 
23
 
24
 const RCTAutoHeightWebView = requireNativeComponent('RCTAutoHeightWebView', AutoHeightWebView, {
24
 const RCTAutoHeightWebView = requireNativeComponent('RCTAutoHeightWebView', AutoHeightWebView, {
25
   nativeOnly: {
25
   nativeOnly: {
32
   }
32
   }
33
 });
33
 });
34
 
34
 
35
+const screenWidth = Dimensions.get('window').width;
36
+
35
 export default class AutoHeightWebView extends PureComponent {
37
 export default class AutoHeightWebView extends PureComponent {
36
   static propTypes = {
38
   static propTypes = {
37
     source: WebView.propTypes.source,
39
     source: WebView.propTypes.source,
38
-    onHeightUpdated: PropTypes.func,
40
+      onHeightUpdated: PropTypes.func,
41
+      onWidthUpdated: PropTypes.func,
42
+      onHeightWidthUpdated: PropTypes.func,
43
+      shouldResizeWidth: PropTypes.bool,
39
     customScript: PropTypes.string,
44
     customScript: PropTypes.string,
40
     customStyle: PropTypes.string,
45
     customStyle: PropTypes.string,
41
     enableAnimation: PropTypes.bool,
46
     enableAnimation: PropTypes.bool,
44
     // only works on enable animation
49
     // only works on enable animation
45
     animationDuration: PropTypes.number,
50
     animationDuration: PropTypes.number,
46
     // offset of rn webView margin
51
     // offset of rn webView margin
47
-    heightOffset: PropTypes.number,
52
+      heightOffset: PropTypes.number,
53
+      widthOffset: PropTypes.number,
48
     // baseUrl not work in android 4.3 or below version
54
     // baseUrl not work in android 4.3 or below version
49
     enableBaseUrl: PropTypes.bool,
55
     enableBaseUrl: PropTypes.bool,
50
     style: ViewPropTypes.style,
56
     style: ViewPropTypes.style,
68
     enableBaseUrl: false,
74
     enableBaseUrl: false,
69
     enableAnimation: true,
75
     enableAnimation: true,
70
     animationDuration: 555,
76
     animationDuration: 555,
71
-    heightOffset: 20
77
+      heightOffset: 20,
78
+      widthOffset: 20,
79
+      shouldResizeWidth: false
72
   };
80
   };
73
 
81
 
74
   constructor(props) {
82
   constructor(props) {
78
     this.state = {
86
     this.state = {
79
       isChangingSource: false,
87
       isChangingSource: false,
80
       height: 0,
88
       height: 0,
81
-      heightOffset: 0,
89
+        heightOffset: 0,
90
+        width: screenWidth,
91
+        widthOffset: 0,
82
       script: getScript(props, baseScript)
92
       script: getScript(props, baseScript)
83
     };
93
     };
84
   }
94
   }
89
 
99
 
90
   componentWillReceiveProps(nextProps) {
100
   componentWillReceiveProps(nextProps) {
91
     // injectedJavaScript only works when webView reload (source changed)
101
     // injectedJavaScript only works when webView reload (source changed)
92
-    if (Immutable.is(Immutable.fromJS(this.props.source), Immutable.fromJS(nextProps.source))) {
93
-      return;
94
-    } else {
95
-      this.setState(
96
-        {
97
-          isChangingSource: true,
98
-          height: 0,
99
-          heightOffset: 0
100
-        },
101
-        () => {
102
-          this.startInterval();
103
-          this.setState({ isChangingSource: false });
104
-        }
105
-      );
106
-    }
102
+      if (Immutable.is(Immutable.fromJS(this.props.source), Immutable.fromJS(nextProps.source))) {
103
+          return;
104
+      }
105
+      else {
106
+          this.setState(
107
+              {
108
+                  isChangingSource: true,
109
+                  height: 0,
110
+                  heightOffset: 0,
111
+                  width: 0,
112
+                  widthOffset: 0,
113
+              },
114
+              () => {
115
+                  this.startInterval();
116
+                  this.setState({ isChangingSource: false });
117
+              }
118
+          );
119
+      }
107
     this.setState({ script: getScript(nextProps, baseScript) });
120
     this.setState({ script: getScript(nextProps, baseScript) });
108
   }
121
   }
109
 
122
 
147
   }
160
   }
148
 
161
 
149
   onMessage = e => {
162
   onMessage = e => {
150
-    const height = parseInt(isBelowKitKat ? e.nativeEvent.message : e.nativeEvent.data);
151
-    if (height && height !== this.state.height) {
152
-      const { enableAnimation, animationDuration, heightOffset } = this.props;
153
-      enableAnimation && this.opacityAnimatedValue.setValue(0);
154
-      this.stopInterval();
155
-      this.setState(
156
-        {
157
-          heightOffset,
158
-          height
159
-        },
160
-        () => {
161
-          enableAnimation
162
-            ? Animated.timing(this.opacityAnimatedValue, {
163
-                toValue: 1,
164
-                duration: animationDuration
165
-              }).start(() => onHeightUpdated(height, this.props))
166
-            : onHeightUpdated(height, this.props);
167
-        }
168
-      );
169
-    }
163
+      const { height, width } = JSON.parse(isBelowKitKat ? e.nativeEvent.message : e.nativeEvent.data);
164
+      if (height && height !== this.state.height && width && width !== this.state.width) {
165
+          const { enableAnimation, animationDuration, heightOffset, widthOffset } = this.props;
166
+          enableAnimation && this.opacityAnimatedValue.setValue(0);
167
+          this.stopInterval();
168
+          
169
+          this.setState(
170
+              {
171
+                  heightOffset,
172
+                  height,
173
+                  widthOffset,
174
+                  width
175
+              },
176
+              () => {
177
+                  enableAnimation
178
+                      ? Animated.timing(this.opacityAnimatedValue, {
179
+                          toValue: 1,
180
+                          duration: animationDuration
181
+                      }).start(() => onHeightWidthUpdated(height, width, this.props))
182
+                      : onHeightWidthUpdated(height, width, this.props);
183
+              }
184
+          );
185
+      }
170
   };
186
   };
171
 
187
 
172
   onLoadingStart = event => {
188
   onLoadingStart = event => {
198
   }
214
   }
199
 
215
 
200
   render() {
216
   render() {
201
-    const { height, script, isChangingSource, heightOffset } = this.state;
217
+      const { height, width, script, isChangingSource, heightOffset, widthOffset } = this.state;
202
     const { scalesPageToFit, enableAnimation, source, customScript, style, enableBaseUrl } = this.props;
218
     const { scalesPageToFit, enableAnimation, source, customScript, style, enableBaseUrl } = this.props;
203
     let webViewSource = source;
219
     let webViewSource = source;
204
     if (enableBaseUrl) {
220
     if (enableBaseUrl) {
206
         baseUrl: 'file:///android_asset/web/'
222
         baseUrl: 'file:///android_asset/web/'
207
       });
223
       });
208
     }
224
     }
225
+      
209
     return (
226
     return (
210
       <Animated.View
227
       <Animated.View
211
         style={[
228
         style={[
212
           styles.container,
229
           styles.container,
213
           {
230
           {
214
             opacity: enableAnimation ? this.opacityAnimatedValue : 1,
231
             opacity: enableAnimation ? this.opacityAnimatedValue : 1,
215
-            height: height + heightOffset
232
+              height: height + heightOffset,
233
+              width: width + widthOffset
216
           },
234
           },
217
           style
235
           style
218
         ]}
236
         ]}
239
   }
257
   }
240
 }
258
 }
241
 
259
 
242
-const screenWidth = Dimensions.get('window').width;
243
-
244
 const isBelowKitKat = Platform.Version < 19;
260
 const isBelowKitKat = Platform.Version < 19;
245
 
261
 
246
 const styles = StyleSheet.create({
262
 const styles = StyleSheet.create({
247
   container: {
263
   container: {
248
-    width: screenWidth,
264
+    //width: screenWidth,
249
     backgroundColor: 'transparent'
265
     backgroundColor: 'transparent'
250
   },
266
   },
251
   webView: {
267
   webView: {
257
 const baseScript = isBelowKitKat
273
 const baseScript = isBelowKitKat
258
   ? `
274
   ? `
259
     ; (function () {
275
     ; (function () {
276
+        var wrapper = document.createElement('div');
277
+        wrapper.id = 'wrapper';
278
+        while (document.body.firstChild instanceof Node) {
279
+            wrapper.appendChild(document.body.firstChild);
280
+        }
281
+        document.body.appendChild(wrapper);
282
+
260
         AutoHeightWebView.onMessage = function (message) {
283
         AutoHeightWebView.onMessage = function (message) {
261
-            AutoHeightWebView.send(String(document.body.offsetHeight));
284
+            var rect = document.body.firstElementChild.getBoundingClientRect().toJSON();
285
+            var width = Math.round(rect.width);
286
+            var height = Math.round(rect.height);
287
+            AutoHeightWebView.send(JSON.stringify({ width, height }));
262
         };
288
         };
263
         ${domMutationObserveScript}
289
         ${domMutationObserveScript}
264
     } ());
290
     } ());
265
     `
291
     `
266
   : `
292
   : `
267
     ; (function () {
293
     ; (function () {
294
+        var wrapper = document.createElement('div');
295
+        wrapper.id = 'wrapper';
296
+        while (document.body.firstChild instanceof Node) {
297
+            wrapper.appendChild(document.body.firstChild);
298
+        }
299
+        document.body.appendChild(wrapper);
300
+
268
         document.addEventListener('message', function (e) {
301
         document.addEventListener('message', function (e) {
269
-            window.postMessage(String(document.body.offsetHeight));
302
+            var rect = document.body.firstElementChild.getBoundingClientRect().toJSON();
303
+            var width = Math.round(rect.width);
304
+            var height = Math.round(rect.height);
305
+            window.postMessage(JSON.stringify({ width, height }));
270
         });
306
         });
271
         ${domMutationObserveScript}
307
         ${domMutationObserveScript}
272
     } ());
308
     } ());
273
     `;
309
     `;
310
+

+ 66
- 39
autoHeightWebView/index.ios.js View File

6
 
6
 
7
 import PropTypes from 'prop-types';
7
 import PropTypes from 'prop-types';
8
 
8
 
9
-import { getScript, onHeightUpdated, domMutationObserveScript } from './common.js';
9
+import { getScript, onHeightUpdated, onWidthUpdated, onHeightWidthUpdated, domMutationObserveScript } from './common.js';
10
+
11
+const screenWidth = Dimensions.get('window').width;
10
 
12
 
11
 export default class AutoHeightWebView extends PureComponent {
13
 export default class AutoHeightWebView extends PureComponent {
12
   static propTypes = {
14
   static propTypes = {
13
     hasIframe: PropTypes.bool,
15
     hasIframe: PropTypes.bool,
14
     source: WebView.propTypes.source,
16
     source: WebView.propTypes.source,
15
-    onHeightUpdated: PropTypes.func,
17
+      onHeightUpdated: PropTypes.func,
18
+      onWidthUpdated: PropTypes.func,
19
+      onHeightWidthUpdated: PropTypes.func,
20
+      shouldResizeWidth: PropTypes.bool,
16
     customScript: PropTypes.string,
21
     customScript: PropTypes.string,
17
     customStyle: PropTypes.string,
22
     customStyle: PropTypes.string,
18
     enableAnimation: PropTypes.bool,
23
     enableAnimation: PropTypes.bool,
21
     // only works on enable animation
26
     // only works on enable animation
22
     animationDuration: PropTypes.number,
27
     animationDuration: PropTypes.number,
23
     // offset of rn webview margin
28
     // offset of rn webview margin
24
-    heightOffset: PropTypes.number,
29
+      heightOffset: PropTypes.number,
30
+      widthOffset: PropTypes.number,
25
     style: ViewPropTypes.style,
31
     style: ViewPropTypes.style,
26
     //  rn WebView callback
32
     //  rn WebView callback
27
     onError: PropTypes.func,
33
     onError: PropTypes.func,
43
     scalesPageToFit: false,
49
     scalesPageToFit: false,
44
     enableAnimation: true,
50
     enableAnimation: true,
45
     animationDuration: 555,
51
     animationDuration: 555,
46
-    heightOffset: 12
52
+      heightOffset: 12,
53
+      widthOffset: 12,
54
+      shouldResizeWidth: false
47
   };
55
   };
48
 
56
 
49
   constructor(props) {
57
   constructor(props) {
50
     super(props);
58
     super(props);
51
     props.enableAnimation && (this.opacityAnimatedValue = new Animated.Value(0));
59
     props.enableAnimation && (this.opacityAnimatedValue = new Animated.Value(0));
52
     this.state = {
60
     this.state = {
53
-      height: 0,
61
+        height: 0,
62
+        //heightOffset: 0, //?? I added this
63
+        width: screenWidth,
64
+        //widthOffset: 0,
54
       script: getScript(props, baseScript, iframeBaseScript)
65
       script: getScript(props, baseScript, iframeBaseScript)
55
     };
66
     };
56
   }
67
   }
59
     this.setState({ script: getScript(nextProps, baseScript, iframeBaseScript) });
70
     this.setState({ script: getScript(nextProps, baseScript, iframeBaseScript) });
60
   }
71
   }
61
 
72
 
62
-  handleNavigationStateChange = navState => {
63
-    const height = Number(navState.title);
64
-    const { enableAnimation, animationDuration } = this.props;
65
-    if (height && height !== this.state.height) {
66
-      enableAnimation && this.opacityAnimatedValue.setValue(0);
67
-      this.setState({ height }, () => {
68
-        enableAnimation
69
-          ? Animated.timing(this.opacityAnimatedValue, {
70
-              toValue: 1,
71
-              duration: animationDuration
72
-            }).start(() => onHeightUpdated(height, this.props))
73
-          : onHeightUpdated(height, this.props);
74
-      });
75
-    }
73
+    handleNavigationStateChange = navState => {
74
+        var [width, height] = navState.title.split(',');
75
+        width = Number(width);
76
+        height = Number(height);
77
+        const { enableAnimation, animationDuration } = this.props;
78
+        if (height && height !== this.state.height) {       // ??? add to logic ??? width && width !== this.state.width
79
+            enableAnimation && this.opacityAnimatedValue.setValue(0);
80
+            this.setState({ height, width }, () => {
81
+                enableAnimation
82
+                    ? Animated.timing(this.opacityAnimatedValue, {
83
+                        toValue: 1,
84
+                        duration: animationDuration
85
+                    }).start(() => onHeightWidthUpdated(height, width, this.props))
86
+                    : onHeightWidthUpdated(height, width, this.props);
87
+            });
88
+        }
76
   };
89
   };
77
 
90
 
78
   getWebView = webView => (this.webView = webView);
91
   getWebView = webView => (this.webView = webView);
82
   }
95
   }
83
 
96
 
84
   render() {
97
   render() {
85
-    const { height, script } = this.state;
98
+    const { height, width, script } = this.state;
86
     const {
99
     const {
87
       onError,
100
       onError,
88
       onLoad,
101
       onLoad,
93
       enableAnimation,
106
       enableAnimation,
94
       source,
107
       source,
95
       heightOffset,
108
       heightOffset,
109
+      widthOffset,
96
       customScript,
110
       customScript,
97
       style
111
       style
98
     } = this.props;
112
     } = this.props;
103
           styles.container,
117
           styles.container,
104
           {
118
           {
105
             opacity: enableAnimation ? this.opacityAnimatedValue : 1,
119
             opacity: enableAnimation ? this.opacityAnimatedValue : 1,
106
-            height: height + heightOffset
120
+              height: height + heightOffset,
121
+              width: width + widthOffset
107
           },
122
           },
108
           style
123
           style
109
         ]}
124
         ]}
127
   }
142
   }
128
 }
143
 }
129
 
144
 
130
-const screenWidth = Dimensions.get('window').width;
131
-
132
 const styles = StyleSheet.create({
145
 const styles = StyleSheet.create({
133
   container: {
146
   container: {
134
-    width: screenWidth,
135
     backgroundColor: 'transparent'
147
     backgroundColor: 'transparent'
136
   },
148
   },
137
   webView: {
149
   webView: {
141
 });
153
 });
142
 
154
 
143
 const commonScript = `
155
 const commonScript = `
144
-    updateHeight();
145
-    window.addEventListener('load', updateHeight);
146
-    window.addEventListener('resize', updateHeight);
156
+    updateSize();
157
+    window.addEventListener('load', updateSize);
158
+    window.addEventListener('resize', updateSize);
147
     `;
159
     `;
148
 
160
 
149
-const getHeight = `
161
+const _getter = `
150
     function getHeight(height) {
162
     function getHeight(height) {
151
       if(height < 1) {
163
       if(height < 1) {
152
         return document.body.offsetHeight;
164
         return document.body.offsetHeight;
153
       }
165
       }
154
       return height;
166
       return height;
155
     }
167
     }
168
+    function getWidth(width) {
169
+      if(width < 1) {
170
+        return document.body.clientWidth; // maybe should be .offsetWidth ??
171
+      }
172
+      return width;
173
+    }
156
     `;
174
     `;
157
-
158
 const baseScript = `
175
 const baseScript = `
159
     ;
176
     ;
160
-    ${getHeight}
177
+    ${_getter}
161
     (function () {
178
     (function () {
162
         var i = 0;
179
         var i = 0;
163
         var height = 0;
180
         var height = 0;
181
+        var width = ${screenWidth};
164
         var wrapper = document.createElement('div');
182
         var wrapper = document.createElement('div');
165
         wrapper.id = 'height-wrapper';
183
         wrapper.id = 'height-wrapper';
166
         while (document.body.firstChild instanceof Node) {
184
         while (document.body.firstChild instanceof Node) {
167
             wrapper.appendChild(document.body.firstChild);
185
             wrapper.appendChild(document.body.firstChild);
168
         }
186
         }
169
         document.body.appendChild(wrapper);
187
         document.body.appendChild(wrapper);
170
-        function updateHeight() {
171
-            if(document.body.offsetHeight !== height) {
172
-                height = getHeight(wrapper.clientHeight);
173
-                document.title = height;
188
+        function updateSize() {
189
+            var rect = document.body.firstElementChild.getBoundingClientRect().toJSON();
190
+            var newWidth = Math.round(rect.width);
191
+            var newHeight = Math.round(rect.height);
192
+            if(newHeight !== height) {
193
+                //height = getHeight(wrapper.clientHeight);
194
+                //width = getWidth(wrapper.clientWidth);
195
+                document.title = newWidth + ',' + newHeight;
174
                 window.location.hash = ++i;
196
                 window.location.hash = ++i;
175
             }
197
             }
176
         }
198
         }
181
 
203
 
182
 const iframeBaseScript = `
204
 const iframeBaseScript = `
183
     ;
205
     ;
184
-    ${getHeight}
206
+    ${_getter}
185
     (function () {
207
     (function () {
186
         var i = 0;
208
         var i = 0;
187
         var height = 0;
209
         var height = 0;
188
-        function updateHeight() {
189
-            if(document.body.offsetHeight !== height) {
190
-                height = getHeight(document.body.firstChild.clientHeight);
191
-                document.title = height;
210
+        var width = ${screenWidth};
211
+        function updateSize() {
212
+            var rect = document.body.firstElementChild.getBoundingClientRect().toJSON();
213
+            var newWidth = Math.round(rect.width);
214
+            var newHeight = Math.round(rect.height);
215
+            if(newHeight !== height) {
216
+                //height = getHeight(document.body.firstChild.clientHeight);
217
+                //width = getWidth(document.body.firstChild.clientHeight);
218
+                document.title = newWidth + ',' + newHeight;
192
                 window.location.hash = ++i;
219
                 window.location.hash = ++i;
193
             }
220
             }
194
         }
221
         }