Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[changed] Remove preserveScrollPosition, add scrollStrategy #326

Merged
merged 1 commit into from
Sep 29, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added modules/.DS_Store
Binary file not shown.
110 changes: 88 additions & 22 deletions modules/actions/LocationActions.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,96 @@
var supportsHistory = require('../utils/supportsHistory');
var HistoryLocation = require('../locations/HistoryLocation');
var RefreshLocation = require('../locations/RefreshLocation');
var LocationDispatcher = require('../dispatchers/LocationDispatcher');
var ActionTypes = require('../constants/ActionTypes');
var warning = require('react/lib/warning');
var isAbsoluteURL = require('../utils/isAbsoluteURL');
var makePath = require('../utils/makePath');

function loadURL(url) {
window.location = url;
}

var _location = null;
var _isDispatching = false;
var _previousPath = null;

function dispatchAction(actionType, operation) {
if (_isDispatching)
throw new Error('Cannot handle ' + actionType + ' in the middle of another action.');

_isDispatching = true;

var scrollPosition = {
x: window.scrollX,
y: window.scrollY
};

if (typeof operation === 'function')
operation(_location);

var path = _location.getCurrentPath();
LocationDispatcher.handleViewAction({
type: actionType,
path: path,
scrollPosition: scrollPosition
});

_isDispatching = false;
_previousPath = path;
}

function handleChange() {
var path = _location.getCurrentPath();

// Ignore changes inside or caused by dispatchAction
if (!_isDispatching && path !== _previousPath) {
dispatchAction(ActionTypes.POP);
}
}

