84 lines
4.2 KiB
Plaintext
84 lines
4.2 KiB
Plaintext
mixin Carousel(items, options)
|
|
// Set options with fallbacks for defaults
|
|
- const id = options.id || "carousel";
|
|
- const color = "black";
|
|
- const colorInverted = "white";
|
|
- const category = options.category || "portfolio";
|
|
- const slideClasses = options.slideClasses || "sm:w-1/2 lg:w-1/3 xl:w-1/4";
|
|
// Pass through options even if not used
|
|
- const autoScroll = options.autoScroll ?? 4_800;
|
|
|
|
.carousel-container.relative.w-full.overflow-hidden-y.px-4(class="sm:px-12", id=id)
|
|
//- Navigation buttons
|
|
button.absolute.z-20.transform(
|
|
class=`-translate-y-1/2 top-1/2 -left-6 sm:left-2 bg-white/40 hover:bg-white/80 text-nls-black rounded-full p-1 sm:p-3 shadow-lg transition-all focus:outline-none focus:ring-2 focus:ring-nls-${color}`,
|
|
aria-label="Previous slide",
|
|
onclick=`carouselPrev('${id}'); umami.track('carousel navigation', { direction: 'prev', category: '${category}', visitDuration: getVisitDuration() })`
|
|
)
|
|
svg.w-8.h-8.text-gray-800(class="sm:w-8 sm:h-8", fill="none", stroke="currentColor", viewBox="0 0 24 24")
|
|
path(stroke-linecap="round", stroke-linejoin="round", stroke-width="2", d="M15 19l-7-7 7-7")
|
|
|
|
button.absolute.z-20.transform(
|
|
class=`top-1/2 -right-6 sm:right-2 -translate-y-1/2 bg-white/40 hover:bg-white/80 text-nls-black rounded-full p-1 sm:p-3 shadow-lg transition-all focus:outline-none focus:ring-2 focus:ring-nls-${color}`,
|
|
aria-label="Next slide",
|
|
onclick=`carouselNext('${id}'); umami.track('carousel navigation', { direction: 'next', category: '${category}', visitDuration: getVisitDuration() })`
|
|
)
|
|
svg.w-8.h-8.text-gray-800(class="sm:w-8 sm:h-8", fill="none", stroke="currentColor", viewBox="0 0 24 24")
|
|
path(stroke-linecap="round", stroke-linejoin="round", stroke-width="2", d="M9 5l7 7-7 7")
|
|
|
|
//- Carousel track
|
|
.carousel-track.flex.overflow-x-auto.rounded-lg.scroll-smooth.scrollbar-hide(
|
|
style="scroll-snap-type: x mandatory",
|
|
role="region",
|
|
aria-label="Image carousel"
|
|
)
|
|
each item, index in items
|
|
- const href = item?.href || "#";
|
|
|
|
.carousel-slide.flex-none.w-full.relative(class=slideClasses, style="scroll-snap-align: start")
|
|
if item.type === "image"
|
|
a.text-left.flex.flex-col.relative.overflow-hidden.rounded-lg.mb-4.py-4(
|
|
class="sm:px-6 sm:flex-row sm:gap-6",
|
|
onclick=`umami.track('carousel image clicked', { category: '${category}', position: 'carousel', label: '${item.alt || item.caption || "image"}', visitDuration: getVisitDuration() })`,
|
|
href=href,
|
|
target="_blank",
|
|
rel="noopener noreferrer"
|
|
)
|
|
img.w-full.h-full.object-contain.transition-transform.rounded-lg.duration-300.shadow-lg(
|
|
class="sm:w-1/2",
|
|
src=item.src,
|
|
alt=item.alt,
|
|
loading=index < 3 ? "eager" : "lazy"
|
|
)
|
|
if item.caption
|
|
.p-4(class="sm:static sm:self-center sm:w-1/2")
|
|
p.text-lg.pt-2.font-bold(class="sm:text-xl")= item.caption
|
|
if item.description
|
|
p.text-sm.mt-2(class="sm:text-base")= item.description
|
|
//- Technology pills
|
|
if item.technologies && item.technologies.length > 0
|
|
.flex.flex-wrap.gap-2.mt-3
|
|
each tech in item.technologies
|
|
span.inline-block.px-3.py-1.text-sm.font-medium.rounded-full.shadow-sm(
|
|
class=`bg-nls-${color}/10 text-nls-${color} border border-nls-${color}/20 dark:bg-nls-${colorInverted}/20 dark:text-nls-${colorInverted} dark:border-nls-${colorInverted}/30`
|
|
)= tech
|
|
if item.href
|
|
p.text-sm.mt-4.underline(class="sm:text-base hover:text-nls-black dark:hover:text-nls-white")= item.href
|
|
else if item.type === "brand"
|
|
.aspect-video.flex.items-center.justify-center.mx-2.mb-4(class="sm:mx-12")
|
|
img.max-w-full.max-h-full.min-h-64.object-contain.p-4(src=item.logo, alt=item.name, loading=index < 6 ? "eager" : "lazy")
|
|
if autoScroll
|
|
script.
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
setInterval(
|
|
() => {
|
|
const track = document.querySelector(`##{id} .carousel-track`);
|
|
if (track.scrollLeft + track.clientWidth >= track.scrollWidth) {
|
|
track.scrollTo({left: 0, behavior: "smooth"});
|
|
} else {
|
|
carouselNext("#{id}");
|
|
}
|
|
},
|
|
#{autoScroll},
|
|
);
|
|
}); |