Ei kuvausta

index.ios.js 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. 'use strict'
  2. import React, { PureComponent } from 'react';
  3. import {
  4. Animated,
  5. Dimensions,
  6. StyleSheet,
  7. View,
  8. ViewPropTypes,
  9. WebView
  10. } from 'react-native';
  11. import PropTypes from 'prop-types';
  12. export default class AutoHeightWebView extends PureComponent {
  13. constructor(props) {
  14. super(props);
  15. this.handleNavigationStateChange = this.handleNavigationStateChange.bind(this);
  16. if (this.props.enableAnimation) {
  17. this.opacityAnimatedValue = new Animated.Value(0);
  18. }
  19. const initialScript = props.files ? this.appendFilesToHead(props.files, BaseScript) : BaseScript;
  20. this.state = {
  21. height: 0,
  22. script: initialScript
  23. };
  24. }
  25. componentWillReceiveProps(nextProps) {
  26. let currentScript = BaseScript;
  27. if (nextProps.files) {
  28. currentScript = this.appendFilesToHead(nextProps.files, BaseScript);
  29. }
  30. this.setState({ script: currentScript });
  31. }
  32. appendFilesToHead(files, script) {
  33. if (!files) {
  34. return script;
  35. }
  36. for (let file of files) {
  37. script =
  38. `
  39. var link = document.createElement('link');
  40. link.rel = '` + file.rel + `';
  41. link.type = '` + file.type + `';
  42. link.href = '` + file.href + `';
  43. document.head.appendChild(link);
  44. `+ script;
  45. }
  46. return script;
  47. }
  48. onHeightUpdated(height) {
  49. if (this.props.onHeightUpdated) {
  50. this.props.onHeightUpdated(height);
  51. }
  52. }
  53. handleNavigationStateChange(navState) {
  54. const height = Number(navState.title);
  55. if (height) {
  56. if (this.props.enableAnimation) {
  57. this.opacityAnimatedValue.setValue(0);
  58. }
  59. this.setState({ height }, () => {
  60. if (this.props.enableAnimation) {
  61. Animated.timing(this.opacityAnimatedValue, {
  62. toValue: 1,
  63. duration: this.props.animationDuration
  64. }).start(() => this.onHeightUpdated(height));
  65. }
  66. else {
  67. this.onHeightUpdated(height);
  68. }
  69. });
  70. }
  71. }
  72. render() {
  73. const { height, script } = this.state;
  74. const { enableAnimation, source, heightOffset, customScript, style } = this.props;
  75. const webViewSource = Object.assign({}, source, { baseUrl: 'web/' });
  76. return (
  77. <Animated.View style={[Styles.container, {
  78. opacity: enableAnimation ? this.opacityAnimatedValue : 1,
  79. height: height + heightOffset,
  80. }, style]}>
  81. <WebView
  82. style={Styles.webView}
  83. injectedJavaScript={script + customScript}
  84. scrollEnabled={false}
  85. source={webViewSource}
  86. onNavigationStateChange={this.handleNavigationStateChange} />
  87. </Animated.View>
  88. );
  89. }
  90. }
  91. AutoHeightWebView.propTypes = {
  92. source: WebView.propTypes.source,
  93. onHeightUpdated: PropTypes.func,
  94. customScript: PropTypes.string,
  95. enableAnimation: PropTypes.bool,
  96. // only works on enable animation
  97. animationDuration: PropTypes.number,
  98. // offset of rn webview margin
  99. heightOffset: PropTypes.number,
  100. style: ViewPropTypes.style,
  101. // add web/files... to project root
  102. files: PropTypes.arrayOf(PropTypes.shape({
  103. href: PropTypes.string,
  104. type: PropTypes.string,
  105. rel: PropTypes.string
  106. }))
  107. }
  108. AutoHeightWebView.defaultProps = {
  109. enableAnimation: true,
  110. animationDuration: 555,
  111. heightOffset: 12
  112. }
  113. const ScreenWidth = Dimensions.get('window').width;
  114. const Styles = StyleSheet.create({
  115. container: {
  116. width: ScreenWidth,
  117. backgroundColor: 'transparent'
  118. },
  119. webView: {
  120. flex: 1,
  121. backgroundColor: 'transparent'
  122. }
  123. });
  124. // note that it can not get height when there are only text objects in a html body which does not make any sense
  125. const BaseScript =
  126. `
  127. ; (function () {
  128. var i = 0;
  129. function updateHeight() {
  130. document.title = document.body.firstChild.clientHeight;
  131. window.location.hash = ++i;
  132. }
  133. updateHeight();
  134. window.addEventListener('load', updateHeight);
  135. window.addEventListener('resize', updateHeight);
  136. } ());
  137. `;