@evermade/overflow-slider 3.3.0 → 4.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.
Files changed (113) hide show
  1. package/.nvmrc +1 -1
  2. package/README.md +120 -29
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.esm.js +609 -1
  5. package/dist/index.esm.js.map +1 -0
  6. package/dist/index.min.js +2 -1
  7. package/dist/index.min.js.map +1 -0
  8. package/dist/mixins.scss +14 -0
  9. package/dist/overflow-slider.css +1 -1
  10. package/dist/plugins/arrows/index.d.ts +26 -0
  11. package/dist/plugins/arrows/index.min.js +1 -1
  12. package/dist/plugins/autoplay/index.d.ts +41 -0
  13. package/dist/plugins/autoplay/index.esm.js +233 -0
  14. package/dist/plugins/autoplay/index.min.js +1 -0
  15. package/dist/plugins/core/index.d.ts +75 -0
  16. package/dist/plugins/core/index.d2.ts +23 -0
  17. package/dist/plugins/dots/index.d.ts +16 -0
  18. package/dist/plugins/dots/index.min.js +1 -1
  19. package/dist/plugins/drag-scrolling/index.d.ts +9 -0
  20. package/dist/plugins/drag-scrolling/index.esm.js +2 -2
  21. package/dist/plugins/drag-scrolling/index.min.js +1 -1
  22. package/dist/plugins/fade/index.d.ts +16 -0
  23. package/dist/plugins/fade/index.min.js +1 -1
  24. package/dist/plugins/full-width/index.d.ts +11 -0
  25. package/dist/plugins/full-width/index.esm.js +15 -4
  26. package/dist/plugins/full-width/index.min.js +1 -1
  27. package/dist/plugins/infinite-scroll/index.d.ts +25 -0
  28. package/dist/plugins/infinite-scroll/index.esm.js +75 -0
  29. package/dist/plugins/infinite-scroll/index.min.js +1 -0
  30. package/dist/plugins/scroll-indicator/index.d.ts +14 -0
  31. package/dist/plugins/scroll-indicator/index.esm.js +3 -1
  32. package/dist/plugins/scroll-indicator/index.min.js +1 -1
  33. package/dist/plugins/skip-links/index.d.ts +17 -0
  34. package/dist/plugins/skip-links/index.esm.js +7 -1
  35. package/dist/plugins/skip-links/index.min.js +1 -1
  36. package/dist/plugins/thumbnails/index.d.ts +9 -0
  37. package/dist/plugins/thumbnails/index.min.js +1 -1
  38. package/dist/{core/utils.min.js → utils-Sxwcz8zp.js} +1 -1
  39. package/dist/{core/utils.esm.js → utils-ayDxlweP.js} +1 -1
  40. package/docs/assets/demo.css +115 -0
  41. package/docs/assets/demo.js +68 -0
  42. package/docs/dist/index.d.ts +1 -0
  43. package/docs/dist/index.esm.js +609 -1
  44. package/docs/dist/index.esm.js.map +1 -0
  45. package/docs/dist/index.min.js +2 -1
  46. package/docs/dist/index.min.js.map +1 -0
  47. package/docs/dist/mixins.scss +14 -0
  48. package/docs/dist/overflow-slider.css +1 -1
  49. package/docs/dist/plugins/arrows/index.d.ts +26 -0
  50. package/docs/dist/plugins/arrows/index.min.js +1 -1
  51. package/docs/dist/plugins/autoplay/index.d.ts +41 -0
  52. package/docs/dist/plugins/autoplay/index.esm.js +233 -0
  53. package/docs/dist/plugins/autoplay/index.min.js +1 -0
  54. package/docs/dist/plugins/core/index.d.ts +23 -0
  55. package/docs/dist/plugins/core/index.d2.ts +75 -0
  56. package/docs/dist/plugins/dots/index.d.ts +16 -0
  57. package/docs/dist/plugins/dots/index.min.js +1 -1
  58. package/docs/dist/plugins/drag-scrolling/index.d.ts +9 -0
  59. package/docs/dist/plugins/drag-scrolling/index.esm.js +2 -2
  60. package/docs/dist/plugins/drag-scrolling/index.min.js +1 -1
  61. package/docs/dist/plugins/fade/index.d.ts +16 -0
  62. package/docs/dist/plugins/fade/index.min.js +1 -1
  63. package/docs/dist/plugins/full-width/index.d.ts +11 -0
  64. package/docs/dist/plugins/full-width/index.esm.js +15 -4
  65. package/docs/dist/plugins/full-width/index.min.js +1 -1
  66. package/docs/dist/plugins/infinite-scroll/index.d.ts +25 -0
  67. package/docs/dist/plugins/infinite-scroll/index.esm.js +75 -0
  68. package/docs/dist/plugins/infinite-scroll/index.min.js +1 -0
  69. package/docs/dist/plugins/scroll-indicator/index.d.ts +14 -0
  70. package/docs/dist/plugins/scroll-indicator/index.esm.js +3 -1
  71. package/docs/dist/plugins/scroll-indicator/index.min.js +1 -1
  72. package/docs/dist/plugins/skip-links/index.d.ts +17 -0
  73. package/docs/dist/plugins/skip-links/index.esm.js +7 -1
  74. package/docs/dist/plugins/skip-links/index.min.js +1 -1
  75. package/docs/dist/plugins/thumbnails/index.d.ts +9 -0
  76. package/docs/dist/plugins/thumbnails/index.min.js +1 -1
  77. package/docs/dist/{core/utils.min.js → utils-Sxwcz8zp.js} +1 -1
  78. package/docs/dist/{core/utils.esm.js → utils-ayDxlweP.js} +1 -1
  79. package/docs/index-rtl.html +78 -2
  80. package/docs/index.html +77 -1
  81. package/package.json +50 -27
  82. package/rollup.config.js +90 -66
  83. package/src/core/details.ts +4 -0
  84. package/src/core/overflow-slider.ts +4 -2
  85. package/src/core/slider.ts +91 -64
  86. package/src/core/types.ts +29 -1
  87. package/src/mixins.scss +14 -0
  88. package/src/overflow-slider.scss +12 -10
  89. package/src/plugins/arrows/index.ts +2 -2
  90. package/src/plugins/autoplay/index.ts +276 -0
  91. package/src/plugins/autoplay/styles.scss +11 -0
  92. package/src/plugins/dots/index.ts +2 -2
  93. package/src/plugins/drag-scrolling/index.ts +4 -4
  94. package/src/plugins/fade/index.ts +2 -2
  95. package/src/plugins/full-width/index.ts +18 -6
  96. package/src/plugins/infinite-scroll/index.ts +109 -0
  97. package/src/plugins/scroll-indicator/index.ts +5 -3
  98. package/src/plugins/skip-links/index.ts +2 -2
  99. package/src/plugins/thumbnails/index.ts +2 -2
  100. package/tsconfig.json +4 -2
  101. package/changelog.md +0 -5
  102. package/dist/core/details.esm.js +0 -35
  103. package/dist/core/details.min.js +0 -1
  104. package/dist/core/overflow-slider.esm.js +0 -29
  105. package/dist/core/overflow-slider.min.js +0 -1
  106. package/dist/core/slider.esm.js +0 -499
  107. package/dist/core/slider.min.js +0 -1
  108. package/docs/dist/core/details.esm.js +0 -35
  109. package/docs/dist/core/details.min.js +0 -1
  110. package/docs/dist/core/overflow-slider.esm.js +0 -29
  111. package/docs/dist/core/overflow-slider.min.js +0 -1
  112. package/docs/dist/core/slider.esm.js +0 -499
  113. package/docs/dist/core/slider.min.js +0 -1
