Created
August 15, 2016 23:17
-
-
Save estrattonbailey/163520be7b8297d8f45be44f49f54d0a to your computer and use it in GitHub Desktop.
Rough Draft - Promo Bar Library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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