@liquidcommercedev/rmn-sdk 1.4.6 → 1.5.0-beta.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. package/dist/index.cjs +2463 -720
  2. package/dist/index.esm.js +2463 -721
  3. package/dist/types/common/helpers/uuid.helper.d.ts +49 -0
  4. package/dist/types/enums.d.ts +59 -1
  5. package/dist/types/index.umd.d.ts +2 -2
  6. package/dist/types/modules/element/component/carousel/carousel.component.d.ts +3 -0
  7. package/dist/types/modules/element/component/carousel/carousel.interface.d.ts +35 -0
  8. package/dist/types/modules/element/component/carousel/carousel.style.d.ts +2 -0
  9. package/dist/types/modules/element/component/carousel/index.d.ts +3 -0
  10. package/dist/types/modules/element/component/spot/index.d.ts +2 -0
  11. package/dist/types/modules/element/component/spot/spot.component.d.ts +3 -0
  12. package/dist/types/modules/element/component/spot/spot.interface.d.ts +10 -0
  13. package/dist/types/modules/element/component/utils.d.ts +1 -0
  14. package/dist/types/modules/{spot/html/constants/html.constant.d.ts → element/element.constant.d.ts} +2 -5
  15. package/dist/types/modules/element/element.interface.d.ts +52 -0
  16. package/dist/types/modules/element/element.service.d.ts +40 -0
  17. package/dist/types/modules/element/index.d.ts +3 -0
  18. package/dist/types/modules/element/template/helper.d.ts +4 -0
  19. package/dist/types/modules/element/template/index.d.ts +1 -0
  20. package/dist/types/modules/element/template/reservebar/collection-banner-without-text-block.template.d.ts +3 -0
  21. package/dist/types/modules/element/template/reservebar/homepage-hero-full-image.template.d.ts +3 -0
  22. package/dist/types/modules/element/template/reservebar/homepage-hero-three-tile.template.d.ts +3 -0
  23. package/dist/types/modules/element/template/reservebar/homepage-hero-two-tile.template.d.ts +3 -0
  24. package/dist/types/modules/element/template/reservebar/large-category-image-tout.template.d.ts +3 -0
  25. package/dist/types/modules/element/template/reservebar/navigation-banner.template.d.ts +3 -0
  26. package/dist/types/modules/element/template/reservebar/small-category-image-tout.template.d.ts +3 -0
  27. package/dist/types/modules/element/template/reservebar/small-discover-tout.template.d.ts +3 -0
  28. package/dist/types/modules/element/template/template.service.d.ts +11 -0
  29. package/dist/types/modules/element/template/template.type.d.ts +11 -0
  30. package/dist/types/modules/event/event.constant.d.ts +1 -0
  31. package/dist/types/modules/event/event.interface.d.ts +72 -0
  32. package/dist/types/modules/event/event.service.d.ts +47 -0
  33. package/dist/types/modules/event/helpers/index.d.ts +3 -0
  34. package/dist/types/modules/event/helpers/intersection.service.d.ts +8 -0
  35. package/dist/types/modules/event/helpers/localstorage.service.d.ts +26 -0
  36. package/dist/types/modules/event/helpers/resize.service.d.ts +30 -0
  37. package/dist/types/modules/event/index.d.ts +4 -0
  38. package/dist/types/modules/event/pubsub.d.ts +69 -0
  39. package/dist/types/modules/selection/index.d.ts +4 -0
  40. package/dist/types/modules/selection/selection.constant.d.ts +1 -0
  41. package/dist/types/modules/{spot/spot.interface.d.ts → selection/selection.interface.d.ts} +10 -13
  42. package/dist/types/modules/selection/selection.service.d.ts +18 -0
  43. package/dist/types/modules/{spot/spot.type.d.ts → selection/selection.type.d.ts} +4 -3
  44. package/dist/types/rmn-client.d.ts +66 -23
  45. package/dist/types/static.constant.d.ts +4 -0
  46. package/dist/types/types.d.ts +17 -6
  47. package/package.json +2 -2
  48. package/umd/liquidcommerce-rmn-sdk.min.js +1 -1
  49. package/dist/types/modules/spot/html/constants/index.d.ts +0 -1
  50. package/dist/types/modules/spot/html/index.d.ts +0 -1
  51. package/dist/types/modules/spot/html/spot.element.service.d.ts +0 -7
  52. package/dist/types/modules/spot/html/templates/index.d.ts +0 -1
  53. package/dist/types/modules/spot/html/templates/reservebar/collection-banner-without-text-block.template.d.ts +0 -2
  54. package/dist/types/modules/spot/html/templates/reservebar/homepage-hero-full-image.template.d.ts +0 -2
  55. package/dist/types/modules/spot/html/templates/reservebar/homepage-hero-three-tile.template.d.ts +0 -2
  56. package/dist/types/modules/spot/html/templates/reservebar/homepage-hero-two-tile.template.d.ts +0 -2
  57. package/dist/types/modules/spot/html/templates/reservebar/large-category-image-tout.template.d.ts +0 -2
  58. package/dist/types/modules/spot/html/templates/reservebar/navigation-banner.template.d.ts +0 -2
  59. package/dist/types/modules/spot/html/templates/reservebar/small-category-image-tout.template.d.ts +0 -2
  60. package/dist/types/modules/spot/html/templates/reservebar/small-discover-tout.template.d.ts +0 -2
  61. package/dist/types/modules/spot/html/templates/spot.template.d.ts +0 -21
  62. package/dist/types/modules/spot/index.d.ts +0 -6
  63. package/dist/types/modules/spot/spot.constant.d.ts +0 -4
  64. package/dist/types/modules/spot/spot.enum.d.ts +0 -57
  65. package/dist/types/modules/spot/spot.html.service.d.ts +0 -22
  66. package/dist/types/modules/spot/spot.selection.service.d.ts +0 -15
  67. /package/dist/types/modules/{spot/html/templates → element/template}/iab/billboard/billboard-v1.template.d.ts +0 -0
  68. /package/dist/types/modules/{spot/html/templates → element/template}/iab/billboard/billboard-v2.template.d.ts +0 -0
  69. /package/dist/types/modules/{spot/html/templates → element/template}/iab/billboard/billboard-v3.template.d.ts +0 -0
  70. /package/dist/types/modules/{spot/html/templates → element/template}/iab/billboard/index.d.ts +0 -0
  71. /package/dist/types/modules/{spot/html/templates → element/template}/iab/in-text/in-text-v1.template.d.ts +0 -0
  72. /package/dist/types/modules/{spot/html/templates → element/template}/iab/in-text/index.d.ts +0 -0
  73. /package/dist/types/modules/{spot/html/templates → element/template}/iab/index.d.ts +0 -0
  74. /package/dist/types/modules/{spot/html/templates → element/template}/iab/large-leaderboard/index.d.ts +0 -0
  75. /package/dist/types/modules/{spot/html/templates → element/template}/iab/large-leaderboard/large-leaderboard-v1.template.d.ts +0 -0
  76. /package/dist/types/modules/{spot/html/templates → element/template}/iab/large-leaderboard/large-leaderboard-v2.template.d.ts +0 -0
  77. /package/dist/types/modules/{spot/html/templates → element/template}/iab/large-rectangle/index.d.ts +0 -0
  78. /package/dist/types/modules/{spot/html/templates → element/template}/iab/large-rectangle/large-rectangle-v1.template.d.ts +0 -0
  79. /package/dist/types/modules/{spot/html/templates → element/template}/iab/square/index.d.ts +0 -0
  80. /package/dist/types/modules/{spot/html/templates → element/template}/iab/square/square-v1.template.d.ts +0 -0
  81. /package/dist/types/modules/{spot/html/templates → element/template}/iab/square/square-v2.template.d.ts +0 -0
  82. /package/dist/types/modules/{spot/html/templates → element/template}/iab/vertical-rectangle/index.d.ts +0 -0
  83. /package/dist/types/modules/{spot/html/templates → element/template}/iab/vertical-rectangle/vertical-rectangle-v1.template.d.ts +0 -0
  84. /package/dist/types/modules/{spot/html/templates → element/template}/iab/wide-skyscraper/index.d.ts +0 -0
  85. /package/dist/types/modules/{spot/html/templates → element/template}/iab/wide-skyscraper/wide-skyscraper-v1.template.d.ts +0 -0
  86. /package/dist/types/modules/{spot/html/templates → element/template}/reservebar/index.d.ts +0 -0
package/dist/index.esm.js CHANGED
@@ -42,6 +42,7 @@ var RMN_SPOT_TYPE;
42
42
  var RMN_FILTER_PROPERTIES;
43
43
  (function (RMN_FILTER_PROPERTIES) {
44
44
  RMN_FILTER_PROPERTIES["KEYWORDS"] = "keywords";
45
+ RMN_FILTER_PROPERTIES["PAGE_LOCATION"] = "pageLocation";
45
46
  RMN_FILTER_PROPERTIES["PARENTCO"] = "parentCo";
46
47
  RMN_FILTER_PROPERTIES["BRAND"] = "brand";
47
48
  RMN_FILTER_PROPERTIES["CATEGORY"] = "category";
@@ -53,6 +54,7 @@ var RMN_FILTER_PROPERTIES;
53
54
  })(RMN_FILTER_PROPERTIES || (RMN_FILTER_PROPERTIES = {}));
54
55
  var RMN_SPOT_EVENT;
55
56
  (function (RMN_SPOT_EVENT) {
57
+ RMN_SPOT_EVENT["LIFECYCLE_STATE"] = "LIFECYCLE_STATE";
56
58
  RMN_SPOT_EVENT["IMPRESSION"] = "IMPRESSION";
57
59
  RMN_SPOT_EVENT["CLICK"] = "CLICK";
58
60
  RMN_SPOT_EVENT["PURCHASE"] = "PURCHASE";
@@ -60,7 +62,6 @@ var RMN_SPOT_EVENT;
60
62
  RMN_SPOT_EVENT["ADD_TO_WISHLIST"] = "ADD_TO_WISHLIST";
61
63
  RMN_SPOT_EVENT["BUY_NOW"] = "BUY_NOW";
62
64
  })(RMN_SPOT_EVENT || (RMN_SPOT_EVENT = {}));
63
-
64
65
  var RMN_ENV;
