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
-
- ))}
-
-
-
+