Add replaceable placeholder images with unique img src paths.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
141f1a30be
commit
1272977923
@@ -44,6 +44,8 @@ export function AboutSanihome() {
|
||||
</div>
|
||||
</div>
|
||||
<Placeholder
|
||||
src="/images/about.svg"
|
||||
alt="درباره سانیهوم"
|
||||
width="100%"
|
||||
height="360px"
|
||||
label="560×360"
|
||||
|
||||
@@ -19,6 +19,8 @@ export function BlogCarousel() {
|
||||
className="w-[280px] shrink-0 overflow-hidden rounded-xl border border-border bg-surface transition-shadow hover:shadow-md md:w-[300px]"
|
||||
>
|
||||
<Placeholder
|
||||
src={post.image}
|
||||
alt={post.title}
|
||||
width="100%"
|
||||
height="160px"
|
||||
label="300×160"
|
||||
|
||||
@@ -20,6 +20,8 @@ export function FeaturedCategories() {
|
||||
style={{ animationDelay: `${i * 60}ms` }}
|
||||
>
|
||||
<Placeholder
|
||||
src={cat.image}
|
||||
alt={cat.name}
|
||||
width="72px"
|
||||
height="72px"
|
||||
label="72×72"
|
||||
|
||||
@@ -7,6 +7,11 @@ import { sliderSlides } from "@/data/sample";
|
||||
export function HeroSlider() {
|
||||
const [active, setActive] = useState(1);
|
||||
|
||||
const getIndex = (offset: number) => {
|
||||
const len = sliderSlides.length;
|
||||
return (active + offset + len) % len;
|
||||
};
|
||||
|
||||
const positions = [
|
||||
{ offset: -1, scale: "scale-[0.92]", opacity: "opacity-55", z: "z-10" },
|
||||
{ offset: 0, scale: "scale-100", opacity: "opacity-100", z: "z-20" },
|
||||
@@ -19,6 +24,7 @@ export function HeroSlider() {
|
||||
<div className="relative h-[220px] sm:h-[280px] md:h-[340px] lg:h-[380px]">
|
||||
{positions.map(({ offset, scale, opacity, z }) => {
|
||||
const isCenter = offset === 0;
|
||||
const slide = sliderSlides[getIndex(offset)];
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -33,6 +39,8 @@ export function HeroSlider() {
|
||||
>
|
||||
<div className="overflow-hidden rounded-2xl border border-border bg-surface shadow-lg">
|
||||
<Placeholder
|
||||
src={slide.image}
|
||||
alt={slide.title}
|
||||
width="100%"
|
||||
label={isCenter ? "1280×380" : "880×350"}
|
||||
className={`w-full rounded-none border-0 h-[200px] sm:h-[260px] md:h-[310px] ${
|
||||
|
||||
@@ -19,6 +19,7 @@ export function SamsungCarousel() {
|
||||
name={product.name}
|
||||
brand={product.brand}
|
||||
price={product.price}
|
||||
image={product.image}
|
||||
variant="brand"
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -11,6 +11,8 @@ function SpecialSaleBanner() {
|
||||
className="relative w-[220px] shrink-0 self-stretch overflow-hidden rounded-xl border border-border md:w-[240px]"
|
||||
>
|
||||
<Placeholder
|
||||
src="/images/sale-banner.svg"
|
||||
alt="فروش ویژه"
|
||||
width="100%"
|
||||
label="240×380"
|
||||
className="h-full min-h-[380px] w-full rounded-none border-0"
|
||||
@@ -38,6 +40,7 @@ export function SpecialSaleCarousel() {
|
||||
price={product.price}
|
||||
oldPrice={product.oldPrice}
|
||||
discount={product.discount}
|
||||
image={product.image}
|
||||
variant="sale"
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Placeholder } from "@/components/ui/Placeholder";
|
||||
|
||||
const banners = [
|
||||
{ title: "ماشین لباسشویی", subtitle: "از ۲۸ میلیون", label: "420×160" },
|
||||
{ title: "جاروبرقی", subtitle: "ارسال فوری", label: "420×160" },
|
||||
{ title: "لوازم آشپزخانه", subtitle: "تا ۲۰٪ تخفیف", label: "420×160" },
|
||||
{ title: "ماشین لباسشویی", subtitle: "از ۲۸ میلیون", label: "420×160", image: "/images/promo-1.svg" },
|
||||
{ title: "جاروبرقی", subtitle: "ارسال فوری", label: "420×160", image: "/images/promo-2.svg" },
|
||||
{ title: "لوازم آشپزخانه", subtitle: "تا ۲۰٪ تخفیف", label: "420×160", image: "/images/promo-3.svg" },
|
||||
];
|
||||
|
||||
export function ThreeBanners() {
|
||||
@@ -18,6 +18,8 @@ export function ThreeBanners() {
|
||||
className="group relative overflow-hidden rounded-xl border border-border bg-surface transition-all hover:border-red hover:shadow-md"
|
||||
>
|
||||
<Placeholder
|
||||
src={banner.image}
|
||||
alt={banner.title}
|
||||
width="100%"
|
||||
height="160px"
|
||||
label={banner.label}
|
||||
|
||||
@@ -19,6 +19,7 @@ export function TopSellingCarousel() {
|
||||
name={product.name}
|
||||
brand={product.brand}
|
||||
price={product.price}
|
||||
image={product.image}
|
||||
/>
|
||||
))}
|
||||
</Carousel>
|
||||
|
||||
@@ -9,7 +9,14 @@ export function TwoBanners() {
|
||||
href="#"
|
||||
className="group relative overflow-hidden rounded-2xl border border-border bg-surface transition-shadow hover:shadow-lg"
|
||||
>
|
||||
<Placeholder width="100%" height="200px" label="640×200" className="w-full rounded-none border-0" />
|
||||
<Placeholder
|
||||
src="/images/banner-1.svg"
|
||||
alt="تخفیف ویژه یخچال و فریزر"
|
||||
width="100%"
|
||||
height="200px"
|
||||
label="640×200"
|
||||
className="w-full rounded-none border-0"
|
||||
/>
|
||||
<div className="absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-red-dark/80 to-transparent p-6">
|
||||
<p className="font-display text-xs tracking-widest text-red-muted uppercase">
|
||||
Summer Sale
|
||||
@@ -22,7 +29,14 @@ export function TwoBanners() {
|
||||
href="#"
|
||||
className="group relative overflow-hidden rounded-2xl border border-border bg-surface transition-shadow hover:shadow-lg"
|
||||
>
|
||||
<Placeholder width="100%" height="200px" label="640×200" className="w-full rounded-none border-0" />
|
||||
<Placeholder
|
||||
src="/images/banner-2.svg"
|
||||
alt="تلویزیونهای هوشمند ۲۰۲۶"
|
||||
width="100%"
|
||||
height="200px"
|
||||
label="640×200"
|
||||
className="w-full rounded-none border-0"
|
||||
/>
|
||||
<div className="absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-red-dark/80 to-transparent p-6">
|
||||
<p className="font-display text-xs tracking-widest text-red-muted uppercase">
|
||||
Smart TV
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
type PlaceholderProps = {
|
||||
src: string;
|
||||
alt?: string;
|
||||
width: string;
|
||||
height?: string;
|
||||
label?: string;
|
||||
@@ -6,6 +8,8 @@ type PlaceholderProps = {
|
||||
};
|
||||
|
||||
export function Placeholder({
|
||||
src,
|
||||
alt = "",
|
||||
width,
|
||||
height,
|
||||
label,
|
||||
@@ -15,12 +19,17 @@ export function Placeholder({
|
||||
<div
|
||||
className={`relative flex shrink-0 items-center justify-center overflow-hidden border border-dashed border-red-muted/60 bg-red-light/40 ${className}`}
|
||||
style={{ width, ...(height ? { height } : {}) }}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="absolute inset-0 bg-[linear-gradient(135deg,transparent_45%,rgba(220,38,38,0.06)_50%,transparent_55%)]" />
|
||||
<span className="font-display relative z-10 text-xs tracking-wider text-red-dark/50 uppercase">
|
||||
{label ?? `${width} × ${height ?? ""}`}
|
||||
</span>
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
{label && (
|
||||
<span className="font-display pointer-events-none relative z-10 rounded bg-white/70 px-1.5 py-0.5 text-xs tracking-wider text-red-dark/50 uppercase">
|
||||
{label}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ type ProductCardProps = {
|
||||
price: number;
|
||||
oldPrice?: number;
|
||||
discount?: number;
|
||||
image: string;
|
||||
variant?: "sale" | "default" | "brand";
|
||||
};
|
||||
|
||||
@@ -16,12 +17,20 @@ export function ProductCard({
|
||||
price,
|
||||
oldPrice,
|
||||
discount,
|
||||
image,
|
||||
variant = "default",
|
||||
}: ProductCardProps) {
|
||||
return (
|
||||
<article className="group w-[220px] shrink-0 overflow-hidden rounded-xl border border-border bg-surface transition-shadow hover:shadow-md md:w-[240px]">
|
||||
<div className="relative">
|
||||
<Placeholder width="100%" height="200px" label="240×200" className="w-full rounded-none border-0 border-b border-dashed" />
|
||||
<Placeholder
|
||||
src={image}
|
||||
alt={name}
|
||||
width="100%"
|
||||
height="200px"
|
||||
label="240×200"
|
||||
className="w-full rounded-none border-0 border-b border-dashed"
|
||||
/>
|
||||
{discount && (
|
||||
<span className="absolute top-3 right-3 rounded-md bg-red px-2 py-0.5 text-xs font-bold text-white">
|
||||
{discount}٪
|
||||
|
||||
Reference in New Issue
Block a user