From 3da584be2803415f52e7aa08c1e6ce4e1929432c Mon Sep 17 00:00:00 2001 From: cgombauld Date: Mon, 20 Oct 2025 12:02:38 +0200 Subject: [PATCH] refactor(locker): migrate to lit --- public/components/icon/icon.js | 18 ++ public/components/locker/locker.css | 50 ----- public/components/locker/locker.js | 192 ++++++++++++++---- public/components/navigation/navigation.js | 9 + .../views/home/maintainers/maintainers.js | 10 +- public/core/events.js | 4 +- public/main.css | 1 - public/main.js | 17 +- 8 files changed, 199 insertions(+), 102 deletions(-) delete mode 100644 public/components/locker/locker.css diff --git a/public/components/icon/icon.js b/public/components/icon/icon.js index 93dc7577..23b8ce28 100644 --- a/public/components/icon/icon.js +++ b/public/components/icon/icon.js @@ -129,6 +129,24 @@ c0-20.25-0.75-88.25 31-88.25c5.25 0 31.25 21.25 65 21.25c11.5 0 22.5-2 33.25-5.7 -1.25-16.5c10.75 3.75 21.75 5.75 33.25 5.75c33.75 0 59.75-21.25 65-21.25c31.75 0 31 68 31 88.25zM448 64c0 35.25-28.75 64-64 64s-64-28.75-64-64s28.75-64 64-64s64 28.75 64 64z"/> +`, + unlock: html` + +`, + lock: html` + ` }; diff --git a/public/components/locker/locker.css b/public/components/locker/locker.css deleted file mode 100644 index dc641c0d..00000000 --- a/public/components/locker/locker.css +++ /dev/null @@ -1,50 +0,0 @@ -#network-locker { - position: absolute; - bottom: 10px; - right: 10px; - z-index: 30; - display: flex; - height: 30px; - border-radius: 4px; - align-items: center; - box-sizing: border-box; - overflow: hidden; - background-color: var(--primary); - transition: 0.3s all linear; - cursor: pointer; -} - -#network-locker:not(.enabled) { - background-color: var(--primary); -} - -#network-locker.enabled { - background-color: #af2222; -} - -#network-locker>i { - height: inherit; - padding: 0 5px; - display: flex; - align-items: center; - border-radius: 4px; - margin-right: 10px; - transition: 0.3s all linear; -} - -#network-locker>i:not(.enabled) { - background-color: var(--primary-lighter); -} - -#network-locker>i.enabled { - background-color: #cb3d3d; -} - -#network-locker>p { - font-family: mononoki; - padding-right: 10px; - display: flex; - align-items: center; - height: inherit; - text-transform: capitalize; -} diff --git a/public/components/locker/locker.js b/public/components/locker/locker.js index 41780227..77ea25c2 100644 --- a/public/components/locker/locker.js +++ b/public/components/locker/locker.js @@ -1,21 +1,104 @@ +// Import Third-party Dependencies +import { LitElement, html, css } from "lit"; +import { classMap } from "lit/directives/class-map.js"; + // Import Internal Dependencies import * as utils from "../../common/utils.js"; import { EVENTS } from "../../core/events.js"; +import "../icon/icon.js"; + +export class Locker extends LitElement { + static styles = css` +#network-locker { + position: absolute; + bottom: 10px; + right: 10px; + z-index: 30; + display: flex; + height: 30px; + border-radius: 4px; + align-items: center; + box-sizing: border-box; + overflow: hidden; + background-color: var(--primary); + transition: 0.3s all linear; + cursor: pointer; +} + +#network-locker:not(.enabled) { + background-color: var(--primary); +} + +#network-locker.enabled { + background-color: #af2222; +} + +#network-locker>div { + height: inherit; + padding: 0 5px; + display: flex; + align-items: center; + border-radius: 4px; + margin-right: 10px; + transition: 0.3s all linear; +} + +#network-locker>div>nsecure-icon { + margin: 0; + transform: translateX(3px); +} + + +#network-locker>div:not(.enabled) { + background-color: var(--primary-lighter); +} + +#network-locker>div.enabled { + background-color: #cb3d3d; +} + +#network-locker>p { + font-family: mononoki; + padding-right: 10px; + display: flex; + align-items: center; + height: inherit; + text-transform: capitalize; +} +`; + + static get properties() { + return { + locked: { type: Boolean }, + unlockAuthorized: { type: Boolean }, + nsn: { type: Object }, + isNetworkViewHidden: { type: Boolean } + }; + } -export class Locker { - constructor(nsn) { - this.dom = document.getElementById("network-locker"); - this.networkView = document.getElementById("network--view"); - this.nsn = nsn; + constructor() { + super(); this.locked = false; this.unlockAuthorized = true; - this.renderUnlock(); + this.isNetworkViewHidden = false; + this.hideNetworkView = () => { + if (this.isNetworkViewHidden) { + return; + } + this.isNetworkViewHidden = true; + }; - document.addEventListener("keydown", (event) => { - const isNetworkViewHidden = this.networkView.classList.contains("hidden"); + this.showNetworkView = () => { + if (!this.isNetworkViewHidden) { + return; + } + this.isNetworkViewHidden = false; + }; + + this.onKeyDown = (event) => { const isTargetInput = event.target.tagName === "INPUT"; const isTargetPopup = event.target.id === "popup--background"; - if (isNetworkViewHidden || isTargetInput || isTargetPopup) { + if (this.isNetworkViewHidden || isTargetInput || isTargetPopup) { return; } @@ -26,9 +109,61 @@ export class Locker { break; } } + }; + } + + connectedCallback() { + super.connectedCallback(); + window.addEventListener(EVENTS.NETWORK_VIEW_HID, this.hideNetworkView); + window.addEventListener(EVENTS.NETWORK_VIEW_SHOWED, this.showNetworkView); + document.addEventListener("keydown", this.onKeyDown); + } + + disconnectedCallback() { + window.removeEventListener(EVENTS.NETWORK_VIEW_HID, this.hideNetworkView); + window.removeEventListener(EVENTS.NETWORK_VIEW_SHOWED, this.showNetworkView); + document.removeEventListener("keydown", this.onKeyDown); + super.disconnectedCallback(); + } + + updated(changedProperties) { + if (changedProperties.has("nsn")) { + const oldNsn = changedProperties.get("nsn"); + + if (oldNsn) { + oldNsn.network.off("highlight_done", this.highlightDone); + } + + if (this.nsn) { + this.nsn.network.on("highlight_done", this.highlightDone); + } + } + } + + render() { + const networkLockerClasses = classMap({ + enabled: this.locked }); - this.dom.addEventListener("click", () => this.auto()); - this.nsn.network.on("highlight_done", this.highlightDone.bind(this)); + const iconClasses = classMap({ + enabled: this.locked + }); + + return html`
+
+ +
+