package/dist/index.esm.js CHANGED
@@ -1 +1,609 @@
1
- export { default as OverflowSlider } from './core/overflow-slider.esm.js';
1
+ function details(slider) {
2
+ var _a;
3
+ let instance;
4
+ let hasOverflow = false;
5
+ let slideCount = 0;
6
+ let containerWidth = 0;
7
+ let containerHeight = 0;
8
+ let scrollableAreaWidth = 0;
9
+ let amountOfPages = 0;
10
+ let currentPage = 1;
11
+ if (Math.floor(slider.getInclusiveScrollWidth()) > Math.floor(slider.getInclusiveClientWidth())) {
12
+ hasOverflow = true;
13
+ }
14
+ slideCount = (_a = slider.slides.length) !== null && _a !== void 0 ? _a : 0;
15
+ containerWidth = slider.container.offsetWidth;
16
+ containerHeight = slider.container.offsetHeight;
17
+ scrollableAreaWidth = slider.getInclusiveScrollWidth();
18
+ amountOfPages = Math.ceil(scrollableAreaWidth / containerWidth);
19
+ if (Math.floor(slider.getScrollLeft()) >= 0) {
20
+ currentPage = Math.floor(slider.getScrollLeft() / containerWidth);
21
+ // consider as last page if the scrollLeft + containerWidth is equal to scrollWidth
22
+ if (Math.floor(slider.getScrollLeft() + containerWidth) === Math.floor(scrollableAreaWidth)) {
23
+ currentPage = amountOfPages - 1;
24
+ }
25
+ }
26
+ instance = {
27
+ hasOverflow,
28
+ slideCount,
29
+ containerWidth,
30
+ containerHeight,
31
+ scrollableAreaWidth,
32
+ amountOfPages,
33
+ currentPage,
34
+ };
35
+ return instance;
36
+ }
37
+
38
+ function generateId(prefix, i = 1) {
39
+ const id = `${prefix}-${i}`;
40
+ if (document.getElementById(id)) {
41
+ return generateId(prefix, i + 1);
42
+ }
43
+ return id;
44
+ }
45
+ function objectsAreEqual(obj1, obj2) {
46
+ const keys1 = Object.keys(obj1);
47
+ const keys2 = Object.keys(obj2);
48
+ if (keys1.length !== keys2.length) {
49
+ return false;
50
+ }
51
+ for (let key of keys1) {
52
+ // Use `Object.prototype.hasOwnProperty.call` for better safety
53
+ if (!Object.prototype.hasOwnProperty.call(obj2, key) || obj1[key] !== obj2[key]) {
54
+ return false;
55
+ }
56
+ }
57
+ return true;
58
+ }
59
+ function getOutermostChildrenEdgeMarginSum(el) {
60
+ if (el.children.length === 0) {
61
+ return 0;
62
+ }
63
+ // get the first child and its left margin
64
+ const firstChild = el.children[0];
65
+ const firstChildStyle = getComputedStyle(firstChild);
66
+ const firstChildMarginLeft = parseFloat(firstChildStyle.marginLeft);
67
+ // Get the last child and its right margin
68
+ const lastChild = el.children[el.children.length - 1];
69
+ const lastChildStyle = getComputedStyle(lastChild);
70
+ const lastChildMarginRight = parseFloat(lastChildStyle.marginRight);
71
+ return firstChildMarginLeft + lastChildMarginRight;
72
+ }
73
+
74
+ function Slider(container, options, plugins) {
75
+ let slider;
76
+ let subs = {};
77
+ function init() {
78
+ slider.container = container;
79
+ // ensure container has id
80
+ let containerId = container.getAttribute('id');
81
+ if (containerId === null) {
82
+ containerId = generateId('overflow-slider');
83
+ container.setAttribute('id', containerId);
84
+ }
85
+ setSlides();
86
+ setDetails(true);
87
+ setActiveSlideIdx();
88
+ slider.on('contentsChanged', () => {
89
+ setSlides();
90
+ setDetails();
91
+ setActiveSlideIdx();
92
+ });
93
+ slider.on('containerSizeChanged', () => setDetails());
94
+ let requestId = 0;
95
+ const setDetailsDebounce = () => {
96
+ if (requestId) {
97
+ window.cancelAnimationFrame(requestId);
98
+ }
99
+ requestId = window.requestAnimationFrame(() => {
100
+ setDetails();
101
+ setActiveSlideIdx();
102
+ });
103
+ };
104
+ slider.on('scroll', setDetailsDebounce);
105
+ addEventListeners();
106
+ setDataAttributes();
107
+ setCSSVariables();
108
+ if (plugins) {
109
+ for (const plugin of plugins) {
110
+ plugin(slider);
111
+ }
112
+ }
113
+ slider.on('detailsChanged', () => {
114
+ setDataAttributes();
115
+ setCSSVariables();
116
+ });
117
+ slider.emit('created');
118
+ slider.container.setAttribute('data-ready', 'true');
119
+ }
120
+ function setDetails(isInit = false) {
121
+ const oldDetails = slider.details;
122
+ const newDetails = details(slider);
123
+ slider.details = newDetails;
124
+ if (!isInit && !objectsAreEqual(oldDetails, newDetails)) {
125
+ slider.emit('detailsChanged');
126
+ }
127
+ else if (isInit) {
128
+ slider.emit('detailsChanged');
129
+ }
130
+ }
131
+ function setSlides() {
132
+ slider.slides = Array.from(slider.container.querySelectorAll(slider.options.slidesSelector));
133
+ }
134
+ function addEventListeners() {
135
+ // changes to DOM
136
+ const observer = new MutationObserver(() => slider.emit('contentsChanged'));
137
+ observer.observe(slider.container, { childList: true });
138
+ // container size changes
139
+ const resizeObserver = new ResizeObserver(() => slider.emit('containerSizeChanged'));
140
+ resizeObserver.observe(slider.container);
141
+ // scroll event with debouncing
142
+ let scrollTimeout;
143
+ let nativeScrollTimeout;
144
+ let programmaticScrollTimeout;
145
+ let scrollLeft = slider.container.scrollLeft;
146
+ let nativeScrollLeft = slider.container.scrollLeft;
147
+ let programmaticScrollLeft = slider.container.scrollLeft;
148
+ let isScrolling = false;
149
+ let isUserScrolling = false;
150
+ let isProgrammaticScrolling = false;
151
+ // all types of scroll
152
+ slider.container.addEventListener('scroll', () => {
153
+ const newScrollLeft = slider.container.scrollLeft;
154
+ if (Math.floor(scrollLeft) !== Math.floor(newScrollLeft)) {
155
+ if (!isScrolling) {
156
+ isScrolling = true;
157
+ slider.emit('scrollStart');
158
+ }
159
+ scrollLeft = newScrollLeft;
160
+ clearTimeout(scrollTimeout);
161
+ scrollTimeout = setTimeout(() => {
162
+ isScrolling = false;
163
+ slider.emit('scrollEnd');
164
+ }, 50);
165
+ slider.emit('scroll');
166
+ }
167
+ // keep up nativeScrolling to take into account scroll-snap
168
+ if (isUserScrolling) {
169
+ nativeScrollHandler();
170
+ }
171
+ });
172
+ // user initted scroll (touchmove, mouse wheel, etc.)
173
+ const nativeScrollHandler = () => {
174
+ const newScrollLeft = slider.container.scrollLeft;
175
+ if (Math.floor(nativeScrollLeft) !== Math.floor(newScrollLeft) && !isProgrammaticScrolling) {
176
+ if (!isUserScrolling) {
177
+ slider.emit('nativeScrollStart');
178
+ isUserScrolling = true;
179
+ }
180
+ slider.emit('nativeScroll');
181
+ nativeScrollLeft = newScrollLeft;
182
+ clearTimeout(nativeScrollTimeout);
183
+ nativeScrollTimeout = setTimeout(() => {
184
+ isUserScrolling = false;
185
+ slider.emit('nativeScrollEnd');
186
+ // update programmaticScrollLeft to match nativeScrollLeft
187
+ // this prevents programmaticScroll triggering with no real change to scrollLeft
188
+ programmaticScrollLeft = nativeScrollLeft;
189
+ }, 50);
190
+ }
191
+ };
192
+ slider.container.addEventListener('touchmove', nativeScrollHandler);
193
+ slider.container.addEventListener('mousewheel', nativeScrollHandler);
194
+ slider.container.addEventListener('wheel', nativeScrollHandler);
195
+ // programmatic scroll (scrollTo, etc.)
196
+ slider.on('programmaticScrollStart', () => {
197
+ isProgrammaticScrolling = true;
198
+ });
199
+ slider.container.addEventListener('scroll', () => {
200
+ const newScrollLeft = slider.container.scrollLeft;
201
+ if (Math.floor(programmaticScrollLeft) !== Math.floor(newScrollLeft) && !isUserScrolling && isProgrammaticScrolling) {
202
+ programmaticScrollLeft = newScrollLeft;
203
+ clearTimeout(programmaticScrollTimeout);
204
+ programmaticScrollTimeout = setTimeout(() => {
205
+ isProgrammaticScrolling = false;
206
+ slider.emit('programmaticScrollEnd');
207
+ // update nativeScrollLeft to match programmaticScrollLeft
208
+ // this prevents nativeScroll triggering with no real change to scrollLeft
209
+ nativeScrollLeft = programmaticScrollLeft;
210
+ }, 50);
211
+ slider.emit('programmaticScroll');
212
+ }
213
+ });
214
+ // Fix issues on scroll snapping not working on programmatic scroll (it's not smooth)
215
+ // by disabling scroll snap if scrolling is programmatic
216
+ slider.on('programmaticScrollStart', () => {
217
+ slider.container.style.scrollSnapType = 'none';
218
+ });
219
+ // restore scroll snap if user scroll starts
220
+ slider.on('nativeScrollStart', () => {
221
+ slider.container.style.scrollSnapType = '';
222
+ });
223
+ // Listen for mouse down and touch start events on the document
224
+ // This handles both mouse clicks and touch interactions
225
+ let wasInteractedWith = false;
226
+ slider.container.addEventListener('mousedown', () => {
227
+ wasInteractedWith = true;
228
+ });
229
+ slider.container.addEventListener('touchstart', () => {
230
+ wasInteractedWith = true;
231
+ }, { passive: true });
232
+ slider.container.addEventListener('focusin', (e) => {
233
+ // move target parents as long as they are not the container
234
+ // but only if focus didn't start from mouse or touch
235
+ if (!wasInteractedWith) {
236
+ let target = e.target;
237
+ while (target.parentElement !== slider.container) {
238
+ if (target.parentElement) {
239
+ target = target.parentElement;
240
+ }
241
+ else {
242
+ break;
243
+ }
244
+ }
245
+ ensureSlideIsInView(target, 'auto');
246
+ }
247
+ wasInteractedWith = false;
248
+ });
249
+ }
250
+ function setCSSVariables() {
251
+ slider.options.cssVariableContainer.style.setProperty('--slider-container-height', `${slider.details.containerHeight}px`);
252
+ slider.options.cssVariableContainer.style.setProperty('--slider-container-width', `${slider.details.containerWidth}px`);
253
+ slider.options.cssVariableContainer.style.setProperty('--slider-scrollable-width', `${slider.details.scrollableAreaWidth}px`);
254
+ slider.options.cssVariableContainer.style.setProperty('--slider-slides-count', `${slider.details.slideCount}`);
255
+ slider.options.cssVariableContainer.style.setProperty('--slider-x-offset', `${getLeftOffset()}px`);
256
+ if (typeof slider.options.targetWidth === 'function') {
257
+ slider.options.cssVariableContainer.style.setProperty('--slider-container-target-width', `${slider.options.targetWidth(slider)}px`);
258
+ }
259
+ }
260
+ function setDataAttributes() {
261
+ slider.container.setAttribute('data-has-overflow', slider.details.hasOverflow ? 'true' : 'false');
262
+ if (slider.options.rtl) {
263
+ slider.container.setAttribute('dir', 'rtl');
264
+ }
265
+ }
266
+ function ensureSlideIsInView(slide, scrollBehavior = null) {
267
+ const behavior = scrollBehavior || slider.options.scrollBehavior;
268
+ const slideRect = slide.getBoundingClientRect();
269
+ const sliderRect = slider.container.getBoundingClientRect();
270
+ const containerWidth = slider.container.offsetWidth;
271
+ const scrollLeft = slider.container.scrollLeft;
272
+ const slideStart = slideRect.left - sliderRect.left + scrollLeft;
273
+ const slideEnd = slideStart + slideRect.width;
274
+ let scrollTarget = null;
275
+ if (Math.floor(slideStart) < Math.floor(scrollLeft)) {
276
+ scrollTarget = slideStart;
277
+ }
278
+ else if (Math.floor(slideEnd) > Math.floor(scrollLeft) + Math.floor(containerWidth)) {
279
+ scrollTarget = slideEnd - containerWidth;
280
+ }
281
+ else if (Math.floor(slideStart) === 0) {
282
+ scrollTarget = 0;
283
+ }
284
+ else {
285
+ scrollTarget = slideStart;
286
+ }
287
+ if (scrollTarget !== null) {
288
+ setTimeout((scrollTarget) => {
289
+ slider.emit('programmaticScrollStart');
290
+ slider.container.scrollTo({ left: scrollTarget, behavior: behavior });
291
+ }, 50, scrollTarget);
292
+ }
293
+ }
294
+ function setActiveSlideIdx() {
295
+ const sliderRect = slider.container.getBoundingClientRect();
296
+ const scrollLeft = slider.getScrollLeft();
297
+ const slides = slider.slides;
298
+ let activeSlideIdx = 0;
299
+ let scrolledPastLastSlide = false;
300
+ if (slider.options.rtl) {
301
+ const scrolledDistance = slider.getInclusiveScrollWidth() - scrollLeft - slider.getInclusiveClientWidth();
302
+ const slidePositions = [];
303
+ for (let i = slides.length - 1; i >= 0; i--) {
304
+ const slideRect = slides[i].getBoundingClientRect();
305
+ const slideEnd = Math.abs(slideRect.left) - Math.abs(sliderRect.left) + scrolledDistance;
306
+ slidePositions.push({
307
+ slide: slides[i],
308
+ slideEnd: slideEnd,
309
+ });
310
+ }
311
+ let closestSlide = null;
312
+ let closestDistance = null;
313
+ for (let i = 0; i < slidePositions.length; i++) {
314
+ const distance = Math.abs(slidePositions[i].slideEnd - scrolledDistance);
315
+ if (closestDistance === null || distance < closestDistance) {
316
+ closestDistance = distance;
317
+ closestSlide = slidePositions[i].slide;
318
+ }
319
+ }
320
+ if (closestSlide) {
321
+ activeSlideIdx = slides.indexOf(closestSlide);
322
+ }
323
+ else {
324
+ activeSlideIdx = slides.length - 1;
325
+ }
326
+ }
327
+ else {
328
+ for (let i = 0; i < slides.length; i++) {
329
+ const slideRect = slides[i].getBoundingClientRect();
330
+ const slideStart = slideRect.left - sliderRect.left + scrollLeft + getGapSize();
331
+ if (Math.floor(slideStart) >= Math.floor(scrollLeft)) {
332
+ activeSlideIdx = i;
333
+ break;
334
+ }
335
+ if (i === slides.length - 1) {
336
+ scrolledPastLastSlide = true;
337
+ }
338
+ }
339
+ }
340
+ if (scrolledPastLastSlide) {
341
+ activeSlideIdx = slides.length - 1;
342
+ }
343
+ const oldActiveSlideIdx = slider.activeSlideIdx;
344
+ slider.activeSlideIdx = activeSlideIdx;
345
+ if (oldActiveSlideIdx !== activeSlideIdx) {
346
+ slider.emit('activeSlideChanged');
347
+ }
348
+ }
349
+ function moveToSlide(idx) {
350
+ const slide = slider.slides[idx];
351
+ if (slide) {
352
+ ensureSlideIsInView(slide);
353
+ }
354
+ }
355
+ function canMoveToSlide(idx) {
356
+ if (idx < 0 || idx >= slider.slides.length) {
357
+ return false;
358
+ }
359
+ if (idx === slider.activeSlideIdx) {
360
+ return false;
361
+ }
362
+ const direction = slider.options.rtl ? (idx < slider.activeSlideIdx ? 'backwards' : 'forwards') : (idx < slider.activeSlideIdx ? 'backwards' : 'forwards');
363
+ // check if the slide is already in view
364
+ const sliderRect = slider.container.getBoundingClientRect();
365
+ const scrollLeft = slider.getScrollLeft();
366
+ const containerWidth = slider.details.containerWidth;
367
+ const hasUpcomingContent = slider.slides.some((s, i) => {
368
+ if (i === slider.activeSlideIdx) {
369
+ return false; // skip the slide we are checking
370
+ }
371
+ const sRect = s.getBoundingClientRect();
372
+ const sStart = sRect.left - sliderRect.left + scrollLeft;
373
+ const sEnd = sStart + sRect.width;
374
+ if (slider.options.rtl) {
375
+ if (scrollLeft === 0 && slider.details.hasOverflow) {
376
+ return true;
377
+ }
378
+ return (direction === 'forwards' && i > slider.activeSlideIdx && Math.floor(sStart) < Math.floor(scrollLeft)) ||
379
+ (direction === 'backwards' && i < slider.activeSlideIdx && Math.floor(sEnd) > Math.floor(scrollLeft + containerWidth));
380
+ }
381
+ else {
382
+ return (direction === 'forwards' && i > slider.activeSlideIdx && Math.floor(sEnd) > Math.floor(scrollLeft + containerWidth)) ||
383
+ (direction === 'backwards' && i < slider.activeSlideIdx && Math.floor(sStart) < Math.floor(scrollLeft));
384
+ }
385
+ });
386
+ return hasUpcomingContent;
387
+ }
388
+ function moveToSlideInDirection(direction) {
389
+ const activeSlideIdx = slider.activeSlideIdx;
390
+ if (direction === 'prev') {
391
+ if (activeSlideIdx > 0) {
392
+ moveToSlide(activeSlideIdx - 1);
393
+ }
394
+ }
395
+ else if (direction === 'next') {
396
+ if (activeSlideIdx < slider.slides.length - 1) {
397
+ moveToSlide(activeSlideIdx + 1);
398
+ }
399
+ }
400
+ }
401
+ function getInclusiveScrollWidth() {
402
+ return slider.container.scrollWidth + getOutermostChildrenEdgeMarginSum(slider.container);
403
+ }
404
+ function getInclusiveClientWidth() {
405
+ return slider.container.clientWidth + getOutermostChildrenEdgeMarginSum(slider.container);
406
+ }
407
+ function getScrollLeft() {
408
+ return slider.options.rtl ? Math.abs(slider.container.scrollLeft) : slider.container.scrollLeft;
409
+ }
410
+ function setScrollLeft(value) {
411
+ slider.container.scrollLeft = slider.options.rtl ? -value : value;
412
+ }
413
+ function getGapSize() {
414
+ let gapSize = 0;
415
+ if (slider.slides.length > 1) {
416
+ const firstSlideRect = slider.slides[0].getBoundingClientRect();
417
+ const secondSlideRect = slider.slides[1].getBoundingClientRect();
418
+ gapSize = slider.options.rtl ? Math.abs(Math.floor(secondSlideRect.right - firstSlideRect.left)) : Math.floor(secondSlideRect.left - firstSlideRect.right);
419
+ }
420
+ return gapSize;
421
+ }
422
+ function getLeftOffset() {
423
+ let offset = 0;
424
+ const fullWidthOffset = slider.container.getAttribute('data-full-width-offset');
425
+ if (fullWidthOffset) {
426
+ offset = parseInt(fullWidthOffset);
427
+ }
428
+ return Math.floor(offset);
429
+ }
430
+ function moveToDirection(direction = "prev") {
431
+ const scrollStrategy = slider.options.scrollStrategy;
432
+ const scrollLeft = slider.container.scrollLeft;
433
+ const sliderRect = slider.container.getBoundingClientRect();
434
+ const containerWidth = slider.container.offsetWidth;
435
+ let targetScrollPosition = scrollLeft;
436
+ const realDirection = slider.options.rtl ? (direction === 'prev' ? 'next' : 'prev') : direction;
437
+ if (realDirection === 'prev') {
438
+ targetScrollPosition = Math.max(0, scrollLeft - slider.container.offsetWidth);
439
+ }
440
+ else if (realDirection === 'next') {
441
+ targetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), scrollLeft + slider.container.offsetWidth);
442
+ }
443
+ if (scrollStrategy === 'fullSlide') {
444
+ let fullSlideTargetScrollPosition = null;
445
+ // extend targetScrollPosition to include gap
446
+ if (realDirection === 'prev') {
447
+ fullSlideTargetScrollPosition = Math.max(0, targetScrollPosition - getGapSize());
448
+ }
449
+ else {
450
+ fullSlideTargetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), targetScrollPosition + getGapSize());
451
+ }
452
+ if (realDirection === 'next') {
453
+ let partialSlideFound = false;
454
+ for (let slide of slider.slides) {
455
+ const slideRect = slide.getBoundingClientRect();
456
+ const slideStart = slideRect.left - sliderRect.left + scrollLeft;
457
+ const slideEnd = slideStart + slideRect.width;
458
+ if (Math.floor(slideStart) < Math.floor(targetScrollPosition) && Math.floor(slideEnd) > Math.floor(targetScrollPosition)) {
459
+ fullSlideTargetScrollPosition = slideStart;
460
+ partialSlideFound = true;
461
+ break;
462
+ }
463
+ }
464
+ if (!partialSlideFound) {
465
+ fullSlideTargetScrollPosition = Math.min(targetScrollPosition, slider.getInclusiveScrollWidth() - slider.container.offsetWidth);
466
+ }
467
+ if (fullSlideTargetScrollPosition) {
468
+ if (Math.floor(fullSlideTargetScrollPosition) > Math.floor(scrollLeft)) {
469
+ // make sure fullSlideTargetScrollPosition is possible considering the container width
470
+ const maxScrollPosition = Math.floor(slider.getInclusiveScrollWidth()) - Math.floor(containerWidth);
471
+ targetScrollPosition = Math.min(fullSlideTargetScrollPosition, maxScrollPosition);
472
+ }
473
+ else {
474
+ // cannot snap to slide, move one page worth of distance
475
+ targetScrollPosition = Math.min(slider.getInclusiveScrollWidth(), scrollLeft + containerWidth);
476
+ }
477
+ }
478
+ }
479
+ else {
480
+ let partialSlideFound = false;
481
+ for (let slide of slider.slides) {
482
+ const slideRect = slide.getBoundingClientRect();
483
+ const slideStart = slideRect.left - sliderRect.left + scrollLeft;
484
+ const slideEnd = slideStart + slideRect.width;
485
+ if (Math.floor(slideStart) < Math.floor(scrollLeft) && Math.floor(slideEnd) > Math.floor(scrollLeft)) {
486
+ fullSlideTargetScrollPosition = slideEnd - containerWidth;
487
+ partialSlideFound = true;
488
+ break;
489
+ }
490
+ }
491
+ if (!partialSlideFound) {
492
+ fullSlideTargetScrollPosition = Math.max(0, scrollLeft - containerWidth);
493
+ }
494
+ if (fullSlideTargetScrollPosition && Math.floor(fullSlideTargetScrollPosition) < Math.floor(scrollLeft)) {
495
+ targetScrollPosition = fullSlideTargetScrollPosition;
496
+ }
497
+ }
498
+ }
499
+ // add left offset
500
+ const offsettedTargetScrollPosition = targetScrollPosition - getLeftOffset();
501
+ if (Math.floor(offsettedTargetScrollPosition) >= 0) {
502
+ targetScrollPosition = offsettedTargetScrollPosition;
503
+ }
504
+ slider.emit('programmaticScrollStart');
505
+ slider.container.style.scrollBehavior = slider.options.scrollBehavior;
506
+ slider.container.scrollLeft = targetScrollPosition;
507
+ setTimeout(() => slider.container.style.scrollBehavior = '', 50);
508
+ }
509
+ function snapToClosestSlide(direction = "prev") {
510
+ var _a, _b;
511
+ const { slides, options, container } = slider;
512
+ const { rtl, emulateScrollSnapMaxThreshold = 10, scrollBehavior = 'smooth', } = options;
513
+ const isForward = rtl ? direction === 'prev' : direction === 'next';
514
+ const scrollPos = getScrollLeft();
515
+ // Get container rect once (includes any CSS transforms)
516
+ const containerRect = container.getBoundingClientRect();
517
+ const factor = rtl ? -1 : 1;
518
+ // Build slide metadata
519
+ const slideData = [...slides].map(slide => {
520
+ const { width } = slide.getBoundingClientRect();
521
+ const slideRect = slide.getBoundingClientRect();
522
+ // position relative to container’s left edge
523
+ const relativeStart = (slideRect.left - containerRect.left) + scrollPos;
524
+ const triggerPoint = Math.min(relativeStart + width / 2, relativeStart + emulateScrollSnapMaxThreshold);
525
+ return { start: relativeStart, trigger: triggerPoint };
526
+ });
527
+ // Pick the target start based on drag direction
528
+ let targetStart = null;
529
+ if (isForward) {
530
+ const found = slideData.find(item => scrollPos <= item.trigger);
531
+ targetStart = (_a = found === null || found === void 0 ? void 0 : found.start) !== null && _a !== void 0 ? _a : null;
532
+ }
533
+ else {
534
+ const found = [...slideData].reverse().find(item => scrollPos >= item.trigger);
535
+ targetStart = (_b = found === null || found === void 0 ? void 0 : found.start) !== null && _b !== void 0 ? _b : null;
536
+ }
537
+ if (targetStart == null)
538
+ return;
539
+ // Clamp to zero and apply RTL factor
540
+ const finalLeft = Math.max(0, Math.floor(targetStart)) * factor;
541
+ container.scrollTo({ left: finalLeft, behavior: scrollBehavior });
542
+ }
543
+ function on(name, cb) {
544
+ if (!subs[name]) {
545
+ subs[name] = [];
546
+ }
547
+ subs[name].push(cb);
548
+ }
549
+ function emit(name) {
550
+ var _a;
551
+ if (subs && subs[name]) {
552
+ subs[name].forEach(cb => {
553
+ cb(slider);
554
+ });
555
+ }
556
+ const optionCallBack = (_a = slider === null || slider === void 0 ? void 0 : slider.options) === null || _a === void 0 ? void 0 : _a[name];
557
+ // Type guard to check if the option callback is a function
558
+ if (typeof optionCallBack === 'function') {
559
+ optionCallBack(slider); // Type assertion here
560
+ }
561
+ }
562
+ slider = {
563
+ emit,
564
+ moveToDirection,
565
+ canMoveToSlide,
566
+ moveToSlide,
567
+ moveToSlideInDirection,
568
+ snapToClosestSlide,
569
+ getInclusiveScrollWidth,
570
+ getInclusiveClientWidth,
571
+ getScrollLeft,
572
+ setScrollLeft,
573
+ setActiveSlideIdx,
574
+ on,
575
+ options,
576
+ };
577
+ init();
578
+ return slider;
579
+ }
580
+
581
+ function OverflowSlider(container, options, plugins) {
582
+ try {
583
+ // check that container HTML element
584
+ if (!(container instanceof Element)) {
585
+ throw new Error(`Container must be HTML element, found ${typeof container}`);
586
+ }
587
+ const defaults = {
588
+ cssVariableContainer: container,
589
+ scrollBehavior: "smooth",
590
+ scrollStrategy: "fullSlide",
591
+ slidesSelector: ":scope > *",
592
+ emulateScrollSnap: false,
593
+ emulateScrollSnapMaxThreshold: 64,
594
+ rtl: false,
595
+ };
596
+ const sliderOptions = Object.assign(Object.assign({}, defaults), options);
597
+ // disable smooth scrolling if user prefers reduced motion
598
+ if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
599
+ sliderOptions.scrollBehavior = "auto";
600
+ }
601
+ return Slider(container, sliderOptions, plugins);
602
+ }
603
+ catch (e) {
604
+ console.error(e);
605
+ }
606
+ }
607
+
608
+ export { OverflowSlider };
609
+ //# sourceMappingURL=index.esm.js.map