properly refocus tag from tag selector on re-enter (#4035)
- do not erase the last recorded tag on blur of the tag selector - on tab from collectionTree or shift-tab from tag selector input, try to refocus that last focused tag. If one does not exist, try to focus the first non-disabled tag. Fixes: #4008
This commit is contained in:
parent
d5194f234d
commit
638efad3eb
3 changed files with 55 additions and 19 deletions
|
@ -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 (
|
||||
<div className="tag-selector-list-container" onBlur={this.handleBlur} onKeyDown={this.handleKeyDown.bind(this)}>
|
||||
<div className="tag-selector-list-container" onKeyDown={this.handleKeyDown.bind(this)}>
|
||||
{tagList}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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': {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue