Skip to content

Commit 84492c3

Browse files
authored
Refactor/index.ts (#160)
Refactored index.ts and introduced handler classes - Reorganized `index.ts` for better readability and maintainability - Created dedicated handler classes: `UndoRedoHandler`, `PauseHandler`, `LayerHandler`, `ResponsiveHandler`, `SpeedControlHandler`, and `ShortcutsManager` - Modularized event listeners and UI logic into separate handler files
1 parent 4e71aae commit 84492c3

File tree

11 files changed

+477
-311
lines changed

11 files changed

+477
-311
lines changed

src/config.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { GlobalContext } from "./context";
2+
import { deselectElement } from "./types/viewportManager";
23
import { Colors } from "./utils";
34

45
export class ConfigModal {
@@ -23,6 +24,7 @@ export class ConfigModal {
2324

2425
this.createModal();
2526
this.setupEventListeners();
27+
this.setUpShortCuts();
2628
}
2729

2830
private createModal() {
@@ -153,6 +155,28 @@ export class ConfigModal {
153155
};
154156
}
155157

158+
private setUpShortCuts() {
159+
document.addEventListener("keydown", (event) => {
160+
const activeElement = document.activeElement as HTMLElement;
161+
if (
162+
activeElement &&
163+
(activeElement instanceof HTMLInputElement ||
164+
activeElement instanceof HTMLTextAreaElement ||
165+
activeElement.isContentEditable)
166+
) {
167+
return;
168+
}
169+
if (event.key.toLowerCase() === "h") {
170+
event.preventDefault();
171+
deselectElement();
172+
this.open();
173+
} else if (event.key === "Escape") {
174+
event.preventDefault();
175+
this.close();
176+
}
177+
});
178+
}
179+
156180
public open() {
157181
if (this.modalOverlay && this.modalContent) {
158182
// Reset tempColor to the last saved color when opening
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { GlobalContext } from "../context";
2+
import { LeftBar } from "../graphics/left_bar";
3+
import { layerToName } from "../types/devices/layer";
4+
import { deselectElement, saveToLocalStorage } from "../types/viewportManager";
5+
6+
export class LayerHandler {
7+
private ctx: GlobalContext;
8+
private leftBar: LeftBar;
9+
private layerSelect: HTMLSelectElement | null;
10+
11+
constructor(ctx: GlobalContext, leftBar: LeftBar) {
12+
this.ctx = ctx;
13+
this.leftBar = leftBar;
14+
this.layerSelect = document.getElementById(
15+
"layer-select",
16+
) as HTMLSelectElement;
17+
18+
if (this.layerSelect) {
19+
this.layerSelect.value = layerToName(this.ctx.getCurrentLayer());
20+
this.layerSelect.onchange = (event) => this.selectNewLayer(event);
21+
this.layerSelect.addEventListener("layerChanged", () =>
22+
this.updateLayer(),
23+
);
24+
}
25+
26+
this.updateLayer();
27+
}
28+
29+
private selectNewLayer(event: Event) {
30+
const selectedLayer = (event.target as HTMLSelectElement).value;
31+
console.log(`Layer selected: ${selectedLayer}`);
32+
33+
if (selectedLayer) {
34+
this.ctx.changeViewGraph(selectedLayer);
35+
saveToLocalStorage(this.ctx);
36+
this.leftBar.setButtonsByLayer(selectedLayer);
37+
deselectElement();
38+
}
39+
}
40+
41+
private updateLayer() {
42+
const currLayer = layerToName(this.ctx.getCurrentLayer());
43+
if (this.layerSelect) {
44+
this.layerSelect.value = currLayer;
45+
}
46+
this.leftBar.setButtonsByLayer(currLayer);
47+
}
48+
}

src/handlers/pauseHandler.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import PlaySvg from "../assets/play-icon.svg";
2+
import PauseSvg from "../assets/pause-icon.svg";
3+
import { Packet } from "../types/packet";
4+
5+
export class PauseHandler {
6+
private pauseButton: HTMLButtonElement | null;
7+
private pauseIcon: HTMLImageElement;
8+
private isPaused: boolean;
9+
10+
constructor() {
11+
this.pauseButton = document.getElementById(
12+
"pause-button",
13+
) as HTMLButtonElement;
14+
this.pauseIcon = document.createElement("img");
15+
this.isPaused = false;
16+
17+
if (this.pauseButton) {
18+
this.setupButton();
19+
}
20+
this.setupShortcut();
21+
}
22+
23+
private setupButton() {
24+
if (this.pauseButton) {
25+
this.pauseIcon.src = PauseSvg;
26+
this.pauseIcon.alt = "Pause Icon";
27+
this.pauseButton.appendChild(this.pauseIcon);
28+
this.pauseButton.onclick = () => this.togglePause();
29+
}
30+
}
31+
32+
private setupShortcut() {
33+
document.addEventListener("keydown", (event) => {
34+
const activeElement = document.activeElement as HTMLElement;
35+
if (
36+
activeElement &&
37+
(activeElement instanceof HTMLInputElement ||
38+
activeElement instanceof HTMLTextAreaElement ||
39+
activeElement.isContentEditable)
40+
) {
41+
return;
42+
}
43+
if (event.code === "Space") {
44+
event.preventDefault();
45+
this.togglePause();
46+
}
47+
});
48+
}
49+
50+
private togglePause() {
51+
this.isPaused = !this.isPaused;
52+
if (this.pauseButton) {
53+
this.pauseButton.title = this.isPaused ? "Resume" : "Pause";
54+
this.pauseButton.classList.toggle("paused", this.isPaused);
55+
}
56+
this.pauseIcon.src = this.isPaused ? PlaySvg : PauseSvg;
57+
58+
// Handle animation pause/unpause
59+
if (this.isPaused) {
60+
Packet.pauseAnimation();
61+
} else {
62+
Packet.unpauseAnimation();
63+
}
64+
}
65+
}

src/handlers/responsiveHandler.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Application } from "pixi.js";
2+
import { Viewport } from "../graphics/viewport";
3+
4+
export class ResponsiveHandler {
5+
private app: Application;
6+
private viewport: Viewport;
7+
private lBar: HTMLElement | null;
8+
private rBar: HTMLElement | null;
9+
private tBar: HTMLElement | null;
10+
11+
constructor(app: Application, viewport: Viewport) {
12+
this.app = app;
13+
this.viewport = viewport;
14+
this.lBar = document.getElementById("left-bar");
15+
this.rBar = document.getElementById("right-bar");
16+
this.tBar = document.getElementById("top-bar");
17+
18+
this.resize();
19+
window.addEventListener("resize", () => this.resize());
20+
this.setupCanvasWrapper();
21+
}
22+
23+
private resize() {
24+
// Check if the layout should be stacked (based on window width)
25+
const isStacked = window.innerWidth <= 768;
26+
27+
// Determine the size of the left bar (width if not stacked, height if stacked)
28+
const leftSize = isStacked
29+
? this.lBar?.offsetHeight || 0
30+
: this.lBar?.offsetWidth || 0;
31+
32+
// Determine the size of the right bar (width if not stacked, height if stacked)
33+
const rightSize = isStacked
34+
? this.rBar?.offsetHeight || 0
35+
: this.rBar?.offsetWidth || 0;
36+
37+
// Get the height of the top bar
38+
const topHeight = this.tBar?.offsetHeight || 0;
39+
40+
// Calculate the new width and height for the canvas
41+
let newWidth = window.innerWidth - (isStacked ? 0 : leftSize + rightSize);
42+
let newHeight =
43+
window.innerHeight - (isStacked ? leftSize + rightSize : topHeight);
44+
45+
// Ensure minimum dimensions to prevent the canvas from becoming too small
46+
newWidth = Math.max(300, newWidth);
47+
newHeight = Math.max(200, newHeight);
48+
49+
// Log the new dimensions for debugging
50+
console.log("📏 Resizing canvas to:", newWidth, "x", newHeight);
51+
52+
// Resize the app renderer and viewport accordingly
53+
this.app.renderer.resize(newWidth, newHeight);
54+
this.viewport.resize(newWidth, newHeight);
55+
}
56+
57+
private setupCanvasWrapper() {
58+
// Get the element with the ID "canvas-wrapper"
59+
const canvasWrapper = document.getElementById("canvas-wrapper");
60+
61+
// Check if the element exists before adding event listeners
62+
if (canvasWrapper) {
63+
// When the mouse enters the canvas wrapper, prevent scrolling
64+
canvasWrapper.addEventListener("mouseenter", () => {
65+
document.body.classList.add("no-scroll");
66+
});
67+
68+
// When the mouse leaves the canvas wrapper, allow scrolling again
69+
canvasWrapper.addEventListener("mouseleave", () => {
70+
document.body.classList.remove("no-scroll");
71+
});
72+
}
73+
}
74+
}

src/handlers/shortcuts.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { GlobalContext } from "../context";
2+
import { Application } from "pixi.js";
3+
import { triggerNew, triggerSave, triggerLoad, triggerPrint } from "./triggers";
4+
5+
export class ShortcutsManager {
6+
private ctx: GlobalContext;
7+
private app: Application;
8+
9+
constructor(ctx: GlobalContext, app: Application) {
10+
this.ctx = ctx;
11+
this.app = app;
12+
this.init();
13+
}
14+
15+
private init() {
16+
document.addEventListener("keydown", this.handleKeydown.bind(this));
17+
}
18+
19+
private handleKeydown(event: KeyboardEvent) {
20+
// Prevent shortcuts from executing while typing in an input or textarea
21+
const activeElement = document.activeElement as HTMLElement;
22+
if (
23+
activeElement &&
24+
(activeElement instanceof HTMLInputElement ||
25+
activeElement instanceof HTMLTextAreaElement ||
26+
activeElement.isContentEditable)
27+
) {
28+
return;
29+
}
30+
31+
switch (event.key.toLowerCase()) {
32+
case "n":
33+
event.preventDefault();
34+
triggerNew(this.ctx);
35+
break;
36+
case "s":
37+
event.preventDefault();
38+
triggerSave(this.ctx);
39+
break;
40+
case "l":
41+
event.preventDefault();
42+
triggerLoad(this.ctx);
43+
break;
44+
case "p":
45+
event.preventDefault();
46+
triggerPrint(this.app, this.ctx);
47+
break;
48+
}
49+
}
50+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { GlobalContext } from "../context";
2+
3+
export class SpeedControlHandler {
4+
private ctx: GlobalContext; // Adjust the type based on GlobalContext
5+
private speedWheel: HTMLInputElement | null;
6+
private valueDisplay: HTMLElement | null;
7+
8+
constructor(ctx: GlobalContext) {
9+
this.ctx = ctx;
10+
this.speedWheel = document.getElementById(
11+
"speed-wheel",
12+
) as HTMLInputElement;
13+
this.valueDisplay = document.querySelector(".value-display");
14+
15+
if (this.speedWheel && this.valueDisplay) {
16+
this.updateSpeedWheel(this.ctx.getCurrentSpeed().value);
17+
this.speedWheel.addEventListener("input", (event) =>
18+
this.handleSpeedChange(event),
19+
);
20+
}
21+
}
22+
23+
private updateSpeedWheel(value: number) {
24+
if (this.speedWheel && this.valueDisplay) {
25+
this.speedWheel.value = value.toString();
26+
this.valueDisplay.textContent = `${value}x`;
27+
}
28+
}
29+
30+
private handleSpeedChange(event: Event) {
31+
const value = parseFloat((event.target as HTMLInputElement).value);
32+
if (this.valueDisplay) {
33+
this.valueDisplay.textContent = `${value}x`;
34+
}
35+
this.ctx.changeSpeedMultiplier(value);
36+
}
37+
}

src/handlers/triggers.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { GlobalContext } from "../context";
2+
import { Application } from "pixi.js";
3+
import {
4+
deselectElement,
5+
saveToFile,
6+
loadFromFile,
7+
} from "../types/viewportManager";
8+
import { captureAndDownloadViewport } from "../utils";
9+
import { ConfigModal } from "../config";
10+
import { DataGraph } from "../types/graphs/datagraph";
11+
12+
// Function to create a new network
13+
export const triggerNew = (ctx: GlobalContext) => {
14+
deselectElement(); // Deselect any currently selected element
15+
ctx.load(new DataGraph()); // Load a new empty DataGraph into the context
16+
};
17+
18+
// Function to save the network
19+
export const triggerSave = (ctx: GlobalContext) => {
20+
deselectElement(); // Deselect any currently selected element
21+
saveToFile(ctx); // Save the current network to a file
22+
};
23+
24+
// Function to load a network from a file
25+
export const triggerLoad = (ctx: GlobalContext) => {
26+
deselectElement(); // Deselect any currently selected element
27+
loadFromFile(ctx); // Load a network from a file into the context
28+
};
29+
30+
// Function to print the network
31+
export const triggerPrint = (app: Application, ctx: GlobalContext) => {
32+
captureAndDownloadViewport(app, ctx.getViewport());
33+
ctx.print(); // Print the current network
34+
};
35+
36+
// Function to open the help modal
37+
export const triggerHelp = (configModal: ConfigModal) => {
38+
deselectElement(); // Deselect any currently selected element
39+
configModal.open(); // Open the configuration/help modal
40+
};

0 commit comments

Comments
 (0)