lazer-slider 1.1.2 → 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/README.md +325 -73
- package/dist/index.cjs +326 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -6
- package/dist/index.d.ts +61 -6
- package/dist/index.js +322 -108
- package/dist/index.js.map +1 -1
- package/dist/lazer-slider.css +263 -0
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -85,16 +85,19 @@ var updateActiveThumb = (thumbs, currentIndex, activeClass = "active") => {
|
|
|
85
85
|
thumb.setAttribute("aria-selected", isActive.toString());
|
|
86
86
|
});
|
|
87
87
|
};
|
|
88
|
-
var setupKeyboardNavigation = (feed, onPrev, onNext, abortSignal) => {
|
|
88
|
+
var setupKeyboardNavigation = (feed, onPrev, onNext, abortSignal, direction = "horizontal") => {
|
|
89
|
+
const isVertical = direction === "vertical";
|
|
90
|
+
const prevKey = isVertical ? "ArrowUp" : "ArrowLeft";
|
|
91
|
+
const nextKey = isVertical ? "ArrowDown" : "ArrowRight";
|
|
89
92
|
feed.addEventListener(
|
|
90
93
|
"keydown",
|
|
91
94
|
(event) => {
|
|
92
95
|
switch (event.key) {
|
|
93
|
-
case
|
|
96
|
+
case prevKey:
|
|
94
97
|
event.preventDefault();
|
|
95
98
|
onPrev();
|
|
96
99
|
break;
|
|
97
|
-
case
|
|
100
|
+
case nextKey:
|
|
98
101
|
event.preventDefault();
|
|
99
102
|
onNext();
|
|
100
103
|
break;
|
|
@@ -111,21 +114,25 @@ var setupKeyboardNavigation = (feed, onPrev, onNext, abortSignal) => {
|
|
|
111
114
|
var createDragState = () => ({
|
|
112
115
|
isDragging: false,
|
|
113
116
|
startX: 0,
|
|
117
|
+
startY: 0,
|
|
114
118
|
startScrollLeft: 0,
|
|
119
|
+
startScrollTop: 0,
|
|
115
120
|
velocity: 0,
|
|
116
121
|
lastX: 0,
|
|
122
|
+
lastY: 0,
|
|
117
123
|
lastTime: 0,
|
|
118
124
|
momentumId: null
|
|
119
125
|
});
|
|
120
|
-
var findNearestSlide = (feed, slides) => {
|
|
126
|
+
var findNearestSlide = (feed, slides, direction = "horizontal") => {
|
|
121
127
|
const feedRect = feed.getBoundingClientRect();
|
|
122
|
-
const
|
|
128
|
+
const isVertical = direction === "vertical";
|
|
129
|
+
const feedCenter = isVertical ? feedRect.top + feedRect.height / 2 : feedRect.left + feedRect.width / 2;
|
|
123
130
|
let nearestSlide = null;
|
|
124
131
|
let minDistance = Infinity;
|
|
125
132
|
for (const slide of slides) {
|
|
126
133
|
if (slide.offsetParent === null) continue;
|
|
127
134
|
const slideRect = slide.getBoundingClientRect();
|
|
128
|
-
const slideCenter = slideRect.left + slideRect.width / 2;
|
|
135
|
+
const slideCenter = isVertical ? slideRect.top + slideRect.height / 2 : slideRect.left + slideRect.width / 2;
|
|
129
136
|
const distance = Math.abs(feedCenter - slideCenter);
|
|
130
137
|
if (distance < minDistance) {
|
|
131
138
|
minDistance = distance;
|
|
@@ -134,20 +141,26 @@ var findNearestSlide = (feed, slides) => {
|
|
|
134
141
|
}
|
|
135
142
|
return nearestSlide;
|
|
136
143
|
};
|
|
137
|
-
var applyMomentum = (state, feed, slides, smoothScrollTo, onDragEnd) => {
|
|
144
|
+
var applyMomentum = (state, feed, slides, smoothScrollTo, onDragEnd, direction = "horizontal") => {
|
|
138
145
|
const friction = 0.95;
|
|
139
146
|
const minVelocity = 0.5;
|
|
147
|
+
const isVertical = direction === "vertical";
|
|
140
148
|
const animate = () => {
|
|
141
149
|
if (Math.abs(state.velocity) < minVelocity) {
|
|
142
150
|
state.momentumId = null;
|
|
143
|
-
const nearestSlide = findNearestSlide(feed, slides);
|
|
151
|
+
const nearestSlide = findNearestSlide(feed, slides, direction);
|
|
144
152
|
if (nearestSlide) {
|
|
145
|
-
|
|
153
|
+
const targetPosition = isVertical ? nearestSlide.offsetTop : nearestSlide.offsetLeft;
|
|
154
|
+
smoothScrollTo(targetPosition, easeOutCubic);
|
|
146
155
|
}
|
|
147
156
|
onDragEnd?.();
|
|
148
157
|
return;
|
|
149
158
|
}
|
|
150
|
-
|
|
159
|
+
if (isVertical) {
|
|
160
|
+
feed.scrollTop += state.velocity;
|
|
161
|
+
} else {
|
|
162
|
+
feed.scrollLeft += state.velocity;
|
|
163
|
+
}
|
|
151
164
|
state.velocity *= friction;
|
|
152
165
|
state.momentumId = requestAnimationFrame(animate);
|
|
153
166
|
};
|
|
@@ -159,9 +172,16 @@ var getEventX = (event) => {
|
|
|
159
172
|
}
|
|
160
173
|
return event.clientX;
|
|
161
174
|
};
|
|
175
|
+
var getEventY = (event) => {
|
|
176
|
+
if ("touches" in event) {
|
|
177
|
+
return event.touches[0]?.clientY ?? 0;
|
|
178
|
+
}
|
|
179
|
+
return event.clientY;
|
|
180
|
+
};
|
|
162
181
|
var setupDragToScroll = (config) => {
|
|
163
|
-
const { feed, slides, abortSignal, smoothScrollTo, onDragEnd,
|
|
182
|
+
const { feed, slides, abortSignal, smoothScrollTo, onDragEnd, direction = "horizontal" } = config;
|
|
164
183
|
const state = createDragState();
|
|
184
|
+
const isVertical = direction === "vertical";
|
|
165
185
|
const handleDragStart = (event) => {
|
|
166
186
|
if (state.momentumId !== null) {
|
|
167
187
|
cancelAnimationFrame(state.momentumId);
|
|
@@ -169,11 +189,13 @@ var setupDragToScroll = (config) => {
|
|
|
169
189
|
}
|
|
170
190
|
state.isDragging = true;
|
|
171
191
|
state.startX = getEventX(event);
|
|
192
|
+
state.startY = getEventY(event);
|
|
172
193
|
state.startScrollLeft = feed.scrollLeft;
|
|
194
|
+
state.startScrollTop = feed.scrollTop;
|
|
173
195
|
state.velocity = 0;
|
|
174
196
|
state.lastX = state.startX;
|
|
197
|
+
state.lastY = state.startY;
|
|
175
198
|
state.lastTime = performance.now();
|
|
176
|
-
feed.style.cursor = "grabbing";
|
|
177
199
|
feed.style.userSelect = "none";
|
|
178
200
|
if (event.type === "mousedown") {
|
|
179
201
|
event.preventDefault();
|
|
@@ -182,14 +204,24 @@ var setupDragToScroll = (config) => {
|
|
|
182
204
|
const handleDragMove = (event) => {
|
|
183
205
|
if (!state.isDragging) return;
|
|
184
206
|
const currentX = getEventX(event);
|
|
207
|
+
const currentY = getEventY(event);
|
|
185
208
|
const currentTime = performance.now();
|
|
186
|
-
const deltaX = state.startX - currentX;
|
|
187
209
|
const deltaTime = currentTime - state.lastTime;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
210
|
+
if (isVertical) {
|
|
211
|
+
const deltaY = state.startY - currentY;
|
|
212
|
+
feed.scrollTop = state.startScrollTop + deltaY;
|
|
213
|
+
if (deltaTime > 0) {
|
|
214
|
+
state.velocity = (state.lastY - currentY) / deltaTime * 16;
|
|
215
|
+
}
|
|
216
|
+
state.lastY = currentY;
|
|
217
|
+
} else {
|
|
218
|
+
const deltaX = state.startX - currentX;
|
|
219
|
+
feed.scrollLeft = state.startScrollLeft + deltaX;
|
|
220
|
+
if (deltaTime > 0) {
|
|
221
|
+
state.velocity = (state.lastX - currentX) / deltaTime * 16;
|
|
222
|
+
}
|
|
223
|
+
state.lastX = currentX;
|
|
191
224
|
}
|
|
192
|
-
state.lastX = currentX;
|
|
193
225
|
state.lastTime = currentTime;
|
|
194
226
|
if (event.type === "touchmove") {
|
|
195
227
|
event.preventDefault();
|
|
@@ -198,41 +230,18 @@ var setupDragToScroll = (config) => {
|
|
|
198
230
|
const handleDragEnd = () => {
|
|
199
231
|
if (!state.isDragging) return;
|
|
200
232
|
state.isDragging = false;
|
|
201
|
-
feed.style.cursor = "grab";
|
|
202
233
|
feed.style.userSelect = "";
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd);
|
|
206
|
-
} else {
|
|
207
|
-
const nearestSlide = findNearestSlide(feed, slides);
|
|
208
|
-
if (nearestSlide) {
|
|
209
|
-
smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic);
|
|
210
|
-
}
|
|
211
|
-
onDragEnd?.();
|
|
212
|
-
}
|
|
234
|
+
if (Math.abs(state.velocity) > 1) {
|
|
235
|
+
applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd, direction);
|
|
213
236
|
} else {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const visibleSlides = slides.filter((slide) => slide.offsetParent !== null);
|
|
219
|
-
const visibleIndex = visibleSlides.indexOf(nearestSlide);
|
|
220
|
-
let targetSlide = null;
|
|
221
|
-
if (swipeDistance > 0 && visibleIndex < visibleSlides.length - 1) {
|
|
222
|
-
targetSlide = visibleSlides[visibleIndex + 1] ?? nearestSlide;
|
|
223
|
-
} else if (swipeDistance < 0 && visibleIndex > 0) {
|
|
224
|
-
targetSlide = visibleSlides[visibleIndex - 1] ?? nearestSlide;
|
|
225
|
-
} else {
|
|
226
|
-
targetSlide = nearestSlide;
|
|
227
|
-
}
|
|
228
|
-
smoothScrollTo(targetSlide.offsetLeft, easeOutCubic);
|
|
229
|
-
} else if (nearestSlide) {
|
|
230
|
-
smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic);
|
|
237
|
+
const nearestSlide = findNearestSlide(feed, slides, direction);
|
|
238
|
+
if (nearestSlide) {
|
|
239
|
+
const targetPosition = isVertical ? nearestSlide.offsetTop : nearestSlide.offsetLeft;
|
|
240
|
+
smoothScrollTo(targetPosition, easeOutCubic);
|
|
231
241
|
}
|
|
232
242
|
onDragEnd?.();
|
|
233
243
|
}
|
|
234
244
|
};
|
|
235
|
-
feed.style.cursor = "grab";
|
|
236
245
|
feed.addEventListener("mousedown", handleDragStart, { signal: abortSignal });
|
|
237
246
|
document.addEventListener("mousemove", handleDragMove, { signal: abortSignal });
|
|
238
247
|
document.addEventListener("mouseup", handleDragEnd, { signal: abortSignal });
|
|
@@ -301,6 +310,67 @@ var generateBullets = ({
|
|
|
301
310
|
return bullets;
|
|
302
311
|
};
|
|
303
312
|
|
|
313
|
+
// src/core/thumbs.ts
|
|
314
|
+
var generateThumbs = ({
|
|
315
|
+
thumbsContainer,
|
|
316
|
+
slides,
|
|
317
|
+
thumbClass,
|
|
318
|
+
thumbActiveClass,
|
|
319
|
+
feedId,
|
|
320
|
+
thumbImageSelector = "img",
|
|
321
|
+
thumbSize
|
|
322
|
+
}) => {
|
|
323
|
+
if (!thumbsContainer || !(thumbsContainer instanceof HTMLElement)) {
|
|
324
|
+
throw new Error("Invalid thumbsContainer: must be a valid HTMLElement");
|
|
325
|
+
}
|
|
326
|
+
if (!Array.isArray(slides) || slides.length === 0) {
|
|
327
|
+
throw new Error("Invalid slides: must be a non-empty array");
|
|
328
|
+
}
|
|
329
|
+
if (!feedId || typeof feedId !== "string") {
|
|
330
|
+
throw new Error("Invalid feedId: must be a non-empty string");
|
|
331
|
+
}
|
|
332
|
+
if (!thumbClass || typeof thumbClass !== "string") {
|
|
333
|
+
throw new Error("Invalid thumbClass: must be a non-empty string");
|
|
334
|
+
}
|
|
335
|
+
thumbsContainer.innerHTML = "";
|
|
336
|
+
thumbsContainer.setAttribute("role", "tablist");
|
|
337
|
+
thumbsContainer.setAttribute("aria-label", "Slide thumbnails");
|
|
338
|
+
const visibleSlides = slides.map((slide, originalIndex) => ({ slide, originalIndex })).filter(({ slide }) => slide.offsetParent !== null);
|
|
339
|
+
if (visibleSlides.length === 0) {
|
|
340
|
+
console.warn("No visible slides found");
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
const thumbs = visibleSlides.map(({ slide, originalIndex }, visibleIndex) => {
|
|
344
|
+
const thumb = document.createElement("button");
|
|
345
|
+
thumb.type = "button";
|
|
346
|
+
thumb.classList.add(thumbClass);
|
|
347
|
+
if (visibleIndex === 0) {
|
|
348
|
+
thumb.classList.add(thumbActiveClass);
|
|
349
|
+
}
|
|
350
|
+
const sourceImage = slide.querySelector(thumbImageSelector);
|
|
351
|
+
if (sourceImage?.src) {
|
|
352
|
+
const thumbImg = document.createElement("img");
|
|
353
|
+
thumbImg.src = sourceImage.src;
|
|
354
|
+
thumbImg.alt = sourceImage.alt || `Slide ${visibleIndex + 1} thumbnail`;
|
|
355
|
+
thumbImg.draggable = false;
|
|
356
|
+
if (thumbSize) {
|
|
357
|
+
thumbImg.style.width = `${thumbSize.width}px`;
|
|
358
|
+
thumbImg.style.height = `${thumbSize.height}px`;
|
|
359
|
+
thumbImg.style.objectFit = "cover";
|
|
360
|
+
}
|
|
361
|
+
thumb.appendChild(thumbImg);
|
|
362
|
+
}
|
|
363
|
+
thumb.setAttribute("role", "tab");
|
|
364
|
+
thumb.setAttribute("aria-selected", visibleIndex === 0 ? "true" : "false");
|
|
365
|
+
thumb.setAttribute("aria-controls", feedId);
|
|
366
|
+
thumb.setAttribute("aria-label", `Go to slide ${visibleIndex + 1}`);
|
|
367
|
+
thumb.setAttribute("data-slide-index", String(visibleIndex));
|
|
368
|
+
thumbsContainer.appendChild(thumb);
|
|
369
|
+
return thumb;
|
|
370
|
+
});
|
|
371
|
+
return thumbs;
|
|
372
|
+
};
|
|
373
|
+
|
|
304
374
|
// src/core/marquee.ts
|
|
305
375
|
var createMarqueeState = () => ({
|
|
306
376
|
initialized: false,
|
|
@@ -415,6 +485,72 @@ var attachMarqueeEventListeners = (settings, state, signal) => {
|
|
|
415
485
|
);
|
|
416
486
|
};
|
|
417
487
|
|
|
488
|
+
// src/core/styles.ts
|
|
489
|
+
var CRITICAL_STYLES = `
|
|
490
|
+
/* Lazer Slider - Critical Styles (Auto-injected) */
|
|
491
|
+
.lazer-feed {
|
|
492
|
+
position: relative;
|
|
493
|
+
display: flex;
|
|
494
|
+
overflow-x: auto;
|
|
495
|
+
overflow-y: hidden;
|
|
496
|
+
-webkit-overflow-scrolling: touch;
|
|
497
|
+
scrollbar-width: none;
|
|
498
|
+
scroll-snap-type: x mandatory;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.lazer-feed::-webkit-scrollbar {
|
|
502
|
+
display: none;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.lazer-feed.lazer-vertical {
|
|
506
|
+
flex-direction: column;
|
|
507
|
+
overflow-x: hidden;
|
|
508
|
+
overflow-y: auto;
|
|
509
|
+
scroll-snap-type: y mandatory;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.lazer-slide {
|
|
513
|
+
scroll-snap-align: start;
|
|
514
|
+
flex-shrink: 0;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.lazer-feed.is-dragging {
|
|
518
|
+
cursor: grabbing;
|
|
519
|
+
user-select: none;
|
|
520
|
+
scroll-snap-type: none;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.lazer-feed:not(.is-dragging) {
|
|
524
|
+
cursor: grab;
|
|
525
|
+
}
|
|
526
|
+
`;
|
|
527
|
+
var stylesInjected = false;
|
|
528
|
+
var STYLE_ID = "lazer-slider-critical-styles";
|
|
529
|
+
var injectStyles = () => {
|
|
530
|
+
if (stylesInjected || typeof document === "undefined") return;
|
|
531
|
+
const existingStyle = document.getElementById(STYLE_ID);
|
|
532
|
+
if (existingStyle) {
|
|
533
|
+
stylesInjected = true;
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
const style = document.createElement("style");
|
|
537
|
+
style.id = STYLE_ID;
|
|
538
|
+
style.textContent = CRITICAL_STYLES;
|
|
539
|
+
document.head.appendChild(style);
|
|
540
|
+
stylesInjected = true;
|
|
541
|
+
};
|
|
542
|
+
var injectStylesOnce = () => {
|
|
543
|
+
injectStyles();
|
|
544
|
+
};
|
|
545
|
+
var removeStyles = () => {
|
|
546
|
+
if (typeof document === "undefined") return;
|
|
547
|
+
const style = document.getElementById(STYLE_ID);
|
|
548
|
+
if (style) {
|
|
549
|
+
style.remove();
|
|
550
|
+
stylesInjected = false;
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
|
|
418
554
|
// src/core/slider.ts
|
|
419
555
|
var ANIMATION = {
|
|
420
556
|
MIN_DURATION: 400,
|
|
@@ -425,6 +561,7 @@ var ANIMATION = {
|
|
|
425
561
|
};
|
|
426
562
|
var DESKTOP_BREAKPOINT = "(min-width: 64rem)";
|
|
427
563
|
var createSlider = (settings) => {
|
|
564
|
+
injectStylesOnce();
|
|
428
565
|
if (!settings.feed) {
|
|
429
566
|
throw new Error("lazer-slider: feed element is required");
|
|
430
567
|
}
|
|
@@ -434,16 +571,40 @@ var createSlider = (settings) => {
|
|
|
434
571
|
if (!settings.feed.id) {
|
|
435
572
|
settings.feed.id = `lazer-slider-feed-${Math.random().toString(36).substr(2, 9)}`;
|
|
436
573
|
}
|
|
574
|
+
settings.feed.classList.add("lazer-feed");
|
|
575
|
+
settings.slides.forEach((slide) => {
|
|
576
|
+
slide.classList.add("lazer-slide");
|
|
577
|
+
});
|
|
437
578
|
if (settings.bulletsContainer && !settings.thumbs) {
|
|
438
579
|
const bullets = generateBullets({
|
|
439
580
|
bulletsContainer: settings.bulletsContainer,
|
|
440
581
|
slides: settings.slides,
|
|
441
|
-
bulletClass: settings.bulletsClass ?? "
|
|
582
|
+
bulletClass: settings.bulletsClass ?? "lazer-bullet",
|
|
442
583
|
bulletActiveClass: settings.bulletsActiveClass ?? "active",
|
|
443
584
|
feedId: settings.feed.id
|
|
444
585
|
});
|
|
445
586
|
settings.thumbs = bullets;
|
|
446
587
|
}
|
|
588
|
+
if (settings.thumbsContainer && !settings.thumbs) {
|
|
589
|
+
const thumbs = generateThumbs({
|
|
590
|
+
thumbsContainer: settings.thumbsContainer,
|
|
591
|
+
slides: settings.slides,
|
|
592
|
+
thumbClass: settings.thumbsClass ?? "lazer-thumb",
|
|
593
|
+
thumbActiveClass: settings.thumbsActiveClass ?? "active",
|
|
594
|
+
feedId: settings.feed.id,
|
|
595
|
+
thumbImageSelector: settings.thumbImageSelector ?? "img",
|
|
596
|
+
thumbSize: settings.thumbSize
|
|
597
|
+
});
|
|
598
|
+
settings.thumbs = thumbs;
|
|
599
|
+
}
|
|
600
|
+
const direction = settings.direction ?? "horizontal";
|
|
601
|
+
const isVertical = direction === "vertical";
|
|
602
|
+
if (isVertical) {
|
|
603
|
+
settings.feed.classList.add("lazer-vertical");
|
|
604
|
+
}
|
|
605
|
+
if (settings.marquee) {
|
|
606
|
+
settings.feed.classList.add("lazer-marquee");
|
|
607
|
+
}
|
|
447
608
|
const state = {
|
|
448
609
|
currentSlideIndex: 0,
|
|
449
610
|
isScrolling: false,
|
|
@@ -452,7 +613,6 @@ var createSlider = (settings) => {
|
|
|
452
613
|
lastWidth: 0,
|
|
453
614
|
updateThumbTimeout: null,
|
|
454
615
|
scrollEndTimeout: null,
|
|
455
|
-
resizeTimeout: null,
|
|
456
616
|
abortController: new AbortController(),
|
|
457
617
|
autoplayIntervalId: null,
|
|
458
618
|
autoplayPaused: false,
|
|
@@ -471,10 +631,10 @@ var createSlider = (settings) => {
|
|
|
471
631
|
const marqueeState = createMarqueeState();
|
|
472
632
|
const easing = settings.easing ?? easeOutExpo;
|
|
473
633
|
const getFeedRect = () => {
|
|
474
|
-
const
|
|
475
|
-
if (!state.cachedFeedRect || state.lastWidth !==
|
|
634
|
+
const currentSize = isVertical ? settings.feed.clientHeight : settings.feed.clientWidth;
|
|
635
|
+
if (!state.cachedFeedRect || state.lastWidth !== currentSize) {
|
|
476
636
|
state.cachedFeedRect = settings.feed.getBoundingClientRect();
|
|
477
|
-
state.lastWidth =
|
|
637
|
+
state.lastWidth = currentSize;
|
|
478
638
|
}
|
|
479
639
|
return state.cachedFeedRect;
|
|
480
640
|
};
|
|
@@ -517,26 +677,38 @@ var createSlider = (settings) => {
|
|
|
517
677
|
requestAnimationFrame(() => {
|
|
518
678
|
const firstRealSlide = realSlides[0];
|
|
519
679
|
if (firstRealSlide) {
|
|
520
|
-
|
|
680
|
+
if (isVertical) {
|
|
681
|
+
settings.feed.scrollTop = firstRealSlide.offsetTop;
|
|
682
|
+
} else {
|
|
683
|
+
settings.feed.scrollLeft = firstRealSlide.offsetLeft;
|
|
684
|
+
}
|
|
521
685
|
}
|
|
522
686
|
});
|
|
523
687
|
loopState.initialized = true;
|
|
524
688
|
};
|
|
525
|
-
const handleLoopReposition = (
|
|
689
|
+
const handleLoopReposition = (navDirection) => {
|
|
526
690
|
if (!settings.loop || !loopState.initialized) return;
|
|
527
691
|
state.isLoopRepositioning = true;
|
|
528
692
|
const realSlides = loopState.realSlides;
|
|
529
693
|
const totalRealSlides = realSlides.length;
|
|
530
|
-
if (
|
|
694
|
+
if (navDirection === "next") {
|
|
531
695
|
const firstRealSlide = realSlides[0];
|
|
532
696
|
if (firstRealSlide) {
|
|
533
|
-
|
|
697
|
+
if (isVertical) {
|
|
698
|
+
settings.feed.scrollTop = firstRealSlide.offsetTop;
|
|
699
|
+
} else {
|
|
700
|
+
settings.feed.scrollLeft = firstRealSlide.offsetLeft;
|
|
701
|
+
}
|
|
534
702
|
}
|
|
535
703
|
state.currentSlideIndex = 0;
|
|
536
704
|
} else {
|
|
537
705
|
const lastRealSlide = realSlides[totalRealSlides - 1];
|
|
538
706
|
if (lastRealSlide) {
|
|
539
|
-
|
|
707
|
+
if (isVertical) {
|
|
708
|
+
settings.feed.scrollTop = lastRealSlide.offsetTop;
|
|
709
|
+
} else {
|
|
710
|
+
settings.feed.scrollLeft = lastRealSlide.offsetLeft;
|
|
711
|
+
}
|
|
540
712
|
}
|
|
541
713
|
state.currentSlideIndex = totalRealSlides - 1;
|
|
542
714
|
}
|
|
@@ -566,39 +738,74 @@ var createSlider = (settings) => {
|
|
|
566
738
|
settings.slides.forEach((slide) => {
|
|
567
739
|
slide.style.flex = "";
|
|
568
740
|
slide.style.minWidth = "";
|
|
741
|
+
slide.style.minHeight = "";
|
|
569
742
|
});
|
|
570
743
|
return;
|
|
571
744
|
}
|
|
572
|
-
const
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
745
|
+
const totalGapSize = gap * (perView - 1);
|
|
746
|
+
const slideSize = `calc((100% - ${totalGapSize}px) / ${perView})`;
|
|
747
|
+
if (isVertical) {
|
|
748
|
+
settings.slides.forEach((slide) => {
|
|
749
|
+
slide.style.flex = `0 0 ${slideSize}`;
|
|
750
|
+
slide.style.minHeight = slideSize;
|
|
751
|
+
slide.style.minWidth = "";
|
|
752
|
+
});
|
|
753
|
+
} else {
|
|
754
|
+
settings.slides.forEach((slide) => {
|
|
755
|
+
slide.style.flex = `0 0 ${slideSize}`;
|
|
756
|
+
slide.style.minWidth = slideSize;
|
|
757
|
+
slide.style.minHeight = "";
|
|
758
|
+
});
|
|
759
|
+
}
|
|
578
760
|
};
|
|
579
761
|
const updateScrollbar = () => {
|
|
580
762
|
if (!settings.scrollbarThumb) return;
|
|
581
763
|
const feedRect = getFeedRect();
|
|
582
|
-
|
|
583
|
-
|
|
764
|
+
if (isVertical) {
|
|
765
|
+
const thumbHeight = feedRect.height / settings.feed.scrollHeight * 100;
|
|
766
|
+
settings.scrollbarThumb.style.height = `${thumbHeight}%`;
|
|
767
|
+
settings.scrollbarThumb.style.width = "";
|
|
768
|
+
} else {
|
|
769
|
+
const thumbWidth = feedRect.width / settings.feed.scrollWidth * 100;
|
|
770
|
+
settings.scrollbarThumb.style.width = `${thumbWidth}%`;
|
|
771
|
+
settings.scrollbarThumb.style.height = "";
|
|
772
|
+
}
|
|
584
773
|
};
|
|
585
774
|
const updateScrollbarPosition = () => {
|
|
586
775
|
if (!settings.scrollbarThumb || !settings.scrollbarTrack) return;
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
776
|
+
if (isVertical) {
|
|
777
|
+
const trackHeight = settings.scrollbarTrack.getBoundingClientRect().height;
|
|
778
|
+
const thumbHeight = settings.scrollbarThumb.getBoundingClientRect().height;
|
|
779
|
+
const totalTransform = trackHeight - thumbHeight;
|
|
780
|
+
const maxScroll = settings.feed.scrollHeight - settings.feed.clientHeight;
|
|
781
|
+
const scrollProgress = maxScroll > 0 ? settings.feed.scrollTop / maxScroll : 0;
|
|
782
|
+
settings.scrollbarThumb.style.transform = `translateY(${totalTransform * scrollProgress}px)`;
|
|
783
|
+
} else {
|
|
784
|
+
const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width;
|
|
785
|
+
const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width;
|
|
786
|
+
const totalTransform = trackWidth - thumbWidth;
|
|
787
|
+
const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth;
|
|
788
|
+
const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0;
|
|
789
|
+
settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`;
|
|
790
|
+
}
|
|
593
791
|
};
|
|
594
792
|
const updateControlsVisibility = () => {
|
|
595
793
|
if (state.isLoopRepositioning) {
|
|
596
794
|
return;
|
|
597
795
|
}
|
|
598
796
|
const feedRect = getFeedRect();
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
797
|
+
let isAtStart;
|
|
798
|
+
let isAtEnd;
|
|
799
|
+
let shouldHideScrollbar;
|
|
800
|
+
if (isVertical) {
|
|
801
|
+
isAtStart = settings.feed.scrollTop <= 1;
|
|
802
|
+
isAtEnd = settings.feed.scrollTop + feedRect.height >= settings.feed.scrollHeight - 1;
|
|
803
|
+
shouldHideScrollbar = settings.feed.scrollHeight <= feedRect.height;
|
|
804
|
+
} else {
|
|
805
|
+
isAtStart = settings.feed.scrollLeft <= 1;
|
|
806
|
+
isAtEnd = settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1;
|
|
807
|
+
shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width;
|
|
808
|
+
}
|
|
602
809
|
if (settings.scrollbarTrack) {
|
|
603
810
|
settings.scrollbarTrack.style.display = shouldHideScrollbar ? "none" : "block";
|
|
604
811
|
}
|
|
@@ -632,21 +839,25 @@ var createSlider = (settings) => {
|
|
|
632
839
|
const viewportVisibleSlides = slidesToCheck.filter((slide) => {
|
|
633
840
|
const slideRect = slide.getBoundingClientRect();
|
|
634
841
|
const tolerance = 20;
|
|
842
|
+
if (isVertical) {
|
|
843
|
+
return slideRect.bottom > feedRect.top + tolerance && slideRect.top < feedRect.bottom - tolerance;
|
|
844
|
+
}
|
|
635
845
|
return slideRect.right > feedRect.left + tolerance && slideRect.left < feedRect.right - tolerance;
|
|
636
846
|
});
|
|
637
847
|
if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {
|
|
638
848
|
const newIndex = slidesToCheck.indexOf(viewportVisibleSlides[0]);
|
|
639
849
|
if (newIndex !== -1) {
|
|
640
850
|
state.currentSlideIndex = newIndex;
|
|
851
|
+
const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
641
852
|
settings.onScroll?.({
|
|
642
|
-
currentScroll
|
|
853
|
+
currentScroll,
|
|
643
854
|
currentSlideIndex: state.currentSlideIndex
|
|
644
855
|
});
|
|
645
856
|
}
|
|
646
857
|
}
|
|
647
858
|
};
|
|
648
859
|
const smoothScrollTo = (target, customEasing = easing, onComplete) => {
|
|
649
|
-
const start = settings.feed.scrollLeft;
|
|
860
|
+
const start = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
650
861
|
const distance = Math.abs(target - start);
|
|
651
862
|
const duration = Math.min(
|
|
652
863
|
ANIMATION.MAX_DURATION,
|
|
@@ -657,11 +868,20 @@ var createSlider = (settings) => {
|
|
|
657
868
|
const elapsed = (currentTime - startTime) / duration;
|
|
658
869
|
const progress = Math.min(elapsed, 1);
|
|
659
870
|
const ease = customEasing(progress);
|
|
660
|
-
|
|
871
|
+
const scrollPosition = start + (target - start) * ease;
|
|
872
|
+
if (isVertical) {
|
|
873
|
+
settings.feed.scrollTop = scrollPosition;
|
|
874
|
+
} else {
|
|
875
|
+
settings.feed.scrollLeft = scrollPosition;
|
|
876
|
+
}
|
|
661
877
|
if (progress < 1) {
|
|
662
878
|
requestAnimationFrame(animateScroll);
|
|
663
879
|
} else {
|
|
664
|
-
|
|
880
|
+
if (isVertical) {
|
|
881
|
+
settings.feed.scrollTop = target;
|
|
882
|
+
} else {
|
|
883
|
+
settings.feed.scrollLeft = target;
|
|
884
|
+
}
|
|
665
885
|
onComplete?.();
|
|
666
886
|
}
|
|
667
887
|
};
|
|
@@ -680,16 +900,17 @@ var createSlider = (settings) => {
|
|
|
680
900
|
state.updateThumbTimeout = setTimeout(() => {
|
|
681
901
|
state.isScrolling = false;
|
|
682
902
|
}, ANIMATION.THUMB_UPDATE_DELAY);
|
|
683
|
-
|
|
903
|
+
const targetPosition = isVertical ? settings.slides[index].offsetTop : settings.slides[index].offsetLeft;
|
|
904
|
+
smoothScrollTo(targetPosition);
|
|
684
905
|
};
|
|
685
|
-
const handleNavButtonClick = (
|
|
906
|
+
const handleNavButtonClick = (navDirection) => {
|
|
686
907
|
const realSlides = loopState.initialized ? loopState.realSlides : getVisibleSlides();
|
|
687
908
|
const slidesToScroll = isDesktop() ? settings.desktopSlidesPerScroll ?? 1 : settings.mobileSlidesPerScroll ?? 1;
|
|
688
909
|
const totalRealSlides = realSlides.length;
|
|
689
910
|
updateCurrentSlideIndex();
|
|
690
911
|
let targetSlide;
|
|
691
912
|
let needsReposition = false;
|
|
692
|
-
if (
|
|
913
|
+
if (navDirection === "prev") {
|
|
693
914
|
if (settings.loop && loopState.initialized && state.currentSlideIndex === 0) {
|
|
694
915
|
const prependedClones = loopState.clonedSlides.filter(
|
|
695
916
|
(clone) => clone.getAttribute("data-lazer-clone") === "prepend"
|
|
@@ -716,17 +937,19 @@ var createSlider = (settings) => {
|
|
|
716
937
|
}
|
|
717
938
|
}
|
|
718
939
|
if (!targetSlide) return;
|
|
940
|
+
const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
719
941
|
settings.onScrollStart?.({
|
|
720
|
-
currentScroll
|
|
942
|
+
currentScroll,
|
|
721
943
|
target: targetSlide,
|
|
722
|
-
direction
|
|
944
|
+
direction: navDirection
|
|
723
945
|
});
|
|
946
|
+
const targetPosition = isVertical ? targetSlide.offsetTop : targetSlide.offsetLeft;
|
|
724
947
|
if (needsReposition) {
|
|
725
|
-
smoothScrollTo(
|
|
726
|
-
handleLoopReposition(
|
|
948
|
+
smoothScrollTo(targetPosition, easing, () => {
|
|
949
|
+
handleLoopReposition(navDirection);
|
|
727
950
|
});
|
|
728
951
|
} else {
|
|
729
|
-
smoothScrollTo(
|
|
952
|
+
smoothScrollTo(targetPosition);
|
|
730
953
|
}
|
|
731
954
|
};
|
|
732
955
|
const updateScrollPosition = () => {
|
|
@@ -741,8 +964,9 @@ var createSlider = (settings) => {
|
|
|
741
964
|
}
|
|
742
965
|
state.scrollEndTimeout = setTimeout(() => {
|
|
743
966
|
state.isScrolling = false;
|
|
967
|
+
const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
744
968
|
settings.onScrollEnd?.({
|
|
745
|
-
currentScroll
|
|
969
|
+
currentScroll,
|
|
746
970
|
currentSlideIndex: state.currentSlideIndex
|
|
747
971
|
});
|
|
748
972
|
}, ANIMATION.SCROLL_END_DELAY);
|
|
@@ -757,20 +981,8 @@ var createSlider = (settings) => {
|
|
|
757
981
|
}
|
|
758
982
|
};
|
|
759
983
|
const handleWindowResize = () => {
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
}
|
|
763
|
-
state.resizeTimeout = setTimeout(() => {
|
|
764
|
-
state.cachedFeedRect = null;
|
|
765
|
-
updateCurrentSlideIndex();
|
|
766
|
-
const currentIndex = state.currentSlideIndex;
|
|
767
|
-
refresh();
|
|
768
|
-
const slides = loopState.initialized ? loopState.realSlides : getVisibleSlides();
|
|
769
|
-
const targetSlide = slides[currentIndex];
|
|
770
|
-
if (targetSlide) {
|
|
771
|
-
settings.feed.scrollLeft = targetSlide.offsetLeft;
|
|
772
|
-
}
|
|
773
|
-
}, 150);
|
|
984
|
+
state.cachedFeedRect = null;
|
|
985
|
+
refresh();
|
|
774
986
|
};
|
|
775
987
|
const attachEventListeners = () => {
|
|
776
988
|
const { signal } = state.abortController;
|
|
@@ -803,9 +1015,10 @@ var createSlider = (settings) => {
|
|
|
803
1015
|
settings.feed,
|
|
804
1016
|
() => handleNavButtonClick("prev"),
|
|
805
1017
|
() => handleNavButtonClick("next"),
|
|
806
|
-
signal
|
|
1018
|
+
signal,
|
|
1019
|
+
direction
|
|
807
1020
|
);
|
|
808
|
-
if (settings.enableDragToScroll) {
|
|
1021
|
+
if (settings.enableDragToScroll !== false) {
|
|
809
1022
|
dragState = setupDragToScroll({
|
|
810
1023
|
feed: settings.feed,
|
|
811
1024
|
slides: settings.slides,
|
|
@@ -815,7 +1028,7 @@ var createSlider = (settings) => {
|
|
|
815
1028
|
updateCurrentSlideIndex();
|
|
816
1029
|
updateActiveThumb(settings.thumbs, state.currentSlideIndex);
|
|
817
1030
|
},
|
|
818
|
-
|
|
1031
|
+
direction
|
|
819
1032
|
});
|
|
820
1033
|
}
|
|
821
1034
|
if (settings.autoplay && settings.pauseOnHover !== false) {
|
|
@@ -870,7 +1083,8 @@ var createSlider = (settings) => {
|
|
|
870
1083
|
if (!targetSlide) return;
|
|
871
1084
|
state.currentSlideIndex = safeIndex;
|
|
872
1085
|
updateActiveThumb(settings.thumbs, safeIndex);
|
|
873
|
-
|
|
1086
|
+
const targetPosition = isVertical ? targetSlide.offsetTop : targetSlide.offsetLeft;
|
|
1087
|
+
smoothScrollTo(targetPosition);
|
|
874
1088
|
};
|
|
875
1089
|
const refresh = () => {
|
|
876
1090
|
state.cachedFeedRect = null;
|
|
@@ -892,9 +1106,6 @@ var createSlider = (settings) => {
|
|
|
892
1106
|
if (state.scrollEndTimeout) {
|
|
893
1107
|
clearTimeout(state.scrollEndTimeout);
|
|
894
1108
|
}
|
|
895
|
-
if (state.resizeTimeout) {
|
|
896
|
-
clearTimeout(state.resizeTimeout);
|
|
897
|
-
}
|
|
898
1109
|
if (dragState) {
|
|
899
1110
|
cleanupDrag(dragState);
|
|
900
1111
|
}
|
|
@@ -950,6 +1161,9 @@ export {
|
|
|
950
1161
|
easeOutExpo,
|
|
951
1162
|
easeOutQuad,
|
|
952
1163
|
generateBullets,
|
|
953
|
-
|
|
1164
|
+
generateThumbs,
|
|
1165
|
+
injectStyles,
|
|
1166
|
+
linear,
|
|
1167
|
+
removeStyles
|
|
954
1168
|
};
|
|
955
1169
|
//# sourceMappingURL=index.js.map
|