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

Sign up to get free protection for your applications and to get access to all the features.
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 };