1import type { NotificationOptions } from "../types/app.js";
2
3interface TimerElement extends HTMLElement {
4 timeout?: ReturnType<typeof setTimeout>;
5}
6
7export class NotificationService {
8 private zoomNotification: HTMLElement | null = null;
9 private generalNotification: HTMLElement | null = null;
10
11 showZoom(message: string, duration = 2000): void {
12 // Create or update zoom notification
13 if (!this.zoomNotification) {
14 this.zoomNotification = this.createNotificationElement(
15 "zoom-notification",
16 {
17 top: "20px",
18 right: "20px",
19 background: "rgba(0, 0, 0, 0.8)",
20 color: "white",
21 },
22 );
23 }
24
25 this.updateNotification(this.zoomNotification, message, duration);
26 }
27
28 show(
29 message: string,
30 type: NotificationOptions["type"] = "info",
31 duration = 3000,
32 ): void {
33 // Create or update general notification
34 if (!this.generalNotification) {
35 this.generalNotification = this.createNotificationElement(
36 "general-notification",
37 {
38 top: "60px",
39 right: "20px",
40 },
41 );
42 }
43
44 // Set colors based on type
45 const colors = this.getNotificationColors(type);
46 this.generalNotification.style.background = colors.background;
47 this.generalNotification.style.color = colors.color;
48
49 this.updateNotification(this.generalNotification, message, duration);
50 }
51
52 private createNotificationElement(
53 id: string,
54 baseStyles: Record<string, string>,
55 ): HTMLElement {
56 const notification = document.createElement("div");
57 notification.id = id;
58
59 const styles = {
60 position: "fixed",
61 padding: "10px 15px",
62 borderRadius: "5px",
63 fontSize: "14px",
64 zIndex: "10000",
65 transition: "opacity 0.3s",
66 opacity: "0",
67 ...baseStyles,
68 };
69
70 notification.style.cssText = Object.entries(styles)
71 .map(([key, value]) => `${this.camelToKebab(key)}: ${value}`)
72 .join("; ");
73
74 document.body.appendChild(notification);
75 return notification;
76 }
77
78 private updateNotification(
79 element: HTMLElement,
80 message: string,
81 duration: number,
82 ): void {
83 element.textContent = message;
84 element.style.opacity = "1";
85
86 // Clear any existing timeout
87 const timerElement = element as TimerElement;
88 if (timerElement.timeout) {
89 clearTimeout(timerElement.timeout);
90 }
91
92 // Hide after duration
93 timerElement.timeout = setTimeout(() => {
94 element.style.opacity = "0";
95 }, duration);
96 }
97
98 private getNotificationColors(type: NotificationOptions["type"]): {
99 background: string;
100 color: string;
101 } {
102 switch (type) {
103 case "error":
104 return { background: "rgba(220, 53, 69, 0.9)", color: "white" };
105 case "success":
106 return { background: "rgba(25, 135, 84, 0.9)", color: "white" };
107 default:
108 return { background: "rgba(13, 110, 253, 0.9)", color: "white" };
109 }
110 }
111
112 private camelToKebab(str: string): string {
113 return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
114 }
115}