From 6dc37ee461c967bc2180fddaa0910007b4552054 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Mon, 2 Jul 2018 16:49:41 +0200 Subject: [PATCH] [REF] web, *: share and adapt code for auto "more" buttons for navbars * portal, web_tour, website, base Before this commit, there were 3 different implementations for the same feature: the "more" button in the community navbar, the "+" button for the website menu and the "+" button in the enterprise navbar. As those implementation were now broken with BS4, it was a good time to share the code of those 3 features. --- addons/portal/static/src/scss/portal.scss | 5 + addons/web/static/src/js/chrome/menu.js | 59 +-------- addons/web/static/src/js/chrome/web_client.js | 6 +- addons/web/static/src/js/core/dom.js | 113 ++++++++++++++++++ addons/web/static/src/scss/navbar.scss | 2 +- addons/web/static/src/scss/ui.scss | 10 +- addons/web/views/webclient_templates.xml | 10 +- addons/web_tour/static/src/js/tour_manager.js | 5 +- addons/website/static/src/js/content/menu.js | 71 +---------- odoo/addons/base/tests/test_translate.py | 6 +- 10 files changed, 145 insertions(+), 142 deletions(-) diff --git a/addons/portal/static/src/scss/portal.scss b/addons/portal/static/src/scss/portal.scss index 9a9269fb649..d742175fa5d 100644 --- a/addons/portal/static/src/scss/portal.scss +++ b/addons/portal/static/src/scss/portal.scss @@ -32,8 +32,13 @@ header { img { @include o-w-fill-navbar; + width: auto; + max-width: none; } } + .nav-link { + white-space: nowrap; + } } .navbar { margin-bottom: 0; diff --git a/addons/web/static/src/js/chrome/menu.js b/addons/web/static/src/js/chrome/menu.js index 8613e90afa8..c96d4b17f8c 100644 --- a/addons/web/static/src/js/chrome/menu.js +++ b/addons/web/static/src/js/chrome/menu.js @@ -2,6 +2,7 @@ odoo.define('web.Menu', function (require) { "use strict"; var core = require('web.core'); +var dom = require('web.dom'); var session = require('web.session'); var Widget = require('web.Widget'); @@ -37,66 +38,10 @@ var Menu = Widget.extend({ } 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'); + dom.initAutoMoreMenu(this.$el); this.is_bound.resolve(); }, - - /** - * 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 diff --git a/addons/web/static/src/js/chrome/web_client.js b/addons/web/static/src/js/chrome/web_client.js index c4886cd0e89..d3ebabfab1e 100644 --- a/addons/web/static/src/js/chrome/web_client.js +++ b/addons/web/static/src/js/chrome/web_client.js @@ -3,6 +3,7 @@ odoo.define('web.WebClient', function (require) { var AbstractWebClient = require('web.AbstractWebClient'); 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'); @@ -48,6 +49,9 @@ return AbstractWebClient.extend({ return $.when(systray_menu_loaded, user_menu_loaded).then(function() { self.menu.start(); self.bind_hashchange(); + setTimeout(function () { + self.menu.$el.removeClass('d-none'); + }, 200) }); }, @@ -165,7 +169,7 @@ return AbstractWebClient.extend({ toggle_fullscreen: function(fullscreen) { this._super(fullscreen); if (!fullscreen) { - this.menu.reflow(); + core.bus.trigger('resize'); } }, diff --git a/addons/web/static/src/js/core/dom.js b/addons/web/static/src/js/core/dom.js index d695cf18296..a34a207fda8 100644 --- a/addons/web/static/src/js/core/dom.js +++ b/addons/web/static/src/js/core/dom.js @@ -20,6 +20,7 @@ odoo.define('web.dom', function (require) { * something happens in the DOM. */ +var config = require('web.config'); var core = require('web.core'); /** @@ -315,5 +316,117 @@ return { .select(); } }, + /** + * Creates an automatic 'more' dropdown-menu for a set of navbar items. + * + * @param {jQuery} $el + * @param {Object} [options] + * @param {string} [options.unfoldable='none'] + * @param {function} [options.maxWidth] + * @param {string} [options.sizeClass='SM'] + */ + initAutoMoreMenu: function ($el, options) { + options = _.extend({ + unfoldable: 'none', + maxWidth: false, + sizeClass: 'SM', + }, options || {}); + + var $extraItemsToggle = null; + + var debouncedAdapt = _.debounce(_adapt, 250); + core.bus.on('resize', null, debouncedAdapt); + _adapt(); + + $el.data('dom:autoMoreMenu:destroy', function () { + _restore(); + core.bus.off('resize', null, debouncedAdapt); + $el.removeData('dom:autoMoreMenu:destroy'); + }); + + function _restore() { + if ($extraItemsToggle === null) { + return; + } + var $items = $extraItemsToggle.children('.dropdown-menu').children(); + $items.addClass('nav-item'); + $items.children().removeClass('dropdown-item').addClass('nav-link'); + $items.insertBefore($extraItemsToggle); + $extraItemsToggle.remove(); + $extraItemsToggle = null; + } + + function _adapt() { + if (!$el.is(':visible')) { + return; + } + + _restore(); + if (config.device.size_class <= config.device.SIZES[options.sizeClass]) { + return; + } + + var $allItems = $el.children(); + var $unfoldableItems = $allItems.filter(options.unfoldable); + var $items = $allItems.not($unfoldableItems); + + var maxWidth = 0; + if (options.maxWidth) { + maxWidth = options.maxWidth(); + } else { + var rect = $el[0].getBoundingClientRect(); + var style = window.getComputedStyle($el[0]); + maxWidth = (rect.right - rect.left); + maxWidth -= (parseFloat(style.paddingLeft) + parseFloat(style.paddingRight) + parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth)); + maxWidth -= _.reduce($unfoldableItems, function (sum, el) { + return sum + computeFloatOuterWidthWithMargins(el); + }, 0); + } + + var nbItems = $items.length; + var menuItemsWidth = _.reduce($items, function (sum, el) { + return sum + computeFloatOuterWidthWithMargins(el); + }, 0); + + if (maxWidth - menuItemsWidth >= -0.001) { + return + } + + var $dropdownMenu = $('