@inappstory/slide-api 0.1.40 → 0.1.41
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 +932 -151
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +91 -31
- package/dist/index.d.ts +91 -31
- package/dist/index.js +932 -151
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1987,6 +1987,65 @@ class Products {
|
|
|
1987
1987
|
static get [Symbol.for("___CTOR_ARGS___")]() { return [`HTMLElement`, `Layer`, `typeof WidgetProducts.api`, `WidgetCallbacks`, `WidgetDeps`]; }
|
|
1988
1988
|
}
|
|
1989
1989
|
|
|
1990
|
+
class ProductCarousel {
|
|
1991
|
+
_elementNodeRef;
|
|
1992
|
+
_layer;
|
|
1993
|
+
_widgetApi;
|
|
1994
|
+
_widgetCallbacks;
|
|
1995
|
+
_widgetDeps;
|
|
1996
|
+
static _className = "narrative-element-product-carousel";
|
|
1997
|
+
static className() {
|
|
1998
|
+
return ProductCarousel._className;
|
|
1999
|
+
}
|
|
2000
|
+
static isTypeOf(element) {
|
|
2001
|
+
return element instanceof ProductCarousel;
|
|
2002
|
+
}
|
|
2003
|
+
constructor(_elementNodeRef, _layer, _widgetApi, _widgetCallbacks, _widgetDeps) {
|
|
2004
|
+
this._elementNodeRef = _elementNodeRef;
|
|
2005
|
+
this._layer = _layer;
|
|
2006
|
+
this._widgetApi = _widgetApi;
|
|
2007
|
+
this._widgetCallbacks = _widgetCallbacks;
|
|
2008
|
+
this._widgetDeps = _widgetDeps;
|
|
2009
|
+
}
|
|
2010
|
+
mediaElementsLoadingPromises = [];
|
|
2011
|
+
get nodeRef() {
|
|
2012
|
+
return this._elementNodeRef;
|
|
2013
|
+
}
|
|
2014
|
+
init(localData) {
|
|
2015
|
+
try {
|
|
2016
|
+
this._widgetApi.init(this._elementNodeRef, localData, this._widgetCallbacks, this._widgetDeps);
|
|
2017
|
+
}
|
|
2018
|
+
catch (e) {
|
|
2019
|
+
console.error(e);
|
|
2020
|
+
}
|
|
2021
|
+
return Promise.resolve(true);
|
|
2022
|
+
}
|
|
2023
|
+
onPause() { }
|
|
2024
|
+
onResume() { }
|
|
2025
|
+
onStart() {
|
|
2026
|
+
this._widgetApi.onStart(this._elementNodeRef);
|
|
2027
|
+
}
|
|
2028
|
+
onStop() {
|
|
2029
|
+
this._widgetApi.onStop(this._elementNodeRef);
|
|
2030
|
+
}
|
|
2031
|
+
onBeforeUnmount() {
|
|
2032
|
+
return Promise.resolve();
|
|
2033
|
+
}
|
|
2034
|
+
handleClick() {
|
|
2035
|
+
return false;
|
|
2036
|
+
}
|
|
2037
|
+
get isClickCapturedByWidget() {
|
|
2038
|
+
return this._widgetApi.isClickCapturedByWidget(this._elementNodeRef);
|
|
2039
|
+
}
|
|
2040
|
+
get isLayerForcePaused() {
|
|
2041
|
+
return this._widgetApi.isForcePaused(this._elementNodeRef);
|
|
2042
|
+
}
|
|
2043
|
+
get elementNodeRef() {
|
|
2044
|
+
return this._elementNodeRef;
|
|
2045
|
+
}
|
|
2046
|
+
static get [Symbol.for("___CTOR_ARGS___")]() { return [`HTMLElement`, `Layer`, `typeof WidgetProductCarousel.api`, `WidgetCallbacks`, `WidgetDeps`]; }
|
|
2047
|
+
}
|
|
2048
|
+
|
|
1990
2049
|
class Quest {
|
|
1991
2050
|
_elementNodeRef;
|
|
1992
2051
|
_layer;
|
|
@@ -3466,6 +3525,10 @@ const tryCreateFromHtmlElement = (nodeRef, layer, widgetCallbacks, widgetDeps) =
|
|
|
3466
3525
|
return layoutApi.widgetVoteApi ? new Vote(nodeRef, layer, layoutApi.widgetVoteApi, widgetCallbacks, widgetDeps) : null;
|
|
3467
3526
|
case Products.className():
|
|
3468
3527
|
return layoutApi.widgetProductsApi ? new Products(nodeRef, layer, layoutApi.widgetProductsApi, widgetCallbacks, widgetDeps) : null;
|
|
3528
|
+
case ProductCarousel.className():
|
|
3529
|
+
return layoutApi.widgetProductCarouselApi
|
|
3530
|
+
? new ProductCarousel(nodeRef, layer, layoutApi.widgetProductCarouselApi, widgetCallbacks, widgetDeps)
|
|
3531
|
+
: null;
|
|
3469
3532
|
case Tooltip.className():
|
|
3470
3533
|
return layoutApi.widgetTooltipApi ? new Tooltip(nodeRef, layer, layoutApi.widgetTooltipApi, widgetCallbacks, widgetDeps) : null;
|
|
3471
3534
|
case Timer.className():
|
|
@@ -4019,6 +4082,14 @@ class Layer {
|
|
|
4019
4082
|
}
|
|
4020
4083
|
return null;
|
|
4021
4084
|
}
|
|
4085
|
+
get productCarouselElement() {
|
|
4086
|
+
for (const element of this._elements) {
|
|
4087
|
+
if (ProductCarousel.isTypeOf(element)) {
|
|
4088
|
+
return element;
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
return null;
|
|
4092
|
+
}
|
|
4022
4093
|
isClickCapturedBySlider() {
|
|
4023
4094
|
return (this.rangeSliderElement?.isClickCapturedBySlider ?? false) || (this.productsElement?.isClickCapturedByWidget ?? false);
|
|
4024
4095
|
}
|
|
@@ -5438,6 +5509,12 @@ let SlideApi$1 = class SlideApi {
|
|
|
5438
5509
|
return result; // disable all clicks if event captured by products widget (slider or swipe down)
|
|
5439
5510
|
}
|
|
5440
5511
|
}
|
|
5512
|
+
if (this.activeLayer.productCarouselElement) {
|
|
5513
|
+
if (this.activeLayer.productCarouselElement.isClickCapturedByWidget) {
|
|
5514
|
+
result.canClickNext = false;
|
|
5515
|
+
return result; // disable all clicks if event captured by product carousel widget
|
|
5516
|
+
}
|
|
5517
|
+
}
|
|
5441
5518
|
if (this.activeLayer.questElement) {
|
|
5442
5519
|
const slideIndex = this.slide.slideIndex;
|
|
5443
5520
|
const slideCount = this.slide.slideCount;
|
|
@@ -19949,6 +20026,10 @@ class BottomSheet extends RenderableComponent {
|
|
|
19949
20026
|
}), h("div", {
|
|
19950
20027
|
class: "ias-bottom-sheet__container",
|
|
19951
20028
|
style: this.props.minHeight != null ? `min-height: ${this.props.minHeight + borderRadius}px` : "",
|
|
20029
|
+
onclick: e => {
|
|
20030
|
+
e.preventDefault();
|
|
20031
|
+
e.stopPropagation();
|
|
20032
|
+
},
|
|
19952
20033
|
}, h("div", { class: "ias-bottom-sheet__content" },
|
|
19953
20034
|
/* h("div", { class: "ias-bottom-sheet__header" }), */
|
|
19954
20035
|
h("div", { class: "ias-bottom-sheet__body" }, ...(this.props?.children?.map(c => c.render()) ?? [])))));
|
|
@@ -20013,7 +20094,7 @@ class ProductDetailsGallery extends RenderableComponent {
|
|
|
20013
20094
|
static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductDetailsGalleryProps`]; }
|
|
20014
20095
|
}
|
|
20015
20096
|
|
|
20016
|
-
const ADD_TO_CART_TIMEOUT = 60000;
|
|
20097
|
+
const ADD_TO_CART_TIMEOUT$1 = 60000;
|
|
20017
20098
|
|
|
20018
20099
|
class ProductDetailsPurchaseAction extends RenderableComponent {
|
|
20019
20100
|
button;
|
|
@@ -20038,7 +20119,13 @@ class ProductDetailsPurchaseAction extends RenderableComponent {
|
|
|
20038
20119
|
await this.addToCart();
|
|
20039
20120
|
}
|
|
20040
20121
|
catch (error) {
|
|
20041
|
-
|
|
20122
|
+
if (error instanceof Error) {
|
|
20123
|
+
this.handleAddToCartError(error);
|
|
20124
|
+
}
|
|
20125
|
+
else if (typeof error === "string") {
|
|
20126
|
+
// Native SDKs return a string
|
|
20127
|
+
this.handleAddToCartError(new Error(error));
|
|
20128
|
+
}
|
|
20042
20129
|
}
|
|
20043
20130
|
finally {
|
|
20044
20131
|
this.hideLoader();
|
|
@@ -20055,7 +20142,7 @@ class ProductDetailsPurchaseAction extends RenderableComponent {
|
|
|
20055
20142
|
this.props.onAddToCartError({ error });
|
|
20056
20143
|
}
|
|
20057
20144
|
async addToCart() {
|
|
20058
|
-
await Promise.race([this.props.onAddToCart(), new Promise((resolve, reject) => setTimeout(reject, ADD_TO_CART_TIMEOUT))]);
|
|
20145
|
+
await Promise.race([this.props.onAddToCart(), new Promise((resolve, reject) => setTimeout(reject, ADD_TO_CART_TIMEOUT$1))]);
|
|
20059
20146
|
}
|
|
20060
20147
|
static get [Symbol.for("___CTOR_ARGS___")]() { return [`ProductDetailsPurchaseActionProps`]; }
|
|
20061
20148
|
}
|
|
@@ -20458,13 +20545,15 @@ class ProductCheckout extends RenderableComponent {
|
|
|
20458
20545
|
}, h("button", {
|
|
20459
20546
|
class: "ias-product-checkout__button ias-product-checkout__button--close",
|
|
20460
20547
|
textContent: this.props.translations.continueBtn,
|
|
20461
|
-
onclick:
|
|
20548
|
+
onclick: e => {
|
|
20549
|
+
e.stopPropagation();
|
|
20462
20550
|
this.props.onClose();
|
|
20463
20551
|
},
|
|
20464
20552
|
}), h("button", {
|
|
20465
20553
|
class: "ias-product-checkout__button ias-product-checkout__button--checkout",
|
|
20466
20554
|
textContent: this.props.translations.goToCartBtn,
|
|
20467
|
-
onclick:
|
|
20555
|
+
onclick: e => {
|
|
20556
|
+
e.stopPropagation();
|
|
20468
20557
|
this.props.onCheckout();
|
|
20469
20558
|
},
|
|
20470
20559
|
})));
|
|
@@ -20521,6 +20610,7 @@ class ProductComponentsFactory {
|
|
|
20521
20610
|
const bs = new BottomSheet({
|
|
20522
20611
|
onClose: () => {
|
|
20523
20612
|
bs.destroy();
|
|
20613
|
+
props.onClose?.();
|
|
20524
20614
|
},
|
|
20525
20615
|
children: [],
|
|
20526
20616
|
minHeight: props.minHeight,
|
|
@@ -20543,10 +20633,7 @@ class ProductComponentsFactory {
|
|
|
20543
20633
|
},
|
|
20544
20634
|
});
|
|
20545
20635
|
}
|
|
20546
|
-
createProductCheckout(bottomSheet
|
|
20547
|
-
const offersByIds = {};
|
|
20548
|
-
for (const offer of offers)
|
|
20549
|
-
offersByIds[offer.offerId] = offer;
|
|
20636
|
+
createProductCheckout(bottomSheet) {
|
|
20550
20637
|
return new ProductCheckout({
|
|
20551
20638
|
onClose: () => bottomSheet.close(),
|
|
20552
20639
|
onCheckout: () => {
|
|
@@ -20560,7 +20647,6 @@ class ProductComponentsFactory {
|
|
|
20560
20647
|
onClick: async () => {
|
|
20561
20648
|
try {
|
|
20562
20649
|
const state = await this.widgetDeps.slideApiDeps.productCartGetState();
|
|
20563
|
-
console.log({ cart: state });
|
|
20564
20650
|
this.widgetDeps.slideApiDeps.showToast(JSON.stringify(state.offers.map(({ offerId, name, quantity }) => ({
|
|
20565
20651
|
offerId,
|
|
20566
20652
|
name,
|
|
@@ -20630,10 +20716,16 @@ class ProductDetailsBottomSheet extends RenderableComponent {
|
|
|
20630
20716
|
super(props);
|
|
20631
20717
|
this.widgetDeps = widgetDeps;
|
|
20632
20718
|
this.factory = new ProductComponentsFactory(this.widgetDeps, this.props);
|
|
20633
|
-
this.bottomSheet = this.factory.createBottomSheet({
|
|
20719
|
+
this.bottomSheet = this.factory.createBottomSheet({
|
|
20720
|
+
minHeight: props.height,
|
|
20721
|
+
onClose: () => {
|
|
20722
|
+
this._root?.remove();
|
|
20723
|
+
this.props.onClose?.();
|
|
20724
|
+
},
|
|
20725
|
+
});
|
|
20634
20726
|
}
|
|
20635
20727
|
renderTemplate() {
|
|
20636
|
-
return this.bottomSheet.render();
|
|
20728
|
+
return h("div", { class: "ias-products-container-view" }, this.bottomSheet.render());
|
|
20637
20729
|
}
|
|
20638
20730
|
open(params) {
|
|
20639
20731
|
this.showProductDetails(params);
|
|
@@ -20652,17 +20744,17 @@ class ProductDetailsBottomSheet extends RenderableComponent {
|
|
|
20652
20744
|
onAddToCart: async (payload) => {
|
|
20653
20745
|
await this.productCartUpdate({
|
|
20654
20746
|
offerDtos,
|
|
20655
|
-
|
|
20747
|
+
offerId: payload.product.offerId,
|
|
20656
20748
|
quantity: payload.quantity,
|
|
20657
20749
|
});
|
|
20658
|
-
this.showProductCheckout(
|
|
20750
|
+
this.showProductCheckout();
|
|
20659
20751
|
},
|
|
20660
20752
|
isCartSupported,
|
|
20661
20753
|
});
|
|
20662
20754
|
this.updateBottomSheetContent(productDetails);
|
|
20663
20755
|
}
|
|
20664
|
-
showProductCheckout(
|
|
20665
|
-
const productCheckout = this.factory.createProductCheckout(this.bottomSheet
|
|
20756
|
+
showProductCheckout() {
|
|
20757
|
+
const productCheckout = this.factory.createProductCheckout(this.bottomSheet);
|
|
20666
20758
|
this.updateBottomSheetContent(productCheckout);
|
|
20667
20759
|
}
|
|
20668
20760
|
updateBottomSheetContent(content) {
|
|
@@ -20672,10 +20764,10 @@ class ProductDetailsBottomSheet extends RenderableComponent {
|
|
|
20672
20764
|
children,
|
|
20673
20765
|
});
|
|
20674
20766
|
}
|
|
20675
|
-
async productCartUpdate({ offerDtos,
|
|
20676
|
-
const offerDto = offerDtos.find(offerDto =>
|
|
20767
|
+
async productCartUpdate({ offerDtos, offerId, quantity }) {
|
|
20768
|
+
const offerDto = offerDtos.find(offerDto => offerId === offerDto.offerId);
|
|
20677
20769
|
if (!offerDto)
|
|
20678
|
-
throw new Error(`[IAS]: Not found offer for ID ${
|
|
20770
|
+
throw new Error(`[IAS]: Not found offer for ID ${offerId}`);
|
|
20679
20771
|
const cartOffer = ProductOfferMapper.fromOfferDtoToProductCartOffer(offerDto, quantity);
|
|
20680
20772
|
const delay = async () => new Promise(resolve => setTimeout(resolve, 300));
|
|
20681
20773
|
const [productCart] = await Promise.all([
|
|
@@ -20695,6 +20787,271 @@ class ProductDetailsBottomSheet extends RenderableComponent {
|
|
|
20695
20787
|
static get [Symbol.for("___CTOR_ARGS___")]() { return [`WidgetDeps`, `ProductDetailsBottomSheetProps`]; }
|
|
20696
20788
|
}
|
|
20697
20789
|
|
|
20790
|
+
class ProductCheckoutBottomSheet extends RenderableComponent {
|
|
20791
|
+
widgetDeps;
|
|
20792
|
+
factory;
|
|
20793
|
+
bottomSheet;
|
|
20794
|
+
constructor(widgetDeps, props) {
|
|
20795
|
+
super(props);
|
|
20796
|
+
this.widgetDeps = widgetDeps;
|
|
20797
|
+
this.factory = new ProductComponentsFactory(this.widgetDeps, this.props);
|
|
20798
|
+
this.bottomSheet = this.factory.createBottomSheet({
|
|
20799
|
+
onClose: () => {
|
|
20800
|
+
this._root?.remove();
|
|
20801
|
+
this.props.onClose?.();
|
|
20802
|
+
},
|
|
20803
|
+
});
|
|
20804
|
+
}
|
|
20805
|
+
renderTemplate() {
|
|
20806
|
+
return h("div", { class: "ias-products-container-view" }, this.bottomSheet.render());
|
|
20807
|
+
}
|
|
20808
|
+
open() {
|
|
20809
|
+
const productCheckout = this.factory.createProductCheckout(this.bottomSheet);
|
|
20810
|
+
this.updateBottomSheetContent(productCheckout);
|
|
20811
|
+
this.bottomSheet.open();
|
|
20812
|
+
}
|
|
20813
|
+
async productCartUpdate({ offerDtos, offerId, quantity }) {
|
|
20814
|
+
const offerDto = offerDtos.find(offerDto => offerId === offerDto.offerId);
|
|
20815
|
+
if (!offerDto)
|
|
20816
|
+
throw new Error(`[IAS]: Not found offer for ID ${offerId}`);
|
|
20817
|
+
const cartOffer = ProductOfferMapper.fromOfferDtoToProductCartOffer(offerDto, quantity);
|
|
20818
|
+
const delay = async () => new Promise(resolve => setTimeout(resolve, 300));
|
|
20819
|
+
const [productCart] = await Promise.all([
|
|
20820
|
+
this.widgetDeps.slideApiDeps.productCartUpdate({
|
|
20821
|
+
offer: cartOffer,
|
|
20822
|
+
}),
|
|
20823
|
+
delay(),
|
|
20824
|
+
]);
|
|
20825
|
+
return productCart.offers;
|
|
20826
|
+
}
|
|
20827
|
+
updateBottomSheetContent(content) {
|
|
20828
|
+
const cartButton = this.factory.createProductCartButton();
|
|
20829
|
+
const children = process.env.NODE_ENV === "staging" ? [cartButton, content] : [content];
|
|
20830
|
+
this.bottomSheet.updateProps({
|
|
20831
|
+
children,
|
|
20832
|
+
});
|
|
20833
|
+
}
|
|
20834
|
+
static get [Symbol.for("___CTOR_ARGS___")]() { return [`WidgetDeps`, `ProductCheckoutBottomSheetProps`]; }
|
|
20835
|
+
}
|
|
20836
|
+
|
|
20837
|
+
class ProductOffersRequestError extends Error {
|
|
20838
|
+
status;
|
|
20839
|
+
code;
|
|
20840
|
+
cause;
|
|
20841
|
+
constructor(message, params) {
|
|
20842
|
+
super(message);
|
|
20843
|
+
this.name = this.constructor.name;
|
|
20844
|
+
this.status = params?.status;
|
|
20845
|
+
this.code = params?.code;
|
|
20846
|
+
this.cause = params?.cause;
|
|
20847
|
+
}
|
|
20848
|
+
static get [Symbol.for("___CTOR_ARGS___")]() { return [`string`, `{ status?: number; code?: string; cause?: unknown }`]; }
|
|
20849
|
+
}
|
|
20850
|
+
class ProductOffersNetworkError extends ProductOffersRequestError {
|
|
20851
|
+
}
|
|
20852
|
+
class ProductOffersServiceError extends ProductOffersRequestError {
|
|
20853
|
+
}
|
|
20854
|
+
class ProductOfferRepository {
|
|
20855
|
+
widgetDeps;
|
|
20856
|
+
getLinkTarget;
|
|
20857
|
+
getMsgNetworkError;
|
|
20858
|
+
getMsgServiceError;
|
|
20859
|
+
static rawOffersCache = new Map();
|
|
20860
|
+
static pendingFetches = new Map();
|
|
20861
|
+
static RAW_CACHE_TTL_MS = 60_000;
|
|
20862
|
+
offers = [];
|
|
20863
|
+
constructor(widgetDeps, getLinkTarget, getMsgNetworkError, getMsgServiceError) {
|
|
20864
|
+
this.widgetDeps = widgetDeps;
|
|
20865
|
+
this.getLinkTarget = getLinkTarget;
|
|
20866
|
+
this.getMsgNetworkError = getMsgNetworkError;
|
|
20867
|
+
this.getMsgServiceError = getMsgServiceError;
|
|
20868
|
+
}
|
|
20869
|
+
getCurrentModels() {
|
|
20870
|
+
return this.offers;
|
|
20871
|
+
}
|
|
20872
|
+
clear() {
|
|
20873
|
+
this.offers = [];
|
|
20874
|
+
}
|
|
20875
|
+
async getOrFetchProducts() {
|
|
20876
|
+
if (this.offers.length > 0)
|
|
20877
|
+
return this.offers;
|
|
20878
|
+
const cacheKey = this.getCacheKey();
|
|
20879
|
+
const cached = ProductOfferRepository.getRawFromCache(cacheKey);
|
|
20880
|
+
if (cached != null && cached.length > 0) {
|
|
20881
|
+
const cloned = cached.slice();
|
|
20882
|
+
this.offers = cloned;
|
|
20883
|
+
return this.offers;
|
|
20884
|
+
}
|
|
20885
|
+
const result = await this.fetchProductsRaw(cacheKey);
|
|
20886
|
+
if (result.models.length > 0) {
|
|
20887
|
+
const offers = result.models.slice();
|
|
20888
|
+
const { objectUrls } = await ProductOfferRepository.hydrateOfferImageBlobs(offers, this.getMsgNetworkError());
|
|
20889
|
+
ProductOfferRepository.rawOffersCache.set(cacheKey, {
|
|
20890
|
+
cachedAtMs: Date.now(),
|
|
20891
|
+
offers,
|
|
20892
|
+
objectUrls,
|
|
20893
|
+
});
|
|
20894
|
+
this.offers = offers;
|
|
20895
|
+
return this.offers;
|
|
20896
|
+
}
|
|
20897
|
+
throw new ProductOffersServiceError(result.message);
|
|
20898
|
+
}
|
|
20899
|
+
async fetchProductsRaw(cacheKey) {
|
|
20900
|
+
const existing = ProductOfferRepository.pendingFetches.get(cacheKey);
|
|
20901
|
+
if (existing)
|
|
20902
|
+
return existing;
|
|
20903
|
+
const request = (async () => {
|
|
20904
|
+
try {
|
|
20905
|
+
const linkTarget = this.getOfferIdsOrThrow();
|
|
20906
|
+
const path = this.buildOffersPath(linkTarget);
|
|
20907
|
+
const headers = this.getOffersHeaders();
|
|
20908
|
+
const profileKey = "fetch-products";
|
|
20909
|
+
const response = await this.widgetDeps.slideApiDeps.sendApiRequest(path, "GET", null, headers, null, profileKey);
|
|
20910
|
+
return this.parseOffersResponse(response.status, response.data);
|
|
20911
|
+
}
|
|
20912
|
+
catch (error) {
|
|
20913
|
+
console.error(error);
|
|
20914
|
+
throw error;
|
|
20915
|
+
}
|
|
20916
|
+
finally {
|
|
20917
|
+
ProductOfferRepository.pendingFetches.delete(cacheKey);
|
|
20918
|
+
}
|
|
20919
|
+
})();
|
|
20920
|
+
const delayed = Promise.all([request, new Promise(t => setTimeout(t, 200))]).then(([result]) => result);
|
|
20921
|
+
ProductOfferRepository.pendingFetches.set(cacheKey, delayed);
|
|
20922
|
+
return delayed;
|
|
20923
|
+
}
|
|
20924
|
+
getOfferIdsOrThrow() {
|
|
20925
|
+
const linkTarget = this.getLinkTarget();
|
|
20926
|
+
if (!linkTarget.length) {
|
|
20927
|
+
throw new ProductOffersServiceError(this.getMsgServiceError() ?? "", { code: "EMPTY_OFFER_IDS" });
|
|
20928
|
+
}
|
|
20929
|
+
return linkTarget;
|
|
20930
|
+
}
|
|
20931
|
+
buildOffersPath(offerIds) {
|
|
20932
|
+
const qs = this.buildOffersQueryString(offerIds);
|
|
20933
|
+
return `product/offer?${qs}`;
|
|
20934
|
+
}
|
|
20935
|
+
buildOffersQueryString(offerIds) {
|
|
20936
|
+
let qs = `id=${offerIds.join(",")}&expand=images,subOffersApi`;
|
|
20937
|
+
const sdkClientVariables = this.widgetDeps.getSdkClientVariables();
|
|
20938
|
+
if (sdkClientVariables?.pos != null) {
|
|
20939
|
+
qs += `&pos=${String(sdkClientVariables.pos)}`;
|
|
20940
|
+
}
|
|
20941
|
+
return qs;
|
|
20942
|
+
}
|
|
20943
|
+
getOffersHeaders() {
|
|
20944
|
+
return {
|
|
20945
|
+
accept: "application/json",
|
|
20946
|
+
"Content-Type": "application/json",
|
|
20947
|
+
};
|
|
20948
|
+
}
|
|
20949
|
+
parseOffersResponse(status, data) {
|
|
20950
|
+
if (status === 200 || status === 201) {
|
|
20951
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
20952
|
+
return { message: "", models: data };
|
|
20953
|
+
}
|
|
20954
|
+
throw new ProductOffersServiceError(this.getMsgServiceError() ?? "", { status, code: "EMPTY_RESPONSE" });
|
|
20955
|
+
}
|
|
20956
|
+
// WinInet-like network errors sometimes bubble up as "status"
|
|
20957
|
+
if (status === 12163 || status === 12002) {
|
|
20958
|
+
throw new ProductOffersNetworkError(this.getMsgNetworkError() ?? "", { status, code: "NETWORK" });
|
|
20959
|
+
}
|
|
20960
|
+
// All other statuses are treated as a general service error.
|
|
20961
|
+
throw new ProductOffersServiceError(this.getMsgServiceError() ?? "", { status, code: "SERVICE_ERROR" });
|
|
20962
|
+
}
|
|
20963
|
+
getCacheKey() {
|
|
20964
|
+
const ids = [...this.getLinkTarget()].sort((a, b) => a - b).join(",");
|
|
20965
|
+
const sdkClientVariables = this.widgetDeps.getSdkClientVariables();
|
|
20966
|
+
const pos = sdkClientVariables?.pos != null ? String(sdkClientVariables.pos) : "";
|
|
20967
|
+
return `offers:${ids}:pos:${pos}`;
|
|
20968
|
+
}
|
|
20969
|
+
static revokeObjectUrls(urls) {
|
|
20970
|
+
for (const u of urls) {
|
|
20971
|
+
try {
|
|
20972
|
+
URL.revokeObjectURL(u);
|
|
20973
|
+
}
|
|
20974
|
+
catch {
|
|
20975
|
+
// ignore
|
|
20976
|
+
}
|
|
20977
|
+
}
|
|
20978
|
+
}
|
|
20979
|
+
static collectDistinctImageUrls(offers, into) {
|
|
20980
|
+
for (const o of offers) {
|
|
20981
|
+
if (o.coverUrl) {
|
|
20982
|
+
into.add(o.coverUrl);
|
|
20983
|
+
}
|
|
20984
|
+
if (o.images?.length) {
|
|
20985
|
+
for (const img of o.images) {
|
|
20986
|
+
if (img.url) {
|
|
20987
|
+
into.add(img.url);
|
|
20988
|
+
}
|
|
20989
|
+
}
|
|
20990
|
+
}
|
|
20991
|
+
if (o.subOffersApi?.length) {
|
|
20992
|
+
this.collectDistinctImageUrls(o.subOffersApi, into);
|
|
20993
|
+
}
|
|
20994
|
+
}
|
|
20995
|
+
}
|
|
20996
|
+
static applyImageBlobMap(offers, originalToBlob) {
|
|
20997
|
+
for (const o of offers) {
|
|
20998
|
+
const coverOrig = o.coverUrl;
|
|
20999
|
+
if (coverOrig) {
|
|
21000
|
+
const blobUrl = originalToBlob.get(coverOrig);
|
|
21001
|
+
if (blobUrl) {
|
|
21002
|
+
o.coverUrl = blobUrl;
|
|
21003
|
+
}
|
|
21004
|
+
}
|
|
21005
|
+
if (o.images?.length) {
|
|
21006
|
+
for (const img of o.images) {
|
|
21007
|
+
const urlOrig = img.url;
|
|
21008
|
+
const blobUrl = originalToBlob.get(urlOrig);
|
|
21009
|
+
if (blobUrl) {
|
|
21010
|
+
img.url = blobUrl;
|
|
21011
|
+
}
|
|
21012
|
+
}
|
|
21013
|
+
}
|
|
21014
|
+
if (o.subOffersApi?.length) {
|
|
21015
|
+
this.applyImageBlobMap(o.subOffersApi, originalToBlob);
|
|
21016
|
+
}
|
|
21017
|
+
}
|
|
21018
|
+
}
|
|
21019
|
+
/**
|
|
21020
|
+
* Fetches `coverUrl` and `images[].url` as blobs and replaces them with `blob:` URLs.
|
|
21021
|
+
* Products are considered unresolved until this completes successfully.
|
|
21022
|
+
*/
|
|
21023
|
+
static async hydrateOfferImageBlobs(offers, networkErrorMessage) {
|
|
21024
|
+
const distinct = new Set();
|
|
21025
|
+
ProductOfferRepository.collectDistinctImageUrls(offers, distinct);
|
|
21026
|
+
const objectUrls = [];
|
|
21027
|
+
const originalToBlob = new Map();
|
|
21028
|
+
await Promise.all([...distinct].map(async (original) => {
|
|
21029
|
+
const response = await fetch(original, { mode: "cors" });
|
|
21030
|
+
if (!response.ok) {
|
|
21031
|
+
throw new ProductOffersNetworkError(networkErrorMessage ?? "", { status: response.status, code: "IMAGE_BLOB_FETCH" });
|
|
21032
|
+
}
|
|
21033
|
+
const blob = await response.blob();
|
|
21034
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
21035
|
+
objectUrls.push(blobUrl);
|
|
21036
|
+
originalToBlob.set(original, blobUrl);
|
|
21037
|
+
}));
|
|
21038
|
+
ProductOfferRepository.applyImageBlobMap(offers, originalToBlob);
|
|
21039
|
+
return { objectUrls };
|
|
21040
|
+
}
|
|
21041
|
+
static getRawFromCache(cacheKey) {
|
|
21042
|
+
const item = this.rawOffersCache.get(cacheKey);
|
|
21043
|
+
if (!item)
|
|
21044
|
+
return null;
|
|
21045
|
+
if (Date.now() - item.cachedAtMs > this.RAW_CACHE_TTL_MS) {
|
|
21046
|
+
ProductOfferRepository.revokeObjectUrls(item.objectUrls);
|
|
21047
|
+
this.rawOffersCache.delete(cacheKey);
|
|
21048
|
+
return null;
|
|
21049
|
+
}
|
|
21050
|
+
return item.offers;
|
|
21051
|
+
}
|
|
21052
|
+
static get [Symbol.for("___CTOR_ARGS___")]() { return [`WidgetDeps`, `() => Array<number>`, `() => string | undefined`, `() => string | undefined`]; }
|
|
21053
|
+
}
|
|
21054
|
+
|
|
20698
21055
|
/**
|
|
20699
21056
|
* adult: null
|
|
20700
21057
|
* availability: 1
|
|
@@ -20750,6 +21107,7 @@ class WidgetProducts extends WidgetBase {
|
|
|
20750
21107
|
this.isScreenSupportsTouch = isScreenSupportsTouch(this.env);
|
|
20751
21108
|
this.msgNetworkError = getTagData(this.element, "msgNetworkError");
|
|
20752
21109
|
this.msgServiceError = getTagData(this.element, "msgServiceError");
|
|
21110
|
+
this.repository = new ProductOfferRepository(this.widgetDeps, () => this.linkTarget, () => this.msgNetworkError, () => this.msgServiceError);
|
|
20753
21111
|
}
|
|
20754
21112
|
/**
|
|
20755
21113
|
* Start or restart widget
|
|
@@ -20845,107 +21203,6 @@ class WidgetProducts extends WidgetBase {
|
|
|
20845
21203
|
console.error(error);
|
|
20846
21204
|
}
|
|
20847
21205
|
}
|
|
20848
|
-
async getOrFetchProducts() {
|
|
20849
|
-
if (this.currentModels.length > 0)
|
|
20850
|
-
return this.currentModels;
|
|
20851
|
-
const result = await this.fetchProducts();
|
|
20852
|
-
if (result.models.length > 0) {
|
|
20853
|
-
this.currentModels = result.models;
|
|
20854
|
-
return result.models;
|
|
20855
|
-
}
|
|
20856
|
-
throw new Error(result.message);
|
|
20857
|
-
}
|
|
20858
|
-
async fetchProducts() {
|
|
20859
|
-
const fetchAndCacheMedia = async () => {
|
|
20860
|
-
if (!this.linkTarget.length) {
|
|
20861
|
-
return { message: this.msgServiceError ?? "", models: [] };
|
|
20862
|
-
}
|
|
20863
|
-
let qs = `id=${this.linkTarget.join(",")}&expand=images,subOffersApi`;
|
|
20864
|
-
const sdkClientVariables = this.widgetDeps.getSdkClientVariables();
|
|
20865
|
-
if (sdkClientVariables != null && sdkClientVariables.pos != null) {
|
|
20866
|
-
qs += `&pos=${String(sdkClientVariables.pos)}`;
|
|
20867
|
-
}
|
|
20868
|
-
const path = `product/offer?${qs}`;
|
|
20869
|
-
const headers = {
|
|
20870
|
-
accept: "application/json",
|
|
20871
|
-
"Content-Type": "application/json",
|
|
20872
|
-
};
|
|
20873
|
-
const profileKey = "fetch-products";
|
|
20874
|
-
try {
|
|
20875
|
-
const response = await this.widgetDeps.slideApiDeps.sendApiRequest(path, "GET", null, headers, null, profileKey);
|
|
20876
|
-
// console.log({response});
|
|
20877
|
-
const status = response.status;
|
|
20878
|
-
if (status === 200 || status === 201) {
|
|
20879
|
-
if (response.data && Array.isArray(response.data) && response.data.length > 0) {
|
|
20880
|
-
try {
|
|
20881
|
-
await this.cacheOffersMediaResources(response.data);
|
|
20882
|
-
}
|
|
20883
|
-
catch (error) {
|
|
20884
|
-
console.error(error);
|
|
20885
|
-
}
|
|
20886
|
-
return { message: "", models: response.data };
|
|
20887
|
-
}
|
|
20888
|
-
else {
|
|
20889
|
-
return { message: this.msgServiceError ?? "", models: [] };
|
|
20890
|
-
}
|
|
20891
|
-
}
|
|
20892
|
-
else if (status === 12163 || status === 12002) {
|
|
20893
|
-
return { message: this.msgNetworkError ?? "", models: [] };
|
|
20894
|
-
}
|
|
20895
|
-
else {
|
|
20896
|
-
return { message: this.msgServiceError ?? "", models: [] };
|
|
20897
|
-
}
|
|
20898
|
-
}
|
|
20899
|
-
catch (error) {
|
|
20900
|
-
console.error(error);
|
|
20901
|
-
return { message: this.msgServiceError ?? "", models: [] };
|
|
20902
|
-
}
|
|
20903
|
-
};
|
|
20904
|
-
return Promise.all([
|
|
20905
|
-
fetchAndCacheMedia(),
|
|
20906
|
-
new Promise(t => {
|
|
20907
|
-
return setTimeout(t, 200);
|
|
20908
|
-
}),
|
|
20909
|
-
]).then(([result]) => result);
|
|
20910
|
-
}
|
|
20911
|
-
cacheOffersMediaResources(offers) {
|
|
20912
|
-
const cacheItem = (offer) => {
|
|
20913
|
-
return new Promise(resolve => {
|
|
20914
|
-
if (offer.coverUrl != null) {
|
|
20915
|
-
this.env
|
|
20916
|
-
.fetch(offer.coverUrl)
|
|
20917
|
-
.then(response => {
|
|
20918
|
-
if (response != null && response.ok) {
|
|
20919
|
-
response
|
|
20920
|
-
.blob()
|
|
20921
|
-
.then(blob => {
|
|
20922
|
-
offer.coverUrl = URL.createObjectURL(blob);
|
|
20923
|
-
resolve();
|
|
20924
|
-
})
|
|
20925
|
-
.catch(resolve);
|
|
20926
|
-
}
|
|
20927
|
-
else {
|
|
20928
|
-
resolve();
|
|
20929
|
-
}
|
|
20930
|
-
})
|
|
20931
|
-
.catch(resolve);
|
|
20932
|
-
}
|
|
20933
|
-
else {
|
|
20934
|
-
resolve();
|
|
20935
|
-
}
|
|
20936
|
-
});
|
|
20937
|
-
};
|
|
20938
|
-
const promises = offers.map(cacheItem);
|
|
20939
|
-
return Promise.all(promises);
|
|
20940
|
-
}
|
|
20941
|
-
revokeOffersMediaResources(offers) {
|
|
20942
|
-
offers.forEach(offer => {
|
|
20943
|
-
if (offer.coverUrl != null) {
|
|
20944
|
-
// todo check if coverUrl really is object URL
|
|
20945
|
-
URL.revokeObjectURL(offer.coverUrl);
|
|
20946
|
-
}
|
|
20947
|
-
});
|
|
20948
|
-
}
|
|
20949
21206
|
initSwipeGestureDetector() {
|
|
20950
21207
|
if (this.isOpen) {
|
|
20951
21208
|
if (this.swipeGestureDetector == null) {
|
|
@@ -20973,7 +21230,7 @@ class WidgetProducts extends WidgetBase {
|
|
|
20973
21230
|
get isForcePaused() {
|
|
20974
21231
|
return this.isOpen;
|
|
20975
21232
|
}
|
|
20976
|
-
|
|
21233
|
+
repository;
|
|
20977
21234
|
async openProductsView() {
|
|
20978
21235
|
if (this.isOpen || this.isLoading) {
|
|
20979
21236
|
return;
|
|
@@ -20987,7 +21244,7 @@ class WidgetProducts extends WidgetBase {
|
|
|
20987
21244
|
}
|
|
20988
21245
|
try {
|
|
20989
21246
|
this.isLoading = true;
|
|
20990
|
-
const models = await this.getOrFetchProducts();
|
|
21247
|
+
const models = await this.repository.getOrFetchProducts();
|
|
20991
21248
|
this.productsView = this.createProductsView(models, this.closeProductsView.bind(this));
|
|
20992
21249
|
this.productsView.classList.add("ias-products-container-view--visible");
|
|
20993
21250
|
this.slide.appendChild(this.productsView);
|
|
@@ -21026,12 +21283,11 @@ class WidgetProducts extends WidgetBase {
|
|
|
21026
21283
|
const onClosed = () => {
|
|
21027
21284
|
this.productsView?.removeEventListener("animationend", onClosed);
|
|
21028
21285
|
this.productsView?.parentElement?.removeChild(this.productsView);
|
|
21029
|
-
this.revokeOffersMediaResources(this.currentModels);
|
|
21030
21286
|
if (!this.disableTimer) {
|
|
21031
21287
|
this.onWidgetRequireResumeUI();
|
|
21032
21288
|
}
|
|
21033
21289
|
this.isOpen = false;
|
|
21034
|
-
this.
|
|
21290
|
+
this.repository.clear();
|
|
21035
21291
|
};
|
|
21036
21292
|
this.productsView?.addEventListener("animationend", onClosed);
|
|
21037
21293
|
}
|
|
@@ -21295,6 +21551,446 @@ class WidgetProducts extends WidgetBase {
|
|
|
21295
21551
|
static get [Symbol.for("___CTOR_ARGS___")]() { return [`HTMLElement`, `Partial`, `WidgetCallbacks`, `WidgetDeps`]; }
|
|
21296
21552
|
}
|
|
21297
21553
|
|
|
21554
|
+
const ADD_TO_CART_TIMEOUT = 60000;
|
|
21555
|
+
class BottomSheetMountingError extends Error {
|
|
21556
|
+
constructor() {
|
|
21557
|
+
super("[IAS]: products bottom sheet mounting error");
|
|
21558
|
+
this.name = "BottomSheetMountingError";
|
|
21559
|
+
}
|
|
21560
|
+
static get [Symbol.for("___CTOR_ARGS___")]() { return []; }
|
|
21561
|
+
}
|
|
21562
|
+
class WidgetProductCarousel extends WidgetBase {
|
|
21563
|
+
static DEFAULTS = {
|
|
21564
|
+
slide: null,
|
|
21565
|
+
activateAfterCreate: false,
|
|
21566
|
+
create: false,
|
|
21567
|
+
localData: {},
|
|
21568
|
+
};
|
|
21569
|
+
static widgetClassName = "narrative-element-product-carousel";
|
|
21570
|
+
captionView;
|
|
21571
|
+
offerIds = [];
|
|
21572
|
+
msgNetworkError;
|
|
21573
|
+
msgServiceError;
|
|
21574
|
+
isScreenSupportsTouch;
|
|
21575
|
+
isLoading = false;
|
|
21576
|
+
repository;
|
|
21577
|
+
canClick = false;
|
|
21578
|
+
isBottomSheetOpened = false;
|
|
21579
|
+
isClickCapturedByScroll = false;
|
|
21580
|
+
$carousel = null;
|
|
21581
|
+
$track = null;
|
|
21582
|
+
isTouchListenersInit = false;
|
|
21583
|
+
constructor(element, options, widgetCallbacks, widgetDeps) {
|
|
21584
|
+
super(element, options, widgetCallbacks, widgetDeps);
|
|
21585
|
+
this.captionView = this.element.querySelector(".narrative-element-text-lines");
|
|
21586
|
+
this.isScreenSupportsTouch = isScreenSupportsTouch(this.env);
|
|
21587
|
+
this.ajustGeometryMargin();
|
|
21588
|
+
const offerIds = decodeURIComponent(getTagData(element, "offerIds") ?? "[]");
|
|
21589
|
+
try {
|
|
21590
|
+
const parsed = JSON.parse(offerIds);
|
|
21591
|
+
if (Array.isArray(parsed)) {
|
|
21592
|
+
this.offerIds = parsed.filter(FilterNumber);
|
|
21593
|
+
}
|
|
21594
|
+
}
|
|
21595
|
+
catch (e) {
|
|
21596
|
+
console.error(e);
|
|
21597
|
+
}
|
|
21598
|
+
this.msgNetworkError = getTagData(this.element, "msgNetworkError");
|
|
21599
|
+
this.msgServiceError = getTagData(this.element, "msgServiceError");
|
|
21600
|
+
this.repository = new ProductOfferRepository(this.widgetDeps, () => this.offerIds, () => this.msgNetworkError, () => this.msgServiceError);
|
|
21601
|
+
}
|
|
21602
|
+
onRefreshUserData(localData) {
|
|
21603
|
+
super.onRefreshUserData(localData);
|
|
21604
|
+
this.isLoading = false;
|
|
21605
|
+
this.renderCarousel();
|
|
21606
|
+
}
|
|
21607
|
+
onStart() {
|
|
21608
|
+
super.onStart();
|
|
21609
|
+
this.canClick = true;
|
|
21610
|
+
this.initTouchListeners();
|
|
21611
|
+
}
|
|
21612
|
+
onStop() {
|
|
21613
|
+
super.onStop();
|
|
21614
|
+
this.isClickCapturedByScroll = false;
|
|
21615
|
+
this.isBottomSheetOpened = false;
|
|
21616
|
+
this.canClick = false;
|
|
21617
|
+
this.isTouchListenersInit = false;
|
|
21618
|
+
this.enableHostUIInteraction();
|
|
21619
|
+
this.widgetDeps.slideRoot.removeEventListener("touchstart", this.handleMouseDown);
|
|
21620
|
+
this.widgetDeps.slideRoot.removeEventListener("mousedown", this.handleMouseDown);
|
|
21621
|
+
this.widgetDeps.slideRoot.removeEventListener("touchend", this.handleMouseUp);
|
|
21622
|
+
this.widgetDeps.slideRoot.removeEventListener("mouseup", this.handleMouseUp);
|
|
21623
|
+
}
|
|
21624
|
+
getIsClickCapturedByWidget() {
|
|
21625
|
+
return this.isClickCapturedByScroll || this.isBottomSheetOpened;
|
|
21626
|
+
}
|
|
21627
|
+
get isForcePaused() {
|
|
21628
|
+
return this.isBottomSheetOpened;
|
|
21629
|
+
}
|
|
21630
|
+
/** Offset compensation for an elongated viewport */
|
|
21631
|
+
ajustGeometryMargin() {
|
|
21632
|
+
this.element.parentElement.style.padding = "0 var(--x-offset, 0px)";
|
|
21633
|
+
}
|
|
21634
|
+
isTransparentElement() {
|
|
21635
|
+
if (!this.element)
|
|
21636
|
+
return false;
|
|
21637
|
+
try {
|
|
21638
|
+
const color = window.getComputedStyle(this.element).color;
|
|
21639
|
+
return color === "transparent" || color === "rgba(0, 0, 0, 0)" || color === "rgba(0,0,0,0)";
|
|
21640
|
+
}
|
|
21641
|
+
catch (err) {
|
|
21642
|
+
console.error(err);
|
|
21643
|
+
}
|
|
21644
|
+
return false;
|
|
21645
|
+
}
|
|
21646
|
+
async renderCarousel() {
|
|
21647
|
+
if (this.isLoading)
|
|
21648
|
+
return;
|
|
21649
|
+
if (!this.offerIds.length)
|
|
21650
|
+
throw new Error("[IAS]: empty offer ids list");
|
|
21651
|
+
this.isLoading = true;
|
|
21652
|
+
if (!this.isTransparentElement()) {
|
|
21653
|
+
this.element.classList.add("loader");
|
|
21654
|
+
}
|
|
21655
|
+
try {
|
|
21656
|
+
const models = await this.repository.getOrFetchProducts();
|
|
21657
|
+
this.renderInlineCarousel(models);
|
|
21658
|
+
}
|
|
21659
|
+
catch (error) {
|
|
21660
|
+
if (error instanceof Error) {
|
|
21661
|
+
this.widgetDeps.slideApiDeps.showToast(error.message);
|
|
21662
|
+
}
|
|
21663
|
+
}
|
|
21664
|
+
finally {
|
|
21665
|
+
this.isLoading = false;
|
|
21666
|
+
this.element.classList.remove("loader");
|
|
21667
|
+
}
|
|
21668
|
+
}
|
|
21669
|
+
initTouchListeners() {
|
|
21670
|
+
if (!this.isTouchListenersInit) {
|
|
21671
|
+
this.widgetDeps.slideRoot.addEventListener("touchstart", this.handleMouseDown);
|
|
21672
|
+
this.widgetDeps.slideRoot.addEventListener("mousedown", this.handleMouseDown);
|
|
21673
|
+
this.isTouchListenersInit = true;
|
|
21674
|
+
}
|
|
21675
|
+
}
|
|
21676
|
+
handleMouseDown = (e) => {
|
|
21677
|
+
let scrollView = e.target;
|
|
21678
|
+
if (!scrollView.classList.contains("ias-products-inline-carousel-scroll-view")) {
|
|
21679
|
+
scrollView = scrollView.closest(".ias-products-inline-carousel-scroll-view");
|
|
21680
|
+
}
|
|
21681
|
+
if (!scrollView) {
|
|
21682
|
+
return;
|
|
21683
|
+
}
|
|
21684
|
+
this.isClickCapturedByScroll = true;
|
|
21685
|
+
this.widgetDeps.slideApiDeps.disableHorizontalSwipeGesture();
|
|
21686
|
+
this.widgetDeps.slideApiDeps.disableVerticalSwipeGesture();
|
|
21687
|
+
this.widgetDeps.slideRoot.addEventListener("touchend", this.handleMouseUp);
|
|
21688
|
+
this.widgetDeps.slideRoot.addEventListener("mouseup", this.handleMouseUp);
|
|
21689
|
+
};
|
|
21690
|
+
handleMouseUp = () => {
|
|
21691
|
+
this.widgetDeps.slideRoot.removeEventListener("touchend", this.handleMouseUp);
|
|
21692
|
+
this.widgetDeps.slideRoot.removeEventListener("mouseup", this.handleMouseUp);
|
|
21693
|
+
this.env.requestAnimationFrame(() => {
|
|
21694
|
+
this.widgetDeps.slideApiDeps.enableHorizontalSwipeGesture();
|
|
21695
|
+
this.widgetDeps.slideApiDeps.enableVerticalSwipeGesture();
|
|
21696
|
+
this.isClickCapturedByScroll = false;
|
|
21697
|
+
});
|
|
21698
|
+
};
|
|
21699
|
+
createCarousel() {
|
|
21700
|
+
this.$carousel = h("div", { class: "ias-products-inline-carousel" });
|
|
21701
|
+
this.element.appendChild(this.$carousel);
|
|
21702
|
+
this.$carousel.dir = this.layoutDirection;
|
|
21703
|
+
this.$track = h("div", { class: "ias-products-inline-carousel-scroll-view", ontouchstart: () => { }, onmousedown: () => { } });
|
|
21704
|
+
this.$carousel.appendChild(this.$track);
|
|
21705
|
+
return { carousel: this.$carousel, track: this.$track };
|
|
21706
|
+
}
|
|
21707
|
+
renderInlineCarousel(offers) {
|
|
21708
|
+
if (this.$carousel)
|
|
21709
|
+
return;
|
|
21710
|
+
const { carousel, track } = this.createCarousel();
|
|
21711
|
+
track.innerHTML = "";
|
|
21712
|
+
offers.forEach(offer => {
|
|
21713
|
+
track.appendChild(this.renderCard(offer));
|
|
21714
|
+
});
|
|
21715
|
+
const controls = this.renderCarouselControls();
|
|
21716
|
+
this.env.requestAnimationFrame(() => {
|
|
21717
|
+
if (track.scrollWidth > track.clientWidth && controls) {
|
|
21718
|
+
carousel.appendChild(controls);
|
|
21719
|
+
}
|
|
21720
|
+
});
|
|
21721
|
+
}
|
|
21722
|
+
renderCarouselControls() {
|
|
21723
|
+
if (this.isScreenSupportsTouch)
|
|
21724
|
+
return null;
|
|
21725
|
+
const scrollControlStartIconView = h("div", { class: "ias-product-carousel-scroll-control-start-icon-view" });
|
|
21726
|
+
const scrollControlEndIconView = h("div", { class: "ias-product-carousel-scroll-control-end-icon-view" });
|
|
21727
|
+
const scrollControlStartView = h("div", {
|
|
21728
|
+
class: "ias-product-carousel-scroll-control-start-view",
|
|
21729
|
+
onclick: (e) => {
|
|
21730
|
+
if (!this.canClick)
|
|
21731
|
+
return;
|
|
21732
|
+
e.preventDefault();
|
|
21733
|
+
e.stopPropagation();
|
|
21734
|
+
if (this.layoutDirection === "ltr") {
|
|
21735
|
+
this.navigatePrev();
|
|
21736
|
+
}
|
|
21737
|
+
else {
|
|
21738
|
+
this.navigateNext();
|
|
21739
|
+
}
|
|
21740
|
+
},
|
|
21741
|
+
}, scrollControlStartIconView);
|
|
21742
|
+
const scrollControlEndView = h("div", {
|
|
21743
|
+
class: "ias-product-carousel-scroll-control-end-view",
|
|
21744
|
+
onclick: (e) => {
|
|
21745
|
+
if (!this.canClick)
|
|
21746
|
+
return;
|
|
21747
|
+
e.preventDefault();
|
|
21748
|
+
e.stopPropagation();
|
|
21749
|
+
if (this.layoutDirection === "ltr") {
|
|
21750
|
+
this.navigateNext();
|
|
21751
|
+
}
|
|
21752
|
+
else {
|
|
21753
|
+
this.navigatePrev();
|
|
21754
|
+
}
|
|
21755
|
+
},
|
|
21756
|
+
}, scrollControlEndIconView);
|
|
21757
|
+
return h("div", { class: "ias-product-carousel-scroll-controls-view" }, scrollControlStartView, scrollControlEndView);
|
|
21758
|
+
}
|
|
21759
|
+
navigatePrev() {
|
|
21760
|
+
this.setScrollLeft(this.getScrollLeft() - this.getScrollViewportWidth());
|
|
21761
|
+
}
|
|
21762
|
+
navigateNext() {
|
|
21763
|
+
this.setScrollLeft(this.getScrollLeft() + this.getScrollViewportWidth());
|
|
21764
|
+
}
|
|
21765
|
+
setScrollLeft(value) {
|
|
21766
|
+
if (this.$track != null) {
|
|
21767
|
+
this.$track.scrollLeft = Math.round(value);
|
|
21768
|
+
}
|
|
21769
|
+
}
|
|
21770
|
+
getScrollLeft() {
|
|
21771
|
+
if (this.$track != null) {
|
|
21772
|
+
return this.$track.scrollLeft;
|
|
21773
|
+
}
|
|
21774
|
+
return 0;
|
|
21775
|
+
}
|
|
21776
|
+
getScrollViewportWidth() {
|
|
21777
|
+
if (this.$track != null) {
|
|
21778
|
+
return this.$track.clientWidth;
|
|
21779
|
+
}
|
|
21780
|
+
return 0;
|
|
21781
|
+
}
|
|
21782
|
+
renderCard(offer) {
|
|
21783
|
+
const contentEl = this.renderCardContent(offer);
|
|
21784
|
+
const card = h("div", { class: "ias-product-card" }, this.renderCardImage(offer), contentEl);
|
|
21785
|
+
card.onclick = e => {
|
|
21786
|
+
if (this.isBottomSheetOpened || !this.canClick)
|
|
21787
|
+
return;
|
|
21788
|
+
e.stopPropagation();
|
|
21789
|
+
e.preventDefault();
|
|
21790
|
+
if (!this.disableTimer) {
|
|
21791
|
+
this.onWidgetRequirePauseUI();
|
|
21792
|
+
}
|
|
21793
|
+
this.statEventWidgetCardClick(offer);
|
|
21794
|
+
this.showProductDetails({ offer, card });
|
|
21795
|
+
};
|
|
21796
|
+
return card;
|
|
21797
|
+
}
|
|
21798
|
+
showProductDetails = ({ offer, card }) => {
|
|
21799
|
+
const bs = new ProductDetailsBottomSheet(this.widgetDeps, this.getBottomSheetParams());
|
|
21800
|
+
const bsNode = bs.render();
|
|
21801
|
+
if (!bsNode)
|
|
21802
|
+
throw new BottomSheetMountingError();
|
|
21803
|
+
this.slide.appendChild(bsNode);
|
|
21804
|
+
bs.open({ offer, isCartSupported: this.isCartSupported() });
|
|
21805
|
+
this.isBottomSheetOpened = true;
|
|
21806
|
+
this.disableHostUIInteraction();
|
|
21807
|
+
};
|
|
21808
|
+
renderCardImage(offer) {
|
|
21809
|
+
const coverUrl = offer.coverUrl ?? "";
|
|
21810
|
+
const title = offer.name ?? "";
|
|
21811
|
+
const img = coverUrl
|
|
21812
|
+
? h("img", {
|
|
21813
|
+
src: coverUrl,
|
|
21814
|
+
alt: title,
|
|
21815
|
+
/* loading: "lazy", */
|
|
21816
|
+
})
|
|
21817
|
+
: null;
|
|
21818
|
+
return h("div", { class: "ias-product-card__image" }, img, h("div", {
|
|
21819
|
+
class: "ias-product-card__image-mask",
|
|
21820
|
+
}));
|
|
21821
|
+
}
|
|
21822
|
+
renderCardContent(offer) {
|
|
21823
|
+
const titleEl = this.renderCardTitle(offer);
|
|
21824
|
+
const pricesEl = this.renderCardPrices(offer);
|
|
21825
|
+
const purchaseButtonEl = this.renderPurchaseButton(offer);
|
|
21826
|
+
const rowEl = h("div", { class: "ias-product-card__row" }, pricesEl, purchaseButtonEl);
|
|
21827
|
+
return h("div", { class: "ias-product-card__content" }, titleEl, rowEl);
|
|
21828
|
+
}
|
|
21829
|
+
renderCardTitle(offer) {
|
|
21830
|
+
const title = offer.name ?? "";
|
|
21831
|
+
return h("h3", { class: "ias-product-card__title", textContent: title });
|
|
21832
|
+
}
|
|
21833
|
+
renderCardPrices(offer) {
|
|
21834
|
+
const priceEl = offer.price != null && offer.currency != null
|
|
21835
|
+
? h("span", {
|
|
21836
|
+
class: "ias-product-card__price",
|
|
21837
|
+
innerHTML: formatter.asCurrency(offer.price, offer.currency),
|
|
21838
|
+
})
|
|
21839
|
+
: null;
|
|
21840
|
+
const oldPriceEl = offer.oldPrice != null && offer.currency != null
|
|
21841
|
+
? h("span", {
|
|
21842
|
+
class: "ias-product-card__price ias-product-card__price--old",
|
|
21843
|
+
innerHTML: formatter.asCurrency(offer.oldPrice, offer.currency),
|
|
21844
|
+
})
|
|
21845
|
+
: null;
|
|
21846
|
+
return h("div", { class: "ias-product-card__prices" }, priceEl, oldPriceEl);
|
|
21847
|
+
}
|
|
21848
|
+
renderPurchaseButton(offer) {
|
|
21849
|
+
const isCartSupported = this.isCartSupported();
|
|
21850
|
+
if (!isCartSupported && !offer.url)
|
|
21851
|
+
return null;
|
|
21852
|
+
const button = h("button", {
|
|
21853
|
+
class: "ias-product-card__purchase-button",
|
|
21854
|
+
type: "button",
|
|
21855
|
+
textContent: getTagData(this.element, "msgBuyNow") ?? "Buy now",
|
|
21856
|
+
onclick: e => {
|
|
21857
|
+
if (!this.canClick)
|
|
21858
|
+
return;
|
|
21859
|
+
if (!this.disableTimer) {
|
|
21860
|
+
this.onWidgetRequirePauseUI();
|
|
21861
|
+
}
|
|
21862
|
+
e.stopPropagation();
|
|
21863
|
+
this.statEventWidgetCardClick(offer);
|
|
21864
|
+
if (isCartSupported) {
|
|
21865
|
+
this.addToCart(offer, button);
|
|
21866
|
+
}
|
|
21867
|
+
else if (offer.url) {
|
|
21868
|
+
this.widgetDeps.slideApiDeps.openUrl({ type: "link", link: { type: "url", target: offer.url } });
|
|
21869
|
+
}
|
|
21870
|
+
},
|
|
21871
|
+
});
|
|
21872
|
+
return button;
|
|
21873
|
+
}
|
|
21874
|
+
async withTimeout(promise, timeout) {
|
|
21875
|
+
await Promise.race([promise, new Promise((resolve, reject) => setTimeout(reject, timeout))]);
|
|
21876
|
+
}
|
|
21877
|
+
async addToCart(offerDto, button) {
|
|
21878
|
+
if (this.isLoading || this.isBottomSheetOpened)
|
|
21879
|
+
return;
|
|
21880
|
+
try {
|
|
21881
|
+
this.isLoading = true;
|
|
21882
|
+
button.classList.add("loader");
|
|
21883
|
+
const bs = new ProductCheckoutBottomSheet(this.widgetDeps, this.getBottomSheetParams());
|
|
21884
|
+
await this.withTimeout(bs.productCartUpdate({
|
|
21885
|
+
offerDtos: this.repository.getCurrentModels(),
|
|
21886
|
+
offerId: offerDto.offerId,
|
|
21887
|
+
quantity: 1,
|
|
21888
|
+
}), ADD_TO_CART_TIMEOUT);
|
|
21889
|
+
const bsNode = bs.render();
|
|
21890
|
+
if (!bsNode)
|
|
21891
|
+
throw new BottomSheetMountingError();
|
|
21892
|
+
this.slide.appendChild(bsNode);
|
|
21893
|
+
bs.open();
|
|
21894
|
+
this.isBottomSheetOpened = true;
|
|
21895
|
+
this.disableHostUIInteraction();
|
|
21896
|
+
}
|
|
21897
|
+
catch (error) {
|
|
21898
|
+
if (error instanceof Error) {
|
|
21899
|
+
this.widgetDeps.slideApiDeps.showToast(error.message);
|
|
21900
|
+
}
|
|
21901
|
+
else if (typeof error === "string") {
|
|
21902
|
+
// Native SDKs return a string
|
|
21903
|
+
this.widgetDeps.slideApiDeps.showToast(error);
|
|
21904
|
+
}
|
|
21905
|
+
}
|
|
21906
|
+
finally {
|
|
21907
|
+
button.classList.remove("loader");
|
|
21908
|
+
this.isLoading = false;
|
|
21909
|
+
}
|
|
21910
|
+
}
|
|
21911
|
+
getBottomSheetParams() {
|
|
21912
|
+
return {
|
|
21913
|
+
translations: {
|
|
21914
|
+
color: getTagData(this.element, "msgColor") ?? "",
|
|
21915
|
+
size: getTagData(this.element, "msgSize") ?? "",
|
|
21916
|
+
addToCart: getTagData(this.element, "msgAddToCart") ?? "",
|
|
21917
|
+
successAddToCart: getTagData(this.element, "msgSuccess") ?? "",
|
|
21918
|
+
successSubAddToCart: getTagData(this.element, "msgSuccessSub") ?? "",
|
|
21919
|
+
errorAddToCart: getTagData(this.element, "msgError") ?? "",
|
|
21920
|
+
goToCartBtn: getTagData(this.element, "msgGoToCart") ?? "",
|
|
21921
|
+
continueBtn: getTagData(this.element, "msgContinue") ?? "",
|
|
21922
|
+
openUrl: getTagData(this.element, "msgOpenUrl") ?? "",
|
|
21923
|
+
},
|
|
21924
|
+
onClose: () => {
|
|
21925
|
+
if (!this.disableTimer) {
|
|
21926
|
+
this.onWidgetRequireResumeUI();
|
|
21927
|
+
}
|
|
21928
|
+
this.isBottomSheetOpened = false;
|
|
21929
|
+
this.enableHostUIInteraction();
|
|
21930
|
+
},
|
|
21931
|
+
};
|
|
21932
|
+
}
|
|
21933
|
+
statEventWidgetCardClick(offer) {
|
|
21934
|
+
try {
|
|
21935
|
+
const captionViewText = this.captionView?.textContent ?? "";
|
|
21936
|
+
this.sendStatisticEventToApp("w-product-carousel-card-click", {
|
|
21937
|
+
...this.statisticEventBaseFieldsShortForm,
|
|
21938
|
+
wi: this.elementId,
|
|
21939
|
+
wl: captionViewText,
|
|
21940
|
+
wv: offer.offerId,
|
|
21941
|
+
wvi: offer.id,
|
|
21942
|
+
}, {
|
|
21943
|
+
...this.statisticEventBaseFieldsFullForm,
|
|
21944
|
+
widget_id: this.elementId,
|
|
21945
|
+
widget_label: captionViewText,
|
|
21946
|
+
widget_value: offer.offerId,
|
|
21947
|
+
widget_value_id: offer.id,
|
|
21948
|
+
}, {
|
|
21949
|
+
forceEnableStatisticV2: true,
|
|
21950
|
+
});
|
|
21951
|
+
}
|
|
21952
|
+
catch (error) {
|
|
21953
|
+
console.error(error);
|
|
21954
|
+
}
|
|
21955
|
+
}
|
|
21956
|
+
isCartSupported() {
|
|
21957
|
+
const isCartEnabled = getTagData(this.element, "isCartEnabled") === "true";
|
|
21958
|
+
return this.widgetDeps.slideApiDeps.isSdkSupportProductCart && isCartEnabled;
|
|
21959
|
+
}
|
|
21960
|
+
disableHostUIInteraction() {
|
|
21961
|
+
this.widgetDeps.slideApiDeps.disableHorizontalSwipeGesture();
|
|
21962
|
+
this.widgetDeps.slideApiDeps.disableVerticalSwipeGesture();
|
|
21963
|
+
this.widgetDeps.slideApiDeps.disableBackpress();
|
|
21964
|
+
}
|
|
21965
|
+
enableHostUIInteraction() {
|
|
21966
|
+
this.widgetDeps.slideApiDeps.enableHorizontalSwipeGesture();
|
|
21967
|
+
this.widgetDeps.slideApiDeps.enableVerticalSwipeGesture();
|
|
21968
|
+
this.widgetDeps.slideApiDeps.enableBackpress();
|
|
21969
|
+
}
|
|
21970
|
+
static api = {
|
|
21971
|
+
widgetClassName: WidgetProductCarousel.widgetClassName,
|
|
21972
|
+
onRefreshUserData: WidgetProductCarousel.onRefreshUserData,
|
|
21973
|
+
init: function (element, localData, widgetCallbacks, widgetDeps) {
|
|
21974
|
+
WidgetProductCarousel.initWidget(element, localData, (el, options) => new WidgetProductCarousel(el, options, widgetCallbacks, widgetDeps));
|
|
21975
|
+
},
|
|
21976
|
+
onStart: function (element) {
|
|
21977
|
+
WidgetProductCarousel.getInstance(element)?.onStart();
|
|
21978
|
+
},
|
|
21979
|
+
onStop: function (element) {
|
|
21980
|
+
WidgetProductCarousel.getInstance(element)?.onStop();
|
|
21981
|
+
},
|
|
21982
|
+
isClickCapturedByWidget: function (element) {
|
|
21983
|
+
const widget = WidgetProductCarousel.getInstance(element);
|
|
21984
|
+
return widget?.getIsClickCapturedByWidget() ?? false;
|
|
21985
|
+
},
|
|
21986
|
+
isForcePaused: function (element) {
|
|
21987
|
+
const widget = WidgetProductCarousel.getInstance(element);
|
|
21988
|
+
return widget?.isForcePaused ?? false;
|
|
21989
|
+
},
|
|
21990
|
+
};
|
|
21991
|
+
static get [Symbol.for("___CTOR_ARGS___")]() { return [`HTMLElement`, `Partial`, `WidgetCallbacks`, `WidgetDeps`]; }
|
|
21992
|
+
}
|
|
21993
|
+
|
|
21298
21994
|
class WidgetQuest extends WidgetBase {
|
|
21299
21995
|
static DEFAULTS = {
|
|
21300
21996
|
slide: null,
|
|
@@ -24230,48 +24926,130 @@ var ResultDisplayFormat;
|
|
|
24230
24926
|
ResultDisplayFormat["Percentage"] = "percentage";
|
|
24231
24927
|
})(ResultDisplayFormat || (ResultDisplayFormat = {}));
|
|
24232
24928
|
|
|
24233
|
-
|
|
24234
|
-
|
|
24235
|
-
|
|
24236
|
-
|
|
24237
|
-
|
|
24238
|
-
|
|
24929
|
+
/**
|
|
24930
|
+
* Tuning for {@link ReactionFloatEffect}: layout, motion, and styling.
|
|
24931
|
+
*
|
|
24932
|
+
* - `minScale` + `scaleVariations` → CSS `--scale` (emoji size). The largest variation is used with `minScale` to size slots in `getSlotWidth`.
|
|
24933
|
+
* - `maxDelay` → upper bound (ms) for random stagger before each spawn in `animate`.
|
|
24934
|
+
* - `yThreshold` → fraction of `container.clientHeight` for `--y-threshold` (vertical float threshold).
|
|
24935
|
+
* - `jitterFactor` → scales horizontal random offset in `getStartX` (jitter is forced to 0 on the first and last column).
|
|
24936
|
+
* - `fastReactionProbability` → chance to pick the fast branch in `getDuration`.
|
|
24937
|
+
* - `fastMinDuration` / `fastExtraDuration` → fast path duration is `fastMinDuration + random * fastExtraDuration` (ms).
|
|
24938
|
+
* - `slowMinDuration` / `slowExtraDuration` → slow path duration (same pattern, ms).
|
|
24939
|
+
* - `reactionClassName` → class on the measurement probe and on each reaction node (metrics + CSS).
|
|
24940
|
+
*/
|
|
24941
|
+
const config = {
|
|
24942
|
+
minScale: 0.75,
|
|
24943
|
+
scaleVariations: [0, 0.12, 0.25],
|
|
24944
|
+
maxDelay: 400,
|
|
24945
|
+
yThreshold: 0.6,
|
|
24946
|
+
jitterFactor: 0.6,
|
|
24947
|
+
fastReactionProbability: 0.6,
|
|
24948
|
+
fastMinDuration: 910,
|
|
24949
|
+
fastExtraDuration: 325,
|
|
24950
|
+
slowMinDuration: 1560,
|
|
24951
|
+
slowExtraDuration: 520,
|
|
24952
|
+
reactionClassName: "narrative-element-reaction--float-effect-v2",
|
|
24953
|
+
};
|
|
24239
24954
|
class ReactionFloatEffect {
|
|
24240
24955
|
container;
|
|
24241
24956
|
emoji;
|
|
24242
24957
|
count;
|
|
24958
|
+
reactionWidth;
|
|
24959
|
+
slotWidth;
|
|
24960
|
+
slotsCount;
|
|
24243
24961
|
constructor(options) {
|
|
24244
24962
|
this.container = options.container;
|
|
24245
24963
|
this.emoji = options.emoji;
|
|
24246
24964
|
this.count = options.count ?? 6;
|
|
24965
|
+
this.reactionWidth = this.getReactionWidth();
|
|
24966
|
+
this.slotWidth = this.getSlotWidth();
|
|
24967
|
+
this.slotsCount = this.getSlotsCount(this.slotWidth);
|
|
24968
|
+
}
|
|
24969
|
+
getReactionWidth() {
|
|
24970
|
+
const el = document.createElement("span");
|
|
24971
|
+
el.textContent = this.emoji;
|
|
24972
|
+
el.style.position = "absolute";
|
|
24973
|
+
el.style.visibility = "hidden";
|
|
24974
|
+
el.classList.add(config.reactionClassName);
|
|
24975
|
+
this.container.appendChild(el);
|
|
24976
|
+
const rect = el.getBoundingClientRect();
|
|
24977
|
+
el.remove();
|
|
24978
|
+
return rect.width;
|
|
24979
|
+
}
|
|
24980
|
+
getSlotsCount(slotWidth) {
|
|
24981
|
+
return Math.max(1, Math.floor(this.container.clientWidth / slotWidth));
|
|
24982
|
+
}
|
|
24983
|
+
getSlotWidth() {
|
|
24984
|
+
const scale = config.minScale + Math.max(...config.scaleVariations);
|
|
24985
|
+
return this.reactionWidth * scale;
|
|
24247
24986
|
}
|
|
24248
24987
|
animate() {
|
|
24249
24988
|
for (let i = 0; i < this.count; i++) {
|
|
24250
|
-
const delay = Math.random() *
|
|
24989
|
+
const delay = Math.random() * config.maxDelay;
|
|
24990
|
+
const slotIndex = i % this.slotsCount;
|
|
24251
24991
|
setTimeout(() => {
|
|
24252
|
-
this.spawnReaction();
|
|
24992
|
+
this.spawnReaction(slotIndex);
|
|
24253
24993
|
}, delay);
|
|
24254
24994
|
}
|
|
24255
24995
|
}
|
|
24256
|
-
spawnReaction() {
|
|
24257
|
-
const
|
|
24258
|
-
|
|
24259
|
-
|
|
24260
|
-
|
|
24261
|
-
|
|
24262
|
-
|
|
24263
|
-
|
|
24264
|
-
|
|
24265
|
-
const
|
|
24266
|
-
|
|
24267
|
-
|
|
24268
|
-
|
|
24269
|
-
|
|
24270
|
-
|
|
24271
|
-
this.
|
|
24996
|
+
spawnReaction(slotIndex) {
|
|
24997
|
+
const reaction = this.createReaction();
|
|
24998
|
+
const options = this.getOptions(slotIndex);
|
|
24999
|
+
this.setStyle(reaction, options);
|
|
25000
|
+
this.container.appendChild(reaction);
|
|
25001
|
+
this.removeReactionWithDelay(reaction, options.duration);
|
|
25002
|
+
return reaction;
|
|
25003
|
+
}
|
|
25004
|
+
createReaction() {
|
|
25005
|
+
const reaction = document.createElement("span");
|
|
25006
|
+
reaction.className = config.reactionClassName;
|
|
25007
|
+
reaction.textContent = this.emoji;
|
|
25008
|
+
return reaction;
|
|
25009
|
+
}
|
|
25010
|
+
getOptions(slotIndex) {
|
|
25011
|
+
const thresholdY = this.getThresholdY();
|
|
25012
|
+
const scale = this.getScale();
|
|
25013
|
+
const startX = this.getStartX(slotIndex);
|
|
25014
|
+
const duration = this.getDuration();
|
|
25015
|
+
return { thresholdY, scale, startX, duration };
|
|
25016
|
+
}
|
|
25017
|
+
getStartX(slotIndex) {
|
|
25018
|
+
const slotsRowCenteringOffset = (this.container.clientWidth - this.slotsCount * this.slotWidth) / 2;
|
|
25019
|
+
const baseX = slotIndex * this.slotWidth + slotsRowCenteringOffset + this.slotWidth / 2;
|
|
25020
|
+
return baseX + this.getJitter(slotIndex);
|
|
25021
|
+
}
|
|
25022
|
+
getJitter(slotIndex) {
|
|
25023
|
+
const jitter = (Math.random() - 0.5) * this.slotWidth * config.jitterFactor;
|
|
25024
|
+
if (slotIndex === 0 && jitter < 0) {
|
|
25025
|
+
return -jitter;
|
|
25026
|
+
}
|
|
25027
|
+
if (slotIndex === this.slotsCount - 1 && jitter > 0) {
|
|
25028
|
+
return -jitter;
|
|
25029
|
+
}
|
|
25030
|
+
return jitter;
|
|
25031
|
+
}
|
|
25032
|
+
getDuration() {
|
|
25033
|
+
const isFast = Math.random() < config.fastReactionProbability;
|
|
25034
|
+
return isFast ? config.fastMinDuration + Math.random() * config.fastExtraDuration : config.slowMinDuration + Math.random() * config.slowExtraDuration;
|
|
25035
|
+
}
|
|
25036
|
+
getThresholdY() {
|
|
25037
|
+
return config.yThreshold * this.container.clientHeight;
|
|
25038
|
+
}
|
|
25039
|
+
getScale() {
|
|
25040
|
+
const scaleVariation = config.scaleVariations[Math.floor(Math.random() * (config.scaleVariations.length - 1))];
|
|
25041
|
+
return config.minScale + scaleVariation;
|
|
25042
|
+
}
|
|
25043
|
+
setStyle(reaction, options) {
|
|
25044
|
+
reaction.style.left = `${options.startX}px`;
|
|
25045
|
+
reaction.style.setProperty("--y-threshold", `${options.thresholdY}px`);
|
|
25046
|
+
reaction.style.setProperty("--duration", `${options.duration}ms`);
|
|
25047
|
+
reaction.style.setProperty("--scale", `${options.scale}`);
|
|
25048
|
+
}
|
|
25049
|
+
removeReactionWithDelay(reaction, delay) {
|
|
24272
25050
|
setTimeout(() => {
|
|
24273
|
-
|
|
24274
|
-
},
|
|
25051
|
+
reaction.remove();
|
|
25052
|
+
}, delay);
|
|
24275
25053
|
}
|
|
24276
25054
|
static get [Symbol.for("___CTOR_ARGS___")]() { return [`ReactionFloatEffectOptions`]; }
|
|
24277
25055
|
}
|
|
@@ -24338,7 +25116,7 @@ class WidgetReactions extends WidgetBase {
|
|
|
24338
25116
|
const geometryParent = this.element.closest(".narrative-slide-elements");
|
|
24339
25117
|
if (!geometryParent)
|
|
24340
25118
|
return;
|
|
24341
|
-
new ReactionFloatEffect({ container: geometryParent, emoji, count:
|
|
25119
|
+
new ReactionFloatEffect({ container: geometryParent, emoji, count: 20 /* fontSize: getComputedStyle(this.reactions[0]).fontSize */ }).animate();
|
|
24342
25120
|
}
|
|
24343
25121
|
initFromLocalData({ selectedIndex, widgetDoneAt, reaction }) {
|
|
24344
25122
|
try {
|
|
@@ -24580,6 +25358,9 @@ class EsModuleLayoutApi {
|
|
|
24580
25358
|
get widgetProductsApi() {
|
|
24581
25359
|
return WidgetProducts.api;
|
|
24582
25360
|
}
|
|
25361
|
+
get widgetProductCarouselApi() {
|
|
25362
|
+
return WidgetProductCarousel.api;
|
|
25363
|
+
}
|
|
24583
25364
|
get widgetTooltipApi() {
|
|
24584
25365
|
return WidgetTooltip.api;
|
|
24585
25366
|
}
|