diff --git a/src/app/(marketing)/about/client.tsx b/src/app/(marketing)/about/client.tsx
index 369407b..1ed04c9 100644
--- a/src/app/(marketing)/about/client.tsx
+++ b/src/app/(marketing)/about/client.tsx
@@ -73,7 +73,7 @@ export function AboutClient() {
transition={{ duration: 0.5 }}
className="p-8 rounded-xl border border-[#E5E5E5]"
>
-
关于 睿新致遠
+ 关于 {COMPANY_INFO.shortName}
智连未来,成长伙伴
企业需要的,不是一个高高在上的专家,也不是一个做完就跑的卖家,而是一个能坐下来、一起想办法的同行者。
@@ -86,10 +86,10 @@ export function AboutClient() {
成长伙伴
-
我们不把"项目交付"当作终点。
+
我们不把“项目交付”当作终点。
您的业务增长了吗?您的团队能力提升了吗?
您下一次遇到难题时,还会第一个想到我们吗?
-
这些问题,比"项目是否按时交付"更让我们在意。
+
这些问题,比“项目是否按时交付”更让我们在意。
@@ -113,7 +113,7 @@ export function AboutClient() {
✓
- 不做路过就忘的"一锤子买卖"
+ 不做路过就忘的“一锤子买卖”
diff --git a/src/app/(marketing)/about/page.test.tsx b/src/app/(marketing)/about/page.test.tsx
index 2d774b8..6b4752d 100644
--- a/src/app/(marketing)/about/page.test.tsx
+++ b/src/app/(marketing)/about/page.test.tsx
@@ -2,40 +2,42 @@ import { describe, it, expect, jest } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
+type MotionProps = { children: React.ReactNode; className?: string; [key: string]: unknown };
+
jest.mock('framer-motion', () => ({
motion: {
- div: ({ children, className, ...props }: any) => (
+ div: ({ children, className, ...props }: MotionProps) => (
{children}
),
- section: ({ children, className, ...props }: any) => (
+ section: ({ children, className, ...props }: MotionProps) => (
),
- span: ({ children, className, ...props }: any) => (
+ span: ({ children, className, ...props }: MotionProps) => (
{children}
),
- h1: ({ children, className, ...props }: any) => (
+ h1: ({ children, className, ...props }: MotionProps) => (
{children}
),
- h2: ({ children, className, ...props }: any) => (
+ h2: ({ children, className, ...props }: MotionProps) => (
{children}
),
},
- AnimatePresence: ({ children }: any) => <>{children}>,
+ AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}>,
useInView: () => [null, true],
}));
jest.mock('next/link', () => {
- const MockLink = ({ children, href, ...props }: any) => (
+ const MockLink = ({ children, href, ...props }: { children: React.ReactNode; href: string; [key: string]: unknown }) => (
{children}
@@ -54,40 +56,48 @@ jest.mock('lucide-react', () => ({
Phone: () => ,
}));
-jest.mock('@/components/ui/card', () => ({
- Card: ({ children, className, ...props }: any) => (
+jest.mock('@/components/ui/card', () => {
+ const Card = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
{children}
- ),
- CardContent: ({ children, className, ...props }: any) => (
+ );
+ const CardContent = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
{children}
- ),
-}));
+ );
+ Card.displayName = 'Card';
+ CardContent.displayName = 'CardContent';
+ return { Card, CardContent };
+});
-jest.mock('@/components/ui/page-header', () => ({
- PageHeader: ({ title, description }: any) => (
+jest.mock('@/components/ui/page-header', () => {
+ const PageHeader = ({ title, description }: { title: string; description?: string }) => (
- ),
-}));
+ );
+ PageHeader.displayName = 'PageHeader';
+ return { PageHeader };
+});
-jest.mock('@/components/ui/flip-clock', () => ({
- FlipClock: ({ years, months, days }: any) => (
+jest.mock('@/components/ui/flip-clock', () => {
+ const FlipClock = ({ years, months, days }: { years: number; months: number; days: number }) => (
{years}年 {months}月 {days}天
- ),
-}));
+ );
+ FlipClock.displayName = 'FlipClock';
+ return { FlipClock };
+});
jest.mock('@/lib/constants', () => ({
COMPANY_INFO: {
name: '四川睿新致远科技有限公司',
shortName: '睿新致遠',
+ displayName: '睿新致远',
description: '以智慧连接数字趋势,以伙伴身份陪您成长',
address: '四川省成都市龙泉驿区',
email: 'contact@ruixin.com',
diff --git a/src/app/(marketing)/about/page.tsx b/src/app/(marketing)/about/page.tsx
index 6d06ae2..0a28e8b 100644
--- a/src/app/(marketing)/about/page.tsx
+++ b/src/app/(marketing)/about/page.tsx
@@ -2,7 +2,7 @@ import { COMPANY_INFO } from '@/lib/constants';
import { AboutClient } from './client';
export const metadata = {
- title: `关于我们 - ${COMPANY_INFO.name}`,
+ title: `关于我们 - ${COMPANY_INFO.displayName}`,
description: `了解${COMPANY_INFO.name}的品牌故事。我们不只是技术供应商,更是您数字化转型的成长伙伴。以智慧连接数字趋势,以伙伴身份陪您成长。`,
};
diff --git a/src/app/(marketing)/contact/layout.tsx b/src/app/(marketing)/contact/layout.tsx
index 3c16dfb..20b8165 100644
--- a/src/app/(marketing)/contact/layout.tsx
+++ b/src/app/(marketing)/contact/layout.tsx
@@ -1,5 +1,7 @@
+import { COMPANY_INFO } from '@/lib/constants';
+
export const metadata = {
- title: '联系我们 - 四川睿新致远科技有限公司',
+ title: `联系我们 - ${COMPANY_INFO.displayName}`,
description: '无论您有任何问题或合作意向,我们都很乐意与您交流',
};
diff --git a/src/app/(marketing)/news/[slug]/page.tsx b/src/app/(marketing)/news/[slug]/page.tsx
index 8314004..3b3ba8c 100644
--- a/src/app/(marketing)/news/[slug]/page.tsx
+++ b/src/app/(marketing)/news/[slug]/page.tsx
@@ -1,5 +1,5 @@
import { notFound } from 'next/navigation';
-import { NEWS } from '@/lib/constants';
+import { NEWS, COMPANY_INFO } from '@/lib/constants';
import { NewsDetailClient } from './NewsDetailClient';
export async function generateStaticParams() {
@@ -19,7 +19,7 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
}
return {
- title: `${news.title} - 睿新致远`,
+ title: `${news.title} - ${COMPANY_INFO.displayName}`,
description: news.excerpt,
};
}
diff --git a/src/app/(marketing)/news/layout.tsx b/src/app/(marketing)/news/layout.tsx
index 301f574..5bbdceb 100644
--- a/src/app/(marketing)/news/layout.tsx
+++ b/src/app/(marketing)/news/layout.tsx
@@ -2,8 +2,8 @@ import { Metadata } from 'next';
import { COMPANY_INFO } from '@/lib/constants';
export const metadata: Metadata = {
- title: `新闻动态 - ${COMPANY_INFO.shortName}`,
- description: `了解${COMPANY_INFO.shortName}最新动态,把握行业发展脉搏。`,
+ title: `新闻动态 - ${COMPANY_INFO.displayName}`,
+ description: `了解${COMPANY_INFO.displayName}最新动态,把握行业发展脉搏。`,
};
export default function NewsLayout({ children }: { children: React.ReactNode }) {
diff --git a/src/app/(marketing)/news/page.tsx b/src/app/(marketing)/news/page.tsx
index 0d8c6a7..f7def9c 100644
--- a/src/app/(marketing)/news/page.tsx
+++ b/src/app/(marketing)/news/page.tsx
@@ -2,7 +2,7 @@
import { useState, useMemo, ChangeEvent } from 'react';
import { motion } from 'framer-motion';
-import { NEWS } from '@/lib/constants';
+import { NEWS, COMPANY_INFO } from '@/lib/constants';
import { Badge } from '@/components/ui/badge';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
@@ -65,7 +65,7 @@ export default function NewsListPage() {
新闻动态
- 了解睿新致远最新动态,把握行业发展脉搏
+ 了解{COMPANY_INFO.displayName}最新动态,把握行业发展脉搏
diff --git a/src/app/(marketing)/products/[id]/page.tsx b/src/app/(marketing)/products/[id]/page.tsx
index 3566406..5071ce2 100644
--- a/src/app/(marketing)/products/[id]/page.tsx
+++ b/src/app/(marketing)/products/[id]/page.tsx
@@ -1,6 +1,6 @@
import { notFound } from 'next/navigation';
import { StaticLink } from '@/components/ui/static-link';
-import { PRODUCTS } from '@/lib/constants';
+import { PRODUCTS, COMPANY_INFO } from '@/lib/constants';
import { Button } from '@/components/ui/button';
import { PageNav } from '@/components/layout/page-nav';
import { CheckCircle2, Zap, Target, Layers, ArrowRight } from 'lucide-react';
@@ -20,7 +20,7 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
}
return {
- title: `${product.title} - 睿新致远`,
+ title: `${product.title} - ${COMPANY_INFO.displayName}`,
description: product.description,
};
}
diff --git a/src/app/(marketing)/products/layout.tsx b/src/app/(marketing)/products/layout.tsx
index b8149a6..f23fd24 100644
--- a/src/app/(marketing)/products/layout.tsx
+++ b/src/app/(marketing)/products/layout.tsx
@@ -2,8 +2,8 @@ import { Metadata } from 'next';
import { COMPANY_INFO } from '@/lib/constants';
export const metadata: Metadata = {
- title: `产品 - ${COMPANY_INFO.shortName}`,
- description: `自主研发的企业级产品,助力企业高效运营,实现数字化转型。${COMPANY_INFO.shortName}提供ERP、CRM、BI、CMS等产品。`,
+ title: `产品 - ${COMPANY_INFO.displayName}`,
+ description: `自主研发的企业级产品,助力企业高效运营,实现数字化转型。${COMPANY_INFO.displayName}提供ERP、CRM、BI、CMS等产品。`,
};
export default function ProductsLayout({ children }: { children: React.ReactNode }) {
diff --git a/src/app/(marketing)/services/[id]/page.tsx b/src/app/(marketing)/services/[id]/page.tsx
index 1bc9f5d..239485d 100644
--- a/src/app/(marketing)/services/[id]/page.tsx
+++ b/src/app/(marketing)/services/[id]/page.tsx
@@ -1,6 +1,6 @@
import { Metadata } from 'next';
import { notFound } from 'next/navigation';
-import { SERVICES } from '@/lib/constants';
+import { SERVICES, COMPANY_INFO } from '@/lib/constants';
import { ServiceDetailClient } from './client';
export async function generateStaticParams() {
@@ -20,7 +20,7 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
}
return {
- title: `${service.title} - 睿新致远`,
+ title: `${service.title} - ${COMPANY_INFO.displayName}`,
description: service.description,
};
}
diff --git a/src/app/(marketing)/services/layout.tsx b/src/app/(marketing)/services/layout.tsx
index 1159a48..a882c24 100644
--- a/src/app/(marketing)/services/layout.tsx
+++ b/src/app/(marketing)/services/layout.tsx
@@ -2,8 +2,8 @@ import { Metadata } from 'next';
import { COMPANY_INFO } from '@/lib/constants';
export const metadata: Metadata = {
- title: `服务 - ${COMPANY_INFO.shortName}`,
- description: `专业技术团队,为您提供全方位的数字化解决方案。${COMPANY_INFO.shortName}涵盖软件开发、数据分析、咨询服务等核心业务。`,
+ title: `服务 - ${COMPANY_INFO.displayName}`,
+ description: `专业技术团队,为您提供全方位的数字化解决方案。${COMPANY_INFO.displayName}涵盖软件开发、数据分析、咨询服务等核心业务。`,
};
export default function ServicesLayout({ children }: { children: React.ReactNode }) {
diff --git a/src/app/(marketing)/solutions/[id]/page.tsx b/src/app/(marketing)/solutions/[id]/page.tsx
index 40b90ae..ae974d0 100644
--- a/src/app/(marketing)/solutions/[id]/page.tsx
+++ b/src/app/(marketing)/solutions/[id]/page.tsx
@@ -2,6 +2,7 @@ import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { SOLUTIONS } from '@/lib/constants/solutions';
import { PRODUCTS } from '@/lib/constants/products';
+import { COMPANY_INFO } from '@/lib/constants';
import { SolutionDetailClient } from './client';
export async function generateStaticParams() {
@@ -19,7 +20,7 @@ export async function generateMetadata({ params }: { params: Promise<{ id: strin
}
return {
- title: `${solution.title} - 睿新致远`,
+ title: `${solution.title} - ${COMPANY_INFO.displayName}`,
description: solution.description,
};
}
diff --git a/src/app/(marketing)/solutions/layout.tsx b/src/app/(marketing)/solutions/layout.tsx
index cef9157..c38511f 100644
--- a/src/app/(marketing)/solutions/layout.tsx
+++ b/src/app/(marketing)/solutions/layout.tsx
@@ -1,7 +1,9 @@
import { Metadata } from 'next';
+import { COMPANY_INFO } from '@/lib/constants';
+
export const metadata: Metadata = {
- title: '解决方案 - 睿新致远',
+ title: `解决方案 - ${COMPANY_INFO.displayName}`,
description: '三种角色,一种身份——您的成长伙伴',
};
diff --git a/src/app/(marketing)/solutions/page.tsx b/src/app/(marketing)/solutions/page.tsx
index 3e96bed..90a0755 100644
--- a/src/app/(marketing)/solutions/page.tsx
+++ b/src/app/(marketing)/solutions/page.tsx
@@ -20,6 +20,7 @@ const modules = [
values: ['行业趋势洞察报告', '数字化转型成熟度评估', '个性化实施路径规划'],
cta: '预约一次免费诊断',
ctaVariant: 'default' as const,
+ ctaHref: '/contact',
},
{
icon: Cpu,
@@ -33,6 +34,7 @@ const modules = [
values: ['业务场景深度调研', '技术方案定制开发', '敏捷交付快速迭代'],
cta: '了解技术方案',
ctaVariant: 'outline' as const,
+ ctaHref: '/products',
},
{
icon: Users,
@@ -46,6 +48,7 @@ const modules = [
values: ['专属客户成功经理', '季度业务复盘会', '7×24小时响应通道'],
cta: '了解陪跑服务',
ctaVariant: 'default' as const,
+ ctaHref: '/services',
},
];
@@ -127,7 +130,7 @@ export default function SolutionsPage() {
className={module.ctaVariant === 'default' ? 'bg-[#C41E3A] hover:bg-[#A01830] text-white' : 'border-[#C41E3A] text-[#C41E3A] hover:bg-[#C41E3A] hover:text-white'}
asChild
>
-
+
{module.cta}
diff --git a/src/app/(marketing)/team/page.tsx b/src/app/(marketing)/team/page.tsx
index 71ad41c..e99b19a 100644
--- a/src/app/(marketing)/team/page.tsx
+++ b/src/app/(marketing)/team/page.tsx
@@ -2,7 +2,7 @@ import { COMPANY_INFO } from '@/lib/constants';
import { TeamClient } from './client';
export const metadata = {
- title: `核心团队 - ${COMPANY_INFO.name}`,
+ title: `核心团队 - ${COMPANY_INFO.displayName}`,
description: `了解${COMPANY_INFO.name}的核心团队。我们的团队成员拥有丰富的行业经验和技术专长,致力于为客户提供专业的数字化转型服务。`,
};
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
index 27df852..0ab13e1 100644
--- a/src/app/not-found.tsx
+++ b/src/app/not-found.tsx
@@ -3,6 +3,7 @@
import { StaticLink } from '@/components/ui/static-link';
import { Button } from '@/components/ui/button';
import { Home, ArrowLeft, Search } from 'lucide-react';
+import { COMPANY_INFO } from '@/lib/constants';
export default function NotFound() {
return (
@@ -62,7 +63,7 @@ export default function NotFound() {
关于我们
-
了解睿新致远
+
了解{COMPANY_INFO.displayName}
diff --git a/src/app/privacy/page.tsx b/src/app/privacy/page.tsx
index e74f829..f98f30c 100644
--- a/src/app/privacy/page.tsx
+++ b/src/app/privacy/page.tsx
@@ -1,8 +1,9 @@
import { Metadata } from 'next';
+import { COMPANY_INFO } from '@/lib/constants';
export const metadata: Metadata = {
- title: '隐私政策 - 睿新致远',
- description: '四川睿新致远科技有限公司隐私政策',
+ title: `隐私政策 - ${COMPANY_INFO.displayName}`,
+ description: `${COMPANY_INFO.name}隐私政策`,
};
export default function PrivacyPolicyPage() {
diff --git a/src/app/terms/page.tsx b/src/app/terms/page.tsx
index c718a40..8433468 100644
--- a/src/app/terms/page.tsx
+++ b/src/app/terms/page.tsx
@@ -1,8 +1,9 @@
import { Metadata } from 'next';
+import { COMPANY_INFO } from '@/lib/constants';
export const metadata: Metadata = {
- title: '服务条款 - 睿新致远',
- description: '四川睿新致远科技有限公司服务条款',
+ title: `服务条款 - ${COMPANY_INFO.displayName}`,
+ description: `${COMPANY_INFO.name}服务条款`,
};
export default function TermsOfServicePage() {
diff --git a/src/components/analytics/CookieConsent.tsx b/src/components/analytics/CookieConsent.tsx
index 7e06037..79c7bdd 100644
--- a/src/components/analytics/CookieConsent.tsx
+++ b/src/components/analytics/CookieConsent.tsx
@@ -1,6 +1,6 @@
'use client';
-import { useState, useEffect, useCallback } from 'react';
+import { useState, useEffect, useCallback, useRef } from 'react';
import {
updateConsentDetailed,
trackButtonClick,
@@ -13,19 +13,37 @@ import { motion, AnimatePresence } from 'framer-motion';
const LEGACY_CONSENT_KEY = 'ga_consent';
+let hasConsentBeenHandled = false;
+
+function getInitialShowConsent(): boolean {
+ if (typeof window === 'undefined') {return false;}
+ if (hasConsentBeenHandled) {return false;}
+ const stored = getStoredPreferences();
+ if (stored) {return false;}
+ const legacyConsent = localStorage.getItem(LEGACY_CONSENT_KEY);
+ if (legacyConsent) {return false;}
+ return false;
+}
+
export function CookieConsent() {
- const [showConsent, setShowConsent] = useState(false);
+ const [showConsent, setShowConsent] = useState(getInitialShowConsent);
const [showSettings, setShowSettings] = useState(false);
const [isAnimating, setIsAnimating] = useState(false);
const [preferences, setPreferences] = useState(getDefaultPreferences());
+ const consentCheckedRef = useRef(false);
useEffect(() => {
+ if (consentCheckedRef.current) {return;}
+ consentCheckedRef.current = true;
+
const stored = getStoredPreferences();
if (stored) {
+ hasConsentBeenHandled = true;
updateConsentDetailed(stored);
} else {
const legacyConsent = localStorage.getItem(LEGACY_CONSENT_KEY);
if (legacyConsent) {
+ hasConsentBeenHandled = true;
const migratedPrefs: CookiePreferences = {
necessary: true,
analytics: legacyConsent === 'granted',
@@ -45,8 +63,18 @@ export function CookieConsent() {
return undefined;
}, []);
+ useEffect(() => {
+ const handleOpenSettings = () => {
+ setShowSettings(true);
+ setShowConsent(true);
+ };
+ window.addEventListener('open-cookie-settings', handleOpenSettings);
+ return () => window.removeEventListener('open-cookie-settings', handleOpenSettings);
+ }, []);
+
const handleSavePreferences = useCallback((prefs: CookiePreferences) => {
setIsAnimating(true);
+ hasConsentBeenHandled = true;
const finalPrefs = { ...prefs, timestamp: Date.now() };
storePreferences(finalPrefs);
updateConsentDetailed(finalPrefs);
diff --git a/src/components/layout/footer.test.tsx b/src/components/layout/footer.test.tsx
index 09a5006..42f23c5 100644
--- a/src/components/layout/footer.test.tsx
+++ b/src/components/layout/footer.test.tsx
@@ -35,6 +35,8 @@ jest.mock('lucide-react', () => ({
jest.mock('@/lib/constants', () => ({
COMPANY_INFO: {
name: '四川睿新致远科技有限公司',
+ shortName: '睿新致遠',
+ displayName: '睿新致远',
description: '以智慧连接数字趋势,以伙伴身份陪您成长',
email: 'contact@novalon.cn',
phone: '028-88888888',
diff --git a/src/components/layout/header.test.tsx b/src/components/layout/header.test.tsx
index 2b82ecf..f4c4677 100644
--- a/src/components/layout/header.test.tsx
+++ b/src/components/layout/header.test.tsx
@@ -15,28 +15,32 @@ jest.mock('next/navigation', () => ({
}));
jest.mock('next/link', () => {
- return ({ children, href, onClick, ...props }: any) => (
+ const MockLink = ({ children, href, onClick, ...props }: { children: React.ReactNode; href: string; onClick?: () => void; [key: string]: unknown }) => (
{children}
);
+ MockLink.displayName = 'MockLink';
+ return MockLink;
});
jest.mock('next/image', () => {
- return ({ src, alt, width, height, className, ...props }: any) => (
+ const MockImage = ({ src, alt, width, height, className, ...props }: { src: string; alt: string; width: number; height: number; className?: string; [key: string]: unknown }) => (
);
+ MockImage.displayName = 'MockImage';
+ return MockImage;
});
jest.mock('framer-motion', () => ({
motion: {
- div: ({ children, className, ...props }: any) => (
+ div: ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
{children}
),
},
- AnimatePresence: ({ children }: any) => <>{children}>,
+ AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}>,
}));
jest.mock('lucide-react', () => ({
@@ -44,18 +48,21 @@ jest.mock('lucide-react', () => ({
X: () => ,
}));
-jest.mock('@/components/ui/button', () => ({
- Button: ({ children, className, asChild, ...props }: any) => (
+jest.mock('@/components/ui/button', () => {
+ const MockButton = ({ children, className, ...props }: { children: React.ReactNode; className?: string; [key: string]: unknown }) => (
- ),
-}));
+ );
+ MockButton.displayName = 'MockButton';
+ return { Button: MockButton };
+});
jest.mock('@/lib/constants', () => ({
COMPANY_INFO: {
name: '四川睿新致远科技有限公司',
shortName: '睿新致遠',
+ displayName: '睿新致远',
},
NAVIGATION: [
{ id: 'home', label: '首页', href: '/' },
diff --git a/src/components/layout/header.tsx b/src/components/layout/header.tsx
index cd75eb9..e479b08 100644
--- a/src/components/layout/header.tsx
+++ b/src/components/layout/header.tsx
@@ -108,7 +108,11 @@ function HeaderContent() {
label={item.label}
items={MEGA_DROPDOWN_DATA[item.dropdownKey!] ?? []}
isOpen={openDropdown === item.id}
- onToggle={() => setOpenDropdown(openDropdown === item.id ? null : item.id)}
+ onToggle={() => {
+ setOpenDropdown((prev) => prev === item.id ? null : item.id);
+ }}
+ onOpen={() => setOpenDropdown(item.id)}
+ onClose={() => setOpenDropdown((prev) => prev === item.id ? null : prev)}
/>
) : (
))}
-
diff --git a/src/components/layout/mega-dropdown.tsx b/src/components/layout/mega-dropdown.tsx
index 7b93c9a..2ae6ebb 100644
--- a/src/components/layout/mega-dropdown.tsx
+++ b/src/components/layout/mega-dropdown.tsx
@@ -11,10 +11,15 @@ interface MegaDropdownProps {
items: MegaDropdownItem[];
isOpen: boolean;
onToggle: () => void;
+ onOpen?: () => void;
+ onClose?: () => void;
}
-export function MegaDropdown({ label, items, isOpen, onToggle }: MegaDropdownProps) {
+const HOVER_DELAY = 150;
+
+export function MegaDropdown({ label, items, isOpen, onToggle, onOpen, onClose }: MegaDropdownProps) {
const dropdownRef = useRef(null);
+ const hoverTimeoutRef = useRef | null>(null);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
@@ -26,8 +31,42 @@ export function MegaDropdown({ label, items, isOpen, onToggle }: MegaDropdownPro
return () => document.removeEventListener('mousedown', handleClickOutside);
}, [isOpen, onToggle]);
+ useEffect(() => {
+ return () => {
+ if (hoverTimeoutRef.current) {
+ clearTimeout(hoverTimeoutRef.current);
+ }
+ };
+ }, []);
+
+ const handleMouseEnter = () => {
+ if (hoverTimeoutRef.current) {
+ clearTimeout(hoverTimeoutRef.current);
+ hoverTimeoutRef.current = null;
+ }
+ if (!isOpen) {
+ if (onOpen) {
+ onOpen();
+ } else {
+ onToggle();
+ }
+ }
+ };
+
+ const handleMouseLeave = () => {
+ hoverTimeoutRef.current = setTimeout(() => {
+ if (isOpen) {
+ if (onClose) {
+ onClose();
+ } else {
+ onToggle();
+ }
+ }
+ }, HOVER_DELAY);
+ };
+
return (
-
+