@inappstory/slide-api 0.1.26 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1045,6 +1045,12 @@ class EsModuleSdkApi {
1045
1045
  constructor(sdkBinding) {
1046
1046
  this.sdkBinding = sdkBinding;
1047
1047
  }
1048
+ onWidgetRequirePauseUI() {
1049
+ return this.sdkBinding.onWidgetRequirePauseUI();
1050
+ }
1051
+ onWidgetRequireResumeUI() {
1052
+ return this.sdkBinding.onWidgetRequireResumeUI();
1053
+ }
1048
1054
  getCardServerData(cardId) {
1049
1055
  return this.sdkBinding.getCardServerData(cardId);
1050
1056
  }
@@ -1138,8 +1144,8 @@ class EsModuleSdkApi {
1138
1144
  isSdkSupportUpdateTimeline() {
1139
1145
  return true;
1140
1146
  }
1141
- updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError) {
1142
- this.sdkBinding.updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError);
1147
+ async updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError) {
1148
+ await this.sdkBinding.updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError);
1143
1149
  }
1144
1150
  cardPausedCallback(currentTime) { }
1145
1151
  cardResumedCallback(currentTime) { }
@@ -1185,6 +1191,18 @@ class EsModuleSdkApi {
1185
1191
  // console.log("onCardLoadingStateChange", { state });
1186
1192
  this.sdkBinding.onCardLoadingStateChange(state, reason);
1187
1193
  }
1194
+ get isSdkSupportProductCart() {
1195
+ return true;
1196
+ }
1197
+ productCartUpdate(data) {
1198
+ return this.sdkBinding.productCartUpdate(data);
1199
+ }
1200
+ productCartGetState() {
1201
+ return this.sdkBinding.productCartGetState();
1202
+ }
1203
+ productCartClicked() {
1204
+ this.sdkBinding.productCartClicked();
1205
+ }
1188
1206
  static get [Symbol.for("___CTOR_ARGS___")]() { return [`SDKInterface`]; }
1189
1207
  }
1190
1208
 
@@ -3413,7 +3431,7 @@ class SlideTimeline {
3413
3431
  await this.slideReady.then();
3414
3432
  // window._log(`updateTimeline, a: ${action} ct: ${currentTime} d: ${duration} tds: ${this.timelineDisabledState}`, true);
3415
3433
  // console.trace(`updateTimeline ${action} slideIndex: ${this.slideIndex} currentTime:${currentTime} duration:${duration} tds: ${this.timelineDisabledState} showLoader: ${showLoader} showError: ${showError}`);
3416
- this.slideApiDeps.updateTimeline(this.slideIndex, action, currentTime, duration, showLoader, showError);
3434
+ await this.slideApiDeps.updateTimeline(this.slideIndex, action, currentTime, duration, showLoader, showError);
3417
3435
  if (action === "pause" /* TIMELINE_ACTION.PAUSE */ && showLoader) {
3418
3436
  this.dataWaitingStartedAt = Date.now();
3419
3437
  }
@@ -3439,13 +3457,13 @@ class SlideTimeline {
3439
3457
  * Start timeline after slide started
3440
3458
  * Nothing do if old sdk
3441
3459
  */
3442
- slideStarted() {
3460
+ async slideStarted() {
3443
3461
  // console.trace("slideStarted");
3444
3462
  this.onBeforeStateChanged();
3445
3463
  if (this.isSDKSupportUpdateTimeline) {
3446
3464
  this.resumedAt = new Date().getTime();
3447
3465
  this.timeSpent = 0; // for case when instance exists, but we return to slide again
3448
- this.updateTimeline("start" /* TIMELINE_ACTION.START */);
3466
+ await this.updateTimeline("start" /* TIMELINE_ACTION.START */);
3449
3467
  }
3450
3468
  }
3451
3469
  slideRestarted() {
@@ -3651,10 +3669,20 @@ class Layer {
3651
3669
  });
3652
3670
  };
3653
3671
  const onWidgetRequirePauseUI = (cardId, slideIndex) => {
3654
- this._slidePauseUI();
3672
+ if (this.slideApiDeps.isWeb) {
3673
+ this.slideApiDeps.onWidgetRequirePauseUI();
3674
+ }
3675
+ else {
3676
+ this._slidePauseUI();
3677
+ }
3655
3678
  };
3656
3679
  const onWidgetRequireResumeUI = (cardId, slideIndex) => {
3657
- this._slideResumeUI();
3680
+ if (this.slideApiDeps.isWeb) {
3681
+ this.slideApiDeps.onWidgetRequireResumeUI();
3682
+ }
3683
+ else {
3684
+ this._slideResumeUI();
3685
+ }
3658
3686
  };
3659
3687
  const _elementsNodes = this._nodeRef.querySelectorAll(".narrative-slide-elements .narrative-element:not(.narrative-element-child-element)");
3660
3688
  let layerWithWidgetQuest = false;
@@ -3893,7 +3921,7 @@ class Layer {
3893
3921
  }
3894
3922
  // skip start timeline if we returned to slide with enabled timer and opened WidgetProducts modal
3895
3923
  if (!this.isLayerForcePaused) {
3896
- this.timeline.slideStarted();
3924
+ await this.timeline.slideStarted();
3897
3925
  }
3898
3926
  return { currentTime };
3899
3927
  }
@@ -5488,6 +5516,12 @@ class SlideApiDepsMultiSlideMode {
5488
5516
  this.sdkApi = sdkApi;
5489
5517
  this.slider = slider;
5490
5518
  }
5519
+ onWidgetRequirePauseUI() {
5520
+ return this.sdkApi.onWidgetRequirePauseUI();
5521
+ }
5522
+ onWidgetRequireResumeUI() {
5523
+ return this.sdkApi.onWidgetRequireResumeUI();
5524
+ }
5491
5525
  getWidgetsSharedData(cardId, widget) {
5492
5526
  return this.sdkApi.getWidgetsSharedData(cardId, widget);
5493
5527
  }
@@ -5611,6 +5645,18 @@ class SlideApiDepsMultiSlideMode {
5611
5645
  showLayer(index) {
5612
5646
  this.sdkApi.showLayer(index);
5613
5647
  }
5648
+ get isSdkSupportProductCart() {
5649
+ return this.sdkApi.isSdkSupportProductCart;
5650
+ }
5651
+ productCartUpdate(data) {
5652
+ return this.sdkApi.productCartUpdate(data);
5653
+ }
5654
+ productCartGetState() {
5655
+ return this.sdkApi.productCartGetState();
5656
+ }
5657
+ productCartClicked() {
5658
+ this.sdkApi.productCartClicked();
5659
+ }
5614
5660
  /**
5615
5661
  * For single slide mode - proxy these methods via SDKApi
5616
5662
  * =================================================================================================================
@@ -5624,8 +5670,8 @@ class SlideApiDepsMultiSlideMode {
5624
5670
  isSdkSupportUpdateTimeline() {
5625
5671
  return true;
5626
5672
  }
5627
- updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError) {
5628
- this.slider.updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError);
5673
+ async updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError) {
5674
+ await this.slider.updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError);
5629
5675
  }
5630
5676
  isSdkSupportTimelineOnBeforeStart() {
5631
5677
  return true;
@@ -5644,6 +5690,12 @@ class SlideApiDepsSingleSlideMode {
5644
5690
  constructor(sdkApi) {
5645
5691
  this.sdkApi = sdkApi;
5646
5692
  }
5693
+ onWidgetRequirePauseUI() {
5694
+ return this.sdkApi.onWidgetRequirePauseUI();
5695
+ }
5696
+ onWidgetRequireResumeUI() {
5697
+ return this.sdkApi.onWidgetRequireResumeUI();
5698
+ }
5647
5699
  getWidgetsSharedData(cardId, widget) {
5648
5700
  return this.sdkApi.getWidgetsSharedData(cardId, widget);
5649
5701
  }
@@ -5765,6 +5817,18 @@ class SlideApiDepsSingleSlideMode {
5765
5817
  showLayer(index) {
5766
5818
  this.sdkApi.showLayer(index);
5767
5819
  }
5820
+ get isSdkSupportProductCart() {
5821
+ return this.sdkApi.isSdkSupportProductCart;
5822
+ }
5823
+ productCartUpdate(data) {
5824
+ return this.sdkApi.productCartUpdate(data);
5825
+ }
5826
+ productCartGetState() {
5827
+ return this.sdkApi.productCartGetState();
5828
+ }
5829
+ productCartClicked() {
5830
+ this.sdkApi.productCartClicked();
5831
+ }
5768
5832
  /**
5769
5833
  * For single slide mode - proxy these methods via SDKApi
5770
5834
  */
@@ -5777,8 +5841,8 @@ class SlideApiDepsSingleSlideMode {
5777
5841
  isSdkSupportUpdateTimeline() {
5778
5842
  return this.sdkApi.isSdkSupportUpdateTimeline();
5779
5843
  }
5780
- updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError) {
5781
- this.sdkApi.updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError);
5844
+ async updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError) {
5845
+ await this.sdkApi.updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError);
5782
5846
  }
