chore: transfer repo

This commit is contained in:
Danijel
2026-01-19 20:21:14 +01:00
commit 7d2fb0c737
213 changed files with 18085 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
"use client";
import { getProductColors } from "@/components/products/utils/colorUtils";
import { Button } from "@/components/ui/Button";
import { ProductCard } from "@/components/ui/ProductCard";
import { cn } from "@/lib/utils";
import useEmblaCarousel from "embla-carousel-react";
import { Product } from "lib/shopify/types";
import { useCallback, useEffect, useState } from "react";
import { FiChevronLeft, FiChevronRight } from "react-icons/fi";
interface ProductSliderProps {
products: Product[];
}
/**
* A product slider component that displays products in a horizontal scrollable slider.
* Uses the Embla Carousel library for the sliding functionality.
*/
export function ProductSlider({ products }: ProductSliderProps) {
const [emblaRef, emblaApi] = useEmblaCarousel({
align: "start",
containScroll: false,
loop: false,
});
const [prevBtnEnabled, setPrevBtnEnabled] = useState(false);
const [nextBtnEnabled, setNextBtnEnabled] = useState(false);
const scrollPrev = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev();
}, [emblaApi]);
const scrollNext = useCallback(() => {
if (emblaApi) emblaApi.scrollNext();
}, [emblaApi]);
const onSelect = useCallback(() => {
if (!emblaApi) return;
setPrevBtnEnabled(emblaApi.canScrollPrev());
setNextBtnEnabled(emblaApi.canScrollNext());
}, [emblaApi]);
useEffect(() => {
if (!emblaApi) return;
onSelect();
emblaApi.on("select", onSelect);
emblaApi.on("reInit", onSelect);
}, [emblaApi, onSelect]);
return (
<div className="relative">
<div className="overflow-visible" ref={emblaRef}>
<div className="flex">
{products.map((product, index) => (
<div
key={product.id}
className={cn(
"w-[85%] min-[500px]:w-[90%] min-[700px]:w-[48.5%] min-[1100px]:w-[32%] min-w-0 flex-grow-0 flex-shrink-0 mr-[2%]",
)}
>
<ProductCard
title={product.title}
variant={product.variants[0]?.title || ""}
price={parseFloat(product.priceRange.maxVariantPrice.amount)}
imageSrc={product.featuredImage?.url || "/assets/images/placeholder_image.svg"}
slug={product.handle}
product={product}
colors={getProductColors(product)}
/>
</div>
))}
</div>
</div>
{/* Previous button */}
<Button
onClick={scrollPrev}
disabled={!prevBtnEnabled}
variant="default"
size="sm"
className={cn(
"absolute left-[-18px] top-1/2 transform -translate-y-1/2 z-10",
"w-9 h-9 text-sm bg-white shadow-md border border-gray-200 rounded-full",
!prevBtnEnabled && "opacity-50 cursor-not-allowed"
)}
>
<FiChevronLeft className="w-5 h-5" />
</Button>
{/* Next button */}
<Button
onClick={scrollNext}
disabled={!nextBtnEnabled}
variant="default"
size="sm"
className={cn(
"absolute right-[-18px] top-1/2 transform -translate-y-1/2 z-10",
"w-9 h-9 text-sm bg-white shadow-md border border-gray-200 rounded-full",
!nextBtnEnabled && "opacity-50 cursor-not-allowed"
)}
>
<FiChevronRight className="w-5 h-5" />
</Button>
</div>
);
}