/**
* Actions that modify the URL.
*/
var LocationActions = {

PUSH: 'push',
REPLACE: 'replace',
POP: 'pop',
UPDATE_SCROLL: 'update-scroll',
getLocation: function () {
return _location;
},

setup: function (location) {
// When using HistoryLocation, automatically fallback
// to RefreshLocation in browsers that do not support
// the HTML5 history API.
if (location === HistoryLocation && !supportsHistory())
location = RefreshLocation;

if (_location !== null) {
warning(
_location === location,
'Cannot use location %s, already using %s', location, _location
);
return;
}

_location = location;

if (_location !== null) {
dispatchAction(ActionTypes.SETUP, function (location) {
if (typeof location.setup === 'function')
location.setup(handleChange);
});
}
},

teardown: function () {
if (_location !== null) {
if (typeof _location.teardown === 'function')
_location.teardown();

_location = null;
}
},

/**
* Transitions to the URL specified in the arguments by pushing
Expand All @@ -24,9 +100,9 @@ var LocationActions = {
if (isAbsoluteURL(to)) {
loadURL(to);
} else {
LocationDispatcher.handleViewAction({
type: LocationActions.PUSH,
path: makePath(to, params, query)
dispatchAction(ActionTypes.PUSH, function (location) {
var path = makePath(to, params, query);
location.push(path);
});
}
},
Expand All @@ -39,9 +115,9 @@ var LocationActions = {
if (isAbsoluteURL(to)) {
loadURL(to);
} else {
LocationDispatcher.handleViewAction({
type: LocationActions.REPLACE,
path: makePath(to, params, query)
dispatchAction(ActionTypes.REPLACE, function (location) {
var path = makePath(to, params, query);
location.replace(path);
});
}
},
Expand All @@ -50,18 +126,8 @@ var LocationActions = {
* Transitions to the previous URL.
*/
goBack: function () {
LocationDispatcher.handleViewAction({
type: LocationActions.POP
});
},

/**
* Updates the window's scroll position to the last known position
* for the current URL path.
*/
updateScroll: function () {
LocationDispatcher.handleViewAction({
type: LocationActions.UPDATE_SCROLL
dispatchAction(ActionTypes.POP, function (location) {
location.pop();
});
}

Expand Down
7 changes: 5 additions & 2 deletions modules/components/Routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var Route = require('../components/Route');
var ActiveDelegate = require('../mixins/ActiveDelegate');
var PathListener = require('../mixins/PathListener');
var RouteStore = require('../stores/RouteStore');
var ScrollStore = require('../stores/ScrollStore');
var Path = require('../utils/Path');
var Promise = require('../utils/Promise');
var Redirect = require('../utils/Redirect');
Expand Down Expand Up @@ -51,9 +52,11 @@ function maybeUpdateScroll(routes) {
return;

var currentRoute = routes.getCurrentRoute();
var scrollPosition = ScrollStore.getScrollPosition();

if (!routes.props.preserveScrollPosition && currentRoute && !currentRoute.props.preserveScrollPosition)
LocationActions.updateScroll();
if (currentRoute && scrollPosition) {
window.scrollTo(scrollPosition.x, scrollPosition.y);
}
}

/**
Expand Down
10 changes: 10 additions & 0 deletions modules/constants/ActionTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var keyMirror = require('react/lib/keyMirror');

var ActionTypes = keyMirror({
SETUP: null,
PUSH: null,
REPLACE: null,
POP: null
});

module.exports = ActionTypes;
2 changes: 0 additions & 2 deletions modules/locations/HistoryLocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,10 @@ var HistoryLocation = {

push: function (path) {
window.history.pushState({ path: path }, '', path);
_onChange();
},

replace: function (path) {
window.history.replaceState({ path: path }, '', path);
_onChange();
},

pop: function () {
Expand Down
8 changes: 0 additions & 8 deletions modules/locations/MemoryLocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,19 @@ var warning = require('react/lib/warning');

var _lastPath = null;
var _currentPath = null;
var _onChange;

/**
* A Location that does not require a DOM.
*/
var MemoryLocation = {

setup: function (onChange) {
_onChange = onChange;
},

push: function (path) {
_lastPath = _currentPath;
_currentPath = path;
_onChange();
},

replace: function (path) {
_currentPath = path;
_onChange();
},

pop: function () {
Expand All @@ -32,7 +25,6 @@ var MemoryLocation = {

_currentPath = _lastPath;
_lastPath = null;
_onChange();
},

getCurrentPath: function () {
Expand Down
45 changes: 41 additions & 4 deletions modules/mixins/PathListener.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
var React = require('react');
var LocationActions = require('../actions/LocationActions');
var DefaultLocation = require('../locations/DefaultLocation');
var HashLocation = require('../locations/HashLocation');
var HistoryLocation = require('../locations/HistoryLocation');
var RefreshLocation = require('../locations/RefreshLocation');
var NoneStrategy = require('../strategies/NoneStrategy');
var ScrollToTopStrategy = require('../strategies/ScrollToTopStrategy');
var ImitateBrowserStrategy = require('../strategies/ImitateBrowserStrategy');
var PathStore = require('../stores/PathStore');
var ScrollStore = require('../stores/ScrollStore');

/**
* A hash of { name, location } pairs.
Expand All @@ -14,6 +18,15 @@ var NAMED_LOCATIONS = {
refresh: RefreshLocation
};

/**
* A hash of { name, scrollStrategy } pairs.
*/
var NAMED_SCROLL_STRATEGIES = {
none: NoneStrategy,
scrollToTop: ScrollToTopStrategy,
imitateBrowser: ImitateBrowserStrategy
};

/**
* A mixin for components that listen for changes to the current
* URL path.
Expand All @@ -26,12 +39,20 @@ var PathListener = {

if (typeof location === 'string' && !(location in NAMED_LOCATIONS))
return new Error('Unknown location "' + location + '", see ' + componentName);
}
},

scrollStrategy: function (props, propName, componentName) {
var scrollStrategy = props[propName];

if (typeof scrollStrategy === 'string' && !(scrollStrategy in NAMED_SCROLL_STRATEGIES))
return new Error('Unknown scrollStrategy "' + scrollStrategy + '", see ' + componentName);
},
},

getDefaultProps: function () {
return {
location: DefaultLocation
location: DefaultLocation,
scrollStrategy: ScrollToTopStrategy
};
},

Expand All @@ -48,8 +69,22 @@ var PathListener = {
return location;
},

/**
* Gets the scroll strategy object this component uses to
* restore scroll position when the path changes.
*/
getScrollStrategy: function () {
var scrollStrategy = this.props.scrollStrategy;

if (typeof scrollStrategy === 'string')
return NAMED_SCROLL_STRATEGIES[scrollStrategy];

return scrollStrategy;
},

componentWillMount: function () {
PathStore.setup(this.getLocation());
ScrollStore.setup(this.getScrollStrategy());
LocationActions.setup(this.getLocation());

if (this.updatePath)
this.updatePath(PathStore.getCurrentPath());
Expand All @@ -61,6 +96,8 @@ var PathListener = {

componentWillUnmount: function () {
PathStore.removeChangeListener(this.handlePathChange);
ScrollStore.teardown();
LocationActions.teardown();
},

handlePathChange: function () {
Expand Down
Loading