289 lines
6.8 KiB
JavaScript
289 lines
6.8 KiB
JavaScript
window.addEventListener("DOMContentLoaded",() => {
|
||
const nc = new NotificationCenter();
|
||
});
|
||
|
||
class NotificationCenter {
|
||
constructor() {
|
||
this.items = [];
|
||
this.itemsToKill = [];
|
||
this.messages = NotificationMessages();
|
||
this.killTimeout = null;
|
||
|
||
this.spawnNotes(3);
|
||
}
|
||
spawnNote() {
|
||
const id = this.random(0,2**32,true).toString(16);
|
||
const draw = this.random(0,this.messages.length - 1,true);
|
||
const message = this.messages[draw];
|
||
const note = new Notification({
|
||
id: `note-${id}`,
|
||
icon: message.icon,
|
||
title: message.title,
|
||
subtitle: message.subtitle,
|
||
actions: message.actions
|
||
});
|
||
const transY = 100 * this.items.length;
|
||
|
||
note.el.style.transform = `translateY(${transY}%)`;
|
||
note.el.addEventListener("click",this.killNote.bind(this,note.id));
|
||
|
||
this.items.push(note);
|
||
}
|
||
spawnNotes(amount) {
|
||
let count = typeof amount === "number" ? amount : this.random(1,5,true);
|
||
|
||
while (count--)
|
||
this.spawnNote();
|
||
}
|
||
killNote(id,e) {
|
||
const note = this.items.find(item => item.id === id);
|
||
const tar = e.target;
|
||
|
||
if (note && tar.getAttribute("data-dismiss") === id) {
|
||
note.el.classList.add("notification--out");
|
||
this.itemsToKill.push(note);
|
||
|
||
clearTimeout(this.killTimeout);
|
||
|
||
this.killTimeout = setTimeout(() => {
|
||
this.itemsToKill.forEach(itemToKill => {
|
||
document.body.removeChild(itemToKill.el);
|
||
|
||
const left = this.items.filter(item => item.id !== itemToKill.id);
|
||
this.items = [...left];
|
||
});
|
||
|
||
this.itemsToKill = [];
|
||
|
||
if (!this.items.length)
|
||
this.spawnNotes();
|
||
else
|
||
this.spawnNotes(this.random(0,1,true));
|
||
|
||
this.shiftNotes();
|
||
},note.killTime);
|
||
}
|
||
}
|
||
shiftNotes() {
|
||
this.items.forEach((item,i) => {
|
||
const transY = 100 * i;
|
||
item.el.style.transform = `translateY(${transY}%)`;
|
||
});
|
||
}
|
||
random(min,max,round = false) {
|
||
const percent = crypto.getRandomValues(new Uint32Array(1))[0] / 2**32;
|
||
const relativeValue = (max - min) * percent;
|
||
|
||
return min + (round === true ? Math.round(relativeValue) : +relativeValue.toFixed(2));
|
||
}
|
||
}
|
||
|
||
class Notification {
|
||
constructor(args) {
|
||
this.args = args;
|
||
this.el = null;
|
||
this.id = null;
|
||
this.killTime = 300;
|
||
this.init(args);
|
||
}
|
||
init(args) {
|
||
const {id,icon,title,subtitle,actions} = args;
|
||
const block = "notification";
|
||
const parent = document.body;
|
||
const xmlnsSVG = "http://www.w3.org/2000/svg";
|
||
const xmlnsUse = "http://www.w3.org/1999/xlink";
|
||
|
||
const note = this.newEl("div");
|
||
note.id = id;
|
||
note.className = block;
|
||
parent.insertBefore(note,parent.lastElementChild);
|
||
|
||
const box = this.newEl("div");
|
||
box.className = `${block}__box`;
|
||
note.appendChild(box);
|
||
|
||
const content = this.newEl("div");
|
||
content.className = `${block}__content`;
|
||
box.appendChild(content);
|
||
|
||
const _icon = this.newEl("div");
|
||
_icon.className = `${block}__icon`;
|
||
content.appendChild(_icon);
|
||
|
||
const iconSVG = this.newEl("svg",xmlnsSVG);
|
||
iconSVG.setAttribute("class",`${block}__icon-svg`);
|
||
iconSVG.setAttribute("role","img");
|
||
iconSVG.setAttribute("aria-label",icon);
|
||
iconSVG.setAttribute("width","32px");
|
||
iconSVG.setAttribute("height","32px");
|
||
_icon.appendChild(iconSVG);
|
||
|
||
const iconUse = this.newEl("use",xmlnsSVG);
|
||
iconUse.setAttributeNS(xmlnsUse,"href",`#${icon}`);
|
||
iconSVG.appendChild(iconUse);
|
||
|
||
const text = this.newEl("div");
|
||
text.className = `${block}__text`;
|
||
content.appendChild(text);
|
||
|
||
const _title = this.newEl("div");
|
||
_title.className = `${block}__text-title`;
|
||
_title.textContent = title;
|
||
text.appendChild(_title);
|
||
|
||
if (subtitle) {
|
||
const _subtitle = this.newEl("div");
|
||
_subtitle.className = `${block}__text-subtitle`;
|
||
_subtitle.textContent = subtitle;
|
||
text.appendChild(_subtitle);
|
||
}
|
||
|
||
const btns = this.newEl("div");
|
||
btns.className = `${block}__btns`;
|
||
box.appendChild(btns);
|
||
|
||
actions.forEach(action => {
|
||
const btn = this.newEl("button");
|
||
btn.className = `${block}__btn`;
|
||
btn.type = "button";
|
||
btn.setAttribute("data-dismiss",id);
|
||
|
||
const btnText = this.newEl("span");
|
||
btnText.className = `${block}__btn-text`;
|
||
btnText.textContent = action;
|
||
|
||
btn.appendChild(btnText);
|
||
btns.appendChild(btn);
|
||
});
|
||
|
||
this.el = note;
|
||
this.id = note.id;
|
||
}
|
||
newEl(elName,NSValue) {
|
||
if (NSValue)
|
||
return document.createElementNS(NSValue,elName);
|
||
else
|
||
return document.createElement(elName);
|
||
}
|
||
}
|
||
|
||
function NotificationMessages() {
|
||
return [
|
||
{
|
||
icon: "error",
|
||
title: "Oh No",
|
||
subtitle: "Something really bad happened.",
|
||
actions: ["Close"]
|
||
},
|
||
{
|
||
icon: "error",
|
||
title: "Error",
|
||
subtitle: "The operation completed successfully.",
|
||
actions: ["OK"]
|
||
},
|
||
{
|
||
icon: "error",
|
||
title: "Critical Error",
|
||
subtitle: "An error has occurred while trying to display an error notification.",
|
||
actions: ["OK"]
|
||
},
|
||
{
|
||
icon: "warning",
|
||
title: "Reminder",
|
||
subtitle: "You will receive more notifications.",
|
||
actions: ["Close"]
|
||
},
|
||
{
|
||
icon: "warning",
|
||
title: "Failed to Save Changes",
|
||
actions: ["Retry","Cancel"]
|
||
},
|
||
{
|
||
icon: "warning",
|
||
title: "Download Failed",
|
||
actions: ["Retry","Cancel"]
|
||
},
|
||
{
|
||
icon: "warning",
|
||
title: "Cannot Send Mail",
|
||
subtitle: "The message was rejected by the server because it is too large.",
|
||
actions: ["OK"]
|
||
},
|
||
{
|
||
icon: "warning",
|
||
title: "Disk Not Ejected Properly",
|
||
subtitle: "Eject “CopyThisFloppy” before disconnecting or turning it off.",
|
||
actions: ["Close"]
|
||
},
|
||
{
|
||
icon: "warning",
|
||
title: "Notifications",
|
||
subtitle: "Notifications may include alerts, sounds, and icon badges.",
|
||
actions: ["Don’t Allow","Allow"]
|
||
},
|
||
{
|
||
icon: "success",
|
||
title: "Changes Saved",
|
||
actions: ["OK"]
|
||
},
|
||
{
|
||
icon: "success",
|
||
title: "Download Complete",
|
||
actions: ["OK"]
|
||
},
|
||
{
|
||
icon: "success",
|
||
title: "Yippee",
|
||
subtitle: "Nothing bad happened.",
|
||
actions: ["OK"]
|
||
},
|
||
{
|
||
icon: "message",
|
||
title: "Mail Password Required",
|
||
subtitle: "Enter your password for user@domain.com.",
|
||
actions: ["Close","Continue"]
|
||
},
|
||
{
|
||
icon: "message",
|
||
title: "Mail",
|
||
subtitle: "You have 10 new messages.",
|
||
actions: ["Read","Dismiss"]
|
||
},
|
||
{
|
||
icon: "clock",
|
||
title: "Coffee Break",
|
||
subtitle: "In 5 minutes",
|
||
actions: ["Close","Snooze"]
|
||
},
|
||
{
|
||
icon: "clock",
|
||
title: "Muffin Time",
|
||
subtitle: "12:30 PM",
|
||
actions: ["Close","Snooze"]
|
||
},
|
||
{
|
||
icon: "clock",
|
||
title: "Hammer Time",
|
||
subtitle: "In 2 minutes",
|
||
actions: ["Close","Snooze"]
|
||
},
|
||
{
|
||
icon: "up",
|
||
title: "Upgrade Available",
|
||
subtitle: "Enjoy the latest technologies and refinements to your favorite apps.",
|
||
actions: ["Install","Details"]
|
||
},
|
||
{
|
||
icon: "up",
|
||
title: "Upgrade Waiting",
|
||
subtitle: "Get it now, or it won’t be long until you are far behind everyone else.",
|
||
actions: ["Install","Details"]
|
||
},
|
||
{
|
||
icon: "up",
|
||
title: "Upgrade Now",
|
||
subtitle: "The current version will soon be obsolete. What are you waiting for?",
|
||
actions: ["Install","Details"]
|
||
}
|
||
];
|
||
} |