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 = () => (