vpat 66-68: fix focus within tagsBox popup (#4231)
- focus will always enter the tagsBox popup opened from the reader - escape from within the popup with tagsBox will close the popup (vpat 67) - one should not be able to collapse the tagsBox. Added `collapsible` getter and setters to the collapsible panel to prevent the section from changing its open status. This may be also used in other cases, such as to prevent itemBox from being collapsed in duplicates mode. - patched a glitch where tab from the last empty tab input would loose focus. - changed the `menupopup` for `panel` to display the `tagsbox` because `menupopup` has implicit `role="menu"` which is not meant to contain inputs, so voiceover completely looses cursor when inputs are focused inside of the popup. Also, tweaked spacing a bit to avoid the focus-ring getting cutoff. Addresses: #4222 Fixes: #4230 Fixes: #4226 Addresses: #4388
This commit is contained in:
parent
f227aeb6e0
commit
a73035c848
5 changed files with 60 additions and 19 deletions
|
@ -43,7 +43,7 @@
|
||||||
set open(newOpen) {
|
set open(newOpen) {
|
||||||
newOpen = !!newOpen;
|
newOpen = !!newOpen;
|
||||||
let oldOpen = this.open;
|
let oldOpen = this.open;
|
||||||
if (oldOpen === newOpen || this.empty) return;
|
if (oldOpen === newOpen || this.empty || !this.collapsible) return;
|
||||||
this.render();
|
this.render();
|
||||||
|
|
||||||
// Force open before getting scrollHeight, so we get the right value
|
// Force open before getting scrollHeight, so we get the right value
|
||||||
|
@ -106,6 +106,19 @@
|
||||||
this.setAttribute('summary', val);
|
this.setAttribute('summary', val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get collapsible() {
|
||||||
|
return !this.getAttribute("no-collapse");
|
||||||
|
}
|
||||||
|
|
||||||
|
set collapsible(val) {
|
||||||
|
if (val) {
|
||||||
|
this.removeAttribute('no-collapse');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setAttribute('no-collapse', val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static get observedAttributes() {
|
static get observedAttributes() {
|
||||||
return ['open', 'empty', 'label', 'summary', 'extra-buttons'];
|
return ['open', 'empty', 'label', 'summary', 'extra-buttons'];
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,14 @@ class ItemPaneSectionElementBase extends XULElementBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get collapsible() {
|
||||||
|
return this._section.collapsible;
|
||||||
|
}
|
||||||
|
|
||||||
|
set collapsible(val) {
|
||||||
|
this._section.collapsible = !!val;
|
||||||
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
if (!this.render && !this.asyncRender) {
|
if (!this.render && !this.asyncRender) {
|
||||||
|
|
|
@ -342,6 +342,18 @@
|
||||||
focusField.focus();
|
focusField.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (event.key == "Tab" && !event.shiftKey) {
|
||||||
|
// On tab from the last empty tag row, the minus icon will be focused
|
||||||
|
// and the row will be immediately removed in this.saveTag, so focus will be lost.
|
||||||
|
// To avoid that, on tab from the last tag input that is empty, focus the next
|
||||||
|
// element after the tag row.
|
||||||
|
let allTags = [...this.querySelectorAll(".row")];
|
||||||
|
let isLastTag = target.closest(".row") == allTags[allTags.length - 1];
|
||||||
|
if (isLastTag && !target.closest("editable-text").value.length) {
|
||||||
|
Services.focus.moveFocus(window, target.closest(".row").lastChild, Services.focus.MOVEFOCUS_FORWARD, 0);
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Intercept paste, check for newlines, and convert textbox
|
// Intercept paste, check for newlines, and convert textbox
|
||||||
|
|
|
@ -896,30 +896,43 @@ class ReaderInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
_openTagsPopup(item, x, y) {
|
_openTagsPopup(item, x, y) {
|
||||||
let menupopup = this._window.document.createXULElement('menupopup');
|
let tagsPopup = this._window.document.createXULElement('panel');
|
||||||
menupopup.addEventListener('popuphidden', function (event) {
|
tagsPopup.addEventListener('popuphidden', function (event) {
|
||||||
if (event.target === menupopup) {
|
if (event.target === tagsPopup) {
|
||||||
menupopup.remove();
|
tagsPopup.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
menupopup.className = 'tags-popup';
|
tagsPopup.addEventListener('keydown', function (event) {
|
||||||
menupopup.setAttribute('ignorekeys', true);
|
if (event.key == "Escape") {
|
||||||
|
tagsPopup.hidePopup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tagsPopup.className = 'tags-popup';
|
||||||
let tagsbox = this._window.document.createXULElement('tags-box');
|
let tagsbox = this._window.document.createXULElement('tags-box');
|
||||||
menupopup.appendChild(tagsbox);
|
tagsPopup.appendChild(tagsbox);
|
||||||
tagsbox.setAttribute('flex', '1');
|
tagsbox.setAttribute('flex', '1');
|
||||||
this._popupset.appendChild(menupopup);
|
this._popupset.appendChild(tagsPopup);
|
||||||
let rect = this._iframe.getBoundingClientRect();
|
let rect = this._iframe.getBoundingClientRect();
|
||||||
x += rect.left;
|
x += rect.left;
|
||||||
y += rect.top;
|
y += rect.top;
|
||||||
tagsbox.editable = true;
|
tagsbox.editable = true;
|
||||||
tagsbox.item = item;
|
tagsbox.item = item;
|
||||||
tagsbox.render();
|
tagsbox.render();
|
||||||
menupopup.openPopup(null, 'before_start', x, y, true);
|
// remove unnecessary tabstop from the section header
|
||||||
setTimeout(() => {
|
tagsbox.querySelector(".head").removeAttribute("tabindex");
|
||||||
|
tagsPopup.addEventListener("popupshown", (_) => {
|
||||||
|
// Ensure tagsbox is open
|
||||||
|
tagsbox.open = true;
|
||||||
if (tagsbox.count == 0) {
|
if (tagsbox.count == 0) {
|
||||||
tagsbox.newTag();
|
tagsbox.newTag();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// Focus + button
|
||||||
|
Services.focus.setFocus(tagsbox.querySelector("toolbarbutton"), Services.focus.FLAG_NOSHOWRING);
|
||||||
|
}
|
||||||
|
tagsbox.collapsible = false;
|
||||||
});
|
});
|
||||||
|
tagsPopup.openPopup(null, 'before_start', x, y, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _openContextMenu({ x, y, itemGroups }) {
|
async _openContextMenu({ x, y, itemGroups }) {
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
menupopup.tags-popup {
|
.tags-popup {
|
||||||
font: inherit;
|
font: inherit;
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
padding-inline: 8px;
|
& > tags-box {
|
||||||
|
padding-inline: 8px;
|
||||||
@media (-moz-platform: windows) {
|
|
||||||
& > tags-box {
|
|
||||||
// padding-inline does not work on Windows
|
|
||||||
margin-inline: 8px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue