Files
SkyArtShop/website/assets/js/notifications.js

225 lines
5.4 KiB
JavaScript
Raw Permalink Normal View History

2026-01-01 22:24:30 -06:00
/**
* Notification System
* Accessible toast notifications
*/
(function () {
"use strict";
class NotificationManager {
constructor() {
this.container = null;
this.notifications = new Map();
this.init();
}
init() {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () =>
this.createContainer()
);
} else {
this.createContainer();
}
}
createContainer() {
if (!document.body || this.container) return;
this.container = document.createElement("div");
this.container.id = "notification-container";
this.container.setAttribute("aria-live", "polite");
this.container.setAttribute("aria-atomic", "true");
this.container.className = "notification-container";
const style = document.createElement("style");
style.textContent = `
.notification-container {
position: fixed;
top: 80px;
right: 20px;
z-index: 10000;
display: flex;
flex-direction: column;
gap: 10px;
max-width: 400px;
pointer-events: none;
}
.notification {
padding: 12px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
display: flex;
align-items: center;
gap: 12px;
color: white;
font-size: 14px;
font-weight: 500;
pointer-events: auto;
animation: slideInRight 0.3s ease;
min-width: 250px;
}
.notification.removing {
animation: slideOutRight 0.3s ease;
}
.notification-success {
background: #10b981;
}
.notification-error {
background: #ef4444;
}
.notification-info {
background: #3b82f6;
}
.notification-warning {
background: #f59e0b;
}
.notification-icon {
font-size: 18px;
flex-shrink: 0;
}
.notification-message {
flex: 1;
}
.notification-close {
background: transparent;
border: none;
color: white;
cursor: pointer;
padding: 4px;
opacity: 0.8;
transition: opacity 0.2s;
}
.notification-close:hover {
opacity: 1;
}
@keyframes slideInRight {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOutRight {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(400px);
opacity: 0;
}
}
@media (max-width: 640px) {
.notification-container {
right: 10px;
left: 10px;
max-width: none;
}
.notification {
min-width: auto;
}
}
`;
document.head.appendChild(style);
document.body.appendChild(this.container);
}
show(message, type = "info", duration = 3000) {
if (!this.container) this.createContainer();
if (!this.container) return;
const id = Date.now() + Math.random();
const notification = document.createElement("div");
notification.className = `notification notification-${type}`;
notification.setAttribute("role", "alert");
const icons = {
success: "✓",
error: "✕",
info: "",
warning: "⚠",
};
notification.innerHTML = `
<span class="notification-icon">${icons[type] || icons.info}</span>
<span class="notification-message">${this.escapeHtml(message)}</span>
<button class="notification-close" aria-label="Close notification">×</button>
`;
const closeBtn = notification.querySelector(".notification-close");
closeBtn.addEventListener("click", () => this.remove(id));
this.container.appendChild(notification);
this.notifications.set(id, notification);
if (duration > 0) {
setTimeout(() => this.remove(id), duration);
}
return id;
}
remove(id) {
const notification = this.notifications.get(id);
if (!notification) return;
notification.classList.add("removing");
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
this.notifications.delete(id);
}, 300);
}
escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
success(message, duration) {
return this.show(message, "success", duration);
}
error(message, duration) {
return this.show(message, "error", duration);
}
info(message, duration) {
return this.show(message, "info", duration);
}
warning(message, duration) {
return this.show(message, "warning", duration);
}
}
// Create global instance
window.Notifications = window.Notifications || new NotificationManager();
// Legacy compatibility
window.showNotification = function (message, type = "info") {
window.Notifications.show(message, type);
};
})();