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; if (shiftSelect || moveFocused) return;
switch (e.key) { switch (e.key) {
case "ArrowLeft": case Zotero.arrowPreviousKey:
const parentIndex = this.props.getParentIndex(this.selection.focused); const parentIndex = this.props.getParentIndex(this.selection.focused);
if (this.props.isContainer(this.selection.focused) if (this.props.isContainer(this.selection.focused)
&& !this.props.isContainerEmpty(this.selection.focused) && !this.props.isContainerEmpty(this.selection.focused)
@ -642,7 +642,7 @@ class VirtualizedTable extends React.Component {
} }
break; break;
case "ArrowRight": case Zotero.arrowNextKey:
if (this.props.isContainer(this.selection.focused) if (this.props.isContainer(this.selection.focused)
&& !this.props.isContainerEmpty(this.selection.focused)) { && !this.props.isContainerEmpty(this.selection.focused)) {
if (!this.props.isContainerOpen(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)) { else if (this.isPaginatedType && ["ArrowLeft", "ArrowRight"].includes(e.key)) {
let gotoType = { let gotoType = {
ArrowLeft: "prev", [Zotero.arrowPreviousKey]: "prev",
ArrowRight: "next" [Zotero.arrowNextKey]: "next"
}; };
this.goto(gotoType[e.key]); this.goto(gotoType[e.key]);
stopEvent = true; stopEvent = true;

View file

@ -353,10 +353,10 @@
} }
} }
// Space/Enter toggle section open/closed. // 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)) { if (["ArrowLeft", "ArrowRight", " ", "Enter"].includes(event.key)) {
stopEvent(); 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(); event.target.focus();
} }
if (["ArrowUp", "ArrowDown"].includes(event.key)) { if (["ArrowUp", "ArrowDown"].includes(event.key)) {

View file

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

View file

@ -54,11 +54,11 @@
// Select pane with left/right arrow key // Select pane with left/right arrow key
this.addEventListener('keypress', (event) => { 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.choosePane(this._rightPane);
this.rightPane.groupbox.focus(); 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.choosePane(this._leftPane);
this._leftPane.groupbox.focus(); 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 // 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(); _lastFocusedInput.focus();
moveFocusBack(_lastFocusedInput); moveFocusBack(_lastFocusedInput);
} }
else if ("ArrowRight" == event.key) { else if (event.key == Zotero.arrowNextKey) {
_lastFocusedInput.focus(); _lastFocusedInput.focus();
moveFocusForward(_lastFocusedInput); moveFocusForward(_lastFocusedInput);
} }
@ -1879,13 +1879,13 @@ var Zotero_QuickFormat = new function () {
Zotero_QuickFormat._bubbleizeSelected(); Zotero_QuickFormat._bubbleizeSelected();
} }
else if (["ArrowLeft", "ArrowRight"].includes(event.key) && !event.shiftKey) { else if (["ArrowLeft", "ArrowRight"].includes(event.key) && !event.shiftKey) {
// On arrow left from the beginning of the input, move to previous bubble // On arrow left (right in RTL) from the beginning of the input, move to previous bubble
if (event.key === "ArrowLeft" && this.selectionStart === 0) { if (event.key === Zotero.arrowPreviousKey && this.selectionStart === 0) {
moveFocusBack(this); moveFocusBack(this);
event.preventDefault(); event.preventDefault();
} }
// On arrow right from the end of the input, move to next bubble // On arrow right (left in RTL) from the end of the input, move to next bubble
else if (event.key === "ArrowRight" && this.selectionStart === this.value.length) { else if (event.key === Zotero.arrowNextKey && this.selectionStart === this.value.length) {
moveFocusForward(this); moveFocusForward(this);
event.preventDefault(); event.preventDefault();
} }
@ -1940,7 +1940,7 @@ var Zotero_QuickFormat = new function () {
event.preventDefault(); event.preventDefault();
let newInput = _createInputField(); let newInput = _createInputField();
if (["ArrowLeft"].includes(event.key)) { if (event.key === Zotero.arrowPreviousKey) {
if (isInput(this.previousElementSibling)) { if (isInput(this.previousElementSibling)) {
moveFocusBack(this); moveFocusBack(this);
} }
@ -1949,7 +1949,7 @@ var Zotero_QuickFormat = new function () {
newInput.focus(); newInput.focus();
} }
} }
else if (["ArrowRight"].includes(event.key)) { else if (event.key === Zotero.arrowNextKey) {
if (isInput(this.nextElementSibling)) { if (isInput(this.nextElementSibling)) {
moveFocusForward(this); moveFocusForward(this);
} }
@ -1965,13 +1965,13 @@ var Zotero_QuickFormat = new function () {
let findNextBubble = () => { let findNextBubble = () => {
let node = event.target; let node = event.target;
do { 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"))); } while (node && !(node.classList.contains("bubble") || node.classList.contains("zotero-bubble-input")));
return node; return node;
}; };
let nextBubble = findNextBubble(); let nextBubble = findNextBubble();
if (nextBubble) { if (nextBubble) {
if (event.key == "ArrowLeft") { if (event.key === Zotero.arrowPreviousKey) {
nextBubble.before(this); nextBubble.before(this);
} }
else { else {

View file

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

View file

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

View file

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