diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/PDFBox.tsx | 112 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 61 |
3 files changed, 100 insertions, 75 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 075914e29..b90303f8c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -699,7 +699,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document Doc.UpdateDocumentExtensionForField(this.props.DataDoc || this.props.Document, this.props.fieldKey); return ( - <div className={"collectionfreeformview-container"} style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? "100%" : this.props.PanelHeight() }} ref={this.createDropTarget} onWheel={this.onPointerWheel} + <div className={"collectionfreeformview-container"} style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: !this.isAnnotationOverlay ? "100%" : this.props.PanelHeight() }} ref={this.createDropTarget} onWheel={this.onPointerWheel} onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu}> <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} isSelected={this.props.isSelected} addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox} setPreviewCursor={this.props.setPreviewCursor} diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 69e438d4f..30f4ce392 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked } from 'mobx'; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked, trace } from 'mobx'; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; @@ -19,6 +19,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); +import { CollectionSchemaBooleanCell } from '../collections/CollectionSchemaCells'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @@ -30,6 +31,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen private _keyValue: string = ""; private _valueValue: string = ""; private _scriptValue: string = ""; + private _searchString: string = ""; @observable private _searching: boolean = false; private _pdfViewer: PDFViewer | undefined; private _keyRef: React.RefObject<HTMLInputElement> = React.createRef(); @@ -58,8 +60,8 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen this._reactionDisposer && this._reactionDisposer(); } - public search(string: string) { - this._pdfViewer && this._pdfViewer.search(string); + public search(string: string, fwd: boolean) { + this._pdfViewer && this._pdfViewer.search(string, fwd); } public prevAnnotation() { this._pdfViewer && this._pdfViewer.prevAnnotation(); @@ -72,26 +74,19 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen this._pdfViewer = pdfViewer; } - public GetPage() { - return this._pdfViewer!.pdfViewer.currentPageNumber; - } - @action public BackPage() { - this._pdfViewer!.pdfViewer.scrollPageIntoView({ pageNumber: Math.max(1, this.GetPage() - 1) }); - this.props.Document.curPage = this.GetPage(); + this._pdfViewer!.pdfViewer.scrollPageIntoView({ pageNumber: Math.max(1, NumCast(this.props.Document.curPage) - 1) }); } @action public GotoPage = (p: number) => { - this._pdfViewer!.pdfViewer.scrollPageIntoView(p); - this.props.Document.curPage = this.GetPage(); + this._pdfViewer!.pdfViewer.scrollPageIntoView({ pageNumber: p }); } @action public ForwardPage() { - this._pdfViewer!.pdfViewer.scrollPageIntoView({ pageNumber: Math.min(this._pdfViewer!.pdfViewer.pagesCount, this.GetPage() + 1) }); - this.props.Document.curPage = this.GetPage(); + this._pdfViewer!.pdfViewer.scrollPageIntoView({ pageNumber: Math.min(this._pdfViewer!.pdfViewer.pagesCount, NumCast(this.props.Document.curPage) + 1) }); } @action @@ -121,49 +116,66 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen private newValueChange = (e: React.ChangeEvent<HTMLInputElement>) => this._valueValue = e.currentTarget.value; private newScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => this._scriptValue = e.currentTarget.value; + _isChildActive = false; + whenActiveChanged = (isActive: boolean) => { + this._isChildActive = isActive; + this.props.whenActiveChanged(isActive); + } + active = () => { + return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; + } searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value; - private _searchString: string = ""; + @observable _pageControls = false; settingsPanel() { + trace(); + let pageBtns = <> + <button className="pdfBox-overlayButton-iconCont" key="back" title="Page Back" + onPointerDown={(e) => e.stopPropagation()} + onClick={() => this.BackPage()} + style={{ left: 50, top: 5, height: "30px", position: "absolute", pointerEvents: "all" }}> + <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-left"} size="sm" /> + </button> + <button className="pdfBox-overlayButton-iconCont" key="fwd" title="Page Forward" + onPointerDown={(e) => e.stopPropagation()} + onClick={() => this.ForwardPage()} + style={{ left: 80, top: 5, height: "30px", position: "absolute", pointerEvents: "all" }}> + <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-right"} size="sm" /> + </button> + </> return !this.props.active() ? (null) : - (<> + (<div className="pdfBox-ui" onKeyDown={e => e.keyCode === KeyCodes.BACKSPACE || e.keyCode === KeyCodes.DELETE ? e.stopPropagation() : true} style={{ display: this.active() ? "flex" : "none" }}> <div className="pdfBox-overlayCont" key="cont" onPointerDown={(e) => e.stopPropagation()} style={{ bottom: 0, left: `${this._searching ? 0 : 100}%` }}> <button className="pdfBox-overlayButton" title="Open Search Bar" /> <input className="pdfBox-overlaySearchBar" placeholder="Search" onChange={this.searchStringChanged} - onKeyDown={(e: React.KeyboardEvent) => e.keyCode === KeyCodes.ENTER ? this.search(this._searchString) : e.keyCode === KeyCodes.BACKSPACE ? e.stopPropagation() : true} /> - <button title="Search" onClick={() => this.search(this._searchString)}> + onKeyDown={e => e.keyCode === KeyCodes.ENTER ? this.search(this._searchString, !e.shiftKey) : e.keyCode === KeyCodes.BACKSPACE ? e.stopPropagation() : true} /> + <button title="Search" onClick={e => this.search(this._searchString, !e.shiftKey)}> <FontAwesomeIcon icon="search" size="sm" color="white" /></button> + <button className="pdfBox-overlayButton-iconCont" title="Previous Annotation" + onClick={e => { e.stopPropagation(); this.prevAnnotation(); }} + onPointerDown={(e) => e.stopPropagation()} + style={{ left: 50, top: 5, height: "30px", position: "absolute" }}> + <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="sm" /> + </button> + <button className="pdfBox-overlayButton-iconCont" title="Next Annotation" + onClick={e => { e.stopPropagation(); this.nextAnnotation(); }} + onPointerDown={(e) => e.stopPropagation()} + style={{ left: 20, top: 5, height: "30px", position: "absolute" }}> + <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="sm" /> + </button> </div> <button className="pdfBox-overlayButton" key="search" onClick={action(() => this._searching = !this._searching)} title="Open Search Bar" - style={{ bottom: 8, right: 0, display: this.props.active() ? "flex" : "none" }}> + style={{ bottom: 8, right: 0 }}> <div className="pdfBox-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div> <div className="pdfBox-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> <FontAwesomeIcon style={{ color: "white", padding: 5 }} icon={this._searching ? "times" : "search"} size="3x" /></div> </button> - <button className="pdfBox-overlayButton-iconCont" title="Previous Annotation" - onClick={e => { e.stopPropagation(); this.prevAnnotation(); }} - onPointerDown={(e) => e.stopPropagation()} - style={{ left: 110, top: 5, height: "30px", position: "absolute", display: this.props.active() ? "flex" : "none" }}> - <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="sm" /> - </button> - <button className="pdfBox-overlayButton-iconCont" title="Next Annotation" - onClick={e => { e.stopPropagation(); this.nextAnnotation(); }} - onPointerDown={(e) => e.stopPropagation()} - style={{ left: 80, top: 5, height: "30px", position: "absolute", display: this.props.active() ? "flex" : "none" }}> - <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="sm" /> - </button> - <button className="pdfBox-overlayButton-iconCont" key="back" title="Page Back" - onPointerDown={(e) => e.stopPropagation()} - onClick={() => this.BackPage()} - style={{ left: 20, top: 5, height: "30px", position: "absolute", pointerEvents: "all", display: this.props.active() ? "flex" : "none" }}> - <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-left"} size="sm" /> - </button> - <button className="pdfBox-overlayButton-iconCont" key="fwd" title="Page Forward" - onPointerDown={(e) => e.stopPropagation()} - onClick={() => this.ForwardPage()} - style={{ left: 50, top: 5, height: "30px", position: "absolute", pointerEvents: "all", display: this.props.active() ? "flex" : "none" }}> - <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-right"} size="sm" /> - </button> + <span contentEditable={true} onInput={e => this.GotoPage(Number(e.currentTarget.textContent))} + style={{ left: 20, top: 5, height: "30px", width: "30px", position: "absolute", pointerEvents: "all" }} + onClick={action(() => this._pageControls = !this._pageControls)}> + {`${NumCast(this.props.Document.curPage)}`} + </span> + {this._pageControls ? pageBtns : (null)} <div className="pdfBox-settingsCont" key="settings" onPointerDown={(e) => e.stopPropagation()}> <button className="pdfBox-settingsButton" onClick={action(() => this._flyout = !this._flyout)} title="Open Annotation Settings" > <div className="pdfBox-settingsButton-arrow" style={{ transform: `scaleX(${this._flyout ? -1 : 1})` }} /> @@ -176,10 +188,8 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen Annotation View Settings </div> <div className="pdfBox-settingsFlyout-kvpInput"> - <input placeholder="Key" className="pdfBox-settingsFlyout-input" onChange={this.newKeyChange} - style={{ gridColumn: 1 }} ref={this._keyRef} /> - <input placeholder="Value" className="pdfBox-settingsFlyout-input" onChange={this.newValueChange} - style={{ gridColumn: 3 }} ref={this._valueRef} /> + <input placeholder="Key" className="pdfBox-settingsFlyout-input" onChange={this.newKeyChange} style={{ gridColumn: 1 }} ref={this._keyRef} /> + <input placeholder="Value" className="pdfBox-settingsFlyout-input" onChange={this.newValueChange} style={{ gridColumn: 3 }} ref={this._valueRef} /> </div> <div className="pdfBox-settingsFlyout-kvpInput"> <input placeholder="Custom Script" onChange={this.newScriptChange} style={{ gridColumn: "1 / 4" }} ref={this._scriptRef} /> @@ -196,7 +206,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen </div> </div> </div> - </>); + </div>); } loaded = (nw: number, nh: number, np: number) => { @@ -211,7 +221,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); - let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive"); + let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); return (!(pdfUrl instanceof PdfField) || !this._pdf ? <div>{`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}</div> : <div className={classname} onWheel={(e: React.WheelEvent) => e.stopPropagation()} onPointerDown={(e: React.PointerEvent) => { @@ -222,12 +232,12 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen }}> <PDFViewer {...this.props} pdf={this._pdf} url={pdfUrl.url.pathname} active={this.props.active} scrollTo={this.scrollTo} loaded={this.loaded} setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView} - renderDepth={this.props.renderDepth} - Document={this.props.Document} DataDoc={this.dataDoc} + renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth} + Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling} addDocTab={this.props.addDocTab} GoToPage={this.GotoPage} pinToPres={this.props.pinToPres} addDocument={this.props.addDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} - isSelected={this.props.isSelected} + isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged} fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} /> {this.settingsPanel()} </div>); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 783495e5a..848f1ddcd 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -34,6 +34,9 @@ interface IViewerProps { fieldExtensionDoc: Doc; fieldKey: string; fieldExt: string; + PanelWidth: () => number; + PanelHeight: () => number; + ContentScaling: () => number; renderDepth: number; isSelected: () => boolean; loaded: (nw: number, nh: number, np: number) => void; @@ -46,6 +49,7 @@ interface IViewerProps { setPdfViewer: (view: PDFViewer) => void; ScreenToLocalTransform: () => Transform; ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>; + whenActiveChanged: (isActive: boolean) => void; } /** @@ -62,6 +66,7 @@ export class PDFViewer extends React.Component<IViewerProps> { @observable private _marqueeY: number = 0; @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; + @observable private _marqueeing: boolean = false; public pdfViewer: any; private _isChildActive = false; @@ -74,7 +79,6 @@ export class PDFViewer extends React.Component<IViewerProps> { private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); private _selectionText: string = ""; private _marquee: React.RefObject<HTMLDivElement> = React.createRef(); - private _marqueeing: boolean = false; private _startX: number = 0; private _startY: number = 0; private _downX: number = 0; @@ -165,7 +169,7 @@ export class PDFViewer extends React.Component<IViewerProps> { @action setupPdfJsViewer = () => { document.addEventListener("pagesinit", () => this.pdfViewer.currentScaleValue = 1); - document.addEventListener("pagerendered", () => console.log("rendered")); + // document.addEventListener("pagerendered", () => console.log("rendered")); // bcz: works, but not needed except to debug var pdfLinkService = new PDFJSViewer.PDFLinkService(); let pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, @@ -249,19 +253,23 @@ export class PDFViewer extends React.Component<IViewerProps> { @action prevAnnotation = () => { this.Index = Math.max(this.Index - 1, 0); - let scrollToAnnotation = this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]; - this.allAnnotations.forEach(d => Doc.UnBrushDoc(d)); - Doc.BrushDoc(scrollToAnnotation); - this.props.scrollTo(NumCast(scrollToAnnotation.y)); + this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]); } @action nextAnnotation = () => { this.Index = Math.min(this.Index + 1, this.allAnnotations.length - 1); - let scrollToAnnotation = this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]; + this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]); + } + + @action + scrollToAnnotation = (scrollToAnnotation: Doc) => { this.allAnnotations.forEach(d => Doc.UnBrushDoc(d)); + let windowHgt = this.props.PanelHeight() / this.props.ContentScaling(); + let scrollRange = this._mainCont.current!.scrollHeight - windowHgt; + let pgScroll = scrollRange / this._pageSizes.length; + this._mainCont.current!.scrollTo(0, NumCast(scrollToAnnotation.y) - pgScroll / 2); Doc.BrushDoc(scrollToAnnotation); - this.props.scrollTo(NumCast(scrollToAnnotation.y)); } sendAnnotations = (page: number) => { @@ -278,6 +286,11 @@ export class PDFViewer extends React.Component<IViewerProps> { } } + @action + onScroll = (e: React.UIEvent<HTMLElement>) => { + this.props.Document.curPage = this.pdfViewer.currentPageNumber; + } + // get the page index that the vertical offset passed in is on getPageFromScroll = (vOffset: number) => { let index = 0; @@ -288,10 +301,6 @@ export class PDFViewer extends React.Component<IViewerProps> { return index; } - getScrollFromPage = (index: number): number => { - return numberRange(Math.min(this.props.pdf.numPages, index)).reduce((counter, i) => counter + this._pageSizes[i].height, 0); - } - @action createAnnotation = (div: HTMLDivElement, page: number) => { if (this._annotationLayer.current) { @@ -311,11 +320,14 @@ export class PDFViewer extends React.Component<IViewerProps> { } @action - search = (searchString: string) => { - if (this.pdfViewer._pageViewsReady) { + search = (searchString: string, fwd: boolean) => { + if (!searchString) { + fwd ? this.nextAnnotation() : this.prevAnnotation(); + } + else if (this.pdfViewer._pageViewsReady) { this.pdfViewer.findController.executeCommand('findagain', { caseSensitive: false, - findPrevious: undefined, + findPrevious: !fwd, highlightAll: true, phraseSearch: true, query: searchString @@ -325,7 +337,7 @@ export class PDFViewer extends React.Component<IViewerProps> { let executeFind = () => { this.pdfViewer.findController.executeCommand('find', { caseSensitive: false, - findPrevious: undefined, + findPrevious: !fwd, highlightAll: true, phraseSearch: true, query: searchString @@ -383,6 +395,7 @@ export class PDFViewer extends React.Component<IViewerProps> { this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth); this._marqueeY = Math.min(this._startY, this._startY + this._marqueeHeight); this._marqueeWidth = Math.abs(this._marqueeWidth); + this._marqueeHeight = Math.abs(this._marqueeHeight); e.stopPropagation(); e.preventDefault(); } @@ -399,7 +412,6 @@ export class PDFViewer extends React.Component<IViewerProps> { for (let i = 0; i < clientRects.length; i++) { let rect = clientRects.item(i); if (rect/* && rect.width !== this._mainCont.current.getBoundingClientRect().width && rect.height !== this._mainCont.current.getBoundingClientRect().height / this.props.pdf.numPages*/) { - let page = this.getPageFromScroll(rect.top); let scaleY = this._mainCont.current.offsetHeight / boundingRect.height; let scaleX = this._mainCont.current.offsetWidth / boundingRect.width; if (rect.width !== this._mainCont.current.clientWidth) { @@ -552,26 +564,29 @@ export class PDFViewer extends React.Component<IViewerProps> { this._setPreviewCursor = func; } onClick = (e: React.MouseEvent) => { - this._setPreviewCursor && this._marqueeing && Math.abs(e.clientX - this._downX) < 3 && Math.abs(e.clientY - this._downY) < 3 && + this._setPreviewCursor && + this._marqueeing && + Math.abs(e.clientX - this._downX) < 3 && + Math.abs(e.clientY - this._downY) < 3 && this._setPreviewCursor(e.clientX, e.clientY); } whenActiveChanged = (isActive: boolean) => { this._isChildActive = isActive; - //this.props.whenActiveChanged(isActive); // bcz: is this needed here? + this.props.whenActiveChanged(isActive); // bcz: is this needed here? } active = () => { return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; } render() { - return (<div className="pdfViewer-viewer" onPointerDown={this.onPointerDown} onWheel={(e) => e.stopPropagation()} onClick={this.onClick} ref={this._mainCont}> + return (<div className="pdfViewer-viewer" onScroll={this.onScroll} onPointerDown={this.onPointerDown} onWheel={(e) => e.stopPropagation()} onClick={this.onClick} ref={this._mainCont}> <div className="pdfViewer-text" ref={this._viewer} /> - <div className="pdfViewer-dragAnnotationBox" ref={this._marquee} + {!this._marqueeing ? (null) : <div className="pdfViewer-dragAnnotationBox" ref={this._marquee} style={{ left: `${this._marqueeX}px`, top: `${this._marqueeY}px`, width: `${this._marqueeWidth}px`, height: `${this._marqueeHeight}px`, - border: `${this._marqueeWidth === 0 ? "" : "10px dashed black"}` + border: `${this._marqueeWidth === 0 ? "" : "2px dashed black"}` }}> - </div> + </div>} <div className="pdfViewer-annotationLayer" style={{ height: NumCast(this.props.Document.nativeHeight) }} ref={this._annotationLayer}> {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => <Annotation {...this.props} anno={anno} key={`${anno[Id]}-annotation`} />)} |