tabs menu update and minor refactoring
- tabs menu button is disabled when no reader tabs are opened - tabs menu popup will be hidden if all tabs are closed - keypress event handling moved from tabBar.jsx to zoteroPane.js because all other keyboard navigation events are handled there and it already has the functionality to skip disabled or hidden components (e.g. tabs menu button, sync error) - minor tweaks to tests to wait for collection search bar to hide to get keyboard navigation tests passing
This commit is contained in:
parent
a2e8294389
commit
8c0116d1db
4 changed files with 52 additions and 51 deletions
|
@ -273,46 +273,6 @@ const TabBar = forwardRef(function (props, ref) {
|
|||
event.preventDefault();
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
event = event.nativeEvent;
|
||||
let key = event.key;
|
||||
if (key == "Tab") {
|
||||
if (event.shiftKey) {
|
||||
// On shift-tab, wrap focus back to itemTree/itemPane
|
||||
props.wrapFocusAround();
|
||||
}
|
||||
else {
|
||||
// On tab go back to opened tabs menu
|
||||
document.getElementById('zotero-tb-tabs-menu').focus();
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
// Move focus between tabs with arrows
|
||||
if (["ArrowLeft", "ArrowRight"].includes(key)) {
|
||||
let direction = key == "ArrowLeft" ? "left" : "right";
|
||||
const cmdOrCtrlOnly = Zotero.isMac
|
||||
? (event.metaKey && !event.shiftKey && !event.ctrlKey && !event.altKey)
|
||||
: (event.ctrlKey && !event.shiftKey && !event.altKey);
|
||||
// Ctrl/CMD and an arrow shifts focus
|
||||
if (cmdOrCtrlOnly) {
|
||||
props.moveFocus(direction);
|
||||
}
|
||||
// Just an arrow selects the tab
|
||||
else if (direction == "left") {
|
||||
props.selectPrev({ keepTabFocused: true });
|
||||
}
|
||||
else {
|
||||
props.selectNext({ keepTabFocused: true });
|
||||
};
|
||||
}
|
||||
// Select focused tab on space or enter and focus on content pane
|
||||
if (key == " " || key == "Enter") {
|
||||
let tabID = event.target.getAttribute('data-id');
|
||||
props.onTabSelect(tabID, false, { keepTabFocused: false });
|
||||
}
|
||||
}
|
||||
|
||||
function renderTab({ id, title, selected, icon }, index) {
|
||||
return (
|
||||
<div
|
||||
|
@ -326,7 +286,6 @@ const TabBar = forwardRef(function (props, ref) {
|
|||
onAuxClick={(event) => handleTabClick(event, id)}
|
||||
onDragStart={(event) => handleDragStart(event, id, index)}
|
||||
onDragEnd={handleDragEnd}
|
||||
onKeyDown={handleKeyDown}
|
||||
tabIndex="-1"
|
||||
>
|
||||
{icon}
|
||||
|
|
|
@ -136,6 +136,12 @@ var Zotero_Tabs = new function () {
|
|||
focusTabsMenuEntry();
|
||||
}
|
||||
}
|
||||
// Disable tabs menu button when no reader tabs are present
|
||||
document.getElementById("zotero-tb-tabs-menu").disabled = this._tabs.length == 1;
|
||||
// Close tabs menu if all tabs are closed
|
||||
if (this._tabs.length == 1 && this.isTabsMenuVisible()) {
|
||||
this.tabsMenuPanel.hidePopup();
|
||||
}
|
||||
};
|
||||
|
||||
this.getTabIDByItemID = function (itemID) {
|
||||
|
@ -157,10 +163,6 @@ var Zotero_Tabs = new function () {
|
|||
onTabMove={this.move.bind(this)}
|
||||
onTabClose={this.close.bind(this)}
|
||||
onContextMenu={this._openMenu.bind(this)}
|
||||
moveFocus={this.moveFocus.bind(this)}
|
||||
wrapFocusAround={this.focusWrapAround.bind(this)}
|
||||
selectNext={this.selectNext.bind(this)}
|
||||
selectPrev={this.selectPrev.bind(this)}
|
||||
/>,
|
||||
document.getElementById('tab-bar-container'),
|
||||
() => {
|
||||
|
|
|
@ -150,7 +150,7 @@ var ZoteroPane = new function()
|
|||
function setUpKeyboardNavigation() {
|
||||
let collectionTreeToolbar = this.document.getElementById("zotero-toolbar-collection-tree");
|
||||
let itemTreeToolbar = this.document.getElementById("zotero-toolbar-item-tree");
|
||||
let tabsToolbar = this.document.getElementById("zotero-tabs-toolbar");
|
||||
let titleBar = this.document.getElementById("zotero-title-bar");
|
||||
let itemTree = this.document.getElementById("zotero-items-tree");
|
||||
let collectionsTree = this.document.getElementById("zotero-collections-tree");
|
||||
let tagSelector = this.document.getElementById("zotero-tag-selector");
|
||||
|
@ -181,14 +181,22 @@ var ZoteroPane = new function()
|
|||
else if (verticalArrowIsTab && key == 'ArrowDown' && !onInput) {
|
||||
key = 'Tab';
|
||||
}
|
||||
// Fetch the focusFunction by target id
|
||||
let focusFunction = actionsMap[event.target.id]?.[key];
|
||||
// If no function found by target id, try to search by class names
|
||||
if (focusFunction === undefined) {
|
||||
for (let className of event.target.classList) {
|
||||
focusFunction = actionsMap[className]?.[key];
|
||||
if (focusFunction) break;
|
||||
}
|
||||
}
|
||||
// If the focusFunction is undefined, nothing was found
|
||||
// for this combination of keys, so do nothing
|
||||
if (focusFunction === undefined) {
|
||||
return;
|
||||
}
|
||||
// Otherwise, fetch the target to focus on
|
||||
let target = focusFunction();
|
||||
let target = focusFunction(event);
|
||||
// If returned target is false, focusing was not handled,
|
||||
// so fallback to default focus target
|
||||
if (target === false) {
|
||||
|
@ -218,7 +226,9 @@ var ZoteroPane = new function()
|
|||
event.stopPropagation();
|
||||
};
|
||||
|
||||
tabsToolbar.addEventListener("keydown", (event) => {
|
||||
titleBar.addEventListener("keydown", (event) => {
|
||||
let cmdOrCtrlOnly = e => (Zotero.isMac ? (e.metaKey && !e.ctrlKey) : e.ctrlKey) && !e.shiftKey && !e.altKey;
|
||||
|
||||
// Mapping of target ids and possible key presses to desired focus outcomes
|
||||
let actionsMap = {
|
||||
'zotero-tb-tabs-menu': {
|
||||
|
@ -249,6 +259,33 @@ var ZoteroPane = new function()
|
|||
.dispatchEvent(new MouseEvent("click", { target: event.target })),
|
||||
' ': () => document.getElementById("zotero-tb-sync-error")
|
||||
.dispatchEvent(new MouseEvent("click", { target: event.target }))
|
||||
},
|
||||
tab: {
|
||||
// keyboard navigation for tabs. 'tab' is the class, not the id
|
||||
Tab: () => document.getElementById('zotero-tb-tabs-menu'),
|
||||
ShiftTab: Zotero_Tabs.focusWrapAround,
|
||||
ArrowRight: (e) => {
|
||||
if (cmdOrCtrlOnly(e)) {
|
||||
Zotero_Tabs.moveFocus("right");
|
||||
}
|
||||
else {
|
||||
Zotero_Tabs.selectNext({ keepTabFocused: true });
|
||||
}
|
||||
},
|
||||
ArrowLeft: (e) => {
|
||||
if (cmdOrCtrlOnly(e)) {
|
||||
Zotero_Tabs.moveFocus("left");
|
||||
}
|
||||
else {
|
||||
Zotero_Tabs.selectPrev({ keepTabFocused: true });
|
||||
}
|
||||
},
|
||||
Enter: (e) => {
|
||||
Zotero_Tabs.select(e.target.getAttribute('data-id'), false, { keepTabFocused: false });
|
||||
},
|
||||
' ': (e) => {
|
||||
Zotero_Tabs.select(e.target.getAttribute('data-id'), false, { keepTabFocused: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
moveFocus(actionsMap, event, true);
|
||||
|
|
|
@ -1533,11 +1533,14 @@ describe("ZoteroPane", function() {
|
|||
doc.activeElement.dispatchEvent(shiftTab);
|
||||
// Wait for collection search to be revealed
|
||||
if (id === "zotero-collections-search") {
|
||||
await Zotero.Promise.delay(300);
|
||||
await Zotero.Promise.delay(250);
|
||||
}
|
||||
assert.equal(doc.activeElement.id, id);
|
||||
// Wait for collection search to be hidden for subsequent tests
|
||||
if (id === "zotero-tb-collection-add") {
|
||||
await Zotero.Promise.delay(50);
|
||||
}
|
||||
}
|
||||
|
||||
doc.activeElement.dispatchEvent(shiftTab);
|
||||
assert.equal(doc.activeElement.className, "tab selected");
|
||||
|
||||
|
@ -1561,7 +1564,7 @@ describe("ZoteroPane", function() {
|
|||
doc.activeElement.dispatchEvent(tab);
|
||||
// Wait for collection search to be revealed
|
||||
if (id === "zotero-collections-search") {
|
||||
await Zotero.Promise.delay(300);
|
||||
await Zotero.Promise.delay(250);
|
||||
}
|
||||
assert.equal(doc.activeElement.id, id);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue