"use client" import Link from '../LocalizedLink' import Image from 'next/image' import { useState, useRef, useEffect } from 'react' import { ArrowRight, ChevronRight, Star, Sparkles, Box, } from 'lucide-react' import type { MegaMenuPayload, MenuColumn } from './menuTypes' import { getLocalizedMenuText } from './menuTypes' import { ICON_REGISTRY, type AvailableIcons } from './iconRegistry' function DynamicIcon({ iconName, defaultIcon: DefaultIcon, className, strokeWidth }: { iconName?: string, defaultIcon: any, className?: string, strokeWidth?: number }) { const IconComponent = (iconName && ICON_REGISTRY[iconName as AvailableIcons]) ? ICON_REGISTRY[iconName as AvailableIcons] : DefaultIcon; return ; } interface DesktopMegaMenuProps { items: MegaMenuPayload[] lang: string currentPath: string } const MAX_TOP_LEVEL_LINKS = 5 const MAX_INLINE_CHILD_LINKS = 3 const getMoreLabel = (count: number, lang: string) => { if (lang === 'th') return `+ อีก ${count} รายการ` if (lang === 'ja') return `+ 他${count}件` if (lang === 'zh') return `+ 还有 ${count} 项` if (lang === 'vi') return `+ ${count} mục khác` if (lang === 'ko') return `+ ${count}개 더` return `+ ${count} more` } // Render a 1:1 image using next/image for optimization (website uses CDN images) function MenuImage({ src, alt, placeholder, className = '', style, }: { src?: string | null alt: string placeholder?: string className?: string style?: React.CSSProperties }) { const [loaded, setLoaded] = useState(false) if (!src) { if (!placeholder) return null return ( {placeholder} ) } return ( setLoaded(true)} loading="lazy" /> ) } export function DesktopMegaMenu({ items, lang, currentPath }: DesktopMegaMenuProps) { const [openId, setOpenId] = useState(null) const [hoveredLinkImage, setHoveredLinkImage] = useState(null) const [activeTabByMenu, setActiveTabByMenu] = useState>({}) const closeTimer = useRef | null>(null) // Automatically close mega menu when route changes useEffect(() => { if (closeTimer.current) clearTimeout(closeTimer.current) setOpenId(null) setHoveredLinkImage(null) }, [currentPath]) const handleOpen = (id: string) => { if (closeTimer.current) clearTimeout(closeTimer.current) if (openId !== id) setHoveredLinkImage(null) setOpenId(id) } const handleClose = () => { closeTimer.current = setTimeout(() => { setOpenId(null) setHoveredLinkImage(null) }, 150) } const handleLinkClick = () => { if (closeTimer.current) clearTimeout(closeTimer.current) setOpenId(null) setHoveredLinkImage(null) } const getFirstImageLink = (columns: MenuColumn[] = []) => { for (const column of columns) { const found = column.links.find((link) => link.imageUrl) if (found) return found } return columns[0]?.links[0] } return ( {items.map((item, index) => { const uniqueId = item.nodeId || item.id || `menu-${item.url || index}` const isOpen = openId === uniqueId const hasColumns = !!item.columns?.length const isActive = item.url ? currentPath === item.url : false const layout = item.layout || 'columns' const isClassic = layout === 'classic' const isTabbed = layout === 'tabbed' const isVisualCards = layout === 'visual-cards' const configuredCols = item.layoutConfig?.columnCount || (item.columns?.length ? Math.min(Math.max(item.columns.length, 2), 4) : 2) const visibleColumns = item.columns?.slice(0, configuredCols) ?? [] const featuredLink = getFirstImageLink(item.columns) const featuredConfig = item.featured const showFeatured = (item.layoutConfig?.showFeatured !== false) && (featuredConfig?.enabled !== false) && !isClassic && !isVisualCards && !isTabbed && ( !!(featuredConfig?.imageUrl || featuredConfig?.title || featuredConfig?.sectionTitle || featuredLink?.imageUrl) ) const configuredImage = featuredConfig?.imageUrl || null const featuredImage = hoveredLinkImage ?? configuredImage ?? featuredLink?.imageUrl ?? item.rootImageUrl const focalPoint = featuredConfig?.focalPoint const objectPosition = focalPoint ? `${focalPoint.x} ${focalPoint.y}` : 'center' const featuredHeading = getLocalizedMenuText(featuredConfig?.sectionTitle, lang) || 'Featured' const featuredBadge = getLocalizedMenuText(featuredConfig?.badge, lang) || 'Recommended' const featuredTitle = getLocalizedMenuText(featuredConfig?.title, lang) || getLocalizedMenuText(featuredLink?.label, lang) || getLocalizedMenuText(item.label, lang) const featuredSubtitle = getLocalizedMenuText(featuredConfig?.subtitle, lang) || (item.columns?.[0] ? getLocalizedMenuText(item.columns[0].title, lang) : getLocalizedMenuText(item.label, lang)) const featuredHref = featuredConfig?.url || featuredLink?.url || item.url || '#' const featuredCta = getLocalizedMenuText(featuredConfig?.ctaLabel, lang) || 'View details' const getMegaContainerClass = (cols: number, hasFeatured: boolean) => { if (hasFeatured) { if (cols >= 4) return 'w-[min(100vw-2rem,1260px)]' if (cols === 3) return 'w-[min(100vw-2rem,1120px)]' return 'w-[min(100vw-2rem,980px)]' } if (cols >= 4) return 'w-[min(100vw-2rem,1180px)]' if (cols === 3) return 'w-[min(100vw-2rem,960px)]' return 'w-[min(100vw-2rem,720px)]' } const getMegaGridColsClass = (cols: number, hasFeatured: boolean) => { if (hasFeatured) { if (cols >= 4) return 'grid-cols-[1fr_1fr_1fr_1fr_260px]' if (cols === 3) return 'grid-cols-[1fr_1fr_1fr_270px]' return 'grid-cols-[1fr_1fr_270px]' } if (cols >= 4) return 'grid-cols-4' if (cols === 3) return 'grid-cols-3' return 'grid-cols-2' } return ( handleOpen(uniqueId)} onMouseLeave={handleClose} > {getLocalizedMenuText(item.label, lang)} {hasColumns && ( )} {hasColumns && isClassic && ( handleOpen(uniqueId)} onMouseLeave={handleClose} > {item.columns?.map((column, columnIndex) => ( 0 ? 'mt-2 border-t border-line-primary/10 pt-2' : ''}> {getLocalizedMenuText(column.title, lang) && item.columns && item.columns.length > 1 && ( {getLocalizedMenuText(column.title, lang)} )} {column.links.map((link, linkIndex) => ( {link.icon && ( )} {getLocalizedMenuText(link.label, lang)} {link.links?.length ? ( ) : null} {link.links && link.links.length > 0 && ( {link.links.map((child, childIdx) => ( {getLocalizedMenuText(child.label, lang)} ))} )} ))} ))} )} {hasColumns && isTabbed && ( handleOpen(uniqueId)} onMouseLeave={handleClose} > {/* Left Category Tabs Sidebar */} {lang === 'th' ? 'หมวดหมู่หลัก' : 'Categories'} {item.columns?.map((column, colIdx) => { const activeIdx = activeTabByMenu[uniqueId] ?? 0 const isSelected = activeIdx === colIdx return ( { setActiveTabByMenu(prev => ({ ...prev, [uniqueId]: colIdx })) setHoveredLinkImage(null) }} className={`flex w-full items-center justify-between rounded-lg px-3.5 py-2.5 text-left transition-all ${ isSelected ? 'bg-brand-primary/10 text-brand-primary font-bold shadow-sm' : 'text-text-main/80 hover:bg-surface-muted hover:text-text-main font-medium' }`} > {getLocalizedMenuText(column.title, lang)} ) })} {/* Right Content Panel for Selected Tab */} {(() => { const activeIdx = activeTabByMenu[uniqueId] ?? 0 const activeCol = item.columns?.[activeIdx] || item.columns?.[0] if (!activeCol) return null return ( {getLocalizedMenuText(activeCol.title, lang)} {lang === 'th' ? 'ดูรายการทั้งหมด' : 'View all'} {activeCol.links.map((link, lIdx) => ( setHoveredLinkImage(link.imageUrl || null)} onMouseLeave={() => setHoveredLinkImage(null)} > {getLocalizedMenuText(link.label, lang)} {link.links && link.links.length > 0 && ( {link.links.slice(0, 3).map((child, cIdx) => ( {getLocalizedMenuText(child.label, lang)} ))} )} ))} {item.columns?.length || 0} {lang === 'th' ? 'หมวดหมู่ย่อย' : 'categories'} {getLocalizedMenuText(item.label, lang)} ) })()} )} {hasColumns && isVisualCards && ( handleOpen(uniqueId)} onMouseLeave={handleClose} > {getLocalizedMenuText(item.label, lang)} {lang === 'th' ? 'ดูทั้งหมด' : 'Explore all'} {item.columns?.slice(0, 4).map((column, colIdx) => { const cardImg = column.iconUrl || column.links?.[0]?.imageUrl || configuredImage || item.rootImageUrl const cardHref = column.links?.[0]?.url || item.url || '#' const cardTitle = getLocalizedMenuText(column.title, lang) const firstSubLink = column.links?.[0] const subText = firstSubLink ? getLocalizedMenuText(firstSubLink.label, lang) : null return ( {cardTitle} {subText && ( {subText} )} ) })} )} {hasColumns && !isClassic && !isTabbed && !isVisualCards && ( handleOpen(uniqueId)} onMouseLeave={handleClose} > {visibleColumns.map((column, columnIndex) => ( 0 ? 'border-l border-line-primary/15 pl-8' : ''}`} > {getLocalizedMenuText(column.title, lang)} {column.links.slice(0, MAX_TOP_LEVEL_LINKS).map((link, linkIndex) => { const hasNestedLinks = !!link.links?.length const visibleChildLinks = link.links?.slice(0, MAX_INLINE_CHILD_LINKS) ?? [] const hiddenChildCount = Math.max((link.links?.length ?? 0) - MAX_INLINE_CHILD_LINKS, 0) const moreChildHref = link.url || link.links?.[MAX_INLINE_CHILD_LINKS]?.url || link.links?.[0]?.url || '#' return ( setHoveredLinkImage(link.imageUrl || null)} onMouseLeave={() => setHoveredLinkImage(null)} > {getLocalizedMenuText(link.label, lang)} {getLocalizedMenuText(column.title, lang)} {hasNestedLinks && ( {visibleChildLinks.map((childLink, childIndex) => ( setHoveredLinkImage(childLink.imageUrl || link.imageUrl || null)} > {getLocalizedMenuText(childLink.label, lang)} ))} {hiddenChildCount > 0 && ( {getMoreLabel(hiddenChildCount, lang)} )} )} ) })} {getLocalizedMenuText(item.label, lang)} ))} {showFeatured && ( {featuredHeading} setHoveredLinkImage(configuredImage ?? featuredLink?.imageUrl ?? null)} onMouseLeave={() => setHoveredLinkImage(null)} > {featuredBadge && ( {featuredBadge} )} {featuredTitle} {featuredSubtitle && ( {featuredSubtitle} )} {featuredConfig?.price && ( {featuredConfig.price} )} {featuredCta} )} )} ) })} ) }
{featuredTitle}
{featuredSubtitle}
{featuredConfig.price}