From 5d799d1a26fa12d666b11b80776151edcbbd67f8 Mon Sep 17 00:00:00 2001 From: loudonclear Date: Fri, 7 Jun 2019 18:14:39 -0400 Subject: some comments, switching computers --- src/client/views/pdf/Page.tsx | 382 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 src/client/views/pdf/Page.tsx (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx new file mode 100644 index 000000000..7cd72b27f --- /dev/null +++ b/src/client/views/pdf/Page.tsx @@ -0,0 +1,382 @@ +import { observer } from "mobx-react"; +import React = require("react"); +import { observable, action, runInAction } from "mobx"; +import * as Pdfjs from "pdfjs-dist"; +import { Opt, Doc, FieldResult, Field } from "../../../new_fields/Doc"; +import "./PDFViewer.scss"; +import "pdfjs-dist/web/pdf_viewer.css"; +import { PDFBox } from "../nodes/PDFBox"; +import { DragManager } from "../../util/DragManager"; +import { Docs } from "../../documents/Documents"; +import { List } from "../../../new_fields/List"; +import { emptyFunction } from "../../../Utils"; +import { Cast, NumCast } from "../../../new_fields/Types"; +import { listSpec } from "../../../new_fields/Schema"; + +interface IPageProps { + pdf: Opt; + name: string; + numPages: number; + page: number; + pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; + parent: PDFBox; +} + +@observer +export default class Page extends React.Component { + @observable private _state: string = "N/A"; + @observable private _width: number = 0; + @observable private _height: number = 0; + @observable private _page: Opt; + @observable private _currPage: number = this.props.page + 1; + @observable private _marqueeX: number = 0; + @observable private _marqueeY: number = 0; + @observable private _marqueeWidth: number = 0; + @observable private _marqueeHeight: number = 0; + @observable private _rotate: string = ""; + @observable private _annotations: List = new List(); + + private _canvas: React.RefObject; + private _textLayer: React.RefObject; + private _annotationLayer: React.RefObject; + private _marquee: React.RefObject; + private _curly: React.RefObject; + private _currentAnnotations: HTMLDivElement[] = []; + private _marqueeing: boolean = false; + private _dragging: boolean = false; + + constructor(props: IPageProps) { + super(props); + this._canvas = React.createRef(); + this._textLayer = React.createRef(); + this._annotationLayer = React.createRef(); + this._marquee = React.createRef(); + this._curly = React.createRef(); + } + + componentDidMount() { + if (this.props.pdf) { + this.update(this.props.pdf); + } + } + + componentDidUpdate() { + if (this.props.pdf) { + this.update(this.props.pdf); + } + } + + private update = (pdf: Pdfjs.PDFDocumentProxy) => { + if (pdf) { + this.loadPage(pdf); + } + else { + this._state = "loading"; + } + } + + private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => { + if (this._state === "rendering" || this._page) return; + + pdf.getPage(this._currPage).then( + (page: Pdfjs.PDFPageProxy) => { + this._state = "rendering"; + this.renderPage(page); + } + ); + } + + @action + private renderPage = (page: Pdfjs.PDFPageProxy) => { + // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes + let scale = 2; + let viewport = page.getViewport(scale); + let canvas = this._canvas.current; + let textLayer = this._textLayer.current; + if (canvas && textLayer) { + let ctx = canvas.getContext("2d"); + canvas.width = viewport.width; + this._width = viewport.width; + canvas.height = viewport.height; + this._height = viewport.height; + this.props.pageLoaded(this._currPage, viewport); + if (ctx) { + // renders the page onto the canvas context + page.render({ canvasContext: ctx, viewport: viewport }); + // renders text onto the text container + page.getTextContent().then((res: Pdfjs.TextContent) => { + //@ts-ignore + Pdfjs.renderTextLayer({ + textContent: res, + container: textLayer, + viewport: viewport + }); + }); + + this._page = page; + } + } + } + + /** + * @param targetDoc: Document that annotations are linked to + * This method makes the list of current annotations into documents linked to + * the parameter passed in. + */ + makeAnnotationDocuments = (targetDoc: Doc): Doc[] => { + let annoDocs: Doc[] = []; + for (let anno of this._currentAnnotations) { + let annoDoc = new Doc(); + annoDoc.x = anno.offsetLeft; + annoDoc.y = anno.offsetTop; + annoDoc.height = anno.offsetHeight; + annoDoc.width = anno.offsetWidth; + annoDoc.target = targetDoc; + annoDocs.push(annoDoc); + anno.remove(); + } + this._currentAnnotations = []; + return annoDocs; + } + + /** + * This is temporary for creating annotations from highlights. It will + * start a drag event and create or put the necessary info into the drag event. + */ + @action + startDrag = (e: PointerEvent) => { + // the first 5 lines is a hack to prevent text selection while dragging + e.preventDefault(); + e.stopPropagation(); + if (this._dragging) { + return; + } + this._dragging = true; + let thisDoc = this.props.parent.Document; + // document that this annotation is linked to + let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); + targetDoc.targetPage = this.props.page; + // creates annotation documents for current highlights + let annotationDocs = this.makeAnnotationDocuments(targetDoc); + let targetAnnotations = Cast(targetDoc.annotations, listSpec(Doc)); + if (targetAnnotations) { + targetAnnotations.push(...annotationDocs); + targetDoc.annotations = targetAnnotations; + } + // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf + let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); + if (thisAnnotations) { + thisAnnotations.push(...annotationDocs); + this.props.parent.Document.annotations = thisAnnotations; + let temp = new List(thisAnnotations); + temp.push(...this._annotations); + this._annotations = temp; + } + // create dragData and star tdrag + let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); + if (this._textLayer.current) { + DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + } + } + + // cleans up events and boolean + endDrag = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.endDrag); + this._dragging = false; + e.stopPropagation(); + } + + @action + onPointerDown = (e: React.PointerEvent) => { + // if alt+left click, drag and annotate + if (e.altKey && e.button === 0) { + e.stopPropagation(); + + document.removeEventListener("pointermove", this.startDrag); + document.addEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.endDrag); + document.addEventListener("pointerup", this.endDrag); + } + else if (e.button === 0) { + let target: any = e.target; + if (target && target.parentElement === this._textLayer.current) { + e.stopPropagation(); + } + else { + e.stopPropagation(); + // set marquee x and y positions to the spatially transformed position + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + } + this._marqueeing = true; + if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; + } + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + if (!e.ctrlKey) { + for (let anno of this._currentAnnotations) { + anno.remove(); + } + this._currentAnnotations = []; + } + } + } + + @action + onPointerMove = (e: PointerEvent) => { + let target: any = e.target; + if (this._marqueeing) { + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; + this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; + if (this._marquee.current && this._curly.current) { + if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { + this._marquee.current.style.background = "red"; + this._curly.current.style.opacity = "0"; + } + else { + this._marquee.current.style.background = "transparent"; + this._curly.current.style.opacity = "1"; + } + + let ratio = this._marqueeWidth / this._marqueeHeight; + if (ratio > 1.5) { + // vertical + this._rotate = "rotate(90deg) scale(1, 2)"; + } + else if (ratio < 0.5) { + // horizontal + this._rotate = "scale(2, 1)"; + } + else { + // diagonal + this._rotate = "rotate(45deg) scale(1.5, 1.5)"; + } + } + } + e.stopPropagation(); + e.preventDefault(); + } + else if (target && target.parentElement === this._textLayer.current) { + e.stopPropagation(); + } + } + + startAnnotation = () => { + console.log("drag starting"); + } + + pointerDownCancel = (e: PointerEvent) => { + e.stopPropagation(); + } + + @action + onPointerUp = () => { + if (this._marqueeing) { + this._marqueeing = false; + if (this._marquee.current) { + let copy = document.createElement("div"); + copy.style.left = this._marquee.current.style.left; + copy.style.top = this._marquee.current.style.top; + copy.style.width = this._marquee.current.style.width; + copy.style.height = this._marquee.current.style.height; + copy.style.opacity = this._marquee.current.style.opacity; + copy.className = this._marquee.current.className; + if (this._annotationLayer.current) { + this._annotationLayer.current.append(copy); + this._currentAnnotations.push(copy); + } + this._marquee.current.style.opacity = "0"; + } + + this._marqueeHeight = this._marqueeWidth = 0; + } + else { + let sel = window.getSelection(); + // if selecting over a range of things + if (sel && sel.type === "Range") { + let clientRects = sel.getRangeAt(0).getClientRects(); + if (this._textLayer.current) { + let boundingRect = this._textLayer.current.getBoundingClientRect(); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + annoBox.ondragstart = this.startAnnotation; + annoBox.onpointerdown = this.pointerDownCancel; + if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); + this._currentAnnotations.push(annoBox); + } + } + } + if (sel.empty) { // Chrome + sel.empty(); + } else if (sel.removeAllRanges) { // Firefox + sel.removeAllRanges(); + } + } + } + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } + + renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { + let annoBox = document.createElement("div"); + if (anno) { + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = NumCast(anno.x).toString(); + annoBox.style.left = NumCast(anno.y).toString(); + annoBox.style.width = NumCast(anno.width).toString(); + annoBox.style.height = NumCast(anno.height).toString() + annoBox.onpointerdown = this.pointerDownCancel; + } + return annoBox; + } + + annotationPointerDown = () => { + console.log("annotation"); + } + + // imgVisible = () => { + // return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" } + // } + + render() { + let annotations = this._annotations; + return ( +
+
+ +
+
+
+ +
+ {annotations.map(anno => this.renderAnnotation(anno instanceof Doc ? anno : undefined))} +
+
+
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 92cdb8dae6162b1aa7e41f3392c670e7c5d377eb Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 7 Jun 2019 19:02:05 -0400 Subject: cleanup --- src/client/views/pdf/Page.tsx | 116 +++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 53 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 7cd72b27f..940f31f41 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -211,19 +211,19 @@ export default class Page extends React.Component { else { e.stopPropagation(); // set marquee x and y positions to the spatially transformed position - let current = this._textLayer.current; - if (current) { - let boundingRect = current.getBoundingClientRect(); - this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); - this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); - } + let current = this._textLayer.current; + if (current) { + let boundingRect = current.getBoundingClientRect(); + this._marqueeX = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + this._marqueeY = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + } this._marqueeing = true; if (this._marquee.current) this._marquee.current.style.opacity = "0.2"; } - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); + document.removeEventListener("pointermove", this.onSelectStart); + document.addEventListener("pointermove", this.onSelectStart); + document.removeEventListener("pointerup", this.onSelectEnd); + document.addEventListener("pointerup", this.onSelectEnd); if (!e.ctrlKey) { for (let anno of this._currentAnnotations) { anno.remove(); @@ -234,37 +234,20 @@ export default class Page extends React.Component { } @action - onPointerMove = (e: PointerEvent) => { + onSelectStart = (e: PointerEvent) => { let target: any = e.target; if (this._marqueeing) { let current = this._textLayer.current; if (current) { + // transform positions and find the width and height to set the marquee to let boundingRect = current.getBoundingClientRect(); this._marqueeWidth = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width) - this._marqueeX; this._marqueeHeight = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height) - this._marqueeY; + let { background, opacity, transform: transform } = this.getCurlyTransform(); if (this._marquee.current && this._curly.current) { - if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { - this._marquee.current.style.background = "red"; - this._curly.current.style.opacity = "0"; - } - else { - this._marquee.current.style.background = "transparent"; - this._curly.current.style.opacity = "1"; - } - - let ratio = this._marqueeWidth / this._marqueeHeight; - if (ratio > 1.5) { - // vertical - this._rotate = "rotate(90deg) scale(1, 2)"; - } - else if (ratio < 0.5) { - // horizontal - this._rotate = "scale(2, 1)"; - } - else { - // diagonal - this._rotate = "rotate(45deg) scale(1.5, 1.5)"; - } + this._marquee.current.style.background = background; + this._curly.current.style.opacity = opacity; + this._rotate = transform; } } e.stopPropagation(); @@ -275,25 +258,62 @@ export default class Page extends React.Component { } } - startAnnotation = () => { - console.log("drag starting"); - } + getCurlyTransform = (): { background: string, opacity: string, transform: string } => { + let background = "", opacity = "", transform = ""; + if (this._marquee.current && this._curly.current) { + if (this._marqueeWidth > 100 && this._marqueeHeight > 100) { + background = "red"; + opacity = "0"; + } + else { + background = "transparent"; + opacity = "1"; + } - pointerDownCancel = (e: PointerEvent) => { - e.stopPropagation(); + // split up for simplicity, could be done in a nested ternary. please do not. -syip2 + let ratio = this._marqueeWidth / this._marqueeHeight; + if (ratio > 1.5) { + // vertical + transform = "rotate(90deg) scale(1, 2)"; + } + else if (ratio < 0.5) { + // horizontal + transform = "scale(2, 1)"; + } + else { + // diagonal + transform = "rotate(45deg) scale(1.5, 1.5)"; + } + } + return { background: background, opacity: opacity, transform: transform }; } @action - onPointerUp = () => { + onSelectEnd = () => { if (this._marqueeing) { this._marqueeing = false; if (this._marquee.current) { let copy = document.createElement("div"); + // make a copy of the marquee copy.style.left = this._marquee.current.style.left; copy.style.top = this._marquee.current.style.top; copy.style.width = this._marquee.current.style.width; copy.style.height = this._marquee.current.style.height; - copy.style.opacity = this._marquee.current.style.opacity; + + // apply the appropriate background, opacity, and transform + let { background, opacity, transform } = this.getCurlyTransform(); + copy.style.background = background; + // if curly bracing, add a curly brace + if (opacity === "1" && this._curly.current) { + copy.style.opacity = opacity; + let img = this._curly.current.cloneNode(); + (img as any).style.opacity = opacity; + (img as any).style.transform = transform; + copy.appendChild(img); + } + else { + copy.style.opacity = this._marquee.current.style.opacity; + } copy.className = this._marquee.current.className; if (this._annotationLayer.current) { this._annotationLayer.current.append(copy); @@ -321,13 +341,12 @@ export default class Page extends React.Component { annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - annoBox.ondragstart = this.startAnnotation; - annoBox.onpointerdown = this.pointerDownCancel; if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); this._currentAnnotations.push(annoBox); } } } + // clear selection if (sel.empty) { // Chrome sel.empty(); } else if (sel.removeAllRanges) { // Firefox @@ -335,8 +354,8 @@ export default class Page extends React.Component { } } } - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); + document.removeEventListener("pointermove", this.onSelectStart); + document.removeEventListener("pointerup", this.onSelectEnd); } renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { @@ -348,19 +367,10 @@ export default class Page extends React.Component { annoBox.style.left = NumCast(anno.y).toString(); annoBox.style.width = NumCast(anno.width).toString(); annoBox.style.height = NumCast(anno.height).toString() - annoBox.onpointerdown = this.pointerDownCancel; } return annoBox; } - annotationPointerDown = () => { - console.log("annotation"); - } - - // imgVisible = () => { - // return this._marqueeWidth < 100 && this._marqueeHeight < 100 ? { opacity: "1" } : { opacity: "0" } - // } - render() { let annotations = this._annotations; return ( -- cgit v1.2.3-70-g09d2 From 81a502f81073e7fb15c75563cd557a6a2c5a31cd Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 10:58:00 -0400 Subject: annotation loading maybe idk not workign rn --- src/client/views/pdf/PDFViewer.tsx | 2 +- src/client/views/pdf/Page.tsx | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0711ead23..0a6886878 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -184,7 +184,7 @@ class Viewer extends React.Component { // render pages for any indices that don't already have pages (force rerender will make these render regardless) for (let i = startIndex; i <= endIndex; i++) { - if (!this._isPage[i] || forceRender) { + if (!this._isPage[i] || (this._isPage[i] && forceRender)) { this._visibleElements[i] = ( { targetAnnotations.push(...annotationDocs); targetDoc.annotations = targetAnnotations; } + else { + targetDoc.annotations = new List(annotationDocs); + } // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (thisAnnotations) { -- cgit v1.2.3-70-g09d2 From bcd6960fe91008bf31d364c48b5c8765eec28701 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 11:33:36 -0400 Subject: oops --- src/client/views/pdf/Page.tsx | 86 ++++++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 25 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 0d86f22c1..7125c820c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -1,8 +1,8 @@ import { observer } from "mobx-react"; import React = require("react"); -import { observable, action, runInAction } from "mobx"; +import { observable, action, runInAction, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt, Doc, FieldResult, Field } from "../../../new_fields/Doc"; +import { Opt, Doc, FieldResult, Field, DocListCast, WidthSym, HeightSym } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; @@ -12,6 +12,7 @@ import { List } from "../../../new_fields/List"; import { emptyFunction } from "../../../Utils"; import { Cast, NumCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; +import { menuBar } from "prosemirror-menu"; interface IPageProps { pdf: Opt; @@ -34,7 +35,7 @@ export default class Page extends React.Component { @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; @observable private _rotate: string = ""; - @observable private _annotations: List = new List(); + @observable private _annotations: Doc[] = []; private _canvas: React.RefObject; private _textLayer: React.RefObject; @@ -44,6 +45,7 @@ export default class Page extends React.Component { private _currentAnnotations: HTMLDivElement[] = []; private _marqueeing: boolean = false; private _dragging: boolean = false; + private _reactionDisposer?: IReactionDisposer; constructor(props: IPageProps) { super(props); @@ -54,19 +56,38 @@ export default class Page extends React.Component { this._curly = React.createRef(); } - componentDidMount() { + componentDidMount = (): void => { if (this.props.pdf) { this.update(this.props.pdf); + + if (this.props.parent.Document) { + this._reactionDisposer = reaction( + () => DocListCast(this.props.parent.Document.annotations), + () => { + let annotations = DocListCast(this.props.parent.Document.annotations); + if (annotations && annotations.length) { + this.renderAnnotations(annotations, true); + } + }, + { fireImmediately: true } + ); + } + } + } + + componentWillUnmount = (): void => { + if (this._reactionDisposer) { + this._reactionDisposer(); } } - componentDidUpdate() { + componentDidUpdate = (): void => { if (this.props.pdf) { this.update(this.props.pdf); } } - private update = (pdf: Pdfjs.PDFDocumentProxy) => { + private update = (pdf: Pdfjs.PDFDocumentProxy): void => { if (pdf) { this.loadPage(pdf); } @@ -75,11 +96,11 @@ export default class Page extends React.Component { } } - private loadPage = (pdf: Pdfjs.PDFDocumentProxy) => { + private loadPage = (pdf: Pdfjs.PDFDocumentProxy): void => { if (this._state === "rendering" || this._page) return; pdf.getPage(this._currPage).then( - (page: Pdfjs.PDFPageProxy) => { + (page: Pdfjs.PDFPageProxy): void => { this._state = "rendering"; this.renderPage(page); } @@ -87,7 +108,18 @@ export default class Page extends React.Component { } @action - private renderPage = (page: Pdfjs.PDFPageProxy) => { + private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { + if (removeOldAnnotations) { + this._annotations = annotations; + } + else { + this._annotations.push(...annotations); + this._annotations = new Array(...this._annotations); + } + } + + @action + private renderPage = (page: Pdfjs.PDFPageProxy): void => { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes let scale = 2; let viewport = page.getViewport(scale); @@ -104,7 +136,7 @@ export default class Page extends React.Component { // renders the page onto the canvas context page.render({ canvasContext: ctx, viewport: viewport }); // renders text onto the text container - page.getTextContent().then((res: Pdfjs.TextContent) => { + page.getTextContent().then((res: Pdfjs.TextContent): void => { //@ts-ignore Pdfjs.renderTextLayer({ textContent: res, @@ -144,7 +176,7 @@ export default class Page extends React.Component { * start a drag event and create or put the necessary info into the drag event. */ @action - startDrag = (e: PointerEvent) => { + startDrag = (e: PointerEvent): void => { // the first 5 lines is a hack to prevent text selection while dragging e.preventDefault(); e.stopPropagation(); @@ -166,15 +198,6 @@ export default class Page extends React.Component { else { targetDoc.annotations = new List(annotationDocs); } - // temporary code (currently broken ? 6/7/19) to get annotations to rerender on pdf - let thisAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); - if (thisAnnotations) { - thisAnnotations.push(...annotationDocs); - this.props.parent.Document.annotations = thisAnnotations; - let temp = new List(thisAnnotations); - temp.push(...this._annotations); - this._annotations = temp; - } // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); if (this._textLayer.current) { @@ -196,7 +219,7 @@ export default class Page extends React.Component { } @action - onPointerDown = (e: React.PointerEvent) => { + onPointerDown = (e: React.PointerEvent): void => { // if alt+left click, drag and annotate if (e.altKey && e.button === 0) { e.stopPropagation(); @@ -237,7 +260,7 @@ export default class Page extends React.Component { } @action - onSelectStart = (e: PointerEvent) => { + onSelectStart = (e: PointerEvent): void => { let target: any = e.target; if (this._marqueeing) { let current = this._textLayer.current; @@ -292,7 +315,7 @@ export default class Page extends React.Component { } @action - onSelectEnd = () => { + onSelectEnd = (): void => { if (this._marqueeing) { this._marqueeing = false; if (this._marquee.current) { @@ -375,7 +398,6 @@ export default class Page extends React.Component { } render() { - let annotations = this._annotations; return (
@@ -386,10 +408,24 @@ export default class Page extends React.Component { style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, background: "transparent" }}>
- {annotations.map(anno => this.renderAnnotation(anno instanceof Doc ? anno : undefined))} + {this._annotations.map(anno => )}
); } +} + +interface IAnnotation { + x: number; + y: number; + width: number; + height: number; +} +class RegionAnnotation extends React.Component { + render() { + return ( +
+ ); + } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 2ecd0c3486ec37d1061468cda12aa64e38d0d3d5 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 14:07:36 -0400 Subject: annotation improvements --- src/client/documents/Documents.ts | 8 +-- src/client/views/collections/CollectionSubView.tsx | 2 + .../collectionFreeForm/CollectionFreeFormView.tsx | 64 ++++++++++++++-------- src/client/views/pdf/Page.tsx | 17 ++++-- 4 files changed, 57 insertions(+), 34 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 973c90ff4..fa6d19a5e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -69,15 +69,15 @@ export interface DocumentOptions { const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; export namespace DocUtils { - export function MakeLink(source: Doc, target: Doc) { + export function MakeLink(source: Doc, target: Doc, title: string = "-link name-", description: string = "", tags: string = "Default") { let protoSrc = source.proto ? source.proto : source; let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; - linkDoc.proto!.title = "-link name-"; - linkDoc.proto!.linkDescription = ""; - linkDoc.proto!.linkTags = "Default"; + linkDoc.proto!.title = title; + linkDoc.proto!.linkDescription = description; + linkDoc.proto!.linkTags = tags; linkDoc.proto!.linkedTo = target; linkDoc.proto!.linkedToPage = target.curPage; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 1ced6a426..d25d07c3d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -110,6 +110,8 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { else if (de.data instanceof DragManager.AnnotationDragData) { console.log("dropped!"); console.log(de.data); + // de.data.dropDocument.x = de.x; + // de.data.dropDocument.y = de.y; return this.props.addDocument(de.data.dropDocument); } return false; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9d19df540..d8e197cbe 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -81,31 +81,47 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { - if (super.drop(e, de) && de.data instanceof DragManager.DocumentDragData) { - if (de.data.droppedDocuments.length) { - let dragDoc = de.data.droppedDocuments[0]; - let zoom = NumCast(dragDoc.zoomBasis, 1); - let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); - let x = xp - de.data.xOffset / zoom; - let y = yp - de.data.yOffset / zoom; - let dropX = NumCast(de.data.droppedDocuments[0].x); - let dropY = NumCast(de.data.droppedDocuments[0].y); - de.data.droppedDocuments.forEach(d => { - d.x = x + NumCast(d.x) - dropX; - d.y = y + NumCast(d.y) - dropY; - if (!NumCast(d.width)) { - d.width = 300; - } - if (!NumCast(d.height)) { - let nw = NumCast(d.nativeWidth); - let nh = NumCast(d.nativeHeight); - d.height = nw && nh ? nh / nw * NumCast(d.width) : 300; - } - this.bringToFront(d); - }); - SelectionManager.ReselectAll(); + if (super.drop(e, de)) { + if (de.data instanceof DragManager.DocumentDragData) { + if (de.data.droppedDocuments.length) { + let dragDoc = de.data.droppedDocuments[0]; + let zoom = NumCast(dragDoc.zoomBasis, 1); + let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); + let x = xp - de.data.xOffset / zoom; + let y = yp - de.data.yOffset / zoom; + let dropX = NumCast(de.data.droppedDocuments[0].x); + let dropY = NumCast(de.data.droppedDocuments[0].y); + de.data.droppedDocuments.forEach(d => { + d.x = x + NumCast(d.x) - dropX; + d.y = y + NumCast(d.y) - dropY; + if (!NumCast(d.width)) { + d.width = 300; + } + if (!NumCast(d.height)) { + let nw = NumCast(d.nativeWidth); + let nh = NumCast(d.nativeHeight); + d.height = nw && nh ? nh / nw * NumCast(d.width) : 300; + } + this.bringToFront(d); + }); + SelectionManager.ReselectAll(); + } + return true; + } + else if (de.data instanceof DragManager.AnnotationDragData) { + if (de.data.dropDocument) { + let dragDoc = de.data.dropDocument; + let zoom = NumCast(dragDoc.zoomBasis, 1); + let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); + let x = xp - de.data.xOffset / zoom; + let y = yp - de.data.yOffset / zoom; + let dropX = NumCast(de.data.dropDocument.x); + let dropY = NumCast(de.data.dropDocument.y); + dragDoc.x = x + NumCast(dragDoc.x) - dropX; + dragDoc.y = y + NumCast(dragDoc.y) - dropY; + this.bringToFront(dragDoc); + } } - return true; } return false; } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 7125c820c..5269f448b 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -7,10 +7,10 @@ import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import { DragManager } from "../../util/DragManager"; -import { Docs } from "../../documents/Documents"; +import { Docs, DocUtils } from "../../documents/Documents"; import { List } from "../../../new_fields/List"; import { emptyFunction } from "../../../Utils"; -import { Cast, NumCast } from "../../../new_fields/Types"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; @@ -66,6 +66,7 @@ export default class Page extends React.Component { () => { let annotations = DocListCast(this.props.parent.Document.annotations); if (annotations && annotations.length) { + annotations = annotations.filter(anno => NumCast(anno.page) === this.props.page); this.renderAnnotations(annotations, true); } }, @@ -163,8 +164,10 @@ export default class Page extends React.Component { annoDoc.y = anno.offsetTop; annoDoc.height = anno.offsetHeight; annoDoc.width = anno.offsetWidth; + annoDoc.page = this.props.page; annoDoc.target = targetDoc; annoDocs.push(annoDoc); + DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); anno.remove(); } this._currentAnnotations = []; @@ -190,13 +193,14 @@ export default class Page extends React.Component { targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights let annotationDocs = this.makeAnnotationDocuments(targetDoc); - let targetAnnotations = Cast(targetDoc.annotations, listSpec(Doc)); + console.log(annotationDocs); + let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(...annotationDocs); - targetDoc.annotations = targetAnnotations; + this.props.parent.Document.annotations = new List(targetAnnotations); } else { - targetDoc.annotations = new List(annotationDocs); + this.props.parent.Document.annotations = new List(annotationDocs); } // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); @@ -422,10 +426,11 @@ interface IAnnotation { width: number; height: number; } + class RegionAnnotation extends React.Component { render() { return ( -
+
); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 82a6a258422666fbe3db2a2cf1934f8673912b67 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 10 Jun 2019 15:49:34 -0400 Subject: annotation updates blehh --- src/client/views/pdf/PDFViewer.scss | 6 +++ src/client/views/pdf/PDFViewer.tsx | 76 +++++++++++++++++++++++++++++++++++-- src/client/views/pdf/Page.tsx | 44 +-------------------- 3 files changed, 81 insertions(+), 45 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index cb1aef410..831e6add1 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -28,4 +28,10 @@ position: absolute; background-color: red; opacity: 0.1; +} + +.pdfViewer-annotationLayer { + position: absolute; + top: 0; + overflow: visible hidden; } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0a6886878..1152587a4 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,11 +2,12 @@ import { observer } from "mobx-react"; import React = require("react"); import { observable, action, runInAction, computed, IReactionDisposer, reaction } from "mobx"; import * as Pdfjs from "pdfjs-dist"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt, HeightSym, WidthSym, Doc, DocListCast } from "../../../new_fields/Doc"; import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; +import { NumCast } from "../../../new_fields/Types"; interface IPDFViewerProps { url: string; @@ -65,9 +66,11 @@ class Viewer extends React.Component { @observable private _endIndex: number = 1; @observable private _loaded: boolean = false; @observable private _pdf: Opt; + @observable private _annotations: Doc[] = []; private _pageBuffer: number = 1; private _reactionDisposer?: IReactionDisposer; + private _annotationReactionDisposer?: IReactionDisposer; componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); @@ -88,6 +91,19 @@ class Viewer extends React.Component { { fireImmediately: true } ); + if (this.props.parent.Document) { + this._annotationReactionDisposer = reaction( + () => DocListCast(this.props.parent.Document.annotations), + () => { + let annotations = DocListCast(this.props.parent.Document.annotations); + if (annotations && annotations.length) { + this.renderAnnotations(annotations, true); + } + }, + { fireImmediately: true } + ); + } + // On load, render pdf setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000); } @@ -96,6 +112,9 @@ class Viewer extends React.Component { if (this._reactionDisposer) { this._reactionDisposer(); } + if (this._annotationReactionDisposer) { + this._annotationReactionDisposer(); + } } @action @@ -135,6 +154,17 @@ class Viewer extends React.Component { } } + @action + private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { + if (removeOldAnnotations) { + this._annotations = annotations; + } + else { + this._annotations.push(...annotations); + this._annotations = new Array(...this._annotations); + } + } + /** * @param startIndex: where to start rendering pages * @param endIndex: where to end rendering pages @@ -158,6 +188,7 @@ class Viewer extends React.Component { name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} parent={this.props.parent} + renderAnnotations={this.renderAnnotations} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(() => false); @@ -194,6 +225,7 @@ class Viewer extends React.Component { name={`${this.props.pdf ? this.props.pdf.fingerprint + `-page${i + 1}` : "undefined"}`} pageLoaded={this.pageLoaded} parent={this.props.parent} + renderAnnotations={this.renderAnnotations} {...this.props} /> ); this._isPage[i] = true; @@ -247,11 +279,49 @@ class Viewer extends React.Component { } } + getPageHeight = (index: number): number => { + let counter = 0; + if (this.props.pdf && index < this.props.pdf.numPages) { + for (let i = 0; i < index; i++) { + if (this._pageSizes[i]) { + counter += this._pageSizes[i].height; + } + } + } + return counter; + } + render() { return ( -
- {this._visibleElements} +
+
+ {this._visibleElements} +
+
+
+ {this._annotations.map(anno => )} +
+
); } } + +interface IAnnotation { + x: number; + y: number; + width: number; + height: number; +} + +class RegionAnnotation extends React.Component { + onPointerDown = (e: React.PointerEvent) => { + console.log("clicked!"); + } + + render() { + return ( +
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 5269f448b..5738889c4 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -21,6 +21,7 @@ interface IPageProps { page: number; pageLoaded: (index: number, page: Pdfjs.PDFPageViewport) => void; parent: PDFBox; + renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; } @observer @@ -35,7 +36,6 @@ export default class Page extends React.Component { @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; @observable private _rotate: string = ""; - @observable private _annotations: Doc[] = []; private _canvas: React.RefObject; private _textLayer: React.RefObject; @@ -60,19 +60,6 @@ export default class Page extends React.Component { if (this.props.pdf) { this.update(this.props.pdf); - if (this.props.parent.Document) { - this._reactionDisposer = reaction( - () => DocListCast(this.props.parent.Document.annotations), - () => { - let annotations = DocListCast(this.props.parent.Document.annotations); - if (annotations && annotations.length) { - annotations = annotations.filter(anno => NumCast(anno.page) === this.props.page); - this.renderAnnotations(annotations, true); - } - }, - { fireImmediately: true } - ); - } } } @@ -108,17 +95,6 @@ export default class Page extends React.Component { ); } - @action - private renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { - if (removeOldAnnotations) { - this._annotations = annotations; - } - else { - this._annotations.push(...annotations); - this._annotations = new Array(...this._annotations); - } - } - @action private renderPage = (page: Pdfjs.PDFPageProxy): void => { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes @@ -407,30 +383,14 @@ export default class Page extends React.Component {
-
+
- {this._annotations.map(anno => )}
); } } - -interface IAnnotation { - x: number; - y: number; - width: number; - height: number; -} - -class RegionAnnotation extends React.Component { - render() { - return ( -
- ); - } -} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 64f6a0d54656ded133af113746003d61eaa1d651 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 11 Jun 2019 15:04:47 -0400 Subject: pin annotations base --- src/client/views/pdf/PDFViewer.scss | 6 ++++ src/client/views/pdf/PDFViewer.tsx | 72 +++++++++++++++++++++++++++++++++++-- src/client/views/pdf/Page.tsx | 22 ++++++++++-- 3 files changed, 96 insertions(+), 4 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') 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 { 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 { 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 { 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(annotations); + } + else { + this.props.parent.Document.annotations = new List([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 { return counter; } + renderAnnotation = (anno: Doc): JSX.Element => { + let type = NumCast(anno.type); + switch (type) { + case AnnotationTypes.Pin: + return ; + case AnnotationTypes.Region: + return ; + default: + return
; + } + } + render() { return (
@@ -303,7 +348,7 @@ class Viewer extends React.Component {
- {this._annotations.map(anno => )} + {this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -311,6 +356,10 @@ class Viewer extends React.Component { } } +export enum AnnotationTypes { + Region, Pin +} + interface IAnnotationProps { x: number; y: number; @@ -319,6 +368,25 @@ interface IAnnotationProps { document: Doc; } +class PinAnnotation extends React.Component { + @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 ( +
+ {/* */} +
+ ); + } +} + class RegionAnnotation extends React.Component { @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; @@ -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 { 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 { 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 { 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 { render() { return ( -
+
-- cgit v1.2.3-70-g09d2 From b96281d18a9c4ca0ea7f8360d7f69d12c325fada Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 11 Jun 2019 19:02:00 -0400 Subject: pin annotations --- src/client/views/nodes/PDFBox.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 136 +++++++++++++++++++++++++++++-------- src/client/views/pdf/Page.tsx | 4 +- 3 files changed, 110 insertions(+), 31 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index de75c67cf..5b118185b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -50,6 +50,7 @@ export class PDFBox extends DocComponent(PdfDocumen trace(); // uses mozilla pdf as default const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); + console.log(pdfUrl); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
{ @action componentDidMount() { const pdfUrl = this.props.url; + console.log("pdf starting to load") let promise = Pdfjs.getDocument(pdfUrl).promise; promise.then((pdf: Pdfjs.PDFDocumentProxy) => { - runInAction(() => this._pdf = pdf); + runInAction(() => { + console.log("pdf url received"); + this._pdf = pdf; + }); }); } @@ -77,8 +85,16 @@ class Viewer extends React.Component { @observable private _annotations: Doc[] = []; private _pageBuffer: number = 1; + private _annotationLayer: React.RefObject; private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; + private _pagesLoaded: number = 0; + + constructor(props: IViewerProps) { + super(props); + + this._annotationLayer = React.createRef(); + } componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); @@ -112,8 +128,9 @@ class Viewer extends React.Component { ); } - // On load, render pdf - setTimeout(() => this.renderPages(this.startIndex, this.endIndex, true), 1000); + setTimeout(() => { + this.renderPages(this.startIndex, this.endIndex, true); + }, 1000); } componentWillUnmount = () => { @@ -150,7 +167,7 @@ class Viewer extends React.Component { } @computed get endIndex(): number { - let width = this._pageSizes.map(i => i.width); + let width = this._pageSizes.map(i => i ? i.width : 0); return Math.min(this.props.pdf ? this.props.pdf.numPages - 1 : 0, this.getIndex(this.scrollY + Math.max(...width)) + this._pageBuffer); } @@ -185,6 +202,10 @@ class Viewer extends React.Component { return; } + if (this._pageSizes.length !== numPages) { + this._pageSizes = new Array(numPages).map(i => ({ width: 0, height: 0 })); + } + // this is only for an initial render to get all of the pages rendered if (this._visibleElements.length !== numPages) { let divs = Array.from(Array(numPages).keys()).map(i => ( @@ -248,14 +269,14 @@ class Viewer extends React.Component { return; } - createPinAnnotation = (x: number, y: number): void => { - let targetDoc = Docs.TextDocument({ title: "New Pin Annotation" }); + createPinAnnotation = (x: number, y: number, page: number): void => { + let targetDoc = Docs.TextDocument({ width: 100, height: 50, 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.page = page; pinAnno.target = targetDoc; pinAnno.type = AnnotationTypes.Pin; // this._annotations.push(pinAnno); @@ -301,18 +322,18 @@ class Viewer extends React.Component { } let numPages = this.props.pdf ? this.props.pdf.numPages : 0; this.props.loaded(page.width, page.height); - if (index > this._pageSizes.length) { - this._pageSizes.push({ width: page.width, height: page.height }); - } - else { - this._pageSizes[index - 1] = { width: page.width, height: page.height }; - } - if (index === numPages) { + this._pageSizes[index - 1] = { width: page.width, height: page.height }; + this._pagesLoaded++; + if (this._pagesLoaded === numPages) { this._loaded = true; let divs = Array.from(Array(numPages).keys()).map(i => (
)); this._visibleElements = new Array(...divs); + // On load, render pdf + // setTimeout(() => { + this.renderPages(this.startIndex, this.endIndex, true); + // }, 1000); } } @@ -332,21 +353,38 @@ class Viewer extends React.Component { let type = NumCast(anno.type); switch (type) { case AnnotationTypes.Pin: - return ; + return ; case AnnotationTypes.Region: - return ; + return ; default: return
; } } + onDrop = (e: React.DragEvent) => { + console.log("Dropped!"); + } + + // ScreenToLocalTransform = (): Transform => { + // if (this._annotationLayer.current) { + // let boundingRect = this._annotationLayer.current.getBoundingClientRect(); + // let x = boundingRect.left; + // let y = boundingRect.top; + // let scale = NumCast(this.props.parent.Document.nativeWidth) / boundingRect.width; + // let t = new Transform(x, y, scale); + // return t; + // } + // return Transform.Identity(); + // } + render() { + trace(); return ( -
+
{this._visibleElements}
-
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -365,25 +403,65 @@ interface IAnnotationProps { y: number; width: number; height: number; + parent: Viewer; document: Doc; } +@observer class PinAnnotation extends React.Component { - @observable private _backgroundColor: string = "red"; + @observable private _backgroundColor: string = "green"; + @observable private _display: string = "initial"; - pointerDown = (e: React.PointerEvent) => { + private _selected: boolean = true; + @action + pointerDown = (e: React.PointerEvent) => { + if (this._selected) { + this._backgroundColor = "red"; + this._display = "none"; + this._selected = false; + } + else { + this._backgroundColor = "green"; + this._display = "initial"; + this._selected = true; + } + e.preventDefault(); + e.stopPropagation(); } render() { - let targetDoc = Cast(this.props.document.targetDoc, Doc, Docs.TextDocument({ title: "New Pin Annotation" })); - return ( -
- {/* */} -
- ); + let targetDoc = Cast(this.props.document.target, Doc); + if (targetDoc instanceof Doc) { + return ( +
+
+ 1} + PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)} + PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)} + focus={emptyFunction} + selectOnLoad={false} + parentActive={this.props.parent.props.parent.props.active} + whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged} + bringToFront={emptyFunction} + addDocTab={this.props.parent.props.parent.props.addDocTab} + /> +
+
+ ); + } + return null; } } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index fe7369aeb..73a7a93a0 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -23,7 +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; + makePin: (x: number, y: number, page: number) => void; } @observer @@ -378,7 +378,7 @@ export default class Page extends React.Component { 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); + this.props.makePin(x, y, this.props.page); } } -- cgit v1.2.3-70-g09d2 From 9ec4a529dc886acca8f147cfe913e60f938f3bda Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 12 Jun 2019 14:35:24 -0400 Subject: reverse annotating --- src/client/util/DragManager.ts | 6 +- src/client/views/pdf/PDFViewer.tsx | 112 ++++++++++++++++++++++++++++++------- src/client/views/pdf/Page.tsx | 27 ++++++--- 3 files changed, 116 insertions(+), 29 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index e92ed9b4a..2ffb77e44 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -154,14 +154,14 @@ export namespace DragManager { } export class AnnotationDragData { - constructor(dragDoc: Doc, annotationDocs: Doc[], dropDoc: Doc) { + constructor(dragDoc: Doc, annotationDoc: Doc, dropDoc: Doc) { this.dragDocument = dragDoc; this.dropDocument = dropDoc; - this.annotationDocuments = annotationDocs; + this.annotationDocument = annotationDoc; this.xOffset = this.yOffset = 0; } dragDocument: Doc; - annotationDocuments: Doc[]; + annotationDocument: Doc; dropDocument: Doc; xOffset: number; yOffset: number; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 6823f640e..bfd0e036f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -7,7 +7,7 @@ import "./PDFViewer.scss"; import "pdfjs-dist/web/pdf_viewer.css"; import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; -import { NumCast, Cast, BoolCast } from "../../../new_fields/Types"; +import { NumCast, Cast, BoolCast, StrCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; import { DocUtils, Docs } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; @@ -18,6 +18,8 @@ import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocum import { Transform } from "../../util/Transform"; import { emptyFunction, returnTrue, returnFalse } from "../../../Utils"; import { DocumentView } from "../nodes/DocumentView"; +import { DragManager } from "../../util/DragManager"; +import { Dictionary } from "typescript-collections"; interface IPDFViewerProps { url: string; @@ -83,12 +85,15 @@ class Viewer extends React.Component { @observable private _loaded: boolean = false; @observable private _pdf: Opt; @observable private _annotations: Doc[] = []; + @observable private _pointerEvents: "all" | "none" = "all"; + @observable private _savedAnnotations: Dictionary = new Dictionary(); private _pageBuffer: number = 1; private _annotationLayer: React.RefObject; private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; private _pagesLoaded: number = 0; + private _dropDisposer?: DragManager.DragDropDisposer; constructor(props: IViewerProps) { super(props); @@ -96,6 +101,7 @@ class Viewer extends React.Component { this._annotationLayer = React.createRef(); } + @action componentDidMount = () => { let wasSelected = this.props.parent.props.isSelected(); // reaction for when document gets (de)selected @@ -105,10 +111,12 @@ class Viewer extends React.Component { // if deselected, render images in place of pdf if (wasSelected && !this.props.parent.props.isSelected()) { this.saveThumbnail(); + this._pointerEvents = "all"; } // if selected, render pdf else if (!wasSelected && this.props.parent.props.isSelected()) { this.renderPages(this.startIndex, this.endIndex, true); + this._pointerEvents = "none"; } wasSelected = this.props.parent.props.isSelected(); }, @@ -133,6 +141,57 @@ class Viewer extends React.Component { }, 1000); } + private mainCont = (div: HTMLDivElement | null) => { + if (this._dropDisposer) { + this._dropDisposer(); + } + if (div) { + this._dropDisposer = DragManager.MakeDropTarget(div, { + handlers: { drop: this.drop.bind(this) } + }); + } + } + + makeAnnotationDocuments = (sourceDoc: Doc): Doc => { + let annoDocs: Doc[] = []; + this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { + for (let anno of value) { + let annoDoc = new Doc(); + if (anno.style.left) annoDoc.x = parseInt(anno.style.left); + if (anno.style.top) annoDoc.y = parseInt(anno.style.top); + if (anno.style.height) annoDoc.height = parseInt(anno.style.height); + if (anno.style.width) annoDoc.width = parseInt(anno.style.width); + annoDoc.page = key; + annoDoc.target = sourceDoc; + annoDoc.type = AnnotationTypes.Region; + annoDocs.push(annoDoc); + anno.remove(); + } + }); + + let annoDoc = new Doc(); + annoDoc.annotations = new List(annoDocs); + DocUtils.MakeLink(sourceDoc, annoDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + this._savedAnnotations.clear(); + return annoDoc; + } + + drop = async (e: Event, de: DragManager.DropEvent) => { + if (de.data instanceof DragManager.LinkDragData) { + let sourceDoc = de.data.linkSourceDocument; + let destDoc = this.makeAnnotationDocuments(sourceDoc); + let targetAnnotations = DocListCast(this.props.parent.Document.annotations); + if (targetAnnotations) { + targetAnnotations.push(destDoc); + this.props.parent.Document.annotations = new List(targetAnnotations); + } + else { + this.props.parent.Document.annotations = new List([destDoc]); + } + } + e.stopPropagation(); + } + componentWillUnmount = () => { if (this._reactionDisposer) { this._reactionDisposer(); @@ -219,6 +278,8 @@ class Viewer extends React.Component { parent={this.props.parent} renderAnnotations={this.renderAnnotations} makePin={this.createPinAnnotation} + sendAnnotations={this.receiveAnnotations} + receiveAnnotations={this.sendAnnotations} {...this.props} /> )); let arr = Array.from(Array(numPages).keys()).map(() => false); @@ -257,6 +318,8 @@ class Viewer extends React.Component { parent={this.props.parent} makePin={this.createPinAnnotation} renderAnnotations={this.renderAnnotations} + sendAnnotations={this.receiveAnnotations} + receiveAnnotations={this.sendAnnotations} {...this.props} /> ); this._isPage[i] = true; @@ -269,6 +332,15 @@ class Viewer extends React.Component { return; } + @action + receiveAnnotations = (annotations: HTMLDivElement[], page: number) => { + this._savedAnnotations.setValue(page, annotations); + } + + sendAnnotations = (page: number): HTMLDivElement[] | undefined => { + return this._savedAnnotations.getValue(page); + } + createPinAnnotation = (x: number, y: number, page: number): void => { let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" }); @@ -280,13 +352,15 @@ class Viewer extends React.Component { pinAnno.target = targetDoc; pinAnno.type = AnnotationTypes.Pin; // this._annotations.push(pinAnno); + let annoDoc = new Doc(); + annoDoc.annotations = new List([pinAnno]); let annotations = DocListCast(this.props.parent.Document.annotations); if (annotations && annotations.length) { - annotations.push(pinAnno); + annotations.push(annoDoc); this.props.parent.Document.annotations = new List(annotations); } else { - this.props.parent.Document.annotations = new List([pinAnno]); + this.props.parent.Document.annotations = new List([annoDoc]); } // let pinAnno = document.createElement("div"); // pinAnno.className = "pdfViewer-pinAnnotation"; @@ -349,20 +423,20 @@ class Viewer extends React.Component { return counter; } - renderAnnotation = (anno: Doc): JSX.Element => { - let type = NumCast(anno.type); - switch (type) { - case AnnotationTypes.Pin: - return ; - case AnnotationTypes.Region: - return ; - default: - return
; - } - } - - onDrop = (e: React.DragEvent) => { - console.log("Dropped!"); + renderAnnotation = (anno: Doc): JSX.Element[] => { + let annotationDocs = DocListCast(anno.annotations); + let res = annotationDocs.map(a => { + let type = NumCast(a.type); + switch (type) { + case AnnotationTypes.Pin: + return ; + case AnnotationTypes.Region: + return ; + default: + return
; + } + }); + return res; } // ScreenToLocalTransform = (): Transform => { @@ -380,11 +454,11 @@ class Viewer extends React.Component { render() { trace(); return ( -
+
{this._visibleElements}
-
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 73a7a93a0..908804605 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -24,6 +24,8 @@ interface IPageProps { parent: PDFBox; renderAnnotations: (annotations: Doc[], removeOld: boolean) => void; makePin: (x: number, y: number, page: number) => void; + sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; + receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; } @observer @@ -61,11 +63,19 @@ export default class Page extends React.Component { componentDidMount = (): void => { if (this.props.pdf) { this.update(this.props.pdf); + } + let received = this.props.receiveAnnotations(this.props.page); + this._currentAnnotations = received ? received : []; + if (this._annotationLayer.current) { + this._annotationLayer.current.append(...this._currentAnnotations); } } componentWillUnmount = (): void => { + console.log(this._currentAnnotations); + this.props.sendAnnotations(this._currentAnnotations, this.props.page); + if (this._reactionDisposer) { this._reactionDisposer(); } @@ -134,8 +144,9 @@ export default class Page extends React.Component { * This method makes the list of current annotations into documents linked to * the parameter passed in. */ - makeAnnotationDocuments = (targetDoc: Doc): Doc[] => { + makeAnnotationDocuments = (targetDoc: Doc): Doc => { let annoDocs: Doc[] = []; + for (let anno of this._currentAnnotations) { let annoDoc = new Doc(); annoDoc.x = anno.offsetLeft; @@ -146,11 +157,13 @@ export default class Page extends React.Component { 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(); } + let annoDoc = new Doc(); + annoDoc.annotations = new List(annoDocs); + DocUtils.MakeLink(annoDoc, targetDoc, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); this._currentAnnotations = []; - return annoDocs; + return annoDoc; } /** @@ -171,17 +184,17 @@ export default class Page extends React.Component { let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights - let annotationDocs = this.makeAnnotationDocuments(targetDoc); + let annotationDoc = this.makeAnnotationDocuments(targetDoc); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { - targetAnnotations.push(...annotationDocs); + targetAnnotations.push(annotationDoc); this.props.parent.Document.annotations = new List(targetAnnotations); } else { - this.props.parent.Document.annotations = new List(annotationDocs); + this.props.parent.Document.annotations = new List([annotationDoc]); } // create dragData and star tdrag - let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDocs, targetDoc); + let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc); if (this._textLayer.current) { DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, { handlers: { -- cgit v1.2.3-70-g09d2 From 0f85b1b7a6198013269c19a36dc8bcf14d2a4952 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 12 Jun 2019 16:02:25 -0400 Subject: Refactored some code --- src/client/views/pdf/PDFViewer.tsx | 76 +++++++++++++++++--------------------- src/client/views/pdf/Page.tsx | 67 +++------------------------------ 2 files changed, 39 insertions(+), 104 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index deacdd623..e54dfea6f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -278,7 +278,9 @@ class Viewer extends React.Component { parent={this.props.parent} renderAnnotations={this.renderAnnotations} makePin={this.createPinAnnotation} + createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} + makeAnnotationDocuments={this.makeAnnotationDocuments} receiveAnnotations={this.sendAnnotations} {...this.props} /> )); @@ -318,7 +320,9 @@ class Viewer extends React.Component { parent={this.props.parent} makePin={this.createPinAnnotation} renderAnnotations={this.renderAnnotations} + createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} + makeAnnotationDocuments={this.makeAnnotationDocuments} receiveAnnotations={this.sendAnnotations} {...this.props} /> ); @@ -334,7 +338,13 @@ class Viewer extends React.Component { @action receiveAnnotations = (annotations: HTMLDivElement[], page: number) => { - this._savedAnnotations.setValue(page, annotations); + if (page === -1) { + this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); + this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, annotations)); + } + else { + this._savedAnnotations.setValue(page, annotations); + } } sendAnnotations = (page: number): HTMLDivElement[] | undefined => { @@ -346,7 +356,7 @@ class Viewer extends React.Component { let pinAnno = new Doc(); pinAnno.x = x; - pinAnno.y = y; + pinAnno.y = y + this.getPageHeight(page); pinAnno.width = pinAnno.height = PinRadius; pinAnno.page = page; pinAnno.target = targetDoc; @@ -362,12 +372,6 @@ class Viewer extends React.Component { else { this.props.parent.Document.annotations = new List([annoDoc]); } - // 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 @@ -404,10 +408,7 @@ class Viewer extends React.Component {
)); this._visibleElements = new Array(...divs); - // On load, render pdf - // setTimeout(() => { this.renderPages(this.startIndex, this.endIndex, true); - // }, 1000); } } @@ -423,15 +424,32 @@ class Viewer extends React.Component { return counter; } + createAnnotation = (div: HTMLDivElement, page: number) => { + if (this._annotationLayer.current) { + if (div.style.top) { + div.style.top = (parseInt(div.style.top) + this.getPageHeight(page)).toString(); + } + this._annotationLayer.current.append(div); + let savedPage = this._savedAnnotations.getValue(page); + if (savedPage) { + savedPage.push(div); + this._savedAnnotations.setValue(page, savedPage); + } + else { + this._savedAnnotations.setValue(page, [div]); + } + } + } + renderAnnotation = (anno: Doc): JSX.Element[] => { let annotationDocs = DocListCast(anno.annotations); let res = annotationDocs.map(a => { let type = NumCast(a.type); switch (type) { case AnnotationTypes.Pin: - return ; + return ; case AnnotationTypes.Region: - return ; + return ; default: return
; } @@ -439,18 +457,6 @@ class Viewer extends React.Component { return res; } - // ScreenToLocalTransform = (): Transform => { - // if (this._annotationLayer.current) { - // let boundingRect = this._annotationLayer.current.getBoundingClientRect(); - // let x = boundingRect.left; - // let y = boundingRect.top; - // let scale = NumCast(this.props.parent.Document.nativeWidth) / boundingRect.width; - // let t = new Transform(x, y, scale); - // return t; - // } - // return Transform.Identity(); - // } - render() { trace(); return ( @@ -458,8 +464,8 @@ class Viewer extends React.Component {
{this._visibleElements}
-
-
+
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -542,26 +548,10 @@ class PinAnnotation extends React.Component { class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; - // private _reactionDisposer?: IReactionDisposer; - - constructor(props: IAnnotationProps) { - super(props); - - // this._reactionDisposer = reaction( - // () => { BoolCast(this.props.document.selected); }, - // () => { this._backgroundColor = BoolCast(this.props.document.selected) ? "yellow" : "red"; }, - // { fireImmediately: true } - // ) - } - onPointerDown = (e: React.PointerEvent) => { let targetDoc = Cast(this.props.document.target, Doc, null); if (targetDoc) { DocumentManager.Instance.jumpToDocument(targetDoc); - // let annotations = DocListCast(targetDoc.proto!.linkedFromDocs); - // if (annotations && annotations.length) { - // annotations.forEach(anno => anno.selected = true); - // } } } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 998ac25fd..9e3bf4954 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -26,6 +26,8 @@ interface IPageProps { makePin: (x: number, y: number, page: number) => void; sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; + createAnnotation: (div: HTMLDivElement, page: number) => void; + makeAnnotationDocuments: (doc: Doc) => Doc; } @observer @@ -46,7 +48,6 @@ export default class Page extends React.Component { private _annotationLayer: React.RefObject; private _marquee: React.RefObject; private _curly: React.RefObject; - private _currentAnnotations: HTMLDivElement[] = []; private _marqueeing: boolean = false; private _dragging: boolean = false; private _reactionDisposer?: IReactionDisposer; @@ -64,18 +65,9 @@ export default class Page extends React.Component { if (this.props.pdf) { this.update(this.props.pdf); } - - let received = this.props.receiveAnnotations(this.props.page); - this._currentAnnotations = received ? received : []; - if (this._annotationLayer.current) { - this._annotationLayer.current.append(...this._currentAnnotations); - } } componentWillUnmount = (): void => { - console.log(this._currentAnnotations); - this.props.sendAnnotations(this._currentAnnotations, this.props.page); - if (this._reactionDisposer) { this._reactionDisposer(); } @@ -139,33 +131,6 @@ export default class Page extends React.Component { } } - /** - * @param targetDoc: Document that annotations are linked to - * This method makes the list of current annotations into documents linked to - * the parameter passed in. - */ - makeAnnotationDocuments = (targetDoc: Doc): Doc => { - let annoDocs: Doc[] = []; - - for (let anno of this._currentAnnotations) { - let annoDoc = new Doc(); - annoDoc.x = anno.offsetLeft; - annoDoc.y = anno.offsetTop; - annoDoc.height = anno.offsetHeight; - annoDoc.width = anno.offsetWidth; - annoDoc.page = this.props.page; - annoDoc.target = targetDoc; - annoDoc.type = AnnotationTypes.Region; - annoDocs.push(annoDoc); - anno.remove(); - } - let annoDoc = new Doc(); - annoDoc.annotations = new List(annoDocs); - DocUtils.MakeLink(annoDoc, targetDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); - this._currentAnnotations = []; - return annoDoc; - } - /** * This is temporary for creating annotations from highlights. It will * start a drag event and create or put the necessary info into the drag event. @@ -184,7 +149,7 @@ export default class Page extends React.Component { let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; // creates annotation documents for current highlights - let annotationDoc = this.makeAnnotationDocuments(targetDoc); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(annotationDoc); @@ -246,10 +211,7 @@ export default class Page extends React.Component { document.removeEventListener("pointerup", this.onSelectEnd); document.addEventListener("pointerup", this.onSelectEnd); if (!e.ctrlKey) { - for (let anno of this._currentAnnotations) { - anno.remove(); - } - this._currentAnnotations = []; + this.props.sendAnnotations([], -1); } } } @@ -336,10 +298,7 @@ export default class Page extends React.Component { copy.style.opacity = this._marquee.current.style.opacity; } copy.className = this._marquee.current.className; - if (this._annotationLayer.current) { - this._annotationLayer.current.append(copy); - this._currentAnnotations.push(copy); - } + this.props.createAnnotation(copy, this.props.page); this._marquee.current.style.opacity = "0"; } @@ -362,8 +321,7 @@ export default class Page extends React.Component { annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - if (this._annotationLayer.current) this._annotationLayer.current.append(annoBox); - this._currentAnnotations.push(annoBox); + this.props.createAnnotation(annoBox, this.props.page); } } } @@ -395,19 +353,6 @@ export default class Page extends React.Component { } } - renderAnnotation = (anno: Doc | undefined): HTMLDivElement => { - let annoBox = document.createElement("div"); - if (anno) { - annoBox.className = "pdfViewer-annotationBox"; - // transforms the positions from screen onto the pdf div - annoBox.style.top = NumCast(anno.x).toString(); - annoBox.style.left = NumCast(anno.y).toString(); - annoBox.style.width = NumCast(anno.width).toString(); - annoBox.style.height = NumCast(anno.height).toString() - } - return annoBox; - } - render() { return (
-- cgit v1.2.3-70-g09d2 From b631beaf11db59549e75cc38ae7288a0ba8845cf Mon Sep 17 00:00:00 2001 From: yipstanley Date: Thu, 13 Jun 2019 22:00:33 -0400 Subject: added collection back --- src/client/documents/Documents.ts | 2 +- src/client/views/collections/CollectionPDFView.tsx | 32 +++++++++++++++++++--- src/client/views/nodes/PDFBox.tsx | 13 ++++++++- src/client/views/pdf/PDFViewer.scss | 1 - src/client/views/pdf/PDFViewer.tsx | 30 ++++++++++++++------ src/client/views/pdf/Page.tsx | 2 +- 6 files changed, 64 insertions(+), 16 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index dfbe2e136..91d3707f6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -170,7 +170,7 @@ export namespace Docs { return textProto; } function CreatePdfPrototype(): Doc { - let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", PDFBox.LayoutString(), + let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"), { x: 0, y: 0, width: 300, height: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 }); return pdfProto; } diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 62e8adbec..4af89d780 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -1,4 +1,4 @@ -import { action, observable } from "mobx"; +import { action, observable, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { ContextMenu } from "../ContextMenu"; import "./CollectionPDFView.scss"; @@ -9,12 +9,36 @@ import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from ". import { emptyFunction } from "../../../Utils"; import { NumCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; +import { HeightSym, WidthSym } from "../../../new_fields/Doc"; @observer export class CollectionPDFView extends React.Component { + private _reactionDisposer?: IReactionDisposer; + private _buttonTray: React.RefObject; + constructor(props: FieldViewProps) { super(props); + + this._buttonTray = React.createRef(); + } + + componentDidMount() { + this._reactionDisposer = reaction( + () => NumCast(this.props.Document.scrollY), + () => { + // let transform = this.props.ScreenToLocalTransform(); + if (this._buttonTray.current) { + // console.log(this._buttonTray.current.offsetHeight); + // console.log(NumCast(this.props.Document.scrollY)); + let scale = this.nativeWidth() / this.props.Document[WidthSym](); + this.props.Document.panY = NumCast(this.props.Document.scrollY); + // console.log(scale); + } + // console.log(this.props.Document[HeightSym]()); + }, + { fireImmediately: true } + ) } public static LayoutString(fieldKey: string = "data") { @@ -52,12 +76,12 @@ export class CollectionPDFView extends React.Component { private get uIButtons() { let ratio = (this.curPage - 1) / this.numPages * 100; return ( -
+
-
+ {/*
-
+
*/}
); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 4214a6777..acb430deb 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -36,6 +36,10 @@ export class PDFBox extends DocComponent(PdfDocumen let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; doc.nativeWidth = nw; doc.nativeHeight = nh; + let ccv = this.props.ContainingCollectionView; + if (ccv) { + ccv.props.Document.pdfHeight = nh; + } doc.height = nh * (doc[WidthSym]() / nw); } } @@ -45,6 +49,10 @@ export class PDFBox extends DocComponent(PdfDocumen if (e.currentTarget) { this._scrollY = e.currentTarget.scrollTop; // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" }); + let ccv = this.props.ContainingCollectionView; + if (ccv) { + ccv.props.Document.scrollY = this._scrollY; + } } } @@ -56,7 +64,10 @@ export class PDFBox extends DocComponent(PdfDocumen let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
e.stopPropagation()} className={classname}> {/*
*/} diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 57be04b93..a73df2d58 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -33,7 +33,6 @@ .pdfViewer-annotationLayer { position: absolute; top: 0; - overflow: visible hidden; } .pdfViewer-pinAnnotation { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index fe442c906..144fca9e0 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -188,8 +188,8 @@ class Viewer extends React.Component { else { this.props.parent.Document.annotations = new List([destDoc]); } + e.stopPropagation(); } - e.stopPropagation(); } componentWillUnmount = () => { @@ -465,7 +465,7 @@ class Viewer extends React.Component { {this._visibleElements}
-
+
{this._annotations.map(anno => this.renderAnnotation(anno))}
@@ -501,17 +501,31 @@ class PinAnnotation extends React.Component { componentDidMount = () => { let selected = this.props.document.selected; - if (selected && BoolCast(selected)) { + if (!BoolCast(selected)) { runInAction(() => { - this._backgroundColor = "green"; - this._display = "initial"; - }) + this._backgroundColor = "red"; + this._display = "none"; + }); + } + if (selected) { + if (BoolCast(selected)) { + runInAction(() => { + this._backgroundColor = "green"; + this._display = "initial"; + }); + } + else { + runInAction(() => { + this._backgroundColor = "red"; + this._display = "none"; + }); + } } else { runInAction(() => { this._backgroundColor = "red"; this._display = "none"; - }) + }); } } @@ -572,7 +586,7 @@ class PinAnnotation extends React.Component { PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)} PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)} focus={emptyFunction} - selectOnLoad={false} + selectOnLoad={true} parentActive={this.props.parent.props.parent.props.active} whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged} bringToFront={emptyFunction} diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 9e3bf4954..1c305caa3 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -257,7 +257,7 @@ export default class Page extends React.Component { let ratio = this._marqueeWidth / this._marqueeHeight; if (ratio > 1.5) { // vertical - transform = "rotate(90deg) scale(1, 2)"; + transform = "rotate(90deg) scale(1, 5)"; } else if (ratio < 0.5) { // horizontal -- cgit v1.2.3-70-g09d2 From 6a2cb71af332d4c782c1678750ba955757eaab45 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 11:56:36 -0400 Subject: fixes --- src/client/views/nodes/PDFBox.scss | 6 ++++++ src/client/views/pdf/PDFViewer.scss | 2 ++ src/client/views/pdf/PDFViewer.tsx | 10 +++++----- src/client/views/pdf/Page.tsx | 3 +-- 4 files changed, 14 insertions(+), 7 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index f4d455be7..bb1f534c6 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -41,6 +41,12 @@ pointer-events: none !important; } } +.textlayer { + span { + pointer-events: all !important; + user-select: text; + } +} .pdfBox-cont-interactive { pointer-events: all; diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index a73df2d58..53c33ce0b 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -35,6 +35,8 @@ top: 0; } + + .pdfViewer-pinAnnotation { background-color: red; position: absolute; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 144fca9e0..4b949aa3e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -103,22 +103,22 @@ class Viewer extends React.Component { @action componentDidMount = () => { - let wasSelected = this.props.parent.props.isSelected(); + let wasSelected = this.props.parent.props.active(); // reaction for when document gets (de)selected this._reactionDisposer = reaction( - () => [this.props.parent.props.isSelected(), this.startIndex], + () => [this.props.parent.props.active(), this.startIndex], () => { // if deselected, render images in place of pdf - if (wasSelected && !this.props.parent.props.isSelected()) { + if (wasSelected && !this.props.parent.props.active()) { this.saveThumbnail(); this._pointerEvents = "all"; } // if selected, render pdf - else if (!wasSelected && this.props.parent.props.isSelected()) { + else if (!wasSelected && this.props.parent.props.active()) { this.renderPages(this.startIndex, this.endIndex, true); this._pointerEvents = "none"; } - wasSelected = this.props.parent.props.isSelected(); + wasSelected = this.props.parent.props.active(); }, { fireImmediately: true } ); diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 1c305caa3..fa3f7baca 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -195,7 +195,6 @@ export default class Page extends React.Component { e.stopPropagation(); } else { - e.stopPropagation(); // set marquee x and y positions to the spatially transformed position let current = this._textLayer.current; if (current) { @@ -355,7 +354,7 @@ export default class Page extends React.Component { render() { return ( -
+
-- cgit v1.2.3-70-g09d2 From 94ed67966e7fdc7aa36b1a8b045153d0d661ce57 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 12:50:18 -0400 Subject: early impl of pdf menu --- src/client/views/MainView.tsx | 5 ++- src/client/views/pdf/Page.tsx | 96 +++++++++++++++++++++++++++++++------------ 2 files changed, 74 insertions(+), 27 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 42d5929bf..0779e1471 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell } from '@fortawesome/free-solid-svg-icons'; +import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; @@ -32,6 +32,7 @@ import { listSpec } from '../../new_fields/Schema'; import { Id } from '../../new_fields/FieldSymbols'; import { HistoryUtil } from '../util/History'; import { CollectionBaseView } from './collections/CollectionBaseView'; +import PDFMenu from './pdf/PDFMenu'; @observer @@ -88,6 +89,7 @@ export class MainView extends React.Component { library.add(faFilm); library.add(faMusic); library.add(faTree); + library.add(faCommentAlt); this.initEventListeners(); this.initAuthenticationRouters(); } @@ -315,6 +317,7 @@ export class MainView extends React.Component { {this.nodesMenu()} {this.miscButtons} +
); diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 1c305caa3..2c237740c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -14,6 +14,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; import { AnnotationTypes } from "./PDFViewer"; +import PDFMenu from "./PDFMenu"; interface IPageProps { pdf: Opt; @@ -272,7 +273,7 @@ export default class Page extends React.Component { } @action - onSelectEnd = (): void => { + onSelectEnd = (e: PointerEvent): void => { if (this._marqueeing) { this._marqueeing = false; if (this._marquee.current) { @@ -303,36 +304,79 @@ export default class Page extends React.Component { } this._marqueeHeight = this._marqueeWidth = 0; + PDFMenu.Instance.Left = e.clientX; + PDFMenu.Instance.Top = e.clientY; } else { let sel = window.getSelection(); - // if selecting over a range of things - if (sel && sel.type === "Range") { - let clientRects = sel.getRangeAt(0).getClientRects(); - if (this._textLayer.current) { - let boundingRect = this._textLayer.current.getBoundingClientRect(); - for (let i = 0; i < clientRects.length; i++) { - let rect = clientRects.item(i); - if (rect) { - let annoBox = document.createElement("div"); - annoBox.className = "pdfViewer-annotationBox"; - // transforms the positions from screen onto the pdf div - annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); - annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); - annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); - annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - this.props.createAnnotation(annoBox, this.props.page); - } - } - } - // clear selection - if (sel.empty) { // Chrome - sel.empty(); - } else if (sel.removeAllRanges) { // Firefox - sel.removeAllRanges(); - } + if (sel && sel.type === "range") { + + PDFMenu.Instance.Left = e.clientX; + PDFMenu.Instance.Top = e.clientY; } } + // let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); + // let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); + // if (this._marqueeing) { + // this._marqueeing = false; + // if (this._marquee.current) { + // let copy = document.createElement("div"); + // // make a copy of the marquee + // copy.style.left = this._marquee.current.style.left; + // copy.style.top = this._marquee.current.style.top; + // copy.style.width = this._marquee.current.style.width; + // copy.style.height = this._marquee.current.style.height; + + // // apply the appropriate background, opacity, and transform + // let { background, opacity, transform } = this.getCurlyTransform(); + // copy.style.background = background; + // // if curly bracing, add a curly brace + // if (opacity === "1" && this._curly.current) { + // copy.style.opacity = opacity; + // let img = this._curly.current.cloneNode(); + // (img as any).style.opacity = opacity; + // (img as any).style.transform = transform; + // copy.appendChild(img); + // } + // else { + // copy.style.opacity = this._marquee.current.style.opacity; + // } + // copy.className = this._marquee.current.className; + // this.props.createAnnotation(copy, this.props.page); + // this._marquee.current.style.opacity = "0"; + // } + + // this._marqueeHeight = this._marqueeWidth = 0; + // } + // else { + // let sel = window.getSelection(); + // // if selecting over a range of things + // if (sel && sel.type === "Range") { + // let clientRects = sel.getRangeAt(0).getClientRects(); + // if (this._textLayer.current) { + // let boundingRect = this._textLayer.current.getBoundingClientRect(); + // for (let i = 0; i < clientRects.length; i++) { + // let rect = clientRects.item(i); + // if (rect) { + // let annoBox = document.createElement("div"); + // annoBox.className = "pdfViewer-annotationBox"; + // // transforms the positions from screen onto the pdf div + // annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + // annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + // annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + // annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + // this.props.createAnnotation(annoBox, this.props.page); + // } + // } + // } + // // clear selection + // if (sel.empty) { // Chrome + // sel.empty(); + // } else if (sel.removeAllRanges) { // Firefox + // sel.removeAllRanges(); + // } + // } + // } document.removeEventListener("pointermove", this.onSelectStart); document.removeEventListener("pointerup", this.onSelectEnd); } -- cgit v1.2.3-70-g09d2 From 2ef1dd2089ad991f1d4897022f2d28c2eb130837 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 15:47:43 -0400 Subject: highlighting --- src/client/views/nodes/PDFBox.tsx | 10 +++++- src/client/views/pdf/PDFMenu.tsx | 25 +++++++++++-- src/client/views/pdf/PDFViewer.tsx | 6 ++-- src/client/views/pdf/Page.tsx | 74 ++++++++++++++++++++++++++++---------- 4 files changed, 91 insertions(+), 24 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index acb430deb..6ee62bbad 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -34,8 +34,16 @@ export class PDFBox extends DocComponent(PdfDocumen loaded = (nw: number, nh: number) => { if (this.props.Document) { let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + let oldnw = NumCast(doc.nativeWidth); doc.nativeWidth = nw; - doc.nativeHeight = nh; + if (!doc.nativeHeight) { + doc.nativeHeight = nh; + } + else { + let oldnh = NumCast(doc.nativeHeight); + let aspect = oldnh / oldnw; + doc.nativeHeight = nw * aspect; + } let ccv = this.props.ContainingCollectionView; if (ccv) { ccv.props.Document.pdfHeight = nh; diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index cc5c0b77b..a0230113b 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -3,6 +3,8 @@ import "./PDFMenu.scss"; import { observable } from "mobx"; import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { emptyFunction } from "../../../Utils"; +import { Doc } from "../../../new_fields/Doc"; @observer export default class PDFMenu extends React.Component { @@ -10,6 +12,8 @@ export default class PDFMenu extends React.Component { @observable Top: number = 0; @observable Left: number = 0; + StartDrag: (e: PointerEvent) => void = emptyFunction; + Highlight: (d: Doc | undefined) => void = emptyFunction; constructor(props: Readonly<{}>) { super(props); @@ -17,11 +21,28 @@ export default class PDFMenu extends React.Component { PDFMenu.Instance = this; } + pointerDown = (e: React.PointerEvent) => { + document.removeEventListener("pointermove", this.StartDrag); + document.addEventListener("pointermove", this.StartDrag); + document.removeEventListener("pointerup", this.pointerUp) + document.addEventListener("pointerup", this.pointerUp) + + e.stopPropagation(); + e.preventDefault(); + } + + pointerUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.StartDrag); + document.removeEventListener("pointerup", this.pointerUp); + e.stopPropagation(); + e.preventDefault(); + } + render() { return (
- - + +
) } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d6081142a..fbad39880 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -152,7 +152,7 @@ class Viewer extends React.Component { } } - makeAnnotationDocument = (sourceDoc: Doc): Doc => { + makeAnnotationDocument = (sourceDoc: Doc | undefined): Doc => { let annoDocs: Doc[] = []; this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { @@ -171,7 +171,9 @@ class Viewer extends React.Component { let annoDoc = new Doc(); annoDoc.annotations = new List(annoDocs); - DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + if (sourceDoc) { + DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + } this._savedAnnotations.clear(); return annoDoc; } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index bdb6952cc..44c502a04 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -28,7 +28,7 @@ interface IPageProps { sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; createAnnotation: (div: HTMLDivElement, page: number) => void; - makeAnnotationDocuments: (doc: Doc) => Doc; + makeAnnotationDocuments: (doc: Doc | undefined) => Doc; } @observer @@ -132,6 +132,20 @@ export default class Page extends React.Component { } } + highlight = (targetDoc: Doc | undefined) => { + // creates annotation documents for current highlights + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); + let targetAnnotations = DocListCast(this.props.parent.Document.annotations); + if (targetAnnotations) { + targetAnnotations.push(annotationDoc); + this.props.parent.Document.annotations = new List(targetAnnotations); + } + else { + this.props.parent.Document.annotations = new List([annotationDoc]); + } + return annotationDoc; + } + /** * This is temporary for creating annotations from highlights. It will * start a drag event and create or put the necessary info into the drag event. @@ -149,16 +163,7 @@ export default class Page extends React.Component { // document that this annotation is linked to let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; - // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); - let targetAnnotations = DocListCast(this.props.parent.Document.annotations); - if (targetAnnotations) { - targetAnnotations.push(annotationDoc); - this.props.parent.Document.annotations = new List(targetAnnotations); - } - else { - this.props.parent.Document.annotations = new List([annotationDoc]); - } + let annotationDoc = this.highlight(targetDoc); // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc); if (this._textLayer.current) { @@ -173,8 +178,8 @@ export default class Page extends React.Component { // cleans up events and boolean endDrag = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.endDrag); + // document.removeEventListener("pointermove", this.startDrag); + // document.removeEventListener("pointerup", this.endDrag); this._dragging = false; e.stopPropagation(); } @@ -185,10 +190,10 @@ export default class Page extends React.Component { if (e.altKey && e.button === 0) { e.stopPropagation(); - document.removeEventListener("pointermove", this.startDrag); - document.addEventListener("pointermove", this.startDrag); - document.removeEventListener("pointerup", this.endDrag); - document.addEventListener("pointerup", this.endDrag); + // document.removeEventListener("pointermove", this.startDrag); + // document.addEventListener("pointermove", this.startDrag); + // document.removeEventListener("pointerup", this.endDrag); + // document.addEventListener("pointerup", this.endDrag); } else if (e.button === 0) { let target: any = e.target; @@ -299,6 +304,8 @@ export default class Page extends React.Component { } copy.className = this._marquee.current.className; this.props.createAnnotation(copy, this.props.page); + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; this._marquee.current.style.opacity = "0"; } @@ -308,8 +315,10 @@ export default class Page extends React.Component { } else { let sel = window.getSelection(); - if (sel && sel.type === "range") { - + if (sel && sel.type === "Range") { + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; + this.createTextAnnotation(sel); PDFMenu.Instance.Left = e.clientX; PDFMenu.Instance.Top = e.clientY; } @@ -380,6 +389,33 @@ export default class Page extends React.Component { document.removeEventListener("pointerup", this.onSelectEnd); } + @action + createTextAnnotation = (sel: Selection) => { + let clientRects = sel.getRangeAt(0).getClientRects(); + if (this._textLayer.current) { + let boundingRect = this._textLayer.current.getBoundingClientRect(); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfViewer-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); + this.props.createAnnotation(annoBox, this.props.page); + } + } + } + // clear selection + if (sel.empty) { // Chrome + sel.empty(); + } else if (sel.removeAllRanges) { // Firefox + sel.removeAllRanges(); + } + } + doubleClick = (e: React.MouseEvent) => { let target: any = e.target; // if double clicking text -- cgit v1.2.3-70-g09d2 From f6e8b7a0f8a13ddf059cf701e46b8cbb8d9228f7 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 14 Jun 2019 17:27:49 -0400 Subject: added page fwd/back/goto --- src/client/views/collections/CollectionPDFView.tsx | 48 ++++++----------- src/client/views/nodes/DocumentView.tsx | 1 - src/client/views/nodes/FieldView.tsx | 2 + src/client/views/nodes/PDFBox.tsx | 62 ++++++++++++++++------ src/client/views/pdf/PDFViewer.tsx | 6 +-- src/client/views/pdf/Page.tsx | 12 ++--- 6 files changed, 72 insertions(+), 59 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 4af89d780..b62d3f7bb 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -1,19 +1,21 @@ -import { action, observable, IReactionDisposer, reaction } from "mobx"; +import { action, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; +import { WidthSym } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { NumCast } from "../../../new_fields/Types"; +import { emptyFunction } from "../../../Utils"; import { ContextMenu } from "../ContextMenu"; +import { FieldView, FieldViewProps } from "../nodes/FieldView"; +import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from "./CollectionBaseView"; +import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; import "./CollectionPDFView.scss"; import React = require("react"); -import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; -import { FieldView, FieldViewProps } from "../nodes/FieldView"; -import { CollectionRenderProps, CollectionBaseView, CollectionViewType } from "./CollectionBaseView"; -import { emptyFunction } from "../../../Utils"; -import { NumCast } from "../../../new_fields/Types"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { HeightSym, WidthSym } from "../../../new_fields/Doc"; +import { PDFBox } from "../nodes/PDFBox"; @observer export class CollectionPDFView extends React.Component { + private _pdfBox?: PDFBox; private _reactionDisposer?: IReactionDisposer; private _buttonTray: React.RefObject; @@ -46,31 +48,12 @@ export class CollectionPDFView extends React.Component { } @observable _inThumb = false; - private set curPage(value: number) { this.props.Document.curPage = value; } + private set curPage(value: number) { this._pdfBox && this._pdfBox.GotoPage(value); } private get curPage() { return NumCast(this.props.Document.curPage, -1); } private get numPages() { return NumCast(this.props.Document.numPages); } - @action onPageBack = () => this.curPage > 1 ? (this.props.Document.curPage = this.curPage - 1) : -1; - @action onPageForward = () => this.curPage < this.numPages ? (this.props.Document.curPage = this.curPage + 1) : -1; + @action onPageBack = () => this._pdfBox && this._pdfBox.BackPage(); + @action onPageForward = () => this._pdfBox && this._pdfBox.ForwardPage(); - @action - onThumbDown = (e: React.PointerEvent) => { - document.addEventListener("pointermove", this.onThumbMove, false); - document.addEventListener("pointerup", this.onThumbUp, false); - e.stopPropagation(); - this._inThumb = true; - } - @action - onThumbMove = (e: PointerEvent) => { - let pso = (e.clientY - (e as any).target.parentElement.getBoundingClientRect().top) / (e as any).target.parentElement.getBoundingClientRect().height; - this.curPage = Math.trunc(Math.min(this.numPages, pso * this.numPages + 1)); - e.stopPropagation(); - } - @action - onThumbUp = (e: PointerEvent) => { - this._inThumb = false; - document.removeEventListener("pointermove", this.onThumbMove); - document.removeEventListener("pointerup", this.onThumbUp); - } nativeWidth = () => NumCast(this.props.Document.nativeWidth); nativeHeight = () => NumCast(this.props.Document.nativeHeight); private get uIButtons() { @@ -92,11 +75,14 @@ export class CollectionPDFView extends React.Component { } } + setPdfBox = (pdfBox: PDFBox) => { this._pdfBox = pdfBox; }; + + private subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; return ( <> - + {renderProps.active() ? this.uIButtons : (null)} ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 583fa3e1a..0d5df550a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -453,7 +453,6 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - let ph = this.props.PanelHeight(); var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
number; PanelHeight: () => number; setVideoBox?: (player: VideoBox) => void; + setPdfBox?: (player: PDFBox) => void; } @observer diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index b9ccd79e4..243982a3b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,25 +1,22 @@ -import * as htmlToImage from "html-to-image"; -import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; +import { action, IReactionDisposer, observable, reaction, trace, untracked } from 'mobx'; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; -import Measure from "react-measure"; +import { WidthSym } from "../../../new_fields/Doc"; +import { makeInterface } from "../../../new_fields/Schema"; +import { Cast, NumCast } from "../../../new_fields/Types"; +import { PdfField } from "../../../new_fields/URLField"; //@ts-ignore // import { Document, Page } from "react-pdf"; // import 'react-pdf/dist/Page/AnnotationLayer.css'; import { RouteStore } from "../../../server/RouteStore"; import { DocComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; +import { PDFViewer } from "../pdf/PDFViewer"; import { positionSchema } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; -import { makeInterface } from "../../../new_fields/Schema"; -import { PDFViewer } from "../pdf/PDFViewer"; -import { PdfField } from "../../../new_fields/URLField"; -import { HeightSym, WidthSym } from "../../../new_fields/Doc"; -import { CollectionStackingView } from "../collections/CollectionStackingView"; type PdfDocument = makeInterface<[typeof positionSchema, typeof pageSchema]>; const PdfDocument = makeInterface(positionSchema, pageSchema); @@ -31,19 +28,50 @@ export class PDFBox extends DocComponent(PdfDocumen @observable private _alt = false; @observable private _scrollY: number = 0; private _reactionDisposer?: IReactionDisposer; - _targetDiv: any = undefined; - componentDidMount: () => void = () => { + componentDidMount() { + if (this.props.setPdfBox) this.props.setPdfBox(this); + } + + public GetPage() { + return Math.floor(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1; + } + public BackPage() { + let cp = Math.ceil(NumCast(this.props.Document.scrollY) / NumCast(this.Document.pdfHeight)) + 1; + cp = cp - 1; + if (cp > 0) { + this.props.Document.curPage = cp; + this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight); + } + } + public GotoPage(p: number) { + if (p > 0 && p <= NumCast(this.props.Document.numPages)) { + this.props.Document.curPage = p; + this.props.Document.scrollY = (p - 1) * NumCast(this.Document.pdfHeight); + } + } + + public ForwardPage() { + let cp = this.GetPage() + 1; + if (cp <= NumCast(this.props.Document.numPages)) { + this.props.Document.curPage = cp; + this.props.Document.scrollY = (cp - 1) * NumCast(this.Document.pdfHeight); + } + } + + createRef = (ele: HTMLDivElement | null) => { if (this._reactionDisposer) this._reactionDisposer(); - this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => - this._targetDiv && this._targetDiv.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" }) - ); + this._reactionDisposer = reaction(() => this.props.Document.scrollY, () => { + ele && ele.scrollTo({ top: NumCast(this.Document.scrollY), behavior: "smooth" }); + }); } - loaded = (nw: number, nh: number) => { + loaded = (nw: number, nh: number, np: number) => { if (this.props.Document) { - if (this.props.Document.nativeWidth && this.props.Document.nativeHeight) return; let doc = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + console.log("pages = " + np); + doc.numPages = np; + if (doc.nativeWidth && doc.nativeHeight) return; let oldaspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1); doc.nativeWidth = nw; if (doc.nativeHeight) doc.nativeHeight = nw * oldaspect; @@ -59,7 +87,6 @@ export class PDFBox extends DocComponent(PdfDocumen @action onScroll = (e: React.UIEvent) => { if (e.currentTarget) { - this._targetDiv = e.currentTarget; this._scrollY = e.currentTarget.scrollTop; // e.currentTarget.scrollTo({ top: 1000, behavior: "smooth" }); let ccv = this.props.ContainingCollectionView; @@ -82,6 +109,7 @@ export class PDFBox extends DocComponent(PdfDocumen overflowY: "scroll", overflowX: "hidden", marginTop: `${NumCast(this.props.ContainingCollectionView!.props.Document.panY)}px` }} + ref={this.createRef} onWheel={(e: React.WheelEvent) => e.stopPropagation()} className={classname}> {/*
*/} diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 17f65c7a6..dee891ba6 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -23,7 +23,7 @@ import { Dictionary } from "typescript-collections"; interface IPDFViewerProps { url: string; - loaded: (nw: number, nh: number) => void; + loaded: (nw: number, nh: number, np: number) => void; scrollY: number; parent: PDFBox; } @@ -61,7 +61,7 @@ export class PDFViewer extends React.Component { interface IViewerProps { pdf: Opt; - loaded: (nw: number, nh: number) => void; + loaded: (nw: number, nh: number, np: number) => void; scrollY: number; parent: PDFBox; mainCont: React.RefObject; @@ -400,7 +400,7 @@ class Viewer extends React.Component { return; } let numPages = this.props.pdf ? this.props.pdf.numPages : 0; - this.props.loaded(page.width, page.height); + this.props.loaded(page.width, page.height, numPages); this._pageSizes[index - 1] = { width: page.width, height: page.height }; this._pagesLoaded++; if (this._pagesLoaded === numPages) { diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 44c502a04..e3dbeaebe 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -132,16 +132,14 @@ export default class Page extends React.Component { } } - highlight = (targetDoc: Doc | undefined) => { + highlight = (targetDoc?: Doc) => { // creates annotation documents for current highlights let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); - let targetAnnotations = DocListCast(this.props.parent.Document.annotations); - if (targetAnnotations) { + let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); + if (targetAnnotations === undefined) { + Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); + } else { targetAnnotations.push(annotationDoc); - this.props.parent.Document.annotations = new List(targetAnnotations); - } - else { - this.props.parent.Document.annotations = new List([annotationDoc]); } return annotationDoc; } -- cgit v1.2.3-70-g09d2 From b987e2edbf7befbe90fafbdee476ee3b6513cc50 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 17:44:17 -0400 Subject: pinning things --- src/client/views/MainView.tsx | 3 +- src/client/views/pdf/PDFMenu.scss | 13 ++++- src/client/views/pdf/PDFMenu.tsx | 119 +++++++++++++++++++++++++++++++++++--- src/client/views/pdf/Page.tsx | 21 ++++--- 4 files changed, 137 insertions(+), 19 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 384cd2860..29015995f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons'; +import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; @@ -92,6 +92,7 @@ export class MainView extends React.Component { library.add(faMusic); library.add(faTree); library.add(faCommentAlt); + library.add(faThumbtack); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/pdf/PDFMenu.scss b/src/client/views/pdf/PDFMenu.scss index b84ebc12d..22868082a 100644 --- a/src/client/views/pdf/PDFMenu.scss +++ b/src/client/views/pdf/PDFMenu.scss @@ -1,18 +1,25 @@ .pdfMenu-cont { position: absolute; z-index: 10000; - width: 100px; - height: 30px; + height: 35px; background: #323232; box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); - border-radius: 0px 4px 4px 4px; + border-radius: 0px 6px 6px 6px; overflow: hidden; + display: flex; .pdfMenu-button { background-color: transparent; + width: 35px; + height: 35px; } .pdfMenu-button:hover { background-color: #121212; } + + .pdfMenu-dragger { + height: 100%; + transition: width .2s; + } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index a0230113b..d2a20fb6e 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -1,6 +1,6 @@ import React = require("react"); import "./PDFMenu.scss"; -import { observable } from "mobx"; +import { observable, action } from "mobx"; import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { emptyFunction } from "../../../Utils"; @@ -10,10 +10,18 @@ import { Doc } from "../../../new_fields/Doc"; export default class PDFMenu extends React.Component { static Instance: PDFMenu; - @observable Top: number = 0; - @observable Left: number = 0; + @observable private _top: number = 0; + @observable private _left: number = 0; + @observable private _opacity: number = 1; + @observable private _transition: string = "opacity 0.5s"; + @observable private _transitionDelay: string = ""; + @observable private _pinned: boolean = false; + StartDrag: (e: PointerEvent) => void = emptyFunction; Highlight: (d: Doc | undefined) => void = emptyFunction; + @observable Highlighting: boolean = false; + + private _timeout: NodeJS.Timeout | undefined; constructor(props: Readonly<{}>) { super(props); @@ -38,12 +46,109 @@ export default class PDFMenu extends React.Component { e.preventDefault(); } + @action + jumpTo = (x: number, y: number) => { + if (!this._pinned) { + this._transition = this._transitionDelay = ""; + this._opacity = 1; + this._left = x; + this._top = y; + } + } + + @action + fadeOut = (forceOut: boolean) => { + if (!this._pinned) { + if (this._opacity === 0.2) { + this._transition = "opacity 0.1s"; + this._transitionDelay = ""; + this._opacity = 0; + this._left = this._top = -300; + } + + if (forceOut) { + this._transition = ""; + this._transitionDelay = ""; + this._opacity = 0; + this._left = this._top = -300; + } + } + } + + @action + pointerLeave = (e: React.PointerEvent) => { + if (!this._pinned) { + this._transition = "opacity 0.5s"; + this._transitionDelay = "1s"; + this._opacity = 0.2; + setTimeout(() => this.fadeOut(false), 3000); + } + } + + @action + pointerEntered = (e: React.PointerEvent) => { + this._transition = "opacity 0.1s"; + this._transitionDelay = ""; + this._opacity = 1; + } + + @action + togglePin = (e: React.MouseEvent) => { + this._pinned = !this._pinned; + } + + @action + dragging = (e: PointerEvent) => { + this._left += e.movementX; + this._top += e.movementY; + + e.stopPropagation(); + e.preventDefault(); + } + + dragEnd = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.dragging); + document.removeEventListener("pointerup", this.dragEnd); + e.stopPropagation(); + e.preventDefault(); + } + + dragStart = (e: React.PointerEvent) => { + document.removeEventListener("pointermove", this.dragging); + document.addEventListener("pointermove", this.dragging); + document.removeEventListener("pointerup", this.dragEnd); + document.addEventListener("pointerup", this.dragEnd); + + e.stopPropagation(); + e.preventDefault(); + } + + @action + highlightClicked = (e: React.MouseEvent) => { + if (!this._pinned) { + this.Highlight(undefined); + } + else { + this.Highlighting = !this.Highlighting; + this.Highlight(undefined); + } + } + render() { return ( -
- - -
+
+ + + +
+
) } } \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 44c502a04..3bc3ac33c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -132,6 +132,7 @@ export default class Page extends React.Component { } } + @action highlight = (targetDoc: Doc | undefined) => { // creates annotation documents for current highlights let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); @@ -196,6 +197,7 @@ export default class Page extends React.Component { // document.addEventListener("pointerup", this.endDrag); } else if (e.button === 0) { + PDFMenu.Instance.fadeOut(true); let target: any = e.target; if (target && target.parentElement === this._textLayer.current) { e.stopPropagation(); @@ -304,25 +306,28 @@ export default class Page extends React.Component { } copy.className = this._marquee.current.className; this.props.createAnnotation(copy, this.props.page); - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; this._marquee.current.style.opacity = "0"; } this._marqueeHeight = this._marqueeWidth = 0; - PDFMenu.Instance.Left = e.clientX; - PDFMenu.Instance.Top = e.clientY; + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } else { let sel = window.getSelection(); if (sel && sel.type === "Range") { - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; this.createTextAnnotation(sel); - PDFMenu.Instance.Left = e.clientX; - PDFMenu.Instance.Top = e.clientY; + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } } + + + if (PDFMenu.Instance.Highlighting) { + this.highlight(undefined); + } + else { + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; + } // let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); // let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); // if (this._marqueeing) { -- cgit v1.2.3-70-g09d2 From 747fdb6d26afabad067f3f8d98789bfc7ca44f8d Mon Sep 17 00:00:00 2001 From: yipstanley Date: Fri, 14 Jun 2019 18:11:41 -0400 Subject: small stuffs --- src/client/views/pdf/PDFMenu.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 13 +++++++------ src/client/views/pdf/Page.tsx | 13 ++++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index d2a20fb6e..2ba875e42 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -95,6 +95,7 @@ export default class PDFMenu extends React.Component { @action togglePin = (e: React.MouseEvent) => { this._pinned = !this._pinned; + this.Highlighting = this._pinned === false; } @action diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index dee891ba6..55d15893a 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -21,6 +21,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { DragManager } from "../../util/DragManager"; import { Dictionary } from "typescript-collections"; +export const scale = 2; interface IPDFViewerProps { url: string; loaded: (nw: number, nh: number, np: number) => void; @@ -156,10 +157,10 @@ class Viewer extends React.Component { this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { let annoDoc = new Doc(); - if (anno.style.left) annoDoc.x = parseInt(anno.style.left); - if (anno.style.top) annoDoc.y = parseInt(anno.style.top); - if (anno.style.height) annoDoc.height = parseInt(anno.style.height); - if (anno.style.width) annoDoc.width = parseInt(anno.style.width); + if (anno.style.left) annoDoc.x = parseInt(anno.style.left) / scale; + if (anno.style.top) annoDoc.y = parseInt(anno.style.top) / scale; + if (anno.style.height) annoDoc.height = parseInt(anno.style.height) / scale; + if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale; annoDoc.page = key; annoDoc.target = sourceDoc; annoDoc.type = AnnotationTypes.Region; @@ -572,7 +573,7 @@ class PinAnnotation extends React.Component {
{ render() { return (
+ style={{ top: this.props.y * scale, left: this.props.x * scale, width: this.props.width * scale, height: this.props.height * scale, pointerEvents: "all", backgroundColor: this._backgroundColor }}>
); } } \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 455e1d831..bd2cae749 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -13,9 +13,10 @@ 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"; +import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer"; import PDFMenu from "./PDFMenu"; + interface IPageProps { pdf: Opt; name: string; @@ -28,7 +29,7 @@ interface IPageProps { sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; createAnnotation: (div: HTMLDivElement, page: number) => void; - makeAnnotationDocuments: (doc: Doc | undefined) => Doc; + makeAnnotationDocuments: (doc: Doc | undefined, scale: number) => Doc; } @observer @@ -103,7 +104,6 @@ export default class Page extends React.Component { @action private renderPage = (page: Pdfjs.PDFPageProxy): void => { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes - let scale = 2; let viewport = page.getViewport(scale); let canvas = this._canvas.current; let textLayer = this._textLayer.current; @@ -135,7 +135,7 @@ export default class Page extends React.Component { @action highlight = (targetDoc?: Doc) => { // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale); let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (targetAnnotations === undefined) { Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); @@ -307,8 +307,11 @@ export default class Page extends React.Component { this._marquee.current.style.opacity = "0"; } + if (this._marqueeWidth > 10 || this._marqueeHeight > 10) { + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); + } + this._marqueeHeight = this._marqueeWidth = 0; - PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } else { let sel = window.getSelection(); -- cgit v1.2.3-70-g09d2 From 90c14f3ad8d9e48698c516bc6549143912e19236 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 17 Jun 2019 10:47:40 -0400 Subject: highlights are a different color --- src/client/views/pdf/PDFViewer.tsx | 5 +++-- src/client/views/pdf/Page.tsx | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index bc7cfecbb..9becfb419 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -156,7 +156,7 @@ class Viewer extends React.Component { } } - makeAnnotationDocument = (sourceDoc: Doc | undefined): Doc => { + makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => { let annoDocs: Doc[] = []; this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { @@ -167,6 +167,7 @@ class Viewer extends React.Component { if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale; annoDoc.page = key; annoDoc.target = sourceDoc; + annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; annoDocs.push(annoDoc); anno.remove(); @@ -624,7 +625,7 @@ class RegionAnnotation extends React.Component { render() { return (
+ style={{ top: this.props.y * scale, left: this.props.x * scale, width: this.props.width * scale, height: this.props.height * scale, pointerEvents: "all", backgroundColor: StrCast(this.props.document.color) }}>
); } } \ No newline at end of file diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index bd2cae749..e706a0d5c 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -29,7 +29,7 @@ interface IPageProps { sendAnnotations: (annotations: HTMLDivElement[], page: number) => void; receiveAnnotations: (page: number) => HTMLDivElement[] | undefined; createAnnotation: (div: HTMLDivElement, page: number) => void; - makeAnnotationDocuments: (doc: Doc | undefined, scale: number) => Doc; + makeAnnotationDocuments: (doc: Doc | undefined, scale: number, color: string) => Doc; } @observer @@ -135,7 +135,7 @@ export default class Page extends React.Component { @action highlight = (targetDoc?: Doc) => { // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, "#f4f442"); let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (targetAnnotations === undefined) { Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); -- cgit v1.2.3-70-g09d2 From 122607408882617235af255af84ce78828b7982f Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 17 Jun 2019 13:42:21 -0400 Subject: page sizes loaded --- src/client/views/pdf/PDFMenu.tsx | 6 +++--- src/client/views/pdf/PDFViewer.tsx | 30 +++++++++++++++++++++++++++--- src/client/views/pdf/Page.tsx | 8 ++++---- 3 files changed, 34 insertions(+), 10 deletions(-) (limited to 'src/client/views/pdf/Page.tsx') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index b0735f63b..b44370e3d 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -18,7 +18,7 @@ export default class PDFMenu extends React.Component { @observable private _pinned: boolean = false; StartDrag: (e: PointerEvent) => void = emptyFunction; - Highlight: (d: Doc | undefined) => void = emptyFunction; + Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction; @observable Highlighting: boolean = false; private _timeout: NodeJS.Timeout | undefined; @@ -129,11 +129,11 @@ export default class PDFMenu extends React.Component { @action highlightClicked = (e: React.MouseEvent) => { if (!this._pinned) { - this.Highlight(undefined); + this.Highlight(undefined, "#f4f442"); } else { this.Highlighting = !this.Highlighting; - this.Highlight(undefined); + this.Highlight(undefined, "#f4f442"); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 9becfb419..d74a16f3f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -23,6 +23,7 @@ import { Dictionary } from "typescript-collections"; import * as rp from "request-promise"; import { restProperty } from "babel-types"; import { DocServer } from "../../DocServer"; +import { number } from "prop-types"; export const scale = 2; interface IPDFViewerProps { @@ -141,10 +142,33 @@ class Viewer extends React.Component { setTimeout(() => { // this.renderPages(this.startIndex, this.endIndex, true); - this.saveThumbnail(); + this.initialLoad(); }, 1000); } + @action + initialLoad = () => { + let pdf = this.props.pdf; + if (pdf) { + this._pageSizes = Array<{ width: number, height: number }>(pdf.numPages); + let rendered = 0; + for (let i = 0; i < pdf.numPages; i++) { + pdf.getPage(i + 1).then( + (page: Pdfjs.PDFPageProxy) => { + runInAction(() => { + this._pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale }; + }); + console.log(`page ${i} size retreieved`); + rendered++; + if (rendered === pdf!.numPages - 1) { + this.saveThumbnail(); + } + } + ); + } + } + } + private mainCont = (div: HTMLDivElement | null) => { if (this._dropDisposer) { this._dropDisposer(); @@ -186,7 +210,7 @@ class Viewer extends React.Component { drop = async (e: Event, de: DragManager.DropEvent) => { if (de.data instanceof DragManager.LinkDragData) { let sourceDoc = de.data.linkSourceDocument; - let destDoc = this.makeAnnotationDocument(sourceDoc); + let destDoc = this.makeAnnotationDocument(sourceDoc, 1, "red"); let targetAnnotations = DocListCast(this.props.parent.Document.annotations); if (targetAnnotations) { targetAnnotations.push(destDoc); @@ -392,7 +416,7 @@ class Viewer extends React.Component { let numPages = this.props.pdf ? this.props.pdf.numPages : 0; let index = 0; let currOffset = vOffset; - while (index < numPages && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { + while (index < this._pageSizes.length && currOffset - (this._pageSizes[index] ? this._pageSizes[index].height : 792 * scale) > 0) { currOffset -= this._pageSizes[index] ? this._pageSizes[index].height : this._pageSizes[0].height; index++; } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index e706a0d5c..bb87ec9d4 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -133,9 +133,9 @@ export default class Page extends React.Component { } @action - highlight = (targetDoc?: Doc) => { + highlight = (targetDoc?: Doc, color: string = "red") => { // creates annotation documents for current highlights - let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, "#f4f442"); + let annotationDoc = this.props.makeAnnotationDocuments(targetDoc, scale, color); let targetAnnotations = Cast(this.props.parent.Document.annotations, listSpec(Doc)); if (targetAnnotations === undefined) { Doc.GetProto(this.props.parent.Document).annotations = new List([annotationDoc]); @@ -162,7 +162,7 @@ export default class Page extends React.Component { // document that this annotation is linked to let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); targetDoc.targetPage = this.props.page; - let annotationDoc = this.highlight(targetDoc); + let annotationDoc = this.highlight(targetDoc, "red"); // create dragData and star tdrag let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc); if (this._textLayer.current) { @@ -323,7 +323,7 @@ export default class Page extends React.Component { if (PDFMenu.Instance.Highlighting) { - this.highlight(undefined); + this.highlight(undefined, "#f4f442"); } else { PDFMenu.Instance.StartDrag = this.startDrag; -- cgit v1.2.3-70-g09d2