forked from obipawan/react-native-hyperlink
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Hyperlink.js
105 lines (92 loc) · 2.78 KB
/
Hyperlink.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
* @providesModule Hyperlink
**/
import React, { Component, PropTypes } from 'react'
import { View, Text } from 'react-native'
class Hyperlink extends Component {
constructor(props){
super(props)
this.linkify = this.linkify.bind(this)
this.parse = this.parse.bind(this)
this.linkifyIt = props.linkify || require('linkify-it')()
}
render(){
return <View
{ ...this.props }
style={ this.props.style }
>
{ !this.props.onPress && !this.props.linkStyle ? this.props.children : this.parse(this).props.children }
</View>
}
isTextNested(component){
if (!React.isValidElement(component))
throw 'Invalid component'
let { type : { displayName } = {} } = component
if (displayName !== 'Text')
throw 'Not a Text component'
return typeof component.props.children !== 'string'
}
linkify(component){
if (!this.linkifyIt.pretest(component.props.children) || !this.linkifyIt.test(component.props.children)) {
return component
}
let elements = []
let _lastIndex = 0
const componentProps = {
...component.props,
ref: undefined,
key: undefined,
}
try {
this.linkifyIt.match(component.props.children).forEach(({ index, lastIndex, text, url }) => {
let nonLinkedText = component.props.children.substring(_lastIndex, index)
nonLinkedText && elements.push(nonLinkedText)
_lastIndex = lastIndex
if (this.props.linkText)
text = typeof this.props.linkText === 'function' ? this.props.linkText(url) : this.props.linkText
elements.push(
<Text
{ ...componentProps }
key={ url }
style={ [ component.props.style, this.props.linkStyle ] }
onPress={ () => this.props.onPress && this.props.onPress(url) }
>
{ text }
</Text>
)
})
elements.push(component.props.children.substring(_lastIndex, component.props.children.length))
return React.cloneElement(component, componentProps, elements)
} catch (err) {
return component
}
}
parse(component){
let { props: { children} = {}, type: { displayName } = {} } = component
if (!children)
return component
const componentProps = {
...component.props,
ref: undefined,
key: undefined,
}
return React.cloneElement(component, componentProps, React.Children.map(children, child => {
let { type : { displayName } = {} } = child
if (typeof child === 'string' && this.linkifyIt.pretest(child))
return this.linkify(<Text { ...componentProps } style={component.props.style}>{ child }</Text>)
if (displayName === 'Text' && !this.isTextNested(child))
return this.linkify(child)
return this.parse(child)
}))
}
}
Hyperlink.propTypes = {
linkify: PropTypes.object,
linkStyle: Text.propTypes.style,
linkText: PropTypes.oneOfType([
PropTypes.string,
PropTypes.func,
]),
onPress: React.PropTypes.func,
}
module.exports = Hyperlink