├── LICENSE ├── example.html ├── page-notifications.css ├── page-notifications.js └── readme.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Rafał Michałuszek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 35 | 36 | -------------------------------------------------------------------------------- /page-notifications.css: -------------------------------------------------------------------------------- 1 | /*version 1.0.3*/ 2 | 3 | #page-notifications-container { 4 | width: 100%; 5 | max-width: 360px; 6 | position: fixed; 7 | 8 | padding: 10px; 9 | box-sizing: border-box; 10 | 11 | font-family: Arial, Helvetica, sans-serif; 12 | z-index: 10; 13 | overflow: hidden; 14 | pointer-events: none; 15 | } 16 | 17 | .container-top-left { 18 | top: 0px; 19 | left: 0px; 20 | padding-top:0px !important; 21 | } 22 | .container-top-middle { 23 | left: 50%; 24 | top: 0px; 25 | transform: translateX(-50%); 26 | padding-top:0px !important; 27 | } 28 | .container-top-right { 29 | top: 0px; 30 | right: 0px; 31 | padding-top:0px !important; 32 | } 33 | .container-bottom-left { 34 | bottom: 0px; 35 | left: 0px; 36 | } 37 | .container-bottom-middle { 38 | left: 50%; 39 | bottom: 0px; 40 | transform: translateX(-50%); 41 | } 42 | .container-bottom-right { 43 | bottom: 0px; 44 | right: 0px; 45 | } 46 | 47 | 48 | .page-notifications-body { 49 | pointer-events: auto; 50 | width: 100%; 51 | 52 | height: auto; 53 | max-height: 200px; 54 | 55 | background-color: white; 56 | border-radius: 2px; 57 | 58 | overflow: hidden; 59 | transform: translateY(0); 60 | 61 | box-shadow: -2px 2px 8px rgba(0,0,0,0.4); 62 | margin-top: 10px; 63 | 64 | animation-duration: 0.3s; 65 | cursor: pointer; 66 | } 67 | 68 | .page-notifications-left { 69 | width: 30px; 70 | 71 | left: 0px; 72 | top: 0px; 73 | bottom: 0px; 74 | 75 | position: absolute; 76 | color: white; 77 | 78 | display: flex; 79 | flex-direction: column; 80 | justify-content: center; 81 | text-align: center; 82 | } 83 | .page-notifications-right { 84 | overflow: hidden; 85 | width: auto; 86 | margin-left: 30px; 87 | padding: 7px; 88 | } 89 | 90 | /* Title */ 91 | .page-notifications-right h1 { 92 | margin: 0px; 93 | padding: 0px; 94 | font-size: 16px; 95 | margin-right: 10px; 96 | text-align: left; 97 | } 98 | /* Content */ 99 | .page-notifications-right h2 { 100 | padding: 0px; 101 | margin: 0px; 102 | margin-top: 3px; 103 | font-size: 12px; 104 | } 105 | /* Time */ 106 | .page-notifications-right h3 { 107 | width: 100%; 108 | text-align: right; 109 | color: #888888; 110 | font-size: 11px; 111 | margin: 0px; 112 | margin-top: 3px; 113 | margin-bottom: 3px; 114 | padding: 0px; 115 | } 116 | .page-notifications-timer { 117 | position: absolute; 118 | 119 | left: 30px; 120 | bottom: 0px; 121 | height: 3px; 122 | 123 | width: 0px; 124 | 125 | animation-duration: 1s; 126 | background: rgba(140,140,140,0.3) 127 | } 128 | 129 | /* light */ 130 | .page-notifications-light .page-notifications-timer { 131 | background: #333333; 132 | } 133 | .page-notifications-light .page-notifications-right { 134 | background: white; 135 | color: #333333; 136 | } 137 | .page-notifications-light .page-notifications-right h1 { 138 | color: #444444; 139 | } 140 | .page-notifications-light .page-notifications-right h2 { 141 | color: #444444; 142 | } 143 | 144 | /* dark */ 145 | .page-notifications-dark .page-notifications-timer { 146 | background: #dfdfff; 147 | } 148 | .page-notifications-dark .page-notifications-right { 149 | background: #222226; 150 | color: #dfdfff; 151 | } 152 | .page-notifications-dark .page-notifications-right h1 { 153 | color: #dfdfff; 154 | } 155 | .page-notifications-dark .page-notifications-right h2 { 156 | color: #aaaaaa; 157 | } 158 | 159 | .page-notifications-close { 160 | position: absolute; 161 | right: 3px; 162 | top: 0px; 163 | font-size: 18px; 164 | padding: 3px; 165 | font-weight: bold; 166 | cursor: pointer; 167 | 168 | -webkit-user-select: none; 169 | -moz-user-select: none; 170 | -ms-user-select: none; 171 | user-select: none; 172 | } 173 | .page-notifications-close:hover { 174 | opacity: 0.8; 175 | } 176 | 177 | /* Types */ 178 | 179 | .page-notifications-info .page-notifications-left{ 180 | background: #03A9F4; 181 | font-size: 1.4em; 182 | } 183 | .page-notifications-success .page-notifications-left{ 184 | background: #4cd137; 185 | font-size: 1.0em; 186 | } 187 | .page-notifications-warning .page-notifications-left{ 188 | background: #e67e22; 189 | font-size: 1.1em; 190 | font-weight: 500; 191 | } 192 | .page-notifications-error .page-notifications-left{ 193 | background: #e84118; 194 | font-size: 1.4em; 195 | font-weight: 500; 196 | } 197 | -------------------------------------------------------------------------------- /page-notifications.js: -------------------------------------------------------------------------------- 1 | /*version 1.0.3*/ 2 | 3 | //constructor 4 | function PageNotifications(args = {}) { 5 | 6 | //theme 7 | if(args.theme=="dark") { 8 | this.theme = "dark"; 9 | } else { 10 | this.theme = "light"; 11 | } 12 | //position 13 | switch(args.position) { 14 | case 'top-left': 15 | this.position = 'top-left';break; 16 | case 'top-middle': 17 | this.position = 'top-middle';break; 18 | case 'top-right': 19 | this.position = 'top-right';break; 20 | case 'bottom-left': 21 | this.position = 'bottom-left';break; 22 | case 'bottom-middle': 23 | this.position = 'bottom-middle';break; 24 | default: 25 | this.position = 'bottom-right';break; 26 | } 27 | //parent element 28 | if(args.parentDiv&&document.getElementById(args.parentDiv)) { 29 | this.parentDiv = document.getElementById(args.parentDiv); 30 | } else { 31 | this.parentDiv = document.body; 32 | } 33 | //target width of notifications 34 | if(args.width&&!isNaN(args.width)) { 35 | this.width = args.width; 36 | } else { 37 | this.width = this.parentDiv.offsetWidth; 38 | } 39 | 40 | this.pageNotifications = []; 41 | this.lastNotificationIdNumber = 0; 42 | 43 | this.notificationsDurationsTimers = {}; 44 | this.notificationsDurationsTimeouts = {}; 45 | this.notificationsDurationsLoop = null; 46 | 47 | this.deltaLastUpdate = Date.now(); 48 | 49 | this.container = document.createElement('div'); 50 | this.container.id = 'page-notifications-container'; 51 | this.container.className = 'container-'+this.position; //apply class for positioning the containe 52 | this.container.style.maxWidth = this.width+"px"; 53 | this.parentDiv.appendChild(this.container); 54 | 55 | this.update = setInterval(function() { 56 | var now = Date.now(); 57 | var deltaTime = now-this.deltaLastUpdate; 58 | this.deltaLastUpdate = now; 59 | 60 | for(let id = 0; id < this.pageNotifications.length; id++) { 61 | this.pageNotifications[id].update(deltaTime); 62 | if(this.pageNotifications[id].toDelete) { 63 | this.pageNotifications.splice(id,1); 64 | } 65 | } 66 | 67 | }.bind(this),17); 68 | 69 | this.push = function(title,content,type,duration) { 70 | this.lastNotificationIdNumber++; 71 | var notificationId = "pn"+this.lastNotificationIdNumber; 72 | this.pageNotifications.push(new PageNotification(notificationId,title,content,type,duration,this.container,this.theme,this.width)); 73 | } 74 | this.closeAll = function(){ 75 | for(let id = 0; id < this.pageNotifications.length; id++) { 76 | this.pageNotifications[id].close(); 77 | } 78 | } 79 | } 80 | 81 | function PageNotification(pnId,title,content,type,duration,container,theme,width) { 82 | this.pnId = pnId; 83 | this.title = title; 84 | this.content = content; 85 | this.type = type; 86 | this.duration = duration; 87 | this.durationLeft = duration; 88 | this.theme = theme; 89 | this.width = width; 90 | 91 | this.heightScale = 0; //from 0 to 1, variable used to make animations of showing/hiding 92 | this.heightCurrentAnimation = 1 // 0 - nothing, 1 - showup, 2 - hide 93 | this.heightAnimationProgress = 0 94 | 95 | this.timerScale = 0; //from 0 to 1, variable used to make animations of showing/hiding 96 | this.timerCurrentAnimation = 1 // 0 - nothing, 1 - timing-out 97 | this.timerAnimationProgress = 0 98 | 99 | this.notification = document.createElement('div'); 100 | this.fullHeight = 0; 101 | 102 | this.toDelete = false; // make true if this should be removed from array 103 | 104 | //constructor 105 | let _this = this; 106 | 107 | if(type!="success"&&type!="warning"&&type!="error") 108 | type = "info"; 109 | 110 | this.notification.className = "page-notifications-body"+" page-notifications-"+this.theme+" page-notifications-"+this.type; 111 | this.notification.setAttribute('notificationid', this.pnId); 112 | this.notification.style.maxWidth = this.width+"px"; 113 | 114 | let left = document.createElement('div'); 115 | left.className = "page-notifications-left"; 116 | 117 | if(type=="success") left.innerHTML = "✓"; 118 | else if(type=="info") left.innerHTML = "i"; 119 | else if(type=="warning") left.innerHTML = "!"; 120 | else if(type=="error") left.innerHTML = "×" 121 | 122 | let right = document.createElement('div'); 123 | right.className = "page-notifications-right"; 124 | 125 | let timer = document.createElement('div'); 126 | timer.className = "page-notifications-timer"; 127 | 128 | let closeButton = document.createElement('div'); 129 | closeButton.className = "page-notifications-close"; 130 | closeButton.innerHTML = "×"; 131 | 132 | closeButton.onclick = function() { 133 | _this.close(); 134 | } 135 | 136 | let h1 = document.createElement('h1'); 137 | let h2 = document.createElement('h2'); 138 | let h3 = document.createElement('h3'); 139 | 140 | let date = new Date(); 141 | 142 | h1.innerHTML = title; 143 | h2.innerHTML = content; 144 | let minutes = date.getMinutes(); 145 | if(minutes <= 10) minutes = "0"+minutes; 146 | h3.innerHTML = date.getHours() + ":" + minutes; 147 | 148 | right.appendChild(h1); 149 | right.appendChild(closeButton); 150 | right.appendChild(h2); 151 | right.appendChild(timer); 152 | right.appendChild(h3); 153 | 154 | this.notification.appendChild(left); 155 | this.notification.appendChild(right); 156 | 157 | container.appendChild(this.notification); 158 | 159 | this.fullHeight = this.notification.offsetHeight; 160 | this.notification.style.height = "0px"; 161 | 162 | this.close = function() { 163 | if(this.heightCurrentAnimation==0) { 164 | var notifications = container.getElementsByClassName('page-notifications-body'); 165 | for(let notification of notifications) { 166 | if(notification.getAttribute('notificationId')==this.pnId) { 167 | this.heightCurrentAnimation = 2; 168 | this.heightAnimationProgress = 0; 169 | setTimeout(function(){ 170 | _this.toDelete = true; 171 | notification.parentNode.removeChild(notification); 172 | },300); 173 | return; 174 | } 175 | } 176 | } 177 | } 178 | this.changeDuration = function(duration) { 179 | if(!isNaN(duration)) { 180 | this.duration = duration; 181 | this.timerAnimationProgress = 0; 182 | this.timerCurrentAnimation = 1; 183 | } else if(duration == 'unknown') { 184 | this.timerAnimationProgress = 0; 185 | this.duration = false; 186 | this.timerCurrentAnimation = 0; 187 | this.timerScale = 1; 188 | } else if(duration == false) { 189 | this.timerAnimationProgress = 0; 190 | this.duration = false; 191 | this.timerCurrentAnimation = 0; 192 | this.timerScale = 0; 193 | } 194 | } 195 | this.update = function(deltaTime) { 196 | //showing and hiding 197 | if(this.heightCurrentAnimation==1) { 198 | this.heightAnimationProgress += deltaTime 199 | if(this.heightAnimationProgress>= 300) this.heightAnimationProgress = 300; 200 | 201 | this.heightScale = this.easeOutQuint(this.heightAnimationProgress/300.0); 202 | if(this.heightAnimationProgress == 300) this.heightCurrentAnimation = 0; 203 | } 204 | else if(this.heightCurrentAnimation==2) { 205 | this.heightAnimationProgress += deltaTime 206 | if(this.heightAnimationProgress>= 300) this.heightAnimationProgress = 300; 207 | 208 | this.heightScale = 1-this.easeOutQuint(this.heightAnimationProgress/300.0); 209 | if(this.heightAnimationProgress == 300) this.heightCurrentAnimation = 0; 210 | } 211 | this.notification.style.marginTop = this.heightScale*10+"px"; 212 | this.notification.style.height = this.heightScale*this.fullHeight+"px"; 213 | //timer 214 | if(this.timerCurrentAnimation==1) { 215 | this.timerAnimationProgress += deltaTime 216 | if(this.timerAnimationProgress>= this.duration) this.timerAnimationProgress = this.duration; 217 | 218 | this.timerScale = 1-this.timerAnimationProgress/this.duration; 219 | if(this.timerAnimationProgress == this.duration) { 220 | this.close(); 221 | this.timerCurrentAnimation = 0; 222 | } 223 | } 224 | console.log(this.timerScale); 225 | this.notification.getElementsByClassName('page-notifications-timer')[0].style.width = this.timerScale*(this.width-40)+"px"; 226 | } 227 | 228 | this.easeOutQuint = function(t) //source: https://gist.github.com/gre/1650294 229 | { return 1+(--t)*t*t*t*t } 230 | 231 | if(!isNaN(duration)&&duration!=false) { 232 | let _this = this; 233 | 234 | this.notification.onmouseover = function() { 235 | _this.changeDuration('unknown'); 236 | } 237 | this.notification.onmouseleave = function() { 238 | _this.changeDuration(duration); 239 | } 240 | _this.changeDuration(duration); 241 | 242 | } else { 243 | _this.changeDuration(false); 244 | } 245 | 246 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Page-Notifications 2 | 3 | Simply library for your page to easily show notifications. 4 | 5 | [www.page-notifications.rafalm.com](http://www.page-notifications.rafalm.com) 6 | 7 | 8 | ## Usage 9 | 10 | Include **page-notifications.js** and **page-notifications.css** in your project: 11 | 12 | ```javascript 13 | 14 | 15 | ``` 16 | 17 | and initialize notifications: 18 | 19 | ```javascript 20 | var notifications = new PageNotifications(); 21 | //or with arguments 22 | var notifications = new PageNotifications({'theme':'dark','parentDiv':'divId'}); 23 | ``` 24 | Full list of arguments and methods is avaible at documentation: [www.page-notifications.rafalm.com#docs](http://www.page-notifications.rafalm.com#docs) 25 | 26 | Now you can push notifications as you wish: 27 | 28 | ```javascript 29 | notifications.push("Title","Content","info",false); 30 | ``` 31 | 32 | ## Credits 33 | 34 | Project build on **MIT** Licence by **Rafał Michałuszek (rafalm99)** 35 | 36 | *Website:* [www.rafalm.com](http://www.rafalm.com) 37 | 38 | 39 | --------------------------------------------------------------------------------