UNPKG

flux-router-component

Version:

Router-related React component and mixin for applications with Fluxible architecture

133 lines (115 loc) 4.24 kB
/** * Copyright 2014, Yahoo! Inc. * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ /*global window */ 'use strict'; var React = require('react'); var NavLink; var navigateAction = require('../actions/navigate'); var debug = require('debug')('NavLink'); var objectAssign = require('object-assign'); function isLeftClickEvent (e) { return e.button === 0; } function isModifiedEvent (e) { return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey); } NavLink = React.createClass({ displayName: 'NavLink', contextTypes: { executeAction: React.PropTypes.func, makePath: React.PropTypes.func }, propTypes: { context: React.PropTypes.object, href: React.PropTypes.string, routeName: React.PropTypes.string, navParams: React.PropTypes.object, followLink: React.PropTypes.bool, preserveScrollPosition: React.PropTypes.bool, replaceState: React.PropTypes.bool }, getInitialState: function () { return { href: this._getHrefFromProps(this.props) }; }, componentWillReceiveProps: function (nextProps) { this.setState({ href: this._getHrefFromProps(nextProps) }); }, _getHrefFromProps: function (props) { var href = props.href; var routeName = props.routeName; if (!href && routeName) { var context = props.context || this.context; href = context.makePath(routeName, props.navParams); } if (!href) { throw new Error('NavLink created without href or unresolvable routeName \'' + routeName + '\''); } return href; }, dispatchNavAction: function (e) { var navType = this.props.replaceState ? 'replacestate' : 'click'; debug('dispatchNavAction: action=NAVIGATE', this.props.href, this.props.followLink, this.props.navParams); if (this.props.followLink) { return; } if (isModifiedEvent(e) || !isLeftClickEvent(e)) { // this is a click with a modifier or not a left-click // let browser handle it natively return; } var href = this.state.href; if (href[0] === '#') { // this is a hash link url for page's internal links. // Do not trigger navigate action. Let browser handle it natively. return; } if (href[0] !== '/') { // this is not a relative url. check for external urls. var location = window.location; var origin = location.origin || (location.protocol + '//' + location.host); if (href.indexOf(origin) !== 0) { // this is an external url, do not trigger navigate action. // let browser handle it natively. return; } href = href.substring(origin.length) || '/'; } var context = this.props.context || this.context; if (!context || !context.executeAction) { console.warn('NavLink does not have access to executeAction. Link using browser default.'); return; } e.preventDefault(); e.stopPropagation(); var onBeforeUnloadText = typeof window.onbeforeunload === 'function' ? window.onbeforeunload() : ''; var confirmResult = onBeforeUnloadText ? window.confirm(onBeforeUnloadText) : true; if (confirmResult) { // Removes the window.onbeforeunload method so that the next page will not be affected window.onbeforeunload = null; context.executeAction(navigateAction, { type: navType, url: href, preserveScrollPosition: this.props.preserveScrollPosition, params: this.props.navParams }); } }, render: function() { return React.createElement( 'a', objectAssign({}, { onClick: this.dispatchNavAction }, this.props, { href: this.state.href }), this.props.children ); } }); module.exports = NavLink;