"use client"; import { useState, useEffect, useRef } from "react"; import { ChevronDown, Search } from "lucide-react"; const POPULAR_FONTS = [ "Inter", "Roboto", "Open Sans", "Lato", "Montserrat", "Oswald", "Raleway", "Playfair Display", "Merriweather", "Nunito", "Dancing Script", "Pacifico", "Poppins", "Source Sans Pro", "Ubuntu", "Rubik", "Work Sans", "Quicksand", "Josefin Sans", "Libre Baskerville", "Crimson Text", "Bitter", "Archivo", "DM Sans", "Space Grotesk", "Outfit", "Sora", "Caveat", "Comfortaa", "Barlow", "Karla", "Manrope", "Lexend", "Roboto Slab", "PT Serif", "Noto Sans", "Fira Sans", "IBM Plex Sans", "Cabin", "Inconsolata", ]; interface FontPickerProps { value: string; onChange: (fontName: string) => void; darkMode?: boolean; } export default function FontPicker({ value, onChange, darkMode }: FontPickerProps) { const [isOpen, setIsOpen] = useState(false); const [query, setQuery] = useState(""); const [allFonts, setAllFonts] = useState(POPULAR_FONTS); const [loadedFonts, setLoadedFonts] = useState>(new Set(["Inter"])); const containerRef = useRef(null); const inputRef = useRef(null); // Try to fetch from Google Fonts API for extended list useEffect(() => { const fetchFonts = async () => { try { const res = await fetch("/api/fonts"); if (res.ok) { const data = await res.json(); if (data.fonts?.length > 0) { setAllFonts(data.fonts.map((f: any) => f.value || f.name || f)); } } } catch { // Keep popular fonts as fallback } }; fetchFonts(); }, []); // Load font for preview const loadFont = (fontName: string) => { if (loadedFonts.has(fontName) || fontName === "Inter") return; const id = `font-preview-${fontName.replace(/\s+/g, "-")}`; if (!document.getElementById(id)) { const link = document.createElement("link"); link.id = id; link.rel = "stylesheet"; link.href = `https://fonts.googleapis.com/css2?family=${fontName.replace(/ /g, "+")}:wght@400;700&display=swap`; document.head.appendChild(link); } setLoadedFonts((prev) => new Set(prev).add(fontName)); }; // Load selected font useEffect(() => { if (value) loadFont(value); }, [value]); // Close on click outside useEffect(() => { const handler = (e: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setIsOpen(false); } }; document.addEventListener("mousedown", handler); return () => document.removeEventListener("mousedown", handler); }, []); // Focus input on open useEffect(() => { if (isOpen && inputRef.current) { inputRef.current.focus(); } }, [isOpen]); const filtered = query ? allFonts.filter((f) => f.toLowerCase().includes(query.toLowerCase())) : allFonts; const bg = darkMode ? "#1f2937" : "#fff"; const border = darkMode ? "#374151" : "#e5e7eb"; const text = darkMode ? "#e5e7eb" : "#333"; const hoverBg = darkMode ? "#374151" : "#f0f9ff"; return (
{isOpen && (
setQuery(e.target.value)} style={{ padding: "4px", width: "100%", border: "none", outline: "none", background: "transparent", color: text, fontSize: "0.8rem", }} />
{filtered.slice(0, 50).map((font) => { loadFont(font); return (
{ onChange(font); setIsOpen(false); setQuery(""); }} onMouseEnter={() => loadFont(font)} style={{ padding: "6px 12px", cursor: "pointer", fontFamily: font, fontSize: "0.85rem", color: text, background: font === value ? hoverBg : "transparent", borderLeft: font === value ? "3px solid #0ea5e9" : "3px solid transparent", }} > {font}
); })} {filtered.length === 0 && (
No fonts found
)}
)}
); }