additions to keyboard nav for toolbar buttons and reader (#3602)
- handle space and enter in zotero pane to always click on toolbarbutton. It ensures consistent behavior across all components to avoid Enter triggering click on some buttons but not others. - address the issue of focus not being able to leave the reader once it gets there. On Escape, when reader is opened, instead of re-focusing the reader, focus the selected tab. From there, Enter will focus the reader, and tabbing allows you to reach the itemPane. - Tab/Shift-tab moves focus between Sync button and itemPane when reader is opened
This commit is contained in:
parent
e69ce9b86c
commit
ceb1dd7da3
5 changed files with 49 additions and 27 deletions
|
@ -277,8 +277,9 @@ var ZoteroContextPane = new function () {
|
||||||
|
|
||||||
if (splitter.getAttribute('state') != 'collapsed') {
|
if (splitter.getAttribute('state') != 'collapsed') {
|
||||||
if (_panesDeck.selectedIndex == 0) {
|
if (_panesDeck.selectedIndex == 0) {
|
||||||
var node = _itemPaneDeck.selectedPanel;
|
// Focus the title in the header
|
||||||
node.focus();
|
var header = _itemPaneDeck.selectedPanel.querySelector("pane-header editable-text");
|
||||||
|
header.focus();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -873,6 +874,7 @@ var ZoteroContextPane = new function () {
|
||||||
// div
|
// div
|
||||||
var div = document.createElement('div');
|
var div = document.createElement('div');
|
||||||
div.className = 'zotero-view-item';
|
div.className = 'zotero-view-item';
|
||||||
|
div.setAttribute("tabindex", "0");
|
||||||
main.append(div);
|
main.append(div);
|
||||||
|
|
||||||
// Info
|
// Info
|
||||||
|
|
|
@ -180,6 +180,16 @@ var ZoteroItemPane = new function() {
|
||||||
let sidenav = document.getElementById(
|
let sidenav = document.getElementById(
|
||||||
isLibraryTab ? 'zotero-view-item-sidenav' : 'zotero-context-pane-sidenav'
|
isLibraryTab ? 'zotero-view-item-sidenav' : 'zotero-context-pane-sidenav'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Shift-tab from title when reader is opened focuses the last button in tabs toolbar
|
||||||
|
if (event.target.closest(".title") && event.key == "Tab"
|
||||||
|
&& event.shiftKey && Zotero_Tabs.selectedType == "reader") {
|
||||||
|
let focusable = [...document.querySelectorAll("#zotero-tabs-toolbar toolbarbutton:not([disabled]):not([hidden])")];
|
||||||
|
let btn = focusable[focusable.length - 1];
|
||||||
|
btn.focus();
|
||||||
|
stopEvent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Tab from the scrollable area focuses the pinned pane if it exists
|
// Tab from the scrollable area focuses the pinned pane if it exists
|
||||||
if (event.target.classList.contains("zotero-view-item") && event.key == "Tab" && !event.shiftKey && sidenav.pinnedPane) {
|
if (event.target.classList.contains("zotero-view-item") && event.key == "Tab" && !event.shiftKey && sidenav.pinnedPane) {
|
||||||
let pane = sidenav.getPane(sidenav.pinnedPane);
|
let pane = sidenav.getPane(sidenav.pinnedPane);
|
||||||
|
@ -187,13 +197,6 @@ var ZoteroItemPane = new function() {
|
||||||
stopEvent();
|
stopEvent();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Space or Enter on a button or 'keyboard-clickable' triggers a click
|
|
||||||
if ([" ", "Enter"].includes(event.key)
|
|
||||||
&& (event.target.tagName == "toolbarbutton"
|
|
||||||
|| event.target.classList.contains("keyboard-clickable"))) {
|
|
||||||
event.target.click();
|
|
||||||
stopEvent();
|
|
||||||
}
|
|
||||||
// Tab tavigation between entries and buttons within library, related and notes boxes
|
// Tab tavigation between entries and buttons within library, related and notes boxes
|
||||||
if (event.key == "Tab" && event.target.closest(".box")) {
|
if (event.key == "Tab" && event.target.closest(".box")) {
|
||||||
let next = null;
|
let next = null;
|
||||||
|
|
|
@ -244,6 +244,10 @@ var ZoteroPane = new function()
|
||||||
ArrowRight: () => null,
|
ArrowRight: () => null,
|
||||||
ArrowLeft: () => null,
|
ArrowLeft: () => null,
|
||||||
Tab: () => {
|
Tab: () => {
|
||||||
|
if (Zotero_Tabs.selectedIndex > 0) {
|
||||||
|
ZoteroContextPane.focus();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (collectionsPane.getAttribute("collapsed")) {
|
if (collectionsPane.getAttribute("collapsed")) {
|
||||||
return document.getElementById('zotero-tb-add');
|
return document.getElementById('zotero-tb-add');
|
||||||
}
|
}
|
||||||
|
@ -313,6 +317,9 @@ var ZoteroPane = new function()
|
||||||
ArrowLeft: () => null,
|
ArrowLeft: () => null,
|
||||||
Tab: () => document.getElementById('zotero-tb-collections-search').click(),
|
Tab: () => document.getElementById('zotero-tb-collections-search').click(),
|
||||||
ShiftTab: () => document.getElementById('zotero-tb-sync')
|
ShiftTab: () => document.getElementById('zotero-tb-sync')
|
||||||
|
// Enter: () => {
|
||||||
|
// document.getElementById('zotero-tb-collection-add').click();
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
'zotero-collections-search': {
|
'zotero-collections-search': {
|
||||||
Tab: () => document.getElementById('zotero-tb-add'),
|
Tab: () => document.getElementById('zotero-tb-add'),
|
||||||
|
@ -349,12 +356,6 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
document.getElementById('zotero-tb-collections-search').click();
|
document.getElementById('zotero-tb-collections-search').click();
|
||||||
return null;
|
return null;
|
||||||
},
|
|
||||||
' ': () => {
|
|
||||||
document.getElementById('zotero-tb-add').open = true;
|
|
||||||
},
|
|
||||||
Enter: () => {
|
|
||||||
document.getElementById('zotero-tb-add').open = true;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'zotero-tb-lookup': {
|
'zotero-tb-lookup': {
|
||||||
|
@ -855,18 +856,17 @@ var ZoteroPane = new function()
|
||||||
function handleKeyDown(event, from) {
|
function handleKeyDown(event, from) {
|
||||||
if (Zotero_Tabs.selectedIndex > 0) {
|
if (Zotero_Tabs.selectedIndex > 0) {
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
if (!document.activeElement.classList.contains('reader')) {
|
// If focus is on an opened popup, let Escape just close it
|
||||||
let reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
|
// Also, do nothing if focus is inside of tabs toolbar
|
||||||
if (reader) {
|
if (document.activeElement.open
|
||||||
reader.focus();
|
|| document.querySelector("#zotero-tabs-toolbar").contains(document.activeElement)) {
|
||||||
// Keep propagating if current focus is on input or textarea
|
return;
|
||||||
// The Escape event needs to be handled by itemBox, tagBox, etc. to undo edits.
|
|
||||||
if (!["input", "textarea"].includes(document.activeElement.tagName)) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Escape when a reader tab is opened re-focuses the tab in the tab bar
|
||||||
|
// Timeout to let the focused editable-text reset the value
|
||||||
|
setTimeout(() => {
|
||||||
|
Zotero_Tabs.moveFocus("current");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else if (event.key === 'Tab' && event.shiftKey) {
|
else if (event.key === 'Tab' && event.shiftKey) {
|
||||||
let node = document.activeElement;
|
let node = document.activeElement;
|
||||||
|
@ -1014,6 +1014,20 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tgt = event.target;
|
||||||
|
if ([" ", "Enter"].includes(event.key)
|
||||||
|
&& (["button", "toolbarbutton"].includes(tgt.tagName)
|
||||||
|
|| tgt.classList.contains("keyboard-clickable"))) {
|
||||||
|
if (event.target.querySelector("menupopup")) {
|
||||||
|
event.target.open = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
event.target.click();
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// Ignore keystrokes outside of Zotero pane
|
// Ignore keystrokes outside of Zotero pane
|
||||||
if (!(event.originalTarget.ownerDocument instanceof HTMLDocument)) {
|
if (!(event.originalTarget.ownerDocument instanceof HTMLDocument)) {
|
||||||
|
|
|
@ -1233,7 +1233,7 @@
|
||||||
<html:div class="zotero-view-item-main">
|
<html:div class="zotero-view-item-main">
|
||||||
<pane-header id="zotero-item-pane-header" />
|
<pane-header id="zotero-item-pane-header" />
|
||||||
|
|
||||||
<html:div id="zotero-view-item" class="zotero-view-item">
|
<html:div id="zotero-view-item" class="zotero-view-item" tabindex="0">
|
||||||
<item-box id="zotero-editpane-item-box" data-pane="info"/>
|
<item-box id="zotero-editpane-item-box" data-pane="info"/>
|
||||||
|
|
||||||
<abstract-box id="zotero-editpane-abstract" class="zotero-editpane-abstract" data-pane="abstract"/>
|
<abstract-box id="zotero-editpane-abstract" class="zotero-editpane-abstract" data-pane="abstract"/>
|
||||||
|
|
|
@ -32,5 +32,8 @@ pane-header {
|
||||||
|
|
||||||
.menu-button toolbarbutton {
|
.menu-button toolbarbutton {
|
||||||
@include svgicon-menu("go-to", "universal", "20");
|
@include svgicon-menu("go-to", "universal", "20");
|
||||||
|
--radius-focus-border: 5px;
|
||||||
|
--width-focus-border: 2px;
|
||||||
|
@include focus-ring;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue