@liquidcommercedev/rmn-sdk 1.4.6-beta.8 → 1.5.0-beta.1

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
@@ -14998,7 +14998,7 @@ class BaseApi extends BaseApiAbstract {
14998
14998
  */
14999
14999
  async post(path, data, configOverrides) {
15000
15000
  let requestData = data;
15001
- if (this.authInfo.env !== RMN_ENV.LOCAL) {
15001
+ if (![RMN_ENV.LOCAL, RMN_ENV.DEVELOPMENT].includes(this.authInfo.env)) {
15002
15002
  const timestamp = new Date().getTime();
15003
15003
  configOverrides = {
15004
15004
  ...configOverrides,
@@ -15203,6 +15203,78 @@ class IntersectionObserverService {
15203
15203
  }
15204
15204
  }
15205
15205
 
15206
+ class LocalStorage {
15207
+ constructor() {
15208
+ this.spots = new Map();
15209
+ // Sync local storage with the current state
15210
+ this.syncLocalStorage();
15211
+ // Remove expired spots
15212
+ this.removeExpiredSpots();
15213
+ }
15214
+ static getInstance() {
15215
+ if (!LocalStorage.instance) {
15216
+ LocalStorage.instance = new LocalStorage();
15217
+ }
15218
+ return LocalStorage.instance;
15219
+ }
15220
+ syncLocalStorage() {
15221
+ const localStorageData = localStorage.getItem(LocalStorage.localStorageKey);
15222
+ // TODO: Encrypt the data before storing it in the local storage
15223
+ if (localStorageData) {
15224
+ try {
15225
+ const parsedData = JSON.parse(localStorageData);
15226
+ if (parsedData && typeof parsedData === 'object') {
15227
+ this.spots = this.objToMap(parsedData);
15228
+ }
15229
+ else {
15230
+ this.clearLocalStorage();
15231
+ }
15232
+ }
15233
+ catch (_a) {
15234
+ // If there is an error parsing the data, clear the local storage to prevent any issues
15235
+ this.clearLocalStorage();
15236
+ }
15237
+ }
15238
+ }
15239
+ setSpot(spotId, data) {
15240
+ data.createdAt = Date.now();
15241
+ this.spots.set(spotId, data);
15242
+ this.updateLocalStorage();
15243
+ }
15244
+ getSpot(spotId) {
15245
+ return this.spots.get(spotId);
15246
+ }
15247
+ removeSpot(spotId) {
15248
+ this.spots.delete(spotId);
15249
+ this.updateLocalStorage();
15250
+ }
15251
+ updateLocalStorage() {
15252
+ const data = this.mapToObj(this.spots);
15253
+ localStorage.setItem(LocalStorage.localStorageKey, JSON.stringify(data));
15254
+ }
15255
+ clearLocalStorage() {
15256
+ localStorage.removeItem(LocalStorage.localStorageKey);
15257
+ }
15258
+ removeExpiredSpots() {
15259
+ const currentTime = Date.now();
15260
+ this.spots.forEach((spot, spotId) => {
15261
+ var _a;
15262
+ if (currentTime - ((_a = spot.createdAt) !== null && _a !== void 0 ? _a : 0) > LocalStorage.spotExpirationTime) {
15263
+ this.spots.delete(spotId);
15264
+ }
15265
+ });
15266
+ this.updateLocalStorage();
15267
+ }
15268
+ mapToObj(map) {
15269
+ return Object.fromEntries(map);
15270
+ }
15271
+ objToMap(obj) {
15272
+ return new Map(Object.entries(obj));
15273
+ }
15274
+ }
15275
+ LocalStorage.localStorageKey = 'lc_rmn';
15276
+ LocalStorage.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
15277
+
15206
15278
  class ResizeObserverService {
15207
15279
  constructor({ element, maxSize, minScale }) {
15208
15280
  this.element = element;
@@ -15335,11 +15407,25 @@ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
15335
15407
  display: flex;
15336
15408
  align-items: center;
15337
15409
  gap: 8px;
15410
+ opacity: var(--opacity, 1);
15338
15411
  }
15339
15412
 
15340
- .dots .dot {
15413
+ .dots.small .dot {
15414
+ width: 8px;
15415
+ height: 8px;
15416
+ }
15417
+
15418
+ .dots.base .dot {
15341
15419
  width: 12px;
15342
15420
  height: 12px;
15421
+ }
15422
+
15423
+ .dots.large .dot {
15424
+ width: 16px;
15425
+ height: 16px;
15426
+ }
15427
+
15428
+ .dots .dot {
15343
15429
  border-radius: 50%;
15344
15430
  cursor: pointer;
15345
15431
  transition: all 0.3s ease;
@@ -15387,6 +15473,10 @@ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
15387
15473
  flex-direction: column;
15388
15474
  }
15389
15475
 
15476
+ .buttons {
15477
+ opacity: var(--opacity, 1);
15478
+ }
15479
+
15390
15480
  .buttons button {
15391
15481
  background-color: #00000080;
15392
15482
  color: #fff;
@@ -15396,6 +15486,18 @@ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
15396
15486
  transition: background-color 0.3s ease;
15397
15487
  }
15398
15488
 
15489
+ .buttons.small button {
15490
+ padding: 6px;
15491
+ }
15492
+
15493
+ .buttons.base button {
15494
+ padding: 10px;
15495
+ }
15496
+
15497
+ .buttons.large button {
15498
+ padding: 14px;
15499
+ }
15500
+
15399
15501
  .buttons button:hover {
15400
15502
  background-color: #000000b3;
15401
15503
  }
@@ -15468,11 +15570,6 @@ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
15468
15570
  padding: 8px 12px;
15469
15571
  font-size: 14px;
15470
15572
  }
15471
-
15472
- .dots .dot {
15473
- width: 8px;
15474
- height: 8px;
15475
- }
15476
15573
  }
15477
15574
  `;
15478
15575
 
@@ -15512,6 +15609,8 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
15512
15609
  position: 'bottom-center',
15513
15610
  color: '#d9d9d9',
15514
15611
  activeColor: '#b5914a',
15612
+ size: 'base',
15613
+ opacity: 1,
15515
15614
  ...(typeof ((_j = this.data) === null || _j === void 0 ? void 0 : _j.useDots) === 'object' ? this.data.useDots : {}),
15516
15615
  };
15517
15616
  this.buttonsOptions = {
@@ -15522,6 +15621,8 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
15522
15621
  borderRadius: '50%',
15523
15622
  prev: 'Prev',
15524
15623
  next: 'Next',
15624
+ size: 'base',
15625
+ opacity: 1,
15525
15626
  ...(typeof ((_k = this.data) === null || _k === void 0 ? void 0 : _k.useButtons) === 'object' ? this.data.useButtons : {}),
15526
15627
  };
15527
15628
  this.validateOptions();
@@ -15607,7 +15708,8 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
15607
15708
  }
15608
15709
  renderDots() {
15609
15710
  const dotsContainer = document.createElement('div');
15610
- dotsContainer.className = `dots ${this.dotsOptions.position}`;
15711
+ dotsContainer.className = `dots ${this.dotsOptions.position} ${this.dotsOptions.size}`;
15712
+ dotsContainer.style.cssText = `--opacity: ${this.dotsOptions.opacity}`;
15611
15713
  this.slides.forEach((_, index) => {
15612
15714
  const dot = document.createElement('span');
15613
15715
  dot.className = `dot ${index === this.currentSlide ? 'active' : ''}`;
@@ -15621,7 +15723,8 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
15621
15723
  const buttonsClass = this.buttonsOptions.together
15622
15724
  ? `buttons-together ${this.buttonsOptions.position}`
15623
15725
  : 'buttons-separate';
15624
- buttonsContainer.className = `buttons ${buttonsClass}`;
15726
+ buttonsContainer.className = `buttons ${buttonsClass} ${this.buttonsOptions.size}`;
15727
+ buttonsContainer.style.cssText = `--opacity: ${this.buttonsOptions.opacity}`;
15625
15728
  this.prevButton = this.createButton('prev-button', this.buttonsOptions.prev);
15626
15729
  this.nextButton = this.createButton('next-button', this.buttonsOptions.next);
15627
15730
  buttonsContainer.appendChild(this.prevButton);
@@ -15855,14 +15958,15 @@ class ElementService {
15855
15958
  * @return {HTMLElement | null} - The html element or null if the browser environment is not available.
15856
15959
  */
15857
15960
  createSpotElement({ content, config }) {
15858
- var _a;
15961
+ var _a, _b;
15859
15962
  if (!this.ensureBrowserEnvironmentAndDefineElement()) {
15860
15963
  return null;
15861
15964
  }
15862
15965
  const spot = document.createElement(SPOT_ELEMENT_TAG);
15966
+ spot.setAttribute('type', (_a = config === null || config === void 0 ? void 0 : config.spot) !== null && _a !== void 0 ? _a : '');
15863
15967
  spot.data = {
15864
15968
  spot: config === null || config === void 0 ? void 0 : config.spot,
15865
- fluid: (_a = config === null || config === void 0 ? void 0 : config.fluid) !== null && _a !== void 0 ? _a : false,
15969
+ fluid: (_b = config === null || config === void 0 ? void 0 : config.fluid) !== null && _b !== void 0 ? _b : false,
15866
15970
  ...config,
15867
15971
  };
15868
15972
  spot.content = content;
@@ -15928,11 +16032,172 @@ class ElementService {
15928
16032
  }
15929
16033
  }
15930
16034
 
15931
- function linearGradientColorStop(overlay, fallback) {
15932
- if (!overlay || overlay.length === 0) {
16035
+ class UniqueIdGenerator {
16036
+ /**
16037
+ * Initialize the generator with a node ID
16038
+ * @param nodeId Number between 0-1023 to identify this instance
16039
+ */
16040
+ static initialize(nodeId = Math.floor(Math.random() * 1024)) {
16041
+ if (nodeId < 0 || nodeId >= 1 << this.nodeBits) {
16042
+ throw new Error(`Node ID must be between 0 and ${(1 << this.nodeBits) - 1}`);
16043
+ }
16044
+ this.nodeId = nodeId;
16045
+ }
16046
+ /**
16047
+ * Convert a number to base32 string with specified length
16048
+ */
16049
+ static toBase32(num, length) {
16050
+ let result = '';
16051
+ while (num > 0) {
16052
+ result = this.base32Chars[Number(num % BigInt(32))] + result;
16053
+ // @ts-expect-error - TS doesn't support bigint division
16054
+ num = num / 32n;
16055
+ }
16056
+ return result.padStart(length, '0');
16057
+ }
16058
+ /**
16059
+ * Generate a cryptographically secure random number
16060
+ */
16061
+ static getSecureRandom() {
16062
+ if (typeof crypto !== 'undefined') {
16063
+ const buffer = new Uint32Array(1);
16064
+ crypto.getRandomValues(buffer);
16065
+ return buffer[0];
16066
+ }
16067
+ return Math.floor(Math.random() * 0xffffffff);
16068
+ }
16069
+ /**
16070
+ * Wait until next millisecond
16071
+ */
16072
+ static waitNextMillis(lastTimestamp) {
16073
+ let timestamp = Date.now();
16074
+ while (timestamp <= lastTimestamp) {
16075
+ timestamp = Date.now();
16076
+ }
16077
+ return timestamp;
16078
+ }
16079
+ /**
16080
+ * Generates a highly unique ID with the following format:
16081
+ * TTTTTTTTTTCCCCNNNNNRRRR
16082
+ * T: Timestamp (10 chars)
16083
+ * C: Counter (4 chars)
16084
+ * N: Node ID (5 chars)
16085
+ * R: Random (4 chars)
16086
+ *
16087
+ * Total length: 23 characters, always uppercase alphanumeric
16088
+ */
16089
+ static generate() {
16090
+ if (this.nodeId === undefined) {
16091
+ this.initialize();
16092
+ }
16093
+ let timestamp = Date.now() - this.epoch;
16094
+ // Handle clock moving backwards or same millisecond
16095
+ if (timestamp < this.lastTimestamp) {
16096
+ throw new Error('Clock moved backwards. Refusing to generate ID.');
16097
+ }
16098
+ if (timestamp === this.lastTimestamp) {
16099
+ this.sequence = (this.sequence + 1) & ((1 << this.sequenceBits) - 1);
16100
+ if (this.sequence === 0) {
16101
+ timestamp = this.waitNextMillis(this.lastTimestamp);
16102
+ }
16103
+ }
16104
+ else {
16105
+ this.sequence = 0;
16106
+ }
16107
+ this.lastTimestamp = timestamp;
16108
+ // Generate random component
16109
+ const random = this.getSecureRandom() & 0xffff; // 16 bits of randomness
16110
+ // Combine all components into a BigInt
16111
+ // const id =
16112
+ // (BigInt(timestamp) << BigInt(this.nodeBits + this.sequenceBits + 16)) |
16113
+ // (BigInt(this.nodeId) << BigInt(this.sequenceBits + 16)) |
16114
+ // (BigInt(this.sequence) << BigInt(16)) |
16115
+ // BigInt(random);
16116
+ // Convert to base32 representation
16117
+ const timeComponent = this.toBase32(BigInt(timestamp), 10);
16118
+ const counterComponent = this.toBase32(BigInt(this.sequence), 4);
16119
+ const nodeComponent = this.toBase32(BigInt(this.nodeId), 5);
16120
+ const randomComponent = this.toBase32(BigInt(random), 4);
16121
+ return `${timeComponent}${counterComponent}${nodeComponent}${randomComponent}`;
16122
+ }
16123
+ /**
16124
+ * Validates if a string matches the expected ID format
16125
+ */
16126
+ static isValid(id) {
16127
+ if (!/^[0-9A-HJ-NP-Z]{23}$/.test(id))
16128
+ return false;
16129
+ try {
16130
+ const timeComponent = id.slice(0, 10);
16131
+ const timestamp = this.decodeBase32(timeComponent);
16132
+ const now = Date.now() - this.epoch;
16133
+ return timestamp >= 0 && timestamp <= now;
16134
+ }
16135
+ catch (_a) {
16136
+ return false;
16137
+ }
16138
+ }
16139
+ /**
16140
+ * Decode base32 string to number
16141
+ */
16142
+ static decodeBase32(str) {
16143
+ let result = 0;
16144
+ for (const char of str) {
16145
+ result = result * 32 + this.base32Chars.indexOf(char);
16146
+ }
16147
+ return result;
16148
+ }
16149
+ /**
16150
+ * Extract timestamp from ID
16151
+ */
16152
+ static getTimestamp(id) {
16153
+ if (!this.isValid(id))
16154
+ throw new Error('Invalid ID format');
16155
+ const timeComponent = id.slice(0, 10);
16156
+ const timestamp = this.decodeBase32(timeComponent);
16157
+ return new Date(timestamp + this.epoch);
16158
+ }
16159
+ }
16160
+ // Constants for bit manipulation
16161
+ UniqueIdGenerator.epoch = 1577836800000; // 2020-01-01 as epoch
16162
+ UniqueIdGenerator.nodeBits = 10;
16163
+ UniqueIdGenerator.sequenceBits = 12;
16164
+ // Instance variables
16165
+ UniqueIdGenerator.lastTimestamp = -1;
16166
+ UniqueIdGenerator.sequence = 0;
16167
+ // Character set for base32 encoding (excluding similar looking characters)
16168
+ UniqueIdGenerator.base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
16169
+
16170
+ function convertHexToRgba(hex, opacity = 1) {
16171
+ // Remove # if present
16172
+ const cleanHex = hex.replace('#', '');
16173
+ // Convert hex to RGB
16174
+ const r = parseInt(cleanHex.substring(0, 2), 16);
16175
+ const g = parseInt(cleanHex.substring(2, 4), 16);
16176
+ const b = parseInt(cleanHex.substring(4, 6), 16);
16177
+ // Return rgba string
16178
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
16179
+ }
16180
+ function generateGradientColor(overlay, fallback = '') {
16181
+ if (!overlay) {
15933
16182
  return fallback;
15934
16183
  }
15935
- return overlay.map(({ color, colorStop }) => `${color} ${colorStop}`).join(', ');
16184
+ const OVERLAY_SIZE = {
16185
+ small: 10,
16186
+ base: 30,
16187
+ large: 50,
16188
+ };
16189
+ const OVERLAY_OPACITY = {
16190
+ light: 0.1,
16191
+ medium: 0.4,
16192
+ dark: 0.6,
16193
+ };
16194
+ const { size, opacity, color } = overlay;
16195
+ const goTo = OVERLAY_SIZE[size];
16196
+ const overlayOpacity = OVERLAY_OPACITY[opacity];
16197
+ const fullColor = convertHexToRgba(color, 1);
16198
+ const transparentColor = convertHexToRgba(color, 0);
16199
+ const gradientColor = convertHexToRgba(color, overlayOpacity);
16200
+ return `${fullColor} 0%, ${gradientColor} ${goTo}%, ${transparentColor} 100%`;
15936
16201
  }
15937
16202
  function spotHtmlStringToElement(htmlString) {
15938
16203
  const spot = document.createElement('div');
@@ -16796,7 +17061,7 @@ function rbCollectionBannerWithoutTextBlockTemplate(spot, config) {
16796
17061
  }
16797
17062
 
16798
17063
  const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderColor = ctaTextColor, primaryImage, mobilePrimaryImage = primaryImage, }, { prefix, overlay }) => {
16799
- const linearGradient = linearGradientColorStop(overlay || [], 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 40%');
17064
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 40%');
16800
17065
  return `
16801
17066
  <style>
16802
17067
  .${prefix} {
@@ -17287,7 +17552,7 @@ function rbHomepageHeroTwoTileTemplate(spot, config) {
17287
17552
  }
17288
17553
 
17289
17554
  const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderColor = ctaTextColor, primaryImage, mobilePrimaryImage = primaryImage, }, { prefix, overlay }) => {
17290
- const linearGradient = linearGradientColorStop(overlay || [], 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
17555
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
17291
17556
  return `
17292
17557
  <style>
17293
17558
  .${prefix} {
@@ -17424,7 +17689,7 @@ function rbLargeCategoryImageToutTemplate(spot, config) {
17424
17689
  }
17425
17690
 
17426
17691
  const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
17427
- const linearGradient = linearGradientColorStop(overlay || [], 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
17692
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
17428
17693
  return `
17429
17694
  <style>
17430
17695
  .${prefix} {
@@ -17441,11 +17706,7 @@ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
17441
17706
  background-position: center;
17442
17707
  background-repeat: no-repeat;
17443
17708
  container-type: inline-size;
17444
- }
17445
-
17446
- .${prefix}__text {
17447
- padding: 15px 10%;
17448
- width: fit-content;
17709
+ position: relative;
17449
17710
  }
17450
17711
 
17451
17712
  .${prefix}__header {
@@ -17456,6 +17717,7 @@ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
17456
17717
  font-family: "Source Sans 3", system-ui;
17457
17718
  font-style: normal;
17458
17719
  margin: 0;
17720
+ padding: 15px 10%;
17459
17721
  }
17460
17722
 
17461
17723
  @container (min-width: 640px) {
@@ -17491,15 +17753,13 @@ function rbNavigationBannerTemplate(spot, config) {
17491
17753
  ${GFONT_SOURCE_SANS_3}
17492
17754
  ${STYLES$2(spot, config)}
17493
17755
  <div class="${prefix}">
17494
- <div class="${prefix}__text">
17495
17756
  ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17496
- </div>
17497
17757
  </div>
17498
17758
  `;
17499
17759
  }
17500
17760
 
17501
17761
  const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
17502
- const linearGradient = linearGradientColorStop(overlay || [], 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
17762
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
17503
17763
  return `
17504
17764
  <style>
17505
17765
  .${prefix} {
@@ -17695,8 +17955,8 @@ const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
17695
17955
  if (!variantTemplate) {
17696
17956
  return null;
17697
17957
  }
17698
- // Generate a random prefix to avoid conflicts with other elements.
17699
- const prefix = 's' + Math.random().toString(36).substring(6);
17958
+ // Generate a highly unique prefix to avoid conflicts with other elements.
17959
+ const prefix = 's' + UniqueIdGenerator.generate().toLowerCase();
17700
17960
  const spotHtmlString = variantTemplate(spot, { ...config, prefix });
17701
17961
  return spotHtmlStringToElement(spotHtmlString);
17702
17962
  };
@@ -17713,6 +17973,12 @@ class PubSub {
17713
17973
  */
17714
17974
  this.subscribers = {};
17715
17975
  }
17976
+ static getInstance() {
17977
+ if (!PubSub.instance) {
17978
+ PubSub.instance = new PubSub();
17979
+ }
17980
+ return PubSub.instance;
17981
+ }
17716
17982
  /**
17717
17983
  * Subscribe to an event
17718
17984
  * @param eventType - The type of event to subscribe to
@@ -17778,7 +18044,8 @@ class PubSub {
17778
18044
 
17779
18045
  class EventService {
17780
18046
  constructor() {
17781
- this.pubSub = new PubSub();
18047
+ this.pubSub = PubSub.getInstance();
18048
+ this.localStorage = LocalStorage.getInstance();
17782
18049
  this.activeSpots = new Map();
17783
18050
  this.spotStates = new Map();
17784
18051
  this.intersectionObserver = new IntersectionObserverService();
@@ -17795,6 +18062,59 @@ class EventService {
17795
18062
  publish(eventType, data) {
17796
18063
  this.pubSub.publish(eventType, data);
17797
18064
  }
18065
+ registerSpot(params) {
18066
+ const { placementId, spot, spotElement } = params;
18067
+ this.activeSpots.set(placementId, { spotElement });
18068
+ // Fire impression event
18069
+ this.fireImpressionEvent(placementId, spot, spotElement);
18070
+ // Handle intersection observer
18071
+ this.handleIntersectionObserver(placementId, spot, spotElement);
18072
+ // Attach click event listener
18073
+ spotElement.addEventListener('click', async () => await this.handleClick(params));
18074
+ }
18075
+ unregisterSpot(placementId) {
18076
+ const placementIdClean = placementId.replace('#', '');
18077
+ const spotData = this.activeSpots.get(placementIdClean);
18078
+ if (!spotData) {
18079
+ this.handleSpotState(placementIdClean, {
18080
+ state: {
18081
+ error: `Active spot with placementId ${placementIdClean} not found.`,
18082
+ },
18083
+ });
18084
+ return;
18085
+ }
18086
+ this.intersectionObserver.unobserve(spotData.spotElement);
18087
+ this.handleSpotState(placementIdClean, {
18088
+ dom: {
18089
+ spotElement: undefined,
18090
+ visibleOnViewport: false,
18091
+ },
18092
+ state: {
18093
+ unmounted: true,
18094
+ mounted: false,
18095
+ },
18096
+ });
18097
+ this.activeSpots.delete(placementIdClean);
18098
+ const placementElement = document.getElementById(placementIdClean);
18099
+ if (!placementElement) {
18100
+ this.handleSpotState(placementIdClean, {
18101
+ state: {
18102
+ error: `Placement element with id ${placementIdClean} not found.`,
18103
+ },
18104
+ });
18105
+ return;
18106
+ }
18107
+ placementElement.innerHTML = '';
18108
+ }
18109
+ /**
18110
+ * Updates the state of a spot.
18111
+ *
18112
+ * @param {string} placementId - The placement ID of the spot.
18113
+ * @param {Partial<ILifecycleState>} updates - The updates to apply to the spot state.
18114
+ * @param {boolean} publish - Whether to publish the updated state.
18115
+ *
18116
+ * @returns {void}
18117
+ */
17798
18118
  handleSpotState(placementId, updates, publish = true) {
17799
18119
  let currentState = this.spotStates.get(placementId);
17800
18120
  if (!currentState) {
@@ -17805,8 +18125,8 @@ class EventService {
17805
18125
  spotType: '',
17806
18126
  },
17807
18127
  dom: {
17808
- element: undefined,
17809
- visible: false,
18128
+ spotElement: undefined,
18129
+ visibleOnViewport: false,
17810
18130
  },
17811
18131
  state: {
17812
18132
  mounted: false,
@@ -17821,81 +18141,57 @@ class EventService {
17821
18141
  },
17822
18142
  };
17823
18143
  }
17824
- this.spotStates.set(placementId, {
17825
- ...currentState,
17826
- ...updates,
17827
- });
18144
+ this.spotStates.set(placementId, { ...currentState, ...updates });
17828
18145
  if (publish) {
17829
18146
  this.pubSub.publish(RMN_SPOT_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
17830
18147
  }
17831
18148
  }
17832
- registerSpot({ placementId, element, spot }) {
17833
- this.activeSpots.set(spot.id, {
18149
+ async handleClick({ placementId, spot, spotElement, }) {
18150
+ var _a, _b, _c;
18151
+ // Publish click event
18152
+ this.pubSub.publish(RMN_SPOT_EVENT.CLICK, {
17834
18153
  placementId,
17835
- element,
17836
- impressionTracked: false,
18154
+ spotId: spot.id,
18155
+ spotElement,
17837
18156
  });
17838
- // Handle intersection observer
17839
- this.handleIntersectionObserver(placementId, spot, element);
17840
- // Attach click event listener
17841
- element.addEventListener('click', async () => {
17842
- var _a, _b;
17843
- this.pubSub.publish(RMN_SPOT_EVENT.CLICK, {
17844
- placementId,
17845
- spotId: spot.id,
17846
- element,
17847
- });
17848
- // Fire click event
17849
- await this.fireEvent({
17850
- event: RMN_SPOT_EVENT.CLICK,
17851
- eventUrl: (_b = (_a = spot.events.find((event) => event.event === RMN_SPOT_EVENT.CLICK)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
17852
- });
18157
+ // Fire click event
18158
+ await this.fireEvent({
18159
+ event: RMN_SPOT_EVENT.CLICK,
18160
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === RMN_SPOT_EVENT.CLICK)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18161
+ });
18162
+ // Save spot to local storage for event tracking
18163
+ this.localStorage.setSpot(spot.id, {
18164
+ spotId: spot.id,
18165
+ spotType: spot.spot,
18166
+ events: spot.events,
18167
+ productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 'GROUPING-12345', 'DAN-12345', 131398103],
17853
18168
  });
17854
18169
  }
17855
- unregisterSpot(spotId) {
17856
- const spotData = this.activeSpots.get(spotId);
17857
- if (spotData) {
17858
- this.intersectionObserver.unobserve(spotData.element);
17859
- this.handleSpotState(spotData.placementId, {
18170
+ handleIntersectionObserver(placementId, _spot, spotElement) {
18171
+ const spotIsVisibleCallback = async () => {
18172
+ this.intersectionObserver.unobserve(spotElement);
18173
+ this.handleSpotState(placementId, {
17860
18174
  dom: {
17861
- element: undefined,
17862
- visible: false,
17863
- },
17864
- state: {
17865
- unmounted: true,
17866
- mounted: false,
18175
+ spotElement,
18176
+ visibleOnViewport: true,
17867
18177
  },
17868
18178
  });
17869
- this.activeSpots.delete(spotId);
17870
- }
18179
+ };
18180
+ this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
17871
18181
  }
17872
- handleIntersectionObserver(placementId, spot, element) {
17873
- const spotIsVisibleCb = async () => {
18182
+ fireImpressionEvent(placementId, spot, spotElement) {
18183
+ this.pubSub.publish(RMN_SPOT_EVENT.IMPRESSION, {
18184
+ placementId,
18185
+ spotId: spot.id,
18186
+ spotElement,
18187
+ });
18188
+ (async () => {
17874
18189
  var _a, _b;
17875
- this.pubSub.publish(RMN_SPOT_EVENT.IMPRESSION, {
17876
- placementId,
17877
- spotId: spot.id,
17878
- element,
17879
- });
17880
- this.intersectionObserver.unobserve(element);
17881
- this.activeSpots.set(spot.id, {
17882
- placementId,
17883
- element,
17884
- impressionTracked: true,
17885
- });
17886
- this.handleSpotState(placementId, {
17887
- dom: {
17888
- element,
17889
- visible: true,
17890
- },
17891
- });
17892
- // Fire impression event
17893
18190
  await this.fireEvent({
17894
18191
  event: RMN_SPOT_EVENT.IMPRESSION,
17895
18192
  eventUrl: (_b = (_a = spot.events.find((event) => event.event === RMN_SPOT_EVENT.IMPRESSION)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
17896
18193
  });
17897
- };
17898
- this.intersectionObserver.observe(element, spotIsVisibleCb);
18194
+ })();
17899
18195
  }
17900
18196
  /**
17901
18197
  * Fires an event using the navigator.sendBeacon method and redirects the user if the event is a click event.
@@ -17960,23 +18256,178 @@ class SelectionService extends BaseApi {
17960
18256
  }
17961
18257
  }
17962
18258
 
18259
+ const SPOT_EVENTS_EXAMPLE = [
18260
+ {
18261
+ event: RMN_SPOT_EVENT.CLICK,
18262
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
18263
+ },
18264
+ {
18265
+ event: RMN_SPOT_EVENT.IMPRESSION,
18266
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
18267
+ },
18268
+ {
18269
+ event: RMN_SPOT_EVENT.PURCHASE,
18270
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
18271
+ },
18272
+ {
18273
+ event: RMN_SPOT_EVENT.ADD_TO_CART,
18274
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
18275
+ },
18276
+ {
18277
+ event: RMN_SPOT_EVENT.ADD_TO_WISHLIST,
18278
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYzfQ&s=m3ISU_iIy-OFtXrTKpI6cJAEC0k',
18279
+ },
18280
+ {
18281
+ event: RMN_SPOT_EVENT.BUY_NOW,
18282
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjY5fQ&s=l6MOscQC-q-FkC2Ksd7w6jjySCQ',
18283
+ },
18284
+ ];
18285
+ const RB_SPOTS_SELECTION_EXAMPLE = {
18286
+ rbHomepageHeroFullImage: [
18287
+ {
18288
+ id: '111111_111111',
18289
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18290
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18291
+ width: 1140,
18292
+ height: 640,
18293
+ header: 'Artisanal Craft Beer Collection',
18294
+ description: 'Discover our curated selection of small-batch, flavor-packed craft beers.',
18295
+ ctaText: 'Explore the Collection',
18296
+ textColor: '#ffffff',
18297
+ ctaTextColor: '#ffffff',
18298
+ primaryImage: 'https://placehold.co/1140x640/png?text=Craft+Beer+Collection',
18299
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Craft+Beer',
18300
+ events: SPOT_EVENTS_EXAMPLE,
18301
+ },
18302
+ {
18303
+ id: '222222_222222',
18304
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18305
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18306
+ width: 1140,
18307
+ height: 640,
18308
+ header: 'Summer Wine Spectacular',
18309
+ description: 'Refresh your palate with our handpicked selection of crisp, summer wines.',
18310
+ ctaText: 'Shop Summer Wines',
18311
+ textColor: '#000000',
18312
+ ctaTextColor: '#ffffff',
18313
+ primaryImage: 'https://placehold.co/1140x640/png?text=Summer+Wines',
18314
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Summer+Wines',
18315
+ events: SPOT_EVENTS_EXAMPLE,
18316
+ },
18317
+ ],
18318
+ rbHomepageHeroTwoTile: [
18319
+ {
18320
+ id: '333333_333333',
18321
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
18322
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
18323
+ width: 1140,
18324
+ height: 640,
18325
+ header: 'Whiskey Wonderland',
18326
+ description: 'Embark on a journey through our premium whiskey selection.',
18327
+ ctaText: 'Discover Whiskeys',
18328
+ textColor: '#ffffff',
18329
+ backgroundColor: '#2c1a05',
18330
+ ctaTextColor: '#2c1a05',
18331
+ primaryImage: 'https://placehold.co/1140x640/png?text=Whiskey+Collection',
18332
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Whiskey',
18333
+ events: SPOT_EVENTS_EXAMPLE,
18334
+ },
18335
+ ],
18336
+ rbHomepageHeroThreeTile: [
18337
+ {
18338
+ id: '444444_444444',
18339
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
18340
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
18341
+ width: 1140,
18342
+ height: 640,
18343
+ header: 'Cocktail Essentials',
18344
+ description: 'Stock your bar with premium spirits and mixers for the perfect cocktail.',
18345
+ ctaText: 'Build Your Bar',
18346
+ textColor: '#ffffff',
18347
+ backgroundColor: '#1a3c4d',
18348
+ ctaTextColor: '#1a3c4d',
18349
+ primaryImage: 'https://placehold.co/1140x640/png?text=Cocktail+Spirits',
18350
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Cocktail+Mixers',
18351
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Cocktail+Kit',
18352
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Cocktail+Mixers',
18353
+ events: SPOT_EVENTS_EXAMPLE,
18354
+ },
18355
+ ],
18356
+ rbLargeCategoryImageTout: [
18357
+ {
18358
+ id: '555555_555555',
18359
+ spot: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
18360
+ variant: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
18361
+ width: 468,
18362
+ height: 410,
18363
+ header: 'Rare & Limited Edition',
18364
+ description: 'Discover our collection of hard-to-find and limited release spirits.',
18365
+ textColor: '#ffffff',
18366
+ ctaTextColor: '#ffffff',
18367
+ primaryImage: 'https://placehold.co/468x410/png?text=Rare+Spirits',
18368
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Rare+Spirits',
18369
+ ctaText: 'Shop Rare Spirits',
18370
+ events: SPOT_EVENTS_EXAMPLE,
18371
+ },
18372
+ ],
18373
+ rbSmallDiscoverTout: [
18374
+ {
18375
+ id: '666666_666666',
18376
+ spot: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
18377
+ variant: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
18378
+ width: 224,
18379
+ height: 378,
18380
+ header: 'Château Margaux 2015 Bordeaux',
18381
+ textColor: '#ffffff',
18382
+ primaryImage: 'https://placehold.co/224x378/png?text=Château+Margaux',
18383
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Château+Margaux',
18384
+ events: SPOT_EVENTS_EXAMPLE,
18385
+ },
18386
+ ],
18387
+ rbSmallCategoryImageTout: [
18388
+ {
18389
+ id: '777777_777777',
18390
+ spot: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
18391
+ variant: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
18392
+ width: 224,
18393
+ height: 410,
18394
+ header: 'Japanese Sake',
18395
+ textColor: '#ffffff',
18396
+ primaryImage: 'https://placehold.co/224x410/png?text=Japanese+Sake',
18397
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Japanese+Sake',
18398
+ events: SPOT_EVENTS_EXAMPLE,
18399
+ },
18400
+ ],
18401
+ rbCollectionBannerWithoutTextBlock: [
18402
+ {
18403
+ id: '888888_888888',
18404
+ spot: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
18405
+ variant: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
18406
+ width: 887,
18407
+ height: 344,
18408
+ primaryImage: 'https://placehold.co/887x344/png?text=Summer+Cocktails',
18409
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Summer+Cocktails',
18410
+ events: SPOT_EVENTS_EXAMPLE,
18411
+ },
18412
+ ],
18413
+ rbNavigationBanner: [
18414
+ {
18415
+ id: '999999_999999',
18416
+ spot: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
18417
+ variant: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
18418
+ width: 440,
18419
+ height: 220,
18420
+ header: 'Explore Tequilas',
18421
+ textColor: '#ffffff',
18422
+ primaryImage: 'https://placehold.co/440x220/png?text=Tequila+Collection',
18423
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Tequila+Collection',
18424
+ events: SPOT_EVENTS_EXAMPLE,
18425
+ },
18426
+ ],
18427
+ };
18428
+
17963
18429
  class LiquidCommerceRmnClient {
17964
18430
  constructor(auth) {
17965
- /**
17966
- * Returns the event manager instance.
17967
- *
17968
- * @return {EventService} - The event manager instance.
17969
- */
17970
- this.eventManager = {
17971
- subscribe: (eventType, callback
17972
- /* eslint-disable arrow-body-style */
17973
- ) => {
17974
- return this.eventService.subscribe(eventType, callback);
17975
- },
17976
- publish: (eventType, data) => {
17977
- this.eventService.publish(eventType, data);
17978
- },
17979
- };
17980
18431
  this.selectionService = SelectionService.getInstance(auth);
17981
18432
  this.elementService = ElementService.getInstance();
17982
18433
  this.eventService = EventService.getInstance();
@@ -18024,6 +18475,7 @@ class LiquidCommerceRmnClient {
18024
18475
  inject = this.preventNonExistentSpotTypes(inject);
18025
18476
  // Make the spot selection request
18026
18477
  const response = await this.spotSelectionRequest({ ...params, inject });
18478
+ // const response = await this.useSpotSelectionExample(inject);
18027
18479
  // Handle the response
18028
18480
  if (typeof response === 'object' && 'error' in response) {
18029
18481
  this.eventService.handleSpotState('all', {
@@ -18056,6 +18508,15 @@ class LiquidCommerceRmnClient {
18056
18508
  });
18057
18509
  continue;
18058
18510
  }
18511
+ // Take over placement styles
18512
+ placement.removeAttribute('style');
18513
+ placement.removeAttribute('class');
18514
+ Object.assign(placement.style, {
18515
+ width: '100%',
18516
+ height: 'auto',
18517
+ display: 'flex',
18518
+ justifyContent: 'center',
18519
+ });
18059
18520
  if (spots.length === 1) {
18060
18521
  const isInjected = this.injectOneSpotElement(item, placement, spots[0], itemConfig);
18061
18522
  if (!isInjected) {
@@ -18082,14 +18543,12 @@ class LiquidCommerceRmnClient {
18082
18543
  const request = {
18083
18544
  url: config === null || config === void 0 ? void 0 : config.url,
18084
18545
  filter,
18085
- spots: inject.map((item) => {
18086
- return {
18087
- placementId: item.placementId,
18088
- spot: item.spotType,
18089
- count: item === null || item === void 0 ? void 0 : item.count,
18090
- ...item === null || item === void 0 ? void 0 : item.filter,
18091
- };
18092
- }),
18546
+ spots: inject.map((item) => ({
18547
+ placementId: item.placementId,
18548
+ spot: item.spotType,
18549
+ count: item === null || item === void 0 ? void 0 : item.count,
18550
+ ...item === null || item === void 0 ? void 0 : item.filter,
18551
+ })),
18093
18552
  };
18094
18553
  return this.spotSelection(request);
18095
18554
  }
@@ -18127,17 +18586,15 @@ class LiquidCommerceRmnClient {
18127
18586
  this.eventService.registerSpot({
18128
18587
  spot,
18129
18588
  placementId: placement.id,
18130
- element: content,
18589
+ spotElement: content,
18131
18590
  });
18132
18591
  carouselSlides.push(content);
18133
18592
  }
18134
18593
  // Get the max width and height of the spots
18135
- const { maxWidth, maxHeight } = spots.reduce((max, spot) => {
18136
- return {
18137
- maxWidth: Math.max(max.maxWidth, spot.width),
18138
- maxHeight: Math.max(max.maxHeight, spot.height),
18139
- };
18140
- }, { maxWidth: 0, maxHeight: 0 });
18594
+ const { maxWidth, maxHeight } = spots.reduce((max, spot) => ({
18595
+ maxWidth: Math.max(max.maxWidth, spot.width),
18596
+ maxHeight: Math.max(max.maxHeight, spot.height),
18597
+ }), { maxWidth: 0, maxHeight: 0 });
18141
18598
  // Create the carousel element
18142
18599
  const carouselElement = this.elementService.createCarouselElement({
18143
18600
  slides: carouselSlides,
@@ -18161,7 +18618,7 @@ class LiquidCommerceRmnClient {
18161
18618
  placement.replaceChildren(carouselElement);
18162
18619
  this.eventService.handleSpotState(placement.id, {
18163
18620
  dom: {
18164
- element: carouselElement,
18621
+ spotElement: carouselElement,
18165
18622
  },
18166
18623
  state: {
18167
18624
  mounted: true,
@@ -18222,12 +18679,12 @@ class LiquidCommerceRmnClient {
18222
18679
  this.eventService.registerSpot({
18223
18680
  spot: spotData,
18224
18681
  placementId: injectItem.placementId,
18225
- element: spotElement,
18682
+ spotElement,
18226
18683
  });
18227
18684
  placement.replaceChildren(spotElement);
18228
18685
  this.eventService.handleSpotState(injectItem.placementId, {
18229
18686
  dom: {
18230
- element: spotElement,
18687
+ spotElement,
18231
18688
  },
18232
18689
  state: {
18233
18690
  mounted: true,
@@ -18288,6 +18745,17 @@ class LiquidCommerceRmnClient {
18288
18745
  });
18289
18746
  }
18290
18747
  }
18748
+ useSpotSelectionExample(inject) {
18749
+ const examples = RB_SPOTS_SELECTION_EXAMPLE;
18750
+ const data = {};
18751
+ inject.map((item) => {
18752
+ var _a, _b, _c;
18753
+ data[item.placementId] = (_c = (_a = examples[item.spotType]) === null || _a === void 0 ? void 0 : _a.slice(0, (_b = item.count) !== null && _b !== void 0 ? _b : 1)) !== null && _c !== void 0 ? _c : [];
18754
+ });
18755
+ return new Promise((resolve) => {
18756
+ resolve(data);
18757
+ });
18758
+ }
18291
18759
  }
18292
18760
  /**
18293
18761
  * Creates a new instance of the RmnClient.
@@ -18302,6 +18770,36 @@ async function RmnClient(apiKey, config) {
18302
18770
  const credentials = await authService.initialize();
18303
18771
  return new LiquidCommerceRmnClient(credentials);
18304
18772
  }
18773
+ /**
18774
+ * Creates a new instance of the RmnEventManager.
18775
+ *
18776
+ * @return {IRmnEventManager} - The RmnEventManager instance.
18777
+ */
18778
+ function RmnEventManager() {
18779
+ const eventService = EventService.getInstance();
18780
+ return {
18781
+ /**
18782
+ * Subscribes to an event type.
18783
+ */
18784
+ subscribe: (eventType, callback
18785
+ /* eslint-disable arrow-body-style */
18786
+ ) => {
18787
+ return eventService.subscribe(eventType, callback);
18788
+ },
18789
+ /**
18790
+ * Publishes an event type.
18791
+ */
18792
+ publish: (eventType, data) => {
18793
+ eventService.publish(eventType, data);
18794
+ },
18795
+ /**
18796
+ * Destroys a spot element
18797
+ */
18798
+ destroySpot: (placementId) => {
18799
+ eventService.unregisterSpot(placementId);
18800
+ },
18801
+ };
18802
+ }
18305
18803
  /**
18306
18804
  * Creates the spot html element based on the provided data using shadow dom.
18307
18805
  *
@@ -18334,4 +18832,4 @@ function RmnCreateSpotElement(spot, config) {
18334
18832
  });
18335
18833
  }
18336
18834
 
18337
- export { LiquidCommerceRmnClient, RMN_ENV, RMN_FILTER_PROPERTIES, RMN_SPOT_EVENT, RMN_SPOT_TYPE, RmnClient, RmnCreateSpotElement };
18835
+ export { LiquidCommerceRmnClient, RMN_ENV, RMN_FILTER_PROPERTIES, RMN_SPOT_EVENT, RMN_SPOT_TYPE, RmnClient, RmnCreateSpotElement, RmnEventManager };