Dropdown menu keyboard navigation (step 1 / USTABLE)

This commit is contained in:
RainLoop Team 2014-04-11 20:09:51 +04:00
parent cd6974ecd2
commit 2ad305e40e
15 changed files with 305 additions and 228 deletions

View file

@ -37,6 +37,7 @@ Enums.KeyState = {
'MessageList': 'message-list', 'MessageList': 'message-list',
'MessageView': 'message-view', 'MessageView': 'message-view',
'Compose': 'compose', 'Compose': 'compose',
'Menu': 'menu',
'PopupComposeOpenPGP': 'compose-open-pgp', 'PopupComposeOpenPGP': 'compose-open-pgp',
'PopupAsk': 'popup-ask' 'PopupAsk': 'popup-ask'
}; };

View file

@ -126,6 +126,15 @@ ko.bindingHandlers.onEsc = {
} }
}; };
ko.bindingHandlers.clickOnTrue = {
'update': function (oElement, fValueAccessor) {
if (ko.utils.unwrapObservable(fValueAccessor()))
{
$(oElement).click();
}
}
};
ko.bindingHandlers.modal = { ko.bindingHandlers.modal = {
'init': function (oElement, fValueAccessor) { 'init': function (oElement, fValueAccessor) {
$(oElement).toggleClass('fade', !Globals.bMobileDevice) .modal({ $(oElement).toggleClass('fade', !Globals.bMobileDevice) .modal({

View file

@ -320,6 +320,7 @@ Selector.prototype.init = function (oContentVisible, oContentScrollable, sKeySco
}) })
; ;
// TODO
key('enter', sKeyScope, function () { key('enter', sKeyScope, function () {
if (self.focusedItem()) if (self.focusedItem())
{ {

View file

@ -39,6 +39,17 @@ function WebMailDataStorage()
this.projectHash = ko.observable(''); this.projectHash = ko.observable('');
this.threading = ko.observable(false); this.threading = ko.observable(false);
this.accountMenuFocus = ko.observable(false);
this.accountMenuFocus.sKeyState = Enums.KeyState.All;
this.accountMenuFocus.subscribe(function (bValue) {
if (bValue)
{
this.accountMenuFocus.sKeyState = RL.data().keyScope();
}
RL.data().keyScope(bValue ? Enums.KeyState.Menu : this.accountMenuFocus.sKeyState);
}, this);
this.lastFoldersHash = ''; this.lastFoldersHash = '';
this.remoteSuggestions = false; this.remoteSuggestions = false;

View file

@ -60,10 +60,10 @@
} }
.e-item.selected > .e-link { .e-item.selected > .e-link {
background-color: #eee; background-color: #eee !important;
} }
.e-item > .e-link:hover { .e-item > .e-link:focus, .e-item > .e-link:hover {
background-color: #555; background-color: #555;
background-image: none; background-image: none;
color: #fff; color: #fff;

View file

@ -13,6 +13,8 @@ function AbstractSystemDropDownViewModel()
this.accounts = oData.accounts; this.accounts = oData.accounts;
this.accountEmail = oData.accountEmail; this.accountEmail = oData.accountEmail;
this.accountsLoading = oData.accountsLoading; this.accountsLoading = oData.accountsLoading;
this.accountMenuFocus = oData.accountMenuFocus;
this.allowAddAccount = RL.settingsGet('AllowAdditionalAccounts'); this.allowAddAccount = RL.settingsGet('AllowAdditionalAccounts');
this.loading = ko.computed(function () { this.loading = ko.computed(function () {
@ -20,6 +22,14 @@ function AbstractSystemDropDownViewModel()
}, this); }, this);
this.accountClick = _.bind(this.accountClick, this); this.accountClick = _.bind(this.accountClick, this);
key('`', function () {
if (oData.useKeyboardShortcuts() && !RL.popupVisibility() &&
!oData.accountMenuFocus())
{
oData.accountMenuFocus(true);
}
});
} }
_.extend(AbstractSystemDropDownViewModel.prototype, KnoinAbstractViewModel.prototype); _.extend(AbstractSystemDropDownViewModel.prototype, KnoinAbstractViewModel.prototype);

View file

@ -89,7 +89,7 @@
background-color: @dropdown-menu-selected-background-color !important; background-color: @dropdown-menu-selected-background-color !important;
} }
.g-ui-menu .e-item > .e-link:hover { .g-ui-menu .e-item > .e-link:hover, .g-ui-menu .e-item > .e-link:focus {
color: @dropdown-menu-hover-color !important; color: @dropdown-menu-hover-color !important;
background-color: @dropdown-menu-hover-background-color !important; background-color: @dropdown-menu-hover-background-color !important;

View file

@ -10,17 +10,19 @@
       
</div>--> </div>-->
<div class="btn-group pull-right"> <div class="btn-group pull-right">
<a class="btn btn-ellipsis btn-block dropdown-toggle system-dropdown" data-placement="left" data-toggle="dropdown" <a id="top-system-dropdown-id" href="#" tabindex="-1" class="btn btn-ellipsis btn-block dropdown-toggle system-dropdown"
data-tooltip-class="tooltip-big" data-bind="tooltip2: emailTitle"> data-placement="left" data-toggle="dropdown"
data-tooltip-class="tooltip-big" data-bind="tooltip2: emailTitle, hasfocus: accountMenuFocus">
<i data-bind="css: {'icon-user': !loading(), 'icon-spinner animated': loading()}"></i> <i data-bind="css: {'icon-user': !loading(), 'icon-spinner animated': loading()}"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="caret"></span> <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu g-ui-menu" role="menu"> <ul class="dropdown-menu g-ui-menu" tabindex="-1" role="menu" aria-labelledby="top-system-dropdown-id">
<!-- ko if: accounts().length --> <!-- ko if: accounts().length -->
<!-- ko foreach: accounts --> <!-- ko foreach: accounts -->
<li class="e-item"> <li class="e-item" role="presentation">
<a class="e-link" data-bind="click: $root.accountClick, attr: {'href': changeAccountLink() }"> <a class="e-link menuitem" href="#" data-bind="click: $root.accountClick, attr: {'href': changeAccountLink() }">
<i class="icon-ok" data-bind="visible: $root.accountEmail() === email"></i> <i class="icon-ok" data-bind="visible: $root.accountEmail() === email"></i>
<i class="icon-user" data-bind="visible: $root.accountEmail() !== email"></i> <i class="icon-user" data-bind="visible: $root.accountEmail() !== email"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
@ -28,28 +30,28 @@
</a> </a>
</li> </li>
<!-- /ko --> <!-- /ko -->
<li class="divider"></li> <li class="divider" role="presentation"></li>
<!-- /ko --> <!-- /ko -->
<!-- ko if: allowAddAccount --> <!-- ko if: allowAddAccount -->
<li class="e-item"> <li class="e-item" role="presentation">
<a class="e-link" data-bind="click: addAccountClick"> <a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: addAccountClick">
<i class="icon-user-add"></i> <i class="icon-user-add"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="i18n" data-i18n-text="TOP_TOOLBAR/BUTTON_ADD_ACCOUNT"></span> <span class="i18n" data-i18n-text="TOP_TOOLBAR/BUTTON_ADD_ACCOUNT"></span>
</a> </a>
</li> </li>
<!-- /ko --> <!-- /ko -->
<li class="e-item"> <li class="e-item" role="presentation">
<a class="e-link" data-bind="click: settingsClick"> <a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: settingsClick">
<i class="icon-cog"></i> <i class="icon-cog"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="i18n" data-i18n-text="TOP_TOOLBAR/BUTTON_SETTINGS"></span> <span class="i18n" data-i18n-text="TOP_TOOLBAR/BUTTON_SETTINGS"></span>
</a> </a>
</li> </li>
<li class="divider"></li> <li class="divider" role="presentation"></li>
<li class="e-item"> <li class="e-item" role="presentation">
<a class="e-link" data-bind="click: logoutClick"> <a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: logoutClick">
<i class="icon-power"></i> <i class="icon-power"></i>
&nbsp;&nbsp; &nbsp;&nbsp;
<span class="i18n" data-i18n-text="TOP_TOOLBAR/BUTTON_LOGOUT"></span> <span class="i18n" data-i18n-text="TOP_TOOLBAR/BUTTON_LOGOUT"></span>

View file

@ -5861,8 +5861,9 @@ html.no-rgba .modal {
cursor: pointer; cursor: pointer;
} }
.g-ui-menu .e-item.selected > .e-link { .g-ui-menu .e-item.selected > .e-link {
background-color: #eee; background-color: #eee !important;
} }
.g-ui-menu .e-item > .e-link:focus,
.g-ui-menu .e-item > .e-link:hover { .g-ui-menu .e-item > .e-link:hover {
background-color: #555; background-color: #555;
background-image: none; background-image: none;

File diff suppressed because one or more lines are too long

View file

@ -377,6 +377,7 @@ Enums.KeyState = {
'MessageList': 'message-list', 'MessageList': 'message-list',
'MessageView': 'message-view', 'MessageView': 'message-view',
'Compose': 'compose', 'Compose': 'compose',
'Menu': 'menu',
'PopupComposeOpenPGP': 'compose-open-pgp', 'PopupComposeOpenPGP': 'compose-open-pgp',
'PopupAsk': 'popup-ask' 'PopupAsk': 'popup-ask'
}; };
@ -2764,6 +2765,15 @@ ko.bindingHandlers.onEsc = {
} }
}; };
ko.bindingHandlers.clickOnTrue = {
'update': function (oElement, fValueAccessor) {
if (ko.utils.unwrapObservable(fValueAccessor()))
{
$(oElement).click();
}
}
};
ko.bindingHandlers.modal = { ko.bindingHandlers.modal = {
'init': function (oElement, fValueAccessor) { 'init': function (oElement, fValueAccessor) {
$(oElement).toggleClass('fade', !Globals.bMobileDevice) .modal({ $(oElement).toggleClass('fade', !Globals.bMobileDevice) .modal({

File diff suppressed because one or more lines are too long

View file

@ -381,6 +381,7 @@ Enums.KeyState = {
'MessageList': 'message-list', 'MessageList': 'message-list',
'MessageView': 'message-view', 'MessageView': 'message-view',
'Compose': 'compose', 'Compose': 'compose',
'Menu': 'menu',
'PopupComposeOpenPGP': 'compose-open-pgp', 'PopupComposeOpenPGP': 'compose-open-pgp',
'PopupAsk': 'popup-ask' 'PopupAsk': 'popup-ask'
}; };
@ -2768,6 +2769,15 @@ ko.bindingHandlers.onEsc = {
} }
}; };
ko.bindingHandlers.clickOnTrue = {
'update': function (oElement, fValueAccessor) {
if (ko.utils.unwrapObservable(fValueAccessor()))
{
$(oElement).click();
}
}
};
ko.bindingHandlers.modal = { ko.bindingHandlers.modal = {
'init': function (oElement, fValueAccessor) { 'init': function (oElement, fValueAccessor) {
$(oElement).toggleClass('fade', !Globals.bMobileDevice) .modal({ $(oElement).toggleClass('fade', !Globals.bMobileDevice) .modal({
@ -4223,6 +4233,7 @@ Selector.prototype.init = function (oContentVisible, oContentScrollable, sKeySco
}) })
; ;
// TODO
key('enter', sKeyScope, function () { key('enter', sKeyScope, function () {
if (self.focusedItem()) if (self.focusedItem())
{ {
@ -11650,6 +11661,8 @@ function AbstractSystemDropDownViewModel()
this.accounts = oData.accounts; this.accounts = oData.accounts;
this.accountEmail = oData.accountEmail; this.accountEmail = oData.accountEmail;
this.accountsLoading = oData.accountsLoading; this.accountsLoading = oData.accountsLoading;
this.accountMenuFocus = oData.accountMenuFocus;
this.allowAddAccount = RL.settingsGet('AllowAdditionalAccounts'); this.allowAddAccount = RL.settingsGet('AllowAdditionalAccounts');
this.loading = ko.computed(function () { this.loading = ko.computed(function () {
@ -11657,6 +11670,14 @@ function AbstractSystemDropDownViewModel()
}, this); }, this);
this.accountClick = _.bind(this.accountClick, this); this.accountClick = _.bind(this.accountClick, this);
key('`', function () {
if (oData.useKeyboardShortcuts() && !RL.popupVisibility() &&
!oData.accountMenuFocus())
{
oData.accountMenuFocus(true);
}
});
} }
_.extend(AbstractSystemDropDownViewModel.prototype, KnoinAbstractViewModel.prototype); _.extend(AbstractSystemDropDownViewModel.prototype, KnoinAbstractViewModel.prototype);
@ -14851,6 +14872,17 @@ function WebMailDataStorage()
this.projectHash = ko.observable(''); this.projectHash = ko.observable('');
this.threading = ko.observable(false); this.threading = ko.observable(false);
this.accountMenuFocus = ko.observable(false);
this.accountMenuFocus.sKeyState = Enums.KeyState.All;
this.accountMenuFocus.subscribe(function (bValue) {
if (bValue)
{
this.accountMenuFocus.sKeyState = RL.data().keyScope();
}
RL.data().keyScope(bValue ? Enums.KeyState.Menu : this.accountMenuFocus.sKeyState);
}, this);
this.lastFoldersHash = ''; this.lastFoldersHash = '';
this.remoteSuggestions = false; this.remoteSuggestions = false;

File diff suppressed because one or more lines are too long