function setupHorizontalCarousel({
rootSelector,
viewportSelector,
trackSelector,
itemSelector,
prevSelector,
nextSelector,
dotsSelector,
dotClassName,
intervalMs = 5000,
fixedPerView = null,
}) {
const root = document.querySelector(rootSelector);
if (!root) return;
const viewport = root.querySelector(viewportSelector);
const track = root.querySelector(trackSelector);
const items = track ? Array.from(track.querySelectorAll(itemSelector)) : [];
const prev = root.querySelector(prevSelector);
const next = root.querySelector(nextSelector);
const dotsRoot = root.querySelector(dotsSelector);
if (!viewport || !track || items.length <= 1) {
return;
}
let currentIndex = 0;
let paused = false;
let intervalId = null;
const getGap = () => {
const styles = window.getComputedStyle(track);
return Number.parseFloat(styles.columnGap || styles.gap || "0") || 0;
};
const getStep = () => {
const firstItem = items[0];
if (!firstItem) return 0;
return firstItem.getBoundingClientRect().width + getGap();
};
const getPerView = () => {
if (fixedPerView !== null) {
return fixedPerView;
}
const step = getStep();
if (!step) return 1;
return Math.max(1, Math.floor((viewport.clientWidth + getGap()) / step));
};
const getMaxIndex = () => Math.max(0, items.length - getPerView());
const renderDots = () => {
if (!dotsRoot) return;
const maxIndex = getMaxIndex();
const totalDots = maxIndex + 1;
dotsRoot.innerHTML = "";
if (totalDots <= 1) {
return;
}
for (let index = 0; index < totalDots; index += 1) {
const button = document.createElement("button");
button.type = "button";
button.className = dotClassName;
button.setAttribute("aria-label", `Ir al slide ${index + 1}`);
if (index === currentIndex) {
button.classList.add("is-active");
}
button.addEventListener("click", () => {
goTo(index);
restartAutoPlay();
});
dotsRoot.appendChild(button);
}
};
const updateControls = () => {
if (prev) {
prev.disabled = getMaxIndex() === 0;
}
if (next) {
next.disabled = getMaxIndex() === 0;
}
if (dotsRoot) {
Array.from(dotsRoot.children).forEach((dot, index) => {
dot.classList.toggle("is-active", index === currentIndex);
});
}
};
const goTo = (index) => {
const maxIndex = getMaxIndex();
currentIndex = Math.max(0, Math.min(index, maxIndex));
track.style.transform = `translateX(-${currentIndex * getStep()}px)`;
updateControls();
};
const nextSlide = () => {
if (paused) return;
const maxIndex = getMaxIndex();
currentIndex = currentIndex >= maxIndex ? 0 : currentIndex + 1;
goTo(currentIndex);
};
const prevSlide = () => {
const maxIndex = getMaxIndex();
currentIndex = currentIndex <= 0 ? maxIndex : currentIndex - 1;
goTo(currentIndex);
};
const stopAutoPlay = () => {
if (intervalId !== null) {
window.clearInterval(intervalId);
intervalId = null;
}
};
const startAutoPlay = () => {
stopAutoPlay();
if (items.length <= getPerView()) {
return;
}
intervalId = window.setInterval(nextSlide, intervalMs);
};
const restartAutoPlay = () => {
startAutoPlay();
};
prev?.addEventListener("click", () => {
prevSlide();
restartAutoPlay();
});
next?.addEventListener("click", () => {
nextSlide();
restartAutoPlay();
});
root.addEventListener("mouseenter", () => {
paused = true;
});
root.addEventListener("mouseleave", () => {
paused = false;
});
root.addEventListener("focusin", () => {
paused = true;
});
root.addEventListener("focusout", (event) => {
if (!(event.relatedTarget instanceof Node) || !root.contains(event.relatedTarget)) {
paused = false;
}
});
window.addEventListener("resize", () => {
const maxIndex = getMaxIndex();
if (currentIndex > maxIndex) {
currentIndex = maxIndex;
}
renderDots();
goTo(currentIndex);
restartAutoPlay();
});
renderDots();
goTo(0);
startAutoPlay();
}
function setupCapacitacionesCalendar() {
const root = document.querySelector(".cap-calendar");
if (!root) return;
const months = Array.from(root.querySelectorAll(".cap-calendar__month"));
const label = root.querySelector("[data-cap-calendar-label]");
const prev = root.querySelector("[data-cap-calendar-prev]");
const next = root.querySelector("[data-cap-calendar-next]");
const detailRoot = document.getElementById("capCalendarDetail");
const detailCategory = document.getElementById("capCalendarDetailCategory");
const detailMode = document.getElementById("capCalendarDetailMode");
const detailTitle = document.getElementById("capCalendarDetailTitle");
const detailDescription = document.getElementById("capCalendarDetailDescription");
const detailDate = document.getElementById("capCalendarDetailDate");
const detailTime = document.getElementById("capCalendarDetailTime");
const detailContextCard = document.getElementById("capCalendarDetailContextCard");
const detailContextLabel = document.getElementById("capCalendarDetailContextLabel");
const detailContextValue = document.getElementById("capCalendarDetailContextValue");
const detailCupo = document.getElementById("capCalendarDetailCupo");
const detailAudienceCard = document.getElementById("capCalendarDetailAudienceCard");
const detailAudience = document.getElementById("capCalendarDetailAudience");
const detailLink = document.getElementById("capCalendarDetailLink");
if (!months.length || !label || !prev || !next) {
return;
}
let currentIndex = 0;
const triggers = Array.from(root.querySelectorAll(".cap-calendar__item-trigger"));
const updateDetail = (trigger) => {
if (!(trigger instanceof HTMLElement) || !detailRoot) {
return;
}
detailRoot.style.setProperty("--cap-detail-accent", trigger.dataset.capAccent || "#8f141b");
if (detailCategory) detailCategory.textContent = trigger.dataset.capCategory || "";
if (detailMode) detailMode.textContent = trigger.dataset.capMode || "";
if (detailTitle) detailTitle.textContent = trigger.dataset.capTitle || "";
if (detailDescription) detailDescription.textContent = trigger.dataset.capDescription || "";
if (detailDate) detailDate.textContent = trigger.dataset.capDate || "";
if (detailTime) detailTime.textContent = trigger.dataset.capTime || "";
if (detailCupo) detailCupo.textContent = trigger.dataset.capCupo || "";
const speaker = trigger.dataset.capSpeaker || "";
const platform = trigger.dataset.capPlatform || "";
const contextValue = speaker || platform;
const contextLabel = speaker ? "Capacitador" : "Espacio";
if (detailContextCard) {
detailContextCard.hidden = contextValue === "";
}
if (detailContextLabel) {
detailContextLabel.textContent = contextLabel;
}
if (detailContextValue) {
detailContextValue.textContent = contextValue;
}
if (detailAudienceCard) {
detailAudienceCard.hidden = !(trigger.dataset.capAudience || "");
}
if (detailAudience) {
detailAudience.textContent = trigger.dataset.capAudience || "";
}
if (detailLink) {
detailLink.href = trigger.dataset.capLink || "#";
detailLink.textContent = trigger.dataset.capLinkLabel || "Registro";
}
};
const selectTrigger = (trigger) => {
if (!(trigger instanceof HTMLElement)) {
return;
}
triggers.forEach((item) => item.classList.remove("is-active"));
trigger.classList.add("is-active");
updateDetail(trigger);
};
const syncMonthSelection = () => {
const visibleMonth = months[currentIndex];
const visibleTriggers = Array.from(visibleMonth.querySelectorAll(".cap-calendar__item-trigger"));
if (!visibleTriggers.length) {
return;
}
const activeVisible = visibleTriggers.find((trigger) => trigger.classList.contains("is-active"));
selectTrigger(activeVisible || visibleTriggers[0]);
};
const render = () => {
months.forEach((month, index) => {
month.hidden = index !== currentIndex;
});
label.textContent = months[currentIndex].dataset.monthLabel || "";
prev.disabled = currentIndex === 0;
next.disabled = currentIndex === months.length - 1;
syncMonthSelection();
};
prev.addEventListener("click", () => {
if (currentIndex > 0) {
currentIndex -= 1;
render();
}
});
next.addEventListener("click", () => {
if (currentIndex < months.length - 1) {
currentIndex += 1;
render();
}
});
triggers.forEach((trigger) => {
const activate = () => selectTrigger(trigger);
trigger.addEventListener("click", activate);
trigger.addEventListener("mouseenter", activate);
trigger.addEventListener("focus", activate);
});
render();
}
document.addEventListener("DOMContentLoaded", () => {
setupHorizontalCarousel({
rootSelector: ".home-publicaciones__shell",
viewportSelector: "[data-posts-viewport]",
trackSelector: "[data-posts-track]",
itemSelector: ".social-post",
prevSelector: "[data-posts-prev]",
nextSelector: "[data-posts-next]",
dotsSelector: "[data-posts-dots]",
dotClassName: "home-publicaciones__dot",
intervalMs: 4600,
});
setupHorizontalCarousel({
rootSelector: ".cap-banners",
viewportSelector: "[data-cap-banner-viewport]",
trackSelector: "[data-cap-banner-track]",
itemSelector: ".cap-banner",
prevSelector: "[data-cap-banner-prev]",
nextSelector: "[data-cap-banner-next]",
dotsSelector: "[data-cap-banner-dots]",
dotClassName: "cap-banners__dot",
intervalMs: 5200,
fixedPerView: 1,
});
setupCapacitacionesCalendar();
});