mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-26 17:52:59 +02:00
Improved performance
This commit is contained in:
parent
b9b425aba0
commit
a8265efff4
86
frontend/src/components/tools/sign/PenSizeSelector.tsx
Normal file
86
frontend/src/components/tools/sign/PenSizeSelector.tsx
Normal file
@ -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 (
|
||||
<Combobox
|
||||
onOptionSubmit={(optionValue) => {
|
||||
const penSize = parseInt(optionValue);
|
||||
if (!isNaN(penSize)) {
|
||||
onValueChange(penSize);
|
||||
onInputChange(optionValue);
|
||||
}
|
||||
combobox.closeDropdown();
|
||||
}}
|
||||
store={combobox}
|
||||
withinPortal={false}
|
||||
>
|
||||
<Combobox.Target>
|
||||
<TextInput
|
||||
placeholder={placeholder}
|
||||
size={size}
|
||||
value={inputValue}
|
||||
onChange={(event) => {
|
||||
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}
|
||||
/>
|
||||
</Combobox.Target>
|
||||
|
||||
<Combobox.Dropdown>
|
||||
<Combobox.Options>
|
||||
{penSizeOptions.map((sizeOption) => (
|
||||
<Combobox.Option value={sizeOption} key={sizeOption}>
|
||||
{sizeOption}px
|
||||
</Combobox.Option>
|
||||
))}
|
||||
</Combobox.Options>
|
||||
</Combobox.Dropdown>
|
||||
</Combobox>
|
||||
);
|
||||
};
|
||||
|
||||
export default PenSizeSelector;
|
@ -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<HTMLCanvasElement>) => {
|
||||
@ -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
|
||||
</div>
|
||||
<div>
|
||||
<Text size="sm" fw={500} mb="xs">Pen Size</Text>
|
||||
<Combobox
|
||||
onOptionSubmit={(optionValue) => {
|
||||
const size = parseInt(optionValue);
|
||||
if (!isNaN(size)) {
|
||||
setPenSize(size);
|
||||
setPenSizeInput(optionValue);
|
||||
}
|
||||
penSizeCombobox.closeDropdown();
|
||||
}}
|
||||
store={penSizeCombobox}
|
||||
withinPortal={false}
|
||||
>
|
||||
<Combobox.Target>
|
||||
<TextInput
|
||||
placeholder="Size"
|
||||
size="compact-sm"
|
||||
value={penSizeInput}
|
||||
onChange={(event) => {
|
||||
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' }}
|
||||
/>
|
||||
</Combobox.Target>
|
||||
|
||||
<Combobox.Dropdown>
|
||||
<Combobox.Options>
|
||||
{['1', '2', '3', '4', '5', '8', '10', '12', '15', '20', '25', '30', '40', '50'].map((size) => (
|
||||
<Combobox.Option value={size} key={size}>
|
||||
{size}px
|
||||
</Combobox.Option>
|
||||
))}
|
||||
</Combobox.Options>
|
||||
</Combobox.Dropdown>
|
||||
</Combobox>
|
||||
<PenSizeSelector
|
||||
value={penSize}
|
||||
inputValue={penSizeInput}
|
||||
onValueChange={setPenSize}
|
||||
onInputChange={setPenSizeInput}
|
||||
disabled={disabled}
|
||||
placeholder="Size"
|
||||
size="compact-sm"
|
||||
style={{ width: '60px' }}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ paddingTop: '24px' }}>
|
||||
<Button
|
||||
@ -733,57 +703,13 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
||||
{/* Pen Size */}
|
||||
<div style={{ flexGrow: 1, maxWidth: '200px' }}>
|
||||
<Text size="sm" fw={500} mb="xs">Pen Size</Text>
|
||||
<Combobox
|
||||
onOptionSubmit={(optionValue) => {
|
||||
const size = parseInt(optionValue);
|
||||
if (!isNaN(size)) {
|
||||
setPenSize(size);
|
||||
setPenSizeInput(optionValue);
|
||||
}
|
||||
penSizeCombobox.closeDropdown();
|
||||
}}
|
||||
store={penSizeCombobox}
|
||||
withinPortal={false}
|
||||
>
|
||||
<Combobox.Target>
|
||||
<TextInput
|
||||
placeholder="Type or select pen size (1-200)"
|
||||
value={penSizeInput}
|
||||
onChange={(event) => {
|
||||
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}
|
||||
/>
|
||||
</Combobox.Target>
|
||||
|
||||
<Combobox.Dropdown>
|
||||
<Combobox.Options>
|
||||
{['1', '2', '3', '4', '5', '8', '10', '12', '15', '20', '25', '30', '40', '50'].map((size) => (
|
||||
<Combobox.Option value={size} key={size}>
|
||||
{size}px
|
||||
</Combobox.Option>
|
||||
))}
|
||||
</Combobox.Options>
|
||||
</Combobox.Dropdown>
|
||||
</Combobox>
|
||||
<PenSizeSelector
|
||||
value={penSize}
|
||||
inputValue={penSizeInput}
|
||||
onValueChange={setPenSize}
|
||||
onInputChange={setPenSizeInput}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</Group>
|
||||
</Stack>
|
||||
@ -834,58 +760,15 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
||||
</div>
|
||||
<div>
|
||||
<Text size="sm" fw={500} mb="xs">Pen Size</Text>
|
||||
<Combobox
|
||||
onOptionSubmit={(optionValue) => {
|
||||
const size = parseInt(optionValue);
|
||||
if (!isNaN(size)) {
|
||||
setPenSize(size);
|
||||
setPenSizeInput(optionValue);
|
||||
}
|
||||
modalPenSizeCombobox.closeDropdown();
|
||||
}}
|
||||
store={modalPenSizeCombobox}
|
||||
withinPortal={false}
|
||||
>
|
||||
<Combobox.Target>
|
||||
<TextInput
|
||||
placeholder="Size"
|
||||
size="compact-sm"
|
||||
value={penSizeInput}
|
||||
onChange={(event) => {
|
||||
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' }}
|
||||
/>
|
||||
</Combobox.Target>
|
||||
|
||||
<Combobox.Dropdown>
|
||||
<Combobox.Options>
|
||||
{['1', '2', '3', '4', '5', '8', '10', '12', '15', '20', '25', '30', '40', '50'].map((size) => (
|
||||
<Combobox.Option value={size} key={size}>
|
||||
{size}px
|
||||
</Combobox.Option>
|
||||
))}
|
||||
</Combobox.Options>
|
||||
</Combobox.Dropdown>
|
||||
</Combobox>
|
||||
<PenSizeSelector
|
||||
value={penSize}
|
||||
inputValue={penSizeInput}
|
||||
onValueChange={setPenSize}
|
||||
onInputChange={setPenSizeInput}
|
||||
placeholder="Size"
|
||||
size="compact-sm"
|
||||
style={{ width: '60px' }}
|
||||
/>
|
||||
</div>
|
||||
</Group>
|
||||
</Paper>
|
||||
|
Loading…
Reference in New Issue
Block a user