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/menu.js | |
| 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/menu.js')
| -rw-r--r-- | addons/web/static/src/js/chrome/menu.js | 300 |
1 files changed, 171 insertions, 129 deletions
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; + }); |
