diff options
-rw-r--r-- | src/views/nodes/Annotation.tsx | 26 | ||||
-rw-r--r-- | src/views/nodes/PDFNode.tsx | 254 |
2 files changed, 160 insertions, 120 deletions
diff --git a/src/views/nodes/Annotation.tsx b/src/views/nodes/Annotation.tsx index 0ba05ba3d..a2c7be1a8 100644 --- a/src/views/nodes/Annotation.tsx +++ b/src/views/nodes/Annotation.tsx @@ -9,6 +9,8 @@ interface IProps{ X: number; Y: number; Highlights: any[]; + Annotations: any[]; + CurrAnno: any[]; } @@ -43,7 +45,22 @@ export class Annotation extends React.Component<IProps> { */ @action onRemove = (e:any) => { - + let index:number = -1; + //finding the highlight in the highlight array + this.props.Highlights.forEach((e) => { + for (let i = 0; i < e.spans.length; i++){ + if (e.spans[i] == this.props.Span){ + index = this.props.Highlights.indexOf(e); + this.props.Highlights.splice(index, 1); + } + } + }) + + //removing from CurrAnno and Annotation array + this.props.Annotations.splice(index, 1); + this.props.CurrAnno.pop() + + //removing span from div if(this.props.Span.parentElement){ let nodesArray = this.props.Span.parentElement.childNodes; nodesArray.forEach((e) => { @@ -55,13 +72,12 @@ export class Annotation extends React.Component<IProps> { } }) e.remove(); - - - } } }) } + + } render() { @@ -75,7 +91,7 @@ export class Annotation extends React.Component<IProps> { transform: `translate(${this.props.X}px, ${this.props.Y}px)`, }}> - <div style = {{width:"200px", height:"50px"}}> + <div style = {{width:"200px", height:"50px", backgroundColor: "orange"}}> <button style = {{borderRadius: "25px", width:"25%", height:"100%"}} onClick = {this.onRemove} diff --git a/src/views/nodes/PDFNode.tsx b/src/views/nodes/PDFNode.tsx index d55e4d6d7..6aead8c58 100644 --- a/src/views/nodes/PDFNode.tsx +++ b/src/views/nodes/PDFNode.tsx @@ -10,16 +10,14 @@ import { Document, Page, PDFPageProxy, PageAnnotation} from "react-pdf"; import { Utils } from '../../Utils'; import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here import { Annotation } from './Annotation'; +import { ObjectPositionProperty } from 'csstype'; +import { keydownHandler } from 'prosemirror-keymap'; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, * area selection (I call it stickies), embedded ink node for directly annotating using a pen or * mouse, and pagination. - * - * Clearly, everything works perfectly. No bugs. Might as well publish it. - * - * ps watch out for some bugs. When highlighting, just highlight a section of one line... do not multiline highlight... plz - * Annotations and Stickies save per page. Highlights do not. + * * * HOW TO USE: * AREA selection: @@ -72,17 +70,13 @@ export class PDFNode extends React.Component<FieldViewProps> { private _highlightTool = React.createRef<HTMLButtonElement>(); //highlighter button reference private _highlightToolOn:boolean = false; - + + @observable perPage:Object[] = []; //stores pageInfo + @observable pageInfo:any = {area:[], divs:[], anno: []}; //divs is array of objects linked to anno @observable private page:number = 1; //default is the first page. @observable private numPage:number = 1; //default number of pages - @observable private stickies:any[] = [] //for storing CURRENT stickies - @observable private stickiesPerPage: any = null; //for indexing stickies for EVERY PAGE - @observable private annotations:any[] = []; //keeps track of annotations - @observable private annotationsPerPage: any = null; // for indexing annotations for EVERY PAGE - @observable private highlights:any[] = []; //keeps track of highlights. - @observable private highlightsPerPage:any = null; // for indexing highlights for EVERY PAGE - + private _pdfCanvas:any; /** * for pagination backwards @@ -91,51 +85,26 @@ export class PDFNode extends React.Component<FieldViewProps> { onPageBack = () => { if (this.page > 1){ this.page -= 1; - this.stickiesPerPage[this.page] = this.stickies; //stores previous sticky and indexes to stickiesPerPage - this.stickies = []; //sets stickies to null array - this.annotationsPerPage[this.page] = this.annotations; - this.annotations = []; - this.highlightsPerPage[this.page] = this.highlights; - this.highlights = []; - if (this.stickies){//checks stickies is null or not - this.stickies = this.stickiesPerPage[this.page - 1]; //pulls up stickies for this page - } - if (this.annotations){//checks if annotation is null or not - this.annotations = this.annotationsPerPage[this.page - 1]; - } - if (this.highlights){//checks if annotation is null or not - this.highlights = this.highlightsPerPage[this.page - 1]; + this.perPage[this.page] = this.pageInfo + this.pageInfo = {area:[], divs:[], anno: []}; //resets the object to default + if (this.perPage[this.page - 1]){ + this.pageInfo = this.perPage[this.page - 1]; } - - } } /** * for pagination forwards - */ - + */ @action onPageForward = () => { - if (this.page < this.numPage){ this.page += 1; - this.stickiesPerPage[this.page - 2] = this.stickies; //stores previous sticky and indexes to stickiesPerPage - this.stickies = []; //sets stickies to null array - this.annotationsPerPage[this.page - 2] = this.annotations; - this.annotations = []; - this.highlightsPerPage[this.page - 2] = this.highlights; - this.highlights = []; - if (this.stickiesPerPage[this.page - 1]){ - this.stickies = this.stickiesPerPage[this.page - 1]; //pulls up sticky for this page - } - if (this.annotationsPerPage[this.page - 1]){ - this.annotations = this.annotationsPerPage[this.page - 1]; //similar to sticky, it binds to previous annotation. + this.perPage[this.page - 2] = this.pageInfo; + this.pageInfo = {area:[], divs:[], anno: []}; //resets the object to default + if (this.perPage[this.page - 1]){ + this.pageInfo = this.perPage[this.page - 1]; } - if (this.annotationsPerPage[this.page - 1]){ - this.highlights = this.highlightsPerPage[this.page - 1]; //similar to sticky, it binds to previous highlight. - } - } } @@ -154,11 +123,10 @@ export class PDFNode extends React.Component<FieldViewProps> { this._pdfContext.beginPath(); this._pdfContext.lineTo(this.initX, this.initY); this._pdfContext.strokeStyle = this._currColor; - document.addEventListener("pointermove", this.drawMove); - document.addEventListener("pointerup", this.drawUp); + this._pdfCanvas.addEventListener("pointermove", this.drawMove); + this._pdfCanvas.addEventListener("pointerup", this.drawUp); } - //when user drags drawMove = (e: PointerEvent):void =>{ //x and y mouse movement @@ -171,9 +139,9 @@ export class PDFNode extends React.Component<FieldViewProps> { drawUp = (e:PointerEvent) => { this._pdfContext.closePath(); - document.removeEventListener("pointermove", this.drawMove); - document.removeEventListener("pointerdown", this.drawDown); - document.addEventListener("pointerdown", this.drawDown); + this._pdfCanvas.removeEventListener("pointermove", this.drawMove); + this._pdfCanvas.removeEventListener("pointerdown", this.drawDown); + this._pdfCanvas.addEventListener("pointerdown", this.drawDown); } @@ -190,49 +158,97 @@ export class PDFNode extends React.Component<FieldViewProps> { document.execCommand("HiliteColor", false, colour); } - if (range) { + if (range) { sel.removeAllRanges(); sel.addRange(range); - let element = range.commonAncestorContainer.parentElement - if (element){ - let childNodes = element.childNodes; - childNodes.forEach((e) => { - if (e.nodeName == "SPAN"){ - let span = e; - this.highlights.push(span); //pushes span into highglights. - span.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler + + let obj:Object = {parentDivs:[], spans:[]}; + //@ts-ignore + if (range.commonAncestorContainer.className == 'react-pdf__Page__textContent'){ //multiline highlighting case + obj = this.highlightNodes(range.commonAncestorContainer.childNodes) + } else{ //single line highlighting case + let parentDiv = range.commonAncestorContainer.parentElement + if (parentDiv){ + if (parentDiv.className == 'react-pdf__Page__textContent'){ //when highlight is overwritten + obj = this.highlightNodes(parentDiv.childNodes) + } else { + parentDiv.childNodes.forEach((child)=>{ + if (child.nodeName == 'SPAN'){ + //@ts-ignore + obj.parentDivs.push(parentDiv) + //@ts-ignore + child.id = "highlighted" + //@ts-ignore + obj.spans.push(child) + child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler + } + }) } - }) + } } + this.pageInfo.divs.push(obj); + } document.designMode = "off"; } + + highlightNodes = (nodes:NodeListOf<ChildNode>) => { + let temp = {parentDivs: [], spans: []} + nodes.forEach((div) => { + div.childNodes.forEach((child)=>{ + if (child.nodeName == 'SPAN'){ + //@ts-ignore + temp.parentDivs.push(div) + //@ts-ignore + child.id = "highlighted" + //@ts-ignore + temp.spans.push(child) + child.addEventListener("mouseover", this.onEnter); //adds mouseover annotation handler + } + }) + + }) + return temp; + } /** * when the cursor enters the highlight, it pops out annotation. ONLY WORKS FOR SINGLE DIV LINES */ + @observable private currAnno:any = [] @action onEnter = (e:any) => { let span:HTMLSpanElement = e.toElement; - this.currSpan = span; - if (e.toElement instanceof HTMLSpanElement){ - this.bool = true; - this.currSpan = span; - if(span.children.length == 0){ //this is why it only works for one div text lines... needs fix - if(span.offsetParent){ - let div = span.offsetParent; - //@ts-ignore - let divX = div.style.left - //@ts-ignore - let divY = div.style.top - //slicing "px" from the end - divX = divX.slice(0, divX.length - 2); //gets X of the DIV element (parent of Span) - divY = divY.slice(0, divY.length - 2); //gets Y of the DIV element (parent of Span) - let annotation = <Annotation key ={Utils.GenerateGuid()} Span = {this.currSpan} X = {divX} Y = {divY - 300} Highlights = {this.highlights}/> - this.annotations.push(annotation); - } - } + let index:any; + this.pageInfo.divs.forEach((obj:any) =>{ + obj.spans.forEach((element:any) =>{ + if (element == span) { + if (!index){ + index = this.pageInfo.divs.indexOf(obj); + } + } + }) + }) + + if (this.pageInfo.anno.length >= index + 1){ + if (this.currAnno.length == 0){ + this.currAnno.push(this.pageInfo.anno[index]); + } + }else{ + if (this.currAnno.length == 0){ //if there are no current annotation + let div = span.offsetParent; + //@ts-ignore + let divX = div.style.left + //@ts-ignore + let divY = div.style.top + //slicing "px" from the end + divX = divX.slice(0, divX.length - 2); //gets X of the DIV element (parent of Span) + divY = divY.slice(0, divY.length - 2); //gets Y of the DIV element (parent of Span) + let annotation = <Annotation key ={Utils.GenerateGuid()} Span = {span} X = {divX} Y = {divY - 300} Highlights = {this.pageInfo.divs} Annotations = {this.pageInfo.anno} CurrAnno = {this.currAnno}/> + this.pageInfo.anno.push(annotation); + this.currAnno.push(annotation); + } } + } /** @@ -244,7 +260,6 @@ export class PDFNode extends React.Component<FieldViewProps> { if (!document.execCommand("hiliteColor", false, color)) { this.makeEditableAndHighlight(color); } - //when the color is not the highlight color } catch (ex) { this.makeEditableAndHighlight(color) } @@ -268,6 +283,7 @@ export class PDFNode extends React.Component<FieldViewProps> { */ @action onPointerUp = (e:React.PointerEvent) => { + if (this._highlightToolOn){ this.highlight("rgba(76, 175, 80, 0.3)"); //highlights to this default color. this._highlightToolOn = false; @@ -289,7 +305,7 @@ export class PDFNode extends React.Component<FieldViewProps> { if (this._mainDiv.current){ let sticky = <Sticky key ={Utils.GenerateGuid()} Height = {height} Width = {width} X = {this.initX} Y = {this.initY}/> - this.stickies.push(sticky); + this.pageInfo.area.push(sticky); } this._toolOn = false; } @@ -303,18 +319,18 @@ export class PDFNode extends React.Component<FieldViewProps> { if (this._currTool != null){ this._currTool.style.backgroundColor = "grey"; } - this._highlightToolOn = false; + if (this._drawTool.current){ this._currTool = this._drawTool.current; if (this._drawToolOn){ this._drawToolOn = false; - document.removeEventListener("pointerdown", this.drawDown); - document.removeEventListener("pointerup", this.drawUp); - document.removeEventListener("pointermove", this.drawMove); + this._pdfCanvas.removeEventListener("pointerdown", this.drawDown); + this._pdfCanvas.removeEventListener("pointerup", this.drawUp); + this._pdfCanvas.removeEventListener("pointermove", this.drawMove); this._drawTool.current.style.backgroundColor = "grey"; } else { this._drawToolOn = true; - document.addEventListener("pointerdown", this.drawDown); + this._pdfCanvas.addEventListener("pointerdown", this.drawDown); this._drawTool.current.style.backgroundColor = "cyan"; } } @@ -346,42 +362,47 @@ export class PDFNode extends React.Component<FieldViewProps> { if (this._currTool != null){ this._currTool.style.backgroundColor = "grey"; } - if (this._highlightTool.current){ - this._currTool = this._drawTool.current; - if (this._highlightToolOn){ - this._highlightToolOn = false; - this._highlightTool.current.style.backgroundColor = "grey"; - } else { - this._highlightToolOn = true; - this.highlight("rgba(76, 175, 80, 0.3)"); - this._highlightTool.current.style.backgroundColor = "orange"; - } + if (this._highlightTool.current){ + this._currTool = this._drawTool.current; + if (this._highlightToolOn){ + this._highlightToolOn = false; + this._highlightTool.current.style.backgroundColor = "grey"; + } else { + this._highlightToolOn = true; + this._highlightTool.current.style.backgroundColor = "orange"; + } } } - + /** * renders whole lot of shets, including pdf, stickies, and annotations. */ + reHighlight = () =>{ + let div = document.getElementsByClassName("react-pdf__Page__textContent"); + if (div){ + + } + + } + + render() { return ( <div ref = {this._mainDiv} onPointerDown ={this.onPointerDown} onPointerUp = {this.onPointerUp} > - {this.stickies.filter( () => { //for loading stickies (area) - return this.stickies[this.stickies.length - 1] - }).map( (element: any) => { + + {this.pageInfo.area.filter( () => { + return this.pageInfo.area}).map((element: any) => { return element - }) - } - {this.annotations.filter( () => { //for loading annotations - return this.annotations[this.annotations.length - 1] - }).map( (element: any) => { - return element - }) - } + }) + } + {this.currAnno.map((element:any) => { + return element + })} <button onClick = {this.onPageBack}>{"<"}</button> <button onClick = {this.onPageForward}>{">"}</button> @@ -402,19 +423,21 @@ export class PDFNode extends React.Component<FieldViewProps> { this._mainDiv.current.childNodes.forEach((element) => { if (element.nodeName == "DIV"){ element.childNodes[0].childNodes.forEach((e) => { + if (e.nodeName == "CANVAS"){ + this._pdfCanvas = e; //@ts-ignore this._pdfContext = e.getContext("2d") - } + + } + }) } }) } this.numPage = page.transport.numPages - if (this.stickiesPerPage == null){ //only runs once, when stickiesPerPage is null - this.stickiesPerPage = [...Array(this.numPage)].map(() => Array(1)); - this.annotationsPerPage = [...Array(this.numPage).map(()=> Array(1))]; - this.highlightsPerPage = [...Array(this.numPage).map(() => Array(1))]; + if (this.perPage.length == 0){ //Makes sure it only runs once + this.perPage = [...Array(this.numPage)] } } } @@ -423,4 +446,5 @@ export class PDFNode extends React.Component<FieldViewProps> { </div> ); } + }
\ No newline at end of file |