From 68496f8f621923e7a002d0de83d89ab78f93ddf7 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Tue, 3 Jun 2014 20:54:37 +0400 Subject: [PATCH] Added keyboard shortcuts Closes #81 --- Makefile | 2 + app/css/app.css | 18 ++++-- app/js/controllers.js | 2 +- app/js/directives.js | 140 +++++++++++++++++++++++++++++++++++++++++- app/partials/im.html | 2 +- 5 files changed, 155 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 1b97d670..74019707 100644 --- a/Makefile +++ b/Makefile @@ -7,4 +7,6 @@ publish: ./node_modules/gulp/bin/gulp.js clean cd dist && git pull origin gh-pages ./node_modules/gulp/bin/gulp.js publish + echo -n "Please open http://localhost:8000/dist/index.html and check if everything works fine." + read -e cd dist && git add --all . && git commit -am "merged with master" && git push origin gh-pages diff --git a/app/css/app.css b/app/css/app.css index 4916de8a..fc5b9055 100644 --- a/app/css/app.css +++ b/app/css/app.css @@ -949,7 +949,8 @@ a.tg_radio_on:hover i.icon-radio { padding: 8px 9px; border-radius: 0; } -.im_dialogs_scrollable_wrap a.im_dialog:hover { +.im_dialogs_scrollable_wrap a.im_dialog:hover, +.im_dialogs_scrollable_wrap a.im_dialog_selected { border-radius: 2px; background: #f2f6fa; } @@ -957,7 +958,8 @@ a.tg_radio_on:hover i.icon-radio { border-radius: 2px; background-color: #6490b1; } -.im_dialogs_scrollable_wrap .active a.im_dialog:hover { +.im_dialogs_scrollable_wrap .active a.im_dialog:hover, +.im_dialogs_scrollable_wrap .active a.im_dialog_selected { background-color: #6490b1; } @@ -978,7 +980,8 @@ a.tg_radio_on:hover i.icon-radio { .im_dialog_message_text { color: #808080; } -a.im_dialog:hover .im_dialog_message_text { +a.im_dialog:hover .im_dialog_message_text, +a.im_dialog_selected .im_dialog_message_text { color: #698192; } .active a.im_dialog .im_dialog_chat_from_wrap, @@ -1031,7 +1034,8 @@ a.im_dialog:hover .im_dialog_message_text { margin: 10px 0 0; } -a.im_dialog:hover .im_dialog_unread { +a.im_dialog:hover .im_dialog_unread, +a.im_dialog_selected .im_dialog_unread { background: #a3c0d4; } .active .im_dialog_unread { @@ -1043,11 +1047,13 @@ a.im_dialog:hover .im_dialog_unread { color: #b3b3b3; font-size: 0.85em; } -a.im_dialog:hover .im_dialog_date { +a.im_dialog:hover .im_dialog_date, +a.im_dialog_selected .im_dialog_date { color: #91a6ba; } .active .im_dialog_date, -.active a.im_dialog:hover .im_dialog_date { +.active a.im_dialog:hover .im_dialog_date, +.active a.im_dialog_selected .im_dialog_date { color: #b8d1e4; } diff --git a/app/js/controllers.js b/app/js/controllers.js index ec3df507..15882377 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -565,7 +565,7 @@ angular.module('myApp.controllers', []) } // console.trace('load history'); - var curJump = jump, + var curJump = ++jump, inputMediaFilter = $scope.mediaType && {_: inputMediaFilters[$scope.mediaType]}, getMessagesPromise = inputMediaFilter ? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID) diff --git a/app/js/directives.js b/app/js/directives.js index cce90e6e..288d1d74 100644 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -29,6 +29,135 @@ angular.module('myApp.directives', ['myApp.filters']) }; }) + .directive('myDialogs', function ($modalStack) { + + return { + link: link + }; + + + function link ($scope, element, attrs) { + var dialogsWrap = $('.im_dialogs_wrap', element)[0], + scrollableWrap = $('.im_dialogs_scrollable_wrap', element)[0], + searchField = $('.im_dialogs_search_field', element)[0], + searchFocused = false; + + + $(searchField).on('focus blur', function (e) { + searchFocused = e.type == 'focus'; + + if (!searchFocused) { + $(scrollableWrap).find('.im_dialog_selected').removeClass('.im_dialog_selected'); + } + }); + + function onKeyDown(e) { + if (!searchFocused && $modalStack.getTop()) { + return true; + } + + if (e.keyCode >= 48 && e.keyCode <= 57 && !e.shiftKey && e.altKey) { // Alt + [0-9 keys] + var currentSelected = $(scrollableWrap).find('.im_dialog_wrap a')[(e.keyCode - 48 || 10) - 1]; + if (currentSelected) { + currentSelected.click(); + } + return cancelEvent(e); + } + + if (e.keyCode == 27 || e.keyCode == 9 && e.shiftKey) { // ESC or Shift + Tab + if (!searchFocused) { + searchField.focus(); + if (searchField.value) { + searchField.select(); + } + } + return cancelEvent(e); + } + + if (searchFocused && e.keyCode == 13) { // Enter + var currentSelected = $(scrollableWrap).find('.im_dialog_selected')[0] || $(scrollableWrap).find('.im_dialog_wrap a')[0]; + if (currentSelected) { + currentSelected.click(); + } + return cancelEvent(e); + } + + if (e.keyCode == 38 || e.keyCode == 40) { // UP, DOWN + var skip = !e.shiftKey && e.altKey; + if (!skip && (!searchFocused || e.metaKey)) { + return true; + } + + var next = e.keyCode == 40, + currentSelected = !skip && $(scrollableWrap).find('.im_dialog_selected')[0] || $(scrollableWrap).find('.active a.im_dialog')[0], + currentSelectedWrap = currentSelected && currentSelected.parentNode, + nextDialogWrap; + + if (currentSelectedWrap) { + var nextDialogWrap = currentSelected[next ? 'nextSibling' : 'previousSibling']; + + if (!nextDialogWrap || !nextDialogWrap.className || nextDialogWrap.className.indexOf('im_dialog_wrap') == -1) { + var dialogWraps = $(scrollableWrap).find('.im_dialog_wrap'), + pos = dialogWraps.index(currentSelected.parentNode), + nextPos = pos + (next ? 1 : -1); + + nextDialogWrap = dialogWraps[nextPos]; + } + } else { + var dialogWraps = $(scrollableWrap).find('.im_dialog_wrap'); + if (next) { + nextDialogWrap = dialogWraps[0]; + } else { + nextDialogWrap = dialogWraps[dialogWraps.length - 1]; + } + } + + if (skip) { + if (nextDialogWrap) { + $(nextDialogWrap).find('a')[0].click(); + } + } else { + if (currentSelectedWrap && nextDialogWrap) { + $(currentSelectedWrap).find('a').removeClass('im_dialog_selected'); + } + if (nextDialogWrap) { + $(nextDialogWrap).find('a').addClass('im_dialog_selected'); + } + } + + if (nextDialogWrap) { + var elTop = nextDialogWrap.offsetTop, + elHeight = nextDialogWrap.offsetHeight, + scrollTop = scrollableWrap.scrollTop, + viewportHeight = scrollableWrap.clientHeight; + + + if (scrollTop > elTop) { + scrollableWrap.scrollTop = elTop; + $(dialogsWrap).nanoScroller({flash: true}); + } + else if (scrollTop < elTop + elHeight - viewportHeight) { + scrollableWrap.scrollTop = elTop + elHeight - viewportHeight; + $(dialogsWrap).nanoScroller({flash: true}); + } + + } + + return cancelEvent(e); + } + } + + $(document).on('keydown', onKeyDown); + + $scope.$on('$destroy', function () { + $(document).off('keydown', onKeyDown); + }); + + } + + + }) + .directive('myDialogsList', function($window, $timeout) { return { @@ -417,7 +546,7 @@ angular.module('myApp.directives', ['myApp.filters']) }) - .directive('mySendForm', function ($timeout, AppConfigManager, ErrorService) { + .directive('mySendForm', function ($timeout, $modalStack, AppConfigManager, ErrorService) { return { link: link, @@ -534,6 +663,14 @@ angular.module('myApp.directives', ['myApp.filters']) } }; + function onKeyDown(e) { + if (e.keyCode == 9 && !e.shiftKey && !$modalStack.getTop()) { // TAB + editorElement.focus(); + return cancelEvent(e); + } + } + $(document).on('keydown', onKeyDown); + $('body').on('dragenter dragleave dragover drop', onDragDropEvent); $(document).on('paste', onPasteEvent); if (richTextarea) { @@ -555,6 +692,7 @@ angular.module('myApp.directives', ['myApp.filters']) $scope.$on('$destroy', function cleanup() { $('body').off('dragenter dragleave dragover drop', onDragDropEvent); $(document).off('paste', onPasteEvent); + $(document).off('keydown', onKeyDown); if (richTextarea) { $(richTextarea).off('DOMNodeInserted', onPastedImageEvent); } diff --git a/app/partials/im.html b/app/partials/im.html index 5f6d82ff..df844137 100644 --- a/app/partials/im.html +++ b/app/partials/im.html @@ -4,7 +4,7 @@
-
+