暫無描述

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. 'use strict';
  2. import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
  3. import { StyleSheet, Platform, ViewPropTypes } from 'react-native';
  4. import PropTypes from 'prop-types';
  5. import { WebView } from 'react-native-webview';
  6. import { reduceData, getWidth, isSizeChanged, shouldUpdate } from './utils';
  7. const AutoHeightWebView = React.memo(
  8. forwardRef((props, ref) => {
  9. const { style, onMessage, onSizeUpdated, scrollEnabledWithZoomedin, scrollEnabled, source } = props;
  10. if (!source) {
  11. return null;
  12. }
  13. let webView = useRef();
  14. useImperativeHandle(ref, () => ({
  15. stopLoading: () => webView.current.stopLoading(),
  16. goForward: () => webView.current.goForward(),
  17. goBack: () => webView.current.goBack(),
  18. reload: () => webView.current.reload(),
  19. injectJavaScript: script => webView.current.injectJavaScript(script)
  20. }));
  21. const [size, setSize] = useState({
  22. height: style && style.height ? style.height : 0,
  23. width: getWidth(style)
  24. });
  25. const [scrollable, setScrollable] = useState(false);
  26. const handleMessage = event => {
  27. onMessage && onMessage(event);
  28. if (!event.nativeEvent) {
  29. return;
  30. }
  31. let data = {};
  32. // Sometimes the message is invalid JSON, so we ignore that case
  33. try {
  34. data = JSON.parse(event.nativeEvent.data);
  35. } catch (error) {
  36. console.error(error);
  37. return;
  38. }
  39. const { height, width, zoomin } = data;
  40. !scrollEnabled && scrollEnabledWithZoomedin && setScrollable(zoomin);
  41. const { height: previousHeight, width: previousWidth } = size;
  42. isSizeChanged({ height, previousHeight, width, previousWidth }) &&
  43. setSize({
  44. height,
  45. width
  46. });
  47. };
  48. const currentScrollEnabled = scrollEnabled === false && scrollEnabledWithZoomedin ? scrollable : scrollEnabled;
  49. const { currentSource, script } = reduceData(props);
  50. const { width, height } = size;
  51. useEffect(
  52. () =>
  53. onSizeUpdated &&
  54. onSizeUpdated({
  55. height,
  56. width
  57. }),
  58. [width, height, onSizeUpdated]
  59. );
  60. return (
  61. <WebView
  62. {...props}
  63. ref={webView}
  64. onMessage={handleMessage}
  65. style={[
  66. styles.webView,
  67. {
  68. width,
  69. height
  70. },
  71. style
  72. ]}
  73. injectedJavaScript={script}
  74. source={currentSource}
  75. scrollEnabled={currentScrollEnabled}
  76. />
  77. );
  78. }),
  79. (prevProps, nextProps) => !shouldUpdate({ prevProps, nextProps })
  80. );
  81. AutoHeightWebView.propTypes = {
  82. onSizeUpdated: PropTypes.func,
  83. files: PropTypes.arrayOf(
  84. PropTypes.shape({
  85. href: PropTypes.string,
  86. type: PropTypes.string,
  87. rel: PropTypes.string
  88. })
  89. ),
  90. style: ViewPropTypes.style,
  91. customScript: PropTypes.string,
  92. customStyle: PropTypes.string,
  93. zoomable: PropTypes.bool,
  94. scrollEnabledWithZoomedin: PropTypes.bool,
  95. // webview props
  96. originWhitelist: PropTypes.arrayOf(PropTypes.string),
  97. onMessage: PropTypes.func,
  98. scalesPageToFit: PropTypes.bool,
  99. source: PropTypes.object
  100. };
  101. let defaultProps = {
  102. showsVerticalScrollIndicator: false,
  103. showsHorizontalScrollIndicator: false,
  104. originWhitelist: ['*'],
  105. zoomable: true
  106. };
  107. Platform.OS === 'android' &&
  108. Object.assign(defaultProps, {
  109. scalesPageToFit: false
  110. });
  111. AutoHeightWebView.defaultProps = defaultProps;
  112. const styles = StyleSheet.create({
  113. webView: {
  114. backgroundColor: 'transparent'
  115. }
  116. });
  117. export default AutoHeightWebView;