diff --git a/web_responsive/readme/DESCRIPTION.rst b/web_responsive/readme/DESCRIPTION.rst index ad22d7cd..f3ce74ee 100644 --- a/web_responsive/readme/DESCRIPTION.rst +++ b/web_responsive/readme/DESCRIPTION.rst @@ -6,18 +6,4 @@ Features: * Keyboard shortcuts for easier navigation * Display kanban views for small screens if an action or field One2x * Set chatter side (Optional per user) -* Quick search (see below) - -The following keyboard shortcuts are implemented: - -* Toggle App Drawer - `ActionKey ` + ``A`` -* Navigate Apps Drawer - Arrow Keys -* Type to select App Links -* ``esc`` to close App Drawer - -The search feature provided in the App Drawer allows you to easily -navigate menus without a mouse. - -To activate the search, just begin typing while within the App Drawer. -You can use the arrow keys or mouse to navigate and select results, -in a similar fashion to navigating the apps in the drawer. +* Quick search diff --git a/web_responsive/readme/ROADMAP.rst b/web_responsive/readme/ROADMAP.rst index 189811a8..97afad6d 100644 --- a/web_responsive/readme/ROADMAP.rst +++ b/web_responsive/readme/ROADMAP.rst @@ -13,10 +13,10 @@ this module. * Sticky header and footer in list view only works on certain browsers: https://caniuse.com/#search=sticky (note that the used feature is in `thead`). -* The ``AppDrawer`` JavaScript object currently extends ``Class``. - We should extend ``Widget`` instead. * On Android (FireFox) - clicking the search icon does not focus the search input. * On Android (FireFox & Chrome) - clicking the search query input will show the on screen keyboard for a split second, but the App Drawer immediately closes and the keyboard closes with it. +* Filter menu items completely on client-side, to make it smoother and allow + users to filter on complete paths of menus and not only on the last item. diff --git a/web_responsive/static/src/js/web_responsive.js b/web_responsive/static/src/js/web_responsive.js index 3b1bde88..0cc2fa1e 100644 --- a/web_responsive/static/src/js/web_responsive.js +++ b/web_responsive/static/src/js/web_responsive.js @@ -5,14 +5,15 @@ odoo.define('web_responsive', function(require) { 'use strict'; var Menu = require('web.Menu'); - var Class = require('web.Class'); var rpc = require('web.rpc'); var SearchView = require('web.SearchView'); var core = require('web.core'); var config = require('web.config'); + var session = require('web.session'); var ViewManager = require('web.ViewManager'); var RelationalFields = require('web.relational_fields'); var FormRenderer = require('web.FormRenderer'); + var Widget = require('web.Widget'); var qweb = core.qweb; @@ -30,7 +31,7 @@ odoo.define('web_responsive', function(require) { this._super(id); if (allowOpen) { return; - }; + } var $clicked_menu = this.$secondary_menus.find('a[data-menu=' + id + ']'); $clicked_menu.parents('.oe_secondary_submenu').css('display', ''); } @@ -64,7 +65,7 @@ odoo.define('web_responsive', function(require) { } }); - var AppDrawer = Class.extend({ + var AppDrawer = Widget.extend({ /* Provides all features inside of the application drawer navigation. @@ -102,7 +103,7 @@ odoo.define('web_responsive', function(require) { searching: false, init: function() { - + this._super.apply(this, arguments); this.directionCodes = { 'left': this.LEFT, 'right': this.RIGHT, @@ -133,7 +134,7 @@ odoo.define('web_responsive', function(require) { this.$el.find('.drawer-search-close').hide().click( $.proxy(this.closeSearchMenus, this) ); - + this.filter_timeout = $.Deferred(); core.bus.on('resize', this, this.handleWindowResize); core.bus.on('keydown', this, this.handleKeyDown); core.bus.on('keyup', this, this.redirectKeyPresses); @@ -199,29 +200,23 @@ odoo.define('web_responsive', function(require) { } var directionCode = $.hotkeys.specialKeys[e.keyCode.toString()]; if (Object.keys(this.directionCodes).indexOf(directionCode) !== -1) { - var $link = this.findAdjacentAppLink( - this.$el.find('a:first, a:focus').last(), - this.directionCodes[directionCode] - ); - this.selectAppLink($link); - } else if ($.hotkeys.specialKeys[e.keyCode.toString()] === 'esc') { - this.handleClickZones(); + var $link = false; if (this.searching) { var $collection = this.$el.find('#appDrawerMenuSearch a'); - var $link = this.findAdjacentLink( + $link = this.findAdjacentLink( this.$el.find('#appDrawerMenuSearch a:first, #appDrawerMenuSearch a.web-responsive-focus').last(), this.directionCodes[directionCode], $collection, true ); } else { - var $link = this.findAdjacentLink( + $link = this.findAdjacentLink( this.$el.find('#appDrawerApps a:first, #appDrawerApps a.web-responsive-focus').last(), this.directionCodes[directionCode] ); } this.selectLink($link); - } else if ($.hotkeys.specialKeys[e.keyCode.toString()] == 'esc') { + } else if ($.hotkeys.specialKeys[e.keyCode.toString()] === 'esc') { // We either back out of the search, or close the app drawer. if (this.searching) { this.closeSearchMenus(); @@ -240,7 +235,6 @@ odoo.define('web_responsive', function(require) { * @param e The key event that was triggered by ``core.bus``. */ redirectKeyPresses: function(e) { - if ( !this.isOpen ) { // Drawer isn't open; Ignore. return; @@ -285,9 +279,8 @@ odoo.define('web_responsive', function(require) { * @listens ``drawer.opened`` and sends to onDrawerOpen */ onDrawerClose: function() { - this.closeSearchMenus(); - this.$searchAction.hide(); core.bus.trigger('drawer.closed'); + this.closeSearchMenus(); this.$el.one('drawer.opened', $.proxy(this.onDrawerOpen, this)); this.isOpen = false; // Remove inline style inserted by drawer.js @@ -295,15 +288,17 @@ odoo.define('web_responsive', function(require) { }, /* Finds app links and register event handlers - * @fires ``drawer.opened`` to the ``core.bus`` - * @listens ``drawer.closed`` and sends to :meth:``onDrawerClose`` - */ - onDrawerOpen: function() { + * @fires ``drawer.opened`` to the ``core.bus`` + * @listens ``drawer.closed`` and sends to :meth:``onDrawerClose`` + */ + onDrawerOpen: function() { + this.closeSearchMenus(); this.$appLinks = $('.app-drawer-icon-app').parent(); this.selectLink($(this.$appLinks[0])); this.$el.one('drawer.closed', $.proxy(this.onDrawerClose, this)); core.bus.trigger('drawer.opened'); this.isOpen = true; + this.$searchInput.val(""); }, // Selects a link visibly & deselects others. @@ -314,25 +309,39 @@ odoo.define('web_responsive', function(require) { } }, - /* Searches for menus by name, then triggers showFoundMenus - * @param query str to search - * @return jQuery obj + /** + * Search matching menus immediately */ - searchMenus: function() { - this.$searchInput = $('#appDrawerSearchInput').focus(); - var domain = [['name', 'ilike', this.$searchInput.val()], - ['action', '!=', false]]; + _searchMenus: function () { rpc.query({ model: 'ir.ui.menu', method: 'search_read', - args: [{ + kwargs: { fields: ['action', 'display_name', 'id'], - domain: domain + domain: [ + ['name', 'ilike', this.$searchInput.val()], + ['action', '!=', false], + ], + context: session.user_context, + }, + }).then(this.showFoundMenus.bind(this)); + }, - }] - }).then( - $.proxy(this.showFoundMenus, this) + /** + * Queue the next menu search for the search input + */ + searchMenus: function() { + // Stop current search, if any + this.filter_timeout.reject(); + this.filter_timeout = $.Deferred(); + // Schedule a new search + this.filter_timeout.done(this._searchMenus.bind(this)); + setTimeout( + this.filter_timeout.resolve.bind(this.filter_timeout), + 200 ); + // Focus search input + this.$searchInput = $('#appDrawerSearchInput').focus(); }, /* Display the menus that are provided as input. @@ -372,7 +381,6 @@ odoo.define('web_responsive', function(require) { this.$el.find('.drawer-search-open').show(); this.$searchResultsContainer.closest('#appDrawerMenuSearch').hide(); this.$searchAction.show(); - $('#appDrawerSearchInput').val(''); }, /* Returns the link adjacent to $link in provided direction. @@ -393,12 +401,12 @@ odoo.define('web_responsive', function(require) { */ findAdjacentLink: function($link, direction, $objs, restrictHorizontal) { - if ($objs === undefined) { + if (_.isUndefined($objs)) { $objs = this.$appLinks; } var obj = []; - var $rows = (restrictHorizontal) ? $objs : this.getRowObjs($link, this.$appLinks); + var $rows = restrictHorizontal ? $objs : this.getRowObjs($link, this.$appLinks); switch (direction) { case this.LEFT: @@ -516,11 +524,6 @@ odoo.define('web_responsive', function(require) { return { 'AppDrawer': AppDrawer, - 'SearchView': SearchView, - 'Menu': Menu, - 'ViewManager': ViewManager, - 'FieldStatus': RelationalFields.FieldStatus, - 'FormRenderer': FormRenderer, }; }); diff --git a/web_responsive/static/tests/js/web_responsive.js b/web_responsive/static/tests/js/web_responsive.js index 7b3fe20a..5a3a43b0 100644 --- a/web_responsive/static/tests/js/web_responsive.js +++ b/web_responsive/static/tests/js/web_responsive.js @@ -118,35 +118,6 @@ odoo.define('web_responsive.test', function(require) { } ); - QUnit.test('It should return keybuffer + new key', - function(assert) { - assert.expect(1); - - this.drawer.keyBuffer = 'TES'; - var res = this.drawer.handleKeyBuffer(84); - assert.equal(res, 'TEST'); - } - ); - - QUnit.test('It should clear keybuffer after timeout', - function(assert) { - assert.expect(1); - - this.drawer.keyBuffer = 'TES'; - this.drawer.keyBufferTime = 10; - this.drawer.handleKeyBuffer(84); - - var self = this; - var d = $.Deferred(); - setTimeout(function() { - assert.equal(self.drawer.keyBuffer, ""); - d.resolve(); - }, 100); - - return d; - } - ); - QUnit.test('It should trigger core bus event for drawer close', function(assert) { assert.expect(1); @@ -224,7 +195,7 @@ odoo.define('web_responsive.test', function(require) { var $appLink = $('#a_1'), $expect = $('#a_2'), - $res = this.drawer.findAdjacentAppLink( + $res = this.drawer.findAdjacentLink( $appLink, this.drawer.RIGHT ); @@ -239,7 +210,7 @@ odoo.define('web_responsive.test', function(require) { this.linkGrid(); var $appLink = $('#a_2'), $expect = $('#a_1'), - $res = this.drawer.findAdjacentAppLink( + $res = this.drawer.findAdjacentLink( $appLink, this.drawer.LEFT ); assert.equal($res[0].id, $expect[0].id); @@ -253,7 +224,7 @@ odoo.define('web_responsive.test', function(require) { this.linkGrid(); var $appLink = $('#a_1'), $expect = $('#a_0'), - $res = this.drawer.findAdjacentAppLink( + $res = this.drawer.findAdjacentLink( $appLink, this.drawer.UP ); assert.equal($res[0].id, $expect[0].id); @@ -267,7 +238,7 @@ odoo.define('web_responsive.test', function(require) { this.linkGrid(); var $appLink = $('#a_1'), $expect = $('#a_2'), - $res = this.drawer.findAdjacentAppLink( + $res = this.drawer.findAdjacentLink( $appLink, this.drawer.DOWN ); assert.equal($res[0].id, $expect[0].id); @@ -281,7 +252,7 @@ odoo.define('web_responsive.test', function(require) { this.linkGrid(); var $appLink = $('#b_2'), $expect = $('#a_0'), - $res = this.drawer.findAdjacentAppLink( + $res = this.drawer.findAdjacentLink( $appLink, this.drawer.RIGHT ); assert.equal($res[0].id, $expect[0].id); @@ -295,7 +266,7 @@ odoo.define('web_responsive.test', function(require) { this.linkGrid(); var $appLink = $('#a_0'), $expect = $('#a_2'), - $res = this.drawer.findAdjacentAppLink( + $res = this.drawer.findAdjacentLink( $appLink, this.drawer.UP ); assert.equal($res[0].id, $expect[0].id); @@ -309,7 +280,7 @@ odoo.define('web_responsive.test', function(require) { this.linkGrid(); var $appLink = $('#a_2'), $expect = $('#a_0'), - $res = this.drawer.findAdjacentAppLink( + $res = this.drawer.findAdjacentLink( $appLink, this.drawer.DOWN ); assert.equal($res[0].id, $expect[0].id);