summaryrefslogtreecommitdiff
path: root/addons/web
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web')
-rw-r--r--addons/web/static/src/js/chrome/abstract_web_client.js26
-rw-r--r--addons/web/static/src/js/chrome/action_manager.js22
-rw-r--r--addons/web/static/src/js/chrome/web_client.js34
-rw-r--r--addons/web/static/src/js/views/abstract_controller.js12
-rw-r--r--addons/web/static/src/js/views/abstract_renderer.js8
-rw-r--r--addons/web/static/src/js/views/basic/basic_controller.js2
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js35
-rw-r--r--addons/web/static/tests/chrome/action_manager_tests.js46
8 files changed, 174 insertions, 11 deletions
diff --git a/addons/web/static/src/js/chrome/abstract_web_client.js b/addons/web/static/src/js/chrome/abstract_web_client.js
index 9fe38adbde6..87219284e4d 100644
--- a/addons/web/static/src/js/chrome/abstract_web_client.js
+++ b/addons/web/static/src/js/chrome/abstract_web_client.js
@@ -88,6 +88,8 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, {
}
});
},
+ getScrollPosition: '_onGetScrollPosition',
+ scrollTo: '_onScrollTo',
},
init: function (parent) {
this.client_options = {};
@@ -374,6 +376,16 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, {
}
},
/**
+ * This function must be implemented to provide to the caller the current
+ * scroll position (left and top) of the webclient.
+ *
+ * @abstract
+ * @param {OdooEvent} ev
+ * @param {function} ev.data.callback
+ */
+ _onGetScrollPosition: function (ev) {
+ },
+ /**
* Loads an action from the database given its ID.
*
* @private
@@ -395,6 +407,20 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, {
this.do_push_state(e.data.state);
},
/**
+ * This function must be implemented by actual webclient to scroll either to
+ * a given offset or to a target element (given a selector).
+ * It must be called with: trigger_up('scrollTo', options).
+ *
+ * @abstract
+ * @param {OdooEvent} ev
+ * @param {integer} [ev.data.top] the number of pixels to scroll from top
+ * @param {integer} [ev.data.left] the number of pixels to scroll from left
+ * @param {string} [ev.data.selector] the selector of the target element to
+ * scroll to
+ */
+ _onScrollTo: function (ev) {
+ },
+ /**
* Displays a visual effect (for example, a rainbowman0
*
* @private
diff --git a/addons/web/static/src/js/chrome/action_manager.js b/addons/web/static/src/js/chrome/action_manager.js
index 1e9c52238bc..4cf10b8813a 100644
--- a/addons/web/static/src/js/chrome/action_manager.js
+++ b/addons/web/static/src/js/chrome/action_manager.js
@@ -258,7 +258,9 @@ var ActionManager = Widget.extend({
callbacks: [{widget: controller.widget}],
});
- this.trigger_up('scrollTo', {offset: controller.scrollTop || 0});
+ if (controller.scrollPosition) {
+ this.trigger_up('scrollTo', controller.scrollPosition);
+ }
if (!controller.widget.need_control_panel) {
this.controlPanel.do_hide();
@@ -294,7 +296,7 @@ var ActionManager = Widget.extend({
_detachCurrentController: function () {
var currentController = this.getCurrentController();
if (currentController) {
- currentController.scrollTop = this._getScrollTop();
+ currentController.scrollPosition = this._getScrollPosition();
dom.detach([{widget: currentController.widget}]);
}
},
@@ -613,19 +615,19 @@ var ActionManager = Widget.extend({
return state;
},
/**
- * Returns the current vertical scroll position.
+ * Returns the current horizontal and vertical scroll positions.
*
* @private
- * @returns {integer}
+ * @returns {Object}
*/
- _getScrollTop: function () {
- var scrollTop;
- this.trigger_up('getScrollTop', {
- callback: function (value) {
- scrollTop = value;
+ _getScrollPosition: function () {
+ var scrollPosition;
+ this.trigger_up('getScrollPosition', {
+ callback: function (_scrollPosition) {
+ scrollPosition = _scrollPosition;
}
});
- return scrollTop;
+ return scrollPosition;
},
/**
* Dispatches the given action to the corresponding handler to execute it,
diff --git a/addons/web/static/src/js/chrome/web_client.js b/addons/web/static/src/js/chrome/web_client.js
index 76aadf49463..3f832a6a49f 100644
--- a/addons/web/static/src/js/chrome/web_client.js
+++ b/addons/web/static/src/js/chrome/web_client.js
@@ -4,6 +4,7 @@ odoo.define('web.WebClient', function (require) {
var AbstractWebClient = require('web.AbstractWebClient');
var config = require('web.config');
var data_manager = require('web.data_manager');
+var dom = require('web.dom');
var framework = require('web.framework');
var Menu = require('web.Menu');
var session = require('web.session');
@@ -177,6 +178,39 @@ return AbstractWebClient.extend({
this.menu.reflow();
}
},
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _onGetScrollPosition: function (ev) {
+ ev.data.callback({
+ left: this.action_manager.el.scrollLeft,
+ top: this.action_manager.el.scrollTop,
+ });
+ },
+ /**
+ * @override
+ */
+ _onScrollTo: function (ev) {
+ var offset;
+ if (ev.data.selector) {
+ offset = dom.getPosition(document.querySelector(ev.data.selector));
+ // substract the position of the ActionManager as it is the
+ // scrolling element
+ var actionManagerOffset = dom.getPosition(this.action_manager.el);
+ offset.left -= actionManagerOffset.left;
+ offset.top -= actionManagerOffset.top;
+ } else {
+ offset = {top: ev.data.top || 0, left: ev.data.left || 0};
+ }
+
+ this.action_manager.el.scrollTop = offset.top;
+ this.action_manager.el.scrollLeft = offset.left;
+ },
});
});
diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js
index 995a5102c37..ee39fd95596 100644
--- a/addons/web/static/src/js/views/abstract_controller.js
+++ b/addons/web/static/src/js/views/abstract_controller.js
@@ -102,6 +102,18 @@ var AbstractController = AbstractAction.extend(ControlPanelMixin, {
}
return this._super.apply(this, arguments);
},
+ /**
+ * Called each time the controller is attached into the DOM.
+ */
+ on_attach_callback: function () {
+ this.renderer.on_attach_callback();
+ },
+ /**
+ * Called each time the controller is detached from the DOM.
+ */
+ on_detach_callback: function () {
+ this.renderer.on_detach_callback();
+ },
//--------------------------------------------------------------------------
// Public
diff --git a/addons/web/static/src/js/views/abstract_renderer.js b/addons/web/static/src/js/views/abstract_renderer.js
index 935fcc6643e..2394fb31a64 100644
--- a/addons/web/static/src/js/views/abstract_renderer.js
+++ b/addons/web/static/src/js/views/abstract_renderer.js
@@ -36,6 +36,14 @@ return Widget.extend({
this.$el.addClass(this.arch.attrs.class);
return $.when(this._render(), this._super());
},
+ /**
+ * Called each time the renderer is attached into the DOM.
+ */
+ on_attach_callback: function () {},
+ /**
+ * Called each time the renderer is detached from the DOM.
+ */
+ on_detach_callback: function () {},
//--------------------------------------------------------------------------
// Public
diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js
index 05031ca2463..263412fd2a2 100644
--- a/addons/web/static/src/js/views/basic/basic_controller.js
+++ b/addons/web/static/src/js/views/basic/basic_controller.js
@@ -144,7 +144,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
.then(function () {
// Reset the scroll position to the top on page changed only
if (!limitChanged) {
- self.trigger_up('scrollTo', {offset: 0});
+ self.trigger_up('scrollTo', {top: 0});
}
})
.then(this.pager.enable.bind(this.pager));
diff --git a/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js b/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js
index c609e45fc60..8619448e793 100644
--- a/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js
+++ b/addons/web/static/src/js/views/kanban/kanban_renderer_mobile.js
@@ -30,6 +30,41 @@ KanbanRenderer.include({
init: function () {
this._super.apply(this, arguments);
this.activeColumnIndex = 0; // index of the currently displayed column
+ this._scrollPosition = null;
+ },
+ /**
+ * As this renderer defines its own scrolling area (the column in grouped
+ * mode), we override this hook to restore the scroll position like it was
+ * when the renderer has been last detached.
+ *
+ * @override
+ */
+ on_attach_callback: function () {
+ if (this._scrollPosition && this.state.groupedBy.length && this.widgets.length) {
+ var $column = this.widgets[this.activeColumnIndex].$el;
+ $column.scrollLeft(this._scrollPosition.left);
+ $column.scrollTop(this._scrollPosition.top);
+ }
+ this._super.apply(this, arguments);
+ },
+ /**
+ * As this renderer defines its own scrolling area (the column in grouped
+ * mode), we override this hook to store the scroll position, so that we can
+ * restore it if the renderer is re-attached to the DOM later.
+ *
+ * @override
+ */
+ on_detach_callback: function () {
+ if (this.state.groupedBy.length && this.widgets.length) {
+ var $column = this.widgets[this.activeColumnIndex].$el;
+ this._scrollPosition = {
+ left: $column.scrollLeft(),
+ top: $column.scrollTop(),
+ };
+ } else {
+ this._scrollPosition = null;
+ }
+ this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
diff --git a/addons/web/static/tests/chrome/action_manager_tests.js b/addons/web/static/tests/chrome/action_manager_tests.js
index 9f7bacc1689..4d46b9167d1 100644
--- a/addons/web/static/tests/chrome/action_manager_tests.js
+++ b/addons/web/static/tests/chrome/action_manager_tests.js
@@ -350,6 +350,52 @@ QUnit.module('ActionManager', {
actionManager.destroy();
});
+ QUnit.test('stores and restores scroll position', function (assert) {
+ assert.expect(7);
+
+ var left;
+ var top;
+ var actionManager = createActionManager({
+ actions: this.actions,
+ archs: this.archs,
+ data: this.data,
+ intercepts: {
+ getScrollPosition: function (ev) {
+ assert.step('getScrollPosition');
+ ev.data.callback({left: left, top: top});
+ },
+ scrollTo: function (ev) {
+ assert.step('scrollTo left ' + ev.data.left + ', top ' + ev.data.top);
+ },
+ },
+ });
+
+ // execute a first action and simulate a scroll
+ assert.step('execute action 3');
+ actionManager.doAction(3);
+ left = 50;
+ top = 100;
+
+ // execute a second action (in which we don't scroll)
+ assert.step('execute action 4');
+ actionManager.doAction(4);
+
+ // go back using the breadcrumbs
+ assert.step('go back to action 3');
+ $('.o_control_panel .breadcrumb a').click();
+
+ assert.verifySteps([
+ 'execute action 3',
+ 'execute action 4',
+ 'getScrollPosition', // of action 3, before leaving it
+ 'go back to action 3',
+ 'getScrollPosition', // of action 4, before leaving it
+ 'scrollTo left 50, top 100', // restore scroll position of action 3
+ ]);
+
+ actionManager.destroy();
+ });
+
QUnit.module('Push State');
QUnit.test('properly push state', function (assert) {