import React from 'react'; import { Undo2, Redo2, Pencil, Hand, Square, Eraser, Sun, Moon, ZoomIn, ZoomOut, Grid, GripHorizontal, RotateCcw, Scan, Download, Menu, X, Ruler, Palette } from 'lucide-react'; const PRESET_COLORS = [ '#000000', '#FFFFFF', '#FF4500', '#FFA500', '#FF0000', '#FF1493', '#800080', '#4B0082', '#008000', '#C0C0C0', '#FFA500', '#4169E1', '#00FF00', '#40E0D0', '#0000FF', '#FFD700' ]; const DrawingBoard = ({ tool, setTool, elements, pencilSize, setPencilSize, scale, setScale, onZoom, undo, redo, action, selectedElement, panOffset, scaleOffset, handleMouseDown, handleMouseMove, handleMouseUp, handleBlur, captureDrawnArea, handleDetectRegions, handleDownloadRegions }) => { const textAreaRef = React.useRef(null); const backgroundCanvasRef = React.useRef(null); const drawingCanvasRef = React.useRef(null); const [backgroundType, setBackgroundType] = React.useState('none'); const [gridSize, setGridSize] = React.useState(45); const [isMenuOpen, setIsMenuOpen] = React.useState(false); const [showDownloadButton, setShowDownloadButton] = React.useState(false); const [showColorPicker, setShowColorPicker] = React.useState(false); const [currentColor, setCurrentColor] = React.useState('#000000'); const [customColor, setCustomColor] = React.useState('#000000'); const [darkMode, setDarkMode] = React.useState(() => { const savedMode = localStorage.getItem('drawingBoardDarkMode'); if (savedMode !== null) { return savedMode === 'true'; } return window.matchMedia('(prefers-color-scheme: dark)').matches; }); React.useEffect(() => { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const handleChange = (e) => { const savedMode = localStorage.getItem('drawingBoardDarkMode'); if (savedMode === null) { setDarkMode(e.matches); } }; mediaQuery.addEventListener('change', handleChange); return () => mediaQuery.removeEventListener('change', handleChange); }, []); React.useEffect(() => { localStorage.setItem('drawingBoardDarkMode', darkMode); drawElements(); }, [darkMode]); const drawBackground = React.useCallback(() => { const canvas = backgroundCanvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); const width = canvas.width; const height = canvas.height; ctx.fillStyle = darkMode ? '#ffffff' : '#000000'; ctx.fillRect(0, 0, width, height); const offsetX = (panOffset.x * scale - scaleOffset.x) % (gridSize * scale); const offsetY = (panOffset.y * scale - scaleOffset.y) % (gridSize * scale); const gridColor = darkMode ? 'rgba(0, 0, 0, 0.6)' : 'rgba(255, 255, 255, 0.6)'; if (backgroundType === 'grid') { const majorGridSize = gridSize *5; const majorGridColor = darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; const minorGridColor = darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; ctx.beginPath(); ctx.strokeStyle = gridColor; ctx.lineWidth = 0.5; for (let x = offsetX; x < width; x += gridSize * scale) { ctx.moveTo(x, 0); ctx.lineTo(x, height); } for (let y = offsetY; y < height; y += gridSize * scale) { ctx.moveTo(0, y); ctx.lineTo(width, y); } ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = majorGridColor; ctx.lineWidth = 1.5; // Tính offset cho grid lớn const majorOffsetX = offsetX - (offsetX % (majorGridSize * scale)); const majorOffsetY = offsetY - (offsetY % (majorGridSize * scale)); for (let x = majorOffsetX; x < width; x += majorGridSize * scale) { ctx.moveTo(x, 0); ctx.lineTo(x, height); } // Vẽ các đường ngang lớn for (let y = majorOffsetY; y < height; y += majorGridSize * scale) { ctx.moveTo(0, y); ctx.lineTo(width, y); } ctx.stroke(); } else if (backgroundType === 'dots') { ctx.fillStyle = gridColor; const dotSize = 2; for (let x = offsetX; x < width; x += gridSize * scale) { for (let y = offsetY; y < height; y += gridSize * scale) { ctx.beginPath(); ctx.arc(x, y, 1, 0, Math.PI * 2); ctx.fill(); } } } }, [backgroundType, gridSize, scale, panOffset, scaleOffset, darkMode]); const toggleBackground = () => { setBackgroundType(prev => { if (prev === 'none') return 'grid'; if (prev === 'grid') return 'dots'; return 'none'; }); }; const drawElements = React.useCallback(() => { const canvas = drawingCanvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.save(); ctx.translate(panOffset.x * scale - scaleOffset.x, panOffset.y * scale - scaleOffset.y); ctx.scale(scale, scale); elements.forEach(element => { if (element.type === 'pencil') { ctx.beginPath(); ctx.strokeStyle = element.color || currentColor; ctx.lineWidth = element.size; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; element.points.forEach((point, index) => { if (index === 0) { ctx.moveTo(point.x, point.y); } else { ctx.lineTo(point.x, point.y); } }); ctx.stroke(); } else if (element.type === 'line') { ctx.beginPath(); ctx.strokeStyle = element.color || currentColor; ctx.lineWidth = 2; ctx.moveTo(element.x1, element.y1); ctx.lineTo(element.x2, element.y2); ctx.stroke(); } else if (element.type === 'rectangle') { ctx.strokeStyle = element.color || currentColor; ctx.lineWidth = 2; ctx.strokeRect( element.x1, element.y1, element.x2 - element.x1, element.y2 - element.y1 ); } else if (element.type === 'text') { ctx.font = '24px sans-serif'; ctx.fillStyle = element.color || currentColor; ctx.fillText(element.text, element.x1, element.y1); } }); ctx.restore(); }, [elements, scale, panOffset, scaleOffset, currentColor]); React.useEffect(() => { drawBackground(); drawElements(); }, [drawBackground, drawElements]); const ColorPickerButton = () => (
{showColorPicker && }
); React.useEffect(() => { const handleResize = () => { if (backgroundCanvasRef.current && drawingCanvasRef.current) { backgroundCanvasRef.current.width = window.innerWidth; backgroundCanvasRef.current.height = window.innerHeight; drawingCanvasRef.current.width = window.innerWidth; drawingCanvasRef.current.height = window.innerHeight; drawBackground(); drawElements(); } }; window.addEventListener('resize', handleResize); handleResize(); return () => window.removeEventListener('resize', handleResize); }, [drawBackground, drawElements]); const toggleDarkMode = () => { setDarkMode(prev => !prev); }; const handleDetectAndShowDownload = () => { handleDetectRegions(); setShowDownloadButton(true); setIsMenuOpen(false); }; const iconColor = darkMode ? "#000000" : "#ffffff"; // Đảo ngược màu icon const barBgColor = darkMode ? "bg-white" : "bg-gray-800"; // Giữ nguyên màu nền const textColor = darkMode ? 'text-black' : 'text-white'; // Đảo ngược màu text const handleColorChange =(color)=>{ setCurrentColor(color); setShowColorPicker(false); }; const handleCustomColorChange = (e) =>{ const newColor = e.target.value; setCustomColor(newColor); setCurrentColor(newColor); }; const ColorPicker = () => (
{PRESET_COLORS.map((color, index) => (
{ const val = e.target.value; if (/^#[0-9A-F]{0,6}$/i.test(val)) { setCustomColor(val); if (val.length === 7) setCurrentColor(val); } }} className={`w-20 px-1 py-0.5 text-sm rounded ${ darkMode ? 'bg-gray-700 text-white' : 'bg-white text-black' }`} placeholder="#000000" />
); return (
{/* Corner Menu Button */} {/* Corner Menu Panel */} {isMenuOpen && (
{/* Theme Toggle */} {/* Grid Toggle */} {backgroundType !== 'none' && (
Grid Size {gridSize}px
setGridSize(parseInt(e.target.value))} className="w-full h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer" />
)} {/* Region Detection */}
)} {/* Main Toolbar */}
{(tool === 'pencil') && (
setPencilSize(parseInt(e.target.value))} className="w-24 h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer" title="Pencil Size" /> {pencilSize}px
)}
{Math.round(scale * 100)}%
{/* Download Button */} {showDownloadButton && ( )} {/* Canvas Container */}
{/* Text Area for Writing */} {action === "writing" && (