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 ButtonSelector from "../../shared/ButtonSelector";
|
||||||
import { SignParameters } from "../../../hooks/tools/sign/useSignParameters";
|
import { SignParameters } from "../../../hooks/tools/sign/useSignParameters";
|
||||||
import { SuggestedToolsSection } from "../shared/SuggestedToolsSection";
|
import { SuggestedToolsSection } from "../shared/SuggestedToolsSection";
|
||||||
|
import PenSizeSelector from "./PenSizeSelector";
|
||||||
|
|
||||||
interface SignSettingsProps {
|
interface SignSettingsProps {
|
||||||
parameters: SignParameters;
|
parameters: SignParameters;
|
||||||
@ -34,8 +35,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
const [penSizeInput, setPenSizeInput] = useState('2');
|
const [penSizeInput, setPenSizeInput] = useState('2');
|
||||||
const [fontSizeInput, setFontSizeInput] = useState((parameters.fontSize || 16).toString());
|
const [fontSizeInput, setFontSizeInput] = useState((parameters.fontSize || 16).toString());
|
||||||
const fontSizeCombobox = useCombobox();
|
const fontSizeCombobox = useCombobox();
|
||||||
const penSizeCombobox = useCombobox();
|
|
||||||
const modalPenSizeCombobox = useCombobox();
|
|
||||||
|
|
||||||
// Drawing functions for signature canvas
|
// Drawing functions for signature canvas
|
||||||
const startDrawing = (e: React.MouseEvent<HTMLCanvasElement>) => {
|
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 x = (e.clientX - rect.left) * scaleX;
|
||||||
const y = (e.clientY - rect.top) * scaleY;
|
const y = (e.clientY - rect.top) * scaleY;
|
||||||
|
|
||||||
|
|
||||||
const ctx = canvasRef.current.getContext('2d');
|
const ctx = canvasRef.current.getContext('2d');
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
ctx.strokeStyle = selectedColor;
|
ctx.strokeStyle = selectedColor;
|
||||||
@ -73,10 +74,8 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
ctx.lineTo(x, y);
|
ctx.lineTo(x, y);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
// Update signature data immediately after each stroke
|
// Don't update signature data during drawing - too expensive
|
||||||
const dataURL = canvasRef.current.toDataURL('image/png');
|
// This will happen in stopDrawing instead
|
||||||
setCanvasSignatureData(dataURL);
|
|
||||||
onParameterChange('signatureData', dataURL);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -136,6 +135,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
const x = (e.clientX - rect.left) * scaleX;
|
const x = (e.clientX - rect.left) * scaleX;
|
||||||
const y = (e.clientY - rect.top) * scaleY;
|
const y = (e.clientY - rect.top) * scaleY;
|
||||||
|
|
||||||
|
|
||||||
// Draw on both the visible modal canvas and hidden canvas
|
// Draw on both the visible modal canvas and hidden canvas
|
||||||
const visibleCtx = visibleModalCanvasRef.current.getContext('2d');
|
const visibleCtx = visibleModalCanvasRef.current.getContext('2d');
|
||||||
const hiddenCtx = modalCanvasRef.current.getContext('2d');
|
const hiddenCtx = modalCanvasRef.current.getContext('2d');
|
||||||
@ -172,7 +172,16 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update signature data from hidden canvas (consistent size)
|
// 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');
|
const dataURL = modalCanvasRef.current.toDataURL('image/png');
|
||||||
setCanvasSignatureData(dataURL);
|
setCanvasSignatureData(dataURL);
|
||||||
onParameterChange('signatureData', dataURL);
|
onParameterChange('signatureData', dataURL);
|
||||||
@ -189,19 +198,24 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
img.src = dataURL;
|
img.src = dataURL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const stopModalDrawing = () => {
|
|
||||||
if (!isModalDrawing) return;
|
|
||||||
setIsModalDrawing(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearModalCanvas = () => {
|
const clearModalCanvas = () => {
|
||||||
if (!modalCanvasRef.current) return;
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const ctx = modalCanvasRef.current.getContext('2d');
|
if (visibleModalCanvasRef.current) {
|
||||||
if (ctx) {
|
const visibleCtx = visibleModalCanvasRef.current.getContext('2d');
|
||||||
ctx.clearRect(0, 0, modalCanvasRef.current.width, modalCanvasRef.current.height);
|
if (visibleCtx) {
|
||||||
|
visibleCtx.clearRect(0, 0, visibleModalCanvasRef.current.width, visibleModalCanvasRef.current.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Also clear the main canvas and signature data
|
// Also clear the main canvas and signature data
|
||||||
if (canvasRef.current) {
|
if (canvasRef.current) {
|
||||||
@ -218,7 +232,6 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
if (onDeactivateSignature) {
|
if (onDeactivateSignature) {
|
||||||
onDeactivateSignature();
|
onDeactivateSignature();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveModalSignature = () => {
|
const saveModalSignature = () => {
|
||||||
@ -496,59 +509,16 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Text size="sm" fw={500} mb="xs">Pen Size</Text>
|
<Text size="sm" fw={500} mb="xs">Pen Size</Text>
|
||||||
<Combobox
|
<PenSizeSelector
|
||||||
onOptionSubmit={(optionValue) => {
|
value={penSize}
|
||||||
const size = parseInt(optionValue);
|
inputValue={penSizeInput}
|
||||||
if (!isNaN(size)) {
|
onValueChange={setPenSize}
|
||||||
setPenSize(size);
|
onInputChange={setPenSizeInput}
|
||||||
setPenSizeInput(optionValue);
|
disabled={disabled}
|
||||||
}
|
|
||||||
penSizeCombobox.closeDropdown();
|
|
||||||
}}
|
|
||||||
store={penSizeCombobox}
|
|
||||||
withinPortal={false}
|
|
||||||
>
|
|
||||||
<Combobox.Target>
|
|
||||||
<TextInput
|
|
||||||
placeholder="Size"
|
placeholder="Size"
|
||||||
size="compact-sm"
|
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' }}
|
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>
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ paddingTop: '24px' }}>
|
<div style={{ paddingTop: '24px' }}>
|
||||||
<Button
|
<Button
|
||||||
@ -733,57 +703,13 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
{/* Pen Size */}
|
{/* Pen Size */}
|
||||||
<div style={{ flexGrow: 1, maxWidth: '200px' }}>
|
<div style={{ flexGrow: 1, maxWidth: '200px' }}>
|
||||||
<Text size="sm" fw={500} mb="xs">Pen Size</Text>
|
<Text size="sm" fw={500} mb="xs">Pen Size</Text>
|
||||||
<Combobox
|
<PenSizeSelector
|
||||||
onOptionSubmit={(optionValue) => {
|
value={penSize}
|
||||||
const size = parseInt(optionValue);
|
inputValue={penSizeInput}
|
||||||
if (!isNaN(size)) {
|
onValueChange={setPenSize}
|
||||||
setPenSize(size);
|
onInputChange={setPenSizeInput}
|
||||||
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}
|
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>
|
|
||||||
</div>
|
</div>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
@ -834,58 +760,15 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Text size="sm" fw={500} mb="xs">Pen Size</Text>
|
<Text size="sm" fw={500} mb="xs">Pen Size</Text>
|
||||||
<Combobox
|
<PenSizeSelector
|
||||||
onOptionSubmit={(optionValue) => {
|
value={penSize}
|
||||||
const size = parseInt(optionValue);
|
inputValue={penSizeInput}
|
||||||
if (!isNaN(size)) {
|
onValueChange={setPenSize}
|
||||||
setPenSize(size);
|
onInputChange={setPenSizeInput}
|
||||||
setPenSizeInput(optionValue);
|
|
||||||
}
|
|
||||||
modalPenSizeCombobox.closeDropdown();
|
|
||||||
}}
|
|
||||||
store={modalPenSizeCombobox}
|
|
||||||
withinPortal={false}
|
|
||||||
>
|
|
||||||
<Combobox.Target>
|
|
||||||
<TextInput
|
|
||||||
placeholder="Size"
|
placeholder="Size"
|
||||||
size="compact-sm"
|
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' }}
|
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>
|
|
||||||
</div>
|
</div>
|
||||||
</Group>
|
</Group>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
Loading…
Reference in New Issue
Block a user