mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-09-24 04:26:14 +00:00
Fix sidebar refresh. Updated UI
This commit is contained in:
parent
efc0c1aab3
commit
d9798badae
@ -1,6 +1,6 @@
|
|||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Stack, TextInput, FileInput, Paper, Group, Button, Text, Alert, Modal, ColorSwatch, Menu, ActionIcon, Slider, Select, Combobox, useCombobox } from '@mantine/core';
|
import { Stack, TextInput, FileInput, Paper, Group, Button, Text, Alert, Modal, ColorSwatch, Menu, ActionIcon, Slider, Select, Combobox, useCombobox, ColorPicker } from '@mantine/core';
|
||||||
import ButtonSelector from "../../shared/ButtonSelector";
|
import ButtonSelector from "../../shared/ButtonSelector";
|
||||||
import { SignParameters } from "../../../hooks/tools/sign/useSignParameters";
|
import { SignParameters } from "../../../hooks/tools/sign/useSignParameters";
|
||||||
|
|
||||||
@ -22,6 +22,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
const [canvasSignatureData, setCanvasSignatureData] = useState<string | null>(null);
|
const [canvasSignatureData, setCanvasSignatureData] = useState<string | null>(null);
|
||||||
const [imageSignatureData, setImageSignatureData] = useState<string | null>(null);
|
const [imageSignatureData, setImageSignatureData] = useState<string | null>(null);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [isColorPickerOpen, setIsColorPickerOpen] = useState(false);
|
||||||
const modalCanvasRef = useRef<HTMLCanvasElement>(null);
|
const modalCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const visibleModalCanvasRef = useRef<HTMLCanvasElement>(null);
|
const visibleModalCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const [isModalDrawing, setIsModalDrawing] = useState(false);
|
const [isModalDrawing, setIsModalDrawing] = useState(false);
|
||||||
@ -31,6 +32,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
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 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>) => {
|
||||||
@ -260,7 +262,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
|
|
||||||
// Set the new image as the active signature
|
// Set the new image as the active signature
|
||||||
setImageSignatureData(e.target.result as string);
|
setImageSignatureData(e.target.result as string);
|
||||||
onParameterChange('signatureData', e.target.result as string);
|
// Don't call onParameterChange here - let the useEffect handle it
|
||||||
|
|
||||||
// Auto-activate placement mode after image upload
|
// Auto-activate placement mode after image upload
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -275,14 +277,12 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
} else if (!file) {
|
} else if (!file) {
|
||||||
// Clear image signature when no file is selected
|
// Clear image signature when no file is selected
|
||||||
setImageSignatureData(null);
|
setImageSignatureData(null);
|
||||||
if (parameters.signatureType === 'image') {
|
// Don't call onParameterChange here - let the useEffect handle it
|
||||||
onParameterChange('signatureData', undefined);
|
|
||||||
// Deactivate signature placement when image is removed
|
// Deactivate signature placement when image is removed
|
||||||
if (onDeactivateSignature) {
|
if (onDeactivateSignature) {
|
||||||
onDeactivateSignature();
|
onDeactivateSignature();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize canvas
|
// Initialize canvas
|
||||||
@ -460,77 +460,25 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
|
|
||||||
{/* Signature Creation based on type */}
|
{/* Signature Creation based on type */}
|
||||||
{parameters.signatureType === 'canvas' && (
|
{parameters.signatureType === 'canvas' && (
|
||||||
<Paper withBorder p="md" style={{ position: 'relative' }}>
|
<Paper withBorder p="md">
|
||||||
<ActionIcon
|
|
||||||
variant="filled"
|
|
||||||
color="blue"
|
|
||||||
size="sm"
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 8,
|
|
||||||
right: 8,
|
|
||||||
zIndex: 10,
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setIsModalOpen(true);
|
|
||||||
// Copy content to modal canvas after a brief delay
|
|
||||||
setTimeout(() => {
|
|
||||||
if (visibleModalCanvasRef.current && modalCanvasRef.current) {
|
|
||||||
const visibleCtx = visibleModalCanvasRef.current.getContext('2d');
|
|
||||||
if (visibleCtx) {
|
|
||||||
visibleCtx.strokeStyle = selectedColor;
|
|
||||||
visibleCtx.lineWidth = penSize;
|
|
||||||
visibleCtx.lineCap = 'round';
|
|
||||||
visibleCtx.lineJoin = 'round';
|
|
||||||
visibleCtx.clearRect(0, 0, visibleModalCanvasRef.current.width, visibleModalCanvasRef.current.height);
|
|
||||||
visibleCtx.drawImage(modalCanvasRef.current, 0, 0, visibleModalCanvasRef.current.width, visibleModalCanvasRef.current.height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
}}
|
|
||||||
disabled={disabled}
|
|
||||||
title="Expand Canvas"
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</ActionIcon>
|
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Group justify="space-between">
|
<Group justify="space-between">
|
||||||
<Text fw={500}>{t('sign.draw.title', 'Draw your signature')}</Text>
|
<Text fw={500}>{t('sign.draw.title', 'Draw your signature')}</Text>
|
||||||
<Group gap="sm">
|
<Group gap="lg">
|
||||||
<Menu shadow="md" width={200}>
|
<div>
|
||||||
<Menu.Target>
|
<Text size="sm" fw={500} mb="xs" ta="center">Color</Text>
|
||||||
<Button
|
<Group justify="center">
|
||||||
variant="subtle"
|
|
||||||
size="compact-sm"
|
|
||||||
disabled={disabled}
|
|
||||||
rightSection={
|
|
||||||
<ColorSwatch
|
<ColorSwatch
|
||||||
color={selectedColor}
|
color={selectedColor}
|
||||||
size={12}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Color
|
|
||||||
</Button>
|
|
||||||
</Menu.Target>
|
|
||||||
<Menu.Dropdown>
|
|
||||||
<Menu.Label>Select Color</Menu.Label>
|
|
||||||
<Group gap="xs" p="xs">
|
|
||||||
{['#000000', '#0066cc', '#cc0000', '#cc6600', '#009900', '#6600cc'].map((color) => (
|
|
||||||
<ColorSwatch
|
|
||||||
key={color}
|
|
||||||
color={color}
|
|
||||||
size={24}
|
size={24}
|
||||||
style={{
|
radius={0}
|
||||||
cursor: 'pointer',
|
style={{ cursor: 'pointer' }}
|
||||||
border: selectedColor === color ? '2px solid #333' : '1px solid #ddd'
|
onClick={() => setIsColorPickerOpen(true)}
|
||||||
}}
|
|
||||||
onClick={() => setSelectedColor(color)}
|
|
||||||
/>
|
/>
|
||||||
))}
|
|
||||||
</Group>
|
</Group>
|
||||||
</Menu.Dropdown>
|
</div>
|
||||||
</Menu>
|
<div>
|
||||||
|
<Text size="sm" fw={500} mb="xs">Pen Size</Text>
|
||||||
<Combobox
|
<Combobox
|
||||||
onOptionSubmit={(optionValue) => {
|
onOptionSubmit={(optionValue) => {
|
||||||
const size = parseInt(optionValue);
|
const size = parseInt(optionValue);
|
||||||
@ -545,7 +493,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
>
|
>
|
||||||
<Combobox.Target>
|
<Combobox.Target>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Pen size"
|
placeholder="Size"
|
||||||
size="compact-sm"
|
size="compact-sm"
|
||||||
value={penSizeInput}
|
value={penSizeInput}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
@ -570,7 +518,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
style={{ width: '80px' }}
|
style={{ width: '60px' }}
|
||||||
/>
|
/>
|
||||||
</Combobox.Target>
|
</Combobox.Target>
|
||||||
|
|
||||||
@ -584,16 +532,33 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
</Combobox.Options>
|
</Combobox.Options>
|
||||||
</Combobox.Dropdown>
|
</Combobox.Dropdown>
|
||||||
</Combobox>
|
</Combobox>
|
||||||
|
</div>
|
||||||
|
<div style={{ paddingTop: '24px' }}>
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="light"
|
||||||
color="red"
|
|
||||||
size="compact-sm"
|
size="compact-sm"
|
||||||
onClick={clearCanvas}
|
onClick={() => {
|
||||||
|
setIsModalOpen(true);
|
||||||
|
// Copy content to modal canvas after a brief delay
|
||||||
|
setTimeout(() => {
|
||||||
|
if (visibleModalCanvasRef.current && modalCanvasRef.current) {
|
||||||
|
const visibleCtx = visibleModalCanvasRef.current.getContext('2d');
|
||||||
|
if (visibleCtx) {
|
||||||
|
visibleCtx.strokeStyle = selectedColor;
|
||||||
|
visibleCtx.lineWidth = penSize;
|
||||||
|
visibleCtx.lineCap = 'round';
|
||||||
|
visibleCtx.lineJoin = 'round';
|
||||||
|
visibleCtx.clearRect(0, 0, visibleModalCanvasRef.current.width, visibleModalCanvasRef.current.height);
|
||||||
|
visibleCtx.drawImage(modalCanvasRef.current, 0, 0, visibleModalCanvasRef.current.width, visibleModalCanvasRef.current.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{t('sign.draw.clear', 'Clear')}
|
Expand
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<canvas
|
<canvas
|
||||||
@ -612,6 +577,17 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
onMouseUp={stopDrawing}
|
onMouseUp={stopDrawing}
|
||||||
onMouseLeave={stopDrawing}
|
onMouseLeave={stopDrawing}
|
||||||
/>
|
/>
|
||||||
|
<Group justify="flex-end">
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
color="red"
|
||||||
|
size="compact-sm"
|
||||||
|
onClick={clearCanvas}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{t('sign.draw.clear', 'Clear')}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
)}
|
)}
|
||||||
@ -733,23 +709,13 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
{/* Drawing Controls */}
|
{/* Drawing Controls */}
|
||||||
<Group gap="md" align="flex-end">
|
<Group gap="md" align="flex-end">
|
||||||
{/* Color Picker */}
|
{/* Color Picker */}
|
||||||
<div>
|
|
||||||
<Text size="sm" fw={500} mb="xs">Color</Text>
|
|
||||||
<Group gap="xs">
|
|
||||||
{['#000000', '#0066cc', '#cc0000', '#cc6600', '#009900', '#6600cc'].map((color) => (
|
|
||||||
<ColorSwatch
|
<ColorSwatch
|
||||||
key={color}
|
color={selectedColor}
|
||||||
color={color}
|
|
||||||
size={24}
|
size={24}
|
||||||
style={{
|
radius={0}
|
||||||
cursor: 'pointer',
|
style={{ cursor: 'pointer' }}
|
||||||
border: selectedColor === color ? '2px solid #333' : '1px solid #ddd'
|
onClick={() => setIsColorPickerOpen(true)}
|
||||||
}}
|
|
||||||
onClick={() => setSelectedColor(color)}
|
|
||||||
/>
|
/>
|
||||||
))}
|
|
||||||
</Group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Pen Size */}
|
{/* Pen Size */}
|
||||||
<div style={{ flexGrow: 1, maxWidth: '200px' }}>
|
<div style={{ flexGrow: 1, maxWidth: '200px' }}>
|
||||||
@ -844,22 +810,17 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
<Paper withBorder p="sm">
|
<Paper withBorder p="sm">
|
||||||
<Group gap="lg" align="flex-end">
|
<Group gap="lg" align="flex-end">
|
||||||
<div>
|
<div>
|
||||||
<Text size="sm" fw={500} mb="xs">Color:</Text>
|
<Text size="sm" fw={500} mb="xs">Color</Text>
|
||||||
<Group gap="xs">
|
|
||||||
{['#000000', '#0066cc', '#cc0000', '#cc6600', '#009900', '#6600cc'].map((color) => (
|
|
||||||
<ColorSwatch
|
<ColorSwatch
|
||||||
key={color}
|
color={selectedColor}
|
||||||
color={color}
|
|
||||||
size={24}
|
size={24}
|
||||||
style={{ cursor: 'pointer', border: selectedColor === color ? '2px solid #333' : 'none' }}
|
radius={0}
|
||||||
onClick={() => setSelectedColor(color)}
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => setIsColorPickerOpen(true)}
|
||||||
/>
|
/>
|
||||||
))}
|
|
||||||
</Group>
|
|
||||||
</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
|
<Combobox
|
||||||
onOptionSubmit={(optionValue) => {
|
onOptionSubmit={(optionValue) => {
|
||||||
const size = parseInt(optionValue);
|
const size = parseInt(optionValue);
|
||||||
@ -867,15 +828,15 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
setPenSize(size);
|
setPenSize(size);
|
||||||
setPenSizeInput(optionValue);
|
setPenSizeInput(optionValue);
|
||||||
}
|
}
|
||||||
penSizeCombobox.closeDropdown();
|
modalPenSizeCombobox.closeDropdown();
|
||||||
}}
|
}}
|
||||||
store={penSizeCombobox}
|
store={modalPenSizeCombobox}
|
||||||
withinPortal={false}
|
withinPortal={false}
|
||||||
>
|
>
|
||||||
<Combobox.Target>
|
<Combobox.Target>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Pen size"
|
placeholder="Size"
|
||||||
size="sm"
|
size="compact-sm"
|
||||||
value={penSizeInput}
|
value={penSizeInput}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
const value = event.currentTarget.value;
|
const value = event.currentTarget.value;
|
||||||
@ -886,19 +847,19 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
setPenSize(size);
|
setPenSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
penSizeCombobox.openDropdown();
|
modalPenSizeCombobox.openDropdown();
|
||||||
penSizeCombobox.updateSelectedOptionIndex();
|
modalPenSizeCombobox.updateSelectedOptionIndex();
|
||||||
}}
|
}}
|
||||||
onClick={() => penSizeCombobox.openDropdown()}
|
onClick={() => modalPenSizeCombobox.openDropdown()}
|
||||||
onFocus={() => penSizeCombobox.openDropdown()}
|
onFocus={() => modalPenSizeCombobox.openDropdown()}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
penSizeCombobox.closeDropdown();
|
modalPenSizeCombobox.closeDropdown();
|
||||||
const size = parseInt(penSizeInput);
|
const size = parseInt(penSizeInput);
|
||||||
if (isNaN(size) || size < 1 || size > 200) {
|
if (isNaN(size) || size < 1 || size > 200) {
|
||||||
setPenSizeInput(penSize.toString());
|
setPenSizeInput(penSize.toString());
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{ width: '100px' }}
|
style={{ width: '60px' }}
|
||||||
/>
|
/>
|
||||||
</Combobox.Target>
|
</Combobox.Target>
|
||||||
|
|
||||||
@ -961,6 +922,32 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
{/* Color Picker Modal */}
|
||||||
|
<Modal
|
||||||
|
opened={isColorPickerOpen}
|
||||||
|
onClose={() => setIsColorPickerOpen(false)}
|
||||||
|
title="Choose Color"
|
||||||
|
size="sm"
|
||||||
|
centered
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
|
<ColorPicker
|
||||||
|
format="hex"
|
||||||
|
value={selectedColor}
|
||||||
|
onChange={setSelectedColor}
|
||||||
|
swatches={['#000000', '#0066cc', '#cc0000', '#cc6600', '#009900', '#6600cc']}
|
||||||
|
swatchesPerRow={6}
|
||||||
|
size="lg"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<Group justify="flex-end">
|
||||||
|
<Button onClick={() => setIsColorPickerOpen(false)}>
|
||||||
|
Done
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -25,7 +25,7 @@ export function ThumbnailSidebar({ visible, onToggle: _onToggle }: ThumbnailSide
|
|||||||
});
|
});
|
||||||
setThumbnails({});
|
setThumbnails({});
|
||||||
}
|
}
|
||||||
}, [visible, thumbnails]);
|
}, [visible]); // Remove thumbnails from dependency to prevent infinite loop
|
||||||
|
|
||||||
// Generate thumbnails when sidebar becomes visible
|
// Generate thumbnails when sidebar becomes visible
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user