diff --git a/README.md b/README.md
index 41ab67a..3142ea0 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@ Funkcje js składające się na customowe ramki rekomendacji
## UWAGI PRZEDWDROŻENIOWE ##
- kod zawiera **app_shop.fn.idmGetOmnibusDetails** który jest przerobionym kodem idosella app_shop.fn.getOmnibusDetails używanym w zwykłych ramkach rekomendacji
+- kod zawiera FrontendComponents/helper-functions/tooltip i FrontendComponents/helper-functions/swiper-scrollbar
+- kod używa naszego idmSetHeight, ale jest to wstawione jako metoda klasowa, nie jako coś osobnego.
### Pliki ###
- **style.css** - style wstawiane do css
@@ -54,8 +56,9 @@ Warto gdzieś później zapisać nową nazwę klasy np w opisie komponentu, albo
- Wystąpił błąd z inicjalizacją. Proszę odśwież stronę
- Nie znaleziono kontenera
- Nie znaleziono metody graphql
+- Najniższa cena
-#### Przykład ####
+#### Przykłady UŻYCIA ####
##### Jedna ramka - obiekt ######
```
new IdmHotspot({
@@ -100,6 +103,8 @@ new IdmHotspot({
* - false = nieaktywny
* - object = konfiguracja Swiper
* @property {Function} options.callbackFn - Funkcja callback która dzieje się po wywołaniu wszystkiego włącznie ze swiperem
+ * @property {boolean} options.swiperScrollbar - Czy włączać scrollbar w swiperze - DO DZIAŁANIA WYMAGA WŁĄCZONEGO SWIPERA
+ * @property {boolean} options.omnibusTooltip - Czy wyświetlać omnibusa w formie tooltip
*
* @type {Hotspot[]}
*/
@@ -125,6 +130,8 @@ new IdmHotspot({
idmInsertHotspotElement(document.getElementByid("idmBlogHotspot1"));
```
+
+Żeby zmienić resztę ustawień trzeba zmieniać defaultowe ustawienia!
#### Wszystkie możliwe dane HTML ####
```
/**
diff --git a/klasa.js b/klasa.js
index 65603d6..90c5b20 100644
--- a/klasa.js
+++ b/klasa.js
@@ -8,6 +8,8 @@ const idmHotspotTextObject = {
[]: ,
[]: ,
[]: ,
+ []: ,
+ []: ,
[]: ,
[]: ,
[]: ,
@@ -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 = `${idmHotspotTextObject["Kod rabatowy"]}`;
+ activeLabel.rebateCodeActive = `${omnibusDetailsTxt?.['Kod rabatowy']}`;
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 = `${idmHotspotTextObject["Okazja"]}`;
+ activeLabel.bargain = `${omnibusDetailsTxt?.['Okazja']}`;
}
// label promocja
if (Object.keys(activeLabel)?.length === 0) {
- activeLabel.bargain = `${idmHotspotTextObject["Promocja"]}`;
+ activeLabel.bargain = `${omnibusDetailsTxt?.['Promocja']}`;
}
- // // labele zones
- // if(productData.zones.find(zone => zone ==="bestseller")) activeLabel.bestseller = `${idmHotspotTextObject["Bestseller"]}`;
- // if(productData.zones.find(zone => zone ==="news")) activeLabel.news = `${idmHotspotTextObject["Nowość"]}`;
-
-
-
let omnibusPercentSign = '';
if (higher) {
omnibusPercentSign = '-';
@@ -325,7 +321,7 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
price: omnibusPrice,
visible: true,
percent: omnibusPercent,
- html: `${idmHotspotTextObject['Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki']}: ${omnibusPrice}${omnibusPercent}`,
+ html: `${omnibusDetailsTxt['Najniższa cena produktu w okresie 30 dni przed wprowadzeniem obniżki']}: ${omnibusPrice}${omnibusPercent}`,
};
const max = (maxPrice) ? {
@@ -333,7 +329,7 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
price: maxPrice,
visible: true,
percent: `-${sizeData.price.youSavePercent}%`,
- html: `${idmHotspotTextObject['Cena regularna']}:
+ html: `${omnibusDetailsTxt['Cena regularna']}:
${maxPrice}-${sizeData.price.youSavePercent}%`,
},
} : {};
@@ -343,7 +339,7 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
price: beforeRebatePrice,
visible: !!classes.add.includes('--omnibus-code'),
percent: `-${sizeData.price.beforeRebateDetails?.youSavePercent}%`,
- html: `${idmHotspotTextObject['Cena bez kodu']}:
+ html: `${omnibusDetailsTxt['Cena bez kodu']}:
${beforeRebatePrice}
-${sizeData.price.beforeRebateDetails?.youSavePercent}%`,
},
@@ -354,12 +350,17 @@ app_shop.fn.idmGetOmnibusDetails = (options) => {
date: newDate,
price: maxPrice,
visible: !!classes.add.includes('--omnibus-new-price'),
- html: `${idmHotspotTextObject['Cena nadchodząca od']}
+ html: `${omnibusDetailsTxt['Cena nadchodząca od']}
${newDate}:
${maxPrice}`,
},
} : {};
-
+ 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 ? `${idmHotspotTextObject["Cena na telefon"]}` : ""}
${prodExchangedData?.beforeRebate?.visible ? `${prodExchangedData?.beforeRebate?.html}` : ""}
${prodExchangedData?.newPriceEffectiveUntil?.visible ? `${prodExchangedData?.newPriceEffectiveUntil?.html}` : ""}
- ${prodExchangedData?.omnibus?.visible ? `${prodExchangedData?.omnibus?.html}` : ""}
+ ${this.markupOmnibus(prodExchangedData?.omnibus)}
${prodExchangedData?.max?.visible ? `${prodExchangedData?.max?.html}` : ""}
`;
}
+ /**
+ * 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 `${omnibus?.html}`;
+ else return `
+
+ ${idmHotspotTextObject[]}:
+ ${omnibus.price}
+ ${omnibus.percent}
+
+
+ ${idmHotspotTextObject[]}
+
+
+ `;
+ }
+
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",
+ `
`
+ );
+
+ 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)
}
diff --git a/ramka.txt b/ramka.txt
index 188ec8c..44df444 100644
--- a/ramka.txt
+++ b/ramka.txt
@@ -1,12 +1,11 @@
1. Ramka
-
-- ulubione? + porównywarka? (wymagają zmiany w komponencie idosella)
-- zakres cen?????????????
- wybór rozmiaru/wersji??
-- Wybór kolorystyczny
+- ulubione? + porównywarka? (wymagają zmiany w komponencie idosella)
+
+- zakres cen?????????????
+- Wybór kolorystyczny???
- AAAAA - banner na hotspocie
-- Pasek jak na Pasiastym Parzystnokopytnym Kosmetyku
@@ -15,14 +14,4 @@
Stara ramka
- getProductXML=t
-- slick
-
-
-bramka z hotspots jeszcze nie działa bad request
-
-
-
-Get-Content 1graphQL.js,2funkcje.js,3markup.js,4init.js,5ainsertHotspotHTML.js,5binsertHotspotObject.js | Set-Content bundle.js
-
-
-Get-Content sklad/1graphQL.js,sklad/2funkcje.js,sklad/3markup.js,sklad/4init.js,sklad/5ainsertHotspotHTML.js,sklad/5binsertHotspotObject.js | Set-Content bundle.js
\ No newline at end of file
+- slick
\ No newline at end of file
diff --git a/style.css b/style.css
deleted file mode 100644
index 5a1beea..0000000
--- a/style.css
+++ /dev/null
@@ -1,56 +0,0 @@
-.idm__hotspot .add_to_basket{
- display: flex;
-}
-.idm__hotspot .add_to_basket.--range{
- flex-direction: column;
-}
-.idm__hotspot .idm-products-banner__qty{
- display: flex;
- justify-content: space-between;
- width: 100%;
- align-items: center;
- gap: 1rem;
-}
-.idm__hotspot .idm-products-banner__qty-input{
- height: 3rem;
- text-align: center;
- border: 1px solid #ccc;
- width: 60%;
- max-width: unset;
- min-width: unset;
-}
-.idm__hotspot .idm-products-banner__qty button{
- background: #000;
- height: 3rem;
- width: 3rem;
- color: #fff;
- border-radius: 0.5rem;
- min-width: 3rem;
-}
- @keyframes idm-skeleton-loading {
- to {
- background-position-x: -200%;
- }
- }
-.idm__hotspot.idm-loading{
- position: relative;
- overflow: hidden;
-
- transition: none;
- border-radius: 8px;
- background: #eee;
- background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
- background-size: 200% 100%;
- animation: 1.5s idm-skeleton-loading linear infinite;
- width: 100%;
- height: 50rem;
-}
-.idm__hotspot.idm-loading .hotspot{
- opacity: 0;
-}
-@media (max-width: 756px) {
- .idm__hotspot.idm-loading{
- width: 100%;
- height: 50rem;
- }
-}
\ No newline at end of file
diff --git a/style.less b/style.less
new file mode 100644
index 0000000..01b9231
--- /dev/null
+++ b/style.less
@@ -0,0 +1,125 @@
+.idm__hotspot .add_to_basket{
+ display: flex;
+}
+.idm__hotspot .add_to_basket.--range{
+ flex-direction: column;
+}
+.idm__hotspot .idm-products-banner__qty{
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+ align-items: center;
+ gap: 1rem;
+}
+.idm__hotspot .idm-products-banner__qty-input{
+ height: 3rem;
+ text-align: center;
+ border: 1px solid #ccc;
+ width: 60%;
+ max-width: unset;
+ min-width: unset;
+}
+.idm__hotspot .idm-products-banner__qty button{
+ background: #000;
+ height: 3rem;
+ width: 3rem;
+ color: #fff;
+ border-radius: 0.5rem;
+ min-width: 3rem;
+}
+ @keyframes idm-skeleton-loading {
+ to {
+ background-position-x: -200%;
+ }
+ }
+.idm__hotspot.idm-loading{
+ position: relative;
+ overflow: hidden;
+
+ transition: none;
+ border-radius: 8px;
+ background: #eee;
+ background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
+ background-size: 200% 100%;
+ animation: 1.5s idm-skeleton-loading linear infinite;
+ width: 100%;
+ height: 50rem;
+}
+.idm__hotspot.idm-loading .hotspot{
+ opacity: 0;
+}
+@media (max-width: 756px) {
+ .idm__hotspot.idm-loading{
+ width: 100%;
+ height: 50rem;
+ }
+}
+
+/* SWIPER PROGRESS */
+.idm-scrollbar{
+ flex: 1;
+ height: 2px!important;
+ position: relative!important;
+ margin-top: 2rem;
+ cursor: grab;
+}
+.idm-scrollbar.--drag-start .idm-progress{
+ transition: none;
+}
+.idm-progress{
+ position: absolute;
+ z-index: 10;
+ height: calc(100% + 2px);
+ background-color: #0d0d0d;
+ left: 0;
+ top: -1px;
+ transition: all 0.3s;
+ border-radius: 5px;
+}
+
+
+
+/* tooltip */
+.idm_tooltip{
+ --tooltip-background: #111;
+ --tooltip-border: #999;
+ --tooltip-color: #fff;
+
+ position: relative;
+ &__info_icon{
+ color: var(--tooltip-border);
+ display: inline-block;
+ &:before{
+ content: "";
+ display: block;
+ width: 1.2rem;
+ height: 1.2rem;
+ margin-left: 0.5rem;
+ cursor: pointer;
+ vertical-align: bottom;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' class='idm_tooltip__info_icon' viewBox='0 0 16 16'%3E%3Cpath d='M8,0a8,8,0,1,0,8,8A8,8,0,0,0,8,0ZM8,14.667A6.667,6.667,0,1,1,14.667,8,6.667,6.667,0,0,1,8,14.667Z' fill='currentColor'%3E%3C/path%3E%3Cpath d='M11.333,10h-.667a.667.667,0,1,0,0,1.333h.667v4a.667.667,0,0,0,1.333,0v-4A1.333,1.333,0,0,0,11.333,10Z' transform='translate(-3.333 -3.333)' fill='currentColor'%3E%3C/path%3E%3Ccircle cx='1' cy='1' r='1' transform='translate(7 3.333)' fill='currentColor'%3E%3C/circle%3E%3C/svg%3E");
+ background-size: cover;
+ }
+ }
+ &__content{
+ position: absolute;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.2s;
+ border-radius: 5px;
+ background: var(--tooltip-background);
+ color: var(--tooltip-color);
+ border: 1px solid var(--tooltip-border);
+ bottom: 150%;
+ padding: 0.5rem 1rem;
+ @media @tablet{
+ &.--one-line{
+ white-space: nowrap;
+ }
+ }
+ &.--visible{
+ opacity: 1;
+ pointer-events: auto;
+ }
+ }
+}