Prevent focus from being lost in itemBox (#4287)
- always render options and link buttons - just do not display them if their respective field is empty. That allows us to easily handle focus after refresh because otherwise, the node may does not exist. If we try to restore focus to such hidden component, simulate a "tab" from it to focus the next possible node. This fixes the issue of focus being lost on tab after all content of editable-text is cleared. - add tabindex=0 to itemType menulist, otherwise it was not perceived as a candidate to return focus to. - somewhat special treatment for restoring focus to creator rows. If the desired node is not found, we'll try to focus the respective node in the last creator row. It prevents focus from being lost on tab after clearing the very last creator. Fixes: #4241
This commit is contained in:
parent
7dc68209a5
commit
a054831a3a
1 changed files with 25 additions and 3 deletions
|
@ -621,11 +621,15 @@
|
|||
.replace(/\?/g, '%3f')
|
||||
.replace(/%/g, '%25')
|
||||
.replace(/"/g, '%22');
|
||||
openLinkButton = this.createOpenLinkIcon(doi);
|
||||
openLinkButton = this.createOpenLinkIcon(doi, fieldName);
|
||||
link = doi;
|
||||
addLinkContextMenu = true;
|
||||
}
|
||||
}
|
||||
// Hidden open-link button just for focus management
|
||||
else if (['url', 'homepage', 'DOI'].includes(fieldName)) {
|
||||
openLinkButton = this.createOpenLinkIcon(null, fieldName);
|
||||
}
|
||||
let rowData = document.createElement('div');
|
||||
rowData.className = "meta-data";
|
||||
rowData.appendChild(valueElement);
|
||||
|
@ -645,7 +649,7 @@
|
|||
}
|
||||
|
||||
// Add options button for title fields
|
||||
if (this.editable && fieldID && val && (fieldName == 'seriesTitle' || fieldName == 'shortTitle'
|
||||
if (this.editable && fieldID && (fieldName == 'seriesTitle' || fieldName == 'shortTitle'
|
||||
|| Zotero.ItemFields.isFieldOfBase(fieldID, 'title')
|
||||
|| Zotero.ItemFields.isFieldOfBase(fieldID, 'publicationTitle'))) {
|
||||
let optionsButton = document.createXULElement("toolbarbutton");
|
||||
|
@ -676,6 +680,8 @@
|
|||
optionsButton.addEventListener("click", triggerPopup);
|
||||
rowData.appendChild(optionsButton);
|
||||
rowData.oncontextmenu = triggerPopup;
|
||||
// Options button is always created for focus management but if the field is empty, it is hidden
|
||||
if (!val) optionsButton.hidden = true;
|
||||
}
|
||||
|
||||
this.addDynamicRow(rowLabel, rowData);
|
||||
|
@ -888,6 +894,8 @@
|
|||
var menulist = document.createXULElement("menulist", { is: "menulist-item-types" });
|
||||
menulist.id = "item-type-menu";
|
||||
menulist.className = "zotero-clicky keyboard-clickable";
|
||||
// This is to make it easier to identify the itemType menu in _saveFieldFocus
|
||||
menulist.setAttribute("tabindex", 0);
|
||||
menulist.addEventListener('command', (event) => {
|
||||
this.changeTypeTo(event.target.value, menulist);
|
||||
});
|
||||
|
@ -1413,6 +1421,7 @@
|
|||
openLink.className = "zotero-clicky zotero-clicky-open-link show-on-hover no-display";
|
||||
openLink.addEventListener("click", event => ZoteroPane.loadURI(value, event));
|
||||
openLink.setAttribute('data-l10n-id', "item-button-view-online");
|
||||
if (!value) openLink.hidden = true;
|
||||
return openLink;
|
||||
}
|
||||
|
||||
|
@ -2249,11 +2258,24 @@
|
|||
return;
|
||||
}
|
||||
|
||||
let refocusField = this.querySelector(`#${CSS.escape(this._selectField)}`);
|
||||
let refocusField = this.querySelector(`#${CSS.escape(this._selectField)}:not([disabled="true"])`);
|
||||
// For creator rows, if a focusable node with desired id does not exist, try to focus
|
||||
// the same component from the last available creator row
|
||||
if (!refocusField && this._selectField.includes("creator")) {
|
||||
let maybeLastCreatorID = this._selectField.replace(/\d+/g, Math.max(this._creatorCount - 1, 0));
|
||||
refocusField = this.querySelector(`#${CSS.escape(maybeLastCreatorID)}`);
|
||||
}
|
||||
if (!refocusField) {
|
||||
this._clearSavedFieldFocus();
|
||||
return;
|
||||
}
|
||||
refocusField.focus();
|
||||
// If the node did not receive focus (e.g. disabled or hidden), focus the next focusable node
|
||||
if (!(refocusField.contains(document.activeElement) || document.activeElement == refocusField)) {
|
||||
// Note: typically, a node contains itself, so node.contains(node) is true.
|
||||
// Somehow, it is not true for itemType menulist, which explains the seemingly redundant || condition.
|
||||
Services.focus.moveFocus(window, refocusField, Services.focus.MOVEFOCUS_FORWARD, 0);
|
||||
}
|
||||
|
||||
if (this._selectFieldSelection) {
|
||||
let input = refocusField.querySelector("input, textarea");
|
||||
|
|
Loading…
Add table
Reference in a new issue