Canvas and dosument draw split, drawing improvements

This commit is contained in:
Reece Browne 2025-09-22 14:03:49 +01:00
parent a70472b172
commit 32fed96aa7
5 changed files with 478 additions and 72 deletions

View File

@ -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 } from '@mantine/core'; import { Stack, TextInput, FileInput, Paper, Group, Button, Text, Alert, Modal, ColorSwatch, Menu, ActionIcon, Slider } 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";
@ -11,13 +11,21 @@ interface SignSettingsProps {
onActivateDrawMode?: () => void; onActivateDrawMode?: () => void;
onActivateSignaturePlacement?: () => void; onActivateSignaturePlacement?: () => void;
onDeactivateSignature?: () => void; onDeactivateSignature?: () => void;
onUpdateDrawSettings?: (color: string, size: number) => void;
} }
const SignSettings = ({ parameters, onParameterChange, disabled = false, onActivateDrawMode, onActivateSignaturePlacement, onDeactivateSignature }: SignSettingsProps) => { const SignSettings = ({ parameters, onParameterChange, disabled = false, onActivateDrawMode, onActivateSignaturePlacement, onDeactivateSignature, onUpdateDrawSettings }: SignSettingsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
const [isDrawing, setIsDrawing] = useState(false); const [isDrawing, setIsDrawing] = useState(false);
const [signatureImage, setSignatureImage] = useState<File | null>(null); const [signatureImage, setSignatureImage] = useState<File | null>(null);
const [canvasSignatureData, setCanvasSignatureData] = useState<string | null>(null);
const [imageSignatureData, setImageSignatureData] = useState<string | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const modalCanvasRef = useRef<HTMLCanvasElement>(null);
const [isModalDrawing, setIsModalDrawing] = useState(false);
const [selectedColor, setSelectedColor] = useState('#000000');
const [penSize, setPenSize] = useState(2);
// Drawing functions for signature canvas // Drawing functions for signature canvas
const startDrawing = (e: React.MouseEvent<HTMLCanvasElement>) => { const startDrawing = (e: React.MouseEvent<HTMLCanvasElement>) => {
@ -25,11 +33,14 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
setIsDrawing(true); setIsDrawing(true);
const rect = canvasRef.current.getBoundingClientRect(); const rect = canvasRef.current.getBoundingClientRect();
const x = e.clientX - rect.left; const scaleX = canvasRef.current.width / rect.width;
const y = e.clientY - rect.top; const scaleY = canvasRef.current.height / rect.height;
const x = (e.clientX - rect.left) * scaleX;
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.beginPath(); ctx.beginPath();
ctx.moveTo(x, y); ctx.moveTo(x, y);
} }
@ -39,8 +50,10 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
if (!isDrawing || !canvasRef.current || disabled) return; if (!isDrawing || !canvasRef.current || disabled) return;
const rect = canvasRef.current.getBoundingClientRect(); const rect = canvasRef.current.getBoundingClientRect();
const x = e.clientX - rect.left; const scaleX = canvasRef.current.width / rect.width;
const y = e.clientY - rect.top; const scaleY = canvasRef.current.height / rect.height;
const x = (e.clientX - rect.left) * scaleX;
const y = (e.clientY - rect.top) * scaleY;
const ctx = canvasRef.current.getContext('2d'); const ctx = canvasRef.current.getContext('2d');
if (ctx) { if (ctx) {
@ -58,7 +71,15 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
if (canvasRef.current) { if (canvasRef.current) {
const dataURL = canvasRef.current.toDataURL('image/png'); const dataURL = canvasRef.current.toDataURL('image/png');
console.log('Saving canvas signature data:', dataURL.substring(0, 50) + '...'); console.log('Saving canvas signature data:', dataURL.substring(0, 50) + '...');
setCanvasSignatureData(dataURL);
onParameterChange('signatureData', dataURL); onParameterChange('signatureData', dataURL);
// Auto-activate placement mode after drawing
setTimeout(() => {
if (onActivateSignaturePlacement) {
onActivateSignaturePlacement();
}
}, 100);
} }
}; };
@ -68,10 +89,90 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
const ctx = canvasRef.current.getContext('2d'); const ctx = canvasRef.current.getContext('2d');
if (ctx) { if (ctx) {
ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height); ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
setCanvasSignatureData(null);
onParameterChange('signatureData', undefined); onParameterChange('signatureData', undefined);
} }
}; };
// Modal canvas drawing functions
const startModalDrawing = (e: React.MouseEvent<HTMLCanvasElement>) => {
if (!modalCanvasRef.current) return;
setIsModalDrawing(true);
const rect = modalCanvasRef.current.getBoundingClientRect();
const scaleX = modalCanvasRef.current.width / rect.width;
const scaleY = modalCanvasRef.current.height / rect.height;
const x = (e.clientX - rect.left) * scaleX;
const y = (e.clientY - rect.top) * scaleY;
const ctx = modalCanvasRef.current.getContext('2d');
if (ctx) {
ctx.strokeStyle = selectedColor;
ctx.beginPath();
ctx.moveTo(x, y);
}
};
const drawModal = (e: React.MouseEvent<HTMLCanvasElement>) => {
if (!isModalDrawing || !modalCanvasRef.current) return;
const rect = modalCanvasRef.current.getBoundingClientRect();
const scaleX = modalCanvasRef.current.width / rect.width;
const scaleY = modalCanvasRef.current.height / rect.height;
const x = (e.clientX - rect.left) * scaleX;
const y = (e.clientY - rect.top) * scaleY;
const ctx = modalCanvasRef.current.getContext('2d');
if (ctx) {
ctx.lineTo(x, y);
ctx.stroke();
}
};
const stopModalDrawing = () => {
if (!isModalDrawing) return;
setIsModalDrawing(false);
};
const clearModalCanvas = () => {
if (!modalCanvasRef.current) return;
const ctx = modalCanvasRef.current.getContext('2d');
if (ctx) {
ctx.clearRect(0, 0, modalCanvasRef.current.width, modalCanvasRef.current.height);
}
};
const saveModalSignature = () => {
if (!modalCanvasRef.current) return;
const dataURL = modalCanvasRef.current.toDataURL('image/png');
setCanvasSignatureData(dataURL);
onParameterChange('signatureData', dataURL);
// Copy to small canvas for display
if (canvasRef.current) {
const ctx = canvasRef.current.getContext('2d');
if (ctx) {
const img = new Image();
img.onload = () => {
ctx.clearRect(0, 0, canvasRef.current!.width, canvasRef.current!.height);
ctx.drawImage(img, 0, 0, canvasRef.current!.width, canvasRef.current!.height);
};
img.src = dataURL;
}
}
setIsModalOpen(false);
// Auto-activate placement mode after saving modal signature
setTimeout(() => {
if (onActivateSignaturePlacement) {
onActivateSignaturePlacement();
}
}, 100);
};
// Handle signature image upload // Handle signature image upload
const handleSignatureImageChange = (file: File | null) => { const handleSignatureImageChange = (file: File | null) => {
console.log('Image file selected:', file); console.log('Image file selected:', file);
@ -80,7 +181,15 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
reader.onload = (e) => { reader.onload = (e) => {
if (e.target?.result) { if (e.target?.result) {
console.log('Image loaded, saving to signatureData, length:', (e.target.result as string).length); console.log('Image loaded, saving to signatureData, length:', (e.target.result as string).length);
setImageSignatureData(e.target.result as string);
onParameterChange('signatureData', e.target.result as string); onParameterChange('signatureData', e.target.result as string);
// Auto-activate placement mode after image upload
setTimeout(() => {
if (onActivateSignaturePlacement) {
onActivateSignaturePlacement();
}
}, 100);
} }
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
@ -90,16 +199,77 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
// Initialize canvas // Initialize canvas
React.useEffect(() => { React.useEffect(() => {
if (canvasRef.current && parameters.signatureType === 'draw') { if (canvasRef.current && parameters.signatureType === 'canvas') {
const ctx = canvasRef.current.getContext('2d'); const ctx = canvasRef.current.getContext('2d');
if (ctx) { if (ctx) {
ctx.strokeStyle = '#000000'; ctx.strokeStyle = selectedColor;
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.lineCap = 'round'; ctx.lineCap = 'round';
ctx.lineJoin = 'round'; ctx.lineJoin = 'round';
} }
} }
}, [parameters.signatureType]); }, [parameters.signatureType, selectedColor]);
// Initialize modal canvas when opened
React.useEffect(() => {
if (modalCanvasRef.current && isModalOpen) {
const ctx = modalCanvasRef.current.getContext('2d');
if (ctx) {
ctx.strokeStyle = selectedColor;
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
}
}
}, [isModalOpen, selectedColor]);
// Switch signature data based on mode
React.useEffect(() => {
if (parameters.signatureType === 'canvas' && canvasSignatureData) {
onParameterChange('signatureData', canvasSignatureData);
} else if (parameters.signatureType === 'image' && imageSignatureData) {
onParameterChange('signatureData', imageSignatureData);
}
}, [parameters.signatureType, canvasSignatureData, imageSignatureData, onParameterChange]);
// Initialize draw mode on mount if draw type is selected
React.useEffect(() => {
console.log('SignSettings: Component mounted, initial signatureType:', parameters.signatureType);
if (parameters.signatureType === 'draw' && onActivateDrawMode) {
console.log('SignSettings: Initial activation of draw mode with delay');
// Add a delay to ensure the API bridge is ready
setTimeout(() => {
onActivateDrawMode();
}, 500);
}
}, [onActivateDrawMode]); // Only run on mount/when callback changes
// Auto-activate draw mode when draw type is selected
React.useEffect(() => {
console.log('SignSettings: signatureType changed to:', parameters.signatureType);
if (parameters.signatureType === 'draw') {
console.log('SignSettings: Activating draw mode, onActivateDrawMode:', !!onActivateDrawMode);
if (onActivateDrawMode) {
onActivateDrawMode();
}
} else if (parameters.signatureType !== 'draw') {
console.log('SignSettings: Deactivating draw mode, onDeactivateSignature:', !!onDeactivateSignature);
if (onDeactivateSignature) {
onDeactivateSignature();
}
}
}, [parameters.signatureType, onActivateDrawMode, onDeactivateSignature]);
// Update draw settings when color or pen size changes
React.useEffect(() => {
console.log('SignSettings: Draw settings changed - color:', selectedColor, 'penSize:', penSize, 'signatureType:', parameters.signatureType);
if (parameters.signatureType === 'draw' && onUpdateDrawSettings) {
console.log('SignSettings: Calling onUpdateDrawSettings');
onUpdateDrawSettings(selectedColor, penSize);
} else {
console.log('SignSettings: Not calling onUpdateDrawSettings - signatureType not draw or function not available');
}
}, [selectedColor, penSize, parameters.signatureType, onUpdateDrawSettings]);
return ( return (
<Stack gap="md"> <Stack gap="md">
@ -110,12 +280,16 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
</Text> </Text>
<ButtonSelector <ButtonSelector
value={parameters.signatureType} value={parameters.signatureType}
onChange={(value) => onParameterChange('signatureType', value as 'image' | 'text' | 'draw')} onChange={(value) => onParameterChange('signatureType', value as 'image' | 'text' | 'draw' | 'canvas')}
options={[ options={[
{ {
value: 'draw', value: 'draw',
label: t('sign.type.draw', 'Draw'), label: t('sign.type.draw', 'Draw'),
}, },
{
value: 'canvas',
label: t('sign.type.canvas', 'Canvas'),
},
{ {
value: 'image', value: 'image',
label: t('sign.type.image', 'Image'), label: t('sign.type.image', 'Image'),
@ -130,20 +304,72 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
</div> </div>
{/* Signature Creation based on type */} {/* Signature Creation based on type */}
{parameters.signatureType === 'draw' && ( {parameters.signatureType === 'canvas' && (
<Paper withBorder p="md"> <Paper withBorder p="md" style={{ position: 'relative' }}>
<ActionIcon
variant="filled"
color="blue"
size="sm"
style={{
position: 'absolute',
top: 8,
right: 8,
zIndex: 10,
}}
onClick={() => setIsModalOpen(true)}
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>
<Button <Group gap="sm">
variant="subtle" <Menu shadow="md" width={200}>
color="red" <Menu.Target>
size="compact-sm" <Button
onClick={clearCanvas} variant="subtle"
disabled={disabled} size="compact-sm"
> disabled={disabled}
{t('sign.draw.clear', 'Clear')} rightSection={
</Button> <ColorSwatch
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}
style={{
cursor: 'pointer',
border: selectedColor === color ? '2px solid #333' : '1px solid #ddd'
}}
onClick={() => setSelectedColor(color)}
/>
))}
</Group>
</Menu.Dropdown>
</Menu>
<Button
variant="subtle"
color="red"
size="compact-sm"
onClick={clearCanvas}
disabled={disabled}
>
{t('sign.draw.clear', 'Clear')}
</Button>
</Group>
</Group> </Group>
<canvas <canvas
ref={canvasRef} ref={canvasRef}
@ -154,15 +380,13 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
borderRadius: '4px', borderRadius: '4px',
cursor: disabled ? 'default' : 'crosshair', cursor: disabled ? 'default' : 'crosshair',
backgroundColor: '#ffffff', backgroundColor: '#ffffff',
width: '100%',
}} }}
onMouseDown={startDrawing} onMouseDown={startDrawing}
onMouseMove={draw} onMouseMove={draw}
onMouseUp={stopDrawing} onMouseUp={stopDrawing}
onMouseLeave={stopDrawing} onMouseLeave={stopDrawing}
/> />
<Text size="sm" c="dimmed">
{t('sign.draw.hint', 'Click and drag to draw your signature')}
</Text>
</Stack> </Stack>
</Paper> </Paper>
)} )}
@ -199,63 +423,172 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
</Stack> </Stack>
)} )}
{/* Direct PDF Drawing */}
{parameters.signatureType === 'draw' && (
<Paper withBorder p="md">
<Stack gap="md">
<Text fw={500}>Direct PDF Drawing</Text>
<Text size="sm" c="dimmed">
Draw signatures and annotations directly on the PDF document. Drawing mode will be activated automatically when you go to the PDF viewer.
</Text>
{/* Drawing Controls */}
<Group gap="md" align="flex-end">
{/* Color Picker */}
<div>
<Text size="sm" fw={500} mb="xs">Color</Text>
<Group gap="xs">
{['#000000', '#0066cc', '#cc0000', '#cc6600', '#009900', '#6600cc'].map((color) => (
<ColorSwatch
key={color}
color={color}
size={24}
style={{
cursor: 'pointer',
border: selectedColor === color ? '2px solid #333' : '1px solid #ddd'
}}
onClick={() => setSelectedColor(color)}
/>
))}
</Group>
</div>
{/* Pen Size */}
<div style={{ flexGrow: 1, maxWidth: '200px' }}>
<Text size="sm" fw={500} mb="xs">Pen Size: {penSize}px</Text>
<Slider
value={penSize}
onChange={setPenSize}
min={1}
max={10}
step={1}
marks={[
{ value: 1, label: '1' },
{ value: 5, label: '5' },
{ value: 10, label: '10' }
]}
/>
</div>
</Group>
</Stack>
</Paper>
)}
{/* Instructions for placing signature */} {/* Instructions for placing signature */}
<Alert color="blue" title={t('sign.instructions.title', 'How to add signature')}> {(parameters.signatureType === 'canvas' || parameters.signatureType === 'image' || parameters.signatureType === 'text') && (
<Text size="sm"> <Alert color="blue" title={t('sign.instructions.title', 'How to add signature')}>
{parameters.signatureType === 'draw' && t('sign.instructions.draw', 'Draw your signature above, then click "Draw Directly on PDF" to draw live, or "Place Canvas Signature" to place your drawn signature.')} <Text size="sm">
{parameters.signatureType === 'image' && t('sign.instructions.image', 'Upload your signature image above, then click "Activate Image Placement" to place it on the PDF.')} {parameters.signatureType === 'canvas' && 'Draw your signature in the canvas above. Placement mode will activate automatically, or click the buttons below to control placement.'}
{parameters.signatureType === 'text' && t('sign.instructions.text', 'Enter your name above, then click "Activate Text Signature" to place it on the PDF.')} {parameters.signatureType === 'image' && 'Upload your signature image above. Placement mode will activate automatically, or click the buttons below to control placement.'}
</Text> {parameters.signatureType === 'text' && 'Enter your name above. Placement mode will activate automatically, or click the buttons below to control placement.'}
</Text>
<Group mt="sm" gap="sm"> <Group mt="sm" gap="sm">
{/* Universal activation button */} {/* Universal activation button */}
{((parameters.signatureType === 'draw' && parameters.signatureData) || {((parameters.signatureType === 'canvas' && parameters.signatureData) ||
(parameters.signatureType === 'image' && parameters.signatureData) || (parameters.signatureType === 'image' && parameters.signatureData) ||
(parameters.signatureType === 'text' && parameters.signerName)) && ( (parameters.signatureType === 'text' && parameters.signerName)) && (
<Button
onClick={() => {
if (onActivateSignaturePlacement) {
onActivateSignaturePlacement();
}
}}
disabled={disabled}
>
{t('sign.activate', 'Activate Signature Placement')}
</Button>
)}
{/* Universal deactivate button */}
<Button <Button
variant="subtle"
color="red"
onClick={() => { onClick={() => {
if (onActivateSignaturePlacement) { if (onDeactivateSignature) {
onActivateSignaturePlacement(); onDeactivateSignature();
} }
}} }}
disabled={disabled} disabled={disabled}
> >
{t('sign.activate', 'Activate Signature Placement')} {t('sign.deactivate', 'Stop Placing Signatures')}
</Button> </Button>
)} </Group>
{/* Draw directly mode for draw type */} </Alert>
{parameters.signatureType === 'draw' && ( )}
<Button
variant="outline" {/* Modal for larger signature canvas */}
onClick={() => { <Modal
if (onActivateDrawMode) { opened={isModalOpen}
onActivateDrawMode(); onClose={() => setIsModalOpen(false)}
} title="Draw Your Signature"
size="xl"
centered
>
<Stack gap="md">
{/* Color picker */}
<Paper withBorder p="sm">
<Group gap="sm" align="center">
<Text size="sm" fw={500}>Color:</Text>
{['#000000', '#0066cc', '#cc0000', '#cc6600', '#009900', '#6600cc'].map((color) => (
<ColorSwatch
key={color}
color={color}
size={24}
style={{ cursor: 'pointer', border: selectedColor === color ? '2px solid #333' : 'none' }}
onClick={() => setSelectedColor(color)}
/>
))}
</Group>
</Paper>
<Paper withBorder p="md">
<canvas
ref={modalCanvasRef}
width={800}
height={400}
style={{
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'crosshair',
backgroundColor: '#ffffff',
width: '100%',
maxWidth: '800px',
height: 'auto',
}} }}
disabled={disabled} onMouseDown={startModalDrawing}
onMouseMove={drawModal}
onMouseUp={stopModalDrawing}
onMouseLeave={stopModalDrawing}
/>
</Paper>
<Group justify="space-between">
<Button
variant="subtle"
color="red"
onClick={clearModalCanvas}
> >
{t('sign.activate.draw', 'Draw Directly on PDF')} Clear Canvas
</Button> </Button>
)} <Group gap="sm">
<Button
{/* Universal deactivate button */} variant="subtle"
<Button onClick={() => setIsModalOpen(false)}
variant="subtle" >
color="red" Cancel
onClick={() => { </Button>
if (onDeactivateSignature) { <Button
onDeactivateSignature(); onClick={saveModalSignature}
} >
}} Save Signature
disabled={disabled} </Button>
> </Group>
{t('sign.deactivate', 'Stop Placing Signatures')} </Group>
</Button> </Stack>
</Group> </Modal>
</Alert>
</Stack> </Stack>
); );
}; };

