@materializecss/materialize 1.2.2 → 2.0.1-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 (90) hide show
  1. package/Gruntfile.js +68 -313
  2. package/README.md +26 -14
  3. package/dist/css/materialize.css +1009 -1822
  4. package/dist/css/materialize.min.css +2 -8
  5. package/dist/js/materialize.js +8414 -12299
  6. package/dist/js/materialize.min.js +8968 -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/_theme_variables.scss +98 -0
  29. package/sass/components/_timepicker.scss +54 -46
  30. package/sass/components/_toast.scss +3 -3
  31. package/sass/components/_tooltip.scss +4 -5
  32. package/sass/components/_typography.scss +1 -1
  33. package/sass/components/_variables.scss +185 -120
  34. package/sass/components/forms/_checkboxes.scss +9 -9
  35. package/sass/components/forms/_file-input.scss +9 -7
  36. package/sass/components/forms/_input-fields.scss +173 -234
  37. package/sass/components/forms/_radio-buttons.scss +1 -1
  38. package/sass/components/forms/_range.scss +11 -11
  39. package/sass/components/forms/_select.scss +29 -19
  40. package/sass/components/forms/_switches.scss +22 -18
  41. package/sass/materialize.scss +1 -1
  42. package/src/autocomplete.ts +459 -0
  43. package/src/bounding.ts +6 -0
  44. package/{js/buttons.js → src/buttons.ts} +103 -162
  45. package/src/cards.ts +54 -0
  46. package/{js/carousel.js → src/carousel.ts} +137 -262
  47. package/src/characterCounter.ts +88 -0
  48. package/src/chips.ts +350 -0
  49. package/src/collapsible.ts +184 -0
  50. package/{js/component.js → src/component.ts} +6 -19
  51. package/{js/datepicker.js → src/datepicker.ts} +213 -299
  52. package/{js/dropdown.js → src/dropdown.ts} +140 -254
  53. package/src/edges.ts +6 -0
  54. package/src/forms.ts +120 -0
  55. package/src/global.ts +385 -0
  56. package/src/materialbox.ts +348 -0
  57. package/src/modal.ts +256 -0
  58. package/{js/parallax.js → src/parallax.ts} +47 -60
  59. package/{js/pushpin.js → src/pushpin.ts} +19 -47
  60. package/{js/range.js → src/range.ts} +58 -139
  61. package/{js/scrollspy.js → src/scrollspy.ts} +81 -153
  62. package/src/select.ts +448 -0
  63. package/{js/sidenav.js → src/sidenav.ts} +96 -202
  64. package/src/slider.ts +415 -0
  65. package/src/tabs.ts +293 -0
  66. package/src/tapTarget.ts +240 -0
  67. package/{js/timepicker.js → src/timepicker.ts} +268 -272
  68. package/{js/toasts.js → src/toasts.ts} +75 -134
  69. package/{js/tooltip.js → src/tooltip.ts} +59 -96
  70. package/src/waves.ts +70 -0
  71. package/extras/noUiSlider/nouislider.css +0 -404
  72. package/extras/noUiSlider/nouislider.js +0 -2147
  73. package/extras/noUiSlider/nouislider.min.js +0 -1
  74. package/js/anime.min.js +0 -34
  75. package/js/autocomplete.js +0 -479
  76. package/js/cards.js +0 -40
  77. package/js/cash.js +0 -960
  78. package/js/characterCounter.js +0 -136
  79. package/js/chips.js +0 -486
  80. package/js/collapsible.js +0 -275
  81. package/js/forms.js +0 -285
  82. package/js/global.js +0 -428
  83. package/js/materialbox.js +0 -453
  84. package/js/modal.js +0 -382
  85. package/js/select.js +0 -391
  86. package/js/slider.js +0 -497
  87. package/js/tabs.js +0 -402
  88. package/js/tapTarget.js +0 -315
  89. package/js/waves.js +0 -615
  90. 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,293 @@
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
+ if (tabLink.hash)
108
+ this._content = document.querySelector(tabLink.hash);
109
+ this._tabLinks = this.el.querySelectorAll('li.tab > a');
110
+ // Make the tab active
111
+ this._activeTabLink.classList.add('active');
112
+ const prevIndex = this._index;
113
+ this._index = Math.max(Array.from(this._tabLinks).indexOf(tabLink), 0);
114
+
115
+ // Swap content
116
+ if (this.options.swipeable) {
117
+ if (this._tabsCarousel) {
118
+ this._tabsCarousel.set(this._index, () => {
119
+ if (typeof this.options.onShow === 'function')
120
+ this.options.onShow.call(this, this._content);
121
+ });
122
+ }
123
+ } else {
124
+ if (this._content) {
125
+ this._content.style.display = 'block';
126
+ this._content.classList.add('active');
127
+ if (typeof this.options.onShow === 'function')
128
+ this.options.onShow.call(this, this._content);
129
+ if (_oldContent && _oldContent !== this._content) {
130
+ _oldContent.style.display = 'none';
131
+ _oldContent.classList.remove('active');
132
+ }
133
+ }
134
+ }
135
+ // Update widths after content is swapped (scrollbar bugfix)
136
+ this._setTabsAndTabWidth();
137
+ this._animateIndicator(prevIndex);
138
+ e.preventDefault();
139
+ }
140
+
141
+ _createIndicator() {
142
+ const indicator = document.createElement('li');
143
+ indicator.classList.add('indicator');
144
+ this.el.appendChild(indicator);
145
+ this._indicator = indicator;
146
+ this._indicator.style.left = this._calcLeftPos(this._activeTabLink)+'px';
147
+ this._indicator.style.right = this._calcRightPos(this._activeTabLink)+'px';
148
+ }
149
+
150
+ _setupActiveTabLink() {
151
+ // If the location.hash matches one of the links, use that as the active tab.
152
+ this._activeTabLink = Array.from(this._tabLinks).find((a: HTMLAnchorElement) => a.getAttribute('href') === location.hash);
153
+ // If no match is found, use the first link or any with class 'active' as the initial active tab.
154
+ if (!this._activeTabLink) {
155
+ this._activeTabLink = this.el.querySelector('li.tab a.active');
156
+ }
157
+ if (this._activeTabLink.length === 0) {
158
+ this._activeTabLink = this.el.querySelector('li.tab a');
159
+ }
160
+ Array.from(this._tabLinks).forEach((a: HTMLAnchorElement) => a.classList.remove('active'));
161
+ this._activeTabLink.classList.add('active');
162
+
163
+ this._index = Math.max(Array.from(this._tabLinks).indexOf(this._activeTabLink), 0);
164
+ if (this._activeTabLink && this._activeTabLink.hash) {
165
+ this._content = document.querySelector(this._activeTabLink.hash);
166
+ this._content.classList.add('active');
167
+ }
168
+ }
169
+
170
+ _setupSwipeableTabs() {
171
+ // Change swipeable according to responsive threshold
172
+ if (window.innerWidth > this.options.responsiveThreshold)
173
+ this.options.swipeable = false;
174
+
175
+ const tabsContent = [];
176
+ this._tabLinks.forEach(a => {
177
+ if (a.hash) {
178
+ const currContent = document.querySelector(a.hash);
179
+ currContent.classList.add('carousel-item');
180
+ tabsContent.push(currContent);
181
+ }
182
+ });
183
+
184
+ // Create Carousel-Wrapper around Tab-Contents
185
+ const tabsWrapper = document.createElement('div');
186
+ tabsWrapper.classList.add('tabs-content', 'carousel', 'carousel-slider');
187
+
188
+ // Wrap around
189
+ tabsContent[0].parentElement.insertBefore(tabsWrapper, tabsContent[0]);
190
+ tabsContent.forEach(tabContent => {
191
+ tabsWrapper.appendChild(tabContent);
192
+ tabContent.style.display = '';
193
+ });
194
+
195
+ // Keep active tab index to set initial carousel slide
196
+ const tab = this._activeTabLink.parentElement;
197
+ const activeTabIndex = Array.from(tab.parentNode.children).indexOf(tab);
198
+
199
+ this._tabsCarousel = Carousel.init(tabsWrapper, {
200
+ fullWidth: true,
201
+ noWrap: true,
202
+ onCycleTo: (item) => {
203
+ const prevIndex = this._index;
204
+ this._index = Array.from(item.parentNode.children).indexOf(item);
205
+ this._activeTabLink.classList.remove('active');
206
+ this._activeTabLink = Array.from(this._tabLinks)[this._index];
207
+ this._activeTabLink.classList.add('active');
208
+ this._animateIndicator(prevIndex);
209
+ if (typeof this.options.onShow === 'function')
210
+ this.options.onShow.call(this, this._content);
211
+ }
212
+ });
213
+ // Set initial carousel slide to active tab
214
+ this._tabsCarousel.set(activeTabIndex);
215
+ }
216
+
217
+ _teardownSwipeableTabs() {
218
+ const tabsWrapper = this._tabsCarousel.el;
219
+ this._tabsCarousel.destroy();
220
+ // Unwrap
221
+ tabsWrapper.after(tabsWrapper.children);
222
+ tabsWrapper.remove();
223
+ }
224
+
225
+ _setupNormalTabs() {
226
+ // Hide Tabs Content
227
+ Array.from(this._tabLinks).forEach(a => {
228
+ if (a === this._activeTabLink) return;
229
+ if ((<HTMLAnchorElement>a).hash) {
230
+ const currContent = document.querySelector((<HTMLAnchorElement>a).hash);
231
+ if (currContent) (<HTMLElement>currContent).style.display = 'none';
232
+ }
233
+ });
234
+ }
235
+
236
+ _teardownNormalTabs() {
237
+ // show Tabs Content
238
+ this._tabLinks.forEach(a => {
239
+ if (a.hash) {
240
+ const currContent = document.querySelector(a.hash);
241
+ if (currContent) currContent.style.display = '';
242
+ }
243
+ });
244
+ }
245
+
246
+ _setTabsAndTabWidth() {
247
+ this._tabsWidth = this.el.getBoundingClientRect().width;
248
+ this._tabWidth = Math.max(this._tabsWidth, this.el.scrollWidth) / this._tabLinks.length;
249
+ }
250
+
251
+ _calcRightPos(el) {
252
+ return Math.ceil(this._tabsWidth - el.offsetLeft - el.getBoundingClientRect().width);
253
+ }
254
+
255
+ _calcLeftPos(el) {
256
+ return Math.floor(el.offsetLeft);
257
+ }
258
+
259
+ updateTabIndicator() {
260
+ this._setTabsAndTabWidth();
261
+ this._animateIndicator(this._index);
262
+ }
263
+
264
+ _animateIndicator(prevIndex) {
265
+ let leftDelay = 0, rightDelay = 0;
266
+
267
+ if (this._index - prevIndex >= 0)
268
+ leftDelay = 90;
269
+ else
270
+ rightDelay = 90;
271
+
272
+ const animOptions = {
273
+ targets: this._indicator,
274
+ left: {
275
+ value: this._calcLeftPos(this._activeTabLink),
276
+ delay: leftDelay
277
+ },
278
+ right: {
279
+ value: this._calcRightPos(this._activeTabLink),
280
+ delay: rightDelay
281
+ },
282
+ duration: this.options.duration,
283
+ easing: 'easeOutQuad'
284
+ };
285
+ anim.remove(this._indicator);
286
+ anim(animOptions);
287
+ }
288
+
289
+ select(tabId) {
290
+ const tab = Array.from(this._tabLinks).find((a: HTMLAnchorElement) => a.getAttribute('href') === '#'+tabId);
291
+ if (tab) (<HTMLAnchorElement>tab).click();
292
+ }
293
+ }