From d1dcba7d72a2babbd12acd4198377d7a0beb4b00 Mon Sep 17 00:00:00 2001 From: cgombauld Date: Fri, 15 Aug 2025 15:22:36 +0200 Subject: [PATCH] refactor(popup): migrate to lit.js --- .../package/pannels/overview/overview.js | 9 +- public/components/popup/popup.css | 41 ---- public/components/popup/popup.html | 4 +- public/components/popup/popup.js | 175 +++++++++++------- public/components/views/home/home.js | 9 +- .../views/home/maintainers/maintainers.css | 37 ++-- .../views/home/maintainers/maintainers.js | 19 +- .../components/views/home/report/report.css | 16 +- public/components/views/home/report/report.js | 7 +- public/core/events.js | 4 +- public/main.css | 1 - public/main.js | 4 +- 12 files changed, 165 insertions(+), 161 deletions(-) delete mode 100644 public/components/popup/popup.css diff --git a/public/components/package/pannels/overview/overview.js b/public/components/package/pannels/overview/overview.js index 93339333..76d83649 100644 --- a/public/components/package/pannels/overview/overview.js +++ b/public/components/package/pannels/overview/overview.js @@ -4,6 +4,7 @@ import prettyBytes from "pretty-bytes"; // Import Internal Dependencies import * as utils from "../../../../common/utils.js"; import { PopupMaintainer } from "../../../views/home/maintainers/maintainers.js"; +import { EVENTS } from "../../../../core/events.js"; // CONSTANTS const kEnGBDateFormat = Intl.DateTimeFormat("en-GB", { @@ -235,9 +236,11 @@ export class Overview { divElement.addEventListener("click", () => { const [name, data] = result; - window.popup.open( - new PopupMaintainer(name, data, this.package.nsn).render() - ); + window.dispatchEvent(new CustomEvent(EVENTS.MODAL_OPENED, { + detail: { + content: new PopupMaintainer(name, data, this.package.nsn).render() + } + })); }); divElement.classList.add("clickable"); } diff --git a/public/components/popup/popup.css b/public/components/popup/popup.css deleted file mode 100644 index 84127ab5..00000000 --- a/public/components/popup/popup.css +++ /dev/null @@ -1,41 +0,0 @@ -/** TODO: ANIMATE ? **/ -section#popup--background { - width: 100%; - height: 100%; - overflow: hidden; - position: fixed; - left: 0; - top: 0; - z-index: 50; - display: flex; - align-items: center; - justify-content: center; - - /* pointer-events: none; */ - background: radial-gradient(ellipse at center, rgb(255 255 255 / 0%) 0%, rgb(30 35 65) 100%); - visibility: hidden; - opacity: 0; - transition: opacity 0.35s ease-in, visibility 0ms ease-in 0.35s; -} - -section#popup--background.show { - visibility: visible; - opacity: 1; - transition: opacity 0.35s ease-in, visibility 0ms ease-in 0ms; -} - -section#popup--background>.popup { - pointer-events: all !important; - background: #f5f4f4; - border-radius: 4px; - box-shadow: 5px 5px 15px rgb(23 27 129 / 41%); - border-left: 2px solid #fff; - border-top: 2px solid #FFF; -} - -body.dark section#popup--background>.popup { - background: #303263; - box-shadow: 5px 5px 15px var(--dark-theme-primary-color); - border-left: 2px solid var(--dark-theme-secondary-darker); - border-top: 2px solid var(--dark-theme-secondary-darker); -} diff --git a/public/components/popup/popup.html b/public/components/popup/popup.html index 5ba86ab9..12b44821 100644 --- a/public/components/popup/popup.html +++ b/public/components/popup/popup.html @@ -1,3 +1 @@ - + diff --git a/public/components/popup/popup.js b/public/components/popup/popup.js index d31be8cc..8a92cef7 100644 --- a/public/components/popup/popup.js +++ b/public/components/popup/popup.js @@ -1,82 +1,125 @@ +// Import Third-party Dependencies +import { html, LitElement, css } from "lit"; +import { classMap } from "lit/directives/class-map.js"; + // Import Internal Dependencies -import * as utils from "../../common/utils"; +import { EVENTS } from "../../core/events"; + +class Popup extends LitElement { + static styles = css` + +:host{ + z-index: 50; +} + +/** TODO: ANIMATE ? **/ +section#popup--background { + width: 100%; + height: 100%; + overflow: hidden; + position: fixed; + left: 0; + top: 0; + display: flex; + align-items: center; + justify-content: center; + z-index: 50; + + /* pointer-events: none; */ + background: radial-gradient(ellipse at center, rgb(255 255 255 / 0%) 0%, rgb(30 35 65) 100%); +} + +.show { + visibility: visible; + opacity: 1 ; + transition: opacity 0.35s ease-in, visibility 0ms ease-in 0ms ; +} + +.hidden{ + visibility: hidden; + opacity: 0; + transition: opacity 0.35s ease-in, visibility 0ms ease-in 0.35s; +} + +section#popup--background>.popup { + pointer-events: all !important; + background: #f5f4f4; + border-radius: 4px; + box-shadow: 5px 5px 15px rgb(23 27 129 / 41%); + border-left: 2px solid #fff; + border-top: 2px solid #FFF; +} -export class Popup { + .dark >.popup { + background: #303263 !important; + box-shadow: 5px 5px 15px var(--dark-theme-primary-color) !important; + border-left: 2px solid var(--dark-theme-secondary-darker) !important; + border-top: 2px solid var(--dark-theme-secondary-darker) !important; +} +`; + + static properties = { + isOpen: { type: Boolean }, + theme: { type: String } + }; constructor() { - this.state = "closed"; - this.dom = { - background: document.getElementById("popup--background"), - popup: document.querySelector(".popup") + super(); + this.isOpen = false; + + this.open = ({ detail: { content } }) => { + if (this.isOpen) { + return; + } + // FIXME: temporary fix until all the popups content are migrated + if (this.theme === "dark" && content.firstElementChild) { + content.firstElementChild.classList.add("popup-dark"); + } + this.appendChild(content); + this.isOpen = true; }; - this.listener = null; - this.templateName = null; - } + this.close = () => { + if (!this.isOpen) { + return; + } + this.replaceChildren(); + this.isOpen = false; + }; - /** - * @param {!PopupTemplate} htmlElement - * @returns {void} - */ - open(template) { - if (!(template instanceof PopupTemplate)) { - throw new Error("You must provide a PopupTemplate"); - } - if (this.state === "open") { - return; - } - - this.templateName = template.name; - this.dom.popup.appendChild(template.HTMLElement); - // TODO: apply additional css customization - - this.dom.background.classList.add("show"); - this.state = "open"; - setTimeout(() => this.#closeOnClickOutside(), 1); + this.settingsChanged = ({ detail: { theme } }) => { + if (theme !== this.theme) { + this.theme = theme; + } + }; } - #closeOnClickOutside() { - this.listener = utils.hideOnClickOutside( - this.dom.popup, - { - reverse: true, - hiddenTarget: null, - callback: () => { - this.listener = null; - this.close(); - } - } - ); + connectedCallback() { + super.connectedCallback(); + window.addEventListener(EVENTS.MODAL_OPENED, this.open); + window.addEventListener(EVENTS.MODAL_CLOSED, this.close); + window.addEventListener(EVENTS.SETTINGS_SAVED, this.settingsChanged); } - #cleanupClickOutside() { - if (this.listener !== null) { - document.removeEventListener("click", this.listener); - } + disconnectedCallback() { + window.removeEventListener(EVENTS.MODAL_OPENED, this.open); + window.removeEventListener(EVENTS.MODAL_CLOSED, this.close); + window.removeEventListener(EVENTS.SETTINGS_SAVED, this.settingsChanged); + super.disconnectedCallback(); } - close() { - if (this.state === "closed") { - return; - } + render() { + const classes = { hidden: !this.isOpen, show: this.isOpen, dark: this.theme === "dark" }; - this.dom.popup.innerHTML = ""; - this.templateName = null; - this.#cleanupClickOutside(); - this.dom.background.classList.remove("show"); - this.state = "closed"; + return html` + +`; } } -export class PopupTemplate { - /** - * @param {!string} name - * @param {!HTMLElement} HTMLElement - */ - constructor( - name, - HTMLElement - ) { - this.name = name; - this.HTMLElement = HTMLElement; - } -} +customElements.define("nsecure-popup", Popup); diff --git a/public/components/views/home/home.js b/public/components/views/home/home.js index 53f79ccc..39268c62 100644 --- a/public/components/views/home/home.js +++ b/public/components/views/home/home.js @@ -7,6 +7,7 @@ import { getScoreColor, getVCSRepositoryPathAndPlatform } from "@nodesecure/util import * as utils from "../../../common/utils.js"; import "../../gauge/gauge.js"; import "../../expandable/expandable.js"; +import { EVENTS } from "../../../core/events.js"; import { fetchScorecardData, getScorecardLink } from "../../../common/scorecard.js"; // Import Components @@ -389,9 +390,11 @@ export class HomeView { handleReport() { document.querySelector(".home--header--report").addEventListener("click", async() => { - window.popup.open( - new PopupReport(this.secureDataSet.data.rootDependencyName).render() - ); + window.dispatchEvent(new CustomEvent(EVENTS.MODAL_OPENED, { + detail: { + content: new PopupReport(this.secureDataSet.data.rootDependencyName).render() + } + })); }); } } diff --git a/public/components/views/home/maintainers/maintainers.css b/public/components/views/home/maintainers/maintainers.css index 547b7396..6f7fe9c5 100644 --- a/public/components/views/home/maintainers/maintainers.css +++ b/public/components/views/home/maintainers/maintainers.css @@ -86,6 +86,7 @@ body.dark .home--maintainers > .highlighted { /** * POPUP + * FIXME: remove the !important when the popup is migrated to lit **/ .maintainers--popup { display: flex; @@ -130,8 +131,8 @@ body.dark .home--maintainers > .highlighted { color: #546884; } -body.dark .maintainers--popup>.header>.informations>p.name { - color: white; +.popup-dark >.header>.informations>p.name { + color: white !important; } .maintainers--popup>.header>.informations>p.email { @@ -140,9 +141,9 @@ body.dark .maintainers--popup>.header>.informations>p.name { font-family: monospace; } -body.dark .maintainers--popup>.header>.informations>p.email { - color: var(--dark-theme-secondary-color); - opacity: 0.9; +.popup-dark >.header>.informations>p.email { + color: var(--dark-theme-secondary-color) !important; + opacity: 0.9 !important; } .maintainers--popup>.header>.icons { @@ -182,8 +183,8 @@ body.dark .maintainers--popup>.header>.informations>p.email { flex-shrink: 0; } -body.dark .maintainers--popup>.separator { - background: var(--dark-theme-secondary-color); +.popup-dark >.separator { + background: var(--dark-theme-secondary-color) !important; } .maintainers--popup>.separator>p { @@ -195,9 +196,9 @@ body.dark .maintainers--popup>.separator { color: #255471; } -body.dark .maintainers--popup>.separator>p { - background: #303263; - color: #3cbde5; +.popup-dark>.separator>p { + background: #303263 !important; + color: #3cbde5 !important; } .maintainers--popup>ul { @@ -224,18 +225,18 @@ body.dark .maintainers--popup>.separator>p { font-size: 15px; } -body.dark .maintainers--popup>ul li { - background: linear-gradient(to right, var(--dark-theme-primary-color) 0%, rgb(28 29 58 / 18.5%) 100%); - border: none; - color: white; +.popup-dark>ul li { + background: linear-gradient(to right, var(--dark-theme-primary-color) 0%, rgb(28 29 58 / 18.5%) 100%) !important; + border: none !important; + color: white !important; } .maintainers--popup>ul li>p{ color: #234c99; } -body.dark .maintainers--popup>ul li>p{ - color: #9ca6b7; +.popup-dark>ul li>p{ + color: #9ca6b7 !important; } .maintainers--popup>ul li>span{ @@ -243,8 +244,8 @@ body.dark .maintainers--popup>ul li>p{ margin-left: 10px; } -body.dark .maintainers--popup>ul li>span{ - color: var(--dark-theme-secondary-color); +.popup-dark>ul li>span{ + color: var(--dark-theme-secondary-color) !important; } .maintainers--popup>ul li>i{ diff --git a/public/components/views/home/maintainers/maintainers.js b/public/components/views/home/maintainers/maintainers.js index b2e83936..67c99985 100644 --- a/public/components/views/home/maintainers/maintainers.js +++ b/public/components/views/home/maintainers/maintainers.js @@ -1,7 +1,7 @@ // Import Internal Dependencies import * as utils from "../../../../common/utils.js"; -import { PopupTemplate } from "../../../popup/popup.js"; import "../../../expandable/expandable.js"; +import { EVENTS } from "../../../../core/events.js"; export class Maintainers { static whois(name, email) { @@ -78,9 +78,11 @@ export class Maintainers { } person.addEventListener("click", () => { // TODO: close package info? - window.popup.open( - new PopupMaintainer(name, data, this.nsn).render() - ); + window.dispatchEvent(new CustomEvent(EVENTS.MODAL_OPENED, { + detail: { + content: new PopupMaintainer(name, data, this.nsn).render() + } + })); }); fragment.appendChild(person); @@ -144,7 +146,7 @@ export class PopupMaintainer { this.nsn.highlightMultipleNodes(nodeIds); window.locker.lock(); - window.popup.close(); + window.dispatchEvent(new CustomEvent(EVENTS.MODAL_CLOSED)); window.navigation.setNavByName("network--view"); const currentSelectedNode = window.networkNav.currentNodeParams; @@ -169,10 +171,7 @@ export class PopupMaintainer { this.generatePackagesList(clone); - return new PopupTemplate( - "maintainer", - clone - ); + return clone; } /** @@ -188,7 +187,7 @@ export class PopupMaintainer { className: "icon-right-open-big" }); iconNetwork.addEventListener("click", () => { - window.popup.close(); + window.dispatchEvent(new CustomEvent(EVENTS.MODAL_CLOSED)); window.navigation.setNavByName("network--view"); setTimeout(() => this.nsn.focusNodeByNameAndVersion(name, version), 25); }); diff --git a/public/components/views/home/report/report.css b/public/components/views/home/report/report.css index ef650fc8..4c41189a 100644 --- a/public/components/views/home/report/report.css +++ b/public/components/views/home/report/report.css @@ -1,3 +1,5 @@ +/* FIXME: remove the !important when the popup is migrated to lit */ + .report--popup { min-width: 400px; padding: 40px; @@ -15,8 +17,8 @@ flex-shrink: 0; } -body.dark .report--popup>.title { - background: var(--dark-theme-secondary-color); +.popup-dark>.title { + background: var(--dark-theme-secondary-color) !important; } .report--popup>.title>p { @@ -29,9 +31,9 @@ body.dark .report--popup>.title { font-size: 20px; } -body.dark .report--popup>.title>p { - background: #303263; - color: #3cbde5; +.popup-dark>.title>p { + background: #303263 !important; + color: #3cbde5 !important; } .report--popup>form { @@ -48,8 +50,8 @@ body.dark .report--popup>.title>p { font-size: 18px; } -body.dark .report--popup>form label { - color: white; +.popup-dark>form label { + color: white !important; } .report--popup>form input { diff --git a/public/components/views/home/report/report.js b/public/components/views/home/report/report.js index 2427e54a..48571b9a 100644 --- a/public/components/views/home/report/report.js +++ b/public/components/views/home/report/report.js @@ -1,5 +1,3 @@ -// Import Internal Dependencies -import { PopupTemplate } from "../../../popup/popup.js"; export class PopupReport { constructor(rootDependencyName) { @@ -52,9 +50,6 @@ export class PopupReport { }); }, { once: true }); - return new PopupTemplate( - "report", - clone - ); + return clone; } } diff --git a/public/core/events.js b/public/core/events.js index d7a4fb81..b2a3371f 100644 --- a/public/core/events.js +++ b/public/core/events.js @@ -5,5 +5,7 @@ export const EVENTS = { PACKAGE_INFO_CLOSED: "package-info-closed", SETTINGS_SAVED: "settings-saved", MOVED_TO_NEXT_LOCKED_NODE: "moved-to-next-locked-node", - MOVED_TO_PREVIOUS_LOCKED_NODE: "moved-to-previous-locked-node" + MOVED_TO_PREVIOUS_LOCKED_NODE: "moved-to-previous-locked-node", + MODAL_CLOSED: "modal-closed", + MODAL_OPENED: "modal-opened" }; diff --git a/public/main.css b/public/main.css index 224946e1..57a40373 100644 --- a/public/main.css +++ b/public/main.css @@ -5,7 +5,6 @@ @import url("./font/roboto/roboto.css"); @import url("./font/mononoki/mononoki.css"); @import url("./components/locker/locker.css"); -@import url("./components/popup/popup.css"); @import url("./components/file-box/file-box.css"); @import url("./components/expandable/expandable.css"); @import url("./components/navigation/navigation.css"); diff --git a/public/main.js b/public/main.js index a2b41f6a..9c2dc00e 100644 --- a/public/main.js +++ b/public/main.js @@ -5,7 +5,7 @@ import { NodeSecureDataSet, NodeSecureNetwork } from "@nodesecure/vis-network"; import { PackageInfo } from "./components/package/package.js"; import { ViewNavigation } from "./components/navigation/navigation.js"; import { Wiki } from "./components/wiki/wiki.js"; -import { Popup } from "./components/popup/popup.js"; +import "./components/popup/popup.js"; import { Locker } from "./components/locker/locker.js"; import "./components/legend/legend.js"; import "./components/locked-navigation/locked-navigation.js"; @@ -28,13 +28,13 @@ document.addEventListener("DOMContentLoaded", async() => { window.scannedPackageCache = []; window.recentPackageCache = []; window.locker = null; - window.popup = new Popup(); window.settings = await new Settings().fetchUserConfig(); window.i18n = await new i18n().fetch(); window.navigation = new ViewNavigation(); window.wiki = new Wiki(); await init(); + window.dispatchEvent(new CustomEvent(EVENTS.SETTINGS_SAVED, { detail: window.settings.config })); onSettingsSaved(window.settings.config); window.socket = new WebSocket(`ws://${window.location.hostname}:1338`);