fix: dropdowns close on click-outside instead of hover-away
Build & Push Portfolio Image / build-push (push) Successful in 2m40s

This commit is contained in:
Story Crater Bot
2026-09-01 17:00:19 -07:00
parent 44ecc6416d
commit a10f5454cd
+16 -4
View File
@@ -3,7 +3,7 @@
import Link from 'next/link' import Link from 'next/link'
import Image from 'next/image' import Image from 'next/image'
import { Menu, X } from 'lucide-react' import { Menu, X } from 'lucide-react'
import { useState } from 'react' import { useState, useEffect, useRef } from 'react'
import { useTerminal } from '@/lib/TerminalContext' import { useTerminal } from '@/lib/TerminalContext'
import { useLanguage } from '@/lib/LanguageContext' import { useLanguage } from '@/lib/LanguageContext'
import { CIStatusIndicator } from './CIStatusIndicator' import { CIStatusIndicator } from './CIStatusIndicator'
@@ -28,7 +28,19 @@ export default function Header() {
setLang(lang === 'en' ? 'zh' : 'en') setLang(lang === 'en' ? 'zh' : 'en')
} }
const skillsRef = useRef<HTMLDivElement>(null)
const contactRef = useRef<HTMLDivElement>(null)
const educationRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (skillsRef.current && !skillsRef.current.contains(e.target as Node)) setSkillsOpen(false)
if (contactRef.current && !contactRef.current.contains(e.target as Node)) setContactOpen(false)
if (educationRef.current && !educationRef.current.contains(e.target as Node)) setEducationOpen(false)
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [])
return ( return (
<header className="sticky top-0 z-50 border-b border-gray-200 dark:border-gray-800 bg-white/95 dark:bg-gray-950/95 backdrop-blur"> <header className="sticky top-0 z-50 border-b border-gray-200 dark:border-gray-800 bg-white/95 dark:bg-gray-950/95 backdrop-blur">
@@ -55,7 +67,7 @@ export default function Header() {
> >
{t.header.nav.askPoimen} {t.header.nav.askPoimen}
</button> </button>
<div className="relative" onMouseLeave={() => setSkillsOpen(false)}> <div className="relative" ref={skillsRef}>
<button <button
onClick={() => setSkillsOpen(!skillsOpen)} onClick={() => setSkillsOpen(!skillsOpen)}
className="text-sm hover:text-blue-600 dark:hover:text-blue-400" className="text-sm hover:text-blue-600 dark:hover:text-blue-400"
@@ -83,7 +95,7 @@ export default function Header() {
</div> </div>
)} )}
</div> </div>
<div className="relative" onMouseLeave={() => setContactOpen(false)}> <div className="relative" ref={contactRef}>
<button <button
onClick={() => setContactOpen(!contactOpen)} onClick={() => setContactOpen(!contactOpen)}
className="text-sm hover:text-blue-600 dark:hover:text-blue-400" className="text-sm hover:text-blue-600 dark:hover:text-blue-400"
@@ -173,7 +185,7 @@ export default function Header() {
<div className="text-gray-600 dark:text-gray-400">{t.header.experience}</div> <div className="text-gray-600 dark:text-gray-400">{t.header.experience}</div>
<div className="font-semibold text-gray-900 dark:text-white">{t.header.experienceValue}</div> <div className="font-semibold text-gray-900 dark:text-white">{t.header.experienceValue}</div>
</div> </div>
<div className="relative" onMouseLeave={() => setEducationOpen(false)}> <div className="relative" ref={educationRef}>
<button <button
onClick={() => setEducationOpen(!educationOpen)} onClick={() => setEducationOpen(!educationOpen)}
className="text-xs cursor-pointer hover:text-blue-600 dark:hover:text-blue-400" className="text-xs cursor-pointer hover:text-blue-600 dark:hover:text-blue-400"