@liquidcommercedev/rmn-sdk 1.5.0-beta.18 → 1.5.0-beta.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +681 -205
- package/dist/index.esm.js +681 -205
- package/dist/types/modules/element/template/helper.d.ts +2 -1
- package/dist/types/modules/event/event.interface.d.ts +5 -0
- package/dist/types/modules/event/event.service.d.ts +4 -3
- package/package.json +1 -1
- package/umd/liquidcommerce-rmn-sdk.min.js +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -15945,7 +15945,7 @@ class LocalStorageService {
|
|
|
15945
15945
|
LocalStorageService.localStorageKeyPrefix = 'lc_rmn';
|
|
15946
15946
|
LocalStorageService.localStorageKey = '';
|
|
15947
15947
|
LocalStorageService.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
|
|
15948
|
-
LocalStorageService.encryptData =
|
|
15948
|
+
LocalStorageService.encryptData = false;
|
|
15949
15949
|
|
|
15950
15950
|
/**
|
|
15951
15951
|
* PubsubService class
|
|
@@ -16106,6 +16106,12 @@ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
|
|
|
16106
16106
|
height: ${fluid ? '100%' : `${height}px`};
|
|
16107
16107
|
}
|
|
16108
16108
|
|
|
16109
|
+
.container {
|
|
16110
|
+
position: relative;
|
|
16111
|
+
width: 100%;
|
|
16112
|
+
height: 100%;
|
|
16113
|
+
}
|
|
16114
|
+
|
|
16109
16115
|
.slides {
|
|
16110
16116
|
position: relative;
|
|
16111
16117
|
height: 100%;
|
|
@@ -16301,57 +16307,74 @@ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
|
|
|
16301
16307
|
let CarouselElement;
|
|
16302
16308
|
if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
|
|
16303
16309
|
class CustomCarouselElement extends HTMLElement {
|
|
16310
|
+
/**
|
|
16311
|
+
* Initializes the web component and attaches shadow DOM
|
|
16312
|
+
* Binds all event handlers to maintain proper 'this' context
|
|
16313
|
+
*/
|
|
16304
16314
|
constructor() {
|
|
16305
16315
|
super();
|
|
16306
|
-
this.
|
|
16307
|
-
|
|
16308
|
-
|
|
16309
|
-
|
|
16310
|
-
|
|
16311
|
-
|
|
16312
|
-
|
|
16316
|
+
this.state = {
|
|
16317
|
+
currentSlide: 0,
|
|
16318
|
+
autoplayInterval: null,
|
|
16319
|
+
isAutoplayPaused: false,
|
|
16320
|
+
useDots: false,
|
|
16321
|
+
useButtons: false,
|
|
16322
|
+
autoplay: true,
|
|
16323
|
+
interval: CustomCarouselElement.defaultConfigs.interval,
|
|
16324
|
+
dots: { ...CustomCarouselElement.defaultConfigs.dots },
|
|
16325
|
+
buttons: { ...CustomCarouselElement.defaultConfigs.buttons },
|
|
16326
|
+
isDragging: false,
|
|
16327
|
+
startX: 0,
|
|
16328
|
+
currentX: 0,
|
|
16329
|
+
dragStartTime: 0,
|
|
16330
|
+
dragDistance: 0,
|
|
16331
|
+
containerWidth: 0,
|
|
16332
|
+
isTransitioning: false,
|
|
16333
|
+
realIndex: 0, // Track the actual slide index
|
|
16334
|
+
virtualIndex: 1, // Track the virtual slide position (start at 1 to show clone)
|
|
16335
|
+
isVirtualizing: false, // Flag for handling virtual slide transitions
|
|
16336
|
+
};
|
|
16337
|
+
this.elements = {
|
|
16338
|
+
slidesContainer: null,
|
|
16339
|
+
dots: [],
|
|
16340
|
+
prevButton: null,
|
|
16341
|
+
nextButton: null,
|
|
16342
|
+
};
|
|
16313
16343
|
this.originalFontSizes = new Map();
|
|
16344
|
+
this.cloneToOriginalMap = new WeakMap();
|
|
16314
16345
|
this.attachShadow({ mode: 'open' });
|
|
16346
|
+
this.handleTransitionEnd = this.handleTransitionEnd.bind(this);
|
|
16347
|
+
this.handleTouchStart = this.handleTouchStart.bind(this);
|
|
16348
|
+
this.handleTouchMove = this.handleTouchMove.bind(this);
|
|
16349
|
+
this.handleTouchEnd = this.handleTouchEnd.bind(this);
|
|
16350
|
+
this.handleDragStart = this.handleDragStart.bind(this);
|
|
16351
|
+
this.handleDrag = this.handleDrag.bind(this);
|
|
16352
|
+
this.handleDragEnd = this.handleDragEnd.bind(this);
|
|
16353
|
+
this.updateContainerWidth = this.updateContainerWidth.bind(this);
|
|
16315
16354
|
}
|
|
16355
|
+
/**
|
|
16356
|
+
* Web component lifecycle method called when element is added to DOM
|
|
16357
|
+
* Initializes state, renders UI, sets up events and starts autoplay
|
|
16358
|
+
*/
|
|
16316
16359
|
connectedCallback() {
|
|
16317
|
-
this.
|
|
16318
|
-
this.setupResizeObserver();
|
|
16360
|
+
this.initializeState();
|
|
16319
16361
|
this.render();
|
|
16320
16362
|
this.setupCarousel();
|
|
16363
|
+
this.setupTouchEvents();
|
|
16364
|
+
this.startAutoplayIfEnabled();
|
|
16365
|
+
this.updateContainerWidth();
|
|
16366
|
+
window.addEventListener('resize', this.updateContainerWidth.bind(this));
|
|
16367
|
+
this.setupResizeObserver();
|
|
16321
16368
|
}
|
|
16369
|
+
/**
|
|
16370
|
+
* Web component lifecycle method called when element is removed from DOM
|
|
16371
|
+
* Cleans up all event listeners and intervals
|
|
16372
|
+
*/
|
|
16322
16373
|
disconnectedCallback() {
|
|
16323
16374
|
var _a;
|
|
16324
|
-
this.
|
|
16375
|
+
this.cleanupEventListeners();
|
|
16325
16376
|
(_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
16326
16377
|
}
|
|
16327
|
-
initializeOptions() {
|
|
16328
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
16329
|
-
this.useDots = ((_a = this.data) === null || _a === void 0 ? void 0 : _a.useDots) === true || typeof ((_b = this.data) === null || _b === void 0 ? void 0 : _b.useDots) === 'object';
|
|
16330
|
-
this.useButtons = ((_c = this.data) === null || _c === void 0 ? void 0 : _c.useButtons) === true || typeof ((_d = this.data) === null || _d === void 0 ? void 0 : _d.useButtons) === 'object';
|
|
16331
|
-
this.autoplay = (_f = (_e = this.data) === null || _e === void 0 ? void 0 : _e.autoplay) !== null && _f !== void 0 ? _f : true;
|
|
16332
|
-
this.interval = (_h = (_g = this.data) === null || _g === void 0 ? void 0 : _g.interval) !== null && _h !== void 0 ? _h : CustomCarouselElement.defaultInterval;
|
|
16333
|
-
this.dotsOptions = {
|
|
16334
|
-
position: 'bottom-center',
|
|
16335
|
-
color: '#d9d9d9',
|
|
16336
|
-
activeColor: '#b5914a',
|
|
16337
|
-
size: 'base',
|
|
16338
|
-
opacity: 1,
|
|
16339
|
-
...(typeof ((_j = this.data) === null || _j === void 0 ? void 0 : _j.useDots) === 'object' ? this.data.useDots : {}),
|
|
16340
|
-
};
|
|
16341
|
-
this.buttonsOptions = {
|
|
16342
|
-
together: false,
|
|
16343
|
-
position: 'middle-sides',
|
|
16344
|
-
textColor: '#000000',
|
|
16345
|
-
backgroundColor: '#ffffff',
|
|
16346
|
-
borderRadius: '50%',
|
|
16347
|
-
prev: 'Prev',
|
|
16348
|
-
next: 'Next',
|
|
16349
|
-
size: 'base',
|
|
16350
|
-
opacity: 1,
|
|
16351
|
-
...(typeof ((_k = this.data) === null || _k === void 0 ? void 0 : _k.useButtons) === 'object' ? this.data.useButtons : {}),
|
|
16352
|
-
};
|
|
16353
|
-
this.validateOptions();
|
|
16354
|
-
}
|
|
16355
16378
|
setupResizeObserver() {
|
|
16356
16379
|
if (this.data && !this.data.fluid) {
|
|
16357
16380
|
this.resizeObserver = new ResizeObserverService({
|
|
@@ -16366,18 +16389,20 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
|
|
|
16366
16389
|
}
|
|
16367
16390
|
}
|
|
16368
16391
|
handleCarouselSizeChanged(event) {
|
|
16369
|
-
|
|
16370
|
-
|
|
16371
|
-
// Adjust text elements font size based on the scale factor
|
|
16372
|
-
this.adjustFontSize(event.detail.scale);
|
|
16373
|
-
}
|
|
16392
|
+
// Adjust text elements font size based on the scale factor
|
|
16393
|
+
this.adjustFontSize(event.detail.scale);
|
|
16374
16394
|
}
|
|
16375
16395
|
adjustFontSize(elementScale) {
|
|
16376
16396
|
var _a;
|
|
16377
|
-
const scaleFactor = calculateScaleFactor(elementScale);
|
|
16378
16397
|
// Find all text elements within the shadow root
|
|
16379
|
-
const
|
|
16380
|
-
|
|
16398
|
+
const selectors = ['h1', 'h2', 'h3', 'h4', 'p', 'span']
|
|
16399
|
+
.map((tag) => `[data-spot="iab"] ${tag}`)
|
|
16400
|
+
.join(', ');
|
|
16401
|
+
const elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors);
|
|
16402
|
+
if (!elements)
|
|
16403
|
+
return;
|
|
16404
|
+
const scaleFactor = calculateScaleFactor(elementScale);
|
|
16405
|
+
elements.forEach((element) => {
|
|
16381
16406
|
if (element instanceof HTMLElement) {
|
|
16382
16407
|
if (!this.originalFontSizes.has(element)) {
|
|
16383
16408
|
const originalSize = parseFloat(window.getComputedStyle(element).fontSize);
|
|
@@ -16389,179 +16414,610 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
|
|
|
16389
16414
|
}
|
|
16390
16415
|
});
|
|
16391
16416
|
}
|
|
16417
|
+
/** State and Configuration Management */
|
|
16418
|
+
/**
|
|
16419
|
+
* Initializes all state values from provided configuration
|
|
16420
|
+
* Sets up dots, buttons, autoplay and other feature flags
|
|
16421
|
+
*/
|
|
16422
|
+
initializeState() {
|
|
16423
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
16424
|
+
this.state.useDots = Boolean((_a = this.data) === null || _a === void 0 ? void 0 : _a.useDots);
|
|
16425
|
+
this.state.useButtons = Boolean((_b = this.data) === null || _b === void 0 ? void 0 : _b.useButtons);
|
|
16426
|
+
this.state.autoplay = (_d = (_c = this.data) === null || _c === void 0 ? void 0 : _c.autoplay) !== null && _d !== void 0 ? _d : true;
|
|
16427
|
+
this.state.interval = (_f = (_e = this.data) === null || _e === void 0 ? void 0 : _e.interval) !== null && _f !== void 0 ? _f : CustomCarouselElement.defaultConfigs.interval;
|
|
16428
|
+
if (typeof ((_g = this.data) === null || _g === void 0 ? void 0 : _g.useDots) === 'object') {
|
|
16429
|
+
this.state.dots = { ...this.state.dots, ...this.data.useDots };
|
|
16430
|
+
}
|
|
16431
|
+
if (typeof ((_h = this.data) === null || _h === void 0 ? void 0 : _h.useButtons) === 'object') {
|
|
16432
|
+
this.state.buttons = { ...this.state.buttons, ...this.data.useButtons };
|
|
16433
|
+
}
|
|
16434
|
+
this.validateConfiguration();
|
|
16435
|
+
}
|
|
16436
|
+
/**
|
|
16437
|
+
* Validates all configuration options
|
|
16438
|
+
* Ensures positions and other settings are valid
|
|
16439
|
+
*/
|
|
16440
|
+
validateConfiguration() {
|
|
16441
|
+
this.validatePosition(this.state.dots.position, 'dotsPosition', 'bottom-center');
|
|
16442
|
+
this.validateButtonsPosition();
|
|
16443
|
+
}
|
|
16444
|
+
/**
|
|
16445
|
+
* Validates navigation element positions
|
|
16446
|
+
* Ensures dots and buttons are placed in valid locations
|
|
16447
|
+
*/
|
|
16448
|
+
validatePosition(position, optionName, defaultValue) {
|
|
16449
|
+
if (!CustomCarouselElement.validPositions.has(position)) {
|
|
16450
|
+
console.warn(`Invalid ${optionName}: ${position}. Defaulting to '${defaultValue}'.`);
|
|
16451
|
+
if (optionName === 'dotsPosition') {
|
|
16452
|
+
this.state.dots.position = defaultValue;
|
|
16453
|
+
}
|
|
16454
|
+
else if (optionName === 'buttonsPosition') {
|
|
16455
|
+
this.state.buttons.position = defaultValue;
|
|
16456
|
+
}
|
|
16457
|
+
}
|
|
16458
|
+
}
|
|
16459
|
+
/** Rendering and DOM Management */
|
|
16460
|
+
/**
|
|
16461
|
+
* Main rendering function that builds the carousel structure
|
|
16462
|
+
* Creates slides, dots, and navigation buttons
|
|
16463
|
+
*/
|
|
16392
16464
|
render() {
|
|
16393
|
-
var _a;
|
|
16394
16465
|
if (!this.shadowRoot)
|
|
16395
16466
|
return;
|
|
16396
16467
|
const style = document.createElement('style');
|
|
16397
16468
|
style.textContent = CAROUSEL_COMPONENT_STYLE(this.data);
|
|
16398
16469
|
this.shadowRoot.appendChild(style);
|
|
16399
|
-
const
|
|
16400
|
-
|
|
16401
|
-
|
|
16402
|
-
|
|
16403
|
-
|
|
16404
|
-
|
|
16405
|
-
|
|
16470
|
+
const carouselContainer = document.createElement('div');
|
|
16471
|
+
carouselContainer.className = 'container';
|
|
16472
|
+
const slides = this.createSlides();
|
|
16473
|
+
carouselContainer.appendChild(slides);
|
|
16474
|
+
if (this.state.useDots) {
|
|
16475
|
+
const dots = this.createDots();
|
|
16476
|
+
carouselContainer.appendChild(dots);
|
|
16406
16477
|
}
|
|
16407
|
-
if (this.useButtons) {
|
|
16408
|
-
const buttons = this.
|
|
16409
|
-
|
|
16410
|
-
this.shadowRoot.appendChild(buttons);
|
|
16411
|
-
}
|
|
16412
|
-
}
|
|
16413
|
-
setupCarousel() {
|
|
16414
|
-
this.setupDots();
|
|
16415
|
-
this.setupButtons();
|
|
16416
|
-
if (this.autoplay) {
|
|
16417
|
-
this.startAutoplay();
|
|
16478
|
+
if (this.state.useButtons) {
|
|
16479
|
+
const buttons = this.createButtons();
|
|
16480
|
+
carouselContainer.appendChild(buttons);
|
|
16418
16481
|
}
|
|
16419
|
-
this.
|
|
16482
|
+
this.shadowRoot.appendChild(carouselContainer);
|
|
16483
|
+
this.cacheElements();
|
|
16420
16484
|
}
|
|
16421
|
-
|
|
16485
|
+
/**
|
|
16486
|
+
* Creates the slides container with infinite scroll support
|
|
16487
|
+
* Adds cloned slides at start and end for seamless scrolling
|
|
16488
|
+
*/
|
|
16489
|
+
createSlides() {
|
|
16422
16490
|
const slidesContainer = document.createElement('div');
|
|
16423
16491
|
slidesContainer.className = 'slides';
|
|
16424
|
-
|
|
16425
|
-
|
|
16426
|
-
|
|
16427
|
-
|
|
16428
|
-
|
|
16429
|
-
}
|
|
16430
|
-
slidesContainer.appendChild(slideElement);
|
|
16492
|
+
// Add the last slide at the beginning for seamless backward transition
|
|
16493
|
+
this.appendSlide(slidesContainer, this.slides.length - 1);
|
|
16494
|
+
// Add all slides
|
|
16495
|
+
this.slides.forEach((_, index) => {
|
|
16496
|
+
this.appendSlide(slidesContainer, index);
|
|
16431
16497
|
});
|
|
16498
|
+
// Add the first slide at the end for seamless forward transition
|
|
16499
|
+
this.appendSlide(slidesContainer, 0);
|
|
16500
|
+
// Set initial position to show first real slide
|
|
16501
|
+
slidesContainer.style.transform = 'translateX(-100%)';
|
|
16432
16502
|
return slidesContainer;
|
|
16433
16503
|
}
|
|
16434
|
-
|
|
16504
|
+
/**
|
|
16505
|
+
* Appends a single slide to the container
|
|
16506
|
+
* Handles cloning of slide elements
|
|
16507
|
+
*/
|
|
16508
|
+
appendSlide(container, index) {
|
|
16509
|
+
const slideElement = document.createElement('div');
|
|
16510
|
+
slideElement.className = 'slide';
|
|
16511
|
+
if (this.slides[index] instanceof HTMLElement) {
|
|
16512
|
+
// Clone the slide
|
|
16513
|
+
const clonedSlide = this.slides[index].cloneNode(true);
|
|
16514
|
+
// Map clone to original for event handling
|
|
16515
|
+
this.cloneToOriginalMap.set(clonedSlide, this.slides[index]);
|
|
16516
|
+
// Add event delegation to the slide container
|
|
16517
|
+
slideElement.addEventListener('click', (e) => {
|
|
16518
|
+
const original = this.slides[index];
|
|
16519
|
+
// Dispatch a new event on the original element
|
|
16520
|
+
const cloneEvent = new Event(e.type, { bubbles: true, cancelable: true });
|
|
16521
|
+
original.dispatchEvent(cloneEvent);
|
|
16522
|
+
});
|
|
16523
|
+
slideElement.appendChild(clonedSlide);
|
|
16524
|
+
}
|
|
16525
|
+
container.appendChild(slideElement);
|
|
16526
|
+
}
|
|
16527
|
+
/**
|
|
16528
|
+
* Creates navigation dots based on number of slides
|
|
16529
|
+
* Sets up initial active state and styling
|
|
16530
|
+
*/
|
|
16531
|
+
createDots() {
|
|
16532
|
+
const { position, size, opacity, color } = this.state.dots;
|
|
16435
16533
|
const dotsContainer = document.createElement('div');
|
|
16436
|
-
dotsContainer.className = `dots ${
|
|
16437
|
-
dotsContainer.style.
|
|
16534
|
+
dotsContainer.className = `dots ${position} ${size}`;
|
|
16535
|
+
dotsContainer.style.setProperty('--opacity', opacity.toString());
|
|
16438
16536
|
this.slides.forEach((_, index) => {
|
|
16439
16537
|
const dot = document.createElement('span');
|
|
16440
|
-
dot.className = `dot ${index === this.currentSlide ? 'active' : ''}`;
|
|
16441
|
-
dot.style.backgroundColor =
|
|
16538
|
+
dot.className = `dot ${index === this.state.currentSlide ? 'active' : ''}`;
|
|
16539
|
+
dot.style.backgroundColor = color;
|
|
16442
16540
|
dotsContainer.appendChild(dot);
|
|
16443
16541
|
});
|
|
16444
16542
|
return dotsContainer;
|
|
16445
16543
|
}
|
|
16446
|
-
|
|
16544
|
+
/**
|
|
16545
|
+
* Creates navigation buttons (prev/next)
|
|
16546
|
+
* Applies styling and position configuration
|
|
16547
|
+
*/
|
|
16548
|
+
createButtons() {
|
|
16549
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
16550
|
+
const { together, position, size, opacity } = this.state.buttons;
|
|
16447
16551
|
const buttonsContainer = document.createElement('div');
|
|
16448
|
-
const buttonsClass =
|
|
16449
|
-
|
|
16450
|
-
|
|
16451
|
-
|
|
16452
|
-
|
|
16453
|
-
|
|
16454
|
-
|
|
16455
|
-
buttonsContainer.appendChild(this.prevButton);
|
|
16456
|
-
buttonsContainer.appendChild(this.nextButton);
|
|
16552
|
+
const buttonsClass = together ? `buttons-together ${position}` : 'buttons-separate';
|
|
16553
|
+
buttonsContainer.className = `buttons ${buttonsClass} ${size}`;
|
|
16554
|
+
buttonsContainer.style.setProperty('--opacity', opacity.toString());
|
|
16555
|
+
this.elements.prevButton = this.createButton('prev-button', this.state.buttons.prev);
|
|
16556
|
+
this.elements.nextButton = this.createButton('next-button', this.state.buttons.next);
|
|
16557
|
+
buttonsContainer.appendChild(this.elements.prevButton);
|
|
16558
|
+
buttonsContainer.appendChild(this.elements.nextButton);
|
|
16457
16559
|
return buttonsContainer;
|
|
16458
16560
|
}
|
|
16561
|
+
/**
|
|
16562
|
+
* Creates a single button element with specified properties
|
|
16563
|
+
* Used for both prev and next buttons
|
|
16564
|
+
*/
|
|
16459
16565
|
createButton(className, text) {
|
|
16566
|
+
const { textColor, backgroundColor, borderRadius } = this.state.buttons;
|
|
16460
16567
|
const button = document.createElement('button');
|
|
16461
16568
|
button.className = className;
|
|
16462
16569
|
button.textContent = text;
|
|
16463
|
-
button.style.color =
|
|
16464
|
-
button.style.backgroundColor =
|
|
16465
|
-
button.style.borderRadius =
|
|
16570
|
+
button.style.color = textColor;
|
|
16571
|
+
button.style.backgroundColor = backgroundColor;
|
|
16572
|
+
button.style.borderRadius = borderRadius;
|
|
16466
16573
|
return button;
|
|
16467
16574
|
}
|
|
16468
|
-
|
|
16469
|
-
|
|
16575
|
+
/**
|
|
16576
|
+
* Caches references to important DOM elements
|
|
16577
|
+
* Stores references to slides container, dots, and buttons
|
|
16578
|
+
*/
|
|
16579
|
+
cacheElements() {
|
|
16580
|
+
if (!this.shadowRoot)
|
|
16470
16581
|
return;
|
|
16471
|
-
this.
|
|
16472
|
-
this.
|
|
16473
|
-
|
|
16474
|
-
|
|
16475
|
-
this.resetAutoplay();
|
|
16476
|
-
});
|
|
16477
|
-
});
|
|
16582
|
+
this.elements.slidesContainer = this.shadowRoot.querySelector('.slides');
|
|
16583
|
+
this.elements.dots = Array.from(this.shadowRoot.querySelectorAll('.dot'));
|
|
16584
|
+
this.elements.prevButton = this.shadowRoot.querySelector('.prev-button');
|
|
16585
|
+
this.elements.nextButton = this.shadowRoot.querySelector('.next-button');
|
|
16478
16586
|
}
|
|
16479
|
-
|
|
16480
|
-
|
|
16481
|
-
|
|
16587
|
+
/** Event Management */
|
|
16588
|
+
/**
|
|
16589
|
+
* Sets up all carousel event listeners
|
|
16590
|
+
* Includes dots, buttons, and transition events
|
|
16591
|
+
*/
|
|
16592
|
+
setupCarousel() {
|
|
16593
|
+
this.setupEventListeners();
|
|
16594
|
+
// Add transition event listener with bound method
|
|
16595
|
+
if (this.elements.slidesContainer) {
|
|
16596
|
+
this.elements.slidesContainer.addEventListener('transitionend', this.handleTransitionEnd);
|
|
16597
|
+
}
|
|
16598
|
+
this.updateCarousel(false);
|
|
16599
|
+
}
|
|
16600
|
+
/**
|
|
16601
|
+
* Sets up touch and drag event listeners
|
|
16602
|
+
* Handles both mobile touch and desktop mouse events
|
|
16603
|
+
*/
|
|
16604
|
+
setupTouchEvents() {
|
|
16605
|
+
if (!this.elements.slidesContainer)
|
|
16482
16606
|
return;
|
|
16483
|
-
|
|
16484
|
-
|
|
16485
|
-
|
|
16607
|
+
// Touch events
|
|
16608
|
+
this.elements.slidesContainer.addEventListener('touchstart', this.handleTouchStart, {
|
|
16609
|
+
passive: true,
|
|
16610
|
+
});
|
|
16611
|
+
this.elements.slidesContainer.addEventListener('touchmove', this.handleTouchMove, {
|
|
16612
|
+
passive: false,
|
|
16486
16613
|
});
|
|
16487
|
-
|
|
16488
|
-
|
|
16489
|
-
|
|
16614
|
+
this.elements.slidesContainer.addEventListener('touchend', this.handleTouchEnd);
|
|
16615
|
+
// Mouse drag events
|
|
16616
|
+
this.elements.slidesContainer.addEventListener('mousedown', this.handleDragStart);
|
|
16617
|
+
document.addEventListener('mousemove', this.handleDrag);
|
|
16618
|
+
document.addEventListener('mouseup', this.handleDragEnd);
|
|
16619
|
+
}
|
|
16620
|
+
/**
|
|
16621
|
+
* Removes all event listeners
|
|
16622
|
+
* Called during cleanup and before re-adding listeners
|
|
16623
|
+
*/
|
|
16624
|
+
removeEventListeners() {
|
|
16625
|
+
this.elements.dots.forEach((dot) => {
|
|
16626
|
+
dot.replaceWith(dot.cloneNode(true));
|
|
16490
16627
|
});
|
|
16628
|
+
if (this.elements.prevButton) {
|
|
16629
|
+
this.elements.prevButton.replaceWith(this.elements.prevButton.cloneNode(true));
|
|
16630
|
+
}
|
|
16631
|
+
if (this.elements.nextButton) {
|
|
16632
|
+
this.elements.nextButton.replaceWith(this.elements.nextButton.cloneNode(true));
|
|
16633
|
+
}
|
|
16634
|
+
}
|
|
16635
|
+
/**
|
|
16636
|
+
* Removes touch and drag event listeners
|
|
16637
|
+
* Separate from other event listeners for better management
|
|
16638
|
+
*/
|
|
16639
|
+
removeTouchEvents() {
|
|
16640
|
+
if (!this.elements.slidesContainer)
|
|
16641
|
+
return;
|
|
16642
|
+
this.elements.slidesContainer.removeEventListener('touchstart', this.handleTouchStart);
|
|
16643
|
+
this.elements.slidesContainer.removeEventListener('touchmove', this.handleTouchMove);
|
|
16644
|
+
this.elements.slidesContainer.removeEventListener('touchend', this.handleTouchEnd);
|
|
16645
|
+
this.elements.slidesContainer.removeEventListener('mousedown', this.handleDragStart);
|
|
16646
|
+
document.removeEventListener('mousemove', this.handleDrag);
|
|
16647
|
+
document.removeEventListener('mouseup', this.handleDragEnd);
|
|
16648
|
+
}
|
|
16649
|
+
/** Touch and Drag Event Handlers */
|
|
16650
|
+
/**
|
|
16651
|
+
* Handles start of touch interaction
|
|
16652
|
+
* Initializes drag tracking and pauses autoplay
|
|
16653
|
+
*/
|
|
16654
|
+
handleTouchStart(e) {
|
|
16655
|
+
if (this.state.isTransitioning)
|
|
16656
|
+
return;
|
|
16657
|
+
this.pauseAutoplay();
|
|
16658
|
+
this.state.startX = e.touches[0].clientX;
|
|
16659
|
+
this.state.dragStartTime = Date.now();
|
|
16660
|
+
this.state.isDragging = true;
|
|
16661
|
+
this.state.dragDistance = 0;
|
|
16662
|
+
}
|
|
16663
|
+
/**
|
|
16664
|
+
* Handles touch movement
|
|
16665
|
+
* Calculates drag distance and updates carousel position
|
|
16666
|
+
*/
|
|
16667
|
+
handleTouchMove(e) {
|
|
16668
|
+
if (!this.state.isDragging || this.state.isTransitioning)
|
|
16669
|
+
return;
|
|
16670
|
+
e.preventDefault();
|
|
16671
|
+
const currentX = e.touches[0].clientX;
|
|
16672
|
+
this.state.dragDistance = this.state.startX - currentX;
|
|
16673
|
+
this.updateDragPosition();
|
|
16674
|
+
}
|
|
16675
|
+
/**
|
|
16676
|
+
* Handles end of touch interaction
|
|
16677
|
+
* Determines if slide should change based on drag distance
|
|
16678
|
+
*/
|
|
16679
|
+
handleTouchEnd() {
|
|
16680
|
+
if (!this.state.isDragging)
|
|
16681
|
+
return;
|
|
16682
|
+
const dragDuration = Date.now() - this.state.dragStartTime;
|
|
16683
|
+
const velocity = Math.abs(this.state.dragDistance) / dragDuration;
|
|
16684
|
+
const isQuickSwipe = velocity > 0.5;
|
|
16685
|
+
this.handleDragComplete(isQuickSwipe);
|
|
16686
|
+
setTimeout(() => this.resumeAutoplay(), 100);
|
|
16687
|
+
}
|
|
16688
|
+
/**
|
|
16689
|
+
* Handles start of mouse drag
|
|
16690
|
+
* Similar to touch start but for desktop interaction
|
|
16691
|
+
*/
|
|
16692
|
+
handleDragStart(e) {
|
|
16693
|
+
if (this.state.isTransitioning)
|
|
16694
|
+
return;
|
|
16695
|
+
this.pauseAutoplay();
|
|
16696
|
+
this.state.startX = e.clientX;
|
|
16697
|
+
this.state.dragStartTime = Date.now();
|
|
16698
|
+
this.state.isDragging = true;
|
|
16699
|
+
this.state.dragDistance = 0;
|
|
16700
|
+
e.preventDefault();
|
|
16491
16701
|
}
|
|
16492
|
-
|
|
16493
|
-
|
|
16702
|
+
/**
|
|
16703
|
+
* Handles mouse movement during drag
|
|
16704
|
+
* Updates carousel position during drag
|
|
16705
|
+
*/
|
|
16706
|
+
handleDrag(e) {
|
|
16707
|
+
if (!this.state.isDragging || this.state.isTransitioning)
|
|
16708
|
+
return;
|
|
16709
|
+
const currentX = e.clientX;
|
|
16710
|
+
this.state.dragDistance = this.state.startX - currentX;
|
|
16711
|
+
this.updateDragPosition();
|
|
16712
|
+
}
|
|
16713
|
+
/**
|
|
16714
|
+
* Handles end of mouse drag
|
|
16715
|
+
* Determines final slide position based on drag
|
|
16716
|
+
*/
|
|
16717
|
+
handleDragEnd() {
|
|
16718
|
+
if (!this.state.isDragging)
|
|
16719
|
+
return;
|
|
16720
|
+
const dragDuration = Date.now() - this.state.dragStartTime;
|
|
16721
|
+
const velocity = Math.abs(this.state.dragDistance) / dragDuration;
|
|
16722
|
+
const isQuickSwipe = velocity > 0.5;
|
|
16723
|
+
this.handleDragComplete(isQuickSwipe);
|
|
16724
|
+
setTimeout(() => this.resumeAutoplay(), 100);
|
|
16494
16725
|
}
|
|
16495
|
-
|
|
16496
|
-
|
|
16726
|
+
/** Navigation and Position Management */
|
|
16727
|
+
/**
|
|
16728
|
+
* Handles navigation button clicks and programmatic navigation
|
|
16729
|
+
* Updates both real and virtual indices
|
|
16730
|
+
*/
|
|
16731
|
+
handleNavigation(direction) {
|
|
16732
|
+
if (this.state.isTransitioning)
|
|
16733
|
+
return;
|
|
16734
|
+
const totalSlides = this.slides.length;
|
|
16735
|
+
if (direction === 'next') {
|
|
16736
|
+
this.state.realIndex = (this.state.realIndex + 1) % totalSlides;
|
|
16737
|
+
this.state.virtualIndex++;
|
|
16738
|
+
}
|
|
16739
|
+
else {
|
|
16740
|
+
this.state.realIndex = (this.state.realIndex - 1 + totalSlides) % totalSlides;
|
|
16741
|
+
this.state.virtualIndex--;
|
|
16742
|
+
}
|
|
16743
|
+
this.updateCarousel(true);
|
|
16744
|
+
}
|
|
16745
|
+
/**
|
|
16746
|
+
* Handles dot navigation clicks
|
|
16747
|
+
* Calculates shortest path to target slide
|
|
16748
|
+
*/
|
|
16749
|
+
handleDotClick(index) {
|
|
16750
|
+
const currentRealIndex = this.state.realIndex;
|
|
16751
|
+
const totalSlides = this.slides.length;
|
|
16752
|
+
// Calculate the shortest path to the target slide
|
|
16753
|
+
let diff = index - currentRealIndex;
|
|
16754
|
+
// Adjust for shortest path when crossing the boundary
|
|
16755
|
+
if (Math.abs(diff) > totalSlides / 2) {
|
|
16756
|
+
diff = diff > 0 ? diff - totalSlides : diff + totalSlides;
|
|
16757
|
+
}
|
|
16758
|
+
// Update both real and virtual indices
|
|
16759
|
+
this.state.realIndex = index;
|
|
16760
|
+
this.state.virtualIndex += diff;
|
|
16761
|
+
this.updateCarousel(true);
|
|
16762
|
+
this.resetAutoplay();
|
|
16763
|
+
}
|
|
16764
|
+
/**
|
|
16765
|
+
* Completes a drag interaction
|
|
16766
|
+
* Determines whether to change slides based on drag distance
|
|
16767
|
+
*/
|
|
16768
|
+
handleDragComplete(isQuickSwipe) {
|
|
16769
|
+
this.state.isDragging = false;
|
|
16770
|
+
if (!this.elements.slidesContainer)
|
|
16771
|
+
return;
|
|
16772
|
+
const dragPercentage = this.state.dragDistance / this.state.containerWidth;
|
|
16773
|
+
const threshold = isQuickSwipe ? 0.1 : CustomCarouselElement.defaultConfigs.dragThreshold;
|
|
16774
|
+
if (Math.abs(dragPercentage) > threshold) {
|
|
16775
|
+
if (this.state.dragDistance > 0) {
|
|
16776
|
+
this.handleNavigation('next');
|
|
16777
|
+
}
|
|
16778
|
+
else {
|
|
16779
|
+
this.handleNavigation('prev');
|
|
16780
|
+
}
|
|
16781
|
+
}
|
|
16782
|
+
else {
|
|
16783
|
+
this.updateCarousel(true);
|
|
16784
|
+
}
|
|
16785
|
+
this.state.dragDistance = 0;
|
|
16786
|
+
}
|
|
16787
|
+
/**
|
|
16788
|
+
* Updates carousel position during drag
|
|
16789
|
+
* Applies transform without transition
|
|
16790
|
+
*/
|
|
16791
|
+
updateDragPosition() {
|
|
16792
|
+
if (!this.elements.slidesContainer || this.state.isTransitioning)
|
|
16793
|
+
return;
|
|
16794
|
+
const translateX = -this.state.virtualIndex * 100 -
|
|
16795
|
+
(this.state.dragDistance / this.state.containerWidth) * 100;
|
|
16796
|
+
this.elements.slidesContainer.style.transform = `translateX(${translateX}%)`;
|
|
16797
|
+
this.elements.slidesContainer.style.transition = 'none';
|
|
16497
16798
|
}
|
|
16799
|
+
/**
|
|
16800
|
+
* Navigates to a specific slide
|
|
16801
|
+
* Used primarily by dot navigation
|
|
16802
|
+
*/
|
|
16498
16803
|
goToSlide(index) {
|
|
16499
|
-
this.
|
|
16500
|
-
|
|
16804
|
+
if (this.state.isTransitioning)
|
|
16805
|
+
return;
|
|
16806
|
+
this.state.currentSlide = index;
|
|
16807
|
+
this.updateCarousel(true);
|
|
16501
16808
|
}
|
|
16502
|
-
|
|
16503
|
-
|
|
16809
|
+
/** Transition and Animation Management */
|
|
16810
|
+
/**
|
|
16811
|
+
* Updates carousel position and state
|
|
16812
|
+
* Handles both animated and immediate updates
|
|
16813
|
+
*/
|
|
16814
|
+
updateCarousel(animated = true) {
|
|
16815
|
+
if (!this.elements.slidesContainer)
|
|
16504
16816
|
return;
|
|
16505
|
-
|
|
16506
|
-
|
|
16507
|
-
this.
|
|
16817
|
+
const totalSlides = this.slides.length;
|
|
16818
|
+
// Calculate the translation for the current virtual position
|
|
16819
|
+
const translateX = -this.state.virtualIndex * 100;
|
|
16820
|
+
if (animated) {
|
|
16821
|
+
this.state.isTransitioning = true;
|
|
16822
|
+
this.elements.slidesContainer.style.transition = 'transform 0.3s ease-out';
|
|
16823
|
+
}
|
|
16824
|
+
else {
|
|
16825
|
+
this.elements.slidesContainer.style.transition = 'none';
|
|
16826
|
+
}
|
|
16827
|
+
this.elements.slidesContainer.style.transform = `translateX(${translateX}%)`;
|
|
16508
16828
|
this.updateDots();
|
|
16829
|
+
// Check if we need to reset virtual position
|
|
16830
|
+
if (this.state.virtualIndex <= 0 || this.state.virtualIndex >= totalSlides + 1) {
|
|
16831
|
+
this.state.isVirtualizing = true;
|
|
16832
|
+
}
|
|
16833
|
+
}
|
|
16834
|
+
/**
|
|
16835
|
+
* Handles the end of slide transitions
|
|
16836
|
+
* Manages infinite scroll position reset
|
|
16837
|
+
*/
|
|
16838
|
+
handleTransitionEnd(event) {
|
|
16839
|
+
if (event.target !== this.elements.slidesContainer || event.propertyName !== 'transform')
|
|
16840
|
+
return;
|
|
16841
|
+
this.state.isTransitioning = false;
|
|
16842
|
+
if (this.state.isVirtualizing) {
|
|
16843
|
+
this.resetVirtualPosition();
|
|
16844
|
+
}
|
|
16845
|
+
if (this.elements.slidesContainer) {
|
|
16846
|
+
this.elements.slidesContainer.style.transition = 'none';
|
|
16847
|
+
}
|
|
16848
|
+
}
|
|
16849
|
+
/**
|
|
16850
|
+
* Resets virtual position for infinite scroll
|
|
16851
|
+
* Called after hitting first or last clone
|
|
16852
|
+
*/
|
|
16853
|
+
resetVirtualPosition() {
|
|
16854
|
+
var _a;
|
|
16855
|
+
const totalSlides = this.slides.length;
|
|
16856
|
+
if (this.state.virtualIndex <= 0) {
|
|
16857
|
+
// If we've moved before the first clone, jump to the last real slide
|
|
16858
|
+
this.state.virtualIndex = totalSlides;
|
|
16859
|
+
}
|
|
16860
|
+
else if (this.state.virtualIndex >= totalSlides + 1) {
|
|
16861
|
+
// If we've moved after the last clone, jump to the first real slide
|
|
16862
|
+
this.state.virtualIndex = 1;
|
|
16863
|
+
}
|
|
16864
|
+
if (this.elements.slidesContainer) {
|
|
16865
|
+
this.elements.slidesContainer.style.transition = 'none';
|
|
16866
|
+
this.elements.slidesContainer.style.transform = `translateX(${-this.state.virtualIndex * 100}%)`;
|
|
16867
|
+
}
|
|
16868
|
+
this.state.isVirtualizing = false;
|
|
16869
|
+
// Force reflow to ensure the style change takes effect immediately
|
|
16870
|
+
void ((_a = this.elements.slidesContainer) === null || _a === void 0 ? void 0 : _a.offsetHeight);
|
|
16509
16871
|
}
|
|
16872
|
+
/**
|
|
16873
|
+
* Updates dot active states
|
|
16874
|
+
* Reflects current slide position in navigation
|
|
16875
|
+
*/
|
|
16510
16876
|
updateDots() {
|
|
16511
|
-
if (!this.useDots)
|
|
16877
|
+
if (!this.state.useDots)
|
|
16512
16878
|
return;
|
|
16513
|
-
this.
|
|
16514
|
-
const isActive = index === this.
|
|
16879
|
+
this.elements.dots.forEach((dot, index) => {
|
|
16880
|
+
const isActive = index === this.state.realIndex;
|
|
16515
16881
|
dot.classList.toggle('active', isActive);
|
|
16516
|
-
dot.style.backgroundColor = isActive
|
|
16517
|
-
? this.dotsOptions.activeColor
|
|
16518
|
-
: this.dotsOptions.color;
|
|
16882
|
+
dot.style.backgroundColor = isActive ? this.state.dots.activeColor : this.state.dots.color;
|
|
16519
16883
|
});
|
|
16520
16884
|
}
|
|
16885
|
+
/** Autoplay Management */
|
|
16886
|
+
/**
|
|
16887
|
+
* Starts autoplay if enabled
|
|
16888
|
+
* Called during initialization
|
|
16889
|
+
*/
|
|
16890
|
+
startAutoplayIfEnabled() {
|
|
16891
|
+
if (this.state.autoplay && !this.state.isAutoplayPaused) {
|
|
16892
|
+
this.startAutoplay();
|
|
16893
|
+
}
|
|
16894
|
+
}
|
|
16895
|
+
/**
|
|
16896
|
+
* Starts the autoplay interval
|
|
16897
|
+
* Ensures only one interval is running
|
|
16898
|
+
*/
|
|
16521
16899
|
startAutoplay() {
|
|
16522
|
-
this.
|
|
16900
|
+
this.stopAutoplay(); // Ensure any existing interval is cleared first
|
|
16901
|
+
if (!this.state.isAutoplayPaused && this.state.autoplay) {
|
|
16902
|
+
this.state.autoplayInterval = window.setInterval(() => this.handleNavigation('next'), this.state.interval);
|
|
16903
|
+
}
|
|
16523
16904
|
}
|
|
16905
|
+
/**
|
|
16906
|
+
* Pauses autoplay
|
|
16907
|
+
* Used during user interaction
|
|
16908
|
+
*/
|
|
16909
|
+
pauseAutoplay() {
|
|
16910
|
+
this.state.isAutoplayPaused = true;
|
|
16911
|
+
this.stopAutoplay();
|
|
16912
|
+
}
|
|
16913
|
+
/**
|
|
16914
|
+
* Resumes autoplay after pause
|
|
16915
|
+
* Used after user interaction ends
|
|
16916
|
+
*/
|
|
16917
|
+
resumeAutoplay() {
|
|
16918
|
+
this.state.isAutoplayPaused = false;
|
|
16919
|
+
if (this.state.autoplay) {
|
|
16920
|
+
this.startAutoplay();
|
|
16921
|
+
}
|
|
16922
|
+
}
|
|
16923
|
+
/**
|
|
16924
|
+
* Stops autoplay completely
|
|
16925
|
+
* Cleans up interval
|
|
16926
|
+
*/
|
|
16524
16927
|
stopAutoplay() {
|
|
16525
|
-
if (this.autoplayInterval !== null) {
|
|
16526
|
-
window.clearInterval(this.autoplayInterval);
|
|
16527
|
-
this.autoplayInterval = null;
|
|
16928
|
+
if (this.state.autoplayInterval !== null) {
|
|
16929
|
+
window.clearInterval(this.state.autoplayInterval);
|
|
16930
|
+
this.state.autoplayInterval = null;
|
|
16528
16931
|
}
|
|
16529
16932
|
}
|
|
16933
|
+
/**
|
|
16934
|
+
* Resets autoplay interval
|
|
16935
|
+
* Used after user interactions
|
|
16936
|
+
*/
|
|
16530
16937
|
resetAutoplay() {
|
|
16531
|
-
if (this.autoplay) {
|
|
16938
|
+
if (!this.state.isDragging && this.state.autoplay) {
|
|
16532
16939
|
this.stopAutoplay();
|
|
16533
16940
|
this.startAutoplay();
|
|
16534
16941
|
}
|
|
16535
16942
|
}
|
|
16536
|
-
|
|
16537
|
-
|
|
16538
|
-
|
|
16943
|
+
/** Utility Methods */
|
|
16944
|
+
/**
|
|
16945
|
+
* Updates container width on resize
|
|
16946
|
+
* Used for drag distance calculations
|
|
16947
|
+
*/
|
|
16948
|
+
updateContainerWidth() {
|
|
16949
|
+
if (this.elements.slidesContainer) {
|
|
16950
|
+
this.state.containerWidth = this.elements.slidesContainer.offsetWidth;
|
|
16951
|
+
}
|
|
16539
16952
|
}
|
|
16540
|
-
|
|
16541
|
-
|
|
16542
|
-
|
|
16543
|
-
|
|
16544
|
-
|
|
16545
|
-
|
|
16546
|
-
|
|
16547
|
-
|
|
16548
|
-
|
|
16953
|
+
/**
|
|
16954
|
+
* Cleans up all event listeners
|
|
16955
|
+
* Called during component cleanup
|
|
16956
|
+
*/
|
|
16957
|
+
cleanupEventListeners() {
|
|
16958
|
+
this.stopAutoplay();
|
|
16959
|
+
this.removeEventListeners();
|
|
16960
|
+
this.removeTouchEvents();
|
|
16961
|
+
// Remove transition listener
|
|
16962
|
+
if (this.elements.slidesContainer) {
|
|
16963
|
+
this.elements.slidesContainer.removeEventListener('transitionend', this.handleTransitionEnd);
|
|
16964
|
+
}
|
|
16965
|
+
window.removeEventListener('resize', this.updateContainerWidth);
|
|
16966
|
+
}
|
|
16967
|
+
/**
|
|
16968
|
+
* Sets up all event listeners
|
|
16969
|
+
* Includes dots, buttons, and transition events
|
|
16970
|
+
*/
|
|
16971
|
+
setupEventListeners() {
|
|
16972
|
+
var _a, _b;
|
|
16973
|
+
if (this.state.useDots) {
|
|
16974
|
+
this.elements.dots.forEach((dot, index) => {
|
|
16975
|
+
dot.addEventListener('click', () => this.handleDotClick(index));
|
|
16976
|
+
});
|
|
16977
|
+
}
|
|
16978
|
+
if (this.state.useButtons) {
|
|
16979
|
+
(_a = this.elements.prevButton) === null || _a === void 0 ? void 0 : _a.addEventListener('click', () => this.handleNavigation('prev'));
|
|
16980
|
+
(_b = this.elements.nextButton) === null || _b === void 0 ? void 0 : _b.addEventListener('click', () => this.handleNavigation('next'));
|
|
16549
16981
|
}
|
|
16550
16982
|
}
|
|
16983
|
+
/**
|
|
16984
|
+
* Validates button position configuration
|
|
16985
|
+
* Ensures only valid positions are used
|
|
16986
|
+
*/
|
|
16551
16987
|
validateButtonsPosition() {
|
|
16552
|
-
if (this.useButtons
|
|
16553
|
-
|
|
16554
|
-
|
|
16555
|
-
|
|
16556
|
-
|
|
16557
|
-
console.warn(`Invalid buttonsPosition: ${this.buttonsOptions.position}. When buttons are not together, only 'middle-sides' is allowed. Defaulting to 'middle-sides'.`);
|
|
16558
|
-
this.buttonsOptions.position = 'middle-sides';
|
|
16559
|
-
}
|
|
16988
|
+
if (this.state.useButtons &&
|
|
16989
|
+
!this.state.buttons.together &&
|
|
16990
|
+
this.state.buttons.position !== 'middle-sides') {
|
|
16991
|
+
console.warn('When buttons are not together, only "middle-sides" is allowed. Defaulting to "middle-sides".');
|
|
16992
|
+
this.state.buttons.position = 'middle-sides';
|
|
16560
16993
|
}
|
|
16561
16994
|
}
|
|
16562
16995
|
}
|
|
16563
|
-
|
|
16564
|
-
CustomCarouselElement.
|
|
16996
|
+
/** Core initialization and lifecycle methods */
|
|
16997
|
+
CustomCarouselElement.defaultConfigs = {
|
|
16998
|
+
interval: 5000,
|
|
16999
|
+
touchThreshold: 50, // minimum swipe distance in pixels
|
|
17000
|
+
dragThreshold: 0.2, // minimum drag percentage of carousel width
|
|
17001
|
+
dots: {
|
|
17002
|
+
position: 'bottom-center',
|
|
17003
|
+
color: '#d9d9d9',
|
|
17004
|
+
activeColor: '#b5914a',
|
|
17005
|
+
size: 'base',
|
|
17006
|
+
opacity: 1,
|
|
17007
|
+
},
|
|
17008
|
+
buttons: {
|
|
17009
|
+
together: false,
|
|
17010
|
+
position: 'middle-sides',
|
|
17011
|
+
textColor: '#000000',
|
|
17012
|
+
backgroundColor: '#ffffff',
|
|
17013
|
+
borderRadius: '50%',
|
|
17014
|
+
prev: 'Prev',
|
|
17015
|
+
next: 'Next',
|
|
17016
|
+
size: 'base',
|
|
17017
|
+
opacity: 1,
|
|
17018
|
+
},
|
|
17019
|
+
};
|
|
17020
|
+
CustomCarouselElement.validPositions = new Set([
|
|
16565
17021
|
'top-left',
|
|
16566
17022
|
'top-center',
|
|
16567
17023
|
'top-right',
|
|
@@ -16571,7 +17027,7 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
|
|
|
16571
17027
|
'middle-left',
|
|
16572
17028
|
'middle-right',
|
|
16573
17029
|
'middle-sides',
|
|
16574
|
-
];
|
|
17030
|
+
]);
|
|
16575
17031
|
CarouselElement = CustomCarouselElement;
|
|
16576
17032
|
}
|
|
16577
17033
|
|
|
@@ -16726,19 +17182,20 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
|
|
|
16726
17182
|
* #########################################################
|
|
16727
17183
|
*/
|
|
16728
17184
|
handleSpotSizeChanged(event) {
|
|
16729
|
-
|
|
16730
|
-
|
|
16731
|
-
if (!isRBSpot) {
|
|
16732
|
-
// Adjust text elements font size based on the scale factor
|
|
16733
|
-
this.adjustFontSize(event.detail.scale);
|
|
16734
|
-
}
|
|
17185
|
+
// Adjust text elements font size based on the scale factor
|
|
17186
|
+
this.adjustFontSize(event.detail.scale);
|
|
16735
17187
|
}
|
|
16736
17188
|
adjustFontSize(elementScale) {
|
|
16737
17189
|
var _a;
|
|
16738
|
-
const scaleFactor = calculateScaleFactor(elementScale);
|
|
16739
17190
|
// Find all text elements within the shadow root
|
|
16740
|
-
const
|
|
16741
|
-
|
|
17191
|
+
const selectors = ['h1', 'h2', 'h3', 'h4', 'p', 'span']
|
|
17192
|
+
.map((tag) => `[data-spot="iab"] ${tag}`)
|
|
17193
|
+
.join(', ');
|
|
17194
|
+
const elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors);
|
|
17195
|
+
if (!elements)
|
|
17196
|
+
return;
|
|
17197
|
+
const scaleFactor = calculateScaleFactor(elementScale);
|
|
17198
|
+
elements.forEach((element) => {
|
|
16742
17199
|
if (element instanceof HTMLElement) {
|
|
16743
17200
|
if (!this.originalFontSizes.has(element)) {
|
|
16744
17201
|
const originalSize = parseFloat(window.getComputedStyle(element).fontSize);
|
|
@@ -16923,8 +17380,9 @@ function generateGradientColor(overlay, fallback = '') {
|
|
|
16923
17380
|
const gradientColor = convertHexToRgba(color, overlayOpacity);
|
|
16924
17381
|
return `${fullColor} 0%, ${gradientColor} ${goTo}%, ${transparentColor} 100%`;
|
|
16925
17382
|
}
|
|
16926
|
-
function spotHtmlStringToElement(htmlString) {
|
|
17383
|
+
function spotHtmlStringToElement(htmlString, spotType) {
|
|
16927
17384
|
const spot = document.createElement('div');
|
|
17385
|
+
spot.setAttribute('data-spot', spotType.startsWith('rb') ? 'rb' : 'iab');
|
|
16928
17386
|
spot.className = 'spot';
|
|
16929
17387
|
spot.innerHTML = htmlString;
|
|
16930
17388
|
Object.assign(spot.style, {
|
|
@@ -17856,6 +18314,10 @@ const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
|
|
|
17856
18314
|
.${prefix} {
|
|
17857
18315
|
background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
|
|
17858
18316
|
}
|
|
18317
|
+
|
|
18318
|
+
.${prefix}__text {
|
|
18319
|
+
width: 70%;
|
|
18320
|
+
}
|
|
17859
18321
|
}
|
|
17860
18322
|
|
|
17861
18323
|
@container (min-width: 768px) {
|
|
@@ -18678,7 +19140,7 @@ const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
|
|
|
18678
19140
|
// Generate a highly unique prefix to avoid conflicts with other elements.
|
|
18679
19141
|
const prefix = 's' + UniqueIdGenerator.generate().toLowerCase();
|
|
18680
19142
|
const spotHtmlString = variantTemplate(spot, { ...config, prefix });
|
|
18681
|
-
return spotHtmlStringToElement(spotHtmlString);
|
|
19143
|
+
return spotHtmlStringToElement(spotHtmlString, spot.spot);
|
|
18682
19144
|
};
|
|
18683
19145
|
|
|
18684
19146
|
// For the moment, we will only focus on sites that use Google Analytics,
|
|
@@ -18873,18 +19335,21 @@ class EventService {
|
|
|
18873
19335
|
}
|
|
18874
19336
|
registerSpot(params) {
|
|
18875
19337
|
const { placementId, spot, spotElement } = params;
|
|
18876
|
-
this.activeSpots.set(
|
|
18877
|
-
//
|
|
18878
|
-
this.
|
|
18879
|
-
// Handle
|
|
18880
|
-
|
|
18881
|
-
|
|
18882
|
-
|
|
19338
|
+
this.activeSpots.set(spot.id, { placementId, spotElement });
|
|
19339
|
+
// Handle Impression Event
|
|
19340
|
+
this.handleImpressionEvent(placementId, spot);
|
|
19341
|
+
// Handle Click Event
|
|
19342
|
+
spotElement.addEventListener('click', async () => {
|
|
19343
|
+
await this.handleClickEvent(params);
|
|
19344
|
+
});
|
|
19345
|
+
// Handle Intersection Observer
|
|
19346
|
+
this.handleIntersectionObserver(placementId, spotElement);
|
|
18883
19347
|
}
|
|
18884
19348
|
unregisterSpot(placementId) {
|
|
18885
19349
|
const placementIdClean = placementId.replace('#', '');
|
|
18886
|
-
const
|
|
18887
|
-
|
|
19350
|
+
const spotId = this.getSpotIdByPlacementId(this.activeSpots, placementIdClean);
|
|
19351
|
+
const data = this.activeSpots.get(placementIdClean !== null && placementIdClean !== void 0 ? placementIdClean : '');
|
|
19352
|
+
if (!spotId || !data) {
|
|
18888
19353
|
this.handleSpotState(placementIdClean, {
|
|
18889
19354
|
state: {
|
|
18890
19355
|
error: `Active spot with placementId ${placementIdClean} not found.`,
|
|
@@ -18892,7 +19357,7 @@ class EventService {
|
|
|
18892
19357
|
});
|
|
18893
19358
|
return;
|
|
18894
19359
|
}
|
|
18895
|
-
this.intersectionObserver.unobserve(
|
|
19360
|
+
this.intersectionObserver.unobserve(data.spotElement);
|
|
18896
19361
|
this.handleSpotState(placementIdClean, {
|
|
18897
19362
|
dom: {
|
|
18898
19363
|
spotElement: undefined,
|
|
@@ -18901,9 +19366,10 @@ class EventService {
|
|
|
18901
19366
|
state: {
|
|
18902
19367
|
unmounted: true,
|
|
18903
19368
|
mounted: false,
|
|
19369
|
+
loading: false,
|
|
18904
19370
|
},
|
|
18905
19371
|
});
|
|
18906
|
-
this.activeSpots.delete(
|
|
19372
|
+
this.activeSpots.delete(spotId);
|
|
18907
19373
|
const placementElement = document.getElementById(placementIdClean);
|
|
18908
19374
|
if (!placementElement) {
|
|
18909
19375
|
this.handleSpotState(placementIdClean, {
|
|
@@ -18956,19 +19422,7 @@ class EventService {
|
|
|
18956
19422
|
this.pubSubService.publish(RMN_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
|
|
18957
19423
|
}
|
|
18958
19424
|
}
|
|
18959
|
-
|
|
18960
|
-
return {
|
|
18961
|
-
identifier: updates.identifier
|
|
18962
|
-
? { ...current.identifier, ...updates.identifier }
|
|
18963
|
-
: current.identifier,
|
|
18964
|
-
dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
|
|
18965
|
-
state: updates.state ? { ...current.state, ...updates.state } : current.state,
|
|
18966
|
-
displayConfig: updates.displayConfig
|
|
18967
|
-
? { ...current.displayConfig, ...updates.displayConfig }
|
|
18968
|
-
: current.displayConfig,
|
|
18969
|
-
};
|
|
18970
|
-
}
|
|
18971
|
-
async handleClick({ placementId, spot }) {
|
|
19425
|
+
async handleClickEvent({ placementId, spot }) {
|
|
18972
19426
|
var _a, _b, _c;
|
|
18973
19427
|
this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
|
|
18974
19428
|
eventType: RMN_SPOT_EVENT.CLICK,
|
|
@@ -18985,22 +19439,10 @@ class EventService {
|
|
|
18985
19439
|
spotId: spot.id,
|
|
18986
19440
|
spotType: spot.spot,
|
|
18987
19441
|
events: spot.events,
|
|
18988
|
-
productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [
|
|
19442
|
+
productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [],
|
|
18989
19443
|
});
|
|
18990
19444
|
}
|
|
18991
|
-
|
|
18992
|
-
const spotIsVisibleCallback = async () => {
|
|
18993
|
-
this.intersectionObserver.unobserve(spotElement);
|
|
18994
|
-
this.handleSpotState(placementId, {
|
|
18995
|
-
dom: {
|
|
18996
|
-
spotElement,
|
|
18997
|
-
visibleOnViewport: true,
|
|
18998
|
-
},
|
|
18999
|
-
});
|
|
19000
|
-
};
|
|
19001
|
-
this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
|
|
19002
|
-
}
|
|
19003
|
-
fireImpressionEvent(placementId, spot) {
|
|
19445
|
+
handleImpressionEvent(placementId, spot) {
|
|
19004
19446
|
this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
|
|
19005
19447
|
eventType: RMN_SPOT_EVENT.IMPRESSION,
|
|
19006
19448
|
placementId,
|
|
@@ -19014,6 +19456,38 @@ class EventService {
|
|
|
19014
19456
|
});
|
|
19015
19457
|
})();
|
|
19016
19458
|
}
|
|
19459
|
+
getSpotIdByPlacementId(activeSpots, searchPlacementId) {
|
|
19460
|
+
for (const [spotId, spotData] of activeSpots) {
|
|
19461
|
+
if (spotData.placementId === searchPlacementId) {
|
|
19462
|
+
return spotId;
|
|
19463
|
+
}
|
|
19464
|
+
}
|
|
19465
|
+
return null;
|
|
19466
|
+
}
|
|
19467
|
+
deepMerge(current, updates) {
|
|
19468
|
+
return {
|
|
19469
|
+
identifier: updates.identifier
|
|
19470
|
+
? { ...current.identifier, ...updates.identifier }
|
|
19471
|
+
: current.identifier,
|
|
19472
|
+
dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
|
|
19473
|
+
state: updates.state ? { ...current.state, ...updates.state } : current.state,
|
|
19474
|
+
displayConfig: updates.displayConfig
|
|
19475
|
+
? { ...current.displayConfig, ...updates.displayConfig }
|
|
19476
|
+
: current.displayConfig,
|
|
19477
|
+
};
|
|
19478
|
+
}
|
|
19479
|
+
handleIntersectionObserver(placementId, spotElement) {
|
|
19480
|
+
const spotIsVisibleCallback = async () => {
|
|
19481
|
+
this.intersectionObserver.unobserve(spotElement);
|
|
19482
|
+
this.handleSpotState(placementId, {
|
|
19483
|
+
dom: {
|
|
19484
|
+
spotElement,
|
|
19485
|
+
visibleOnViewport: true,
|
|
19486
|
+
},
|
|
19487
|
+
});
|
|
19488
|
+
};
|
|
19489
|
+
this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
|
|
19490
|
+
}
|
|
19017
19491
|
}
|
|
19018
19492
|
|
|
19019
19493
|
const SELECTION_API_PATH = '/spots/selection';
|
|
@@ -19790,6 +20264,8 @@ class LiquidCommerceRmnClient {
|
|
|
19790
20264
|
this.eventService.handleSpotState(item.placementId, {
|
|
19791
20265
|
state: {
|
|
19792
20266
|
error: `Placement not found for id "${item.placementId}".`,
|
|
20267
|
+
mounted: false,
|
|
20268
|
+
loading: false,
|
|
19793
20269
|
},
|
|
19794
20270
|
});
|
|
19795
20271
|
continue;
|