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