Remove annotation ligic from signature bridge/context

This commit is contained in:
Reece 2025-12-12 18:17:42 +00:00
parent 36c74e2fb8
commit 4e9cdc91a6
9 changed files with 153 additions and 274 deletions

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
import { Stack, TextInput, Select, Combobox, useCombobox, Group, Box } from '@mantine/core';
import { Stack, TextInput, Select, Combobox, useCombobox, Group, Box, SegmentedControl } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { ColorPicker } from '@app/components/annotation/shared/ColorPicker';
@ -12,6 +12,8 @@ interface TextInputWithFontProps {
onFontFamilyChange: (family: string) => void;
textColor?: string;
onTextColorChange?: (color: string) => void;
textAlign?: 'left' | 'center' | 'right';
onTextAlignChange?: (align: 'left' | 'center' | 'right') => void;
disabled?: boolean;
label?: string;
placeholder?: string;
@ -27,6 +29,8 @@ export const TextInputWithFont: React.FC<TextInputWithFontProps> = ({
onFontFamilyChange,
textColor = '#000000',
onTextColorChange,
textAlign = 'left',
onTextAlignChange,
disabled = false,
label,
placeholder,
@ -206,6 +210,23 @@ export const TextInputWithFont: React.FC<TextInputWithFontProps> = ({
}}
/>
)}
{/* Text Alignment */}
{onTextAlignChange && (
<SegmentedControl
value={textAlign}
onChange={(value) => {
onTextAlignChange(value as 'left' | 'center' | 'right');
onAnyChange?.();
}}
disabled={disabled}
data={[
{ label: t('textAlign.left', 'Left'), value: 'left' },
{ label: t('textAlign.center', 'Center'), value: 'center' },
{ label: t('textAlign.right', 'Right'), value: 'right' },
]}
/>
)}
</Stack>
);
};

View File

