Skip to content

Instantly share code, notes, and snippets.

@estrattonbailey
Created August 15, 2016 23:17
Show Gist options
  • Save estrattonbailey/163520be7b8297d8f45be44f49f54d0a to your computer and use it in GitHub Desktop.
Save estrattonbailey/163520be7b8297d8f45be44f49f54d0a to your computer and use it in GitHub Desktop.
Rough Draft - Promo Bar Library
import jss from 'jss-browserify'
import stockpile from 'stockpile.js'
const storage = stockpile('GV')
window.STORE = storage
/**
* requestAnimationFrame polyfill
*/
const rAF = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60)
}
const addToStorage = (content) => {
let timestamp = new Date().getTime()
storage.set('promo', {
timestamp,
content
})
}
const promoEnabled = (timestamp, lifespan, instance) => {
let store = storage.get('promo')
let day = 1000*60*60*24
if (!store) return true
if (store.content !== instance.content) return true
let daysPassed = (timestamp - store.timestamp) / day
if (daysPassed >= lifespan) return true
return false
}
/**
* @param {object} el DOM element
* @return {number} Height of passed element
*/
const getHeight = (el) => {
return Math.max(el.offsetHeight, el.scrollHeight, el.clientHeight)
}
/**
* Set prefixed transform values
*
* @param {object} el DOM element
* @param {number} value A pixel value, positive or negative
*/
const writeCSS = (selector, height) => {
if (!height){
jss.remove(selector)
return
}
jss.set(selector, {
'transform': `translateY(${height}px)`,
'-webkit-transform': `translateY(${height}px)`
})
}
/**
* Apply transforms to all
* specified elements
*
* @param {array} nodes DOM nodes to apply transforms to
* @param {number} height Distance to displace elements
*/
const displaceElements = (nodes, height) => {
for (let i = 0; i < nodes.length; i++){
let item = nodes[i]
if (typeof item === 'string'){
writeCSS(item, height)
}
else if (typeof item === 'object'){
if (item.test()){
writeCSS(item.selector, height)
} else {
writeCSS(item.selector)
}
}
}
}
/**
* CONSTRUCTOR
*
* @param {object} promo Outer promo DOM element
* @param {object} config Options object
*/
export default (promo, config) => {
/**
* Default option values
*/
const defaults = {
watchResize: true,
displace: [document.body],
animationSpeed: 200,
close: promo.querySelector('.js-close'),
active: true,
lifespan: 1
}
/**
* Merge user defined config
*/
config = Object.assign({}, defaults, config)
const proto = {
init: function(){
Object.defineProperty(this, 'enabled', {
value: promoEnabled(new Date().getTime(), this.lifespan, this),
writable: true
})
if (this.enabled === false) return
/**
* Close functionality
*/
config.close.onclick = () => {
hide()
this.enabled = false
addToStorage(this.content)
}
/**
* Setup window resize
*/
window.onresize = checkHeight
/**
* Instance is ready
*/
rAF(() => {
instance.emit('ready', this)
})
/**
* Check height on initial load,
* displace elements
*/
checkHeight()
return this
},
update: () => {
checkHeight(true)
},
hide: hide,
show: show,
listeners: {
all: {
queue: []
}
},
on: subscribe,
emit: publish
}
const instance = Object.create(proto, {
config: {
value: config
},
height: {
value: 0,
writable: true
},
active: {
value: config.active,
writable: true
},
element: {
value: promo
},
content: {
value: promo.querySelector('.js-content').innerHTML
}
})
/**
* Return instance!
*/
return instance
/**
* Subscribe and add event listener
*
* @param {string} event Event to listen for
* @param {function} callback Listener callback function
*/
function subscribe(event, callback){
if (typeof event === 'function'){
callback = event
event = 'all'
}
instance.listeners[event] = instance.listeners[event] || { queue: [] }
instance.listeners[event].queue.push(callback)
}
/**
* Publish event
*
* @param {string} event Event name
* @param {object} data Instance object
*/
function publish(event, data){
let items = instance.listeners[event] ? instance.listeners[event].queue : false
if (!items || items.length < 1) return
for (let i = 0; i < items.length; i++){
items[i](data || null)
}
}
/**
* Run hide sequence
*/
function hide(){
instance.element.classList.remove('is-enabled')
instance.emit('hide', instance)
rAF(() => {
instance.active = false
writeCSS(`#${promo.id}`, instance.height * -1)
rAF(() => {
displaceElements(instance.config.displace, 0)
})
})
}
/**
* Run show sequence
*/
function show(){
if (!instance.enabled) return
instance.element.classList.add('is-enabled')
instance.emit('show', instance)
rAF(() => {
instance.active = true
writeCSS(`#${promo.id}`)
rAF(() => {
displaceElements(instance.config.displace, instance.height)
})
})
}
/**
* Watch height of promo bar
* for changes. If changed, update
* displaced elements
*
* @param {boolean} force Forces update of displaced elements
*/
function checkHeight(force = false){
if (!instance.enabled) return
let height = getHeight(promo)
if (force === true || height !== instance.height){
instance.height = height
/**
* If the instance isn't active
* don't update the elements
*/
if (instance.active === false) return
displaceElements(instance.config.displace, instance.height)
rAF(() => {
instance.emit('update', instance)
})
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment