No Description

index.ios.js 4.5KB

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