Files
reui-blocks/blocks/product-card23.tsx
T

230 lines
7.2 KiB
TypeScript

"use client";
import { Search } from "lucide-react";
import { useState } from "react";
import { Price, PriceValue } from "@/components/shadcnblocks/price";
import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardTitle } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { cn } from "@/lib/utils";
type Image = {
src: string;
srcset?: string;
alt: string;
sizes?: string;
};
interface ProductPrice {
regular: number;
sale?: number;
currency: string;
}
type ProductColorOptions = {
images: Image[];
id: string;
value: string;
colors: string[];
};
interface Product {
images: Array<Image>;
name: string;
price: ProductPrice;
brand: {
text: string;
link: string;
};
badges?: Array<string>;
variantId?: string;
link: string;
variants?: Array<ProductColorOptions>;
}
interface ColorRadioGroupProps {
options: ProductColorOptions[];
onValueChange: (value: string) => void;
value: string;
}
const PRODUCT_CARD: Product = {
name: "لیوان سرامیکی سولستیس",
brand: {
text: "خاک‌سفال ترا",
link: "#",
},
link: "#",
price: { regular: 240000.0, currency: "ریال" },
images: [
{
src: "/block-media/shadcnblocks/block/ecommerce/other/Ceramic-Mug-in-Sunlight-2.png",
srcset:
"/block-media/shadcnblocks/block/ecommerce/other/Ceramic-Mug-in-Sunlight-1.png 484w, /block-media/shadcnblocks/block/ecommerce/other/Ceramic-Mug-in-Sunlight-2.png 908w",
sizes: "(min-width: 768px) 908px, 100vw",
alt: "لیوان سرامیکی سولستیس",
},
],
variantId: "product-1-1",
badges: ["جدید"],
variants: [
{
id: "product-1-1",
value: "product-1-1",
colors: ["#A5BFB1", "#E38035"],
images: [
{
src: "/block-media/shadcnblocks/block/ecommerce/other/Ceramic-Mug-in-Sunlight-2.png",
srcset:
"/block-media/shadcnblocks/block/ecommerce/other/Ceramic-Mug-in-Sunlight-1.png 484w, /block-media/shadcnblocks/block/ecommerce/other/Ceramic-Mug-in-Sunlight-2.png 908w",
sizes: "(min-width: 768px) 908px, 100vw",
alt: "لیوان سرامیکی طرح اول",
},
],
},
{
id: "product-1-2",
value: "product-1-2",
colors: ["#C12F06", "#32414C"],
images: [
{
src: "/block-media/shadcnblocks/block/ecommerce/other/Colorful-Ceramic-Mug-2.png",
srcset:
"/block-media/shadcnblocks/block/ecommerce/other/Colorful-Ceramic-Mug-1.png 484w, /block-media/shadcnblocks/block/ecommerce/other/Colorful-Ceramic-Mug-2.png 904w",
sizes: "(min-width: 768px) 908px, 100vw",
alt: "لیوان سرامیکی طرح دوم",
},
],
},
],
};
interface ProductCard23Props {
className?: string;
}
const ProductCard23 = ({ className }: ProductCard23Props) => {
const { regular, sale, currency } = PRODUCT_CARD.price;
const [selectedColorValue, setSelectedColorValue] = useState<string>(
PRODUCT_CARD.variantId ?? "",
);
const onColorChange = (value: string) => {
setSelectedColorValue(value);
};
const selectedColorImages =
PRODUCT_CARD.variants?.find(
(variant) => variant.value === selectedColorValue,
)?.images ?? PRODUCT_CARD.images;
return (
<div className={cn("flex w-full justify-center", className)}>
<Card className="w-full max-w-64 rounded-none border-none bg-background p-0 shadow-none">
<CardContent className="p-0">
<div className="group relative">
<a href={PRODUCT_CARD.link}>
<AspectRatio
ratio={1}
className="flex items-center justify-center overflow-hidden bg-muted/20"
>
{selectedColorImages.map((img, index) => (
<img
src={img.src}
srcSet={img.srcset}
sizes={img.sizes}
alt={img.alt}
key={`product-card-23-img-${index}`}
className="absolute inset-0 size-full origin-center object-contain object-center transition-opacity duration-500 first:opacity-100 group-hover:first:not-only:opacity-0 nth-2:opacity-0 group-hover:nth-2:opacity-100"
/>
))}
</AspectRatio>
</a>
<div className="absolute end-4 top-4">
<Button
size="icon"
className="transition-opacity duration-500 md:opacity-0 md:group-hover:opacity-100"
>
<Search />
</Button>
</div>
{PRODUCT_CARD.variants && (
<div className="absolute inset-x-0 bottom-0 z-10 p-4">
<ColorRadioGroup
onValueChange={onColorChange}
value={selectedColorValue}
options={PRODUCT_CARD.variants}
/>
</div>
)}
</div>
<div className="flex min-h-44 flex-col gap-2 px-5 pb-8 pt-6 lg:px-7 lg:pb-8">
<a
href={PRODUCT_CARD.brand.link}
className="text-xs text-muted-foreground uppercase"
>
{PRODUCT_CARD.brand.text}
</a>
<a href={PRODUCT_CARD.link}>
<CardTitle className="text-xl leading-none font-medium">
{PRODUCT_CARD.name}
</CardTitle>
</a>
<Price className="gap-x-1.5 text-sm" onSale={sale != null}>
<PriceValue price={sale} currency={currency} variant="sale" />
<PriceValue price={regular} currency={currency} variant="regular" />
</Price>
{PRODUCT_CARD.badges && PRODUCT_CARD.badges.length > 0 && (
<div className="flex flex-wrap items-center gap-1.5">
{PRODUCT_CARD.badges.map((text, index) => (
<Badge variant="outline" key={`product-card-23-badge-${index}`}>
{text}
</Badge>
))}
</div>
)}
</div>
</CardContent>
</Card>
</div>
);
};
const ColorRadioGroup = ({
options,
value,
onValueChange,
}: ColorRadioGroupProps) => {
return (
<RadioGroup onValueChange={onValueChange} value={value} className="flex">
{options.map((option, index) => (
<div className="shrink-0" key={`product-card-23-color-option-${index}`}>
<RadioGroupItem
value={option.value}
id={option.id}
className="peer sr-only"
/>
<Label
htmlFor={option.id}
style={
option.colors?.length === 1
? {
backgroundColor: option.colors[0],
}
: {
backgroundImage: `linear-gradient(45deg, ${option.colors?.[0]} 0%, ${option.colors?.[0]} 50%, ${option.colors?.[1]} 50%, ${option.colors?.[1]} 100%)`,
}
}
className="size-3 cursor-pointer rounded-full peer-data-[state=checked]:ring"
></Label>
</div>
))}
</RadioGroup>
);
};
export { ProductCard23 };