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:
abaevbog 2024-02-06 12:51:06 -05:00 committed by GitHub
parent e69ce9b86c
commit ceb1dd7da3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 49 additions and 27 deletions

View file

@ -277,8 +277,9 @@ var ZoteroContextPane = new function () {
if (splitter.getAttribute('state') != 'collapsed') {
if (_panesDeck.selectedIndex == 0) {
var node = _itemPaneDeck.selectedPanel;
node.focus();
// Focus the title in the header
var header = _itemPaneDeck.selectedPanel.querySelector("pane-header editable-text");
header.focus();
return true;
}
else {
@ -873,6 +874,7 @@ var ZoteroContextPane = new function () {
// div
var div = document.createElement('div');
div.className = 'zotero-view-item';
div.setAttribute("tabindex", "0");
main.append(div);
// Info

View file

@ -180,6 +180,16 @@ var ZoteroItemPane = new function() {
let sidenav = document.getElementById(
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
if (event.target.classList.contains("zotero-view-item") && event.key == "Tab" && !event.shiftKey && sidenav.pinnedPane) {
let pane = sidenav.getPane(sidenav.pinnedPane);
@ -187,13 +197,6 @@ var ZoteroItemPane = new function() {
stopEvent();
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
if (event.key == "Tab" && event.target.closest(".box")) {
let next = null;

View file

@ -244,6 +244,10 @@ var ZoteroPane = new function()
ArrowRight: () => null,
ArrowLeft: () => null,
Tab: () => {
if (Zotero_Tabs.selectedIndex > 0) {
ZoteroContextPane.focus();
return null;
}
if (collectionsPane.getAttribute("collapsed")) {
return document.getElementById('zotero-tb-add');
}
@ -313,6 +317,9 @@ var ZoteroPane = new function()
ArrowLeft: () => null,
Tab: () => document.getElementById('zotero-tb-collections-search').click(),
ShiftTab: () => document.getElementById('zotero-tb-sync')
// Enter: () => {
// document.getElementById('zotero-tb-collection-add').click();
// }
},
'zotero-collections-search': {
Tab: () => document.getElementById('zotero-tb-add'),
@ -349,12 +356,6 @@ var ZoteroPane = new function()
}
document.getElementById('zotero-tb-collections-search').click();
return null;
},
' ': () => {
document.getElementById('zotero-tb-add').open = true;
},
Enter: () => {
document.getElementById('zotero-tb-add').open = true;
}
},
'zotero-tb-lookup': {
@ -855,18 +856,17 @@ var ZoteroPane = new function()
function handleKeyDown(event, from) {
if (Zotero_Tabs.selectedIndex > 0) {
if (event.key === 'Escape') {
if (!document.activeElement.classList.contains('reader')) {
let reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
reader.focus();
// Keep propagating if current focus is on input or textarea
// 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();
}
}
// If focus is on an opened popup, let Escape just close it
// Also, do nothing if focus is inside of tabs toolbar
if (document.activeElement.open
|| document.querySelector("#zotero-tabs-toolbar").contains(document.activeElement)) {
return;
}
// 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) {
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 {
// Ignore keystrokes outside of Zotero pane
if (!(event.originalTarget.ownerDocument instanceof HTMLDocument)) {

View file

@ -1233,7 +1233,7 @@
<html:div class="zotero-view-item-main">
<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"/>
<abstract-box id="zotero-editpane-abstract" class="zotero-editpane-abstract" data-pane="abstract"/>

View file

@ -32,5 +32,8 @@ pane-header {
.menu-button toolbarbutton {
@include svgicon-menu("go-to", "universal", "20");
--radius-focus-border: 5px;
--width-focus-border: 2px;
@include focus-ring;
}
}