summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/menu.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/menu.js')
-rw-r--r--addons/web/static/src/js/menu.js261
1 files changed, 261 insertions, 0 deletions
diff --git a/addons/web/static/src/js/menu.js b/addons/web/static/src/js/menu.js
new file mode 100644
index 00000000000..c7bf4b07828
--- /dev/null
+++ b/addons/web/static/src/js/menu.js
@@ -0,0 +1,261 @@
+odoo.define('web.Menu', ['web.core', 'web.session', 'web.Widget'], function (require) {
+"use strict";
+
+var core = require('web.core');
+var session = require('web.session');
+var Widget = require('web.Widget');
+
+var QWeb = core.qweb;
+
+var Menu = Widget.extend({
+ init: function() {
+ var self = this;
+ this._super.apply(this, arguments);
+ this.is_bound = $.Deferred();
+ this.data = {data:{children:[]}};
+ this.on("menu_bound", this, function() {
+ // launch the fetch of needaction counters, asynchronous
+ var $all_menus = self.$el.parents('body').find('.oe_webclient').find('[data-menu]');
+ var all_menu_ids = _.map($all_menus, function (menu) {return parseInt($(menu).attr('data-menu'), 10);});
+ if (!_.isEmpty(all_menu_ids)) {
+ this.do_load_needaction(all_menu_ids);
+ }
+ });
+ core.bus.on('do_reload_needaction', this, this.do_reload_needaction);
+ },
+ start: function() {
+ this._super.apply(this, arguments);
+ return this.bind_menu();
+ },
+ do_reload: function() {
+ var self = this;
+ self.bind_menu();
+ },
+ bind_menu: function() {
+ var self = this;
+ this.$secondary_menus = this.$el.parents().find('.oe_secondary_menus_container');
+ this.$secondary_menus.on('click', 'a[data-menu]', this.on_menu_click);
+ this.$el.on('click', 'a[data-menu]', this.on_top_menu_click);
+ // Hide second level submenus
+ this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').hide();
+ if (self.current_menu) {
+ self.open_menu(self.current_menu);
+ }
+ this.trigger('menu_bound');
+
+ var lazyreflow = _.debounce(this.reflow.bind(this), 200);
+ core.bus.on('resize', this, function() {
+ if ($(window).width() < 768 ) {
+ lazyreflow('all_outside');
+ } else {
+ lazyreflow();
+ }
+ });
+ core.bus.trigger('resize');
+
+ this.is_bound.resolve();
+ },
+ do_load_needaction: function (menu_ids) {
+ var self = this;
+ menu_ids = _.compact(menu_ids);
+ if (_.isEmpty(menu_ids)) {
+ return $.when();
+ }
+ return this.rpc("/web/menu/load_needaction", {'menu_ids': menu_ids}).done(function(r) {
+ self.on_needaction_loaded(r);
+ });
+ },
+ on_needaction_loaded: function(data) {
+ var self = this;
+ this.needaction_data = data;
+ _.each(this.needaction_data, function (item, menu_id) {
+ var $item = self.$secondary_menus.find('a[data-menu="' + menu_id + '"]');
+ $item.find('.badge').remove();
+ if (item.needaction_counter && item.needaction_counter > 0) {
+ $item.append(QWeb.render("Menu.needaction_counter", { widget : item }));
+ }
+ });
+ },
+ /**
+ * Reflow the menu items and dock overflowing items into a "More" menu item.
+ * Automatically called when 'menu_bound' event is triggered and on window resizing.
+ *
+ * @param {string} behavior If set to 'all_outside', all the items are displayed.
+ * If not set, only the overflowing items are hidden.
+ */
+ reflow: function(behavior) {
+ var self = this;
+ var $more_container = this.$('#menu_more_container').hide();
+ var $more = this.$('#menu_more');
+ var $systray = this.$el.parents().find('.oe_systray');
+
+ $more.children('li').insertBefore($more_container); // Pull all the items out of the more menu
+
+ // 'all_outside' beahavior should display all the items, so hide the more menu and exit
+ if (behavior === 'all_outside') {
+ // Show list of menu items
+ self.$el.show();
+ this.$el.find('li').show();
+ $more_container.hide();
+ return;
+ }
+
+ // Hide all menu items
+ var $toplevel_items = this.$el.find('li').not($more_container).not($systray.find('li')).hide();
+ // Show list of menu items (which is empty for now since all menu items are hidden)
+ self.$el.show();
+ $toplevel_items.each(function() {
+ var remaining_space = self.$el.parent().width() - $more_container.outerWidth();
+ self.$el.parent().children(':visible').each(function() {
+ remaining_space -= $(this).outerWidth();
+ });
+
+ if ($(this).width() >= remaining_space) {
+ return false; // the current item will be appended in more_container
+ }
+ $(this).show(); // show the current item in menu bar
+ });
+ $more.append($toplevel_items.filter(':hidden').show());
+ $more_container.toggle(!!$more.children().length);
+ // Hide toplevel item if there is only one
+ var $toplevel = self.$el.children("li:visible");
+ if ($toplevel.length === 1) {
+ $toplevel.hide();
+ }
+ },
+ /**
+ * 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;
+ }
+
+ // Activate current main menu
+ this.$el.find('.active').removeClass('active');
+ $main_menu.parent().addClass('active');
+
+ // Show current sub menu
+ this.$secondary_menus.find('.oe_secondary_menu').hide();
+ $sub_menu.show();
+
+ // Hide/Show the leftbar menu depending of the presence of sub-items
+ this.$secondary_menus.parent('.oe_leftbar').toggle(!!$sub_menu.children().length);
+
+ // Activate current menu item and show parents
+ this.$secondary_menus.find('.active').removeClass('active');
+ if ($main_menu !== $clicked_menu) {
+ $clicked_menu.parents().show();
+ if ($clicked_menu.is('.oe_menu_toggler')) {
+ $clicked_menu.toggleClass('oe_menu_opened').siblings('.oe_secondary_submenu:first').toggle();
+ } else {
+ $clicked_menu.parent().addClass('active');
+ }
+ }
+ // 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'} :'destroy');
+ });
+ },
+ /**
+ * Call open_menu with the first menu_item matching an action_id
+ *
+ * @param {Number} id the action_id to match
+ */
+ open_action: function (id) {
+ var $menu = this.$el.add(this.$secondary_menus).find('a[data-action-id="' + id + '"]');
+ var menu_id = $menu.data('menu');
+ if (menu_id) {
+ this.open_menu(menu_id);
+ }
+ },
+ /**
+ * Process a click on a menu item
+ *
+ * @param {Number} id the menu_id
+ * @param {Boolean} [needaction=false] whether the triggered action should execute in a `needs action` context
+ */
+ menu_click: function(id, needaction) {
+ if (!id) { return; }
+
+ // 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 + ']');
+ }
+ 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');
+ }
+ }
+ }
+ if (action_id) {
+ this.trigger('menu_click', {
+ action_id: action_id,
+ needaction: needaction,
+ 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);
+ },
+ do_reload_needaction: function () {
+ var self = this;
+ if (self.current_menu) {
+ self.do_load_needaction([self.current_menu]).then(function () {
+ self.trigger("need_action_reloaded");
+ });
+ }
+ },
+ /**
+ * Jquery event handler for menu click
+ *
+ * @param {Event} ev the jquery event
+ */
+ on_top_menu_click: function(ev) {
+ ev.preventDefault();
+ var self = this;
+ var id = $(ev.currentTarget).data('menu');
+
+ // Fetch the menu leaves ids in order to check if they need a 'needaction'
+ var $secondary_menu = this.$el.parents().find('.oe_secondary_menu[data-menu-parent=' + id + ']');
+ var $menu_leaves = $secondary_menu.children().find('.oe_menu_leaf');
+ var menu_ids = _.map($menu_leaves, function (leave) {return parseInt($(leave).attr('data-menu'), 10);});
+
+ self.do_load_needaction(menu_ids).then(function () {
+ self.trigger("need_action_reloaded");
+ });
+ this.$el.parents().find(".oe_secondary_menus_container").scrollTop(0,0);
+
+ this.on_menu_click(ev);
+ },
+ on_menu_click: function(ev) {
+ ev.preventDefault();
+ var needaction = $(ev.target).is('div#menu_counter');
+ this.menu_click($(ev.currentTarget).data('menu'), needaction);
+ },
+});
+
+return Menu;
+});