mirror of
https://github.com/zen-browser/desktop.git
synced 2026-08-26 08:21:34 +00:00
gh-13258: Implement new loading indicator (gh-13259)
This commit is contained in:
127
src/zen/common/sys/ui/ZenProgressBar.sys.mjs
Normal file
127
src/zen/common/sys/ui/ZenProgressBar.sys.mjs
Normal file
@@ -0,0 +1,127 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import { ZenUIComponent } from "resource:///modules/zen/ui/ZenUIComponent.sys.mjs";
|
||||
|
||||
const WAIT_BEFORE_SHOWING_LONG_LOAD = 3000;
|
||||
|
||||
export class ZenProgressBar extends ZenUIComponent {
|
||||
#element = null;
|
||||
#loadingTab = null;
|
||||
#longLoadTimer = null;
|
||||
|
||||
init() {
|
||||
this.listenBrowserTabsProgress();
|
||||
this.addEventListener("TabSelect");
|
||||
}
|
||||
|
||||
onStateChange(aWebProgress) {
|
||||
this.#checkBrowserProgress(aWebProgress);
|
||||
}
|
||||
|
||||
onLocationChange(webProgress) {
|
||||
this.#checkBrowserProgress(webProgress);
|
||||
}
|
||||
|
||||
on_TabSelect() {
|
||||
const gBrowser = this.window.gBrowser;
|
||||
const selectedTab = gBrowser.selectedTab;
|
||||
this.onLocationChange(gBrowser.getBrowserForTab(selectedTab));
|
||||
}
|
||||
|
||||
get #progressBar() {
|
||||
if (!this.#loadingTab) {
|
||||
return null;
|
||||
}
|
||||
if (!this.#element) {
|
||||
this.#element = this.window.document.createXULElement("hbox");
|
||||
this.#element.id = "zen-loading-progress-bar";
|
||||
}
|
||||
if (
|
||||
this.#element._loadingTab?.deref() !== this.#loadingTab &&
|
||||
this.#loadingTab
|
||||
) {
|
||||
this.#element._loadingTab = new WeakRef(this.#loadingTab);
|
||||
const container = this.window.document.getElementById(
|
||||
this.#loadingTab.linkedPanel
|
||||
);
|
||||
container.firstChild.before(this.#element);
|
||||
this.window.gZenUIManager.elementAnimate(
|
||||
this.#element,
|
||||
{
|
||||
opacity: [0, 0.6],
|
||||
},
|
||||
{
|
||||
duration: 400,
|
||||
}
|
||||
);
|
||||
}
|
||||
return this.#element;
|
||||
}
|
||||
|
||||
#checkBrowserProgress(webProgress) {
|
||||
const window = this.window;
|
||||
const gBrowser = window.gBrowser;
|
||||
const tab = gBrowser.getTabForBrowser(webProgress);
|
||||
const isLoading =
|
||||
tab?.selected &&
|
||||
(tab.hasAttribute("busy") || tab.hasAttribute("progress"));
|
||||
if (isLoading) {
|
||||
this.#showProgressBar(tab);
|
||||
} else {
|
||||
this.#hideProgressBar();
|
||||
}
|
||||
}
|
||||
|
||||
#hideProgressBar() {
|
||||
const progressBar = this.#element;
|
||||
const window = this.window;
|
||||
if (this.#longLoadTimer) {
|
||||
window.clearTimeout(this.#longLoadTimer);
|
||||
this.#longLoadTimer = null;
|
||||
}
|
||||
|
||||
this.#loadingTab = null;
|
||||
if (!progressBar) {
|
||||
return;
|
||||
}
|
||||
const callback = () => {
|
||||
delete progressBar._loadingTab;
|
||||
progressBar.remove();
|
||||
this.#element = null;
|
||||
};
|
||||
if (this.window.gReduceMotion) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
this.window.gZenUIManager
|
||||
.elementAnimate(
|
||||
progressBar,
|
||||
{
|
||||
transform: ["scaleX(0.8) translate(-50%, -50%)"],
|
||||
opacity: [0],
|
||||
},
|
||||
{
|
||||
duration: 300,
|
||||
}
|
||||
)
|
||||
.then(callback);
|
||||
}
|
||||
|
||||
#showProgressBar(aTab) {
|
||||
if (this.#loadingTab === aTab) {
|
||||
return;
|
||||
}
|
||||
this.#loadingTab = aTab;
|
||||
const progressBar = this.#progressBar;
|
||||
progressBar.removeAttribute("fade-out");
|
||||
progressBar.removeAttribute("long-load");
|
||||
this.#longLoadTimer = this.window.setTimeout(() => {
|
||||
if (this.#loadingTab === aTab) {
|
||||
progressBar.setAttribute("long-load", "true");
|
||||
}
|
||||
this.#longLoadTimer = null;
|
||||
}, WAIT_BEFORE_SHOWING_LONG_LOAD);
|
||||
}
|
||||
}
|
||||
61
src/zen/common/sys/ui/ZenUIComponent.sys.mjs
Normal file
61
src/zen/common/sys/ui/ZenUIComponent.sys.mjs
Normal file
@@ -0,0 +1,61 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
/**
|
||||
* Base class for UI components in Zen.
|
||||
* UI components are responsible for managing their own event listeners
|
||||
* and providing a consistent interface for handling events.
|
||||
*/
|
||||
export class ZenUIComponent {
|
||||
#window = null;
|
||||
#eventListeners = new Set();
|
||||
|
||||
constructor(aWindow) {
|
||||
this.#window = aWindow;
|
||||
this.init();
|
||||
this.#window.addEventListener("unload", () => {
|
||||
if (typeof this.uninit === "function") {
|
||||
this.uninit();
|
||||
}
|
||||
for (const { type, options } of this.#eventListeners) {
|
||||
this.#window.removeEventListener(type, this, options);
|
||||
}
|
||||
this.#eventListeners.clear();
|
||||
});
|
||||
}
|
||||
|
||||
get window() {
|
||||
return this.#window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event listener to the component that will automatically be removed when the window unloads.
|
||||
*
|
||||
* @param {string} type - The event type to listen for.
|
||||
* @param {object} options - The event listener function or an object containing options.
|
||||
* @returns {void}
|
||||
*/
|
||||
addEventListener(type, options = {}) {
|
||||
this.#window.addEventListener(type, this, options);
|
||||
if (options?.once) {
|
||||
return;
|
||||
}
|
||||
this.#eventListeners.add({ type, options });
|
||||
}
|
||||
|
||||
listenBrowserTabsProgress() {
|
||||
this.#window.gBrowser.addTabsProgressListener(this);
|
||||
}
|
||||
|
||||
listenBrowserProgress() {
|
||||
this.#window.gBrowser.addProgressListener(this);
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
const handlerName = "on_" + event.type;
|
||||
if (typeof this[handlerName] === "function") {
|
||||
this[handlerName](event);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user