65
66
  (function (RMN_ENV) {
66
67
  RMN_ENV["LOCAL"] = "local";
@@ -14997,7 +14998,7 @@ class BaseApi extends BaseApiAbstract {
14997
14998
  */
14998
14999
  async post(path, data, configOverrides) {
14999
15000
  let requestData = data;
15000
- if (this.authInfo.env !== RMN_ENV.DEVELOPMENT) {
15001
+ if (![RMN_ENV.LOCAL, RMN_ENV.DEVELOPMENT].includes(this.authInfo.env)) {
15001
15002
  const timestamp = new Date().getTime();
15002
15003
  configOverrides = {
15003
15004
  ...configOverrides,
@@ -15150,9 +15151,7 @@ class AuthService extends BaseApi {
15150
15151
  }
15151
15152
 
15152
15153
  const SPOT_ELEMENT_TAG = 'spot-element';
15153
- const SPOT_ELEMENT_SLOT_NAME = `${SPOT_ELEMENT_TAG}-custom-content`;
15154
- const SPOTS_SELECTION_API_PATH = '/spots/selection';
15155
-
15154
+ const CAROUSEL_ELEMENT_TAG = 'spot-carousel-element';
15156
15155
  const GFONT_PRECONNECT = `
15157
15156
  <link rel="preconnect" href="https://fonts.googleapis.com">
15158
15157
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@@ -15163,141 +15162,1156 @@ const GFONT_SOURCE_SANS_3 = `
15163
15162
  const GFONT_CORMORANT = `
15164
15163
  <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">
15165
15164
  `;
15166
- const SPOT_ELEMENT_TEMPLATE = (width, height, hasCustomContent) => {
15167
- const style = document.createElement('style');
15168
- style.textContent = `
15169
- :host {
15170
- display: block;
15171
- position: relative;
15172
- container-type: inline-size;
15173
- overflow: hidden;
15165
+
15166
+ class IntersectionObserverService {
15167
+ constructor(defaultOptions = {}) {
15168
+ this.observers = new Map();
15169
+ this.defaultOptions = {
15170
+ root: null,
15171
+ rootMargin: '0px',
15172
+ threshold: 0.5,
15173
+ ...defaultOptions,
15174
+ };
15174
15175
  }
15175
- :host([fluid="true"]) {
15176
- width: 100%;
15177
- height: 100%;
15176
+ observe(element, callback, options = {}) {
15177
+ const mergedOptions = { ...this.defaultOptions, ...options };
15178
+ const ioCallback = (entries) => {
15179
+ entries.forEach((entry) => {
15180
+ if (entry.isIntersecting) {
15181
+ callback(entry);
15182
+ }
15183
+ });
15184
+ };
15185
+ const observer = new IntersectionObserver(ioCallback, mergedOptions);
15186
+ this.observers.set(element, observer);
15187
+ observer.observe(element);
15188
+ }
15189
+ unobserve(element) {
15190
+ const observer = this.observers.get(element);
15191
+ if (observer) {
15192
+ observer.unobserve(element);
15193
+ observer.disconnect();
15194
+ this.observers.delete(element);
15195
+ }
15178
15196
  }
15179
- :host([fluid="false"]) {
15180
- width: ${width}px;
15181
- height: ${height}px;
15197
+ unobserveAll() {
15198
+ this.observers.forEach((observer, element) => {
15199
+ observer.unobserve(element);
15200
+ observer.disconnect();
15201
+ });
15202
+ this.observers.clear();
15182
15203
  }
15183
- `;
15184
- const wrapper = document.createElement('div');
15185
- wrapper.className = 'wrapper';
15186
- const slot = document.createElement('slot');
15187
- slot.name = SPOT_ELEMENT_SLOT_NAME;
15188
- if (!hasCustomContent) {
15189
- style.textContent += `
15190
- :host .wrapper {
15191
- position: absolute;
15192
- top: 0;
15193
- left: 0;
15204
+ }
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
+
15278
+ class ResizeObserverService {
15279
+ constructor({ element, maxSize, minScale }) {
15280
+ this.element = element;
15281
+ if (!element.parentElement) {
15282
+ throw new Error('RmnSdk: Spot element must have a parent container.');
15283
+ }
15284
+ this.container = element.parentElement;
15285
+ this.setDimensions(maxSize, minScale);
15286
+ this.resizeObserver = new ResizeObserver(() => this.updateElementSize());
15287
+ this.resizeObserver.observe(this.container);
15288
+ // Initial size update
15289
+ this.updateElementSize();
15290
+ }
15291
+ setDimensions(maxSize, minScale) {
15292
+ if (minScale <= 0 || minScale > 1) {
15293
+ throw new Error('RmnSdk: Invalid minScale value');
15294
+ }
15295
+ const minSize = {
15296
+ width: maxSize.width * minScale,
15297
+ height: maxSize.height * minScale,
15298
+ };
15299
+ if (maxSize.width <= 0 || maxSize.height <= 0 || minSize.width <= 0 || minSize.height <= 0) {
15300
+ throw new Error('RmnSdk: Invalid dimensions');
15301
+ }
15302
+ if (minSize.width > maxSize.width || minSize.height > maxSize.height) {
15303
+ throw new Error('RmnSdk: Minimum size cannot be greater than maximum size');
15304
+ }
15305
+ this.maxSize = maxSize;
15306
+ this.minSize = minSize;
15307
+ this.aspectRatio = this.maxSize.width / this.maxSize.height;
15308
+ }
15309
+ updateElementSize() {
15310
+ const { clientWidth: containerWidth, clientHeight: containerHeight } = this.container;
15311
+ let newWidth;
15312
+ let newHeight;
15313
+ // First, try to fit the maximum width
15314
+ newWidth = Math.min(containerWidth, this.maxSize.width);
15315
+ newHeight = newWidth / this.aspectRatio;
15316
+ // If the height exceeds the container, adjust based on height
15317
+ if (newWidth > containerWidth) {
15318
+ newWidth = containerWidth;
15319
+ newHeight = containerHeight * this.aspectRatio;
15320
+ }
15321
+ // Ensure we're not going below minimum dimensions
15322
+ newWidth = Math.max(newWidth, this.minSize.width);
15323
+ newHeight = Math.max(newHeight, this.minSize.height);
15324
+ this.element.style.width = `${newWidth}px`;
15325
+ this.element.style.height = `${newHeight}px`;
15326
+ // Calculate the scale percentage
15327
+ const scaleWidth = newWidth / this.maxSize.width;
15328
+ const scaleHeight = newHeight / this.maxSize.height;
15329
+ const scale = Math.min(scaleWidth, scaleHeight);
15330
+ // Dispatch a custom event
15331
+ this.element.dispatchEvent(new CustomEvent('spotSizeChanged', {
15332
+ detail: {
15333
+ width: this.maxSize.width,
15334
+ height: this.maxSize.height,
15335
+ newWidth,
15336
+ newHeight,
15337
+ scale,
15338
+ },
15339
+ }));
15340
+ }
15341
+ disconnect() {
15342
+ this.resizeObserver.disconnect();
15343
+ }
15344
+ }
15345
+
15346
+ function calculateScaleFactor(elementScale) {
15347
+ // Step 1: Apply square root for non-linear scaling
15348
+ // This creates a more gradual scaling effect, especially for larger changes
15349
+ // For example:
15350
+ // - elementScale of 0.25 (1/4 size) becomes 0.5
15351
+ // - elementScale of 1 (unchanged) remains 1
15352
+ // - elementScale of 4 (4x size) becomes 2
15353
+ const baseFactor = Math.sqrt(elementScale);
15354
+ // Step 2: Apply additional dampening to further soften the scaling effect
15355
+ // The dampening factor (0.5) can be adjusted:
15356
+ // - Lower values (closer to 0) make scaling more subtle
15357
+ // - Higher values (closer to 1) make scaling more pronounced
15358
+ const dampening = 0.35;
15359
+ // Calculate the scaleFactor:
15360
+ // 1. (baseFactor - 1) represents the change from the original size
15361
+ // 2. Multiply by dampening to reduce the effect
15362
+ // 3. Add 1 to center the scaling around the original size
15363
+ // For example, if baseFactor is 2:
15364
+ // scaleFactor = 1 + (2 - 1) * 0.5 = 1.5
15365
+ const scaleFactor = 1 + (baseFactor - 1) * dampening;
15366
+ // Step 3: Define the allowed range for the scale factor
15367
+ // This ensures that the font size never changes too drastically
15368
+ const minScale = 0.35; // Font will never be smaller than 50% of original
15369
+ const maxScale = 1.5; // Font will never be larger than 150% of original
15370
+ // Step 4: Clamp the scale factor to the defined range
15371
+ // Math.min ensures the value doesn't exceed maxScale
15372
+ // Math.max ensures the value isn't less than minScale
15373
+ return Math.max(minScale, Math.min(maxScale, scaleFactor));
15374
+ }
15375
+
15376
+ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
15377
+ :host {
15378
+ position: relative;
15379
+ display: inline-block;
15380
+ margin: 0;
15381
+ overflow: hidden;
15382
+ width: ${fluid ? '100%' : `${width}px`};
15383
+ height: ${fluid ? '100%' : `${height}px`};
15384
+ }
15385
+
15386
+ .slides {
15387
+ position: relative;
15388
+ height: 100%;
15194
15389
  width: 100%;
15390
+ }
15391
+
15392
+ .slide {
15393
+ display: none;
15394
+
15395
+ justify-content: center;
15396
+ align-items: center;
15195
15397
  height: 100%;
15196
- overflow: hidden;
15197
- }
15198
- `;
15398
+ width: 100%;
15199
15399
  }
15200
- return { style, wrapper, slot };
15201
- };
15202
15400
 
15203
- const STYLES$i = ({ primaryImage, secondaryImage }) => `
15204
- <style>
15205
- .content {
15206
- width: 100%;
15207
- height: 100%;
15208
- display: flex;
15209
- flex-direction: row;
15210
- background-color: #FFFFFF;
15211
- gap: 0.5cqw;
15212
- cursor: pointer;
15213
- color: inherit;
15214
- }
15215
- .big-image {
15216
- width: 65%;
15217
- height: 100%;
15218
- background-image: url("${primaryImage}");
15219
- background-position: center;
15220
- background-repeat: no-repeat;
15221
- background-size: cover;
15401
+ .slide.active {
15402
+ display: flex;
15403
+ }
15404
+
15405
+ .dots {
15406
+ position: absolute;
15407
+ display: flex;
15408
+ align-items: center;
15409
+ gap: 8px;
15410
+ opacity: var(--opacity, 1);
15411
+ }
15412
+
15413
+ .dots.small .dot {
15414
+ width: 8px;
15415
+ height: 8px;
15416
+ }
15417
+
15418
+ .dots.base .dot {
15419
+ width: 12px;
15420
+ height: 12px;
15421
+ }
15422
+
15423
+ .dots.large .dot {
15424
+ width: 16px;
15425
+ height: 16px;
15426
+ }
15427
+
15428
+ .dots .dot {
15429
+ border-radius: 50%;
15430
+ cursor: pointer;
15431
+ transition: all 0.3s ease;
15432
+ }
15433
+
15434
+ .dots.top-left,
15435
+ .dots.bottom-left {
15436
+ left: 10px;
15437
+ }
15438
+
15439
+ .dots.top-center,
15440
+ .dots.bottom-center {
15441
+ left: 50%;
15442
+ transform: translateX(-50%);
15443
+ }
15444
+
15445
+ .dots.top-right,
15446
+ .dots.bottom-right {
15447
+ right: 10px;
15448
+ }
15449
+
15450
+ .dots.top-left,
15451
+ .dots.top-center,
15452
+ .dots.top-right {
15453
+ top: 10px;
15454
+ }
15455
+
15456
+ .dots.bottom-left,
15457
+ .dots.bottom-center,
15458
+ .dots.bottom-right {
15459
+ bottom: 10px;
15460
+ }
15461
+
15462
+ .dots.middle-left {
15463
+ left: 10px;
15464
+ top: 50%;
15465
+ transform: translateY(-50%);
15466
+ flex-direction: column;
15467
+ }
15468
+
15469
+ .dots.middle-right {
15470
+ right: 10px;
15471
+ top: 50%;
15472
+ transform: translateY(-50%);
15473
+ flex-direction: column;
15474
+ }
15475
+
15476
+ .buttons {
15477
+ opacity: var(--opacity, 1);
15478
+ }
15479
+
15480
+ .buttons button {
15481
+ background-color: #00000080;
15482
+ color: #fff;
15483
+ border: none;
15484
+ padding: 10px;
15485
+ cursor: pointer;
15486
+ transition: background-color 0.3s ease;
15487
+ }
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
+
15501
+ .buttons button:hover {
15502
+ background-color: #000000b3;
15503
+ }
15504
+
15505
+ .buttons.buttons-separate button {
15506
+ position: absolute;
15507
+ top: 50%;
15508
+ transform: translateY(-50%);
15509
+ }
15510
+
15511
+ .buttons.buttons-separate .prev-button {
15512
+ left: 10px;
15513
+ }
15514
+
15515
+ .buttons.buttons-separate .next-button {
15516
+ right: 10px;
15517
+ }
15518
+
15519
+ .buttons.buttons-together {
15520
+ position: absolute;
15521
+ display: flex;
15522
+ gap: 10px;
15523
+ }
15524
+
15525
+ .buttons.buttons-together.top-left,
15526
+ .buttons.buttons-together.bottom-left {
15527
+ left: 10px;
15528
+ }
15529
+
15530
+ .buttons.buttons-together.top-center,
15531
+ .buttons.buttons-together.bottom-center {
15532
+ left: 50%;
15533
+ transform: translateX(-50%);
15534
+ }
15535
+
15536
+ .buttons.buttons-together.top-right,
15537
+ .buttons.buttons-together.bottom-right {
15538
+ right: 10px;
15539
+ }
15540
+
15541
+ .buttons.buttons-together.top-left,
15542
+ .buttons.buttons-together.top-center,
15543
+ .buttons.buttons-together.top-right {
15544
+ top: 10px;
15545
+ }
15546
+
15547
+ .buttons.buttons-together.bottom-left,
15548
+ .buttons.buttons-together.bottom-center,
15549
+ .buttons.buttons-together.bottom-right {
15550
+ bottom: 10px;
15551
+ }
15552
+
15553
+ .buttons.buttons-together.middle-left,
15554
+ .buttons.buttons-together.middle-right {
15555
+ top: 50%;
15556
+ transform: translateY(-50%);
15557
+ flex-direction: column;
15558
+ }
15559
+
15560
+ .buttons.buttons-together.middle-left {
15561
+ left: 10px;
15562
+ }
15563
+
15564
+ .buttons.buttons-together.middle-right {
15565
+ right: 10px;
15566
+ }
15567
+
15568
+ @media (max-width: 768px) {
15569
+ .buttons button {
15570
+ padding: 8px 12px;
15571
+ font-size: 14px;
15222
15572
  }
15223
- .main {
15224
- width: 35%;
15225
- height: 100%;
15226
- display: flex;
15227
- flex-direction: column;
15228
- gap: 0.5cqw;
15573
+ }
15574
+ `;
15575
+
15576
+ let CarouselElement;
15577
+ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
15578
+ class CustomCarouselElement extends HTMLElement {
15579
+ constructor() {
15580
+ super();
15581
+ this.currentSlide = 0;
15582
+ this.dotElements = [];
15583
+ this.prevButton = null;
15584
+ this.nextButton = null;
15585
+ this.autoplayInterval = null;
15586
+ this.useDots = false;
15587
+ this.useButtons = false;
15588
+ this.originalFontSizes = new Map();
15589
+ this.attachShadow({ mode: 'open' });
15229
15590
  }
15230
- .small-image {
15231
- width: 100%;
15232
- height: 50%;
15233
- background-image: url("${secondaryImage}");
15234
- background-position: center;
15235
- background-repeat: no-repeat;
15236
- background-size: cover;
15591
+ connectedCallback() {
15592
+ this.initializeOptions();
15593
+ this.setupResizeObserver();
15594
+ this.render();
15595
+ this.setupCarousel();
15237
15596
  }
15238
- .text {
15239
- background: #E8E6DE;
15240
- text-align: center;
15241
- display: flex;
15242
- flex-direction: column;
15243
- justify-content: center;
15244
- align-items: center;
15245
- height: 50%;
15246
- width: 100%;
15247
- padding: 5% 10%;
15248
- box-sizing: border-box;
15249
- font-family: "Source Sans 3", sans-serif;
15597
+ disconnectedCallback() {
15598
+ var _a;
15599
+ this.stopAutoplay();
15600
+ (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
15250
15601
  }
15251
- .header {
15252
- font-size: 2.2cqw;
15253
- color: #000000;
15254
- font-style: normal;
15255
- font-weight: 400;
15256
- font-family: "Cormorant", serif;
15257
- margin: 0;
15602
+ initializeOptions() {
15603
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
15604
+ this.useDots = ((_a = this.data) === null || _a === void 0 ? void 0 : _a.useDots) === true || typeof ((_b = this.data) === null || _b === void 0 ? void 0 : _b.useDots) === 'object';
15605
+ this.useButtons = ((_c = this.data) === null || _c === void 0 ? void 0 : _c.useButtons) === true || typeof ((_d = this.data) === null || _d === void 0 ? void 0 : _d.useButtons) === 'object';
15606
+ this.autoplay = (_f = (_e = this.data) === null || _e === void 0 ? void 0 : _e.autoplay) !== null && _f !== void 0 ? _f : true;
15607
+ this.interval = (_h = (_g = this.data) === null || _g === void 0 ? void 0 : _g.interval) !== null && _h !== void 0 ? _h : CustomCarouselElement.defaultInterval;
15608
+ this.dotsOptions = {
15609
+ position: 'bottom-center',
15610
+ color: '#d9d9d9',
15611
+ activeColor: '#b5914a',
15612
+ size: 'base',
15613
+ opacity: 1,
15614
+ ...(typeof ((_j = this.data) === null || _j === void 0 ? void 0 : _j.useDots) === 'object' ? this.data.useDots : {}),
15615
+ };
15616
+ this.buttonsOptions = {
15617
+ together: false,
15618
+ position: 'middle-sides',
15619
+ textColor: '#000000',
15620
+ backgroundColor: '#ffffff',
15621
+ borderRadius: '50%',
15622
+ prev: 'Prev',
15623
+ next: 'Next',
15624
+ size: 'base',
15625
+ opacity: 1,
15626
+ ...(typeof ((_k = this.data) === null || _k === void 0 ? void 0 : _k.useButtons) === 'object' ? this.data.useButtons : {}),
15627
+ };
15628
+ this.validateOptions();
15258
15629
  }
15259
- .description {
15260
- font-size: 1.2cqw;
15261
- color: #000000;
15262
- font-style: normal;
15263
- font-weight: 400;
15264
- margin: 1cqh 0;
15630
+ setupResizeObserver() {
15631
+ if (this.data && !this.data.fluid) {
15632
+ this.resizeObserver = new ResizeObserverService({
15633
+ element: this,
15634
+ maxSize: {
15635
+ width: this.data.width,
15636
+ height: this.data.height,
15637
+ },
15638
+ minScale: this.data.minScale,
15639
+ });
15640
+ this.addEventListener('spotSizeChanged', this.handleCarouselSizeChanged.bind(this));
15641
+ }
15265
15642
  }
15266
- .button {
15267
- font-size: 1cqw;
15268
- color: #000000;
15269
- background-color: transparent;
15270
- font-style: normal;
15271
- font-weight: 700;
15272
- text-decoration: underline;
15273
- text-transform: uppercase;
15274
- border: none;
15275
- cursor: pointer;
15276
- padding: 0;
15277
- transition: opacity 0.3s ease;
15643
+ handleCarouselSizeChanged(event) {
15644
+ const isRBSpot = 'rbHeroadaksda'.startsWith('rb');
15645
+ if (!isRBSpot) {
15646
+ // Adjust text elements font size based on the scale factor
15647
+ this.adjustFontSize(event.detail.scale);
15648
+ }
15278
15649
  }
15279
- .content:hover .button {
15280
- opacity: 0.7;
15650
+ adjustFontSize(elementScale) {
15651
+ var _a;
15652
+ const scaleFactor = calculateScaleFactor(elementScale);
15653
+ // Find all text elements within the shadow root
15654
+ const elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('h1, h2, h3, h4, p, span');
15655
+ elements === null || elements === void 0 ? void 0 : elements.forEach((element) => {
15656
+ if (element instanceof HTMLElement) {
15657
+ if (!this.originalFontSizes.has(element)) {
15658
+ const originalSize = parseFloat(window.getComputedStyle(element).fontSize);
15659
+ this.originalFontSizes.set(element, originalSize);
15660
+ }
15661
+ const originalSize = this.originalFontSizes.get(element);
15662
+ const newFontSize = originalSize * scaleFactor;
15663
+ element.style.fontSize = `${newFontSize}px`;
15664
+ }
15665
+ });
15281
15666
  }
15282
- @container (max-width: 600px) {
15283
- .header {
15284
- font-size: 3cqw;
15667
+ render() {
15668
+ var _a;
15669
+ if (!this.shadowRoot)
15670
+ return;
15671
+ const style = document.createElement('style');
15672
+ style.textContent = CAROUSEL_COMPONENT_STYLE(this.data);
15673
+ this.shadowRoot.appendChild(style);
15674
+ const slides = this.renderSlides();
15675
+ this.shadowRoot.appendChild(slides);
15676
+ this.slidesContainer = (_a = this.shadowRoot.querySelector('.slides')) !== null && _a !== void 0 ? _a : undefined;
15677
+ if (this.useDots) {
15678
+ const dots = this.renderDots();
15679
+ if (dots)
15680
+ this.shadowRoot.appendChild(dots);
15285
15681
  }
15286
- .description {
15287
- font-size: 1.6cqw;
15288
- margin: 0;
15682
+ if (this.useButtons) {
15683
+ const buttons = this.renderButtons();
15684
+ if (buttons)
15685
+ this.shadowRoot.appendChild(buttons);
15289
15686
  }
15290
- .button {
15291
- font-size: 1.4cqw;
15687
+ }
15688
+ setupCarousel() {
15689
+ this.setupDots();
15690
+ this.setupButtons();
15691
+ if (this.autoplay) {
15692
+ this.startAutoplay();
15292
15693
  }
15694
+ this.updateCarousel();
15293
15695
  }
15294
- </style>
15295
- `;
15296
- function billboardV1Template(spot) {
15297
- return `
15298
- ${GFONT_PRECONNECT}
15299
- ${GFONT_SOURCE_SANS_3}
15300
- ${GFONT_CORMORANT}
15696
+ renderSlides() {
15697
+ const slidesContainer = document.createElement('div');
15698
+ slidesContainer.className = 'slides';
15699
+ this.slides.forEach((slide, index) => {
15700
+ const slideElement = document.createElement('div');
15701
+ slideElement.className = `slide ${index === this.currentSlide ? 'active' : ''}`;
15702
+ if (slide instanceof HTMLElement) {
15703
+ slideElement.appendChild(slide);
15704
+ }
15705
+ slidesContainer.appendChild(slideElement);
15706
+ });
15707
+ return slidesContainer;
15708
+ }
15709
+ renderDots() {
15710
+ const dotsContainer = document.createElement('div');
15711
+ dotsContainer.className = `dots ${this.dotsOptions.position} ${this.dotsOptions.size}`;
15712
+ dotsContainer.style.cssText = `--opacity: ${this.dotsOptions.opacity}`;
15713
+ this.slides.forEach((_, index) => {
15714
+ const dot = document.createElement('span');
15715
+ dot.className = `dot ${index === this.currentSlide ? 'active' : ''}`;
15716
+ dot.style.backgroundColor = this.dotsOptions.color;
15717
+ dotsContainer.appendChild(dot);
15718
+ });
15719
+ return dotsContainer;
15720
+ }
15721
+ renderButtons() {
15722
+ const buttonsContainer = document.createElement('div');
15723
+ const buttonsClass = this.buttonsOptions.together
15724
+ ? `buttons-together ${this.buttonsOptions.position}`
15725
+ : 'buttons-separate';
15726
+ buttonsContainer.className = `buttons ${buttonsClass} ${this.buttonsOptions.size}`;
15727
+ buttonsContainer.style.cssText = `--opacity: ${this.buttonsOptions.opacity}`;
15728
+ this.prevButton = this.createButton('prev-button', this.buttonsOptions.prev);
15729
+ this.nextButton = this.createButton('next-button', this.buttonsOptions.next);
15730
+ buttonsContainer.appendChild(this.prevButton);
15731
+ buttonsContainer.appendChild(this.nextButton);
15732
+ return buttonsContainer;
15733
+ }
15734
+ createButton(className, text) {
15735
+ const button = document.createElement('button');
15736
+ button.className = className;
15737
+ button.textContent = text;
15738
+ button.style.color = this.buttonsOptions.textColor;
15739
+ button.style.backgroundColor = this.buttonsOptions.backgroundColor;
15740
+ button.style.borderRadius = this.buttonsOptions.borderRadius;
15741
+ return button;
15742
+ }
15743
+ setupDots() {
15744
+ if (!this.shadowRoot || !this.useDots)
15745
+ return;
15746
+ this.dotElements = Array.from(this.shadowRoot.querySelectorAll('.dot'));
15747
+ this.dotElements.forEach((dot, index) => {
15748
+ dot.addEventListener('click', () => {
15749
+ this.goToSlide(index);
15750
+ this.resetAutoplay();
15751
+ });
15752
+ });
15753
+ }
15754
+ setupButtons() {
15755
+ var _a, _b;
15756
+ if (!this.useButtons)
15757
+ return;
15758
+ (_a = this.prevButton) === null || _a === void 0 ? void 0 : _a.addEventListener('click', () => {
15759
+ this.prevSlide();
15760
+ this.resetAutoplay();
15761
+ });
15762
+ (_b = this.nextButton) === null || _b === void 0 ? void 0 : _b.addEventListener('click', () => {
15763
+ this.nextSlide();
15764
+ this.resetAutoplay();
15765
+ });
15766
+ }
15767
+ nextSlide() {
15768
+ this.goToSlide((this.currentSlide + 1) % this.slides.length);
15769
+ }
15770
+ prevSlide() {
15771
+ this.goToSlide((this.currentSlide - 1 + this.slides.length) % this.slides.length);
15772
+ }
15773
+ goToSlide(index) {
15774
+ this.currentSlide = index;
15775
+ this.updateCarousel();
15776
+ }
15777
+ updateCarousel() {
15778
+ if (!this.slidesContainer)
15779
+ return;
15780
+ const slides = Array.from(this.slidesContainer.children);
15781
+ slides.forEach((slide, index) => {
15782
+ slide.classList.toggle('active', index === this.currentSlide);
15783
+ });
15784
+ this.updateDots();
15785
+ }
15786
+ updateDots() {
15787
+ if (!this.useDots)
15788
+ return;
15789
+ this.dotElements.forEach((dot, index) => {
15790
+ const isActive = index === this.currentSlide;
15791
+ dot.classList.toggle('active', isActive);
15792
+ dot.style.backgroundColor = isActive
15793
+ ? this.dotsOptions.activeColor
15794
+ : this.dotsOptions.color;
15795
+ });
15796
+ }
15797
+ startAutoplay() {
15798
+ this.autoplayInterval = window.setInterval(() => this.nextSlide(), this.interval);
15799
+ }
15800
+ stopAutoplay() {
15801
+ if (this.autoplayInterval !== null) {
15802
+ window.clearInterval(this.autoplayInterval);
15803
+ this.autoplayInterval = null;
15804
+ }
15805
+ }
15806
+ resetAutoplay() {
15807
+ if (this.autoplay) {
15808
+ this.stopAutoplay();
15809
+ this.startAutoplay();
15810
+ }
15811
+ }
15812
+ validateOptions() {
15813
+ this.validatePosition(this.dotsOptions.position, 'dotsPosition', 'bottom-center');
15814
+ this.validateButtonsPosition();
15815
+ }
15816
+ validatePosition(position, optionName, defaultValue) {
15817
+ if (!CustomCarouselElement.validPositions.includes(position)) {
15818
+ console.warn(`Invalid ${optionName}: ${position}. Defaulting to '${defaultValue}'.`);
15819
+ if (optionName === 'dotsPosition') {
15820
+ this.dotsOptions.position = defaultValue;
15821
+ }
15822
+ else if (optionName === 'buttonsPosition') {
15823
+ this.buttonsOptions.position = defaultValue;
15824
+ }
15825
+ }
15826
+ }
15827
+ validateButtonsPosition() {
15828
+ if (this.useButtons) {
15829
+ if (this.buttonsOptions.together) {
15830
+ this.validatePosition(this.buttonsOptions.position, 'buttonsPosition', 'bottom-center');
15831
+ }
15832
+ else if (this.buttonsOptions.position !== 'middle-sides') {
15833
+ console.warn(`Invalid buttonsPosition: ${this.buttonsOptions.position}. When buttons are not together, only 'middle-sides' is allowed. Defaulting to 'middle-sides'.`);
15834
+ this.buttonsOptions.position = 'middle-sides';
15835
+ }
15836
+ }
15837
+ }
15838
+ }
15839
+ CustomCarouselElement.defaultInterval = 5000;
15840
+ CustomCarouselElement.validPositions = [
15841
+ 'top-left',
15842
+ 'top-center',
15843
+ 'top-right',
15844
+ 'bottom-left',
15845
+ 'bottom-center',
15846
+ 'bottom-right',
15847
+ 'middle-left',
15848
+ 'middle-right',
15849
+ 'middle-sides',
15850
+ ];
15851
+ CarouselElement = CustomCarouselElement;
15852
+ }
15853
+
15854
+ let SpotElement;
15855
+ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
15856
+ class CustomSpotElement extends HTMLElement {
15857
+ constructor() {
15858
+ super();
15859
+ this.originalFontSizes = new Map();
15860
+ this.attachShadow({ mode: 'open' });
15861
+ }
15862
+ connectedCallback() {
15863
+ this.setupResizeObserver();
15864
+ this.render();
15865
+ }
15866
+ disconnectedCallback() {
15867
+ var _a;
15868
+ this.removeEventListener('spotSizeChanged', this.handleSpotSizeChanged);
15869
+ (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
15870
+ }
15871
+ /**
15872
+ * Setup observers for the spot element
15873
+ * #########################################################
15874
+ */
15875
+ setupResizeObserver() {
15876
+ if (this.data && !this.data.fluid) {
15877
+ this.resizeObserver = new ResizeObserverService({
15878
+ element: this,
15879
+ maxSize: {
15880
+ width: this.data.width,
15881
+ height: this.data.height,
15882
+ },
15883
+ minScale: this.data.minScale,
15884
+ });
15885
+ this.addEventListener('spotSizeChanged', this.handleSpotSizeChanged.bind(this));
15886
+ }
15887
+ }
15888
+ /**
15889
+ * Observer additional event handlers
15890
+ * #########################################################
15891
+ */
15892
+ handleSpotSizeChanged(event) {
15893
+ var _a, _b, _c;
15894
+ const isRBSpot = (_c = (_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a.spot) === null || _b === void 0 ? void 0 : _b.startsWith('rb')) !== null && _c !== void 0 ? _c : false;
15895
+ if (!isRBSpot) {
15896
+ // Adjust text elements font size based on the scale factor
15897
+ this.adjustFontSize(event.detail.scale);
15898
+ }
15899
+ }
15900
+ adjustFontSize(elementScale) {
15901
+ var _a;
15902
+ const scaleFactor = calculateScaleFactor(elementScale);
15903
+ // Find all text elements within the shadow root
15904
+ const elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('h1, h2, h3, h4, p, span');
15905
+ elements === null || elements === void 0 ? void 0 : elements.forEach((element) => {
15906
+ if (element instanceof HTMLElement) {
15907
+ if (!this.originalFontSizes.has(element)) {
15908
+ const originalSize = parseFloat(window.getComputedStyle(element).fontSize);
15909
+ this.originalFontSizes.set(element, originalSize);
15910
+ }
15911
+ const originalSize = this.originalFontSizes.get(element);
15912
+ const newFontSize = originalSize * scaleFactor;
15913
+ element.style.fontSize = `${newFontSize}px`;
15914
+ }
15915
+ });
15916
+ }
15917
+ /**
15918
+ * Spot element rendering
15919
+ * #########################################################
15920
+ */
15921
+ render() {
15922
+ if (!this.shadowRoot || !this.data || !this.content)
15923
+ return;
15924
+ const style = this.getTemplateStyle(this.data.width, this.data.height);
15925
+ if (this.content instanceof HTMLElement) {
15926
+ this.shadowRoot.replaceChildren(style, this.content);
15927
+ }
15928
+ }
15929
+ getTemplateStyle(width, height) {
15930
+ const style = document.createElement('style');
15931
+ style.textContent = `
15932
+ :host {
15933
+ display: block;
15934
+ position: relative;
15935
+ box-sizing: border-box;
15936
+ overflow: hidden;
15937
+ width: ${this.data.fluid ? '100%' : `${width}px`};
15938
+ height: ${this.data.fluid ? '100%' : `${height}px`};
15939
+ }
15940
+ `;
15941
+ return style;
15942
+ }
15943
+ }
15944
+ SpotElement = CustomSpotElement;
15945
+ }
15946
+
15947
+ class ElementService {
15948
+ static getInstance() {
15949
+ return SingletonManager.getInstance('ElementService', () => new ElementService());
15950
+ }
15951
+ /**
15952
+ * Creates the html element based on the provided data, content and configs using shadow dom.
15953
+ *
15954
+ * This method is only available in browser environments.
15955
+ *
15956
+ * @param {ICreateSpotElementParams} params - The parameters to create the final element.
15957
+ *
15958
+ * @return {HTMLElement | null} - The html element or null if the browser environment is not available.
15959
+ */
15960
+ createSpotElement({ content, config }) {
15961
+ var _a, _b;
15962
+ if (!this.ensureBrowserEnvironmentAndDefineElement()) {
15963
+ return null;
15964
+ }
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 : '');
15967
+ spot.data = {
15968
+ spot: config === null || config === void 0 ? void 0 : config.spot,
15969
+ fluid: (_b = config === null || config === void 0 ? void 0 : config.fluid) !== null && _b !== void 0 ? _b : false,
15970
+ ...config,
15971
+ };
15972
+ spot.content = content;
15973
+ return spot;
15974
+ }
15975
+ /**
15976
+ * Creates the carousel html element based on the provided slides and configs using shadow dom.
15977
+ *
15978
+ * This method is only available in browser environments.
15979
+ *
15980
+ * @param {ICreateCarouselElementParams} params - The parameters to create the final element.
15981
+ *
15982
+ * @return {HTMLElement | null} - The html element or null if the browser environment is not available.
15983
+ */
15984
+ createCarouselElement({ slides, config, }) {
15985
+ if (!this.ensureBrowserEnvironmentAndDefineElement()) {
15986
+ return null;
15987
+ }
15988
+ const carousel = document.createElement(CAROUSEL_ELEMENT_TAG);
15989
+ carousel.data = {
15990
+ spot: config === null || config === void 0 ? void 0 : config.spot,
15991
+ fluid: false,
15992
+ ...config,
15993
+ };
15994
+ carousel.slides = slides;
15995
+ return carousel;
15996
+ }
15997
+ /**
15998
+ * Overrides the spot colors with the provided colors.
15999
+ *
16000
+ * @param {ISpot} spot - The spot data.
16001
+ * @param {ISpotColors} colors - The colors to override.
16002
+ *
16003
+ * @return {ISpot} - The spot data with the colors overridden.
16004
+ */
16005
+ overrideSpotColors(spot, colors) {
16006
+ if (!colors)
16007
+ return spot;
16008
+ const { textColor, backgroundColor, ctaTextColor, ctaBorderColor } = colors;
16009
+ return {
16010
+ ...spot,
16011
+ textColor: textColor !== null && textColor !== void 0 ? textColor : spot.textColor,
16012
+ backgroundColor: backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : spot.backgroundColor,
16013
+ ctaTextColor: ctaTextColor !== null && ctaTextColor !== void 0 ? ctaTextColor : spot.ctaTextColor,
16014
+ ctaBorderColor: ctaBorderColor !== null && ctaBorderColor !== void 0 ? ctaBorderColor : spot.ctaBorderColor,
16015
+ };
16016
+ }
16017
+ /**
16018
+ * @returns {boolean} - True if the browser environment is available and the element is defined.
16019
+ */
16020
+ ensureBrowserEnvironmentAndDefineElement() {
16021
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
16022
+ console.warn('LiquidCommerce Rmn Sdk: Methods which create elements are only available in browser environments.');
16023
+ return false;
16024
+ }
16025
+ if (!window.customElements.get(SPOT_ELEMENT_TAG)) {
16026
+ window.customElements.define(SPOT_ELEMENT_TAG, SpotElement);
16027
+ }
16028
+ if (!window.customElements.get(CAROUSEL_ELEMENT_TAG)) {
16029
+ window.customElements.define(CAROUSEL_ELEMENT_TAG, CarouselElement);
16030
+ }
16031
+ return true;
16032
+ }
16033
+ }
16034
+
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) {
16182
+ return fallback;
16183
+ }
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%`;
16201
+ }
16202
+ function spotHtmlStringToElement(htmlString) {
16203
+ const spot = document.createElement('div');
16204
+ spot.className = 'spot';
16205
+ spot.innerHTML = htmlString;
16206
+ Object.assign(spot.style, {
16207
+ position: 'relative',
16208
+ display: 'block',
16209
+ width: '100%',
16210
+ height: '100%',
16211
+ margin: '0',
16212
+ padding: '0',
16213
+ });
16214
+ return spot;
16215
+ }
16216
+
16217
+ const STYLES$i = ({ primaryImage, secondaryImage }) => `
16218
+ <style>
16219
+ .content {
16220
+ width: 100%;
16221
+ height: 100%;
16222
+ display: flex;
16223
+ flex-direction: row;
16224
+ background-color: #FFFFFF;
16225
+ gap: 0.5cqw;
16226
+ cursor: pointer;
16227
+ color: inherit;
16228
+ }
16229
+ .big-image {
16230
+ width: 65%;
16231
+ height: 100%;
16232
+ background-image: url("${primaryImage}");
16233
+ background-position: center;
16234
+ background-repeat: no-repeat;
16235
+ background-size: cover;
16236
+ }
16237
+ .main {
16238
+ width: 35%;
16239
+ height: 100%;
16240
+ display: flex;
16241
+ flex-direction: column;
16242
+ gap: 0.5cqw;
16243
+ }
16244
+ .small-image {
16245
+ width: 100%;
16246
+ height: 50%;
16247
+ background-image: url("${secondaryImage}");
16248
+ background-position: center;
16249
+ background-repeat: no-repeat;
16250
+ background-size: cover;
16251
+ }
16252
+ .text {
16253
+ background: #E8E6DE;
16254
+ text-align: center;
16255
+ display: flex;
16256
+ flex-direction: column;
16257
+ justify-content: center;
16258
+ align-items: center;
16259
+ height: 50%;
16260
+ width: 100%;
16261
+ padding: 5% 10%;
16262
+ box-sizing: border-box;
16263
+ font-family: "Source Sans 3", sans-serif;
16264
+ }
16265
+ .header {
16266
+ font-size: 2.2cqw;
16267
+ color: #000000;
16268
+ font-style: normal;
16269
+ font-weight: 400;
16270
+ font-family: "Cormorant", serif;
16271
+ margin: 0;
16272
+ }
16273
+ .description {
16274
+ font-size: 1.2cqw;
16275
+ color: #000000;
16276
+ font-style: normal;
16277
+ font-weight: 400;
16278
+ margin: 1cqh 0;
16279
+ }
16280
+ .button {
16281
+ font-size: 1cqw;
16282
+ color: #000000;
16283
+ background-color: transparent;
16284
+ font-style: normal;
16285
+ font-weight: 700;
16286
+ text-decoration: underline;
16287
+ text-transform: uppercase;
16288
+ border: none;
16289
+ cursor: pointer;
16290
+ padding: 0;
16291
+ transition: opacity 0.3s ease;
16292
+ }
16293
+ .content:hover .button {
16294
+ opacity: 0.7;
16295
+ }
16296
+ @container (max-width: 600px) {
16297
+ .header {
16298
+ font-size: 3cqw;
16299
+ }
16300
+ .description {
16301
+ font-size: 1.6cqw;
16302
+ margin: 0;
16303
+ }
16304
+ .button {
16305
+ font-size: 1.4cqw;
16306
+ }
16307
+ }
16308
+ </style>
16309
+ `;
16310
+ function billboardV1Template(spot) {
16311
+ return `
16312
+ ${GFONT_PRECONNECT}
16313
+ ${GFONT_SOURCE_SANS_3}
16314
+ ${GFONT_CORMORANT}
15301
16315
  ${STYLES$i(spot)}
15302
16316
  <div class="content">
15303
16317
  <div class="big-image"></div>
@@ -16017,185 +17031,204 @@ function wideSkyscraperV1Template(spot) {
16017
17031
  `;
16018
17032
  }
16019
17033
 
16020
- const STYLES$7 = ({ primaryImage, mobilePrimaryImage = primaryImage }) => `
16021
- <style>
16022
- :host {
16023
- min-width: 320px;
16024
- min-height: 223px;
16025
- }
16026
- .content {
16027
- display: block;
16028
- width: 100%;
16029
- height: 100%;
16030
- background-image: url("${mobilePrimaryImage}");
16031
- background-size: cover;
16032
- background-repeat: no-repeat;
16033
- background-position: center;
16034
- cursor: pointer;
16035
- }
16036
- @media (min-width: 640px) {
16037
- .content {
16038
- background-image: url("${primaryImage}");
16039
- }
17034
+ const STYLES$7 = ({ primaryImage, mobilePrimaryImage = primaryImage }, { prefix }) => `
17035
+ <style>
17036
+ .${prefix} {
17037
+ display: block;
17038
+ width: 100%;
17039
+ height: 100%;
17040
+ background-image: url("${mobilePrimaryImage}");
17041
+ background-size: cover;
17042
+ background-repeat: no-repeat;
17043
+ background-position: center;
17044
+ cursor: pointer;
17045
+ container-type: inline-size;
17046
+ }
17047
+
17048
+ @container (min-width: 640px) {
17049
+ .${prefix} {
17050
+ background-image: url("${primaryImage}");
16040
17051
  }
16041
- </style>
17052
+ }
17053
+ </style>
16042
17054
  `;
16043
- function rbCollectionBannerWithoutTextBlockTemplate(spot) {
17055
+ function rbCollectionBannerWithoutTextBlockTemplate(spot, config) {
17056
+ const { prefix = '' } = config;
16044
17057
  return `
16045
- ${STYLES$7(spot)}
16046
- <div class="content"></div>
17058
+ ${STYLES$7(spot, config)}
17059
+ <div class="${prefix}"></div>
16047
17060
  `;
16048
17061
  }
16049
17062
 
16050
- const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderColor = ctaTextColor, primaryImage, mobilePrimaryImage = primaryImage, }) => `
16051
- <style>
16052
- :host {
16053
- min-width: 320px;
16054
- min-height: 388px;
16055
- }
16056
- .content {
16057
- display: flex;
16058
- flex-direction: column;
16059
- justify-content: flex-end;
16060
- align-items: flex-start;
16061
- width: 100%;
16062
- height: 100%;
16063
- background-image: url("${mobilePrimaryImage}");
16064
- background-size: cover;
16065
- background-repeat: no-repeat;
16066
- background-position: center;
16067
- padding: 3%;
16068
- box-sizing: border-box;
16069
- color: ${textColor};
16070
- cursor: pointer;
16071
- }
16072
- .text {
16073
- display: flex;
16074
- flex-direction: column;
16075
- justify-content: flex-start;
16076
- align-items: flex-start;
16077
- width: 100%;
16078
- height: fit-content;
16079
- gap: 5px;
16080
- }
16081
- .header {
16082
- font-size: 18px;
16083
- margin: 0;
16084
- font-family: "Cormorant";
16085
- font-style: normal;
16086
- font-weight: 300;
16087
- line-height: normal;
16088
- }
16089
- .description {
16090
- font-size: 10px;
16091
- font-family: "Source Sans 3", system-ui;
16092
- font-style: normal;
16093
- font-weight: 400;
16094
- line-height: 20px;
16095
- margin: 0;
16096
- }
16097
- .cta-button {
16098
- font-size: 8px;
16099
- font-family: "Source Sans 3", system-ui;
16100
- font-style: normal;
16101
- font-weight: 400;
16102
- line-height: 18px;
16103
- border-radius: 5px;
16104
- border: 0.5px solid ${ctaBorderColor};
16105
- display: inline-block;
16106
- padding: 7px 20px;
16107
- color: ${ctaTextColor};
16108
- transition: background-color 0.3s ease;
16109
- }
16110
- .content:hover .cta-button {
16111
- background-color: ${ctaBorderColor.length === 7 ? `${ctaBorderColor}15` : `${ctaBorderColor}`};
16112
- }
16113
- @media (min-width: 640px) {
16114
- .content {
16115
- background-image: url("${primaryImage}");
16116
- }
16117
- }
16118
- @media (min-width: 768px) {
16119
- .primary-image {
16120
- width: 66.66666%;
16121
- height: 100%;
16122
- }
16123
- .main {
17063
+ const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderColor = ctaTextColor, primaryImage, mobilePrimaryImage = primaryImage, }, { prefix, overlay }) => {
17064
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 40%');
17065
+ return `
17066
+ <style>
17067
+ .${prefix} {
17068
+ display: flex;
16124
17069
  flex-direction: column;
17070
+ justify-content: flex-end;
17071
+ align-items: flex-start;
17072
+ width: 100%;
16125
17073
  height: 100%;
16126
- width: 33.33333%;
17074
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
17075
+ background-size: cover;
17076
+ background-repeat: no-repeat;
17077
+ background-position: center;
17078
+ padding: 3%;
17079
+ box-sizing: border-box;
17080
+ color: ${textColor};
17081
+ cursor: pointer;
17082
+ container-type: inline-size;
16127
17083
  }
16128
- .secondary-image {
17084
+
17085
+ .${prefix}__text {
17086
+ display: flex;
17087
+ flex-direction: column;
17088
+ justify-content: flex-start;
17089
+ align-items: flex-start;
16129
17090
  width: 100%;
16130
- height: 50%;
17091
+ height: fit-content;
17092
+ gap: 8px;
16131
17093
  }
16132
- .header {
16133
- font-size: 22px;
16134
- }
16135
- .description {
16136
- font-size: 12px;
16137
- }
16138
- .cta-button {
16139
- font-size: 12px;
16140
- }
16141
- }
16142
- @media (min-width: 1024px) {
16143
- .header {
17094
+
17095
+ .${prefix}__header {
16144
17096
  font-size: 24px;
17097
+ margin: 0;
17098
+ font-family: "Cormorant";
17099
+ font-style: normal;
17100
+ font-weight: 300;
17101
+ line-height: normal;
16145
17102
  }
16146
- .description {
16147
- font-size: 13px;
17103
+
17104
+ .${prefix}__description {
17105
+ font-size: 10px;
17106
+ font-family: "Source Sans 3", system-ui;
17107
+ font-style: normal;
17108
+ font-weight: 400;
17109
+ line-height: 20px;
17110
+ margin: 0;
16148
17111
  }
16149
- .cta-button {
16150
- font-size: 13px;
17112
+
17113
+ .${prefix}__cta-button {
17114
+ font-size: 8px;
17115
+ font-family: "Source Sans 3", system-ui;
17116
+ font-style: normal;
17117
+ font-weight: 400;
17118
+ line-height: 18px;
17119
+ border-radius: 5px;
17120
+ border: 0.5px solid ${ctaBorderColor};
17121
+ display: inline-block;
17122
+ padding: 7px 20px;
17123
+ color: ${ctaTextColor};
17124
+ transition: background-color 0.3s ease;
16151
17125
  }
16152
- }
16153
- @media (min-width: 1280px) {
16154
- .header {
16155
- font-size: 28px;
17126
+
17127
+ .${prefix}:hover .cta-button {
17128
+ background-color: ${ctaBorderColor.length === 7 ? `${ctaBorderColor}15` : `${ctaBorderColor}`};
16156
17129
  }
16157
- .description {
16158
- font-size: 14px;
17130
+
17131
+ @container (min-width: 640px) {
17132
+ .${prefix} {
17133
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
17134
+ }
16159
17135
  }
16160
- .cta-button {
16161
- font-size: 14px;
17136
+
17137
+ @container (min-width: 768px) {
17138
+ .${prefix}__primary-image {
17139
+ width: 66.66666%;
17140
+ height: 100%;
17141
+ }
17142
+
17143
+ .${prefix}__main {
17144
+ flex-direction: column;
17145
+ height: 100%;
17146
+ width: 33.33333%;
17147
+ }
17148
+
17149
+ .${prefix}__secondary-image {
17150
+ width: 100%;
17151
+ height: 50%;
17152
+ }
17153
+
17154
+ .${prefix}__description {
17155
+ font-size: 12px;
17156
+ }
17157
+
17158
+ .${prefix}__cta-button {
17159
+ font-size: 12px;
17160
+ }
16162
17161
  }
16163
- }
16164
- </style>
16165
- `;
16166
- function rbHomepageHeroFullImageTemplate(spot) {
17162
+
17163
+ @container (min-width: 1024px) {
17164
+ .${prefix}__header {
17165
+ font-size: 26px;
17166
+ }
17167
+
17168
+ .${prefix}__description {
17169
+ font-size: 13px;
17170
+ }
17171
+
17172
+ .${prefix}__cta-button {
17173
+ font-size: 13px;
17174
+ }
17175
+ }
17176
+
17177
+ @container (min-width: 1280px) {
17178
+ .${prefix}__header {
17179
+ font-size: 28px;
17180
+ }
17181
+
17182
+ .${prefix}__description {
17183
+ font-size: 14px;
17184
+ }
17185
+
17186
+ .${prefix}__cta-button {
17187
+ font-size: 14px;
17188
+ }
17189
+ }
17190
+ </style>
17191
+ `;
17192
+ };
17193
+ function rbHomepageHeroFullImageTemplate(spot, config) {
17194
+ const { prefix = '' } = config;
16167
17195
  return `
16168
17196
  ${GFONT_PRECONNECT}
16169
17197
  ${GFONT_SOURCE_SANS_3}
16170
17198
  ${GFONT_CORMORANT}
16171
- ${STYLES$6(spot)}
16172
- <div class="content">
16173
- <div class="text">
16174
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16175
- ${spot.description ? `<p class="description">${spot.description}</p>` : ''}
16176
- ${spot.ctaText ? `<span class="cta-button">${spot.ctaText}</span>` : ''}
16177
- </div>
17199
+ ${STYLES$6(spot, config)}
17200
+ <div class="${prefix}">
17201
+ <div class="${prefix}__text">
17202
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17203
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
17204
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
17205
+ </div>
16178
17206
  </div>
16179
17207
  `;
16180
17208
  }
16181
17209
 
16182
- const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextColor = textColor, primaryImage, mobilePrimaryImage = primaryImage, secondaryImage, mobileSecondaryImage = secondaryImage, }) => `
16183
- <style>
16184
- :host {
16185
- min-width: 320px;
16186
- min-height: 388px;
17210
+ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextColor = textColor, primaryImage, mobilePrimaryImage = primaryImage, secondaryImage, mobileSecondaryImage = secondaryImage, }, { prefix }) => `
17211
+ <style>
17212
+ .${prefix} {
17213
+ width: 100%;
17214
+ height: 100%;
17215
+ display: block;
17216
+ position: relative;
17217
+ container-type: inline-size;
16187
17218
  }
16188
- .content {
17219
+
17220
+ .${prefix}__content {
16189
17221
  width: 100%;
16190
17222
  height: 100%;
16191
17223
  display: flex;
16192
- flex-direction: column;
17224
+ flex-direction: column;
16193
17225
  background-color: transparent;
16194
- gap: 5px;
17226
+ gap: 6px;
16195
17227
  color: inherit;
16196
17228
  cursor: pointer;
16197
17229
  }
16198
- .primary-image {
17230
+
17231
+ .${prefix}__primary-image {
16199
17232
  width: 100%;
16200
17233
  height: 60%;
16201
17234
  background-image: url("${mobilePrimaryImage}");
@@ -16203,14 +17236,16 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16203
17236
  background-repeat: no-repeat;
16204
17237
  background-size: cover;
16205
17238
  }
16206
- .main {
17239
+
17240
+ .${prefix}__main {
16207
17241
  width: 100%;
16208
17242
  height: 40%;
16209
17243
  display: flex;
16210
17244
  flex-direction: row;
16211
- gap: 5px;
17245
+ gap: 6px;
16212
17246
  }
16213
- .secondary-image {
17247
+
17248
+ .${prefix}__secondary-image {
16214
17249
  width: 50%;
16215
17250
  height: 100%;
16216
17251
  background-image: url("${mobileSecondaryImage}");
@@ -16218,7 +17253,8 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16218
17253
  background-repeat: no-repeat;
16219
17254
  background-size: cover;
16220
17255
  }
16221
- .text {
17256
+
17257
+ .${prefix}__text {
16222
17258
  color: ${textColor};
16223
17259
  background-color: ${backgroundColor};
16224
17260
  text-align: center;
@@ -16228,11 +17264,12 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16228
17264
  align-items: center;
16229
17265
  width: 50%;
16230
17266
  height: 100%;
16231
- gap: 5px;
17267
+ gap: 10px;
16232
17268
  padding: 0 10px;
16233
17269
  box-sizing: border-box;
16234
17270
  }
16235
- .header {
17271
+
17272
+ .${prefix}__header {
16236
17273
  color: inherit;
16237
17274
  margin: 0;
16238
17275
  font-size: 18px;
@@ -16241,7 +17278,8 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16241
17278
  font-weight: 700;
16242
17279
  line-height: normal;
16243
17280
  }
16244
- .description {
17281
+
17282
+ .${prefix}__description {
16245
17283
  color: inherit;
16246
17284
  margin: 0;
16247
17285
  font-size: 10px;
@@ -16249,7 +17287,8 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16249
17287
  font-style: normal;
16250
17288
  font-weight: 400;
16251
17289
  }
16252
- .cta-button {
17290
+
17291
+ .${prefix}__cta-button {
16253
17292
  color: ${ctaTextColor};
16254
17293
  background-color: transparent;
16255
17294
  font-size: 8px;
@@ -16264,108 +17303,120 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16264
17303
  padding: 0;
16265
17304
  transition: opacity 0.3s ease;
16266
17305
  }
16267
- .content:hover .cta-button {
17306
+
17307
+ .${prefix}__content:hover .${prefix}__cta-button {
16268
17308
  opacity: 0.8;
16269
17309
  }
16270
- @media (min-width: 640px) {
16271
- .primary-image {
17310
+
17311
+ @container (min-width: 640px) {
17312
+ .${prefix}__primary-image {
16272
17313
  background-image: url("${primaryImage}");
16273
17314
  }
16274
- .secondary-image {
17315
+
17316
+ .${prefix}__secondary-image {
16275
17317
  background-image: url("${secondaryImage}");
16276
17318
  }
16277
17319
  }
16278
- @media (min-width: 768px) {
16279
- .content {
17320
+
17321
+ @container (min-width: 768px) {
17322
+ .${prefix}__content {
16280
17323
  flex-direction: row;
16281
17324
  }
16282
- .primary-image {
17325
+
17326
+ .${prefix}__primary-image {
16283
17327
  width: 66.66666%;
16284
17328
  height: 100%;
16285
17329
  }
16286
- .main {
17330
+
17331
+ .${prefix}__main {
16287
17332
  flex-direction: column;
16288
17333
  height: 100%;
16289
17334
  width: 33.33333%;
16290
17335
  }
16291
- .secondary-image {
17336
+
17337
+ .${prefix}__secondary-image {
16292
17338
  width: 100%;
16293
17339
  height: 50%;
16294
17340
  }
16295
- .text {
17341
+
17342
+ .${prefix}__text {
16296
17343
  width: 100%;
16297
17344
  height: 50%;
16298
17345
  }
16299
- .header {
17346
+ .${prefix}__header {
16300
17347
  font-size: 22px;
16301
17348
  }
16302
- .description {
17349
+ .${prefix}__description {
16303
17350
  font-size: 12px;
16304
17351
  }
16305
- .cta-button {
17352
+ .${prefix}__cta-button {
16306
17353
  font-size: 12px;
16307
17354
  }
16308
17355
  }
16309
- @media (min-width: 1024px) {
16310
- .header {
17356
+
17357
+ @container (min-width: 1024px) {
17358
+ .${prefix}__header {
16311
17359
  font-size: 24px;
16312
17360
  }
16313
- .description {
17361
+ .${prefix}__description {
16314
17362
  font-size: 13px;
16315
- }
16316
- .cta-button {
16317
- font-size: 13px;
16318
- }
17363
+ }
17364
+ .${prefix}__cta-button {
17365
+ font-size: 13px;
17366
+ }
16319
17367
  }
16320
- @media (min-width: 1280px) {
16321
- .header {
17368
+
17369
+ @container (min-width: 1280px) {
17370
+ .${prefix}__header {
16322
17371
  font-size: 28px;
16323
17372
  }
16324
- .description {
17373
+ .${prefix}__description {
16325
17374
  font-size: 14px;
16326
17375
  }
16327
- .cta-button {
17376
+ .${prefix}__cta-button {
16328
17377
  font-size: 14px;
16329
17378
  }
16330
17379
  }
16331
- </style>
17380
+ </style>
16332
17381
  `;
16333
- function rbHomepageHeroThreeTileTemplate(spot) {
17382
+ function rbHomepageHeroThreeTileTemplate(spot, config) {
17383
+ const { prefix = '' } = config;
16334
17384
  return `
16335
17385
  ${GFONT_PRECONNECT}
16336
17386
  ${GFONT_SOURCE_SANS_3}
16337
17387
  ${GFONT_CORMORANT}
16338
- ${STYLES$5(spot)}
16339
- <div class="content">
16340
- <div class="primary-image"></div>
16341
- <div class="main">
16342
- <div class="secondary-image"></div>
16343
- <div class="text">
16344
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16345
- ${spot.description ? `<p class="description">${spot.description}</p>` : ''}
16346
- ${spot.ctaText ? `<span class="cta-button">${spot.ctaText}</span>` : ''}
17388
+ ${STYLES$5(spot, config)}
17389
+ <div class="${prefix}">
17390
+ <div class="${prefix}__content">
17391
+ <div class="${prefix}__primary-image"></div>
17392
+ <div class="${prefix}__main">
17393
+ <div class="${prefix}__secondary-image"></div>
17394
+ <div class="${prefix}__text">
17395
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17396
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
17397
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
17398
+ </div>
16347
17399
  </div>
16348
17400
  </div>
16349
17401
  </div>
16350
17402
  `;
16351
17403
  }
16352
17404
 
16353
- const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextColor = textColor, primaryImage, mobilePrimaryImage = primaryImage, }) => `
17405
+ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextColor = textColor, primaryImage, mobilePrimaryImage = primaryImage, }, { prefix }) => `
16354
17406
  <style>
16355
- :host {
16356
- min-width: 320px;
16357
- min-height: 388px;
16358
- }
16359
- .content {
17407
+ .${prefix} {
16360
17408
  width: 100%;
16361
17409
  height: 100%;
16362
17410
  display: flex;
16363
- flex-direction: column-reverse;
17411
+ flex-direction: column-reverse;
16364
17412
  background-color: transparent;
16365
- gap: 5px;
17413
+ gap: 6px;
16366
17414
  cursor: pointer;
17415
+ container-type: inline-size;
17416
+ position: relative;
16367
17417
  }
16368
- .image {
17418
+
17419
+ .${prefix}__image {
16369
17420
  width: 100%;
16370
17421
  height: 100%;
16371
17422
  background-image: url("${mobilePrimaryImage}");
@@ -16373,7 +17424,8 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16373
17424
  background-repeat: no-repeat;
16374
17425
  background-size: cover;
16375
17426
  }
16376
- .text {
17427
+
17428
+ .${prefix}__text {
16377
17429
  color: ${textColor};
16378
17430
  background-color: ${backgroundColor};
16379
17431
  text-align: center;
@@ -16383,11 +17435,12 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16383
17435
  align-items: center;
16384
17436
  width: 100%;
16385
17437
  height: 100%;
16386
- gap: 5px;
17438
+ gap: 10px;
16387
17439
  padding: 0 10px;
16388
17440
  box-sizing: border-box;
16389
17441
  }
16390
- .header {
17442
+
17443
+ .${prefix}__header {
16391
17444
  font-size: 18px;
16392
17445
  margin: 0;
16393
17446
  color: inherit;
@@ -16396,7 +17449,8 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16396
17449
  font-weight: 700;
16397
17450
  line-height: normal;
16398
17451
  }
16399
- .description {
17452
+
17453
+ .${prefix}__description {
16400
17454
  color: inherit;
16401
17455
  margin: 0;
16402
17456
  font-size: 10px;
@@ -16404,7 +17458,8 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16404
17458
  font-style: normal;
16405
17459
  font-weight: 400;
16406
17460
  }
16407
- .cta-button {
17461
+
17462
+ .${prefix}__cta-button {
16408
17463
  color: ${ctaTextColor};
16409
17464
  background-color: transparent;
16410
17465
  font-size: 8px;
@@ -16419,205 +17474,225 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16419
17474
  padding: 0;
16420
17475
  transition: opacity 0.3s ease;
16421
17476
  }
16422
- .content:hover .cta-button {
17477
+
17478
+ .${prefix}__content:hover .${prefix}__cta-button {
16423
17479
  opacity: 0.8;
16424
17480
  }
16425
- @media (min-width: 640px) {
16426
- .image {
17481
+
17482
+ @container (min-width: 640px) {
17483
+ .${prefix}__image {
16427
17484
  background-image: url("${primaryImage}");
16428
17485
  }
16429
17486
  }
16430
- @media (min-width: 768px) {
16431
- .content {
17487
+
17488
+ @container (min-width: 768px) {
17489
+ .${prefix} {
16432
17490
  flex-direction: row;
16433
17491
  }
16434
- .image {
17492
+ .${prefix}__image {
16435
17493
  width: 66.66666%;
16436
17494
  height: 100%;
16437
17495
  }
16438
- .text {
16439
- width: 33.333%;
17496
+ .${prefix}__text {
17497
+ width: 33.33333%;
16440
17498
  height: 100%;
16441
17499
  }
16442
- .header {
17500
+ .${prefix}__header {
16443
17501
  font-size: 22px;
16444
17502
  }
16445
- .description {
17503
+ .${prefix}__description {
16446
17504
  font-size: 12px;
16447
17505
  }
16448
- .cta-button {
17506
+ .${prefix}__cta-button {
16449
17507
  font-size: 12px;
16450
17508
  }
16451
17509
  }
16452
- @media (min-width: 1024px) {
16453
- .header {
17510
+
17511
+ @container (min-width: 1024px) {
17512
+ .${prefix}__header {
16454
17513
  font-size: 24px;
16455
17514
  }
16456
- .description {
17515
+ .${prefix}__description {
16457
17516
  font-size: 13px;
16458
- }
16459
- .cta-button {
16460
- font-size: 13px;
16461
- }
17517
+ }
17518
+ .${prefix}__cta-button {
17519
+ font-size: 13px;
17520
+ }
16462
17521
  }
16463
- @media (min-width: 1280px) {
16464
- .header {
17522
+
17523
+ @container (min-width: 1280px) {
17524
+ .${prefix}__header {
16465
17525
  font-size: 28px;
16466
17526
  }
16467
- .description {
17527
+ .${prefix}__description {
16468
17528
  font-size: 14px;
16469
17529
  }
16470
- .cta-button {
17530
+ .${prefix}__cta-button {
16471
17531
  font-size: 14px;
16472
17532
  }
16473
17533
  }
16474
17534
  </style>
16475
17535
  `;
16476
- function rbHomepageHeroTwoTileTemplate(spot) {
17536
+ function rbHomepageHeroTwoTileTemplate(spot, config) {
17537
+ const { prefix = '' } = config;
16477
17538
  return `
16478
17539
  ${GFONT_PRECONNECT}
16479
17540
  ${GFONT_SOURCE_SANS_3}
16480
17541
  ${GFONT_CORMORANT}
16481
- ${STYLES$4(spot)}
16482
- <div class="content">
16483
- <div class="text">
16484
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16485
- ${spot.description ? `<p class="description">${spot.description}</p>` : ''}
16486
- ${spot.ctaText ? `<span class="cta-button">${spot.ctaText}</span>` : ''}
17542
+ ${STYLES$4(spot, config)}
17543
+ <div class="${prefix}">
17544
+ <div class="${prefix}__text">
17545
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17546
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
17547
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
16487
17548
  </div>
16488
- <div class="image"></div>
17549
+ <div class="${prefix}__image"></div>
16489
17550
  </div>
16490
17551
  `;
16491
17552
  }
16492
17553
 
16493
- const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderColor = ctaTextColor, primaryImage, mobilePrimaryImage = primaryImage, }) => `
16494
- <style>
16495
- :host {
16496
- min-width: 320px;
16497
- min-height: 334px;
16498
- }
16499
- .content {
16500
- width: 100%;
16501
- height: 100%;
16502
- display: flex;
16503
- flex-direction: column;
16504
- justify-content: flex-end;
16505
- background-image: url("${mobilePrimaryImage}");
16506
- background-size: cover;
16507
- background-repeat: no-repeat;
16508
- background-position: center;
16509
- border-radius: 5px;
16510
- overflow: hidden;
16511
- cursor: pointer;
16512
- color: ${textColor};
16513
- }
16514
- .text {
16515
- padding: 20px;
16516
- width: 70%;
16517
- display: flex;
16518
- flex-direction: column;
16519
- justify-content: center;
16520
- align-items: flex-start;
16521
- gap: 10px;
16522
- }
16523
- .header {
16524
- color: inherit;
16525
- margin: 0;
16526
- font-size: 20px;
16527
- font-family: "Cormorant";
16528
- font-style: normal;
16529
- font-weight: 300;
16530
- line-height: normal;
16531
- }
16532
- .description {
16533
- color: inherit;
16534
- font-size: 12px;
16535
- line-height: 16px;
16536
- font-family: "Source Sans 3", system-ui;
16537
- font-style: normal;
16538
- font-weight: 400;
16539
- margin: 0;
16540
- }
16541
- .cta-button {
16542
- width: fit-content;
16543
- display: inline-block;
16544
- padding: 7px 20px;
16545
- border: 0.5px solid ${ctaBorderColor};
16546
- border-radius: 5px;
16547
- color: ${ctaTextColor};
16548
- font-size: 8px;
16549
- transition: background-color 0.3s ease;
16550
- font-family: "Source Sans 3", system-ui;
16551
- font-style: normal;
16552
- font-weight: 400;
16553
- }
16554
- .content:hover .cta-button {
16555
- background-color: ${ctaBorderColor.length === 7 ? `${ctaBorderColor}15` : `${ctaBorderColor}`};
16556
- }
16557
- @media (min-width: 640px) {
16558
- .content {
16559
- background-image: url("${primaryImage}");
16560
- }
16561
- }
16562
- @media (min-width: 768px) {
16563
- .text {
16564
- padding: 25px;
17554
+ const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderColor = ctaTextColor, primaryImage, mobilePrimaryImage = primaryImage, }, { prefix, overlay }) => {
17555
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
17556
+ return `
17557
+ <style>
17558
+ .${prefix} {
17559
+ width: 100%;
17560
+ height: 100%;
17561
+ display: flex;
17562
+ flex-direction: column;
17563
+ justify-content: flex-end;
17564
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
17565
+ background-size: cover;
17566
+ background-repeat: no-repeat;
17567
+ background-position: center;
17568
+ border-radius: 5px;
17569
+ overflow: hidden;
17570
+ cursor: pointer;
17571
+ color: ${textColor};
17572
+ container-type: inline-size;
16565
17573
  }
16566
- .header {
16567
- font-size: 24px;
17574
+
17575
+ .${prefix}__text {
17576
+ padding: 20px;
17577
+ width: 70%;
17578
+ display: flex;
17579
+ flex-direction: column;
17580
+ justify-content: center;
17581
+ align-items: flex-start;
17582
+ gap: 10px;
16568
17583
  }
16569
- .description {
16570
- font-size: 13px;
16571
- line-height: 18px;
17584
+
17585
+ .${prefix}__header {
17586
+ color: inherit;
17587
+ margin: 0;
17588
+ font-size: 20px;
17589
+ font-family: "Cormorant";
17590
+ font-style: normal;
17591
+ font-weight: 300;
17592
+ line-height: normal;
16572
17593
  }
16573
- .cta-button {
17594
+
17595
+ .${prefix}__description {
17596
+ color: inherit;
16574
17597
  font-size: 12px;
17598
+ line-height: 16px;
17599
+ font-family: "Source Sans 3", system-ui;
17600
+ font-style: normal;
17601
+ font-weight: 400;
17602
+ margin: 0;
16575
17603
  }
16576
- }
16577
- @media (min-width: 1024px) {
16578
- .text {
16579
- padding: 30px;
17604
+
17605
+ .${prefix}__cta-button {
17606
+ width: fit-content;
17607
+ display: inline-block;
17608
+ padding: 7px 20px;
17609
+ border: 0.5px solid ${ctaBorderColor};
17610
+ border-radius: 5px;
17611
+ color: ${ctaTextColor};
17612
+ font-size: 8px;
17613
+ transition: background-color 0.3s ease;
17614
+ font-family: "Source Sans 3", system-ui;
17615
+ font-style: normal;
17616
+ font-weight: 400;
16580
17617
  }
16581
- .header {
16582
- font-size: 28px;
17618
+
17619
+ .${prefix}:hover .${prefix}__cta-button {
17620
+ background-color: ${ctaBorderColor.length === 7 ? `${ctaBorderColor}15` : `${ctaBorderColor}`};
17621
+ }
17622
+
17623
+ @container (min-width: 640px) {
17624
+ .${prefix} {
17625
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
17626
+ }
16583
17627
  }
16584
- .description {
16585
- font-size: 14px;
17628
+
17629
+ @container (min-width: 768px) {
17630
+ .${prefix}__text {
17631
+ padding: 25px;
17632
+ }
17633
+
17634
+ .${prefix}__header {
17635
+ font-size: 24px;
17636
+ }
17637
+
17638
+ .${prefix}__description {
17639
+ font-size: 13px;
17640
+ line-height: 18px;
17641
+ }
17642
+
17643
+ .${prefix}__cta-button {
17644
+ font-size: 12px;
17645
+ }
16586
17646
  }
16587
- .cta-button {
16588
- font-size: 13px;
17647
+
17648
+ @container (min-width: 1024px) {
17649
+ .${prefix}__text {
17650
+ padding: 30px;
17651
+ }
17652
+
17653
+ .${prefix}__header {
17654
+ font-size: 28px;
17655
+ }
17656
+
17657
+ .${prefix}__description {
17658
+ font-size: 14px;
17659
+ }
17660
+
17661
+ .${prefix}__cta-button {
17662
+ font-size: 13px;
17663
+ }
16589
17664
  }
16590
- }
16591
- @media (min-width: 1280px) {
16592
- .cta-button {
16593
- font-size: 14px;
17665
+
17666
+ @container (min-width: 1280px) {
17667
+ .${prefix}__cta-button {
17668
+ font-size: 14px;
17669
+ }
16594
17670
  }
16595
- }
16596
- </style>
16597
- `;
16598
- function rbLargeCategoryImageToutTemplate(spot) {
17671
+ </style>
17672
+ `;
17673
+ };
17674
+ function rbLargeCategoryImageToutTemplate(spot, config) {
17675
+ const { prefix = '' } = config;
16599
17676
  return `
16600
17677
  ${GFONT_PRECONNECT}
16601
17678
  ${GFONT_SOURCE_SANS_3}
16602
17679
  ${GFONT_CORMORANT}
16603
- ${STYLES$3(spot)}
16604
- <div class="content">
16605
- <div class="text">
16606
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16607
- ${spot.description ? `<p class="description">${spot.description}</p>` : ''}
16608
- ${spot.ctaText ? `<span class="cta-button">${spot.ctaText}</span>` : ''}
17680
+ ${STYLES$3(spot, config)}
17681
+ <div class="${prefix}">
17682
+ <div class="${prefix}__text">
17683
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17684
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
17685
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
16609
17686
  </div>
16610
17687
  </div>
16611
17688
  `;
16612
17689
  }
16613
17690
 
16614
- const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage, }) => `
17691
+ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
17692
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
17693
+ return `
16615
17694
  <style>
16616
- :host {
16617
- min-width: 320px;
16618
- min-height: 150px;
16619
- }
16620
- .content {
17695
+ .${prefix} {
16621
17696
  width: 100%;
16622
17697
  height: 100%;
16623
17698
  display: flex;
@@ -16626,17 +17701,15 @@ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
16626
17701
  border-radius: 5px;
16627
17702
  overflow: hidden;
16628
17703
  cursor: pointer;
16629
- background-image: url("${mobilePrimaryImage}");
17704
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
16630
17705
  background-size: cover;
16631
17706
  background-position: center;
16632
- background-blend-mode: overlay;
16633
17707
  background-repeat: no-repeat;
17708
+ container-type: inline-size;
17709
+ position: relative;
16634
17710
  }
16635
- .text {
16636
- padding: 15px 10%;
16637
- width: fit-content;
16638
- }
16639
- .header {
17711
+
17712
+ .${prefix}__header {
16640
17713
  font-size: 16px;
16641
17714
  color: ${textColor};
16642
17715
  line-height: 20px;
@@ -16644,103 +17717,107 @@ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
16644
17717
  font-family: "Source Sans 3", system-ui;
16645
17718
  font-style: normal;
16646
17719
  margin: 0;
17720
+ padding: 15px 10%;
16647
17721
  }
16648
- @media (min-width: 640px) {
16649
- .content {
16650
- background-image: url("${primaryImage}");
17722
+
17723
+ @container (min-width: 640px) {
17724
+ .${prefix} {
17725
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
16651
17726
  }
16652
17727
  }
16653
- @media (min-width: 768px) {
16654
- .header {
17728
+
17729
+ @container (min-width: 768px) {
17730
+ .${prefix}__header {
16655
17731
  font-size: 22px;
16656
17732
  }
16657
17733
  }
16658
- @media (min-width: 1024px) {
16659
- .header {
17734
+
17735
+ @container (min-width: 1024px) {
17736
+ .${prefix}__header {
16660
17737
  font-size: 24px;
16661
17738
  }
16662
17739
  }
16663
- @media (min-width: 1280px) {
16664
- .header {
17740
+
17741
+ @container (min-width: 1280px) {
17742
+ .${prefix}__header {
16665
17743
  font-size: 28px;
16666
17744
  }
16667
17745
  }
16668
17746
  </style>
16669
17747
  `;
16670
- function rbNavigationBannerTemplate(spot) {
17748
+ };
17749
+ function rbNavigationBannerTemplate(spot, config) {
17750
+ const { prefix = '' } = config;
16671
17751
  return `
16672
17752
  ${GFONT_PRECONNECT}
16673
17753
  ${GFONT_SOURCE_SANS_3}
16674
- ${STYLES$2(spot)}
16675
- <div class="content">
16676
- <div class="text">
16677
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16678
- </div>
17754
+ ${STYLES$2(spot, config)}
17755
+ <div class="${prefix}">
17756
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
16679
17757
  </div>
16680
17758
  `;
16681
17759
  }
16682
17760
 
16683
- const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage, }) => `
16684
- <style>
16685
- :host {
16686
- min-width: 165px;
16687
- min-height: 300px;
16688
- }
16689
- .content {
16690
- width: 100%;
16691
- height: 100%;
16692
- display: flex;
16693
- flex-direction: column;
16694
- justify-content: flex-end;
16695
- background-image: url("${mobilePrimaryImage}");
16696
- background-size: cover;
16697
- background-repeat: no-repeat;
16698
- background-position: center;
16699
- border-radius: 5px;
16700
- overflow: hidden;
16701
- cursor: pointer;
16702
- }
16703
- .text {
16704
- padding: 10px;
16705
- width: 70%;
16706
- }
16707
- .header {
16708
- font-size: 12px;
16709
- color: ${textColor};
16710
- line-height: 16px;
16711
- font-family: "Source Sans 3", system-ui;
16712
- font-style: normal;
16713
- font-weight: 400;
16714
- line-height: normal;
16715
- margin: 0;
16716
- }
16717
- @media (min-width: 640px) {
16718
- .content {
16719
- background-image: url("${primaryImage}");
17761
+ const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
17762
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
17763
+ return `
17764
+ <style>
17765
+ .${prefix} {
17766
+ width: 100%;
17767
+ height: 100%;
17768
+ display: flex;
17769
+ flex-direction: column;
17770
+ justify-content: flex-end;
17771
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
17772
+ background-size: cover;
17773
+ background-repeat: no-repeat;
17774
+ background-position: center;
17775
+ border-radius: 5px;
17776
+ overflow: hidden;
17777
+ cursor: pointer;
17778
+ container-type: inline-size;
16720
17779
  }
16721
- }
16722
- </style>
16723
- `;
16724
- function rbSmallCategoryImageToutTemplate(spot) {
17780
+
17781
+ .${prefix}__text {
17782
+ padding: 10px;
17783
+ width: 70%;
17784
+ }
17785
+
17786
+ .${prefix}__header {
17787
+ font-size: 12px;
17788
+ color: ${textColor};
17789
+ line-height: 16px;
17790
+ font-family: "Source Sans 3", system-ui;
17791
+ font-style: normal;
17792
+ font-weight: 400;
17793
+ margin: 0;
17794
+ }
17795
+
17796
+ @container (min-width: 640px) {
17797
+ .${prefix} {
17798
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
17799
+ }
17800
+ }
17801
+ </style>
17802
+ `;
17803
+ };
17804
+ function rbSmallCategoryImageToutTemplate(spot, config) {
17805
+ const { prefix = '' } = config;
16725
17806
  return `
16726
17807
  ${GFONT_PRECONNECT}
16727
17808
  ${GFONT_CORMORANT}
16728
- ${STYLES$1(spot)}
16729
- <div class="content">
16730
- <div class="text">
16731
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
17809
+ ${STYLES$1(spot, config)}
17810
+ <div class="${prefix}">
17811
+ <div class="${prefix}__text">
17812
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
16732
17813
  </div>
16733
17814
  </div>
16734
17815
  `;
16735
17816
  }
16736
17817
 
16737
- const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primaryImage, mobilePrimaryImage = primaryImage, }) => `
17818
+ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primaryImage, mobilePrimaryImage = primaryImage, }, { prefix }) => `
16738
17819
  <style>
16739
- :host {
16740
- min-width: 165px;
16741
- min-height: 250px;
16742
- }
16743
- .content {
17820
+ .${prefix} {
16744
17821
  width: 100%;
16745
17822
  height: 100%;
16746
17823
  background-color: ${backgroundColor};
@@ -16748,8 +17825,10 @@ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primar
16748
17825
  display: flex;
16749
17826
  flex-direction: column;
16750
17827
  border-radius: 5px;
17828
+ container-type: inline-size;
16751
17829
  }
16752
- .image {
17830
+
17831
+ .${prefix}__image {
16753
17832
  width: 100%;
16754
17833
  height: 100%;
16755
17834
  background-image: url("${mobilePrimaryImage}");
@@ -16758,7 +17837,8 @@ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primar
16758
17837
  background-position: center;
16759
17838
  border-radius: 5px;
16760
17839
  }
16761
- .text {
17840
+
17841
+ .${prefix}__text {
16762
17842
  text-align: left;
16763
17843
  display: flex;
16764
17844
  flex-direction: row;
@@ -16768,7 +17848,8 @@ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primar
16768
17848
  height: fit-content;
16769
17849
  position: relative;
16770
17850
  }
16771
- .header {
17851
+
17852
+ .${prefix}__header {
16772
17853
  font-size: 12px;
16773
17854
  color: ${textColor};
16774
17855
  padding-top: 5px;
@@ -16776,46 +17857,42 @@ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primar
16776
17857
  font-family: "Source Sans 3", system-ui;
16777
17858
  font-style: normal;
16778
17859
  font-weight: 400;
16779
- line-height: normal;
16780
17860
  margin: 0;
16781
17861
  }
16782
- @media (min-width: 640px) {
16783
- .image {
17862
+
17863
+ @container (min-width: 640px) {
17864
+ .${prefix}__image {
16784
17865
  background-image: url("${primaryImage}");
16785
17866
  }
16786
17867
  }
16787
17868
  </style>
16788
17869
  `;
16789
- function rbSmallDiscoverToutTemplate(spot) {
17870
+ function rbSmallDiscoverToutTemplate(spot, config) {
17871
+ const { prefix = '' } = config;
16790
17872
  return `
16791
17873
  ${GFONT_PRECONNECT}
16792
17874
  ${GFONT_CORMORANT}
16793
- ${STYLES(spot)}
16794
- <div class="content">
16795
- <div class="image"></div>
16796
- <div class="text">
16797
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
17875
+ ${STYLES(spot, config)}
17876
+ <div class="${prefix}">
17877
+ <div class="${prefix}__image"></div>
17878
+ <div class="${prefix}__text">
17879
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
16798
17880
  </div>
16799
17881
  </div>
16800
17882
  `;
16801
17883
  }
16802
17884
 
16803
- var ENUM_SPOT_ELEMENT_ATTRIBUTE;
16804
- (function (ENUM_SPOT_ELEMENT_ATTRIBUTE) {
16805
- ENUM_SPOT_ELEMENT_ATTRIBUTE["WIDTH"] = "width";
16806
- ENUM_SPOT_ELEMENT_ATTRIBUTE["HEIGHT"] = "height";
16807
- ENUM_SPOT_ELEMENT_ATTRIBUTE["FLUID"] = "fluid";
16808
- ENUM_SPOT_ELEMENT_ATTRIBUTE["REDIRECT_ON_CLICK"] = "redirect-on-click";
16809
- })(ENUM_SPOT_ELEMENT_ATTRIBUTE || (ENUM_SPOT_ELEMENT_ATTRIBUTE = {}));
16810
17885
  /**
16811
- * Creates the spot html string based on the provided spot data.
17886
+ * Returns the HTML element for the given spot.
16812
17887
  *
16813
- * @param {ISpot} data - The spot data.
17888
+ * @param {ISpot} spot - The spot object.
17889
+ * @param {ISpotTemplateConfig} config - The spot template configuration.
16814
17890
  *
16815
- * @return {string} - The spot html string.
17891
+ * @return {HTMLElement | null} - The HTML element for the given spot or null if the spot template is not found.
16816
17892
  */
16817
- const GET_SPOT_TEMPLATE_HTML_STRING = (data) => {
17893
+ const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
16818
17894
  const templates = {
17895
+ // Reserve Bar Spot Templates
16819
17896
  [RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE]: {
16820
17897
  rbHomepageHeroThreeTile: rbHomepageHeroThreeTileTemplate,
16821
17898
  },
@@ -16843,6 +17920,7 @@ const GET_SPOT_TEMPLATE_HTML_STRING = (data) => {
16843
17920
  [RMN_SPOT_TYPE.RB_PRODUCT_UPCS]: {
16844
17921
  rbProductUpcs: () => '', // No template for this spot type, it will be handled by ReserveBar App.
16845
17922
  },
17923
+ // IAB Standard Spot Templates
16846
17924
  [RMN_SPOT_TYPE.BILLBOARD]: {
16847
17925
  billboardV1: billboardV1Template,
16848
17926
  billboardV2: billboardV2Template,
@@ -16869,232 +17947,854 @@ const GET_SPOT_TEMPLATE_HTML_STRING = (data) => {
16869
17947
  inTextV1: inTextV1Template,
16870
17948
  },
16871
17949
  };
16872
- const spotVariants = templates[data.spot];
17950
+ const spotVariants = templates[spot.spot];
16873
17951
  if (!spotVariants) {
16874
- return '';
17952
+ return null;
16875
17953
  }
16876
- const variantTemplate = spotVariants[data.variant];
17954
+ const variantTemplate = spotVariants[spot.variant];
16877
17955
  if (!variantTemplate) {
16878
- return '';
17956
+ return null;
16879
17957
  }
16880
- return variantTemplate(data);
17958
+ // Generate a highly unique prefix to avoid conflicts with other elements.
17959
+ const prefix = 's' + UniqueIdGenerator.generate().toLowerCase();
17960
+ const spotHtmlString = variantTemplate(spot, { ...config, prefix });
17961
+ return spotHtmlStringToElement(spotHtmlString);
16881
17962
  };
16882
17963
 
16883
- let SpotElement;
16884
- if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
16885
- class CustomSpotElement extends HTMLElement {
16886
- constructor() {
16887
- super();
16888
- this.hasCustomContent = false;
16889
- this.handleClick = this.handleClick.bind(this);
16890
- this.attachShadow({ mode: 'open' });
17964
+ /**
17965
+ * PubSub class
17966
+ * Manages event subscriptions and publications
17967
+ * @template IEventMap A record type defining the structure of events and their data
17968
+ */
17969
+ class PubSub {
17970
+ constructor() {
17971
+ /**
17972
+ * Object to store subscribers for each event type
17973
+ */
17974
+ this.subscribers = {};
17975
+ }
17976
+ static getInstance() {
17977
+ if (!PubSub.instance) {
17978
+ PubSub.instance = new PubSub();
16891
17979
  }
16892
- connectedCallback() {
16893
- this.hasCustomContent = Boolean(this.customContent);
16894
- this.addEventListener('click', this.handleClick);
16895
- this.setupIntersectionObserver();
16896
- this.render();
17980
+ return PubSub.instance;
17981
+ }
17982
+ /**
17983
+ * Subscribe to an event
17984
+ * @param eventType - The type of event to subscribe to
17985
+ * @param callback - The function to be called when the event is published
17986
+ * @returns A function to unsubscribe from the event
17987
+ *
17988
+ * @Example:
17989
+ * const unsubscribe = pubSub.subscribe('userLogin', (data) => {
17990
+ * console.log(`User ${data.username} logged in`);
17991
+ * });
17992
+ */
17993
+ subscribe(eventType, callback) {
17994
+ if (!this.subscribers[eventType]) {
17995
+ this.subscribers[eventType] = [];
16897
17996
  }
16898
- disconnectedCallback() {
16899
- var _a;
16900
- (_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
16901
- this.removeEventListener('click', this.handleClick);
17997
+ this.subscribers[eventType].push(callback);
17998
+ // Return an unsubscribe function
17999
+ return () => {
18000
+ this.subscribers[eventType] = this.subscribers[eventType].filter((cb) => cb !== callback);
18001
+ };
18002
+ }
18003
+ /**
18004
+ * Publish an event
18005
+ * @param eventType - The type of event to publish
18006
+ * @param data - The data to be passed to the event subscribers
18007
+ *
18008
+ * @Example:
18009
+ * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
18010
+ */
18011
+ publish(eventType, data) {
18012
+ if (!this.subscribers[eventType]) {
18013
+ return;
16902
18014
  }
16903
- attributeChangedCallback(_name, oldValue, newValue) {
16904
- if (oldValue !== newValue) {
16905
- this.render();
16906
- }
18015
+ this.subscribers[eventType].forEach((callback) => callback(data));
18016
+ }
18017
+ }
18018
+ /**
18019
+ * Usage Example:
18020
+ *
18021
+ * interface IEventMap {
18022
+ * userLogin: { username: string; timestamp: number };
18023
+ * pageView: { url: string; timestamp: number };
18024
+ * }
18025
+ *
18026
+ * const pubSub = new PubSub<IEventMap>();
18027
+ *
18028
+ * // Subscribe to events
18029
+ * const unsubscribeLogin = pubSub.subscribe('userLogin', (data) => {
18030
+ * console.log(`User ${data.username} logged in at ${new Date(data.timestamp)}`);
18031
+ * });
18032
+ *
18033
+ * pubSub.subscribe('pageView', (data) => {
18034
+ * console.log(`Page ${data.url} viewed at ${new Date(data.timestamp)}`);
18035
+ * });
18036
+ *
18037
+ * // Publish events
18038
+ * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
18039
+ * pubSub.publish('pageView', { url: '/home', timestamp: Date.now() });
18040
+ *
18041
+ * // Unsubscribe from an event
18042
+ * unsubscribeLogin();
18043
+ */
18044
+
18045
+ class EventService {
18046
+ constructor() {
18047
+ this.pubSub = PubSub.getInstance();
18048
+ this.localStorage = LocalStorage.getInstance();
18049
+ this.activeSpots = new Map();
18050
+ this.spotStates = new Map();
18051
+ this.intersectionObserver = new IntersectionObserverService();
18052
+ }
18053
+ static getInstance() {
18054
+ if (!EventService.instance) {
18055
+ EventService.instance = new EventService();
16907
18056
  }
16908
- render() {
16909
- if (!this.shadowRoot || !this.data)
16910
- return;
16911
- const { style, wrapper, slot } = SPOT_ELEMENT_TEMPLATE(this.data.width, this.data.height, this.hasCustomContent);
16912
- this.shadowRoot.replaceChildren(style, slot);
16913
- if (this.hasCustomContent) {
16914
- this.setCustomContent();
16915
- }
16916
- if (!this.hasCustomContent) {
16917
- wrapper.innerHTML = GET_SPOT_TEMPLATE_HTML_STRING(this.data);
16918
- this.shadowRoot.appendChild(wrapper);
16919
- }
18057
+ return EventService.instance;
18058
+ }
18059
+ subscribe(eventType, callback) {
18060
+ return this.pubSub.subscribe(eventType, callback);
18061
+ }
18062
+ publish(eventType, data) {
18063
+ this.pubSub.publish(eventType, data);
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;
16920
18085
  }
16921
- setCustomContent() {
16922
- const wrapper = document.createElement('div');
16923
- wrapper.setAttribute('slot', SPOT_ELEMENT_SLOT_NAME);
16924
- if (typeof this.customContent === 'string') {
16925
- wrapper.innerHTML = this.customContent;
16926
- }
16927
- if (this.customContent instanceof HTMLElement) {
16928
- wrapper.appendChild(this.customContent);
16929
- }
16930
- this.appendChild(wrapper);
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;
16931
18106
  }
16932
- setupIntersectionObserver() {
16933
- const options = {
16934
- root: null,
16935
- rootMargin: '0px',
16936
- threshold: 0.5, // The element is considered visible when 50% of it is visible
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
+ */
18118
+ handleSpotState(placementId, updates, publish = true) {
18119
+ let currentState = this.spotStates.get(placementId);
18120
+ if (!currentState) {
18121
+ currentState = {
18122
+ identifier: {
18123
+ placementId,
18124
+ spotId: '',
18125
+ spotType: '',
18126
+ },
18127
+ dom: {
18128
+ spotElement: undefined,
18129
+ visibleOnViewport: false,
18130
+ },
18131
+ state: {
18132
+ mounted: false,
18133
+ unmounted: false,
18134
+ loading: false,
18135
+ error: undefined,
18136
+ },
18137
+ displayConfig: {
18138
+ isCarousel: false,
18139
+ isCarouselItem: false,
18140
+ isSingleItem: false,
18141
+ },
16937
18142
  };
16938
- this.observer = new IntersectionObserver((entries) => {
16939
- var _a;
16940
- if (entries[0].isIntersecting) {
16941
- this.registerEvent(RMN_SPOT_EVENT.IMPRESSION);
16942
- (_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
16943
- }
16944
- }, options);
16945
- this.observer.observe(this);
16946
18143
  }
16947
- handleClick() {
16948
- this.registerEvent(RMN_SPOT_EVENT.CLICK);
18144
+ this.spotStates.set(placementId, { ...currentState, ...updates });
18145
+ if (publish) {
18146
+ this.pubSub.publish(RMN_SPOT_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
16949
18147
  }
16950
- async registerEvent(event) {
18148
+ }
18149
+ async handleClick({ placementId, spot, spotElement, }) {
18150
+ var _a, _b, _c;
18151
+ // Publish click event
18152
+ this.pubSub.publish(RMN_SPOT_EVENT.CLICK, {
18153
+ placementId,
18154
+ spotId: spot.id,
18155
+ spotElement,
18156
+ });
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],
18168
+ });
18169
+ }
18170
+ handleIntersectionObserver(placementId, _spot, spotElement) {
18171
+ const spotIsVisibleCallback = async () => {
18172
+ this.intersectionObserver.unobserve(spotElement);
18173
+ this.handleSpotState(placementId, {
18174
+ dom: {
18175
+ spotElement,
18176
+ visibleOnViewport: true,
18177
+ },
18178
+ });
18179
+ };
18180
+ this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
18181
+ }
18182
+ fireImpressionEvent(placementId, spot, spotElement) {
18183
+ this.pubSub.publish(RMN_SPOT_EVENT.IMPRESSION, {
18184
+ placementId,
18185
+ spotId: spot.id,
18186
+ spotElement,
18187
+ });
18188
+ (async () => {
16951
18189
  var _a, _b;
16952
- if (!this.data)
18190
+ await this.fireEvent({
18191
+ event: RMN_SPOT_EVENT.IMPRESSION,
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 : '',
18193
+ });
18194
+ })();
18195
+ }
18196
+ /**
18197
+ * Fires an event using the navigator.sendBeacon method or a fallback method if sendBeacon is not available.
18198
+ * If the event is a click event and a redirect URL is found, it redirects the user to that URL.
18199
+ *
18200
+ * @param {IFireEventParams} params - The parameters for firing the event.
18201
+ * @param {RMN_SPOT_EVENT} params.event - The event type.
18202
+ * @param {string} params.eventUrl - The URL to which the event is sent.
18203
+ * @returns {Promise<void>} - A promise that resolves when the event is fired.
18204
+ */
18205
+ async fireEvent({ event, eventUrl }) {
18206
+ var _a;
18207
+ try {
18208
+ const didFireEvent = ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.sendBeacon) === null || _a === void 0 ? void 0 : _a.call(navigator, eventUrl)) || (await this.fallbackEventFire(eventUrl));
18209
+ if (!didFireEvent) {
16953
18210
  return;
16954
- const shouldRedirectOnClick = this.getAttribute(ENUM_SPOT_ELEMENT_ATTRIBUTE.REDIRECT_ON_CLICK) === 'true';
16955
- const eventUrl = (_b = (_a = this.data.events.find((e) => e.event === event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '';
16956
- try {
16957
- const options = {
16958
- method: 'POST',
16959
- redirect: event === RMN_SPOT_EVENT.CLICK && shouldRedirectOnClick ? 'follow' : 'manual',
16960
- };
16961
- const response = await fetch(eventUrl, options);
16962
- if (response.ok && event === RMN_SPOT_EVENT.CLICK && shouldRedirectOnClick) {
16963
- window.location.href = this.getRedirectUrlFromPayload(eventUrl);
16964
- }
16965
18211
  }
16966
- catch (error) {
16967
- console.error(`Rmn error sending ${event} event:`, error);
18212
+ if (event === RMN_SPOT_EVENT.CLICK) {
18213
+ const redirectUrl = this.getRedirectUrlFromPayload(eventUrl);
18214
+ if (redirectUrl) {
18215
+ window.location.href = redirectUrl;
18216
+ }
16968
18217
  }
16969
18218
  }
16970
- getRedirectUrlFromPayload(url) {
16971
- var _a, _b;
16972
- const base64String = (_a = new URL(url).searchParams.get('e')) !== null && _a !== void 0 ? _a : '';
16973
- try {
16974
- const data = JSON.parse(atob(base64String));
16975
- return (_b = data.ur) !== null && _b !== void 0 ? _b : '';
16976
- }
16977
- catch (_c) {
16978
- return '';
16979
- }
18219
+ catch (_b) {
18220
+ // Handle errors silently
16980
18221
  }
16981
18222
  }
16982
- CustomSpotElement.observedAttributes = Object.values(ENUM_SPOT_ELEMENT_ATTRIBUTE);
16983
- SpotElement = CustomSpotElement;
16984
- }
16985
-
16986
- class SpotHtmlService {
16987
- static getInstance() {
16988
- return SingletonManager.getInstance('SpotHtmlService', () => new SpotHtmlService());
16989
- }
16990
- /**
16991
- * Creates the html element based on the provided spot data using shadow dom.
16992
- *
16993
- * This method is only available in browser environments.
16994
- *
16995
- * @param {ISpot} spot - The spot data.
16996
- * @param {ICreateSpotElementConfig} config - The configuration object.
16997
- * @param {ICreateSpotElementConfig.fluid} config.fluid - If the spot should be fluid or not.
16998
- * @param {ICreateSpotElementConfig.customContent} config.customContent - Use a custom html element/string.
16999
- * @param {ICreateSpotElementConfig.redirectOnClick} config.redirectOnClick - If the spot should redirect on click.
17000
- *
17001
- * @return {HTMLElement | null} - The spot html element or null if the browser environment is not available.
17002
- */
17003
- createSpotHtmlElement(spot, config) {
17004
- var _a, _b;
17005
- if (!this.ensureBrowserEnvironmentAndDefineElement()) {
17006
- return null;
18223
+ // Fallback method using fetch if sendBeacon isn't available
18224
+ async fallbackEventFire(url) {
18225
+ try {
18226
+ const racePromise = Promise.race([
18227
+ // Promise #1: The fetch request
18228
+ fetch(url, {
18229
+ method: 'POST',
18230
+ keepalive: true,
18231
+ }),
18232
+ // Promise #2: The timeout
18233
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), 2000)),
18234
+ ]);
18235
+ /**
18236
+ * Prevent requests from hanging indefinitely
18237
+ * Improve user experience by failing fast
18238
+ * Handle slow network conditions gracefully
18239
+ * Ensure resources are freed up in a timely manner
18240
+ */
18241
+ const response = await racePromise;
18242
+ return response.ok;
18243
+ }
18244
+ catch (_a) {
18245
+ return false;
17007
18246
  }
17008
- const isFluid = (_a = config === null || config === void 0 ? void 0 : config.fluid) !== null && _a !== void 0 ? _a : false;
17009
- const shouldRedirectOnClick = (_b = config === null || config === void 0 ? void 0 : config.redirectOnClick) !== null && _b !== void 0 ? _b : true;
17010
- const element = document.createElement(SPOT_ELEMENT_TAG);
17011
- element.setAttribute(ENUM_SPOT_ELEMENT_ATTRIBUTE.WIDTH, spot.width.toString());
17012
- element.setAttribute(ENUM_SPOT_ELEMENT_ATTRIBUTE.HEIGHT, spot.height.toString());
17013
- element.setAttribute(ENUM_SPOT_ELEMENT_ATTRIBUTE.FLUID, isFluid.toString());
17014
- element.setAttribute(ENUM_SPOT_ELEMENT_ATTRIBUTE.REDIRECT_ON_CLICK, shouldRedirectOnClick.toString());
17015
- // Share the spot data with the element
17016
- element.data = spot;
17017
- // Set custom content
17018
- if (config === null || config === void 0 ? void 0 : config.customContent) {
17019
- element.customContent = config.customContent;
17020
- }
17021
- return element;
17022
18247
  }
17023
18248
  /**
17024
- * @returns {boolean} - True if the browser environment is available and the element is defined.
18249
+ * Extracts and decodes a URL from a base64-encoded query parameter.
18250
+ *
18251
+ * @param {string} url - The URL containing the base64-encoded query parameter.
18252
+ * @returns {string | null} - The decoded URL or null if not found or invalid.
17025
18253
  */
17026
- ensureBrowserEnvironmentAndDefineElement() {
17027
- if (typeof window === 'undefined' || typeof document === 'undefined') {
17028
- console.warn('LiquidCommerce Rmn Sdk: createSpotElement is only available in browser environments!!!');
17029
- return false;
18254
+ getRedirectUrlFromPayload(url) {
18255
+ try {
18256
+ const base64String = new URL(url).searchParams.get('e');
18257
+ if (!base64String) {
18258
+ return null;
18259
+ }
18260
+ const data = JSON.parse(atob(base64String));
18261
+ return data.ur || null;
17030
18262
  }
17031
- if (!customElements.get(SPOT_ELEMENT_TAG)) {
17032
- customElements.define(SPOT_ELEMENT_TAG, SpotElement);
18263
+ catch (_a) {
18264
+ return null;
17033
18265
  }
17034
- return true;
17035
18266
  }
17036
18267
  }
17037
18268
 
17038
- class SpotSelectionService extends BaseApi {
18269
+ const SELECTION_API_PATH = '/spots/selection';
18270
+
18271
+ class SelectionService extends BaseApi {
17039
18272
  constructor(auth) {
17040
18273
  super(auth);
17041
18274
  }
17042
18275
  static getInstance(auth) {
17043
- return SingletonManager.getInstance('SpotSelectionService', () => new SpotSelectionService(auth));
18276
+ return SingletonManager.getInstance('SelectionService', () => new SelectionService(auth));
17044
18277
  }
17045
18278
  /**
17046
18279
  * Makes a selection request on our server based on the provided data.
17047
18280
  *
17048
18281
  * @param {ISpotSelectionParams} data - Spots selection parameters.
17049
18282
  *
17050
- * @return {Promise<ISpots>} - The spots response object.
18283
+ * @return {Promise<ISpots | { error: string }>} - The spots response object.
17051
18284
  */
17052
18285
  async spotSelection(data) {
17053
- const { isOk, val, isErr } = await this.post(SPOTS_SELECTION_API_PATH, data, {});
18286
+ const { isOk, val, isErr } = await this.post(SELECTION_API_PATH, data, {});
17054
18287
  if (isErr) {
17055
- throw new Error(`There was an error during spot selection: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})`);
18288
+ return { error: `There was an error during spot selection: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})` };
17056
18289
  }
17057
18290
  if (isOk && val && val.data && (val === null || val === void 0 ? void 0 : val.refresh.token)) {
17058
18291
  this.authInfo.authenticated = true;
17059
18292
  this.authInfo.token = val.refresh.token;
17060
18293
  return val.data.spots;
17061
18294
  }
17062
- throw new Error('Spot selection response was not successful');
18295
+ return { error: 'Spot selection response was not successful' };
17063
18296
  }
17064
18297
  }
17065
18298
 
18299
+ const SPOT_EVENTS_EXAMPLE = [
18300
+ {
18301
+ event: RMN_SPOT_EVENT.CLICK,
18302
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
18303
+ },
18304
+ {
18305
+ event: RMN_SPOT_EVENT.IMPRESSION,
18306
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
18307
+ },
18308
+ {
18309
+ event: RMN_SPOT_EVENT.PURCHASE,
18310
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
18311
+ },
18312
+ {
18313
+ event: RMN_SPOT_EVENT.ADD_TO_CART,
18314
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
18315
+ },
18316
+ {
18317
+ event: RMN_SPOT_EVENT.ADD_TO_WISHLIST,
18318
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYzfQ&s=m3ISU_iIy-OFtXrTKpI6cJAEC0k',
18319
+ },
18320
+ {
18321
+ event: RMN_SPOT_EVENT.BUY_NOW,
18322
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjY5fQ&s=l6MOscQC-q-FkC2Ksd7w6jjySCQ',
18323
+ },
18324
+ ];
18325
+ const RB_SPOTS_SELECTION_EXAMPLE = {
18326
+ rbHomepageHeroFullImage: [
18327
+ {
18328
+ id: '111111_111111',
18329
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18330
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18331
+ width: 1140,
18332
+ height: 640,
18333
+ header: 'Artisanal Craft Beer Collection',
18334
+ description: 'Discover our curated selection of small-batch, flavor-packed craft beers.',
18335
+ ctaText: 'Explore the Collection',
18336
+ textColor: '#ffffff',
18337
+ ctaTextColor: '#ffffff',
18338
+ primaryImage: 'https://placehold.co/1140x640/png?text=Craft+Beer+Collection',
18339
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Craft+Beer',
18340
+ events: SPOT_EVENTS_EXAMPLE,
18341
+ },
18342
+ {
18343
+ id: '222222_222222',
18344
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18345
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18346
+ width: 1140,
18347
+ height: 640,
18348
+ header: 'Summer Wine Spectacular',
18349
+ description: 'Refresh your palate with our handpicked selection of crisp, summer wines.',
18350
+ ctaText: 'Shop Summer Wines',
18351
+ textColor: '#000000',
18352
+ ctaTextColor: '#ffffff',
18353
+ primaryImage: 'https://placehold.co/1140x640/png?text=Summer+Wines',
18354
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Summer+Wines',
18355
+ events: SPOT_EVENTS_EXAMPLE,
18356
+ },
18357
+ ],
18358
+ rbHomepageHeroTwoTile: [
18359
+ {
18360
+ id: '333333_333333',
18361
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
18362
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
18363
+ width: 1140,
18364
+ height: 640,
18365
+ header: 'Whiskey Wonderland',
18366
+ description: 'Embark on a journey through our premium whiskey selection.',
18367
+ ctaText: 'Discover Whiskeys',
18368
+ textColor: '#ffffff',
18369
+ backgroundColor: '#2c1a05',
18370
+ ctaTextColor: '#2c1a05',
18371
+ primaryImage: 'https://placehold.co/1140x640/png?text=Whiskey+Collection',
18372
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Whiskey',
18373
+ events: SPOT_EVENTS_EXAMPLE,
18374
+ },
18375
+ ],
18376
+ rbHomepageHeroThreeTile: [
18377
+ {
18378
+ id: '444444_444444',
18379
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
18380
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
18381
+ width: 1140,
18382
+ height: 640,
18383
+ header: 'Cocktail Essentials',
18384
+ description: 'Stock your bar with premium spirits and mixers for the perfect cocktail.',
18385
+ ctaText: 'Build Your Bar',
18386
+ textColor: '#ffffff',
18387
+ backgroundColor: '#1a3c4d',
18388
+ ctaTextColor: '#1a3c4d',
18389
+ primaryImage: 'https://placehold.co/1140x640/png?text=Cocktail+Spirits',
18390
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Cocktail+Mixers',
18391
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Cocktail+Kit',
18392
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Cocktail+Mixers',
18393
+ events: SPOT_EVENTS_EXAMPLE,
18394
+ },
18395
+ ],
18396
+ rbLargeCategoryImageTout: [
18397
+ {
18398
+ id: '555555_555555',
18399
+ spot: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
18400
+ variant: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
18401
+ width: 468,
18402
+ height: 410,
18403
+ header: 'Rare & Limited Edition',
18404
+ description: 'Discover our collection of hard-to-find and limited release spirits.',
18405
+ textColor: '#ffffff',
18406
+ ctaTextColor: '#ffffff',
18407
+ primaryImage: 'https://placehold.co/468x410/png?text=Rare+Spirits',
18408
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Rare+Spirits',
18409
+ ctaText: 'Shop Rare Spirits',
18410
+ events: SPOT_EVENTS_EXAMPLE,
18411
+ },
18412
+ ],
18413
+ rbSmallDiscoverTout: [
18414
+ {
18415
+ id: '666666_666666',
18416
+ spot: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
18417
+ variant: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
18418
+ width: 224,
18419
+ height: 378,
18420
+ header: 'Château Margaux 2015 Bordeaux',
18421
+ textColor: '#ffffff',
18422
+ primaryImage: 'https://placehold.co/224x378/png?text=Château+Margaux',
18423
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Château+Margaux',
18424
+ events: SPOT_EVENTS_EXAMPLE,
18425
+ },
18426
+ ],
18427
+ rbSmallCategoryImageTout: [
18428
+ {
18429
+ id: '777777_777777',
18430
+ spot: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
18431
+ variant: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
18432
+ width: 224,
18433
+ height: 410,
18434
+ header: 'Japanese Sake',
18435
+ textColor: '#ffffff',
18436
+ primaryImage: 'https://placehold.co/224x410/png?text=Japanese+Sake',
18437
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Japanese+Sake',
18438
+ events: SPOT_EVENTS_EXAMPLE,
18439
+ },
18440
+ ],
18441
+ rbCollectionBannerWithoutTextBlock: [
18442
+ {
18443
+ id: '888888_888888',
18444
+ spot: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
18445
+ variant: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
18446
+ width: 887,
18447
+ height: 344,
18448
+ primaryImage: 'https://placehold.co/887x344/png?text=Summer+Cocktails',
18449
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Summer+Cocktails',
18450
+ events: SPOT_EVENTS_EXAMPLE,
18451
+ },
18452
+ ],
18453
+ rbNavigationBanner: [
18454
+ {
18455
+ id: '999999_999999',
18456
+ spot: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
18457
+ variant: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
18458
+ width: 440,
18459
+ height: 220,
18460
+ header: 'Explore Tequilas',
18461
+ textColor: '#ffffff',
18462
+ primaryImage: 'https://placehold.co/440x220/png?text=Tequila+Collection',
18463
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Tequila+Collection',
18464
+ events: SPOT_EVENTS_EXAMPLE,
18465
+ },
18466
+ ],
18467
+ };
18468
+
17066
18469
  class LiquidCommerceRmnClient {
17067
18470
  constructor(auth) {
17068
- this.spotSelectionService = SpotSelectionService.getInstance(auth);
17069
- this.spotHtmlService = SpotHtmlService.getInstance();
18471
+ this.selectionService = SelectionService.getInstance(auth);
18472
+ this.elementService = ElementService.getInstance();
18473
+ this.eventService = EventService.getInstance();
17070
18474
  }
17071
18475
  /**
17072
18476
  * Makes a selection request on our server based on the provided data.
17073
18477
  *
17074
18478
  * To create a spot html element, use the RmnCreateSpotElement function.
17075
18479
  *
17076
- * @param {ISpotSelectionParams} data - Spots selection parameters.
18480
+ * @param {ISpotSelectionParams} params - Spots selection parameters.
17077
18481
  *
17078
- * @return {Promise<ISpots>} - The spots response object.
18482
+ * @return {Promise<ISpots | {error : string}>} - The spots response object.
17079
18483
  */
17080
- async spotSelection(data) {
17081
- return this.spotSelectionService.spotSelection(data);
18484
+ async spotSelection(params) {
18485
+ return this.selectionService.spotSelection(params);
18486
+ }
18487
+ /**
18488
+ * Injects the spot elements into their provided placement.
18489
+ *
18490
+ * @param {IInjectSpotElementParams} params - Parameters for injecting spot elements.
18491
+ *
18492
+ * @return {Promise<void>} - A promise that resolves when the spot elements are injected.
18493
+ */
18494
+ async injectSpotElement(params) {
18495
+ var _a;
18496
+ const config = params.config;
18497
+ let inject = params.inject;
18498
+ if (!inject.length) {
18499
+ this.eventService.handleSpotState('all', {
18500
+ state: {
18501
+ error: 'No spot elements provided for injection.',
18502
+ loading: false,
18503
+ },
18504
+ });
18505
+ return;
18506
+ }
18507
+ // Update the state of the spots to loading
18508
+ this.updateSpotsState(inject);
18509
+ // Prevent duplicate placement ids
18510
+ const hasDuplicatePlacementIds = this.preventDuplicateSpotPlacementIds(inject);
18511
+ if (!hasDuplicatePlacementIds) {
18512
+ return;
18513
+ }
18514
+ // Prevent non-existent spot types
18515
+ inject = this.preventNonExistentSpotTypes(inject);
18516
+ // Make the spot selection request
18517
+ const response = await this.spotSelectionRequest({ ...params, inject });
18518
+ // const response = await this.useSpotSelectionExample(inject);
18519
+ // Handle the response
18520
+ if (typeof response === 'object' && 'error' in response) {
18521
+ this.eventService.handleSpotState('all', {
18522
+ state: {
18523
+ error: response.error,
18524
+ },
18525
+ });
18526
+ return;
18527
+ }
18528
+ for (const item of inject) {
18529
+ const itemConfig = (_a = item.config) !== null && _a !== void 0 ? _a : config;
18530
+ const spots = response[item.placementId];
18531
+ if (!(spots === null || spots === void 0 ? void 0 : spots.length)) {
18532
+ this.eventService.handleSpotState(item.placementId, {
18533
+ state: {
18534
+ error: `No spots found for type "${item.spotType}".`,
18535
+ loading: false,
18536
+ },
18537
+ });
18538
+ continue;
18539
+ }
18540
+ const placementId = item.placementId.replace('#', '');
18541
+ const placement = document.getElementById(placementId);
18542
+ if (!placement) {
18543
+ this.eventService.handleSpotState(item.placementId, {
18544
+ state: {
18545
+ error: `Placement not found for id "${placementId}".`,
18546
+ loading: false,
18547
+ },
18548
+ });
18549
+ continue;
18550
+ }
18551
+ // Take over placement styles
18552
+ placement.removeAttribute('style');
18553
+ placement.removeAttribute('class');
18554
+ Object.assign(placement.style, {
18555
+ width: '100%',
18556
+ height: 'auto',
18557
+ display: 'flex',
18558
+ justifyContent: 'center',
18559
+ });
18560
+ if (spots.length === 1) {
18561
+ const isInjected = this.injectOneSpotElement(item, placement, spots[0], itemConfig);
18562
+ if (!isInjected) {
18563
+ continue;
18564
+ }
18565
+ }
18566
+ if (spots.length > 1) {
18567
+ const isInjected = this.injectCarouselSpotElement(placement, spots, itemConfig);
18568
+ if (!isInjected) {
18569
+ continue;
18570
+ }
18571
+ }
18572
+ }
18573
+ }
18574
+ /**
18575
+ * Makes a selection request on our server based on the provided data.
18576
+ *
18577
+ * @param {IInjectSpotElementParams} params - Parameters for injecting spot elements.
18578
+ *
18579
+ * @return {Promise<ISpots | {error: string}>} - The spots response object.
18580
+ */
18581
+ async spotSelectionRequest(params) {
18582
+ const { inject, filter, config } = params;
18583
+ const request = {
18584
+ url: config === null || config === void 0 ? void 0 : config.url,
18585
+ filter,
18586
+ spots: inject.map((item) => ({
18587
+ placementId: item.placementId,
18588
+ spot: item.spotType,
18589
+ count: item === null || item === void 0 ? void 0 : item.count,
18590
+ ...item === null || item === void 0 ? void 0 : item.filter,
18591
+ })),
18592
+ };
18593
+ return this.spotSelection(request);
17082
18594
  }
17083
18595
  /**
17084
- * Creates the spot html element based on the provided data using shadow dom.
18596
+ * Injects a carousel element with the provided spots into the placement.
18597
+ *
18598
+ * @param {HTMLElement} placement - The placement element.
18599
+ * @param {ISpot[]} spots - The spot data.
18600
+ * @param {IInjectSpotElementConfig} config - The configuration object.
17085
18601
  *
17086
- * This method is useful when you are initializing the client in a browser environment, so you can create the spot html element directly from the RmnClient instance.
18602
+ * @return {void}
18603
+ */
18604
+ injectCarouselSpotElement(placement, spots, config) {
18605
+ var _a;
18606
+ const carouselSlides = [];
18607
+ for (const spotItem of spots) {
18608
+ this.eventService.handleSpotState(placement.id, {
18609
+ displayConfig: {
18610
+ isCarousel: true,
18611
+ isCarouselItem: true,
18612
+ isSingleItem: false,
18613
+ },
18614
+ }, false);
18615
+ const spot = this.elementService.overrideSpotColors(spotItem, config === null || config === void 0 ? void 0 : config.colors);
18616
+ const content = SPOT_TEMPLATE_HTML_ELEMENT(spot, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
18617
+ if (!content) {
18618
+ this.eventService.handleSpotState(placement.id, {
18619
+ state: {
18620
+ error: `Failed to inject carousel spot item element. Could not create element for type "${spot.spot}".`,
18621
+ loading: false,
18622
+ },
18623
+ });
18624
+ continue;
18625
+ }
18626
+ this.eventService.registerSpot({
18627
+ spot,
18628
+ placementId: placement.id,
18629
+ spotElement: content,
18630
+ });
18631
+ carouselSlides.push(content);
18632
+ }
18633
+ // Get the max width and height of the spots
18634
+ const { maxWidth, maxHeight } = spots.reduce((max, spot) => ({
18635
+ maxWidth: Math.max(max.maxWidth, spot.width),
18636
+ maxHeight: Math.max(max.maxHeight, spot.height),
18637
+ }), { maxWidth: 0, maxHeight: 0 });
18638
+ // Create the carousel element
18639
+ const carouselElement = this.elementService.createCarouselElement({
18640
+ slides: carouselSlides,
18641
+ config: {
18642
+ fluid: config === null || config === void 0 ? void 0 : config.fluid,
18643
+ width: maxWidth,
18644
+ height: maxHeight,
18645
+ minScale: (_a = config === null || config === void 0 ? void 0 : config.minScale) !== null && _a !== void 0 ? _a : 0.25, // Scale down to 25% of the original size
18646
+ ...config === null || config === void 0 ? void 0 : config.carousel,
18647
+ },
18648
+ });
18649
+ if (!carouselElement) {
18650
+ this.eventService.handleSpotState(placement.id, {
18651
+ state: {
18652
+ error: `Failed to inject spot carousel element. Could not create spot carousel element.`,
18653
+ loading: false,
18654
+ },
18655
+ });
18656
+ return false;
18657
+ }
18658
+ placement.replaceChildren(carouselElement);
18659
+ this.eventService.handleSpotState(placement.id, {
18660
+ dom: {
18661
+ spotElement: carouselElement,
18662
+ },
18663
+ state: {
18664
+ mounted: true,
18665
+ loading: false,
18666
+ },
18667
+ });
18668
+ return true;
18669
+ }
18670
+ /**
18671
+ * Injects a single spot element into the provided placement.
17087
18672
  *
18673
+ * @param {IInjectSpotElement} injectItem - The inject item data.
18674
+ * @param {HTMLElement} placement - The placement element.
17088
18675
  * @param {ISpot} spot - The spot data.
17089
- * @param {ICreateSpotElementConfig} config - The configuration object.
17090
- * @param {ICreateSpotElementConfig.fluid} config.fluid - If the spot should be fluid or not.
17091
- * @param {ICreateSpotElementConfig.customContent} config.customContent - Use a custom html element/string.
17092
- * @param {ICreateSpotElementConfig.redirectOnClick} config.redirectOnClick - If the spot should redirect on click.
18676
+ * @param {IInjectSpotElementConfig} config - The configuration object.
18677
+ *
18678
+ * @return {void}
18679
+ */
18680
+ injectOneSpotElement(injectItem, placement, spot, config) {
18681
+ var _a;
18682
+ const spotData = this.elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
18683
+ this.eventService.handleSpotState(injectItem.placementId, {
18684
+ displayConfig: {
18685
+ isSingleItem: true,
18686
+ },
18687
+ }, false);
18688
+ // Create the spot template element
18689
+ const content = SPOT_TEMPLATE_HTML_ELEMENT(spotData, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
18690
+ if (!content) {
18691
+ this.eventService.handleSpotState(injectItem.placementId, {
18692
+ state: {
18693
+ error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
18694
+ loading: false,
18695
+ },
18696
+ });
18697
+ return false;
18698
+ }
18699
+ // Create the spot element
18700
+ const spotElement = this.elementService.createSpotElement({
18701
+ content,
18702
+ config: {
18703
+ fluid: config === null || config === void 0 ? void 0 : config.fluid,
18704
+ spot: spot.spot,
18705
+ width: spot.width,
18706
+ height: spot.height,
18707
+ minScale: (_a = config === null || config === void 0 ? void 0 : config.minScale) !== null && _a !== void 0 ? _a : 0.25, // Scale down to 25% of the original size
18708
+ },
18709
+ });
18710
+ if (!spotElement) {
18711
+ this.eventService.handleSpotState(injectItem.placementId, {
18712
+ state: {
18713
+ error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
18714
+ loading: false,
18715
+ },
18716
+ });
18717
+ return false;
18718
+ }
18719
+ this.eventService.registerSpot({
18720
+ spot: spotData,
18721
+ placementId: injectItem.placementId,
18722
+ spotElement,
18723
+ });
18724
+ placement.replaceChildren(spotElement);
18725
+ this.eventService.handleSpotState(injectItem.placementId, {
18726
+ dom: {
18727
+ spotElement,
18728
+ },
18729
+ state: {
18730
+ mounted: true,
18731
+ loading: false,
18732
+ },
18733
+ });
18734
+ return true;
18735
+ }
18736
+ /**
18737
+ * Prevents duplicate placement ids in the inject data.
18738
+ *
18739
+ * @param {IInjectSpotElement[]} inject - The inject data.
17093
18740
  *
17094
- * @return {HTMLElement | null} - The spot html element or null if the browser environment is not available.
18741
+ * @throws {Error} - If a duplicate placement id is found.
18742
+ *
18743
+ * @return {void}
17095
18744
  */
17096
- createSpotElement(spot, config) {
17097
- return this.spotHtmlService.createSpotHtmlElement(spot, config);
18745
+ preventDuplicateSpotPlacementIds(inject) {
18746
+ const placementIds = new Set();
18747
+ for (const item of inject) {
18748
+ if (placementIds.has(item.placementId)) {
18749
+ this.eventService.handleSpotState(item.placementId, {
18750
+ state: {
18751
+ error: `Duplicate placement id (${item.placementId}) found. Please provide a unique placement id for each spot element.`,
18752
+ },
18753
+ });
18754
+ return false;
18755
+ }
18756
+ placementIds.add(item.placementId);
18757
+ }
18758
+ return true;
18759
+ }
18760
+ preventNonExistentSpotTypes(inject) {
18761
+ const newInject = [];
18762
+ for (const item of inject) {
18763
+ if (!Object.values(RMN_SPOT_TYPE).includes(item.spotType)) {
18764
+ this.eventService.handleSpotState(item.placementId, {
18765
+ state: {
18766
+ error: `Invalid spot type (${item.spotType}) found. Please provide a valid spot type for each spot element.`,
18767
+ },
18768
+ });
18769
+ continue;
18770
+ }
18771
+ newInject.push(item);
18772
+ }
18773
+ return newInject;
18774
+ }
18775
+ updateSpotsState(inject) {
18776
+ for (const item of inject) {
18777
+ this.eventService.handleSpotState(item.placementId, {
18778
+ identifier: {
18779
+ placementId: item.placementId,
18780
+ spotType: item.spotType,
18781
+ },
18782
+ state: {
18783
+ loading: true,
18784
+ },
18785
+ });
18786
+ }
18787
+ }
18788
+ useSpotSelectionExample(inject) {
18789
+ const examples = RB_SPOTS_SELECTION_EXAMPLE;
18790
+ const data = {};
18791
+ inject.map((item) => {
18792
+ var _a, _b, _c;
18793
+ 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 : [];
18794
+ });
18795
+ return new Promise((resolve) => {
18796
+ resolve(data);
18797
+ });
17098
18798
  }
17099
18799
  }
17100
18800
  /**
@@ -17110,24 +18810,66 @@ async function RmnClient(apiKey, config) {
17110
18810
  const credentials = await authService.initialize();
17111
18811
  return new LiquidCommerceRmnClient(credentials);
17112
18812
  }
18813
+ /**
18814
+ * Creates a new instance of the RmnEventManager.
18815
+ *
18816
+ * @return {IRmnEventManager} - The RmnEventManager instance.
18817
+ */
18818
+ function RmnEventManager() {
18819
+ const eventService = EventService.getInstance();
18820
+ return {
18821
+ /**
18822
+ * Subscribes to an event type.
18823
+ */
18824
+ subscribe: (eventType, callback
18825
+ /* eslint-disable arrow-body-style */
18826
+ ) => {
18827
+ return eventService.subscribe(eventType, callback);
18828
+ },
18829
+ /**
18830
+ * Publishes an event type.
18831
+ */
18832
+ publish: (eventType, data) => {
18833
+ eventService.publish(eventType, data);
18834
+ },
18835
+ /**
18836
+ * Destroys a spot element
18837
+ */
18838
+ destroySpot: (placementId) => {
18839
+ eventService.unregisterSpot(placementId);
18840
+ },
18841
+ };
18842
+ }
17113
18843
  /**
17114
18844
  * Creates the spot html element based on the provided data using shadow dom.
17115
18845
  *
17116
18846
  * This method is useful when you are initializing the client in a non-browser environment.
17117
- * When you request a spot selection, you will receive the spot data in server-side and return them back to the client.
18847
+ * When you request a spot selection, you will receive the spot data in server-side and return them to the client.
17118
18848
  * Then you can use this function to create the spot html element based on the provided data without the need of the RmnClient instance.
17119
18849
  *
17120
18850
  * @param {ISpot} spot - The spot data.
17121
- * @param {ICreateSpotElementConfig} config - The configuration object.
17122
- * @param {ICreateSpotElementConfig.fluid} config.fluid - If the spot should be fluid or not.
17123
- * @param {ICreateSpotElementConfig.customContent} config.customContent - Use a custom html element/string.
17124
- * @param {ICreateSpotElementConfig.redirectOnClick} config.redirectOnClick - If the spot should redirect on click.
18851
+ * @param {IRmnCreateSpotElementConfig} config - The configuration object.
17125
18852
  *
17126
18853
  * @return {HTMLElement | null} - The spot html element or null if the browser environment is not available.
17127
18854
  */
17128
18855
  function RmnCreateSpotElement(spot, config) {
17129
- const spotHtmlService = SpotHtmlService.getInstance();
17130
- return spotHtmlService.createSpotHtmlElement(spot, config);
18856
+ var _a;
18857
+ const elementService = ElementService.getInstance();
18858
+ const spotData = elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
18859
+ const content = SPOT_TEMPLATE_HTML_ELEMENT(spotData, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
18860
+ if (!content) {
18861
+ console.warn(`RmnSdk: Failed to create spot element for type "${spotData.spot}".`);
18862
+ return null;
18863
+ }
18864
+ return elementService.createSpotElement({
18865
+ content,
18866
+ config: {
18867
+ fluid: true,
18868
+ width: spot.width,
18869
+ height: spot.height,
18870
+ minScale: (_a = config === null || config === void 0 ? void 0 : config.minScale) !== null && _a !== void 0 ? _a : 0.25, // Scale down to 25% of the original size
18871
+ },
18872
+ });
17131
18873
  }
17132
18874
 
17133
- export { LiquidCommerceRmnClient, RMN_ENV, RMN_FILTER_PROPERTIES, RMN_SPOT_EVENT, RMN_SPOT_TYPE, RmnClient, RmnCreateSpotElement };
18875
+ export { LiquidCommerceRmnClient, RMN_ENV, RMN_FILTER_PROPERTIES, RMN_SPOT_EVENT, RMN_SPOT_TYPE, RmnClient, RmnCreateSpotElement, RmnEventManager };