Deploy
This commit is contained in:
@@ -0,0 +1,128 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState } from 'react'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import { Flame } from 'lucide-react'
|
||||||
|
import { useVulaContent } from '@/hooks/useVulaContent'
|
||||||
|
import Modal from '@/components/ui/Modal'
|
||||||
|
|
||||||
|
type GalleryEntry = {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
image: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MadadeniFoodGallerySection() {
|
||||||
|
const { items, loading } = useVulaContent('gallery')
|
||||||
|
const [active, setActive] = useState<GalleryEntry | null>(null)
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center py-16">
|
||||||
|
<div className="w-8 h-8 rounded-full border-2 border-current border-t-transparent animate-spin" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.length === 0) return null
|
||||||
|
|
||||||
|
const gallery: GalleryEntry[] = items.map((item) => {
|
||||||
|
const d = item.data as { title?: string; description?: string; image?: string }
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
title: d.title ?? '',
|
||||||
|
description: d.description ?? '',
|
||||||
|
image: d.image ?? '',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
id="food-venue-gallery"
|
||||||
|
data-vula-section="madadenifoodgallery"
|
||||||
|
data-vula-cms="gallery"
|
||||||
|
className="bg-background text-foreground py-24 sm:py-32"
|
||||||
|
>
|
||||||
|
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||||
|
<div className="mx-auto max-w-3xl text-center">
|
||||||
|
<div className="flex items-center justify-center gap-4">
|
||||||
|
<span className="h-px w-14 bg-accent" aria-hidden="true" />
|
||||||
|
<span className="text-[11px] uppercase tracking-[0.3em] text-accent">Madadeni Section 4, Newcastle</span>
|
||||||
|
<span className="h-px w-14 bg-accent" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
<h2 className="font-display mt-6 text-3xl font-light leading-tight tracking-tight sm:text-4xl lg:text-5xl">
|
||||||
|
Smoke off the wok, and a box still <span className="italic text-accent">too hot to hold</span>.
|
||||||
|
</h2>
|
||||||
|
<p className="mx-auto mt-6 max-w-2xl text-base leading-relaxed text-muted-foreground sm:text-lg">
|
||||||
|
None of this was shot in a studio. These are the plates that leave the corner, the flames they come off, and the Section 4 space where it all happens.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-16 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{gallery.map((entry, i) => (
|
||||||
|
<button
|
||||||
|
key={entry.id}
|
||||||
|
type="button"
|
||||||
|
onClick={() => setActive(entry)}
|
||||||
|
aria-label={entry.title ? 'Open ' + entry.title + ' full screen' : 'Open photo full screen'}
|
||||||
|
className={'group relative block cursor-pointer overflow-hidden rounded-lg border border-foreground/20 text-left transition-all duration-200 ease-out hover:border-accent/60 ' + (i === 0 ? 'sm:col-span-2' : '')}
|
||||||
|
>
|
||||||
|
<div className={'relative w-full ' + (i === 0 ? 'aspect-[16/9]' : 'aspect-[4/3]')}>
|
||||||
|
{entry.image ? (
|
||||||
|
<Image
|
||||||
|
src={entry.image}
|
||||||
|
alt={entry.title || 'Food from the wok'}
|
||||||
|
fill
|
||||||
|
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||||
|
className="object-cover transition-transform duration-500 ease-out group-hover:scale-105"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full w-full items-center justify-center bg-foreground/5 text-muted-foreground">
|
||||||
|
<Flame className="h-10 w-10" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-background via-background/30 to-transparent" />
|
||||||
|
<div className="absolute inset-x-0 bottom-0 border-t border-foreground/20 bg-foreground/10 p-4 backdrop-blur-md">
|
||||||
|
<p className="font-display text-lg font-light text-foreground">{entry.title}</p>
|
||||||
|
{entry.description ? (
|
||||||
|
<p className="mt-1 text-sm leading-relaxed text-muted-foreground">{entry.description}</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="mt-10 text-center text-[11px] uppercase tracking-[0.3em] text-muted-foreground">
|
||||||
|
Tap a frame to open it full screen
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
isOpen={active !== null}
|
||||||
|
onClose={() => setActive(null)}
|
||||||
|
title={active?.title}
|
||||||
|
description={active?.description}
|
||||||
|
>
|
||||||
|
{active ? (
|
||||||
|
<div className="relative aspect-[4/3] w-full overflow-hidden rounded-lg border border-foreground/20 bg-foreground/10">
|
||||||
|
{active.image ? (
|
||||||
|
<Image
|
||||||
|
src={active.image}
|
||||||
|
alt={active.title || 'Food from the wok'}
|
||||||
|
fill
|
||||||
|
sizes="(max-width: 768px) 100vw, 900px"
|
||||||
|
className="object-cover"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
|
||||||
|
<Flame className="h-12 w-12" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</Modal>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user