You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
6.3 KiB

  1. /**********************************************************************************
  2. *
  3. * Copyright (C) 2017 MuK IT GmbH
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. **********************************************************************************/
  19. odoo.define('muk_web_theme.AppsMenu', function (require) {
  20. "use strict";
  21. var core = require('web.core');
  22. var config = require("web.config");
  23. var session = require("web.session");
  24. var AppsMenu = require("web.AppsMenu");
  25. var _t = core._t;
  26. var QWeb = core.qweb;
  27. AppsMenu.include({
  28. events: _.extend({}, AppsMenu.prototype.events, {
  29. "keydown .mk_search_input input": "_onSearchResultsNavigate",
  30. "click .mk_menu_search_result": "_onSearchResultChosen",
  31. "shown.bs.dropdown": "_onMenuShow",
  32. "hidden.bs.dropdown": "_onMenuHide",
  33. }),
  34. init: function (parent, menuData) {
  35. this._super.apply(this, arguments);
  36. for (var n in this._apps) {
  37. this._apps[n].web_icon_data = menuData.children[n].web_icon_data;
  38. }
  39. this._searchableMenus = _.reduce(
  40. menuData.children, this._findNames.bind(this), {}
  41. );
  42. this._search_def = $.Deferred();
  43. },
  44. start: function () {
  45. this._setBackgroundImage();
  46. this.$search_container = this.$(".mk_search_container");
  47. this.$search_input = this.$(".mk_search_input input");
  48. this.$search_results = this.$(".mk_search_results");
  49. return this._super.apply(this, arguments);
  50. },
  51. _findNames: function (memo, menu) {
  52. if (menu.action) {
  53. var key = menu.parent_id ? menu.parent_id[1] + "/" : "";
  54. memo[key + menu.name] = menu;
  55. }
  56. if (menu.children.length) {
  57. _.reduce(menu.children, this._findNames.bind(this), memo);
  58. }
  59. return memo;
  60. },
  61. _setBackgroundImage: function () {
  62. var url = session.url('/web/image', {
  63. model: 'res.company',
  64. id: session.company_id,
  65. field: 'background_image',
  66. });
  67. this.$('.dropdown-menu').css({
  68. "background-size": "cover",
  69. "background-image": "url(" + url + ")"
  70. });
  71. },
  72. _menuInfo: function (key) {
  73. var original = this._searchableMenus[key];
  74. return _.extend({
  75. action_id: parseInt(original.action.split(',')[1], 10),
  76. }, original);
  77. },
  78. _onMenuShow: function(event) {
  79. this._searchFocus();
  80. },
  81. _onMenuHide: function(event) {
  82. this._searchReset();
  83. },
  84. _searchFocus: function () {
  85. if (!config.device.isMobile) {
  86. this.$search_input.focus();
  87. } else {
  88. this.$search_input.blur();
  89. }
  90. },
  91. _searchReset: function () {
  92. this.$search_container.removeClass("has-results");
  93. this.$search_results.empty();
  94. this.$search_input.val("");
  95. },
  96. _searchMenusSchedule: function () {
  97. this._search_def.reject();
  98. this._search_def = $.Deferred();
  99. setTimeout(this._search_def.resolve.bind(this._search_def), 50);
  100. this._search_def.done(this._searchMenus.bind(this));
  101. },
  102. _searchMenus: function () {
  103. var query = this.$search_input.val();
  104. if (query === "") {
  105. this.$search_container.removeClass("has-results");
  106. this.$search_results.empty();
  107. return;
  108. }
  109. var results = fuzzy.filter(query, _.keys(this._searchableMenus), {
  110. pre: "<b>",
  111. post: "</b>",
  112. });
  113. this.$search_container.toggleClass("has-results", Boolean(results.length));
  114. this.$search_results.html(QWeb.render("muk_web_theme.MenuSearchResults", {
  115. results: results,
  116. widget: this,
  117. }));
  118. },
  119. _onSearchResultChosen: function (event) {
  120. event.preventDefault();
  121. var $result = $(event.currentTarget),
  122. text = $result.text().trim(),
  123. data = $result.data(),
  124. suffix = ~text.indexOf("/") ? "/" : "";
  125. this.trigger_up("menu_clicked", {
  126. action_id: data.actionId,
  127. id: data.menuId,
  128. previous_menu_id: data.parentId,
  129. });
  130. var app = _.find(this._apps, function (_app) {
  131. return text.indexOf(_app.name + suffix) === 0;
  132. });
  133. core.bus.trigger("change_menu_section", app.menuID);
  134. },
  135. _onSearchResultsNavigate: function (event) {
  136. if (this.$search_results.is(":empty")) {
  137. this._searchMenusSchedule();
  138. return;
  139. }
  140. var all = this.$search_results.find(".mk_menu_search_result"),
  141. pre_focused = all.filter(".active") || $(all[0]),
  142. offset = all.index(pre_focused),
  143. key = event.key;
  144. if (key === "Tab") {
  145. event.preventDefault();
  146. key = event.shiftKey ? "ArrowUp" : "ArrowDown";
  147. }
  148. switch (key) {
  149. case "Enter":
  150. pre_focused.click();
  151. break;
  152. case "ArrowUp":
  153. offset--;
  154. break;
  155. case "ArrowDown":
  156. offset++;
  157. break;
  158. default:
  159. this._searchMenusSchedule();
  160. return;
  161. }
  162. if (offset < 0) {
  163. offset = all.length + offset;
  164. } else if (offset >= all.length) {
  165. offset -= all.length;
  166. }
  167. var new_focused = $(all[offset]);
  168. pre_focused.removeClass("active");
  169. new_focused.addClass("active");
  170. this.$search_results.scrollTo(new_focused, {
  171. offset: {
  172. top: this.$search_results.height() * -0.5,
  173. },
  174. });
  175. },
  176. });
  177. });