diff options
| author | Alexandre Kühn <aku@odoo.com> | 2018-09-17 12:28:39 +0200 |
|---|---|---|
| committer | Alexandre Kühn <aku@odoo.com> | 2018-09-17 16:46:53 +0200 |
| commit | 19eacf7d23c9413de4430a3422b5ed74b37ef242 (patch) | |
| tree | fc96508d942cf2a31465545293f8856bb85838aa /addons/web/static/src/js/chrome | |
| parent | 110d37a31d579d4a221e0a0abc275bd6920a5159 (diff) | |
[IMP] web: new navbar in community
The navbar in community now looks more like the navbar in enterprise.
Here is a sum-up of the changes in community:
- The navbar in community is now in blue, instead of black.
- No more app list that almost fills the whole navbar: there is
now an "Apps Menu" in the left-most side of the navbar.
- No more sidebar for the menus inside an app: those menus are
now located in the middle part of the navbar.
The navbar in community does not support mobile (< 768px width),
but it should still somewhat usable on most mobile devices.
As for the website navbar, it has been unchanged and should still
support mobile layout.
Task-ID 1878649
Diffstat (limited to 'addons/web/static/src/js/chrome')
| -rw-r--r-- | addons/web/static/src/js/chrome/apps_menu.js | 104 | ||||
| -rw-r--r-- | addons/web/static/src/js/chrome/menu.js | 300 | ||||
| -rw-r--r-- | addons/web/static/src/js/chrome/web_client.js | 353 |
3 files changed, 491 insertions, 266 deletions
diff --git a/addons/web/static/src/js/chrome/apps_menu.js b/addons/web/static/src/js/chrome/apps_menu.js new file mode 100644 index 00000000000..bdc330b0e29 --- /dev/null +++ b/addons/web/static/src/js/chrome/apps_menu.js @@ -0,0 +1,104 @@ +odoo.define('web.AppsMenu', function (require) { +"use strict"; + +var Widget = require('web.Widget'); + +var AppsMenu = Widget.extend({ + template: 'AppsMenu', + events: { + 'click .o_app': '_onAppsMenuItemClicked', + }, + /** + * @override + * @param {web.Widget} parent + * @param {Object} menuData + * @param {Object[]} menuData.children + */ + init: function (parent, menuData) { + this._super.apply(this, arguments); + this._activeApp = undefined; + this._apps = _.map(menuData.children, function (appMenuData) { + return { + actionID: parseInt(appMenuData.action.split(',')[1]), + menuID: appMenuData.id, + name: appMenuData.name, + xmlID: appMenuData.xmlid, + }; + }); + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @returns {Object} + */ + getActiveApp: function () { + return this._activeApp || {}; + }, + /** + * @returns {Object[]} + */ + getApps: function () { + return this._apps; + }, + /** + * Open the given app + */ + openApp: function () {}, + /** + * Open the first app in the list of apps + */ + openFirstApp: function () { + var firstApp = this._apps[0]; + this._openApp(firstApp); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {Object} app + */ + _openApp: function (app) { + this._setActiveApp(app); + this.trigger_up('app_clicked', { + action_id: app.actionID, + menu_id: app.menuID, + }); + }, + /** + * @private + * @param {Object} app + */ + _setActiveApp: function (app) { + this._activeApp = app; + this.renderElement(); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Called when clicking on an item in the apps menu. + * + * @private + * @param {MouseEvent} ev + */ + _onAppsMenuItemClicked: function (ev) { + var $target = $(ev.currentTarget); + var actionID = $target.data('action-id'); + var menuID = $target.data('menu-id'); + var app = _.findWhere(this._apps, { actionID: actionID, menuID: menuID }); + this._openApp(app); + }, + +}); + +return AppsMenu; + +}); diff --git a/addons/web/static/src/js/chrome/menu.js b/addons/web/static/src/js/chrome/menu.js index c96d4b17f8c..7f60e2ea44f 100644 --- a/addons/web/static/src/js/chrome/menu.js +++ b/addons/web/static/src/js/chrome/menu.js @@ -1,179 +1,221 @@ odoo.define('web.Menu', function (require) { "use strict"; +var AppsMenu = require('web.AppsMenu'); +var config = require('web.config'); var core = require('web.core'); var dom = require('web.dom'); -var session = require('web.session'); +var SystrayMenu = require('web.SystrayMenu'); +var UserMenu = require('web.UserMenu'); var Widget = require('web.Widget'); +UserMenu.prototype.sequence = 0; // force UserMenu to be the right-most item in the systray +SystrayMenu.Items.push(UserMenu); + +var QWeb = core.qweb; + var Menu = Widget.extend({ - init: function() { - this._super.apply(this, arguments); - this.is_bound = $.Deferred(); - this.data = {data:{children:[]}}; - core.bus.on('change_menu_section', this, this.on_change_top_menu); - }, - start: function() { - this._super.apply(this, arguments); - return this.bind_menu(); + template: 'Menu', + menusTemplate: 'Menu.sections', + events: { + 'mouseover .o_menu_sections > li:not(.show)': '_onMouseOverMenu', + 'click .o_menu_brand': '_onAppNameClicked', }, - do_reload: function() { - var self = this; - self.bind_menu(); - }, - bind_menu: function() { + + init: function (parent, menu_data) { var self = this; - this.$secondary_menus = this.$el.parents().find('.o_sub_menu'); - this.$secondary_menus.on('click', 'a[data-menu]', this.on_menu_click); - this.$el.on('click', 'a[data-menu]', function (event) { - event.preventDefault(); - var menu_id = $(event.currentTarget).data('menu'); - core.bus.trigger('change_menu_section', menu_id); - }); + this._super.apply(this, arguments); - // Hide second level submenus - this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').addClass('o_hidden'); - if (self.current_menu) { - self.open_menu(self.current_menu); - } - this.trigger('menu_bound'); + this.$menu_sections = {}; + this.menu_data = menu_data; - dom.initAutoMoreMenu(this.$el); + // Prepare navbar's menus + var $menu_sections = $(QWeb.render(this.menusTemplate, { + menu_data: this.menu_data, + })); + $menu_sections.filter('section').each(function () { + self.$menu_sections[parseInt(this.className, 10)] = $(this).children('li'); + }); - this.is_bound.resolve(); + // Bus event + core.bus.on('change_menu_section', this, this._onChangeMenuSection); }, - /** - * Opens a given menu by id, as if a user had browsed to that menu by hand - * except does not trigger any event on the way - * - * @param {Number} id database id of the terminal menu to select - */ - open_menu: function (id) { - this.current_menu = id; - session.active_id = id; - var $clicked_menu, $sub_menu, $main_menu; - $clicked_menu = this.$el.add(this.$secondary_menus).find('a[data-menu=' + id + ']'); - this.trigger('open_menu', id, $clicked_menu); - - if (this.$secondary_menus.has($clicked_menu).length) { - $sub_menu = $clicked_menu.parents('.oe_secondary_menu'); - $main_menu = this.$el.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']'); - } else { - $sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + $clicked_menu.attr('data-menu') + ']'); - $main_menu = $clicked_menu; + start: function () { + var self = this; + + this.$menu_apps = this.$('.o_menu_apps'); + this.$menu_brand_placeholder = this.$('.o_menu_brand'); + this.$section_placeholder = this.$('.o_menu_sections'); + + // Navbar's menus event handlers + var on_secondary_menu_click = function (ev) { + ev.preventDefault(); + var menu_id = $(ev.currentTarget).data('menu'); + var action_id = $(ev.currentTarget).data('action-id'); + self._on_secondary_menu_click(menu_id, action_id); + }; + var menu_ids = _.keys(this.$menu_sections); + var primary_menu_id, $section; + for (var i = 0; i < menu_ids.length; i++) { + primary_menu_id = menu_ids[i]; + $section = this.$menu_sections[primary_menu_id]; + $section.on('click', 'a[data-menu]', self, on_secondary_menu_click.bind(this)); } - this.current_primary_menu = $main_menu.data('menu'); + // Apps Menu + this._appsMenu = new AppsMenu(self, this.menu_data); + this._appsMenu.appendTo(this.$menu_apps); - // Activate current main menu - this.$el.find('.active').removeClass('active'); - $main_menu.parent().addClass('active'); + // Systray Menu + this.systray_menu = new SystrayMenu(this); + this.systray_menu.attachTo(this.$('.o_menu_systray')); - // Show current sub menu - this.$secondary_menus.find('.oe_secondary_menu').hide(); - $sub_menu.show(); + dom.initAutoMoreMenu(this.$section_placeholder, { + maxWidth: function () { + return self.$el.width() - (self.$menu_apps.outerWidth(true) + self.$menu_brand_placeholder.outerWidth(true) + self.systray_menu.$el.outerWidth(true)); + }, + sizeClass: 'SM', + }); - // Hide/Show the leftbar menu depending of the presence of sub-items - this.$secondary_menus.toggleClass('o_hidden', !$sub_menu.children().length); + return this._super.apply(this, arguments); + }, + _trigger_menu_clicked: function (menu_id, action_id) { + this.trigger_up('menu_clicked', { + id: menu_id, + action_id: action_id, + previous_menu_id: this.current_secondary_menu || this.current_primary_menu, + }); + }, + _on_secondary_menu_click: function (menu_id, action_id) { + var self = this; - // Activate current menu item and show parents - this.$secondary_menus.find('.active').removeClass('active'); - if ($main_menu !== $clicked_menu) { - $clicked_menu.parents().removeClass('o_hidden'); - if ($clicked_menu.is('.oe_menu_toggler')) { - $clicked_menu.toggleClass('oe_menu_opened').siblings('.oe_secondary_submenu:first').toggleClass('o_hidden'); - } else { - $clicked_menu.parent().addClass('active'); - } + // It is still possible that we don't have an action_id (for example, menu toggler) + if (action_id) { + self._trigger_menu_clicked(menu_id, action_id); + this.current_secondary_menu = menu_id; } - // add a tooltip to cropped menu items - this.$secondary_menus.find('.oe_secondary_submenu li a span').each(function() { - $(this).tooltip(this.scrollWidth > this.clientWidth ? {title: $(this).text().trim(), placement: 'right'} : 'dispose'); - }); }, /** - * Call open_menu on a menu_item that matches the action_id - * - * If `menuID` is a match on this action, open this menu_item. - * Otherwise open the first menu_item that matches the action_id. - * - * @param {Number} id the action_id to match - * @param {Number} [menuID] a menu ID that may match with provided action + * Helpers used by web_client in order to restore the state from + * an url (by restore, read re-synchronize menu and action manager) */ - open_action: function (id, menuID) { - var $menu = this.$el.add(this.$secondary_menus).find('a[data-action-id="' + id + '"]'); - if (!(menuID && $menu.filter("[data-menu='" + menuID + "']").length)) { - // menuID doesn't match action, so pick first menu_item - menuID = $menu.data('menu'); + action_id_to_primary_menu_id: function (action_id) { + var primary_menu_id, found; + for (var i = 0; i < this.menu_data.children.length && !primary_menu_id; i++) { + found = this._action_id_in_subtree(this.menu_data.children[i], action_id); + if (found) { + primary_menu_id = this.menu_data.children[i].id; + } } - if (menuID) { - this.open_menu(menuID); + return primary_menu_id; + }, + _action_id_in_subtree: function (root, action_id) { + // action_id can be a string or an integer + if (root.action && root.action.split(',')[1] === String(action_id)) { + return true; + } + var found; + for (var i = 0; i < root.children.length && !found; i++) { + found = this._action_id_in_subtree(root.children[i], action_id); } + return found; }, - /** - * Process a click on a menu item - * - * @param {Number} id the menu_id - */ - menu_click: function(id) { - if (!id) { return; } + menu_id_to_action_id: function (menu_id, root) { + if (!root) { + root = $.extend(true, {}, this.menu_data); + } - // find back the menuitem in dom to get the action - var $item = this.$el.find('a[data-menu=' + id + ']'); - if (!$item.length) { - $item = this.$secondary_menus.find('a[data-menu=' + id + ']'); + if (root.id === menu_id) { + return root.action.split(',')[1] ; } - var action_id = $item.data('action-id'); - // If first level menu doesnt have action trigger first leaf - if (!action_id) { - if(this.$el.has($item).length) { - var $sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + id + ']'); - var $items = $sub_menu.find('a[data-action-id]').filter('[data-action-id!=""]'); - if($items.length) { - action_id = $items.data('action-id'); - id = $items.data('menu'); - } + for (var i = 0; i < root.children.length; i++) { + var action_id = this.menu_id_to_action_id(menu_id, root.children[i]); + if (action_id !== undefined) { + return action_id; } } - if (action_id) { - this.trigger('menu_click', { - action_id: action_id, - id: id, - previous_menu_id: this.current_menu // Here we don't know if action will fail (in which case we have to revert menu) - }, $item); - } else { - console.log('Menu no action found web test 04 will fail'); - } - this.open_menu(id); + return undefined; }, + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + /** - * Change the current top menu + * Returns the id of the current primary (first level) menu. * - * @param {int} [menu_id] the top menu id + * @returns {integer} */ - on_change_top_menu: function(menu_id) { - var self = this; - this.menu_click(menu_id); + getCurrentPrimaryMenu: function () { + return this.current_primary_menu; }, - on_menu_click: function(ev) { - ev.preventDefault(); - this.menu_click($(ev.currentTarget).data('menu')); + /** + * Open the first app + */ + openFirstApp: function () { + this._appsMenu.openFirstApp(); }, //-------------------------------------------------------------------------- - // Public + // Handlers //-------------------------------------------------------------------------- /** - * Returns the id of the current primary (first level) menu. + * When clicking on app name, opens the first action of the app * - * @returns {integer} + * @private + * @param {MouseEvent} ev */ - getCurrentPrimaryMenu: function () { - return this.current_primary_menu; + _onAppNameClicked: function (ev) { + var actionID = parseInt(this.menu_id_to_action_id(this.current_primary_menu)); + this._trigger_menu_clicked(this.current_primary_menu, actionID); + }, + /** + * @private + * @param {integer} primaryMenuID + */ + _onChangeMenuSection: function (primaryMenuID) { + if (!this.$menu_sections[primaryMenuID]) { + return; // unknown menu_id + } + + if (this.current_primary_menu === primaryMenuID) { + return; // already in that menu + } + + if (this.current_primary_menu) { + this.$menu_sections[this.current_primary_menu].detach(); + } + + // Get back the application name + for (var i = 0; i < this.menu_data.children.length; i++) { + if (this.menu_data.children[i].id === primaryMenuID) { + this.$menu_brand_placeholder.text(this.menu_data.children[i].name); + break; + } + } + + this.$menu_sections[primaryMenuID].appendTo(this.$section_placeholder); + this.current_primary_menu = primaryMenuID; + + core.bus.trigger('resize'); + }, + /** + * @private + * @param {MouseEvent} ev + */ + _onMouseOverMenu: function (ev) { + if (config.device.isMobile) { + return; + } + var $target = $(ev.currentTarget); + var $opened = $target.siblings('.show'); + if ($opened.length) { + $target.find('[data-toggle="dropdown"]').dropdown('toggle'); + } }, }); return Menu; + }); diff --git a/addons/web/static/src/js/chrome/web_client.js b/addons/web/static/src/js/chrome/web_client.js index ca777d470ae..8b725423d0f 100644 --- a/addons/web/static/src/js/chrome/web_client.js +++ b/addons/web/static/src/js/chrome/web_client.js @@ -6,149 +6,164 @@ var config = require('web.config'); var core = require('web.core'); 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'); -var SystrayMenu = require('web.SystrayMenu'); -var UserMenu = require('web.UserMenu'); return AbstractWebClient.extend({ - events: _.extend({}, AbstractWebClient.prototype.events, { - 'click .oe_logo_edit_admin': 'logo_edit', - 'click .oe_logo img': function(ev) { - ev.preventDefault(); - return this.clear_uncommitted_changes().then(function() { - framework.redirect("/web" + (config.debug ? "?debug" : "")); - }); - }, + custom_events: _.extend({}, AbstractWebClient.prototype.custom_events, { + app_clicked: 'on_app_clicked', + menu_clicked: 'on_menu_clicked', }), - show_application: function() { - var self = this; - - this.toggle_bars(true); - this.set_title(); - this.update_logo(); - - // Menu is rendered server-side thus we don't want the widget to create any dom - this.menu = new Menu(this); - this.menu.setElement(this.$el.parents().find('.oe_application_menu_placeholder')); - this.menu.on('menu_click', this, this.on_menu_action); - - // Create the user menu (rendered client-side) - this.user_menu = new UserMenu(this); - var $user_menu_placeholder = $('body').find('.oe_user_menu_placeholder').show(); - var user_menu_loaded = this.user_menu.appendTo($user_menu_placeholder); - - // Create the systray menu (rendered server-side) - this.systray_menu = new SystrayMenu(this); - this.systray_menu.setElement(this.$el.parents().find('.oe_systray')); - var systray_menu_loaded = this.systray_menu.start(); - - // Start the menu once both systray and user menus are rendered - // to prevent overflows while loading - return $.when(systray_menu_loaded, user_menu_loaded).then(function() { - self.bind_hashchange(); - self.menu.$el.removeClass('d-none'); - self.menu.start(); + start: function () { + core.bus.on('change_menu_section', this, function (menuID) { + this.do_push_state(_.extend($.bbq.getState(), { + menu_id: menuID, + })); }); + return this._super.apply(this, arguments); }, - toggle_bars: function(value) { - this.$('tr:has(td.navbar),.oe_leftbar').toggle(value); - }, - update_logo: function(reload) { - var company = session.company_id; - var img = session.url('/web/binary/company_logo' + '?db=' + session.db + (company ? '&company=' + company : '')); - this.$('.o_sub_menu_logo img').attr('src', '').attr('src', img + (reload ? "&t=" + Date.now() : '')); - this.$('.oe_logo_edit').toggleClass('oe_logo_edit_admin', session.is_superuser); - }, - logo_edit: function(ev) { + bind_events: function () { var self = this; - ev.preventDefault(); - this._rpc({ - model: 'res.users', - method: 'read', - args: [[session.uid], ['company_id']], + this._super.apply(this, arguments); + + /* + Small patch to allow having a link with a href towards an anchor. Since odoo use hashtag + to represent the current state of the view, we can't easily distinguish between a link + towards an anchor and a link towards anoter view/state. If we want to navigate towards an + anchor, we must not change the hash of the url otherwise we will be redirected to the app + switcher instead. + To check if we have an anchor, first check if we have an href attributes starting with #. + Try to find a element in the DOM using JQuery selector. + If we have a match, it means that it is probably a link to an anchor, so we jump to that anchor. + */ + this.$el.on('click', 'a', function (ev) { + var disable_anchor = ev.target.attributes.disable_anchor; + if (disable_anchor && disable_anchor.value === "true") { + return; + } + + var href = ev.target.attributes.href; + if (href) { + if (href.value[0] === '#' && href.value.length > 1) { + if (self.$("[id='"+href.value.substr(1)+"']").length) { + ev.preventDefault(); + self.trigger_up('scrollTo', {'selector': href.value}); + } + } + } + }); + }, + load_menus: function () { + return this._rpc({ + model: 'ir.ui.menu', + method: 'load_menus', + args: [config.debug], + context: session.user_context, }) - .then(function(data) { - self._rpc({ - route: '/web/action/load', - params: { action_id: 'base.action_res_company_form' }, - }) - .done(function(result) { - result.res_id = data[0].company_id[0]; - result.target = "new"; - result.views = [[false, 'form']]; - result.flags = { - action_buttons: true, - headless: true, - }; - self.action_manager.doAction(result, { - on_close: self.update_logo.bind(self, true), - }); - }); + .then(function (menuData) { + // Compute action_id if not defined on a top menu item + for (var i = 0; i < menuData.children.length; i++) { + var child = menuData.children[i]; + if (child.action === false) { + while (child.children && child.children.length) { + child = child.children[0]; + if (child.action) { + menuData.children[i].action = child.action; + break; + } + } + } + } + return menuData; }); - return false; }, - bind_hashchange: function() { + show_application: function () { var self = this; - $(window).bind('hashchange', this.on_hashchange); - var didHashChanged = false; - $(window).one('hashchange', function () { - didHashChanged = true; - }); + this.set_title(); + + return this.instanciate_menu_widgets().then(function () { + $(window).bind('hashchange', self.on_hashchange); - var state = $.bbq.getState(true); - if (_.isEmpty(state) || state.action === "login") { - self.menu.is_bound.done(function() { - self._rpc({ + // If the url's state is empty, we execute the user's home action if there is one (we + // show the first app if not) + if (_.isEmpty($.bbq.getState(true))) { + return self._rpc({ model: 'res.users', method: 'read', - args: [[session.uid], ['action_id']], + args: [session.uid, ["action_id"]], }) - .done(function(result) { - var amDef = self.action_manager.dp.current_def; - if (didHashChanged || (amDef && amDef.state() === 'pending')) { - return; - } + .then(function (result) { var data = result[0]; - if(data.action_id) { - self.action_manager.doAction(data.action_id[0]); - self.menu.open_action(data.action_id[0]); + if (data.action_id) { + return self.do_action(data.action_id[0]).then(function () { + self.menu.change_menu_section(self.menu.action_id_to_primary_menu_id(data.action_id[0])); + }); } else { - var first_menu_id = self.menu.$el.find("a:first").data("menu"); - if(first_menu_id) { - self.menu.menu_click(first_menu_id); - } + self.menu.openFirstApp(); } }); - }); - } else { - $(window).trigger('hashchange'); - } + } else { + return self.on_hashchange(); + } + }); }, - on_hashchange: function(event) { + + instanciate_menu_widgets: function () { + var self = this; + var defs = []; + return this.load_menus().then(function (menuData) { + self.menu_data = menuData; + + // Here, we instanciate every menu widgets and we immediately append them into dummy + // document fragments, so that their `start` method are executed before inserting them + // into the DOM. + if (self.menu) { + self.menu.destroy(); + } + self.menu = new Menu(self, menuData); + defs.push(self.menu.prependTo(self.$el)); + return $.when.apply($, defs); + }); + }, + + // -------------------------------------------------------------- + // URL state handling + // -------------------------------------------------------------- + on_hashchange: function (event) { if (this._ignore_hashchange) { this._ignore_hashchange = false; - return; + return $.when(); } var self = this; - this.clear_uncommitted_changes().then(function () { - var stringstate = event.getState(false); + return this.clear_uncommitted_changes().then(function () { + var stringstate = $.bbq.getState(false); if (!_.isEqual(self._current_state, stringstate)) { - var state = event.getState(true); - if(!state.action && state.menu_id) { - self.menu.is_bound.done(function() { - self.menu.menu_click(state.menu_id); - }); - } else { - self.action_manager.loadState(state, !!self._current_state).then(function () { - var action = self.action_manager.getCurrentAction(); - if (action) { - self.menu.open_action(action.id, state.menu_id); + var state = $.bbq.getState(true); + if (state.action || (state.model && (state.view_type || state.id))) { + return self.action_manager.loadState(state, !!self._current_state).then(function () { + if (state.menu_id) { + if (state.menu_id !== self.menu.current_primary_menu) { + core.bus.trigger('change_menu_section', state.menu_id); + } + } else { + var action = self.action_manager.getCurrentAction(); + if (action) { + var menu_id = self.menu.action_id_to_primary_menu_id(action.id); + if (menu_id) { + core.bus.trigger('change_menu_section', menu_id); + } + } } }); + } else if (state.menu_id) { + var action_id = self.menu.menu_id_to_action_id(state.menu_id); + return self.do_action(action_id, {clear_breadcrumbs: true}).then(function () { + core.bus.trigger('change_menu_section', state.menu_id); + }); + } else { + self.menu.openFirstApp(); } } self._current_state = stringstate; @@ -159,17 +174,88 @@ return AbstractWebClient.extend({ } }); }, - on_menu_action: function(options) { - this.action_manager.doAction(options.action_id, { - clear_breadcrumbs: true, - action_menu_id: options.id, - }); + + // -------------------------------------------------------------- + // Menu handling + // -------------------------------------------------------------- + on_app_clicked: function (ev) { + var self = this; + return this.menu_dm.add(data_manager.load_action(ev.data.action_id)) + .then(function (result) { + return self.action_mutex.exec(function () { + var completed = $.Deferred(); + var options = _.extend({}, ev.data.options, { + clear_breadcrumbs: true, + action_menu_id: ev.data.menu_id, + }); + $.when(self._openMenu(result, options)).fail(function () { + completed.resolve(); + }).done(function () { + self._on_app_clicked_done(ev) + .then(completed.resolve.bind(completed)) + .fail(completed.reject.bind(completed)); + }); + setTimeout(function () { + completed.resolve(); + }, 2000); + return completed; + }); + }); }, - toggle_fullscreen: function(fullscreen) { - this._super(fullscreen); - if (!fullscreen) { - core.bus.trigger('resize'); - } + _on_app_clicked_done: function (ev) { + core.bus.trigger('change_menu_section', ev.data.menu_id); + return $.Deferred().resolve(); + }, + on_menu_clicked: function (ev) { + var self = this; + return this.menu_dm.add(data_manager.load_action(ev.data.action_id)) + .then(function (result) { + return self.action_mutex.exec(function () { + var completed = $.Deferred(); + $.when(self._openMenu(result, { + clear_breadcrumbs: true, + })).always(function () { + completed.resolve(); + }); + + setTimeout(function () { + completed.resolve(); + }, 2000); + + return completed; + }); + }).always(function () { + self.$el.removeClass('o_mobile_menu_opened'); + }); + }, + /** + * Open the action linked to a menu. + * This function is mostly used to allow override in other modules. + * + * @private + * @param {Object} action + * @param {Object} options + * @returns {Deferred} + */ + _openMenu: function (action, options) { + return this.do_action(action, options); + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * Returns the left and top scroll positions of the main scrolling area + * (i.e. the action manager in desktop). + * + * @returns {Object} with keys left and top + */ + getScrollPosition: function () { + return { + left: this.action_manager.el.scrollLeft, + top: this.action_manager.el.scrollTop, + }; }, //-------------------------------------------------------------------------- @@ -178,29 +264,22 @@ return AbstractWebClient.extend({ /** * @override + * @private */ _onGetScrollPosition: function (ev) { - ev.data.callback({ - left: this.action_manager.el.scrollLeft, - top: this.action_manager.el.scrollTop, - }); + ev.data.callback(this.getScrollPosition()); }, /** * @override + * @private */ _onScrollTo: function (ev) { - var offset; - if (ev.data.selector) { + var offset = {top: ev.data.top, left: ev.data.left || 0}; + if (!offset.top) { 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}; + // Substract the position of the action_manager as it is the scrolling part + offset.top -= dom.getPosition(this.action_manager.el).top; } - this.action_manager.el.scrollTop = offset.top; this.action_manager.el.scrollLeft = offset.left; }, |
