initial commit, cuz im getting lost in this

This commit is contained in:
2026-05-22 10:54:32 +02:00
commit 8d4ff16d9b
28 changed files with 2195 additions and 0 deletions
@@ -0,0 +1,107 @@
document.addEventListener("DOMContentLoaded", () => {
const sliders = document.querySelectorAll(".js-slider-wrapper");
sliders.forEach((slider) => {
const track = slider.querySelector(".js-slider-track");
let slides = Array.from(track.querySelectorAll(".slide"));
const prevBtn = slider.querySelector(".js-slider-prev");
const nextBtn = slider.querySelector(".js-slider-next");
const dotsContainer = slider.querySelector(".js-slider-dots");
const dots = Array.from(slider.querySelectorAll(".slider-dot"));
if (slides.length <= 1) {
if (prevBtn) prevBtn.style.display = "none";
if (nextBtn) nextBtn.style.display = "none";
if (dotsContainer) dotsContainer.style.display = "none";
return;
}
const firstClone = slides[0].cloneNode(true);
const lastClone = slides[slides.length - 1].cloneNode(true);
track.appendChild(firstClone);
track.insertBefore(lastClone, slides[0]);
const allSlides = track.querySelectorAll(".slide");
requestAnimationFrame(() => {
track.scrollTo({ left: track.clientWidth, behavior: "instant" });
});
let scrollTimeout;
track.addEventListener("scroll", () => {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
const slideWidth = track.clientWidth;
const scrollLeft = track.scrollLeft;
if (
Math.abs(scrollLeft - slideWidth * (allSlides.length - 1)) < 5
) {
track.scrollTo({ left: slideWidth, behavior: "instant" });
} else if (scrollLeft < 5) {
track.scrollTo({
left: slideWidth * (allSlides.length - 2),
behavior: "instant",
});
}
let activeIndex = Math.round(track.scrollLeft / slideWidth) - 1;
if (activeIndex < 0) activeIndex = dots.length - 1;
if (activeIndex >= dots.length) activeIndex = 0;
dots.forEach((d) => d.classList.remove("active"));
if (dots[activeIndex]) dots[activeIndex].classList.add("active");
}, 50);
});
window.addEventListener("resize", () => {
const activeIndex =
dots.findIndex((d) => d.classList.contains("active")) + 1;
track.scrollTo({
left: track.clientWidth * activeIndex,
behavior: "instant",
});
});
if (nextBtn) {
nextBtn.addEventListener("click", () => {
track.scrollBy({ left: track.clientWidth, behavior: "smooth" });
});
}
if (prevBtn) {
prevBtn.addEventListener("click", () => {
track.scrollBy({ left: -track.clientWidth, behavior: "smooth" });
});
}
dots.forEach((dot, index) => {
dot.addEventListener("click", () => {
track.scrollTo({
left: track.clientWidth * (index + 1),
behavior: "smooth",
});
});
});
let autoPlayTimer;
const startAutoPlay = () => {
clearInterval(autoPlayTimer);
autoPlayTimer = setInterval(() => {
if (slider.offsetWidth === 0) return;
track.scrollBy({ left: track.clientWidth, behavior: "smooth" });
}, 4000);
};
const stopAutoPlay = () => clearInterval(autoPlayTimer);
startAutoPlay();
slider.addEventListener("mouseenter", stopAutoPlay);
slider.addEventListener("mouseleave", startAutoPlay);
slider.addEventListener("touchstart", stopAutoPlay, { passive: true });
slider.addEventListener("touchend", startAutoPlay);
});
});
@@ -0,0 +1 @@
main#main > #AjaxMainSection > .section_top
+28
View File
@@ -0,0 +1,28 @@
(() => {
const allBanners = document.querySelectorAll(`
.vc-content_slidebanner .owl-carousel .owl-stage .owl-item:not(.cloned) .item img,
.vc-content_slidebanner .owl-carousel > .item:not(.cloned) img
`);
const allBannersResponsive = document.querySelectorAll(`
.vc-content_slidebanner--responsive .owl-carousel .owl-stage .owl-item:not(.cloned) .item img,
.vc-content_slidebanner--responsive .owl-carousel > .item:not(.cloned) img
`);
const banners = Array.from(allBanners).map((banner) => {
const src = banner.getAttribute("data-src");
const alt = banner.getAttribute("alt");
return { src, alt };
});
const bannersResponsive = Array.from(allBannersResponsive).map((banner) => {
const src = banner.getAttribute("data-src");
const alt = banner.getAttribute("alt");
return { src, alt };
});
return {
banners,
bannersResponsive,
}
})();
+148
View File
@@ -0,0 +1,148 @@
/* Base Container */
.promo-section {
padding: 2rem 0;
}
@media (min-width: 1024px) {
.promo-section {
padding: 3rem 0;
}
}
.container {
max-width: var(--max-w-7xl, 80rem);
margin: 0 auto;
padding: 0 1rem;
}
@media (min-width: 640px) {
.container {
padding: 0 1.5rem;
}
}
@media (min-width: 1024px) {
.container {
padding: 0 2rem;
}
}
/* Display Logic & Breakpoints (770px as requested) */
.desktop-slider {
display: none;
}
.mobile-slider {
display: block;
}
@media (min-width: 770px) {
.desktop-slider {
display: block;
}
.mobile-slider {
display: none;
}
}
/* Slider Container */
.slider-wrapper {
position: relative;
width: 100%;
border-radius: 0.75rem;
overflow: hidden;
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
/* Scroll Track */
.slider-track {
display: flex;
width: 100%;
overflow-x: auto;
scroll-snap-type: x mandatory;
/* Removed CSS scroll-behavior: smooth so JS can perform 'instant' infinite jumps */
-ms-overflow-style: none;
scrollbar-width: none;
}
.slider-track::-webkit-scrollbar {
display: none;
}
/* Individual Slide */
.slide {
flex: 0 0 100%;
scroll-snap-align: start;
position: relative;
width: 100%;
}
/* Exact Aspect Ratios */
.desktop-slider .slide {
aspect-ratio: 1920 / 640;
}
.mobile-slider .slide {
aspect-ratio: 1 / 1;
}
.slide img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* Navigation Arrows */
.slider-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(255, 255, 255, 0.8);
color: #1f2937;
border: none;
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
transition: background 0.2s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.slider-btn:hover {
background: rgba(255, 255, 255, 1);
}
.slider-btn svg {
width: 1.25rem;
height: 1.25rem;
}
.slider-btn.prev {
left: 1rem;
}
.slider-btn.next {
right: 1rem;
}
/* Pagination Dots */
.slider-dots {
position: absolute;
bottom: 1rem;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 0.5rem;
z-index: 10;
}
.slider-dot {
width: 0.625rem;
height: 0.625rem;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
border: none;
cursor: pointer;
transition: all 0.2s ease-in-out;
}
.slider-dot.active {
background: rgba(255, 255, 255, 1);
transform: scale(1.2);
}
@@ -0,0 +1,103 @@
<section class="promo-section">
<div class="container">
<!-- DESKTOP SLIDER (1920x640) -->
<div class="desktop-slider">
<div class="slider-wrapper js-slider-wrapper">
<div class="slider-track js-slider-track">
{{#each banners}}
<div class="slide">
<img src="{{this.src}}" alt="{{this.alt}}" loading="lazy" />
</div>
{{/each}}
</div>
<button class="slider-btn prev js-slider-prev" aria-label="Předchozí">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button class="slider-btn next js-slider-next" aria-label="Další">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
<div class="slider-dots js-slider-dots">
{{#each banners}}
<button
class="slider-dot {{#if @first}}active{{/if}}"
data-index="{{@index}}"
aria-label="Zobrazit banner {{@index}}"
></button>
{{/each}}
</div>
</div>
</div>
<!-- MOBILE SLIDER (600x600) -->
<div class="mobile-slider">
<div class="slider-wrapper js-slider-wrapper">
<div class="slider-track js-slider-track">
{{#each bannersResponsive}}
<div class="slide">
<img src="{{this.src}}" alt="{{this.alt}}" loading="lazy" />
</div>
{{/each}}
</div>
<button class="slider-btn prev js-slider-prev" aria-label="Předchozí">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button class="slider-btn next js-slider-next" aria-label="Další">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
<div class="slider-dots js-slider-dots">
{{#each bannersResponsive}}
<button
class="slider-dot {{#if @first}}active{{/if}}"
data-index="{{@index}}"
aria-label="Zobrazit banner {{@index}}"
></button>
{{/each}}
</div>
</div>
</div>
</div>
</section>