minsky-kit
Version:
Kit of components for MINSKY web agency
199 lines (161 loc) • 5.79 kB
JavaScript
// imports
import Component from "./Component";
import Ticker from "../managers/Ticker";
import { getInstances } from "../core/Core";
import { strToCssClass } from "../utils/css";
import {
generateAccordionElement as factory1,
removeAccordionElement as factory2,
} from "../factories/AccordionFactory";
// static const
// class definition
export default class Accordion extends Component {
// constructor
constructor(args = {}, objectName = 'Accordion') {
// call super
super(args, objectName);
// set properties
this._name = args.name || 'accordion';
this.$titleWrapper = this.$el.querySelector(`.${this._name}__title-wrapper`);
this.$trigger = this.$el.querySelector(`.${this._name}__trigger`);
this.$arrow = this.$el.querySelector(`.${this._name}__arrow`);
this.$contentWrapper = this.$el.querySelector(`.${this._name}__content-wrapper`);
this.$content = this.$el.querySelector(`.${this._name}__content`);
this.hash = `#${this.$el.getAttribute('id')}`;
this.isOpen = args.isOpen ? args.isOpen : false;
this.group = args.group || null;
this.closedHeight = args.closedHeight || 0;
this.preventDefault = args.preventDefault || false;
// add group to el as data
if (this.group) {
this.$el.dataset.accordionGroup = this.group;
}
// toggle animation for init to remove transition on creation
setNoAnimation.call(this);
// check for hash in URL
if (window.location.hash && window.location.hash === this.hash && !this.isOpen) this.isOpen = true;
// set start state
this.toggle(this.isOpen, true);
// add listeners
this.$titleWrapper.addEventListener('click', this.blm.add('onTitleClick', onTitleClick));
}
// methods
open(hard = false, dynamic = false) {
// check noAnimation
if (hard) setNoAnimation.call(this);
// loop over instances and close them if groups match
if (this.group) {
const instances = getInstances();
for (const instance of instances[this.objectName]) {
if (instance.isOpen && instance.group === this.group) instance.close();
}
}
// open
this.$el.classList.add(strToCssClass(this._name, null, 'open'));
this.$el.classList.remove(strToCssClass(this._name, null, 'closed'));
// update state
this.isOpen = true;
// updateSize before opening
if (dynamic) {
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
this.refresh();
});
})
} else {
this.refresh();
}
// update accesibility
this.$trigger.setAttribute('aria-expanded', true);
// dispatch event
this.dispatch('open');
}
close(hard = false) {
// check noAnimation
if (hard) setNoAnimation.call(this);
// open
this.$el.classList.add(strToCssClass(this._name, null, 'closed'));
this.$el.classList.remove(strToCssClass(this._name, null, 'open'));
// update state
this.isOpen = false;
// updateSize before opening
this.refresh();
// update accesibility
this.$trigger.setAttribute('aria-expanded', false);
// dispatch event
this.dispatch('close');
}
toggle(forceState = null, hard = false) {
// check forced state
const isOpen = typeof forceState === 'boolean' ? !!forceState : !this.isOpen;
// toggle
if (!isOpen) {
this.close(hard);
} else {
this.open(hard);
}
// !isOpen ? this.close(hard) : this.open(hard);
}
refresh() {
const height = this.isOpen ? this.$content.offsetHeight : this.closedHeight;
this.$contentWrapper.style.height = height + (parseFloat(height).toString().length === height.toString().length ? 'px' : '');
// dispatch event
this.dispatch('refresh');
}
destroy() {
this.$contentWrapper.style.height = null;
this.$el.classList.remove(strToCssClass(this._name, null, 'open'), strToCssClass(this._name, null, 'closed'), this._name);
this.$titleWrapper.removeEventListener('click', this.blm.remove('onTitleClick', onTitleClick));
super.destroy();
}
// getters & setters
get title() {
return this.$titleWrapper.querySelector(`.${this._name}__title`).innerText;
}
set title(value) {
if (value !== this.title) {
this.$titleWrapper.querySelector(`.${this._name}__title`).innerText = value;
}
}
get transition() {
return this.$el.classList.contains(strToCssClass(this._name, 'noAnimation'));
}
set transition(value) {
if (this.transition !== value) {
if (!value) {
this.$el.classList.add(strToCssClass(this._name, 'noAnimation'));
this.$contentWrapper.style.transition = 'none';
this.$arrow.style.transition = 'none';
} else {
this.$el.classList.remove(strToCssClass(this._name, 'noAnimation'));
this.$contentWrapper.style.transition = null;
this.$arrow.style.transition = null;
}
}
}
}
// event listeners
function onTitleClick(e) {
this.dispatch('titleClick', {
originalEvent: e,
});
if (!e.defaultPrevented) {
this.toggle();
}
if (this.preventDefault) e.preventDefault();
}
function setNoAnimation() {
this.transition = false;
Ticker.quick(50, () => {
this.transition = true;
});
}
// deprecated methods
export function generateAccordionElement(args) {
this.log('warn', "Deprecation warning: 'generateAccordionElement' in Accortdion is deprecated, use 'generateAccordionElement()` from /factories/AccordionFactory.js instead.");
return factory1(args);
}
export function removeAccordionElement(args) {
this.log('warn', "Deprecation warning: 'removeAccordionElement' in Accortdion is deprecated, use 'removeAccordionElement()` from /factories/AccordionFactory.js instead.");
return factory2(args);
}