${this.locked ? window.i18n[utils.currentLang()].network.locked + : window.i18n[utils.currentLang()].network.unlocked}

+
`; + } + + auto() { + // Refuse locking if there is no multi selections + if (this.nsn.lastHighlightedIds === null) { + return; + } + + this[this.locked ? "unlock" : "lock"](); } highlightDone() { @@ -45,19 +180,9 @@ export class Locker { this.unlock(); } - auto() { - // Refuse locking if there is no multi selections - if (this.nsn.lastHighlightedIds === null) { - return; - } - - this[this.locked ? "unlock" : "lock"](); - } - lock() { if (!this.locked) { console.log("[LOCKER] lock triggered"); - this.renderLock(); this.locked = true; window.dispatchEvent(new CustomEvent(EVENTS.LOCKED, { composed: true })); } @@ -69,7 +194,6 @@ export class Locker { } console.log("[LOCKER] unlock triggered"); - this.renderUnlock(); this.locked = false; window.dispatchEvent(new CustomEvent(EVENTS.UNLOCKED, { composed: true })); @@ -83,26 +207,6 @@ export class Locker { this.nsn.neighbourHighlight(selectedNode, window.i18n[utils.currentLang()]); } } - - renderLock() { - this.dom.classList.add("enabled"); - this.dom.querySelector("p").textContent = window.i18n[utils.currentLang()].network.locked; - this.networkView.classList.add("locked"); - - const iconElement = this.dom.querySelector("i"); - iconElement.classList.remove("icon-lock-open"); - iconElement.classList.add("icon-lock"); - iconElement.classList.add("enabled"); - } - - renderUnlock() { - this.dom.classList.remove("enabled"); - this.dom.querySelector("p").textContent = window.i18n[utils.currentLang()].network.unlocked; - this.networkView.classList.remove("locked"); - - const iconElement = this.dom.querySelector("i"); - iconElement.classList.remove("icon-lock"); - iconElement.classList.remove("enabled"); - iconElement.classList.add("icon-lock-open"); - } } + +customElements.define("nsecure-locker", Locker); diff --git a/public/components/navigation/navigation.js b/public/components/navigation/navigation.js index 03ebb0c4..1e692a1e 100644 --- a/public/components/navigation/navigation.js +++ b/public/components/navigation/navigation.js @@ -1,5 +1,6 @@ // Import Internal Dependencies import { PackageInfo } from "../package/package.js"; +import { EVENTS } from "../../core/events.js"; // CONSTANTS const kAvailableView = new Set([ @@ -122,6 +123,10 @@ export class ViewNavigation { return; } + if (menuName === "network--view") { + window.dispatchEvent(new CustomEvent(EVENTS.NETWORK_VIEW_HID, { composed: true })); + } + menu.classList.add("hidden"); } @@ -131,6 +136,10 @@ export class ViewNavigation { return; } + if (menuName === "network--view") { + window.dispatchEvent(new CustomEvent(EVENTS.NETWORK_VIEW_SHOWED, { composed: true })); + } + menu.classList.remove("hidden"); } } diff --git a/public/components/views/home/maintainers/maintainers.js b/public/components/views/home/maintainers/maintainers.js index 2ff6d423..ff93a6b9 100644 --- a/public/components/views/home/maintainers/maintainers.js +++ b/public/components/views/home/maintainers/maintainers.js @@ -523,7 +523,7 @@ export class PopupMaintainer extends LitElement { .map((spec) => { const { name, version } = utils.parseNpmSpec(spec); - return `${name} @${version} `; + return `${name}@${version}`; }); return html` @@ -565,12 +565,14 @@ export class PopupMaintainer extends LitElement { const moveTo = currentSelectedNode === null || !nodeIds.includes(currentSelectedNode.nodes[0]); if (moveTo) { const origin = this.nsn.network.getViewPosition(); - const closestNode = nodeIds + const nodes = nodeIds .map((id) => { return { id, pos: this.nsn.network.getPosition(id) }; - }) + }); + const closestNode = nodes .reduce( - (a, b) => (utils.vec2Distance(origin, a.pos) < utils.vec2Distance(origin, b.pos) ? a : b) + (a, b) => (utils.vec2Distance(origin, a.pos) < utils.vec2Distance(origin, b.pos) ? a : b), + nodes[0] ); const scale = nodeIds.length > 3 ? 0.25 : 0.35; diff --git a/public/core/events.js b/public/core/events.js index b2a3371f..cffbfb4d 100644 --- a/public/core/events.js +++ b/public/core/events.js @@ -7,5 +7,7 @@ export const EVENTS = { MOVED_TO_NEXT_LOCKED_NODE: "moved-to-next-locked-node", MOVED_TO_PREVIOUS_LOCKED_NODE: "moved-to-previous-locked-node", MODAL_CLOSED: "modal-closed", - MODAL_OPENED: "modal-opened" + MODAL_OPENED: "modal-opened", + NETWORK_VIEW_HID: "network-view-hid", + NETWORK_VIEW_SHOWED: "network-view-showed" }; diff --git a/public/main.css b/public/main.css index 57a40373..c9f37642 100644 --- a/public/main.css +++ b/public/main.css @@ -4,7 +4,6 @@ @import url("./font/fontello/fontello.css"); @import url("./font/roboto/roboto.css"); @import url("./font/mononoki/mononoki.css"); -@import url("./components/locker/locker.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 9c2dc00e..9df9c15d 100644 --- a/public/main.js +++ b/public/main.js @@ -6,7 +6,7 @@ import { PackageInfo } from "./components/package/package.js"; import { ViewNavigation } from "./components/navigation/navigation.js"; import { Wiki } from "./components/wiki/wiki.js"; import "./components/popup/popup.js"; -import { Locker } from "./components/locker/locker.js"; +import "./components/locker/locker.js"; import "./components/legend/legend.js"; import "./components/locked-navigation/locked-navigation.js"; import { Settings } from "./components/views/settings/settings.js"; @@ -126,7 +126,10 @@ async function init(options = {}) { i18n: window.i18n[utils.currentLang()], theme: window.settings.config.theme }); - window.locker = new Locker(nsn); + window.locker = document.createElement("nsecure-locker"); + window.locker.nsn = nsn; + const locker = document.getElementById("network-locker"); + locker.replaceWith(window.locker); const legend = document.getElementById("legend"); legend.isVisible = window.settings.config.showFriendlyDependencies; window.legend = legend; @@ -263,7 +266,17 @@ function onSettingsSaved(defaultConfig = null) { updateSettings(defaultConfig); } + const networkView = document.getElementById("network--view"); + window.addEventListener(EVENTS.SETTINGS_SAVED, async(event) => { updateSettings(event.detail); }); + + window.addEventListener(EVENTS.LOCKED, () => { + networkView.classList.add("locked"); + }); + + window.addEventListener(EVENTS.UNLOCKED, () => { + networkView.classList.remove("locked"); + }); }