'use client' import { useState, useRef, useEffect, useCallback } from 'react' import { motion, AnimatePresence } from 'motion/react' import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { cn } from '@/lib/utils' import { Pencil, Check, X } from 'lucide-react' type InlineEditableTextVariant = 'h1' | 'h2' | 'body' | 'mono' type InlineEditableTextProps = { value: string onSave: (newValue: string) => void | Promise variant?: InlineEditableTextVariant placeholder?: string multiline?: boolean disabled?: boolean className?: string } const variantStyles: Record = { h1: 'text-xl font-bold', h2: 'text-base font-semibold', body: 'text-sm', mono: 'text-sm font-mono text-muted-foreground', } export function InlineEditableText({ value, onSave, variant = 'body', placeholder = 'Click to edit...', multiline = false, disabled = false, className, }: InlineEditableTextProps) { const [isEditing, setIsEditing] = useState(false) const [editValue, setEditValue] = useState(value) const [isSaving, setIsSaving] = useState(false) const inputRef = useRef(null) useEffect(() => { if (isEditing && inputRef.current) { inputRef.current.focus() inputRef.current.select() } }, [isEditing]) useEffect(() => { setEditValue(value) }, [value]) const handleSave = useCallback(async () => { const trimmed = editValue.trim() if (trimmed === value) { setIsEditing(false) return } if (!trimmed) { setEditValue(value) setIsEditing(false) return } setIsSaving(true) try { await onSave(trimmed) setIsEditing(false) } catch { setEditValue(value) } finally { setIsSaving(false) } }, [editValue, value, onSave]) const handleCancel = useCallback(() => { setEditValue(value) setIsEditing(false) }, [value]) const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === 'Escape') { handleCancel() } if (e.key === 'Enter') { if (multiline && !e.ctrlKey) return e.preventDefault() handleSave() } }, [handleCancel, handleSave, multiline] ) if (disabled) { return ( {value || {placeholder}} ) } return ( {isEditing ? ( {multiline ? (