RTL: Use direction-aware arrow key handling

This commit is contained in:
Abe Jellinek 2024-07-08 15:49:42 -04:00 committed by Dan Stillman
parent ea2d4417e1
commit 043c6e1795
9 changed files with 55 additions and 49 deletions

View file

@ -630,7 +630,7 @@ class VirtualizedTable extends React.Component {
if (shiftSelect || moveFocused) return;
switch (e.key) {
case "ArrowLeft":
case Zotero.arrowPreviousKey:
const parentIndex = this.props.getParentIndex(this.selection.focused);
if (this.props.isContainer(this.selection.focused)
&& !this.props.isContainerEmpty(this.selection.focused)
@ -642,7 +642,7 @@ class VirtualizedTable extends React.Component {
}
break;
case "ArrowRight":
case Zotero.arrowNextKey:
if (this.props.isContainer(this.selection.focused)
&& !this.props.isContainerEmpty(this.selection.focused)) {
if (!this.props.isContainerOpen(this.selection.focused)) {

View file

@ -519,8 +519,8 @@
}
else if (this.isPaginatedType && ["ArrowLeft", "ArrowRight"].includes(e.key)) {
let gotoType = {
ArrowLeft: "prev",
ArrowRight: "next"
[Zotero.arrowPreviousKey]: "prev",
[Zotero.arrowNextKey]: "next"
};
this.goto(gotoType[e.key]);
stopEvent = true;

View file

@ -353,10 +353,10 @@
}
}
// Space/Enter toggle section open/closed.
// ArrowLeft/ArrowRight on actual header will close/open
// ArrowLeft/ArrowRight on actual header will close/open (depending on locale direction)
if (["ArrowLeft", "ArrowRight", " ", "Enter"].includes(event.key)) {
stopEvent();
this.open = ([" ", "Enter"].includes(event.key)) ? !this.open : (event.key == "ArrowRight");
this.open = ([" ", "Enter"].includes(event.key)) ? !this.open : (event.key == Zotero.arrowNextKey);
event.target.focus();
}
if (["ArrowUp", "ArrowDown"].includes(event.key)) {

View file

@ -195,10 +195,10 @@
});
tile.addEventListener('keydown', (event) => {
switch (event.key) {
case 'ArrowLeft':
case Zotero.arrowPreviousKey:
(tile.previousElementSibling || tile.parentElement.lastElementChild).focus();
break;
case 'ArrowRight':
case Zotero.arrowNextKey:
(tile.nextElementSibling || tile.parentElement.firstElementChild).focus();
break;
case 'ArrowUp': {

View file

@ -54,11 +54,11 @@
// Select pane with left/right arrow key
this.addEventListener('keypress', (event) => {
if (event.key == "ArrowRight" && !this._rightPane.hasAttribute("selected")) {
if (event.key == Zotero.arrowNextKey && !this._rightPane.hasAttribute("selected")) {
this.choosePane(this._rightPane);
this.rightPane.groupbox.focus();
}
else if (event.key == "ArrowLeft" && !this._leftPane.hasAttribute("selected")) {
else if (event.key == Zotero.arrowPreviousKey && !this._leftPane.hasAttribute("selected")) {
this.choosePane(this._leftPane);
this._leftPane.groupbox.focus();
}

View file

@ -133,11 +133,11 @@ var Zotero_QuickFormat = new function () {
}
}
// Right/Left arrow will hide ref panel and move focus to the previour/next element
else if ("ArrowLeft" == event.key) {
else if (event.key == Zotero.arrowPreviousKey) {
_lastFocusedInput.focus();
moveFocusBack(_lastFocusedInput);
}
else if ("ArrowRight" == event.key) {
else if (event.key == Zotero.arrowNextKey) {
_lastFocusedInput.focus();
moveFocusForward(_lastFocusedInput);
}
@ -1879,13 +1879,13 @@ var Zotero_QuickFormat = new function () {
Zotero_QuickFormat._bubbleizeSelected();
}
else if (["ArrowLeft", "ArrowRight"].includes(event.key) && !event.shiftKey) {
// On arrow left from the beginning of the input, move to previous bubble
if (event.key === "ArrowLeft" && this.selectionStart === 0) {
// On arrow left (right in RTL) from the beginning of the input, move to previous bubble
if (event.key === Zotero.arrowPreviousKey && this.selectionStart === 0) {
moveFocusBack(this);
event.preventDefault();
}
// On arrow right from the end of the input, move to next bubble
else if (event.key === "ArrowRight" && this.selectionStart === this.value.length) {
// On arrow right (left in RTL) from the end of the input, move to next bubble
else if (event.key === Zotero.arrowNextKey && this.selectionStart === this.value.length) {
moveFocusForward(this);
event.preventDefault();
}
@ -1940,7 +1940,7 @@ var Zotero_QuickFormat = new function () {
event.preventDefault();
let newInput = _createInputField();
if (["ArrowLeft"].includes(event.key)) {
if (event.key === Zotero.arrowPreviousKey) {
if (isInput(this.previousElementSibling)) {
moveFocusBack(this);
}
@ -1949,7 +1949,7 @@ var Zotero_QuickFormat = new function () {
newInput.focus();
}
}
else if (["ArrowRight"].includes(event.key)) {
else if (event.key === Zotero.arrowNextKey) {
if (isInput(this.nextElementSibling)) {
moveFocusForward(this);
}
@ -1965,13 +1965,13 @@ var Zotero_QuickFormat = new function () {
let findNextBubble = () => {
let node = event.target;
do {
node = event.key == "ArrowLeft" ? node.previousElementSibling : node.nextElementSibling;
node = event.key == Zotero.arrowPreviousKey ? node.previousElementSibling : node.nextElementSibling;
} while (node && !(node.classList.contains("bubble") || node.classList.contains("zotero-bubble-input")));
return node;
};
let nextBubble = findNextBubble();
if (nextBubble) {
if (event.key == "ArrowLeft") {
if (event.key === Zotero.arrowPreviousKey) {
nextBubble.before(this);
}
else {

View file

@ -910,7 +910,7 @@ var ItemTree = class ItemTree extends LibraryTree {
// Handle arrow keys specially on multiple selection, since
// otherwise the tree just applies it to the last-selected row
if (this.selection.count > 1 && ["ArrowLeft", "ArrowRight"].includes(event.key)) {
if (event.key == "ArrowRight") {
if (event.key == Zotero.arrowNextKey) {
this.expandSelectedRows();
}
else {

View file

@ -599,8 +599,8 @@ var Zotero_Tabs = new function () {
/**
* Moves focus to a tab in the specified direction.
* @param {String} direction. "first", "last", "left", "right", or "current"
* If document.activeElement is a tab, "left" or "right" direction moves focus from that tab.
* @param {String} direction. "first", "last", "previous", "next", or "current"
* If document.activeElement is a tab, "previous" or "next" direction moves focus from that tab.
* Otherwise, focus is moved in the given direction from the currently selected tab.
*/
this.moveFocus = function (direction) {
@ -627,10 +627,10 @@ var Zotero_Tabs = new function () {
}
switch (direction) {
case "left":
case "previous":
tabIndexToFocus = focusedTabIndex > 0 ? focusedTabIndex - 1 : null;
break;
case "right":
case "next":
tabIndexToFocus = focusedTabIndex < this._tabs.length - 1 ? focusedTabIndex + 1 : null;
break;
default:

View file

@ -195,6 +195,12 @@ var ZoteroPane = new function()
else if (verticalArrowIsTab && key == 'ArrowDown' && !onInput) {
key = 'Tab';
}
if (key == Zotero.arrowPreviousKey) {
key = 'ArrowPrevious';
}
else if (key == Zotero.arrowNextKey) {
key = 'ArrowNext';
}
// 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
@ -246,16 +252,16 @@ var ZoteroPane = new function()
// Mapping of target ids and possible key presses to desired focus outcomes
let actionsMap = {
'zotero-tb-tabs-menu': {
ArrowRight: () => null,
ArrowLeft: () => null,
ArrowNext: () => null,
ArrowPrevious: () => null,
Tab: () => document.getElementById('zotero-tb-sync-error'),
ShiftTab: () => {
Zotero_Tabs.moveFocus("current");
},
},
'zotero-tb-sync': {
ArrowRight: () => null,
ArrowLeft: () => null,
ArrowNext: () => null,
ArrowPrevious: () => null,
Tab: () => {
if (Zotero_Tabs.selectedIndex > 0) {
let reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
@ -274,8 +280,8 @@ var ZoteroPane = new function()
ShiftTab: () => document.getElementById('zotero-tb-sync-error')
},
'zotero-tb-sync-error': {
ArrowRight: () => null,
ArrowLeft: () => null,
ArrowNext: () => null,
ArrowPrevious: () => null,
Tab: () => document.getElementById('zotero-tb-sync'),
ShiftTab: () => document.getElementById('zotero-tb-tabs-menu'),
Enter: () => document.getElementById("zotero-tb-sync-error")
@ -287,17 +293,17 @@ var ZoteroPane = new function()
// keyboard navigation for tabs. 'tab' is the class, not the id
Tab: () => document.getElementById('zotero-tb-tabs-menu'),
ShiftTab: Zotero_Tabs.focusWrapAround,
ArrowRight: (e) => {
ArrowNext: (e) => {
if (cmdOrCtrlOnly(e)) {
Zotero_Tabs.moveFocus("right");
Zotero_Tabs.moveFocus("next");
}
else {
Zotero_Tabs.selectNext({ keepTabFocused: true });
}
},
ArrowLeft: (e) => {
ArrowPrevious: (e) => {
if (cmdOrCtrlOnly(e)) {
Zotero_Tabs.moveFocus("left");
Zotero_Tabs.moveFocus("previous");
}
else {
Zotero_Tabs.selectPrev({ keepTabFocused: true });
@ -345,8 +351,8 @@ var ZoteroPane = new function()
collectionTreeToolbar.addEventListener("keydown", (event) => {
let actionsMap = {
'zotero-tb-collection-add': {
ArrowRight: () => null,
ArrowLeft: () => null,
ArrowNext: () => null,
ArrowPrevious: () => null,
Tab: () => document.getElementById('zotero-tb-collections-search').click(),
ShiftTab: () => document.getElementById('zotero-tb-sync')
},
@ -363,8 +369,8 @@ var ZoteroPane = new function()
itemTreeToolbar.addEventListener("keydown", (event) => {
let actionsMap = {
'zotero-tb-add': {
ArrowRight: () => document.getElementById("zotero-tb-lookup"),
ArrowLeft: () => null,
ArrowNext: () => document.getElementById("zotero-tb-lookup"),
ArrowPrevious: () => null,
Tab: () => document.getElementById("zotero-tb-search")._searchModePopup.flattenedTreeParentNode.focus(),
ShiftTab: () => {
if (collectionsPane.getAttribute("collapsed")) {
@ -377,22 +383,22 @@ var ZoteroPane = new function()
}
},
'zotero-tb-lookup': {
ArrowRight: () => document.getElementById("zotero-tb-attachment-add"),
ArrowLeft: () => document.getElementById("zotero-tb-add"),
ArrowNext: () => document.getElementById("zotero-tb-attachment-add"),
ArrowPrevious: () => document.getElementById("zotero-tb-add"),
Tab: () => document.getElementById("zotero-tb-search")._searchModePopup.flattenedTreeParentNode.focus(),
ShiftTab: () => document.getElementById('zotero-tb-collections-search').click(),
Enter: () => Zotero_Lookup.showPanel(event.target),
' ': () => Zotero_Lookup.showPanel(event.target)
},
'zotero-tb-attachment-add': {
ArrowRight: () => document.getElementById("zotero-tb-note-add"),
ArrowLeft: () => document.getElementById("zotero-tb-lookup"),
ArrowNext: () => document.getElementById("zotero-tb-note-add"),
ArrowPrevious: () => document.getElementById("zotero-tb-lookup"),
Tab: () => document.getElementById("zotero-tb-search")._searchModePopup.flattenedTreeParentNode.focus(),
ShiftTab: () => document.getElementById('zotero-tb-collections-search').click()
},
'zotero-tb-note-add': {
ArrowRight: () => null,
ArrowLeft: () => document.getElementById("zotero-tb-attachment-add"),
ArrowNext: () => null,
ArrowPrevious: () => document.getElementById("zotero-tb-attachment-add"),
Tab: () => document.getElementById("zotero-tb-search")._searchModePopup.flattenedTreeParentNode.focus(),
ShiftTab: () => document.getElementById('zotero-tb-collections-search').click()
},
@ -403,8 +409,8 @@ var ZoteroPane = new function()
Tab: () => document.getElementById('item-tree-main-default')
},
'zotero-tb-search-dropmarker': {
ArrowRight: () => null,
ArrowLeft: () => null,
ArrowNext: () => null,
ArrowPrevious: () => null,
Tab: () => document.getElementById("zotero-tb-search-textbox"),
ShiftTab: () => document.getElementById('zotero-tb-add')
}
@ -951,13 +957,13 @@ var ZoteroPane = new function()
}
}
else if (event.metaKey && event.altKey) {
if (event.key == 'ArrowLeft') {
if (event.key == Zotero.arrowPreviousKey) {
Zotero_Tabs.selectPrev();
event.preventDefault();
event.stopPropagation();
return;
}
else if (event.key == 'ArrowRight') {
else if (event.key == Zotero.arrowNextKey) {
Zotero_Tabs.selectNext();
event.preventDefault();
event.stopPropagation();