kmaterialize 2.0.0 → 2.3.3-1.0.0
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.
- package/LICENSE +21 -21
- package/README.md +55 -91
- package/components/appbar/_navbar.old.scss +223 -0
- package/components/appbar/appbar.css +235 -0
- package/components/appbar/appbar.mjs +54 -0
- package/components/appbar/appbar.test.mjs +9 -0
- package/components/atomic/atomic.mjs +19 -0
- package/components/atomic/component.mjs +67 -0
- package/components/badge/_badges.scss +75 -0
- package/components/badge/badge.mjs +11 -0
- package/components/breadcrumb/_breadcrumb.scss +27 -0
- package/components/button/_buttons.scss +352 -0
- package/components/button/button.mjs +11 -0
- package/components/button/buttons.stories.ts +150 -0
- package/components/button/buttons.ts +282 -0
- package/components/button/examples.iso.js +18 -0
- package/components/button/fabSpec.js +65 -0
- package/components/button/test-button.mjs +16 -0
- package/components/card/_cards.scss +229 -0
- package/components/card/card.mjs +10 -0
- package/components/card/cards.ts +185 -0
- package/components/card/cardsSpec.js +269 -0
- package/components/carousel/_carousel.scss +93 -0
- package/components/carousel/_slider.scss +119 -0
- package/components/carousel/carousel.ts +693 -0
- package/components/carousel/carouselSpec.js +61 -0
- package/components/carousel/slider.ts +431 -0
- package/components/carousel/sliderSpec.js +99 -0
- package/components/checkbox/_checkboxes.scss +281 -0
- package/components/checkbox/checkbox.mdx +24 -0
- package/components/checkbox/checkbox.stories.ts +120 -0
- package/components/chip/chip.mjs +37 -0
- package/components/chip/chip.test.mjs +10 -0
- package/components/chip/chips.css +126 -0
- package/components/chip/chips.mdx +33 -0
- package/components/chip/chips.ts +440 -0
- package/components/chip/chipsSpec.js +165 -0
- package/components/chip/readme.md +323 -0
- package/components/collapsible/_collapsible.scss +82 -0
- package/components/collapsible/collapsible.ts +230 -0
- package/components/collapsible/collapsibleSpec.js +240 -0
- package/components/datepicker/_datepicker.scss +268 -0
- package/components/datepicker/datepicker.ts +1450 -0
- package/components/datepicker/datepickerSpec.js +162 -0
- package/components/dialog/_materialbox.scss +42 -0
- package/components/dialog/_modal.scss +79 -0
- package/components/dialog/materialbox.ts +446 -0
- package/components/dialog/materialboxSpec.js +37 -0
- package/components/dialog/modal.ts +111 -0
- package/components/dialog/modalSpec.js +83 -0
- package/components/dialog/readme.md +5 -0
- package/components/divider/readme.md +15 -0
- package/components/dropdown/_dropdown.scss +83 -0
- package/components/dropdown/dropdown.ts +653 -0
- package/components/dropdown/dropdownSpec.js +108 -0
- package/components/list/_collection.scss +114 -0
- package/components/list/_list.scss +6 -0
- package/components/list/list.mjs +58 -0
- package/components/list/list.test.mjs +16 -0
- package/components/loading/loading.mjs +26 -0
- package/components/loading/loading.test.mjs +10 -0
- package/components/navigation-bar/_old +582 -0
- package/components/navigation-bar/navigation-bar.mdx +20 -0
- package/components/navigation-bar/navigation-bar.stories.ts +39 -0
- package/components/navigation-drawer/_sidenav.scss +247 -0
- package/components/navigation-drawer/sidenav.ts +546 -0
- package/components/navigation-drawer/sidenavSpec.js +124 -0
- package/components/pagination/_pagination.scss +47 -0
- package/components/progress/_preloader.scss +416 -0
- package/components/radio-button/_radio-buttons.scss +112 -0
- package/components/radio-button/radio-buttons.mdx +28 -0
- package/components/radio-button/radio-buttons.stories.ts +51 -0
- package/components/search/autocomplete.ts +620 -0
- package/components/search/autocompleteSpec.js +321 -0
- package/components/slider/_range.scss +157 -0
- package/components/slider/range.ts +216 -0
- package/components/snackbar/_toast.scss +68 -0
- package/components/snackbar/toastSpec.js +132 -0
- package/components/snackbar/toasts.ts +312 -0
- package/components/switch/_switches.scss +121 -0
- package/components/tabs/_tabs.scss +171 -0
- package/components/tabs/tabs.mjs +31 -0
- package/components/tabs/tabs.ts +358 -0
- package/components/tabs/tabsSpec.js +180 -0
- package/components/textfield/_file-input.scss +41 -0
- package/components/textfield/_input-fields.scss +368 -0
- package/components/textfield/_select.scss +104 -0
- package/components/textfield/formfields.css +311 -0
- package/components/textfield/forms.ts +182 -0
- package/components/textfield/formsSpec.js +142 -0
- package/components/textfield/select.ts +472 -0
- package/components/textfield/selectSpec.js +390 -0
- package/components/textfield/textfield.mjs +29 -0
- package/components/textfield/textfield.test.mjs +10 -0
- package/components/timepicker/_timepicker.scss +362 -0
- package/components/timepicker/timepicker.ts +905 -0
- package/components/tooltip/_tooltip.scss +40 -0
- package/components/tooltip/tooltip.ts +380 -0
- package/components/tooltip/tooltipSpec.js +145 -0
- package/dist/css/materialize.colors.min.css +6 -0
- package/dist/css/materialize.css +7578 -6326
- package/dist/css/materialize.min.css +6 -13
- package/dist/css/materialize.min.css.map +1 -0
- package/dist/js/materialize.cjs.js +8410 -0
- package/dist/js/materialize.d.ts +2625 -0
- package/dist/js/materialize.js +8415 -12374
- package/dist/js/materialize.min.js +6 -6
- package/dist/js/materialize.mjs +8382 -0
- package/package.json +82 -47
- package/sass/_colors.scss +417 -0
- package/sass/_global.scss +490 -0
- package/sass/_grid.scss +172 -0
- package/sass/_table_of_contents.scss +28 -0
- package/sass/_tapTarget.scss +102 -0
- package/sass/_typography.scss +58 -0
- package/sass/_variables.scss +56 -0
- package/sass/materialize.scss +51 -0
- package/sass/mixins.module.scss +168 -0
- package/extras/noUiSlider/nouislider.css +0 -406
- package/extras/noUiSlider/nouislider.js +0 -2147
- package/extras/noUiSlider/nouislider.min.js +0 -1
- package/js/anime.min.js +0 -34
- package/js/autocomplete.js +0 -448
- package/js/buttons.js +0 -354
- package/js/cards.js +0 -40
- package/js/carousel.js +0 -717
- package/js/cash.js +0 -960
- package/js/characterCounter.js +0 -136
- package/js/chips.js +0 -481
- package/js/collapsible.js +0 -275
- package/js/component.js +0 -44
- package/js/datepicker.js +0 -975
- package/js/dropdown.js +0 -635
- package/js/forms.js +0 -275
- package/js/global.js +0 -444
- package/js/materialbox.js +0 -453
- package/js/modal.js +0 -382
- package/js/parallax.js +0 -138
- package/js/pushpin.js +0 -148
- package/js/range.js +0 -263
- package/js/scrollspy.js +0 -295
- package/js/select.js +0 -450
- package/js/sidenav.js +0 -580
- package/js/slider.js +0 -359
- package/js/tabs.js +0 -402
- package/js/tapTarget.js +0 -314
- package/js/timepicker.js +0 -647
- package/js/toasts.js +0 -310
- package/js/tooltip.js +0 -303
- package/js/waves.js +0 -335
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
describe('Carousel', () => {
|
|
2
|
+
const fixture = `<div class="carousel carousel-slider" id="slider-no-wrap">
|
|
3
|
+
<div class="carousel-item">
|
|
4
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">
|
|
5
|
+
</div>
|
|
6
|
+
<div class="carousel-item">
|
|
7
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">
|
|
8
|
+
</div>
|
|
9
|
+
<div class="carousel-item">
|
|
10
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">
|
|
11
|
+
</div>
|
|
12
|
+
<div class="carousel-item">
|
|
13
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">
|
|
14
|
+
</div>
|
|
15
|
+
</div>`;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => XloadHtml(fixture));
|
|
18
|
+
afterEach(() => XunloadFixtures());
|
|
19
|
+
|
|
20
|
+
describe('carousel plugin', () => {
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
M.Carousel.getInstance(document.querySelector('.carousel')).destroy();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('No wrap next and prev should not overflow', (done) => {
|
|
26
|
+
const noWrap = M.Carousel.init(document.querySelector('#slider-no-wrap'), {
|
|
27
|
+
duration: 10,
|
|
28
|
+
noWrap: true
|
|
29
|
+
});
|
|
30
|
+
noWrap.prev();
|
|
31
|
+
expect(noWrap.center).toEqual(0, 'Prev should do nothing');
|
|
32
|
+
|
|
33
|
+
noWrap.set(3);
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
expect(noWrap.center).toEqual(3);
|
|
36
|
+
|
|
37
|
+
noWrap.next();
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
expect(noWrap.center).toEqual(3, 'Next should do nothing');
|
|
40
|
+
done();
|
|
41
|
+
}, 30);
|
|
42
|
+
}, 50);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should change index and focus its respective item on indicator click', (done) => {
|
|
46
|
+
const carousel = M.Carousel.init(document.querySelector('.carousel'), {
|
|
47
|
+
duration: 10,
|
|
48
|
+
indicators: true
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
document.querySelectorAll('.indicator-item')[1].click();
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
expect(carousel.center).toEqual(
|
|
54
|
+
1,
|
|
55
|
+
'carousel item was not visible after indicator interaction'
|
|
56
|
+
);
|
|
57
|
+
done();
|
|
58
|
+
}, 30);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import { Utils } from '../../src/utils';
|
|
2
|
+
import { Component, BaseOptions, InitElements, MElement } from '../../src/component';
|
|
3
|
+
|
|
4
|
+
export interface SliderOptions extends BaseOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Set to false to hide slide indicators.
|
|
7
|
+
* @default true
|
|
8
|
+
*/
|
|
9
|
+
indicators: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Set height of slider.
|
|
12
|
+
* @default 400
|
|
13
|
+
*/
|
|
14
|
+
height: number;
|
|
15
|
+
/**
|
|
16
|
+
* Set the duration of the transition animation in ms.
|
|
17
|
+
* @default 500
|
|
18
|
+
*/
|
|
19
|
+
duration: number;
|
|
20
|
+
/**
|
|
21
|
+
* Set the duration between transitions in ms.
|
|
22
|
+
* @default 6000
|
|
23
|
+
*/
|
|
24
|
+
interval: number;
|
|
25
|
+
/**
|
|
26
|
+
* If slider should pause when keyboard focus is received.
|
|
27
|
+
* @default true
|
|
28
|
+
*/
|
|
29
|
+
pauseOnFocus: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* If slider should pause when is hovered by a pointer.
|
|
32
|
+
* @default true
|
|
33
|
+
*/
|
|
34
|
+
pauseOnHover: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Optional function used to generate ARIA label to indicators (for accessibility purposes).
|
|
37
|
+
* @param index Current index, starting from "1".
|
|
38
|
+
* @param current A which indicates whether it is the current element or not
|
|
39
|
+
* @returns a string to be used as label indicator.
|
|
40
|
+
* @default null
|
|
41
|
+
*/
|
|
42
|
+
indicatorLabelFunc: (index: number, current: boolean) => string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const _defaults: SliderOptions = {
|
|
46
|
+
indicators: true,
|
|
47
|
+
height: 400,
|
|
48
|
+
duration: 500,
|
|
49
|
+
interval: 6000,
|
|
50
|
+
pauseOnFocus: true,
|
|
51
|
+
pauseOnHover: true,
|
|
52
|
+
indicatorLabelFunc: null // Function which will generate a label for the indicators (ARIA)
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export class Slider extends Component<SliderOptions> {
|
|
56
|
+
/** Index of current slide. */
|
|
57
|
+
activeIndex: number;
|
|
58
|
+
interval: string | number | NodeJS.Timeout;
|
|
59
|
+
eventPause: boolean;
|
|
60
|
+
_slider: HTMLUListElement;
|
|
61
|
+
_slides: HTMLLIElement[];
|
|
62
|
+
_activeSlide: HTMLLIElement;
|
|
63
|
+
_indicators: HTMLLIElement[];
|
|
64
|
+
_hovered: boolean;
|
|
65
|
+
_focused: boolean;
|
|
66
|
+
_focusCurrent: boolean;
|
|
67
|
+
_sliderId: string;
|
|
68
|
+
|
|
69
|
+
constructor(el: HTMLElement, options: Partial<SliderOptions>) {
|
|
70
|
+
super(el, options, Slider);
|
|
71
|
+
this.el['M_Slider'] = this;
|
|
72
|
+
|
|
73
|
+
this.options = {
|
|
74
|
+
...Slider.defaults,
|
|
75
|
+
...options
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// init props
|
|
79
|
+
this.interval = null;
|
|
80
|
+
this.eventPause = false;
|
|
81
|
+
this._hovered = false;
|
|
82
|
+
this._focused = false;
|
|
83
|
+
this._focusCurrent = false;
|
|
84
|
+
|
|
85
|
+
// setup
|
|
86
|
+
this._slider = this.el.querySelector('.slides');
|
|
87
|
+
this._slides = Array.from(this._slider.querySelectorAll('li'));
|
|
88
|
+
this.activeIndex = this._slides.findIndex((li) => li.classList.contains('active'));
|
|
89
|
+
|
|
90
|
+
if (this.activeIndex !== -1) {
|
|
91
|
+
this._activeSlide = this._slides[this.activeIndex];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this._setSliderHeight();
|
|
95
|
+
|
|
96
|
+
// Sets element id if it does not have one
|
|
97
|
+
if (this._slider.hasAttribute('id')) this._sliderId = this._slider.getAttribute('id');
|
|
98
|
+
else {
|
|
99
|
+
this._sliderId = 'slider-' + Utils.guid();
|
|
100
|
+
this._slider.setAttribute('id', this._sliderId);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const placeholderBase64 =
|
|
104
|
+
'data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
|
105
|
+
// Set initial positions of captions
|
|
106
|
+
this._slides.forEach((slide) => {
|
|
107
|
+
// Caption
|
|
108
|
+
//const caption = <HTMLElement|null>slide.querySelector('.caption');
|
|
109
|
+
//if (caption) this._animateCaptionIn(caption, 0);
|
|
110
|
+
// Set Images as Background Images
|
|
111
|
+
const img = slide.querySelector('img');
|
|
112
|
+
if (img) {
|
|
113
|
+
if (img.src !== placeholderBase64) {
|
|
114
|
+
img.style.backgroundImage = 'url(' + img.src + ')';
|
|
115
|
+
img.src = placeholderBase64;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Sets slide as focusable by code
|
|
119
|
+
if (!slide.hasAttribute('tabindex')) slide.setAttribute('tabindex', '-1');
|
|
120
|
+
// Removes initial visibility from "inactive" slides
|
|
121
|
+
slide.style.visibility = 'hidden';
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
this._setupIndicators();
|
|
125
|
+
|
|
126
|
+
// Show active slide
|
|
127
|
+
if (this._activeSlide) {
|
|
128
|
+
this._activeSlide.style.display = 'block';
|
|
129
|
+
this._activeSlide.style.visibility = 'visible';
|
|
130
|
+
} else {
|
|
131
|
+
this.activeIndex = 0;
|
|
132
|
+
this._slides[0].classList.add('active');
|
|
133
|
+
this._slides[0].style.visibility = 'visible';
|
|
134
|
+
this._activeSlide = this._slides[0];
|
|
135
|
+
this._animateSlide(this._slides[0], true);
|
|
136
|
+
// Update indicators
|
|
137
|
+
if (this.options.indicators) {
|
|
138
|
+
this._indicators[this.activeIndex].children[0].classList.add('active');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
this._setupEventHandlers();
|
|
142
|
+
// auto scroll
|
|
143
|
+
this.start();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static get defaults() {
|
|
147
|
+
return _defaults;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Initializes instance of Slider.
|
|
152
|
+
* @param el HTML element.
|
|
153
|
+
* @param options Component options.
|
|
154
|
+
*/
|
|
155
|
+
static init(el: HTMLElement, options?: Partial<SliderOptions>): Slider;
|
|
156
|
+
/**
|
|
157
|
+
* Initializes instances of Slider.
|
|
158
|
+
* @param els HTML elements.
|
|
159
|
+
* @param options Component options.
|
|
160
|
+
*/
|
|
161
|
+
static init(els: InitElements<MElement>, options?: Partial<SliderOptions>): Slider[];
|
|
162
|
+
/**
|
|
163
|
+
* Initializes instances of Slider.
|
|
164
|
+
* @param els HTML elements.
|
|
165
|
+
* @param options Component options.
|
|
166
|
+
*/
|
|
167
|
+
static init(
|
|
168
|
+
els: HTMLElement | InitElements<MElement>,
|
|
169
|
+
options: Partial<SliderOptions> = {}
|
|
170
|
+
): Slider | Slider[] {
|
|
171
|
+
return super.init(els, options, Slider);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
static getInstance(el: HTMLElement): Slider {
|
|
175
|
+
return el['M_Slider'];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
destroy() {
|
|
179
|
+
this.pause();
|
|
180
|
+
this._removeIndicators();
|
|
181
|
+
this._removeEventHandlers();
|
|
182
|
+
this.el['M_Slider'] = undefined;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private _setupEventHandlers() {
|
|
186
|
+
if (this.options.pauseOnFocus) {
|
|
187
|
+
this.el.addEventListener('focusin', this._handleAutoPauseFocus);
|
|
188
|
+
this.el.addEventListener('focusout', this._handleAutoStartFocus);
|
|
189
|
+
}
|
|
190
|
+
if (this.options.pauseOnHover) {
|
|
191
|
+
this.el.addEventListener('mouseenter', this._handleAutoPauseHover);
|
|
192
|
+
this.el.addEventListener('mouseleave', this._handleAutoStartHover);
|
|
193
|
+
}
|
|
194
|
+
if (this.options.indicators) {
|
|
195
|
+
this._indicators.forEach((el) => {
|
|
196
|
+
el.addEventListener('click', this._handleIndicatorClick);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private _removeEventHandlers() {
|
|
202
|
+
if (this.options.pauseOnFocus) {
|
|
203
|
+
this.el.removeEventListener('focusin', this._handleAutoPauseFocus);
|
|
204
|
+
this.el.removeEventListener('focusout', this._handleAutoStartFocus);
|
|
205
|
+
}
|
|
206
|
+
if (this.options.pauseOnHover) {
|
|
207
|
+
this.el.removeEventListener('mouseenter', this._handleAutoPauseHover);
|
|
208
|
+
this.el.removeEventListener('mouseleave', this._handleAutoStartHover);
|
|
209
|
+
}
|
|
210
|
+
if (this.options.indicators) {
|
|
211
|
+
this._indicators.forEach((el) => {
|
|
212
|
+
el.removeEventListener('click', this._handleIndicatorClick);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private _handleIndicatorClick = (e: MouseEvent) => {
|
|
218
|
+
const el = (<HTMLElement>e.target).parentElement;
|
|
219
|
+
const currIndex = [...el.parentNode.children].indexOf(el);
|
|
220
|
+
this._focusCurrent = true;
|
|
221
|
+
this.set(currIndex);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
private _handleAutoPauseHover = () => {
|
|
225
|
+
this._hovered = true;
|
|
226
|
+
if (this.interval != null) {
|
|
227
|
+
this._pause(true);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
private _handleAutoPauseFocus = () => {
|
|
232
|
+
this._focused = true;
|
|
233
|
+
if (this.interval != null) {
|
|
234
|
+
this._pause(true);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
private _handleAutoStartHover = () => {
|
|
239
|
+
this._hovered = false;
|
|
240
|
+
if (!(this.options.pauseOnFocus && this._focused) && this.eventPause) {
|
|
241
|
+
this.start();
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
private _handleAutoStartFocus = () => {
|
|
246
|
+
this._focused = false;
|
|
247
|
+
if (!(this.options.pauseOnHover && this._hovered) && this.eventPause) {
|
|
248
|
+
this.start();
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
private _handleInterval = () => {
|
|
253
|
+
const activeElem = this._slider.querySelector('.active');
|
|
254
|
+
let newActiveIndex = [...activeElem.parentNode.children].indexOf(activeElem);
|
|
255
|
+
if (this._slides.length === newActiveIndex + 1) newActiveIndex = 0; // loop to start
|
|
256
|
+
else newActiveIndex += 1;
|
|
257
|
+
this.set(newActiveIndex);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
private _animateSlide(slide: HTMLElement, isDirectionIn: boolean): void {
|
|
261
|
+
let dx = 0,
|
|
262
|
+
dy = 0;
|
|
263
|
+
// from
|
|
264
|
+
slide.style.opacity = isDirectionIn ? '0' : '1';
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
slide.style.transition = `opacity ${this.options.duration}ms ease`;
|
|
267
|
+
// to
|
|
268
|
+
slide.style.opacity = isDirectionIn ? '1' : '0';
|
|
269
|
+
}, 1);
|
|
270
|
+
// Caption
|
|
271
|
+
const caption: HTMLElement = slide.querySelector('.caption');
|
|
272
|
+
if (!caption) return;
|
|
273
|
+
if (caption.classList.contains('center-align')) dy = -100;
|
|
274
|
+
else if (caption.classList.contains('right-align')) dx = 100;
|
|
275
|
+
else if (caption.classList.contains('left-align')) dx = -100;
|
|
276
|
+
// from
|
|
277
|
+
caption.style.opacity = isDirectionIn ? '0' : '1';
|
|
278
|
+
caption.style.transform = isDirectionIn ? `translate(${dx}px, ${dy}px)` : `translate(0, 0)`;
|
|
279
|
+
setTimeout(() => {
|
|
280
|
+
caption.style.transition = `opacity ${this.options.duration}ms ease, transform ${this.options.duration}ms ease`;
|
|
281
|
+
// to
|
|
282
|
+
caption.style.opacity = isDirectionIn ? '1' : '0';
|
|
283
|
+
caption.style.transform = isDirectionIn ? `translate(0, 0)` : `translate(${dx}px, ${dy}px)`;
|
|
284
|
+
}, this.options.duration); // delay
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private _setSliderHeight() {
|
|
288
|
+
// If fullscreen, do nothing
|
|
289
|
+
if (!this.el.classList.contains('fullscreen')) {
|
|
290
|
+
if (this.options.indicators) {
|
|
291
|
+
// Add height if indicators are present
|
|
292
|
+
this.el.style.height = this.options.height + 40 + 'px'; //.css('height', this.options.height + 40 + 'px');
|
|
293
|
+
} else {
|
|
294
|
+
this.el.style.height = this.options.height + 'px';
|
|
295
|
+
}
|
|
296
|
+
this._slider.style.height = this.options.height + 'px';
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private _setupIndicators() {
|
|
301
|
+
if (this.options.indicators) {
|
|
302
|
+
const ul = document.createElement('ul');
|
|
303
|
+
ul.classList.add('indicators');
|
|
304
|
+
|
|
305
|
+
const arrLi = [];
|
|
306
|
+
this._slides.forEach((el, i) => {
|
|
307
|
+
const label = this.options.indicatorLabelFunc
|
|
308
|
+
? this.options.indicatorLabelFunc.call(this, i + 1, i === 0)
|
|
309
|
+
: `${i + 1}`;
|
|
310
|
+
const li = document.createElement('li');
|
|
311
|
+
li.classList.add('indicator-item');
|
|
312
|
+
li.innerHTML = `<button type="button" class="indicator-item-btn" aria-label="${label}" aria-controls="${this._sliderId}"></button>`;
|
|
313
|
+
arrLi.push(li);
|
|
314
|
+
ul.append(li);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
this.el.append(ul);
|
|
318
|
+
this._indicators = arrLi;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private _removeIndicators() {
|
|
323
|
+
this.el.querySelector('ul.indicators').remove(); //find('ul.indicators').remove();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
set(index: number) {
|
|
327
|
+
// Wrap around indices.
|
|
328
|
+
if (index >= this._slides.length) index = 0;
|
|
329
|
+
else if (index < 0) index = this._slides.length - 1;
|
|
330
|
+
|
|
331
|
+
// Only do if index changes
|
|
332
|
+
if (this.activeIndex === index) return;
|
|
333
|
+
|
|
334
|
+
this._activeSlide = this._slides[this.activeIndex];
|
|
335
|
+
const _caption = <HTMLElement | null>this._activeSlide.querySelector('.caption');
|
|
336
|
+
|
|
337
|
+
this._activeSlide.classList.remove('active');
|
|
338
|
+
// Enables every slide
|
|
339
|
+
this._slides.forEach((slide) => (slide.style.visibility = 'visible'));
|
|
340
|
+
|
|
341
|
+
//--- Hide active Slide + Caption
|
|
342
|
+
this._activeSlide.style.opacity = '0';
|
|
343
|
+
setTimeout(() => {
|
|
344
|
+
this._slides.forEach((slide) => {
|
|
345
|
+
if (slide.classList.contains('active')) return;
|
|
346
|
+
slide.style.opacity = '0';
|
|
347
|
+
slide.style.transform = 'translate(0, 0)';
|
|
348
|
+
// Disables invisible slides (for assistive technologies)
|
|
349
|
+
slide.style.visibility = 'hidden';
|
|
350
|
+
});
|
|
351
|
+
}, this.options.duration);
|
|
352
|
+
|
|
353
|
+
// Hide active Caption
|
|
354
|
+
//this._animateCaptionIn(_caption, this.options.duration);
|
|
355
|
+
_caption.style.opacity = '0';
|
|
356
|
+
|
|
357
|
+
// Update indicators
|
|
358
|
+
if (this.options.indicators) {
|
|
359
|
+
const activeIndicator = this._indicators[this.activeIndex].children[0];
|
|
360
|
+
const nextIndicator = this._indicators[index].children[0];
|
|
361
|
+
activeIndicator.classList.remove('active');
|
|
362
|
+
nextIndicator.classList.add('active');
|
|
363
|
+
if (typeof this.options.indicatorLabelFunc === 'function') {
|
|
364
|
+
activeIndicator.ariaLabel = this.options.indicatorLabelFunc.call(
|
|
365
|
+
this,
|
|
366
|
+
this.activeIndex,
|
|
367
|
+
false
|
|
368
|
+
);
|
|
369
|
+
nextIndicator.ariaLabel = this.options.indicatorLabelFunc.call(this, index, true);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
//--- Show new Slide + Caption
|
|
374
|
+
this._animateSlide(this._slides[index], true);
|
|
375
|
+
this._slides[index].classList.add('active');
|
|
376
|
+
this.activeIndex = index;
|
|
377
|
+
|
|
378
|
+
// Reset interval, if allowed. This check prevents autostart
|
|
379
|
+
// when slider is paused, since it can be changed though indicators.
|
|
380
|
+
if (this.interval != null) {
|
|
381
|
+
this.start();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
_pause(fromEvent: boolean) {
|
|
386
|
+
clearInterval(this.interval);
|
|
387
|
+
this.eventPause = fromEvent;
|
|
388
|
+
this.interval = null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Pause slider autoslide.
|
|
393
|
+
*/
|
|
394
|
+
pause = () => {
|
|
395
|
+
this._pause(false);
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Start slider autoslide.
|
|
400
|
+
*/
|
|
401
|
+
start = () => {
|
|
402
|
+
clearInterval(this.interval);
|
|
403
|
+
this.interval = setInterval(
|
|
404
|
+
this._handleInterval,
|
|
405
|
+
this.options.duration + this.options.interval
|
|
406
|
+
);
|
|
407
|
+
this.eventPause = false;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Move to next slider.
|
|
412
|
+
*/
|
|
413
|
+
next = () => {
|
|
414
|
+
let newIndex = this.activeIndex + 1;
|
|
415
|
+
// Wrap around indices.
|
|
416
|
+
if (newIndex >= this._slides.length) newIndex = 0;
|
|
417
|
+
else if (newIndex < 0) newIndex = this._slides.length - 1;
|
|
418
|
+
this.set(newIndex);
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Move to prev slider.
|
|
423
|
+
*/
|
|
424
|
+
prev = () => {
|
|
425
|
+
let newIndex = this.activeIndex - 1;
|
|
426
|
+
// Wrap around indices.
|
|
427
|
+
if (newIndex >= this._slides.length) newIndex = 0;
|
|
428
|
+
else if (newIndex < 0) newIndex = this._slides.length - 1;
|
|
429
|
+
this.set(newIndex);
|
|
430
|
+
};
|
|
431
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
describe('Slider Plugin:', () => {
|
|
2
|
+
const fixture = `<div class="slider simple-slider">
|
|
3
|
+
<ul class="slides">
|
|
4
|
+
<li>
|
|
5
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="First slide">
|
|
6
|
+
<div class="caption center-align">
|
|
7
|
+
<h3>This is our big Tagline!</h3>
|
|
8
|
+
<h5 class="light grey-text text-lighten-3">Here's our small slogan.</h5>
|
|
9
|
+
</div>
|
|
10
|
+
</li>
|
|
11
|
+
<li>
|
|
12
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Second slide">
|
|
13
|
+
<div class="caption left-align">
|
|
14
|
+
<h3>Left Aligned Caption</h3>
|
|
15
|
+
<h5 class="light grey-text text-lighten-3">Here's our small slogan.</h5>
|
|
16
|
+
</div>
|
|
17
|
+
</li>
|
|
18
|
+
<li>
|
|
19
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Third slide">
|
|
20
|
+
<div class="caption right-align">
|
|
21
|
+
<h3>Right Aligned Caption</h3>
|
|
22
|
+
<h5 class="light grey-text text-lighten-3">Here's our small slogan.</h5>
|
|
23
|
+
</div>
|
|
24
|
+
</li>
|
|
25
|
+
<li>
|
|
26
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Fourth slide">
|
|
27
|
+
<div class="caption center-align">
|
|
28
|
+
<h3>This is our big Tagline!</h3>
|
|
29
|
+
<h5 class="light grey-text text-lighten-3">Here's our small slogan.</h5>
|
|
30
|
+
</div>
|
|
31
|
+
</li>
|
|
32
|
+
</ul>
|
|
33
|
+
</div>`;
|
|
34
|
+
|
|
35
|
+
beforeEach(() => XloadHtml(fixture));
|
|
36
|
+
afterEach(() => XunloadFixtures());
|
|
37
|
+
|
|
38
|
+
describe('Slider:', () => {
|
|
39
|
+
let slider;
|
|
40
|
+
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
slider = M.Slider.init(document.querySelector('.slider'), {
|
|
43
|
+
interval: 100,
|
|
44
|
+
duration: 0, // transition
|
|
45
|
+
pauseOnFocus: true,
|
|
46
|
+
indicatorLabelFunc: (idx) => 'Slide ' + idx
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
if (slider) slider.destroy();
|
|
52
|
+
slider = null;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should change after first interval', (done) => {
|
|
56
|
+
const O_INDEX = slider.activeIndex;
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
expect(slider.activeIndex).not.toBe(O_INDEX);
|
|
59
|
+
done();
|
|
60
|
+
}, 150);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should not change if paused', (done) => {
|
|
64
|
+
const O_INDEX = slider.activeIndex;
|
|
65
|
+
slider.pause();
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
expect(slider.activeIndex).toBe(O_INDEX);
|
|
68
|
+
done();
|
|
69
|
+
}, 200);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should not change if focused', (done) => {
|
|
73
|
+
const O_INDEX = slider.activeIndex;
|
|
74
|
+
slider.start();
|
|
75
|
+
slider.el.dispatchEvent(new Event('focusin'));
|
|
76
|
+
//focus(slider.el);
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
expect(slider.eventPause).toBe(true);
|
|
79
|
+
expect(slider.activeIndex).toBe(O_INDEX);
|
|
80
|
+
done();
|
|
81
|
+
}, 150);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("start with 'Slide ' in indicators labels ", () => {
|
|
85
|
+
expect(
|
|
86
|
+
Array.from(document.querySelectorAll('button')).map((btn) =>
|
|
87
|
+
btn.getAttribute('aria-label').startsWith('Slide ')
|
|
88
|
+
)
|
|
89
|
+
).toEqual([true, true, true, true]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should change index and focus its respective item on indicator click', () => {
|
|
93
|
+
const IDX = 2;
|
|
94
|
+
document.querySelectorAll('button')[IDX].click();
|
|
95
|
+
expect(slider.activeIndex).toBe(IDX);
|
|
96
|
+
//expect(document.activeElement).toBe(document.querySelectorAll(".slides > li")[IDX]);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|