additional focus management for popups

When a menupopup is opened, the active element does not change, so
their focus-ring will be hidden until the menupopup closes for it to be
less distracting.

When a panel popup is opened, the focus will be moved inside
of the panel, so we keep track of whichever element was previously
focused, and re-focus it when the panel goes away.

Minor reorganization of focus-ring mixin to use variables instead
of parameters to make hiding of focus-ring easier.
This commit is contained in:
Bogdan Abaev 2024-01-10 15:42:53 -05:00 committed by Dan Stillman
parent 1427e3e324
commit b6d5a52417
5 changed files with 46 additions and 6 deletions

View file

@ -37,6 +37,7 @@ var ZoteroPane = new function()
this._listeners = {}; this._listeners = {};
this.__defineGetter__('loaded', function () { return _loaded; }); this.__defineGetter__('loaded', function () { return _loaded; });
var _lastSelectedItems = []; var _lastSelectedItems = [];
var lastFocusedElement = null;
//Privileged methods //Privileged methods
this.destroy = destroy; this.destroy = destroy;
@ -434,6 +435,28 @@ var ZoteroPane = new function()
}); });
} }
function addFocusHandlers() {
// When a menupopup shows, hide the focus ring around the currently focused element
document.addEventListener("popupshowing", (e) => {
if (e.target.tagName == "menupopup") {
document.activeElement.style.setProperty('--width-focus-border', '0');
document.activeElement.classList.add("hidden-focus");
}
});
// When a panel popup hides, refocus the previous element
// When a menupopup hides, stop hiding the focus-ring
document.addEventListener("popuphiding", e => {
if (e.target.tagName == "panel") {
ZoteroPane.lastFocusedElement.focus();
}
let noFocus = [...document.querySelectorAll(".hidden-focus")];
for (let node of noFocus) {
node.style.removeProperty('--width-focus-border');
node.classList.remove("hidden-focus");
}
});
}
/** /**
* Called on window load or when pane has been reloaded after switching into or out of connector * Called on window load or when pane has been reloaded after switching into or out of connector
@ -589,6 +612,7 @@ var ZoteroPane = new function()
catch (e) { catch (e) {
Zotero.logError(e); Zotero.logError(e);
} }
addFocusHandlers();
} }
@ -1042,6 +1066,15 @@ var ZoteroPane = new function()
} }
this.handleBlur = (event) => { this.handleBlur = (event) => {
// When focus shifts, unless we are inside of a panel, save
// the last focused element to be able to return focus to it when the panel closes
if (!event.target.closest("panel")) {
this.lastFocusedElement = event.target;
// Special treatment to focus on quick-search dropmarker inside of the shadow DOM
if (this.lastFocusedElement.id == "zotero-tb-search-dropmarker") {
this.lastFocusedElement = document.getElementById("zotero-tb-search")._searchModePopup.parentElement;
}
}
if (this.highlightTimer) { if (this.highlightTimer) {
this.highlightTimer.cancel(); this.highlightTimer.cancel();
this.highlightTimer = null; this.highlightTimer = null;

View file

@ -233,10 +233,10 @@
components (e.g. toolbarbutton) and sometimes are too wide (e.g. around textfield on macOS). components (e.g. toolbarbutton) and sometimes are too wide (e.g. around textfield on macOS).
Box-shadow is used to be able to set the radius. Box-shadow is used to be able to set the radius.
*/ */
@mixin focus-ring($width: 1px, $radius: 5px) { @mixin focus-ring {
&:focus-visible { &:focus-visible {
outline: none; outline: none;
box-shadow: 0 0 0 $width -moz-accent-color; box-shadow: 0 0 0 var(--width-focus-border) -moz-accent-color;
border-radius: $radius; border-radius: var(--radius-focus-border);
} }
} }

View file

@ -2,6 +2,8 @@
background: var(--material-sidepane); background: var(--material-sidepane);
padding: 7px; padding: 7px;
border-radius: 5px; border-radius: 5px;
--width-focus-border: 3px;
--radius-focus-border: 5px;
} }
#zotero-tabs-menu-filter { #zotero-tabs-menu-filter {
@ -10,7 +12,7 @@
border: 1px solid transparent; border: 1px solid transparent;
padding-inline-start: 5px !important; padding-inline-start: 5px !important;
padding: 2px; padding: 2px;
@include focus-ring(3px, 5px); @include focus-ring;
} }
#zotero-tabs-menu-list { #zotero-tabs-menu-list {
@ -18,7 +20,7 @@
margin: 0; margin: 0;
.zotero-tabs-menu-entry { .zotero-tabs-menu-entry {
@include focus-ring(3px, 5px); @include focus-ring;
border-radius: 6px; border-radius: 6px;
height: 22px; height: 22px;
margin: 0; margin: 0;

View file

@ -65,6 +65,9 @@ editable-text {
} }
.input { .input {
--width-focus-border: 1px;
--radius-focus-border: 5px;
@include focus-ring; @include focus-ring;
// Necessary for consistent padding, even if it's actually an <input> // Necessary for consistent padding, even if it's actually an <input>
-moz-default-appearance: textarea; -moz-default-appearance: textarea;

View file

@ -7,6 +7,8 @@ item-box {
&[hidden] { &[hidden] {
display: none; display: none;
} }
--width-focus-border: 1px;
--radius-focus-border: 5px;
#item-box { #item-box {
width: 100%; width: 100%;