"use client"; import { Icons } from "@midday/ui/icons"; import { motion } from "motion/react"; import { useCallback, useEffect, useRef, useState } from "react"; import type { docsNavigation } from "@/lib/docs"; import { useDocsChat } from "./docs-chat-provider"; import { DocsNavPanel } from "./docs-nav-panel"; type FloatingChatInputProps = { navigation: typeof docsNavigation; }; export function FloatingChatInput({ navigation }: FloatingChatInputProps) { const { sendMessage, isChatOpen } = useDocsChat(); const [input, setInput] = useState(""); const [isFocused, setIsFocused] = useState(false); const [isNavOpen, setIsNavOpen] = useState(false); const [isVisible, setIsVisible] = useState(true); const inputRef = useRef(null); const menuButtonRef = useRef(null); const lastScrollY = useRef(0); const scrollThreshold = 10; // Minimum scroll distance before hiding/showing // Track scroll direction to show/hide the floating input const handleScroll = useCallback(() => { const currentScrollY = window.scrollY; const diff = currentScrollY - lastScrollY.current; // Only trigger if scrolled more than threshold if (Math.abs(diff) > scrollThreshold) { if (diff > 0 && currentScrollY > 20) { // Scrolling down setIsVisible(false); } else { // Scrolling up setIsVisible(true); } lastScrollY.current = currentScrollY; } }, []); useEffect(() => { window.addEventListener("scroll", handleScroll, { passive: true }); return () => window.removeEventListener("scroll", handleScroll); }, [handleScroll]); // Autofocus input when chat is active (including on initial load with ?chat=true) useEffect(() => { if (isChatOpen && inputRef.current) { // Small delay to ensure DOM is ready after hydration const timer = setTimeout(() => { inputRef.current?.focus(); }, 100); return () => clearTimeout(timer); } }, [isChatOpen]); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!input.trim()) return; sendMessage(input.trim()); setInput(""); // Keep focus after sending inputRef.current?.focus(); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.nativeEvent.isComposing) { e.preventDefault(); handleSubmit(e); } }; const isExpanded = isFocused || input.trim() || isChatOpen || isNavOpen; // Always show when chat is open, input has content, or is focused const shouldShow = isVisible || isChatOpen || input.trim() || isFocused || isNavOpen; return ( {/* Docs navigation panel - positioned above input */} setIsNavOpen(false)} triggerRef={menuButtonRef} />
{/* Blur layer fades in separately to avoid backdrop-filter animation issues */} {/* Browse docs icon - animated hamburger to X */} setInput(e.target.value)} onKeyDown={handleKeyDown} onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} placeholder="Ask anything" className="relative flex-1 bg-transparent px-2 pr-12 text-sm outline-none placeholder:text-[rgba(102,102,102,0.5)]" /> e.preventDefault()} // Prevent input blur className="absolute z-10 right-3 top-1/2 -translate-y-1/2 size-8 flex items-center justify-center bg-primary text-primary-foreground hover:bg-primary/90" initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: isExpanded ? 1 : 0, scale: isExpanded ? 1 : 0.95, }} transition={{ type: "spring", stiffness: 300, damping: 30, }} style={{ pointerEvents: isExpanded ? "auto" : "none", }} >
); }