forked from mirceapiturca/Sections
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
778c2a6
commit 46ec120
Showing
1 changed file
with
204 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
(function FAQ(SD) { | ||
'use strict'; | ||
|
||
var support = getSupport(); | ||
var config = getConfig(); | ||
var init = compose(publicAPI, setEvents, getBlocks, getConfig); | ||
|
||
SD.faq = {} | ||
SD.faq[config.sectionId] = init(); | ||
|
||
function publicAPI(config) { | ||
return { | ||
id: config.sectionId, | ||
config: config, | ||
blocks: zipObj(config.blockIds, config.blocks), | ||
init: init | ||
} | ||
} | ||
|
||
//************************** | ||
|
||
|
||
/** | ||
* Click event. | ||
* @param {Object} block Section block elements and methods. | ||
* @return {Object} Section block elements and methods. | ||
*/ | ||
function blockEvents(block) { | ||
block.trigger.addEventListener('click', function triggerClick() { | ||
toggle(block); | ||
}); | ||
|
||
return block; | ||
} | ||
|
||
/** | ||
* Toggle block state. | ||
* @param {Object} block Section block elements and methods. | ||
* @return {Object} Section block elements and methods. | ||
*/ | ||
function toggle(block) { | ||
block.collapsed ? expand(block) : collapse(block); | ||
return block; | ||
} | ||
|
||
/** | ||
* Expand block. | ||
* @param {Object} block Section block elements and methods. | ||
* @return {Object} Section block elements and methods. | ||
*/ | ||
function expand(block) { | ||
block.button.setAttribute('aria-expanded', true); | ||
block.panel.removeAttribute('hidden'); | ||
animate(block.panel, 'normal'); | ||
return block; | ||
} | ||
|
||
/** | ||
* Collapse block. | ||
* @param {Object} block Section block elements and methods. | ||
* @return {Object} Section block elements and methods. | ||
*/ | ||
function collapse(block) { | ||
block.button.setAttribute('aria-expanded', false); | ||
block.panel.setAttribute('hidden', ''); | ||
animate(block.panel, 'reverse'); | ||
return block; | ||
} | ||
|
||
/** | ||
* Collapse block. | ||
* @param {Object} block Section block elements and methods. | ||
* @return {boolean} Collapsed block state. | ||
*/ | ||
function isCollapsed(block) { | ||
return Boolean(block.button.getAttribute('aria-expanded') === 'false'); | ||
} | ||
|
||
/** | ||
* Collapse block. | ||
* @param {HTMLElement} element Block panel element to animate. | ||
* @param {String} direction Animation direction, normal or reverse. | ||
* @return {undefined} Nothing to return. | ||
*/ | ||
function animate(element, direction) { | ||
if (!support.WebAnimations) return; | ||
|
||
element.setAttribute('data-is-animating', true); | ||
|
||
element.animate([ | ||
{ height: 0 }, | ||
{ height: element.offsetHeight + 'px' }], | ||
|
||
{ duration: 240, | ||
fill: 'both', | ||
easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)', | ||
direction: direction | ||
} | ||
|
||
).onfinish = function() { | ||
element.removeAttribute('data-is-animating'); | ||
this.cancel(); | ||
}; | ||
} | ||
|
||
//************************** | ||
|
||
|
||
/** | ||
* Maps all block IDs to exposed block API. | ||
* @param {Object} config Section and section blocks IDs. | ||
* @return {Object} config Section and section blocks IDs. | ||
*/ | ||
function getBlocks(config) { | ||
config.blocks = config.blockIds.map(block); | ||
return config; | ||
} | ||
|
||
/** | ||
* Create section block API. | ||
* @param {String} blockId Section Liquid block ID. | ||
* @return {Object} block elements and methods. | ||
*/ | ||
function block(blockId) { | ||
return { | ||
trigger: document.querySelector('[data-faq-trigger="' + blockId + '"]'), | ||
button: document.querySelector('[data-faq-button="' + blockId + '"]'), | ||
panel: document.querySelector('[data-faq-panel="' + blockId + '"]'), | ||
|
||
get collapsed() { return isCollapsed(this) }, | ||
select: function select() { return expand(this) }, | ||
deselect: function deselect() { return collapse(this) } | ||
} | ||
} | ||
|
||
/** | ||
* Adds event listeners to block elements. | ||
* @param {Object} config Section and section blocks IDs. | ||
* @return {Object} config Section and section blocks IDs. | ||
*/ | ||
function setEvents(config) { | ||
config.blocks.forEach(blockEvents); | ||
return config; | ||
} | ||
|
||
/** | ||
* Pass the Liquid assigned section variabiles. | ||
* @param {String} sectionId Current section ID. | ||
* @return {Object} Section and section blocks IDs. | ||
*/ | ||
function getConfig() { | ||
return JSON.parse(document.querySelector('[data-faq-config]').innerHTML); | ||
} | ||
|
||
/** | ||
* Feature detection. | ||
* @return {Object} Browser support. | ||
*/ | ||
function getSupport() { | ||
return { | ||
WebAnimations: (typeof Element.prototype.animate === 'function') | ||
} | ||
} | ||
|
||
//************************** | ||
|
||
|
||
/** | ||
* Creates a new object out of a list of keys and a list of values. | ||
* Key/value pairing is truncated to the length of the shorter of the two lists. | ||
* @example | ||
* zipObj(['a', 'b', 'c'], [1, 2, 3]); //=> {a: 1, b: 2, c: 3} | ||
* @param {Array} keys The array that will be properties on the output object. | ||
* @param {Array} values The list of values on the output object. | ||
* @return {Object} The object made by pairing up same-indexed elements of `keys` and `values`. | ||
*/ | ||
function zipObj(keys, values) { | ||
return keys.reduce( | ||
function zipObj(acc, key, idx) { | ||
acc[key] = values[idx]; | ||
return acc; | ||
}, {} | ||
) | ||
} | ||
|
||
/** | ||
* Performs right-to-left function composition. | ||
* The rightmost function may have any arity, the remaining functions must be unary. | ||
* @example | ||
* function plus1(n) {return n + 1}; | ||
* function plus2(n) {return n + 2}; | ||
* compose(plus2,plus1)(1) => 4 | ||
* @return {Function} Composed function | ||
*/ | ||
function compose() { | ||
var funcs = Array.prototype.slice.call(arguments).reverse(); | ||
return function() { | ||
return funcs.slice(1).reduce(function(res, fn) { | ||
return fn(res); | ||
}, funcs[0].apply(undefined, arguments)); | ||
}; | ||
} | ||
|
||
})(window.SectionsDesign = window.SectionsDesign || {}); |