mirror of
https://github.com/zen-browser/desktop.git
synced 2026-09-24 05:13:53 +00:00
gh-15460: Fixed folder expansion expanding real folders on library (gh-15498)
This commit is contained in:
@@ -14,13 +14,11 @@
|
||||
.zen-library-media-preview-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
-moz-window-dragging: drag;
|
||||
background: light-dark(rgba(255, 255, 255, 0.55), rgba(0, 0, 0, 0.45));
|
||||
}
|
||||
|
||||
.zen-library-media-preview-panel {
|
||||
position: relative;
|
||||
-moz-window-dragging: no-drag;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: min(78vw, 1100px);
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
background-image: var(--download-image);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: var(--border-radius-small);
|
||||
border-radius: var(--border-radius-xsmall);
|
||||
background-size: cover;
|
||||
scale: 2;
|
||||
|
||||
|
||||
@@ -100,7 +100,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
|
||||
const folderActionsMenu = document.getElementById("zenFolderActions");
|
||||
folderActionsMenu.addEventListener("popupshowing", event => {
|
||||
const target = event.explicitOriginalTarget;
|
||||
const target =
|
||||
event.target === folderActionsMenu
|
||||
? (folderActionsMenu.triggerNode ?? event.explicitOriginalTarget)
|
||||
: event.explicitOriginalTarget;
|
||||
let folder;
|
||||
if (gBrowser.isTabGroupLabel(target)) {
|
||||
folder = target.group;
|
||||
@@ -498,16 +501,6 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
await this.animateCollapse(group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Settles a collapsed folder's own height. The library toggles folders
|
||||
* through copies, so the real one needs telling once the dust settles.
|
||||
*
|
||||
* @param {Element} group - A folder
|
||||
*/
|
||||
relayoutCollapsedFolder(group) {
|
||||
this.#queueCollapsedRelayout(group);
|
||||
}
|
||||
|
||||
#queueCollapsedRelayout(group) {
|
||||
if (this.#collapsedRelayoutQueue.size === 0) {
|
||||
requestAnimationFrame(() => this.#flushCollapsedRelayout());
|
||||
|
||||
@@ -33,6 +33,7 @@ ChromeUtils.defineESModuleGetters(
|
||||
);
|
||||
|
||||
const LAST_TAB_PREF = "zen.library.last-tab";
|
||||
const CLEANUP_DELAY_MS = 30000;
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "appContentWrapper", function () {
|
||||
return document.getElementById("zen-appcontent-wrapper");
|
||||
@@ -183,18 +184,8 @@ export class ZenLibrary extends MozLitElement {
|
||||
}
|
||||
|
||||
if (isOpen && !wasOpen) {
|
||||
this.setAttribute("open", "true");
|
||||
document
|
||||
.getElementById("zen-sidebar-splitter")
|
||||
.setAttribute("zen-library-open", "true");
|
||||
this.#init();
|
||||
} else if (!isOpen && wasOpen) {
|
||||
this.removeAttribute("open");
|
||||
this.#mounted = new Set([this.activeTab]);
|
||||
this.requestUpdate();
|
||||
document
|
||||
.getElementById("zen-sidebar-splitter")
|
||||
.removeAttribute("zen-library-open");
|
||||
this.#cleanup();
|
||||
}
|
||||
|
||||
@@ -293,20 +284,29 @@ export class ZenLibrary extends MozLitElement {
|
||||
return this.#stylesLoaded;
|
||||
}
|
||||
|
||||
#cleanupTimer = null;
|
||||
#idleCleanup = null;
|
||||
|
||||
#scheduleIdleCleanup() {
|
||||
this.#idleCleanup = window.requestIdleCallback(() => {
|
||||
this.#idleCleanup = null;
|
||||
this.#stylesLoaded = null;
|
||||
this.#cancelIdleCleanup();
|
||||
this.#cleanupTimer = window.setTimeout(() => {
|
||||
this.#cleanupTimer = null;
|
||||
this.#idleCleanup = window.requestIdleCallback(() => {
|
||||
this.#idleCleanup = null;
|
||||
this.#stylesLoaded = null;
|
||||
|
||||
this.#contentMounted = false;
|
||||
this.#mounted = new Set([this.activeTab]);
|
||||
this.requestUpdate();
|
||||
});
|
||||
this.#contentMounted = false;
|
||||
this.#mounted = new Set([this.activeTab]);
|
||||
this.requestUpdate();
|
||||
});
|
||||
}, CLEANUP_DELAY_MS);
|
||||
}
|
||||
|
||||
#cancelIdleCleanup() {
|
||||
if (this.#cleanupTimer) {
|
||||
window.clearTimeout(this.#cleanupTimer);
|
||||
this.#cleanupTimer = null;
|
||||
}
|
||||
if (this.#idleCleanup) {
|
||||
window.cancelIdleCallback(this.#idleCleanup);
|
||||
this.#idleCleanup = null;
|
||||
@@ -376,11 +376,6 @@ export class ZenLibrary extends MozLitElement {
|
||||
lib.openProgress = target;
|
||||
lib.#springControls = null;
|
||||
lib.removeAttribute("transitioning");
|
||||
if (target === 0) {
|
||||
lib.#mounted = new Set([lib.activeTab]);
|
||||
lib.requestUpdate();
|
||||
lib.#scheduleIdleCleanup();
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -401,12 +396,12 @@ export class ZenLibrary extends MozLitElement {
|
||||
lib.#springControls = null;
|
||||
}
|
||||
|
||||
lib.style.setProperty("pointer-events", "none");
|
||||
lib.style.pointerEvents = "none";
|
||||
}
|
||||
|
||||
static stopSwipe(direction) {
|
||||
const lib = this.getInstance();
|
||||
lib.style.setProperty("pointer-events", "unset");
|
||||
lib.style.pointerEvents = "";
|
||||
lib.#canSwipe = false;
|
||||
|
||||
if (lib.#libraryOnRight) {
|
||||
@@ -418,7 +413,6 @@ export class ZenLibrary extends MozLitElement {
|
||||
this.animateProgress(target);
|
||||
}
|
||||
|
||||
// Return library open state
|
||||
return lib.#isOpen;
|
||||
}
|
||||
|
||||
@@ -499,6 +493,15 @@ export class ZenLibrary extends MozLitElement {
|
||||
}
|
||||
|
||||
#init() {
|
||||
this.#cancelIdleCleanup();
|
||||
if (!this.#contentMounted) {
|
||||
this.#contentMounted = true;
|
||||
this.requestUpdate();
|
||||
}
|
||||
this.setAttribute("open", "true");
|
||||
document
|
||||
.getElementById("zen-sidebar-splitter")
|
||||
.setAttribute("zen-library-open", "true");
|
||||
document.addEventListener("keydown", this, true);
|
||||
window.addEventListener("TabOpen", this);
|
||||
|
||||
@@ -511,11 +514,18 @@ export class ZenLibrary extends MozLitElement {
|
||||
}
|
||||
|
||||
#cleanup() {
|
||||
this.#cancelIdleCleanup();
|
||||
this.removeAttribute("open");
|
||||
this.#mounted = new Set([this.activeTab]);
|
||||
this.requestUpdate();
|
||||
document
|
||||
.getElementById("zen-sidebar-splitter")
|
||||
.removeAttribute("zen-library-open");
|
||||
|
||||
if (this.#springControls) {
|
||||
this.#springControls.stop();
|
||||
this.#springControls = null;
|
||||
}
|
||||
this.removeAttribute("transitioning");
|
||||
|
||||
this.#detachWrapperOfSwipe();
|
||||
if (this.#gestureControl) {
|
||||
@@ -531,6 +541,7 @@ export class ZenLibrary extends MozLitElement {
|
||||
document.removeEventListener("keydown", this, true);
|
||||
window.removeEventListener("TabOpen", this);
|
||||
this.isHidden = true;
|
||||
this.#scheduleIdleCleanup();
|
||||
}
|
||||
|
||||
get #isCompactMode() {
|
||||
|
||||
@@ -43,6 +43,8 @@ export class ZenLibraryMediaPreview {
|
||||
#root = null;
|
||||
#parts = {};
|
||||
#menu = null;
|
||||
/** @type {object|null} The media the open menu acts on */
|
||||
#menuItem = null;
|
||||
#items = [];
|
||||
#index = -1;
|
||||
#origin = null;
|
||||
@@ -135,6 +137,9 @@ export class ZenLibraryMediaPreview {
|
||||
openButton.addEventListener("click", () => this.#launch());
|
||||
|
||||
const stage = make("div", "zen-library-media-preview-stage", panel);
|
||||
stage.addEventListener("contextmenu", event =>
|
||||
this.openContextMenu(event, this.#item)
|
||||
);
|
||||
|
||||
this.#parts = {
|
||||
panel,
|
||||
@@ -472,8 +477,7 @@ export class ZenLibraryMediaPreview {
|
||||
event.dataTransfer.setData("text/uri-list", item.url);
|
||||
}
|
||||
|
||||
#launch() {
|
||||
const item = this.#item;
|
||||
#launch(item = this.#item) {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
@@ -484,15 +488,13 @@ export class ZenLibraryMediaPreview {
|
||||
}
|
||||
}
|
||||
|
||||
#reveal() {
|
||||
const item = this.#item;
|
||||
#reveal(item = this.#item) {
|
||||
if (item) {
|
||||
new lazy.FileUtils.File(item.path).reveal();
|
||||
}
|
||||
}
|
||||
|
||||
#copy() {
|
||||
const item = this.#item;
|
||||
#copy(item = this.#item) {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
@@ -513,6 +515,40 @@ export class ZenLibraryMediaPreview {
|
||||
}
|
||||
|
||||
#openMenu(event) {
|
||||
this.#menuItem = this.#item;
|
||||
this.#ensureMenu().openPopup(
|
||||
event.currentTarget,
|
||||
"after_end",
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the same menu as the more button, at the pointer, for any media
|
||||
* in the grid or the one on show.
|
||||
*
|
||||
* @param {MouseEvent} event - The right click
|
||||
* @param {object} item - The media it was on
|
||||
*/
|
||||
openContextMenu(event, item) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
this.#menuItem = item;
|
||||
this.#ensureMenu().openPopupAtScreen(
|
||||
event.screenX,
|
||||
event.screenY,
|
||||
true,
|
||||
event
|
||||
);
|
||||
}
|
||||
|
||||
#ensureMenu() {
|
||||
if (!this.#menu) {
|
||||
this.#menu = this.#window.MozXULElement.parseXULToFragment(`
|
||||
<menupopup class="zen-library-media-menu">
|
||||
@@ -523,20 +559,21 @@ export class ZenLibraryMediaPreview {
|
||||
</menupopup>
|
||||
`).firstElementChild;
|
||||
this.#menu.addEventListener("command", menuEvent => {
|
||||
const item = this.#menuItem;
|
||||
switch (menuEvent.target.dataset.action) {
|
||||
case "open":
|
||||
this.#launch();
|
||||
this.#launch(item);
|
||||
break;
|
||||
case "show":
|
||||
this.#reveal();
|
||||
this.#reveal(item);
|
||||
break;
|
||||
case "copy":
|
||||
this.#copy();
|
||||
this.#copy(item);
|
||||
break;
|
||||
}
|
||||
});
|
||||
this.#document.getElementById("mainPopupSet").appendChild(this.#menu);
|
||||
}
|
||||
this.#menu.openPopup(event.currentTarget, "after_end", 0, 0, false, false);
|
||||
return this.#menu;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ const FILE_MIME = "application/x-moz-file";
|
||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const FOLDERS_PREF = "zen.library.media.folders";
|
||||
const ENABLED_PREF = "zen.library.media.enabled";
|
||||
const SKELETON_CARDS = 10;
|
||||
|
||||
const FOLDERS = {
|
||||
downloads: { key: "DfltDwnld", home: "Downloads" },
|
||||
@@ -287,6 +286,11 @@ export class ZenLibraryMediaSection extends ZenLibrarySearchSection {
|
||||
dataTransfer.addElement(event.currentTarget);
|
||||
}
|
||||
|
||||
#onItemContextMenu(event, item) {
|
||||
this.#preview ??= new ZenLibraryMediaPreview(window);
|
||||
this.#preview.openContextMenu(event, item);
|
||||
}
|
||||
|
||||
#onItemClick(event, item) {
|
||||
this.#preview ??= new ZenLibraryMediaPreview(window);
|
||||
const shown = this.#shown;
|
||||
@@ -313,6 +317,9 @@ export class ZenLibraryMediaSection extends ZenLibrarySearchSection {
|
||||
card.title = item.name;
|
||||
card.addEventListener("dragstart", event => this.#onDragStart(event, item));
|
||||
card.addEventListener("click", event => this.#onItemClick(event, item));
|
||||
card.addEventListener("contextmenu", event =>
|
||||
this.#onItemContextMenu(event, item)
|
||||
);
|
||||
|
||||
const frame = document.createElementNS(HTML_NS, "div");
|
||||
frame.className = "zen-library-media-frame";
|
||||
@@ -443,12 +450,7 @@ export class ZenLibraryMediaSection extends ZenLibrarySearchSection {
|
||||
*/
|
||||
#renderOptIn() {
|
||||
return html`
|
||||
<div class="zen-library-media-grid zen-library-media-skeleton" behind>
|
||||
${Array.from(
|
||||
{ length: SKELETON_CARDS },
|
||||
() => html`<div class="zen-library-media-item"></div>`
|
||||
)}
|
||||
</div>
|
||||
<div class="zen-library-media-skeleton" behind></div>
|
||||
<div class="zen-library-media-opt-in">
|
||||
<div class="zen-library-media-opt-in-icon">
|
||||
<div class="empty-state-icon-image"></div>
|
||||
|
||||
@@ -108,6 +108,13 @@ export class ZenLibrarySpacesSection extends MozLitElement {
|
||||
#pendingGroups = [];
|
||||
/** @type {WeakMap<Element, Element>} copied tab or group to the real one */
|
||||
#realElements = new WeakMap();
|
||||
/**
|
||||
* Folders are opened and closed here without touching the real ones, so
|
||||
* rebuilt copies keep what was chosen here.
|
||||
*
|
||||
* @type {WeakMap<Element, boolean>} real group to its copy's collapsed state
|
||||
*/
|
||||
#collapsedCopies = new WeakMap();
|
||||
#dnd = new ZenLibraryDragAndDrop(this);
|
||||
#dragIndex = -1;
|
||||
#dropIndex = -1;
|
||||
@@ -455,6 +462,9 @@ export class ZenLibrarySpacesSection extends MozLitElement {
|
||||
if (GROUP_TAGS.includes(node.localName)) {
|
||||
const copy = node.cloneNode(false);
|
||||
this.#renameIds(copy);
|
||||
if (this.#collapsedCopies.has(node)) {
|
||||
copy.toggleAttribute("collapsed", this.#collapsedCopies.get(node));
|
||||
}
|
||||
container.appendChild(copy);
|
||||
this.#realElements.set(copy, node);
|
||||
const icon = copy.querySelector(".tab-group-folder-icon");
|
||||
@@ -502,15 +512,12 @@ export class ZenLibrarySpacesSection extends MozLitElement {
|
||||
const group = this.#realElements.get(copy);
|
||||
if (group) {
|
||||
event.stopPropagation();
|
||||
const collapsed = !group.collapsed;
|
||||
group.collapsed = collapsed;
|
||||
copy.collapsed = collapsed;
|
||||
if (collapsed) {
|
||||
this.#collapsedCopies.set(group, copy.collapsed);
|
||||
if (copy.collapsed) {
|
||||
gZenFolders.animateCollapse(copy);
|
||||
} else {
|
||||
gZenFolders.animateExpand(copy);
|
||||
}
|
||||
gZenFolders.relayoutCollapsedFolder(group);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -527,6 +534,35 @@ export class ZenLibrarySpacesSection extends MozLitElement {
|
||||
this.library?.constructor.toggle();
|
||||
};
|
||||
|
||||
/**
|
||||
* A copy carries the real element's context menu, which would then act on
|
||||
* the copy. The menu opens for the real tab or folder instead.
|
||||
*
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
#onStripContextMenu = event => {
|
||||
let real = null;
|
||||
const label = event.target.closest(".tab-group-label-container");
|
||||
if (label) {
|
||||
const group = this.#realElements.get(label.closest(GROUP_TAGS.join()));
|
||||
real = group?.labelElement?.parentElement;
|
||||
} else {
|
||||
real = this.#realElements.get(event.target.closest("tab"));
|
||||
}
|
||||
const menu = real && document.getElementById(real.getAttribute("context"));
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
const trigger = new MouseEvent("ZenLibraryContextMenu", {
|
||||
screenX: event.screenX,
|
||||
screenY: event.screenY,
|
||||
});
|
||||
real.dispatchEvent(trigger);
|
||||
menu.openPopupAtScreen(event.screenX, event.screenY, true, trigger);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Element} copy - A copied tab, group or folder
|
||||
* @returns {Element|undefined} The real element it stands for
|
||||
@@ -591,6 +627,7 @@ export class ZenLibrarySpacesSection extends MozLitElement {
|
||||
orient="vertical"
|
||||
expanded="true"
|
||||
@click=${this.#onStripClick}
|
||||
@contextmenu=${this.#onStripContextMenu}
|
||||
@mouseover=${this.#containHover}
|
||||
@mouseout=${this.#containHover}
|
||||
@dragstart=${this.#onStripDragStart}
|
||||
|
||||
@@ -338,33 +338,22 @@ zen-library[zen-library-downloading] .zen-library-tab[data-section="downloads"]:
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#zen-library-panel
|
||||
:is(.zen-library-section, .zen-library-search-results)
|
||||
> * {
|
||||
animation: zen-library-section-fade 0.22s ease;
|
||||
}
|
||||
|
||||
@keyframes zen-library-section-fade {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.zen-library-media-skeleton {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
|
||||
& .zen-library-media-item {
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--zen-library-pill-bg);
|
||||
mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 122 122'><rect x='6' y='6' width='110' height='110' rx='16'/></svg>");
|
||||
mask-size: 150px auto;
|
||||
mask-repeat: round repeat;
|
||||
animation: zen-library-media-wait 1.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
& .zen-library-media-item:nth-child(even) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
&[behind] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
@@ -821,6 +810,7 @@ zen-library:not([open]) :is(.zen-library-search-header, .zen-library-filter-head
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
margin-inline: 2px 1px;
|
||||
color-scheme: light;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
|
||||
@@ -474,14 +474,17 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
|
||||
// Remove everything except the entry we want to keep
|
||||
let url;
|
||||
try {
|
||||
url = Services.io.newURI(initialState.entry.url);
|
||||
} catch {}
|
||||
state.entries = [
|
||||
{
|
||||
...initialState.entry,
|
||||
triggeringPrincipal_base64: E10SUtils.serializePrincipal(
|
||||
Services.scriptSecurityManager.createContentPrincipal(
|
||||
Services.io.newURI(initialState.entry.url),
|
||||
{}
|
||||
)
|
||||
url
|
||||
? Services.scriptSecurityManager.createContentPrincipal(url, {})
|
||||
: Services.scriptSecurityManager.createNullPrincipal()
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user