summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/widgets/dropdown_menu.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/widgets/dropdown_menu.js')
-rw-r--r--addons/web/static/src/js/widgets/dropdown_menu.js243
1 files changed, 243 insertions, 0 deletions
diff --git a/addons/web/static/src/js/widgets/dropdown_menu.js b/addons/web/static/src/js/widgets/dropdown_menu.js
new file mode 100644
index 00000000000..d7e4bff32a4
--- /dev/null
+++ b/addons/web/static/src/js/widgets/dropdown_menu.js
@@ -0,0 +1,243 @@
+odoo.define('web.DropdownMenu', function (require) {
+"use strict";
+
+var core = require('web.core');
+var Widget = require('web.Widget');
+
+var QWeb = core.qweb;
+
+// interface IItem {
+// itemId: string;
+// description: string;
+// groupId: string;
+// isActive: boolean; (optional)
+// isOpen: boolean; (optional)
+// isRemovable: boolean; (optional)
+// options: array of objects with 'optionId' and 'description' keys; (optional)
+// defaultOptionId: string refers to an optionId (optional)
+// currentOptionId: string refers to an optionId that is activated if item is active (optional)
+// }
+
+var DropdownMenu = Widget.extend({
+ template: 'web.DropdownMenu',
+ events: {
+ 'click li.o_menu_item': '_onItemClick',
+ 'click li.o_item_option': '_onOptionClick',
+ 'click li.o_menu_item span.o_submenu_switcher': '_openItemOptions',
+ 'click span.o_trash_button': '_onTrashClick',
+ 'hidden.bs.dropdown': '_onBootstrapClose',
+ },
+
+ /**
+ * @param {Widget} parent
+ * @param {Object} dropdowHeader object with keys 'title' and 'icon'
+ * @param {Object} items values are arrays of items (see interface IItems)
+ * @param {Object} itemGenerator
+ */
+ init: function (parent, dropdownHeader, items) {
+ this._super(parent);
+ this.dropdownCategory = dropdownHeader.category;
+ this.dropdownTitle = dropdownHeader.title;
+ this.dropdownIcon = dropdownHeader.icon;
+ this.dropdownSymbol = dropdownHeader.symbol || 'caret';
+ // 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-sm btn-default dropdown-toggle'},
+ };
+ this.items = _.sortBy(items, 'groupId');
+ _.each(this.items, this._prepareItem.bind(this));
+ },
+
+ start: function () {
+ this.$menu = this.$('.o_dropdown_menu');
+ },
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @param {Array[number]} groupIds
+ */
+ unsetGroups: function (groupIds) {
+ var self = this;
+ _.each(self.items, function (item) {
+ if (_.contains(groupIds, item.groupId)) {
+ item.isActive = false;
+ item.currentOptionId = 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,.divider[data-removable="1"]').remove();
+ this.$('ul.o_dropdown_menu').prepend($(newMenuItems));
+ },
+ /**
+ * @private
+ * @param {Object} item
+ */
+ _prepareItem: function (item) {
+ item.isOpen = item.isOpen || false;
+ item.isRemovable = item.isRemovable || false;
+ item.hasOptions = item.options && item.options.length !== 0 ? true : false;
+ item.defaultOptionId = item.hasOptions ?
+ (item.defaultOptionId || item.options[0].optionId) :
+ false;
+ item.currentOptionId = item.isActive ? (item.currentOptionId || item.defaultOptionId ) : false;
+ },
+ /**
+ * @private
+ * @param {string} itemId
+ */
+ _toggleMenuItem: function (itemId) {
+ var item = _.findWhere(this.items, {itemId: itemId});
+ item.isActive = !item.isActive;
+ var eventData = {
+ category: this.dropdownCategory,
+ itemId: itemId,
+ isActive: item.isActive,
+ groupId: item.groupId,
+ };
+ if (item.hasOptions) {
+ if (item.isActive) {
+ item.currentOptionId = item.defaultOptionId;
+ eventData.optionId = item.currentOptionId;
+ } else {
+ item.currentOptionId = false;
+ }
+ }
+ this._renderMenuItems();
+ this.trigger_up('menu_item_toggled', eventData);
+ },
+ /**
+ * @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);
+ }
+ },
+ /**
+ * @private
+ * @param {string} itemId
+ */
+ _toggleMenuOptions: function (itemId) {
+ var item = _.findWhere(this.items, {itemId: itemId});
+ item.isOpen = !item.isOpen;
+ this._renderMenuItems();
+ },
+
+ //--------------------------------------------------------------------------
+ // 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);
+ },
+ /**
+ * @private
+ * @param {MouseEvent} event
+ */
+ _openItemOptions: function (event) {
+ // we preventdefault the event here to avoid changing the hash # in the
+ // url.
+ event.preventDefault();
+ event.stopPropagation();
+ var itemId = $(event.currentTarget).data('id');
+ this._toggleMenuOptions(itemId);
+ },
+});
+
+return DropdownMenu;
+
+}); \ No newline at end of file