@evermade/overflow-slider 3.1.0 → 3.2.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/README.md +59 -6
- package/dist/core/details.esm.js +3 -3
- package/dist/core/details.min.js +1 -1
- package/dist/core/overflow-slider.esm.js +1 -0
- package/dist/core/overflow-slider.min.js +1 -1
- package/dist/core/slider.esm.js +66 -20
- package/dist/core/slider.min.js +1 -1
- package/dist/overflow-slider.css +1 -1
- package/dist/plugins/arrows/arrows/index.esm.js +7 -4
- package/dist/plugins/arrows/arrows/index.min.js +1 -1
- package/dist/plugins/dots/dots/index.esm.js +0 -4
- package/dist/plugins/fade/fade/index.esm.js +4 -4
- package/dist/plugins/fade/fade/index.min.js +1 -1
- package/dist/plugins/full-width/full-width/index.esm.js +7 -2
- package/dist/plugins/full-width/full-width/index.min.js +1 -1
- package/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +18 -18
- package/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -1
- package/dist/plugins/thumbnails/thumbnails/index.esm.js +8 -5
- package/dist/plugins/thumbnails/thumbnails/index.min.js +1 -1
- package/docs/assets/demo.css +11 -1
- package/docs/assets/demo.js +38 -12
- package/docs/dist/core/details.esm.js +3 -3
- package/docs/dist/core/details.min.js +1 -1
- package/docs/dist/core/overflow-slider.esm.js +1 -0
- package/docs/dist/core/overflow-slider.min.js +1 -1
- package/docs/dist/core/slider.esm.js +66 -20
- package/docs/dist/core/slider.min.js +1 -1
- package/docs/dist/overflow-slider.css +1 -1
- package/docs/dist/plugins/arrows/arrows/index.esm.js +7 -4
- package/docs/dist/plugins/arrows/arrows/index.min.js +1 -1
- package/docs/dist/plugins/dots/dots/index.esm.js +0 -4
- package/docs/dist/plugins/fade/fade/index.esm.js +4 -4
- package/docs/dist/plugins/fade/fade/index.min.js +1 -1
- package/docs/dist/plugins/full-width/full-width/index.esm.js +7 -2
- package/docs/dist/plugins/full-width/full-width/index.min.js +1 -1
- package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +18 -18
- package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -1
- package/docs/dist/plugins/thumbnails/thumbnails/index.esm.js +8 -5
- package/docs/dist/plugins/thumbnails/thumbnails/index.min.js +1 -1
- package/docs/index-rtl.html +396 -0
- package/docs/index.html +1 -1
- package/package.json +1 -1
- package/src/core/details.ts +3 -3
- package/src/core/overflow-slider.ts +1 -0
- package/src/core/slider.ts +71 -21
- package/src/core/types.ts +3 -0
- package/src/plugins/arrows/index.ts +7 -5
- package/src/plugins/dots/index.ts +0 -4
- package/src/plugins/fade/index.ts +4 -4
- package/src/plugins/fade/styles.scss +10 -0
- package/src/plugins/full-width/index.ts +8 -2
- package/src/plugins/scroll-indicator/index.ts +60 -62
- package/src/plugins/thumbnails/index.ts +8 -5
package/src/core/details.ts
CHANGED
|
@@ -23,10 +23,10 @@ export default function details( slider: Slider) {
|
|
|
23
23
|
|
|
24
24
|
amountOfPages = Math.ceil(scrollableAreaWidth / containerWidth);
|
|
25
25
|
|
|
26
|
-
if ( Math.floor( slider.
|
|
27
|
-
currentPage = Math.floor(slider.
|
|
26
|
+
if ( Math.floor( slider.getScrollLeft() ) >= 0) {
|
|
27
|
+
currentPage = Math.floor(slider.getScrollLeft() / containerWidth);
|
|
28
28
|
// consider as last page if the scrollLeft + containerWidth is equal to scrollWidth
|
|
29
|
-
if (Math.floor( slider.
|
|
29
|
+
if (Math.floor( slider.getScrollLeft() + containerWidth ) === Math.floor( scrollableAreaWidth ) ) {
|
|
30
30
|
currentPage = amountOfPages - 1;
|
|
31
31
|
}
|
|
32
32
|
}
|
package/src/core/slider.ts
CHANGED
|
@@ -207,6 +207,9 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
207
207
|
|
|
208
208
|
function setDataAttributes() {
|
|
209
209
|
slider.container.setAttribute('data-has-overflow', slider.details.hasOverflow ? 'true' : 'false');
|
|
210
|
+
if ( slider.options.rtl ) {
|
|
211
|
+
slider.container.setAttribute('dir', 'rtl');
|
|
212
|
+
}
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
function ensureSlideIsInView( slide: HTMLElement, scrollBehavior: null|ScrollBehavior = null) {
|
|
@@ -237,24 +240,51 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
237
240
|
|
|
238
241
|
function setActiveSlideIdx() {
|
|
239
242
|
const sliderRect = slider.container.getBoundingClientRect();
|
|
240
|
-
const scrollLeft = slider.
|
|
243
|
+
const scrollLeft = slider.getScrollLeft();
|
|
241
244
|
const slides = slider.slides;
|
|
242
245
|
let activeSlideIdx = 0;
|
|
243
246
|
let scrolledPastLastSlide = false;
|
|
244
247
|
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
248
|
+
if (slider.options.rtl) {
|
|
249
|
+
const scrolledDistance = slider.getInclusiveScrollWidth() - scrollLeft - slider.getInclusiveClientWidth();
|
|
250
|
+
const slidePositions = [];
|
|
251
|
+
for (let i = slides.length - 1; i >= 0; i--) {
|
|
252
|
+
const slideRect = slides[i].getBoundingClientRect();
|
|
253
|
+
const slideEnd = Math.abs(slideRect.left) - Math.abs(sliderRect.left) + scrolledDistance;
|
|
254
|
+
slidePositions.push({
|
|
255
|
+
slide: slides[i],
|
|
256
|
+
slideEnd: slideEnd,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
let closestSlide = null;
|
|
260
|
+
let closestDistance = null;
|
|
261
|
+
for (let i = 0; i < slidePositions.length; i++) {
|
|
262
|
+
const distance = Math.abs(slidePositions[i].slideEnd - scrolledDistance);
|
|
263
|
+
if (closestDistance === null || distance < closestDistance) {
|
|
264
|
+
closestDistance = distance;
|
|
265
|
+
closestSlide = slidePositions[i].slide;
|
|
266
|
+
}
|
|
252
267
|
}
|
|
253
|
-
if (
|
|
254
|
-
|
|
268
|
+
if (closestSlide) {
|
|
269
|
+
activeSlideIdx = slides.indexOf(closestSlide);
|
|
270
|
+
} else {
|
|
271
|
+
activeSlideIdx = slides.length - 1;
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
for (let i = 0; i < slides.length; i++) {
|
|
275
|
+
const slideRect = slides[i].getBoundingClientRect();
|
|
276
|
+
const slideStart = slideRect.left - sliderRect.left + scrollLeft + getGapSize();
|
|
277
|
+
if (Math.floor(slideStart) >= Math.floor(scrollLeft)) {
|
|
278
|
+
activeSlideIdx = i;
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
if ( i === slides.length - 1 ) {
|
|
282
|
+
scrolledPastLastSlide = true;
|
|
283
|
+
}
|
|
255
284
|
}
|
|
256
285
|
}
|
|
257
286
|
|
|
287
|
+
|
|
258
288
|
if ( scrolledPastLastSlide ) {
|
|
259
289
|
activeSlideIdx = slides.length - 1;
|
|
260
290
|
}
|
|
@@ -262,6 +292,8 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
262
292
|
const oldActiveSlideIdx = slider.activeSlideIdx;
|
|
263
293
|
slider.activeSlideIdx = activeSlideIdx;
|
|
264
294
|
|
|
295
|
+
// console.log('activeSlideIdx', activeSlideIdx);
|
|
296
|
+
|
|
265
297
|
if (oldActiveSlideIdx !== activeSlideIdx) {
|
|
266
298
|
slider.emit('activeSlideChanged');
|
|
267
299
|
}
|
|
@@ -283,12 +315,20 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
283
315
|
return slider.container.clientWidth + getOutermostChildrenEdgeMarginSum(slider.container);
|
|
284
316
|
}
|
|
285
317
|
|
|
318
|
+
function getScrollLeft() : number {
|
|
319
|
+
return slider.options.rtl ? Math.abs(slider.container.scrollLeft) : slider.container.scrollLeft;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
function setScrollLeft(value: number) : void {
|
|
323
|
+
slider.container.scrollLeft = slider.options.rtl ? -value : value;
|
|
324
|
+
};
|
|
325
|
+
|
|
286
326
|
function getGapSize() : number {
|
|
287
327
|
let gapSize = 0;
|
|
288
328
|
if (slider.slides.length > 1) {
|
|
289
329
|
const firstSlideRect = slider.slides[0].getBoundingClientRect();
|
|
290
330
|
const secondSlideRect = slider.slides[1].getBoundingClientRect();
|
|
291
|
-
gapSize = Math.floor( secondSlideRect.left - firstSlideRect.right );
|
|
331
|
+
gapSize = slider.options.rtl ? Math.abs( Math.floor( secondSlideRect.right - firstSlideRect.left ) ) : Math.floor( secondSlideRect.left - firstSlideRect.right );
|
|
292
332
|
}
|
|
293
333
|
return gapSize;
|
|
294
334
|
};
|
|
@@ -308,22 +348,25 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
308
348
|
const sliderRect = slider.container.getBoundingClientRect();
|
|
309
349
|
const containerWidth = slider.container.offsetWidth;
|
|
310
350
|
let targetScrollPosition = scrollLeft;
|
|
311
|
-
|
|
351
|
+
|
|
352
|
+
const realDirection = slider.options.rtl ? (direction === 'prev' ? 'next' : 'prev') : direction;
|
|
353
|
+
|
|
354
|
+
if (realDirection === 'prev') {
|
|
312
355
|
targetScrollPosition = Math.max(0, scrollLeft - slider.container.offsetWidth);
|
|
313
|
-
} else if (
|
|
356
|
+
} else if (realDirection === 'next') {
|
|
314
357
|
targetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), scrollLeft + slider.container.offsetWidth);
|
|
315
358
|
}
|
|
316
359
|
if (scrollStrategy === 'fullSlide') {
|
|
317
360
|
let fullSlideTargetScrollPosition = null;
|
|
318
361
|
|
|
319
362
|
// extend targetScrollPosition to include gap
|
|
320
|
-
if (
|
|
363
|
+
if (realDirection === 'prev') {
|
|
321
364
|
fullSlideTargetScrollPosition = Math.max(0, targetScrollPosition - getGapSize());
|
|
322
365
|
} else {
|
|
323
366
|
fullSlideTargetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), targetScrollPosition + getGapSize());
|
|
324
367
|
}
|
|
325
368
|
|
|
326
|
-
if (
|
|
369
|
+
if (realDirection === 'next') {
|
|
327
370
|
let partialSlideFound = false;
|
|
328
371
|
for (let slide of slider.slides) {
|
|
329
372
|
const slideRect = slide.getBoundingClientRect();
|
|
@@ -383,12 +426,12 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
383
426
|
};
|
|
384
427
|
|
|
385
428
|
function snapToClosestSlide(direction = "prev") {
|
|
386
|
-
const isMovingForward = direction === 'next';
|
|
429
|
+
const isMovingForward = slider.options.rtl ? direction === 'prev' : direction === 'next';
|
|
387
430
|
const slideReference = [];
|
|
388
431
|
for (let i = 0; i < slider.slides.length; i++) {
|
|
389
432
|
const slide = slider.slides[i];
|
|
390
433
|
const slideWidth = slide.offsetWidth;
|
|
391
|
-
const slideStart = slide.offsetLeft;
|
|
434
|
+
const slideStart = slider.options.rtl ? Math.abs( slide.offsetLeft + slideWidth - slider.details.containerWidth ) : slide.offsetLeft;
|
|
392
435
|
const slideEnd = slideStart + slideWidth;
|
|
393
436
|
const slideMiddle = slideStart + slideWidth / 2;
|
|
394
437
|
const trigger = Math.min(slideMiddle, slideStart + slider.options.emulateScrollSnapMaxThreshold);
|
|
@@ -399,10 +442,14 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
399
442
|
width: slideWidth,
|
|
400
443
|
trigger: trigger,
|
|
401
444
|
slide: slide,
|
|
445
|
+
// debug
|
|
446
|
+
offSetleft: slide.offsetLeft,
|
|
447
|
+
rect: slide.getBoundingClientRect(),
|
|
402
448
|
});
|
|
403
449
|
}
|
|
450
|
+
console.log('slideReference', slideReference);
|
|
404
451
|
let snapTarget = null;
|
|
405
|
-
const scrollPosition =
|
|
452
|
+
const scrollPosition = getScrollLeft();
|
|
406
453
|
if (isMovingForward) {
|
|
407
454
|
for (let i = 0; i < slideReference.length; i++) {
|
|
408
455
|
const item = slideReference[i];
|
|
@@ -410,7 +457,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
410
457
|
snapTarget = 0;
|
|
411
458
|
break;
|
|
412
459
|
}
|
|
413
|
-
if ( Math.floor(
|
|
460
|
+
if ( Math.floor( getScrollLeft() ) <= Math.floor( item.trigger ) ) {
|
|
414
461
|
snapTarget = item.start;
|
|
415
462
|
break;
|
|
416
463
|
}
|
|
@@ -422,7 +469,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
422
469
|
snapTarget = item.start;
|
|
423
470
|
break;
|
|
424
471
|
}
|
|
425
|
-
if ( Math.floor(
|
|
472
|
+
if ( Math.floor( getScrollLeft() ) >= Math.floor( item.trigger ) ) {
|
|
426
473
|
snapTarget = item.start;
|
|
427
474
|
break;
|
|
428
475
|
}
|
|
@@ -435,8 +482,9 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
435
482
|
}
|
|
436
483
|
|
|
437
484
|
const scrollBehavior = slider.options.scrollBehavior || 'smooth';
|
|
485
|
+
|
|
438
486
|
slider.container.scrollTo({
|
|
439
|
-
left: snapTarget,
|
|
487
|
+
left: slider.options.rtl ? -snapTarget : snapTarget,
|
|
440
488
|
behavior: scrollBehavior as ScrollBehavior
|
|
441
489
|
});
|
|
442
490
|
}
|
|
@@ -468,6 +516,8 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
|
|
|
468
516
|
snapToClosestSlide,
|
|
469
517
|
getInclusiveScrollWidth,
|
|
470
518
|
getInclusiveClientWidth,
|
|
519
|
+
getScrollLeft,
|
|
520
|
+
setScrollLeft,
|
|
471
521
|
on,
|
|
472
522
|
options,
|
|
473
523
|
};
|
package/src/core/types.ts
CHANGED
|
@@ -13,6 +13,8 @@ export type Slider<O = {}, C = {}, H extends string = string> = {
|
|
|
13
13
|
) => void
|
|
14
14
|
getInclusiveScrollWidth: () => number
|
|
15
15
|
getInclusiveClientWidth: () => number
|
|
16
|
+
getScrollLeft: () => number
|
|
17
|
+
setScrollLeft: (value: number) => void
|
|
16
18
|
on: (
|
|
17
19
|
name: H | SliderHooks,
|
|
18
20
|
cb: (props: Slider<O, C, H>) => void
|
|
@@ -28,6 +30,7 @@ export type SliderOptions = {
|
|
|
28
30
|
slidesSelector: string;
|
|
29
31
|
emulateScrollSnap: boolean;
|
|
30
32
|
emulateScrollSnapMaxThreshold: number;
|
|
33
|
+
rtl: boolean;
|
|
31
34
|
[key: string]: any;
|
|
32
35
|
}
|
|
33
36
|
|
|
@@ -65,7 +65,7 @@ export default function ArrowsPlugin( args: { [key: string]: any } ) {
|
|
|
65
65
|
prev.setAttribute( 'aria-label', options.texts.buttonPrevious );
|
|
66
66
|
prev.setAttribute( 'aria-controls', slider.container.getAttribute( 'id' ) ?? '');
|
|
67
67
|
prev.setAttribute( 'data-type', 'prev' );
|
|
68
|
-
prev.innerHTML = options.icons.prev;
|
|
68
|
+
prev.innerHTML = slider.options.rtl ? options.icons.next : options.icons.prev;
|
|
69
69
|
prev.addEventListener( 'click', () => {
|
|
70
70
|
if ( prev.getAttribute('data-has-content') === 'true' ) {
|
|
71
71
|
slider.moveToDirection( 'prev' );
|
|
@@ -78,7 +78,7 @@ export default function ArrowsPlugin( args: { [key: string]: any } ) {
|
|
|
78
78
|
next.setAttribute( 'aria-label', options.texts.buttonNext );
|
|
79
79
|
next.setAttribute( 'aria-controls', slider.container.getAttribute( 'id' ) ?? '');
|
|
80
80
|
next.setAttribute( 'data-type', 'next' );
|
|
81
|
-
next.innerHTML = options.icons.next;
|
|
81
|
+
next.innerHTML = slider.options.rtl ? options.icons.prev : options.icons.next;
|
|
82
82
|
next.addEventListener( 'click', () => {
|
|
83
83
|
if ( next.getAttribute('data-has-content') === 'true' ) {
|
|
84
84
|
slider.moveToDirection( 'next' );
|
|
@@ -90,20 +90,22 @@ export default function ArrowsPlugin( args: { [key: string]: any } ) {
|
|
|
90
90
|
nav.appendChild( next );
|
|
91
91
|
|
|
92
92
|
const update = () => {
|
|
93
|
-
const scrollLeft = slider.
|
|
93
|
+
const scrollLeft = slider.getScrollLeft();
|
|
94
94
|
const scrollWidth = slider.getInclusiveScrollWidth();
|
|
95
95
|
const clientWidth = slider.getInclusiveClientWidth();
|
|
96
|
-
const buffer =
|
|
96
|
+
const buffer = 1;
|
|
97
97
|
if ( Math.floor( scrollLeft ) === 0 ) {
|
|
98
98
|
prev.setAttribute( 'data-has-content', 'false' );
|
|
99
99
|
} else {
|
|
100
100
|
prev.setAttribute( 'data-has-content', 'true' );
|
|
101
101
|
}
|
|
102
|
-
|
|
102
|
+
const maxWidthDifference = Math.abs( Math.floor( scrollLeft + clientWidth ) - Math.floor( scrollWidth ) );
|
|
103
|
+
if ( maxWidthDifference <= buffer ) {
|
|
103
104
|
next.setAttribute( 'data-has-content', 'false' );
|
|
104
105
|
} else {
|
|
105
106
|
next.setAttribute( 'data-has-content', 'true' );
|
|
106
107
|
}
|
|
108
|
+
console.log( 'next', scrollLeft + clientWidth, scrollWidth );
|
|
107
109
|
};
|
|
108
110
|
|
|
109
111
|
if ( options.containerNext && options.containerPrev ) {
|
|
@@ -105,10 +105,6 @@ export default function DotsPlugin( args: { [key: string]: any } ) {
|
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
const activateDot = ( item: number ) => {
|
|
108
|
-
// const scrollTargetPosition = slider.details.containerWidth * ( page - 1 );
|
|
109
|
-
// slider.container.style.scrollBehavior = slider.options.scrollBehavior;
|
|
110
|
-
// slider.container.scrollLeft = scrollTargetPosition;
|
|
111
|
-
// slider.container.style.scrollBehavior = '';
|
|
112
108
|
slider.moveToSlide( item - 1 );
|
|
113
109
|
};
|
|
114
110
|
|
|
@@ -48,11 +48,11 @@ export default function FadePlugin( args: { [key: string]: any } ) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const hasFadeAtStart = () => {
|
|
51
|
-
return slider.
|
|
51
|
+
return slider.getScrollLeft() > fadeItemStart.offsetWidth;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const fadeAtStartOpacity = () => {
|
|
55
|
-
const position = slider.
|
|
55
|
+
const position = slider.getScrollLeft();
|
|
56
56
|
if ( Math.floor( position ) <= Math.floor( fadeItemStart.offsetWidth ) ) {
|
|
57
57
|
return position / Math.max(fadeItemStart.offsetWidth, 1);
|
|
58
58
|
}
|
|
@@ -60,11 +60,11 @@ export default function FadePlugin( args: { [key: string]: any } ) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
const hasFadeAtEnd = () => {
|
|
63
|
-
return Math.floor( slider.
|
|
63
|
+
return Math.floor( slider.getScrollLeft() ) < Math.floor( slider.getInclusiveScrollWidth() - slider.getInclusiveClientWidth() - fadeItemEnd.offsetWidth );
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
const fadeAtEndOpacity = () => {
|
|
67
|
-
const position = slider.
|
|
67
|
+
const position = slider.getScrollLeft();
|
|
68
68
|
const maxPosition = slider.getInclusiveScrollWidth() - slider.getInclusiveClientWidth();
|
|
69
69
|
const maxFadePosition = maxPosition - fadeItemEnd.offsetWidth;
|
|
70
70
|
if ( Math.floor( position ) >= Math.floor( maxFadePosition ) ) {
|
|
@@ -19,9 +19,19 @@
|
|
|
19
19
|
.overflow-slider-fade--start {
|
|
20
20
|
left: 0;
|
|
21
21
|
background: linear-gradient(to right, var(--overflow-slider-fade-color) 0%, transparent 100%);
|
|
22
|
+
[dir="rtl"] & {
|
|
23
|
+
left: auto;
|
|
24
|
+
right: 0;
|
|
25
|
+
background: linear-gradient(to left, var(--overflow-slider-fade-color) 0%, transparent 100%);
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
.overflow-slider-fade--end {
|
|
25
30
|
right: 0;
|
|
26
31
|
background: linear-gradient(to left, var(--overflow-slider-fade-color) 0%, transparent 100%);
|
|
32
|
+
[dir="rtl"] & {
|
|
33
|
+
right: auto;
|
|
34
|
+
left: 0;
|
|
35
|
+
background: linear-gradient(to right, var(--overflow-slider-fade-color) 0%, transparent 100%);
|
|
36
|
+
}
|
|
27
37
|
}
|
|
@@ -29,16 +29,22 @@ export default function FullWidthPlugin( args: { [key: string]: any } ) {
|
|
|
29
29
|
|
|
30
30
|
const marginAmount = Math.floor((window.innerWidth - options.targetWidth(slider)) / 2);
|
|
31
31
|
if ( options.addMarginBefore ) {
|
|
32
|
-
firstSlide.style.
|
|
32
|
+
firstSlide.style.marginInlineStart = `${marginAmount}px`;
|
|
33
33
|
}
|
|
34
34
|
if ( options.addMarginAfter ) {
|
|
35
|
-
lastSlide.style.
|
|
35
|
+
lastSlide.style.marginInlineEnd = `${marginAmount}px`;
|
|
36
36
|
}
|
|
37
37
|
slider.container.setAttribute( 'data-full-width-offset', marginAmount.toString() );
|
|
38
|
+
setCSS();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const setCSS = () => {
|
|
42
|
+
slider.container.style.setProperty('--slider-container-target-width', `${options.targetWidth(slider)}px`);
|
|
38
43
|
};
|
|
39
44
|
|
|
40
45
|
update();
|
|
41
46
|
slider.on( 'contentsChanged', update );
|
|
42
47
|
slider.on( 'containerSizeChanged', update );
|
|
48
|
+
window.addEventListener( 'resize', setCSS );
|
|
43
49
|
};
|
|
44
50
|
}
|
|
@@ -15,8 +15,8 @@ export type ScrollIndicatorOptions = {
|
|
|
15
15
|
container: HTMLElement | null,
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
export default function ScrollIndicatorPlugin(
|
|
19
|
-
return (
|
|
18
|
+
export default function ScrollIndicatorPlugin(args: { [key: string]: any }) {
|
|
19
|
+
return (slider: Slider) => {
|
|
20
20
|
|
|
21
21
|
const options = <ScrollIndicatorOptions>{
|
|
22
22
|
classNames: {
|
|
@@ -26,42 +26,44 @@ export default function ScrollIndicatorPlugin( args: { [key: string]: any } ) {
|
|
|
26
26
|
container: args?.container ?? null,
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
const scrollbarContainer = document.createElement('div');
|
|
30
|
+
scrollbarContainer.setAttribute('class', options.classNames.scrollIndicator);
|
|
31
|
+
scrollbarContainer.setAttribute('tabindex', '0');
|
|
32
|
+
scrollbarContainer.setAttribute('role', 'scrollbar');
|
|
33
|
+
scrollbarContainer.setAttribute('aria-controls', slider.container.getAttribute('id') ?? '');
|
|
34
|
+
scrollbarContainer.setAttribute('aria-orientation', 'horizontal');
|
|
35
|
+
scrollbarContainer.setAttribute('aria-valuemax', '100');
|
|
36
|
+
scrollbarContainer.setAttribute('aria-valuemin', '0');
|
|
37
|
+
scrollbarContainer.setAttribute('aria-valuenow', '0');
|
|
29
38
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
scrollbarContainer.setAttribute( 'tabindex', '0' );
|
|
33
|
-
scrollbarContainer.setAttribute( 'role', 'scrollbar' );
|
|
34
|
-
scrollbarContainer.setAttribute( 'aria-controls', slider.container.getAttribute( 'id' ) ?? '' );
|
|
35
|
-
scrollbarContainer.setAttribute( 'aria-orientation', 'horizontal' );
|
|
36
|
-
scrollbarContainer.setAttribute( 'aria-valuemax', '100' );
|
|
37
|
-
scrollbarContainer.setAttribute( 'aria-valuemin', '0' );
|
|
38
|
-
scrollbarContainer.setAttribute( 'aria-valuenow', '0' );
|
|
39
|
+
const scrollbar = document.createElement('div');
|
|
40
|
+
scrollbar.setAttribute('class', options.classNames.scrollIndicatorBar);
|
|
39
41
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
+
const scrollbarButton = document.createElement('div');
|
|
43
|
+
scrollbarButton.setAttribute('class', options.classNames.scrollIndicatorButton);
|
|
44
|
+
scrollbarButton.setAttribute('data-is-grabbed', 'false');
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
scrollbarButton.setAttribute( 'data-is-grabbed', 'false' );
|
|
46
|
-
|
|
47
|
-
scrollbar.appendChild( scrollbarButton );
|
|
48
|
-
scrollbarContainer.appendChild( scrollbar );
|
|
46
|
+
scrollbar.appendChild(scrollbarButton);
|
|
47
|
+
scrollbarContainer.appendChild(scrollbar);
|
|
49
48
|
|
|
50
49
|
const setDataAttributes = () => {
|
|
51
|
-
scrollbarContainer.setAttribute(
|
|
50
|
+
scrollbarContainer.setAttribute('data-has-overflow', slider.details.hasOverflow.toString());
|
|
52
51
|
}
|
|
53
52
|
setDataAttributes();
|
|
54
53
|
|
|
55
54
|
const getScrollbarButtonLeftOffset = () => {
|
|
56
|
-
const
|
|
57
|
-
|
|
55
|
+
const contentRatio = scrollbarButton.offsetWidth / slider.details.containerWidth;
|
|
56
|
+
const scrollAmount = slider.getScrollLeft() * contentRatio;
|
|
57
|
+
if (slider.options.rtl) {
|
|
58
|
+
return scrollbar.offsetWidth - scrollbarButton.offsetWidth - scrollAmount;
|
|
59
|
+
}
|
|
60
|
+
return scrollAmount;
|
|
58
61
|
};
|
|
59
62
|
|
|
60
|
-
// scrollbarbutton width and position is calculated based on the scroll position and available width
|
|
61
63
|
let requestId = 0;
|
|
62
64
|
const update = () => {
|
|
63
|
-
if (
|
|
64
|
-
window.cancelAnimationFrame(
|
|
65
|
+
if (requestId) {
|
|
66
|
+
window.cancelAnimationFrame(requestId);
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
requestId = window.requestAnimationFrame(() => {
|
|
@@ -71,64 +73,60 @@ export default function ScrollIndicatorPlugin( args: { [key: string]: any } ) {
|
|
|
71
73
|
scrollbarButton.style.width = `${scrollbarButtonWidth}%`;
|
|
72
74
|
scrollbarButton.style.transform = `translateX(${scrollLeftInPortion}px)`;
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
const scrollLeft = slider.container.scrollLeft;
|
|
76
|
+
const scrollLeft = slider.getScrollLeft();
|
|
76
77
|
const scrollWidth = slider.getInclusiveScrollWidth();
|
|
77
78
|
const containerWidth = slider.container.offsetWidth;
|
|
78
79
|
const scrollPercentage = (scrollLeft / (scrollWidth - containerWidth)) * 100;
|
|
79
|
-
scrollbarContainer.setAttribute(
|
|
80
|
+
scrollbarContainer.setAttribute('aria-valuenow', Math.round(Number.isNaN(scrollPercentage) ? 0 : scrollPercentage).toString());
|
|
80
81
|
});
|
|
81
82
|
};
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
options.container.appendChild( scrollbarContainer );
|
|
84
|
+
if (options.container) {
|
|
85
|
+
options.container.appendChild(scrollbarContainer);
|
|
86
86
|
} else {
|
|
87
|
-
slider.container.parentNode?.insertBefore(
|
|
87
|
+
slider.container.parentNode?.insertBefore(scrollbarContainer, slider.container.nextSibling);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
// update the scrollbar when the slider is scrolled
|
|
91
90
|
update();
|
|
92
|
-
slider.on(
|
|
93
|
-
slider.on(
|
|
94
|
-
slider.on(
|
|
95
|
-
slider.on(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
slider.moveToDirection( 'next' );
|
|
91
|
+
slider.on('scroll', update);
|
|
92
|
+
slider.on('contentsChanged', update);
|
|
93
|
+
slider.on('containerSizeChanged', update);
|
|
94
|
+
slider.on('detailsChanged', setDataAttributes);
|
|
95
|
+
|
|
96
|
+
scrollbarContainer.addEventListener('keydown', (e) => {
|
|
97
|
+
if (e.key === 'ArrowLeft') {
|
|
98
|
+
slider.moveToDirection('prev');
|
|
99
|
+
} else if (e.key === 'ArrowRight') {
|
|
100
|
+
slider.moveToDirection('next');
|
|
103
101
|
}
|
|
104
102
|
});
|
|
105
103
|
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
let isInteractionDown = false;
|
|
105
|
+
let startX = 0;
|
|
106
|
+
let scrollLeft = slider.getScrollLeft();
|
|
107
|
+
|
|
108
|
+
scrollbarContainer.addEventListener('click', (e) => {
|
|
109
|
+
if ( e.target == scrollbarButton ) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
108
112
|
const scrollbarButtonWidth = scrollbarButton.offsetWidth;
|
|
109
113
|
const scrollbarButtonLeft = getScrollbarButtonLeftOffset();
|
|
110
114
|
const scrollbarButtonRight = scrollbarButtonLeft + scrollbarButtonWidth;
|
|
111
|
-
const clickX = e.pageX - scrollbarContainer.offsetLeft;
|
|
112
|
-
if (
|
|
113
|
-
slider.moveToDirection( 'prev'
|
|
114
|
-
} else if (
|
|
115
|
-
slider.moveToDirection( 'next'
|
|
115
|
+
const clickX = e.pageX - Math.abs( scrollbarContainer.offsetLeft );
|
|
116
|
+
if (Math.floor(clickX) < Math.floor(scrollbarButtonLeft)) {
|
|
117
|
+
slider.moveToDirection(slider.options.rtl ? 'next' : 'prev');
|
|
118
|
+
} else if (Math.floor(clickX) > Math.floor(scrollbarButtonRight)) {
|
|
119
|
+
slider.moveToDirection(slider.options.rtl ? 'prev' : 'next');
|
|
116
120
|
}
|
|
117
121
|
});
|
|
118
122
|
|
|
119
|
-
// make scrollbar button draggable via mouse/touch and update the scroll position
|
|
120
|
-
let isInteractionDown = false;
|
|
121
|
-
let startX = 0;
|
|
122
|
-
let scrollLeft = 0;
|
|
123
|
-
|
|
124
123
|
const onInteractionDown = (e: MouseEvent | TouchEvent) => {
|
|
125
124
|
isInteractionDown = true;
|
|
126
125
|
const pageX = (e as MouseEvent).pageX || (e as TouchEvent).touches[0].pageX;
|
|
127
126
|
startX = pageX - scrollbarContainer.offsetLeft;
|
|
128
|
-
scrollLeft = slider.
|
|
129
|
-
// change cursor to grabbing
|
|
127
|
+
scrollLeft = slider.getScrollLeft();
|
|
130
128
|
scrollbarButton.style.cursor = 'grabbing';
|
|
131
|
-
scrollbarButton.setAttribute(
|
|
129
|
+
scrollbarButton.setAttribute('data-is-grabbed', 'true');
|
|
132
130
|
|
|
133
131
|
e.preventDefault();
|
|
134
132
|
e.stopPropagation();
|
|
@@ -144,13 +142,14 @@ export default function ScrollIndicatorPlugin( args: { [key: string]: any } ) {
|
|
|
144
142
|
const scrollingFactor = slider.details.scrollableAreaWidth / scrollbarContainer.offsetWidth;
|
|
145
143
|
|
|
146
144
|
const walk = (x - startX) * scrollingFactor;
|
|
147
|
-
slider.
|
|
145
|
+
const distance = slider.options.rtl ? scrollLeft - walk : scrollLeft + walk;
|
|
146
|
+
slider.setScrollLeft(distance);
|
|
148
147
|
};
|
|
149
148
|
|
|
150
149
|
const onInteractionUp = () => {
|
|
151
150
|
isInteractionDown = false;
|
|
152
151
|
scrollbarButton.style.cursor = '';
|
|
153
|
-
scrollbarButton.setAttribute(
|
|
152
|
+
scrollbarButton.setAttribute('data-is-grabbed', 'false');
|
|
154
153
|
};
|
|
155
154
|
|
|
156
155
|
scrollbarButton.addEventListener('mousedown', onInteractionDown);
|
|
@@ -161,6 +160,5 @@ export default function ScrollIndicatorPlugin( args: { [key: string]: any } ) {
|
|
|
161
160
|
|
|
162
161
|
window.addEventListener('mouseup', onInteractionUp);
|
|
163
162
|
window.addEventListener('touchend', onInteractionUp);
|
|
164
|
-
|
|
165
163
|
};
|
|
166
164
|
}
|
|
@@ -39,13 +39,16 @@ export default function FullWidthPlugin( args: { [key: string]: any } ) {
|
|
|
39
39
|
setActiveThumbnail();
|
|
40
40
|
addClickListeners();
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
mainSlider.on( 'activeSlideChanged', () => {
|
|
42
|
+
mainSlider.on( 'scrollEnd', () => {
|
|
44
43
|
setTimeout(() => {
|
|
45
|
-
const
|
|
46
|
-
const
|
|
44
|
+
const mainActiveSlideIdx = mainSlider.activeSlideIdx;
|
|
45
|
+
const thumbActiveSlideIdx = slider.activeSlideIdx;
|
|
46
|
+
if ( thumbActiveSlideIdx === mainActiveSlideIdx ) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const activeThumbnail = slider.slides[mainActiveSlideIdx] as HTMLElement;
|
|
47
50
|
setActiveThumbnail(activeThumbnail);
|
|
48
|
-
slider.moveToSlide(
|
|
51
|
+
slider.moveToSlide(mainActiveSlideIdx);
|
|
49
52
|
}, 50);
|
|
50
53
|
});
|
|
51
54
|
|