summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/widgets
diff options
context:
space:
mode:
authorJulien Mougenot <jum@odoo.com>2020-03-11 09:38:28 +0000
committerJulien Mougenot <jum@odoo.com>2020-03-13 14:08:33 +0000
commitfbf347498f1cc7b74ef373179b7bcae201715c24 (patch)
treeb8d1eb29ec93b6c2e43dbade712e636d32fe6716 /addons/web/static/src/js/widgets
parent0a129284e4856b6485c48a194fd86c3ab5c736e7 (diff)
[REF] web: Control panel Owl
Refactoring of the control panel with the Owl framework. The MVC structure of the control panel has been changed to a 'component <-> model' relationship. As such: - Removed the old control panel: view/model/controller/renderer - Created a new Owl Component (ControlPanel) - Created a new class: the Model (an Owl Store did not fit here) and a newly designed ControlPanelModel extending it Refactored all inner subcomponents, including: - Pager - ActionMenus (peviously SideBar) - Filter menu > Custom filter item - Group by menu > Custom group by item > Removal of groupby_menu_mixin (and refactored graph controller in sibling commit) - Time range menu - Favorite menu > Custom favorite item > Moved the favorite menu registry to the favorite_menu file - Search bar > Removed and splitted auto-completion dropdown logic between the new SearchBar Component (descriptions) and to the new ControlPanelModel (domains). The templates (all in web/base.xml) have been adapted to the new standards. Most of the DOM structure remains unchanged with some occasional improvements (replace hiding with not rendering, removed pointless wrapper divs, etc.). Part of task 2196029 Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
Diffstat (limited to 'addons/web/static/src/js/widgets')
-rw-r--r--addons/web/static/src/js/widgets/auto_complete.js268
-rw-r--r--addons/web/static/src/js/widgets/dropdown_menu.js145
2 files changed, 0 insertions, 413 deletions
diff --git a/addons/web/static/src/js/widgets/auto_complete.js b/addons/web/static/src/js/widgets/auto_complete.js
deleted file mode 100644
index fef24527e46..00000000000
--- a/addons/web/static/src/js/widgets/auto_complete.js
+++ /dev/null
@@ -1,268 +0,0 @@
-odoo.define('web.AutoComplete', function (require) {
-"use strict";
-
-var Widget = require('web.Widget');
-
-return Widget.extend({
- template: "SearchView.autocomplete",
-
- // Parameters for autocomplete constructor:
- //
- // parent: this is used to detect keyboard events
- //
- // options.source: function ({term:query}, callback). This function will be called to
- // obtain the search results corresponding to the query string. It is assumed that
- // options.source will call callback with the results.
- // options.select: function (ev, {item: {facet:facet}}). Autocomplete widget will call
- // that function when a selection is made by the user
- // options.get_search_string: function (). This function will be called by autocomplete
- // to obtain the current search string.
- init: function (parent, options) {
- this._super(parent);
- this.$input = options.$input;
- this.source = options.source;
- this.select = options.select;
- this.get_search_string = options.get_search_string;
-
- this.current_result = null;
-
- this.searching = true;
- this.search_string = '';
- this.current_search = null;
- this._isInputComposing = false;
- },
- start: function () {
- var self = this;
- this.$input.on('compositionend', function (ev) {
- self._isInputComposing = false;
- });
- this.$input.on('compositionstart', function (ev) {
- self._isInputComposing = true;
- });
- this.$input.on('keyup', function (ev) {
- if (ev.which === $.ui.keyCode.RIGHT && !self._isInputComposing) {
- self.searching = true;
- ev.preventDefault();
- return;
- }
- if (ev.which === $.ui.keyCode.ENTER && !self._isInputComposing) {
- if (self.search_string.length) {
- self.select_item(ev);
- }
- return;
- }
- self._updateSearch();
- });
- this.$input.on('input', function (ev) {
- if (ev.originalEvent.inputType === 'insertCompositionText') {
- // click inside keyboard IME suggestions menu
- self._updateSearch();
- }
- });
- this.$input.on('keypress', function (ev) {
- if (ev.which > 31 && ev.which !== 127) {
- // we filter control character out of the search string
- self.search_string = self.search_string + String.fromCharCode(ev.which);
- }
- if (self.search_string.length) {
- self.searching = true;
- var search_string = self.search_string;
- self.initiate_search(search_string);
- } else {
- self.close();
- }
- });
- this.$input.on('keydown', function (ev) {
- if (self._isInputComposing) {
- return;
- }
- switch (ev.which) {
- case $.ui.keyCode.ENTER:
-
- // TAB and direction keys are handled at KeyDown because KeyUp
- // is not guaranteed to fire.
- // See e.g. https://github.com/aef-/jquery.masterblaster/issues/13
- case $.ui.keyCode.TAB:
- if (self.search_string.length) {
- self.select_item(ev);
- }
- break;
- case $.ui.keyCode.DOWN:
- self.move('down');
- self.searching = false;
- ev.preventDefault();
- break;
- case $.ui.keyCode.UP:
- self.move('up');
- self.searching = false;
- ev.preventDefault();
- break;
- case $.ui.keyCode.RIGHT:
- if(self.$input[0].selectionStart < self.search_string.length) {
- ev.stopPropagation();
- return;
- }
- self.searching = false;
- var current = self.current_result;
- if (current && current.expand && !current.expanded) {
- self.expand();
- self.searching = true;
- ev.stopPropagation();
- }
- ev.preventDefault();
- break;
- case $.ui.keyCode.LEFT:
- if(self.$input[0].selectionStart > 0) {
- ev.stopPropagation();
- }
- break;
- case $.ui.keyCode.ESCAPE:
- self.close();
- self.searching = false;
- break;
- }
- });
- },
- initiate_search: function (query) {
- if (query === this.search_string && query !== this.current_search) {
- this.search(query);
- }
- },
- search: function (query) {
- var self = this;
- this.current_search = query;
- this.source({term:query}, function (results) {
- if (results.length) {
- self.render_search_results(results);
- self.focus_element(self.$('li:first-child'));
- } else {
- self.close();
- }
- });
- },
- render_search_results: function (results) {
- var self = this;
- var $list = this.$el;
- $list.empty();
- results.forEach(function (result) {
- var $item = self.make_list_item(result).appendTo($list);
- result.$el = $item;
- });
- // IE9 doesn't support addEventListener with option { once: true }
- this.el.onmousemove = function (ev) {
- self.$('li').each(function (index, li) {
- li.onmouseenter = self.focus_element.bind(self, $(li));
- });
- var targetFocus = ev.target.tagName === 'LI' ?
- ev.target :
- ev.target.closest('li');
- self.focus_element($(targetFocus));
- self.el.onmousemove = null;
- };
- this.show();
- },
- make_list_item: function (result) {
- var self = this;
- var $li = $('<li>')
- .mousedown(function (ev) {
- if (ev.button === 0) { // left button
- self.select(ev, {item: {facet: result.facet}});
- self.close();
- }
- })
- .data('result', result);
- if (result.expand) {
- var $expand = $('<a class="o-expand" href="#">').appendTo($li);
- $expand.mousedown(function (ev) {
- ev.stopPropagation();
- ev.preventDefault(); // to prevent dropdown from closing
- if (result.expanded) {
- self.fold();
- } else {
- self.expand();
- }
- });
- $expand.click(function(ev) {
- ev.preventDefault(); // to prevent url from changing due to href="#"
- });
- result.expanded = false;
- }
- if (result.indent) $li.addClass('o-indent');
- $li.append($('<a href="#">').html(result.label));
- return $li;
- },
- expand: function () {
- var self = this;
- var current_result = this.current_result;
- current_result.expand(this.get_search_string()).then(function (results) {
- (results || [{label: '(no result)'}]).reverse().forEach(function (result) {
- result.indent = true;
- var $li = self.make_list_item(result);
- current_result.$el.after($li);
- });
- self.current_result.expanded = true;
- self.current_result.$el.find('a.o-expand').removeClass('o-expand').addClass('o-expanded');
- });
- },
- fold: function () {
- var $next = this.current_result.$el.next();
- while ($next.hasClass('o-indent')) {
- $next.remove();
- $next = this.current_result.$el.next();
- }
- this.current_result.expanded = false;
- this.current_result.$el.find('a.o-expanded').removeClass('o-expanded').addClass('o-expand');
- },
- focus_element: function ($li) {
- this.$('li').removeClass('o-selection-focus');
- $li.addClass('o-selection-focus');
- this.current_result = $li.data('result');
- },
- select_item: function (ev) {
- this.select(ev, {item: {facet: this.current_result.facet}});
- this.close();
- },
- show: function () {
- this.$el.show();
- },
- close: function () {
- this.current_search = null;
- this.search_string = '';
- this.searching = true;
- this.$el.hide();
- },
- move: function (direction) {
- var $next;
- if (direction === 'down') {
- $next = this.$('li.o-selection-focus').next();
- if (!$next.length) $next = this.$('li').first();
- } else {
- $next = this.$('li.o-selection-focus').prev();
- if (!$next.length) $next = this.$('li').last();
- }
- this.focus_element($next);
- },
- is_expandable: function () {
- return !!this.$('.o-selection-focus .o-expand').length;
- },
- is_expanded: function() {
- return this.$el[0].style.display === "block";
- },
- /**
- * Update search dropdown menu based on new input content.
- *
- * @private
- */
- _updateSearch: function () {
- var search_string = this.get_search_string();
- if (this.search_string !== search_string) {
- if (search_string.length) {
- this.search_string = search_string;
- this.initiate_search(search_string);
- } else {
- this.close();
- }
- }
- },
-});
-});
diff --git a/addons/web/static/src/js/widgets/dropdown_menu.js b/addons/web/static/src/js/widgets/dropdown_menu.js
deleted file mode 100644
index d8663731c67..00000000000
--- a/addons/web/static/src/js/widgets/dropdown_menu.js
+++ /dev/null
@@ -1,145 +0,0 @@
-odoo.define('web.DropdownMenu', function (require) {
-"use strict";
-
-var core = require('web.core');
-var Widget = require('web.Widget');
-
-var QWeb = core.qweb;
-
-var _t = core._t;
-
-var DropdownMenu = Widget.extend({
- template: 'web.DropdownMenu',
-
- events: {
- 'click .o_menu_item': '_onItemClick',
- 'click .o_item_option': '_onOptionClick',
- 'click span.o_trash_button': '_onTrashButtonClick',
- 'hidden.bs.dropdown': '_onBootstrapClose',
- 'click .dropdown-item-text': '_onDropDownItemTextClick',
- },
-
- init: function (parent, items) {
- this._super(parent);
- // should be specified
- this.dropdownCategory = null;
- this.dropdownTitle = null;
- this.dropdownIcon = null;
- this.dropdownSymbol = false;
- // this parameter fixes the menu style. By default,
- // the style used is the one used in the search view
- this.dropdownStyle = {
- 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;
- this.openItems = {};
- },
- /**
- * @override
- */
- start: function () {
- this.$menu = this.$('.o_dropdown_menu');
- this.$dropdownReference = this.$('.o_dropdown_toggler_btn');
-
- if (_t.database.parameters.direction === 'rtl') {
- this.$menu.addClass('dropdown-menu-right');
- }
- return this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * @param {Object} items
- */
- update: function (items) {
- this.items = items;
- this._renderMenuItems();
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @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);
- },
-
- //--------------------------------------------------------------------------
- // 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 () {
- this.openItems = {};
- this._renderMenuItems();
- },
- /**
- * Reacts to click on bootstrap's dropdown-item-text
- * Protects against Bootstrap dropdown close from inside click
- *
- * @private
- */
- _onDropDownItemTextClick: function (ev) {
- ev.stopPropagation();
- },
- /**
- * @private
- * @param {MouseEvent} event
- */
- _onItemClick: function (event) {
- event.preventDefault();
- event.stopPropagation();
- var id = $(event.currentTarget).data('id');
- var item = this.items.find(function (item) {
- return item.id === id;
- });
- if (item.hasOptions) {
- this.openItems[id] = !this.openItems[id];
- this._renderMenuItems();
- } else {
- this.trigger_up('menu_item_clicked', {id: id});
- }
- },
- /**
- * @private
- * @param {MouseEvent} event
- */
- _onOptionClick: function (event) {
- event.preventDefault();
- event.stopPropagation();
- var optionId = $(event.currentTarget).data('option_id');
- var id = $(event.currentTarget).data('item_id');
- this.trigger_up('item_option_clicked', {id: id, optionId: optionId});
- },
- /**
- * @private
- * @param {MouseEvent} event
- *
- * To implement in child
- */
- _onTrashButtonClick: function (event) {
- },
-});
-
-return DropdownMenu;
-
-}); \ No newline at end of file