
useState, useEffect, dan useRef untuk mengelola state, efek samping, dan referensi DOM langsung.clsx untuk penerapan class secara kondisional.<header> dengan properti position: fixed.
<header className="fixed top-4 left-4 right-4 md:left-1/2 md:-translate-x-1/2 ... z-50">
<nav className="relative ... rounded-full bg-primary/90 backdrop-blur-sm ...">
{/* Konten di sini */}
</nav>
</header>
fixed top-4: Menempelkan header di dekat bagian atas layar.left-4 right-4: Di layar seluler, header merentang hampir di seluruh lebar.md:left-1/2 md:-translate-x-1/2: Di layar sedang ke atas, header memposisikan dirinya tepat di tengah secara horizontal.bg-primary/90 backdrop-blur-sm: Menciptakan efek kaca buram yang semi-transparan.rounded-full: Memberikan bentuk "pil" yang ikonik.useState untuk ini:
type ActiveView = 'none' | 'search' | 'menu' | 'readingList';
const [activeView, setActiveView] = useState<ActiveView>('none');
Setiap elemen interaktif di header cukup memanggil setActiveView untuk mengubah tampilan. Contohnya, mengklik ikon pencarian akan mengubah state ke 'search', yang kemudian memicu rendering kondisional untuk input pencarian dan panel hasil.
Bentuk dan gaya komponen juga bereaksi terhadap state ini. Saat isSearchOpen aktif, lebar nav melebar. Saat isMenuOpen aktif, radius sudut berubah dari rounded-full menjadi rounded-t-2xl agar menyatu dengan panel menu di bawahnya.
activeView = 'search'.clsx. Link navigasi utama memudar (opacity-0) sementara wadah input pencarian muncul (opacity-100).useEffect berjalan saat tampilan pencarian aktif, secara otomatis memfokuskan kursor ke elemen <input> demi kenyamanan pengguna.
const searchInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (isSearchOpen) {
// Timeout singkat memastikan elemen terlihat sebelum difokuskan
setTimeout(() => searchInputRef.current?.focus(), 100);
}
}, [isSearchOpen]);
useEffect lain memantau perubahan pada state query. Ia menyaring array data postingan dan catatan untuk menampilkan hasil secara instan.useEffect lainnya.
const lastScrollY = useRef(0);
const [isVisible, setIsVisible] = useState(true);
useEffect(() => {
const handleScroll = () => {
const currentScrollY = window.scrollY;
// Tampilkan jika scroll ke atas atau dekat puncak halaman
if (currentScrollY < lastScrollY.current || currentScrollY < 10) {
setIsVisible(true);
} else if (currentScrollY > 100 && currentScrollY > lastScrollY.current) {
// Sembunyikan jika scroll ke bawah melewati ambang batas tertentu
setIsVisible(false);
}
lastScrollY.current = currentScrollY;
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
State isVisible kemudian menambah atau menghapus class yang mengontrol opacity dan transform pada header.
md:).Loading Conversation...