From a8265efff49e16339b1d64dca6fa9702884301c2 Mon Sep 17 00:00:00 2001 From: Reece Date: Fri, 26 Sep 2025 03:19:05 +0100 Subject: [PATCH] Improved performance --- .../components/tools/sign/PenSizeSelector.tsx | 86 ++++++ .../components/tools/sign/SignSettings.tsx | 277 +++++------------- 2 files changed, 166 insertions(+), 197 deletions(-) create mode 100644 frontend/src/components/tools/sign/PenSizeSelector.tsx diff --git a/frontend/src/components/tools/sign/PenSizeSelector.tsx b/frontend/src/components/tools/sign/PenSizeSelector.tsx new file mode 100644 index 000000000..7c74bf47a --- /dev/null +++ b/frontend/src/components/tools/sign/PenSizeSelector.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { TextInput, Combobox, useCombobox } from '@mantine/core'; + +interface PenSizeSelectorProps { + value: number; + inputValue: string; + onValueChange: (size: number) => void; + onInputChange: (input: string) => void; + disabled?: boolean; + placeholder?: string; + style?: React.CSSProperties; + size?: string; +} + +const PenSizeSelector = ({ + value, + inputValue, + onValueChange, + onInputChange, + disabled = false, + placeholder = "Type or select pen size (1-200)", + style, + size +}: PenSizeSelectorProps) => { + const combobox = useCombobox(); + + const penSizeOptions = ['1', '2', '3', '4', '5', '8', '10', '12', '15', '20']; + + return ( + { + const penSize = parseInt(optionValue); + if (!isNaN(penSize)) { + onValueChange(penSize); + onInputChange(optionValue); + } + combobox.closeDropdown(); + }} + store={combobox} + withinPortal={false} + > + + { + const inputVal = event.currentTarget.value; + onInputChange(inputVal); + + const penSize = parseInt(inputVal); + if (!isNaN(penSize) && penSize >= 1 && penSize <= 200) { + onValueChange(penSize); + } + + combobox.openDropdown(); + combobox.updateSelectedOptionIndex(); + }} + onClick={() => combobox.openDropdown()} + onFocus={() => combobox.openDropdown()} + onBlur={() => { + combobox.closeDropdown(); + const penSize = parseInt(inputValue); + if (isNaN(penSize) || penSize < 1 || penSize > 200) { + onInputChange(value.toString()); + } + }} + disabled={disabled} + style={style} + /> + + + + + {penSizeOptions.map((sizeOption) => ( + + {sizeOption}px + + ))} + + + + ); +}; + +export default PenSizeSelector; \ No newline at end of file diff --git a/frontend/src/components/tools/sign/SignSettings.tsx b/frontend/src/components/tools/sign/SignSettings.tsx index b23c3d142..0f234c69e 100644 --- a/frontend/src/components/tools/sign/SignSettings.tsx +++ b/frontend/src/components/tools/sign/SignSettings.tsx @@ -4,6 +4,7 @@ import { Stack, TextInput, FileInput, Paper, Group, Button, Text, Alert, Modal, import ButtonSelector from "../../shared/ButtonSelector"; import { SignParameters } from "../../../hooks/tools/sign/useSignParameters"; import { SuggestedToolsSection } from "../shared/SuggestedToolsSection"; +import PenSizeSelector from "./PenSizeSelector"; interface SignSettingsProps { parameters: SignParameters; @@ -34,8 +35,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv const [penSizeInput, setPenSizeInput] = useState('2'); const [fontSizeInput, setFontSizeInput] = useState((parameters.fontSize || 16).toString()); const fontSizeCombobox = useCombobox(); - const penSizeCombobox = useCombobox(); - const modalPenSizeCombobox = useCombobox(); + // Drawing functions for signature canvas const startDrawing = (e: React.MouseEvent) => { @@ -48,6 +48,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv const x = (e.clientX - rect.left) * scaleX; const y = (e.clientY - rect.top) * scaleY; + const ctx = canvasRef.current.getContext('2d'); if (ctx) { ctx.strokeStyle = selectedColor; @@ -73,10 +74,8 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv ctx.lineTo(x, y); ctx.stroke(); - // Update signature data immediately after each stroke - const dataURL = canvasRef.current.toDataURL('image/png'); - setCanvasSignatureData(dataURL); - onParameterChange('signatureData', dataURL); + // Don't update signature data during drawing - too expensive + // This will happen in stopDrawing instead } }; @@ -136,6 +135,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv const x = (e.clientX - rect.left) * scaleX; const y = (e.clientY - rect.top) * scaleY; + // Draw on both the visible modal canvas and hidden canvas const visibleCtx = visibleModalCanvasRef.current.getContext('2d'); const hiddenCtx = modalCanvasRef.current.getContext('2d'); @@ -172,53 +172,66 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv } }); - // Update signature data from hidden canvas (consistent size) - const dataURL = modalCanvasRef.current.toDataURL('image/png'); - setCanvasSignatureData(dataURL); - onParameterChange('signatureData', dataURL); - - // Also update the small canvas display - if (canvasRef.current) { - const smallCtx = canvasRef.current.getContext('2d'); - if (smallCtx) { - const img = new Image(); - img.onload = () => { - smallCtx.clearRect(0, 0, canvasRef.current!.width, canvasRef.current!.height); - smallCtx.drawImage(img, 0, 0, canvasRef.current!.width, canvasRef.current!.height); - }; - img.src = dataURL; - } - } + // Don't update signature data or sync canvases during drawing - too expensive + // This will happen in stopModalDrawing instead }; const stopModalDrawing = () => { if (!isModalDrawing) return; setIsModalDrawing(false); + + // Now sync the canvases and update signature data (only when drawing stops) + if (modalCanvasRef.current) { + const dataURL = modalCanvasRef.current.toDataURL('image/png'); + setCanvasSignatureData(dataURL); + onParameterChange('signatureData', dataURL); + + // Also update the small canvas display + if (canvasRef.current) { + const smallCtx = canvasRef.current.getContext('2d'); + if (smallCtx) { + const img = new Image(); + img.onload = () => { + smallCtx.clearRect(0, 0, canvasRef.current!.width, canvasRef.current!.height); + smallCtx.drawImage(img, 0, 0, canvasRef.current!.width, canvasRef.current!.height); + }; + img.src = dataURL; + } + } + } }; const clearModalCanvas = () => { - if (!modalCanvasRef.current) return; - - const ctx = modalCanvasRef.current.getContext('2d'); - if (ctx) { - ctx.clearRect(0, 0, modalCanvasRef.current.width, modalCanvasRef.current.height); - - // Also clear the main canvas and signature data - if (canvasRef.current) { - const mainCtx = canvasRef.current.getContext('2d'); - if (mainCtx) { - mainCtx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height); - } + // Clear both modal canvases (visible and hidden) + if (modalCanvasRef.current) { + const hiddenCtx = modalCanvasRef.current.getContext('2d'); + if (hiddenCtx) { + hiddenCtx.clearRect(0, 0, modalCanvasRef.current.width, modalCanvasRef.current.height); } + } - setCanvasSignatureData(null); - onParameterChange('signatureData', undefined); - - // Deactivate signature placement when cleared - if (onDeactivateSignature) { - onDeactivateSignature(); + if (visibleModalCanvasRef.current) { + const visibleCtx = visibleModalCanvasRef.current.getContext('2d'); + if (visibleCtx) { + visibleCtx.clearRect(0, 0, visibleModalCanvasRef.current.width, visibleModalCanvasRef.current.height); } } + + // Also clear the main canvas and signature data + if (canvasRef.current) { + const mainCtx = canvasRef.current.getContext('2d'); + if (mainCtx) { + mainCtx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height); + } + } + + setCanvasSignatureData(null); + onParameterChange('signatureData', undefined); + + // Deactivate signature placement when cleared + if (onDeactivateSignature) { + onDeactivateSignature(); + } }; const saveModalSignature = () => { @@ -496,59 +509,16 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
Pen Size - { - const size = parseInt(optionValue); - if (!isNaN(size)) { - setPenSize(size); - setPenSizeInput(optionValue); - } - penSizeCombobox.closeDropdown(); - }} - store={penSizeCombobox} - withinPortal={false} - > - - { - const value = event.currentTarget.value; - setPenSizeInput(value); - - const size = parseInt(value); - if (!isNaN(size) && size >= 1 && size <= 200) { - setPenSize(size); - } - - penSizeCombobox.openDropdown(); - penSizeCombobox.updateSelectedOptionIndex(); - }} - onClick={() => penSizeCombobox.openDropdown()} - onFocus={() => penSizeCombobox.openDropdown()} - onBlur={() => { - penSizeCombobox.closeDropdown(); - const size = parseInt(penSizeInput); - if (isNaN(size) || size < 1 || size > 200) { - setPenSizeInput(penSize.toString()); - } - }} - disabled={disabled} - style={{ width: '60px' }} - /> - - - - - {['1', '2', '3', '4', '5', '8', '10', '12', '15', '20', '25', '30', '40', '50'].map((size) => ( - - {size}px - - ))} - - - +
@@ -834,58 +760,15 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
Pen Size - { - const size = parseInt(optionValue); - if (!isNaN(size)) { - setPenSize(size); - setPenSizeInput(optionValue); - } - modalPenSizeCombobox.closeDropdown(); - }} - store={modalPenSizeCombobox} - withinPortal={false} - > - - { - const value = event.currentTarget.value; - setPenSizeInput(value); - - const size = parseInt(value); - if (!isNaN(size) && size >= 1 && size <= 200) { - setPenSize(size); - } - - modalPenSizeCombobox.openDropdown(); - modalPenSizeCombobox.updateSelectedOptionIndex(); - }} - onClick={() => modalPenSizeCombobox.openDropdown()} - onFocus={() => modalPenSizeCombobox.openDropdown()} - onBlur={() => { - modalPenSizeCombobox.closeDropdown(); - const size = parseInt(penSizeInput); - if (isNaN(size) || size < 1 || size > 200) { - setPenSizeInput(penSize.toString()); - } - }} - style={{ width: '60px' }} - /> - - - - - {['1', '2', '3', '4', '5', '8', '10', '12', '15', '20', '25', '30', '40', '50'].map((size) => ( - - {size}px - - ))} - - - +