Skip to content

Instantly share code, notes, and snippets.

@glorat
Created November 30, 2024 01:13
Show Gist options
  • Save glorat/6904419046ba3ab12b541bb67e2bd816 to your computer and use it in GitHub Desktop.
Save glorat/6904419046ba3ab12b541bb67e2bd816 to your computer and use it in GitHub Desktop.
PDF Viewer selection
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