| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 | 'use strict'
import React, { PureComponent } from 'react';
import {
    Animated,
    Dimensions,
    StyleSheet,
    View,
    ViewPropTypes,
    WebView
} from 'react-native';
import PropTypes from 'prop-types';
export default class AutoHeightWebView extends PureComponent {
    constructor(props) {
        super(props);
        this.handleNavigationStateChange = this.handleNavigationStateChange.bind(this);
        if (this.props.enableAnimation) {
            this.opacityAnimatedValue = new Animated.Value(0);
        }
        const initialScript = props.files ? this.appendFilesToHead(props.files, BaseScript) : BaseScript;
        this.state = {
            height: 0,
            script: initialScript
        };
    }
    componentWillReceiveProps(nextProps) {
        let currentScript = BaseScript;
        if (nextProps.files) {
            currentScript = this.appendFilesToHead(nextProps.files, BaseScript);
        }
        this.setState({ script: currentScript });
    }
    appendFilesToHead(files, script) {
        if (!files) {
            return script;
        }
        for (let file of files) {
            script =
                `
                var link  = document.createElement('link');
                link.rel  = '` + file.rel + `';
                link.type = '` + file.type + `';
                link.href = '` + file.href + `';
                document.head.appendChild(link);
                `+ script;
        }
        return script;
    }
    onHeightUpdated(height) {
        if (this.props.onHeightUpdated) {
            this.props.onHeightUpdated(height);
        }
    }
    handleNavigationStateChange(navState) {
        const height = Number(navState.title);
        if (height) {
            if (this.props.enableAnimation) {
                this.opacityAnimatedValue.setValue(0);
            }
            this.setState({ height }, () => {
                if (this.props.enableAnimation) {
                    Animated.timing(this.opacityAnimatedValue, {
                        toValue: 1,
                        duration: this.props.animationDuration
                    }).start(() => this.onHeightUpdated(height));
                }
                else {
                    this.onHeightUpdated(height);
                }
            });
        }
    }
    render() {
        const { height, script } = this.state;
        const { enableAnimation, source, heightOffset, customScript, style } = this.props;
        const webViewSource = Object.assign({}, source, { baseUrl: 'web/' });
        return (
            <Animated.View style={[Styles.container, {
                opacity: enableAnimation ? this.opacityAnimatedValue : 1,
                height: height + heightOffset,
            }, style]}>
                <WebView
                    style={Styles.webView}
                    injectedJavaScript={script + customScript}
                    scrollEnabled={false}
                    source={webViewSource}
                    onNavigationStateChange={this.handleNavigationStateChange} />
            </Animated.View>
        );
    }
}
AutoHeightWebView.propTypes = {
    source: WebView.propTypes.source,
    onHeightUpdated: PropTypes.func,
    customScript: PropTypes.string,
    enableAnimation: PropTypes.bool,
    // only works on enable animation
    animationDuration: PropTypes.number,
    // offset of rn webview margin
    heightOffset: PropTypes.number,
    style: ViewPropTypes.style,
    // add web/files... to project root
    files: PropTypes.arrayOf(PropTypes.shape({
        href: PropTypes.string,
        type: PropTypes.string,
        rel: PropTypes.string
    }))
}
AutoHeightWebView.defaultProps = {
    enableAnimation: true,
    animationDuration: 555,
    heightOffset: 12
}
const ScreenWidth = Dimensions.get('window').width;
const Styles = StyleSheet.create({
    container: {
        width: ScreenWidth,
        backgroundColor: 'transparent'
    },
    webView: {
        flex: 1,
        backgroundColor: 'transparent'
    }
});
// note that it can not get height when there are only text objects in a html body which does not make any sense 
const BaseScript =
    `
    ; (function () {
        var i = 0;
        function updateHeight() {
            document.title = document.body.firstChild.clientHeight;
            window.location.hash = ++i;
        }
        updateHeight();
        window.addEventListener('load', updateHeight);
        window.addEventListener('resize', updateHeight);
    } ());
    `;
 |