Skip to content

Commit 2df35b6

Browse files
committed
feat(locked-navigation): add navigation arrows when lock mode is enabled
1 parent 7ba3781 commit 2df35b6

10 files changed

Lines changed: 212 additions & 32 deletions

File tree

i18n/english.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ const ui = {
227227
default: "The package is fine.",
228228
warn: "The package has warnings.",
229229
friendly: "The package is maintained by the same authors as the root package."
230+
},
231+
lockedNavigation: {
232+
next: "Next",
233+
prev: "Prev"
230234
}
231235
};
232236

i18n/french.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ const ui = {
227227
default: "Rien à signaler.",
228228
warn: "La dépendance contient des menaces.",
229229
friendly: "La dépendance est maintenu par des auteurs du package principal."
230+
},
231+
lockedNavigation: {
232+
next: "Suivant",
233+
prev: "Précédent"
230234
}
231235
};
232236

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Import Third-party Dependencies
2+
import { LitElement, html, css } from "lit";
3+
4+
// Import Internal Dependencies
5+
import { EVENTS } from "../../core/events";
6+
7+
export class LockedNavigation extends LitElement {
8+
static styles = css`
9+
:host {
10+
position: absolute;
11+
right: 132px;
12+
bottom: 10px;
13+
z-index: 100;
14+
display: flex;
15+
align-items: center;
16+
gap: 10px;
17+
}
18+
19+
.btn{
20+
width: 0;
21+
height: 0;
22+
background: none;
23+
border: none;
24+
cursor: pointer;
25+
transition: all 0.3s ease;
26+
border-radius: 5px;
27+
}
28+
29+
.next{
30+
border-top: 12px solid transparent;
31+
border-bottom: 12px solid transparent;
32+
border-left: 16px solid #af2222;
33+
}
34+
35+
.prev{
36+
border-top: 12px solid transparent;
37+
border-bottom: 12px solid transparent;
38+
border-right: 16px solid #af2222;
39+
}
40+
41+
.next:hover{
42+
border-left-color: #cb3d3d;
43+
}
44+
45+
.prev:hover{
46+
border-right-color: #cb3d3d;
47+
}
48+
49+
.next:disabled{
50+
opacity: 0.6;
51+
cursor: not-allowed;
52+
border-left-color: var(--primary);
53+
}
54+
55+
.prev:disabled{
56+
opacity: 0.6;
57+
cursor: not-allowed;
58+
border-right-color: var(--primary);
59+
}
60+
`;
61+
62+
static properties = {
63+
isLocked: { type: Boolean },
64+
nextLabel: { type: String },
65+
prevLabel: { type: String }
66+
};
67+
68+
constructor() {
69+
super();
70+
this.isLocked = false;
71+
72+
this.lock = () => {
73+
this.isLocked = true;
74+
};
75+
76+
this.unlock = () => {
77+
this.isLocked = false;
78+
};
79+
}
80+
81+
connectedCallback() {
82+
super.connectedCallback();
83+
window.addEventListener(EVENTS.LOCKED, this.lock);
84+
window.addEventListener(EVENTS.UNLOCKED, this.unlock);
85+
}
86+
87+
disconnectedCallback() {
88+
window.removeEventListener(EVENTS.LOCKED, this.lock);
89+
window.removeEventListener(EVENTS.UNLOCKED, this.unlock);
90+
super.disconnectedCallback();
91+
}
92+
93+
render() {
94+
return html`
95+
<button ?disabled=${!this.isLocked} type="button" @click=${this.moveToPreviousLockedNode}
96+
class="btn prev" ariaLabel=${this.prevLabel} ></button>
97+
<button ?disabled=${!this.isLocked} type="button" @click=${this.moveToNextLockedNode}
98+
class=" btn next" ariaLabel=${this.nextLabel}></button>
99+
`;
100+
}
101+
102+
moveToNextLockedNode() {
103+
window.dispatchEvent(new CustomEvent(EVENTS.MOVED_TO_NEXT_LOCKED_NODE, { composed: true }));
104+
}
105+
moveToPreviousLockedNode() {
106+
window.dispatchEvent(new CustomEvent(EVENTS.MOVED_TO_PREVIOUS_LOCKED_NODE, { composed: true }));
107+
}
108+
}
109+
110+
customElements.define("locked-navigation", LockedNavigation);

public/components/locker/locker.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Import Internal Dependencies
22
import * as utils from "../../common/utils.js";
3+
import { EVENTS } from "../../core/events.js";
34

