@liquidcommercedev/rmn-sdk 1.5.0-beta.12 → 1.5.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -6866,40 +6866,60 @@ class ObjectHelper {
6866
6866
  /**
6867
6867
  * Recursively extracts ID values from a nested data structure.
6868
6868
  * Searches for specified property names and collects their primitive values (strings/numbers).
6869
+ * Captures properties ending with 'id' and any additional specified property names.
6869
6870
  *
6870
6871
  * @param data - The data structure to search through (can be nested objects/arrays)
6871
- * @param propertyNames - Array of property names to look for
6872
+ * @param propertyNames - Array of additional property names to look for (optional)
6872
6873
  * @returns Array of extracted ID values (strings/numbers only)
6873
6874
  *
6874
6875
  * @example
6875
6876
  * const data = {
6876
6877
  * id: [1, 2, 3],
6877
- * nested: { id: 'abc' },
6878
- * items: [{ id: 456 }]
6878
+ * nested: { id: 'abc', userId: 123 },
6879
+ * items: [{ id: 456, productId: '789', sku: 'ABC123' }]
6879
6880
  * };
6880
- * extractDeepIds(data); // Returns [1, 2, 3, 'abc', 456]
6881
+ * extractDeepIds(data); // Returns [1, 2, 3, 'abc', 123, 456, '789', 'ABC123']
6881
6882
  */
6882
6883
  function extractDeepIds(data, propertyNames) {
6883
6884
  const ids = [];
6884
- const defaulPropertyNames = [
6885
- 'id',
6886
- 'upc',
6887
- 'groupingId',
6888
- 'sku',
6889
- 'productId',
6890
- 'item_id',
6891
- 'isbn',
6892
- 'asin',
6893
- 'mpn',
6885
+ const defaultPropertyNames = [
6886
+ // Universal product identifiers
6887
+ 'gtin', // Global Trade Item Number
6888
+ 'gtin8', // 8-digit GTIN
6889
+ 'gtin12', // 12-digit GTIN (UPC)
6890
+ 'gtin13', // 13-digit GTIN (EAN)
6891
+ 'gtin14', // 14-digit GTIN
6892
+ 'mpn', // Manufacturer Part Number
6893
+ 'sku', // Stock Keeping Unit
6894
+ 'upc', // Universal Product Code
6895
+ 'ean', // European Article Number
6896
+ 'isbn', // International Standard Book Number
6897
+ 'isbn10', // 10-digit ISBN
6898
+ 'isbn13', // 13-digit ISBN
6899
+ 'asin', // Amazon Standard Identification Number
6900
+ // Product codes and references
6901
+ 'coupon',
6902
+ 'barcode',
6903
+ 'product_code',
6904
+ 'part_number',
6894
6905
  'model_number',
6895
- 'article_number',
6896
- 'variant_id',
6906
+ 'item_variant',
6897
6907
  'item_number',
6898
- 'catalog_id',
6899
- 'reference_id',
6908
+ 'article_number',
6909
+ 'reference',
6910
+ 'groupingId',
6900
6911
  ];
6901
- // Set for faster property name lookups
6902
- const propertySet = new Set(defaulPropertyNames);
6912
+ // Convert property names to lowercase for consistent comparison
6913
+ const additionalProperties = new Set((defaultPropertyNames).map((name) => name.toLowerCase()));
6914
+ /**
6915
+ * Checks if a property name is an ID field
6916
+ * @param key - The property name to check
6917
+ * @returns boolean indicating if the key is an ID field
6918
+ */
6919
+ const isIdField = (key) => {
6920
+ const lowercaseKey = key.toLowerCase();
6921
+ return lowercaseKey.endsWith('id') || additionalProperties.has(lowercaseKey);
6922
+ };
6903
6923
  /**
6904
6924
  * Processes a value and extracts IDs if it matches criteria
6905
6925
  * @param value - The value to process
@@ -6910,7 +6930,7 @@ function extractDeepIds(data, propertyNames) {
6910
6930
  if (value == null)
6911
6931
  return;
6912
6932
  // If current key matches our target properties
6913
- if (currentKey && propertySet.has(currentKey)) {
6933
+ if (currentKey && isIdField(currentKey)) {
6914
6934
  if (Array.isArray(value)) {
6915
6935
  // Filter and push valid array values in one pass
6916
6936
  ids.push(...value.filter((item) => typeof item === 'string' || typeof item === 'number'));
@@ -6932,7 +6952,7 @@ function extractDeepIds(data, propertyNames) {
6932
6952
  }
6933
6953
  };
6934
6954
  processValue(data);
6935
- return ids; // No need to filter nulls as we handle that during collection
6955
+ return ids;
6936
6956
  }
6937
6957
  // Fallback method using fetch if sendBeacon isn't available
6938
6958
  async function fallbackEventFire(url) {
@@ -16125,6 +16145,7 @@ class AuthService extends BaseApi {
16125
16145
 
16126
16146
  const SPOT_ELEMENT_TAG = 'spot-element';
16127
16147
  const CAROUSEL_ELEMENT_TAG = 'spot-carousel-element';
16148
+ const SKELETON_ELEMENT_TAG = 'spot-skeleton-element';
16128
16149
  const GFONT_PRECONNECT = `
16129
16150
  <link rel="preconnect" href="https://fonts.googleapis.com">
16130
16151
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@@ -16135,6 +16156,45 @@ const GFONT_SOURCE_SANS_3 = `
16135
16156
  const GFONT_CORMORANT = `
16136
16157
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Cormorant:ital,wght@0,300..700;1,300..700&family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap">
16137
16158
  `;
16159
+ const SPOT_DIMENSIONS = {
16160
+ [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO]: { width: 1140, height: 640 },
16161
+ [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE]: { width: 1140, height: 640 },
16162
+ [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE]: { width: 1140, height: 640 },
16163
+ [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE]: { width: 1140, height: 640 },
16164
+ [exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT]: { width: 468, height: 410 },
16165
+ [exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT]: { width: 224, height: 378 },
16166
+ [exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT]: { width: 224, height: 410 },
16167
+ [exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK]: { width: 887, height: 344 },
16168
+ [exports.RMN_SPOT_TYPE.RB_PRODUCT_UPCS]: { width: 1, height: 1 },
16169
+ [exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER]: { width: 440, height: 220 },
16170
+ [exports.RMN_SPOT_TYPE.SMALL_RECTANGLE]: { width: 180, height: 150 },
16171
+ [exports.RMN_SPOT_TYPE.MEDIUM_RECTANGLE]: { width: 300, height: 250 },
16172
+ [exports.RMN_SPOT_TYPE.LARGE_RECTANGLE]: { width: 336, height: 280 },
16173
+ [exports.RMN_SPOT_TYPE.VERTICAL_RECTANGLE]: { width: 240, height: 400 },
16174
+ [exports.RMN_SPOT_TYPE.BANNER]: { width: 468, height: 60 },
16175
+ [exports.RMN_SPOT_TYPE.LEADERBOARD]: { width: 728, height: 90 },
16176
+ [exports.RMN_SPOT_TYPE.LARGE_LEADERBOARD]: { width: 970, height: 90 },
16177
+ [exports.RMN_SPOT_TYPE.BILLBOARD]: { width: 970, height: 250 },
16178
+ [exports.RMN_SPOT_TYPE.SKYSCRAPER]: { width: 120, height: 600 },
16179
+ [exports.RMN_SPOT_TYPE.WIDE_SKYSCRAPER]: { width: 160, height: 600 },
16180
+ [exports.RMN_SPOT_TYPE.HALF_PAGE]: { width: 300, height: 600 },
16181
+ [exports.RMN_SPOT_TYPE.SMALL_SQUARE]: { width: 200, height: 200 },
16182
+ [exports.RMN_SPOT_TYPE.SQUARE]: { width: 250, height: 250 },
16183
+ [exports.RMN_SPOT_TYPE.VERTICAL_BANNER]: { width: 120, height: 240 },
16184
+ [exports.RMN_SPOT_TYPE.BUTTON_2]: { width: 120, height: 60 },
16185
+ [exports.RMN_SPOT_TYPE.MICRO_BAR]: { width: 88, height: 31 },
16186
+ [exports.RMN_SPOT_TYPE.POP_UP]: { width: 550, height: 480 },
16187
+ [exports.RMN_SPOT_TYPE.PORTRAIT]: { width: 300, height: 1050 },
16188
+ [exports.RMN_SPOT_TYPE.SMARTPHONE_BANNER_1]: { width: 300, height: 50 },
16189
+ [exports.RMN_SPOT_TYPE.SMARTPHONE_BANNER_2]: { width: 320, height: 50 },
16190
+ [exports.RMN_SPOT_TYPE.MOBILE_PHONE_INTERSTITIAL_1]: { width: 640, height: 1136 },
16191
+ [exports.RMN_SPOT_TYPE.MOBILE_PHONE_INTERSTITIAL_2]: { width: 750, height: 1334 },
16192
+ [exports.RMN_SPOT_TYPE.MOBILE_PHONE_INTERSTITIAL_3]: { width: 1080, height: 1920 },
16193
+ [exports.RMN_SPOT_TYPE.FEATURE_PHONE_SMALL_BANNER]: { width: 120, height: 20 },
16194
+ [exports.RMN_SPOT_TYPE.FEATURE_PHONE_MEDIUM_BANNER]: { width: 168, height: 28 },
16195
+ [exports.RMN_SPOT_TYPE.FEATURE_PHONE_LARGE_BANNER]: { width: 216, height: 36 },
16196
+ [exports.RMN_SPOT_TYPE.IN_TEXT]: { width: 1, height: 1 },
16197
+ };
16138
16198
 
16139
16199
  class IntersectionObserverService {
16140
16200
  constructor(defaultOptions = {}) {
@@ -16973,6 +17033,118 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
16973
17033
  CarouselElement = CustomCarouselElement;
16974
17034
  }
16975
17035
 
17036
+ function SkeletonTemplate({ fluid, width, height }) {
17037
+ return `
17038
+ <style>
17039
+ :host {
17040
+ display: block;
17041
+ position: relative;
17042
+ box-sizing: border-box;
17043
+ overflow: hidden;
17044
+ width: ${fluid ? '100%' : `${width}px`};
17045
+ height: ${fluid ? '100%' : `${height}px`};
17046
+ background: #ffffff;
17047
+ padding: 20px;
17048
+ border-radius: 5px;
17049
+ }
17050
+
17051
+ .content {
17052
+ height: 100%;
17053
+ display: flex;
17054
+ flex-direction: column;
17055
+ gap: 20px;
17056
+ }
17057
+
17058
+ .image-placeholder {
17059
+ width: 100%;
17060
+ height: 100%;
17061
+ background: #f0f0f0;
17062
+ border-radius: 4px;
17063
+ position: relative;
17064
+ overflow: hidden;
17065
+ }
17066
+
17067
+ .lines-container {
17068
+ display: flex;
17069
+ flex-direction: column;
17070
+ justify-content: flex-end;
17071
+ }
17072
+
17073
+ .line {
17074
+ height: 20px;
17075
+ background: #f0f0f0;
17076
+ border-radius: 4px;
17077
+ margin-bottom: 15px;
17078
+ position: relative;
17079
+ overflow: hidden;
17080
+ }
17081
+
17082
+ .image-placeholder::after,
17083
+ .line::after {
17084
+ content: "";
17085
+ position: absolute;
17086
+ top: 0;
17087
+ left: 0;
17088
+ width: 100%;
17089
+ height: 100%;
17090
+ background: linear-gradient(
17091
+ 90deg,
17092
+ rgba(255, 255, 255, 0) 0%,
17093
+ rgba(255, 255, 255, 0.5) 50%,
17094
+ rgba(255, 255, 255, 0) 100%
17095
+ );
17096
+ animation: shimmer 1.5s infinite;
17097
+ }
17098
+
17099
+ .line.header {
17100
+ width: 25%;
17101
+ }
17102
+
17103
+ .line.description {
17104
+ width: 65%;
17105
+ }
17106
+
17107
+ .line.button {
17108
+ width: 40%;
17109
+ }
17110
+
17111
+ @keyframes shimmer {
17112
+ 0% {
17113
+ transform: translateX(-100%);
17114
+ }
17115
+ 100% {
17116
+ transform: translateX(100%);
17117
+ }
17118
+ }
17119
+ </style>
17120
+
17121
+ <div class="content">
17122
+ <div class="image-placeholder"></div>
17123
+ <div class="lines-container">
17124
+ <div class="line header"></div>
17125
+ <div class="line description"></div>
17126
+ <div class="line button"></div>
17127
+ </div>
17128
+ </div>
17129
+ `;
17130
+ }
17131
+
17132
+ let SkeletonElement;
17133
+ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
17134
+ class CustomSkeletonElement extends HTMLElement {
17135
+ constructor() {
17136
+ super();
17137
+ this.attachShadow({ mode: 'open' });
17138
+ }
17139
+ connectedCallback() {
17140
+ if (!this.shadowRoot || !this.data)
17141
+ return;
17142
+ this.shadowRoot.innerHTML = SkeletonTemplate(this.data);
17143
+ }
17144
+ }
17145
+ SkeletonElement = CustomSkeletonElement;
17146
+ }
17147
+
16976
17148
  let SpotElement;
16977
17149
  if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
16978
17150
  class CustomSpotElement extends HTMLElement {
@@ -17080,15 +17252,14 @@ class ElementService {
17080
17252
  * @return {HTMLElement | null} - The html element or null if the browser environment is not available.
17081
17253
  */
17082
17254
  createSpotElement({ content, config }) {
17083
- var _a, _b;
17255
+ var _a;
17084
17256
  if (!this.ensureBrowserEnvironmentAndDefineElement()) {
17085
17257
  return null;
17086
17258
  }
17087
17259
  const spot = document.createElement(SPOT_ELEMENT_TAG);
17088
- spot.setAttribute('type', (_a = config === null || config === void 0 ? void 0 : config.spot) !== null && _a !== void 0 ? _a : '');
17089
17260
  spot.data = {
17090
17261
  spot: config === null || config === void 0 ? void 0 : config.spot,
17091
- fluid: (_b = config === null || config === void 0 ? void 0 : config.fluid) !== null && _b !== void 0 ? _b : false,
17262
+ fluid: (_a = config === null || config === void 0 ? void 0 : config.fluid) !== null && _a !== void 0 ? _a : false,
17092
17263
  ...config,
17093
17264
  };
17094
17265
  spot.content = content;
@@ -17116,6 +17287,27 @@ class ElementService {
17116
17287
  carousel.slides = slides;
17117
17288
  return carousel;
17118
17289
  }
17290
+ /**
17291
+ * Creates the skeleton html element based on the provided data using shadow dom.
17292
+ *
17293
+ * This method is only available in browser environments.
17294
+ *
17295
+ * @param {ICreateSkeletonElementParams} params - The parameters to create the final element.
17296
+ *
17297
+ * @return {HTMLElement | null} - The html element or null if the browser environment is not available.
17298
+ */
17299
+ createSkeletonElement(params) {
17300
+ if (!this.ensureBrowserEnvironmentAndDefineElement()) {
17301
+ return null;
17302
+ }
17303
+ const skeleton = document.createElement(SKELETON_ELEMENT_TAG);
17304
+ const dimensions = SPOT_DIMENSIONS[params.spotType];
17305
+ skeleton.data = {
17306
+ fluid: params.fluid,
17307
+ ...dimensions,
17308
+ };
17309
+ return skeleton;
17310
+ }
17119
17311
  /**
17120
17312
  * Overrides the spot colors with the provided colors.
17121
17313
  *
@@ -17150,6 +17342,9 @@ class ElementService {
17150
17342
  if (!window.customElements.get(CAROUSEL_ELEMENT_TAG)) {
17151
17343
  window.customElements.define(CAROUSEL_ELEMENT_TAG, CarouselElement);
17152
17344
  }
17345
+ if (!window.customElements.get(SKELETON_ELEMENT_TAG)) {
17346
+ window.customElements.define(SKELETON_ELEMENT_TAG, SkeletonElement);
17347
+ }
17153
17348
  return true;
17154
17349
  }
17155
17350
  }
@@ -18986,7 +19181,7 @@ class DataLayerMonitor {
18986
19181
  if (!eventName) {
18987
19182
  return null;
18988
19183
  }
18989
- const productIds = extractDeepIds(data.value);
19184
+ const productIds = extractDeepIds(data);
18990
19185
  return {
18991
19186
  event: eventName,
18992
19187
  productIds,
@@ -19295,6 +19490,7 @@ class LiquidCommerceRmnClient {
19295
19490
  this.selectionService = SelectionService.getInstance(auth);
19296
19491
  this.elementService = ElementService.getInstance();
19297
19492
  this.eventService = EventService.getInstance();
19493
+ this.intersectionObserver = new IntersectionObserverService();
19298
19494
  }
19299
19495
  /**
19300
19496
  * Makes a selection request on our server based on the provided data.
@@ -19318,24 +19514,29 @@ class LiquidCommerceRmnClient {
19318
19514
  async injectSpotElement(params) {
19319
19515
  var _a;
19320
19516
  if (typeof window === 'undefined' || typeof document === 'undefined') {
19321
- console.warn('LiquidCommerce Rmn Sdk: Methods which create elements are only available in browser environments.');
19517
+ console.warn('LiquidCommerce Rmn Sdk: injectSpotElement method is only available in the browser environment.');
19322
19518
  return;
19323
19519
  }
19324
19520
  const config = params.config;
19325
19521
  let inject = params.inject;
19522
+ // Handle no spots error state
19326
19523
  if (!inject.length) {
19327
- // Handle no spots error state
19328
19524
  this.eventService.handleSpotState('all', {
19329
19525
  state: {
19330
19526
  error: 'No spot elements provided for injection.',
19331
- loading: false,
19332
- mounted: false,
19333
19527
  },
19334
19528
  });
19335
19529
  return;
19336
19530
  }
19337
- // Update the state of the spots to loading
19338
- this.updateSpotsState(inject);
19531
+ // Identify the spot elements
19532
+ for (const item of inject) {
19533
+ this.eventService.handleSpotState(item.placementId, {
19534
+ identifier: {
19535
+ placementId: item.placementId,
19536
+ spotType: item.spotType,
19537
+ },
19538
+ }, false);
19539
+ }
19339
19540
  // Prevent duplicate placement ids
19340
19541
  const hasDuplicatePlacementIds = this.preventDuplicateSpotPlacementIds(inject);
19341
19542
  if (!hasDuplicatePlacementIds) {
@@ -19343,35 +19544,9 @@ class LiquidCommerceRmnClient {
19343
19544
  }
19344
19545
  // Prevent non-existent spot types
19345
19546
  inject = this.preventNonExistentSpotTypes(inject);
19346
- // Make the spot selection request
19347
- const response = await this.spotSelectionRequest({ ...params, inject });
19348
- // const response = await this.useSpotSelectionExample(inject);
19349
- // Handle the response
19350
- if (typeof response === 'object' && 'error' in response) {
19351
- // Handle request error state
19352
- this.eventService.handleSpotState('all', {
19353
- state: {
19354
- error: response.error,
19355
- mounted: false,
19356
- loading: false,
19357
- },
19358
- });
19359
- return;
19360
- }
19547
+ // Add Intersection Observer to the spot elements to track visibility
19548
+ // This is useful for lazy loading the spot elements
19361
19549
  for (const item of inject) {
19362
- const itemConfig = (_a = item.config) !== null && _a !== void 0 ? _a : config;
19363
- const spots = response[item.placementId];
19364
- if (!(spots === null || spots === void 0 ? void 0 : spots.length)) {
19365
- // Handle no spots found error state
19366
- this.eventService.handleSpotState(item.placementId, {
19367
- state: {
19368
- error: `No spots found for type "${item.spotType}".`,
19369
- mounted: false,
19370
- loading: false,
19371
- },
19372
- });
19373
- continue;
19374
- }
19375
19550
  const placementId = item.placementId.replace('#', '');
19376
19551
  const placement = document.getElementById(placementId);
19377
19552
  if (!placement) {
@@ -19379,8 +19554,6 @@ class LiquidCommerceRmnClient {
19379
19554
  this.eventService.handleSpotState(item.placementId, {
19380
19555
  state: {
19381
19556
  error: `Placement not found for id "${placementId}".`,
19382
- mounted: false,
19383
- loading: false,
19384
19557
  },
19385
19558
  });
19386
19559
  continue;
@@ -19394,43 +19567,73 @@ class LiquidCommerceRmnClient {
19394
19567
  display: 'flex',
19395
19568
  justifyContent: 'center',
19396
19569
  });
19397
- // Handle single spot
19398
- if (spots.length === 1) {
19399
- const isInjected = this.injectOneSpotElement(item, placement, spots[0], itemConfig);
19400
- if (!isInjected) {
19401
- continue;
19402
- }
19570
+ const skeletonElement = this.elementService.createSkeletonElement({
19571
+ fluid: (_a = config === null || config === void 0 ? void 0 : config.fluid) !== null && _a !== void 0 ? _a : false,
19572
+ spotType: item.spotType,
19573
+ });
19574
+ if (!skeletonElement) {
19575
+ this.eventService.handleSpotState(item.placementId, {
19576
+ state: {
19577
+ error: `Failed to create skeleton loader element.`,
19578
+ mounted: false,
19579
+ loading: true,
19580
+ },
19581
+ });
19582
+ continue;
19403
19583
  }
19404
- // Handle multiple spots (carousel)
19405
- if (spots.length > 1) {
19406
- const isInjected = this.injectCarouselSpotElement(placement, spots, itemConfig);
19407
- if (!isInjected) {
19408
- continue;
19584
+ placement.replaceChildren(skeletonElement);
19585
+ const spotPlacementIsNear = async () => {
19586
+ var _a;
19587
+ // Set the spot element to loading state
19588
+ this.eventService.handleSpotState(item.placementId, {
19589
+ state: {
19590
+ loading: true,
19591
+ },
19592
+ });
19593
+ // Stop observing the placement
19594
+ this.intersectionObserver.unobserve(placement);
19595
+ // Make the spot selection request
19596
+ const response = await this.spotSelectionRequest({ ...params, inject: [item] });
19597
+ // const response = await this.useSpotSelectionExample(inject);
19598
+ // Handle request error state
19599
+ if (typeof response === 'object' && 'error' in response) {
19600
+ this.eventService.handleSpotState(item.placementId, {
19601
+ state: {
19602
+ error: response.error,
19603
+ mounted: false,
19604
+ loading: false,
19605
+ },
19606
+ });
19607
+ this.clearPlacement(item.placementId);
19608
+ return;
19409
19609
  }
19410
- }
19610
+ const itemConfig = (_a = item.config) !== null && _a !== void 0 ? _a : config;
19611
+ const spots = response[item.placementId];
19612
+ if (!(spots === null || spots === void 0 ? void 0 : spots.length)) {
19613
+ // Handle no spots found error state
19614
+ this.eventService.handleSpotState(item.placementId, {
19615
+ state: {
19616
+ error: `No spots found for type "${item.spotType}".`,
19617
+ mounted: false,
19618
+ loading: false,
19619
+ },
19620
+ });
19621
+ this.clearPlacement(item.placementId);
19622
+ return;
19623
+ }
19624
+ // Handle single spot
19625
+ if (spots.length === 1) {
19626
+ this.injectOneSpotElement(item, placement, spots[0], itemConfig);
19627
+ }
19628
+ // Handle multiple spots (carousel)
19629
+ if (spots.length > 1) {
19630
+ this.injectCarouselSpotElement(placement, spots, itemConfig);
19631
+ }
19632
+ };
19633
+ this.intersectionObserver.observe(placement, spotPlacementIsNear, { rootMargin: '500px' });
19411
19634
  }
19412
19635
  }
19413
- /**
19414
- * Makes a selection request on our server based on the provided data.
19415
- *
19416
- * @param {IInjectSpotElementParams} params - Parameters for injecting spot elements.
19417
- *
19418
- * @return {Promise<ISpots | {error: string}>} - The spots response object.
19419
- */
19420
- async spotSelectionRequest(params) {
19421
- const { inject, filter, config } = params;
19422
- const request = {
19423
- url: config === null || config === void 0 ? void 0 : config.url,
19424
- filter,
19425
- spots: inject.map((item) => ({
19426
- placementId: item.placementId,
19427
- spot: item.spotType,
19428
- count: item === null || item === void 0 ? void 0 : item.count,
19429
- ...item === null || item === void 0 ? void 0 : item.filter,
19430
- })),
19431
- };
19432
- return this.spotSelection(request);
19433
- }
19636
+ /** ========================= HELPER METHODS ========================= **/
19434
19637
  /**
19435
19638
  * Injects a carousel element with the provided spots into the placement.
19436
19639
  *
@@ -19499,7 +19702,8 @@ class LiquidCommerceRmnClient {
19499
19702
  loading: false,
19500
19703
  },
19501
19704
  });
19502
- return false;
19705
+ this.clearPlacement(placement.id);
19706
+ return;
19503
19707
  }
19504
19708
  placement.replaceChildren(carouselElement);
19505
19709
  this.eventService.handleSpotState(placement.id, {
@@ -19513,7 +19717,6 @@ class LiquidCommerceRmnClient {
19513
19717
  error: undefined,
19514
19718
  },
19515
19719
  });
19516
- return true;
19517
19720
  }
19518
19721
  /**
19519
19722
  * Injects a single spot element into the provided placement.
@@ -19549,7 +19752,8 @@ class LiquidCommerceRmnClient {
19549
19752
  loading: false,
19550
19753
  },
19551
19754
  });
19552
- return false;
19755
+ this.clearPlacement(injectItem.placementId);
19756
+ return;
19553
19757
  }
19554
19758
  // Create the spot element
19555
19759
  const spotElement = this.elementService.createSpotElement({
@@ -19570,7 +19774,8 @@ class LiquidCommerceRmnClient {
19570
19774
  loading: false,
19571
19775
  },
19572
19776
  });
19573
- return false;
19777
+ this.clearPlacement(injectItem.placementId);
19778
+ return;
19574
19779
  }
19575
19780
  this.eventService.registerSpot({
19576
19781
  spot: spotData,
@@ -19589,7 +19794,38 @@ class LiquidCommerceRmnClient {
19589
19794
  error: undefined,
19590
19795
  },
19591
19796
  });
19592
- return true;
19797
+ }
19798
+ /**
19799
+ * Clears the placement element by removing all its children.
19800
+ *
19801
+ * @param {string} placementId - The placement id.
19802
+ *
19803
+ * @return {void}
19804
+ */
19805
+ clearPlacement(placementId) {
19806
+ var _a;
19807
+ (_a = document.getElementById(placementId)) === null || _a === void 0 ? void 0 : _a.replaceChildren();
19808
+ }
19809
+ /**
19810
+ * Makes a selection request on our server based on the provided data.
19811
+ *
19812
+ * @param {IInjectSpotElementParams} params - Parameters for injecting spot elements.
19813
+ *
19814
+ * @return {Promise<ISpots | {error: string}>} - The spots response object.
19815
+ */
19816
+ async spotSelectionRequest(params) {
19817
+ const { inject, filter, config } = params;
19818
+ const request = {
19819
+ url: config === null || config === void 0 ? void 0 : config.url,
19820
+ filter,
19821
+ spots: inject.map((item) => ({
19822
+ placementId: item.placementId,
19823
+ spot: item.spotType,
19824
+ count: item === null || item === void 0 ? void 0 : item.count,
19825
+ ...item === null || item === void 0 ? void 0 : item.filter,
19826
+ })),
19827
+ };
19828
+ return this.spotSelection(request);
19593
19829
  }
19594
19830
  /**
19595
19831
  * Prevents duplicate placement ids in the inject data.
@@ -19607,8 +19843,6 @@ class LiquidCommerceRmnClient {
19607
19843
  this.eventService.handleSpotState(item.placementId, {
19608
19844
  state: {
19609
19845
  error: `Duplicate placement id (${item.placementId}) found. Please provide a unique placement id for each spot element.`,
19610
- mounted: false,
19611
- loading: false,
19612
19846
  },
19613
19847
  });
19614
19848
  return false;
@@ -19617,6 +19851,12 @@ class LiquidCommerceRmnClient {
19617
19851
  }
19618
19852
  return true;
19619
19853
  }
19854
+ /**
19855
+ * Prevents non-existent spot types in the inject data.
19856
+ *
19857
+ * @param {IInjectSpotElement[]} inject - The inject data.
19858
+ * @return {IInjectSpotElement[]} - The filtered inject data.
19859
+ */
19620
19860
  preventNonExistentSpotTypes(inject) {
19621
19861
  const newInject = [];
19622
19862
  for (const item of inject) {
@@ -19624,8 +19864,6 @@ class LiquidCommerceRmnClient {
19624
19864
  this.eventService.handleSpotState(item.placementId, {
19625
19865
  state: {
19626
19866
  error: `Invalid spot type (${item.spotType}) found. Please provide a valid spot type for each spot element.`,
19627
- mounted: false,
19628
- loading: false,
19629
19867
  },
19630
19868
  });
19631
19869
  continue;
@@ -19634,22 +19872,7 @@ class LiquidCommerceRmnClient {
19634
19872
  }
19635
19873
  return newInject;
19636
19874
  }
19637
- // Initialize spots with loading state and identifiers
19638
- updateSpotsState(inject) {
19639
- for (const item of inject) {
19640
- this.eventService.handleSpotState(item.placementId, {
19641
- identifier: {
19642
- placementId: item.placementId,
19643
- spotType: item.spotType,
19644
- },
19645
- state: {
19646
- loading: true,
19647
- mounted: false,
19648
- error: undefined,
19649
- },
19650
- });
19651
- }
19652
- }
19875
+ // Use spot selection example data for private testing
19653
19876
  useSpotSelectionExample(inject) {
19654
19877
  const examples = { ...RB_SPOTS_SELECTION_EXAMPLE, ...IAB_SPOTS_SELECTION_EXAMPLE };
19655
19878
  const data = {};