summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/chrome/menu.js
diff options
context:
space:
mode:
authorAlexandre Kühn <aku@odoo.com>2018-09-17 12:28:39 +0200
committerAlexandre Kühn <aku@odoo.com>2018-09-17 16:46:53 +0200
commit19eacf7d23c9413de4430a3422b5ed74b37ef242 (patch)
treefc96508d942cf2a31465545293f8856bb85838aa /addons/web/static/src/js/chrome/menu.js
parent110d37a31d579d4a221e0a0abc275bd6920a5159 (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.js300
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;
+
});