File: development/modules/accordion/js/accordion.js
import $ from 'jquery';
import DOMModule from '../../__base/dommodule';
/**
* @module Modules
*/
const AccordionStates = [
{name: 'init', events: {render: 'rendered'}},
{name: 'rendered', events: {start: 'closed'}},
{name: 'closed', events: {open: 'opened'}},
{name: 'opened', events: {close: 'closed'}}
];
// PRIVATE METHODS
const handleClick = Symbol('_handleClick');
const handleResize = Symbol('_handleResize');
const handleTransition = Symbol('_handleTransition');
/**
* Accordion module
* @class Modules.Accordion
* @constructor
* @extends Modules._DOMModule
*/
class Accordion extends DOMModule {
constructor(options) {
super(options);
this.addStates(AccordionStates);
this.iconClassesOpen = this.$data('accordion-icon-open');
this.iconClassesClose = this.$data('accordion-icon-close');
this.initialState = this.$data('accordion-initialstate');
this.prefix = this.$data('accordion-prefix') || 'accordion'
this.$headNode = this.$findChilds('[data-accordion-action]');
this.$bodyNode = this.$findChilds('[data-accordion-effect]');
this.$maskNode = $('<div></div>').addClass('accordion_mask');
this.$iconNode = $('<div></div>').
addClass(this.prefix + '_head_icon').
addClass(this.iconClassesOpen);
}
/**
* Creates a new Accordion instance without the "new" keyword but does not
* initialize it
* @method of
* @static
* @for Modules.Accordion
* @param {node|string|configs} node Node, CSS selector or configuration object
* @return {Accordion} A new instance
*
* @example
* import Accordion from './modules/accordion/js/accordion';
*
* const myAccordion = Accordion.of('[data-widget=Accordion]');
* myAccordion.render().startUp();
*/
static of (node) {
return new Accordion(node);
}
/**
* Finds and initializes all Accordions on a page at once
* @method findAll
* @static
* @for Modules.Accordion
* @param {string} selector CSS selector to match nodes against
* @return {array} A list of instances
*
* @example
* import Accordion from './modules/accordion/js/accordion';
*
* Accordion.findAll('[data-widget=Accordion]');
*/
static findAll(selector) {
return $(selector).toArray().filter((node) => {
return !$(node).data('defer');
}).map((node) => {
const accordion = Accordion.of(node);
accordion.render().startUp();
return accordion;
});
}
/**
* Renders missing nodes into the instance and returns it
* @method render
* @for Modules.Accordion
* @return {this} The instance
*
* @example
* import Accordion from './modules/accordion/js/accordion';
*
* const myAccordion = Accordion.of('[data-widget=Accordion]');
* myAccordion.render();
*/
render() {
if (this.getState() === 'init') {
this.consume('render');
const body = this.$bodyNode.detach();
this.$maskNode.
append(body).
css('height', '0').
addClass('accordion_mask--hidden');
this.$rootNode.append(this.$maskNode);
this.$headNode.append(this.$iconNode);
}
return this;
}
/**
* Attaches all eventlisteners to the accordion instance and opens it if
* the initial state is defined as visible
* @method startUp
* @for Modules.Accordion
* @return {undefined} Nothing
*
* @example
* import Accordion from './modules/accordion/js/accordion';
*
* const myAccordion = Accordion.of('[data-widget=Accordion]');
* myAccordion.render().startUp();
*/
startUp() {
if (this.getState() === 'rendered') {
this.consume('start');
this.$rootNode.removeClass('is-open');
this.onClick('[data-accordion-action]', handleClick);
this.onTransitionEnd('.accordion_mask', handleTransition);
this.onResize(handleResize);
if (this.initialState === 'visible') {
this.show();
}
}
}
/**
* Opens the accordion if it is closed
* @method show
* @for Modules.Accordion
* @return {this} The instance
*
* @example
* import Accordion from './modules/accordion/js/accordion';
*
* const myAccordion = Accordion.of('[data-widget=Accordion]');
* myAccordion.render().startUp();
*
* myAccordion.show();
*/
show() {
if (this.getState() === 'closed') {
this.consume('open');
this.$rootNode.addClass('is-open');
this.$maskNode.
css({ height: this.$bodyNode.innerHeight() + 1 }).
removeClass('accordion_mask--hidden');
this.$iconNode.
removeClass(this.iconClassesOpen).
addClass(this.iconClassesClose);
}
return this;
}
/**
* Closes the accordion if it is opened
* @method hide
* @for Modules.Accordion
* @return {this} The instance
*
* @example
* import Accordion from './modules/accordion/js/accordion';
*
* const myAccordion = Accordion.of('[data-widget=Accordion]');
* myAccordion.render().startUp();
*
* myAccordion.hide();
*/
hide() {
if (this.getState() === 'opened') {
this.consume('close');
this.$rootNode.removeClass('is-open');
this.$maskNode.
css({ height: '0px' }).
addClass('accordion_mask--hidden').
removeClass('accordion_mask--overflow');
this.$iconNode.
removeClass(this.iconClassesClose).
addClass(this.iconClassesOpen);
}
return this;
}
// === PRIVATE STUFF ===
// === EVENT HANDLERS ===
[handleClick](event) {
event.preventDefault();
if (this.getState() === 'opened') {
this.hide();
return null;
}
this.show();
}
[handleTransition]() {
if (this.getState() === 'opened'){
this.$maskNode.addClass('accordion_mask--overflow');
}
this.publish('w-widget-action-viewchanged', {
$root: this.$rootNode,
module: this
});
}
[handleResize]() {
if (this.getState() === 'opened') {
this.$maskNode.css({height: this.$bodyNode.innerHeight() + 1});
}
return this;
}
}
export default Accordion;