@materializecss/materialize 1.2.2 → 2.0.0-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/Gruntfile.js +68 -313
  2. package/README.md +2 -2
  3. package/dist/css/materialize.css +1009 -1822
  4. package/dist/css/materialize.min.css +2 -8
  5. package/dist/js/materialize.js +8402 -12300
  6. package/dist/js/materialize.min.js +3 -2
  7. package/dist/js/materialize.min.js.map +1 -0
  8. package/package.json +13 -9
  9. package/sass/components/_badges.scss +12 -2
  10. package/sass/components/_buttons.scss +16 -11
  11. package/sass/components/_cards.scss +14 -9
  12. package/sass/components/_carousel.scss +5 -2
  13. package/sass/components/_chips.scss +3 -3
  14. package/sass/components/_collapsible.scss +22 -8
  15. package/sass/components/_collection.scss +14 -6
  16. package/sass/components/_datepicker.scss +30 -11
  17. package/sass/components/_dropdown.scss +6 -4
  18. package/sass/components/_global.scss +132 -111
  19. package/sass/components/_grid.scss +119 -98
  20. package/sass/components/_modal.scss +3 -3
  21. package/sass/components/_navbar.scss +31 -17
  22. package/sass/components/_normalize.scss +26 -124
  23. package/sass/components/_sidenav.scss +21 -20
  24. package/sass/components/_slider.scss +27 -7
  25. package/sass/components/_table_of_contents.scss +12 -12
  26. package/sass/components/_tabs.scss +47 -16
  27. package/sass/components/_tapTarget.scss +6 -6
  28. package/sass/components/_timepicker.scss +54 -46
  29. package/sass/components/_toast.scss +3 -3
  30. package/sass/components/_tooltip.scss +4 -5
  31. package/sass/components/_typography.scss +1 -1
  32. package/sass/components/_variables.scss +185 -120
  33. package/sass/components/forms/_checkboxes.scss +9 -9
  34. package/sass/components/forms/_file-input.scss +9 -7
  35. package/sass/components/forms/_input-fields.scss +173 -234
  36. package/sass/components/forms/_radio-buttons.scss +1 -1
  37. package/sass/components/forms/_range.scss +11 -11
  38. package/sass/components/forms/_select.scss +29 -19
  39. package/sass/components/forms/_switches.scss +22 -18
  40. package/sass/materialize.scss +1 -1
  41. package/src/autocomplete.ts +459 -0
  42. package/src/bounding.ts +6 -0
  43. package/{js/buttons.js → src/buttons.ts} +103 -162
  44. package/src/cards.ts +54 -0
  45. package/{js/carousel.js → src/carousel.ts} +137 -262
  46. package/src/characterCounter.ts +88 -0
  47. package/src/chips.ts +350 -0
  48. package/src/collapsible.ts +184 -0
  49. package/{js/component.js → src/component.ts} +6 -19
  50. package/{js/datepicker.js → src/datepicker.ts} +213 -299
  51. package/{js/dropdown.js → src/dropdown.ts} +140 -254
  52. package/src/edges.ts +6 -0
  53. package/src/forms.ts +120 -0
  54. package/src/global.ts +385 -0
  55. package/src/materialbox.ts +348 -0
  56. package/src/modal.ts +256 -0
  57. package/{js/parallax.js → src/parallax.ts} +47 -60
  58. package/{js/pushpin.js → src/pushpin.ts} +19 -47
  59. package/{js/range.js → src/range.ts} +58 -139
  60. package/{js/scrollspy.js → src/scrollspy.ts} +81 -153
  61. package/src/select.ts +448 -0
  62. package/{js/sidenav.js → src/sidenav.ts} +96 -202
  63. package/src/slider.ts +415 -0
  64. package/src/tabs.ts +290 -0
  65. package/src/tapTarget.ts +240 -0
  66. package/{js/timepicker.js → src/timepicker.ts} +268 -272
  67. package/{js/toasts.js → src/toasts.ts} +75 -134
  68. package/{js/tooltip.js → src/tooltip.ts} +59 -96
  69. package/src/waves.ts +70 -0
  70. package/extras/noUiSlider/nouislider.css +0 -404
  71. package/extras/noUiSlider/nouislider.js +0 -2147
  72. package/extras/noUiSlider/nouislider.min.js +0 -1
  73. package/js/anime.min.js +0 -34
  74. package/js/autocomplete.js +0 -479
  75. package/js/cards.js +0 -40
  76. package/js/cash.js +0 -960
  77. package/js/characterCounter.js +0 -136
  78. package/js/chips.js +0 -486
  79. package/js/collapsible.js +0 -275
  80. package/js/forms.js +0 -285
  81. package/js/global.js +0 -428
  82. package/js/materialbox.js +0 -453
  83. package/js/modal.js +0 -382
  84. package/js/select.js +0 -391
  85. package/js/slider.js +0 -497
  86. package/js/tabs.js +0 -402
  87. package/js/tapTarget.js +0 -315
  88. package/js/waves.js +0 -615
  89. package/sass/components/_waves.scss +0 -187
