summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/chrome/action_manager_act_window.js16
-rw-r--r--addons/web/static/src/js/chrome/search_view.js15
-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
-rw-r--r--addons/web/static/src/js/widgets/auto_complete.js3
8 files changed, 128 insertions, 2 deletions
diff --git a/addons/web/static/src/js/chrome/action_manager_act_window.js b/addons/web/static/src/js/chrome/action_manager_act_window.js
index 8ac11f60537..9dc0ff4c1c8 100644
--- a/addons/web/static/src/js/chrome/action_manager_act_window.js
+++ b/addons/web/static/src/js/chrome/action_manager_act_window.js
@@ -25,6 +25,7 @@ ActionManager.include({
update_filters: '_onUpdateFilters',
search: '_onSearch',
switch_view: '_onSwitchView',
+ navigation_move: '_onNavigationMove',
}),
//--------------------------------------------------------------------------
@@ -821,6 +822,21 @@ ActionManager.include({
data.callback(addedFilters);
},
/**
+ * Called mainly from the control panel when the focus should be given to a controller
+ *
+ * @param {OdooEvent} event
+ * @private
+ */
+ _onNavigationMove : function(event) {
+ switch(event.data.direction) {
+ case 'down' :
+ var currentController = this.getCurrentController().widget;
+ currentController.giveFocus();
+ event.stopPropagation();
+ break;
+ }
+ },
+ /**
* Called when there is a change in the search view, so the current action's
* environment needs to be updated with the new domain, context and groupby.
*
diff --git a/addons/web/static/src/js/chrome/search_view.js b/addons/web/static/src/js/chrome/search_view.js
index 8ec1eb9ec71..94087c999b6 100644
--- a/addons/web/static/src/js/chrome/search_view.js
+++ b/addons/web/static/src/js/chrome/search_view.js
@@ -248,6 +248,12 @@ var SearchView = Widget.extend({
}
e.preventDefault();
break;
+ case $.ui.keyCode.DOWN:
+ if (!this.autocomplete.is_expanded()) {
+ e.preventDefault();
+ this.trigger_up('navigation_move', {direction: 'down'});
+ break;
+ }
}
},
},
@@ -336,6 +342,9 @@ var SearchView = Widget.extend({
}
return $.when.apply($, menu_defs).then(this.set_default_filters.bind(this));
},
+ on_attach_callback: function () {
+ this._focusInput();
+ },
get_title: function () {
return this.title;
},
@@ -417,6 +426,12 @@ var SearchView = Widget.extend({
if (this.$buttons) {
this.$buttons.toggle(!this.headless && is_visible && this.visible_filters);
}
+ this._focusInput();
+ },
+ /**
+ * puts the focus on the search input
+ */
+ _focusInput: function () {
if (!config.device.touch && config.device.size_class >= config.device.SIZES.SM) {
this.$('input').focus();
}
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
*/
diff --git a/addons/web/static/src/js/widgets/auto_complete.js b/addons/web/static/src/js/widgets/auto_complete.js
index 883206ae4c2..a25dc0150f5 100644
--- a/addons/web/static/src/js/widgets/auto_complete.js
+++ b/addons/web/static/src/js/widgets/auto_complete.js
@@ -216,5 +216,8 @@ return Widget.extend({
is_expandable: function () {
return !!this.$('.o-selection-focus .o-expand').length;
},
+ is_expanded: function() {
+ return this.$el[0].style.display === "block";
+ }
});
});