View File

@ -9,6 +9,7 @@ export interface SignatureAPI {
addTextSignature: (text: string, x: number, y: number, pageIndex: number) => void; addTextSignature: (text: string, x: number, y: number, pageIndex: number) => void;
activateDrawMode: () => void; activateDrawMode: () => void;
activateSignaturePlacementMode: () => void; activateSignaturePlacementMode: () => void;
updateDrawSettings: (color: string, size: number) => void;
deactivateTools: () => void; deactivateTools: () => void;
applySignatureFromParameters: (params: SignParameters) => void; applySignatureFromParameters: (params: SignParameters) => void;
} }
@ -63,9 +64,26 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
}, },
activateDrawMode: () => { activateDrawMode: () => {
if (!annotationApi) return; console.log('SignatureAPIBridge.activateDrawMode called, annotationApi:', !!annotationApi);
if (!annotationApi) {
console.log('No annotationApi available');
return;
}
console.log('Setting active tool to ink');
// Activate the built-in ink tool for drawing // Activate the built-in ink tool for drawing
annotationApi.setActiveTool('ink'); annotationApi.setActiveTool('ink');
// Set default ink tool properties (black color, 2px width)
const activeTool = annotationApi.getActiveTool();
console.log('Active tool after setting ink:', activeTool);
if (activeTool && activeTool.id === 'ink') {
console.log('Setting ink tool defaults');
annotationApi.setToolDefaults('ink', {
color: '#000000',
thickness: 2
});
}
}, },
activateSignaturePlacementMode: () => { activateSignaturePlacementMode: () => {
@ -107,6 +125,28 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
} }
}, },
updateDrawSettings: (color: string, size: number) => {
console.log('SignatureAPIBridge.updateDrawSettings called with color:', color, 'size:', size);
if (!annotationApi) {
console.log('No annotationApi available for updateDrawSettings');
return;
}
// Always update ink tool defaults regardless of current tool
console.log('Setting ink tool defaults');
annotationApi.setToolDefaults('ink', {
color: color,
thickness: size
});
// If ink tool is currently active, reactivate it to apply settings immediately
const activeTool = annotationApi.getActiveTool();
console.log('Current active tool:', activeTool);
if (activeTool && activeTool.id === 'ink') {
console.log('Reactivating ink tool to apply new settings');
annotationApi.setActiveTool('ink');
}
},
deactivateTools: () => { deactivateTools: () => {
if (!annotationApi) return; if (!annotationApi) return;

View File

@ -17,6 +17,7 @@ interface SignatureActions {
activateDrawMode: () => void; activateDrawMode: () => void;
deactivateDrawMode: () => void; deactivateDrawMode: () => void;
activateSignaturePlacementMode: () => void; activateSignaturePlacementMode: () => void;
updateDrawSettings: (color: string, size: number) => void;
} }
// Combined context interface // Combined context interface
@ -60,9 +61,14 @@ export const SignatureProvider: React.FC<{ children: ReactNode }> = ({ children
}, []); }, []);
const activateDrawMode = useCallback(() => { const activateDrawMode = useCallback(() => {
console.log('SignatureContext.activateDrawMode called, apiRef:', !!signatureApiRef.current);
if (signatureApiRef.current) { if (signatureApiRef.current) {
console.log('Calling signatureApiRef.current.activateDrawMode()');
signatureApiRef.current.activateDrawMode(); signatureApiRef.current.activateDrawMode();
setPlacementMode(true); setPlacementMode(true);
console.log('Draw mode activated successfully');
} else {
console.log('signatureApiRef.current is null - cannot activate draw mode');
} }
}, [setPlacementMode]); }, [setPlacementMode]);
@ -84,6 +90,16 @@ export const SignatureProvider: React.FC<{ children: ReactNode }> = ({ children
} }
}, [state.signatureConfig, setPlacementMode]); }, [state.signatureConfig, setPlacementMode]);
const updateDrawSettings = useCallback((color: string, size: number) => {
console.log('SignatureContext.updateDrawSettings called with color:', color, 'size:', size);
console.log('signatureApiRef.current available:', !!signatureApiRef.current);
if (signatureApiRef.current) {
signatureApiRef.current.updateDrawSettings(color, size);
} else {
console.log('signatureApiRef.current is null - cannot update draw settings');
}
}, []);
// No auto-activation - all modes use manual buttons // No auto-activation - all modes use manual buttons
@ -95,6 +111,7 @@ export const SignatureProvider: React.FC<{ children: ReactNode }> = ({ children
activateDrawMode, activateDrawMode,
deactivateDrawMode, deactivateDrawMode,
activateSignaturePlacementMode, activateSignaturePlacementMode,
updateDrawSettings,
}; };
return ( return (

View File

@ -9,7 +9,7 @@ export interface SignaturePosition {
} }
export interface SignParameters { export interface SignParameters {
signatureType: 'image' | 'text' | 'draw'; signatureType: 'image' | 'text' | 'draw' | 'canvas';
signatureData?: string; // Base64 encoded image or text content signatureData?: string; // Base64 encoded image or text content
signaturePosition?: SignaturePosition; signaturePosition?: SignaturePosition;
reason?: string; reason?: string;
@ -41,6 +41,11 @@ const validateSignParameters = (parameters: SignParameters): boolean => {
return false; return false;
} }
// For canvas signatures, require signature data
if (parameters.signatureType === 'canvas' && !parameters.signatureData) {
return false;
}
// For text signatures, require signer name // For text signatures, require signer name
if (parameters.signatureType === 'text' && !parameters.signerName) { if (parameters.signatureType === 'text' && !parameters.signerName) {
return false; return false;

View File

@ -12,7 +12,7 @@ import { useSignature } from "../contexts/SignatureContext";
const Sign = (props: BaseToolProps) => { const Sign = (props: BaseToolProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { setWorkbench } = useNavigation(); const { setWorkbench } = useNavigation();
const { setSignatureConfig, activateDrawMode, activateSignaturePlacementMode, deactivateDrawMode } = useSignature(); const { setSignatureConfig, activateDrawMode, activateSignaturePlacementMode, deactivateDrawMode, updateDrawSettings } = useSignature();
// Manual sync function // Manual sync function
const syncSignatureConfig = () => { const syncSignatureConfig = () => {
@ -41,6 +41,16 @@ const Sign = (props: BaseToolProps) => {
} }
}, [base.selectedFiles.length, setWorkbench]); }, [base.selectedFiles.length, setWorkbench]);
// Auto-activate draw mode when files are loaded and draw type is selected
useEffect(() => {
if (base.selectedFiles.length > 0 && base.params.parameters.signatureType === 'draw') {
console.log('Sign: Files loaded with draw mode, activating after delay');
setTimeout(() => {
activateDrawMode();
}, 1000); // Give viewer time to initialize
}
}, [base.selectedFiles.length, base.params.parameters.signatureType, activateDrawMode]);
// Sync signature configuration with context // Sync signature configuration with context
useEffect(() => { useEffect(() => {
setSignatureConfig(base.params.parameters); setSignatureConfig(base.params.parameters);
@ -60,9 +70,10 @@ const Sign = (props: BaseToolProps) => {
parameters={base.params.parameters} parameters={base.params.parameters}
onParameterChange={base.params.updateParameter} onParameterChange={base.params.updateParameter}
disabled={base.endpointLoading} disabled={base.endpointLoading}
onActivateDrawMode={activateDrawMode} onActivateDrawMode={() => activateDrawMode()}
onActivateSignaturePlacement={handleSignaturePlacement} onActivateSignaturePlacement={handleSignaturePlacement}
onDeactivateSignature={deactivateDrawMode} onDeactivateSignature={deactivateDrawMode}
onUpdateDrawSettings={updateDrawSettings}
/> />
), ),
}); });
@ -79,7 +90,7 @@ const Sign = (props: BaseToolProps) => {
steps: getSteps(), steps: getSteps(),
executeButton: { executeButton: {
text: t('sign.submit', 'Sign Document'), text: t('sign.submit', 'Sign Document'),
isVisible: base.operation.files.length === 0, isVisible: false, // Hide the execute button - signatures are placed directly
loadingText: t('loading'), loadingText: t('loading'),
onClick: base.handleExecute, onClick: base.handleExecute,
disabled: !base.params.validateParameters() || base.selectedFiles.length === 0 || !base.endpointEnabled, disabled: !base.params.validateParameters() || base.selectedFiles.length === 0 || !base.endpointEnabled,