137 lines
4.6 KiB
TypeScript
137 lines
4.6 KiB
TypeScript
'use client';
|
|
|
|
import type { MenuItem } from '@/domain/menu/entities';
|
|
import WishlistButton from '@/components/WishlistButton';
|
|
import {
|
|
applyNextImageFallback,
|
|
getMenuPosterCandidates,
|
|
getMenuPosterSrc,
|
|
} from '@/lib/assets';
|
|
|
|
export type SignatureDish = MenuItem & {
|
|
desc: string;
|
|
};
|
|
|
|
interface SignatureMenuMarqueeProps {
|
|
dishes: SignatureDish[];
|
|
addToCartLabel: string;
|
|
perHalfKgLabel: string;
|
|
perKgLabel: string;
|
|
onAdd: (dish: SignatureDish) => void;
|
|
}
|
|
|
|
function renderMarqueePrice(
|
|
item: MenuItem,
|
|
perHalfKgLabel: string,
|
|
perKgLabel: string
|
|
) {
|
|
if (item.pricing === 'weight') {
|
|
return (
|
|
<div className="shrink-0 text-right">
|
|
<div className="text-[#B38B4D] text-lg font-medium tracking-[-0.5px] tabular-nums leading-tight">
|
|
{item.pricePerHalfKg ?? item.price}
|
|
</div>
|
|
<div className="text-[10px] text-[#8A8478] tracking-wide">{perHalfKgLabel}</div>
|
|
<div className="text-[#B38B4D] text-sm font-medium tabular-nums mt-0.5">
|
|
{item.pricePerKg} {perKgLabel}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="shrink-0 text-right">
|
|
<div className="text-[#B38B4D] text-xl font-medium tracking-[-0.5px] tabular-nums">
|
|
{item.price}
|
|
</div>
|
|
<div className="text-[10px] text-[#8A8478] tracking-widest -mt-1">KR</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const CARD_WIDTH = 240;
|
|
const CARD_GAP = 20;
|
|
|
|
export default function SignatureMenuMarquee({
|
|
dishes,
|
|
addToCartLabel,
|
|
perHalfKgLabel,
|
|
perKgLabel,
|
|
onAdd,
|
|
}: SignatureMenuMarqueeProps) {
|
|
if (dishes.length === 0) return null;
|
|
|
|
const reelItems = [...dishes, ...dishes];
|
|
const trackWidth = reelItems.length * (CARD_WIDTH + CARD_GAP);
|
|
const durationSeconds = Math.max(50, dishes.length * 2.8);
|
|
|
|
return (
|
|
<div className="signature-menu-reel relative -mx-6 overflow-hidden lg:-mx-8">
|
|
<div
|
|
aria-hidden
|
|
className="pointer-events-none absolute inset-y-0 left-0 z-10 w-16 bg-gradient-to-r from-[#fffdf8] to-transparent sm:w-24"
|
|
/>
|
|
<div
|
|
aria-hidden
|
|
className="pointer-events-none absolute inset-y-0 right-0 z-10 w-16 bg-gradient-to-l from-[#fffdf8] to-transparent sm:w-24"
|
|
/>
|
|
|
|
<div
|
|
className="signature-menu-reel-track flex w-max gap-5 py-2 pl-6 lg:pl-8"
|
|
style={{
|
|
width: `${trackWidth}px`,
|
|
animationDuration: `${durationSeconds}s`,
|
|
}}
|
|
>
|
|
{reelItems.map((dish, index) => (
|
|
<article
|
|
key={`${dish.id}-${index}`}
|
|
className="signature-card group flex w-[240px] shrink-0 flex-col overflow-hidden rounded-[1.75rem] border border-[#EDE6D9] bg-white shadow-sm transition-shadow hover:border-[#c99a2e]/45 hover:shadow-md"
|
|
>
|
|
<div className="relative h-40 overflow-hidden bg-[#F2EDE4]">
|
|
<img
|
|
src={getMenuPosterSrc(dish)}
|
|
alt={dish.name}
|
|
className="signature-image h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
|
|
loading="lazy"
|
|
onError={(e) => {
|
|
applyNextImageFallback(
|
|
e.target as HTMLImageElement,
|
|
getMenuPosterCandidates(dish),
|
|
(e.target as HTMLImageElement).src
|
|
);
|
|
}}
|
|
/>
|
|
<div className="absolute inset-0 bg-gradient-to-b from-black/5 via-transparent to-black/20" />
|
|
<div className="absolute top-3 left-3 z-10">
|
|
<WishlistButton
|
|
item={{ id: dish.id, name: dish.name, price: dish.price, image: dish.image }}
|
|
size="sm"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-1 flex-col p-4">
|
|
<div className="mb-2 flex items-start justify-between gap-3">
|
|
<h4 className="min-w-0 line-clamp-2 text-[17px] leading-tight tracking-[-0.3px] text-[#101724] group-hover:text-[#B38B4D]">
|
|
{dish.name}
|
|
</h4>
|
|
{renderMarqueePrice(dish, perHalfKgLabel, perKgLabel)}
|
|
</div>
|
|
<p className="mb-3 line-clamp-2 flex-1 text-[12px] leading-snug text-[#6B665F]">
|
|
{dish.desc}
|
|
</p>
|
|
<button
|
|
type="button"
|
|
onClick={() => onAdd(dish)}
|
|
className="w-full rounded-full border border-[#B38B4D] py-2.5 text-xs font-medium tracking-wide text-[#B38B4D] transition-all hover:bg-[#B38B4D] hover:text-white active:scale-[0.98]"
|
|
>
|
|
{addToCartLabel}
|
|
</button>
|
|
</div>
|
|
</article>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
} |