Skip to content

Instantly share code, notes, and snippets.

@nhz-io
Created January 31, 2017 21:10
Show Gist options
  • Save nhz-io/73fb631003323d27a4a9b02f0aa4db9b to your computer and use it in GitHub Desktop.
Save nhz-io/73fb631003323d27a4a9b02f0aa4db9b to your computer and use it in GitHub Desktop.
Notifications Stack created by nhz-io - https://repl.it/F0BV/68
class Balloon {
constructor(text) {
this.id = Math.random().toString(36)
this.text = text
this.html = `
<div class="balloon">
<div class="close" onclick="balloons.remove('${this.id}')">✖</div>
<span class="text">${this.text}</span>
</div>
`
this.dom = $(this.html)
this._x = 0
this._y = 0
}
get x() {
return this._x
}
set x(value) {
this._x = value
this.dom.css({left: `${value}px`})
}
get y() {
return this._y
}
set y(value) {
this._y = value
this.dom.css({top: `${value}px`})
}
destroy() {
this.dom.remove()
}
}
class Guide {
constructor(...tourists) {
this.tourists = tourists
this._x = 0
this._y = 0
}
get x() {
return this._x
}
set x(value) {
if (value === undefined) {
return
}
const offset = value - this._x
this._x = value
this.tourists.forEach(tourist => tourist.x += offset)
}
get y() {
return this._y
}
set y(value) {
if (value === undefined) {
return
}
const offset = value - this._y
this._y = value
this.tourists.forEach(tourist => tourist.y += offset)
}
}
.notifications {
width: 500px;
height: 323px;
overflow: hidden;
background: url(https://i.imgflip.com/1ipp3y.jpg);
border: solid #333 4px;
position: relative;
float: left;
box-sizing: border-box;
}
.balloon {
box-sizing: border-box;
position: absolute;
background: #555;
border-radius: 2px;
color: white;
font-size: 18px;
text-align: center;
width: 200px;
padding: 8px;
font-family: monospace;
border: solid #444 1px;
}
.close {
position: absolute;
right: 6px;
top: 6px;
width: 17px;
height: 17px;
display: block;
border-radius: 100%;
background: #333;
font-family: monospace;
font-size: 14px;
cursor: pointer;
}
.abuse {
width: 120px;
font-size: 20px;
line-height: 4em;
position: absolute;
top: 90px;
right: 10px;
background: red;
border-color: red;
color: white;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>repl.it</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenMax.min.js"></script>
<script src="//code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="guide.js"></script>
<script src="stack.js"></script>
<script src="n-stack.js"></script>
<script src="balloon.js"></script>
<script src="notifications.js"></script>
<link href="index.css" rel="stylesheet" type="text/css" />
<script>
</script>
</head>
<body>
<div class="stack"></div>
<script src="index.js"></script>
</body>
</html>
class Balloons extends Notifications {
constructor(...args) {
super(...args)
this.dom = $(`
<div class="notifications">
<button class="abuse" onclick="sing()">ABUSE ME</button>
</div>
`)
}
animateRemove(...items) {
return super.animateRemove(...items).then(() => {
items.forEach(balloon => balloon.destroy())
})
}
remove(id, time = 1) {
const balloon = this._items.find(balloon => balloon.id === id)
if (!balloon) {
return Promise.resolve()
}
this.time = time
return super.remove(balloon)
}
add(text, time = 1) {
const balloon = new Balloon(text)
this.dom.append(balloon.dom)
this.time = time
return this.push(balloon)
}
}
const balloons = new Balloons({
size: 5,
width: 200,
height: 40,
spacing: 5,
offset: 250,
x: 0,
y: 260,
})
$(document.body).append(balloons.dom)
window.balloons = balloons
const song = [
'לכובע שלי', 'שלוש פינות',
'שלוש פינות', 'לכובע שלי',
'לולא היו לו', 'שלוש פינות',
'לא היה זה', 'הכובע שלי',
]
window.sing = () => {
const verse = song.shift()
song.push(verse)
window.balloons.add(verse)
}
class NStack extends Stack {
remove(...items) {
const history = this._items.slice()
super.remove(...items)
const _items = this._items.slice()
const collapse = _items.reduce((acc, item, i) => {
const amount = i - history.indexOf(item)
if (amount) {
acc[amount] = acc[amount] || []
acc[amount].push(item)
}
return acc
}, {})
return Promise.all([
this.animateRemove(...items),
this.animateCollapse(collapse),
])
}
shift() {
const item = super.shift()
return Promise.resolve(this.animateRemove(item))
}
push(...items) {
const history = this._items.slice()
const size = super.push(...items)
const _items = this._items.slice()
items = _items.filter(item => !history.includes(item))
const [remove, expand] = history.reduce((acc, item, i) => {
const idx = _items.indexOf(item)
if (idx > -1) {
const amount = idx - i
if (amount) {
const expand = acc[1]
expand[amount] = expand[amount] || []
expand[amount].push(item)
}
}
else {
acc[0].push(item)
}
return acc
}, [[], {}])
return Promise.all([
this.animateRemove(...remove),
this.animateExpand(expand),
this.animateInsert(...items)
])
}
}
class Notifications extends NStack {
constructor(args) {
super(args.size)
this.width = args.width
this.height = args.height
this.x = args.x
this.y = args.y
this.offset = args.offset
this.spacing = args.spacing
this.time = args.time
}
animateRemove(...items) {
const guide = new Guide(...items)
return new Promise(resolve => {
TweenMax.fromTo(
guide,
this.time || 1,
{x: 0},
{
x: this.offset,
ease: Expo.easeOut,
onComplete: resolve,
}
)
})
}
animateInsert(...items) {
const guide = new Guide(...items)
items.forEach((item, i) => {
item.x = this.x + this.offset
item.y = this.y - this.height * (i + 1) - this.spacing * i
})
return new Promise(resolve => {
TweenMax.fromTo(
guide,
this.time || 1,
{x: 0},
{
x: -1 * this.offset,
ease: Expo.easeOut,
onComplete: resolve,
}
)
})
}
animateCollapse(collapse) {
return Promise.all(Object.keys(collapse).reduce((acc, n) => {
const guide = new Guide(...collapse[n])
acc.push(new Promise(resolve => {
TweenMax.fromTo(
guide,
this.time || 1,
{y: 0},
{
y: -1 * this.height * parseInt(n, 10) + this.spacing,
ease: Expo.easeOut,
onCompleted: resolve,
}
)
}))
return acc
}, []))
}
animateExpand(expand) {
return Promise.all(Object.keys(expand).reduce((acc, n) => {
const guide = new Guide(...expand[n])
acc.push(new Promise(resolve => {
TweenMax.fromTo(
guide,
this.time || 1,
{y: 0},
{
y: -1 * this.height * parseInt(n, 10) - this.spacing,
ease: Expo.easeOut,
onCompleted: resolve,
}
)
}))
return acc
}, []))
}
}
class Stack {
constructor(arr) {
if (Array.isArray(arr)) {
this._items = arr.slice()
this._size = this._items.length
return
}
if (isNaN(arr)) {
throw new Error('Need size or array')
}
this._size = arr
this._items = []
}
get size() {
return this._size
}
set size(value) {
this._size = size
this._items = this._items.slice(0, this._size)
}
get items() {
this._items.reverse()
}
set items(value) {
this._items = value.reverse().slice(0, this._size)
}
indexOf(item) {
const i = this._items.indexOf(item)
return i > -1 ? this._size - i - 1 : -1
}
shift() {
return this._items.shift()
}
push(...items) {
const size = this._size
items = items.reverse().slice(0, size)
this._items = items.concat(this._items).slice(0, size)
return this._items.length
}
remove(...items) {
this._items = this._items.filter(item => !items.includes(item))
return this._items.length
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment