summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views
diff options
context:
space:
mode:
authorVincent Schippefilt <vsc@odoo.com>2018-02-19 15:26:15 +0100
committerVincent Schippefilt <vsc@odoo.com>2018-04-10 09:39:08 +0200
commite0924fd01d8ffaf95b677b10ea0a5627b484a7d6 (patch)
treef4292d6f97686a0d4376cc42f768c5525bc4c13c /addons/web/static/src/js/views
parenta4d00a56b640d9da5129e2611667b3f491157ca5 (diff)
[IMP] web: keyboard navigation in form view
1. Added the ability to navigate within the List view using UP and DOWN keys. 2. By defaults, the focus should be set on the search input field. 3. From the search input field, using the DOWN key, the focus goes to the list 4. From the search input field, using TAB key, the focus goes to the create button 5. From the create/import buttons, using TAB or DOWN key goes to the first row of the list, then UP and DOWN keys moves the focus from item to item in the list. 6. When on a row, hitting ENTER opens the view of that row. Also in this commit 1. When on the first row, hitting the UP key does nothing 2. When on the last row, hitting the DOWN key does nothing Not included in this commit 1. Memorize the position on the list when opening an row, than restoring when going back to that list 2. Navigation on grouped list using the keyboard
Diffstat (limited to 'addons/web/static/src/js/views')
-rw-r--r--addons/web/static/src/js/views/abstract_controller.js6
-rw-r--r--addons/web/static/src/js/views/basic/basic_controller.js6
-rw-r--r--addons/web/static/src/js/views/basic/basic_renderer.js6
-rw-r--r--addons/web/static/src/js/views/list/list_controller.js40
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js38
5 files changed, 94 insertions, 2 deletions
diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js
index 4fff15d5663..deb7c37bb59 100644
--- a/addons/web/static/src/js/views/abstract_controller.js
+++ b/addons/web/static/src/js/views/abstract_controller.js
@@ -115,6 +115,12 @@ var AbstractController = AbstractAction.extend(ControlPanelMixin, {
this.renderer.on_detach_callback();
},
+ /**
+ * Gives the focus to the renderer
+ */
+ giveFocus:function(){
+ this.renderer.giveFocus();
+ },
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js
index 1a87c3433f6..8a9b3713f15 100644
--- a/addons/web/static/src/js/views/basic/basic_controller.js
+++ b/addons/web/static/src/js/views/basic/basic_controller.js
@@ -121,6 +121,12 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
return [];
},
/**
+ * Gives the focus to the renderer
+ */
+ giveFocus:function() {
+ this.renderer.giveFocus();
+ },
+ /**
* Returns true iff the given recordID (or the main recordID) is dirty.
*
* @param {string} [recordID] - default to main recordID
diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js
index 4fb7983d00f..55a6c7a6c94 100644
--- a/addons/web/static/src/js/views/basic/basic_renderer.js
+++ b/addons/web/static/src/js/views/basic/basic_renderer.js
@@ -136,6 +136,12 @@ var BasicRenderer = AbstractRenderer.extend({
}
},
+ /**
+ * Order to focus to be given to the content of the current view
+ */
+ giveFocus:function() {
+ },
+
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js
index 551fa8102be..3262196fa51 100644
--- a/addons/web/static/src/js/views/list/list_controller.js
+++ b/addons/web/static/src/js/views/list/list_controller.js
@@ -116,6 +116,15 @@ var ListController = BasicController.extend({
if (!this.noLeaf && this.hasButtons) {
this.$buttons = $(qweb.render('ListView.buttons', {widget: this}));
this.$buttons.on('click', '.o_list_button_add', this._onCreateRecord.bind(this));
+
+ this._assignCreateKeyboardBehavior(this.$buttons.find('.o_list_button_add'));
+ this.$buttons.find('.o_list_button_add').tooltip({
+ delay: {show: 200, hide:0},
+ title: function(){
+ return qweb.render('CreateButton.tooltip');
+ },
+ trigger: 'manual',
+ });
this.$buttons.on('click', '.o_list_button_discard', this._onDiscard.bind(this));
this.$buttons.appendTo($node);
}
@@ -222,6 +231,33 @@ var ListController = BasicController.extend({
.then(this.update.bind(this, {}, {reload: false}));
},
/**
+ * Assign on the buttons create additionnal behavior to facilitate the work of the users doing input only using the keyboard
+ *
+ * @param {jQueryElement} $createButton The create button itself
+ */
+ _assignCreateKeyboardBehavior: function($createButton) {
+ var self = this;
+ $createButton.on('keydown', function(e) {
+ $createButton.tooltip('hide');
+ switch(e.which) {
+ case $.ui.keyCode.ENTER:
+ e.preventDefault();
+ self._onCreateRecord.apply(self);
+ break;
+ case $.ui.keyCode.DOWN:
+ e.preventDefault();
+ self.renderer.giveFocus();
+ break;
+ case $.ui.keyCode.TAB:
+ if (!e.shiftKey && (e.target.classList.contains("btn-primary") || e.target.classList.contains("oe_highlight"))) {
+ e.preventDefault();
+ $createButton.tooltip('show');
+ }
+ break;
+ }
+ });
+ },
+ /**
* This function is the hook called by the field manager mixin to confirm
* that a record has been saved.
*
@@ -353,7 +389,9 @@ var ListController = BasicController.extend({
// we prevent the event propagation because we don't want this event to
// trigger a click on the main bus, which would be then caught by the
// list editable renderer and would unselect the newly created row
- event.stopPropagation();
+ if (event) {
+ event.stopPropagation();
+ }
var state = this.model.get(this.handle, {raw: true});
if (this.editable && !state.groupedBy.length) {
this._addRecord();
diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js
index e9c9a4d1f76..354e7c8a90b 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -37,6 +37,9 @@ var ListRenderer = BasicRenderer.extend({
'click thead th.o_column_sortable': '_onSortColumn',
'click .o_group_header': '_onToggleGroup',
'click thead .o_list_record_selector input': '_onToggleSelection',
+ 'keypress thead tr td' : '_onKeyPress',
+ 'keydown tr' : '_onKeyDown',
+ 'keydown thead tr' : '_onKeyDown',
},
/**
* @constructor
@@ -65,7 +68,14 @@ var ListRenderer = BasicRenderer.extend({
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
-
+ /**
+ * Order to focus to be given to the content of the current view
+ * @override
+ * @public
+ */
+ giveFocus:function() {
+ this.$('tbody .o_list_record_selector input:first()').focus();
+ },
/**
* @override
*/
@@ -717,6 +727,32 @@ var ListRenderer = BasicRenderer.extend({
//--------------------------------------------------------------------------
/**
+ * Manages the keyboard events on the list. If the list is not editable, when the user navigates to
+ * a cell using the keyboard, if he presses enter, enter the model represented by the line
+ *
+ * @private
+ * @param {KeyboardEvent} e
+ */
+ _onKeyDown : function(e) {
+ if (!this.editable) {
+ switch(e.which) {
+ case $.ui.keyCode.DOWN:
+ $(e.currentTarget).next().find('input').focus();
+ break;
+ case $.ui.keyCode.UP:
+ $(e.currentTarget).prev().find('input').focus();
+ break;
+ case $.ui.keyCode.ENTER:
+ e.preventDefault();
+ var id = $(e.currentTarget).data('id');
+ if (id) {
+ this.trigger_up('open_record', {id:id, target: e.target});
+ }
+ break;
+ }
+ }
+ },
+ /**
* @private
* @param {MouseEvent} event
*/