5783
5847
  isSdkSupportTimelineOnBeforeStart() {
5784
5848
  return this.sdkApi.isSdkSupportTimelineOnBeforeStart();
@@ -5942,6 +6006,8 @@ class Slider {
5942
6006
  });
5943
6007
  }
5944
6008
  isAnimating = false;
6009
+ // block click while slide loading and starting (prevent several timeline starting)
6010
+ isSlideStarting = false;
5945
6011
  activeIndex = -1;
5946
6012
  get activeSlide() {
5947
6013
  return this.slides[this.activeIndex];
@@ -5958,7 +6024,7 @@ class Slider {
5958
6024
  css += `--navbar-edge-offset: ${navbarAppearance.edge_offset};`;
5959
6025
  }
5960
6026
  if (navbarAppearance.active_timeline_width != null) {
5961
- css += `--navbar-active-timeline_width: ${navbarAppearance.active_timeline_width};`;
6027
+ css += `--navbar-active-timeline-width: ${navbarAppearance.active_timeline_width};`;
5962
6028
  }
5963
6029
  if (navbarAppearance.background_color != null) {
5964
6030
  css += `--navbar-background-color: ${navbarAppearance.background_color};`;
@@ -6051,25 +6117,28 @@ class Slider {
6051
6117
  bullets.classList.add("cards-slider__navbar--position-bottom");
6052
6118
  }
6053
6119
  bullets.dir = getLayoutDirection();
6054
- for (let i = 0; i < count; ++i) {
6055
- const bullet = document.createElement("div");
6056
- bullet.classList.add("cards-slider__timeline", "touchable");
6057
- bullet.setAttribute("data-index", String(i));
6058
- bullet.onclick = (e) => {
6059
- e.stopPropagation();
6060
- e.preventDefault();
6061
- const bullet = e.target;
6062
- if (bullet != null) {
6063
- const index = bullet.dataset.index;
6064
- if (index != null) {
6065
- this.showByIndex(parseInt(index));
6120
+ // skip creating view elements for single slide
6121
+ if (count > 1) {
6122
+ for (let i = 0; i < count; ++i) {
6123
+ const bullet = document.createElement("div");
6124
+ bullet.classList.add("cards-slider__timeline", "touchable");
6125
+ bullet.setAttribute("data-index", String(i));
6126
+ bullet.onclick = (e) => {
6127
+ e.stopPropagation();
6128
+ e.preventDefault();
6129
+ const bullet = e.target;
6130
+ if (bullet != null) {
6131
+ const index = bullet.dataset.index;
6132
+ if (index != null) {
6133
+ this.showByIndex(parseInt(index));
6134
+ }
6066
6135
  }
6067
- }
6068
- };
6069
- const bulletFill = document.createElement("div");
6070
- bulletFill.classList.add("cards-slider__timeline-fill");
6071
- bullet.append(bulletFill);
6072
- bullets.appendChild(bullet);
6136
+ };
6137
+ const bulletFill = document.createElement("div");
6138
+ bulletFill.classList.add("cards-slider__timeline-fill");
6139
+ bullet.append(bulletFill);
6140
+ bullets.appendChild(bullet);
6141
+ }
6073
6142
  }
6074
6143
  // const onUpdateActiveIndex = (activeIndex: number) => {
6075
6144
  // if (activeIndex >= 0 && activeIndex < count) {
@@ -6097,42 +6166,25 @@ class Slider {
6097
6166
  }
6098
6167
  async showByIndex(newIndex) {
6099
6168
  const prevIndex = this.activeIndex;
6100
- if (this.isAnimating)
6169
+ if (this.isAnimating) {
6101
6170
  return prevIndex;
6102
- if (newIndex === prevIndex)
6103
- return prevIndex;
6104
- if (prevIndex !== -1) {
6105
- // skip for slider start
6106
- this.config.onSlideLeft(this.slides[prevIndex].element, prevIndex);
6107
- await this.config.onSlideStop();
6108
- }
6109
- const { index, loadingError } = await this.initAndRenderSlide(newIndex);
6110
- if (loadingError) {
6111
- // todo via updateTimeline ????
6112
- this.config.onSlideLoadingError(index, loadingError);
6113
6171
  }
6114
- if (!loadingError) {
6115
- this.onShowSlide(index);
6116
- }
6117
- await this.slideTo(index);
6118
- if (!loadingError) {
6119
- this.config.onSlideStart();
6172
+ if (this.isSlideStarting) {
6173
+ // block click while slide loading and starting (prevent several timeline starting)
6174
+ return prevIndex;
6120
6175
  }
6121
- return newIndex;
6122
- }
6123
- async showNextSlide() {
6124
- const prevIndex = this.activeIndex;
6125
- if (this.isAnimating)
6176
+ if (newIndex === prevIndex) {
6126
6177
  return prevIndex;
6127
- const newIndex = prevIndex + 1;
6178
+ }
6128
6179
  if (newIndex < 0 || newIndex >= this.slides.length) {
6129
- return null;
6180
+ return prevIndex;
6130
6181
  }
6131
6182
  if (prevIndex !== -1) {
6132
6183
  // skip for slider start
6133
6184
  this.config.onSlideLeft(this.slides[prevIndex].element, prevIndex);
6134
6185
  await this.config.onSlideStop();
6135
6186
  }
6187
+ this.isSlideStarting = true;
6136
6188
  const { index, loadingError } = await this.initAndRenderSlide(newIndex);
6137
6189
  if (loadingError) {
6138
6190
  // todo via updateTimeline ????
@@ -6141,12 +6193,16 @@ class Slider {
6141
6193
  if (!loadingError) {
6142
6194
  this.onShowSlide(index);
6143
6195
  }
6144
- await this.slideTo(index);
6196
+ this.config.onSlideChangeActiveIndex(await this.slideTo(index));
6145
6197
  if (!loadingError) {
6146
- this.config.onSlideStart();
6198
+ await this.config.onSlideStart();
6147
6199
  }
6200
+ this.isSlideStarting = false;
6148
6201
  return newIndex;
6149
6202
  }
6203
+ async showNextSlide() {
6204
+ return this.showByIndex(this.activeIndex + 1);
6205
+ }
6150
6206
  async initAndRenderSlide(index) {
6151
6207
  this.config.onBeforeLoadSlide(index);
6152
6208
  let showSlidePromise;
@@ -6194,11 +6250,13 @@ class Slider {
6194
6250
  return k * index * cardWidth;
6195
6251
  }
6196
6252
  async slideTo(index, speed = 300) {
6197
- if (index < 0 || index > this.slides.length - 1 || this.isAnimating)
6198
- return;
6253
+ if (index < 0 || index > this.slides.length - 1 || this.isAnimating) {
6254
+ return this.activeIndex;
6255
+ }
6199
6256
  const cardOffset = this.getSlideOffset(index);
6200
6257
  await this.translateTo(cardOffset, speed);
6201
6258
  this.activeIndex = index;
6259
+ return this.activeIndex;
6202
6260
  }
6203
6261
  setTranslate(value) {
6204
6262
  this.sliderTrack?.style.setProperty("transform", `translateX(${value}px)`);
@@ -6237,7 +6295,7 @@ class Slider {
6237
6295
  this.config.root.removeChild(this.slideWrapperElement);
6238
6296
  }
6239
6297
  }
6240
- updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError) {
6298
+ async updateTimeline(slideIndex, action, currentTime, duration, showLoader, showError) {
6241
6299
  switch (action) {
6242
6300
  case "before_start" /* TIMELINE_ACTION.BEFORE_START */: {
6243
6301
  // switch timeline to active slide and wait for start (wait VOD loading)
@@ -6311,6 +6369,7 @@ class Slider {
6311
6369
  getLayoutDirection: GetLayoutDirection;
6312
6370
 
6313
6371
  onSlideTimerEnd: OnSlideTimerEnd;
6372
+ onSlideChangeActiveIndex: OnSlideChangeActiveIndex;
6314
6373
  onSlideStart: OnSlideStart;
6315
6374
  onSlideStop: OnSlideStop;
6316
6375
  onSlideDataWaiting: OnSlideDataWaiting;
@@ -6699,6 +6758,7 @@ class CardApi {
6699
6758
  onSlideLeft,
6700
6759
  getLayoutDirection: () => this.layoutDirection,
6701
6760
  onSlideTimerEnd: () => this.activeSlide.slideTimerEnd(),
6761
+ onSlideChangeActiveIndex: index => (this.activeSlide = this.slides[index].slide),
6702
6762
  onSlideStart: () => {
6703
6763
  // return Promise.resolve({currentTime: 0});
6704
6764
  this.cardLoadingStateController.onSetLoadEndState();
@@ -6797,11 +6857,7 @@ class CardApi {
6797
6857
  else if (index < 0) {
6798
6858
  index = this.slides.length - 1;
6799
6859
  }
6800
- this.slider.showByIndex(index).then(index => {
6801
- if (currentIndex === index)
6802
- return;
6803
- this.activeSlide = this.slides[index].slide;
6804
- });
6860
+ this.slider.showByIndex(index);
6805
6861
  }
6806
6862
  return result;
6807
6863
  }
@@ -18953,6 +19009,852 @@ class SwipeGestureDetector {
18953
19009
  static get [Symbol.for("___CTOR_ARGS___")]() { return [`HTMLElement`, `boolean`, `(e: MouseEvent | TouchEvent) => boolean`, `(e: MouseEvent | TouchEvent) => void`, `(e: MouseEvent | TouchEvent) => void`, `(e: MouseEvent | TouchEvent, gesture: SwipeGesture) => void`]; }
18954
19010
  }
18955
19011
 
19012
+ class RenderableComponent {
19013
+ _root = null;
19014
+ props;
19015
+ constructor(props) {
19016
+ this.props = (props ?? {});
19017
+ }
19018
+ render() {
19019
+ const newRoot = this.renderTemplate();
19020
+ const style = this.styleContent();
19021
+ if (style) {
19022
+ newRoot?.prepend(this.createStyleElement(style));
19023
+ }
19024
+ if (this._root) {
19025
+ this._root.replaceWith(newRoot ?? "");
19026
+ }
19027
+ this._root = newRoot;
19028
+ return this._root;
19029
+ }
19030
+ updateProps(props) {
19031
+ this.props = { ...this.props, ...props };
19032
+ this.render();
19033
+ }
19034
+ destroy() {
19035
+ this._root?.remove();
19036
+ this._root = null;
19037
+ }
19038
+ createStyleElement(css) {
19039
+ const styleEl = document.createElement("style");
19040
+ styleEl.textContent = css;
19041
+ return styleEl;
19042
+ }
19043
+ styleContent() {
19044
+ return "";
19045
+ }
19046
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`Props`]; }
19047
+ }
19048
+
19049
+ function h(tag, ...rest) {
19050
+ let props = {};
19051
+ let children = [];
19052
+ if (rest[0] && typeof rest[0] === "object" && !(rest[0] instanceof Node)) {
19053
+ props = rest.shift();
19054
+ }
19055
+ children = rest.flat();
19056
+ const el = document.createElement(tag);
19057
+ for (const [key, value] of Object.entries(props)) {
19058
+ if (value == null)
19059
+ continue;
19060
+ if (key === "class" || key === "className") {
19061
+ el.className = value;
19062
+ }
19063
+ else if (key === "textContent") {
19064
+ el.textContent = value;
19065
+ }
19066
+ else if (key === "html" || key === "innerHTML") {
19067
+ el.innerHTML = value;
19068
+ }
19069
+ else if (key === "ref" && typeof value === "function") {
19070
+ value(el);
19071
+ }
19072
+ else if (key.startsWith("data-")) {
19073
+ el.setAttribute(key, value);
19074
+ }
19075
+ else if (key.startsWith("on") && typeof value === "function") {
19076
+ const event = key.slice(2).toLowerCase();
19077
+ el.addEventListener(event, value);
19078
+ }
19079
+ else if (key in el) {
19080
+ // @ts-ignore — any valid DOM props
19081
+ el[key] = value;
19082
+ }
19083
+ else {
19084
+ el.setAttribute(key, value);
19085
+ }
19086
+ }
19087
+ for (const child of children) {
19088
+ if (child == null)
19089
+ continue;
19090
+ if (typeof child === "string") {
19091
+ el.appendChild(document.createTextNode(child));
19092
+ }
19093
+ else {
19094
+ el.appendChild(child);
19095
+ }
19096
+ }
19097
+ return el;
19098
+ }
19099
+
19100
+ class BottomSheet extends RenderableComponent {
19101
+ isClosing = false;
19102
+ _isOpened = false;
19103
+ constructor(props) {
19104
+ super(props ?? {});
19105
+ }
19106
+ get isOpened() {
19107
+ return this._isOpened;
19108
+ }
19109
+ renderTemplate() {
19110
+ // TODO: calculate border-radius;
19111
+ const borderRadius = 30;
19112
+ return h("div", { class: `ias-bottom-sheet ${this._isOpened ? "ias-bottom-sheet--open" : ""}` }, h("div", {
19113
+ class: "ias-bottom-sheet__backdrop",
19114
+ onClick: this.handleBackdropClick,
19115
+ }), h("div", {
19116
+ class: "ias-bottom-sheet__container",
19117
+ style: this.props.minHeight != null ? `min-height: ${this.props.minHeight + borderRadius}px` : "",
19118
+ }, h("div", { class: "ias-bottom-sheet__content" },
19119
+ /* h("div", { class: "ias-bottom-sheet__header" }), */
19120
+ h("div", { class: "ias-bottom-sheet__body" }, ...(this.props?.children?.map(c => c.render()) ?? [])))));
19121
+ }
19122
+ setMinHeight(height) {
19123
+ if (!this._root)
19124
+ return;
19125
+ const container = this._root.querySelector(".ias-bottom-sheet__container");
19126
+ if (!container)
19127
+ return;
19128
+ container.style.minHeight = `${height}px`;
19129
+ }
19130
+ open() {
19131
+ if (this.isOpened)
19132
+ return;
19133
+ this._isOpened = true;
19134
+ requestAnimationFrame(() => {
19135
+ requestAnimationFrame(() => {
19136
+ this._root?.classList.add("ias-bottom-sheet--open");
19137
+ });
19138
+ });
19139
+ }
19140
+ async close() {
19141
+ if (!this._root || this.isClosing)
19142
+ return;
19143
+ const root = this._root;
19144
+ root.classList.remove("ias-bottom-sheet--open");
19145
+ this.isClosing = true;
19146
+ await new Promise(resolve => {
19147
+ root.addEventListener("transitionend", () => resolve(), { once: true });
19148
+ });
19149
+ this.isClosing = false;
19150
+ this.props?.onClose?.();
19151
+ }
19152
+ handleBackdropClick = (e) => {
19153
+ e.preventDefault();
19154
+ e.stopPropagation();
19155
+ this.close();
19156
+ };
19157
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`BottomSheetProps`]; }
19158
+ }
19159
+
19160
+ class ProductDetailsDescription extends RenderableComponent {
19161
+ constructor(props) {
19162
+ super(props);
19163
+ }
19164
+ renderTemplate() {
19165
+ const { name, description } = this.props;
19166
+ return h("div", { class: "ias-product-details__description" }, name ? h("h6", { class: "ias-product-details__name", textContent: name }) : null, description ? h("p", { class: "ias-product-details__text", textContent: description }) : null);
19167
+ }
19168
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductDetailsDescriptionProps`]; }
19169
+ }
19170
+
19171
+ class ProductDetailsGallery extends RenderableComponent {
19172
+ constructor(props) {
19173
+ super(props);
19174
+ }
19175
+ renderTemplate() {
19176
+ const { imageUrls = [] } = this.props;
19177
+ return h("div", { class: "ias-product-details__gallery" }, imageUrls.map(url => h("figure", { class: "ias-product-details__image" }, h("img", { src: url /* , loading: "lazy" */ }))));
19178
+ }
19179
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductDetailsGalleryProps`]; }
19180
+ }
19181
+
19182
+ const ADD_TO_CART_TIMEOUT = 60000;
19183
+
19184
+ class ProductDetailsPurchaseAction extends RenderableComponent {
19185
+ button;
19186
+ isLoading = false;
19187
+ constructor(props) {
19188
+ super(props);
19189
+ }
19190
+ renderTemplate() {
19191
+ this.button = h("button", {
19192
+ class: "ias-product-details__action-button",
19193
+ textContent: this.props.translations.addToCart,
19194
+ onClick: this.handleClickAddToCart,
19195
+ });
19196
+ return this.button;
19197
+ }
19198
+ handleClickAddToCart = async () => {
19199
+ if (this.isLoading)
19200
+ return;
19201
+ try {
19202
+ this.isLoading = true;
19203
+ this.showLoader();
19204
+ await this.addToCart();
19205
+ }
19206
+ catch (error) {
19207
+ this.handleAddToCartError(error);
19208
+ }
19209
+ finally {
19210
+ this.hideLoader();
19211
+ this.isLoading = false;
19212
+ }
19213
+ };
19214
+ showLoader() {
19215
+ this.button.classList.add("ias-product-details__action-button--loading");
19216
+ }
19217
+ hideLoader() {
19218
+ this.button.classList.remove("ias-product-details__action-button--loading");
19219
+ }
19220
+ handleAddToCartError(error) {
19221
+ this.props.onAddToCartError({ error });
19222
+ }
19223
+ async addToCart() {
19224
+ await Promise.race([this.props.onAddToCart(), new Promise((resolve, reject) => setTimeout(reject, ADD_TO_CART_TIMEOUT))]);
19225
+ }
19226
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductDetailsPurchaseActionProps`]; }
19227
+ }
19228
+
19229
+ class ProductDetailsPurchasePrice extends RenderableComponent {
19230
+ constructor(props) {
19231
+ super(props);
19232
+ }
19233
+ renderTemplate() {
19234
+ const { price, oldPrice, currency } = this.props.product;
19235
+ const rootEl = h("div", { class: "ias-product-details__price" });
19236
+ if (price != null && currency) {
19237
+ const currentPriceEl = h("div", {
19238
+ class: "ias-product-details__current-price",
19239
+ html: formatter.asCurrency(price, currency),
19240
+ });
19241
+ rootEl.appendChild(currentPriceEl);
19242
+ }
19243
+ if (oldPrice != null && currency) {
19244
+ const oldPriceEl = h("div", {
19245
+ class: "ias-product-details__old-price",
19246
+ html: formatter.asCurrency(oldPrice, currency),
19247
+ });
19248
+ rootEl.appendChild(oldPriceEl);
19249
+ }
19250
+ return rootEl;
19251
+ }
19252
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductDetailsPurchasePriceProps`]; }
19253
+ }
19254
+
19255
+ class ProductDetailsCounter extends RenderableComponent {
19256
+ _value = 1;
19257
+ countElement = null;
19258
+ get value() {
19259
+ return this._value;
19260
+ }
19261
+ updateProps() {
19262
+ this._value = 1;
19263
+ this.render();
19264
+ }
19265
+ renderTemplate() {
19266
+ return h("div", { class: "ias-product-details__counter" }, h("button", {
19267
+ class: "ias-product-details__counter-button",
19268
+ textContent: "−",
19269
+ onClick: this.handleClickOnMinusButton,
19270
+ }), h("span", {
19271
+ class: "ias-product-details__counter-count",
19272
+ textContent: this._value.toString(),
19273
+ ref: (el) => (this.countElement = el),
19274
+ }), h("button", {
19275
+ class: "ias-product-details__counter-button",
19276
+ textContent: "+",
19277
+ onClick: this.handleClickOnPlusButton,
19278
+ }));
19279
+ }
19280
+ handleClickOnMinusButton = () => {
19281
+ this._value = Math.max(this._value - 1, 1);
19282
+ this.updateCountValue();
19283
+ };
19284
+ handleClickOnPlusButton = () => {
19285
+ this._value++;
19286
+ this.updateCountValue();
19287
+ };
19288
+ updateCountValue() {
19289
+ if (this.countElement) {
19290
+ this.countElement.textContent = this._value.toString();
19291
+ }
19292
+ }
19293
+ }
19294
+
19295
+ class ProductDetailsPurchase extends RenderableComponent {
19296
+ price;
19297
+ action;
19298
+ counter = new ProductDetailsCounter();
19299
+ constructor(props) {
19300
+ super(props);
19301
+ }
19302
+ renderTemplate() {
19303
+ this.price = new ProductDetailsPurchasePrice(this.props);
19304
+ this.action = new ProductDetailsPurchaseAction({
19305
+ onAddToCart: this.handleOnAddToCart,
19306
+ onAddToCartError: this.props.onAddToCartError,
19307
+ translations: this.props.translations,
19308
+ });
19309
+ return h("div", { class: "ias-product-details__purchase" }, h("div", { class: "ias-product-details__price-box" }, this.price.render(), this.counter.render()), this.action.render());
19310
+ }
19311
+ updateProps(props) {
19312
+ this.props = { ...this.props, ...props };
19313
+ this.price.updateProps(this.props);
19314
+ this.counter.updateProps();
19315
+ }
19316
+ handleOnAddToCart = async () => {
19317
+ return this.props.onAddToCart({
19318
+ quantity: this.counter.value,
19319
+ product: this.props.product,
19320
+ });
19321
+ };
19322
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductDetailsPurchaseProps`]; }
19323
+ }
19324
+
19325
+ class ProductOptionButton extends RenderableComponent {
19326
+ constructor(props) {
19327
+ super(props);
19328
+ }
19329
+ renderTemplate() {
19330
+ const params = {
19331
+ class: `ias-product-details__option-button ${this.props.selected ? "ias-product-details__option-button--active" : ""} ${this.props.disabled ? "ias-product-details__option-button--disabled" : ""}`,
19332
+ textContent: this.props.label,
19333
+ onclick: () => {
19334
+ this.props.onClick();
19335
+ },
19336
+ };
19337
+ return h("button", params);
19338
+ }
19339
+ setSelected(selected) {
19340
+ if (!this._root)
19341
+ return;
19342
+ if (selected)
19343
+ this._root.classList.add("ias-product-details__option-button--active");
19344
+ else
19345
+ this._root.classList.remove("ias-product-details__option-button--active");
19346
+ }
19347
+ setDisabled(disabled) {
19348
+ if (!this._root)
19349
+ return;
19350
+ if (disabled)
19351
+ this._root.classList.add("ias-product-details__option-button--disabled");
19352
+ else
19353
+ this._root.classList.remove("ias-product-details__option-button--disabled");
19354
+ }
19355
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductOptionButtonProps`]; }
19356
+ }
19357
+
19358
+ class ProductOptionsManager {
19359
+ optionNames;
19360
+ productVariants;
19361
+ subscribers = [];
19362
+ selectedOptions = {};
19363
+ allOptionsByName = {};
19364
+ availableOptionsByName = {};
19365
+ constructor(optionNames, productVariants) {
19366
+ this.optionNames = optionNames;
19367
+ this.productVariants = productVariants;
19368
+ this.allOptionsByName = this.extractAllOptions();
19369
+ }
19370
+ get selected() {
19371
+ return { ...this.selectedOptions };
19372
+ }
19373
+ get availableOptions() {
19374
+ return this.availableOptionsByName;
19375
+ }
19376
+ selectOption(option) {
19377
+ const compatibleVariants = this.findCompatibleVariants(option);
19378
+ this.ensureVariantsExist(option, compatibleVariants);
19379
+ const matchingVariant = this.findMatchingVariant(compatibleVariants);
19380
+ this.selectVariant(matchingVariant);
19381
+ }
19382
+ selectVariant(variant) {
19383
+ this.optionNames.forEach(name => {
19384
+ const currentSelected = this.selectedOptions[name];
19385
+ const newOption = variant.options[name];
19386
+ if (currentSelected && newOption && currentSelected.value === newOption.value) {
19387
+ this.selectedOptions[name] = currentSelected;
19388
+ }
19389
+ else {
19390
+ this.selectedOptions[name] = newOption;
19391
+ }
19392
+ });
19393
+ this.availableOptionsByName = this.extractAvailableOptions();
19394
+ this.notifySubscribers(variant);
19395
+ }
19396
+ onChange(callback) {
19397
+ this.subscribers.push(callback);
19398
+ }
19399
+ isOptionSelected(option) {
19400
+ return this.selected[option.name]?.value === option.value;
19401
+ }
19402
+ isOptionDisabled(option) {
19403
+ const options = this.availableOptions[option.name];
19404
+ return !options?.some(o => o.value === option.value);
19405
+ }
19406
+ getOptionsFor(name) {
19407
+ return this.allOptionsByName[name] ?? [];
19408
+ }
19409
+ findCompatibleVariants(option) {
19410
+ return this.productVariants.filter(variant => variant.options[option.name]?.value === option.value);
19411
+ }
19412
+ ensureVariantsExist(option, variants) {
19413
+ if (variants.length === 0) {
19414
+ throw new Error(`[IAS]: No compatible variant for option <${option.name}:${option.value}>`);
19415
+ }
19416
+ }
19417
+ findMatchingVariant(variants) {
19418
+ return (variants.find(variant => this.optionNames.every(name => !this.selectedOptions[name] || variant.options[name]?.value === this.selectedOptions[name]?.value)) ?? variants[0]);
19419
+ }
19420
+ extractAvailableOptions() {
19421
+ const result = {};
19422
+ for (const name of this.optionNames) {
19423
+ const filteredVariants = this.filterVariantsExcludingOption(name);
19424
+ const available = filteredVariants.filter(v => v.available && v.options[name]).map(v => v.options[name]);
19425
+ result[name] = this.getUniqueByKey(available, o => o.value);
19426
+ }
19427
+ return result;
19428
+ }
19429
+ filterVariantsExcludingOption(excludedName) {
19430
+ const filters = { ...this.selectedOptions };
19431
+ delete filters[excludedName];
19432
+ return this.productVariants.filter(variant => Object.entries(filters).every(([name, selected]) => variant.options[name]?.value === selected?.value));
19433
+ }
19434
+ extractAllOptions() {
19435
+ const all = {};
19436
+ for (const name of this.optionNames) {
19437
+ const options = this.productVariants.map(v => v.options[name]).filter(Boolean);
19438
+ all[name] = this.getUniqueByKey(options, o => o.value);
19439
+ }
19440
+ return all;
19441
+ }
19442
+ getUniqueByKey(items, getKey) {
19443
+ return items.filter((item, index, self) => index === self.findIndex(i => getKey(i) === getKey(item)));
19444
+ }
19445
+ notifySubscribers(variant) {
19446
+ this.subscribers.forEach(cb => cb(variant));
19447
+ }
19448
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductOptionName[]`, `ProductVariant[]`]; }
19449
+ }
19450
+
19451
+ class ProductOptions extends RenderableComponent {
19452
+ optionsManager;
19453
+ optionsContainer;
19454
+ optionButtonsMap = new Map();
19455
+ constructor(props) {
19456
+ super(props);
19457
+ this.optionsManager = new ProductOptionsManager(props.optionNames, props.products);
19458
+ this.init(props.products);
19459
+ }
19460
+ renderTemplate() {
19461
+ this.optionsContainer = h("div", { class: "ias-product-details__options" }, this.renderOptionSelectors());
19462
+ return this.optionsContainer;
19463
+ }
19464
+ init(products) {
19465
+ this.optionsManager.selectVariant(products[0]);
19466
+ this.optionsManager.onChange(variant => {
19467
+ this.updateButtonsState();
19468
+ this.props.onChange(variant);
19469
+ });
19470
+ }
19471
+ renderOptionSelectors() {
19472
+ return this.props.optionNames.map(optionName => this.optionsManager.getOptionsFor(optionName).length > 0
19473
+ ? h("div", { class: "ias-product-details__selector" }, h("div", { class: "ias-product-details__selector-name", textContent: this.getOptionLabelByName(optionName) }), h("div", { class: "ias-product-details__option-selector" }, this.renderOptionsBySelector(optionName)))
19474
+ : null);
19475
+ }
19476
+ getOptionLabelByName(optionName) {
19477
+ switch (optionName) {
19478
+ case "color":
19479
+ return this.props.translations.color;
19480
+ case "size":
19481
+ return this.props.translations.size;
19482
+ }
19483
+ }
19484
+ renderOptionsBySelector(name) {
19485
+ const options = this.optionsManager.getOptionsFor(name);
19486
+ const buttons = [];
19487
+ const buttonElements = options.map(option => {
19488
+ const disabled = this.optionsManager.isOptionDisabled(option);
19489
+ const selected = this.optionsManager.isOptionSelected(option);
19490
+ const productOptionButton = new ProductOptionButton({
19491
+ selected,
19492
+ label: option.value,
19493
+ disabled,
19494
+ onClick: () => {
19495
+ this.optionsManager.selectOption(option);
19496
+ },
19497
+ });
19498
+ buttons.push(productOptionButton);
19499
+ return productOptionButton.render();
19500
+ });
19501
+ this.optionButtonsMap.set(name, buttons);
19502
+ return buttonElements;
19503
+ }
19504
+ updateButtonsState() {
19505
+ for (const [optionName, buttons] of this.optionButtonsMap.entries()) {
19506
+ const options = this.optionsManager.getOptionsFor(optionName);
19507
+ buttons.forEach((button, index) => {
19508
+ const option = options[index];
19509
+ const isSelected = this.optionsManager.isOptionSelected(option);
19510
+ const isDisabled = this.optionsManager.isOptionDisabled(option);
19511
+ button.setSelected(isSelected);
19512
+ button.setDisabled(isDisabled);
19513
+ });
19514
+ }
19515
+ }
19516
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductOptionsProps`]; }
19517
+ }
19518
+
19519
+ class ProductDetailsCover extends RenderableComponent {
19520
+ renderTemplate() {
19521
+ return h(`div`, { class: "ias-product-details__cover" }, h("img", { src: this.props.src }));
19522
+ }
19523
+ styleContent() {
19524
+ return `
19525
+ .ias-product-details__cover {
19526
+ position: absolute;
19527
+ z-index: -1;
19528
+ inset: 0;
19529
+ }
19530
+
19531
+ .ias-product-details__cover img {
19532
+ width: 100%;
19533
+ height: 100%;
19534
+ object-fit: cover;
19535
+ }
19536
+ `;
19537
+ }
19538
+ }
19539
+
19540
+ class ProductDetails extends RenderableComponent {
19541
+ constructor(props) {
19542
+ super(props);
19543
+ }
19544
+ renderTemplate() {
19545
+ const { props } = this;
19546
+ const purchase = new ProductDetailsPurchase(props);
19547
+ const cover = new ProductDetailsCover({
19548
+ src: this.props.product.coverUrl ?? "",
19549
+ });
19550
+ const gallery = new ProductDetailsGallery({
19551
+ imageUrls: this.props.product.imageUrls ?? [],
19552
+ });
19553
+ const description = new ProductDetailsDescription(props.product);
19554
+ const productOptions = this.createProductOptions({ purchase, gallery, description, cover });
19555
+ const relatedProductsLength = this.props.relatedProducts.length ?? 0;
19556
+ return h("div", { class: "ias-product-details" },
19557
+ /* this.props.product.coverUrl ? cover.render() : null, */
19558
+ h("div", { class: "ias-product-details__scrollable" }, gallery.render(), h("div", { class: "ias-product-details__content" }, description.render(), relatedProductsLength > 0 ? productOptions.render() : null)), this.createFooter(purchase));
19559
+ }
19560
+ createProductOptions({ purchase, gallery, description, cover, }) {
19561
+ return new ProductOptions({
19562
+ optionNames: ["color", "size"],
19563
+ products: [this.props.product, ...this.props.relatedProducts],
19564
+ onChange: (product) => {
19565
+ if (this.props.isCartSupported)
19566
+ purchase.updateProps({ product: product });
19567
+ gallery.updateProps(product);
19568
+ description.updateProps(product);
19569
+ cover.updateProps({
19570
+ src: product.coverUrl,
19571
+ });
19572
+ },
19573
+ translations: this.props.translations,
19574
+ });
19575
+ }
19576
+ createFooter(purchase) {
19577
+ if (this.props.isCartSupported) {
19578
+ return h("div", { class: "ias-product-details__footer" }, purchase.render());
19579
+ }
19580
+ else if (this.props.product.url) {
19581
+ return h("div", { class: "ias-product-details__footer" }, this.createOpenUrlButton());
19582
+ }
19583
+ return null;
19584
+ }
19585
+ createOpenUrlButton() {
19586
+ return h("button", {
19587
+ class: "ias-product-details__action-button ias-product-details__open-url-button",
19588
+ textContent: this.props.translations.openUrl,
19589
+ onclick: () => {
19590
+ this.props.onOpenUrl();
19591
+ },
19592
+ });
19593
+ }
19594
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductDetailsProps`]; }
19595
+ }
19596
+
19597
+ const successSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="none"><path stroke="#212121" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M40 14 18 36 8 26"></path></svg>`;
19598
+ class ProductCheckout extends RenderableComponent {
19599
+ constructor(props) {
19600
+ super(props);
19601
+ }
19602
+ renderTemplate() {
19603
+ return h("div", { class: "ias-product-checkout" }, h("div", { class: "ias-product-checkout__box" }, h("div", {
19604
+ class: "ias-product-checkout__icon",
19605
+ html: successSvg,
19606
+ }), h("span", {
19607
+ class: "ias-product-checkout__text",
19608
+ textContent: this.props.translations.successAddToCart,
19609
+ }), h("span", {
19610
+ class: "ias-product-checkout__sub-text",
19611
+ textContent: this.props.translations.successSubAddToCart,
19612
+ })), h("div", {
19613
+ class: "ias-product-checkout__actions",
19614
+ }, h("button", {
19615
+ class: "ias-product-checkout__button ias-product-checkout__button--close",
19616
+ textContent: this.props.translations.continueBtn,
19617
+ onclick: () => {
19618
+ this.props.onClose();
19619
+ },
19620
+ }), h("button", {
19621
+ class: "ias-product-checkout__button ias-product-checkout__button--checkout",
19622
+ textContent: this.props.translations.goToCartBtn,
19623
+ onclick: () => {
19624
+ this.props.onCheckout();
19625
+ },
19626
+ })));
19627
+ }
19628
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductCheckoutProps`]; }
19629
+ }
19630
+
19631
+ const cartSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 20 20" fill="none">
19632
+ <path stroke="rgba(255, 255, 255, 1)" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.333" d="M4 1.333 2 4v9.333a1.333 1.333 0 0 0 1.333 1.334h9.334A1.334 1.334 0 0 0 14 13.333V4l-2-2.667H4ZM2 4h12"></path>
19633
+ <path stroke="rgba(255, 255, 255, 1)" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.333" d="M10.667 6.667a2.667 2.667 0 1 1-5.333 0"></path>
19634
+ </svg>`;
19635
+ class ProductCartButton extends RenderableComponent {
19636
+ constructor(props) {
19637
+ super(props);
19638
+ }
19639
+ renderTemplate() {
19640
+ return h("div", { class: "ias-product-cart-button", onclick: () => this.props.onClick(), innerHTML: cartSvg });
19641
+ }
19642
+ styleContent() {
19643
+ return `
19644
+ .ias-product-cart-button {
19645
+ position: absolute;
19646
+ left: 0;
19647
+ top: 20%;
19648
+ padding: .25em;
19649
+ padding-left: .5em;
19650
+ background: black;
19651
+ border-top-right-radius: 0.69em;
19652
+ border-bottom-right-radius: 0.69em;
19653
+ display: flex;
19654
+ justify-content: center;
19655
+ align-items: center;
19656
+ cursor: pointer;
19657
+ transition: all 200ms ease-in;
19658
+ transform: translate3d(-.25em, 0, 0);
19659
+ }
19660
+
19661
+ .ias-product-cart-button:hover {
19662
+ transform: translate3d(0, 0, 0);
19663
+ }
19664
+ `;
19665
+ }
19666
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductCartButtonProps`]; }
19667
+ }
19668
+
19669
+ class ProductComponentsFactory {
19670
+ widgetDeps;
19671
+ props;
19672
+ constructor(widgetDeps, props) {
19673
+ this.widgetDeps = widgetDeps;
19674
+ this.props = props;
19675
+ }
19676
+ createBottomSheet(props) {
19677
+ const bs = new BottomSheet({
19678
+ onClose: () => {
19679
+ bs.destroy();
19680
+ },
19681
+ children: [],
19682
+ minHeight: props.minHeight,
19683
+ });
19684
+ return bs;
19685
+ }
19686
+ createProductDetails(params) {
19687
+ return new ProductDetails({
19688
+ product: params.product,
19689
+ relatedProducts: params.relatedProducts,
19690
+ onAddToCart: params.onAddToCart,
19691
+ onAddToCartError: ({ error }) => {
19692
+ this.widgetDeps.slideApiDeps.showToast(error.message);
19693
+ },
19694
+ translations: this.props.translations,
19695
+ isCartSupported: params.isCartSupported,
19696
+ onOpenUrl: () => {
19697
+ if (params.product.url)
19698
+ this.widgetDeps.slideApiDeps.openUrl({ type: "link", link: { type: "url", target: params.product.url } });
19699
+ },
19700
+ });
19701
+ }
19702
+ createProductCheckout(bottomSheet, offers) {
19703
+ const offersByIds = {};
19704
+ for (const offer of offers)
19705
+ offersByIds[offer.offerId] = offer;
19706
+ return new ProductCheckout({
19707
+ onClose: () => bottomSheet.close(),
19708
+ onCheckout: () => {
19709
+ this.widgetDeps.slideApiDeps.productCartClicked();
19710
+ },
19711
+ translations: this.props.translations,
19712
+ });
19713
+ }
19714
+ createProductCartButton() {
19715
+ return new ProductCartButton({
19716
+ onClick: async () => {
19717
+ try {
19718
+ const state = await this.widgetDeps.slideApiDeps.productCartGetState();
19719
+ console.log({ cart: state });
19720
+ this.widgetDeps.slideApiDeps.showToast(JSON.stringify(state.offers.map(({ offerId, name, quantity }) => ({
19721
+ offerId,
19722
+ name,
19723
+ quantity,
19724
+ }))));
19725
+ }
19726
+ catch (error) {
19727
+ this.widgetDeps.slideApiDeps.showToast(error.message);
19728
+ }
19729
+ },
19730
+ });
19731
+ }
19732
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`WidgetDeps`, `{
19733
+ translations: TranslationsMap;
19734
+ }`]; }
19735
+ }
19736
+
19737
+ class ProductOfferMapper {
19738
+ static fromOfferDtoToProductOffer(offerDto) {
19739
+ return {
19740
+ id: offerDto.id.toString(),
19741
+ offerId: offerDto.offerId,
19742
+ available: offerDto.availability !== 0,
19743
+ availability: offerDto.availability,
19744
+ options: {
19745
+ color: offerDto.color ? { name: "color", value: offerDto.color } : { name: "color", value: "Undefined" },
19746
+ size: offerDto.size ? { name: "size", value: offerDto.size } : { name: "size", value: "Undefined" },
19747
+ },
19748
+ name: offerDto.name,
19749
+ price: offerDto.price,
19750
+ oldPrice: offerDto.oldPrice,
19751
+ currency: offerDto.currency,
19752
+ description: offerDto.description,
19753
+ imageUrls: offerDto.images?.map(image => image.url) ?? [],
19754
+ adult: offerDto.adult,
19755
+ coverUrl: offerDto.coverUrl,
19756
+ groupId: offerDto.groupId,
19757
+ url: offerDto.url,
19758
+ };
19759
+ }
19760
+ static fromOfferDtoToProductCartOffer(offerDto, quantity) {
19761
+ return {
19762
+ offerId: offerDto.offerId,
19763
+ imageUrls: offerDto.images?.map(image => image.url) ?? [],
19764
+ availability: offerDto.availability,
19765
+ quantity,
19766
+ groupId: offerDto.groupId,
19767
+ name: offerDto.name,
19768
+ description: offerDto.description,
19769
+ url: offerDto.url,
19770
+ coverUrl: offerDto.coverUrl,
19771
+ currency: offerDto.currency,
19772
+ price: offerDto.price,
19773
+ oldPrice: offerDto.oldPrice,
19774
+ adult: offerDto.adult,
19775
+ size: offerDto.size,
19776
+ color: offerDto.color,
19777
+ };
19778
+ }
19779
+ }
19780
+
19781
+ class ProductDetailsBottomSheet extends RenderableComponent {
19782
+ widgetDeps;
19783
+ factory;
19784
+ bottomSheet;
19785
+ constructor(widgetDeps, props) {
19786
+ super(props);
19787
+ this.widgetDeps = widgetDeps;
19788
+ this.factory = new ProductComponentsFactory(this.widgetDeps, this.props);
19789
+ this.bottomSheet = this.factory.createBottomSheet({ minHeight: props.height });
19790
+ }
19791
+ renderTemplate() {
19792
+ return this.bottomSheet.render();
19793
+ }
19794
+ open(params) {
19795
+ this.showProductDetails(params);
19796
+ this.bottomSheet.open();
19797
+ }
19798
+ showProductDetails = ({ offer, isCartSupported }) => {
19799
+ const factory = new ProductComponentsFactory(this.widgetDeps, this.props);
19800
+ const offerDtos = [offer, ...(offer.subOffersApi ?? [])];
19801
+ this.renderProductDetails({ factory, offer, offerDtos, isCartSupported });
19802
+ };
19803
+ renderProductDetails({ factory, offer, offerDtos, isCartSupported, }) {
19804
+ const { product, relatedProducts } = this.getProductAndRelatedProducts(offer);
19805
+ const productDetails = factory.createProductDetails({
19806
+ product,
19807
+ relatedProducts,
19808
+ onAddToCart: async (payload) => {
19809
+ await this.productCartUpdate({
19810
+ offerDtos,
19811
+ product: payload.product,
19812
+ quantity: payload.quantity,
19813
+ });
19814
+ this.showProductCheckout({ offerDtos });
19815
+ },
19816
+ isCartSupported,
19817
+ });
19818
+ this.updateBottomSheetContent(productDetails);
19819
+ }
19820
+ showProductCheckout({ offerDtos }) {
19821
+ const productCheckout = this.factory.createProductCheckout(this.bottomSheet, offerDtos);
19822
+ this.updateBottomSheetContent(productCheckout);
19823
+ }
19824
+ updateBottomSheetContent(content) {
19825
+ const cartButton = this.factory.createProductCartButton();
19826
+ const children = process.env.NODE_ENV === "staging" ? [cartButton, content] : [content];
19827
+ this.bottomSheet.updateProps({
19828
+ children,
19829
+ });
19830
+ }
19831
+ async productCartUpdate({ offerDtos, product, quantity }) {
19832
+ const offerDto = offerDtos.find(offerDto => product.offerId === offerDto.offerId);
19833
+ if (!offerDto)
19834
+ throw new Error(`[IAS]: Not found offer for ID ${product.offerId}`);
19835
+ const cartOffer = ProductOfferMapper.fromOfferDtoToProductCartOffer(offerDto, quantity);
19836
+ const delay = async () => new Promise(resolve => setTimeout(resolve, 300));
19837
+ const [productCart] = await Promise.all([
19838
+ await await this.widgetDeps.slideApiDeps.productCartUpdate({
19839
+ offer: cartOffer,
19840
+ }),
19841
+ delay(),
19842
+ ]);
19843
+ return productCart.offers;
19844
+ }
19845
+ getProductAndRelatedProducts(offerDto) {
19846
+ return {
19847
+ product: ProductOfferMapper.fromOfferDtoToProductOffer(offerDto),
19848
+ relatedProducts: offerDto.subOffersApi
19849
+ ? offerDto.subOffersApi
19850
+ .filter(subOffer => subOffer.size != null && subOffer.color != null)
19851
+ .map(subOffer => ProductOfferMapper.fromOfferDtoToProductOffer(subOffer))
19852
+ : [],
19853
+ };
19854
+ }
19855
+ static get [Symbol.for("___CTOR_ARGS___")]() { return [`WidgetDeps`, `ProductDetailsBottomSheetProps`]; }
19856
+ }
19857
+
18956
19858
  /**
18957
19859
  * adult: null
18958
19860
  * availability: 1
@@ -19018,6 +19920,7 @@ class WidgetProducts extends WidgetBase {
19018
19920
  }
19019
19921
  onStart() {
19020
19922
  super.onStart();
19923
+ this.isLoading = false;
19021
19924
  // reinit for case when we returned to slide from other slide and onStop destroyed SwipeGestureDetector
19022
19925
  this.initSwipeGestureDetector();
19023
19926
  }
@@ -19102,12 +20005,22 @@ class WidgetProducts extends WidgetBase {
19102
20005
  console.error(error);
19103
20006
  }
19104
20007
  }
20008
+ async getOrFetchProducts() {
20009
+ if (this.currentModels.length > 0)
20010
+ return this.currentModels;
20011
+ const result = await this.fetchProducts();
20012
+ if (result.models.length > 0) {
20013
+ this.currentModels = result.models;
20014
+ return result.models;
20015
+ }
20016
+ throw new Error(result.message);
20017
+ }
19105
20018
  async fetchProducts() {
19106
20019
  const fetchAndCacheMedia = async () => {
19107
20020
  if (!this.linkTarget.length) {
19108
20021
  return { message: this.msgServiceError ?? "", models: [] };
19109
20022
  }
19110
- let qs = `id=${this.linkTarget.join(",")}`;
20023
+ let qs = `id=${this.linkTarget.join(",")}&expand=images,subOffersApi`;
19111
20024
  const sdkClientVariables = this.widgetDeps.getSdkClientVariables();
19112
20025
  if (sdkClientVariables != null && sdkClientVariables.pos != null) {
19113
20026
  qs += `&pos=${String(sdkClientVariables.pos)}`;
@@ -19216,12 +20129,13 @@ class WidgetProducts extends WidgetBase {
19216
20129
  }
19217
20130
  productsView = null;
19218
20131
  isOpen = false;
20132
+ isLoading = false;
19219
20133
  get isForcePaused() {
19220
20134
  return this.isOpen;
19221
20135
  }
19222
20136
  currentModels = [];
19223
20137
  async openProductsView() {
19224
- if (this.isOpen) {
20138
+ if (this.isOpen || this.isLoading) {
19225
20139
  return;
19226
20140
  }
19227
20141
  this._statEventWidgetClick();
@@ -19231,14 +20145,10 @@ class WidgetProducts extends WidgetBase {
19231
20145
  if (!this.isTransparentElement()) {
19232
20146
  this.element.classList.add("loader");
19233
20147
  }
19234
- // const start = window.performance.now();
19235
- const result = await this.fetchProducts();
19236
- // @ts-ignore
19237
- // _log(`fetched for ${window.performance.now() - start}ms`, true);
19238
- // console.log({result})
19239
- if (result.models.length > 0) {
19240
- this.currentModels = result.models;
19241
- this.productsView = this.createProductsView(this.currentModels, this.closeProductsView.bind(this));
20148
+ try {
20149
+ this.isLoading = true;
20150
+ const models = await this.getOrFetchProducts();
20151
+ this.productsView = this.createProductsView(models, this.closeProductsView.bind(this));
19242
20152
  this.productsView.classList.add("ias-products-container-view--visible");
19243
20153
  this.slide.appendChild(this.productsView);
19244
20154
  this.element.classList.add("hidden");
@@ -19248,15 +20158,16 @@ class WidgetProducts extends WidgetBase {
19248
20158
  this.widgetDeps.slideApiDeps.disableHorizontalSwipeGesture();
19249
20159
  this.widgetDeps.slideApiDeps.disableVerticalSwipeGesture();
19250
20160
  this.widgetDeps.slideApiDeps.disableBackpress();
19251
- this._statEventWidgetOpen(this.currentModels);
20161
+ this._statEventWidgetOpen(models);
19252
20162
  this.initSwipeGestureDetector();
19253
20163
  }
19254
- else {
19255
- if (result.message) {
19256
- this.widgetDeps.slideApiDeps.showToast(result.message);
19257
- }
20164
+ catch (error) {
20165
+ this.widgetDeps.slideApiDeps.showToast(error.message);
20166
+ }
20167
+ finally {
20168
+ this.isLoading = false;
20169
+ this.element.classList.remove("loader");
19258
20170
  }
19259
- this.element.classList.remove("loader");
19260
20171
  }
19261
20172
  closeProductsView() {
19262
20173
  if (!this.isOpen) {
@@ -19330,9 +20241,7 @@ class WidgetProducts extends WidgetBase {
19330
20241
  e.stopPropagation();
19331
20242
  e.preventDefault();
19332
20243
  this._statEventWidgetCardClick(offer);
19333
- if (offer.url) {
19334
- this.widgetDeps.slideApiDeps.openUrl({ type: "link", link: { type: "url", target: offer.url } });
19335
- }
20244
+ this.showProductDetails({ offer, card });
19336
20245
  };
19337
20246
  card.appendChild(figure);
19338
20247
  // card.appendChild(subTitle);
@@ -19340,6 +20249,33 @@ class WidgetProducts extends WidgetBase {
19340
20249
  card.appendChild(title);
19341
20250
  return card;
19342
20251
  }
20252
+ showProductDetails = ({ card, offer }) => {
20253
+ if (!this.productsView)
20254
+ return;
20255
+ const backgroundView = this.slide.querySelector(".ias-products-container-background-view");
20256
+ const scrollViewGroup = this.slide.querySelector(".ias-products-scroll-view-group");
20257
+ const backgroundViewHeight = backgroundView?.clientHeight ?? 0;
20258
+ let scrollViewGroupHeight = scrollViewGroup?.offsetTop ?? 0;
20259
+ scrollViewGroupHeight = scrollViewGroupHeight > 0 ? 0 : -scrollViewGroupHeight;
20260
+ const bs = new ProductDetailsBottomSheet(this.widgetDeps, {
20261
+ translations: {
20262
+ color: getTagData(this.element, "msgColor") ?? "",
20263
+ size: getTagData(this.element, "msgSize") ?? "",
20264
+ addToCart: getTagData(this.element, "msgAddToCart") ?? "",
20265
+ successAddToCart: getTagData(this.element, "msgSuccess") ?? "",
20266
+ successSubAddToCart: getTagData(this.element, "msgSuccessSub") ?? "",
20267
+ errorAddToCart: getTagData(this.element, "msgError") ?? "",
20268
+ goToCartBtn: getTagData(this.element, "msgGoToCart") ?? "",
20269
+ continueBtn: getTagData(this.element, "msgContinue") ?? "",
20270
+ openUrl: getTagData(this.element, "msgOpenUrl") ?? "",
20271
+ },
20272
+ height: backgroundViewHeight + scrollViewGroupHeight,
20273
+ });
20274
+ const isCartEnabled = getTagData(this.element, "isCartEnabled") === "true";
20275
+ const isCartSupported = this.widgetDeps.slideApiDeps.isSdkSupportProductCart && isCartEnabled;
20276
+ this.productsView.appendChild(bs.render());
20277
+ bs.open({ offer, isCartSupported });
20278
+ };
19343
20279
  createScrollView(offers) {
19344
20280
  const scrollViewGroup = document.createElement("div");
19345
20281
  scrollViewGroup.classList.add("ias-products-scroll-view-group");