mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-26 17:52:59 +02:00
Add undo/redo functionality and refactor signature settings UI
- Introduced HistoryAPIBridge for managing undo/redo actions. - Updated SignSettings component to include undo/redo buttons. - Refactored signature type selection to use Tabs for better UI. - Enhanced SignatureAPIBridge to store image data for annotations. - Integrated history management into SignatureContext for state handling.
This commit is contained in:
parent
bac61c7e9e
commit
a12e457577
@ -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, ColorPicker } from '@mantine/core';
|
import { Stack, TextInput, FileInput, Paper, Group, Button, Text, Alert, Modal, ColorSwatch, Menu, ActionIcon, Slider, Select, Combobox, useCombobox, ColorPicker, Tabs } 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";
|
||||||
|
|
||||||
@ -12,9 +12,11 @@ interface SignSettingsProps {
|
|||||||
onActivateSignaturePlacement?: () => void;
|
onActivateSignaturePlacement?: () => void;
|
||||||
onDeactivateSignature?: () => void;
|
onDeactivateSignature?: () => void;
|
||||||
onUpdateDrawSettings?: (color: string, size: number) => void;
|
onUpdateDrawSettings?: (color: string, size: number) => void;
|
||||||
|
onUndo?: () => void;
|
||||||
|
onRedo?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SignSettings = ({ parameters, onParameterChange, disabled = false, onActivateDrawMode, onActivateSignaturePlacement, onDeactivateSignature, onUpdateDrawSettings }: SignSettingsProps) => {
|
const SignSettings = ({ parameters, onParameterChange, disabled = false, onActivateDrawMode, onActivateSignaturePlacement, onDeactivateSignature, onUpdateDrawSettings, onUndo, onRedo }: 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);
|
||||||
@ -432,35 +434,43 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
return (
|
return (
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
{/* Signature Type Selection */}
|
{/* Signature Type Selection */}
|
||||||
<div>
|
<Tabs
|
||||||
<Text size="sm" fw={500} mb="xs">
|
|
||||||
{t('sign.type.title', 'Signature Type')}
|
|
||||||
</Text>
|
|
||||||
<ButtonSelector
|
|
||||||
value={parameters.signatureType}
|
value={parameters.signatureType}
|
||||||
onChange={(value) => onParameterChange('signatureType', value as 'image' | 'text' | 'draw' | 'canvas')}
|
onChange={(value) => onParameterChange('signatureType', value as 'image' | 'text' | 'draw' | 'canvas')}
|
||||||
options={[
|
>
|
||||||
{
|
<Tabs.List grow>
|
||||||
value: 'draw',
|
<Tabs.Tab value="draw" style={{ fontSize: '0.8rem' }}>
|
||||||
label: t('sign.type.draw', 'Draw'),
|
{t('sign.type.draw', 'Draw')}
|
||||||
},
|
</Tabs.Tab>
|
||||||
{
|
<Tabs.Tab value="canvas" style={{ fontSize: '0.8rem' }}>
|
||||||
value: 'canvas',
|
{t('sign.type.canvas', 'Canvas')}
|
||||||
label: t('sign.type.canvas', 'Canvas'),
|
</Tabs.Tab>
|
||||||
},
|
<Tabs.Tab value="image" style={{ fontSize: '0.8rem' }}>
|
||||||
{
|
{t('sign.type.image', 'Image')}
|
||||||
value: 'image',
|
</Tabs.Tab>
|
||||||
label: t('sign.type.image', 'Image'),
|
<Tabs.Tab value="text" style={{ fontSize: '0.8rem' }}>
|
||||||
},
|
{t('sign.type.text', 'Text')}
|
||||||
{
|
</Tabs.Tab>
|
||||||
value: 'text',
|
</Tabs.List>
|
||||||
label: t('sign.type.text', 'Text'),
|
</Tabs>
|
||||||
},
|
|
||||||
]}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
{/* Undo/Redo Controls */}
|
||||||
|
<Group justify="space-between" grow>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={onUndo}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{t('sign.undo', 'Undo')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={onRedo}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{t('sign.redo', 'Redo')}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
|
||||||
{/* Signature Creation based on type */}
|
{/* Signature Creation based on type */}
|
||||||
{parameters.signatureType === 'canvas' && (
|
{parameters.signatureType === 'canvas' && (
|
||||||
@ -782,9 +792,9 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
|
|||||||
{(parameters.signatureType === 'canvas' || parameters.signatureType === 'image' || parameters.signatureType === 'text') && (
|
{(parameters.signatureType === 'canvas' || parameters.signatureType === 'image' || parameters.signatureType === 'text') && (
|
||||||
<Alert color="blue" title={t('sign.instructions.title', 'How to add signature')}>
|
<Alert color="blue" title={t('sign.instructions.title', 'How to add signature')}>
|
||||||
<Text size="sm">
|
<Text size="sm">
|
||||||
{parameters.signatureType === 'canvas' && 'Draw your signature in the canvas above. Placement mode will activate automatically when you finish drawing.'}
|
{parameters.signatureType === 'canvas' && 'After drawing your signature in the canvas above, click anywhere on the PDF to place it.'}
|
||||||
{parameters.signatureType === 'image' && 'Upload your signature image above. Placement mode will activate automatically when the image is loaded.'}
|
{parameters.signatureType === 'image' && 'After uploading your signature image above, click anywhere on the PDF to place it.'}
|
||||||
{parameters.signatureType === 'text' && 'Enter your name above. Placement mode will activate automatically when you type your name.'}
|
{parameters.signatureType === 'text' && 'After entering your name above, click anywhere on the PDF to place your signature.'}
|
||||||
</Text>
|
</Text>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
@ -40,7 +40,7 @@ const EmbedPdfViewerContent = ({
|
|||||||
const isSignatureMode = selectedTool === 'sign';
|
const isSignatureMode = selectedTool === 'sign';
|
||||||
|
|
||||||
// Get signature context
|
// Get signature context
|
||||||
const { signatureApiRef } = useSignature();
|
const { signatureApiRef, historyApiRef } = useSignature();
|
||||||
|
|
||||||
|
|
||||||
// Get current file from FileContext
|
// Get current file from FileContext
|
||||||
@ -189,8 +189,17 @@ const EmbedPdfViewerContent = ({
|
|||||||
url={effectiveFile.url}
|
url={effectiveFile.url}
|
||||||
enableSignature={isSignatureMode}
|
enableSignature={isSignatureMode}
|
||||||
signatureApiRef={signatureApiRef as React.RefObject<any>}
|
signatureApiRef={signatureApiRef as React.RefObject<any>}
|
||||||
|
historyApiRef={historyApiRef as React.RefObject<any>}
|
||||||
onSignatureAdded={(annotation) => {
|
onSignatureAdded={(annotation) => {
|
||||||
console.log('Signature added:', annotation);
|
console.log('Signature added:', annotation);
|
||||||
|
if (annotation.type === 13) {
|
||||||
|
console.log('- imageSrc:', !!annotation.imageSrc, annotation.imageSrc?.length);
|
||||||
|
console.log('- contents:', !!annotation.contents, annotation.contents?.length);
|
||||||
|
console.log('- data:', !!annotation.data, annotation.data?.length);
|
||||||
|
console.log('- imageData:', !!annotation.imageData, annotation.imageData?.length);
|
||||||
|
console.log('- appearance:', !!annotation.appearance, typeof annotation.appearance);
|
||||||
|
console.log('- All keys:', Object.keys(annotation));
|
||||||
|
}
|
||||||
// Future: Handle signature completion
|
// Future: Handle signature completion
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
86
frontend/src/components/viewer/HistoryAPIBridge.tsx
Normal file
86
frontend/src/components/viewer/HistoryAPIBridge.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import React, { useImperativeHandle, forwardRef, useEffect } from 'react';
|
||||||
|
import { useHistoryCapability } from '@embedpdf/plugin-history/react';
|
||||||
|
import { useAnnotationCapability } from '@embedpdf/plugin-annotation/react';
|
||||||
|
import { useSignature } from '../../contexts/SignatureContext';
|
||||||
|
|
||||||
|
export interface HistoryAPI {
|
||||||
|
undo: () => void;
|
||||||
|
redo: () => void;
|
||||||
|
canUndo: () => boolean;
|
||||||
|
canRedo: () => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HistoryAPIBridgeProps {}
|
||||||
|
|
||||||
|
export const HistoryAPIBridge = forwardRef<HistoryAPI, HistoryAPIBridgeProps>((props, ref) => {
|
||||||
|
const { provides: historyApi } = useHistoryCapability();
|
||||||
|
const { provides: annotationApi } = useAnnotationCapability();
|
||||||
|
const { getImageData } = useSignature();
|
||||||
|
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
undo: () => {
|
||||||
|
if (historyApi) {
|
||||||
|
historyApi.undo();
|
||||||
|
|
||||||
|
// Restore image data for STAMP annotations after undo
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!annotationApi) return;
|
||||||
|
|
||||||
|
for (let pageIndex = 0; pageIndex < 10; pageIndex++) {
|
||||||
|
const pageAnnotationsTask = annotationApi.getPageAnnotations?.({ pageIndex });
|
||||||
|
if (pageAnnotationsTask) {
|
||||||
|
pageAnnotationsTask.toPromise().then((pageAnnotations: any) => {
|
||||||
|
if (pageAnnotations) {
|
||||||
|
pageAnnotations.forEach((ann: any) => {
|
||||||
|
if (ann.type === 13) {
|
||||||
|
const storedImageData = getImageData(ann.id);
|
||||||
|
|
||||||
|
if (storedImageData && (!ann.imageSrc || ann.imageSrc !== storedImageData)) {
|
||||||
|
const originalData = {
|
||||||
|
type: ann.type,
|
||||||
|
rect: ann.rect,
|
||||||
|
author: ann.author || 'Digital Signature',
|
||||||
|
subject: ann.subject || 'Digital Signature',
|
||||||
|
pageIndex: pageIndex,
|
||||||
|
id: ann.id,
|
||||||
|
created: ann.created || new Date(),
|
||||||
|
imageSrc: storedImageData
|
||||||
|
};
|
||||||
|
|
||||||
|
annotationApi.deleteAnnotation(pageIndex, ann.id);
|
||||||
|
setTimeout(() => {
|
||||||
|
annotationApi.createAnnotation(pageIndex, originalData);
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch((error: any) => {
|
||||||
|
console.error(`Failed to get annotations for page ${pageIndex}:`, error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
redo: () => {
|
||||||
|
if (historyApi) {
|
||||||
|
historyApi.redo();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
canUndo: () => {
|
||||||
|
return historyApi ? historyApi.canUndo() : false;
|
||||||
|
},
|
||||||
|
|
||||||
|
canRedo: () => {
|
||||||
|
return historyApi ? historyApi.canRedo() : false;
|
||||||
|
},
|
||||||
|
}), [historyApi]);
|
||||||
|
|
||||||
|
return null; // This is a bridge component with no UI
|
||||||
|
});
|
||||||
|
|
||||||
|
HistoryAPIBridge.displayName = 'HistoryAPIBridge';
|
@ -35,6 +35,7 @@ import { SearchAPIBridge } from './SearchAPIBridge';
|
|||||||
import { ThumbnailAPIBridge } from './ThumbnailAPIBridge';
|
import { ThumbnailAPIBridge } from './ThumbnailAPIBridge';
|
||||||
import { RotateAPIBridge } from './RotateAPIBridge';
|
import { RotateAPIBridge } from './RotateAPIBridge';
|
||||||
import { SignatureAPIBridge, SignatureAPI } from './SignatureAPIBridge';
|
import { SignatureAPIBridge, SignatureAPI } from './SignatureAPIBridge';
|
||||||
|
import { HistoryAPIBridge, HistoryAPI } from './HistoryAPIBridge';
|
||||||
|
|
||||||
interface LocalEmbedPDFProps {
|
interface LocalEmbedPDFProps {
|
||||||
file?: File | Blob;
|
file?: File | Blob;
|
||||||
@ -42,9 +43,10 @@ interface LocalEmbedPDFProps {
|
|||||||
enableSignature?: boolean;
|
enableSignature?: boolean;
|
||||||
onSignatureAdded?: (annotation: any) => void;
|
onSignatureAdded?: (annotation: any) => void;
|
||||||
signatureApiRef?: React.RefObject<SignatureAPI>;
|
signatureApiRef?: React.RefObject<SignatureAPI>;
|
||||||
|
historyApiRef?: React.RefObject<HistoryAPI>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LocalEmbedPDF({ file, url, enableSignature = false, onSignatureAdded, signatureApiRef }: LocalEmbedPDFProps) {
|
export function LocalEmbedPDF({ file, url, enableSignature = false, onSignatureAdded, signatureApiRef, historyApiRef }: LocalEmbedPDFProps) {
|
||||||
const [pdfUrl, setPdfUrl] = useState<string | null>(null);
|
const [pdfUrl, setPdfUrl] = useState<string | null>(null);
|
||||||
const [annotations, setAnnotations] = useState<Array<{id: string, pageIndex: number, rect: any}>>([]);
|
const [annotations, setAnnotations] = useState<Array<{id: string, pageIndex: number, rect: any}>>([]);
|
||||||
|
|
||||||
@ -257,6 +259,7 @@ export function LocalEmbedPDF({ file, url, enableSignature = false, onSignatureA
|
|||||||
<ThumbnailAPIBridge />
|
<ThumbnailAPIBridge />
|
||||||
<RotateAPIBridge />
|
<RotateAPIBridge />
|
||||||
{enableSignature && <SignatureAPIBridge ref={signatureApiRef} />}
|
{enableSignature && <SignatureAPIBridge ref={signatureApiRef} />}
|
||||||
|
{enableSignature && <HistoryAPIBridge ref={historyApiRef} />}
|
||||||
<GlobalPointerProvider>
|
<GlobalPointerProvider>
|
||||||
<Viewport
|
<Viewport
|
||||||
style={{
|
style={{
|
||||||
|
@ -20,7 +20,8 @@ export interface SignatureAPIBridgeProps {}
|
|||||||
|
|
||||||
export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgeProps>((props, ref) => {
|
export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgeProps>((props, ref) => {
|
||||||
const { provides: annotationApi } = useAnnotationCapability();
|
const { provides: annotationApi } = useAnnotationCapability();
|
||||||
const { signatureConfig } = useSignature();
|
const { signatureConfig, storeImageData } = useSignature();
|
||||||
|
|
||||||
|
|
||||||
// Enable keyboard deletion of selected annotations
|
// Enable keyboard deletion of selected annotations
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -31,15 +32,32 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
|
|||||||
const selectedAnnotation = annotationApi.getSelectedAnnotation?.();
|
const selectedAnnotation = annotationApi.getSelectedAnnotation?.();
|
||||||
|
|
||||||
if (selectedAnnotation) {
|
if (selectedAnnotation) {
|
||||||
const annotation = selectedAnnotation as any;
|
// First, let EmbedPDF try to handle this natively
|
||||||
|
// Only intervene if needed
|
||||||
|
setTimeout(() => {
|
||||||
|
// Check if the annotation is still selected after a brief delay
|
||||||
|
// If EmbedPDF handled it natively, it should be gone
|
||||||
|
const stillSelected = annotationApi.getSelectedAnnotation?.();
|
||||||
|
|
||||||
|
if (stillSelected) {
|
||||||
|
// EmbedPDF didn't handle it, so we need to delete manually
|
||||||
|
const annotation = stillSelected as any;
|
||||||
const pageIndex = annotation.object?.pageIndex || 0;
|
const pageIndex = annotation.object?.pageIndex || 0;
|
||||||
const id = annotation.object?.id;
|
const id = annotation.object?.id;
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
|
// Try deleteSelected method first (should integrate with history)
|
||||||
|
if ((annotationApi as any).deleteSelected) {
|
||||||
|
(annotationApi as any).deleteSelected();
|
||||||
|
} else {
|
||||||
|
// Fallback to direct deletion
|
||||||
annotationApi.deleteAnnotation(pageIndex, id);
|
annotationApi.deleteAnnotation(pageIndex, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('keydown', handleKeyDown);
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
@ -50,7 +68,14 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
|
|||||||
addImageSignature: (signatureData: string, x: number, y: number, width: number, height: number, pageIndex: number) => {
|
addImageSignature: (signatureData: string, x: number, y: number, width: number, height: number, pageIndex: number) => {
|
||||||
if (!annotationApi) return;
|
if (!annotationApi) return;
|
||||||
|
|
||||||
// Create image stamp annotation
|
// Create image stamp annotation with proper image data
|
||||||
|
console.log('Creating image annotation with data length:', signatureData?.length);
|
||||||
|
|
||||||
|
const annotationId = uuidV4();
|
||||||
|
|
||||||
|
// Store image data in our persistent store
|
||||||
|
storeImageData(annotationId, signatureData);
|
||||||
|
|
||||||
annotationApi.createAnnotation(pageIndex, {
|
annotationApi.createAnnotation(pageIndex, {
|
||||||
type: PdfAnnotationSubtype.STAMP,
|
type: PdfAnnotationSubtype.STAMP,
|
||||||
rect: {
|
rect: {
|
||||||
@ -60,8 +85,14 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
|
|||||||
author: 'Digital Signature',
|
author: 'Digital Signature',
|
||||||
subject: 'Digital Signature',
|
subject: 'Digital Signature',
|
||||||
pageIndex: pageIndex,
|
pageIndex: pageIndex,
|
||||||
id: uuidV4(),
|
id: annotationId,
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
|
// Store image data in multiple places to ensure history captures it
|
||||||
|
imageSrc: signatureData,
|
||||||
|
contents: signatureData, // Some annotation systems use contents
|
||||||
|
data: signatureData, // Try data field
|
||||||
|
imageData: signatureData, // Try imageData field
|
||||||
|
appearance: signatureData // Try appearance field
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -86,6 +117,10 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
|
|||||||
pageIndex: pageIndex,
|
pageIndex: pageIndex,
|
||||||
id: uuidV4(),
|
id: uuidV4(),
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
|
customData: {
|
||||||
|
signatureText: text,
|
||||||
|
signatureType: 'text'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -235,6 +270,11 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
|
|||||||
switch (params.signatureType) {
|
switch (params.signatureType) {
|
||||||
case 'image':
|
case 'image':
|
||||||
if (params.signatureData) {
|
if (params.signatureData) {
|
||||||
|
const annotationId = uuidV4();
|
||||||
|
|
||||||
|
// Store image data in our persistent store
|
||||||
|
storeImageData(annotationId, params.signatureData);
|
||||||
|
|
||||||
annotationApi.createAnnotation(page, {
|
annotationApi.createAnnotation(page, {
|
||||||
type: PdfAnnotationSubtype.STAMP,
|
type: PdfAnnotationSubtype.STAMP,
|
||||||
rect: {
|
rect: {
|
||||||
@ -244,8 +284,14 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
|
|||||||
author: 'Digital Signature',
|
author: 'Digital Signature',
|
||||||
subject: `Digital Signature - ${params.reason || 'Document signing'}`,
|
subject: `Digital Signature - ${params.reason || 'Document signing'}`,
|
||||||
pageIndex: page,
|
pageIndex: page,
|
||||||
id: uuidV4(),
|
id: annotationId,
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
|
// Store image data in multiple places to ensure history captures it
|
||||||
|
imageSrc: params.signatureData,
|
||||||
|
contents: params.signatureData, // Some annotation systems use contents
|
||||||
|
data: params.signatureData, // Try data field
|
||||||
|
imageData: params.signatureData, // Try imageData field
|
||||||
|
appearance: params.signatureData // Try appearance field
|
||||||
});
|
});
|
||||||
|
|
||||||
// Switch to select mode after placing signature so it can be easily deleted
|
// Switch to select mode after placing signature so it can be easily deleted
|
||||||
@ -274,6 +320,10 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI, SignatureAPIBridgePro
|
|||||||
pageIndex: page,
|
pageIndex: page,
|
||||||
id: uuidV4(),
|
id: uuidV4(),
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
|
customData: {
|
||||||
|
signatureText: params.signerName,
|
||||||
|
signatureType: 'text'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Switch to select mode after placing signature so it can be easily deleted
|
// Switch to select mode after placing signature so it can be easily deleted
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { createContext, useContext, useState, ReactNode, useCallback, useRef, useEffect } from 'react';
|
import React, { createContext, useContext, useState, ReactNode, useCallback, useRef, useEffect } from 'react';
|
||||||
import { SignParameters } from '../hooks/tools/sign/useSignParameters';
|
import { SignParameters } from '../hooks/tools/sign/useSignParameters';
|
||||||
import { SignatureAPI } from '../components/viewer/SignatureAPIBridge';
|
import { SignatureAPI } from '../components/viewer/SignatureAPIBridge';
|
||||||
|
import { HistoryAPI } from '../components/viewer/HistoryAPIBridge';
|
||||||
|
|
||||||
// Signature state interface
|
// Signature state interface
|
||||||
interface SignatureState {
|
interface SignatureState {
|
||||||
@ -19,11 +20,16 @@ interface SignatureActions {
|
|||||||
activateSignaturePlacementMode: () => void;
|
activateSignaturePlacementMode: () => void;
|
||||||
activateDeleteMode: () => void;
|
activateDeleteMode: () => void;
|
||||||
updateDrawSettings: (color: string, size: number) => void;
|
updateDrawSettings: (color: string, size: number) => void;
|
||||||
|
undo: () => void;
|
||||||
|
redo: () => void;
|
||||||
|
storeImageData: (id: string, data: string) => void;
|
||||||
|
getImageData: (id: string) => string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combined context interface
|
// Combined context interface
|
||||||
interface SignatureContextValue extends SignatureState, SignatureActions {
|
interface SignatureContextValue extends SignatureState, SignatureActions {
|
||||||
signatureApiRef: React.RefObject<SignatureAPI | null>;
|
signatureApiRef: React.RefObject<SignatureAPI | null>;
|
||||||
|
historyApiRef: React.RefObject<HistoryAPI | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create context
|
// Create context
|
||||||
@ -39,6 +45,8 @@ const initialState: SignatureState = {
|
|||||||
export const SignatureProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
export const SignatureProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
const [state, setState] = useState<SignatureState>(initialState);
|
const [state, setState] = useState<SignatureState>(initialState);
|
||||||
const signatureApiRef = useRef<SignatureAPI>(null);
|
const signatureApiRef = useRef<SignatureAPI>(null);
|
||||||
|
const historyApiRef = useRef<HistoryAPI>(null);
|
||||||
|
const imageDataStore = useRef<Map<string, string>>(new Map());
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
const setSignatureConfig = useCallback((config: SignParameters | null) => {
|
const setSignatureConfig = useCallback((config: SignParameters | null) => {
|
||||||
@ -100,12 +108,36 @@ export const SignatureProvider: React.FC<{ children: ReactNode }> = ({ children
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const undo = useCallback(() => {
|
||||||
|
if (historyApiRef.current) {
|
||||||
|
historyApiRef.current.undo();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const redo = useCallback(() => {
|
||||||
|
if (historyApiRef.current) {
|
||||||
|
historyApiRef.current.redo();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const storeImageData = useCallback((id: string, data: string) => {
|
||||||
|
console.log('Storing image data for annotation:', id, data.length, 'chars');
|
||||||
|
imageDataStore.current.set(id, data);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getImageData = useCallback((id: string) => {
|
||||||
|
const data = imageDataStore.current.get(id);
|
||||||
|
console.log('Retrieving image data for annotation:', id, data?.length, 'chars');
|
||||||
|
return data;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
// No auto-activation - all modes use manual buttons
|
// No auto-activation - all modes use manual buttons
|
||||||
|
|
||||||
const contextValue: SignatureContextValue = {
|
const contextValue: SignatureContextValue = {
|
||||||
...state,
|
...state,
|
||||||
signatureApiRef,
|
signatureApiRef,
|
||||||
|
historyApiRef,
|
||||||
setSignatureConfig,
|
setSignatureConfig,
|
||||||
setPlacementMode,
|
setPlacementMode,
|
||||||
activateDrawMode,
|
activateDrawMode,
|
||||||
@ -113,6 +145,10 @@ export const SignatureProvider: React.FC<{ children: ReactNode }> = ({ children
|
|||||||
activateSignaturePlacementMode,
|
activateSignaturePlacementMode,
|
||||||
activateDeleteMode,
|
activateDeleteMode,
|
||||||
updateDrawSettings,
|
updateDrawSettings,
|
||||||
|
undo,
|
||||||
|
redo,
|
||||||
|
storeImageData,
|
||||||
|
getImageData,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -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, updateDrawSettings } = useSignature();
|
const { setSignatureConfig, activateDrawMode, activateSignaturePlacementMode, deactivateDrawMode, updateDrawSettings, undo, redo } = useSignature();
|
||||||
|
|
||||||
// Manual sync function
|
// Manual sync function
|
||||||
const syncSignatureConfig = () => {
|
const syncSignatureConfig = () => {
|
||||||
@ -65,6 +65,8 @@ const Sign = (props: BaseToolProps) => {
|
|||||||
onActivateSignaturePlacement={handleSignaturePlacement}
|
onActivateSignaturePlacement={handleSignaturePlacement}
|
||||||
onDeactivateSignature={deactivateDrawMode}
|
onDeactivateSignature={deactivateDrawMode}
|
||||||
onUpdateDrawSettings={updateDrawSettings}
|
onUpdateDrawSettings={updateDrawSettings}
|
||||||
|
onUndo={undo}
|
||||||
|
onRedo={redo}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user