import React, { useRef, useEffect, useState, useCallback } from 'react'; import { NodeResizer, Handle, Position } from '@xyflow/react'; import { Pencil, Eraser, Trash2 } from 'lucide-react'; import { useTheme } from '../../../../context/ThemeContext'; const DrawNode = ({ id, data, selected }) => { const { isDarkMode } = useTheme(); const canvasRef = useRef(null); const [isDrawing, setIsDrawing] = useState(false); const [tool, setTool] = useState('pencil'); // Use a ref for drawing properties to avoid re-renders during active draw const drawState = useRef({ ctx: null, lastX: 0, lastY: 0 }); const initCanvas = useCallback(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); drawState.current.ctx = ctx; // Use absolute size from data or measure parent const rect = canvas.getBoundingClientRect(); if (canvas.width !== rect.width || canvas.height !== rect.height) { // Save current content before resizing const tempImage = canvas.toDataURL(); canvas.width = rect.width; canvas.height = rect.height; const img = new Image(); img.onload = () => ctx.drawImage(img, 0, 0); img.src = tempImage || data.image; } if (data.image && !isDrawing) { const img = new Image(); img.onload = () => ctx.drawImage(img, 0, 0); img.src = data.image; } }, [data.image, isDrawing]); useEffect(() => { initCanvas(); }, [initCanvas, data.width, data.height]); const getCoordinates = (e) => { const canvas = canvasRef.current; const rect = canvas.getBoundingClientRect(); // Support both mouse and touch const clientX = e.clientX || (e.touches && e.touches[0].clientX); const clientY = e.clientY || (e.touches && e.touches[0].clientY); return { x: clientX - rect.left, y: clientY - rect.top }; }; const startDrawing = (e) => { if (!selected) return; setIsDrawing(true); const { x, y } = getCoordinates(e); drawState.current.lastX = x; drawState.current.lastY = y; }; const draw = (e) => { if (!isDrawing || !selected) return; // Prevent canvas movement while drawing e.preventDefault(); e.stopPropagation(); const { x, y } = getCoordinates(e); const ctx = drawState.current.ctx; ctx.beginPath(); ctx.moveTo(drawState.current.lastX, drawState.current.lastY); ctx.lineTo(x, y); ctx.strokeStyle = tool === 'eraser' ? (isDarkMode ? '#1e1e1e' : '#ffffff') : (data.strokeColor || (isDarkMode ? '#ffffff' : '#000000')); ctx.lineWidth = tool === 'eraser' ? 20 : (data.lineWidth || 3); ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.globalCompositeOperation = tool === 'eraser' ? 'destination-out' : 'source-over'; ctx.stroke(); drawState.current.lastX = x; drawState.current.lastY = y; }; const stopDrawing = () => { if (!isDrawing) return; setIsDrawing(false); const canvas = canvasRef.current; if (data.updateNodeData) { data.updateNodeData(id, { image: canvas.toDataURL() }); } }; const clearCanvas = (e) => { e.stopPropagation(); const ctx = drawState.current.ctx; ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height); if (data.updateNodeData) { data.updateNodeData(id, { image: null }); } }; return (