# Description of Changes This pull request removes unnecessary imports of the default React object from multiple frontend files. The changes help clean up the codebase by only importing specific React hooks where needed, rather than importing the entire React object. **Code cleanup and import optimization:** * Removed default `React` imports from component files, retaining only the necessary React hooks (such as `useEffect`, `useState`, `useMemo`, etc.) in files like `FileEditor.tsx`, `FullscreenToolList.tsx`, `ToolPanel.tsx`, `PageNumberPreview.tsx`, `AdjustContrastPreview.tsx`, `AutomationRun.tsx`, `LocalEmbedPDFWithAnnotations.tsx`, `ToolRegistryProvider.tsx`, `useTranslatedToolRegistry.tsx`, and `AdjustContrast.tsx`. [[1]](diffhunk://#diff-481d0a2d8a1714d34d21181db63a020b08dfccfbfa80bf47ac9af382dff25310L1-R1) [[2]](diffhunk://#diff-1d6e9507cb0744e03ec0e80c510874bfc5054986b0275ae3b8592eb67b5ec0f2L1-R1) [[3]](diffhunk://#diff-8ee3da71652291722dc6130f44565c098fe0f9cdf5e8ec0ba3c631be8980b13eL1-R1) [[4]](diffhunk://#diff-ecc12bf9b557e947ae2f1866d07446b19bad1fbdf143bf231dd3076b1e794826L1-R1) [[5]](diffhunk://#diff-6ba4ca6f491368b62e160639e97207f5c1d35fee77f4eebd39133630e0ecb7a1L1-R1) [[6]](diffhunk://#diff-ff7cba3dba3b1f4ec4c8758a9fbe539351f96225284d0c61cca2642ec7a8e486L1-R1) [[7]](diffhunk://#diff-d99cf54aa50d266c08844fac31c79e73a7f1714adeedb186d1decab8b9fb7f78L1-R1) [[8]](diffhunk://#diff-3467ae2b00d2ea95c360bc367adfbae124a4fb1d2627e889d12fb00e288bf508L1-R1) [[9]](diffhunk://#diff-0a3e636736c137356dd9354ff3cacbd302ebda40147545e13c62d073525d1969L1-R1) [[10]](diffhunk://#diff-2fed64bea41254c30dcc038f6b92943272bcaa771af200b8a3dc1a2cef6b5ca7L2-R2) * Removed default `React` imports from presentational and settings components that do not use JSX at the top level, such as `Workbench.tsx`, `SliderWithInput.tsx`, `AdjustContrastBasicSettings.tsx`, `AdjustContrastColorSettings.tsx`, `AdjustContrastSingleStepSettings.tsx`, `FileSummaryHeader.tsx`, `SignatureSection.tsx`, `SignatureStatusBadge.tsx`, and `ThumbnailPreview.tsx`. [[1]](diffhunk://#diff-6ffa9f7048b8e2a454ccf52b712179784cf32d42ecac9c85331c595a4cee39b4L1) [[2]](diffhunk://#diff-a1159e58f6668bc6de9595b4014fd7b8e0a19f9efa75294ba80184cfe54b601fL1) [[3]](diffhunk://#diff-ce5bbd748c15bc456e7f01180b7ff04c80c782e3d6662384f28e032af36ed3ccL1) [[4]](diffhunk://#diff-494006ec5e237eb7b3a16b9bc144a6ed49ed38c547d95b68a89f69a5af6676ceL1) [[5]](diffhunk://#diff-e61a3e2d98c9601eea868062258b925e6f6d672f49df14e3684b12f736622db4L1) [[6]](diffhunk://#diff-97df8b451114e347bb3f581ff5c91057601fb821e224479e1106493ce9479dcdL1) [[7]](diffhunk://#diff-cc070bfc4dc892a4e9a2be725c9f27ab66bdbc821a525fad10e14b27096d4e5aL1) [[8]](diffhunk://#diff-c179df2634412e4938bcd686f86b3bdbd1a6039d8a8b62c44fd0c085cc58af74L1) [[9]](diffhunk://#diff-64403230a8c8e90135bd8d7cd275c40d8e22bd3a22ed642dec5451018eec3c10L1) These changes reduce unnecessary imports and make the codebase cleaner and more consistent. --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. Co-authored-by: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com>
320 lines
11 KiB
TypeScript
320 lines
11 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react';
|
|
import { createPluginRegistration } from '@embedpdf/core';
|
|
import { EmbedPDF } from '@embedpdf/core/react';
|
|
import { usePdfiumEngine } from '@embedpdf/engines/react';
|
|
|
|
// Import the essential plugins
|
|
import { Viewport, ViewportPluginPackage } from '@embedpdf/plugin-viewport/react';
|
|
import { Scroller, ScrollPluginPackage, ScrollStrategy } from '@embedpdf/plugin-scroll/react';
|
|
import { LoaderPluginPackage } from '@embedpdf/plugin-loader/react';
|
|
import { RenderPluginPackage } from '@embedpdf/plugin-render/react';
|
|
import { ZoomPluginPackage } from '@embedpdf/plugin-zoom/react';
|
|
import { InteractionManagerPluginPackage, PagePointerProvider, GlobalPointerProvider } from '@embedpdf/plugin-interaction-manager/react';
|
|
import { SelectionLayer, SelectionPluginPackage } from '@embedpdf/plugin-selection/react';
|
|
import { TilingLayer, TilingPluginPackage } from '@embedpdf/plugin-tiling/react';
|
|
import { PanPluginPackage } from '@embedpdf/plugin-pan/react';
|
|
import { SpreadPluginPackage, SpreadMode } from '@embedpdf/plugin-spread/react';
|
|
import { SearchPluginPackage } from '@embedpdf/plugin-search/react';
|
|
import { ThumbnailPluginPackage } from '@embedpdf/plugin-thumbnail/react';
|
|
import { RotatePluginPackage, Rotate } from '@embedpdf/plugin-rotate/react';
|
|
import { Rotation } from '@embedpdf/models';
|
|
|
|
// Import annotation plugins
|
|
import { HistoryPluginPackage } from '@embedpdf/plugin-history/react';
|
|
import { AnnotationLayer, AnnotationPluginPackage } from '@embedpdf/plugin-annotation/react';
|
|
import { PdfAnnotationSubtype } from '@embedpdf/models';
|
|
|
|
import { CustomSearchLayer } from './CustomSearchLayer';
|
|
import { ZoomAPIBridge } from './ZoomAPIBridge';
|
|
import ToolLoadingFallback from '../tools/ToolLoadingFallback';
|
|
import { Center, Stack, Text } from '@mantine/core';
|
|
import { ScrollAPIBridge } from './ScrollAPIBridge';
|
|
import { SelectionAPIBridge } from './SelectionAPIBridge';
|
|
import { PanAPIBridge } from './PanAPIBridge';
|
|
import { SpreadAPIBridge } from './SpreadAPIBridge';
|
|
import { SearchAPIBridge } from './SearchAPIBridge';
|
|
import { ThumbnailAPIBridge } from './ThumbnailAPIBridge';
|
|
import { RotateAPIBridge } from './RotateAPIBridge';
|
|
|
|
interface LocalEmbedPDFWithAnnotationsProps {
|
|
file?: File | Blob;
|
|
url?: string | null;
|
|
onAnnotationChange?: (annotations: any[]) => void;
|
|
}
|
|
|
|
export function LocalEmbedPDFWithAnnotations({
|
|
file,
|
|
url,
|
|
onAnnotationChange
|
|
}: LocalEmbedPDFWithAnnotationsProps) {
|
|
const [pdfUrl, setPdfUrl] = useState<string | null>(null);
|
|
|
|
// Convert File to URL if needed
|
|
useEffect(() => {
|
|
if (file) {
|
|
const objectUrl = URL.createObjectURL(file);
|
|
setPdfUrl(objectUrl);
|
|
return () => URL.revokeObjectURL(objectUrl);
|
|
} else if (url) {
|
|
setPdfUrl(url);
|
|
}
|
|
}, [file, url]);
|
|
|
|
// Create plugins configuration with annotation support
|
|
const plugins = useMemo(() => {
|
|
if (!pdfUrl) return [];
|
|
|
|
// Calculate 3.5rem in pixels dynamically based on root font size
|
|
const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
const viewportGap = rootFontSize * 3.5;
|
|
|
|
return [
|
|
createPluginRegistration(LoaderPluginPackage, {
|
|
loadingOptions: {
|
|
type: 'url',
|
|
pdfFile: {
|
|
id: 'stirling-pdf-signing-viewer',
|
|
url: pdfUrl,
|
|
},
|
|
},
|
|
}),
|
|
createPluginRegistration(ViewportPluginPackage, {
|
|
viewportGap,
|
|
}),
|
|
createPluginRegistration(ScrollPluginPackage, {
|
|
strategy: ScrollStrategy.Vertical,
|
|
initialPage: 0,
|
|
}),
|
|
createPluginRegistration(RenderPluginPackage),
|
|
|
|
// Register interaction manager (required for annotations)
|
|
createPluginRegistration(InteractionManagerPluginPackage),
|
|
|
|
// Register selection plugin (depends on InteractionManager)
|
|
createPluginRegistration(SelectionPluginPackage),
|
|
|
|
// Register history plugin for undo/redo (recommended for annotations)
|
|
createPluginRegistration(HistoryPluginPackage),
|
|
|
|
// Register annotation plugin (depends on InteractionManager, Selection, History)
|
|
createPluginRegistration(AnnotationPluginPackage, {
|
|
annotationAuthor: 'Digital Signature',
|
|
autoCommit: true,
|
|
deactivateToolAfterCreate: false,
|
|
selectAfterCreate: true,
|
|
}),
|
|
|
|
// Register pan plugin
|
|
createPluginRegistration(PanPluginPackage, {
|
|
defaultMode: 'mobile',
|
|
}),
|
|
|
|
// Register zoom plugin
|
|
createPluginRegistration(ZoomPluginPackage, {
|
|
defaultZoomLevel: 1.4,
|
|
minZoom: 0.2,
|
|
maxZoom: 3.0,
|
|
}),
|
|
|
|
// Register tiling plugin
|
|
createPluginRegistration(TilingPluginPackage, {
|
|
tileSize: 768,
|
|
overlapPx: 5,
|
|
extraRings: 1,
|
|
}),
|
|
|
|
// Register spread plugin
|
|
createPluginRegistration(SpreadPluginPackage, {
|
|
defaultSpreadMode: SpreadMode.None,
|
|
}),
|
|
|
|
// Register search plugin
|
|
createPluginRegistration(SearchPluginPackage),
|
|
|
|
// Register thumbnail plugin
|
|
createPluginRegistration(ThumbnailPluginPackage),
|
|
|
|
// Register rotate plugin
|
|
createPluginRegistration(RotatePluginPackage, {
|
|
defaultRotation: Rotation.Degree0,
|
|
}),
|
|
];
|
|
}, [pdfUrl]);
|
|
|
|
// Initialize the engine
|
|
const { engine, isLoading, error } = usePdfiumEngine();
|
|
|
|
// Early return if no file or URL provided
|
|
if (!file && !url) {
|
|
return (
|
|
<Center h="100%" w="100%">
|
|
<Stack align="center" gap="md">
|
|
<div style={{ fontSize: '24px' }}>📄</div>
|
|
<Text c="dimmed" size="sm">
|
|
No PDF provided
|
|
</Text>
|
|
</Stack>
|
|
</Center>
|
|
);
|
|
}
|
|
|
|
if (isLoading || !engine || !pdfUrl) {
|
|
return <ToolLoadingFallback toolName="PDF Engine" />;
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<Center h="100%" w="100%">
|
|
<Stack align="center" gap="md">
|
|
<div style={{ fontSize: '24px' }}>❌</div>
|
|
<Text c="red" size="sm" style={{ textAlign: 'center' }}>
|
|
Error loading PDF engine: {error.message}
|
|
</Text>
|
|
</Stack>
|
|
</Center>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div style={{
|
|
height: '100%',
|
|
width: '100%',
|
|
position: 'relative',
|
|
overflow: 'hidden',
|
|
flex: 1,
|
|
minHeight: 0,
|
|
minWidth: 0
|
|
}}>
|
|
<EmbedPDF
|
|
engine={engine}
|
|
plugins={plugins}
|
|
onInitialized={async (registry) => {
|
|
const annotationPlugin = registry.getPlugin('annotation');
|
|
if (!annotationPlugin || !annotationPlugin.provides) return;
|
|
|
|
const annotationApi = annotationPlugin.provides();
|
|
if (!annotationApi) return;
|
|
|
|
// Add custom signature stamp tool
|
|
annotationApi.addTool({
|
|
id: 'signatureStamp',
|
|
name: 'Digital Signature',
|
|
interaction: { exclusive: false, cursor: 'copy' },
|
|
matchScore: () => 0,
|
|
defaults: {
|
|
type: PdfAnnotationSubtype.STAMP,
|
|
// Will be set dynamically when user creates signature
|
|
},
|
|
});
|
|
|
|
// Add custom ink signature tool
|
|
annotationApi.addTool({
|
|
id: 'signatureInk',
|
|
name: 'Signature Draw',
|
|
interaction: { exclusive: true, cursor: 'crosshair' },
|
|
matchScore: () => 0,
|
|
defaults: {
|
|
type: PdfAnnotationSubtype.INK,
|
|
color: '#000000',
|
|
opacity: 1.0,
|
|
borderWidth: 2,
|
|
},
|
|
});
|
|
|
|
// Listen for annotation events to notify parent
|
|
if (onAnnotationChange) {
|
|
annotationApi.onAnnotationEvent((event: any) => {
|
|
if (event.committed) {
|
|
// Get all annotations and notify parent
|
|
// This is a simplified approach - in reality you'd need to get all annotations
|
|
onAnnotationChange([event.annotation]);
|
|
}
|
|
});
|
|
}
|
|
}}
|
|
>
|
|
<ZoomAPIBridge />
|
|
<ScrollAPIBridge />
|
|
<SelectionAPIBridge />
|
|
<PanAPIBridge />
|
|
<SpreadAPIBridge />
|
|
<SearchAPIBridge />
|
|
<ThumbnailAPIBridge />
|
|
<RotateAPIBridge />
|
|
<GlobalPointerProvider>
|
|
<Viewport
|
|
style={{
|
|
backgroundColor: 'var(--bg-surface)',
|
|
height: '100%',
|
|
width: '100%',
|
|
maxHeight: '100%',
|
|
maxWidth: '100%',
|
|
overflow: 'auto',
|
|
position: 'relative',
|
|
flex: 1,
|
|
minHeight: 0,
|
|
minWidth: 0,
|
|
contain: 'strict',
|
|
}}
|
|
>
|
|
<Scroller
|
|
renderPage={({ width, height, pageIndex, scale, rotation }: {
|
|
width: number;
|
|
height: number;
|
|
pageIndex: number;
|
|
scale: number;
|
|
rotation?: number;
|
|
}) => (
|
|
<Rotate pageSize={{ width, height }}>
|
|
<PagePointerProvider {...{
|
|
pageWidth: width,
|
|
pageHeight: height,
|
|
pageIndex,
|
|
scale,
|
|
rotation: rotation || 0
|
|
}}>
|
|
<div
|
|
style={{
|
|
width,
|
|
height,
|
|
position: 'relative',
|
|
userSelect: 'none',
|
|
WebkitUserSelect: 'none',
|
|
MozUserSelect: 'none',
|
|
msUserSelect: 'none'
|
|
}}
|
|
draggable={false}
|
|
onDragStart={(e) => e.preventDefault()}
|
|
onDrop={(e) => e.preventDefault()}
|
|
onDragOver={(e) => e.preventDefault()}
|
|
>
|
|
{/* High-resolution tile layer */}
|
|
<TilingLayer pageIndex={pageIndex} scale={scale} />
|
|
|
|
{/* Search highlight layer */}
|
|
<CustomSearchLayer pageIndex={pageIndex} scale={scale} />
|
|
|
|
{/* Selection layer for text interaction */}
|
|
<SelectionLayer pageIndex={pageIndex} scale={scale} />
|
|
|
|
{/* Annotation layer for signatures */}
|
|
<AnnotationLayer
|
|
pageIndex={pageIndex}
|
|
scale={scale}
|
|
pageWidth={width}
|
|
pageHeight={height}
|
|
rotation={rotation || 0}
|
|
selectionOutlineColor="#007ACC"
|
|
/>
|
|
</div>
|
|
</PagePointerProvider>
|
|
</Rotate>
|
|
)}
|
|
/>
|
|
</Viewport>
|
|
</GlobalPointerProvider>
|
|
</EmbedPDF>
|
|
</div>
|
|
);
|
|
}
|