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