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` - - - - - + + + + ${Object.values(lazy.ZenLibrarySections).map( + Section => html` + this.activeTab = Section.id} + > + + + ` + )} - test - + + + + + `; } - 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); + } }