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