diff --git a/README.md b/README.md
index fc8383a..c78c8ff 100644
--- a/README.md
+++ b/README.md
@@ -93,6 +93,7 @@ new IdmHotspot({
* @property {object} source - Dane źródłowe dla hotspotu.
+ * @property {string} [source.link] - link do listingu z którego mają być pobierane produkty (NIEZALECANE)
* @property {string} [source.hotspotType] - Typ hotspotu (np. "promotion").
* @property {number[]} [source.productsId] - Tablica ID produktów.
* @property {number} [source.productsMenu] - Identyfikator menu produktów.
@@ -162,6 +163,7 @@ new IdmHotspot({
* @property {string} id - Identyfikator elementu (np. "idmBlogHotspot1").
* @property {string} class - Klasy CSS używane do stylowania.
*
+ * @attribute {string} data-link - link do listingu z którego mają być pobierane produkty (NIEZALECANE)
* @attribute {string} data-hotspots-type - Typ hotspotu (np. "promotion").
* @attribute {number[]} data-products-id - Lista ID produktów (rozdzielona przecinkami).
* @attribute {number} data-products-menu - Identyfikator menu produktów.
diff --git a/klasa.js b/klasa.js
index 669d930..5f8db15 100644
--- a/klasa.js
+++ b/klasa.js
@@ -438,6 +438,13 @@ class IdmHotspot{
// DOMYŚLNE OPCJE HOTSPOTA
// ============================
static idmDefaultHotspotOptions = {
+ cssVariables: {
+ version: {
+ columnDesktop: 5,
+ columnTablet: 3,
+ columnMobile: 4,
+ }
+ },
options: {
lazy: true,
devMode: false,
@@ -467,7 +474,7 @@ class IdmHotspot{
* Konstruktor
* @param {object} object - Dane konfiguracyjne hotspotu
*/
- constructor({id, title, classes, placement, source, query, options = {}, hotspotEl, products}){
+ constructor({id, title, classes, placement, source, query, options = {}, hotspotEl, products, cssVariables}){
this.id = id || "";
this.title = title || "";
this.classes = classes || "";
@@ -484,6 +491,10 @@ class IdmHotspot{
...IdmHotspot.idmDefaultHotspotOptions.options,
...options,
};
+ this.cssVariables = {
+ ...IdmHotspot.idmDefaultHotspotOptions.cssVariables,
+ ...cssVariables,
+ };
//
// this.hotspots = {};
@@ -568,15 +579,21 @@ class IdmHotspot{
async getHotspotData(){
if(this.products) return;
try{
- const res = await fetch(`/graphql/v1/`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: this.query.graphFn(this.query.string),
- });
- const data = await res.json();
- const products = data?.data?.[this.query.graphFn === IDM_HOTSPOTS_GQL ? "hotspots" : "products"]?.products;
+ let products;
+ if(this.source?.link){
+ products = await this.xmlGetData();
+ }else{
+ const res = await fetch(`/graphql/v1/`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: this.query.graphFn(this.query.string),
+ });
+ const data = await res.json();
+ products = data?.data?.[this.query.graphFn === IDM_HOTSPOTS_GQL ? "hotspots" : "products"]?.products;
+ }
+
if(!products || !products.length) throw new Error(idmHotspotTextObject["Nie znaleziono produktów"]);
this.products = products;
@@ -671,7 +688,14 @@ class IdmHotspot{
markupVersions(prod){
if(!this.options?.selectVersion || !prod.group?.versions || prod.group?.versions?.length === 1 ) return "";
- const MAX_VERION_AMOUNT = 5;
+ let MAX_VERSION_AMOUNT = 5;
+ if(app_shop.vars.view > 2){
+ MAX_VERSION_AMOUNT = this.cssVariables.version.columnDesktop;
+ }else if(app_shop.vars.view === 2){
+ MAX_VERSION_AMOUNT = this.cssVariables.version.columnTablet;
+ }else if(app_shop.vars.view === 1){
+ MAX_VERSION_AMOUNT = this.cssVariables.version.columnMobile;
+ }
const sortedVersions = prod.group.versions.sort(function (a, b) {
if (a.name < b.name) {
@@ -686,9 +710,9 @@ class IdmHotspot{
return `
${sortedVersions.reduce((acc, val, index)=>{
- if(index + 1 > MAX_VERION_AMOUNT) return acc;
+ if(index + 1 > MAX_VERSION_AMOUNT) return acc;
- if(index + 1 === MAX_VERION_AMOUNT && prod.group.versions.length > MAX_VERION_AMOUNT) return acc + `
+${(prod.group.versions.length - MAX_VERION_AMOUNT) + 1}`;
+ if(index + 1 === MAX_VERSION_AMOUNT && prod.group.versions.length > MAX_VERSION_AMOUNT) return acc + `
+${(prod.group.versions.length - MAX_VERSION_AMOUNT) + 1}`;
return acc + `

`;
},"")}
@@ -1112,6 +1136,24 @@ class IdmHotspot{
observer.observe(this.hotspotEl);
}
+ // ========================================================
+ // USTAWIANIE ZMIENNYCH CSS DLA RAMKI
+ // ========================================================
+ cssSetAllVariables(){
+ this.cssVariableVersionColumnCount();
+ }
+
+ cssVariableVersionColumnCount(){
+ this.cssSetVariable("--version-desktop-columns", this.cssVariables?.version?.columnDesktop || 5)
+ this.cssSetVariable("--version-tablet-columns", this.cssVariables?.version?.columnTablet || 3)
+ this.cssSetVariable("--version-mobile-columns", this.cssVariables?.version?.columnMobile || 4)
+ }
+
+
+ cssSetVariable(name, value){
+ this.hotspotEl.style.setProperty(name, value);
+ }
+
// ========================================================
// FUNKCJE POMOCNICZE
// ========================================================
@@ -1196,6 +1238,338 @@ class IdmHotspot{
prodEl.classList.remove("--loading");
}
}
+
+ // ========================================================
+ // XML to GraphQL
+ // ========================================================
+ xmlGetGrossNetPrices({priceNode, name}){
+ return {
+ gross: {
+ value: priceNode?.getAttribute(`${name}`) !== null ? +priceNode.getAttribute(`${name}`) : undefined,
+ formatted: priceNode?.getAttribute(`${name}_formatted`) ?? undefined,
+ },
+ net: {
+ value: priceNode?.getAttribute(`${name}_net`) !== null ? +priceNode.getAttribute(`${name}_net`) : undefined,
+ formatted: priceNode?.getAttribute(`${name}_net_formatted`) ?? undefined,
+ }
+ }
+ }
+
+ xmlGetPriceFromNode(priceNode){
+ const priceObj = {
+ price: {
+ gross: {
+ value: priceNode?.getAttribute("value") !== null ? +priceNode.getAttribute("value") : undefined,
+ formatted: priceNode?.getAttribute("price_formatted") ?? undefined,
+ },
+ net: {
+ value: priceNode?.getAttribute("price_net") !== null ? +priceNode.getAttribute("price_net") : undefined,
+ formatted: priceNode?.getAttribute("price_net_formatted") ?? undefined,
+ }
+ },
+ rebateCodeActive: priceNode?.getAttribute("rebate_code_active") === "y" ? true : false,
+ omnibusPrice: {
+ ...this.xmlGetGrossNetPrices({priceNode, name: "omnibus_price"})
+ },
+ // depositPrice: {},
+ // depositPriceUnit: {},
+ // totalDepositPrice: {},
+ // totalDepositPriceUnit: {},
+ omnibusPriceDetails: {
+ // unit: {},
+ youSave: {
+ ...this.xmlGetGrossNetPrices({priceNode, name: "omnibus_yousave"})
+ },
+ youSavePercent: priceNode?.getAttribute("price_net") !== null ? +priceNode.getAttribute("omnibus_yousave_percent") : undefined,
+ omnibusPriceIsHigherThanSellingPrice: priceNode?.getAttribute("omnibus_price_is_higher_than_selling_price") === "true" ? true : false,
+ // newPriceEffectiveUntil: {},
+ },
+ tax: {
+ // worth: {},
+ vatPercent: priceNode?.getAttribute("tax") !== null ? +priceNode.getAttribute("tax") : undefined,
+ // vatString: ""
+ },
+ beforeRebate: {
+ ...this.xmlGetGrossNetPrices({priceNode, name: "beforerebate"})
+ },
+ // beforeRebateDetails: {
+ // youSave: {
+
+ // },
+ // youSavePercent: "",
+ // unit: {}
+ // },
+ // crossedPrice: {},
+ youSave: {
+ ...this.xmlGetGrossNetPrices({priceNode, name: "yousave"})
+ },
+ youSavePercent: priceNode?.getAttribute("yousave_percent") !== null ? +priceNode.getAttribute("yousave_percent") : undefined,
+ // unit: {},
+ max: {
+ ...this.xmlGetGrossNetPrices({priceNode, name: "maxprice"})
+ },
+ // maxPriceUnit: {},
+ // suggested: {},
+ unitConvertedPrice: {
+ ...this.xmlGetGrossNetPrices({priceNode, name: "unit_converted_price"})
+ },
+ // rebateNumber: {},
+ lastPriceChangeDate: priceNode?.getAttribute("last_price_change_date") ?? undefined,
+ // advancePrice: {},
+ promotionDuration: {
+ promotionTill: {
+ date: {
+ date: priceNode?.getAttribute("last_price_change_date")?.split("-")?.[2] ?? undefined,
+ month: priceNode?.getAttribute("last_price_change_date")?.split("-")?.[1] ?? undefined,
+ year: priceNode?.getAttribute("last_price_change_date")?.split("-")?.[0] ?? undefined,
+ // weekDay: "",
+ // formatted: ""
+ },
+ time: {
+ hour: priceNode?.getAttribute("promotiontillhour")?.split(":")?.[0] ?? undefined,
+ minutes: priceNode?.getAttribute("promotiontillhour")?.split(":")?.[1] ?? undefined,
+ seconds: priceNode?.getAttribute("promotiontillhour")?.split(":")?.[2] ?? undefined,
+ },
+ // timestamp: ""
+ },
+ // discountTill: {},
+ // distinguishedTill: {},
+ // specialTill: {},
+ },
+ // subscriptionPrice: {},
+ };
+
+ return priceObj;
+ }
+
+ xmlGetTraitsFromNode(node){
+ const awardedParameters = [];
+
+ node.querySelectorAll(":scope > trait").forEach(trait=>{
+ const currParameter = awardedParameters.find(param=>param.id === trait.getAttribute("groupid")) || {
+ id: trait.getAttribute("groupid") ?? undefined,
+ name: trait.getAttribute("groupdescription") ?? undefined,
+ // description: "",
+ values: [],
+ // contextValue: "",
+ // search: {
+ // icon: "",
+ // },
+ }
+
+ currParameter.values.push({
+ id: trait.getAttribute("traitid") ?? undefined,
+ name: trait.getAttribute("traitid") ?? undefined,
+ link: trait.getAttribute("link") ?? undefined,
+ // description: "",
+ // search: {
+ // icon: ""
+ // }
+ })
+
+
+ if(currParameter.values.length === 1) awardedParameters.push(currParameter);
+ });
+
+
+ return awardedParameters;
+ }
+
+ async xmlGetData(){
+ if(!this.source?.link) return null;
+ try{
+ const res = await fetch(`${this.source?.link}${this.source?.link.includes("?") ? "&getProductXML=t" : "?getProductXML=t"}`);
+ const str = await res.text();
+ const xml = new window.DOMParser().parseFromString(str, "text/xml");
+ const now = new Date();
+
+ const allProducts = xml.querySelectorAll("product");
+ if(allProducts.length === 0) throw new Error("Nie znaleziono produktów");
+
+ const data = [];
+
+
+ allProducts.forEach(prod=>{
+
+ // NODES
+ const priceNode = prod.querySelector(":scope >price");
+ const firmNode = prod.querySelector(":scope > firm");
+ const categoryNode = prod.querySelector(":scope > category");
+ const seriesNode = prod.querySelector(":scope > series");
+ const commentsNode = prod.querySelector(":scope > comments");
+ const sizesNode = prod.querySelector(":scope > sizes");
+ const versionsNode = prod.querySelector(":scope > versions");
+
+ // STREFY
+ const zones = [];
+
+ if(prod.getAttribute("promo") === "yes") zones.push("promotion");
+ if(prod.getAttribute("discount") === "yes") zones.push("discount");
+ if(prod.getAttribute("distinguished") === "yes") zones.push("distinguished");
+ if(prod.getAttribute("special") === "yes") zones.push("special");
+ if(prod.getAttribute("new") === "yes") zones.push("new");
+ if(prod.getAttribute("bestseller") === "yes") zones.push("bestseller");
+ if(prod.getAttribute("subscription") === "yes") zones.push("subscription");
+
+ // ZDJĘCIA
+ const enclosuresImages = [];
+ prod.querySelectorAll(":scope > enclosures > images > enclosure").forEach(img=>{
+ enclosuresImages.push({
+ position: img.getAttribute("position") ?? undefined,
+ type: img.getAttribute("type") ?? undefined,
+ typeSecond: img.getAttribute("type_second") ?? undefined,
+ url: img.getAttribute("url") ?? undefined,
+ urlSecond: img.getAttribute("url_second") ?? undefined,
+ width: img.getAttribute("width") ?? undefined,
+ height: img.getAttribute("height") ?? undefined,
+ iconUrl: img.getAttribute("icon") ?? undefined,
+ iconUrlSecond: img.getAttribute("icon_second") ?? undefined,
+ iconWidth: img.getAttribute("icon_width") ?? undefined,
+ iconHeight: img.getAttribute("icon_height") ?? undefined,
+ mediumUrl: img.getAttribute("medium") ?? undefined,
+ mediumUrlSecond: img.getAttribute("medium_second") ?? undefined,
+ mediumWidth: img.getAttribute("medium_width") ?? undefined,
+ mediumHeight: img.getAttribute("medium_height") ?? undefined,
+ })
+ })
+
+ // SIZES
+ const sizes = [];
+ sizesNode.querySelectorAll(":scope > size").forEach(size=>{
+ const availabilityNode = size.querySelector(":scope > availability");
+ const shippingTimeNode = availabilityNode.querySelector(":scope > shipping_time");
+ const sizePriceNode = size.querySelector(":scope > price");
+ const weightNode = size.querySelector(":scope > weight");
+
+ sizes.push({
+ id: size?.getAttribute("type") ?? undefined,
+ name: size?.getAttribute("name") ?? undefined,
+ code: size?.getAttribute("code") ?? undefined,
+ codeProducer: size?.getAttribute("code_producer") ?? undefined,
+ codeExtern: size?.getAttribute("code_extern") ?? undefined,
+ amount: size?.getAttribute("amount") !== null ? +size.getAttribute("amount") : undefined,
+ amount_mo: size?.getAttribute("amount_mo") !== null ? +size.getAttribute("amount_mo") : undefined,
+ amount_mw: size?.getAttribute("amount_mw") !== null ? +size.getAttribute("amount_mw") : undefined,
+ amount_mp: size?.getAttribute("amount_mp") !== null ? +size.getAttribute("amount_mp") : undefined,
+ weight: weightNode?.getAttribute("g") !== null ? +weightNode.getAttribute("g") : undefined,
+ availability: {
+ visible: availabilityNode?.getAttribute("visible") === "y" ? true : false,
+ description: availabilityNode?.getAttribute("status_description") ?? undefined,
+ status: availabilityNode?.getAttribute("status") ?? undefined,
+ icon: availabilityNode?.getAttribute("status_gfx") ?? undefined,
+ deliveryDate: shippingTimeNode?.getAttribute("today") === "true" ? `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate().toString().padStart(2, 0)}` : availabilityNode?.getAttribute("delivery_date"),
+ minimumStockOfProduct: availabilityNode?.getAttribute("minimum_stock_of_product") ?? undefined,
+ // descriptionTel: "",
+ // iconTel: "",
+ },
+ shipping: {
+ today: shippingTimeNode?.getAttribute("today") === "true" ? true : false,
+ },
+ price: this.xmlGetPriceFromNode(sizePriceNode),
+ // amountWholesale: "",
+ })
+ })
+
+ // VERSIONS
+ const versions = [];
+ versionsNode.querySelectorAll(":scope > version").forEach(ver=>{
+ versions.push({
+ id: ver?.getAttribute("id") !== null ? +ver?.getAttribute("id") : undefined,
+ name: ver?.getAttribute("name") ?? undefined,
+ icon: ver?.getAttribute("gfx") ?? undefined,
+ iconSecond: ver?.getAttribute("gfx_second") ?? undefined,
+ iconSmall: ver?.getAttribute("gfx_small") ?? undefined,
+ iconSmallSecond: ver?.getAttribute("gfx_small_second") ?? undefined,
+ productIcon: ver?.getAttribute("icon") ?? undefined,
+ productIconSecond: ver?.getAttribute("icon_second") ?? undefined,
+ productIconSmall: ver?.getAttribute("icon_small") ?? undefined,
+ productIconSmallSecond: ver?.getAttribute("icon_small_second") ?? undefined,
+ link: ver?.getAttribute("link") ?? undefined,
+ // parameterValues: [],
+ });
+ });
+
+ // DATA
+ const typeParts = prod.getAttribute("product_type");
+ data.push({
+ id: prod?.getAttribute("id") !== null ? +prod?.getAttribute("id") : undefined,
+ type: typeParts[1] === "item" ? typeParts[0] : typeParts[1],
+ code: prod?.getAttribute("code") ?? undefined,
+ name: prod.querySelector(":scope > name")?.textContent ?? undefined,
+ versionName: prod.querySelector(":scope > versions")?.getAttribute("name") ?? undefined,
+ description: prod.querySelector(":scope > description")?.textContent ?? undefined,
+ // longDescription: prod.querySelector("vlongdescription")?.textContent ?? undefined,
+ // longDescriptionSections: "",
+ link: prod?.getAttribute("link"),
+ zones,
+ icon: prod.querySelector(":scope > icon")?.textContent ?? undefined,
+ iconSecond: prod.querySelector(":scope > icon_second")?.textContent ?? undefined,
+ iconSmall: prod.querySelector(":scope > icon_small")?.textContent ?? undefined,
+ iconSmallSecond: prod.querySelector(":scope > icon_small_second")?.textContent ?? undefined,
+ price: this.xmlGetPriceFromNode(priceNode),
+ unit: {
+ id: sizesNode?.getAttribute("unit_id") ?? undefined,
+ // name: "",
+ singular: sizesNode?.getAttribute("unit_single") !== null ? +sizesNode?.getAttribute("unit_single") : undefined,
+ plural: sizesNode?.getAttribute("unit_plural") ?? undefined,
+ fraction: sizesNode?.getAttribute("unit_fraction") ?? undefined,
+ sellBy: sizesNode?.getAttribute("unit_sellby") !== null ? +sizesNode?.getAttribute("unit_sellby") : undefined,
+ precision: sizesNode?.getAttribute("unit_precision") !== null ? +sizesNode?.getAttribute("unit_precision") : undefined,
+ unitConvertedFormat: sizesNode?.getAttribute("unit_converted_format") ?? undefined,
+ },
+ producer: {
+ id: firmNode?.getAttribute("id") !== null ? +firmNode?.getAttribute("id") : undefined,
+ name: firmNode?.getAttribute("name") ?? undefined,
+ link: firmNode?.getAttribute("productslink") ?? undefined,
+ searchIcons: {
+ icon: firmNode?.getAttribute("icon") ?? undefined,
+ },
+ // projectorIcons: {},
+ },
+ category: {
+ id: categoryNode?.getAttribute("id") !== null ? +categoryNode?.getAttribute("id") : undefined,
+ name: categoryNode?.getAttribute("name") ?? undefined,
+ link: categoryNode?.getAttribute("productslink") ?? undefined
+ },
+ group: {
+ id: versionsNode?.getAttribute("id") !== null ? +versionsNode?.getAttribute("id") : undefined,
+ name: versionsNode?.getAttribute("name") ?? undefined,
+ displayAll: versionsNode?.getAttribute("display_all") === "true" ? true : false,
+ link: versionsNode?.getAttribute("link") ?? undefined,
+ versions,
+ groupParameters: this.xmlGetTraitsFromNode(versionsNode.querySelector(":scope > groupParameters"))
+ },
+ opinion: {
+ rating: commentsNode?.getAttribute("avg") !== null ? +commentsNode?.getAttribute("avg") : undefined,
+ count: commentsNode?.getAttribute("count") !== null ? +commentsNode?.getAttribute("count") : undefined,
+ link: commentsNode?.getAttribute("link") ?? undefined
+ },
+ enclosuresImages,
+ // enclosuresAttachments: [],
+ series: {
+ id: seriesNode?.getAttribute("id") !== null ? +seriesNode?.getAttribute("id") : undefined,
+ name: seriesNode?.getAttribute("name") ?? undefined,
+ link: seriesNode?.getAttribute("link") ?? undefined
+ },
+ awardedParameters: this.xmlGetTraitsFromNode(prod.querySelector(":scope > traits")),
+ // parameteresWithContext: [],
+ sizes,
+ points: priceNode?.getAttribute("points") !== null ? +priceNode?.getAttribute("points") : undefined,
+ pointsReceive: priceNode?.getAttribute("points_recive") !== null ? +priceNode?.getAttribute("points_recive") : undefined,
+ // subscription: {},
+ // bundled: [],
+ // responsibleEntity: {},
+ })
+ })
+
+ return data;
+ }catch(err){
+ console.error(err);
+ return null;
+ }
+ }
+
+
// ========================================================
// INICJALIZACJA
// ========================================================
@@ -1225,6 +1599,10 @@ class IdmHotspot{
this.initEvents();
console.log(`Initialized hotspot #${this.id}`);
+ // Ustawienie wszystkich zmiennych CSS
+ this.cssSetAllVariables();
+
+ // ca
if(typeof this.options?.callbackFn === "function") this.options?.callbackFn();
}catch(err){
console.error(idmHotspotTextObject["Wystąpił błąd z inicjalizacją. Proszę odśwież stronę"], err);
@@ -1243,7 +1621,7 @@ class IdmHotspot{
this.initExternalFunctions();
try{
if(!this.products){
- if(!this?.query?.graphFn || !this?.query?.string) this.setQueryData();
+ if((!this?.query?.graphFn || !this?.query?.string) && !this.source?.link) this.setQueryData();
// pobranie danych o produktach
await this.getHotspotData();
@@ -1371,109 +1749,110 @@ class IdmHotspot{
*/
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;
+ constructor(swiper, selector) {
+ this.swiper = swiper?.slider?.slider ?? swiper?.slider ?? swiper;
+ this.selector = selector;
+ this.scrollbarEl = null;
+ this.progressEl = null;
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);
-}
+ 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
// ========================================================
@@ -1511,6 +1890,7 @@ function idmShowTooltip(tooltipEl){
function idmHideTooltipTimer(tooltipEl){
return setTimeout(() => idmHideTooltip(tooltipEl), 1500);
}
+
function idmHideTooltip(tooltipEl){
const tooltipContentEl = tooltipEl.querySelector(".idm_tooltip__content");
if (!tooltipContentEl) return;
@@ -1526,7 +1906,6 @@ function idmHideTooltip(tooltipEl){
delete tooltipEl._onScroll;
}
-
document.addEventListener("DOMContentLoaded", ()=>{
document.body.addEventListener("click", e=>{
const tooltipEl = e.target.closest(".idm_tooltip");
@@ -1582,8 +1961,8 @@ async function idmPrepareHotspotObject(selectedContainerEl){
selectedContainerEl.classList.add("--init");
const source = {};
-
- if(selectedContainerEl.dataset?.hotspotsType) source.hotspotsType = selectedContainerEl.dataset.hotspotsType;
+ if(selectedContainerEl.dataset?.link) source.link = selectedContainerEl.dataset.link;
+ else if(selectedContainerEl.dataset?.hotspotsType) source.hotspotsType = selectedContainerEl.dataset.hotspotsType;
else {
if(selectedContainerEl.dataset?.productsId) source.productsId = selectedContainerEl.dataset.productsId.split(",");
if(selectedContainerEl.dataset?.productsMenu) source.productsMenu = selectedContainerEl.dataset.productsMenu;
@@ -1593,7 +1972,6 @@ async function idmPrepareHotspotObject(selectedContainerEl){
if(selectedContainerEl.dataset?.priceFrom && selectedContainerEl.dataset?.priceTo) source.priceRange = {from: +selectedContainerEl.dataset.priceFrom, to: +selectedContainerEl.dataset.priceTo};
}
-
if(Object.keys(source).length === 0){
console.error();
selectedContainerEl?.remove();
@@ -1611,7 +1989,6 @@ async function idmPrepareHotspotObject(selectedContainerEl){
new IdmHotspot(idmHotspotObj)
}
-
document.querySelectorAll(".hotspot__wrapper.idm__hotspot").forEach(currentHotspot=>{
idmPrepareHotspotObject(currentHotspot)
})