├── .gitignore ├── .travis.yml ├── bundle-css.js ├── example ├── example.js └── index.html ├── index.js ├── main.js ├── notifications.css ├── package.json ├── readme.md └── style.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | example/bundle.js 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | notifications: 4 | email: false 5 | node_js: 6 | - '9' 7 | after_success: 8 | - npx semantic-release 9 | -------------------------------------------------------------------------------- /bundle-css.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | 3 | var css = fs.readFileSync('./notifications.css', 'utf-8').replace(/\n+/g, '') 4 | var style = `// Generated by bundle-css.js 5 | module.exports = '${css}' 6 | ` 7 | fs.writeFileSync('style.js', style) 8 | -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | var notifications = require('../')() 2 | var button = document.querySelector('button') 3 | var message = document.querySelector('#message') 4 | var repo = document.querySelector('#repo') 5 | 6 | document.body.appendChild(notifications.render([])) 7 | 8 | button.onclick = function () { 9 | var type = document.querySelector('input:checked').value 10 | notifications.repo = repo.value 11 | notifications.add({ message: message.value, type: type }) 12 | } 13 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | notifications 6 | 7 | 44 | 45 |

dom-notifications example

46 | GitHub repository 47 |

Message

48 |
49 |

Type

50 |

51 |
52 |
53 |
54 |
55 |

56 |

Options

57 |

Repo

58 | 59 |

60 | 61 |

