diff options
-rw-r--r-- | src/client/views/pdf/PDFViewer.scss | 6 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 72 | ||||
-rw-r--r-- | src/client/views/pdf/Page.tsx | 22 |
3 files changed, 96 insertions, 4 deletions
diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 831e6add1..57be04b93 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -34,4 +34,10 @@ position: absolute; top: 0; overflow: visible hidden; +} + +.pdfViewer-pinAnnotation { + background-color: red; + position: absolute; + border-radius: 100%; }
\ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 092765324..bc773ebef 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -9,9 +9,11 @@ import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; import { NumCast, Cast, BoolCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; -import { DocUtils } from "../../documents/Documents"; +import { DocUtils, Docs } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; import { SelectionManager } from "../../util/SelectionManager"; +import { List } from "../../../new_fields/List"; +import { DocumentContentsView } from "../nodes/DocumentContentsView"; interface IPDFViewerProps { url: string; @@ -56,6 +58,8 @@ interface IViewerProps { url: string; } +const PinRadius = 25; + /** * Handles rendering and virtualization of the pdf */ @@ -193,6 +197,7 @@ class Viewer extends React.Component<IViewerProps> { pageLoaded={this.pageLoaded} parent={this.props.parent} renderAnnotations={this.renderAnnotations} + makePin={this.createPinAnnotation} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(() => false); @@ -229,6 +234,7 @@ class Viewer extends React.Component<IViewerProps> { name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} parent={this.props.parent} + makePin={this.createPinAnnotation} renderAnnotations={this.renderAnnotations} {...this.props} /> ); @@ -242,6 +248,33 @@ class Viewer extends React.Component<IViewerProps> { return; } + createPinAnnotation = (x: number, y: number): void => { + let targetDoc = Docs.TextDocument({ title: "New Pin Annotation" }); + + let pinAnno = new Doc(); + pinAnno.x = x; + pinAnno.y = y; + pinAnno.width = pinAnno.height = PinRadius; + pinAnno.page = this.getIndex(y); + pinAnno.target = targetDoc; + pinAnno.type = AnnotationTypes.Pin; + // this._annotations.push(pinAnno); + let annotations = DocListCast(this.props.parent.Document.annotations); + if (annotations && annotations.length) { + annotations.push(pinAnno); + this.props.parent.Document.annotations = new List<Doc>(annotations); + } + else { + this.props.parent.Document.annotations = new List<Doc>([pinAnno]); + } + // let pinAnno = document.createElement("div"); + // pinAnno.className = "pdfViewer-pinAnnotation"; + // pinAnno.style.top = (y - (radius / 2)).toString(); + // pinAnno.style.left = (x - (radius / 2)).toString(); + // pinAnno.style.height = pinAnno.style.width = radius.toString(); + // if (this._annotationLayer.current) this._annotationLayer.current.append(pinAnno); + } + // get the page index that the vertical offset passed in is on getIndex = (vOffset: number) => { if (this._loaded) { @@ -295,6 +328,18 @@ class Viewer extends React.Component<IViewerProps> { return counter; } + renderAnnotation = (anno: Doc): JSX.Element => { + let type = NumCast(anno.type); + switch (type) { + case AnnotationTypes.Pin: + return <PinAnnotation document={anno} x={NumCast(anno.x)} y={NumCast(anno.y) + this.getPageHeight(NumCast(anno.page))} width={anno[WidthSym]()} height={anno[HeightSym]()} key={anno[Id]} />; + case AnnotationTypes.Region: + return <RegionAnnotation document={anno} x={NumCast(anno.x)} y={NumCast(anno.y) + this.getPageHeight(NumCast(anno.page))} width={anno[WidthSym]()} height={anno[HeightSym]()} key={anno[Id]} />; + default: + return <div></div>; + } + } + render() { return ( <div> @@ -303,7 +348,7 @@ class Viewer extends React.Component<IViewerProps> { </div> <div className="pdfViewer-annotationLayer" style={{ height: this.props.parent.Document.nativeHeight, width: `100%`, pointerEvents: "none" }}> <div className="pdfViewer-annotationLayer-subCont" style={{ transform: `translateY(${-this.scrollY}px)` }}> - {this._annotations.map(anno => <RegionAnnotation document={anno} x={NumCast(anno.x)} y={NumCast(anno.y) + this.getPageHeight(NumCast(anno.page))} width={anno[WidthSym]()} height={anno[HeightSym]()} key={anno[Id]} />)} + {this._annotations.map(anno => this.renderAnnotation(anno))} </div> </div> </div> @@ -311,6 +356,10 @@ class Viewer extends React.Component<IViewerProps> { } } +export enum AnnotationTypes { + Region, Pin +} + interface IAnnotationProps { x: number; y: number; @@ -319,6 +368,25 @@ interface IAnnotationProps { document: Doc; } +class PinAnnotation extends React.Component<IAnnotationProps> { + @observable private _backgroundColor: string = "red"; + + pointerDown = (e: React.PointerEvent) => { + + } + + render() { + let targetDoc = Cast(this.props.document.targetDoc, Doc, Docs.TextDocument({ title: "New Pin Annotation" })); + return ( + <div className="pdfViewer-pinAnnotation" onPointerDown={this.pointerDown} + style={{ top: this.props.y - PinRadius / 2, left: this.props.x - PinRadius / 2, width: PinRadius, height: PinRadius, pointerEvents: "all", backgroundColor: this._backgroundColor }}> + {/* <DocumentContentsView Document={targetDoc} + isSelected={} /> */} + </div> + ); + } +} + class RegionAnnotation extends React.Component<IAnnotationProps> { @observable private _backgroundColor: string = "red"; diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 5738889c4..fe7369aeb 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -13,6 +13,7 @@ import { emptyFunction } from "../../../Utils"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; +import { AnnotationTypes } from "./PDFViewer"; interface IPageProps { pdf: Opt<Pdfjs.PDFDocumentProxy>; @@ -22,6 +23,7 @@ interface IPageProps { pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; parent: PDFBox; renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; + makePin: (x: number, y: number) => void; } @observer @@ -142,6 +144,7 @@ export default class Page extends React.Component<IPageProps> { annoDoc.width = anno.offsetWidth; annoDoc.page = this.props.page; annoDoc.target = targetDoc; + annoDoc.type = AnnotationTypes.Region; annoDocs.push(annoDoc); DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); anno.remove(); @@ -169,7 +172,6 @@ export default class Page extends React.Component<IPageProps> { targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights let annotationDocs = this.makeAnnotationDocuments(targetDoc); - console.log(annotationDocs); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(...annotationDocs); @@ -364,6 +366,22 @@ export default class Page extends React.Component<IPageProps> { document.removeEventListener("pointerup", this.onSelectEnd); } + doubleClick = (e: React.MouseEvent) => { + let target: any = e.target; + // if double clicking text + if (target && target.parentElement === this._textLayer.current) { + // do something to select the paragraph ideally + } + + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + this.props.makePin(x, y); + } + } + renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { let annoBox = document.createElement("div"); if (anno) { @@ -379,7 +397,7 @@ export default class Page extends React.Component<IPageProps> { render() { return ( - <div onPointerDown={this.onPointerDown} className={this.props.name} style={{ "width": this._width, "height": this._height }}> + <div onPointerDown={this.onPointerDown} onDoubleClick={this.doubleClick} className={this.props.name} style={{ "width": this._width, "height": this._height }}> <div className="canvasContainer"> <canvas ref={this._canvas} /> </div> |