@evermade/overflow-slider 3.0.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.
Files changed (63) hide show
  1. package/README.md +73 -8
  2. package/dist/core/details.esm.js +5 -5
  3. package/dist/core/details.min.js +1 -1
  4. package/dist/core/overflow-slider.esm.js +1 -0
  5. package/dist/core/overflow-slider.min.js +1 -1
  6. package/dist/core/slider.esm.js +113 -44
  7. package/dist/core/slider.min.js +1 -1
  8. package/dist/core/utils.esm.js +15 -1
  9. package/dist/core/utils.min.js +1 -1
  10. package/dist/overflow-slider.css +1 -1
  11. package/dist/plugins/arrows/arrows/index.esm.js +20 -10
  12. package/dist/plugins/arrows/arrows/index.min.js +1 -1
  13. package/dist/plugins/dots/dots/index.esm.js +0 -4
  14. package/dist/plugins/drag-scrolling/drag-scrolling/index.esm.js +1 -1
  15. package/dist/plugins/drag-scrolling/drag-scrolling/index.min.js +1 -1
  16. package/dist/plugins/fade/fade/index.esm.js +7 -7
  17. package/dist/plugins/fade/fade/index.min.js +1 -1
  18. package/dist/plugins/full-width/full-width/index.esm.js +7 -2
  19. package/dist/plugins/full-width/full-width/index.min.js +1 -1
  20. package/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +22 -22
  21. package/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -1
  22. package/dist/plugins/thumbnails/thumbnails/index.esm.js +7 -4
  23. package/dist/plugins/thumbnails/thumbnails/index.min.js +1 -1
  24. package/docs/assets/demo.css +11 -1
  25. package/docs/assets/demo.js +38 -12
  26. package/docs/dist/core/details.esm.js +5 -5
  27. package/docs/dist/core/details.min.js +1 -1
  28. package/docs/dist/core/overflow-slider.esm.js +1 -0
  29. package/docs/dist/core/overflow-slider.min.js +1 -1
  30. package/docs/dist/core/slider.esm.js +113 -44
  31. package/docs/dist/core/slider.min.js +1 -1
  32. package/docs/dist/core/utils.esm.js +15 -1
  33. package/docs/dist/core/utils.min.js +1 -1
  34. package/docs/dist/overflow-slider.css +1 -1
  35. package/docs/dist/plugins/arrows/arrows/index.esm.js +20 -10
  36. package/docs/dist/plugins/arrows/arrows/index.min.js +1 -1
  37. package/docs/dist/plugins/dots/dots/index.esm.js +0 -4
  38. package/docs/dist/plugins/drag-scrolling/drag-scrolling/index.esm.js +1 -1
  39. package/docs/dist/plugins/drag-scrolling/drag-scrolling/index.min.js +1 -1
  40. package/docs/dist/plugins/fade/fade/index.esm.js +7 -7
  41. package/docs/dist/plugins/fade/fade/index.min.js +1 -1
  42. package/docs/dist/plugins/full-width/full-width/index.esm.js +7 -2
  43. package/docs/dist/plugins/full-width/full-width/index.min.js +1 -1
  44. package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +22 -22
  45. package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -1
  46. package/docs/dist/plugins/thumbnails/thumbnails/index.esm.js +7 -4
  47. package/docs/dist/plugins/thumbnails/thumbnails/index.min.js +1 -1
  48. package/docs/index-rtl.html +396 -0
  49. package/docs/index.html +1 -1
  50. package/package.json +1 -1
  51. package/src/core/details.ts +5 -5
  52. package/src/core/overflow-slider.ts +1 -0
  53. package/src/core/slider.ts +125 -47
  54. package/src/core/types.ts +5 -0
  55. package/src/core/utils.ts +19 -1
  56. package/src/plugins/arrows/index.ts +20 -10
  57. package/src/plugins/dots/index.ts +0 -4
  58. package/src/plugins/drag-scrolling/index.ts +1 -1
  59. package/src/plugins/fade/index.ts +7 -8
  60. package/src/plugins/fade/styles.scss +10 -0
  61. package/src/plugins/full-width/index.ts +8 -2
  62. package/src/plugins/scroll-indicator/index.ts +62 -64
  63. package/src/plugins/thumbnails/index.ts +7 -5
@@ -1,6 +1,6 @@
1
1
  import { Slider, SliderOptions, SliderPlugin } from './types';
2
2
  import details from './details';
3
- import { generateId, objectsAreEqual } from './utils';
3
+ import { generateId, objectsAreEqual, getOutermostChildrenEdgeMarginSum } from './utils';
4
4
 
5
5
  export default function Slider( container: HTMLElement, options : SliderOptions, plugins? : SliderPlugin[] ) {
6
6
  let slider: Slider;
@@ -94,7 +94,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
94
94
  // any scroll
95
95
  slider.container.addEventListener('scroll', () => {
96
96
  const newScrollLeft = slider.container.scrollLeft;
97
- if (scrollLeft !== newScrollLeft) {
97
+ if ( Math.floor( scrollLeft ) !== Math.floor( newScrollLeft ) ) {
98
98
  if (!isScrolling) {
99
99
  isScrolling = true;
100
100
  slider.emit('scrollStart');
@@ -116,7 +116,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
116
116
  // user initted scroll (touchmove, mouse wheel, etc.)
117
117
  const nativeScrollHandler = () => {
118
118
  const newScrollLeft = slider.container.scrollLeft;
119
- if (nativeScrollLeft !== newScrollLeft && !isProgrammaticScrolling) {
119
+ if ( Math.floor( nativeScrollLeft ) !== Math.floor( newScrollLeft ) && ! isProgrammaticScrolling ) {
120
120
  if (!isUserScrolling) {
121
121
  slider.emit('nativeScrollStart');
122
122
  isUserScrolling = true;
@@ -145,7 +145,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
145
145
 
146
146
  slider.container.addEventListener('scroll', () => {
147
147
  const newScrollLeft = slider.container.scrollLeft;
148
- if (programmaticScrollLeft !== newScrollLeft && !isUserScrolling && isProgrammaticScrolling) {
148
+ if ( Math.floor( programmaticScrollLeft ) !== Math.floor( newScrollLeft ) && !isUserScrolling && isProgrammaticScrolling) {
149
149
  programmaticScrollLeft = newScrollLeft;
150
150
  clearTimeout(programmaticScrollTimeout);
151
151
  programmaticScrollTimeout = setTimeout(() => {
@@ -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) {
@@ -218,11 +221,11 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
218
221
  const slideStart = slideRect.left - sliderRect.left + scrollLeft;
219
222
  const slideEnd = slideStart + slideRect.width;
220
223
  let scrollTarget = null;
221
- if (slideStart < scrollLeft) {
224
+ if ( Math.floor( slideStart ) < Math.floor( scrollLeft ) ) {
222
225
  scrollTarget = slideStart;
223
- } else if (slideEnd > scrollLeft + containerWidth) {
226
+ } else if ( Math.floor( slideEnd ) > Math.floor( scrollLeft ) + Math.floor( containerWidth ) ) {
224
227
  scrollTarget = slideEnd - containerWidth;
225
- } else if (slideStart === 0) {
228
+ } else if ( Math.floor( slideStart ) === 0) {
226
229
  scrollTarget = 0;
227
230
  } else {
228
231
  scrollTarget = slideStart;
@@ -237,27 +240,66 @@ 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.container.scrollLeft;
243
+ const scrollLeft = slider.getScrollLeft();
241
244
  const slides = slider.slides;
242
245
  let activeSlideIdx = 0;
246
+ let scrolledPastLastSlide = false;
247
+
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
+ }
267
+ }
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
+ }
284
+ }
285
+ }
243
286
 
244
- for (let i = 0; i < slides.length; i++) {
245
- const slideRect = slides[i].getBoundingClientRect();
246
- const slideStart = slideRect.left - sliderRect.left + scrollLeft + getGapSize();
247
287
 
248
- if (slideStart > scrollLeft) {
249
- activeSlideIdx = i;
250
- break;
251
- }
288
+ if ( scrolledPastLastSlide ) {
289
+ activeSlideIdx = slides.length - 1;
252
290
  }
253
291
 
254
292
  const oldActiveSlideIdx = slider.activeSlideIdx;
255
293
  slider.activeSlideIdx = activeSlideIdx;
294
+
295
+ // console.log('activeSlideIdx', activeSlideIdx);
296
+
256
297
  if (oldActiveSlideIdx !== activeSlideIdx) {
257
298
  slider.emit('activeSlideChanged');
258
299
  }
259
300
  }
260
301
 
302
+
261
303
  function moveToSlide( idx: number ) {
262
304
  const slide = slider.slides[idx];
263
305
  if (slide) {
@@ -265,12 +307,28 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
265
307
  }
266
308
  };
267
309
 
310
+ function getInclusiveScrollWidth() : number {
311
+ return slider.container.scrollWidth + getOutermostChildrenEdgeMarginSum(slider.container);
312
+ };
313
+
314
+ function getInclusiveClientWidth() : number {
315
+ return slider.container.clientWidth + getOutermostChildrenEdgeMarginSum(slider.container);
316
+ }
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
+
268
326
  function getGapSize() : number {
269
327
  let gapSize = 0;
270
328
  if (slider.slides.length > 1) {
271
329
  const firstSlideRect = slider.slides[0].getBoundingClientRect();
272
330
  const secondSlideRect = slider.slides[1].getBoundingClientRect();
273
- gapSize = secondSlideRect.left - firstSlideRect.right;
331
+ gapSize = slider.options.rtl ? Math.abs( Math.floor( secondSlideRect.right - firstSlideRect.left ) ) : Math.floor( secondSlideRect.left - firstSlideRect.right );
274
332
  }
275
333
  return gapSize;
276
334
  };
@@ -281,7 +339,7 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
281
339
  if (fullWidthOffset) {
282
340
  offset = parseInt(fullWidthOffset);
283
341
  }
284
- return offset;
342
+ return Math.floor( offset );
285
343
  };
286
344
 
287
345
  function moveToDirection(direction = "prev") {
@@ -290,63 +348,74 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
290
348
  const sliderRect = slider.container.getBoundingClientRect();
291
349
  const containerWidth = slider.container.offsetWidth;
292
350
  let targetScrollPosition = scrollLeft;
293
- if (direction === 'prev') {
351
+
352
+ const realDirection = slider.options.rtl ? (direction === 'prev' ? 'next' : 'prev') : direction;
353
+
354
+ if (realDirection === 'prev') {
294
355
  targetScrollPosition = Math.max(0, scrollLeft - slider.container.offsetWidth);
295
- } else if (direction === 'next') {
296
- targetScrollPosition = Math.min(slider.container.scrollWidth, scrollLeft + slider.container.offsetWidth);
356
+ } else if (realDirection === 'next') {
357
+ targetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), scrollLeft + slider.container.offsetWidth);
297
358
  }
298
359
  if (scrollStrategy === 'fullSlide') {
299
- let fullSldeTargetScrollPosition = null;
360
+ let fullSlideTargetScrollPosition = null;
300
361
 
301
362
  // extend targetScrollPosition to include gap
302
- if (direction === 'prev') {
303
- fullSldeTargetScrollPosition = Math.max(0, targetScrollPosition - getGapSize());
363
+ if (realDirection === 'prev') {
364
+ fullSlideTargetScrollPosition = Math.max(0, targetScrollPosition - getGapSize());
304
365
  } else {
305
- fullSldeTargetScrollPosition = Math.min(slider.container.scrollWidth, targetScrollPosition + getGapSize());
366
+ fullSlideTargetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), targetScrollPosition + getGapSize());
306
367
  }
307
368
 
308
- if (direction === 'next') {
369
+ if (realDirection === 'next') {
309
370
  let partialSlideFound = false;
310
371
  for (let slide of slider.slides) {
311
372
  const slideRect = slide.getBoundingClientRect();
312
373
  const slideStart = slideRect.left - sliderRect.left + scrollLeft;
313
374
  const slideEnd = slideStart + slideRect.width;
314
- if (slideStart < targetScrollPosition && slideEnd > targetScrollPosition) {
315
- fullSldeTargetScrollPosition = slideStart;
375
+ if ( Math.floor( slideStart ) < Math.floor( targetScrollPosition ) && Math.floor( slideEnd ) > Math.floor( targetScrollPosition ) ) {
376
+ fullSlideTargetScrollPosition = slideStart;
316
377
  partialSlideFound = true;
317
378
  break;
318
379
  }
319
380
  }
320
- if (!partialSlideFound) {
321
- fullSldeTargetScrollPosition = Math.min(targetScrollPosition, slider.container.scrollWidth - slider.container.offsetWidth);
381
+ if ( ! partialSlideFound ) {
382
+ fullSlideTargetScrollPosition = Math.min(targetScrollPosition, slider.getInclusiveScrollWidth() - slider.container.offsetWidth);
322
383
  }
323
- if (fullSldeTargetScrollPosition && fullSldeTargetScrollPosition > scrollLeft) {
324
- targetScrollPosition = fullSldeTargetScrollPosition;
384
+ if ( fullSlideTargetScrollPosition ) {
385
+ if ( Math.floor( fullSlideTargetScrollPosition ) > Math.floor( scrollLeft ) ) {
386
+ // make sure fullSlideTargetScrollPosition is possible considering the container width
387
+ const maxScrollPosition = Math.floor( slider.getInclusiveScrollWidth() ) - Math.floor( containerWidth );
388
+ targetScrollPosition = Math.min( fullSlideTargetScrollPosition, maxScrollPosition );
389
+ } else {
390
+ // cannot snap to slide, move one page worth of distance
391
+ targetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), scrollLeft + containerWidth);
392
+ }
325
393
  }
394
+
326
395
  } else {
327
396
  let partialSlideFound = false;
328
397
  for (let slide of slider.slides) {
329
398
  const slideRect = slide.getBoundingClientRect();
330
399
  const slideStart = slideRect.left - sliderRect.left + scrollLeft;
331
400
  const slideEnd = slideStart + slideRect.width;
332
- if (slideStart < scrollLeft && slideEnd > scrollLeft) {
333
- fullSldeTargetScrollPosition = slideEnd - containerWidth;
401
+ if ( Math.floor( slideStart ) < Math.floor( scrollLeft ) && Math.floor( slideEnd ) > Math.floor( scrollLeft ) ) {
402
+ fullSlideTargetScrollPosition = slideEnd - containerWidth;
334
403
  partialSlideFound = true;
335
404
  break;
336
405
  }
337
406
  }
338
- if (!partialSlideFound) {
339
- fullSldeTargetScrollPosition = Math.max(0, scrollLeft - containerWidth);
407
+ if ( ! partialSlideFound ) {
408
+ fullSlideTargetScrollPosition = Math.max(0, scrollLeft - containerWidth);
340
409
  }
341
- if (fullSldeTargetScrollPosition && fullSldeTargetScrollPosition < scrollLeft) {
342
- targetScrollPosition = fullSldeTargetScrollPosition;
410
+ if ( fullSlideTargetScrollPosition && Math.floor( fullSlideTargetScrollPosition ) < Math.floor( scrollLeft ) ) {
411
+ targetScrollPosition = fullSlideTargetScrollPosition;
343
412
  }
344
413
  }
345
414
  }
346
415
 
347
416
  // add left offset
348
417
  const offsettedTargetScrollPosition = targetScrollPosition - getLeftOffset();
349
- if (offsettedTargetScrollPosition >= 0) {
418
+ if ( Math.floor( offsettedTargetScrollPosition ) >= 0) {
350
419
  targetScrollPosition = offsettedTargetScrollPosition;
351
420
  }
352
421
 
@@ -357,12 +426,12 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
357
426
  };
358
427
 
359
428
  function snapToClosestSlide(direction = "prev") {
360
- const isMovingForward = direction === 'next';
429
+ const isMovingForward = slider.options.rtl ? direction === 'prev' : direction === 'next';
361
430
  const slideReference = [];
362
431
  for (let i = 0; i < slider.slides.length; i++) {
363
432
  const slide = slider.slides[i];
364
433
  const slideWidth = slide.offsetWidth;
365
- const slideStart = slide.offsetLeft;
434
+ const slideStart = slider.options.rtl ? Math.abs( slide.offsetLeft + slideWidth - slider.details.containerWidth ) : slide.offsetLeft;
366
435
  const slideEnd = slideStart + slideWidth;
367
436
  const slideMiddle = slideStart + slideWidth / 2;
368
437
  const trigger = Math.min(slideMiddle, slideStart + slider.options.emulateScrollSnapMaxThreshold);
@@ -373,18 +442,22 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
373
442
  width: slideWidth,
374
443
  trigger: trigger,
375
444
  slide: slide,
445
+ // debug
446
+ offSetleft: slide.offsetLeft,
447
+ rect: slide.getBoundingClientRect(),
376
448
  });
377
449
  }
450
+ console.log('slideReference', slideReference);
378
451
  let snapTarget = null;
379
- const scrollPosition = slider.container.scrollLeft;
452
+ const scrollPosition = getScrollLeft();
380
453
  if (isMovingForward) {
381
454
  for (let i = 0; i < slideReference.length; i++) {
382
455
  const item = slideReference[i];
383
- if ( i === 0 && scrollPosition <= item.trigger ) {
456
+ if ( i === 0 && Math.floor( scrollPosition ) <= Math.floor( item.trigger ) ) {
384
457
  snapTarget = 0;
385
458
  break;
386
459
  }
387
- if (slider.container.scrollLeft <= item.trigger) {
460
+ if ( Math.floor( getScrollLeft() ) <= Math.floor( item.trigger ) ) {
388
461
  snapTarget = item.start;
389
462
  break;
390
463
  }
@@ -392,11 +465,11 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
392
465
  } else {
393
466
  for (let i = slideReference.length - 1; i >= 0; i--) {
394
467
  const item = slideReference[i];
395
- if ( i === slideReference.length - 1 && scrollPosition >= item.trigger ) {
468
+ if ( i === slideReference.length - 1 && Math.floor( scrollPosition ) >= Math.floor( item.trigger ) ) {
396
469
  snapTarget = item.start;
397
470
  break;
398
471
  }
399
- if (slider.container.scrollLeft >= item.trigger) {
472
+ if ( Math.floor( getScrollLeft() ) >= Math.floor( item.trigger ) ) {
400
473
  snapTarget = item.start;
401
474
  break;
402
475
  }
@@ -404,13 +477,14 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
404
477
  }
405
478
  if ( snapTarget !== null ) {
406
479
  const offsettedSnapTarget = snapTarget - getLeftOffset();
407
- if (offsettedSnapTarget >= 0) {
480
+ if ( Math.floor( offsettedSnapTarget ) >= 0 ) {
408
481
  snapTarget = offsettedSnapTarget;
409
482
  }
410
483
 
411
484
  const scrollBehavior = slider.options.scrollBehavior || 'smooth';
485
+
412
486
  slider.container.scrollTo({
413
- left: snapTarget,
487
+ left: slider.options.rtl ? -snapTarget : snapTarget,
414
488
  behavior: scrollBehavior as ScrollBehavior
415
489
  });
416
490
  }
@@ -440,6 +514,10 @@ export default function Slider( container: HTMLElement, options : SliderOptions,
440
514
  moveToDirection,
441
515
  moveToSlide,
442
516
  snapToClosestSlide,
517
+ getInclusiveScrollWidth,
518
+ getInclusiveClientWidth,
519
+ getScrollLeft,
520
+ setScrollLeft,
443
521
  on,
444
522
  options,
445
523
  };
package/src/core/types.ts CHANGED
@@ -11,6 +11,10 @@ export type Slider<O = {}, C = {}, H extends string = string> = {
11
11
  moveToSlide: (
12
12
  index: number
13
13
  ) => void
14
+ getInclusiveScrollWidth: () => number
15
+ getInclusiveClientWidth: () => number
16
+ getScrollLeft: () => number
17
+ setScrollLeft: (value: number) => void
14
18
  on: (
15
19
  name: H | SliderHooks,
16
20
  cb: (props: Slider<O, C, H>) => void
@@ -26,6 +30,7 @@ export type SliderOptions = {
26
30
  slidesSelector: string;
27
31
  emulateScrollSnap: boolean;
28
32
  emulateScrollSnapMaxThreshold: number;
33
+ rtl: boolean;
29
34
  [key: string]: any;
30
35
  }
31
36
 
package/src/core/utils.ts CHANGED
@@ -21,4 +21,22 @@ function objectsAreEqual( obj1: any, obj2: any ) {
21
21
  return true;
22
22
  }
23
23
 
24
- export { generateId, objectsAreEqual };
24
+ function getOutermostChildrenEdgeMarginSum( el: HTMLElement ): number {
25
+ if (el.children.length === 0) {
26
+ return 0;
27
+ }
28
+
29
+ // get the first child and its left margin
30
+ const firstChild = el.children[0];
31
+ const firstChildStyle = getComputedStyle(firstChild);
32
+ const firstChildMarginLeft = parseFloat(firstChildStyle.marginLeft);
33
+
34
+ // Get the last child and its right margin
35
+ const lastChild = el.children[el.children.length - 1];
36
+ const lastChildStyle = getComputedStyle(lastChild);
37
+ const lastChildMarginRight = parseFloat(lastChildStyle.marginRight);
38
+
39
+ return firstChildMarginLeft + lastChildMarginRight;
40
+ }
41
+
42
+ export { generateId, objectsAreEqual, getOutermostChildrenEdgeMarginSum };
@@ -65,8 +65,12 @@ 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;
69
- prev.addEventListener( 'click', () => slider.moveToDirection( 'prev' ) );
68
+ prev.innerHTML = slider.options.rtl ? options.icons.next : options.icons.prev;
69
+ prev.addEventListener( 'click', () => {
70
+ if ( prev.getAttribute('data-has-content') === 'true' ) {
71
+ slider.moveToDirection( 'prev' );
72
+ }
73
+ } );
70
74
 
71
75
  const next = document.createElement( 'button' );
72
76
  next.setAttribute( 'class', options.classNames.nextButton );
@@ -74,28 +78,34 @@ export default function ArrowsPlugin( args: { [key: string]: any } ) {
74
78
  next.setAttribute( 'aria-label', options.texts.buttonNext );
75
79
  next.setAttribute( 'aria-controls', slider.container.getAttribute( 'id' ) ?? '');
76
80
  next.setAttribute( 'data-type', 'next' );
77
- next.innerHTML = options.icons.next;
78
- next.addEventListener( 'click', () => slider.moveToDirection( 'next' ) );
81
+ next.innerHTML = slider.options.rtl ? options.icons.prev : options.icons.next;
82
+ next.addEventListener( 'click', () => {
83
+ if ( next.getAttribute('data-has-content') === 'true' ) {
84
+ slider.moveToDirection( 'next' );
85
+ }
86
+ } );
79
87
 
80
88
  // insert buttons to the nav
81
89
  nav.appendChild( prev );
82
90
  nav.appendChild( next );
83
91
 
84
92
  const update = () => {
85
- const scrollLeft = slider.container.scrollLeft;
86
- const scrollWidth = slider.container.scrollWidth;
87
- const clientWidth = slider.container.clientWidth;
93
+ const scrollLeft = slider.getScrollLeft();
94
+ const scrollWidth = slider.getInclusiveScrollWidth();
95
+ const clientWidth = slider.getInclusiveClientWidth();
88
96
  const buffer = 1;
89
- if ( scrollLeft === 0 ) {
97
+ if ( Math.floor( scrollLeft ) === 0 ) {
90
98
  prev.setAttribute( 'data-has-content', 'false' );
91
99
  } else {
92
100
  prev.setAttribute( 'data-has-content', 'true' );
93
101
  }
94
- if ( scrollLeft + clientWidth >= scrollWidth - buffer ) {
102
+ const maxWidthDifference = Math.abs( Math.floor( scrollLeft + clientWidth ) - Math.floor( scrollWidth ) );
103
+ if ( maxWidthDifference <= buffer ) {
95
104
  next.setAttribute( 'data-has-content', 'false' );
96
105
  } else {
97
106
  next.setAttribute( 'data-has-content', 'true' );
98
107
  }
108
+ console.log( 'next', scrollLeft + clientWidth, scrollWidth );
99
109
  };
100
110
 
101
111
  if ( options.containerNext && options.containerPrev ) {
@@ -110,7 +120,7 @@ export default function ArrowsPlugin( args: { [key: string]: any } ) {
110
120
  }
111
121
 
112
122
  update();
113
- slider.on( 'scroll', update );
123
+ slider.on( 'scrollEnd', update );
114
124
  slider.on( 'contentsChanged', update );
115
125
  slider.on( 'containerSizeChanged', update );
116
126
  };
@@ -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
 
@@ -59,7 +59,7 @@ export default function DragScrollingPlugin( args: { [key: string]: any } ) {
59
59
  const walk = (x - startX);
60
60
  const newScrollLeft = scrollLeft - walk;
61
61
  mayNeedToSnap = true;
62
- if ( slider.container.scrollLeft !== newScrollLeft ) {
62
+ if ( Math.floor( slider.container.scrollLeft ) !== Math.floor( newScrollLeft ) ) {
63
63
  isMovingForward = slider.container.scrollLeft < newScrollLeft;
64
64
  }
65
65
  slider.container.scrollLeft = newScrollLeft;
@@ -48,26 +48,26 @@ export default function FadePlugin( args: { [key: string]: any } ) {
48
48
  }
49
49
 
50
50
  const hasFadeAtStart = () => {
51
- return slider.container.scrollLeft > fadeItemStart.offsetWidth;
51
+ return slider.getScrollLeft() > fadeItemStart.offsetWidth;
52
52
  }
53
53
 
54
54
  const fadeAtStartOpacity = () => {
55
- const position = slider.container.scrollLeft;
56
- if ( position <= fadeItemStart.offsetWidth ) {
55
+ const position = slider.getScrollLeft();
56
+ if ( Math.floor( position ) <= Math.floor( fadeItemStart.offsetWidth ) ) {
57
57
  return position / Math.max(fadeItemStart.offsetWidth, 1);
58
58
  }
59
59
  return 1;
60
60
  }
61
61
 
62
62
  const hasFadeAtEnd = () => {
63
- return slider.container.scrollLeft < (slider.container.scrollWidth - slider.container.clientWidth - fadeItemEnd.offsetWidth);
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.container.scrollLeft;
68
- const maxPosition = slider.container.scrollWidth - slider.container.clientWidth;
67
+ const position = slider.getScrollLeft();
68
+ const maxPosition = slider.getInclusiveScrollWidth() - slider.getInclusiveClientWidth();
69
69
  const maxFadePosition = maxPosition - fadeItemEnd.offsetWidth;
70
- if ( position >= maxFadePosition ) {
70
+ if ( Math.floor( position ) >= Math.floor( maxFadePosition ) ) {
71
71
  return ( ( maxFadePosition - position ) / Math.max(fadeItemEnd.offsetWidth, 1) ) + 1;
72
72
  }
73
73
  return 1;
@@ -97,6 +97,5 @@ export default function FadePlugin( args: { [key: string]: any } ) {
97
97
  };
98
98
  slider.on('scroll', debouncedUpdate);
99
99
 
100
-
101
100
  };
102
101
  }
@@ -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.marginLeft = `${marginAmount}px`;
32
+ firstSlide.style.marginInlineStart = `${marginAmount}px`;
33
33
  }
34
34
  if ( options.addMarginAfter ) {
35
- lastSlide.style.marginRight = `${marginAmount}px`;
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
  }