Skip to content

Commit 869dfcd

Browse files
committed
initial commit
0 parents  commit 869dfcd

File tree

7 files changed

+8060
-0
lines changed

7 files changed

+8060
-0
lines changed

.gitignore

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Ignore docs files
2+
_gh_pages
3+
_site
4+
.ruby-version
5+
6+
# Numerous always-ignore extensions
7+
*.diff
8+
*.err
9+
*.orig
10+
*.log
11+
*.rej
12+
*.swo
13+
*.swp
14+
*.zip
15+
*.vi
16+
*~
17+
18+
# OS or Editor folders
19+
.DS_Store
20+
._*
21+
Thumbs.db
22+
.cache
23+
.project
24+
.settings
25+
.tmproj
26+
*.esproj
27+
nbproject
28+
*.sublime-project
29+
*.sublime-workspace
30+
.idea
31+
32+
# Komodo
33+
*.komodoproject
34+
.komodotools
35+
36+
# Folders to ignore
37+
node_modules
38+
bower_components
39+
40+
lib/
41+
example/output/
42+
43+
main.jsbundle
44+
45+
images/generated/

HTMLView.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
var htmlparser = require('./vendor/htmlparser2')
2+
var entities = require('./vendor/entities')
3+
var React = require('react-native')
4+
var {
5+
LinkingIOS,
6+
StyleSheet,
7+
Text,
8+
} = React
9+
10+
var LINE_BREAK = '\n'
11+
var PARAGRAPH_BREAK = '\n\n'
12+
13+
function htmlToElement(rawHtml, opts, done) {
14+
function domToElement(dom, parent) {
15+
if (!dom) return null
16+
17+
return dom.map((node, index, list) => {
18+
if (opts.customRenderer) {
19+
var rendered = opts.customRenderer(node, index, list)
20+
if (rendered || rendered === null) return rendered
21+
}
22+
23+
if (node.type == 'text') {
24+
return (
25+
<Text key={index} style={parent ? opts.styles[parent.name] : null}>
26+
{entities.decodeHTML(node.data)}
27+
</Text>
28+
)
29+
}
30+
31+
if (node.type == 'tag') {
32+
var linkPressHandler = null
33+
if (node.name == 'a' && node.attribs && node.attribs.href) {
34+
linkPressHandler = () => opts.linkHandler(entities.decodeHTML(node.attribs.href))
35+
}
36+
37+
return (
38+
<Text key={index} onPress={linkPressHandler}>
39+
{node.name == 'pre' ? LINE_BREAK : null}
40+
{domToElement(node.children, node)}
41+
{node.name == 'br' ? LINE_BREAK : null}
42+
{node.name == 'p' && index < list.length-1 ? PARAGRAPH_BREAK : null}
43+
</Text>
44+
)
45+
}
46+
})
47+
}
48+
49+
var handler = new htmlparser.DomHandler(function (err, dom) {
50+
if (err) done(err)
51+
done(null, domToElement(dom))
52+
})
53+
var parser = new htmlparser.Parser(handler)
54+
parser.write(rawHtml)
55+
parser.done()
56+
}
57+
58+
var HTMLView = React.createClass({
59+
mixins: [
60+
React.addons.PureRenderMixin,
61+
],
62+
getDefaultProps() {
63+
return {
64+
onLinkPress: LinkingIOS.openURL,
65+
}
66+
},
67+
getInitialState() {
68+
return {
69+
element: null,
70+
}
71+
},
72+
componentWillReceiveProps() {
73+
if (this.state.element) return
74+
this.startHtmlRender()
75+
},
76+
componentDidMount() {
77+
this.startHtmlRender()
78+
},
79+
startHtmlRender() {
80+
if (!this.props.value) return
81+
if (this.renderingHtml) return
82+
83+
var opts = {
84+
linkHandler: this.props.onLinkPress,
85+
styles: Object.assign({}, baseStyles, this.props.stylesheet),
86+
customRenderer: this.props.renderNode,
87+
}
88+
89+
this.renderingHtml = true
90+
htmlToElement(this.props.value, opts, (err, element) => {
91+
this.renderingHtml = false
92+
93+
if (err) return (this.props.onError || console.error)(err)
94+
95+
if (this.isMounted()) this.setState({element})
96+
})
97+
},
98+
render() {
99+
if (this.state.element) {
100+
return <Text children={this.state.element} />
101+
}
102+
return <Text />
103+
}
104+
})
105+
106+
var boldStyle = {fontWeight: '500'}
107+
var italicStyle = {fontStyle: 'italic'}
108+
var codeStyle = {fontFamily: 'Menlo'}
109+
110+
var baseStyles = StyleSheet.create({
111+
b: boldStyle,
112+
strong: boldStyle,
113+
i: italicStyle,
114+
em: italicStyle,
115+
pre: codeStyle,
116+
code: codeStyle,
117+
a: {
118+
fontWeight: '500',
119+
color: '#007AFF',
120+
},
121+
})
122+
123+
module.exports = HTMLView

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# React Native HTMLView
2+
A component which takes HTML content and renders it as native views, with
3+
customisable style and handling of links, etc.
4+
5+
### usage
6+
7+
props:
8+
9+
- `value`: a string of HTML content to render
10+
- `onLinkPress`: a function which will be called with a url when a link is pressed.
11+
Passing this prop will override how links are handled (defaults to calling LinkingIOS.openURL(url))
12+
- `stylesheet`: a stylesheet object keyed by tag name, which will override the
13+
styles applied to those respective tags.
14+
- `renderNode`: a custom function to render HTML nodes however you see fit. If
15+
the function returns `undefined` (not `null`), the default renderer will be
16+
used for that node.
17+
18+
### example
19+
20+
```js
21+
var React = require('react-native')
22+
var {Text, View, ListView} = React
23+
24+
var HTMLView = require('react-native-htmlview')
25+
26+
var ContentView = React.createClass({
27+
render() {
28+
return (
29+
var htmlContent = '<p><a href="">&hearts; nice job!</a></p>'
30+
<HTMLView
31+
value={htmlContent}
32+
onLinkPress={(url) => console.log('navigating to: ', url)}
33+
stylesheet={styles}
34+
/>
35+
)
36+
}
37+
})
38+
```
39+
40+
var styles = StyleSheet.create({
41+
a: {
42+
fontWeight: '300',
43+
color: '#FF3366', // pink links
44+
},
45+
})
46+
47+
### screenshot
48+
49+
In action (from [ReactNativeHackerNews](https://github.com/jsdf/ReactNativeHackerNews)):
50+
51+
![React Native Hacker News Comments](http://i.imgur.com/FYOgBYc.png)
52+
53+
54+
![Under Construction](https://jamesfriend.com.au/files/under-construction.gif)
55+
56+
I just wrote this... use at your own risk. Not API stable.

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./HTMLView')

package.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "react-native-htmlview",
3+
"version": "0.1.1",
4+
"description": "A component which renders HTML content as native views",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"author": "James Friend <[email protected]> (http://jsdf.co/)",
10+
"license": "ISC",
11+
"keywords": [
12+
"react",
13+
"html",
14+
"react-native",
15+
"react-component",
16+
"react-native-component",
17+
"mobile",
18+
"ui"
19+
],
20+
"files": [
21+
"index.js",
22+
"HTMLView.js",
23+
"vendor/"
24+
],
25+
"homepage": "https://github.com/jsdf/react-native-htmlview",
26+
"bugs": "https://github.com/jsdf/react-native-htmlview/issues",
27+
"repository": {
28+
"type": "git",
29+
"url": "git://github.com/jsdf/react-native-htmlview.git"
30+
}
31+
}

0 commit comments

Comments
 (0)