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.__defineGetter__('loaded', function () { return _loaded; });
var _lastSelectedItems = [];
var lastFocusedElement = null;
//Privileged methods
this.destroy = destroy;
@ -434,7 +435,29 @@ 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
* mode
@ -589,6 +612,7 @@ var ZoteroPane = new function()
catch (e) {
Zotero.logError(e);
}
addFocusHandlers();
}
@ -1042,6 +1066,15 @@ var ZoteroPane = new function()
}
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) {
this.highlightTimer.cancel();
this.highlightTimer = null;

View file

@ -233,10 +233,10 @@
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.
*/
@mixin focus-ring($width: 1px, $radius: 5px) {
@mixin focus-ring {
&:focus-visible {
outline: none;
box-shadow: 0 0 0 $width -moz-accent-color;
border-radius: $radius;
box-shadow: 0 0 0 var(--width-focus-border) -moz-accent-color;
border-radius: var(--radius-focus-border);
}
}

View file

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

View file

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

View file

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