lazer-slider 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -26,7 +26,10 @@ __export(index_exports, {
26
26
  easeOutExpo: () => easeOutExpo,
27
27
  easeOutQuad: () => easeOutQuad,
28
28
  generateBullets: () => generateBullets,
29
- linear: () => linear
29
+ generateThumbs: () => generateThumbs,
30
+ injectStyles: () => injectStyles,
31
+ linear: () => linear,
32
+ removeStyles: () => removeStyles
30
33
  });
31
34
  module.exports = __toCommonJS(index_exports);
32
35
 
@@ -117,16 +120,19 @@ var updateActiveThumb = (thumbs, currentIndex, activeClass = "active") => {
117
120
  thumb.setAttribute("aria-selected", isActive.toString());
118
121
  });
119
122
  };
120
- var setupKeyboardNavigation = (feed, onPrev, onNext, abortSignal) => {
123
+ var setupKeyboardNavigation = (feed, onPrev, onNext, abortSignal, direction = "horizontal") => {
124
+ const isVertical = direction === "vertical";
125
+ const prevKey = isVertical ? "ArrowUp" : "ArrowLeft";
126
+ const nextKey = isVertical ? "ArrowDown" : "ArrowRight";
121
127
  feed.addEventListener(
122
128
  "keydown",
123
129
  (event) => {
124
130
  switch (event.key) {
125
- case "ArrowLeft":
131
+ case prevKey:
126
132
  event.preventDefault();
127
133
  onPrev();
128
134
  break;
129
- case "ArrowRight":
135
+ case nextKey:
130
136
  event.preventDefault();
131
137
  onNext();
132
138
  break;
@@ -143,21 +149,25 @@ var setupKeyboardNavigation = (feed, onPrev, onNext, abortSignal) => {
143
149
  var createDragState = () => ({
144
150
  isDragging: false,
145
151
  startX: 0,
152
+ startY: 0,
146
153
  startScrollLeft: 0,
154
+ startScrollTop: 0,
147
155
  velocity: 0,
148
156
  lastX: 0,
157
+ lastY: 0,
149
158
  lastTime: 0,
150
159
  momentumId: null
151
160
  });
152
- var findNearestSlide = (feed, slides) => {
161
+ var findNearestSlide = (feed, slides, direction = "horizontal") => {
153
162
  const feedRect = feed.getBoundingClientRect();
154
- const feedCenter = feedRect.left + feedRect.width / 2;
163
+ const isVertical = direction === "vertical";
164
+ const feedCenter = isVertical ? feedRect.top + feedRect.height / 2 : feedRect.left + feedRect.width / 2;
155
165
  let nearestSlide = null;
156
166
  let minDistance = Infinity;
157
167
  for (const slide of slides) {
158
168
  if (slide.offsetParent === null) continue;
159
169
  const slideRect = slide.getBoundingClientRect();
160
- const slideCenter = slideRect.left + slideRect.width / 2;
170
+ const slideCenter = isVertical ? slideRect.top + slideRect.height / 2 : slideRect.left + slideRect.width / 2;
161
171
  const distance = Math.abs(feedCenter - slideCenter);
162
172
  if (distance < minDistance) {
163
173
  minDistance = distance;
@@ -166,20 +176,26 @@ var findNearestSlide = (feed, slides) => {
166
176
  }
167
177
  return nearestSlide;
168
178
  };
169
- var applyMomentum = (state, feed, slides, smoothScrollTo, onDragEnd) => {
179
+ var applyMomentum = (state, feed, slides, smoothScrollTo, onDragEnd, direction = "horizontal") => {
170
180
  const friction = 0.95;
171
181
  const minVelocity = 0.5;
182
+ const isVertical = direction === "vertical";
172
183
  const animate = () => {
173
184
  if (Math.abs(state.velocity) < minVelocity) {
174
185
  state.momentumId = null;
175
- const nearestSlide = findNearestSlide(feed, slides);
186
+ const nearestSlide = findNearestSlide(feed, slides, direction);
176
187
  if (nearestSlide) {
177
- smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic);
188
+ const targetPosition = isVertical ? nearestSlide.offsetTop : nearestSlide.offsetLeft;
189
+ smoothScrollTo(targetPosition, easeOutCubic);
178
190
  }
179
191
  onDragEnd?.();
180
192
  return;
181
193
  }
182
- feed.scrollLeft += state.velocity;
194
+ if (isVertical) {
195
+ feed.scrollTop += state.velocity;
196
+ } else {
197
+ feed.scrollLeft += state.velocity;
198
+ }
183
199
  state.velocity *= friction;
184
200
  state.momentumId = requestAnimationFrame(animate);
185
201
  };
@@ -191,9 +207,16 @@ var getEventX = (event) => {
191
207
  }
192
208
  return event.clientX;
193
209
  };
210
+ var getEventY = (event) => {
211
+ if ("touches" in event) {
212
+ return event.touches[0]?.clientY ?? 0;
213
+ }
214
+ return event.clientY;
215
+ };
194
216
  var setupDragToScroll = (config) => {
195
- const { feed, slides, abortSignal, smoothScrollTo, onDragEnd, dragFree = false } = config;
217
+ const { feed, slides, abortSignal, smoothScrollTo, onDragEnd, direction = "horizontal" } = config;
196
218
  const state = createDragState();
219
+ const isVertical = direction === "vertical";
197
220
  const handleDragStart = (event) => {
198
221
  if (state.momentumId !== null) {
199
222
  cancelAnimationFrame(state.momentumId);
@@ -201,11 +224,13 @@ var setupDragToScroll = (config) => {
201
224
  }
202
225
  state.isDragging = true;
203
226
  state.startX = getEventX(event);
227
+ state.startY = getEventY(event);
204
228
  state.startScrollLeft = feed.scrollLeft;
229
+ state.startScrollTop = feed.scrollTop;
205
230
  state.velocity = 0;
206
231
  state.lastX = state.startX;
232
+ state.lastY = state.startY;
207
233
  state.lastTime = performance.now();
208
- feed.style.cursor = "grabbing";
209
234
  feed.style.userSelect = "none";
210
235
  if (event.type === "mousedown") {
211
236
  event.preventDefault();
@@ -214,14 +239,24 @@ var setupDragToScroll = (config) => {
214
239
  const handleDragMove = (event) => {
215
240
  if (!state.isDragging) return;
216
241
  const currentX = getEventX(event);
242
+ const currentY = getEventY(event);
217
243
  const currentTime = performance.now();
218
- const deltaX = state.startX - currentX;
219
244
  const deltaTime = currentTime - state.lastTime;
220
- feed.scrollLeft = state.startScrollLeft + deltaX;
221
- if (deltaTime > 0) {
222
- state.velocity = (state.lastX - currentX) / deltaTime * 16;
245
+ if (isVertical) {
246
+ const deltaY = state.startY - currentY;
247
+ feed.scrollTop = state.startScrollTop + deltaY;
248
+ if (deltaTime > 0) {
249
+ state.velocity = (state.lastY - currentY) / deltaTime * 16;
250
+ }
251
+ state.lastY = currentY;
252
+ } else {
253
+ const deltaX = state.startX - currentX;
254
+ feed.scrollLeft = state.startScrollLeft + deltaX;
255
+ if (deltaTime > 0) {
256
+ state.velocity = (state.lastX - currentX) / deltaTime * 16;
257
+ }
258
+ state.lastX = currentX;
223
259
  }
224
- state.lastX = currentX;
225
260
  state.lastTime = currentTime;
226
261
  if (event.type === "touchmove") {
227
262
  event.preventDefault();
@@ -230,41 +265,18 @@ var setupDragToScroll = (config) => {
230
265
  const handleDragEnd = () => {
231
266
  if (!state.isDragging) return;
232
267
  state.isDragging = false;
233
- feed.style.cursor = "grab";
234
268
  feed.style.userSelect = "";
235
- if (dragFree) {
236
- if (Math.abs(state.velocity) > 1) {
237
- applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd);
238
- } else {
239
- const nearestSlide = findNearestSlide(feed, slides);
240
- if (nearestSlide) {
241
- smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic);
242
- }
243
- onDragEnd?.();
244
- }
269
+ if (Math.abs(state.velocity) > 1) {
270
+ applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd, direction);
245
271
  } else {
246
- const swipeDistance = state.startX - state.lastX;
247
- const swipeThreshold = 50;
248
- const nearestSlide = findNearestSlide(feed, slides);
249
- if (nearestSlide && Math.abs(swipeDistance) > swipeThreshold) {
250
- const visibleSlides = slides.filter((slide) => slide.offsetParent !== null);
251
- const visibleIndex = visibleSlides.indexOf(nearestSlide);
252
- let targetSlide = null;
253
- if (swipeDistance > 0 && visibleIndex < visibleSlides.length - 1) {
254
- targetSlide = visibleSlides[visibleIndex + 1] ?? nearestSlide;
255
- } else if (swipeDistance < 0 && visibleIndex > 0) {
256
- targetSlide = visibleSlides[visibleIndex - 1] ?? nearestSlide;
257
- } else {
258
- targetSlide = nearestSlide;
259
- }
260
- smoothScrollTo(targetSlide.offsetLeft, easeOutCubic);
261
- } else if (nearestSlide) {
262
- smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic);
272
+ const nearestSlide = findNearestSlide(feed, slides, direction);
273
+ if (nearestSlide) {
274
+ const targetPosition = isVertical ? nearestSlide.offsetTop : nearestSlide.offsetLeft;
275
+ smoothScrollTo(targetPosition, easeOutCubic);
263
276
  }
264
277
  onDragEnd?.();
265
278
  }
266
279
  };
267
- feed.style.cursor = "grab";
268
280
  feed.addEventListener("mousedown", handleDragStart, { signal: abortSignal });
269
281
  document.addEventListener("mousemove", handleDragMove, { signal: abortSignal });
270
282
  document.addEventListener("mouseup", handleDragEnd, { signal: abortSignal });
@@ -333,6 +345,67 @@ var generateBullets = ({
333
345
  return bullets;
334
346
  };
335
347
 
348
+ // src/core/thumbs.ts
349
+ var generateThumbs = ({
350
+ thumbsContainer,
351
+ slides,
352
+ thumbClass,
353
+ thumbActiveClass,
354
+ feedId,
355
+ thumbImageSelector = "img",
356
+ thumbSize
357
+ }) => {
358
+ if (!thumbsContainer || !(thumbsContainer instanceof HTMLElement)) {
359
+ throw new Error("Invalid thumbsContainer: must be a valid HTMLElement");
360
+ }
361
+ if (!Array.isArray(slides) || slides.length === 0) {
362
+ throw new Error("Invalid slides: must be a non-empty array");
363
+ }
364
+ if (!feedId || typeof feedId !== "string") {
365
+ throw new Error("Invalid feedId: must be a non-empty string");
366
+ }
367
+ if (!thumbClass || typeof thumbClass !== "string") {
368
+ throw new Error("Invalid thumbClass: must be a non-empty string");
369
+ }
370
+ thumbsContainer.innerHTML = "";
371
+ thumbsContainer.setAttribute("role", "tablist");
372
+ thumbsContainer.setAttribute("aria-label", "Slide thumbnails");
373
+ const visibleSlides = slides.map((slide, originalIndex) => ({ slide, originalIndex })).filter(({ slide }) => slide.offsetParent !== null);
374
+ if (visibleSlides.length === 0) {
375
+ console.warn("No visible slides found");
376
+ return [];
377
+ }
378
+ const thumbs = visibleSlides.map(({ slide, originalIndex }, visibleIndex) => {
379
+ const thumb = document.createElement("button");
380
+ thumb.type = "button";
381
+ thumb.classList.add(thumbClass);
382
+ if (visibleIndex === 0) {
383
+ thumb.classList.add(thumbActiveClass);
384
+ }
385
+ const sourceImage = slide.querySelector(thumbImageSelector);
386
+ if (sourceImage?.src) {
387
+ const thumbImg = document.createElement("img");
388
+ thumbImg.src = sourceImage.src;
389
+ thumbImg.alt = sourceImage.alt || `Slide ${visibleIndex + 1} thumbnail`;
390
+ thumbImg.draggable = false;
391
+ if (thumbSize) {
392
+ thumbImg.style.width = `${thumbSize.width}px`;
393
+ thumbImg.style.height = `${thumbSize.height}px`;
394
+ thumbImg.style.objectFit = "cover";
395
+ }
396
+ thumb.appendChild(thumbImg);
397
+ }
398
+ thumb.setAttribute("role", "tab");
399
+ thumb.setAttribute("aria-selected", visibleIndex === 0 ? "true" : "false");
400
+ thumb.setAttribute("aria-controls", feedId);
401
+ thumb.setAttribute("aria-label", `Go to slide ${visibleIndex + 1}`);
402
+ thumb.setAttribute("data-slide-index", String(visibleIndex));
403
+ thumbsContainer.appendChild(thumb);
404
+ return thumb;
405
+ });
406
+ return thumbs;
407
+ };
408
+
336
409
  // src/core/marquee.ts
337
410
  var createMarqueeState = () => ({
338
411
  initialized: false,
@@ -447,6 +520,72 @@ var attachMarqueeEventListeners = (settings, state, signal) => {
447
520
  );
448
521
  };
449
522
 
523
+ // src/core/styles.ts
524
+ var CRITICAL_STYLES = `
525
+ /* Lazer Slider - Critical Styles (Auto-injected) */
526
+ .lazer-feed {
527
+ position: relative;
528
+ display: flex;
529
+ overflow-x: auto;
530
+ overflow-y: hidden;
531
+ -webkit-overflow-scrolling: touch;
532
+ scrollbar-width: none;
533
+ scroll-snap-type: x mandatory;
534
+ }
535
+
536
+ .lazer-feed::-webkit-scrollbar {
537
+ display: none;
538
+ }
539
+
540
+ .lazer-feed.lazer-vertical {
541
+ flex-direction: column;
542
+ overflow-x: hidden;
543
+ overflow-y: auto;
544
+ scroll-snap-type: y mandatory;
545
+ }
546
+
547
+ .lazer-slide {
548
+ scroll-snap-align: start;
549
+ flex-shrink: 0;
550
+ }
551
+
552
+ .lazer-feed.is-dragging {
553
+ cursor: grabbing;
554
+ user-select: none;
555
+ scroll-snap-type: none;
556
+ }
557
+
558
+ .lazer-feed:not(.is-dragging) {
559
+ cursor: grab;
560
+ }
561
+ `;
562
+ var stylesInjected = false;
563
+ var STYLE_ID = "lazer-slider-critical-styles";
564
+ var injectStyles = () => {
565
+ if (stylesInjected || typeof document === "undefined") return;
566
+ const existingStyle = document.getElementById(STYLE_ID);
567
+ if (existingStyle) {
568
+ stylesInjected = true;
569
+ return;
570
+ }
571
+ const style = document.createElement("style");
572
+ style.id = STYLE_ID;
573
+ style.textContent = CRITICAL_STYLES;
574
+ document.head.appendChild(style);
575
+ stylesInjected = true;
576
+ };
577
+ var injectStylesOnce = () => {
578
+ injectStyles();
579
+ };
580
+ var removeStyles = () => {
581
+ if (typeof document === "undefined") return;
582
+ const style = document.getElementById(STYLE_ID);
583
+ if (style) {
584
+ style.remove();
585
+ stylesInjected = false;
586
+ }
587
+ };
588
+
450
589
  // src/core/slider.ts
451
590
  var ANIMATION = {
452
591
  MIN_DURATION: 400,
@@ -457,6 +596,7 @@ var ANIMATION = {
457
596
  };
458
597
  var DESKTOP_BREAKPOINT = "(min-width: 64rem)";
459
598
  var createSlider = (settings) => {
599
+ injectStylesOnce();
460
600
  if (!settings.feed) {
461
601
  throw new Error("lazer-slider: feed element is required");
462
602
  }
@@ -466,16 +606,40 @@ var createSlider = (settings) => {
466
606
  if (!settings.feed.id) {
467
607
  settings.feed.id = `lazer-slider-feed-${Math.random().toString(36).substr(2, 9)}`;
468
608
  }
609
+ settings.feed.classList.add("lazer-feed");
610
+ settings.slides.forEach((slide) => {
611
+ slide.classList.add("lazer-slide");
612
+ });
469
613
  if (settings.bulletsContainer && !settings.thumbs) {
470
614
  const bullets = generateBullets({
471
615
  bulletsContainer: settings.bulletsContainer,
472
616
  slides: settings.slides,
473
- bulletClass: settings.bulletsClass ?? "slider-bullet",
617
+ bulletClass: settings.bulletsClass ?? "lazer-bullet",
474
618
  bulletActiveClass: settings.bulletsActiveClass ?? "active",
475
619
  feedId: settings.feed.id
476
620
  });
477
621
  settings.thumbs = bullets;
478
622
  }
623
+ if (settings.thumbsContainer && !settings.thumbs) {
624
+ const thumbs = generateThumbs({
625
+ thumbsContainer: settings.thumbsContainer,
626
+ slides: settings.slides,
627
+ thumbClass: settings.thumbsClass ?? "lazer-thumb",
628
+ thumbActiveClass: settings.thumbsActiveClass ?? "active",
629
+ feedId: settings.feed.id,
630
+ thumbImageSelector: settings.thumbImageSelector ?? "img",
631
+ thumbSize: settings.thumbSize
632
+ });
633
+ settings.thumbs = thumbs;
634
+ }
635
+ const direction = settings.direction ?? "horizontal";
636
+ const isVertical = direction === "vertical";
637
+ if (isVertical) {
638
+ settings.feed.classList.add("lazer-vertical");
639
+ }
640
+ if (settings.marquee) {
641
+ settings.feed.classList.add("lazer-marquee");
642
+ }
479
643
  const state = {
480
644
  currentSlideIndex: 0,
481
645
  isScrolling: false,
@@ -484,7 +648,6 @@ var createSlider = (settings) => {
484
648
  lastWidth: 0,
485
649
  updateThumbTimeout: null,
486
650
  scrollEndTimeout: null,
487
- resizeTimeout: null,
488
651
  abortController: new AbortController(),
489
652
  autoplayIntervalId: null,
490
653
  autoplayPaused: false,
@@ -503,10 +666,10 @@ var createSlider = (settings) => {
503
666
  const marqueeState = createMarqueeState();
504
667
  const easing = settings.easing ?? easeOutExpo;
505
668
  const getFeedRect = () => {
506
- const currentWidth = settings.feed.clientWidth;
507
- if (!state.cachedFeedRect || state.lastWidth !== currentWidth) {
669
+ const currentSize = isVertical ? settings.feed.clientHeight : settings.feed.clientWidth;
670
+ if (!state.cachedFeedRect || state.lastWidth !== currentSize) {
508
671
  state.cachedFeedRect = settings.feed.getBoundingClientRect();
509
- state.lastWidth = currentWidth;
672
+ state.lastWidth = currentSize;
510
673
  }
511
674
  return state.cachedFeedRect;
512
675
  };
@@ -549,26 +712,38 @@ var createSlider = (settings) => {
549
712
  requestAnimationFrame(() => {
550
713
  const firstRealSlide = realSlides[0];
551
714
  if (firstRealSlide) {
552
- settings.feed.scrollLeft = firstRealSlide.offsetLeft;
715
+ if (isVertical) {
716
+ settings.feed.scrollTop = firstRealSlide.offsetTop;
717
+ } else {
718
+ settings.feed.scrollLeft = firstRealSlide.offsetLeft;
719
+ }
553
720
  }
554
721
  });
555
722
  loopState.initialized = true;
556
723
  };
557
- const handleLoopReposition = (direction) => {
724
+ const handleLoopReposition = (navDirection) => {
558
725
  if (!settings.loop || !loopState.initialized) return;
559
726
  state.isLoopRepositioning = true;
560
727
  const realSlides = loopState.realSlides;
561
728
  const totalRealSlides = realSlides.length;
562
- if (direction === "next") {
729
+ if (navDirection === "next") {
563
730
  const firstRealSlide = realSlides[0];
564
731
  if (firstRealSlide) {
565
- settings.feed.scrollLeft = firstRealSlide.offsetLeft;
732
+ if (isVertical) {
733
+ settings.feed.scrollTop = firstRealSlide.offsetTop;
734
+ } else {
735
+ settings.feed.scrollLeft = firstRealSlide.offsetLeft;
736
+ }
566
737
  }
567
738
  state.currentSlideIndex = 0;
568
739
  } else {
569
740
  const lastRealSlide = realSlides[totalRealSlides - 1];
570
741
  if (lastRealSlide) {
571
- settings.feed.scrollLeft = lastRealSlide.offsetLeft;
742
+ if (isVertical) {
743
+ settings.feed.scrollTop = lastRealSlide.offsetTop;
744
+ } else {
745
+ settings.feed.scrollLeft = lastRealSlide.offsetLeft;
746
+ }
572
747
  }
573
748
  state.currentSlideIndex = totalRealSlides - 1;
574
749
  }
@@ -598,39 +773,74 @@ var createSlider = (settings) => {
598
773
  settings.slides.forEach((slide) => {
599
774
  slide.style.flex = "";
600
775
  slide.style.minWidth = "";
776
+ slide.style.minHeight = "";
601
777
  });
602
778
  return;
603
779
  }
604
- const totalGapWidth = gap * (perView - 1);
605
- const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`;
606
- settings.slides.forEach((slide) => {
607
- slide.style.flex = `0 0 ${slideWidth}`;
608
- slide.style.minWidth = slideWidth;
609
- });
780
+ const totalGapSize = gap * (perView - 1);
781
+ const slideSize = `calc((100% - ${totalGapSize}px) / ${perView})`;
782
+ if (isVertical) {
783
+ settings.slides.forEach((slide) => {
784
+ slide.style.flex = `0 0 ${slideSize}`;
785
+ slide.style.minHeight = slideSize;
786
+ slide.style.minWidth = "";
787
+ });
788
+ } else {
789
+ settings.slides.forEach((slide) => {
790
+ slide.style.flex = `0 0 ${slideSize}`;
791
+ slide.style.minWidth = slideSize;
792
+ slide.style.minHeight = "";
793
+ });
794
+ }
610
795
  };
611
796
  const updateScrollbar = () => {
612
797
  if (!settings.scrollbarThumb) return;
613
798
  const feedRect = getFeedRect();
614
- const thumbWidth = feedRect.width / settings.feed.scrollWidth * 100;
615
- settings.scrollbarThumb.style.width = `${thumbWidth}%`;
799
+ if (isVertical) {
800
+ const thumbHeight = feedRect.height / settings.feed.scrollHeight * 100;
801
+ settings.scrollbarThumb.style.height = `${thumbHeight}%`;
802
+ settings.scrollbarThumb.style.width = "";
803
+ } else {
804
+ const thumbWidth = feedRect.width / settings.feed.scrollWidth * 100;
805
+ settings.scrollbarThumb.style.width = `${thumbWidth}%`;
806
+ settings.scrollbarThumb.style.height = "";
807
+ }
616
808
  };
617
809
  const updateScrollbarPosition = () => {
618
810
  if (!settings.scrollbarThumb || !settings.scrollbarTrack) return;
619
- const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width;
620
- const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width;
621
- const totalTransform = trackWidth - thumbWidth;
622
- const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth;
623
- const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0;
624
- settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`;
811
+ if (isVertical) {
812
+ const trackHeight = settings.scrollbarTrack.getBoundingClientRect().height;
813
+ const thumbHeight = settings.scrollbarThumb.getBoundingClientRect().height;
814
+ const totalTransform = trackHeight - thumbHeight;
815
+ const maxScroll = settings.feed.scrollHeight - settings.feed.clientHeight;
816
+ const scrollProgress = maxScroll > 0 ? settings.feed.scrollTop / maxScroll : 0;
817
+ settings.scrollbarThumb.style.transform = `translateY(${totalTransform * scrollProgress}px)`;
818
+ } else {
819
+ const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width;
820
+ const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width;
821
+ const totalTransform = trackWidth - thumbWidth;
822
+ const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth;
823
+ const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0;
824
+ settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`;
825
+ }
625
826
  };
626
827
  const updateControlsVisibility = () => {
627
828
  if (state.isLoopRepositioning) {
628
829
  return;
629
830
  }
630
831
  const feedRect = getFeedRect();
631
- const isAtStart = settings.feed.scrollLeft <= 1;
632
- const isAtEnd = settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1;
633
- const shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width;
832
+ let isAtStart;
833
+ let isAtEnd;
834
+ let shouldHideScrollbar;
835
+ if (isVertical) {
836
+ isAtStart = settings.feed.scrollTop <= 1;
837
+ isAtEnd = settings.feed.scrollTop + feedRect.height >= settings.feed.scrollHeight - 1;
838
+ shouldHideScrollbar = settings.feed.scrollHeight <= feedRect.height;
839
+ } else {
840
+ isAtStart = settings.feed.scrollLeft <= 1;
841
+ isAtEnd = settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1;
842
+ shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width;
843
+ }
634
844
  if (settings.scrollbarTrack) {
635
845
  settings.scrollbarTrack.style.display = shouldHideScrollbar ? "none" : "block";
636
846
  }
@@ -664,21 +874,25 @@ var createSlider = (settings) => {
664
874
  const viewportVisibleSlides = slidesToCheck.filter((slide) => {
665
875
  const slideRect = slide.getBoundingClientRect();
666
876
  const tolerance = 20;
877
+ if (isVertical) {
878
+ return slideRect.bottom > feedRect.top + tolerance && slideRect.top < feedRect.bottom - tolerance;
879
+ }
667
880
  return slideRect.right > feedRect.left + tolerance && slideRect.left < feedRect.right - tolerance;
668
881
  });
669
882
  if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {
670
883
  const newIndex = slidesToCheck.indexOf(viewportVisibleSlides[0]);
671
884
  if (newIndex !== -1) {
672
885
  state.currentSlideIndex = newIndex;
886
+ const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
673
887
  settings.onScroll?.({
674
- currentScroll: settings.feed.scrollLeft,
888
+ currentScroll,
675
889
  currentSlideIndex: state.currentSlideIndex
676
890
  });
677
891
  }
678
892
  }
679
893
  };
680
894
  const smoothScrollTo = (target, customEasing = easing, onComplete) => {
681
- const start = settings.feed.scrollLeft;
895
+ const start = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
682
896
  const distance = Math.abs(target - start);
683
897
  const duration = Math.min(
684
898
  ANIMATION.MAX_DURATION,
@@ -689,11 +903,20 @@ var createSlider = (settings) => {
689
903
  const elapsed = (currentTime - startTime) / duration;
690
904
  const progress = Math.min(elapsed, 1);
691
905
  const ease = customEasing(progress);
692
- settings.feed.scrollLeft = start + (target - start) * ease;
906
+ const scrollPosition = start + (target - start) * ease;
907
+ if (isVertical) {
908
+ settings.feed.scrollTop = scrollPosition;
909
+ } else {
910
+ settings.feed.scrollLeft = scrollPosition;
911
+ }
693
912
  if (progress < 1) {
694
913
  requestAnimationFrame(animateScroll);
695
914
  } else {
696
- settings.feed.scrollLeft = target;
915
+ if (isVertical) {
916
+ settings.feed.scrollTop = target;
917
+ } else {
918
+ settings.feed.scrollLeft = target;
919
+ }
697
920
  onComplete?.();
698
921
  }
699
922
  };
@@ -712,16 +935,17 @@ var createSlider = (settings) => {
712
935
  state.updateThumbTimeout = setTimeout(() => {
713
936
  state.isScrolling = false;
714
937
  }, ANIMATION.THUMB_UPDATE_DELAY);
715
- smoothScrollTo(settings.slides[index].offsetLeft);
938
+ const targetPosition = isVertical ? settings.slides[index].offsetTop : settings.slides[index].offsetLeft;
939
+ smoothScrollTo(targetPosition);
716
940
  };
717
- const handleNavButtonClick = (direction) => {
941
+ const handleNavButtonClick = (navDirection) => {
718
942
  const realSlides = loopState.initialized ? loopState.realSlides : getVisibleSlides();
719
943
  const slidesToScroll = isDesktop() ? settings.desktopSlidesPerScroll ?? 1 : settings.mobileSlidesPerScroll ?? 1;
720
944
  const totalRealSlides = realSlides.length;
721
945
  updateCurrentSlideIndex();
722
946
  let targetSlide;
723
947
  let needsReposition = false;
724
- if (direction === "prev") {
948
+ if (navDirection === "prev") {
725
949
  if (settings.loop && loopState.initialized && state.currentSlideIndex === 0) {
726
950
  const prependedClones = loopState.clonedSlides.filter(
727
951
  (clone) => clone.getAttribute("data-lazer-clone") === "prepend"
@@ -748,17 +972,19 @@ var createSlider = (settings) => {
748
972
  }
749
973
  }
750
974
  if (!targetSlide) return;
975
+ const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
751
976
  settings.onScrollStart?.({
752
- currentScroll: settings.feed.scrollLeft,
977
+ currentScroll,
753
978
  target: targetSlide,
754
- direction
979
+ direction: navDirection
755
980
  });
981
+ const targetPosition = isVertical ? targetSlide.offsetTop : targetSlide.offsetLeft;
756
982
  if (needsReposition) {
757
- smoothScrollTo(targetSlide.offsetLeft, easing, () => {
758
- handleLoopReposition(direction);
983
+ smoothScrollTo(targetPosition, easing, () => {
984
+ handleLoopReposition(navDirection);
759
985
  });
760
986
  } else {
761
- smoothScrollTo(targetSlide.offsetLeft);
987
+ smoothScrollTo(targetPosition);
762
988
  }
763
989
  };
764
990
  const updateScrollPosition = () => {
@@ -773,8 +999,9 @@ var createSlider = (settings) => {
773
999
  }
774
1000
  state.scrollEndTimeout = setTimeout(() => {
775
1001
  state.isScrolling = false;
1002
+ const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
776
1003
  settings.onScrollEnd?.({
777
- currentScroll: settings.feed.scrollLeft,
1004
+ currentScroll,
778
1005
  currentSlideIndex: state.currentSlideIndex
779
1006
  });
780
1007
  }, ANIMATION.SCROLL_END_DELAY);
@@ -789,20 +1016,8 @@ var createSlider = (settings) => {
789
1016
  }
790
1017
  };
791
1018
  const handleWindowResize = () => {
792
- if (state.resizeTimeout) {
793
- clearTimeout(state.resizeTimeout);
794
- }
795
- state.resizeTimeout = setTimeout(() => {
796
- state.cachedFeedRect = null;
797
- updateCurrentSlideIndex();
798
- const currentIndex = state.currentSlideIndex;
799
- refresh();
800
- const slides = loopState.initialized ? loopState.realSlides : getVisibleSlides();
801
- const targetSlide = slides[currentIndex];
802
- if (targetSlide) {
803
- settings.feed.scrollLeft = targetSlide.offsetLeft;
804
- }
805
- }, 150);
1019
+ state.cachedFeedRect = null;
1020
+ refresh();
806
1021
  };
807
1022
  const attachEventListeners = () => {
808
1023
  const { signal } = state.abortController;
@@ -835,9 +1050,10 @@ var createSlider = (settings) => {
835
1050
  settings.feed,
836
1051
  () => handleNavButtonClick("prev"),
837
1052
  () => handleNavButtonClick("next"),
838
- signal
1053
+ signal,
1054
+ direction
839
1055
  );
840
- if (settings.enableDragToScroll) {
1056
+ if (settings.enableDragToScroll !== false) {
841
1057
  dragState = setupDragToScroll({
842
1058
  feed: settings.feed,
843
1059
  slides: settings.slides,
@@ -847,7 +1063,7 @@ var createSlider = (settings) => {
847
1063
  updateCurrentSlideIndex();
848
1064
  updateActiveThumb(settings.thumbs, state.currentSlideIndex);
849
1065
  },
850
- dragFree: settings.dragFree
1066
+ direction
851
1067
  });
852
1068
  }
853
1069
  if (settings.autoplay && settings.pauseOnHover !== false) {
@@ -902,7 +1118,8 @@ var createSlider = (settings) => {
902
1118
  if (!targetSlide) return;
903
1119
  state.currentSlideIndex = safeIndex;
904
1120
  updateActiveThumb(settings.thumbs, safeIndex);
905
- smoothScrollTo(targetSlide.offsetLeft);
1121
+ const targetPosition = isVertical ? targetSlide.offsetTop : targetSlide.offsetLeft;
1122
+ smoothScrollTo(targetPosition);
906
1123
  };
907
1124
  const refresh = () => {
908
1125
  state.cachedFeedRect = null;
@@ -924,9 +1141,6 @@ var createSlider = (settings) => {
924
1141
  if (state.scrollEndTimeout) {
925
1142
  clearTimeout(state.scrollEndTimeout);
926
1143
  }
927
- if (state.resizeTimeout) {
928
- clearTimeout(state.resizeTimeout);
929
- }
930
1144
  if (dragState) {
931
1145
  cleanupDrag(dragState);
932
1146
  }
@@ -983,6 +1197,9 @@ var createSlider = (settings) => {
983
1197
  easeOutExpo,
984
1198
  easeOutQuad,
985
1199
  generateBullets,
986
- linear
1200
+ generateThumbs,
1201
+ injectStyles,
1202
+ linear,
1203
+ removeStyles
987
1204
  });
988
1205
  //# sourceMappingURL=index.cjs.map