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