fix: animateUnload for folder, p=#10440

* fix: `animateUnload` for folder

* fix: Formatting

* fix: Some fixes

* fix: Formatting

* fix: Fix unloading split views, b=no-bug, c=folders, split-view

* test: Fixed tests, b=no-bug, c=folders, tabs

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>

---------

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
This commit is contained in:
octaviusz
2025-09-20 01:19:33 +03:00
committed by GitHub
parent 40b7fa67cb
commit 81f7ae46d3
4 changed files with 163 additions and 80 deletions

View File

@@ -144,6 +144,10 @@
return activeGroups;
}
get childActiveGroups() {
return Array.from(this.querySelectorAll('zen-folder[has-active]'));
}
rename() {
if (!document.documentElement.hasAttribute('zen-sidebar-expanded')) {
return;
@@ -246,6 +250,7 @@
let activeGroup = folders.get(group?.id);
if (!activeGroup) {
tab.removeAttribute('folder-active');
tab.style.removeProperty('--zen-folder-indent');
}
}
this._activeTabs = [];
@@ -266,12 +271,10 @@
}
async #unloadAllActiveTabs(event, noClose = false) {
for (const tab of this.tabs) {
await gZenPinnedTabManager._onCloseTabShortcut(event, tab, {
noClose,
expandSplitViewList: false,
});
}
await gZenPinnedTabManager._onCloseTabShortcut(event, this.tabs, {
noClose,
folderToUnload: this,
});
this.activeTabs = [];
}

View File

@@ -1398,7 +1398,7 @@
const groupStart = group.querySelector('.zen-tab-group-start');
const itemsToShow = this.#normalizeGroupItems(group.childGroupsAndTabs);
const activeFolders = Array.from(group.querySelectorAll('zen-folder[has-active]'));
const activeFolders = group.childActiveGroups;
for (const folder of activeFolders) {
const splitViewIds = new Set();
@@ -1502,6 +1502,45 @@
this.styleCleanup(itemsToHide);
}
async animateUnloadAll(group) {
const animations = [];
const activeGroups = [group, ...group.childActiveGroups];
for (const folder of activeGroups) {
folder.removeAttribute('has-active');
folder.activeTabs = [];
const groupItems = this.#normalizeGroupItems(folder.allItems);
const tabsContainer = folder.querySelector('.tab-group-container');
this.styleCleanup(groupItems);
const groupStart = folder.querySelector('.zen-tab-group-start');
// Trigger a reflow
tabsContainer.offsetHeight;
// tabsContainer.setAttribute('hidden', true);
const heightUntilSelected = this.#calculateHeightShift(tabsContainer, []);
// Collect animations for this specific folder becoming inactive
animations.push(
...this.updateFolderIcon(folder, 'close', false),
...this.#createAnimation(
groupStart,
{
marginTop: -(heightUntilSelected + 4),
},
{ duration: 0.12, ease: 'easeInOut' }
)
);
}
this.#animationCount += 1;
await Promise.all(animations);
this.#animationCount -= 1;
gBrowser.tabContainer._invalidateCachedTabs();
}
async animateUnload(group, tabToUnload, ungroup = false) {
const isSplitView = tabToUnload.group?.hasAttribute('split-view-group');
if ((!group?.isZenFolder || !isSplitView) && !tabToUnload.hasAttribute('folder-active'))
@@ -1513,29 +1552,34 @@
folder.activeTabs = folder.activeTabs.filter((tab) => tab !== tabToUnload);
if (folder.activeTabs.length === 0) {
folder.removeAttribute('has-active');
const groupItems = this.#normalizeGroupItems(folder.allItems);
const tabsContainer = folder.querySelector('.tab-group-container');
animations.push(async () => {
folder.removeAttribute('has-active');
const groupItems = this.#normalizeGroupItems(folder.allItems);
const tabsContainer = folder.querySelector('.tab-group-container');
this.styleCleanup(groupItems);
this.styleCleanup(groupItems);
const groupStart = folder.querySelector('.zen-tab-group-start');
const groupStart = folder.querySelector('.zen-tab-group-start');
tabsContainer.offsetHeight;
tabsContainer.setAttribute('hidden', true);
// Trigger a reflow
tabsContainer.offsetHeight;
tabsContainer.setAttribute('hidden', true);
const heightUntilSelected = this.#calculateHeightShift(tabsContainer, []);
const heightUntilSelected = this.#calculateHeightShift(tabsContainer, []);
animations.push(
...this.updateFolderIcon(folder, 'close', false),
...this.#createAnimation(
groupStart,
{
marginTop: -(heightUntilSelected + 4),
},
{ duration: 0.12, ease: 'easeInOut' }
)
);
// Collect animations for this specific folder becoming inactive
const folderAnimation = [
...this.updateFolderIcon(folder, 'close', false),
...this.#createAnimation(
groupStart,
{
marginTop: -(heightUntilSelected + 4),
},
{ duration: 0.12, ease: 'easeInOut' }
),
];
await Promise.all(folderAnimation);
});
}
}
@@ -1546,24 +1590,27 @@
tabToUnload.style.removeProperty('--zen-folder-indent');
let tabUnloadAnimations = [];
if (!ungroup) {
animations.push(
...this.#createAnimation(
tabToUnload,
{
opacity: 0,
height: 0,
},
{
duration: 0.12,
ease: 'easeInOut',
}
)
tabUnloadAnimations = this.#createAnimation(
tabToUnload,
{
opacity: 0,
height: 0,
},
{
duration: 0.12,
ease: 'easeInOut',
}
);
}
// Manage global animation count
this.#animationCount += 1;
await Promise.all(animations);
// Await the tab unload animation first
await Promise.all(tabUnloadAnimations);
await Promise.all(animations.map((item) => (typeof item === 'function' ? item() : item)));
this.#animationCount -= 1;
gBrowser.tabContainer._invalidateCachedTabs();
}