45
export class Locker {
56
constructor(nsn) {
@@ -58,6 +59,7 @@ export class Locker {
5859
console.log("[LOCKER] lock triggered");
5960
this.renderLock();
6061
this.locked = true;
62+
window.dispatchEvent(new CustomEvent(EVENTS.LOCKED, { composed: true }));
6163
}
6264
}
6365

@@ -69,6 +71,7 @@ export class Locker {
6971
console.log("[LOCKER] unlock triggered");
7072
this.renderUnlock();
7173
this.locked = false;
74+
window.dispatchEvent(new CustomEvent(EVENTS.UNLOCKED, { composed: true }));
7275

7376
// No node selected, so we reset highlight
7477
const selectedNode = window.networkNav.currentNodeParams;

public/components/package/package.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import "../bundlephobia/bundlephobia.js";
33
import { PackageHeader } from "./header/header.js";
44
import * as Pannels from "./pannels/index.js";
55
import * as utils from "../../common/utils.js";
6+
import { EVENTS } from "../../core/events.js";
67

78
export class PackageInfo {
89
static DOMElementName = "package-info";
@@ -13,7 +14,7 @@ export class PackageInfo {
1314
domElement.setAttribute("class", "slide-out");
1415
}
1516

16-
window.dispatchEvent(new CustomEvent("package-info-closed", { detail: null }));
17+
window.dispatchEvent(new CustomEvent(EVENTS.PACKAGE_INFO_CLOSED, { detail: null }));
1718
}
1819

1920
/**

public/components/views/settings/settings.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { warnings } from "@nodesecure/js-x-ray/warnings";
44

55
// Import Internal Dependencies
66
import * as utils from "../../../common/utils.js";
7+
import { EVENTS } from "../../../core/events.js";
78

89
// CONSTANTS
910
const kAllowedHotKeys = new Set([
@@ -231,7 +232,7 @@ export class Settings {
231232
this.config = newConfig;
232233
this.saveButton.classList.add("disabled");
233234

234-
window.dispatchEvent(new CustomEvent("settings-saved", { detail: this.config }));
235+
window.dispatchEvent(new CustomEvent(EVENTS.SETTINGS_SAVED, { detail: this.config }));
235236
}
236237

237238
updateSettings() {

public/core/events.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Constants
2+
export const EVENTS = {
3+
LOCKED: "locked",
4+
UNLOCKED: "unlocked",
5+
PACKAGE_INFO_CLOSED: "package-info-closed",
6+
SETTINGS_SAVED: "settings-saved",
7+
MOVED_TO_NEXT_LOCKED_NODE: "moved-to-next-locked-node",
8+
MOVED_TO_PREVIOUS_LOCKED_NODE: "moved-to-previous-locked-node"
9+
};

public/core/network-navigation.js

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Import Internal Dependencies
22
import { PackageInfo } from "../components/package/package.js";
3+
import { EVENTS } from "./events.js";
34

45
export class NetworkNavigation {
56
/**
@@ -118,6 +119,13 @@ export class NetworkNavigation {
118119

119120
this.#dependenciesMapByLevel.set(0, this.rootNodeParams);
120121

122+
window.addEventListener(EVENTS.MOVED_TO_NEXT_LOCKED_NODE, () => {
123+
this.#moveToNextLockedNode();
124+
});
125+
window.addEventListener(EVENTS.MOVED_TO_PREVIOUS_LOCKED_NODE, () => {
126+
this.#moveToPreviousLockedNode();
127+
});
128+
121129
document.addEventListener("keydown", (event) => {
122130
const isNetworkViewHidden = document.getElementById("network--view").classList.contains("hidden");
123131
const isWikiOpen = document.getElementById("documentation-root-element").classList.contains("slide-in");
@@ -147,22 +155,9 @@ export class NetworkNavigation {
147155

148156
const nodeParam = this.#currentNodeParams ?? this.rootNodeParams;
149157

150-
if (this.#nsn.lastHighlightedIds === null) {
151-
this.#lockedNodes = [];
152-
}
153-
else {
154-
this.#lockedNodes = this.#sortByAngle(
155-
[...this.#nsn.lastHighlightedIds].map(
156-
(id) => [id, {
157-
...this.#secureDataSet.linker.get(id),
158-
position: nsn.network.getPosition(id)
159-
}]
160-
),
161-
{ ...nsn.network.getPosition(this.rootNodeParams.nodes[0]) }
162-
);
163-
}
158+
this.#updateLockedNodes();
164159

165-
if (this.#lockedNodes.length > 0) {
160+
if (this.#hasLockedNodes()) {
166161
this.#navigateBetweenLockedNodes(event);
167162

168163
return;
@@ -361,32 +356,81 @@ export class NetworkNavigation {
361356
this.#navigateTreeLevel(nearthestNode);
362357
}
363358

359+
#updateLockedNodes() {
360+
if (this.#nsn.lastHighlightedIds === null) {
361+
this.#lockedNodes = [];
362+
}
363+
else {
364+
this.#lockedNodes = this.#sortByAngle(
365+
[...this.#nsn.lastHighlightedIds].map(
366+
(id) => [id, {
367+
...this.#secureDataSet.linker.get(id),
368+
position: this.#nsn.network.getPosition(id)
369+
}]
370+
),
371+
{ ...this.#nsn.network.getPosition(this.rootNodeParams.nodes[0]) }
372+
);
373+
}
374+
}
375+
376+
#moveToPreviousLockedNode() {
377+
this.#updateLockedNodes();
378+
if (this.#hasLockedNodes()) {
379+
this.#selectPreviousLockedNode();
380+
this.#focusOnActiveLockedNode();
381+
}
382+
}
383+
384+
#moveToNextLockedNode() {
385+
this.#updateLockedNodes();
386+
if (this.#hasLockedNodes()) {
387+
this.#selectNextLockedNode();
388+
this.#focusOnActiveLockedNode();
389+
}
390+
}
391+
364392
#navigateBetweenLockedNodes(event) {
365393
switch (event.code) {
366394
case "ArrowLeft":
367-
if (this.#lockedNodesActiveIndex === 0) {
368-
this.#lockedNodesActiveIndex = this.#lockedNodes.length - 1;
369-
}
370-
else {
371-
this.#lockedNodesActiveIndex--;
372-
}
395+
this.#selectPreviousLockedNode();
373396
break;
374397
case "ArrowRight":
375-
if (this.#lockedNodesActiveIndex === this.#lockedNodes.length - 1) {
376-
this.#lockedNodesActiveIndex = 0;
377-
}
378-
else {
379-
this.#lockedNodesActiveIndex++;
380-
}
398+
this.#selectNextLockedNode();
381399
break;
382400
default:
383401
return;
384402
}
385403

404+
this.#focusOnActiveLockedNode();
405+
}
406+
407+
#selectPreviousLockedNode() {
408+
if (this.#lockedNodesActiveIndex === 0) {
409+
this.#lockedNodesActiveIndex = this.#lockedNodes.length - 1;
410+
}
411+
else {
412+
this.#lockedNodesActiveIndex--;
413+
}
414+
}
415+
416+
#selectNextLockedNode() {
417+
if (this.#lockedNodesActiveIndex === this.#lockedNodes.length - 1) {
418+
this.#lockedNodesActiveIndex = 0;
419+
}
420+
else {
421+
this.#lockedNodesActiveIndex++;
422+
}
423+
}
424+
425+
#focusOnActiveLockedNode() {
386426
this.#nsn.network.focus(this.#lockedNodes[this.#lockedNodesActiveIndex][0], {
387427
animation: true,
388428
scale: 0.35,
389429
offset: { x: 150, y: 0 }
390430
});
391431
}
432+
433+
#hasLockedNodes() {
434+
return this.#lockedNodes.length > 0;
435+
}
392436
}

public/main.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import { Wiki } from "./components/wiki/wiki.js";
88
import { Popup } from "./components/popup/popup.js";
99
import { Locker } from "./components/locker/locker.js";
1010
import "./components/legend/legend.js";
11+
import "./components/locked-navigation/locked-navigation.js";
1112
import { Settings } from "./components/views/settings/settings.js";
1213
import { HomeView } from "./components/views/home/home.js";
1314
import { SearchView } from "./components/views/search/search.js";
1415
import { NetworkNavigation } from "./core/network-navigation.js";
1516
import { i18n } from "./core/i18n.js";
1617
import { initSearchNav } from "./core/search-nav.js";
1718
import * as utils from "./common/utils.js";
19+
import { EVENTS } from "./core/events.js";
1820

1921
let secureDataSet;
2022
let nsn;
@@ -131,7 +133,7 @@ async function init(options = {}) {
131133
homeView ??= new HomeView(secureDataSet, nsn);
132134
searchview ??= new SearchView(secureDataSet, nsn);
133135

134-
window.addEventListener("package-info-closed", () => {
136+
window.addEventListener(EVENTS.PACKAGE_INFO_CLOSED, () => {
135137
window.networkNav.currentNodeParams = null;
136138
packageInfoOpened = false;
137139
});
@@ -261,7 +263,7 @@ function onSettingsSaved(defaultConfig = null) {
261263
updateSettings(defaultConfig);
262264
}
263265

264-
window.addEventListener("settings-saved", async(event) => {
266+
window.addEventListener(EVENTS.SETTINGS_SAVED, async(event) => {
265267
updateSettings(event.detail);
266268
});
267269
}

views/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@
8484
<i class="icon-lock-open"></i>
8585
<p>[[=z.token('network.unlocked')]]</p>
8686
</div>
87-
<nsecure-legend id="legend">
87+
<nsecure-legend id="legend"></nsecure-legend>
88+
<locked-navigation nextLabel="[[=z.token('lockedNavigation.next')]]" prevLabel="[[=z.token('lockedNavigation.prev')]]">
89+
</locked-navigation>
8890
</div>
8991
</div>
9092
<div id="search--view" class="view hidden">

0 commit comments

Comments
 (0)