Add keyboard shortcuts
issue6104 review32881002
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Add keyboard shortcuts
|
||||
* Update states of both toolbar and menu
|
||||
* Improve toolbar order
|
||||
* Use login details in title
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@
|
||||
"d3": "^3.5",
|
||||
"c3": "^0.4",
|
||||
"papaparse": "^4.1",
|
||||
"fullcalendar": "^3.0"
|
||||
"fullcalendar": "^3.0",
|
||||
"mousetrap": "^1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"qunit": "^1.18"
|
||||
|
||||
@@ -20,6 +20,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<script type="text/javascript" src="bower_components/papaparse/papaparse.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/fullcalendar/dist/fullcalendar.min.js"></script>
|
||||
<script type="text/javascript" src="bower_components/fullcalendar/dist/locale-all.js"></script>
|
||||
<script type="text/javascript" src="bower_components/mousetrap/mousetrap.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css">
|
||||
|
||||
+162
-1
@@ -529,7 +529,8 @@ var Sao = {};
|
||||
'class': 'global-search-container'
|
||||
});
|
||||
this.search_entry = jQuery('<input>', {
|
||||
'class': 'form-control',
|
||||
'id': 'global-search-entry',
|
||||
'class': 'form-control mousetrap',
|
||||
'placeholder': Sao.i18n.gettext('Search...')
|
||||
});
|
||||
this.el.append(this.search_entry);
|
||||
@@ -591,6 +592,166 @@ var Sao = {};
|
||||
}
|
||||
});
|
||||
|
||||
function shortcuts_defs() {
|
||||
// Shortcuts available on Tab on this format:
|
||||
// {shortcut, label, id of tab button or callback method}
|
||||
return [
|
||||
{
|
||||
shortcut: 'ctrl+a',
|
||||
label: Sao.i18n.gettext('New'),
|
||||
id: 'new_',
|
||||
}, {
|
||||
shortcut: 'ctrl+s',
|
||||
label: Sao.i18n.gettext('Save'),
|
||||
id: 'save',
|
||||
}, {
|
||||
shortcut: 'ctrl+l',
|
||||
label: Sao.i18n.gettext('Switch'),
|
||||
id: 'switch_',
|
||||
}, {
|
||||
shortcut: 'ctrl+r',
|
||||
label: Sao.i18n.gettext('Reload/Undo'),
|
||||
id: 'reload',
|
||||
}, {
|
||||
shortcut: 'ctrl+shift+d',
|
||||
label: Sao.i18n.gettext('Duplicate'),
|
||||
id: 'copy',
|
||||
}, {
|
||||
shortcut: 'ctrl+d',
|
||||
label: Sao.i18n.gettext('Delete'),
|
||||
id: 'delete_',
|
||||
}, {
|
||||
shortcut: 'ctrl+up',
|
||||
label: Sao.i18n.gettext('Previous'),
|
||||
id: 'previous',
|
||||
}, {
|
||||
shortcut: 'ctrl+down',
|
||||
label: Sao.i18n.gettext('Next'),
|
||||
id: 'next',
|
||||
}, {
|
||||
shortcut: 'ctrl+f',
|
||||
label: Sao.i18n.gettext('Search'),
|
||||
id: 'search',
|
||||
}, {
|
||||
shortcut: 'ctrl+x',
|
||||
label: Sao.i18n.gettext('Close Tab'),
|
||||
id: 'close',
|
||||
}, {
|
||||
shortcut: 'ctrl+shift+t',
|
||||
label: Sao.i18n.gettext('Attachment'),
|
||||
id: 'attach',
|
||||
}, {
|
||||
shortcut: 'ctrl+shift+o',
|
||||
label: Sao.i18n.gettext('Note'),
|
||||
id: 'note',
|
||||
}, {
|
||||
shortcut: 'ctrl+e',
|
||||
label: Sao.i18n.gettext('Action'),
|
||||
id: 'action',
|
||||
}, {
|
||||
shortcut: 'ctrl+shift+r',
|
||||
label: Sao.i18n.gettext('Relate'),
|
||||
id: 'relate',
|
||||
}, {
|
||||
shortcut: 'ctrl+p',
|
||||
label: Sao.i18n.gettext('Print'),
|
||||
id: 'print',
|
||||
}, {
|
||||
shortcut: 'ctrl+left',
|
||||
label: Sao.i18n.gettext('Previous tab'),
|
||||
callback: function() {
|
||||
Sao.Tab.previous_tab();
|
||||
},
|
||||
}, {
|
||||
shortcut: 'ctrl+right',
|
||||
label: Sao.i18n.gettext('Next tab'),
|
||||
callback: function() {
|
||||
Sao.Tab.next_tab();
|
||||
},
|
||||
}, {
|
||||
shortcut: 'ctrl+k',
|
||||
label: Sao.i18n.gettext('Global search'),
|
||||
callback: function() {
|
||||
jQuery('#global-search-entry').focus();
|
||||
},
|
||||
}, {
|
||||
shortcut: 'ctrl+h',
|
||||
label: Sao.i18n.gettext('Show this help'),
|
||||
callback: function() {
|
||||
shortcuts_dialog();
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
set_shortcuts();
|
||||
});
|
||||
|
||||
function set_shortcuts() {
|
||||
shortcuts_defs().forEach(function(definition) {
|
||||
Mousetrap.bind(definition.shortcut, function() {
|
||||
if (definition.id){
|
||||
var current_tab = Sao.Tab.tabs.get_current();
|
||||
if (current_tab) {
|
||||
var focused = $(':focus');
|
||||
focused.blur();
|
||||
current_tab.el.find('a[id="' + definition.id + '"]').click();
|
||||
focused.focus();
|
||||
}
|
||||
} else if (definition.callback) {
|
||||
jQuery.when().then(definition.callback);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shortcuts_dialog() {
|
||||
var dialog = new Sao.Dialog(Sao.i18n.gettext('Keyboard shortcuts'),
|
||||
'shortcut-dialog', 'm');
|
||||
jQuery('<button>', {
|
||||
'class': 'close',
|
||||
'data-dismiss': 'modal',
|
||||
'aria-label': Sao.i18n.gettext("Close"),
|
||||
}).append(jQuery('<span>', {
|
||||
'aria-hidden': true,
|
||||
}).append('×')).prependTo(dialog.header);
|
||||
var row = jQuery('<div/>', {
|
||||
'class': 'row'
|
||||
}).appendTo(dialog.body);
|
||||
var global_shortcuts_dl = jQuery('<dl/>', {
|
||||
'class': 'dl-horizontal col-md-6'
|
||||
}).append(jQuery('<h5/>')
|
||||
.append(Sao.i18n.gettext('Global shortcuts')))
|
||||
.appendTo(row);
|
||||
var tab_shortcuts_dl = jQuery('<dl/>', {
|
||||
'class': 'dl-horizontal col-md-6'
|
||||
}).append(jQuery('<h5/>')
|
||||
.append(Sao.i18n.gettext('Tab shortcuts')))
|
||||
.appendTo(row);
|
||||
|
||||
shortcuts_defs().forEach(function(definition) {
|
||||
var dt = jQuery('<dt/>').append(definition.label);
|
||||
var dd = jQuery('<dd/>').append(jQuery('<kbd>')
|
||||
.append(definition.shortcut));
|
||||
var dest_dl;
|
||||
if (definition.id) {
|
||||
dest_dl = tab_shortcuts_dl;
|
||||
} else {
|
||||
dest_dl = global_shortcuts_dl;
|
||||
}
|
||||
dt.appendTo(dest_dl);
|
||||
dd.appendTo(dest_dl);
|
||||
});
|
||||
dialog.modal.on('hidden.bs.modal', function() {
|
||||
jQuery(this).remove();
|
||||
});
|
||||
|
||||
dialog.modal.modal('show');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fix stacked modal
|
||||
jQuery(document)
|
||||
.on('show.bs.modal', '.modal', function(event) {
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@
|
||||
}).append(Sao.i18n.gettext('Filters'));
|
||||
this.filter_button.click(this.search_box.bind(this));
|
||||
this.search_entry = jQuery('<input/>', {
|
||||
'class': 'form-control',
|
||||
'class': 'form-control mousetrap',
|
||||
'placeholder': Sao.i18n.gettext('Search')
|
||||
});
|
||||
this.search_list = jQuery('<datalist/>');
|
||||
|
||||
+21
@@ -128,6 +128,7 @@
|
||||
'role': 'presentation'
|
||||
});
|
||||
var link = jQuery('<a/>', {
|
||||
'id': item.id,
|
||||
'role': 'menuitem',
|
||||
'href': '#',
|
||||
'tabindex': -1
|
||||
@@ -389,6 +390,26 @@
|
||||
tabs.trigger('ready');
|
||||
};
|
||||
|
||||
Sao.Tab.previous_tab = function() {
|
||||
Sao.Tab.move('prevAll');
|
||||
};
|
||||
|
||||
Sao.Tab.next_tab = function() {
|
||||
Sao.Tab.move('nextAll');
|
||||
};
|
||||
|
||||
Sao.Tab.move = function(direction) {
|
||||
var current_tab = this.tabs.get_current();
|
||||
var tabs = jQuery('#tabs');
|
||||
var tablist = jQuery('#tablist');
|
||||
var tab = tablist.find('#nav-' + current_tab.id);
|
||||
var next = tab[direction]('li').first();
|
||||
if (next) {
|
||||
next.find('a').tab('show');
|
||||
tabs.trigger('ready');
|
||||
}
|
||||
};
|
||||
|
||||
Sao.Tab.Form = Sao.class_(Sao.Tab, {
|
||||
class_: 'tab-form',
|
||||
init: function(model_name, attributes) {
|
||||
|
||||
+11
-11
@@ -1202,7 +1202,7 @@ function eval_pyson(value){
|
||||
}).appendTo(this.el);
|
||||
this.input = this.labelled = jQuery('<input/>', {
|
||||
'type': 'text',
|
||||
'class': 'form-control input-sm'
|
||||
'class': 'form-control input-sm mousetrap'
|
||||
}).appendTo(this.group);
|
||||
if (!jQuery.isEmptyObject(attributes.autocomplete)) {
|
||||
this.datalist = jQuery('<datalist/>').appendTo(this.el);
|
||||
@@ -1336,7 +1336,7 @@ function eval_pyson(value){
|
||||
}).appendTo(this.el);
|
||||
this.input = jQuery('<input/>', {
|
||||
'type': 'text',
|
||||
'class': 'form-control input-sm'
|
||||
'class': 'form-control input-sm mousetrap'
|
||||
}).appendTo(this.date);
|
||||
jQuery('<span/>', {
|
||||
'class': 'input-group-btn'
|
||||
@@ -1428,7 +1428,7 @@ function eval_pyson(value){
|
||||
});
|
||||
this.input = this.labelled = jQuery('<input/>', {
|
||||
'type': 'text',
|
||||
'class': 'form-control input-sm'
|
||||
'class': 'form-control input-sm mousetrap'
|
||||
}).appendTo(this.el);
|
||||
this.el.change(this.focus_out.bind(this));
|
||||
},
|
||||
@@ -1487,7 +1487,7 @@ function eval_pyson(value){
|
||||
'class': this.class_
|
||||
});
|
||||
this.select = this.labelled = jQuery('<select/>', {
|
||||
'class': 'form-control input-sm'
|
||||
'class': 'form-control input-sm mousetrap'
|
||||
});
|
||||
this.el.append(this.select);
|
||||
this.select.change(this.focus_out.bind(this));
|
||||
@@ -1578,7 +1578,7 @@ function eval_pyson(value){
|
||||
});
|
||||
this.input = this.labelled = jQuery('<input/>', {
|
||||
'type': 'checkbox',
|
||||
'class': 'form-control input-sm'
|
||||
'class': 'form-control input-sm mousetrap'
|
||||
}).appendTo(this.el);
|
||||
this.input.change(this.focus_out.bind(this));
|
||||
this.input.click(function() {
|
||||
@@ -1617,7 +1617,7 @@ function eval_pyson(value){
|
||||
'class': this.class_
|
||||
});
|
||||
this.input = this.labelled = jQuery('<textarea/>', {
|
||||
'class': 'form-control input-sm'
|
||||
'class': 'form-control input-sm mousetrap'
|
||||
}).appendTo(this.el);
|
||||
this.input.change(this.focus_out.bind(this));
|
||||
if (this.attributes.translate) {
|
||||
@@ -1673,7 +1673,7 @@ function eval_pyson(value){
|
||||
this.get_toolbar().appendTo(this.el);
|
||||
}
|
||||
this.input = this.labelled = jQuery('<div/>', {
|
||||
'class': 'richtext',
|
||||
'class': 'richtext mousetrap',
|
||||
'contenteditable': true
|
||||
}).appendTo(jQuery('<div/>', {
|
||||
'class': 'panel-body'
|
||||
@@ -1855,7 +1855,7 @@ function eval_pyson(value){
|
||||
}).appendTo(this.el);
|
||||
this.entry = this.labelled = jQuery('<input/>', {
|
||||
'type': 'input',
|
||||
'class': 'form-control input-sm'
|
||||
'class': 'form-control input-sm mousetrap'
|
||||
}).appendTo(group);
|
||||
// Use keydown to not receive focus-in TAB
|
||||
this.entry.on('keydown', this.key_press.bind(this));
|
||||
@@ -2924,7 +2924,7 @@ function eval_pyson(value){
|
||||
}).appendTo(toolbar);
|
||||
this.entry = jQuery('<input/>', {
|
||||
type: 'text',
|
||||
'class': 'form-control input-sm'
|
||||
'class': 'form-control input-sm mousetrap'
|
||||
}).appendTo(group);
|
||||
// Use keydown to not receive focus-in TAB
|
||||
this.entry.on('keydown', this.key_press.bind(this));
|
||||
@@ -3912,7 +3912,7 @@ function eval_pyson(value){
|
||||
}).appendTo(this.el);
|
||||
this.input = this.labelled = jQuery('<input/>', {
|
||||
'type': 'text',
|
||||
'class': 'form-control input-sm'
|
||||
'class': 'form-control input-sm mousetrap'
|
||||
}).appendTo(group);
|
||||
this.button = jQuery('<button/>', {
|
||||
'class': 'btn btn-default',
|
||||
@@ -3960,7 +3960,7 @@ function eval_pyson(value){
|
||||
create_widget: function() {
|
||||
Sao.View.Form.Dict.Selection._super.create_widget.call(this);
|
||||
var select = jQuery('<select/>', {
|
||||
'class': 'form-control input-sm'
|
||||
'class': 'form-control input-sm mousetrap'
|
||||
});
|
||||
select.change(
|
||||
this.parent_widget.focus_out.bind(this.parent_widget));
|
||||
|
||||
Reference in New Issue
Block a user