kv-tube/frontend/app/components/Header.tsx
KV-Tube Deployer 95cfe06f2c
Some checks failed
CI / lint (push) Failing after 6s
CI / test (push) Failing after 1s
Docker Build & Push / build (push) Failing after 1s
CI / build (push) Has been skipped
chore: setup Dockerfiles and CI for Forgejo and Synology
2026-02-22 17:29:42 +07:00

124 lines
6.2 KiB
TypeScript
Executable file

'use client';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useState, useRef, useEffect } from 'react';
import { IoSearchOutline, IoMoonOutline, IoSunnyOutline, IoArrowBack } from 'react-icons/io5';
import RegionSelector from './RegionSelector';
import { useTheme } from '../context/ThemeContext';
export default function Header() {
const [searchQuery, setSearchQuery] = useState('');
const [isMobileSearchActive, setIsMobileSearchActive] = useState(false);
const [isFocused, setIsFocused] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const mobileInputRef = useRef<HTMLInputElement>(null);
const router = useRouter();
const { theme, toggleTheme } = useTheme();
const handleSearch = (e: React.FormEvent) => {
e.preventDefault();
if (searchQuery.trim()) {
router.push(`/search?q=${encodeURIComponent(searchQuery)}`);
setIsMobileSearchActive(false);
setIsFocused(false);
}
};
useEffect(() => {
if (isMobileSearchActive && mobileInputRef.current) {
mobileInputRef.current.focus();
}
}, [isMobileSearchActive]);
return (
<header className="yt-header">
{!isMobileSearchActive ? (
<>
{/* Left */}
<div className="yt-header-left">
<Link href="/" style={{ display: 'flex', alignItems: 'center', gap: '4px', marginLeft: '12px' }}>
<span style={{ fontSize: '18px', fontWeight: '700', letterSpacing: '-0.5px', fontFamily: 'YouTube Sans, Roboto, Arial, sans-serif' }} className="hidden-mobile">KV-Tube</span>
</Link>
</div>
{/* Center Search Pill - Desktop */}
<div className="yt-header-center hidden-mobile">
<form className="search-container" onSubmit={handleSearch}>
<div className="search-input-wrapper">
<IoSearchOutline size={18} className="search-input-icon" />
<input
ref={inputRef}
type="text"
placeholder="Search videos, channels, and more..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
/>
{searchQuery && (
<button
type="button"
className="search-btn"
onClick={() => { setSearchQuery(''); inputRef.current?.focus(); }}
title="Clear"
style={{ color: 'var(--yt-text-secondary)' }}
>
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
</svg>
</button>
)}
<button type="submit" className="search-btn" title="Search">
<IoSearchOutline size={18} />
</button>
</div>
</form>
</div>
{/* Right - Region and Theme */}
<div className="yt-header-right">
<button className="yt-icon-btn visible-mobile" onClick={() => setIsMobileSearchActive(true)} title="Search">
<IoSearchOutline size={22} />
</button>
<button className="yt-icon-btn" onClick={toggleTheme} title="Toggle Theme">
{theme === 'dark' ? <IoSunnyOutline size={22} /> : <IoMoonOutline size={22} />}
</button>
<RegionSelector />
</div>
</>
) : (
/* Mobile Search Overlay */
<div className="mobile-search-bar">
<button className="mobile-search-back" onClick={() => setIsMobileSearchActive(false)}>
<IoArrowBack size={22} />
</button>
<form className="search-container" onSubmit={handleSearch} style={{ flex: 1 }}>
<div className="search-input-wrapper">
<IoSearchOutline size={16} className="search-input-icon" />
<input
ref={mobileInputRef}
type="text"
placeholder="Search KV-Tube"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
{searchQuery && (
<button
type="button"
className="search-btn"
onClick={() => { setSearchQuery(''); mobileInputRef.current?.focus(); }}
title="Clear"
>
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
</svg>
</button>
)}
</div>
</form>
</div>
)}
</header>
);
}