2017-01-02 06:22:07 +00:00
|
|
|
import { resolve } from 'url'
|
2016-12-29 12:53:20 +00:00
|
|
|
import React, { Component, Children, PropTypes } from 'react'
|
2016-12-19 14:40:26 +00:00
|
|
|
import Router from './router'
|
2017-01-16 21:02:11 +00:00
|
|
|
import { warn, execOnce } from './utils'
|
2016-10-06 07:07:41 +00:00
|
|
|
|
|
|
|
export default class Link extends Component {
|
|
|
|
constructor (props) {
|
|
|
|
super(props)
|
|
|
|
this.linkClicked = this.linkClicked.bind(this)
|
|
|
|
}
|
|
|
|
|
2016-12-29 12:53:20 +00:00
|
|
|
static propTypes = {
|
|
|
|
children: PropTypes.oneOfType([
|
|
|
|
PropTypes.string,
|
|
|
|
PropTypes.element
|
|
|
|
]).isRequired
|
|
|
|
}
|
|
|
|
|
2016-10-06 07:07:41 +00:00
|
|
|
linkClicked (e) {
|
2016-10-17 00:00:17 +00:00
|
|
|
if (e.target.nodeName === 'A' &&
|
2016-12-15 04:33:02 +00:00
|
|
|
(e.metaKey || e.ctrlKey || e.shiftKey || (e.nativeEvent && e.nativeEvent.which === 2))) {
|
2016-10-06 07:07:41 +00:00
|
|
|
// ignore click for new tab / new window behavior
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-01-02 06:22:07 +00:00
|
|
|
let { href, as } = this.props
|
2016-10-06 07:07:41 +00:00
|
|
|
|
|
|
|
if (!isLocal(href)) {
|
|
|
|
// ignore click if it's outside our scope
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-01-02 06:22:07 +00:00
|
|
|
const { pathname } = window.location
|
|
|
|
href = resolve(pathname, href)
|
|
|
|
as = as ? resolve(pathname, as) : href
|
|
|
|
|
2016-10-06 07:07:41 +00:00
|
|
|
e.preventDefault()
|
|
|
|
|
2016-12-22 08:15:49 +00:00
|
|
|
// avoid scroll for urls with anchor refs
|
|
|
|
let { scroll } = this.props
|
|
|
|
if (scroll == null) {
|
2016-12-28 05:27:52 +00:00
|
|
|
scroll = as.indexOf('#') < 0
|
2016-12-22 08:15:49 +00:00
|
|
|
}
|
|
|
|
|
2016-10-06 07:07:41 +00:00
|
|
|
// straight up redirect
|
2016-12-28 05:27:52 +00:00
|
|
|
Router.push(href, as)
|
2016-12-19 14:40:26 +00:00
|
|
|
.then((success) => {
|
|
|
|
if (!success) return
|
2016-12-22 08:17:50 +00:00
|
|
|
if (scroll) window.scrollTo(0, 0)
|
2016-12-19 14:40:26 +00:00
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
if (this.props.onError) this.props.onError(err)
|
|
|
|
})
|
2016-10-06 07:07:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
|
|
|
const children = Children.map(this.props.children, (child) => {
|
|
|
|
const props = {
|
|
|
|
onClick: this.linkClicked
|
|
|
|
}
|
|
|
|
|
2016-10-17 00:00:17 +00:00
|
|
|
const isAnchor = child && child.type === 'a'
|
2016-10-06 07:07:41 +00:00
|
|
|
|
|
|
|
// if child does not specify a href, specify it
|
|
|
|
// so that repetition is not needed by the user
|
2016-10-08 05:12:51 +00:00
|
|
|
if (!isAnchor || !('href' in child.props)) {
|
2016-12-16 20:33:08 +00:00
|
|
|
props.href = this.props.as || this.props.href
|
2016-10-06 07:07:41 +00:00
|
|
|
}
|
|
|
|
|
2016-10-08 05:12:51 +00:00
|
|
|
if (isAnchor) {
|
2016-10-06 07:07:41 +00:00
|
|
|
return React.cloneElement(child, props)
|
|
|
|
} else {
|
2017-01-16 21:02:11 +00:00
|
|
|
warnLink(`Warning: Every Link must be the parent of an anchor, this pattern is deprecated. Please add an anchor inside the <Link>.`)
|
2016-10-06 07:07:41 +00:00
|
|
|
return <a {...props}>{child}</a>
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return children[0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-15 19:13:40 +00:00
|
|
|
export function isLocal (href) {
|
2016-10-17 00:00:17 +00:00
|
|
|
const origin = window.location.origin
|
2017-01-02 06:22:07 +00:00
|
|
|
return !/^(https?:)?\/\//.test(href) ||
|
2016-10-06 07:07:41 +00:00
|
|
|
origin === href.substr(0, origin.length)
|
|
|
|
}
|
2017-01-16 21:02:11 +00:00
|
|
|
|
|
|
|
const warnLink = execOnce(warn)
|