From 043c6e179529b133a865196fd2cffcf4fa20ea88 Mon Sep 17 00:00:00 2001 From: Abe Jellinek Date: Mon, 8 Jul 2024 15:49:42 -0400 Subject: [PATCH] RTL: Use direction-aware arrow key handling --- .../zotero/components/virtualized-table.jsx | 4 +- .../zotero/elements/attachmentPreview.js | 4 +- .../zotero/elements/collapsibleSection.js | 4 +- chrome/content/zotero/elements/colorPicker.js | 4 +- chrome/content/zotero/elements/mergeGroup.js | 4 +- .../content/zotero/integration/quickFormat.js | 20 +++---- chrome/content/zotero/itemTree.jsx | 2 +- chrome/content/zotero/tabs.js | 8 +-- chrome/content/zotero/zoteroPane.js | 54 ++++++++++--------- 9 files changed, 55 insertions(+), 49 deletions(-) diff --git a/chrome/content/zotero/components/virtualized-table.jsx b/chrome/content/zotero/components/virtualized-table.jsx index 48e71383a2..a1f59c8324 100644 --- a/chrome/content/zotero/components/virtualized-table.jsx +++ b/chrome/content/zotero/components/virtualized-table.jsx @@ -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)) { diff --git a/chrome/content/zotero/elements/attachmentPreview.js b/chrome/content/zotero/elements/attachmentPreview.js index 4972ca4e0d..58c50ab225 100644 --- a/chrome/content/zotero/elements/attachmentPreview.js +++ b/chrome/content/zotero/elements/attachmentPreview.js @@ -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; diff --git a/chrome/content/zotero/elements/collapsibleSection.js b/chrome/content/zotero/elements/collapsibleSection.js index 8fb86911c9..015e7e7bda 100644 --- a/chrome/content/zotero/elements/collapsibleSection.js +++ b/chrome/content/zotero/elements/collapsibleSection.js @@ -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)) { diff --git a/chrome/content/zotero/elements/colorPicker.js b/chrome/content/zotero/elements/colorPicker.js index 610b2d0a1d..c9c8189690 100644 --- a/chrome/content/zotero/elements/colorPicker.js +++ b/chrome/content/zotero/elements/colorPicker.js @@ -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': { diff --git a/chrome/content/zotero/elements/mergeGroup.js b/chrome/content/zotero/elements/mergeGroup.js index 276cf6186f..f0e890b806 100644 --- a/chrome/content/zotero/elements/mergeGroup.js +++ b/chrome/content/zotero/elements/mergeGroup.js @@ -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(); } diff --git a/chrome/content/zotero/integration/quickFormat.js b/chrome/content/zotero/integration/quickFormat.js index dd3072ac0d..db828ab386 100644 --- a/chrome/content/zotero/integration/quickFormat.js +++ b/chrome/content/zotero/integration/quickFormat.js @@ -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 { diff --git a/chrome/content/zotero/itemTree.jsx b/chrome/content/zotero/itemTree.jsx index 6846c953c8..cabe996b5c 100644 --- a/chrome/content/zotero/itemTree.jsx +++ b/chrome/content/zotero/itemTree.jsx @@ -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 { diff --git a/chrome/content/zotero/tabs.js b/chrome/content/zotero/tabs.js index c38c1ff583..34b3f5deb0 100644 --- a/chrome/content/zotero/tabs.js +++ b/chrome/content/zotero/tabs.js @@ -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: diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index fb1a01f1da..d0832eca7d 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -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();