-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
/
Link.js
139 lines (111 loc) · 3.5 KB
/
Link.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import React from 'react'
import warning from './routerWarning'
const { bool, object, string, func, oneOfType } = React.PropTypes
function isLeftClickEvent(event) {
return event.button === 0
}
function isModifiedEvent(event) {
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)
}
// TODO: De-duplicate against hasAnyProperties in createTransitionManager.
function isEmptyObject(object) {
for (const p in object)
if (Object.prototype.hasOwnProperty.call(object, p))
return false
return true
}
function createLocationDescriptor(to, { query, hash, state }) {
if (query || hash || state) {
return { pathname: to, query, hash, state }
}
return to
}
/**
* A <Link> is used to create an <a> element that links to a route.
* When that route is active, the link gets the value of its
* activeClassName prop.
*
* For example, assuming you have the following route:
*
* <Route path="/posts/:postID" component={Post} />
*
* You could use the following component to link to that route:
*
* <Link to={`/posts/${post.id}`} />
*
* Links may pass along location state and/or query string parameters
* in the state/query props, respectively.
*
* <Link ... query={{ show: true }} state={{ the: 'state' }} />
*/
const Link = React.createClass({
contextTypes: {
router: object
},
propTypes: {
to: oneOfType([ string, object ]).isRequired,
query: object,
hash: string,
state: object,
activeStyle: object,
activeClassName: string,
onlyActiveOnIndex: bool.isRequired,
onClick: func
},
getDefaultProps() {
return {
onlyActiveOnIndex: false,
style: {}
}
},
handleClick(event) {
let allowTransition = true
if (this.props.onClick)
this.props.onClick(event)
if (isModifiedEvent(event) || !isLeftClickEvent(event))
return
if (event.defaultPrevented === true)
allowTransition = false
// If target prop is set (e.g. to "_blank") let browser handle link.
/* istanbul ignore if: untestable with Karma */
if (this.props.target) {
if (!allowTransition)
event.preventDefault()
return
}
event.preventDefault()
if (allowTransition) {
const { to, query, hash, state } = this.props
const location = createLocationDescriptor(to, { query, hash, state })
this.context.router.push(location)
}
},
render() {
const { to, query, hash, state, activeClassName, activeStyle, onlyActiveOnIndex, ...props } = this.props
warning(
!(query || hash || state),
'the `query`, `hash`, and `state` props on `<Link>` are deprecated, use `<Link to={{ pathname, query, hash, state }}/>. http://tiny.cc/router-isActivedeprecated'
)
// Ignore if rendered outside the context of router, simplifies unit testing.
const { router } = this.context
if (router) {
const location = createLocationDescriptor(to, { query, hash, state })
props.href = router.createHref(location)
if (activeClassName || (activeStyle != null && !isEmptyObject(activeStyle))) {
if (router.isActive(location, onlyActiveOnIndex)) {
if (activeClassName) {
if (props.className) {
props.className += ` ${activeClassName}`
} else {
props.className = activeClassName
}
}
if (activeStyle)
props.style = { ...props.style, ...activeStyle }
}
}
}
return <a {...props} onClick={this.handleClick} />
}
})
export default Link