Build Kozmos QR menu site — full implementation
Next.js 16 App Router, TR/EN i18n via [lang] routing and proxy.ts, Tailwind v4, minimal list menu style, sticky bottom tab bar with Intersection Observer, PWA manifest, WhatsApp fixed button. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
f879d65cfc
commit
718e0a6228
@@ -0,0 +1,72 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { Category } from '@/data/menu'
|
||||
|
||||
type Props = {
|
||||
categories: Category[]
|
||||
lang: string
|
||||
}
|
||||
|
||||
export default function BottomTabBar({ categories, lang }: Props) {
|
||||
const l = lang as 'tr' | 'en'
|
||||
const [activeId, setActiveId] = useState<string>(categories[0]?.id ?? '')
|
||||
|
||||
useEffect(() => {
|
||||
const observers: IntersectionObserver[] = []
|
||||
|
||||
categories.forEach((cat) => {
|
||||
const el = document.getElementById(cat.id)
|
||||
if (!el) return
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
setActiveId(cat.id)
|
||||
}
|
||||
})
|
||||
},
|
||||
{ rootMargin: '-20% 0px -75% 0px', threshold: 0 }
|
||||
)
|
||||
|
||||
observer.observe(el)
|
||||
observers.push(observer)
|
||||
})
|
||||
|
||||
return () => {
|
||||
observers.forEach((obs) => obs.disconnect())
|
||||
}
|
||||
}, [categories])
|
||||
|
||||
return (
|
||||
<nav
|
||||
className="fixed bottom-0 left-0 right-0 z-40 bg-coffee border-t border-coffee-light"
|
||||
style={{ height: 'var(--tabbar-h)' }}
|
||||
>
|
||||
<div className="flex items-start justify-around pt-2.5 h-full">
|
||||
{categories.map((cat) => {
|
||||
const isActive = activeId === cat.id
|
||||
return (
|
||||
<a
|
||||
key={cat.id}
|
||||
href={`#${cat.id}`}
|
||||
className={`tab-item flex flex-col items-center gap-0.5 flex-1 py-0.5 ${
|
||||
isActive ? 'text-teal' : 'text-cream/35'
|
||||
}`}
|
||||
>
|
||||
<span className="text-lg leading-none">{cat.emoji}</span>
|
||||
<span className="font-body text-[0.55rem] font-medium leading-none tracking-wide text-center px-0.5 truncate w-full text-center">
|
||||
{cat.label[l]}
|
||||
</span>
|
||||
<div
|
||||
className="tab-active-dot"
|
||||
style={{ opacity: isActive ? 1 : 0 }}
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user