Text selection

This commit is contained in:
Reece Browne 2025-09-11 19:36:44 +01:00
parent 83a3222cf6
commit 687ab39286
4 changed files with 53 additions and 5 deletions

View File

@ -16,6 +16,7 @@
"@embedpdf/plugin-loader": "^1.1.1", "@embedpdf/plugin-loader": "^1.1.1",
"@embedpdf/plugin-render": "^1.1.1", "@embedpdf/plugin-render": "^1.1.1",
"@embedpdf/plugin-scroll": "^1.1.1", "@embedpdf/plugin-scroll": "^1.1.1",
"@embedpdf/plugin-selection": "^1.1.1",
"@embedpdf/plugin-viewport": "^1.1.1", "@embedpdf/plugin-viewport": "^1.1.1",
"@embedpdf/plugin-zoom": "^1.1.1", "@embedpdf/plugin-zoom": "^1.1.1",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
@ -708,6 +709,23 @@
"vue": ">=3.2.0" "vue": ">=3.2.0"
} }
}, },
"node_modules/@embedpdf/plugin-selection": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-selection/-/plugin-selection-1.1.1.tgz",
"integrity": "sha512-VHiLcXvM0a/bkWF2yAc7ltEIrwp1HHahtd5pMO0egODTqGKFzX6QhQ4PkL35KlVyDxMpowHj4pjgYYKPTqla0A==",
"dependencies": {
"@embedpdf/models": "1.1.1"
},
"peerDependencies": {
"@embedpdf/core": "1.1.1",
"@embedpdf/plugin-interaction-manager": "1.1.1",
"@embedpdf/plugin-viewport": "1.1.1",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
"vue": ">=3.2.0"
}
},
"node_modules/@embedpdf/plugin-viewport": { "node_modules/@embedpdf/plugin-viewport": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-viewport/-/plugin-viewport-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@embedpdf/plugin-viewport/-/plugin-viewport-1.1.1.tgz",

View File

@ -12,6 +12,7 @@
"@embedpdf/plugin-loader": "^1.1.1", "@embedpdf/plugin-loader": "^1.1.1",
"@embedpdf/plugin-render": "^1.1.1", "@embedpdf/plugin-render": "^1.1.1",
"@embedpdf/plugin-scroll": "^1.1.1", "@embedpdf/plugin-scroll": "^1.1.1",
"@embedpdf/plugin-selection": "^1.1.1",
"@embedpdf/plugin-viewport": "^1.1.1", "@embedpdf/plugin-viewport": "^1.1.1",
"@embedpdf/plugin-zoom": "^1.1.1", "@embedpdf/plugin-zoom": "^1.1.1",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",

View File

@ -9,9 +9,11 @@ import { Scroller, ScrollPluginPackage, ScrollStrategy } from '@embedpdf/plugin-
import { LoaderPluginPackage } from '@embedpdf/plugin-loader/react'; import { LoaderPluginPackage } from '@embedpdf/plugin-loader/react';
import { RenderLayer, RenderPluginPackage } from '@embedpdf/plugin-render/react'; import { RenderLayer, RenderPluginPackage } from '@embedpdf/plugin-render/react';
import { ZoomPluginPackage, ZoomMode } from '@embedpdf/plugin-zoom/react'; import { ZoomPluginPackage, ZoomMode } from '@embedpdf/plugin-zoom/react';
import { InteractionManagerPluginPackage } from '@embedpdf/plugin-interaction-manager/react'; import { InteractionManagerPluginPackage, PagePointerProvider } from '@embedpdf/plugin-interaction-manager/react';
import { SelectionLayer, SelectionPluginPackage } from '@embedpdf/plugin-selection/react';
import { ZoomControlsExporter } from './ZoomControlsExporter'; import { ZoomControlsExporter } from './ZoomControlsExporter';
import { ScrollControlsExporter } from './ScrollControlsExporter'; import { ScrollControlsExporter } from './ScrollControlsExporter';
import { SelectionControlsExporter } from './SelectionControlsExporter';
interface LocalEmbedPDFProps { interface LocalEmbedPDFProps {
file?: File | Blob; file?: File | Blob;
@ -59,9 +61,12 @@ export function LocalEmbedPDF({ file, url, colorScheme }: LocalEmbedPDFProps) {
}), }),
createPluginRegistration(RenderPluginPackage), createPluginRegistration(RenderPluginPackage),
// Register interaction manager (required for zoom features) // Register interaction manager (required for zoom and selection features)
createPluginRegistration(InteractionManagerPluginPackage), createPluginRegistration(InteractionManagerPluginPackage),
// Register selection plugin (depends on InteractionManager)
createPluginRegistration(SelectionPluginPackage),
// Register zoom plugin with configuration // Register zoom plugin with configuration
createPluginRegistration(ZoomPluginPackage, { createPluginRegistration(ZoomPluginPackage, {
defaultZoomLevel: ZoomMode.FitPage, defaultZoomLevel: ZoomMode.FitPage,
@ -136,6 +141,7 @@ export function LocalEmbedPDF({ file, url, colorScheme }: LocalEmbedPDFProps) {
<EmbedPDF engine={engine} plugins={plugins}> <EmbedPDF engine={engine} plugins={plugins}>
<ZoomControlsExporter /> <ZoomControlsExporter />
<ScrollControlsExporter /> <ScrollControlsExporter />
<SelectionControlsExporter />
<Viewport <Viewport
style={{ style={{
backgroundColor: actualColorScheme === 'dark' ? '#1a1b1e' : '#f1f3f5', backgroundColor: actualColorScheme === 'dark' ? '#1a1b1e' : '#f1f3f5',
@ -145,10 +151,11 @@ export function LocalEmbedPDF({ file, url, colorScheme }: LocalEmbedPDFProps) {
}} }}
> >
<Scroller <Scroller
renderPage={({ width, height, pageIndex, scale }: { width: number; height: number; pageIndex: number; scale: number }) => ( renderPage={({ width, height, pageIndex, scale, rotation }: { width: number; height: number; pageIndex: number; scale: number; rotation?: number }) => (
<div style={{ width, height }}> <PagePointerProvider width={width} height={height} pageIndex={pageIndex} scale={scale} rotation={rotation}>
<RenderLayer pageIndex={pageIndex} scale={scale} /> <RenderLayer pageIndex={pageIndex} scale={scale} />
</div> <SelectionLayer pageIndex={pageIndex} scale={scale} />
</PagePointerProvider>
)} )}
/> />
</Viewport> </Viewport>

View File

@ -0,0 +1,22 @@
import { useEffect } from 'react';
import { useSelectionCapability } from '@embedpdf/plugin-selection/react';
/**
* Component that runs inside EmbedPDF context and exports selection controls globally
*/
export function SelectionControlsExporter() {
const { provides: selection } = useSelectionCapability();
useEffect(() => {
if (selection) {
// Export selection controls to global window
(window as any).embedPdfSelection = {
copyToClipboard: () => selection.copyToClipboard(),
getSelectedText: () => selection.getSelectedText(),
getFormattedSelection: () => selection.getFormattedSelection(),
};
}
}, [selection]);
return null; // This component doesn't render anything
}