UNPKG

react-native-htmlview

Version:

A component which renders HTML content as native views

169 lines (148 loc) 4.23 kB
import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import htmlToElement from './htmlToElement'; import {Linking, Platform, StyleSheet, View} from 'react-native'; import {ViewPropTypes} from 'deprecated-react-native-prop-types'; const boldStyle = {fontWeight: 'bold'}; const italicStyle = {fontStyle: 'italic'}; const underlineStyle = {textDecorationLine: 'underline'}; const strikethroughStyle = {textDecorationLine: 'line-through'}; const codeStyle = {fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace'}; const baseStyles = StyleSheet.create({ b: boldStyle, strong: boldStyle, i: italicStyle, em: italicStyle, u: underlineStyle, s: strikethroughStyle, strike: strikethroughStyle, pre: codeStyle, code: codeStyle, a: { fontWeight: 'bold', color: '#007AFF', }, h1: {fontWeight: 'bold', fontSize: 36}, h2: {fontWeight: 'bold', fontSize: 30}, h3: {fontWeight: 'bold', fontSize: 24}, h4: {fontWeight: 'bold', fontSize: 18}, h5: {fontWeight: 'bold', fontSize: 14}, h6: {fontWeight: 'bold', fontSize: 12}, }); const htmlToElementOptKeys = [ 'lineBreak', 'paragraphBreak', 'bullet', 'TextComponent', 'textComponentProps', 'NodeComponent', 'nodeComponentProps', ]; class HtmlView extends PureComponent { constructor() { super(); this.state = { element: null, }; } componentDidMount() { this.mounted = true; this.startHtmlRender(this.props.value); } componentDidUpdate(prevProps) { if ( this.props.value !== prevProps.value || this.props.stylesheet !== prevProps.stylesheet || this.props.textComponentProps !== prevProps.textComponentProps || this.props.nodeComponentProps !== prevProps.nodeComponentProps ) { this.startHtmlRender( this.props.value, this.props.stylesheet, this.props.textComponentProps, this.props.nodeComponentProps ); } } componentWillUnmount() { this.mounted = false; } startHtmlRender(value, style, textComponentProps, nodeComponentProps) { const { addLineBreaks, onLinkPress, onLinkLongPress, stylesheet, renderNode, onError, } = this.props; if (!value) { this.setState({element: null}); } const opts = { addLineBreaks, linkHandler: onLinkPress, linkLongPressHandler: onLinkLongPress, styles: {...baseStyles, ...stylesheet, ...style}, customRenderer: renderNode, }; htmlToElementOptKeys.forEach(key => { if (typeof this.props[key] !== 'undefined') { opts[key] = this.props[key]; } }); if (textComponentProps) { opts.textComponentProps = textComponentProps; } if (nodeComponentProps) { opts.nodeComponentProps = nodeComponentProps; } htmlToElement(value, opts, (err, element) => { if (err) { onError(err); } if (this.mounted) { this.setState({element}); } }); } render() { const {RootComponent, style} = this.props; const {element} = this.state; if (element) { return ( <RootComponent {...this.props.rootComponentProps} style={style}> {element} </RootComponent> ); } return <RootComponent {...this.props.rootComponentProps} style={style} />; } } HtmlView.propTypes = { addLineBreaks: PropTypes.bool, bullet: PropTypes.string, lineBreak: PropTypes.string, NodeComponent: PropTypes.func, nodeComponentProps: PropTypes.object, onError: PropTypes.func, onLinkPress: PropTypes.func, onLinkLongPress: PropTypes.func, paragraphBreak: PropTypes.string, renderNode: PropTypes.func, RootComponent: PropTypes.func, rootComponentProps: PropTypes.object, style: ViewPropTypes.style, stylesheet: PropTypes.object, TextComponent: PropTypes.func, textComponentProps: PropTypes.object, value: PropTypes.string, }; HtmlView.defaultProps = { addLineBreaks: true, onLinkPress: url => Linking.openURL(url), onLinkLongPress: null, onError: console.error.bind(console), RootComponent: element => <View {...element} />, // eslint-disable-line react/display-name }; export default HtmlView;