package/src/slider.ts ADDED
@@ -0,0 +1,415 @@
1
+ import { Component } from "./component";
2
+ import { M } from "./global";
3
+ import anim from "animejs";
4
+
5
+ let _defaults = {
6
+ indicators: true,
7
+ height: 400,
8
+ duration: 500,
9
+ interval: 6000,
10
+ pauseOnFocus: true,
11
+ pauseOnHover: true,
12
+ indicatorLabelFunc: null // Function which will generate a label for the indicators (ARIA)
13
+ };
14
+
15
+ export class Slider extends Component {
16
+ el: HTMLElement;
17
+ _slider: HTMLUListElement;
18
+ _slides: HTMLLIElement[];
19
+ activeIndex: number;
20
+ _activeSlide: HTMLLIElement;
21
+ _indicators: HTMLLIElement[];
22
+ private _handleIntervalBound: any;
23
+ private _handleIndicatorClickBound: any;
24
+ interval: string | number | NodeJS.Timeout;
25
+ eventPause: any;
26
+ _hovered: boolean;
27
+ _focused: boolean;
28
+ _focusCurrent: boolean;
29
+ _sliderId: string;
30
+ private _handleAutoPauseFocusBound: any;
31
+ private _handleAutoStartFocusBound: any;
32
+ private _handleAutoPauseHoverBound: any;
33
+ private _handleAutoStartHoverBound: any;
34
+
35
+ constructor(el, options) {
36
+ super(Slider, el, options);
37
+ (this.el as any).M_Slider = this;
38
+ this.options = {...Slider.defaults, ...options};
39
+
40
+ // init props
41
+ this.interval = null;
42
+ this.eventPause = false;
43
+ this._hovered = false;
44
+ this._focused = false;
45
+ this._focusCurrent = false;
46
+
47
+ // setup
48
+ this._slider = this.el.querySelector('.slides');
49
+ this._slides = Array.from(this._slider.querySelectorAll('li'));
50
+ this.activeIndex = this._slides.findIndex(li => li.classList.contains('active'));
51
+
52
+ if (this.activeIndex !== -1) {
53
+ this._activeSlide = this._slides[this.activeIndex];
54
+ }
55
+
56
+ this._setSliderHeight();
57
+
58
+ // Sets element id if it does not have one
59
+ if (this._slider.hasAttribute('id'))
60
+ this._sliderId = this._slider.getAttribute('id');
61
+ else {
62
+ this._sliderId = 'slider-' + M.guid();
63
+ this._slider.setAttribute('id', this._sliderId);
64
+ }
65
+
66
+ const placeholderBase64 = 'data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
67
+ // Set initial positions of captions
68
+ this._slides.forEach(slide => {
69
+ // Caption
70
+ //const caption = <HTMLElement|null>slide.querySelector('.caption');
71
+ //if (caption) this._animateCaptionIn(caption, 0);
72
+ // Set Images as Background Images
73
+ const img = slide.querySelector('img');
74
+ if (img) {
75
+ if (img.src !== placeholderBase64) {
76
+ img.style.backgroundImage = 'url('+ img.src +')';
77
+ img.src = placeholderBase64;
78
+ }
79
+ }
80
+ // Sets slide as focusable by code
81
+ if (!slide.hasAttribute('tabindex'))
82
+ slide.setAttribute('tabindex', '-1');
83
+ // Removes initial visibility from "inactive" slides
84
+ slide.style.visibility = 'hidden';
85
+ });
86
+
87
+ this._setupIndicators();
88
+
89
+ // Show active slide
90
+ if (this._activeSlide) {
91
+ this._activeSlide.style.display = 'block';
92
+ this._activeSlide.style.visibility = 'visible';
93
+ }
94
+ else {
95
+ this.activeIndex = 0;
96
+ this._slides[0].classList.add('active');
97
+ this._slides[0].style.visibility = 'visible';
98
+ this._activeSlide = this._slides[0];
99
+ this._animateSlide(this._slides[0], true);
100
+ /*anim({
101
+ targets: this._slides[0],
102
+ opacity: 1,
103
+ duration: this.options.duration,
104
+ easing: 'easeOutQuad'
105
+ });
106
+ */
107
+ // Update indicators
108
+ if (this.options.indicators) {
109
+ this._indicators[this.activeIndex].children[0].classList.add('active');
110
+ }
111
+ }
112
+
113
+ // Adjust height to current slide
114
+ // TODO: ??? Code does not do what it says in comment
115
+ /*
116
+ this._activeSlide.querySelectorAll('img').forEach(el => {
117
+ anim({
118
+ targets: this._activeSlide.querySelector('.caption'),
119
+ opacity: 1,
120
+ translateX: 0,
121
+ translateY: 0,
122
+ duration: this.options.duration,
123
+ easing: 'easeOutQuad'
124
+ });
125
+ });
126
+ */
127
+
128
+ this._setupEventHandlers();
129
+ // auto scroll
130
+ this.start();
131
+ }
132
+
133
+ static get defaults() {
134
+ return _defaults;
135
+ }
136
+
137
+ static init(els, options) {
138
+ return super.init(this, els, options);
139
+ }
140
+
141
+ static getInstance(el) {
142
+ let domElem = !!el.jquery ? el[0] : el;
143
+ return domElem.M_Slider;
144
+ }
145
+
146
+ destroy() {
147
+ this.pause();
148
+ this._removeIndicators();
149
+ this._removeEventHandlers();
150
+ (this.el as any).M_Slider = undefined;
151
+ }
152
+
153
+ _setupEventHandlers() {
154
+ this._handleIntervalBound = this._handleInterval.bind(this);
155
+ this._handleIndicatorClickBound = this._handleIndicatorClick.bind(this);
156
+ this._handleAutoPauseFocusBound = this._handleAutoPauseFocus.bind(this);
157
+ this._handleAutoStartFocusBound = this._handleAutoStartFocus.bind(this);
158
+ this._handleAutoPauseHoverBound = this._handleAutoPauseHover.bind(this);
159
+ this._handleAutoStartHoverBound = this._handleAutoStartHover.bind(this);
160
+ if (this.options.pauseOnFocus) {
161
+ this.el.addEventListener('focusin', this._handleAutoPauseFocusBound);
162
+ this.el.addEventListener('focusout', this._handleAutoStartFocusBound);
163
+ }
164
+ if (this.options.pauseOnHover) {
165
+ this.el.addEventListener('mouseenter', this._handleAutoPauseHoverBound);
166
+ this.el.addEventListener('mouseleave', this._handleAutoStartHoverBound);
167
+ }
168
+ if (this.options.indicators) {
169
+ this._indicators.forEach((el) => {
170
+ el.addEventListener('click', this._handleIndicatorClickBound);
171
+ });
172
+ }
173
+ }
174
+
175
+ _removeEventHandlers() {
176
+ if (this.options.pauseOnFocus) {
177
+ this.el.removeEventListener('focusin', this._handleAutoPauseFocusBound);
178
+ this.el.removeEventListener('focusout', this._handleAutoStartFocusBound);
179
+ }
180
+ if (this.options.pauseOnHover) {
181
+ this.el.removeEventListener('mouseenter', this._handleAutoPauseHoverBound);
182
+ this.el.removeEventListener('mouseleave', this._handleAutoStartHoverBound);
183
+ }
184
+ if (this.options.indicators) {
185
+ this._indicators.forEach((el) => {
186
+ el.removeEventListener('click', this._handleIndicatorClickBound);
187
+ });
188
+ }
189
+ }
190
+
191
+ _handleIndicatorClick(e) {
192
+ const el = (<HTMLElement>e.target).parentElement;
193
+ const currIndex = [...el.parentNode.children].indexOf(el);
194
+ this._focusCurrent = true;
195
+ this.set(currIndex);
196
+ }
197
+
198
+ _handleAutoPauseHover() {
199
+ this._hovered = true;
200
+ if (this.interval != null) {
201
+ this._pause(true);
202
+ }
203
+ }
204
+
205
+ _handleAutoPauseFocus() {
206
+ this._focused = true;
207
+ if (this.interval != null) {
208
+ this._pause(true);
209
+ }
210
+ }
211
+
212
+ _handleAutoStartHover() {
213
+ this._hovered = false;
214
+ if (!(this.options.pauseOnFocus && this._focused) && this.eventPause) {
215
+ this.start();
216
+ }
217
+ }
218
+
219
+ _handleAutoStartFocus() {
220
+ this._focused = false;
221
+ if (!(this.options.pauseOnHover && this._hovered) && this.eventPause) {
222
+ this.start();
223
+ }
224
+ }
225
+
226
+ _handleInterval() {
227
+ const activeElem = this._slider.querySelector('.active');
228
+ let newActiveIndex = [...activeElem.parentNode.children].indexOf(activeElem);
229
+ if (this._slides.length === newActiveIndex + 1)
230
+ newActiveIndex = 0; // loop to start
231
+ else
232
+ newActiveIndex += 1;
233
+ this.set(newActiveIndex);
234
+ }
235
+
236
+ _animateSlide(slide: HTMLElement, isDirectionIn: boolean): void {
237
+ let dx = 0, dy = 0;
238
+ anim({
239
+ targets: slide,
240
+ opacity: isDirectionIn ? [0, 1] : [1, 0],
241
+ duration: this.options.duration,
242
+ easing: 'easeOutQuad'
243
+ });
244
+
245
+ const caption = slide.querySelector('.caption');
246
+ if (!caption) return;
247
+ if (caption.classList.contains('center-align')) dy = -100;
248
+ else if (caption.classList.contains('right-align')) dx = 100;
249
+ else if (caption.classList.contains('left-align')) dx = -100;
250
+ anim({
251
+ targets: caption,
252
+ opacity: isDirectionIn ? [0, 1] : [1, 0],
253
+ translateX: isDirectionIn ? [dx, 0] : [0, dx],
254
+ translateY: isDirectionIn ? [dy, 0] : [0, dy],
255
+ duration: this.options.duration,
256
+ delay: this.options.duration,
257
+ easing: 'easeOutQuad'
258
+ });
259
+ }
260
+
261
+ _setSliderHeight() {
262
+ // If fullscreen, do nothing
263
+ if (!this.el.classList.contains('fullscreen')) {
264
+ if (this.options.indicators) {
265
+ // Add height if indicators are present
266
+ this.el.style.height = (this.options.height + 40)+'px'; //.css('height', this.options.height + 40 + 'px');
267
+ }
268
+ else {
269
+ this.el.style.height = this.options.height+'px';
270
+ }
271
+ this._slider.style.height = this.options.height+'px';
272
+ }
273
+ }
274
+
275
+ _setupIndicators() {
276
+ if (this.options.indicators) {
277
+ const ul = document.createElement('ul');
278
+ ul.classList.add('indicators');
279
+
280
+ const arrLi = [];
281
+ this._slides.forEach((el, i) => {
282
+ const label = this.options.indicatorLabelFunc
283
+ ? this.options.indicatorLabelFunc.call(this, i + 1, i === 0)
284
+ : `${i + 1}`;
285
+ const li = document.createElement('li');
286
+ li.classList.add('indicator-item');
287
+ li.innerHTML = `<button type="button" class="indicator-item-btn" aria-label="${label}" aria-controls="${this._sliderId}"></button>`;
288
+ arrLi.push(li);
289
+ ul.append(li);
290
+ });
291
+
292
+ this.el.append(ul);
293
+ this._indicators = arrLi;
294
+ }
295
+ }
296
+
297
+ _removeIndicators() {
298
+ this.el.querySelector('ul.indicators').remove(); //find('ul.indicators').remove();
299
+ }
300
+
301
+ set(index: number) {
302
+ // Wrap around indices.
303
+ if (index >= this._slides.length) index = 0;
304
+ else if (index < 0) index = this._slides.length - 1;
305
+
306
+ // Only do if index changes
307
+ if (this.activeIndex === index) return;
308
+
309
+ this._activeSlide = this._slides[this.activeIndex];
310
+ const _caption = <HTMLElement|null>this._activeSlide.querySelector('.caption');
311
+
312
+ this._activeSlide.classList.remove('active');
313
+ // Enables every slide
314
+ this._slides.forEach(slide => slide.style.visibility = 'visible');
315
+
316
+ //--- Hide active Slide + Caption
317
+ // TODO: What does this do?
318
+ anim({
319
+ targets: this._activeSlide,
320
+ opacity: 0,
321
+ duration: this.options.duration,
322
+ easing: 'easeOutQuad',
323
+ complete: () => {
324
+ this._slides.forEach(el => {
325
+ if (el.classList.contains('active')) return;
326
+ anim({
327
+ targets: el,
328
+ opacity: 0,
329
+ translateX: 0,
330
+ translateY: 0,
331
+ duration: 0, // Animation with duration 0... why use anim at all then?
332
+ easing: 'easeOutQuad'
333
+ });
334
+ // Disables invisible slides (for assistive technologies)
335
+ el.style.visibility = 'hidden';
336
+ });
337
+ }
338
+ });
339
+
340
+ // Hide active Caption
341
+ //this._animateCaptionIn(_caption, this.options.duration);
342
+ _caption.style.opacity = '0';
343
+
344
+ // Update indicators
345
+ if (this.options.indicators) {
346
+ const activeIndicator = this._indicators[this.activeIndex].children[0];
347
+ const nextIndicator = this._indicators[index].children[0];
348
+ activeIndicator.classList.remove('active');
349
+ nextIndicator.classList.add('active');
350
+ if (typeof this.options.indicatorLabelFunc === "function"){
351
+ activeIndicator.ariaLabel = this.options.indicatorLabelFunc.call(this, this.activeIndex, false);
352
+ nextIndicator.ariaLabel = this.options.indicatorLabelFunc.call(this, index, true);
353
+ }
354
+ }
355
+
356
+ //--- Show new Slide + Caption
357
+ this._animateSlide(this._slides[index], true);
358
+
359
+ this._slides[index].classList.add('active');
360
+
361
+ // TODO: Why focus? => causes uncontrollable page scroll
362
+ /*
363
+ if (this._focusCurrent) {
364
+ this._slides[index].focus();
365
+ this._focusCurrent = false;
366
+ }
367
+ */
368
+
369
+ this.activeIndex = index;
370
+
371
+ // Reset interval, if allowed. This check prevents autostart
372
+ // when slider is paused, since it can be changed though indicators.
373
+ if (this.interval != null) {
374
+ this.start();
375
+ }
376
+ }
377
+
378
+ _pause(fromEvent: boolean) {
379
+ clearInterval(this.interval);
380
+ this.eventPause = fromEvent;
381
+ this.interval = null;
382
+ }
383
+
384
+ pause() {
385
+ this._pause(false);
386
+ }
387
+
388
+ start() {
389
+ clearInterval(this.interval);
390
+ this.interval = setInterval(
391
+ this._handleIntervalBound,
392
+ this.options.duration + this.options.interval
393
+ );
394
+ this.eventPause = false;
395
+ }
396
+
397
+ next() {
398
+ let newIndex = this.activeIndex + 1;
399
+ // Wrap around indices.
400
+ if (newIndex >= this._slides.length) newIndex = 0;
401
+ else if (newIndex < 0) newIndex = this._slides.length - 1;
402
+ this.set(newIndex);
403
+ }
404
+
405
+ prev() {
406
+ let newIndex = this.activeIndex - 1;
407
+ // Wrap around indices.
408
+ if (newIndex >= this._slides.length) newIndex = 0;
409
+ else if (newIndex < 0) newIndex = this._slides.length - 1;
410
+ this.set(newIndex);
411
+ }
412
+ }
413
+
414
+
415
+
package/src/tabs.ts ADDED
@@ -0,0 +1,290 @@
1
+ import { Component } from "./component";
2
+ import { Carousel } from "./carousel";
3
+ import anim from "animejs";
4
+
5
+ let _defaults = {
6
+ duration: 300,
7
+ onShow: null,
8
+ swipeable: false,
9
+ responsiveThreshold: Infinity, // breakpoint for swipeable
10
+ };
11
+
12
+ export class Tabs extends Component {
13
+ el: HTMLElement;
14
+ _tabLinks: any;
15
+ _index: number;
16
+ _indicator: any;
17
+ _handleWindowResizeBound: (this: Window, ev: UIEvent) => any;
18
+ _handleTabClickBound: (this: Window, ev: UIEvent) => any;
19
+ _tabWidth: number;
20
+ _tabsWidth: number;
21
+ _tabsCarousel: any;
22
+ _activeTabLink: any;
23
+ _content: any;
24
+
25
+ constructor(el, options: any) {
26
+ super(Tabs, el, options);
27
+ (this.el as any).M_Tabs = this;
28
+
29
+ this.options = {...Tabs.defaults, ...options};
30
+ this._tabLinks = this.el.querySelectorAll('li.tab > a');
31
+ this._index = 0;
32
+ this._setupActiveTabLink();
33
+ if (this.options.swipeable) {
34
+ this._setupSwipeableTabs();
35
+ } else {
36
+ this._setupNormalTabs();
37
+ }
38
+ // Setup tabs indicator after content to ensure accurate widths
39
+ this._setTabsAndTabWidth();
40
+ this._createIndicator();
41
+ this._setupEventHandlers();
42
+ }
43
+
44
+ static get defaults() {
45
+ return _defaults;
46
+ }
47
+
48
+ static init(els, options) {
49
+ return super.init(this, els, options);
50
+ }
51
+
52
+ static getInstance(el) {
53
+ const domElem = !!el.jquery ? el[0] : el;
54
+ return domElem.M_Tabs;
55
+ }
56
+
57
+ destroy() {
58
+ this._removeEventHandlers();
59
+ this._indicator.parentNode.removeChild(this._indicator);
60
+ if (this.options.swipeable) {
61
+ this._teardownSwipeableTabs();
62
+ }
63
+ else {
64
+ this._teardownNormalTabs();
65
+ }
66
+ (this.el as any).M_Tabs = undefined;
67
+ }
68
+
69
+ _setupEventHandlers() {
70
+ this._handleWindowResizeBound = this._handleWindowResize.bind(this);
71
+ window.addEventListener('resize', this._handleWindowResizeBound);
72
+ this._handleTabClickBound = this._handleTabClick.bind(this);
73
+ this.el.addEventListener('click', this._handleTabClickBound);
74
+ }
75
+
76
+ _removeEventHandlers() {
77
+ window.removeEventListener('resize', this._handleWindowResizeBound);
78
+ this.el.removeEventListener('click', this._handleTabClickBound);
79
+ }
80
+
81
+ _handleWindowResize() {
82
+ this._setTabsAndTabWidth();
83
+ if (this._tabWidth !== 0 && this._tabsWidth !== 0) {
84
+ this._indicator.style.left = this._calcLeftPos(this._activeTabLink)+'px';
85
+ this._indicator.style.right = this._calcRightPos(this._activeTabLink)+'px';
86
+ }
87
+ }
88
+
89
+ _handleTabClick(e) {
90
+ const tabLink = e.target;
91
+ const tab = tabLink.parentElement;
92
+ // Handle click on tab link only
93
+ if (!tabLink || !tab.classList.contains('tab')) return;
94
+ // is disabled?
95
+ if (tab.classList.contains('disabled')) {
96
+ e.preventDefault();
97
+ return;
98
+ }
99
+ // Act as regular link if target attribute is specified.
100
+ if (tabLink.hasAttribute('target')) return;
101
+ // Make the old tab inactive.
102
+ this._activeTabLink.classList.remove('active');
103
+ const _oldContent = this._content;
104
+ // Update the variables with the new link and content
105
+
106
+ this._activeTabLink = tabLink;
107
+ this._content = document.querySelector(tabLink.hash);
108
+ this._tabLinks = this.el.querySelectorAll('li.tab > a');
109
+ // Make the tab active
110
+ this._activeTabLink.classList.add('active');
111
+ const prevIndex = this._index;
112
+ this._index = Math.max(Array.from(this._tabLinks).indexOf(tabLink), 0);
113
+
114
+ // Swap content
115
+ if (this.options.swipeable) {
116
+ if (this._tabsCarousel) {
117
+ this._tabsCarousel.set(this._index, () => {
118
+ if (typeof this.options.onShow === 'function')
119
+ this.options.onShow.call(this, this._content);
120
+ });
121
+ }
122
+ } else {
123
+ if (this._content) {
124
+ this._content.style.display = 'block';
125
+ this._content.classList.add('active');
126
+ if (typeof this.options.onShow === 'function')
127
+ this.options.onShow.call(this, this._content);
128
+ if (_oldContent && _oldContent !== this._content) {
129
+ _oldContent.style.display = 'none';
130
+ _oldContent.classList.remove('active');
131
+ }
132
+ }
133
+ }
134
+ // Update widths after content is swapped (scrollbar bugfix)
135
+ this._setTabsAndTabWidth();
136
+ this._animateIndicator(prevIndex);
137
+ e.preventDefault();
138
+ }
139
+
140
+ _createIndicator() {
141
+ const indicator = document.createElement('li');
142
+ indicator.classList.add('indicator');
143
+ this.el.appendChild(indicator);
144
+ this._indicator = indicator;
145
+ this._indicator.style.left = this._calcLeftPos(this._activeTabLink)+'px';
146
+ this._indicator.style.right = this._calcRightPos(this._activeTabLink)+'px';
147
+ }
148
+
149
+ _setupActiveTabLink() {
150
+ // If the location.hash matches one of the links, use that as the active tab.
151
+ this._activeTabLink = Array.from(this._tabLinks).find((a: HTMLAnchorElement) => a.getAttribute('href') === location.hash);
152
+ // If no match is found, use the first link or any with class 'active' as the initial active tab.
153
+ if (!this._activeTabLink) {
154
+ this._activeTabLink = this.el.querySelector('li.tab a.active');
155
+ }
156
+ if (this._activeTabLink.length === 0) {
157
+ this._activeTabLink = this.el.querySelector('li.tab a');
158
+ }
159
+ Array.from(this._tabLinks).forEach((a: HTMLAnchorElement) => a.classList.remove('active'));
160
+ this._activeTabLink.classList.add('active');
161
+
162
+ this._index = Math.max(Array.from(this._tabLinks).indexOf(this._activeTabLink), 0);
163
+ if (this._activeTabLink) {
164
+ this._content = document.querySelector(this._activeTabLink.hash);
165
+ this._content.classList.add('active');
166
+ }
167
+ }
168
+
169
+ _setupSwipeableTabs() {
170
+ // Change swipeable according to responsive threshold
171
+ if (window.innerWidth > this.options.responsiveThreshold)
172
+ this.options.swipeable = false;
173
+
174
+ const tabsContent = [];
175
+ this._tabLinks.forEach(a => {
176
+ const currContent = document.querySelector(a.hash);
177
+ currContent.classList.add('carousel-item');
178
+ tabsContent.push(currContent);
179
+ });
180
+
181
+ // Create Carousel-Wrapper around Tab-Contents
182
+ const tabsWrapper = document.createElement('div');
183
+ tabsWrapper.classList.add('tabs-content', 'carousel', 'carousel-slider');
184
+
185
+ // Wrap around
186
+ tabsContent[0].parentElement.insertBefore(tabsWrapper, tabsContent[0]);
187
+ tabsContent.forEach(tabContent => {
188
+ tabsWrapper.appendChild(tabContent);
189
+ tabContent.style.display = '';
190
+ });
191
+
192
+ // Keep active tab index to set initial carousel slide
193
+ const tab = this._activeTabLink.parentElement;
194
+ const activeTabIndex = Array.from(tab.parentNode.children).indexOf(tab);
195
+
196
+ this._tabsCarousel = Carousel.init(tabsWrapper, {
197
+ fullWidth: true,
198
+ noWrap: true,
199
+ onCycleTo: (item) => {
200
+ const prevIndex = this._index;
201
+ this._index = Array.from(item.parentNode.children).indexOf(item);
202
+ this._activeTabLink.classList.remove('active');
203
+ this._activeTabLink = Array.from(this._tabLinks)[this._index];
204
+ this._activeTabLink.classList.add('active');
205
+ this._animateIndicator(prevIndex);
206
+ if (typeof this.options.onShow === 'function')
207
+ this.options.onShow.call(this, this._content);
208
+ }
209
+ });
210
+ // Set initial carousel slide to active tab
211
+ this._tabsCarousel.set(activeTabIndex);
212
+ }
213
+
214
+ _teardownSwipeableTabs() {
215
+ const tabsWrapper = this._tabsCarousel.el;
216
+ this._tabsCarousel.destroy();
217
+ // Unwrap
218
+ tabsWrapper.after(tabsWrapper.children);
219
+ tabsWrapper.remove();
220
+ }
221
+
222
+ _setupNormalTabs() {
223
+ // Hide Tabs Content
224
+ Array.from(this._tabLinks).forEach(a => {
225
+ if (a === this._activeTabLink) return;
226
+ if ((<HTMLAnchorElement>a).hash) {
227
+ const currContent = document.querySelector((<HTMLAnchorElement>a).hash);
228
+ if (currContent) (<HTMLElement>currContent).style.display = 'none';
229
+ }
230
+ });
231
+ }
232
+
233
+ _teardownNormalTabs() {
234
+ // show Tabs Content
235
+ this._tabLinks.forEach(a => {
236
+ if (a.hash) {
237
+ const currContent = document.querySelector(a.hash);
238
+ if (currContent) currContent.style.display = '';
239
+ }
240
+ });
241
+ }
242
+
243
+ _setTabsAndTabWidth() {
244
+ this._tabsWidth = this.el.getBoundingClientRect().width;
245
+ this._tabWidth = Math.max(this._tabsWidth, this.el.scrollWidth) / this._tabLinks.length;
246
+ }
247
+
248
+ _calcRightPos(el) {
249
+ return Math.ceil(this._tabsWidth - el.offsetLeft - el.getBoundingClientRect().width);
250
+ }
251
+
252
+ _calcLeftPos(el) {
253
+ return Math.floor(el.offsetLeft);
254
+ }
255
+
256
+ updateTabIndicator() {
257
+ this._setTabsAndTabWidth();
258
+ this._animateIndicator(this._index);
259
+ }
260
+
261
+ _animateIndicator(prevIndex) {
262
+ let leftDelay = 0, rightDelay = 0;
263
+
264
+ if (this._index - prevIndex >= 0)
265
+ leftDelay = 90;
266
+ else
267
+ rightDelay = 90;
268
+
269
+ const animOptions = {
270
+ targets: this._indicator,
271
+ left: {
272
+ value: this._calcLeftPos(this._activeTabLink),
273
+ delay: leftDelay
274
+ },
275
+ right: {
276
+ value: this._calcRightPos(this._activeTabLink),
277
+ delay: rightDelay
278
+ },
279
+ duration: this.options.duration,
280
+ easing: 'easeOutQuad'
281
+ };
282
+ anim.remove(this._indicator);
283
+ anim(animOptions);
284
+ }
285
+
286
+ select(tabId) {
287
+ const tab = Array.from(this._tabLinks).find((a: HTMLAnchorElement) => a.getAttribute('href') === '#'+tabId);
288
+ if (tab) (<HTMLAnchorElement>tab).click();
289
+ }
290
+ }