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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 };