View File

@@ -182,7 +182,7 @@
position: absolute;
height: 100%;
border: 2px solid var(--zen-primary-color);
background: rgba(255, 255, 255, 0.01);
background: rgba(255, 255, 255, 0.1);
border-radius: var(--zen-native-inner-radius);
box-shadow: var(--zen-big-shadow);
overflow: hidden;

View File

@@ -774,14 +774,30 @@
behavior = lazy.zenPinnedTabCloseShortcutBehavior,
noClose = false,
closeIfPending = false,
expandSplitViewList = true,
folderToUnload = null,
} = {}
) {
try {
if (!selectedTab?.pinned) {
const tabs = Array.isArray(selectedTab) ? selectedTab : [selectedTab];
const pinnedTabs = [
...new Set(
tabs
.flatMap((tab) => {
if (tab.group?.hasAttribute('split-view-group')) {
return tab.group.tabs;
}
return tab;
})
.filter((tab) => tab?.pinned)
),
];
if (!pinnedTabs.length) {
return;
}
const selectedTabs = pinnedTabs.filter((tab) => tab.selected);
event.stopPropagation();
event.preventDefault();
@@ -791,63 +807,80 @@
switch (behavior) {
case 'close':
this._removePinnedAttributes(selectedTab, true);
gBrowser.removeTab(selectedTab, { animate: true });
for (const tab of pinnedTabs) {
this._removePinnedAttributes(tab, true);
gBrowser.removeTab(tab, { animate: true });
}
break;
case 'reset-unload-switch':
case 'unload-switch':
case 'reset-switch':
case 'switch':
if (behavior.includes('unload')) {
if (selectedTab.hasAttribute('glance-id')) {
// We have a glance tab inside the tab we are trying to unload,
// before we used to just ignore it but now we need to fully close
// it as well.
gZenGlanceManager.manageTabClose(selectedTab.glanceTab);
await new Promise((resolve) => {
let hasRan = false;
const onGlanceClose = () => {
hasRan = true;
resolve();
};
window.addEventListener('GlanceClose', onGlanceClose, { once: true });
// Set a timeout to resolve the promise if the event doesn't fire.
// We do this to prevent any future issues where glance woudnt close such as
// glance requering to ask for permit unload.
setTimeout(() => {
if (!hasRan) {
console.warn('GlanceClose event did not fire within 3 seconds');
for (const tab of pinnedTabs) {
if (tab.hasAttribute('glance-id')) {
// We have a glance tab inside the tab we are trying to unload,
// before we used to just ignore it but now we need to fully close
// it as well.
gZenGlanceManager.manageTabClose(tab.glanceTab);
await new Promise((resolve) => {
let hasRan = false;
const onGlanceClose = () => {
hasRan = true;
resolve();
}
}, 3000);
});
};
window.addEventListener('GlanceClose', onGlanceClose, { once: true });
// Set a timeout to resolve the promise if the event doesn't fire.
// We do this to prevent any future issues where glance woudnt close such as
// glance requering to ask for permit unload.
setTimeout(() => {
if (!hasRan) {
console.warn('GlanceClose event did not fire within 3 seconds');
resolve();
}
}, 3000);
});
}
const isSpltView = tab.group?.hasAttribute('split-view-group');
const group = isSpltView ? tab.group.group : tab.group;
if (!folderToUnload && tab.hasAttribute('folder-active')) {
await gZenFolders.animateUnload(group, tab);
}
}
const group = selectedTab.group?.hasAttribute('split-view-group')
? selectedTab.group.group
: selectedTab.group;
await gZenFolders.animateUnload(group, selectedTab);
let tabsToUnload = [selectedTab];
if (selectedTab.group?.hasAttribute('split-view-group') && expandSplitViewList) {
tabsToUnload = selectedTab.group.tabs;
if (folderToUnload) {
await gZenFolders.animateUnloadAll(folderToUnload);
}
const allAreUnloaded = tabsToUnload.every(
const allAreUnloaded = pinnedTabs.every(
(tab) => tab.hasAttribute('pending') && !tab.hasAttribute('zen-essential')
);
if (allAreUnloaded && closeIfPending) {
return await this._onCloseTabShortcut(event, selectedTab, { behavior: 'close' });
for (const tab of pinnedTabs) {
if (allAreUnloaded && closeIfPending) {
return await this._onCloseTabShortcut(event, tab, { behavior: 'close' });
}
}
await gBrowser.explicitUnloadTabs(pinnedTabs);
for (const tab of pinnedTabs) {
tab.removeAttribute('discarded');
}
await gBrowser.explicitUnloadTabs(tabsToUnload);
selectedTab.removeAttribute('discarded');
}
if (selectedTab.selected) {
this._handleTabSwitch(selectedTab);
if (selectedTabs.length) {
this._handleTabSwitch(selectedTabs[0]);
}
if (behavior.includes('reset')) {
this._resetTabToStoredState(selectedTab);
for (const tab of pinnedTabs) {
this._resetTabToStoredState(tab);
}
}
break;
case 'reset':
this._resetTabToStoredState(selectedTab);
for (const tab of pinnedTabs) {
this._resetTabToStoredState(tab);
}
break;
default:
return;