Skip to content

Commit

Permalink
Merge pull request remix-run#249 from rackt/window-scrolling
Browse files Browse the repository at this point in the history
[fixed] Window scrolling
  • Loading branch information
ryanflorence committed Aug 29, 2014
2 parents 94c7a35 + b7e21bb commit 4c26d1a
Show file tree
Hide file tree
Showing 15 changed files with 162 additions and 80 deletions.
2 changes: 1 addition & 1 deletion goBack.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('./modules/helpers/goBack');
module.exports = require('./modules/actions/LocationActions').goBack;
57 changes: 57 additions & 0 deletions modules/actions/LocationActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
var LocationDispatcher = require('../dispatchers/LocationDispatcher');
var makePath = require('../helpers/makePath');

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

PUSH: 'push',
REPLACE: 'replace',
POP: 'pop',
UPDATE_SCROLL: 'update-scroll',

/**
* Transitions to the URL specified in the arguments by pushing
* a new URL onto the history stack.
*/
transitionTo: function (to, params, query) {
LocationDispatcher.handleViewAction({
type: LocationActions.PUSH,
path: makePath(to, params, query)
});
},

/**
* Transitions to the URL specified in the arguments by replacing
* the current URL in the history stack.
*/
replaceWith: function (to, params, query) {
LocationDispatcher.handleViewAction({
type: LocationActions.REPLACE,
path: makePath(to, params, query)
});
},

/**
* 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
});
}

};

module.exports = LocationActions;
2 changes: 1 addition & 1 deletion modules/components/Link.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var React = require('react');
var ActiveState = require('../mixins/ActiveState');
var transitionTo = require('../actions/LocationActions').transitionTo;
var withoutProperties = require('../helpers/withoutProperties');
var transitionTo = require('../helpers/transitionTo');
var hasOwnProperty = require('../helpers/hasOwnProperty');
var makeHref = require('../helpers/makeHref');
var warning = require('react/lib/warning');
Expand Down
22 changes: 9 additions & 13 deletions modules/components/Routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ var React = require('react');
var warning = require('react/lib/warning');
var copyProperties = require('react/lib/copyProperties');
var Promise = require('es6-promise').Promise;
var LocationActions = require('../actions/LocationActions');
var Route = require('../components/Route');
var goBack = require('../helpers/goBack');
var replaceWith = require('../helpers/replaceWith');
var Path = require('../helpers/Path');
var Redirect = require('../helpers/Redirect');
var Transition = require('../helpers/Transition');
Expand Down Expand Up @@ -37,9 +36,9 @@ function defaultAbortedTransitionHandler(transition) {
var reason = transition.abortReason;

if (reason instanceof Redirect) {
replaceWith(reason.to, reason.params, reason.query);
LocationActions.replaceWith(reason.to, reason.params, reason.query);
} else {
goBack();
LocationActions.goBack();
}
}

Expand All @@ -59,6 +58,11 @@ function defaultTransitionErrorHandler(error) {
throw error; // This error probably originated in a transition hook.
}

function maybeUpdateScroll(routes, rootRoute) {
if (!routes.props.preserveScrollPosition && !rootRoute.props.preserveScrollPosition)
LocationActions.updateScroll();
}

/**
* The <Routes> component configures the route hierarchy and renders the
* route matching the current location when rendered into a document.
Expand Down Expand Up @@ -98,7 +102,6 @@ var Routes = React.createClass({
};
},


getLocation: function () {
var location = this.props.location;

Expand Down Expand Up @@ -185,7 +188,7 @@ var Routes = React.createClass({
var rootMatch = getRootMatch(nextState.matches);

if (rootMatch)
maybeScrollWindow(routes, rootMatch.route);
maybeUpdateScroll(routes, rootMatch.route);
}

return transition;
Expand Down Expand Up @@ -443,11 +446,4 @@ function reversedArray(array) {
return array.slice(0).reverse();
}

function maybeScrollWindow(routes, rootRoute) {
if (routes.props.preserveScrollPosition || rootRoute.props.preserveScrollPosition)
return;

window.scrollTo(0, 0);
}

module.exports = Routes;
18 changes: 18 additions & 0 deletions modules/dispatchers/LocationDispatcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
var copyProperties = require('react/lib/copyProperties');
var Dispatcher = require('react-dispatcher');

/**
* Dispatches actions that modify the URL.
*/
var LocationDispatcher = copyProperties(new Dispatcher, {

handleViewAction: function (action) {
this.dispatch({
source: 'VIEW_ACTION',
action: action
});
}

});

module.exports = LocationDispatcher;
2 changes: 1 addition & 1 deletion modules/helpers/Transition.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var mixInto = require('react/lib/mixInto');
var transitionTo = require('./transitionTo');
var transitionTo = require('../actions/LocationActions').transitionTo;
var Redirect = require('./Redirect');

