lazer-slider 1.1.5 → 1.1.7
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 +132 -153
- package/dist/index.cjs +2 -1195
- package/dist/index.d.cts +1 -33
- package/dist/index.d.ts +1 -33
- package/dist/index.js +2 -1159
- package/dist/lazer-slider.css +282 -243
- package/package.json +2 -3
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,427 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
createSlider: () => createSlider,
|
|
24
|
-
easeInOutCubic: () => easeInOutCubic,
|
|
25
|
-
easeOutCubic: () => easeOutCubic,
|
|
26
|
-
easeOutExpo: () => easeOutExpo,
|
|
27
|
-
easeOutQuad: () => easeOutQuad,
|
|
28
|
-
generateBullets: () => generateBullets,
|
|
29
|
-
generateThumbs: () => generateThumbs,
|
|
30
|
-
injectStyles: () => injectStyles,
|
|
31
|
-
linear: () => linear,
|
|
32
|
-
removeStyles: () => removeStyles
|
|
33
|
-
});
|
|
34
|
-
module.exports = __toCommonJS(index_exports);
|
|
35
|
-
|
|
36
|
-
// src/core/easing.ts
|
|
37
|
-
var easeOutExpo = (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
|
|
38
|
-
var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
|
|
39
|
-
var easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
|
40
|
-
var easeOutQuad = (t) => 1 - (1 - t) * (1 - t);
|
|
41
|
-
var linear = (t) => t;
|
|
42
|
-
|
|
43
|
-
// src/core/accessibility.ts
|
|
44
|
-
var generateSliderId = () => `slider-${Math.random().toString(36).substring(2, 9)}`;
|
|
45
|
-
var initAria = (settings) => {
|
|
46
|
-
const { feed, prevSlideButton, nextSlideButton, thumbs, slides } = settings;
|
|
47
|
-
if (!feed.id) {
|
|
48
|
-
feed.id = generateSliderId();
|
|
49
|
-
}
|
|
50
|
-
feed.setAttribute("role", "region");
|
|
51
|
-
feed.setAttribute("aria-label", "Carousel");
|
|
52
|
-
feed.setAttribute("aria-roledescription", "carousel");
|
|
53
|
-
feed.removeAttribute("tabindex");
|
|
54
|
-
slides.forEach((slide, index) => {
|
|
55
|
-
slide.setAttribute("role", "group");
|
|
56
|
-
slide.setAttribute("aria-roledescription", "slide");
|
|
57
|
-
slide.setAttribute("aria-label", `Slide ${index + 1} of ${slides.length}`);
|
|
58
|
-
});
|
|
59
|
-
if (prevSlideButton) {
|
|
60
|
-
prevSlideButton.setAttribute("aria-label", "Previous slide");
|
|
61
|
-
prevSlideButton.setAttribute("aria-controls", feed.id);
|
|
62
|
-
prevSlideButton.setAttribute("tabindex", "0");
|
|
63
|
-
if (prevSlideButton.tagName !== "BUTTON") {
|
|
64
|
-
prevSlideButton.setAttribute("role", "button");
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if (nextSlideButton) {
|
|
68
|
-
nextSlideButton.setAttribute("aria-label", "Next slide");
|
|
69
|
-
nextSlideButton.setAttribute("aria-controls", feed.id);
|
|
70
|
-
nextSlideButton.setAttribute("tabindex", "0");
|
|
71
|
-
if (nextSlideButton.tagName !== "BUTTON") {
|
|
72
|
-
nextSlideButton.setAttribute("role", "button");
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (thumbs?.length) {
|
|
76
|
-
thumbs.forEach((thumb, index) => {
|
|
77
|
-
if (thumb.tagName !== "BUTTON") {
|
|
78
|
-
thumb.setAttribute("role", "button");
|
|
79
|
-
}
|
|
80
|
-
thumb.setAttribute("aria-label", `Go to slide ${index + 1}`);
|
|
81
|
-
thumb.setAttribute("tabindex", "0");
|
|
82
|
-
thumb.setAttribute("aria-controls", feed.id);
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
var toggleControlVisibility = (button, shouldShow, feedElement) => {
|
|
87
|
-
if (!button) return;
|
|
88
|
-
if (!shouldShow && button === document.activeElement && feedElement) {
|
|
89
|
-
feedElement.focus();
|
|
90
|
-
}
|
|
91
|
-
const transition = "opacity 0.3s ease";
|
|
92
|
-
const opacity = shouldShow ? "1" : "0";
|
|
93
|
-
Object.assign(button.style, {
|
|
94
|
-
opacity,
|
|
95
|
-
transition,
|
|
96
|
-
pointerEvents: shouldShow ? "auto" : "none"
|
|
97
|
-
});
|
|
98
|
-
if (!shouldShow) {
|
|
99
|
-
button.setAttribute("aria-hidden", "true");
|
|
100
|
-
button.setAttribute("tabindex", "-1");
|
|
101
|
-
} else {
|
|
102
|
-
button.removeAttribute("aria-hidden");
|
|
103
|
-
button.setAttribute("tabindex", "0");
|
|
104
|
-
}
|
|
105
|
-
if (!shouldShow) {
|
|
106
|
-
setTimeout(() => {
|
|
107
|
-
if (button.style.opacity === "0") {
|
|
108
|
-
button.style.visibility = "hidden";
|
|
109
|
-
}
|
|
110
|
-
}, 300);
|
|
111
|
-
} else {
|
|
112
|
-
button.style.visibility = "visible";
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
var updateActiveThumb = (thumbs, currentIndex, activeClass = "active") => {
|
|
116
|
-
if (!thumbs?.length) return;
|
|
117
|
-
thumbs.forEach((thumb, index) => {
|
|
118
|
-
const isActive = index === currentIndex;
|
|
119
|
-
thumb.classList.toggle(activeClass, isActive);
|
|
120
|
-
thumb.setAttribute("aria-selected", isActive.toString());
|
|
121
|
-
});
|
|
122
|
-
};
|
|
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";
|
|
127
|
-
feed.addEventListener(
|
|
128
|
-
"keydown",
|
|
129
|
-
(event) => {
|
|
130
|
-
switch (event.key) {
|
|
131
|
-
case prevKey:
|
|
132
|
-
event.preventDefault();
|
|
133
|
-
onPrev();
|
|
134
|
-
break;
|
|
135
|
-
case nextKey:
|
|
136
|
-
event.preventDefault();
|
|
137
|
-
onNext();
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
{ signal: abortSignal }
|
|
142
|
-
);
|
|
143
|
-
if (!feed.hasAttribute("tabindex")) {
|
|
144
|
-
feed.setAttribute("tabindex", "0");
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// src/core/drag.ts
|
|
149
|
-
var createDragState = () => ({
|
|
150
|
-
isDragging: false,
|
|
151
|
-
startX: 0,
|
|
152
|
-
startY: 0,
|
|
153
|
-
startScrollLeft: 0,
|
|
154
|
-
startScrollTop: 0,
|
|
155
|
-
velocity: 0,
|
|
156
|
-
lastX: 0,
|
|
157
|
-
lastY: 0,
|
|
158
|
-
lastTime: 0,
|
|
159
|
-
momentumId: null
|
|
160
|
-
});
|
|
161
|
-
var findNearestSlide = (feed, slides, direction = "horizontal") => {
|
|
162
|
-
const feedRect = feed.getBoundingClientRect();
|
|
163
|
-
const isVertical = direction === "vertical";
|
|
164
|
-
const feedCenter = isVertical ? feedRect.top + feedRect.height / 2 : feedRect.left + feedRect.width / 2;
|
|
165
|
-
let nearestSlide = null;
|
|
166
|
-
let minDistance = Infinity;
|
|
167
|
-
for (const slide of slides) {
|
|
168
|
-
if (slide.offsetParent === null) continue;
|
|
169
|
-
const slideRect = slide.getBoundingClientRect();
|
|
170
|
-
const slideCenter = isVertical ? slideRect.top + slideRect.height / 2 : slideRect.left + slideRect.width / 2;
|
|
171
|
-
const distance = Math.abs(feedCenter - slideCenter);
|
|
172
|
-
if (distance < minDistance) {
|
|
173
|
-
minDistance = distance;
|
|
174
|
-
nearestSlide = slide;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return nearestSlide;
|
|
178
|
-
};
|
|
179
|
-
var applyMomentum = (state, feed, slides, smoothScrollTo, onDragEnd, direction = "horizontal") => {
|
|
180
|
-
const friction = 0.95;
|
|
181
|
-
const minVelocity = 0.5;
|
|
182
|
-
const isVertical = direction === "vertical";
|
|
183
|
-
const animate = () => {
|
|
184
|
-
if (Math.abs(state.velocity) < minVelocity) {
|
|
185
|
-
state.momentumId = null;
|
|
186
|
-
const nearestSlide = findNearestSlide(feed, slides, direction);
|
|
187
|
-
if (nearestSlide) {
|
|
188
|
-
const targetPosition = isVertical ? nearestSlide.offsetTop : nearestSlide.offsetLeft;
|
|
189
|
-
smoothScrollTo(targetPosition, easeOutCubic);
|
|
190
|
-
}
|
|
191
|
-
onDragEnd?.();
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
if (isVertical) {
|
|
195
|
-
feed.scrollTop += state.velocity;
|
|
196
|
-
} else {
|
|
197
|
-
feed.scrollLeft += state.velocity;
|
|
198
|
-
}
|
|
199
|
-
state.velocity *= friction;
|
|
200
|
-
state.momentumId = requestAnimationFrame(animate);
|
|
201
|
-
};
|
|
202
|
-
state.momentumId = requestAnimationFrame(animate);
|
|
203
|
-
};
|
|
204
|
-
var getEventX = (event) => {
|
|
205
|
-
if ("touches" in event) {
|
|
206
|
-
return event.touches[0]?.clientX ?? 0;
|
|
207
|
-
}
|
|
208
|
-
return event.clientX;
|
|
209
|
-
};
|
|
210
|
-
var getEventY = (event) => {
|
|
211
|
-
if ("touches" in event) {
|
|
212
|
-
return event.touches[0]?.clientY ?? 0;
|
|
213
|
-
}
|
|
214
|
-
return event.clientY;
|
|
215
|
-
};
|
|
216
|
-
var setupDragToScroll = (config) => {
|
|
217
|
-
const { feed, slides, abortSignal, smoothScrollTo, onDragEnd, direction = "horizontal" } = config;
|
|
218
|
-
const state = createDragState();
|
|
219
|
-
const isVertical = direction === "vertical";
|
|
220
|
-
const handleDragStart = (event) => {
|
|
221
|
-
if (state.momentumId !== null) {
|
|
222
|
-
cancelAnimationFrame(state.momentumId);
|
|
223
|
-
state.momentumId = null;
|
|
224
|
-
}
|
|
225
|
-
state.isDragging = true;
|
|
226
|
-
state.startX = getEventX(event);
|
|
227
|
-
state.startY = getEventY(event);
|
|
228
|
-
state.startScrollLeft = feed.scrollLeft;
|
|
229
|
-
state.startScrollTop = feed.scrollTop;
|
|
230
|
-
state.velocity = 0;
|
|
231
|
-
state.lastX = state.startX;
|
|
232
|
-
state.lastY = state.startY;
|
|
233
|
-
state.lastTime = performance.now();
|
|
234
|
-
feed.style.userSelect = "none";
|
|
235
|
-
feed.classList.add("is-dragging");
|
|
236
|
-
if (event.type === "mousedown") {
|
|
237
|
-
event.preventDefault();
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
const handleDragMove = (event) => {
|
|
241
|
-
if (!state.isDragging) return;
|
|
242
|
-
const currentX = getEventX(event);
|
|
243
|
-
const currentY = getEventY(event);
|
|
244
|
-
const currentTime = performance.now();
|
|
245
|
-
const deltaTime = currentTime - state.lastTime;
|
|
246
|
-
if (isVertical) {
|
|
247
|
-
const deltaY = state.startY - currentY;
|
|
248
|
-
feed.scrollTop = state.startScrollTop + deltaY;
|
|
249
|
-
if (deltaTime > 0) {
|
|
250
|
-
state.velocity = (state.lastY - currentY) / deltaTime * 16;
|
|
251
|
-
}
|
|
252
|
-
state.lastY = currentY;
|
|
253
|
-
} else {
|
|
254
|
-
const deltaX = state.startX - currentX;
|
|
255
|
-
feed.scrollLeft = state.startScrollLeft + deltaX;
|
|
256
|
-
if (deltaTime > 0) {
|
|
257
|
-
state.velocity = (state.lastX - currentX) / deltaTime * 16;
|
|
258
|
-
}
|
|
259
|
-
state.lastX = currentX;
|
|
260
|
-
}
|
|
261
|
-
state.lastTime = currentTime;
|
|
262
|
-
if (event.type === "touchmove") {
|
|
263
|
-
event.preventDefault();
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
const handleDragEnd = () => {
|
|
267
|
-
if (!state.isDragging) return;
|
|
268
|
-
state.isDragging = false;
|
|
269
|
-
feed.style.userSelect = "";
|
|
270
|
-
feed.classList.remove("is-dragging");
|
|
271
|
-
if (Math.abs(state.velocity) > 1) {
|
|
272
|
-
applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd, direction);
|
|
273
|
-
} else {
|
|
274
|
-
const nearestSlide = findNearestSlide(feed, slides, direction);
|
|
275
|
-
if (nearestSlide) {
|
|
276
|
-
const targetPosition = isVertical ? nearestSlide.offsetTop : nearestSlide.offsetLeft;
|
|
277
|
-
smoothScrollTo(targetPosition, easeOutCubic);
|
|
278
|
-
}
|
|
279
|
-
onDragEnd?.();
|
|
280
|
-
}
|
|
281
|
-
};
|
|
282
|
-
feed.addEventListener("mousedown", handleDragStart, { signal: abortSignal });
|
|
283
|
-
document.addEventListener("mousemove", handleDragMove, { signal: abortSignal });
|
|
284
|
-
document.addEventListener("mouseup", handleDragEnd, { signal: abortSignal });
|
|
285
|
-
feed.addEventListener("touchstart", handleDragStart, {
|
|
286
|
-
passive: true,
|
|
287
|
-
signal: abortSignal
|
|
288
|
-
});
|
|
289
|
-
feed.addEventListener("touchmove", handleDragMove, {
|
|
290
|
-
passive: false,
|
|
291
|
-
signal: abortSignal
|
|
292
|
-
});
|
|
293
|
-
feed.addEventListener("touchend", handleDragEnd, { signal: abortSignal });
|
|
294
|
-
document.addEventListener("mouseleave", handleDragEnd, { signal: abortSignal });
|
|
295
|
-
return state;
|
|
296
|
-
};
|
|
297
|
-
var cleanupDrag = (state) => {
|
|
298
|
-
if (state.momentumId !== null) {
|
|
299
|
-
cancelAnimationFrame(state.momentumId);
|
|
300
|
-
state.momentumId = null;
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
// src/core/bullets.ts
|
|
305
|
-
var generateBullets = ({
|
|
306
|
-
bulletsContainer,
|
|
307
|
-
slides,
|
|
308
|
-
bulletClass,
|
|
309
|
-
bulletActiveClass,
|
|
310
|
-
feedId
|
|
311
|
-
}) => {
|
|
312
|
-
if (!bulletsContainer || !(bulletsContainer instanceof HTMLElement)) {
|
|
313
|
-
throw new Error("Invalid bulletsContainer: must be a valid HTMLElement");
|
|
314
|
-
}
|
|
315
|
-
if (!Array.isArray(slides) || slides.length === 0) {
|
|
316
|
-
throw new Error("Invalid slides: must be a non-empty array");
|
|
317
|
-
}
|
|
318
|
-
if (!feedId || typeof feedId !== "string") {
|
|
319
|
-
throw new Error("Invalid feedId: must be a non-empty string");
|
|
320
|
-
}
|
|
321
|
-
if (!bulletClass || typeof bulletClass !== "string") {
|
|
322
|
-
throw new Error("Invalid bulletClass: must be a non-empty string");
|
|
323
|
-
}
|
|
324
|
-
bulletsContainer.innerHTML = "";
|
|
325
|
-
bulletsContainer.setAttribute("role", "tablist");
|
|
326
|
-
bulletsContainer.setAttribute("aria-label", "Slide navigation");
|
|
327
|
-
const visibleSlides = slides.map((slide, originalIndex) => ({ slide, originalIndex })).filter(({ slide }) => slide.offsetParent !== null);
|
|
328
|
-
if (visibleSlides.length === 0) {
|
|
329
|
-
console.warn("No visible slides found");
|
|
330
|
-
return [];
|
|
331
|
-
}
|
|
332
|
-
const bullets = visibleSlides.map(({ slide, originalIndex }, visibleIndex) => {
|
|
333
|
-
const bullet = document.createElement("button");
|
|
334
|
-
bullet.type = "button";
|
|
335
|
-
bullet.classList.add(bulletClass);
|
|
336
|
-
if (visibleIndex === 0) {
|
|
337
|
-
bullet.classList.add(bulletActiveClass);
|
|
338
|
-
}
|
|
339
|
-
bullet.setAttribute("role", "tab");
|
|
340
|
-
bullet.setAttribute("aria-selected", visibleIndex === 0 ? "true" : "false");
|
|
341
|
-
bullet.setAttribute("aria-controls", feedId);
|
|
342
|
-
bullet.setAttribute("aria-label", `Go to slide ${visibleIndex + 1}`);
|
|
343
|
-
bullet.setAttribute("data-slide-index", String(visibleIndex));
|
|
344
|
-
bulletsContainer.appendChild(bullet);
|
|
345
|
-
return bullet;
|
|
346
|
-
});
|
|
347
|
-
return bullets;
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
// src/core/thumbs.ts
|
|
351
|
-
var generateThumbs = ({
|
|
352
|
-
thumbsContainer,
|
|
353
|
-
slides,
|
|
354
|
-
thumbClass,
|
|
355
|
-
thumbActiveClass,
|
|
356
|
-
feedId,
|
|
357
|
-
thumbImageSelector = "img",
|
|
358
|
-
thumbSize
|
|
359
|
-
}) => {
|
|
360
|
-
if (!thumbsContainer || !(thumbsContainer instanceof HTMLElement)) {
|
|
361
|
-
throw new Error("Invalid thumbsContainer: must be a valid HTMLElement");
|
|
362
|
-
}
|
|
363
|
-
if (!Array.isArray(slides) || slides.length === 0) {
|
|
364
|
-
throw new Error("Invalid slides: must be a non-empty array");
|
|
365
|
-
}
|
|
366
|
-
if (!feedId || typeof feedId !== "string") {
|
|
367
|
-
throw new Error("Invalid feedId: must be a non-empty string");
|
|
368
|
-
}
|
|
369
|
-
if (!thumbClass || typeof thumbClass !== "string") {
|
|
370
|
-
throw new Error("Invalid thumbClass: must be a non-empty string");
|
|
371
|
-
}
|
|
372
|
-
thumbsContainer.innerHTML = "";
|
|
373
|
-
thumbsContainer.setAttribute("role", "tablist");
|
|
374
|
-
thumbsContainer.setAttribute("aria-label", "Slide thumbnails");
|
|
375
|
-
const visibleSlides = slides.map((slide, originalIndex) => ({ slide, originalIndex })).filter(({ slide }) => slide.offsetParent !== null);
|
|
376
|
-
if (visibleSlides.length === 0) {
|
|
377
|
-
console.warn("No visible slides found");
|
|
378
|
-
return [];
|
|
379
|
-
}
|
|
380
|
-
const thumbs = visibleSlides.map(({ slide, originalIndex }, visibleIndex) => {
|
|
381
|
-
const thumb = document.createElement("button");
|
|
382
|
-
thumb.type = "button";
|
|
383
|
-
thumb.classList.add(thumbClass);
|
|
384
|
-
if (visibleIndex === 0) {
|
|
385
|
-
thumb.classList.add(thumbActiveClass);
|
|
386
|
-
}
|
|
387
|
-
const sourceImage = slide.querySelector(thumbImageSelector);
|
|
388
|
-
if (sourceImage?.src) {
|
|
389
|
-
const thumbImg = document.createElement("img");
|
|
390
|
-
thumbImg.src = sourceImage.src;
|
|
391
|
-
thumbImg.alt = sourceImage.alt || `Slide ${visibleIndex + 1} thumbnail`;
|
|
392
|
-
thumbImg.draggable = false;
|
|
393
|
-
if (thumbSize) {
|
|
394
|
-
thumbImg.style.width = `${thumbSize.width}px`;
|
|
395
|
-
thumbImg.style.height = `${thumbSize.height}px`;
|
|
396
|
-
thumbImg.style.objectFit = "cover";
|
|
397
|
-
}
|
|
398
|
-
thumb.appendChild(thumbImg);
|
|
399
|
-
}
|
|
400
|
-
thumb.setAttribute("role", "tab");
|
|
401
|
-
thumb.setAttribute("aria-selected", visibleIndex === 0 ? "true" : "false");
|
|
402
|
-
thumb.setAttribute("aria-controls", feedId);
|
|
403
|
-
thumb.setAttribute("aria-label", `Go to slide ${visibleIndex + 1}`);
|
|
404
|
-
thumb.setAttribute("data-slide-index", String(visibleIndex));
|
|
405
|
-
thumbsContainer.appendChild(thumb);
|
|
406
|
-
return thumb;
|
|
407
|
-
});
|
|
408
|
-
return thumbs;
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
// src/core/marquee.ts
|
|
412
|
-
var createMarqueeState = () => ({
|
|
413
|
-
initialized: false,
|
|
414
|
-
clonedSlides: [],
|
|
415
|
-
styleElement: null
|
|
416
|
-
});
|
|
417
|
-
var injectMarqueeKeyframes = () => {
|
|
418
|
-
const existingStyle = document.getElementById("lazer-marquee-keyframes");
|
|
419
|
-
if (existingStyle) {
|
|
420
|
-
return existingStyle;
|
|
421
|
-
}
|
|
422
|
-
const style = document.createElement("style");
|
|
423
|
-
style.id = "lazer-marquee-keyframes";
|
|
424
|
-
style.textContent = `
|
|
1
|
+
'use strict';var C={MIN_DURATION:400,MAX_DURATION:1e3,SPEED_FACTOR:1.5,SCROLL_END_DELAY:50,THUMB_UPDATE_DELAY:500},Ee="(min-width: 64rem)",O=()=>window.matchMedia(Ee).matches;var N=e=>e===1?1:1-Math.pow(2,-10*e),H=e=>1-Math.pow(1-e,3),ye=e=>e<.5?4*e*e*e:1-Math.pow(-2*e+2,3)/2,Le=e=>1-(1-e)*(1-e),ge=e=>e;var Ae=()=>`slider-${Math.random().toString(36).substring(2,9)}`,ee=e=>{let{feed:t,prevSlideButton:o,nextSlideButton:l,thumbs:r,slides:a}=e;t.id||(t.id=Ae()),t.setAttribute("role","region"),t.setAttribute("aria-label","Carousel"),t.setAttribute("aria-roledescription","carousel"),t.removeAttribute("tabindex"),a.forEach((i,s)=>{i.setAttribute("role","group"),i.setAttribute("aria-roledescription","slide"),i.setAttribute("aria-label",`Slide ${s+1} of ${a.length}`);}),o&&(o.setAttribute("aria-label","Previous slide"),o.setAttribute("aria-controls",t.id),o.setAttribute("tabindex","0"),o.tagName!=="BUTTON"&&o.setAttribute("role","button")),l&&(l.setAttribute("aria-label","Next slide"),l.setAttribute("aria-controls",t.id),l.setAttribute("tabindex","0"),l.tagName!=="BUTTON"&&l.setAttribute("role","button")),r?.length&&r.forEach((i,s)=>{i.tagName!=="BUTTON"&&i.setAttribute("role","button"),i.setAttribute("aria-label",`Go to slide ${s+1}`),i.setAttribute("tabindex","0"),i.setAttribute("aria-controls",t.id);});},z=(e,t,o)=>{if(!e)return;!t&&e===document.activeElement&&o&&o.focus();let l="opacity 0.3s ease",r=t?"1":"0";Object.assign(e.style,{opacity:r,transition:l,pointerEvents:t?"auto":"none"}),t?(e.removeAttribute("aria-hidden"),e.setAttribute("tabindex","0")):(e.setAttribute("aria-hidden","true"),e.setAttribute("tabindex","-1")),t?e.style.visibility="visible":setTimeout(()=>{e.style.opacity==="0"&&(e.style.visibility="hidden");},300);},F=(e,t,o="active")=>{e?.length&&e.forEach((l,r)=>{let a=r===t;l.classList.toggle(o,a),l.setAttribute("aria-selected",a.toString());});},te=(e,t,o,l,r="horizontal")=>{let a=r==="vertical",i=a?"ArrowUp":"ArrowLeft",s=a?"ArrowDown":"ArrowRight";e.addEventListener("keydown",m=>{switch(m.key){case i:m.preventDefault(),t();break;case s:m.preventDefault(),o();break}},{signal:l}),e.hasAttribute("tabindex")||e.setAttribute("tabindex","0");};var g={FRICTION:.94,MIN_VELOCITY:.3,MOMENTUM_RATIO:.8,VELOCITY_SMOOTHING:.4,EDGE_RESISTANCE:.3,MAX_EDGE_OVERSCROLL:100,SHORT_SWIPE_VELOCITY:.5,BOUNCE_DURATION:400},Me=()=>({isDragging:false,startX:0,startY:0,startScrollLeft:0,startScrollTop:0,velocity:0,lastX:0,lastY:0,lastTime:0,momentumId:null}),xe=()=>({...Me(),velocityHistory:[],dragDistance:0,dragStartTime:0}),$=(e,t,o="horizontal")=>{let l=e.getBoundingClientRect(),r=o==="vertical",a=r?l.top:l.left,i=null,s=1/0;for(let m of t){if(m.offsetParent===null)continue;let u=m.getBoundingClientRect(),h=r?u.top:u.left,n=Math.abs(a-h);n<s&&(s=n,i=m);}return i},Ie=(e,t,o,l="horizontal")=>{let r=e.getBoundingClientRect(),a=l==="vertical",i=a?r.top:r.left,s=t.filter(d=>d.offsetParent!==null);if(s.length===0)return null;let m=0,u=1/0;s.forEach((d,v)=>{let b=d.getBoundingClientRect(),P=a?b.top:b.left,I=Math.abs(i-P);I<u&&(u=I,m=v);});let h=2,n=m;return Math.abs(o)>h&&(o>0?n=Math.min(m+1,s.length-1):n=Math.max(m-1,0)),s[n]??null},De=e=>{if(e.length===0)return 0;let t=0,o=0;return e.forEach((l,r)=>{let a=r+1;t+=l*a,o+=a;}),t/o},Ce=(e,t,o,l,r,a="horizontal",i=false)=>{let s=a==="vertical",m=e.velocity*g.MOMENTUM_RATIO,u=()=>{if(Math.abs(m)<g.MIN_VELOCITY){e.momentumId=null;let v=$(t,o,a);if(v){let b=s?v.offsetTop:v.offsetLeft;l(b,H);}r?.(v);return}let h=s?t.scrollHeight-t.clientHeight:t.scrollWidth-t.clientWidth,d=(s?t.scrollTop:t.scrollLeft)+m;i||(d<0||d>h)&&(m*=.5),s?t.scrollTop=Math.max(0,Math.min(h,d)):t.scrollLeft=Math.max(0,Math.min(h,d)),m*=g.FRICTION,e.momentumId=requestAnimationFrame(u);};e.momentumId=requestAnimationFrame(u);},re=e=>"touches"in e?e.touches[0]?.clientX??0:e.clientX,oe=e=>"touches"in e?e.touches[0]?.clientY??0:e.clientY,He=(e,t)=>t==="vertical"?oe(e):re(e),le=e=>{let {feed:t,slides:o,abortSignal:l,smoothScrollTo:r,onDragEnd:a,direction:i="horizontal",loop:s=false,touchRatio:m=1,shortSwipeThreshold:u=300,swipeDistanceThreshold:h=10}=e,n=xe(),d=i==="vertical",v=0,P=()=>{v=d?t.scrollHeight-t.clientHeight:t.scrollWidth-t.clientWidth,d?t.clientHeight:t.clientWidth;},I=L=>{n.momentumId!==null&&(cancelAnimationFrame(n.momentumId),n.momentumId=null),P(),n.isDragging=true,n.startX=re(L),n.startY=oe(L),n.startScrollLeft=t.scrollLeft,n.startScrollTop=t.scrollTop,n.velocity=0,n.lastX=n.startX,n.lastY=n.startY,n.lastTime=performance.now(),n.velocityHistory=[],n.dragDistance=0,n.dragStartTime=performance.now(),t.style.userSelect="none",t.style.cursor="grabbing",t.classList.add("is-dragging"),L.type==="mousedown"&&L.preventDefault();},q=L=>{if(!n.isDragging)return;let A=He(L,i),R=d?n.startY:n.startX,E=d?n.lastY:n.lastX,M=d?n.startScrollTop:n.startScrollLeft,c=performance.now(),f=c-n.lastTime,T=(R-A)*m,S=M+T;if(n.dragDistance=Math.abs(T),!s){if(S<0){let p=Math.abs(S),y=1-Math.min(p/g.MAX_EDGE_OVERSCROLL,1)*(1-g.EDGE_RESISTANCE);S=-p*y;}else if(S>v){let p=S-v,y=1-Math.min(p/g.MAX_EDGE_OVERSCROLL,1)*(1-g.EDGE_RESISTANCE);S=v+p*y;}}if(d?t.scrollTop=S:t.scrollLeft=S,f>0){let p=(E-A)/f*16;if(n.velocityHistory.length>0){let y=n.velocity;n.velocity=y*(1-g.VELOCITY_SMOOTHING)+p*g.VELOCITY_SMOOTHING;}else n.velocity=p;n.velocityHistory.push(p),n.velocityHistory.length>5&&n.velocityHistory.shift();}d?n.lastY=A:n.lastX=A,n.lastTime=c,L.type==="touchmove"&&L.preventDefault();},w=()=>{if(!n.isDragging)return;n.isDragging=false,t.style.userSelect="",t.style.cursor="",t.classList.remove("is-dragging");let A=performance.now()-n.dragStartTime<u&&n.dragDistance>h,R=De(n.velocityHistory);if(!s){let E=d?t.scrollTop:t.scrollLeft;if(E<0||E>v){let M=E<0?0:v,c=E,f=performance.now(),T=()=>{let S=performance.now()-f,p=Math.min(S/g.BOUNCE_DURATION,1),y=H(p),B=c+(M-c)*y;if(d?t.scrollTop=B:t.scrollLeft=B,p<1)requestAnimationFrame(T);else {let D=$(t,o,i);if(D){let x=d?D.offsetTop:D.offsetLeft;r(x,H);}a?.(D);}};requestAnimationFrame(T);return}}if(A||Math.abs(R)>g.SHORT_SWIPE_VELOCITY){let E=Ie(t,o,R,i);if(E){let M=d?E.offsetTop:E.offsetLeft;r(M,H),a?.(E);}else a?.(null);}else if(Math.abs(n.velocity)>1)Ce(n,t,o,r,a,i,s);else {let E=$(t,o,i);if(E){let M=d?E.offsetTop:E.offsetLeft;r(M,H);}a?.(E);}};return t.addEventListener("mousedown",I,{signal:l}),document.addEventListener("mousemove",q,{signal:l}),document.addEventListener("mouseup",w,{signal:l}),t.addEventListener("touchstart",I,{passive:true,signal:l}),t.addEventListener("touchmove",q,{passive:false,signal:l}),t.addEventListener("touchend",w,{signal:l}),t.addEventListener("touchcancel",w,{signal:l}),document.addEventListener("mouseleave",w,{signal:l}),t.style.cursor="grab",n},ie=e=>{e.momentumId!==null&&(cancelAnimationFrame(e.momentumId),e.momentumId=null);};var G=({bulletsContainer:e,slides:t,bulletClass:o,bulletActiveClass:l,feedId:r})=>{if(!e||!(e instanceof HTMLElement))throw new Error("Invalid bulletsContainer: must be a valid HTMLElement");if(!Array.isArray(t)||t.length===0)throw new Error("Invalid slides: must be a non-empty array");if(!r||typeof r!="string")throw new Error("Invalid feedId: must be a non-empty string");if(!o||typeof o!="string")throw new Error("Invalid bulletClass: must be a non-empty string");e.innerHTML="",e.setAttribute("role","tablist"),e.setAttribute("aria-label","Slide navigation");let a=t.map((s,m)=>({slide:s,originalIndex:m})).filter(({slide:s})=>s.offsetParent!==null);return a.length===0?(console.warn("No visible slides found"),[]):a.map(({slide:s,originalIndex:m},u)=>{let h=document.createElement("button");return h.type="button",h.classList.add(o),u===0&&h.classList.add(l),h.setAttribute("role","tab"),h.setAttribute("aria-selected",u===0?"true":"false"),h.setAttribute("aria-controls",r),h.setAttribute("aria-label",`Go to slide ${u+1}`),h.setAttribute("data-slide-index",String(u)),e.appendChild(h),h})};var X=({thumbsContainer:e,slides:t,thumbClass:o,thumbActiveClass:l,feedId:r,thumbImageSelector:a="img",thumbSize:i})=>{if(!e||!(e instanceof HTMLElement))throw new Error("Invalid thumbsContainer: must be a valid HTMLElement");if(!Array.isArray(t)||t.length===0)throw new Error("Invalid slides: must be a non-empty array");if(!r||typeof r!="string")throw new Error("Invalid feedId: must be a non-empty string");if(!o||typeof o!="string")throw new Error("Invalid thumbClass: must be a non-empty string");e.innerHTML="",e.setAttribute("role","tablist"),e.setAttribute("aria-label","Slide thumbnails");let s=t.map((u,h)=>({slide:u,originalIndex:h})).filter(({slide:u})=>u.offsetParent!==null);return s.length===0?(console.warn("No visible slides found"),[]):s.map(({slide:u,originalIndex:h},n)=>{let d=document.createElement("button");d.type="button",d.classList.add(o),n===0&&d.classList.add(l);let v=u.querySelector(a);if(v?.src){let b=document.createElement("img");b.src=v.src,b.alt=v.alt||`Slide ${n+1} thumbnail`,b.draggable=false,i&&(b.style.width=`${i.width}px`,b.style.height=`${i.height}px`,b.style.objectFit="cover"),d.appendChild(b);}return d.setAttribute("role","tab"),d.setAttribute("aria-selected",n===0?"true":"false"),d.setAttribute("aria-controls",r),d.setAttribute("aria-label",`Go to slide ${n+1}`),d.setAttribute("data-slide-index",String(n)),e.appendChild(d),d})};var ne=()=>({initialized:false,clonedSlides:[],styleElement:null}),ae=()=>{let e=document.getElementById("lazer-marquee-keyframes");if(e)return e;let t=document.createElement("style");return t.id="lazer-marquee-keyframes",t.textContent=`
|
|
425
2
|
@keyframes lazer-marquee-scroll {
|
|
426
3
|
0% {
|
|
427
4
|
transform: translateX(0);
|
|
@@ -430,774 +7,4 @@ var injectMarqueeKeyframes = () => {
|
|
|
430
7
|
transform: translateX(-50%);
|
|
431
8
|
}
|
|
432
9
|
}
|
|
433
|
-
`;
|
|
434
|
-
document.head.appendChild(style);
|
|
435
|
-
return style;
|
|
436
|
-
};
|
|
437
|
-
var setupMarqueeClones = (settings, marqueeState) => {
|
|
438
|
-
if (marqueeState.initialized) return;
|
|
439
|
-
marqueeState.styleElement = injectMarqueeKeyframes();
|
|
440
|
-
settings.feed.style.display = "flex";
|
|
441
|
-
settings.feed.style.willChange = "transform";
|
|
442
|
-
settings.slides.forEach((slide) => {
|
|
443
|
-
const clone = slide.cloneNode(true);
|
|
444
|
-
clone.setAttribute("data-lazer-marquee-clone", "true");
|
|
445
|
-
clone.setAttribute("aria-hidden", "true");
|
|
446
|
-
settings.feed.appendChild(clone);
|
|
447
|
-
marqueeState.clonedSlides.push(clone);
|
|
448
|
-
});
|
|
449
|
-
marqueeState.initialized = true;
|
|
450
|
-
};
|
|
451
|
-
var cleanupMarqueeClones = (marqueeState) => {
|
|
452
|
-
if (!marqueeState.initialized) return;
|
|
453
|
-
marqueeState.clonedSlides.forEach((clone) => {
|
|
454
|
-
clone.remove();
|
|
455
|
-
});
|
|
456
|
-
marqueeState.clonedSlides = [];
|
|
457
|
-
marqueeState.initialized = false;
|
|
458
|
-
};
|
|
459
|
-
var setupMarqueeCss = (settings, state) => {
|
|
460
|
-
injectMarqueeKeyframes();
|
|
461
|
-
requestAnimationFrame(() => {
|
|
462
|
-
const scrollWidth = settings.feed.scrollWidth;
|
|
463
|
-
const speed = settings.marqueeSpeed ?? 50;
|
|
464
|
-
const direction = settings.marqueeDirection ?? "left";
|
|
465
|
-
const distance = scrollWidth / 2;
|
|
466
|
-
if (distance <= 0 || speed <= 0) {
|
|
467
|
-
console.warn("[lazer-slider] Invalid marquee values:", { distance, speed, scrollWidth });
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
const duration = distance / speed;
|
|
471
|
-
const animationDirection = direction === "right" ? "reverse" : "normal";
|
|
472
|
-
const animationValue = `lazer-marquee-scroll ${duration}s linear infinite ${animationDirection}`;
|
|
473
|
-
settings.feed.style.animation = "none";
|
|
474
|
-
void settings.feed.offsetWidth;
|
|
475
|
-
settings.feed.style.animation = animationValue;
|
|
476
|
-
settings.feed.style.animationPlayState = state.marqueePaused ? "paused" : "running";
|
|
477
|
-
});
|
|
478
|
-
};
|
|
479
|
-
var startMarquee = (settings, state) => {
|
|
480
|
-
setupMarqueeCss(settings, state);
|
|
481
|
-
};
|
|
482
|
-
var stopMarquee = (state, settings) => {
|
|
483
|
-
settings.feed.style.animation = "";
|
|
484
|
-
settings.feed.style.animationPlayState = "";
|
|
485
|
-
settings.feed.style.transform = "";
|
|
486
|
-
settings.feed.style.willChange = "";
|
|
487
|
-
};
|
|
488
|
-
var pauseMarquee = (state, settings) => {
|
|
489
|
-
state.marqueePaused = true;
|
|
490
|
-
settings.feed.style.animationPlayState = "paused";
|
|
491
|
-
};
|
|
492
|
-
var resumeMarquee = (state, settings) => {
|
|
493
|
-
state.marqueePaused = false;
|
|
494
|
-
settings.feed.style.animationPlayState = "running";
|
|
495
|
-
};
|
|
496
|
-
var setupMarquee = (settings, state, marqueeState) => {
|
|
497
|
-
if (!settings.marquee) return;
|
|
498
|
-
setupMarqueeClones(settings, marqueeState);
|
|
499
|
-
startMarquee(settings, state);
|
|
500
|
-
};
|
|
501
|
-
var attachMarqueeEventListeners = (settings, state, signal) => {
|
|
502
|
-
if (!settings.marquee || settings.pauseOnHover === false) return;
|
|
503
|
-
settings.feed.addEventListener(
|
|
504
|
-
"mouseenter",
|
|
505
|
-
() => pauseMarquee(state, settings),
|
|
506
|
-
{ signal }
|
|
507
|
-
);
|
|
508
|
-
settings.feed.addEventListener(
|
|
509
|
-
"mouseleave",
|
|
510
|
-
() => resumeMarquee(state, settings),
|
|
511
|
-
{ signal }
|
|
512
|
-
);
|
|
513
|
-
settings.feed.addEventListener(
|
|
514
|
-
"touchstart",
|
|
515
|
-
() => pauseMarquee(state, settings),
|
|
516
|
-
{ passive: true, signal }
|
|
517
|
-
);
|
|
518
|
-
settings.feed.addEventListener(
|
|
519
|
-
"touchend",
|
|
520
|
-
() => resumeMarquee(state, settings),
|
|
521
|
-
{ signal }
|
|
522
|
-
);
|
|
523
|
-
};
|
|
524
|
-
|
|
525
|
-
// src/core/styles.ts
|
|
526
|
-
var CRITICAL_STYLES = `
|
|
527
|
-
/* Lazer Slider - Critical Styles (Auto-injected) */
|
|
528
|
-
.lazer-feed {
|
|
529
|
-
position: relative;
|
|
530
|
-
display: flex;
|
|
531
|
-
overflow-x: auto;
|
|
532
|
-
overflow-y: hidden;
|
|
533
|
-
-webkit-overflow-scrolling: touch;
|
|
534
|
-
scrollbar-width: none;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
.lazer-feed::-webkit-scrollbar {
|
|
538
|
-
display: none;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
.lazer-feed.lazer-vertical {
|
|
542
|
-
flex-direction: column;
|
|
543
|
-
overflow-x: hidden;
|
|
544
|
-
overflow-y: auto;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
.lazer-slide {
|
|
548
|
-
flex-shrink: 0;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
.lazer-feed.lazer-draggable {
|
|
552
|
-
cursor: grab;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
.lazer-feed.lazer-draggable.is-dragging {
|
|
556
|
-
cursor: grabbing;
|
|
557
|
-
user-select: none;
|
|
558
|
-
}
|
|
559
|
-
`;
|
|
560
|
-
var stylesInjected = false;
|
|
561
|
-
var STYLE_ID = "lazer-slider-critical-styles";
|
|
562
|
-
var injectStyles = () => {
|
|
563
|
-
if (stylesInjected || typeof document === "undefined") return;
|
|
564
|
-
const existingStyle = document.getElementById(STYLE_ID);
|
|
565
|
-
if (existingStyle) {
|
|
566
|
-
stylesInjected = true;
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
const style = document.createElement("style");
|
|
570
|
-
style.id = STYLE_ID;
|
|
571
|
-
style.textContent = CRITICAL_STYLES;
|
|
572
|
-
document.head.appendChild(style);
|
|
573
|
-
stylesInjected = true;
|
|
574
|
-
};
|
|
575
|
-
var injectStylesOnce = () => {
|
|
576
|
-
injectStyles();
|
|
577
|
-
};
|
|
578
|
-
var removeStyles = () => {
|
|
579
|
-
if (typeof document === "undefined") return;
|
|
580
|
-
const style = document.getElementById(STYLE_ID);
|
|
581
|
-
if (style) {
|
|
582
|
-
style.remove();
|
|
583
|
-
stylesInjected = false;
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
|
|
587
|
-
// src/core/slider.ts
|
|
588
|
-
var ANIMATION = {
|
|
589
|
-
MIN_DURATION: 400,
|
|
590
|
-
MAX_DURATION: 1e3,
|
|
591
|
-
SPEED_FACTOR: 1.5,
|
|
592
|
-
SCROLL_END_DELAY: 50,
|
|
593
|
-
THUMB_UPDATE_DELAY: 500
|
|
594
|
-
};
|
|
595
|
-
var DESKTOP_BREAKPOINT = "(min-width: 64rem)";
|
|
596
|
-
var createSlider = (settings) => {
|
|
597
|
-
injectStylesOnce();
|
|
598
|
-
if (!settings.feed) {
|
|
599
|
-
throw new Error("lazer-slider: feed element is required");
|
|
600
|
-
}
|
|
601
|
-
if (!settings.slides?.length) {
|
|
602
|
-
throw new Error("lazer-slider: slides array is required and must not be empty");
|
|
603
|
-
}
|
|
604
|
-
if (!settings.feed.id) {
|
|
605
|
-
settings.feed.id = `lazer-slider-feed-${Math.random().toString(36).substr(2, 9)}`;
|
|
606
|
-
}
|
|
607
|
-
settings.feed.classList.add("lazer-feed");
|
|
608
|
-
settings.slides.forEach((slide) => {
|
|
609
|
-
slide.classList.add("lazer-slide");
|
|
610
|
-
});
|
|
611
|
-
if (settings.bulletsContainer && !settings.thumbs) {
|
|
612
|
-
const bullets = generateBullets({
|
|
613
|
-
bulletsContainer: settings.bulletsContainer,
|
|
614
|
-
slides: settings.slides,
|
|
615
|
-
bulletClass: settings.bulletsClass ?? "lazer-bullet",
|
|
616
|
-
bulletActiveClass: settings.bulletsActiveClass ?? "active",
|
|
617
|
-
feedId: settings.feed.id
|
|
618
|
-
});
|
|
619
|
-
settings.thumbs = bullets;
|
|
620
|
-
}
|
|
621
|
-
if (settings.thumbsContainer && !settings.thumbs) {
|
|
622
|
-
const thumbs = generateThumbs({
|
|
623
|
-
thumbsContainer: settings.thumbsContainer,
|
|
624
|
-
slides: settings.slides,
|
|
625
|
-
thumbClass: settings.thumbsClass ?? "lazer-thumb",
|
|
626
|
-
thumbActiveClass: settings.thumbsActiveClass ?? "active",
|
|
627
|
-
feedId: settings.feed.id,
|
|
628
|
-
thumbImageSelector: settings.thumbImageSelector ?? "img",
|
|
629
|
-
thumbSize: settings.thumbSize
|
|
630
|
-
});
|
|
631
|
-
settings.thumbs = thumbs;
|
|
632
|
-
}
|
|
633
|
-
const direction = settings.direction ?? "horizontal";
|
|
634
|
-
const isVertical = direction === "vertical";
|
|
635
|
-
if (isVertical) {
|
|
636
|
-
settings.feed.classList.add("lazer-vertical");
|
|
637
|
-
}
|
|
638
|
-
if (settings.marquee) {
|
|
639
|
-
settings.feed.classList.add("lazer-marquee");
|
|
640
|
-
}
|
|
641
|
-
const state = {
|
|
642
|
-
currentSlideIndex: 0,
|
|
643
|
-
isScrolling: false,
|
|
644
|
-
ticking: false,
|
|
645
|
-
cachedFeedRect: null,
|
|
646
|
-
lastWidth: 0,
|
|
647
|
-
updateThumbTimeout: null,
|
|
648
|
-
scrollEndTimeout: null,
|
|
649
|
-
abortController: new AbortController(),
|
|
650
|
-
autoplayIntervalId: null,
|
|
651
|
-
autoplayPaused: false,
|
|
652
|
-
marqueeAnimationId: null,
|
|
653
|
-
marqueePaused: false,
|
|
654
|
-
marqueeLastTimestamp: 0,
|
|
655
|
-
isLoopRepositioning: false
|
|
656
|
-
};
|
|
657
|
-
let dragState = null;
|
|
658
|
-
const loopState = {
|
|
659
|
-
initialized: false,
|
|
660
|
-
clonedSlides: [],
|
|
661
|
-
realSlides: [...settings.slides],
|
|
662
|
-
clonesPerSide: 0
|
|
663
|
-
};
|
|
664
|
-
const marqueeState = createMarqueeState();
|
|
665
|
-
const easing = settings.easing ?? easeOutExpo;
|
|
666
|
-
const getFeedRect = () => {
|
|
667
|
-
const currentSize = isVertical ? settings.feed.clientHeight : settings.feed.clientWidth;
|
|
668
|
-
if (!state.cachedFeedRect || state.lastWidth !== currentSize) {
|
|
669
|
-
state.cachedFeedRect = settings.feed.getBoundingClientRect();
|
|
670
|
-
state.lastWidth = currentSize;
|
|
671
|
-
}
|
|
672
|
-
return state.cachedFeedRect;
|
|
673
|
-
};
|
|
674
|
-
const getVisibleSlides = () => {
|
|
675
|
-
return settings.slides.filter((slide) => slide.offsetParent !== null);
|
|
676
|
-
};
|
|
677
|
-
const isDesktop = () => {
|
|
678
|
-
return window.matchMedia(DESKTOP_BREAKPOINT).matches;
|
|
679
|
-
};
|
|
680
|
-
const getLoopClonesCount = () => {
|
|
681
|
-
const perView = isDesktop() ? settings.desktopSlidesPerView : settings.mobileSlidesPerView;
|
|
682
|
-
if (!perView || perView === "auto") {
|
|
683
|
-
return 1;
|
|
684
|
-
}
|
|
685
|
-
return Math.ceil(perView);
|
|
686
|
-
};
|
|
687
|
-
const setupLoopClones = () => {
|
|
688
|
-
if (!settings.loop || loopState.initialized) return;
|
|
689
|
-
const realSlides = loopState.realSlides;
|
|
690
|
-
const clonesCount = getLoopClonesCount();
|
|
691
|
-
loopState.clonesPerSide = clonesCount;
|
|
692
|
-
for (let i = realSlides.length - clonesCount; i < realSlides.length; i++) {
|
|
693
|
-
const slide = realSlides[i];
|
|
694
|
-
if (!slide) continue;
|
|
695
|
-
const clone = slide.cloneNode(true);
|
|
696
|
-
clone.setAttribute("data-lazer-clone", "prepend");
|
|
697
|
-
clone.setAttribute("aria-hidden", "true");
|
|
698
|
-
settings.feed.insertBefore(clone, settings.feed.firstChild);
|
|
699
|
-
loopState.clonedSlides.push(clone);
|
|
700
|
-
}
|
|
701
|
-
for (let i = 0; i < clonesCount; i++) {
|
|
702
|
-
const slide = realSlides[i];
|
|
703
|
-
if (!slide) continue;
|
|
704
|
-
const clone = slide.cloneNode(true);
|
|
705
|
-
clone.setAttribute("data-lazer-clone", "append");
|
|
706
|
-
clone.setAttribute("aria-hidden", "true");
|
|
707
|
-
settings.feed.appendChild(clone);
|
|
708
|
-
loopState.clonedSlides.push(clone);
|
|
709
|
-
}
|
|
710
|
-
requestAnimationFrame(() => {
|
|
711
|
-
const firstRealSlide = realSlides[0];
|
|
712
|
-
if (firstRealSlide) {
|
|
713
|
-
if (isVertical) {
|
|
714
|
-
settings.feed.scrollTop = firstRealSlide.offsetTop;
|
|
715
|
-
} else {
|
|
716
|
-
settings.feed.scrollLeft = firstRealSlide.offsetLeft;
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
loopState.initialized = true;
|
|
721
|
-
};
|
|
722
|
-
const handleLoopReposition = (navDirection) => {
|
|
723
|
-
if (!settings.loop || !loopState.initialized) return;
|
|
724
|
-
state.isLoopRepositioning = true;
|
|
725
|
-
const realSlides = loopState.realSlides;
|
|
726
|
-
const totalRealSlides = realSlides.length;
|
|
727
|
-
if (navDirection === "next") {
|
|
728
|
-
const firstRealSlide = realSlides[0];
|
|
729
|
-
if (firstRealSlide) {
|
|
730
|
-
if (isVertical) {
|
|
731
|
-
settings.feed.scrollTop = firstRealSlide.offsetTop;
|
|
732
|
-
} else {
|
|
733
|
-
settings.feed.scrollLeft = firstRealSlide.offsetLeft;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
state.currentSlideIndex = 0;
|
|
737
|
-
} else {
|
|
738
|
-
const lastRealSlide = realSlides[totalRealSlides - 1];
|
|
739
|
-
if (lastRealSlide) {
|
|
740
|
-
if (isVertical) {
|
|
741
|
-
settings.feed.scrollTop = lastRealSlide.offsetTop;
|
|
742
|
-
} else {
|
|
743
|
-
settings.feed.scrollLeft = lastRealSlide.offsetLeft;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
state.currentSlideIndex = totalRealSlides - 1;
|
|
747
|
-
}
|
|
748
|
-
requestAnimationFrame(() => {
|
|
749
|
-
requestAnimationFrame(() => {
|
|
750
|
-
state.isLoopRepositioning = false;
|
|
751
|
-
updateControlsVisibility();
|
|
752
|
-
});
|
|
753
|
-
});
|
|
754
|
-
};
|
|
755
|
-
const cleanupLoopClones = () => {
|
|
756
|
-
if (!loopState.initialized) return;
|
|
757
|
-
loopState.clonedSlides.forEach((clone) => {
|
|
758
|
-
clone.remove();
|
|
759
|
-
});
|
|
760
|
-
loopState.clonedSlides = [];
|
|
761
|
-
loopState.initialized = false;
|
|
762
|
-
loopState.clonesPerSide = 0;
|
|
763
|
-
};
|
|
764
|
-
const applySlideWidths = () => {
|
|
765
|
-
const perView = isDesktop() ? settings.desktopSlidesPerView : settings.mobileSlidesPerView;
|
|
766
|
-
const gap = settings.slideGap ?? 0;
|
|
767
|
-
if (gap > 0) {
|
|
768
|
-
settings.feed.style.gap = `${gap}px`;
|
|
769
|
-
}
|
|
770
|
-
if (!perView || perView === "auto") {
|
|
771
|
-
settings.slides.forEach((slide) => {
|
|
772
|
-
slide.style.flex = "";
|
|
773
|
-
slide.style.minWidth = "";
|
|
774
|
-
slide.style.minHeight = "";
|
|
775
|
-
});
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
const totalGapSize = gap * (perView - 1);
|
|
779
|
-
const slideSize = `calc((100% - ${totalGapSize}px) / ${perView})`;
|
|
780
|
-
if (isVertical) {
|
|
781
|
-
settings.slides.forEach((slide) => {
|
|
782
|
-
slide.style.flex = `0 0 ${slideSize}`;
|
|
783
|
-
slide.style.minHeight = slideSize;
|
|
784
|
-
slide.style.minWidth = "";
|
|
785
|
-
});
|
|
786
|
-
} else {
|
|
787
|
-
settings.slides.forEach((slide) => {
|
|
788
|
-
slide.style.flex = `0 0 ${slideSize}`;
|
|
789
|
-
slide.style.minWidth = slideSize;
|
|
790
|
-
slide.style.minHeight = "";
|
|
791
|
-
});
|
|
792
|
-
}
|
|
793
|
-
};
|
|
794
|
-
const updateScrollbar = () => {
|
|
795
|
-
if (!settings.scrollbarThumb) return;
|
|
796
|
-
const feedRect = getFeedRect();
|
|
797
|
-
if (isVertical) {
|
|
798
|
-
const thumbHeight = feedRect.height / settings.feed.scrollHeight * 100;
|
|
799
|
-
settings.scrollbarThumb.style.height = `${thumbHeight}%`;
|
|
800
|
-
settings.scrollbarThumb.style.width = "";
|
|
801
|
-
} else {
|
|
802
|
-
const thumbWidth = feedRect.width / settings.feed.scrollWidth * 100;
|
|
803
|
-
settings.scrollbarThumb.style.width = `${thumbWidth}%`;
|
|
804
|
-
settings.scrollbarThumb.style.height = "";
|
|
805
|
-
}
|
|
806
|
-
};
|
|
807
|
-
const updateScrollbarPosition = () => {
|
|
808
|
-
if (!settings.scrollbarThumb || !settings.scrollbarTrack) return;
|
|
809
|
-
if (isVertical) {
|
|
810
|
-
const trackHeight = settings.scrollbarTrack.getBoundingClientRect().height;
|
|
811
|
-
const thumbHeight = settings.scrollbarThumb.getBoundingClientRect().height;
|
|
812
|
-
const totalTransform = trackHeight - thumbHeight;
|
|
813
|
-
const maxScroll = settings.feed.scrollHeight - settings.feed.clientHeight;
|
|
814
|
-
const scrollProgress = maxScroll > 0 ? settings.feed.scrollTop / maxScroll : 0;
|
|
815
|
-
settings.scrollbarThumb.style.transform = `translateY(${totalTransform * scrollProgress}px)`;
|
|
816
|
-
} else {
|
|
817
|
-
const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width;
|
|
818
|
-
const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width;
|
|
819
|
-
const totalTransform = trackWidth - thumbWidth;
|
|
820
|
-
const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth;
|
|
821
|
-
const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0;
|
|
822
|
-
settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`;
|
|
823
|
-
}
|
|
824
|
-
};
|
|
825
|
-
const updateControlsVisibility = () => {
|
|
826
|
-
if (state.isLoopRepositioning) {
|
|
827
|
-
return;
|
|
828
|
-
}
|
|
829
|
-
const feedRect = getFeedRect();
|
|
830
|
-
let isAtStart;
|
|
831
|
-
let isAtEnd;
|
|
832
|
-
let shouldHideScrollbar;
|
|
833
|
-
if (isVertical) {
|
|
834
|
-
isAtStart = settings.feed.scrollTop <= 1;
|
|
835
|
-
isAtEnd = settings.feed.scrollTop + feedRect.height >= settings.feed.scrollHeight - 1;
|
|
836
|
-
shouldHideScrollbar = settings.feed.scrollHeight <= feedRect.height;
|
|
837
|
-
} else {
|
|
838
|
-
isAtStart = settings.feed.scrollLeft <= 1;
|
|
839
|
-
isAtEnd = settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1;
|
|
840
|
-
shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width;
|
|
841
|
-
}
|
|
842
|
-
if (settings.scrollbarTrack) {
|
|
843
|
-
settings.scrollbarTrack.style.display = shouldHideScrollbar ? "none" : "block";
|
|
844
|
-
}
|
|
845
|
-
if (settings.loop) {
|
|
846
|
-
toggleControlVisibility(
|
|
847
|
-
settings.prevSlideButton,
|
|
848
|
-
!shouldHideScrollbar,
|
|
849
|
-
settings.feed
|
|
850
|
-
);
|
|
851
|
-
toggleControlVisibility(
|
|
852
|
-
settings.nextSlideButton,
|
|
853
|
-
!shouldHideScrollbar,
|
|
854
|
-
settings.feed
|
|
855
|
-
);
|
|
856
|
-
return;
|
|
857
|
-
}
|
|
858
|
-
toggleControlVisibility(
|
|
859
|
-
settings.prevSlideButton,
|
|
860
|
-
!isAtStart && !shouldHideScrollbar,
|
|
861
|
-
settings.feed
|
|
862
|
-
);
|
|
863
|
-
toggleControlVisibility(
|
|
864
|
-
settings.nextSlideButton,
|
|
865
|
-
!isAtEnd && !shouldHideScrollbar,
|
|
866
|
-
settings.feed
|
|
867
|
-
);
|
|
868
|
-
};
|
|
869
|
-
const updateCurrentSlideIndex = () => {
|
|
870
|
-
const feedRect = getFeedRect();
|
|
871
|
-
const slidesToCheck = loopState.initialized ? loopState.realSlides : getVisibleSlides();
|
|
872
|
-
const viewportVisibleSlides = slidesToCheck.filter((slide) => {
|
|
873
|
-
const slideRect = slide.getBoundingClientRect();
|
|
874
|
-
const tolerance = 20;
|
|
875
|
-
if (isVertical) {
|
|
876
|
-
return slideRect.bottom > feedRect.top + tolerance && slideRect.top < feedRect.bottom - tolerance;
|
|
877
|
-
}
|
|
878
|
-
return slideRect.right > feedRect.left + tolerance && slideRect.left < feedRect.right - tolerance;
|
|
879
|
-
});
|
|
880
|
-
if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {
|
|
881
|
-
const newIndex = slidesToCheck.indexOf(viewportVisibleSlides[0]);
|
|
882
|
-
if (newIndex !== -1) {
|
|
883
|
-
state.currentSlideIndex = newIndex;
|
|
884
|
-
const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
885
|
-
settings.onScroll?.({
|
|
886
|
-
currentScroll,
|
|
887
|
-
currentSlideIndex: state.currentSlideIndex
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
};
|
|
892
|
-
const smoothScrollTo = (target, customEasing = easing, onComplete) => {
|
|
893
|
-
const start = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
894
|
-
const distance = Math.abs(target - start);
|
|
895
|
-
const duration = Math.min(
|
|
896
|
-
ANIMATION.MAX_DURATION,
|
|
897
|
-
Math.max(ANIMATION.MIN_DURATION, distance / ANIMATION.SPEED_FACTOR)
|
|
898
|
-
);
|
|
899
|
-
const startTime = performance.now();
|
|
900
|
-
const animateScroll = (currentTime) => {
|
|
901
|
-
const elapsed = (currentTime - startTime) / duration;
|
|
902
|
-
const progress = Math.min(elapsed, 1);
|
|
903
|
-
const ease = customEasing(progress);
|
|
904
|
-
const scrollPosition = start + (target - start) * ease;
|
|
905
|
-
if (isVertical) {
|
|
906
|
-
settings.feed.scrollTop = scrollPosition;
|
|
907
|
-
} else {
|
|
908
|
-
settings.feed.scrollLeft = scrollPosition;
|
|
909
|
-
}
|
|
910
|
-
if (progress < 1) {
|
|
911
|
-
requestAnimationFrame(animateScroll);
|
|
912
|
-
} else {
|
|
913
|
-
if (isVertical) {
|
|
914
|
-
settings.feed.scrollTop = target;
|
|
915
|
-
} else {
|
|
916
|
-
settings.feed.scrollLeft = target;
|
|
917
|
-
}
|
|
918
|
-
onComplete?.();
|
|
919
|
-
}
|
|
920
|
-
};
|
|
921
|
-
requestAnimationFrame(animateScroll);
|
|
922
|
-
};
|
|
923
|
-
const handleThumbClick = (thumb) => {
|
|
924
|
-
if (!settings.thumbs) return;
|
|
925
|
-
const index = settings.thumbs.indexOf(thumb);
|
|
926
|
-
if (index === -1 || !settings.slides[index]) return;
|
|
927
|
-
state.currentSlideIndex = index;
|
|
928
|
-
updateActiveThumb(settings.thumbs, index);
|
|
929
|
-
state.isScrolling = true;
|
|
930
|
-
if (state.updateThumbTimeout) {
|
|
931
|
-
clearTimeout(state.updateThumbTimeout);
|
|
932
|
-
}
|
|
933
|
-
state.updateThumbTimeout = setTimeout(() => {
|
|
934
|
-
state.isScrolling = false;
|
|
935
|
-
}, ANIMATION.THUMB_UPDATE_DELAY);
|
|
936
|
-
const targetPosition = isVertical ? settings.slides[index].offsetTop : settings.slides[index].offsetLeft;
|
|
937
|
-
smoothScrollTo(targetPosition);
|
|
938
|
-
};
|
|
939
|
-
const handleNavButtonClick = (navDirection) => {
|
|
940
|
-
const realSlides = loopState.initialized ? loopState.realSlides : getVisibleSlides();
|
|
941
|
-
const slidesToScroll = isDesktop() ? settings.desktopSlidesPerScroll ?? 1 : settings.mobileSlidesPerScroll ?? 1;
|
|
942
|
-
const totalRealSlides = realSlides.length;
|
|
943
|
-
updateCurrentSlideIndex();
|
|
944
|
-
let targetSlide;
|
|
945
|
-
let needsReposition = false;
|
|
946
|
-
if (navDirection === "prev") {
|
|
947
|
-
if (settings.loop && loopState.initialized && state.currentSlideIndex === 0) {
|
|
948
|
-
const prependedClones = loopState.clonedSlides.filter(
|
|
949
|
-
(clone) => clone.getAttribute("data-lazer-clone") === "prepend"
|
|
950
|
-
);
|
|
951
|
-
targetSlide = prependedClones[prependedClones.length - 1];
|
|
952
|
-
needsReposition = true;
|
|
953
|
-
} else {
|
|
954
|
-
state.currentSlideIndex = Math.max(0, state.currentSlideIndex - slidesToScroll);
|
|
955
|
-
targetSlide = realSlides[state.currentSlideIndex];
|
|
956
|
-
}
|
|
957
|
-
} else {
|
|
958
|
-
if (settings.loop && loopState.initialized && state.currentSlideIndex >= totalRealSlides - 1) {
|
|
959
|
-
const appendedClones = loopState.clonedSlides.filter(
|
|
960
|
-
(clone) => clone.getAttribute("data-lazer-clone") === "append"
|
|
961
|
-
);
|
|
962
|
-
targetSlide = appendedClones[0];
|
|
963
|
-
needsReposition = true;
|
|
964
|
-
} else {
|
|
965
|
-
state.currentSlideIndex = Math.min(
|
|
966
|
-
totalRealSlides - 1,
|
|
967
|
-
state.currentSlideIndex + slidesToScroll
|
|
968
|
-
);
|
|
969
|
-
targetSlide = realSlides[state.currentSlideIndex];
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
if (!targetSlide) return;
|
|
973
|
-
const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
974
|
-
settings.onScrollStart?.({
|
|
975
|
-
currentScroll,
|
|
976
|
-
target: targetSlide,
|
|
977
|
-
direction: navDirection
|
|
978
|
-
});
|
|
979
|
-
const targetPosition = isVertical ? targetSlide.offsetTop : targetSlide.offsetLeft;
|
|
980
|
-
if (needsReposition) {
|
|
981
|
-
smoothScrollTo(targetPosition, easing, () => {
|
|
982
|
-
handleLoopReposition(navDirection);
|
|
983
|
-
});
|
|
984
|
-
} else {
|
|
985
|
-
smoothScrollTo(targetPosition);
|
|
986
|
-
}
|
|
987
|
-
};
|
|
988
|
-
const updateScrollPosition = () => {
|
|
989
|
-
updateScrollbarPosition();
|
|
990
|
-
updateControlsVisibility();
|
|
991
|
-
updateCurrentSlideIndex();
|
|
992
|
-
if (!state.isScrolling) {
|
|
993
|
-
updateActiveThumb(settings.thumbs, state.currentSlideIndex);
|
|
994
|
-
}
|
|
995
|
-
if (state.scrollEndTimeout) {
|
|
996
|
-
clearTimeout(state.scrollEndTimeout);
|
|
997
|
-
}
|
|
998
|
-
state.scrollEndTimeout = setTimeout(() => {
|
|
999
|
-
state.isScrolling = false;
|
|
1000
|
-
const currentScroll = isVertical ? settings.feed.scrollTop : settings.feed.scrollLeft;
|
|
1001
|
-
settings.onScrollEnd?.({
|
|
1002
|
-
currentScroll,
|
|
1003
|
-
currentSlideIndex: state.currentSlideIndex
|
|
1004
|
-
});
|
|
1005
|
-
}, ANIMATION.SCROLL_END_DELAY);
|
|
1006
|
-
};
|
|
1007
|
-
const handleFeedScroll = () => {
|
|
1008
|
-
if (!state.ticking) {
|
|
1009
|
-
requestAnimationFrame(() => {
|
|
1010
|
-
updateScrollPosition();
|
|
1011
|
-
state.ticking = false;
|
|
1012
|
-
});
|
|
1013
|
-
state.ticking = true;
|
|
1014
|
-
}
|
|
1015
|
-
};
|
|
1016
|
-
const handleWindowResize = () => {
|
|
1017
|
-
state.cachedFeedRect = null;
|
|
1018
|
-
refresh();
|
|
1019
|
-
};
|
|
1020
|
-
const attachEventListeners = () => {
|
|
1021
|
-
const { signal } = state.abortController;
|
|
1022
|
-
window.addEventListener("resize", handleWindowResize);
|
|
1023
|
-
settings.feed.addEventListener("scroll", handleFeedScroll, {
|
|
1024
|
-
passive: true,
|
|
1025
|
-
signal
|
|
1026
|
-
});
|
|
1027
|
-
if (settings.prevSlideButton) {
|
|
1028
|
-
settings.prevSlideButton.addEventListener(
|
|
1029
|
-
"click",
|
|
1030
|
-
() => handleNavButtonClick("prev"),
|
|
1031
|
-
{ signal }
|
|
1032
|
-
);
|
|
1033
|
-
}
|
|
1034
|
-
if (settings.nextSlideButton) {
|
|
1035
|
-
settings.nextSlideButton.addEventListener(
|
|
1036
|
-
"click",
|
|
1037
|
-
() => handleNavButtonClick("next"),
|
|
1038
|
-
{ signal }
|
|
1039
|
-
);
|
|
1040
|
-
}
|
|
1041
|
-
if (settings.thumbs?.length) {
|
|
1042
|
-
settings.thumbs[0]?.classList.add("active");
|
|
1043
|
-
settings.thumbs.forEach((thumb) => {
|
|
1044
|
-
thumb.addEventListener("click", () => handleThumbClick(thumb), { signal });
|
|
1045
|
-
});
|
|
1046
|
-
}
|
|
1047
|
-
setupKeyboardNavigation(
|
|
1048
|
-
settings.feed,
|
|
1049
|
-
() => handleNavButtonClick("prev"),
|
|
1050
|
-
() => handleNavButtonClick("next"),
|
|
1051
|
-
signal,
|
|
1052
|
-
direction
|
|
1053
|
-
);
|
|
1054
|
-
if (settings.enableDragToScroll !== false) {
|
|
1055
|
-
dragState = setupDragToScroll({
|
|
1056
|
-
feed: settings.feed,
|
|
1057
|
-
slides: settings.slides,
|
|
1058
|
-
abortSignal: signal,
|
|
1059
|
-
smoothScrollTo,
|
|
1060
|
-
onDragEnd: () => {
|
|
1061
|
-
updateCurrentSlideIndex();
|
|
1062
|
-
updateActiveThumb(settings.thumbs, state.currentSlideIndex);
|
|
1063
|
-
},
|
|
1064
|
-
direction
|
|
1065
|
-
});
|
|
1066
|
-
}
|
|
1067
|
-
if (settings.autoplay && settings.pauseOnHover !== false) {
|
|
1068
|
-
settings.feed.addEventListener(
|
|
1069
|
-
"mouseenter",
|
|
1070
|
-
pauseAutoplay,
|
|
1071
|
-
{ signal }
|
|
1072
|
-
);
|
|
1073
|
-
settings.feed.addEventListener(
|
|
1074
|
-
"mouseleave",
|
|
1075
|
-
resumeAutoplay,
|
|
1076
|
-
{ signal }
|
|
1077
|
-
);
|
|
1078
|
-
settings.feed.addEventListener(
|
|
1079
|
-
"touchstart",
|
|
1080
|
-
pauseAutoplay,
|
|
1081
|
-
{ passive: true, signal }
|
|
1082
|
-
);
|
|
1083
|
-
settings.feed.addEventListener(
|
|
1084
|
-
"touchend",
|
|
1085
|
-
resumeAutoplay,
|
|
1086
|
-
{ signal }
|
|
1087
|
-
);
|
|
1088
|
-
}
|
|
1089
|
-
attachMarqueeEventListeners(settings, state, signal);
|
|
1090
|
-
};
|
|
1091
|
-
const startAutoplay = () => {
|
|
1092
|
-
if (state.autoplayIntervalId) return;
|
|
1093
|
-
const interval = settings.autoplayInterval ?? 3e3;
|
|
1094
|
-
state.autoplayIntervalId = setInterval(() => {
|
|
1095
|
-
if (!state.autoplayPaused) {
|
|
1096
|
-
handleNavButtonClick("next");
|
|
1097
|
-
}
|
|
1098
|
-
}, interval);
|
|
1099
|
-
};
|
|
1100
|
-
const stopAutoplay = () => {
|
|
1101
|
-
if (state.autoplayIntervalId) {
|
|
1102
|
-
clearInterval(state.autoplayIntervalId);
|
|
1103
|
-
state.autoplayIntervalId = null;
|
|
1104
|
-
}
|
|
1105
|
-
};
|
|
1106
|
-
const pauseAutoplay = () => {
|
|
1107
|
-
state.autoplayPaused = true;
|
|
1108
|
-
};
|
|
1109
|
-
const resumeAutoplay = () => {
|
|
1110
|
-
state.autoplayPaused = false;
|
|
1111
|
-
};
|
|
1112
|
-
const goToIndex = (index) => {
|
|
1113
|
-
const slides = loopState.initialized ? loopState.realSlides : getVisibleSlides();
|
|
1114
|
-
const safeIndex = Math.max(0, Math.min(index, slides.length - 1));
|
|
1115
|
-
const targetSlide = slides[safeIndex];
|
|
1116
|
-
if (!targetSlide) return;
|
|
1117
|
-
state.currentSlideIndex = safeIndex;
|
|
1118
|
-
updateActiveThumb(settings.thumbs, safeIndex);
|
|
1119
|
-
const targetPosition = isVertical ? targetSlide.offsetTop : targetSlide.offsetLeft;
|
|
1120
|
-
smoothScrollTo(targetPosition);
|
|
1121
|
-
};
|
|
1122
|
-
const refresh = () => {
|
|
1123
|
-
state.cachedFeedRect = null;
|
|
1124
|
-
applySlideWidths();
|
|
1125
|
-
updateScrollbar();
|
|
1126
|
-
updateControlsVisibility();
|
|
1127
|
-
if (settings.marquee && !state.marqueePaused) {
|
|
1128
|
-
startMarquee(settings, state);
|
|
1129
|
-
}
|
|
1130
|
-
};
|
|
1131
|
-
const unload = () => {
|
|
1132
|
-
stopAutoplay();
|
|
1133
|
-
stopMarquee(state, settings);
|
|
1134
|
-
state.abortController.abort();
|
|
1135
|
-
window.removeEventListener("resize", handleWindowResize);
|
|
1136
|
-
if (state.updateThumbTimeout) {
|
|
1137
|
-
clearTimeout(state.updateThumbTimeout);
|
|
1138
|
-
}
|
|
1139
|
-
if (state.scrollEndTimeout) {
|
|
1140
|
-
clearTimeout(state.scrollEndTimeout);
|
|
1141
|
-
}
|
|
1142
|
-
if (dragState) {
|
|
1143
|
-
cleanupDrag(dragState);
|
|
1144
|
-
}
|
|
1145
|
-
cleanupLoopClones();
|
|
1146
|
-
cleanupMarqueeClones(marqueeState);
|
|
1147
|
-
state.cachedFeedRect = null;
|
|
1148
|
-
};
|
|
1149
|
-
initAria(settings);
|
|
1150
|
-
applySlideWidths();
|
|
1151
|
-
if (settings.marquee) {
|
|
1152
|
-
setupMarquee(settings, state, marqueeState);
|
|
1153
|
-
} else {
|
|
1154
|
-
setupLoopClones();
|
|
1155
|
-
if (settings.autoplay) {
|
|
1156
|
-
startAutoplay();
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
updateControlsVisibility();
|
|
1160
|
-
attachEventListeners();
|
|
1161
|
-
updateScrollbar();
|
|
1162
|
-
const play = () => {
|
|
1163
|
-
if (settings.marquee) {
|
|
1164
|
-
if (state.marqueePaused) {
|
|
1165
|
-
resumeMarquee(state, settings);
|
|
1166
|
-
} else {
|
|
1167
|
-
startMarquee(settings, state);
|
|
1168
|
-
}
|
|
1169
|
-
} else if (settings.autoplay) {
|
|
1170
|
-
startAutoplay();
|
|
1171
|
-
}
|
|
1172
|
-
};
|
|
1173
|
-
const pause = () => {
|
|
1174
|
-
if (settings.marquee) {
|
|
1175
|
-
pauseMarquee(state, settings);
|
|
1176
|
-
} else {
|
|
1177
|
-
stopAutoplay();
|
|
1178
|
-
}
|
|
1179
|
-
};
|
|
1180
|
-
return {
|
|
1181
|
-
goToIndex,
|
|
1182
|
-
refresh,
|
|
1183
|
-
unload,
|
|
1184
|
-
play,
|
|
1185
|
-
pause,
|
|
1186
|
-
next: () => handleNavButtonClick("next"),
|
|
1187
|
-
prev: () => handleNavButtonClick("prev")
|
|
1188
|
-
};
|
|
1189
|
-
};
|
|
1190
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1191
|
-
0 && (module.exports = {
|
|
1192
|
-
createSlider,
|
|
1193
|
-
easeInOutCubic,
|
|
1194
|
-
easeOutCubic,
|
|
1195
|
-
easeOutExpo,
|
|
1196
|
-
easeOutQuad,
|
|
1197
|
-
generateBullets,
|
|
1198
|
-
generateThumbs,
|
|
1199
|
-
injectStyles,
|
|
1200
|
-
linear,
|
|
1201
|
-
removeStyles
|
|
1202
|
-
});
|
|
1203
|
-
//# sourceMappingURL=index.cjs.map
|
|
10
|
+
`,document.head.appendChild(t),t},we=(e,t)=>{t.initialized||(t.styleElement=ae(),e.feed.style.display="flex",e.feed.style.willChange="transform",e.slides.forEach(o=>{let l=o.cloneNode(true);l.setAttribute("data-lazer-marquee-clone","true"),l.setAttribute("aria-hidden","true"),e.feed.appendChild(l),t.clonedSlides.push(l);}),t.initialized=true);},se=e=>{e.initialized&&(e.clonedSlides.forEach(t=>{t.remove();}),e.clonedSlides=[],e.initialized=false);},Re=(e,t)=>{ae(),requestAnimationFrame(()=>{let o=e.feed.scrollWidth,l=e.marqueeSpeed??50,r=e.marqueeDirection??"left",a=o/2;if(a<=0||l<=0){console.warn("[lazer-slider] Invalid marquee values:",{distance:a,speed:l,scrollWidth:o});return}let m=`lazer-marquee-scroll ${a/l}s linear infinite ${r==="right"?"reverse":"normal"}`;e.feed.style.animation="none",e.feed.offsetWidth,e.feed.style.animation=m,e.feed.style.animationPlayState=t.marqueePaused?"paused":"running";});},V=(e,t)=>{Re(e,t);},ce=(e,t)=>{t.feed.style.animation="",t.feed.style.animationPlayState="",t.feed.style.transform="",t.feed.style.willChange="";},_=(e,t)=>{e.marqueePaused=true,t.feed.style.animationPlayState="paused";},k=(e,t)=>{e.marqueePaused=false,t.feed.style.animationPlayState="running";},de=(e,t,o)=>{e.marquee&&(we(e,o),V(e,t));},ue=(e,t,o)=>{!e.marquee||e.pauseOnHover===false||(e.feed.addEventListener("mouseenter",()=>_(t,e),{signal:o}),e.feed.addEventListener("mouseleave",()=>k(t,e),{signal:o}),e.feed.addEventListener("touchstart",()=>_(t,e),{passive:true,signal:o}),e.feed.addEventListener("touchend",()=>k(t,e),{signal:o}));};var me=e=>({initialized:false,clonedSlides:[],realSlides:[...e],clonesPerSide:0}),Oe=e=>{let t=O()?e.desktopSlidesPerView:e.mobileSlidesPerView;return !t||t==="auto"?1:Math.ceil(t)},fe=(e,t,o)=>{if(!e.loop||t.initialized)return;let l=t.realSlides,r=Oe(e);t.clonesPerSide=r;for(let a=l.length-r;a<l.length;a++){let i=l[a];if(!i)continue;let s=i.cloneNode(true);s.setAttribute("data-lazer-clone","prepend"),s.setAttribute("aria-hidden","true"),e.feed.insertBefore(s,e.feed.firstChild),t.clonedSlides.push(s);}for(let a=0;a<r;a++){let i=l[a];if(!i)continue;let s=i.cloneNode(true);s.setAttribute("data-lazer-clone","append"),s.setAttribute("aria-hidden","true"),e.feed.appendChild(s),t.clonedSlides.push(s);}requestAnimationFrame(()=>{let a=l[0];a&&(o?e.feed.scrollTop=a.offsetTop:e.feed.scrollLeft=a.offsetLeft);}),t.initialized=true;},U=(e,t,o,l,r,a,i)=>{if(!t.loop||!o.initialized)return;a(true);let s=o.realSlides,m=s.length;if(e==="next"){let u=s[0];u&&(l?t.feed.scrollTop=u.offsetTop:t.feed.scrollLeft=u.offsetLeft),r(0);}else {let u=s[m-1];u&&(l?t.feed.scrollTop=u.offsetTop:t.feed.scrollLeft=u.offsetLeft),r(m-1);}requestAnimationFrame(()=>{requestAnimationFrame(()=>{a(false),i();});});},pe=e=>{e.initialized&&(e.clonedSlides.forEach(t=>{t.remove();}),e.clonedSlides=[],e.initialized=false,e.clonesPerSide=0);};var j=(e,t,o)=>{if(t.autoplayIntervalId)return;let l=e.autoplayInterval??3e3;t.autoplayIntervalId=setInterval(()=>{t.autoplayPaused||o("next");},l);},K=e=>{e.autoplayIntervalId&&(clearInterval(e.autoplayIntervalId),e.autoplayIntervalId=null);},Se=e=>{e.autoplayPaused=true;},he=e=>{e.autoplayPaused=false;},be=(e,t,o)=>{!e.autoplay||e.pauseOnHover===false||(e.feed.addEventListener("mouseenter",()=>Se(t),{signal:o}),e.feed.addEventListener("mouseleave",()=>he(t),{signal:o}),e.feed.addEventListener("touchstart",()=>Se(t),{passive:true,signal:o}),e.feed.addEventListener("touchend",()=>he(t),{signal:o}));};var Q=(e,t,o)=>{if(e.scrollbarThumb)if(o){let l=t.height/e.feed.scrollHeight*100;e.scrollbarThumb.style.height=`${l}%`,e.scrollbarThumb.style.width="";}else {let l=t.width/e.feed.scrollWidth*100;e.scrollbarThumb.style.width=`${l}%`,e.scrollbarThumb.style.height="";}},Te=(e,t)=>{if(!(!e.scrollbarThumb||!e.scrollbarTrack))if(t){let o=e.scrollbarTrack.getBoundingClientRect().height,l=e.scrollbarThumb.getBoundingClientRect().height,r=o-l,a=e.feed.scrollHeight-e.feed.clientHeight,i=a>0?e.feed.scrollTop/a:0;e.scrollbarThumb.style.transform=`translateY(${r*i}px)`;}else {let o=e.scrollbarTrack.getBoundingClientRect().width,l=e.scrollbarThumb.getBoundingClientRect().width,r=o-l,a=e.feed.scrollWidth-e.feed.clientWidth,i=a>0?e.feed.scrollLeft/a:0;e.scrollbarThumb.style.transform=`translateX(${r*i}px)`;}};var J=(e,t,o,l=N,r)=>{let a=o?e.scrollTop:e.scrollLeft,i=Math.abs(t-a),s=Math.min(C.MAX_DURATION,Math.max(C.MIN_DURATION,i/C.SPEED_FACTOR)),m=performance.now(),u=h=>{let n=(h-m)/s,d=Math.min(n,1),v=l(d),b=a+(t-a)*v;o?e.scrollTop=b:e.scrollLeft=b,d<1?requestAnimationFrame(u):(o?e.scrollTop=t:e.scrollLeft=t,r?.());};requestAnimationFrame(u);},W=e=>e.filter(t=>t.offsetParent!==null),Z=(e,t,o)=>{let l=o?e.desktopSlidesPerView:e.mobileSlidesPerView,r=e.slideGap??0;if(r>0&&(e.feed.style.gap=`${r}px`),!l||l==="auto"){e.slides.forEach(s=>{s.style.flex="",s.style.minWidth="",s.style.minHeight="";});return}let i=`calc((100% - ${r*(l-1)}px) / ${l})`;t?e.slides.forEach(s=>{s.style.flex=`0 0 ${i}`,s.style.minHeight=i,s.style.minWidth="";}):e.slides.forEach(s=>{s.style.flex=`0 0 ${i}`,s.style.minWidth=i,s.style.minHeight="";});},ve=(e,t,o,l)=>{let r=l?e.clientHeight:e.clientWidth;return !t||o!==r?{rect:e.getBoundingClientRect(),size:r}:{rect:t,size:o}};var Pe=e=>{if(!e.feed)throw new Error("lazer-slider: feed element is required");if(!e.slides?.length)throw new Error("lazer-slider: slides array is required and must not be empty");if(e.feed.id||(e.feed.id=`lazer-slider-feed-${Math.random().toString(36).substr(2,9)}`),e.bulletsContainer&&!e.thumbs){let c=G({bulletsContainer:e.bulletsContainer,slides:e.slides,bulletClass:e.bulletsClass??"slider-bullet",bulletActiveClass:e.bulletsActiveClass??"active",feedId:e.feed.id});e.thumbs=c;}if(e.thumbsContainer&&!e.thumbs){let c=X({thumbsContainer:e.thumbsContainer,slides:e.slides,thumbClass:e.thumbsClass??"slider-thumb",thumbActiveClass:e.thumbsActiveClass??"active",feedId:e.feed.id,thumbImageSelector:e.thumbImageSelector??"img",thumbSize:e.thumbSize});e.thumbs=c;}let t=e.direction??"horizontal",o=t==="vertical",l=e.easing??N,r={currentSlideIndex:0,isScrolling:false,ticking:false,cachedFeedRect:null,lastWidth:0,updateThumbTimeout:null,scrollEndTimeout:null,abortController:new AbortController,autoplayIntervalId:null,autoplayPaused:false,marqueePaused:false,isLoopRepositioning:false},a=null,i=me(e.slides),s=ne(),m=()=>{let c=ve(e.feed,r.cachedFeedRect,r.lastWidth,o);return r.cachedFeedRect=c.rect,r.lastWidth=c.size,c.rect},u=(c,f)=>{J(e.feed,c,o,l,f);},h=(c,f)=>{J(e.feed,c,o,f??l);},n=()=>{if(r.isLoopRepositioning)return;let c=m(),f,T,S;if(o?(f=e.feed.scrollTop<=1,T=e.feed.scrollTop+c.height>=e.feed.scrollHeight-1,S=e.feed.scrollHeight<=c.height):(f=e.feed.scrollLeft<=1,T=e.feed.scrollLeft+c.width>=e.feed.scrollWidth-1,S=e.feed.scrollWidth<=c.width),e.scrollbarTrack&&(e.scrollbarTrack.style.display=S?"none":"block"),e.loop){z(e.prevSlideButton,!S,e.feed),z(e.nextSlideButton,!S,e.feed);return}z(e.prevSlideButton,!f&&!S,e.feed),z(e.nextSlideButton,!T&&!S,e.feed);},d=()=>{let c=m(),f=i.initialized?i.realSlides:W(e.slides),T=f.filter(S=>{let p=S.getBoundingClientRect(),y=20;return o?p.bottom>c.top+y&&p.top<c.bottom-y:p.right>c.left+y&&p.left<c.right-y});if(T.length&&T[0]){let S=f.indexOf(T[0]);if(S!==-1){r.currentSlideIndex=S;let p=o?e.feed.scrollTop:e.feed.scrollLeft;e.onScroll?.({currentScroll:p,currentSlideIndex:r.currentSlideIndex});}}},v=c=>{if(!e.thumbs)return;let f=e.thumbs.indexOf(c);if(f===-1||!e.slides[f])return;r.currentSlideIndex=f,F(e.thumbs,f),r.isScrolling=true,r.updateThumbTimeout&&clearTimeout(r.updateThumbTimeout),r.updateThumbTimeout=setTimeout(()=>{r.isScrolling=false;},C.THUMB_UPDATE_DELAY);let T=o?e.slides[f].offsetTop:e.slides[f].offsetLeft;u(T);},b=c=>{let f=i.initialized?i.realSlides:W(e.slides),T=O()?e.desktopSlidesPerScroll??1:e.mobileSlidesPerScroll??1,S=f.length;d();let p,y=false;if(c==="prev")if(e.loop&&i.initialized&&r.currentSlideIndex===0){let x=i.clonedSlides.filter(Y=>Y.getAttribute("data-lazer-clone")==="prepend");p=x[x.length-1],y=true;}else r.currentSlideIndex=Math.max(0,r.currentSlideIndex-T),p=f[r.currentSlideIndex];else e.loop&&i.initialized&&r.currentSlideIndex>=S-1?(p=i.clonedSlides.filter(Y=>Y.getAttribute("data-lazer-clone")==="append")[0],y=true):(r.currentSlideIndex=Math.min(S-1,r.currentSlideIndex+T),p=f[r.currentSlideIndex]);if(!p)return;let B=o?e.feed.scrollTop:e.feed.scrollLeft;e.onScrollStart?.({currentScroll:B,target:p,direction:c});let D=o?p.offsetTop:p.offsetLeft;y?u(D,()=>{U(c,e,i,o,x=>{r.currentSlideIndex=x;},x=>{r.isLoopRepositioning=x;},n);}):u(D);},P=()=>{Te(e,o),n(),d(),r.isScrolling||F(e.thumbs,r.currentSlideIndex),r.scrollEndTimeout&&clearTimeout(r.scrollEndTimeout),r.scrollEndTimeout=setTimeout(()=>{r.isScrolling=false;let c=o?e.feed.scrollTop:e.feed.scrollLeft;e.onScrollEnd?.({currentScroll:c,currentSlideIndex:r.currentSlideIndex});},C.SCROLL_END_DELAY);},I=()=>{r.ticking||(requestAnimationFrame(()=>{P(),r.ticking=false;}),r.ticking=true);},q=()=>{r.cachedFeedRect=null,A();},w=()=>{let{signal:c}=r.abortController;window.addEventListener("resize",q),e.feed.addEventListener("scroll",I,{passive:true,signal:c}),e.prevSlideButton&&e.prevSlideButton.addEventListener("click",()=>b("prev"),{signal:c}),e.nextSlideButton&&e.nextSlideButton.addEventListener("click",()=>b("next"),{signal:c}),e.thumbs?.length&&(e.thumbs[0]?.classList.add("active"),e.thumbs.forEach(f=>{f.addEventListener("click",()=>v(f),{signal:c});})),te(e.feed,()=>b("prev"),()=>b("next"),c,t),e.enableDragToScroll!==false&&(a=le({feed:e.feed,slides:e.slides,abortSignal:c,smoothScrollTo:h,onDragEnd:f=>{if(f&&e.loop&&i.initialized){let T=f.getAttribute("data-lazer-clone");if(T){let S=T==="append"?"next":"prev";setTimeout(()=>{U(S,e,i,o,p=>{r.currentSlideIndex=p;},p=>{r.isLoopRepositioning=p;},n);},50);}}d(),F(e.thumbs,r.currentSlideIndex);},direction:t,loop:e.loop})),be(e,r,c),ue(e,r,c);},L=c=>{let f=i.initialized?i.realSlides:W(e.slides),T=Math.max(0,Math.min(c,f.length-1)),S=f[T];if(!S)return;r.currentSlideIndex=T,F(e.thumbs,T);let p=o?S.offsetTop:S.offsetLeft;u(p);},A=()=>{r.cachedFeedRect=null,Z(e,o,O()),Q(e,m(),o),n(),e.marquee&&!r.marqueePaused&&V(e,r);},R=()=>{K(r),ce(r,e),r.abortController.abort(),window.removeEventListener("resize",q),r.updateThumbTimeout&&clearTimeout(r.updateThumbTimeout),r.scrollEndTimeout&&clearTimeout(r.scrollEndTimeout),a&&ie(a),pe(i),se(s),r.cachedFeedRect=null;},E=()=>{e.marquee?r.marqueePaused?k(r,e):V(e,r):e.autoplay&&j(e,r,b);},M=()=>{e.marquee?_(r,e):K(r);};return ee(e),Z(e,o,O()),e.marquee?de(e,r,s):(fe(e,i,o),e.autoplay&&j(e,r,b)),n(),w(),Q(e,m(),o),{goToIndex:L,refresh:A,unload:R,play:E,pause:M,next:()=>b("next"),prev:()=>b("prev")}};exports.createSlider=Pe;exports.easeInOutCubic=ye;exports.easeOutCubic=H;exports.easeOutExpo=N;exports.easeOutQuad=Le;exports.generateBullets=G;exports.generateThumbs=X;exports.linear=ge;
|