Merge version_3_1776752954568 into main
Merge version_3_1776752954568 into main
This commit was merged in pull request #2.
This commit is contained in:
60
src/App.tsx
60
src/App.tsx
@@ -3,7 +3,7 @@ import ContactSplitEmail from '@/components/sections/contact/ContactSplitEmail';
|
||||
import FaqSimple from '@/components/sections/faq/FaqSimple';
|
||||
import FeaturesTaggedCards from '@/components/sections/features/FeaturesTaggedCards';
|
||||
import FooterSimpleMedia from '@/components/sections/footer/FooterSimpleMedia';
|
||||
import HeroBillboardScroll from '@/components/sections/hero/HeroBillboardScroll';
|
||||
import HeroBillboardCarousel from '@/components/sections/hero/HeroBillboardCarousel';
|
||||
import NavbarCentered from '@/components/ui/NavbarCentered';
|
||||
import ProductVariantCards from '@/components/sections/product/ProductVariantCards';
|
||||
import TestimonialAvatarCard from '@/components/sections/testimonial/TestimonialAvatarCard';
|
||||
@@ -40,19 +40,51 @@ export default function App() {
|
||||
</div>
|
||||
|
||||
<div id="hero" data-section="hero">
|
||||
<HeroBillboardScroll
|
||||
tag="Fusion Takeout"
|
||||
title="Seoul Meets Italy in Every Bite"
|
||||
description="Experience the bold heat of Korea blended with the timeless comfort of Italian pasta. Order your fusion favorites for quick, hot takeout today."
|
||||
primaryButton={{
|
||||
text: "Browse Menu",
|
||||
href: "#menu",
|
||||
}}
|
||||
secondaryButton={{
|
||||
text: "Our Story",
|
||||
href: "#about",
|
||||
}}
|
||||
imageSrc="http://img.b2bpic.net/free-photo/pasta-with-germany-sausage_1339-5598.jpg"
|
||||
<HeroBillboardCarousel
|
||||
slides={[
|
||||
{
|
||||
tag: "Fusion Takeout",
|
||||
title: "Seoul Meets Italy in Every Bite",
|
||||
description: "Experience the bold heat of Korea blended with the timeless comfort of Italian pasta. Order your fusion favorites for quick, hot takeout today.",
|
||||
primaryButton: {
|
||||
text: "Browse Menu",
|
||||
href: "#menu",
|
||||
},
|
||||
secondaryButton: {
|
||||
text: "Our Story",
|
||||
href: "#about",
|
||||
},
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/pasta-with-germany-sausage_1339-5598.jpg",
|
||||
},
|
||||
{
|
||||
tag: "Signature Dishes",
|
||||
title: "Discover Our Kimchi Carbonara",
|
||||
description: "A creamy, spicy twist on a classic. The ultimate comfort food with a Korean kick. You have to try it to believe it.",
|
||||
primaryButton: {
|
||||
text: "View Dish",
|
||||
href: "#menu",
|
||||
},
|
||||
secondaryButton: {
|
||||
text: "Full Menu",
|
||||
href: "#menu",
|
||||
},
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/delicious-pasta-fork_23-2147749504.jpg",
|
||||
},
|
||||
{
|
||||
tag: "Fast & Fresh",
|
||||
title: "Hot, Fresh, and Ready For You",
|
||||
description: "We prepare every dish with the freshest ingredients and deliver it piping hot to your door. Quality you can taste.",
|
||||
primaryButton: {
|
||||
text: "Order Delivery",
|
||||
href: "#contact",
|
||||
},
|
||||
secondaryButton: {
|
||||
text: "Why Choose Us",
|
||||
href: "#features",
|
||||
},
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/top-view-plastic-box-with-leftover-cookie_23-2148666825.jpg",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,69 +1,96 @@
|
||||
"use client";
|
||||
|
||||
import useEmblaCarousel from "embla-carousel-react";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { motion } from "motion/react";
|
||||
|
||||
import Button from "@/components/ui/Button";
|
||||
import TextAnimation from "@/components/ui/TextAnimation";
|
||||
import ImageOrVideo from "@/components/ui/ImageOrVideo";
|
||||
import { useCarouselControls } from "@/hooks/useCarouselControls";
|
||||
|
||||
type HeroBillboardCarouselProps = {
|
||||
type Slide = {
|
||||
tag: string;
|
||||
title: string;
|
||||
description: string;
|
||||
primaryButton: { text: string; href: string };
|
||||
secondaryButton: { text: string; href: string };
|
||||
items: ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never })[];
|
||||
} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
|
||||
|
||||
type HeroBillboardCarouselProps = {
|
||||
slides: Slide[];
|
||||
};
|
||||
|
||||
const HeroBillboardCarousel = ({
|
||||
tag,
|
||||
title,
|
||||
description,
|
||||
primaryButton,
|
||||
secondaryButton,
|
||||
items,
|
||||
}: HeroBillboardCarouselProps) => {
|
||||
const duplicated = [...items, ...items, ...items, ...items];
|
||||
const HeroBillboardCarousel = ({ slides }: HeroBillboardCarouselProps) => {
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true });
|
||||
const { prevDisabled, nextDisabled, scrollPrev, scrollNext, scrollProgress } = useCarouselControls(emblaApi);
|
||||
|
||||
return (
|
||||
<section
|
||||
aria-label="Hero section"
|
||||
className="flex flex-col items-center justify-center gap-8 w-full min-h-svh py-25"
|
||||
>
|
||||
<div className="flex flex-col items-center gap-3 w-content-width mx-auto text-center">
|
||||
<span className="px-3 py-1 mb-1 text-sm card rounded">{tag}</span>
|
||||
|
||||
<TextAnimation
|
||||
text={title}
|
||||
variant="slide-up"
|
||||
tag="h1"
|
||||
className="text-6xl font-medium text-balance"
|
||||
/>
|
||||
|
||||
<TextAnimation
|
||||
text={description}
|
||||
variant="slide-up"
|
||||
tag="p"
|
||||
className="text-base md:text-lg leading-tight text-balance"
|
||||
/>
|
||||
|
||||
<div className="flex flex-wrap justify-center gap-3 mt-2">
|
||||
<Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />
|
||||
<Button text={secondaryButton.text} href={secondaryButton.href} variant="secondary" animate delay={0.1} />
|
||||
<section aria-label="Hero section" className="relative">
|
||||
<div className="overflow-hidden" ref={emblaRef}>
|
||||
<div className="flex">
|
||||
{slides.map((slide, index) => (
|
||||
<div className="flex-[0_0_100%] min-w-0 relative h-screen" key={index}>
|
||||
<div className="absolute inset-0 bg-black/40 z-10" />
|
||||
<ImageOrVideo
|
||||
imageSrc={slide.imageSrc}
|
||||
videoSrc={slide.videoSrc}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 z-20 flex flex-col justify-center items-center text-center text-foreground p-6">
|
||||
<div className="w-content-width mx-auto flex flex-col items-center gap-3">
|
||||
<span className="px-3 py-1 mb-1 text-sm bg-white/10 backdrop-blur-sm border border-white/20 rounded">{slide.tag}</span>
|
||||
<TextAnimation
|
||||
text={slide.title}
|
||||
variant="slide-up"
|
||||
tag="h1"
|
||||
className="text-6xl font-medium text-balance"
|
||||
/>
|
||||
<TextAnimation
|
||||
text={slide.description}
|
||||
variant="slide-up"
|
||||
tag="p"
|
||||
className="text-base md:text-lg leading-tight text-balance max-w-3xl"
|
||||
/>
|
||||
<div className="flex flex-wrap justify-center gap-3 mt-2">
|
||||
<Button text={slide.primaryButton.text} href={slide.primaryButton.href} variant="primary" animateImmediately />
|
||||
<Button text={slide.secondaryButton.text} href={slide.secondaryButton.href} variant="secondary" animateImmediately delay={0.1} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-content-width mx-auto overflow-hidden mask-fade-x">
|
||||
<div className="flex w-max animate-marquee-horizontal" style={{ animationDuration: "60s" }}>
|
||||
{duplicated.map((item, i) => (
|
||||
<div key={i} className="shrink-0 w-60 md:w-75 2xl:w-80 aspect-4/5 mr-3 md:mr-5 p-1.5 card rounded-lg overflow-hidden">
|
||||
<ImageOrVideo
|
||||
imageSrc={item.imageSrc}
|
||||
videoSrc={item.videoSrc}
|
||||
className="w-full h-full rounded-lg object-cover"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 z-20 w-content-width mx-auto flex items-center justify-between px-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={scrollPrev}
|
||||
disabled={prevDisabled}
|
||||
className="size-10 flex items-center justify-center bg-white/10 backdrop-blur-sm border border-white/20 rounded-full text-foreground disabled:opacity-50 transition-opacity"
|
||||
aria-label="Previous slide"
|
||||
>
|
||||
<ChevronLeft className="size-5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={scrollNext}
|
||||
disabled={nextDisabled}
|
||||
className="size-10 flex items-center justify-center bg-white/10 backdrop-blur-sm border border-white/20 rounded-full text-foreground disabled:opacity-50 transition-opacity"
|
||||
aria-label="Next slide"
|
||||
>
|
||||
<ChevronRight className="size-5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="w-full max-w-xs h-1 bg-white/20 rounded-full overflow-hidden">
|
||||
<motion.div
|
||||
className="h-full bg-background"
|
||||
style={{ width: `${scrollProgress}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeroBillboardCarousel;
|
||||
export default HeroBillboardCarousel;
|
||||
Reference in New Issue
Block a user