/**
Expand Down
10 changes: 0 additions & 10 deletions modules/helpers/goBack.js

This file was deleted.

12 changes: 0 additions & 12 deletions modules/helpers/replaceWith.js

This file was deleted.

12 changes: 0 additions & 12 deletions modules/helpers/transitionTo.js

This file was deleted.

67 changes: 54 additions & 13 deletions modules/stores/PathStore.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
var warning = require('react/lib/warning');
var EventEmitter = require('events').EventEmitter;
var LocationActions = require('../actions/LocationActions');
var LocationDispatcher = require('../dispatchers/LocationDispatcher');
var supportsHistory = require('../helpers/supportsHistory');
var HistoryLocation = require('../locations/HistoryLocation');
var RefreshLocation = require('../locations/RefreshLocation');
Expand All @@ -11,6 +13,15 @@ function notifyChange() {
_events.emit(CHANGE_EVENT);
}

var _scrollPositions = {};

function recordScrollPosition(path) {
_scrollPositions[path] = {
x: window.scrollX,
y: window.scrollY
};
}

var _location;

/**
Expand Down Expand Up @@ -58,27 +69,57 @@ var PathStore = {
_location = null;
},

/**
* Returns the location object currently in use.
*/
getLocation: function () {
return _location;
},

push: function (path) {
if (_location.getCurrentPath() !== path)
_location.push(path);
},

replace: function (path) {
if (_location.getCurrentPath() !== path)
_location.replace(path);
/**
* Returns the current URL path.
*/
getCurrentPath: function () {
return _location.getCurrentPath();
},

pop: function () {
_location.pop();
/**
* Returns the last known scroll position for the given path.
*/
getScrollPosition: function (path) {
return _scrollPositions[path] || { x: 0, y: 0 };
},

getCurrentPath: function () {
return _location.getCurrentPath();
}
dispatchToken: LocationDispatcher.register(function (payload) {
var action = payload.action;
var currentPath = _location.getCurrentPath();

switch (action.type) {
case LocationActions.PUSH:
if (currentPath !== action.path) {
recordScrollPosition(currentPath);
_location.push(action.path);
}
break;

case LocationActions.REPLACE:
if (currentPath !== action.path) {
recordScrollPosition(currentPath);
_location.replace(action.path);
}
break;

case LocationActions.POP:
recordScrollPosition(currentPath);
_location.pop();
break;

case LocationActions.UPDATE_SCROLL:
var p = PathStore.getScrollPosition(currentPath);
window.scrollTo(p.x, p.y);
break;
}
})

};

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"dependencies": {
"es6-promise": "^1.0.0",
"events": "^1.0.1",
"qs": "^1.2.2"
"qs": "^1.2.2",
"react-dispatcher": "^0.2.1"
},
"keywords": [
"react",
Expand Down
2 changes: 1 addition & 1 deletion replaceWith.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('./modules/helpers/replaceWith');
module.exports = require('./modules/actions/LocationActions').replaceWith;
28 changes: 15 additions & 13 deletions specs/PathStore.spec.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,54 @@
require('./helper');
var MemoryLocation = require('../modules/locations/MemoryLocation');
var PathStore = require('../modules/stores/PathStore');
var transitionTo = require('../modules/actions/LocationActions').transitionTo;
var replaceWith = require('../modules/actions/LocationActions').replaceWith;
var goBack = require('../modules/actions/LocationActions').goBack;
var getCurrentPath = require('../modules/stores/PathStore').getCurrentPath;

describe('PathStore', function () {

beforeEach(function () {
PathStore.push('/one');
transitionTo('/one');
});

describe('when a new path is pushed to the URL', function () {
beforeEach(function () {
PathStore.push('/two');
transitionTo('/two');
});

it('has the correct path', function () {
expect(PathStore.getCurrentPath()).toEqual('/two');
expect(getCurrentPath()).toEqual('/two');
});
});

describe('when a new path is used to replace the URL', function () {
beforeEach(function () {
PathStore.push('/two');
PathStore.replace('/three');
transitionTo('/two');
replaceWith('/three');
});

it('has the correct path', function () {
expect(PathStore.getCurrentPath()).toEqual('/three');
expect(getCurrentPath()).toEqual('/three');
});

describe('going back in history', function () {
beforeEach(function () {
PathStore.pop();
goBack();
});

it('has the path before the one that was replaced', function () {
expect(PathStore.getCurrentPath()).toEqual('/one');
expect(getCurrentPath()).toEqual('/one');
});
});
});

describe('when going back in history', function () {
beforeEach(function () {
PathStore.push('/two');
PathStore.pop();
transitionTo('/two');
goBack();
});

it('has the correct path', function () {
expect(PathStore.getCurrentPath()).toEqual('/one');
expect(getCurrentPath()).toEqual('/one');
});
});

Expand Down
3 changes: 2 additions & 1 deletion specs/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ beforeEach(function () {
RouteStore.unregisterAllRoutes();
});

var transitionTo = require('../modules/actions/LocationActions').transitionTo;
var MemoryLocation = require('../modules/locations/MemoryLocation');
var PathStore = require('../modules/stores/PathStore');

beforeEach(function () {
PathStore.setup(MemoryLocation);
PathStore.push('/');
transitionTo('/');
});

afterEach(function () {
Expand Down
2 changes: 1 addition & 1 deletion transitionTo.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('./modules/helpers/transitionTo');
module.exports = require('./modules/actions/LocationActions').transitionTo;

0 comments on commit 4c26d1a

Please sign in to comment.