diff --git a/chrome/content/zotero/components/tagSelector/tagSelectorList.jsx b/chrome/content/zotero/components/tagSelector/tagSelectorList.jsx
index af642672d8..0cc125ad47 100644
--- a/chrome/content/zotero/components/tagSelector/tagSelectorList.jsx
+++ b/chrome/content/zotero/components/tagSelector/tagSelectorList.jsx
@@ -224,6 +224,36 @@ class TagList extends React.PureComponent {
);
};
+ tagSelectorList() {
+ return document.querySelector('.tag-selector-list');
+ }
+
+ isEmpty() {
+ return !this.tagSelectorList().querySelector('.tag-selector-item:not(.disabled)');
+ }
+
+ clearRecordedFocusedTag() {
+ this.lastFocusedTagIndex = null;
+ this.focusedTagIndex = null;
+ }
+
+ // Focus the last focused tag from the list. If there is none, focus the first
+ // non-disabled tag.
+ async focus() {
+ if (this.focusedTagIndex === null) {
+ let enabledTag = this.tagSelectorList().querySelector('.tag-selector-item:not(.disabled)');
+ if (!enabledTag) return;
+ enabledTag.focus();
+ return;
+ }
+ let tagRefocused = this.refocusTag();
+ if (tagRefocused) return;
+ // If the tag could not be refocused, it means it was removed due to windowing,
+ // so we need to scroll to it.
+ this.setState({ scrollToCell: this.focusedTagIndex });
+ await this.waitForSectionRender();
+ }
+
// Try to refocus a focused tag that was removed due to windowing
refocusTag() {
let tagsList = document.querySelector('.tag-selector-list');
@@ -232,7 +262,9 @@ class TagList extends React.PureComponent {
let nodeToFocus = tagsNodes.find(node => node.textContent == tagToFocus.name);
if (nodeToFocus) {
nodeToFocus.focus();
+ return true;
}
+ return false;
}
waitForSectionRender() {
@@ -266,15 +298,6 @@ class TagList extends React.PureComponent {
}
};
- handleBlur = (event) => {
- // If the focus leaves the tags list, clear the last focused tag index
- let tagsList = document.querySelector('.tag-selector-list');
- if (!tagsList.contains(event.relatedTarget)) {
- this.focusedTagIndex = null;
- this.lastFocusedTagIndex = null;
- }
- };
-
async handleKeyDown(e) {
if (!["ArrowRight", "ArrowLeft"].includes(e.key)) return;
// If the windowing kicks in, the node of the initially-focused tag may not
@@ -346,7 +369,7 @@ class TagList extends React.PureComponent {
}
return (
-
+
{tagList}
);
diff --git a/chrome/content/zotero/containers/tagSelectorContainer.jsx b/chrome/content/zotero/containers/tagSelectorContainer.jsx
index 1159d3936d..c3125ee725 100644
--- a/chrome/content/zotero/containers/tagSelectorContainer.jsx
+++ b/chrome/content/zotero/containers/tagSelectorContainer.jsx
@@ -81,6 +81,14 @@ Zotero.TagSelector = class TagSelectorContainer extends React.PureComponent {
this.searchBoxRef.current.focus();
}
+ focusTagList() {
+ this.tagListRef.current.focus();
+ }
+
+ isTagListEmpty() {
+ return this.tagListRef.current.isEmpty();
+ }
+
componentDidCatch(error, info) {
// Async operations might attempt to update the react components
// after window close in tests, which will cause unnecessary crashing.
@@ -100,6 +108,12 @@ Zotero.TagSelector = class TagSelectorContainer extends React.PureComponent {
this.prevTreeViewID = this.collectionTreeRow.id;
}
}
+
+ getSnapshotBeforeUpdate(_) {
+ // Clear the focused tag's record if the props change
+ this.tagListRef.current.clearRecordedFocusedTag();
+ return null;
+ }
// Update trigger #1 (triggered by ZoteroPane)
async onItemViewChanged({ collectionTreeRow, libraryID }) {
@@ -528,7 +542,6 @@ Zotero.TagSelector = class TagSelectorContainer extends React.PureComponent {
searchString={this.state.searchString}
dragObserver={this.dragObserver}
onSelect={this.handleTagSelected}
- onKeyDown={this.handleKeyDown}
onTagContext={this.handleTagContext}
onSearch={this.handleSearch}
onSettings={this.handleSettings.bind(this)}
diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js
index 9897af0b4f..6f84533bd6 100644
--- a/chrome/content/zotero/zoteroPane.js
+++ b/chrome/content/zotero/zoteroPane.js
@@ -420,11 +420,11 @@ var ZoteroPane = new function()
return document.getElementById('zotero-tb-add');
}
// If tag selector is collapsed, go to "New item" button, otherwise focus tag selector
- let firstNonDisabledTag = tagSelector.querySelector('.tag-selector-item:not(.disabled)');
- if (firstNonDisabledTag) {
- return firstNonDisabledTag;
+ if (ZoteroPane.tagSelector.isTagListEmpty()) {
+ return tagSelector.querySelector(".search-input");
}
- return tagSelector.querySelector(".search-input");
+ ZoteroPane.tagSelector.focusTagList();
+ return null;
},
Escape: clearCollectionSearch
}
@@ -446,11 +446,11 @@ var ZoteroPane = new function()
'search-input': {
Tab: () => tagSelector.querySelector('.tag-selector-actions'),
ShiftTab: () => {
- let firstNonDisabledTag = tagSelector.querySelector('.tag-selector-item:not(.disabled)');
- if (firstNonDisabledTag) {
- return firstNonDisabledTag;
+ if (ZoteroPane.tagSelector.isTagListEmpty()) {
+ return document.getElementById("collection-tree");
}
- return document.getElementById("collection-tree");
+ ZoteroPane.tagSelector.focusTagList();
+ return null;
},
},
'tag-selector-item': {