@ -1,57 +1,74 @@
import React, { useState } from 'react';
import { Stack } from '@mantine/core';
import { BaseAnnotationTool } from '@app/components/annotation/shared/BaseAnnotationTool';
import React, { useState, useEffect } from 'react';
import { Stack, Button } from '@mantine/core';
import { TextInputWithFont } from '@app/components/annotation/shared/TextInputWithFont';
import { useSignature } from '@app/contexts/SignatureContext';
interface TextToolProps {
onTextChange?: (text: string) => void;
disabled?: boolean;
}
const textAlignToNumber = (align: 'left' | 'center' | 'right'): number => {
switch (align) {
case 'left': return 0;
case 'center': return 1;
case 'right': return 2;
}
};
export const TextTool: React.FC<TextToolProps> = ({
onTextChange,
disabled = false
}) => {
const { signatureApiRef } = useSignature();
const [text, setText] = useState('');
const [fontSize, setFontSize] = useState(16);
const [fontFamily, setFontFamily] = useState('Helvetica');
const [textColor, setTextColor] = useState('#000000');
const [textAlign, setTextAlign] = useState<'left' | 'center' | 'right'>('left');
const handleTextChange = (newText: string) => {
setText(newText);
onTextChange?.(newText);
};
const handleSignatureDataChange = (data: string | null) => {
if (data) {
onTextChange?.(data);
const handlePlaceText = () => {
const api = signatureApiRef?.current;
if (api && text) {
api.activateAnnotationTool?.('text', {
color: textColor,
fontSize,
fontFamily,
textAlign: textAlignToNumber(textAlign),
contents: text,
});
}
};
const toolConfig = {
enableTextInput: true,
showPlaceButton: true,
placeButtonText: "Place Text"
};
return (
<BaseAnnotationTool
config={toolConfig}
onSignatureDataChange={handleSignatureDataChange}
disabled={disabled}
>
<Stack gap="sm">
<TextInputWithFont
text={text}
onTextChange={handleTextChange}
fontSize={fontSize}
onFontSizeChange={setFontSize}
fontFamily={fontFamily}
onFontFamilyChange={setFontFamily}
disabled={disabled}
label="Text Content"
placeholder="Enter text to place on the PDF"
/>
</Stack>
</BaseAnnotationTool>
<Stack gap="md">
<TextInputWithFont
text={text}
onTextChange={handleTextChange}
fontSize={fontSize}
onFontSizeChange={setFontSize}
fontFamily={fontFamily}
onFontFamilyChange={setFontFamily}
textColor={textColor}
onTextColorChange={setTextColor}
textAlign={textAlign}
onTextAlignChange={setTextAlign}
disabled={disabled}
label="Text Content"
placeholder="Enter text to place on the PDF"
/>
<Button
onClick={handlePlaceText}
disabled={disabled || !text.trim()}
fullWidth
>
Place Text
</Button>
</Stack>
);
};

View File

@ -66,17 +66,19 @@ export const AnnotationAPIBridge = forwardRef<AnnotationAPI>(function Annotation
case 'text':
return {
type: PdfAnnotationSubtype.FREETEXT,
textColor: options?.color ?? '#111111',
fontColor: options?.color ?? '#111111',
fontSize: options?.fontSize ?? 14,
fontFamily: options?.fontFamily ?? 'Helvetica',
textAlign: options?.textAlign ?? 0, // 0 = Left, 1 = Center, 2 = Right
opacity: options?.opacity ?? 1,
interiorColor: options?.fillColor ?? '#fffef7',
backgroundColor: options?.fillColor ?? '#fffef7',
borderWidth: options?.thickness ?? 1,
};
case 'note':
return {
type: PdfAnnotationSubtype.TEXT,
color: options?.color ?? '#ffa000',
backgroundColor: '#ffff00',
opacity: options?.opacity ?? 1,
icon: getIconEnum(options?.icon),
contents: options?.contents ?? '',

View File

@ -11,7 +11,6 @@ import { ThumbnailSidebar } from '@app/components/viewer/ThumbnailSidebar';
import { BookmarkSidebar } from '@app/components/viewer/BookmarkSidebar';
import { useNavigationGuard, useNavigationState } from '@app/contexts/NavigationContext';
import { useSignature } from '@app/contexts/SignatureContext';
import { useAnnotation } from '@app/contexts/AnnotationContext';
import { createStirlingFilesAndStubs } from '@app/services/fileStubHelpers';
import NavigationWarningModal from '@app/components/shared/NavigationWarningModal';
import { isStirlingFile } from '@app/types/fileContext';
@ -69,8 +68,7 @@ const EmbedPdfViewerContent = ({
}, [rotationState.rotation]);
// Get signature and annotation contexts
const { signatureApiRef, historyApiRef, signatureConfig, isPlacementMode } = useSignature();
const { annotationApiRef } = useAnnotation();
const { signatureApiRef, annotationApiRef, historyApiRef, signatureConfig, isPlacementMode } = useSignature();
// Get current file from FileContext
const { selectors, state } = useFileState();

View File

@ -1,8 +1,8 @@
import { useImperativeHandle, forwardRef, useEffect, useCallback, useRef, useState } from 'react';
import { useAnnotationCapability } from '@embedpdf/plugin-annotation/react';
import { PdfAnnotationSubtype, PdfAnnotationIcon, uuidV4 } from '@embedpdf/models';
import { PdfAnnotationSubtype, uuidV4 } from '@embedpdf/models';
import { useSignature } from '@app/contexts/SignatureContext';
import type { AnnotationToolId, AnnotationToolOptions, SignatureAPI } from '@app/components/viewer/viewerTypes';
import type { SignatureAPI } from '@app/components/viewer/viewerTypes';
import type { SignParameters } from '@app/hooks/tools/sign/useSignParameters';
import { useViewer } from '@app/contexts/ViewerContext';
@ -104,12 +104,20 @@ const createTextStampImage = (
ctx.fillStyle = textColor;
ctx.font = `${fontSize}px ${fontFamily}`;
ctx.textAlign = 'left';
ctx.textAlign = config.textAlign || 'left';
ctx.textBaseline = 'middle';
const horizontalPadding = paddingX;
const verticalCenter = naturalHeight / 2;
ctx.fillText(text, horizontalPadding, verticalCenter);
let xPosition = horizontalPadding;
if (config.textAlign === 'center') {
xPosition = naturalWidth / 2;
} else if (config.textAlign === 'right') {
xPosition = naturalWidth - horizontalPadding;
}
ctx.fillText(text, xPosition, verticalCenter);
return {
dataUrl: canvas.toDataURL('image/png'),
@ -199,157 +207,6 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI>(function SignatureAPI
}
}, [annotationApi, signatureConfig, placementPreviewSize, applyStampDefaults, cssToPdfSize]);
const getIconEnum = (icon?: string): PdfAnnotationIcon => {
switch (icon) {
case 'Comment': return PdfAnnotationIcon.Comment;
case 'Key': return PdfAnnotationIcon.Key;
case 'Note': return PdfAnnotationIcon.Note;
case 'Help': return PdfAnnotationIcon.Help;
case 'NewParagraph': return PdfAnnotationIcon.NewParagraph;
case 'Paragraph': return PdfAnnotationIcon.Paragraph;
case 'Insert': return PdfAnnotationIcon.Insert;
default: return PdfAnnotationIcon.Comment;
}
};
const buildAnnotationDefaults = useCallback(
(toolId: AnnotationToolId, options?: AnnotationToolOptions) => {
switch (toolId) {
case 'highlight':
return {
type: PdfAnnotationSubtype.HIGHLIGHT,
color: options?.color ?? '#ffd54f',
opacity: options?.opacity ?? 0.6,
};
case 'underline':
return {
type: PdfAnnotationSubtype.UNDERLINE,
color: options?.color ?? '#ffb300',
opacity: options?.opacity ?? 1,
};
case 'strikeout':
return {
type: PdfAnnotationSubtype.STRIKEOUT,
color: options?.color ?? '#e53935',
opacity: options?.opacity ?? 1,
};
case 'squiggly':
return {
type: PdfAnnotationSubtype.SQUIGGLY,
color: options?.color ?? '#00acc1',
opacity: options?.opacity ?? 1,
};
case 'ink':
return {
type: PdfAnnotationSubtype.INK,
color: options?.color ?? '#1f2933',
opacity: options?.opacity ?? 1,
borderWidth: options?.thickness ?? 2,
lineWidth: options?.thickness ?? 2,
strokeWidth: options?.thickness ?? 2,
};
case 'inkHighlighter':
return {
type: PdfAnnotationSubtype.INK,
color: options?.color ?? '#ffd54f',
opacity: options?.opacity ?? 0.5,
borderWidth: options?.thickness ?? 6,
lineWidth: options?.thickness ?? 6,
strokeWidth: options?.thickness ?? 6,
};
case 'text':
return {
type: PdfAnnotationSubtype.FREETEXT,
textColor: options?.color ?? '#111111',
fontSize: options?.fontSize ?? 14,
fontFamily: options?.fontFamily ?? 'Helvetica',
opacity: options?.opacity ?? 1,
interiorColor: options?.fillColor ?? '#fffef7',
borderWidth: options?.thickness ?? 1,
};
case 'note':
return {
type: PdfAnnotationSubtype.TEXT,
color: options?.color ?? '#ffa000',
opacity: options?.opacity ?? 1,
icon: getIconEnum(options?.icon),
contents: options?.contents ?? '',
};
case 'square':
return {
type: PdfAnnotationSubtype.SQUARE,
color: options?.color ?? '#1565c0',
interiorColor: options?.fillColor ?? '#e3f2fd',
opacity: options?.opacity ?? 0.35,
borderWidth: options?.thickness ?? 2,
};
case 'circle':
return {
type: PdfAnnotationSubtype.CIRCLE,
color: options?.color ?? '#1565c0',
interiorColor: options?.fillColor ?? '#e3f2fd',
opacity: options?.opacity ?? 0.35,
borderWidth: options?.thickness ?? 2,
};
case 'line':
return {
type: PdfAnnotationSubtype.LINE,
color: options?.color ?? '#1565c0',
opacity: options?.opacity ?? 1,
borderWidth: options?.thickness ?? 2,
};
case 'lineArrow':
return {
type: PdfAnnotationSubtype.LINE,
color: options?.color ?? '#1565c0',
opacity: options?.opacity ?? 1,
borderWidth: options?.thickness ?? 2,
startStyle: 'None',
endStyle: 'ClosedArrow',
lineEndingStyles: { start: 'None', end: 'ClosedArrow' },
};
case 'polyline':
return {
type: PdfAnnotationSubtype.POLYLINE,
color: options?.color ?? '#1565c0',
opacity: options?.opacity ?? 1,
borderWidth: options?.thickness ?? 2,
};
case 'polygon':
return {
type: PdfAnnotationSubtype.POLYGON,
color: options?.color ?? '#1565c0',
interiorColor: options?.fillColor ?? '#e3f2fd',
opacity: options?.opacity ?? 0.35,
borderWidth: options?.thickness ?? 2,
};
case 'stamp':
return {
type: PdfAnnotationSubtype.STAMP,
};
case 'select':
default:
return null;
}
},
[]
);
const configureAnnotationTool = useCallback(
(toolId: AnnotationToolId, options?: AnnotationToolOptions) => {
if (!annotationApi) return;
const defaults = buildAnnotationDefaults(toolId, options);
const api = annotationApi as any;
if (defaults) {
api.setToolDefaults?.(toolId, defaults);
}
api.setActiveTool?.(toolId === 'select' ? null : toolId);
},
[annotationApi, buildAnnotationDefaults]
);
// Enable keyboard deletion of selected annotations
useEffect(() => {
@ -531,27 +388,7 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI>(function SignatureAPI
return [];
}
},
activateAnnotationTool: (toolId: AnnotationToolId, options?: AnnotationToolOptions) => {
configureAnnotationTool(toolId, options);
},
setAnnotationStyle: (toolId: AnnotationToolId, options?: AnnotationToolOptions) => {
const defaults = buildAnnotationDefaults(toolId, options);
const api = annotationApi as any;
if (defaults && api?.setToolDefaults) {
api.setToolDefaults(toolId, defaults);
}
},
getSelectedAnnotation: () => {
return annotationApi?.getSelectedAnnotation?.() ?? null;
},
deselectAnnotation: () => {
const api = annotationApi as any;
api?.deselectAnnotation?.();
},
updateAnnotation: (pageIndex: number, annotationId: string, patch: Partial<any>) => {
annotationApi?.updateAnnotation?.(pageIndex, annotationId, patch);
},
}), [annotationApi, signatureConfig, placementPreviewSize, applyStampDefaults, configureAnnotationTool, buildAnnotationDefaults]);
}), [annotationApi, signatureConfig, placementPreviewSize, applyStampDefaults]);
useEffect(() => {
if (!annotationApi?.onAnnotationEvent) {

View File

@ -14,11 +14,6 @@ export interface SignatureAPI {
updateDrawSettings: (color: string, size: number) => void;
deactivateTools: () => void;
getPageAnnotations: (pageIndex: number) => Promise<any[]>;
activateAnnotationTool?: (toolId: AnnotationToolId, options?: AnnotationToolOptions) => void;
setAnnotationStyle?: (toolId: AnnotationToolId, options?: AnnotationToolOptions) => void;
getSelectedAnnotation?: () => any | null;
deselectAnnotation?: () => void;
updateAnnotation?: (pageIndex: number, annotationId: string, patch: Partial<any>) => void;
}
export interface AnnotationAPI {
@ -67,6 +62,7 @@ export interface AnnotationToolOptions {
thickness?: number;
fontSize?: number;
fontFamily?: string;
textAlign?: number; // 0 = Left, 1 = Center, 2 = Right
imageSrc?: string;
icon?: 'Comment' | 'Key' | 'Note' | 'Help' | 'NewParagraph' | 'Paragraph' | 'Insert';
contents?: string;

View File

@ -1,6 +1,6 @@
import React, { createContext, useContext, useState, ReactNode, useCallback, useRef } from 'react';
import { SignParameters } from '@app/hooks/tools/sign/useSignParameters';
import type { SignatureAPI, HistoryAPI } from '@app/components/viewer/viewerTypes';
import type { SignatureAPI, HistoryAPI, AnnotationAPI } from '@app/components/viewer/viewerTypes';
// Signature state interface
interface SignatureState {
@ -34,6 +34,7 @@ interface SignatureActions {
// Combined context interface
interface SignatureContextValue extends SignatureState, SignatureActions {
signatureApiRef: React.RefObject<SignatureAPI | null>;
annotationApiRef: React.RefObject<AnnotationAPI | null>;
historyApiRef: React.RefObject<HistoryAPI | null>;
}
@ -52,6 +53,7 @@ const initialState: SignatureState = {
export const SignatureProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [state, setState] = useState<SignatureState>(initialState);
const signatureApiRef = useRef<SignatureAPI>(null);
const annotationApiRef = useRef<AnnotationAPI>(null);
const historyApiRef = useRef<HistoryAPI>(null);
const imageDataStore = useRef<Map<string, string>>(new Map());
@ -157,6 +159,7 @@ export const SignatureProvider: React.FC<{ children: ReactNode }> = ({ children
const contextValue: SignatureContextValue = {
...state,
signatureApiRef,
annotationApiRef,
historyApiRef,
setSignatureConfig,
setPlacementMode,

View File

@ -18,6 +18,7 @@ export interface SignParameters {
fontFamily?: string;
fontSize?: number;
textColor?: string;
textAlign?: 'left' | 'center' | 'right';
}
export const DEFAULT_PARAMETERS: SignParameters = {
@ -28,6 +29,7 @@ export const DEFAULT_PARAMETERS: SignParameters = {
fontFamily: 'Helvetica',
fontSize: 16,
textColor: '#000000',
textAlign: 'left',
};
const validateSignParameters = (parameters: SignParameters): boolean => {

View File

@ -20,6 +20,7 @@ const Annotate = (_props: BaseToolProps) => {
const { selectors } = useFileContext();
const {
signatureApiRef,
annotationApiRef,
historyApiRef,
undo,
redo,
@ -109,7 +110,8 @@ const Annotate = (_props: BaseToolProps) => {
case 'squiggly':
return { color: squigglyColor, opacity: squigglyOpacity / 100, ...metadata };
case 'text':
return { color: textColor, fontSize: textSize, textAlign: textAlignment, ...metadata };
const textAlignNumber = textAlignment === 'left' ? 0 : textAlignment === 'center' ? 1 : 2;
return { color: textColor, fontSize: textSize, textAlign: textAlignNumber, ...metadata };
case 'square':
case 'circle':
case 'polygon':
@ -163,7 +165,7 @@ const Annotate = (_props: BaseToolProps) => {
if (viewerContext.isAnnotationMode) return;
viewerContext.setAnnotationMode(true);
signatureApiRef?.current?.activateAnnotationTool?.(activeTool, buildToolOptions(activeTool));
annotationApiRef?.current?.activateAnnotationTool?.(activeTool, buildToolOptions(activeTool));
}, [viewerContext?.isAnnotationMode, signatureApiRef, activeTool, buildToolOptions]);
const activateAnnotationTool = (toolId: AnnotationToolId) => {
@ -179,7 +181,7 @@ const Annotate = (_props: BaseToolProps) => {
manualToolSwitch.current = true;
// Deselect annotation in the viewer first
signatureApiRef?.current?.deselectAnnotation?.();
annotationApiRef?.current?.deselectAnnotation?.();
// Clear selection state to show default controls
setSelectedAnn(null);
@ -191,10 +193,10 @@ const Annotate = (_props: BaseToolProps) => {
// For stamp, apply the image if we have one
if (toolId === 'stamp' && stampImageData) {
signatureApiRef?.current?.setAnnotationStyle?.('stamp', { imageSrc: stampImageData });
signatureApiRef?.current?.activateAnnotationTool?.('stamp', { imageSrc: stampImageData });
annotationApiRef?.current?.setAnnotationStyle?.('stamp', { imageSrc: stampImageData });
annotationApiRef?.current?.activateAnnotationTool?.('stamp', { imageSrc: stampImageData });
} else {
signatureApiRef?.current?.activateAnnotationTool?.(toolId, options);
annotationApiRef?.current?.activateAnnotationTool?.(toolId, options);
}
// Reset flag after a short delay
@ -206,9 +208,9 @@ const Annotate = (_props: BaseToolProps) => {
useEffect(() => {
// push style updates to EmbedPDF when sliders/colors change
if (activeTool === 'stamp' && stampImageData) {
signatureApiRef?.current?.setAnnotationStyle?.('stamp', { imageSrc: stampImageData });
annotationApiRef?.current?.setAnnotationStyle?.('stamp', { imageSrc: stampImageData });
} else {
signatureApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
annotationApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
}
}, [activeTool, buildToolOptions, signatureApiRef, stampImageData]);
@ -219,7 +221,7 @@ const Annotate = (_props: BaseToolProps) => {
// and apply the converted size to the stamp tool automatically
if (activeTool === 'stamp' && placementPreviewSize && stampImageData) {
// Just update the image source; size is handled by SignatureAPIBridge
signatureApiRef?.current?.setAnnotationStyle?.('stamp', {
annotationApiRef?.current?.setAnnotationStyle?.('stamp', {
imageSrc: stampImageData,
});
}
@ -230,10 +232,10 @@ const Annotate = (_props: BaseToolProps) => {
const handler = (e: KeyboardEvent) => {
if (e.key !== 'Escape') return;
if (['polyline', 'polygon'].includes(activeTool)) {
signatureApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
signatureApiRef?.current?.activateAnnotationTool?.(null as any);
annotationApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
annotationApiRef?.current?.activateAnnotationTool?.(null as any);
setTimeout(() => {
signatureApiRef?.current?.activateAnnotationTool?.(activeTool, buildToolOptions(activeTool));
annotationApiRef?.current?.activateAnnotationTool?.(activeTool, buildToolOptions(activeTool));
}, 50);
}
};
@ -244,7 +246,7 @@ const Annotate = (_props: BaseToolProps) => {
// Poll selected annotation to allow editing existing highlights/text
useEffect(() => {
const interval = setInterval(() => {
const ann = signatureApiRef?.current?.getSelectedAnnotation?.();
const ann = annotationApiRef?.current?.getSelectedAnnotation?.();
const annId = ann?.object?.id ?? null;
// Only update state when selection actually changes
if (annId !== selectedAnnId) {
@ -321,6 +323,7 @@ const Annotate = (_props: BaseToolProps) => {
const otherTools: { id: AnnotationToolId; label: string; icon: string }[] = [
{ id: 'text', label: t('annotation.text', 'Text box'), icon: 'text-fields' },
{ id: 'note', label: t('annotation.note', 'Note'), icon: 'sticky-note-2' },
{ id: 'stamp', label: t('annotation.stamp', 'Add Image'), icon: 'add-photo-alternate' },
];
@ -391,8 +394,8 @@ const Annotate = (_props: BaseToolProps) => {
setTimeout(() => {
viewerContext?.setAnnotationMode(true);
setPlacementMode(true); // This shows the preview overlay
signatureApiRef?.current?.setAnnotationStyle?.('stamp', { imageSrc: dataUrl });
signatureApiRef?.current?.activateAnnotationTool?.('stamp', { imageSrc: dataUrl });
annotationApiRef?.current?.setAnnotationStyle?.('stamp', { imageSrc: dataUrl });
annotationApiRef?.current?.activateAnnotationTool?.('stamp', { imageSrc: dataUrl });
}, 150);
} catch (err) {
console.error('Failed to load stamp image', err);
@ -603,7 +606,7 @@ const Annotate = (_props: BaseToolProps) => {
max={100}
value={Math.round(((selectedAnn.object?.opacity ?? 1) * 100) || 100)}
onChange={(value) => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{ opacity: value / 100 }
@ -638,7 +641,7 @@ const Annotate = (_props: BaseToolProps) => {
max={12}
value={selectedAnn.object?.borderWidth ?? inkWidth}
onChange={(value) => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{
@ -698,7 +701,7 @@ const Annotate = (_props: BaseToolProps) => {
clearTimeout(selectedUpdateTimer.current);
}
selectedUpdateTimer.current = setTimeout(() => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{ contents: newVal, textColor: selectedAnn.object?.textColor ?? textColor }
@ -713,7 +716,7 @@ const Annotate = (_props: BaseToolProps) => {
clearTimeout(selectedUpdateTimer.current);
}
selectedUpdateTimer.current = setTimeout(() => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{ contents: val, textColor: selectedAnn.object?.textColor ?? textColor }
@ -729,7 +732,7 @@ const Annotate = (_props: BaseToolProps) => {
value={selectedFontSize}
onChange={(size) => {
setSelectedFontSize(size);
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{ fontSize: size }
@ -743,7 +746,7 @@ const Annotate = (_props: BaseToolProps) => {
<ActionIcon
variant={(selectedAnn.object?.textAlign ?? 'left') === 'left' ? 'filled' : 'default'}
onClick={() => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{ textAlign: 'left' }
@ -756,7 +759,7 @@ const Annotate = (_props: BaseToolProps) => {
<ActionIcon
variant={(selectedAnn.object?.textAlign ?? 'left') === 'center' ? 'filled' : 'default'}
onClick={() => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{ textAlign: 'center' }
@ -769,7 +772,7 @@ const Annotate = (_props: BaseToolProps) => {
<ActionIcon
variant={(selectedAnn.object?.textAlign ?? 'left') === 'right' ? 'filled' : 'default'}
onClick={() => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{ textAlign: 'right' }
@ -808,7 +811,7 @@ const Annotate = (_props: BaseToolProps) => {
max={100}
value={Math.round(((selectedAnn.object?.opacity ?? 1) * 100) || 100)}
onChange={(value) => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{ opacity: value / 100 }
@ -823,7 +826,7 @@ const Annotate = (_props: BaseToolProps) => {
max={12}
value={selectedAnn.object?.borderWidth ?? shapeThickness}
onChange={(value) => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{
@ -877,7 +880,7 @@ const Annotate = (_props: BaseToolProps) => {
max={100}
value={Math.round(((selectedAnn.object?.opacity ?? 1) * 100) || 100)}
onChange={(value) => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{ opacity: value / 100 }
@ -893,7 +896,7 @@ const Annotate = (_props: BaseToolProps) => {
max={12}
value={selectedAnn.object?.borderWidth ?? shapeThickness}
onChange={(value) => {
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{
@ -911,7 +914,7 @@ const Annotate = (_props: BaseToolProps) => {
variant={(selectedAnn.object?.borderWidth ?? shapeThickness) === 0 ? 'filled' : 'light'}
onClick={() => {
const newValue = (selectedAnn.object?.borderWidth ?? shapeThickness) === 0 ? 1 : 0;
signatureApiRef?.current?.updateAnnotation?.(
annotationApiRef?.current?.updateAnnotation?.(
selectedAnn.object?.pageIndex ?? 0,
selectedAnn.object?.id,
{
@ -989,40 +992,40 @@ const Annotate = (_props: BaseToolProps) => {
if (colorPickerTarget === 'highlight') {
setHighlightOpacity(opacity);
if (activeTool === 'highlight' || activeTool === 'inkHighlighter') {
signatureApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
annotationApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
}
if (selectedAnn?.object?.id && (selectedAnn.object?.type === 9 || selectedAnn.object?.type === 15)) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { opacity: opacity / 100 });
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { opacity: opacity / 100 });
}
} else if (colorPickerTarget === 'underline') {
setUnderlineOpacity(opacity);
signatureApiRef?.current?.setAnnotationStyle?.('underline', buildToolOptions('underline'));
annotationApiRef?.current?.setAnnotationStyle?.('underline', buildToolOptions('underline'));
if (selectedAnn?.object?.id && selectedAnn.object?.type === 10) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { opacity: opacity / 100 });
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { opacity: opacity / 100 });
}
} else if (colorPickerTarget === 'strikeout') {
setStrikeoutOpacity(opacity);
signatureApiRef?.current?.setAnnotationStyle?.('strikeout', buildToolOptions('strikeout'));
annotationApiRef?.current?.setAnnotationStyle?.('strikeout', buildToolOptions('strikeout'));
if (selectedAnn?.object?.id && selectedAnn.object?.type === 12) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { opacity: opacity / 100 });
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { opacity: opacity / 100 });
}
} else if (colorPickerTarget === 'squiggly') {
setSquigglyOpacity(opacity);
signatureApiRef?.current?.setAnnotationStyle?.('squiggly', buildToolOptions('squiggly'));
annotationApiRef?.current?.setAnnotationStyle?.('squiggly', buildToolOptions('squiggly'));
if (selectedAnn?.object?.id && selectedAnn.object?.type === 11) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { opacity: opacity / 100 });
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { opacity: opacity / 100 });
}
} else if (colorPickerTarget === 'shapeStroke') {
setShapeStrokeOpacity(opacity);
const shapeTools = ['square', 'circle', 'polygon'] as AnnotationToolId[];
if (shapeTools.includes(activeTool)) {
signatureApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
annotationApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
}
} else if (colorPickerTarget === 'shapeFill') {
setShapeFillOpacity(opacity);
const fillShapeTools = ['square', 'circle', 'polygon'] as AnnotationToolId[];
if (fillShapeTools.includes(activeTool)) {
signatureApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
annotationApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
}
}
}}
@ -1030,44 +1033,44 @@ const Annotate = (_props: BaseToolProps) => {
if (colorPickerTarget === 'ink') {
setInkColor(color);
if (activeTool === 'ink') {
signatureApiRef?.current?.setAnnotationStyle?.('ink', buildToolOptions('ink'));
annotationApiRef?.current?.setAnnotationStyle?.('ink', buildToolOptions('ink'));
}
if (selectedAnn?.object?.type === 15) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { color });
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { color });
}
} else if (colorPickerTarget === 'highlight') {
setHighlightColor(color);
if (activeTool === 'highlight' || activeTool === 'inkHighlighter') {
signatureApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
annotationApiRef?.current?.setAnnotationStyle?.(activeTool, buildToolOptions(activeTool));
}
if (selectedAnn?.object?.type === 9 || selectedAnn?.object?.type === 15) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { color });
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { color });
}
} else if (colorPickerTarget === 'underline') {
setUnderlineColor(color);
signatureApiRef?.current?.setAnnotationStyle?.('underline', buildToolOptions('underline'));
annotationApiRef?.current?.setAnnotationStyle?.('underline', buildToolOptions('underline'));
if (selectedAnn?.object?.id) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { color });
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { color });
}
} else if (colorPickerTarget === 'strikeout') {
setStrikeoutColor(color);
signatureApiRef?.current?.setAnnotationStyle?.('strikeout', buildToolOptions('strikeout'));
annotationApiRef?.current?.setAnnotationStyle?.('strikeout', buildToolOptions('strikeout'));
if (selectedAnn?.object?.id) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { color });
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { color });
}
} else if (colorPickerTarget === 'squiggly') {
setSquigglyColor(color);
signatureApiRef?.current?.setAnnotationStyle?.('squiggly', buildToolOptions('squiggly'));
annotationApiRef?.current?.setAnnotationStyle?.('squiggly', buildToolOptions('squiggly'));
if (selectedAnn?.object?.id) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { color });
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, { color });
}
} else {
setTextColor(color);
if (activeTool === 'text') {
signatureApiRef?.current?.setAnnotationStyle?.('text', buildToolOptions('text'));
annotationApiRef?.current?.setAnnotationStyle?.('text', buildToolOptions('text'));
}
if (selectedAnn?.object?.type === 3 || selectedAnn?.object?.type === 1) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, {
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, {
textColor: color,
color,
});
@ -1080,10 +1083,10 @@ const Annotate = (_props: BaseToolProps) => {
setShapeStrokeColor(color);
const styleTool = shapeTools.includes(activeTool) ? activeTool : null;
if (styleTool) {
signatureApiRef?.current?.setAnnotationStyle?.(styleTool, buildToolOptions(styleTool));
annotationApiRef?.current?.setAnnotationStyle?.(styleTool, buildToolOptions(styleTool));
}
if (selectedAnn?.object?.id) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, {
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, {
strokeColor: color, // border color
color: selectedAnn.object?.color ?? shapeFillColor, // preserve fill
borderWidth: shapeThickness,
@ -1094,10 +1097,10 @@ const Annotate = (_props: BaseToolProps) => {
setShapeFillColor(color);
const styleTool = fillShapeTools.includes(activeTool) ? activeTool : null;
if (styleTool) {
signatureApiRef?.current?.setAnnotationStyle?.(styleTool, buildToolOptions(styleTool));
annotationApiRef?.current?.setAnnotationStyle?.(styleTool, buildToolOptions(styleTool));
}
if (selectedAnn?.object?.id) {
signatureApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, {
annotationApiRef?.current?.updateAnnotation?.(selectedAnn.object.pageIndex ?? 0, selectedAnn.object.id, {
color, // fill color
strokeColor: selectedAnn.object?.strokeColor ?? shapeStrokeColor, // preserve border
borderWidth: shapeThickness,
@ -1126,7 +1129,7 @@ const Annotate = (_props: BaseToolProps) => {
size="lg"
onClick={() => {
viewerContext?.setAnnotationMode(true);
signatureApiRef?.current?.activateAnnotationTool?.(activeTool, buildToolOptions(activeTool));
annotationApiRef?.current?.activateAnnotationTool?.(activeTool, buildToolOptions(activeTool));
setIsAnnotationPaused(false);
}}
style={{