swiperScrollbar + omnibusTooltip

This commit is contained in:
2025-11-13 12:20:16 +01:00
parent e97fa83e4a
commit 0030ce8928
5 changed files with 371 additions and 95 deletions

255
klasa.js
View File

@@ -8,6 +8,8 @@ const idmHotspotTextObject = {
[<iai:variable vid="Bestseller"/>]: <iai:variable vid="Bestseller"/>,
[<iai:variable vid="Nowość"/>]: <iai:variable vid="Nowość"/>,
[<iai:variable vid="Ilość"/>]: <iai:variable vid="Ilość"/>,
[<iai:variable vid="Najniższa cena"/>]: <iai:variable vid="Najniższa cena"/>,
[<iai:variable vid="Najniższa cena z 30 dni przed obniżką"/>]: <iai:variable vid="Najniższa cena z 30 dni przed obniżką"/>,
[<iai:variable vid="Zwiększ ilość"/>]: <iai:variable vid="Zwiększ ilość"/>,
[<iai:variable vid="Zmniejsz ilość"/>]: <iai:variable vid="Zmniejsz ilość"/>,
[<iai:variable vid="Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki"/>]: <iai:variable vid="Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki"/>,
@@ -278,7 +280,7 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
// Aktywny kod rabatowy
if (app_shop.vars.omnibus?.rebateCodeActivate && sizeData.price?.rebateCodeActive) {
classes.add.push('--omnibus-code');
activeLabel.rebateCodeActive = `<span class="label --code --omnibus">${idmHotspotTextObject["Kod rabatowy"]}</span>`;
activeLabel.rebateCodeActive = `<span class="label --code --omnibus">${omnibusDetailsTxt?.['Kod rabatowy']}</span>`;
classes.remove = classes.remove.filter((item) => item !== '--omnibus-code');
}
// Skrócona wersja omnibusa, gdy aktywny kod rabatowy
@@ -301,19 +303,13 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
}
// label okazja
if ((!higher || newDate) && !activeLabel?.rebateCodeActive) {
activeLabel.bargain = `<span class="label --bargain --omnibus">${idmHotspotTextObject["Okazja"]}</span>`;
activeLabel.bargain = `<span class="label --bargain --omnibus">${omnibusDetailsTxt?.['Okazja']}</span>`;
}
// label promocja
if (Object.keys(activeLabel)?.length === 0) {
activeLabel.bargain = `<span class="label --promo --omnibus">${idmHotspotTextObject["Promocja"]}</span>`;
activeLabel.bargain = `<span class="label --promo --omnibus">${omnibusDetailsTxt?.['Promocja']}</span>`;
}
// // labele zones
// if(productData.zones.find(zone => zone ==="bestseller")) activeLabel.bestseller = `<span class="label --bestseller --omnibus">${idmHotspotTextObject["Bestseller"]}</span>`;
// if(productData.zones.find(zone => zone ==="news")) activeLabel.news = `<span class="label --news --omnibus">${idmHotspotTextObject["Nowość"]}</span>`;
let omnibusPercentSign = '';
if (higher) {
omnibusPercentSign = '-';
@@ -325,7 +321,7 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
price: omnibusPrice,
visible: true,
percent: omnibusPercent,
html: `<span class="omnibus_price__text">${idmHotspotTextObject['Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki']}: </span><del class="omnibus_price__value">${omnibusPrice}</del><span class="price_percent">${omnibusPercent}</span>`,
html: `<span class="omnibus_price__text">${omnibusDetailsTxt['Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki']}: </span><del class="omnibus_price__value">${omnibusPrice}</del><span class="price_percent">${omnibusPercent}</span>`,
};
const max = (maxPrice) ? {
@@ -333,7 +329,7 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
price: maxPrice,
visible: true,
percent: `-${sizeData.price.youSavePercent}%`,
html: `<span class="omnibus_label">${idmHotspotTextObject['Cena regularna']}: </span>
html: `<span class="omnibus_label">${omnibusDetailsTxt['Cena regularna']}: </span>
<del>${maxPrice}</del><span class="price_percent">-${sizeData.price.youSavePercent}%</span>`,
},
} : {};
@@ -343,7 +339,7 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
price: beforeRebatePrice,
visible: !!classes.add.includes('--omnibus-code'),
percent: `-${sizeData.price.beforeRebateDetails?.youSavePercent}%`,
html: `<span class="omnibus_label">${idmHotspotTextObject['Cena bez kodu']}: </span>
html: `<span class="omnibus_label">${omnibusDetailsTxt['Cena bez kodu']}: </span>
<del>${beforeRebatePrice}</del>
<span class="price_percent">-${sizeData.price.beforeRebateDetails?.youSavePercent}%</span>`,
},
@@ -354,12 +350,17 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
date: newDate,
price: maxPrice,
visible: !!classes.add.includes('--omnibus-new-price'),
html: `<span class="omnibus_label">${idmHotspotTextObject['Cena nadchodząca od']} </span>
html: `<span class="omnibus_label">${omnibusDetailsTxt['Cena nadchodząca od']} </span>
<span class="new_price__date">${newDate}: </span>
<span class="new_price__value">${maxPrice}</span>`,
},
} : {};
const omnibusLabel = {
omnibusLabel: {
name: Object.keys(activeLabel)?.[0] || '',
html: activeLabel[Object.keys(activeLabel)?.[0]],
},
};
return {
classes,
@@ -367,7 +368,7 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
...max,
...beforeRebate,
...newPriceEffectiveUntil,
activeLabel,
...omnibusLabel,
};
};
@@ -409,7 +410,9 @@ class IdmHotspot{
lazy: true,
addToBasket: true, // true, false, "range"
swiper: true,
callbackFn: ()=>{}
callbackFn: ()=>{},
swiperScrollbar: false,
omnibusTooltip: false,
}
}
/**
@@ -523,7 +526,7 @@ class IdmHotspot{
*/
markupProduct(prod){
// IDM DO POPRAWKI
const prodExchangedData = app_shop.fn?.idmGetOmnibusDetails({productData: prod});
const prodExchangedData = app_shop?.fn?.getOmnibusDetails?.({productData: prod}) || app_shop.fn?.idmGetOmnibusDetails({productData: prod});
// markup pojedynczego produktu
let singleMarkup = "";
@@ -611,11 +614,39 @@ class IdmHotspot{
${price.value === 0 ? `<a class="price --phone" href="/contact.php" tabindex="-1" title="${idmHotspotTextObject["Kliknij, by przejść do formularza kontaktu"]}">${idmHotspotTextObject["Cena na telefon"]}</a>` : ""}
${prodExchangedData?.beforeRebate?.visible ? `<span class="price --before-rebate">${prodExchangedData?.beforeRebate?.html}</span>` : ""}
${prodExchangedData?.newPriceEffectiveUntil?.visible ? `<span class="price --new-price new_price">${prodExchangedData?.newPriceEffectiveUntil?.html}</span>` : ""}
${prodExchangedData?.omnibus?.visible ? `<span class="price --omnibus omnibus_price">${prodExchangedData?.omnibus?.html}</span>` : ""}
${this.markupOmnibus(prodExchangedData?.omnibus)}
${prodExchangedData?.max?.visible ? `<span class="price --max">${prodExchangedData?.max?.html}</span>` : ""}
`;
}
/**
* Tworzy znacznik HTML dla omnibusa.
*
* @param {object} omnibus - dane omnibusa
* @param {string} omnibus.html - kod html zwykłego omnibusa
* @param {string} omnibus.percent - % przeceny omnibusa
* @param {string} omnibus.price - cena omnibusa
* @param {boolean} omnibus.visible - czy omnibus jest widoczny
* @returns {string} Zwraca HTML dla sekcji omnibusa lub pusty string, jeśli niewidoczny.
*/
markupOmnibus(omnibus){
if(!omnibus?.visible) return "";
if(!this.options.omnibusTooltip) return `<span class="price --omnibus omnibus_price">${omnibus?.html}</span>`;
else return `
<span class="price --omnibus omnibus_price">
<span class="omnibus_price__text">${idmHotspotTextObject[<iai:variable vid="Najniższa cena"/>]}:</span>
<del class="omnibus_price__value">${omnibus.price}</del>
<span class="price_percent">${omnibus.percent}</span>
<span class="idm_tooltip">
<span class="idm_tooltip__info_icon"></span>
<p class="idm_tooltip__content --one-line">${idmHotspotTextObject[<iai:variable vid="Najniższa cena z 30 dni przed obniżką"/>]}</p>
</span>
</span>
`;
}
markupAddToBasket(prod){
let markup = "";
if(!this.options.addToBasket) return markup;
@@ -847,7 +878,7 @@ class IdmHotspot{
if (selector) adjustAllHeights(selector)
if (selectors?.length) selectors.forEach(adjustAllHeights)
}
// ========================================================
// ========================================================
// INICJALIZACJA
// ========================================================
@@ -929,6 +960,8 @@ class IdmHotspot{
options: this.options.swiper,
});
await selectedSwiper.init();
if(this.options.swiperScrollbar) new IdmSwiperProgress(selectedSwiper, `#${this.id} .swiper`);
}
}catch(err){
console.error(idmHotspotTextObject["Wystąpił błąd z inicjalizacją. Proszę odśwież stronę"], err);
@@ -945,7 +978,7 @@ class IdmHotspot{
}
initSingleEvent(prodEl){
// DODAWANIE DO KOSZYKA
if(this?.options?.addToBasket){
if(this.options?.addToBasket){
const addToBasketEl = prodEl.querySelector("form.add_to_basket");
if(!addToBasketEl) return;
addToBasketEl.addEventListener("submit", this.handleAddToBasket);
@@ -956,6 +989,14 @@ class IdmHotspot{
addToBasketEl.querySelector(".idm-products-banner__qty-input")?.addEventListener("input",this.handleQuantityInputChange);
}
}
// Tooltip
if(this.options?.omnibusTooltip){
const tooltipEl = prodEl.querySelector(".idm_tooltip");
tooltipEl.addEventListener("click", ()=>{
this.showTooltip(tooltipEl);
})
}
}
/**
@@ -981,6 +1022,178 @@ class IdmHotspot{
}
}
/*
==============================================================
SWIPER PASEK
==============================================================
*/
class IdmSwiperProgress {
constructor(swiper, selector) {
this.swiper = swiper?.slider?.slider ?? swiper?.slider ?? swiper;
this.selector = selector;
this.scrollbarEl = null;
this.progressEl = null;
this.isDragging = false;
this.init();
}
init() {
const el = document.querySelector(this.selector);
if (!el || el.querySelector(".idm-scrollbar")) return;
el.insertAdjacentHTML(
"beforeend",
`<div class="idm-scrollbar swiper-scrollbar"><div class="idm-progress"></div></div>`
);
this.scrollbarEl = el.querySelector(".idm-scrollbar");
this.progressEl = this.scrollbarEl.querySelector(".idm-progress");
this.updateBarWidth();
this.addDragTracking();
this.swiper.on("progress", () => this.updateProgress());
this.swiper.on("breakpoint", () => {this.updateBarWidth()});
}
updateBarWidth() {
const { slidesPerGroup, slidesPerView } = this.swiper.params;
const totalSlides = this.swiper.slides.length;
const progressWidth = 100 / (((totalSlides - slidesPerView) / slidesPerGroup) + 1);
this.progressEl.style.width = `${progressWidth}%`;
if (progressWidth >= 100 || progressWidth <= 0) this.scrollbarEl.style.display = "none";
else this.scrollbarEl.style.display = "";
}
updateProgress() {
const progress = this.swiper.progress;
const { slidesPerGroup, slidesPerView } = this.swiper.params;
const totalSlides = this.swiper.slides.length;
const progressWidth = 100 / (((totalSlides - slidesPerView) / slidesPerGroup) + 1);
const newLeft = (100 - progressWidth) * progress;
this.progressEl.style.left = `${Math.min(
100 - progressWidth,
Math.max(0, newLeft)
)}%`;
}
addDragTracking() {
const handle = this.progressEl;
let grabOffset = 0;
let scrollbarWidth = 0;
let handleWidth = 0;
const startDrag = (e) => {
this.isDragging = true;
this.scrollbarEl.classList.add("--drag-start");
const rect = this.scrollbarEl.getBoundingClientRect();
const handleRect = handle.getBoundingClientRect();
scrollbarWidth = rect.width;
handleWidth = handleRect.width;
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
grabOffset = clientX - handleRect.left;
document.addEventListener("mousemove", handleDrag);
document.addEventListener("mouseup", stopDrag);
document.addEventListener("touchmove", handleDrag);
document.addEventListener("touchend", stopDrag);
};
const handleDrag = (e) => {
if (!this.isDragging) return;
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
const rect = this.scrollbarEl.getBoundingClientRect();
let newLeftPx = clientX - rect.left - grabOffset;
const maxLeft = scrollbarWidth - handleWidth;
newLeftPx = Math.max(0, Math.min(maxLeft, newLeftPx));
const progress = newLeftPx / maxLeft;
this.swiper.setProgress(progress, 0);
};
const stopDrag = () => {
if (!this.isDragging) return;
this.isDragging = false;
document.removeEventListener("mousemove", handleDrag);
document.removeEventListener("mouseup", stopDrag);
document.removeEventListener("touchmove", handleDrag);
document.removeEventListener("touchend", stopDrag);
this.scrollbarEl.classList.remove("--drag-start");
this.swiper.slideReset(400);
};
handle.addEventListener("mousedown", startDrag);
handle.addEventListener("touchstart", startDrag);
}
}
// ========================================================
// TOOLTIP
// ========================================================
function idmShowTooltip(tooltipEl){
const tooltipContentEl = tooltipEl.querySelector(".idm_tooltip__content");
if(!tooltipContentEl) return;
tooltipContentEl.classList.add("--visible");
// Logika pokazywania się i chowania tooltipa
let timeoutVar;
function onMouseLeave() {
timeoutVar = idmHideTooltipTimer(tooltipEl);
}
function onMouseEnter() {
clearTimeout(timeoutVar);
}
function onScroll() {
idmHideTooltip(tooltipEl);
}
// Store references for later removal
tooltipEl._onMouseLeave = onMouseLeave;
tooltipEl._onMouseEnter = onMouseEnter;
tooltipEl._onScroll = onScroll;
tooltipEl.addEventListener("mouseleave", onMouseLeave);
tooltipEl.addEventListener("mouseenter", onMouseEnter);
document.addEventListener("scroll", onScroll);
}
function idmHideTooltipTimer(tooltipEl){
return setTimeout(() => idmHideTooltip(tooltipEl), 1500);
}
function idmHideTooltip(tooltipEl){
const tooltipContentEl = tooltipEl.querySelector(".idm_tooltip__content");
if (!tooltipContentEl) return;
tooltipContentEl.classList.remove("--visible");
tooltipEl.removeEventListener("mouseleave", tooltipEl._onMouseLeave);
tooltipEl.removeEventListener("mouseenter", tooltipEl._onMouseEnter);
document.removeEventListener("scroll", tooltipEl._onScroll);
delete tooltipEl._onMouseLeave;
delete tooltipEl._onMouseEnter;
delete tooltipEl._onScroll;
}
document.addEventListener("DOMContentLoaded", ()=>{
document.body.addEventListener("click", e=>{
const tooltipEl = e.target.closest(".idm_tooltip");
if(!e.target.closest(".idm_tooltip__info_icon") || !tooltipEl) return;
e.preventDefault();
idmShowTooltip(tooltipEl);
});
});
// new IdmHotspot({
// id: "idmTestHotspot1",
// title: "tescik",
@@ -1031,8 +1244,6 @@ async function idmPrepareHotspotObject(selectedContainerEl){
if(selectedContainerEl?.dataset?.lazy) idmHotspotObj.options = {lazy: selectedContainerEl?.dataset?.lazy === "true" ? true : false};
new IdmHotspot(idmHotspotObj)
}