(js,css) Improve keyboard shortcuts

- Defined some hotkeys in all modules;
- Added generation of cheat sheet.
This commit is contained in:
Francis Lachapelle
2016-09-27 16:16:09 -04:00
parent 13dd21bebb
commit 57a735753f
21 changed files with 630 additions and 74 deletions
@@ -212,7 +212,7 @@
this.$$cards = [];
}
this.idsMap = {};
this.$cards = []; // TODO Keep the "selected" state of cards
this.$cards = [];
// Extend instance with all attributes of data except headers
angular.forEach(data, function(value, key) {
if (key != 'headers' && key != 'cards') {
@@ -370,6 +370,16 @@
return _.find(this.$cards, function(card) { return card.id == _this.selectedCard; });
};
/**
* @function $selectedCardIndex
* @memberof AddressBook.prototype
* @desc Return the index of the currently visible card.
* @returns a number or undefined if no card is selected
*/
AddressBook.prototype.$selectedCardIndex = function() {
return _.indexOf(_.map(this.$cards, 'id'), this.selectedCard);
};
/**
* @function $selectedCards
* @memberof AddressBook.prototype
@@ -6,9 +6,9 @@
/**
* @ngInject
*/
AddressBookController.$inject = ['$scope', '$q', '$window', '$state', '$timeout', '$mdDialog', '$mdToast', 'Account', 'Card', 'AddressBook', 'Dialog', 'sgSettings', 'stateAddressbooks', 'stateAddressbook'];
function AddressBookController($scope, $q, $window, $state, $timeout, $mdDialog, $mdToast, Account, Card, AddressBook, Dialog, Settings, stateAddressbooks, stateAddressbook) {
var vm = this;
AddressBookController.$inject = ['$scope', '$q', '$window', '$state', '$timeout', '$mdDialog', '$mdToast', 'Account', 'Card', 'AddressBook', 'Dialog', 'sgSettings', 'sgHotkeys', 'stateAddressbooks', 'stateAddressbook'];
function AddressBookController($scope, $q, $window, $state, $timeout, $mdDialog, $mdToast, Account, Card, AddressBook, Dialog, Settings, sgHotkeys, stateAddressbooks, stateAddressbook) {
var vm = this, hotkeys = [];
AddressBook.selectedFolder = stateAddressbook;
@@ -29,12 +29,72 @@
vm.newMessageWithSelectedCards = newMessageWithSelectedCards;
vm.newMessageWithRecipient = newMessageWithRecipient;
vm.mode = { search: false, multiple: 0 };
_registerHotkeys(hotkeys);
$scope.$on('$destroy', function() {
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
});
});
function _registerHotkeys(keys) {
keys.push(sgHotkeys.createHotkey({
key: l('key_create_card'),
description: l('Create a new address book card'),
callback: angular.bind(vm, newComponent, 'card')
}));
keys.push(sgHotkeys.createHotkey({
key: l('key_create_list'),
description: l('Create a new list'),
callback: angular.bind(vm, newComponent, 'list')
}));
keys.push(sgHotkeys.createHotkey({
key: 'space',
description: l('Toggle item'),
callback: toggleCardSelection
}));
keys.push(sgHotkeys.createHotkey({
key: 'up',
description: l('View next item'),
callback: _nextCard
}));
keys.push(sgHotkeys.createHotkey({
key: 'down',
description: l('View previous item'),
callback: _previousCard
}));
keys.push(sgHotkeys.createHotkey({
key: 'shift+up',
description: l('Add next item to selection'),
callback: _addNextCardToSelection
}));
keys.push(sgHotkeys.createHotkey({
key: 'shift+down',
description: l('Add previous item to selection'),
callback: _addPreviousCardToSelection
}));
keys.push(sgHotkeys.createHotkey({
key: 'backspace',
description: l('Delete selected card or address book'),
callback: confirmDeleteSelectedCards
}));
// Register the hotkeys
_.forEach(keys, function(key) {
sgHotkeys.registerHotkey(key);
});
}
function selectCard(card) {
$state.go('app.addressbook.card.view', {cardId: card.id});
}
function toggleCardSelection($event, card) {
if (!card) card = vm.selectedFolder.$selectedCard();
card.selected = !card.selected;
vm.mode.multiple += card.selected? 1 : -1;
$event.preventDefault();
@@ -51,20 +111,92 @@
});
vm.mode.multiple = 0;
}
function confirmDeleteSelectedCards() {
Dialog.confirm(l('Warning'),
l('Are you sure you want to delete the selected contacts?'),
{ ok: l('Delete') })
/**
* User has pressed up arrow key
*/
function _nextCard($event) {
var index = vm.selectedFolder.$selectedCardIndex();
if (angular.isDefined(index))
index--;
else
// No message is selected, show oldest message
index = vm.selectedFolder.$cards.length() - 1;
if (index > -1)
selectCard(vm.selectedFolder.$cards[index]);
if (vm.selectedFolder.$topIndex > 0)
vm.selectedFolder.$topIndex--;
$event.preventDefault();
return index;
}
/**
* User has pressed the down arrow key
*/
function _previousCard($event) {
var index = vm.selectedFolder.$selectedCardIndex();
if (angular.isDefined(index))
index++;
else
// No message is selected, show newest
index = 0;
if (index < vm.selectedFolder.$cards.length)
selectCard(vm.selectedFolder.$cards[index]);
else
index = -1;
if (vm.selectedFolder.$topIndex < vm.selectedFolder.$cards.length)
vm.selectedFolder.$topIndex++;
$event.preventDefault();
return index;
}
function _addNextCardToSelection($event) {
var index;
if (vm.selectedFolder.hasSelectedCard()) {
index = _nextCard($event);
if (index >= 0)
toggleCardSelection($event, vm.selectedFolder.$cards[index]);
}
}
function _addPreviousCardToSelection($event) {
var index;
if (vm.selectedFolder.hasSelectedCard()) {
index = _previousCard($event);
if (index >= 0)
toggleCardSelection($event, vm.selectedFolder.$cards[index]);
}
}
function confirmDeleteSelectedCards($event) {
var selectedCards = vm.selectedFolder.$selectedCards();
if (_.size(selectedCards) > 0)
Dialog.confirm(l('Warning'),
l('Are you sure you want to delete the selected contacts?'),
{ ok: l('Delete') })
.then(function() {
// User confirmed the deletion
var selectedCards = _.filter(vm.selectedFolder.$cards, function(card) { return card.selected; });
vm.selectedFolder.$deleteCards(selectedCards).then(function() {
vm.mode.multiple = 0;
if (!vm.selectedFolder.selectedCard)
$state.go('app.addressbook');
});
});
$event.preventDefault();
}
/**
@@ -214,6 +346,6 @@
}
angular
.module('SOGo.ContactsUI')
.controller('AddressBookController', AddressBookController);
.module('SOGo.ContactsUI')
.controller('AddressBookController', AddressBookController);
})();
@@ -6,9 +6,9 @@
/**
* @ngInject
*/
AddressBooksController.$inject = ['$state', '$scope', '$rootScope', '$stateParams', '$timeout', '$window', '$mdDialog', '$mdToast', '$mdMedia', '$mdSidenav', 'FileUploader', 'sgConstant', 'sgFocus', 'Card', 'AddressBook', 'Dialog', 'sgSettings', 'User', 'stateAddressbooks'];
function AddressBooksController($state, $scope, $rootScope, $stateParams, $timeout, $window, $mdDialog, $mdToast, $mdMedia, $mdSidenav, FileUploader, sgConstant, focus, Card, AddressBook, Dialog, Settings, User, stateAddressbooks) {
var vm = this;
AddressBooksController.$inject = ['$state', '$scope', '$rootScope', '$stateParams', '$timeout', '$window', '$mdDialog', '$mdToast', '$mdMedia', '$mdSidenav', 'FileUploader', 'sgConstant', 'sgHotkeys', 'sgFocus', 'Card', 'AddressBook', 'Dialog', 'sgSettings', 'User', 'stateAddressbooks'];
function AddressBooksController($state, $scope, $rootScope, $stateParams, $timeout, $window, $mdDialog, $mdToast, $mdMedia, $mdSidenav, FileUploader, sgConstant, sgHotkeys, focus, Card, AddressBook, Dialog, Settings, User, stateAddressbooks) {
var vm = this, hotkeys = [];
vm.activeUser = Settings.activeUser;
vm.service = AddressBook;
@@ -26,6 +26,33 @@
vm.isDroppableFolder = isDroppableFolder;
vm.dragSelectedCards = dragSelectedCards;
_registerHotkeys(hotkeys);
$scope.$on('$destroy', function() {
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
});
});
function _registerHotkeys(keys) {
keys.push(sgHotkeys.createHotkey({
key: 'backspace',
description: l('Delete selected card or address book'),
callback: function() {
if (AddressBook.selectedFolder && !AddressBook.selectedFolder.hasSelectedCard())
confirmDelete();
}
}));
// Register the hotkeys
_.forEach(keys, function(key) {
sgHotkeys.registerHotkey(key);
});
}
function select($event, folder) {
if ($state.params.addressbookId != folder.id &&
vm.editMode != folder.id) {
@@ -109,10 +136,12 @@
return true;
})
.catch(function(response) {
var message = response.data.message || response.statusText;
Dialog.alert(l('An error occured while deleting the addressbook "%{0}".',
vm.service.selectedFolder.name),
message);
if (response) {
var message = response.data.message || response.statusText;
Dialog.alert(l('An error occured while deleting the addressbook "%{0}".',
vm.service.selectedFolder.name),
message);
}
});
}
}
@@ -7,9 +7,9 @@
* Controller to view and edit a card
* @ngInject
*/
CardController.$inject = ['$scope', '$timeout', '$window', '$mdDialog', 'AddressBook', 'Card', 'Dialog', 'sgFocus', '$state', '$stateParams', 'stateCard'];
function CardController($scope, $timeout, $window, $mdDialog, AddressBook, Card, Dialog, focus, $state, $stateParams, stateCard) {
var vm = this;
CardController.$inject = ['$scope', '$timeout', '$window', '$mdDialog', 'AddressBook', 'Card', 'Dialog', 'sgHotkeys', 'sgFocus', '$state', '$stateParams', 'stateCard'];
function CardController($scope, $timeout, $window, $mdDialog, AddressBook, Card, Dialog, sgHotkeys, focus, $state, $stateParams, stateCard) {
var vm = this, hotkeys = [];
vm.card = stateCard;
@@ -37,6 +37,34 @@
vm.toggleRawSource = toggleRawSource;
vm.showRawSource = false;
_registerHotkeys(hotkeys);
$scope.$on('$destroy', function() {
// Deregister hotkeys
_.forEach(hotkeys, function(key) {
sgHotkeys.deregisterHotkey(key);
});
});
function _registerHotkeys(keys) {
keys.push(sgHotkeys.createHotkey({
key: 'backspace',
description: l('Delete'),
callback: function($event) {
if (vm.currentFolder.$selectedCount() === 0)
confirmDelete();
$event.preventDefault();
}
}));
// Register the hotkeys
_.forEach(keys, function(key) {
sgHotkeys.registerHotkey(key);
});
}
function transformCategory(input) {
if (angular.isString(input))
return { value: input };
@@ -112,7 +140,9 @@
$state.go('app.addressbook.card.view', { cardId: vm.card.id });
}
}
function confirmDelete(card) {
function confirmDelete() {
var card = stateCard;
Dialog.confirm(l('Warning'),
l('Are you sure you want to delete the card of %{0}?', '<b>' + card.$fullname() + '</b>'),
{ ok: l('Delete') })