UNPKG

8.26 kBJavaScriptView Raw
1'use strict';
2
3import $ from 'jquery';
4import { MediaQuery } from './foundation.util.mediaQuery';
5import { GetYoDigits } from './foundation.util.core';
6import { Plugin }from './foundation.plugin';
7
8import { Accordion } from './foundation.accordion';
9import { Tabs } from './foundation.tabs';
10
11// The plugin matches the plugin classes with these plugin instances.
12var MenuPlugins = {
13 tabs: {
14 cssClass: 'tabs',
15 plugin: Tabs
16 },
17 accordion: {
18 cssClass: 'accordion',
19 plugin: Accordion
20 }
21};
22
23
24/**
25 * ResponsiveAccordionTabs module.
26 * @module foundation.responsiveAccordionTabs
27 * @requires foundation.util.motion
28 * @requires foundation.accordion
29 * @requires foundation.tabs
30 */
31
32class ResponsiveAccordionTabs extends Plugin{
33 /**
34 * Creates a new instance of a responsive accordion tabs.
35 * @class
36 * @name ResponsiveAccordionTabs
37 * @fires ResponsiveAccordionTabs#init
38 * @param {jQuery} element - jQuery object to make into Responsive Accordion Tabs.
39 * @param {Object} options - Overrides to the default plugin settings.
40 */
41 _setup(element, options) {
42 this.$element = $(element);
43 this.options = $.extend({}, this.$element.data(), options);
44 this.rules = this.$element.data('responsive-accordion-tabs');
45 this.currentMq = null;
46 this.currentPlugin = null;
47 this.className = 'ResponsiveAccordionTabs'; // ie9 back compat
48 if (!this.$element.attr('id')) {
49 this.$element.attr('id',GetYoDigits(6, 'responsiveaccordiontabs'));
50 };
51
52 this._init();
53 this._events();
54 }
55
56 /**
57 * Initializes the Menu by parsing the classes from the 'data-responsive-accordion-tabs' attribute on the element.
58 * @function
59 * @private
60 */
61 _init() {
62 MediaQuery._init();
63
64 // The first time an Interchange plugin is initialized, this.rules is converted from a string of "classes" to an object of rules
65 if (typeof this.rules === 'string') {
66 let rulesTree = {};
67
68 // Parse rules from "classes" pulled from data attribute
69 let rules = this.rules.split(' ');
70
71 // Iterate through every rule found
72 for (let i = 0; i < rules.length; i++) {
73 let rule = rules[i].split('-');
74 let ruleSize = rule.length > 1 ? rule[0] : 'small';
75 let rulePlugin = rule.length > 1 ? rule[1] : rule[0];
76
77 if (MenuPlugins[rulePlugin] !== null) {
78 rulesTree[ruleSize] = MenuPlugins[rulePlugin];
79 }
80 }
81
82 this.rules = rulesTree;
83 }
84
85 this._getAllOptions();
86
87 if (!$.isEmptyObject(this.rules)) {
88 this._checkMediaQueries();
89 }
90 }
91
92 _getAllOptions() {
93 //get all defaults and options
94 var _this = this;
95 _this.allOptions = {};
96 for (var key in MenuPlugins) {
97 if (MenuPlugins.hasOwnProperty(key)) {
98 var obj = MenuPlugins[key];
99 try {
100 var dummyPlugin = $('<ul></ul>');
101 var tmpPlugin = new obj.plugin(dummyPlugin,_this.options);
102 for (var keyKey in tmpPlugin.options) {
103 if (tmpPlugin.options.hasOwnProperty(keyKey) && keyKey !== 'zfPlugin') {
104 var objObj = tmpPlugin.options[keyKey];
105 _this.allOptions[keyKey] = objObj;
106 }
107 }
108 tmpPlugin.destroy();
109 }
110 catch(e) {
111 }
112 }
113 }
114 }
115
116 /**
117 * Initializes events for the Menu.
118 * @function
119 * @private
120 */
121 _events() {
122 var _this = this;
123
124 $(window).on('changed.zf.mediaquery', function() {
125 _this._checkMediaQueries();
126 });
127 }
128
129 /**
130 * Checks the current screen width against available media queries. If the media query has changed, and the plugin needed has changed, the plugins will swap out.
131 * @function
132 * @private
133 */
134 _checkMediaQueries() {
135 var matchedMq, _this = this;
136 // Iterate through each rule and find the last matching rule
137 $.each(this.rules, function(key) {
138 if (MediaQuery.atLeast(key)) {
139 matchedMq = key;
140 }
141 });
142
143 // No match? No dice
144 if (!matchedMq) return;
145
146 // Plugin already initialized? We good
147 if (this.currentPlugin instanceof this.rules[matchedMq].plugin) return;
148
149 // Remove existing plugin-specific CSS classes
150 $.each(MenuPlugins, function(key, value) {
151 _this.$element.removeClass(value.cssClass);
152 });
153
154 // Add the CSS class for the new plugin
155 this.$element.addClass(this.rules[matchedMq].cssClass);
156
157 // Create an instance of the new plugin
158 if (this.currentPlugin) {
159 //don't know why but on nested elements data zfPlugin get's lost
160 if (!this.currentPlugin.$element.data('zfPlugin') && this.storezfData) this.currentPlugin.$element.data('zfPlugin',this.storezfData);
161 this.currentPlugin.destroy();
162 }
163 this._handleMarkup(this.rules[matchedMq].cssClass);
164 this.currentPlugin = new this.rules[matchedMq].plugin(this.$element, {});
165 this.storezfData = this.currentPlugin.$element.data('zfPlugin');
166
167 }
168
169 _handleMarkup(toSet){
170 var _this = this, fromString = 'accordion';
171 var $panels = $('[data-tabs-content='+this.$element.attr('id')+']');
172 if ($panels.length) fromString = 'tabs';
173 if (fromString === toSet) {
174 return;
175 };
176
177 var tabsTitle = _this.allOptions.linkClass?_this.allOptions.linkClass:'tabs-title';
178 var tabsPanel = _this.allOptions.panelClass?_this.allOptions.panelClass:'tabs-panel';
179
180 this.$element.removeAttr('role');
181 var $liHeads = this.$element.children('.'+tabsTitle+',[data-accordion-item]').removeClass(tabsTitle).removeClass('accordion-item').removeAttr('data-accordion-item');
182 var $liHeadsA = $liHeads.children('a').removeClass('accordion-title');
183
184 if (fromString === 'tabs') {
185 $panels = $panels.children('.'+tabsPanel).removeClass(tabsPanel).removeAttr('role').removeAttr('aria-hidden').removeAttr('aria-labelledby');
186 $panels.children('a').removeAttr('role').removeAttr('aria-controls').removeAttr('aria-selected');
187 }else{
188 $panels = $liHeads.children('[data-tab-content]').removeClass('accordion-content');
189 };
190
191 $panels.css({display:'',visibility:''});
192 $liHeads.css({display:'',visibility:''});
193 if (toSet === 'accordion') {
194 $panels.each(function(key,value){
195 $(value).appendTo($liHeads.get(key)).addClass('accordion-content').attr('data-tab-content','').removeClass('is-active').css({height:''});
196 $('[data-tabs-content='+_this.$element.attr('id')+']').after('<div id="tabs-placeholder-'+_this.$element.attr('id')+'"></div>').detach();
197 $liHeads.addClass('accordion-item').attr('data-accordion-item','');
198 $liHeadsA.addClass('accordion-title');
199 });
200 }else if (toSet === 'tabs'){
201 var $tabsContent = $('[data-tabs-content='+_this.$element.attr('id')+']');
202 var $placeholder = $('#tabs-placeholder-'+_this.$element.attr('id'));
203 if ($placeholder.length) {
204 $tabsContent = $('<div class="tabs-content"></div>').insertAfter($placeholder).attr('data-tabs-content',_this.$element.attr('id'));
205 $placeholder.remove();
206 }else{
207 $tabsContent = $('<div class="tabs-content"></div>').insertAfter(_this.$element).attr('data-tabs-content',_this.$element.attr('id'));
208 };
209 $panels.each(function(key,value){
210 var tempValue = $(value).appendTo($tabsContent).addClass(tabsPanel);
211 var hash = $liHeadsA.get(key).hash.slice(1);
212 var id = $(value).attr('id') || GetYoDigits(6, 'accordion');
213 if (hash !== id) {
214 if (hash !== '') {
215 $(value).attr('id',hash);
216 }else{
217 hash = id;
218 $(value).attr('id',hash);
219 $($liHeadsA.get(key)).attr('href',$($liHeadsA.get(key)).attr('href').replace('#','')+'#'+hash);
220 };
221 };
222 var isActive = $($liHeads.get(key)).hasClass('is-active');
223 if (isActive) {
224 tempValue.addClass('is-active');
225 };
226 });
227 $liHeads.addClass(tabsTitle);
228 };
229 }
230
231 /**
232 * Destroys the instance of the current plugin on this element, as well as the window resize handler that switches the plugins out.
233 * @function
234 */
235 _destroy() {
236 if (this.currentPlugin) this.currentPlugin.destroy();
237 $(window).off('.zf.ResponsiveAccordionTabs');
238 }
239}
240
241ResponsiveAccordionTabs.defaults = {};
242
243export {ResponsiveAccordionTabs};