62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var Notifications = require('./main') 2 | var style = require('./style') 3 | var insertCss = require('insert-css') 4 | 5 | module.exports = function (opts) { 6 | insertCss(style) 7 | return new Notifications(opts) 8 | } 9 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | var Nanocomponent = require('nanocomponent') 2 | var html = require('nanohtml') 3 | 4 | class Notifications extends Nanocomponent { 5 | constructor (opts) { 6 | opts = opts || {} 7 | super() 8 | 9 | this.repo = opts.repo 10 | this.icons = Object.assign({ 11 | error: 'octicon octicon-flame', 12 | warning: 'octicon octicon-alert', 13 | info: 'octicon octicon-info', 14 | success: 'octicon octicon-check', 15 | close: 'octicon octicon-x' 16 | }, opts.icons) 17 | 18 | this.getMessageBody = this.getMessageBody.bind(this) 19 | this.notifications = [] 20 | this.renderedLength = -1 21 | } 22 | 23 | add (notification) { 24 | this.notifications.push(notification) 25 | this.rerender() 26 | } 27 | 28 | info (text) { 29 | this.add({ type: 'info', message: text }) 30 | } 31 | 32 | error (text) { 33 | this.add({ type: 'error', message: text }) 34 | } 35 | 36 | warning (text) { 37 | this.add({ type: 'warning', message: text }) 38 | } 39 | 40 | success (text) { 41 | this.add({ type: 'success', message: text }) 42 | } 43 | 44 | getMessageBody (notification) { 45 | if (notification.element) { 46 | notification.element.className = 'notification-message' 47 | return notification.element 48 | } 49 | if (this.repo && notification.type === 'error') { 50 | return html` 51 |
52 | ${notification.message}
53 | Create an issue for this error 54 |
` 55 | } else { 56 | return html`
${notification.message}
` 57 | } 58 | } 59 | 60 | createElement (notifications) { 61 | if (notifications) this.notifications = notifications 62 | this.renderedLength = notifications.length 63 | return html`
${ 64 | notifications 65 | .map((notification) => { 66 | var classNames = [ 67 | 'notification', 68 | notification.closed ? 'notification-hidden' : 'notification-show', 69 | 'notification-' + (notification.type || 'info') 70 | ] 71 | return html` 72 |
73 |
74 | 75 | 76 | 77 |
78 | ${this.getMessageBody(notification)} 79 | { notification.closed = true; this.rerender() }} class="notification-close" title="Dismiss this notification"> 80 | 81 | 82 |
` 83 | }) 84 | } 85 |
` 86 | } 87 | 88 | update (notifications) { 89 | return notifications !== this.notifications || notifications.length !== this.renderedLength 90 | } 91 | } 92 | 93 | module.exports = Notifications 94 | -------------------------------------------------------------------------------- /notifications.css: -------------------------------------------------------------------------------- 1 | .notification-container { 2 | top: 10px; 3 | right: 0; 4 | bottom: auto; 5 | left: 0; 6 | position: fixed; 7 | z-index: 1060; 8 | width: 80%; 9 | max-width: 400px; 10 | margin: auto; 11 | } 12 | 13 | .notification-error { 14 | background-color: #e74c3c; 15 | } 16 | 17 | .notification-warning { 18 | background-color: #ff7f48; 19 | } 20 | 21 | .notification-info { 22 | background-color: #3ea2ff; 23 | } 24 | 25 | .notification-success { 26 | background-color: #64ce83; 27 | } 28 | 29 | .notification-error .notification-icon { 30 | background-color: #ba2c1d; 31 | } 32 | 33 | .notification-warning .notification-icon { 34 | background-color: #f44e06; 35 | } 36 | 37 | .notification-info .notification-icon { 38 | background-color: #067cea; 39 | } 40 | 41 | .notification-success .notification-icon { 42 | background-color: #3da95c; 43 | } 44 | 45 | .notification-hidden { 46 | animation: notification-hide 250ms cubic-bezier(.33859,-.42,1,-.22),notification-shrink 250ms 250ms cubic-bezier(.5,0,0,1); 47 | -webkit-animation: notification-hide 250ms cubic-bezier(.33859,-.42,1,-.22),notification-shrink 250ms 250ms cubic-bezier(.5,0,0,1); 48 | animation-fill-mode: forwards; 49 | -webkit-animation-fill-mode: forwards; 50 | } 51 | 52 | .notification-show { 53 | animation: notification-show 180ms cubic-bezier(.175,.885,.32,1.27499); 54 | -webkit-animation: notification-show 180ms cubic-bezier(.175,.885,.32,1.27499); 55 | } 56 | 57 | .notification { 58 | font: 14px Helvetica,sans-serif; 59 | position: relative; 60 | border-radius: 4px; 61 | margin-bottom: 2px; 62 | max-height: 800px; 63 | color: #fff; 64 | overflow: hidden; 65 | } 66 | 67 | .notification-icon { 68 | position: absolute; 69 | left: 0; 70 | top: 0; 71 | height: 100%; 72 | width: 30px; 73 | color: rgba(255,255,255,.74); 74 | text-align: center; 75 | } 76 | 77 | .notification-icon span { 78 | position: relative; 79 | top: 5px; 80 | } 81 | 82 | .notification-message { 83 | word-break: break-word; 84 | padding: 10px 30px 10px 40px; 85 | } 86 | 87 | .notification-close { 88 | position: absolute; 89 | top: 10px; 90 | right: 10px; 91 | opacity: .3; 92 | cursor: pointer; 93 | } 94 | 95 | .notification-countdown { 96 | position: absolute; 97 | bottom: 0; 98 | width: 0; 99 | height: 4px; 100 | animation: notification-countdown linear 1; 101 | -webkit-animation: notification-countdown linear 1; 102 | } 103 | 104 | .notification-btn { 105 | position: relative; 106 | color: #fff; 107 | background-color: #ba2c1d; 108 | display: inline-block; 109 | padding: 6px 8px 7px; 110 | margin-top: 5px; 111 | margin-bottom: 0; 112 | margin-right: 10px; 113 | font-size: 12px; 114 | font-weight: 400; 115 | line-height: 1; 116 | text-align: center; 117 | white-space: nowrap; 118 | vertical-align: top; 119 | cursor: pointer; 120 | border-radius: 3px; 121 | text-decoration: none; 122 | border: 0px; 123 | } 124 | 125 | @keyframes notification-show { 126 | 0% { 127 | opacity: 0; 128 | transform: perspective(450px) translate(0, -30px) rotateX(90deg); 129 | } 130 | 131 | 100% { 132 | opacity: 1; 133 | transform: perspective(450px) translate(0, 0) rotateX(0deg); 134 | } 135 | } 136 | 137 | @-webkit-keyframes notification-show { 138 | 0% { 139 | opacity: 0; 140 | -webkit-transform: perspective(450px) translate(0, -30px) rotateX(90deg); 141 | } 142 | 143 | 100% { 144 | opacity: 1; 145 | -webkit-transform: perspective(450px) translate(0, 0) rotateX(0deg); 146 | } 147 | } 148 | 149 | @keyframes notification-shrink { 150 | 0% { 151 | opacity: 0; 152 | transform: scale(.8); 153 | } 154 | 155 | 100% { 156 | opacity: 0; 157 | max-height: 0; 158 | margin-bottom: 0; 159 | transform: scale(.8); 160 | } 161 | } 162 | 163 | @-webkit-keyframes notification-shrink { 164 | 0% { 165 | opacity: 0; 166 | -webkit-transform: scale(.8); 167 | } 168 | 169 | 100% { 170 | opacity: 0; 171 | max-height: 0; 172 | margin-bottom: 0; 173 | -webkit-transform: scale(.8); 174 | } 175 | } 176 | 177 | @keyframes notification-hide { 178 | 0% { 179 | opacity: 1; 180 | transform: scale(1); 181 | } 182 | 183 | 100% { 184 | opacity: 0; 185 | transform: scale(.8); 186 | } 187 | } 188 | 189 | @-webkit-keyframes notification-hide { 190 | 0% { 191 | opacity: 1; 192 | -webkit-transform: scale(1); 193 | } 194 | 195 | 100% { 196 | opacity: 0; 197 | -webkit-transform: scale(.8); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dom-notifications", 3 | "description": "atom-inspired notifications", 4 | "main": "index.js", 5 | "style": "notifications.css", 6 | "scripts": { 7 | "test": "standard", 8 | "deploy": "browserify example/example.js -o example/bundle.js && gh-pages -d example", 9 | "semantic-release": "semantic-release pre && npm publish && semantic-release post" 10 | }, 11 | "author": "Finn Pauls", 12 | "license": "ISC", 13 | "dependencies": { 14 | "insert-css": "2.0.0", 15 | "nanocomponent": "^6.5.2", 16 | "nanohtml": "^1.2.4" 17 | }, 18 | "devDependencies": { 19 | "browserify": "^16.2.2", 20 | "cz-conventional-changelog": "^2.1.0", 21 | "gh-pages": "^1.2.0", 22 | "standard": "^12.0.1" 23 | }, 24 | "directories": { 25 | "example": "example" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/finnp/dom-notifications.git" 30 | }, 31 | "keywords": [ 32 | "nanohtml", 33 | "yo-yo", 34 | "bel", 35 | "choo", 36 | "atom", 37 | "notifications" 38 | ], 39 | "bugs": { 40 | "url": "https://github.com/finnp/dom-notifications/issues" 41 | }, 42 | "homepage": "https://github.com/finnp/dom-notifications#readme", 43 | "config": { 44 | "commitizen": { 45 | "path": "./node_modules/cz-conventional-changelog" 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # dom-notifications 2 | [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/finnp/dom-notifications.svg)](https://greenkeeper.io/) 4 | [![nanocomponent 6](https://img.shields.io/badge/nanocomponent-6-green.svg)](https://github.com/choojs/nanocomponent) 5 | 6 | ![example gif](http://i.giphy.com/l41YBkA7AKgVXXwjK.gif) 7 | 8 | Have a look at the [example page](http://www.finnpauls.de/dom-notifications/). 9 | 10 | ## usage 11 | 12 | Install with `npm install dom-notifications --save` and use something like 13 | [browserify](http://browserify.org/) to create a bundle for the browser. 14 | 15 | ```js 16 | var domNotifications = require('dom-notifications') 17 | var notifications = domNotifications(options) 18 | 19 | document.body.appendChild(notifications.render()) 20 | 21 | notifications.add({message: 'You are now logged in'}) // defaults to `info` 22 | notifications.add({message: 'This is a warning', type: 'warning'}) 23 | notifications.error('Oh noes: File not found') 24 | ``` 25 | 26 | By default this uses [octicons](https://octicons.github.com/) icon classes 27 | that are not included automatically. Here's a CDN link that serves octicons that 28 | you can include in your HTML: 29 | ```html 30 |