164 lines
4.4 KiB
JavaScript
164 lines
4.4 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
import $ from 'jquery';
|
||
|
import { Motion } from './foundation.util.motion';
|
||
|
import { Plugin } from './foundation.core.plugin';
|
||
|
import { RegExpEscape } from './foundation.core.utils';
|
||
|
import { Triggers } from './foundation.util.triggers';
|
||
|
|
||
|
/**
|
||
|
* Toggler module.
|
||
|
* @module foundation.toggler
|
||
|
* @requires foundation.util.motion
|
||
|
* @requires foundation.util.triggers
|
||
|
*/
|
||
|
|
||
|
class Toggler extends Plugin {
|
||
|
/**
|
||
|
* Creates a new instance of Toggler.
|
||
|
* @class
|
||
|
* @name Toggler
|
||
|
* @fires Toggler#init
|
||
|
* @param {Object} element - jQuery object to add the trigger to.
|
||
|
* @param {Object} options - Overrides to the default plugin settings.
|
||
|
*/
|
||
|
_setup(element, options) {
|
||
|
this.$element = element;
|
||
|
this.options = $.extend({}, Toggler.defaults, element.data(), options);
|
||
|
this.className = '';
|
||
|
this.className = 'Toggler'; // ie9 back compat
|
||
|
|
||
|
// Triggers init is idempotent, just need to make sure it is initialized
|
||
|
Triggers.init($);
|
||
|
|
||
|
this._init();
|
||
|
this._events();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initializes the Toggler plugin by parsing the toggle class from data-toggler, or animation classes from data-animate.
|
||
|
* @function
|
||
|
* @private
|
||
|
*/
|
||
|
_init() {
|
||
|
var input;
|
||
|
// Parse animation classes if they were set
|
||
|
if (this.options.animate) {
|
||
|
input = this.options.animate.split(' ');
|
||
|
|
||
|
this.animationIn = input[0];
|
||
|
this.animationOut = input[1] || null;
|
||
|
}
|
||
|
// Otherwise, parse toggle class
|
||
|
else {
|
||
|
input = this.$element.data('toggler');
|
||
|
// Allow for a . at the beginning of the string
|
||
|
this.className = input[0] === '.' ? input.slice(1) : input;
|
||
|
}
|
||
|
|
||
|
// Add ARIA attributes to triggers:
|
||
|
var id = this.$element[0].id,
|
||
|
$triggers = $(`[data-open~="${id}"], [data-close~="${id}"], [data-toggle~="${id}"]`);
|
||
|
|
||
|
// - aria-expanded: according to the element visibility.
|
||
|
$triggers.attr('aria-expanded', !this.$element.is(':hidden'));
|
||
|
// - aria-controls: adding the element id to it if not already in it.
|
||
|
$triggers.each((index, trigger) => {
|
||
|
const $trigger = $(trigger);
|
||
|
const controls = $trigger.attr('aria-controls') || '';
|
||
|
|
||
|
const containsId = new RegExp(`\\b${RegExpEscape(id)}\\b`).test(controls);
|
||
|
if (!containsId) $trigger.attr('aria-controls', controls ? `${controls} ${id}` : id);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initializes events for the toggle trigger.
|
||
|
* @function
|
||
|
* @private
|
||
|
*/
|
||
|
_events() {
|
||
|
this.$element.off('toggle.zf.trigger').on('toggle.zf.trigger', this.toggle.bind(this));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Toggles the target class on the target element. An event is fired from the original trigger depending on if the resultant state was "on" or "off".
|
||
|
* @function
|
||
|
* @fires Toggler#on
|
||
|
* @fires Toggler#off
|
||
|
*/
|
||
|
toggle() {
|
||
|
this[ this.options.animate ? '_toggleAnimate' : '_toggleClass']();
|
||
|
}
|
||
|
|
||
|
_toggleClass() {
|
||
|
this.$element.toggleClass(this.className);
|
||
|
|
||
|
var isOn = this.$element.hasClass(this.className);
|
||
|
if (isOn) {
|
||
|
/**
|
||
|
* Fires if the target element has the class after a toggle.
|
||
|
* @event Toggler#on
|
||
|
*/
|
||
|
this.$element.trigger('on.zf.toggler');
|
||
|
}
|
||
|
else {
|
||
|
/**
|
||
|
* Fires if the target element does not have the class after a toggle.
|
||
|
* @event Toggler#off
|
||
|
*/
|
||
|
this.$element.trigger('off.zf.toggler');
|
||
|
}
|
||
|
|
||
|
this._updateARIA(isOn);
|
||
|
this.$element.find('[data-mutate]').trigger('mutateme.zf.trigger');
|
||
|
}
|
||
|
|
||
|
_toggleAnimate() {
|
||
|
var _this = this;
|
||
|
|
||
|
if (this.$element.is(':hidden')) {
|
||
|
Motion.animateIn(this.$element, this.animationIn, function() {
|
||
|
_this._updateARIA(true);
|
||
|
this.trigger('on.zf.toggler');
|
||
|
this.find('[data-mutate]').trigger('mutateme.zf.trigger');
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
Motion.animateOut(this.$element, this.animationOut, function() {
|
||
|
_this._updateARIA(false);
|
||
|
this.trigger('off.zf.toggler');
|
||
|
this.find('[data-mutate]').trigger('mutateme.zf.trigger');
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_updateARIA(isOn) {
|
||
|
var id = this.$element[0].id;
|
||
|
$(`[data-open="${id}"], [data-close="${id}"], [data-toggle="${id}"]`)
|
||
|
.attr({
|
||
|
'aria-expanded': isOn ? true : false
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Destroys the instance of Toggler on the element.
|
||
|
* @function
|
||
|
*/
|
||
|
_destroy() {
|
||
|
this.$element.off('.zf.toggler');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Toggler.defaults = {
|
||
|
/**
|
||
|
* Tells the plugin if the element should animated when toggled.
|
||
|
* @option
|
||
|
* @type {boolean}
|
||
|
* @default false
|
||
|
*/
|
||
|
animate: false
|
||
|
};
|
||
|
|
||
|
export {Toggler};
|