"use client" import { useRef, useCallback, useEffect, useMemo, useState } from "react" import { createPortal } from "react-dom" import { AnimatePresence, motion } from "motion/react" import { cn } from "@lib/utils" import { dmSansClassName } from "@/lib/fonts" import { Maximize2, Plus, Loader2, X } from "lucide-react" import { useProject } from "@/stores" import { useQuickNoteDraft } from "@/stores/quick-note-draft" import { TextEditor } from "./text-editor" interface QuickNoteCardProps { onSave: (content: string) => void onMaximize: (content: string) => void isSaving?: boolean } type NoteRect = { left: number top: number width: number height: number } function getExpandedRect(source: NoteRect): NoteRect { const viewportWidth = window.innerWidth const viewportHeight = window.innerHeight const margin = viewportWidth < 768 ? 16 : 32 const availableWidth = viewportWidth - source.left - margin const availableHeight = viewportHeight - source.top - margin const width = Math.min(1008, Math.max(source.width, availableWidth)) const height = Math.min(720, Math.max(source.height, availableHeight)) return { left: source.left, top: source.top, width, height, } } export function QuickNoteCard({ onSave, onMaximize, isSaving = false, }: QuickNoteCardProps) { const cardRef = useRef(null) const wasSavingRef = useRef(isSaving) const { selectedProject } = useProject() const { draft, setDraft } = useQuickNoteDraft(selectedProject) const [isExpanded, setIsExpanded] = useState(false) const [sourceRect, setSourceRect] = useState(null) const [targetRect, setTargetRect] = useState(null) const [expandedInitialContent, setExpandedInitialContent] = useState< string | undefined >(undefined) const [isMounted, setIsMounted] = useState(false) const handleChange = useCallback( (content: string) => { setDraft(content) }, [setDraft], ) const handleExpand = useCallback(() => { const rect = cardRef.current?.getBoundingClientRect() if (!rect) return const nextSourceRect = { left: rect.left, top: rect.top, width: rect.width, height: rect.height, } setSourceRect(nextSourceRect) setTargetRect(getExpandedRect(nextSourceRect)) setExpandedInitialContent(draft || undefined) setIsExpanded(true) }, [draft]) const handleClose = useCallback(() => { setIsExpanded(false) }, []) const handleSaveClick = useCallback(() => { if (draft.trim() && !isSaving) { onSave(draft) } }, [draft, isSaving, onSave]) const handleMaximizeClick = useCallback(() => { setIsExpanded(false) onMaximize(draft) }, [draft, onMaximize]) const canSave = draft.trim().length > 0 && !isSaving const previewText = useMemo(() => { const trimmed = draft.trim() if (!trimmed) return null return trimmed.replace(/\s+/g, " ") }, [draft]) useEffect(() => { setIsMounted(true) }, []) useEffect(() => { if (!isExpanded || !sourceRect) return const handleResize = () => setTargetRect(getExpandedRect(sourceRect)) window.addEventListener("resize", handleResize) return () => window.removeEventListener("resize", handleResize) }, [isExpanded, sourceRect]) useEffect(() => { if (!isExpanded) return const previousOverflow = document.body.style.overflow document.body.style.overflow = "hidden" const handleGlobalKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { e.preventDefault() handleClose() } } document.addEventListener("keydown", handleGlobalKeyDown) return () => { document.body.style.overflow = previousOverflow document.removeEventListener("keydown", handleGlobalKeyDown) } }, [isExpanded, handleClose]) useEffect(() => { if (wasSavingRef.current && !isSaving && draft.trim().length === 0) { setIsExpanded(false) } wasSavingRef.current = isSaving }, [draft, isSaving]) return ( <>
{isMounted && createPortal( {isExpanded && sourceRect && targetRect && (
)}
, document.body, )} ) }