Created
November 30, 2024 01:13
-
-
Save glorat/6904419046ba3ab12b541bb67e2bd816 to your computer and use it in GitHub Desktop.
PDF Viewer selection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Ref, watch } from 'vue' | |
import type { VPVInstance } from '@vue-pdf-viewer/viewer' | |
/** | |
* Represents the data structure for text selection within a PDF viewer | |
*/ | |
export interface SelectionData { | |
/** The selected text content */ | |
text: string | |
/** Information about the selection range within the document */ | |
range: { | |
/** The offset from the start of the text node where the selection begins */ | |
startOffset: number | |
/** The offset from the start of the text node where the selection ends */ | |
endOffset: number | |
} | |
/** The position of the selection in the viewport */ | |
position: { | |
/** Distance from the top of the viewport in pixels */ | |
top: number | |
/** Distance from the left of the viewport in pixels */ | |
left: number | |
} | |
} | |
export function usePdfViewerSelection( | |
pdfViewer: Ref<VPVInstance | null | undefined>, | |
onSelection?: (data: SelectionData) => void | |
) { | |
const handleMouseUp = (el: HTMLElement) => { | |
const selection = window.getSelection() | |
const selectedText = selection?.toString() | |
const selectedRange = selection?.getRangeAt(0) | |
const textParentElement = selectedRange?.commonAncestorContainer?.parentElement | |
const startContainer = selectedRange?.startContainer | |
const endContainer = selectedRange?.endContainer | |
if (!selectedText || !selectedRange || !textParentElement) return | |
const rect = selectedRange.getBoundingClientRect() | |
const { top, left } = rect | |
const selectionData: SelectionData = { | |
text: selectedText, | |
range: { | |
startOffset: selectedRange.startOffset, | |
endOffset: selectedRange.endOffset, | |
}, | |
position: { | |
top, | |
left, | |
}, | |
} | |
onSelection?.(selectionData) | |
} | |
let cleanup: (() => void) | undefined | |
// Watch for pdfViewer changes and set up/clean up listeners | |
watch( | |
pdfViewer, | |
(newViewer, oldViewer) => { | |
// Clean up old listener if it exists | |
if (cleanup) { | |
cleanup() | |
cleanup = undefined | |
} | |
if (!newViewer) return | |
const el = (newViewer as any).$el | |
if (!el) return | |
const boundHandler = () => handleMouseUp(el) | |
el.addEventListener('mouseup', boundHandler) | |
cleanup = () => { | |
el.removeEventListener('mouseup', boundHandler) | |
} | |
}, | |
{ immediate: true } | |
) | |
// Return cleanup function for component unmount | |
return () => { | |
if (cleanup) { | |
cleanup() | |
cleanup = undefined | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment