@evermade/overflow-slider 2.0.2 → 3.1.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 (64) hide show
  1. package/.nvmrc +1 -1
  2. package/README.md +26 -4
  3. package/dist/core/details.esm.js +4 -4
  4. package/dist/core/details.min.js +1 -1
  5. package/dist/core/overflow-slider.esm.js +2 -0
  6. package/dist/core/overflow-slider.min.js +1 -1
  7. package/dist/core/slider.esm.js +202 -29
  8. package/dist/core/slider.min.js +1 -1
  9. package/dist/core/utils.esm.js +15 -1
  10. package/dist/core/utils.min.js +1 -1
  11. package/dist/overflow-slider.css +1 -1
  12. package/dist/plugins/arrows/arrows/index.esm.js +15 -8
  13. package/dist/plugins/arrows/arrows/index.min.js +1 -1
  14. package/dist/plugins/dots/dots/index.esm.js +21 -22
  15. package/dist/plugins/dots/dots/index.min.js +1 -1
  16. package/dist/plugins/drag-scrolling/drag-scrolling/index.esm.js +30 -3
  17. package/dist/plugins/drag-scrolling/drag-scrolling/index.min.js +1 -1
  18. package/dist/plugins/fade/fade/index.esm.js +81 -0
  19. package/dist/plugins/fade/fade/index.min.js +1 -0
  20. package/dist/plugins/full-width/full-width/index.esm.js +1 -0
  21. package/dist/plugins/full-width/full-width/index.min.js +1 -1
  22. package/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +4 -6
  23. package/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -1
  24. package/dist/plugins/thumbnails/thumbnails/index.esm.js +1 -0
  25. package/docs/assets/demo.css +16 -0
  26. package/docs/assets/demo.js +44 -7
  27. package/docs/dist/core/details.esm.js +4 -4
  28. package/docs/dist/core/details.min.js +1 -1
  29. package/docs/dist/core/overflow-slider.esm.js +2 -0
  30. package/docs/dist/core/overflow-slider.min.js +1 -1
  31. package/docs/dist/core/slider.esm.js +202 -29
  32. package/docs/dist/core/slider.min.js +1 -1
  33. package/docs/dist/core/utils.esm.js +15 -1
  34. package/docs/dist/core/utils.min.js +1 -1
  35. package/docs/dist/overflow-slider.css +1 -1
  36. package/docs/dist/plugins/arrows/arrows/index.esm.js +15 -8
  37. package/docs/dist/plugins/arrows/arrows/index.min.js +1 -1
  38. package/docs/dist/plugins/dots/dots/index.esm.js +21 -22
  39. package/docs/dist/plugins/dots/dots/index.min.js +1 -1
  40. package/docs/dist/plugins/drag-scrolling/drag-scrolling/index.esm.js +30 -3
  41. package/docs/dist/plugins/drag-scrolling/drag-scrolling/index.min.js +1 -1
  42. package/docs/dist/plugins/fade/fade/index.esm.js +81 -0
  43. package/docs/dist/plugins/fade/fade/index.min.js +1 -0
  44. package/docs/dist/plugins/full-width/full-width/index.esm.js +1 -0
  45. package/docs/dist/plugins/full-width/full-width/index.min.js +1 -1
  46. package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.esm.js +4 -6
  47. package/docs/dist/plugins/scroll-indicator/scroll-indicator/index.min.js +1 -1
  48. package/docs/dist/plugins/thumbnails/thumbnails/index.esm.js +1 -0
  49. package/docs/index.html +39 -7
  50. package/package.json +5 -1
  51. package/src/core/details.ts +4 -4
  52. package/src/core/overflow-slider.ts +2 -0
  53. package/src/core/slider.ts +226 -36
  54. package/src/core/types.ts +33 -1
  55. package/src/core/utils.ts +19 -1
  56. package/src/overflow-slider.scss +2 -1
  57. package/src/plugins/arrows/index.ts +16 -8
  58. package/src/plugins/dots/index.ts +21 -23
  59. package/src/plugins/drag-scrolling/index.ts +34 -5
  60. package/src/plugins/fade/index.ts +101 -0
  61. package/src/plugins/fade/styles.scss +27 -0
  62. package/src/plugins/full-width/index.ts +1 -0
  63. package/src/plugins/scroll-indicator/index.ts +4 -6
  64. package/src/plugins/thumbnails/index.ts +1 -1
@@ -15,23 +15,26 @@ export default function DragScrollingPlugin( args: { [key: string]: any } ) {
15
15
  let startX = 0;
16
16
  let scrollLeft = 0;
17
17
 
18
+ let isMovingForward = false;
19
+ let programmaticScrollStarted = false;
20
+ let mayNeedToSnap = false;
21
+
18
22
  // add data attribute to container
19
23
  slider.container.setAttribute( 'data-has-drag-scrolling', 'true' );
20
24
 
21
25
  const mouseDown = (e: MouseEvent) => {
26
+ programmaticScrollStarted = false;
22
27
  if ( ! slider.details.hasOverflow ) {
23
28
  return;
24
29
  }
25
30
  if ( ! slider.container.contains( e.target as Node ) ) {
26
31
  return;
27
32
  }
28
-
29
33
  isMouseDown = true;
30
34
  startX = e.pageX - slider.container.offsetLeft;
31
35
  scrollLeft = slider.container.scrollLeft;
32
36
  // change cursor to grabbing
33
37
  slider.container.style.cursor = 'grabbing';
34
- slider.container.style.scrollSnapType = 'none';
35
38
  slider.container.style.scrollBehavior = 'auto';
36
39
  // prevent focus going to the slides
37
40
  e.preventDefault();
@@ -40,15 +43,26 @@ export default function DragScrollingPlugin( args: { [key: string]: any } ) {
40
43
 
41
44
  const mouseMove = (e: MouseEvent) => {
42
45
  if ( ! slider.details.hasOverflow ) {
46
+ programmaticScrollStarted = false;
43
47
  return;
44
48
  }
45
49
  if (!isMouseDown) {
50
+ programmaticScrollStarted = false;
46
51
  return;
47
52
  }
48
53
  e.preventDefault();
54
+ if (!programmaticScrollStarted) {
55
+ programmaticScrollStarted = true;
56
+ slider.emit('programmaticScrollStart');
57
+ }
49
58
  const x = e.pageX - slider.container.offsetLeft;
50
59
  const walk = (x - startX);
51
- slider.container.scrollLeft = scrollLeft - walk;
60
+ const newScrollLeft = scrollLeft - walk;
61
+ mayNeedToSnap = true;
62
+ if ( Math.floor( slider.container.scrollLeft ) !== Math.floor( newScrollLeft ) ) {
63
+ isMovingForward = slider.container.scrollLeft < newScrollLeft;
64
+ }
65
+ slider.container.scrollLeft = newScrollLeft;
52
66
 
53
67
  const absWalk = Math.abs(walk);
54
68
  const slides = slider.container.querySelectorAll( slider.options.slidesSelector );
@@ -59,13 +73,15 @@ export default function DragScrollingPlugin( args: { [key: string]: any } ) {
59
73
  };
60
74
 
61
75
  const mouseUp = () => {
62
- if ( ! slider.details.hasOverflow ) {
76
+ if (!slider.details.hasOverflow) {
77
+ programmaticScrollStarted = false;
63
78
  return;
64
79
  }
65
80
  isMouseDown = false;
81
+
66
82
  slider.container.style.cursor = '';
67
83
  setTimeout(() => {
68
- slider.container.style.scrollSnapType = '';
84
+ programmaticScrollStarted = false;
69
85
  slider.container.style.scrollBehavior = '';
70
86
  const slides = slider.container.querySelectorAll( slider.options.slidesSelector );
71
87
  slides.forEach((slide) => {
@@ -78,5 +94,18 @@ export default function DragScrollingPlugin( args: { [key: string]: any } ) {
78
94
  window.addEventListener('mousemove', mouseMove);
79
95
  window.addEventListener('mouseup', mouseUp);
80
96
 
97
+ // emulate scroll snapping
98
+ if ( slider.options.emulateScrollSnap ) {
99
+ const snap = () => {
100
+ if (!mayNeedToSnap || isMouseDown) {
101
+ return;
102
+ }
103
+ mayNeedToSnap = false;
104
+ slider.snapToClosestSlide(isMovingForward ? 'next' : 'prev');
105
+ };
106
+ slider.on( 'programmaticScrollEnd', snap );
107
+ window.addEventListener( 'mouseup', snap );
108
+ }
109
+
81
110
  };
82
111
  };
@@ -0,0 +1,101 @@
1
+ import { Slider } from '../../core/types';
2
+
3
+ export type FadeOptions = {
4
+ classNames: {
5
+ fadeItem: string;
6
+ fadeItemStart: string;
7
+ fadeItemEnd: string;
8
+ },
9
+ container: HTMLElement | null,
10
+ containerStart: HTMLElement | null,
11
+ containerEnd: HTMLElement | null,
12
+ };
13
+
14
+ export default function FadePlugin( args: { [key: string]: any } ) {
15
+ return ( slider: Slider ) => {
16
+
17
+ const options = <FadeOptions>{
18
+ classNames: {
19
+ fadeItem: 'overflow-slider-fade',
20
+ fadeItemStart: 'overflow-slider-fade--start',
21
+ fadeItemEnd: 'overflow-slider-fade--end',
22
+ },
23
+ container: args?.container ?? null,
24
+ containerStart: args?.containerStart ?? null,
25
+ containerEnd: args?.containerEnd ?? null,
26
+ };
27
+
28
+ const fadeItemStart = document.createElement( 'div' );
29
+ fadeItemStart.classList.add( options.classNames.fadeItem, options.classNames.fadeItemStart );
30
+ fadeItemStart.setAttribute( 'aria-hidden', 'true' );
31
+ fadeItemStart.setAttribute( 'tabindex', '-1' );
32
+
33
+ const fadeItemEnd = document.createElement( 'div' );
34
+ fadeItemEnd.classList.add( options.classNames.fadeItem, options.classNames.fadeItemEnd );
35
+ fadeItemEnd.setAttribute( 'aria-hidden', 'true' );
36
+ fadeItemEnd.setAttribute( 'tabindex', '-1' );
37
+
38
+ if ( options.containerStart ) {
39
+ options.containerStart.appendChild( fadeItemStart );
40
+ } else if ( options.container ) {
41
+ options.container.appendChild( fadeItemStart );
42
+ }
43
+
44
+ if ( options.containerEnd ) {
45
+ options.containerEnd.appendChild( fadeItemEnd );
46
+ } else if ( options.container ) {
47
+ options.container.appendChild( fadeItemEnd );
48
+ }
49
+
50
+ const hasFadeAtStart = () => {
51
+ return slider.container.scrollLeft > fadeItemStart.offsetWidth;
52
+ }
53
+
54
+ const fadeAtStartOpacity = () => {
55
+ const position = slider.container.scrollLeft;
56
+ if ( Math.floor( position ) <= Math.floor( fadeItemStart.offsetWidth ) ) {
57
+ return position / Math.max(fadeItemStart.offsetWidth, 1);
58
+ }
59
+ return 1;
60
+ }
61
+
62
+ const hasFadeAtEnd = () => {
63
+ return Math.floor( slider.container.scrollLeft ) < Math.floor( slider.getInclusiveScrollWidth() - slider.getInclusiveClientWidth() - fadeItemEnd.offsetWidth );
64
+ }
65
+
66
+ const fadeAtEndOpacity = () => {
67
+ const position = slider.container.scrollLeft;
68
+ const maxPosition = slider.getInclusiveScrollWidth() - slider.getInclusiveClientWidth();
69
+ const maxFadePosition = maxPosition - fadeItemEnd.offsetWidth;
70
+ if ( Math.floor( position ) >= Math.floor( maxFadePosition ) ) {
71
+ return ( ( maxFadePosition - position ) / Math.max(fadeItemEnd.offsetWidth, 1) ) + 1;
72
+ }
73
+ return 1;
74
+ }
75
+
76
+ const update = () => {
77
+ fadeItemStart.setAttribute( 'data-has-fade', hasFadeAtStart().toString() );
78
+ fadeItemStart.style.opacity = fadeAtStartOpacity().toString();
79
+ fadeItemEnd.setAttribute( 'data-has-fade', hasFadeAtEnd().toString() );
80
+ fadeItemEnd.style.opacity = fadeAtEndOpacity().toString();
81
+ };
82
+
83
+ update();
84
+ slider.on( 'created', update );
85
+ slider.on( 'contentsChanged', update );
86
+ slider.on( 'containerSizeChanged', update );
87
+ slider.on( 'scrollEnd', update );
88
+ slider.on( 'scrollStart', update );
89
+ let requestId = 0;
90
+ const debouncedUpdate = () => {
91
+ if ( requestId ) {
92
+ window.cancelAnimationFrame( requestId );
93
+ }
94
+ requestId = window.requestAnimationFrame(() => {
95
+ update();
96
+ });
97
+ };
98
+ slider.on('scroll', debouncedUpdate);
99
+
100
+ };
101
+ }
@@ -0,0 +1,27 @@
1
+ /* --------------------------------------------------------------
2
+ # FadePlugin
3
+ -------------------------------------------------------------- */
4
+
5
+ :root {
6
+ --overflow-slider-fade-color: #fff;
7
+ --overflow-slider-fade-width: 3rem;
8
+ }
9
+
10
+ .overflow-slider-fade {
11
+ position: absolute;
12
+ top: 0;
13
+ width: var(--overflow-slider-fade-width);
14
+ height: 100%;
15
+ pointer-events: none;
16
+ z-index: 1;
17
+ }
18
+
19
+ .overflow-slider-fade--start {
20
+ left: 0;
21
+ background: linear-gradient(to right, var(--overflow-slider-fade-color) 0%, transparent 100%);
22
+ }
23
+
24
+ .overflow-slider-fade--end {
25
+ right: 0;
26
+ background: linear-gradient(to left, var(--overflow-slider-fade-color) 0%, transparent 100%);
27
+ }
@@ -34,6 +34,7 @@ export default function FullWidthPlugin( args: { [key: string]: any } ) {
34
34
  if ( options.addMarginAfter ) {
35
35
  lastSlide.style.marginRight = `${marginAmount}px`;
36
36
  }
37
+ slider.container.setAttribute( 'data-full-width-offset', marginAmount.toString() );
37
38
  };
38
39
 
39
40
  update();
@@ -65,7 +65,7 @@ export default function ScrollIndicatorPlugin( args: { [key: string]: any } ) {
65
65
  }
66
66
 
67
67
  requestId = window.requestAnimationFrame(() => {
68
- const scrollbarButtonWidth = (slider.details.containerWidth / slider.details.scrollableAreaWidth) * 100;
68
+ const scrollbarButtonWidth = (slider.details.containerWidth / slider.container.scrollWidth) * 100;
69
69
 
70
70
  const scrollLeftInPortion = getScrollbarButtonLeftOffset();
71
71
  scrollbarButton.style.width = `${scrollbarButtonWidth}%`;
@@ -73,7 +73,7 @@ export default function ScrollIndicatorPlugin( args: { [key: string]: any } ) {
73
73
 
74
74
  // aria-valuenow
75
75
  const scrollLeft = slider.container.scrollLeft;
76
- const scrollWidth = slider.container.scrollWidth;
76
+ const scrollWidth = slider.getInclusiveScrollWidth();
77
77
  const containerWidth = slider.container.offsetWidth;
78
78
  const scrollPercentage = (scrollLeft / (scrollWidth - containerWidth)) * 100;
79
79
  scrollbarContainer.setAttribute( 'aria-valuenow', Math.round(Number.isNaN(scrollPercentage) ? 0 : scrollPercentage).toString() );
@@ -109,9 +109,9 @@ export default function ScrollIndicatorPlugin( args: { [key: string]: any } ) {
109
109
  const scrollbarButtonLeft = getScrollbarButtonLeftOffset();
110
110
  const scrollbarButtonRight = scrollbarButtonLeft + scrollbarButtonWidth;
111
111
  const clickX = e.pageX - scrollbarContainer.offsetLeft;
112
- if ( clickX < scrollbarButtonLeft ) {
112
+ if ( Math.floor( clickX ) < Math.floor( scrollbarButtonLeft ) ) {
113
113
  slider.moveToDirection( 'prev' );
114
- } else if ( clickX > scrollbarButtonRight ) {
114
+ } else if ( Math.floor( clickX ) > Math.floor( scrollbarButtonRight ) ) {
115
115
  slider.moveToDirection( 'next' );
116
116
  }
117
117
  });
@@ -128,7 +128,6 @@ export default function ScrollIndicatorPlugin( args: { [key: string]: any } ) {
128
128
  scrollLeft = slider.container.scrollLeft;
129
129
  // change cursor to grabbing
130
130
  scrollbarButton.style.cursor = 'grabbing';
131
- slider.container.style.scrollSnapType = 'none';
132
131
  scrollbarButton.setAttribute( 'data-is-grabbed', 'true' );
133
132
 
134
133
  e.preventDefault();
@@ -151,7 +150,6 @@ export default function ScrollIndicatorPlugin( args: { [key: string]: any } ) {
151
150
  const onInteractionUp = () => {
152
151
  isInteractionDown = false;
153
152
  scrollbarButton.style.cursor = '';
154
- slider.container.style.scrollSnapType = '';
155
153
  scrollbarButton.setAttribute( 'data-is-grabbed', 'false' );
156
154
  };
157
155
 
@@ -13,7 +13,6 @@ export default function FullWidthPlugin( args: { [key: string]: any } ) {
13
13
 
14
14
  const mainSlider = options.mainSlider;
15
15
 
16
-
17
16
  const setActiveThumbnail = (slide: HTMLElement | null = null) => {
18
17
  if ( slide === null && slider.slides.length > 0 ) {
19
18
  slide = slider.slides[0] as HTMLElement;
@@ -40,6 +39,7 @@ export default function FullWidthPlugin( args: { [key: string]: any } ) {
40
39
  setActiveThumbnail();
41
40
  addClickListeners();
42
41
 
42
+ // @todo debounce on scroll
43
43
  mainSlider.on( 'activeSlideChanged', () => {
44
44
  setTimeout(() => {
45
45
  const activeSlideIdx = mainSlider.activeSlideIdx;