diff --git a/crowdin.yml b/crowdin.yml
index a9f073ac0..fe20b9a6f 100644
--- a/crowdin.yml
+++ b/crowdin.yml
@@ -20,3 +20,5 @@ files:
translation: browser/browser/preferences/zen-preferences.ftl
- source: en-US/browser/browser/zen-folders.ftl
translation: browser/browser/zen-folders.ftl
+ - source: en-US/browser/browser/zen-library.ftl
+ translation: browser/browser/zen-library.ftl
diff --git a/locales/en-US/browser/browser/zen-library.ftl b/locales/en-US/browser/browser/zen-library.ftl
new file mode 100644
index 000000000..82595a1ce
--- /dev/null
+++ b/locales/en-US/browser/browser/zen-library.ftl
@@ -0,0 +1,7 @@
+# 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/.
+
+library-spaces-section-title = Spaces
+library-downloads-section-title = Downloads
+library-history-section-title = History
\ No newline at end of file
diff --git a/src/zen/library/ZenLibrary.mjs b/src/zen/library/ZenLibrary.mjs
index 4385a22aa..17dc317d8 100644
--- a/src/zen/library/ZenLibrary.mjs
+++ b/src/zen/library/ZenLibrary.mjs
@@ -5,8 +5,24 @@
import { html } from "chrome://global/content/vendor/lit.all.mjs";
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
+let lazy = {};
let gZenLibraryInstance = null;
+ChromeUtils.defineESModuleGetters(lazy, {
+ ZenLibrarySections: "moz-src:///zen/library/ZenLibrarySections.mjs",
+}, { global: "current" });
+
+ChromeUtils.defineLazyGetter(lazy, "l10n", function () {
+ return new Localization(
+ ["browser/zen-library.ftl"],
+ true
+ );
+});
+
+ChromeUtils.defineLazyGetter(lazy, "appContentWrapper", function () {
+ return document.getElementById("zen-appcontent-wrapper");
+});
+
/**
* The ZenLibrary class is responsible for managing the UI for the library feature.
* This feature allows users to view and manage their browsing history, downloads,
@@ -14,14 +30,16 @@ let gZenLibraryInstance = null;
*/
export class ZenLibrary extends MozLitElement {
#initialized = false;
+ #resizeObserver = null;
+ _deletionIdleCallbackId = null;
static properties = {
activeTab: { type: String },
};
static queries = {
- content: "#zen-library-content",
- tabs: { all: "#zen-library-sidebar-tabs > .library-tab" },
+ _content: "#zen-library-content",
+ _tabs: { all: "#zen-library-sidebar-tabs > .library-tab" },
};
constructor() {
@@ -35,31 +53,69 @@ export class ZenLibrary extends MozLitElement {
return;
}
window.addEventListener("keydown", this);
+ // Add connected call back and make `appContentWrapper` transform translate the oposite of this element
+ this.#resizeObserver = new ResizeObserver(() => {
+ requestAnimationFrame(() => {
+ let width = window.windowUtils.getBoundsWithoutFlushing(this).width;
+ if (gZenVerticalTabsManager._prefsRightSide) {
+ width = -width;
+ }
+ lazy.appContentWrapper.style.transform = `translateX(${width}px)`;
+ });
+ });
+ this.#resizeObserver.observe(this);
this.#initialized = true;
}
disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener("keydown", this);
+ if (this.#resizeObserver) {
+ this.#resizeObserver.disconnect();
+ this.#resizeObserver = null;
+ }
this.#initialized = false;
}
render() {
return html`
-
-
+
+
+
+
+
`;
}
- handleEvent() {
- // Handle events related to the library UI here
+ handleEvent(event) {
+ switch (event.type) {
+ case "keydown": {
+ if (event.key === "Escape" && this.isOpen) {
+ ZenLibrary.toggle();
+ }
+ break;
+ }
+ }
+ }
+
+ get isOpen() {
+ return this.hasAttribute("open");
}
static getInstance() {
@@ -72,14 +128,31 @@ export class ZenLibrary extends MozLitElement {
static clearInstance() {
if (gZenLibraryInstance) {
+ gZenLibraryInstance._deletionIdleCallbackId = null;
gZenLibraryInstance.remove();
gZenLibraryInstance = null;
}
}
static toggle() {
+ window.docShell.treeOwner.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIAppWindow).rollupAllPopups();
let instance = this.getInstance();
instance.toggleAttribute("open");
+ if (!instance.isOpen) {
+ gNavToolbox.style.display = "";
+ lazy.appContentWrapper.style.transform = "";
+ if (!instance._deletionIdleCallbackId) {
+ instance._deletionIdleCallbackId = requestIdleCallback(() => {
+ this.clearInstance();
+ });
+ }
+ } else {
+ if (instance._deletionIdleCallbackId) {
+ cancelIdleCallback(instance._deletionIdleCallbackId);
+ instance._deletionIdleCallbackId = null;
+ }
+ gNavToolbox.style.display = "none";
+ }
}
}
diff --git a/src/zen/library/ZenLibrarySections.mjs b/src/zen/library/ZenLibrarySections.mjs
new file mode 100644
index 000000000..10a5a1d7a
--- /dev/null
+++ b/src/zen/library/ZenLibrarySections.mjs
@@ -0,0 +1,34 @@
+// 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 { html } from "chrome://global/content/vendor/lit.all.mjs";
+import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
+
+class ZenLibrarySection extends MozLitElement {
+ static largeContent = false;
+
+ static get id() {
+ throw new Error("Unimplemented");
+ }
+
+ static get label() {
+ throw new Error("Unimplemented");
+ }
+}
+
+export const ZenLibrarySections = {
+ history: class extends ZenLibrarySection {
+ static id = "history";
+ static label = "library-history-section-title";
+ },
+ downloads: class extends ZenLibrarySection {
+ static id = "downloads";
+ static label = "library-downloads-section-title";
+ },
+ spaces: class extends ZenLibrarySection {
+ static largeContent = true;
+ static id = "spaces";
+ static label = "library-spaces-section-title";
+ },
+};
diff --git a/src/zen/library/jar.inc.mn b/src/zen/library/jar.inc.mn
index 762275b77..b93130cf9 100644
--- a/src/zen/library/jar.inc.mn
+++ b/src/zen/library/jar.inc.mn
@@ -2,4 +2,4 @@
# 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/.
- content/browser/zen-components/zen-library.css (../../zen/library/zen-library.css)
+ content/browser/zen-styles/zen-library.css (../../zen/library/zen-library.css)
diff --git a/src/zen/library/moz.build b/src/zen/library/moz.build
index 41e3e1aae..006a06fd1 100644
--- a/src/zen/library/moz.build
+++ b/src/zen/library/moz.build
@@ -4,4 +4,5 @@
MOZ_SRC_FILES += [
"ZenLibrary.mjs",
+ "ZenLibrarySections.mjs",
]
diff --git a/src/zen/library/zen-library.css b/src/zen/library/zen-library.css
index 88d24da3e..b0470ea26 100644
--- a/src/zen/library/zen-library.css
+++ b/src/zen/library/zen-library.css
@@ -5,4 +5,74 @@
*/
:host {
+ transition: max-width 0.1s ease-in-out;
+ max-width: 0;
+ z-index: 1;
+ display: block;
+ position: absolute;
+ height: 100%;
+}
+
+:host([open]) {
+ max-width: 100%;
+ }
+
+#zen-library-sidebar {
+ display: flex;
+ flex-direction: column;
+ background: rgba(255, 255, 255, 0.05);
+ padding: 8px;
+ justify-content: space-between;
+ height: 100%;
+ width: 84px;
+ -moz-window-dragging: drag;
+ box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.025);
+}
+
+#zen-library-content {
+ display: flex;
+ transition: width 0.15s ease-in-out;
+ width: 45vw;
+ max-width: 100%;
+
+ &[large-content] {
+ width: 80vw;
+ }
+}
+
+#zen-library-sidebar-tabs {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ -moz-window-dragging: no-drag;
+}
+
+.zen-library-tab {
+ display: flex;
+ padding: 12px 0;
+ justify-content: center;
+ align-items: center;
+ background: transparent;
+ position: relative;
+ font-weight: 600;
+
+ &::before {
+ content: "";
+ inset: 0;
+ border-radius: 6px;
+ transition: transform 0.1s ease-in-out;
+ position: absolute;
+ }
+
+ &:hover::before {
+ background: color-mix(in srgb, currentColor 5%, transparent);
+ }
+
+ &[active]::before {
+ background: color-mix(in srgb, currentColor 10%, transparent);
+ }
+
+ &:active:hover::before {
+ transform: scale(0.95);
+ }
}