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