@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.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 = true;
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.currentSlide = 0;
16307
- this.dotElements = [];
16308
- this.prevButton = null;
16309
- this.nextButton = null;
16310
- this.autoplayInterval = null;
16311
- this.useDots = false;
16312
- this.useButtons = false;
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.initializeOptions();
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.stopAutoplay();
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
- const isRBSpot = 'rbHeroadaksda'.startsWith('rb');
16370
- if (!isRBSpot) {
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 elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('h1, h2, h3, h4, p, span');
16380
- elements === null || elements === void 0 ? void 0 : elements.forEach((element) => {
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 slides = this.renderSlides();
16400
- this.shadowRoot.appendChild(slides);
16401
- this.slidesContainer = (_a = this.shadowRoot.querySelector('.slides')) !== null && _a !== void 0 ? _a : undefined;
16402
- if (this.useDots) {
16403
- const dots = this.renderDots();
16404
- if (dots)
16405
- this.shadowRoot.appendChild(dots);
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.renderButtons();
16409
- if (buttons)
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.updateCarousel();
16482
+ this.shadowRoot.appendChild(carouselContainer);
16483
+ this.cacheElements();
16420
16484
  }
16421
- renderSlides() {
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
- this.slides.forEach((slide) => {
16425
- const slideElement = document.createElement('div');
16426
- slideElement.className = 'slide';
16427
- if (slide instanceof HTMLElement) {
16428
- slideElement.appendChild(slide);
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
- renderDots() {
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 ${this.dotsOptions.position} ${this.dotsOptions.size}`;
16437
- dotsContainer.style.cssText = `--opacity: ${this.dotsOptions.opacity}`;
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 = this.dotsOptions.color;
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
- renderButtons() {
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 = this.buttonsOptions.together
16449
- ? `buttons-together ${this.buttonsOptions.position}`
16450
- : 'buttons-separate';
16451
- buttonsContainer.className = `buttons ${buttonsClass} ${this.buttonsOptions.size}`;
16452
- buttonsContainer.style.cssText = `--opacity: ${this.buttonsOptions.opacity}`;
16453
- this.prevButton = this.createButton('prev-button', this.buttonsOptions.prev);
16454
- this.nextButton = this.createButton('next-button', this.buttonsOptions.next);
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 = this.buttonsOptions.textColor;
16464
- button.style.backgroundColor = this.buttonsOptions.backgroundColor;
16465
- button.style.borderRadius = this.buttonsOptions.borderRadius;
16570
+ button.style.color = textColor;
16571
+ button.style.backgroundColor = backgroundColor;
16572
+ button.style.borderRadius = borderRadius;
16466
16573
  return button;
16467
16574
  }
16468
- setupDots() {
16469
- if (!this.shadowRoot || !this.useDots)
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.dotElements = Array.from(this.shadowRoot.querySelectorAll('.dot'));
16472
- this.dotElements.forEach((dot, index) => {
16473
- dot.addEventListener('click', () => {
16474
- this.goToSlide(index);
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
- setupButtons() {
16480
- var _a, _b;
16481
- if (!this.useButtons)
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
- (_a = this.prevButton) === null || _a === void 0 ? void 0 : _a.addEventListener('click', () => {
16484
- this.prevSlide();
16485
- this.resetAutoplay();
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
- (_b = this.nextButton) === null || _b === void 0 ? void 0 : _b.addEventListener('click', () => {
16488
- this.nextSlide();
16489
- this.resetAutoplay();
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
- nextSlide() {
16493
- this.goToSlide((this.currentSlide + 1) % this.slides.length);
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
- prevSlide() {
16496
- this.goToSlide((this.currentSlide - 1 + this.slides.length) % this.slides.length);
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.currentSlide = index;
16500
- this.updateCarousel();
16804
+ if (this.state.isTransitioning)
16805
+ return;
16806
+ this.state.currentSlide = index;
16807
+ this.updateCarousel(true);
16501
16808
  }
16502
- updateCarousel() {
16503
- if (!this.slidesContainer)
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
- // Calculate the translation distance based on current slide
16506
- const translateX = -this.currentSlide * 100;
16507
- this.slidesContainer.style.transform = `translateX(${translateX}%)`;
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.dotElements.forEach((dot, index) => {
16514
- const isActive = index === this.currentSlide;
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.autoplayInterval = window.setInterval(() => this.nextSlide(), this.interval);
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
- validateOptions() {
16537
- this.validatePosition(this.dotsOptions.position, 'dotsPosition', 'bottom-center');
16538
- this.validateButtonsPosition();
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
- validatePosition(position, optionName, defaultValue) {
16541
- if (!CustomCarouselElement.validPositions.includes(position)) {
16542
- console.warn(`Invalid ${optionName}: ${position}. Defaulting to '${defaultValue}'.`);
16543
- if (optionName === 'dotsPosition') {
16544
- this.dotsOptions.position = defaultValue;
16545
- }
16546
- else if (optionName === 'buttonsPosition') {
16547
- this.buttonsOptions.position = defaultValue;
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
- if (this.buttonsOptions.together) {
16554
- this.validatePosition(this.buttonsOptions.position, 'buttonsPosition', 'bottom-center');
16555
- }
16556
- else if (this.buttonsOptions.position !== 'middle-sides') {
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
- CustomCarouselElement.defaultInterval = 5000;
16564
- CustomCarouselElement.validPositions = [
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
- var _a, _b, _c;
16730
- const isRBSpot = (_c = (_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a.spot) === null || _b === void 0 ? void 0 : _b.startsWith('rb')) !== null && _c !== void 0 ? _c : false;
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 elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('h1, h2, h3, h4, p, span');
16741
- elements === null || elements === void 0 ? void 0 : elements.forEach((element) => {
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(placementId, { spotElement });
18877
- // Fire impression event
18878
- this.fireImpressionEvent(placementId, spot);
18879
- // Handle intersection observer
18880
- this.handleIntersectionObserver(placementId, spot, spotElement);
18881
- // Attach click event listener
18882
- spotElement.addEventListener('click', async () => await this.handleClick(params));
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 spotData = this.activeSpots.get(placementIdClean);
18887
- if (!spotData) {
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(spotData.spotElement);
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(placementIdClean);
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
- deepMerge(current, updates) {
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 : [1, 2, 3],
19442
+ productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [],
18989
19443
  });
18990
19444
  }
18991
- handleIntersectionObserver(placementId, _spot, spotElement) {
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;