UNPKG

@unaxiom/unnotify

Version:

Pure JS growlish notifications with zero dependencies, simple API written in TypeScript.

442 lines (380 loc) 18.2 kB
let notificationCenter: HTMLDivElement; const notificationCenterClassName = "unnotify-center"; const eachNotificationClassName = "unnotify-panel"; const notificationButtonClassName = "unnotify-button"; const defaultTimeout = 5000; /**Creates and appends the stylesheet to the document */ function __unnotifyCreateStyleSheet(localNotificationCenterClassName: string, localEachNotificationClassName: string, notificationButtonClassName: string, side: string, clickable: boolean) { if (side === undefined || side === null) { side = "right"; } else if (side === 'left') { side = 'left'; } else { side = 'right'; } let notificationCenterStyle = document.createElement("style"); notificationCenterStyle.type = "text/css"; notificationCenterStyle.innerHTML = __unnotifyReturnClasses(localNotificationCenterClassName, localEachNotificationClassName, notificationButtonClassName, side, clickable) document.getElementsByTagName('head')[0].appendChild(notificationCenterStyle); // Create the notification center notificationCenter = document.createElement("div"); notificationCenter.classList.add(localNotificationCenterClassName); // document.body.appendChild(notificationCenter); document.body.insertBefore(notificationCenter, document.body.firstChild); } /**Returns the required classes */ function __unnotifyReturnClasses(localNotificationCenterClassName: string, localEachNotificationClassName: string, localNotificationButtonClassName: string, side: string, clickable: boolean): string { let pointerEvents = "initial"; if (!clickable) { pointerEvents = "none"; } return ` .${localNotificationCenterClassName} { position: absolute; top: 20px; z-index: 25000; overflow-y: auto; overflow-x: hidden; pointer-events: ${pointerEvents}; } .${localEachNotificationClassName} { padding: 10px; margin: 10px; border-radius: 5px; color: #fff; width: 350px; min-height: 40px; position: static; top: 30px; z-index: 25100; pointer-events: ${pointerEvents}; } .${localNotificationButtonClassName} { float: right; position: relative; top: -7px; right: -10px; background-color: transparent; border: none; pointer-events: initial; } .unnotify-close-btn { color: #fff; cursor: pointer; } .unnotify-action-btn { width: 50%; color: #fff; text-align: center; padding: 5px 0px; margin-top: 10px; border: none; cursor: pointer; background-color: rgba(0, 0, 0, 0); outline: none; } .unnotify-action-btn:hover { background-color: rgba(0, 0, 0, 0.1); outline: none; } .unnotify-input { background-color: rgba(0, 0, 0, 0.2); width: 100%; margin: 5px 0px; padding: 5px 0px; text-align: center; color: #fff; border-top-style: hidden; border-right-style: hidden; border-left-style: hidden; border-bottom-style: hidden; } .unnotify-success { background-color: rgba(27, 94, 32, 0.8); } .unnotify-info { background-color: rgba(29, 121, 198, 0.8); } .unnotify-warning { background-color: rgba(251, 114, 4, 0.8); } .unnotify-danger { background-color: rgba(213, 0, 0, 0.8); } /* Custom, iPhone Retina */ @media only screen and (min-width : 320px) { .${localNotificationCenterClassName} { margin: 0px 2px; padding: 0px 2px; } .${localEachNotificationClassName} { width: 300px; } } /* Extra Small Devices, Phones */ @media only screen and (min-width : 480px) { .${localNotificationCenterClassName} { width: 360px; ${side}: 0px; padding: 0px 10px; margin: 0px 20px; } .${localEachNotificationClassName} { width: 90%; } } /* Small Devices, Tablets */ @media only screen and (min-width : 768px) { .${localNotificationCenterClassName} { width: 360px; ${side}: 0px; padding: 0px 10px; margin: 0px 20px; } .${localEachNotificationClassName} { width: 90%; } } `; } /**Returns a random ID */ function randomID(): string { return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) } /**Returns the Div Element that houses the notification */ function __unnotifyDiv(eachNotificationClassName: string, options: options): HTMLDivElement { let div = document.createElement("div"); div.id = randomID(); div.classList.add(eachNotificationClassName); if (options.type == "success" || options.type == "info" || options.type == "danger" || options.type == "warning") { div.classList.add("unnotify-" + options.type); } else if (typeof (options.customClass) != "undefined" || typeof (options.customClass) != null || options.customClass != "") { div.classList.add(options.customClass); } if (typeof (options.animateIn) != "undefined") { div.classList.add("animated"); div.classList.add(options.animateIn); div.setAttribute("data-animate-in", options.animateIn); } else { div.setAttribute("data-animate-in", ""); } if (typeof (options.animateOut) != "undefined") { div.setAttribute("data-animate-out", options.animateOut); } else { div.setAttribute("data-animate-out", ""); } return div; } /**Returns the title div */ function __unnotifyTitle(title: string): HTMLDivElement { let titleDiv = document.createElement("div"); let titleSpan = document.createElement("span"); titleSpan.style.fontWeight = "700"; titleSpan.style.fontSize = "1.6rem"; titleSpan.innerText = title; titleDiv.appendChild(titleSpan); return titleDiv; } /**Returns the button which would close the notification */ function __unnotifyCloseButton(closeButtonClass: string): HTMLButtonElement { let closeButton = document.createElement("button"); closeButton.classList.add(closeButtonClass); closeButton.classList.add("unnotify-close-btn"); closeButton.innerText = "x"; return closeButton; } function __unnotifyActionButton(text: string): HTMLButtonElement { let btn = document.createElement("button"); btn.classList.add("unnotify-action-btn"); btn.innerText = text; return btn } /**Returns the div that displays the content of the notification */ function __unnotifyContent(content: string): HTMLDivElement { let contentDiv = document.createElement("div"); contentDiv.innerHTML = content; return contentDiv; } function __setupDestroyEventHandlers(div: HTMLDivElement, options: options) { // If timeout is 0, then don't autodestroy it if (typeof (options.timeout) == "undefined" || typeof (options.timeout) == null || options.timeout < 0) { setTimeout(function () { destroy(div.id); }, defaultTimeout); } else if (options.timeout > 0) { setTimeout(function () { destroy(div.id); }, options.timeout) } } /**Internal function to display the notification */ function __unnotifyShow(eachNotificationClassName: string, notificationButtonClassName: string, title: string, content: string, options: options): string { let div = __unnotifyDiv(eachNotificationClassName, options); let titleDiv = __unnotifyTitle(title); let closeButton = __unnotifyCloseButton(notificationButtonClassName); titleDiv.appendChild(closeButton); closeButton.addEventListener('click', function () { destroy(div.id); }); let contentDiv = __unnotifyContent(content); div.appendChild(titleDiv); div.appendChild(contentDiv); __setupDestroyEventHandlers(div, options); notificationCenter.appendChild(div); return div.id; } /**Internal function to display a confirmation notification */ function __unnotifyConfirm(eachNotificationClassName: string, notificationButtonClassName: string, content: string, options: options, confirmButtonName: "Confirm" | "Yes", cancelButtonName: "Cancel" | "No", onConfirmCallback: (evt: MouseEvent, id: string) => void, onCancelCallback: (evt: MouseEvent, id: string) => void): string { let div = __unnotifyDiv(eachNotificationClassName, options); let closeButton = __unnotifyCloseButton(notificationButtonClassName); closeButton.addEventListener('click', function () { destroy(div.id); }); let contentDiv = __unnotifyContent(content); let confirmButton = __unnotifyActionButton(confirmButtonName); let cancelButton = __unnotifyActionButton(cancelButtonName); div.appendChild(closeButton); div.appendChild(contentDiv); div.appendChild(confirmButton); div.appendChild(cancelButton); if (onConfirmCallback != undefined && onConfirmCallback != null) { confirmButton.addEventListener('click', evt => { onConfirmCallback(evt, div.id); }); } if (onCancelCallback != undefined && onCancelCallback != null) { cancelButton.addEventListener('click', evt => { onCancelCallback(evt, div.id); }); } notificationCenter.appendChild(div); return div.id; } /**Internal function to display an input notification */ function __unnotifyInputHandler(eachNotificationClassName: string, notificationButtonClassName: string, title: string, options: options, onNextCallback: (evt: MouseEvent, id: string, valueEntered: string) => void, onCancelCallback: (evt: MouseEvent, id: string) => void): string { let div = __unnotifyDiv(eachNotificationClassName, options); let closeButton = __unnotifyCloseButton(notificationButtonClassName); closeButton.addEventListener('click', function () { destroy(div.id); }); let titleDiv = __unnotifyContent(title); let inp = document.createElement("input"); inp.id = randomID(); inp.classList.add("unnotify-input"); let confirmButton = __unnotifyActionButton("Next"); let cancelButton = __unnotifyActionButton("Cancel"); div.appendChild(closeButton); div.appendChild(titleDiv); div.appendChild(inp); div.appendChild(confirmButton); div.appendChild(cancelButton); if (onNextCallback != undefined && onNextCallback != null) { confirmButton.addEventListener('click', evt => { let valueEntered = inp.value; onNextCallback(evt, div.id, valueEntered); }); } if (onCancelCallback != undefined && onCancelCallback != null) { cancelButton.addEventListener('click', evt => { onCancelCallback(evt, div.id); }); } notificationCenter.appendChild(div); return div.id; } /**Internal function to destroy the notification with the given id */ function __unnotifyDestroy(id: string) { try { let div = <HTMLDivElement>document.getElementById(id); // Apply the animate-out class let animateOut = div.getAttribute("data-animate-out"); let animateIn = div.getAttribute("data-animate-in"); if (animateOut.length != 0) { if (!div.classList.contains("animated")) { div.classList.add("animated"); } if (animateIn.length != 0) { div.classList.remove(animateIn); } div.classList.add(animateOut); setTimeout(function () { div.parentNode.removeChild(div); }, 1000); } else { div.parentNode.removeChild(div); } } catch (e) { } } /**Initialises the notification module */ export function init(side?: 'left' | 'right', clickable?: boolean) { __unnotifyCreateStyleSheet(notificationCenterClassName, eachNotificationClassName, notificationButtonClassName, side, clickable); } /**Displays the notification and returns the ID of the notification element. Title is a string, content can either be a string or HTML. */ export function show(title: string, content: string, options: options): string { return __unnotifyShow(eachNotificationClassName, notificationButtonClassName, title, content, options); } /**Shows a confirmation notification (with two options: Confirm and Cancel) and accepts a confirmation callback (executed if the user confirms) * and an optional on-cancel callback (executed if the user cancels) and returns the ID of the notification */ export function confirm(content: string, options: options, onConfirmCallback: (evt: MouseEvent, id: string) => void, onCancelCallback?: (evt: MouseEvent, id: string) => void): string { return __unnotifyConfirm(eachNotificationClassName, notificationButtonClassName, content, options, "Confirm", "Cancel", onConfirmCallback, onCancelCallback); } /**Shows a confirmation notification (with two options: Yes and No) and accepts a confirmation callback (executed if the user clicks on Yes) * and an optional callback that is executed if the user clicks on No, and returns the ID of the notification */ export function affirm(content: string, options: options, onConfirmCallback: (evt: MouseEvent, id: string) => void, onCancelCallback?: (evt: MouseEvent, id: string) => void): string { return __unnotifyConfirm(eachNotificationClassName, notificationButtonClassName, content, options, "Yes", "No", onConfirmCallback, onCancelCallback); } /**Displays a notification with the provision for an input, which is passed to the onNextCallback when the user clicks on "Next" */ export function input(title: string, options: options, onNextCallback: (evt: MouseEvent, id: string, valueEntered: string) => void, onCancelCallback?: (evt: MouseEvent, id: string) => void): string { return __unnotifyInputHandler(eachNotificationClassName, notificationButtonClassName, title, options, onNextCallback, onCancelCallback); } /**Destroys the notification with the associated ID */ export function destroy(id: string) { __unnotifyDestroy(id); } /**Class that can display the notifications */ export class Unnotify { localNotificationCenterClassName: string localEachNotificationClassName: string /**Initialises everything. Accepts the side, whose dafault is right. Possible values are 'right', 'left' */ constructor(side?: 'left' | 'right', clickable?: boolean) { this.localNotificationCenterClassName = `${notificationCenterClassName}-${side}`; this.localEachNotificationClassName = `${eachNotificationClassName}-${side}`; // Create the CSS rules required for the notification center __unnotifyCreateStyleSheet(this.localNotificationCenterClassName, this.localEachNotificationClassName, notificationButtonClassName, side, clickable) } /**Displays the notification and returns the ID of the notification element. Title is a string, content can either be a string or HTML. */ show(title: string, content: string, options: options): string { return __unnotifyShow(this.localEachNotificationClassName, notificationButtonClassName, title, content, options); } /**Shows a confirmation notification and accepts a confirmation callback (executed if the user confirms) * and an optional on-cancel callback (executed if the user cancels) and returns the ID of the notification */ confirm(content: string, options: options, onConfirmCallback: (evt: MouseEvent, id: string) => void, onCancelCallback?: (evt: MouseEvent, id: string) => void): string { return __unnotifyConfirm(this.localEachNotificationClassName, notificationButtonClassName, content, options, "Confirm", "Cancel", onConfirmCallback, onCancelCallback); } /**Shows a confirmation notification (with two options: Yes and No) and accepts a confirmation callback (executed if the user clicks on Yes) * and an optional callback that is executed if the user clicks on No, and returns the ID of the notification */ affirm(content: string, options: options, onConfirmCallback: (evt: MouseEvent, id: string) => void, onCancelCallback?: (evt: MouseEvent, id: string) => void): string { return __unnotifyConfirm(this.localEachNotificationClassName, notificationButtonClassName, content, options, "Yes", "No", onConfirmCallback, onCancelCallback); } /**Displays a notification with the provision for an input, which is passed to the onNextCallback when the user clicks on "Next" */ input(title: string, options: options, onNextCallback: (evt: MouseEvent, id: string, valueEntered: string) => void, onCancelCallback?: (evt: MouseEvent, id: string) => void): string { return __unnotifyInputHandler(this.localEachNotificationClassName, notificationButtonClassName, title, options, onNextCallback, onCancelCallback); } /**Destroys the notification with the associated ID */ destroy(id: string) { __unnotifyDestroy(id); } } /**Stores the available options */ export interface options { /**Notification type, if custom, then customClass will hold the class name that needs to be applied. */ type: 'success' | 'info' | 'warning' | 'danger' | 'custom' | string; /**Timeout in milliseconds. Default is 5000. */ timeout?: number; /**Class name to be applied to the notification. `type` should be set to `custom`. */ customClass?: string; /**Set the animate.css animationIn class */ animateIn?: string; /**Set the animate.css animationOut class */ animateOut?: string; }