diff --git a/lib/NavLink.js b/lib/NavLink.js index 160f66a..92d821c 100644 --- a/lib/NavLink.js +++ b/lib/NavLink.js @@ -6,28 +6,7 @@ 'use strict'; var React = require('react'); -var navLinkUtils = require('./navLinkUtils'); +var createNavLink = require('./createNavLink'); var objectAssign = require('object-assign'); -var NavLink = React.createClass({ - displayName: 'NavLink', - contextTypes: navLinkUtils.contextTypes, - propTypes: navLinkUtils.propTypes, - render: function () { - var href = navLinkUtils.getHrefFromProps.apply(this); - var toggledStyles = navLinkUtils.getToggledStyles.apply(this, [href]); - return React.createElement( - 'a', - objectAssign({}, { - onClick: navLinkUtils.dispatchNavAction.bind(this) - }, this.props, { - href: href, - className: toggledStyles.className, - style: toggledStyles.style - }), - this.props.children - ); - } -}); - -module.exports = NavLink; +module.exports = createNavLink(); diff --git a/lib/createNavLink.js b/lib/createNavLink.js new file mode 100644 index 0000000..319a492 --- /dev/null +++ b/lib/createNavLink.js @@ -0,0 +1,138 @@ +/** + * Copyright 2015, 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 RouteStore = require('./RouteStore'); +var debug = require('debug')('NavLink'); +var navigateAction = require('./navigateAction'); +var objectAssign = require('object-assign'); + +function isLeftClickEvent (e) { + return e.button === 0; +} + +function isModifiedEvent (e) { + return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey); +} + +/** + * create NavLink with custom options + * @param {Object} options to overwrite the default attributes to create NavLink + * @returns {React.Component} NavLink + */ +module.exports = function createNavLink (options) { + var NavLink = React.createClass(objectAssign({}, { + displayName: 'NavLink', + contextTypes: { + executeAction: React.PropTypes.func, + getStore: React.PropTypes.func + }, + propTypes: { + currentRoute: React.PropTypes.object, + currentNavigate: React.PropTypes.object, + href: React.PropTypes.string, + stopPropagation: React.PropTypes.bool, + routeName: React.PropTypes.string, + navParams: React.PropTypes.object, + followLink: React.PropTypes.bool, + preserveScrollPosition: React.PropTypes.bool, + replaceState: React.PropTypes.bool + }, + 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._getHrefFromProps(); + + 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) || '/'; + } + + e.preventDefault(); + if (this.props.stopPropagation) { + e.stopPropagation(); + } + + var context = this.props.context || this.context; + 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 + }); + } + }, + _getHrefFromProps: function () { + var href = this.props.href; + var routeName = this.props.routeName; + var routeStore = this.context.getStore(RouteStore); + if (!href && routeName) { + href = routeStore.makePath(routeName, this.props.navParams); + } + if (!href) { + throw new Error('NavLink created without href or unresolvable routeName \'' + routeName + '\''); + } + return href; + }, + render: function () { + var href = this._getHrefFromProps(); + var routeStore = this.context.getStore(RouteStore); + var isActive = routeStore.isActive(href); + var className = this.props.className; + var style = this.props.style; + if (isActive) { + className = className ? (className + ' ') : ''; + className += this.props.activeClass || 'active'; + style = objectAssign({}, style, this.props.activeStyle); + } + return React.createElement( + 'a', + objectAssign({}, { + onClick: this.dispatchNavAction + }, this.props, { + href: href, + className: className, + style: style + }), + this.props.children + ); + } + }, options)); + return NavLink; +}; diff --git a/lib/navLinkUtils.js b/lib/navLinkUtils.js deleted file mode 100644 index 31a625d..0000000 --- a/lib/navLinkUtils.js +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright 2015, 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 RouteStore = require('./RouteStore'); -var debug = require('debug')('NavLink'); -var navigateAction = require('./navigateAction'); -var objectAssign = require('object-assign'); - -function isLeftClickEvent (e) { - return e.button === 0; -} - -function isModifiedEvent (e) { - return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey); -} - -function getHrefFromProps () { - var href = this.props.href; - var routeName = this.props.routeName; - var routeStore = this.context.getStore(RouteStore); - if (!href && routeName) { - href = routeStore.makePath(routeName, this.props.navParams); - } - if (!href) { - throw new Error('NavLink created without href or unresolvable routeName \'' + routeName + '\''); - } - return href; -} - -module.exports = { - contextTypes: { - executeAction: React.PropTypes.func, - getStore: React.PropTypes.func - }, - propTypes: { - currentRoute: React.PropTypes.object, - currentNavigate: React.PropTypes.object, - href: React.PropTypes.string, - stopPropagation: React.PropTypes.bool, - routeName: React.PropTypes.string, - navParams: React.PropTypes.object, - followLink: React.PropTypes.bool, - preserveScrollPosition: React.PropTypes.bool, - replaceState: React.PropTypes.bool - }, - 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 = getHrefFromProps.apply(this); - - 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) || '/'; - } - - e.preventDefault(); - if (this.props.stopPropagation) { - e.stopPropagation(); - } - - var context = this.props.context || this.context; - 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 - }); - } - }, - getHrefFromProps: getHrefFromProps, - getToggledStyles: function (href) { - var routeStore = this.context.getStore(RouteStore); - var isActive = routeStore.isActive(href); - var className = this.props.className; - var style = this.props.style; - if (isActive) { - className = className ? (className + ' ') : ''; - className += this.props.activeClass || 'active'; - style = objectAssign({}, style, this.props.activeStyle); - } - return { - className: className, - style: style - }; - } -};