odoo.define('web.DropdownMenu', function (require) { "use strict"; var core = require('web.core'); var Widget = require('web.Widget'); var QWeb = core.qweb; var DropdownMenu = Widget.extend({ template: 'web.DropdownMenu', events: { 'click .o_menu_item': '_onItemClick', 'click .o_item_option': '_onOptionClick', 'click span.o_trash_button': '_onTrashClick', 'hidden.bs.dropdown': '_onBootstrapClose', }, /** * override * * @param {Widget} parent * @param {Object} dropdownHeader object used to customize the dropdown menu. * @param {String} dropdownHeader.title * @param {String} dropdownHeader.icon * @param {String} dropdownHeader.symbol * @param {String} dropdownHeader.category descripbes the type of items * @param {String} dropdownHeader.style the button style * @param {Object[]} items list of menu items * * Menu items: * * * itemId: string; (optional) unique id associated with the item * * description: string; label printed on screen * * groupId: string; * * isActive: boolean; (optional) determines if the item is considered active * * isOpen: boolean; (optional) in case there are options the submenu presenting the options is opened or closed according to isOpen * * isRemovable: boolean; (optional) can be removed from menu options: array of objects with 'optionId' and 'description' keys; (optional) * * currentOptionId: string refers to an optionId that is activated if item is active (optional) */ init: function (parent, dropdownHeader, items) { this._super(parent); this.dropdownCategory = dropdownHeader.category; this.dropdownTitle = dropdownHeader.title; this.dropdownIcon = dropdownHeader.icon; this.dropdownSymbol = dropdownHeader.symbol || false; // this parameter fixes the menu style. By default, // the style used is the one used in the search view this.dropdownStyle = dropdownHeader.style || { el: {class: 'btn-group o_dropdown', attrs: {}}, mainButton: {class: 'o_dropdown_toggler_btn btn btn-secondary dropdown-toggle' + (this.dropdownSymbol ? ' o-no-caret' : '')}, }; this.items = items; _.each(this.items, this._prepareItem.bind(this)); }, /** * override */ start: function () { this.$menu = this.$('.o_dropdown_menu'); }, //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- /** * @param {string[]} activeItemIds */ updateItemsStatus: function (activeItemIds) { _.each(this.items, function (item) { if (!_.contains(activeItemIds, item.itemId)) { item.isActive = false; item.currentOptionId = false; } else { item.isActive = true; item.currentOptionId = item.hasOptions ? (item.currentOptionId || item.defaultOptionId) : false; } }); this._renderMenuItems(); }, //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- /** * @private * @param {string} itemId */ _removeItem: function (itemId) { var item, itemIndex; item = _.findWhere(this.items, {itemId: itemId}); itemIndex = this.items.indexOf(item); this.items.splice(itemIndex, 1); this._renderMenuItems(); var eventData = { category: this.dropdownCategory, itemId: itemId, groupId: item.groupId, }; this.trigger_up('menu_item_deleted', eventData); }, /** * @private */ _renderMenuItems: function () { var newMenuItems = QWeb.render('DropdownMenu.MenuItems', {widget: this}); this.$el.find('.o_menu_item, .dropdown-divider[data-removable="1"]').remove(); this.$('.o_dropdown_menu').prepend($(newMenuItems)); }, /** * this function computes some default values for some of the item keys * according to values passed at initialization * * @private * @param {Object} item */ _prepareItem: function (item) { item.itemId = item.itemId || _.uniqueId('__item__'); item.isOpen = item.isOpen || false; item.isRemovable = item.isRemovable || false; if (item.options && item.options.length !== 0) { item.options = _.sortBy(item.options, 'groupId'); item.hasOptions = true; item.defaultOptionId = item.defaultOptionId || this.defaultOptionId; item.currentOptionId = item.isActive && item.defaultOptionId ? item.defaultOptionId : false; } }, /** * @private * @param {string} itemId */ _toggleMenuItem: function (itemId) { var item = _.findWhere(this.items, {itemId: itemId}); if (!item.hasOptions) { item.isActive = !item.isActive; var eventData = { category: this.dropdownCategory, itemId: itemId, isActive: item.isActive, groupId: item.groupId, }; this._renderMenuItems(); this.trigger_up('menu_item_toggled', eventData); } if (item.hasOptions) { item.isOpen = !item.isOpen; this._renderMenuItems(); } }, /** * @private * @param {string} itemId * @param {string} optionId */ _toggleItemOption: function (itemId, optionId) { var item = _.findWhere(this.items, {itemId: itemId}); var initialState = item.isActive; if (item.currentOptionId === optionId) { item.isActive = false; item.currentOptionId = false; } else { item.isActive = true; item.currentOptionId = optionId; } var eventData = { category: this.dropdownCategory, itemId: itemId, groupId: item.groupId, isActive: item.isActive, optionId: item.currentOptionId, }; this._renderMenuItems(); if (item.isActive !== initialState) { this.trigger_up('menu_item_toggled', eventData); } else { this.trigger_up('item_option_changed', eventData); } }, //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- /** * This method is called when Bootstrap trigger a 'close' event (for example * when the user clicks outside the dropdown menu). * * @private * @param {jQueryEvent} event */ _onBootstrapClose: function () { _.each(this.items, function (item) { item.isOpen = false; }); this._renderMenuItems(); }, /** * @private * @param {MouseEvent} event */ _onItemClick: function (event) { event.preventDefault(); event.stopPropagation(); var itemId = $(event.currentTarget).data('id'); this._toggleMenuItem(itemId); }, /** * @private * @param {MouseEvent} event */ _onOptionClick: function (event) { event.preventDefault(); event.stopPropagation(); var optionId = $(event.currentTarget).data('option_id'); var itemId = $(event.currentTarget).data('item_id'); this._toggleItemOption(itemId, optionId); }, /** * @private * @param {MouseEvent} event */ _onTrashClick: function (event) { event.stopPropagation(); var itemId = $(event.currentTarget).data('id'); this._removeItem(itemId); }, }); return DropdownMenu; });