diff options
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 260 | ||||
-rw-r--r-- | src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 16 | ||||
-rw-r--r-- | src/client/views/nodes/PDFBox.scss | 12 | ||||
-rw-r--r-- | src/client/views/nodes/PDFBox.tsx | 64 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.scss | 7 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 184 |
6 files changed, 211 insertions, 332 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 36e62842c..438529596 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,7 +1,7 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons"; -import { action, computed, IReactionDisposer, observable, reaction, trace } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction, trace, ObservableMap, untracked } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCastAsync, Field, FieldResult, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; @@ -60,6 +60,17 @@ export interface ViewDefBounds { z?: number; width: number; height: number; + transition?: string; +} + +interface PivotData { + type: string; + text: string; + x: number; + y: number; + width: number; + height: number; + fontSize: number; } export interface ViewDefResult { @@ -67,116 +78,6 @@ export interface ViewDefResult { bounds?: ViewDefBounds; } -export namespace PivotView { - - export interface PivotData { - type: string; - text: string; - x: number; - y: number; - width: number; - height: number; - fontSize: number; - } - - export const elements = (target: CollectionFreeFormView) => { - let collection = target.Document; - const field = StrCast(collection.pivotField) || "title"; - const width = NumCast(collection.pivotWidth) || 200; - const groups = new Map<FieldResult<Field>, Doc[]>(); - - for (const doc of target.childDocs) { - const val = doc[field]; - if (val === undefined) continue; - - const l = groups.get(val); - if (l) { - l.push(doc); - } else { - groups.set(val, [doc]); - } - } - - let minSize = Infinity; - - groups.forEach((val, key) => minSize = Math.min(minSize, val.length)); - - const numCols = NumCast(collection.pivotNumColumns) || Math.ceil(Math.sqrt(minSize)); - const fontSize = NumCast(collection.pivotFontSize); - - const docMap = new Map<Doc, ViewDefBounds>(); - const groupNames: PivotData[] = []; - - let x = 0; - groups.forEach((val, key) => { - let y = 0; - let xCount = 0; - groupNames.push({ - type: "text", - text: String(key), - x, - y: width + 50, - width: width * 1.25 * numCols, - height: 100, fontSize: fontSize - }); - for (const doc of val) { - docMap.set(doc, { - x: x + xCount * width * 1.25, - y: -y, - width, - height: width - }); - xCount++; - if (xCount >= numCols) { - xCount = 0; - y += width * 1.25; - } - } - x += width * 1.25 * (numCols + 1); - }); - - let elements = target.viewDefsToJSX(groupNames); - let docViews = target.childDocs.reduce((prev, doc) => { - let minim = BoolCast(doc.isMinimized); - if (minim === undefined || !minim) { - let defaultPosition = (): ViewDefBounds => { - return { - x: NumCast(doc.x), - y: NumCast(doc.y), - z: NumCast(doc.z), - width: NumCast(doc.width), - height: NumCast(doc.height) - }; - }; - const pos = docMap.get(doc) || defaultPosition(); - prev.push({ - ele: <CollectionFreeFormDocumentView - key={doc[Id]} - x={pos.x} - y={pos.y} - width={pos.width} - height={pos.height} - transition={"transform 1s"} - jitterRotation={NumCast(target.props.Document.jitterRotation)} - {...target.getChildDocumentViewProps(doc)} - />, - bounds: { - x: pos.x, - y: pos.y, - z: pos.z, - width: NumCast(pos.width), - height: NumCast(pos.height) - } - }); - } - return prev; - }, elements); - - return docViews; - }; - -} - type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchema, typeof positionSchema, typeof pageSchema]>; const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema); @@ -300,7 +201,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let y = (z ? ypo : yp) - de.data.offset[1]; let dropX = NumCast(de.data.droppedDocuments[0].x); let dropY = NumCast(de.data.droppedDocuments[0].y); - de.data.droppedDocuments.forEach(d => { + de.data.droppedDocuments.forEach(action((d: Doc) => { d.x = x + NumCast(d.x) - dropX; d.y = y + NumCast(d.y) - dropY; if (!NumCast(d.width)) { @@ -312,7 +213,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { d.height = nw && nh ? nh / nw * NumCast(d.width) : 300; } this.bringToFront(d); - }); + })); de.data.droppedDocuments.length === 1 && this.updateCluster(de.data.droppedDocuments[0]); } @@ -645,6 +546,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { getScale = () => this.Document.scale ? this.Document.scale : 1; getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { + trace(); return { DataDoc: childData, Document: childLayout, @@ -742,9 +644,99 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - @computed.struct - get elements() { - if (this.Document.usePivotLayout) return PivotView.elements(this); + lookupLayout = (doc: Doc, dataDoc?: Doc) => { + let data: any = undefined; + let compute = this.Document.usePivotLayout ? this.doPivotComputation.map : this._doComputation.map; + compute.forEach((value: any, key: { layout: Doc, data?: Doc }) => { + if (key.layout === doc && key.data === dataDoc) { + data = value; + } + }); + return data ? { x: data.x, y: data.y, z: data.z, width: data.width, height: data.height, transition: data.transition } : undefined; + } + + @computed get doPivotComputation() { + let layoutPoolData: Map<{ layout: Doc, data?: Doc }, any> = new Map(); + const field = StrCast(this.props.Document.pivotField) || "title"; + const width = NumCast(this.props.Document.pivotWidth) || 200; + const groups = new Map<FieldResult<Field>, Doc[]>(); + + for (const doc of this.childDocs) { + const val = doc[field]; + if (val === undefined) continue; + + const l = groups.get(val); + if (l) { + l.push(doc); + } else { + groups.set(val, [doc]); + } + } + + let minSize = Infinity; + + groups.forEach((val, key) => minSize = Math.min(minSize, val.length)); + + const numCols = NumCast(this.props.Document.pivotNumColumns) || Math.ceil(Math.sqrt(minSize)); + const fontSize = NumCast(this.props.Document.pivotFontSize); + + const docMap = new Map<Doc, ViewDefBounds>(); + const groupNames: PivotData[] = []; + + let x = 0; + groups.forEach((val, key) => { + let y = 0; + let xCount = 0; + groupNames.push({ + type: "text", + text: String(key), + x, + y: width + 50, + width: width * 1.25 * numCols, + height: 100, fontSize: fontSize + }); + for (const doc of val) { + docMap.set(doc, { + x: x + xCount * width * 1.25, + y: -y, + width, + height: width, + }); + xCount++; + if (xCount >= numCols) { + xCount = 0; + y += width * 1.25; + } + } + x += width * 1.25 * (numCols + 1); + }); + + let elements = this.viewDefsToJSX(groupNames); + let pairs = this.childLayoutPairs; + pairs.map((pair, i) => { + let minim = BoolCast(pair.layout.isMinimized); + if (minim === undefined || !minim) { + let defaultPosition = (): ViewDefBounds => { + return { + x: NumCast(pair.layout.x), + y: NumCast(pair.layout.y), + z: NumCast(pair.layout.z), + width: NumCast(pair.layout.width), + height: NumCast(pair.layout.height), + transition: "transform 1s" + }; + }; + const pos = docMap.get(pair.layout) || defaultPosition(); + layoutPoolData.set(pair, { transition: "transform 1s", ...pos }); + } + }); + return { map: layoutPoolData, elements: elements }; + }; + + + @computed + get _doComputation() { + let layoutPoolData: Map<{ layout: Doc, data?: Doc }, any> = new Map(); let curPage = FieldValue(this.Document.curPage, -1); const initScript = this.Document.arrangeInit; let state: any = undefined; @@ -759,24 +751,40 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { elements = this.viewDefsToJSX(views); } } - let docviews = pairs.reduce((prev, pair) => { + pairs.map((pair, i) => { var page = NumCast(pair.layout.page, -1); if (!pair.layout.isMinimized && ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1)) { - const pos = this.getCalculatedPositions({ doc: pair.layout, index: prev.length, collection: this.Document, docs: pairs.map(pair => pair.layout), state }); + const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: pairs.map(pair => pair.layout), state }); state = pos.state === undefined ? state : pos.state; + layoutPoolData.set(pair, pos); + } + }); + return { map: layoutPoolData, elements: elements }; + } + + @computed + get doComputation() { + let dc = this.Document.usePivotLayout ? this.doPivotComputation : this._doComputation; + let curPage = FieldValue(this.Document.curPage, -1); + let pairs = this.childLayoutPairs; + let docviews = pairs.reduce((prev, pair) => { + var page = NumCast(pair.layout.page, -1); + if (!pair.layout.isMinimized && ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1)) { prev.push({ - ele: <CollectionFreeFormDocumentView key={pair.layout[Id]} + ele: <CollectionFreeFormDocumentView key={pair.layout[Id]} dataProvider={this.lookupLayout} ruleProvider={this.Document.isRuleProvider ? this.props.Document : this.props.ruleProvider} - jitterRotation={NumCast(this.props.Document.jitterRotation)} - transition={pos.transition} x={pos.x} y={pos.y} width={pos.width} height={pos.height} - {...this.getChildDocumentViewProps(pair.layout, pair.data)} />, - bounds: { x: pos.x || 0, y: pos.y || 0, z: pos.z, width: pos.width || 0, height: pos.height || 0 } + jitterRotation={NumCast(this.props.Document.jitterRotation)} {...this.getChildDocumentViewProps(pair.layout, pair.data)} />, + bounds: this.lookupLayout(pair.layout, pair.data) }); } return prev; - }, elements); + }, dc.elements); - return docviews; + return { map: dc.map, elements: docviews }; + } + @computed.struct + get elements() { + return this.doComputation.elements; } @computed.struct @@ -945,7 +953,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY} easing={easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}> <CollectionFreeFormLinksView {...this.props} key="freeformLinks"> - <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={this._inkKey} > + <InkingCanvas getScreenTransform={this.getTransform} Document={this.props.Document} AnnotationDocument={this.fieldExtensionDoc} inkFieldKey={"ink"} > {this.childViews} </InkingCanvas> </CollectionFreeFormLinksView> diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 9685f9bca..0d9ace473 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -12,6 +12,7 @@ import { Doc, WidthSym, HeightSym } from "../../../new_fields/Doc"; import { random } from "animejs"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { + dataProvider?: (doc: Doc, dataDoc?: Doc) => { x: number, y: number, width: number, height: number, z: number, transition?: string } | undefined x?: number; y?: number; width?: number; @@ -32,11 +33,12 @@ export const PositionDocument = makeInterface(documentSchema, positionSchema); @observer export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) { _disposer: IReactionDisposer | undefined = undefined; - @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; } - @computed get X() { return this._animPos !== undefined ? this._animPos[0] : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; } - @computed get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; } - @computed get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.Document[WidthSym](); } - @computed get height() { return this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.Document[HeightSym](); } + get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; } + get X() { return this._animPos !== undefined ? this._animPos[0] : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : this.Document.x || 0; } + get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : this.Document.y || 0; } + get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.dataProvider && this.dataProvider ? this.dataProvider.width : this.props.Document[WidthSym](); } + get height() { return this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.props.Document[HeightSym](); } + @computed get dataProvider() { return this.props.dataProvider && this.props.dataProvider(this.props.Document, this.props.DataDoc) ? this.props.dataProvider(this.props.Document, this.props.DataDoc) : undefined; } @computed get nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); } @computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); } @computed get scaleToOverridingWidth() { return this.width / FieldValue(this.Document.width, this.width); } @@ -99,6 +101,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF @observable _animPos: number[] | undefined = undefined; render() { + console.log("this.props =" + this.props.dataProvider); + trace(); return ( <div className="collectionFreeFormDocumentView-container" style={{ @@ -110,7 +114,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF StrCast(this.layoutDoc.boxShadow, ""), borderRadius: this.borderRounding(), transform: this.transform, - transition: this.Document.isAnimating !== undefined ? ".5s ease-in" : this.props.transition ? this.props.transition : StrCast(this.layoutDoc.transition), + transition: this.Document.isAnimating !== undefined ? ".5s ease-in" : this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition), width: this.width, height: this.height, zIndex: this.Document.zIndex || 0, diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index c88a94c28..cbea47e20 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -5,9 +5,6 @@ height: 100%; overflow-y: scroll; overflow-x: hidden; - .pdfBox-scrollHack { - pointer-events: none; - } } .pdfBox-cont { @@ -22,10 +19,11 @@ .pdfBox-cont-interactive { pointer-events: all; - .pdfPage-textlayer { - span { - pointer-events: all !important; - user-select: text; + .pdfViewer-text { + .textLayer { + span { + user-select: text; + } } } } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 764051d62..d15f2b82c 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -46,16 +46,12 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen componentDidMount() { this.props.setPdfBox && this.props.setPdfBox(this); - this.props.Document.curPage = ComputedField.MakeFunction("Math.floor(Number(this.panY) / Number(this.nativeHeight) + 1)"); + this.props.Document.curPage = 1; // ComputedField.MakeFunction("Math.floor(Number(this.panY) / Number(this.nativeHeight) + 1)"); const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); if (pdfUrl instanceof PdfField) { Pdfjs.getDocument(pdfUrl.url.pathname).promise.then(pdf => runInAction(() => this._pdf = pdf)); } - this._reactionDisposer = reaction( - () => this.Document.panY, - () => this._mainCont.current && this._mainCont.current.scrollTo({ top: this.Document.panY || 0, behavior: "auto" }) - ); } componentWillUnmount() { @@ -63,36 +59,36 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen } public GetPage() { - return Math.floor((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1; + return 1;//Math.floor((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1; } @action public BackPage() { - let cp = Math.ceil((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1; - cp = cp - 1; - if (cp > 0) { - this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0); - } + // let cp = Math.ceil((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1; + // cp = cp - 1; + // if (cp > 0) { + // this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0); + // } } @action public GotoPage = (p: number) => { - if (p > 0 && p <= NumCast(this.dataDoc.numPages)) { - this.Document.panY = (p - 1) * (this.Document.nativeHeight || 0); - } + // if (p > 0 && p <= NumCast(this.dataDoc.numPages)) { + // this.Document.panY = (p - 1) * (this.Document.nativeHeight || 0); + // } } @action public ForwardPage() { - let cp = this.GetPage() + 1; - if (cp <= NumCast(this.dataDoc.numPages)) { - this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0); - } + // let cp = this.GetPage() + 1; + // if (cp <= NumCast(this.dataDoc.numPages)) { + // this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0); + // } } @action setPanY = (y: number) => { - this.Document.panY = y; + //this.Document.panY = y; } @action @@ -120,8 +116,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen settingsPanel() { return !this.props.active() ? (null) : (<div className="pdfBox-settingsCont" onPointerDown={(e) => e.stopPropagation()}> - <button className="pdfBox-settingsButton" onClick={action(() => this._flyout = !this._flyout)} title="Open Annotation Settings" - style={{ marginTop: `${this.Document.panY || 0}px` }}> + <button className="pdfBox-settingsButton" onClick={action(() => this._flyout = !this._flyout)} title="Open Annotation Settings" > <div className="pdfBox-settingsButton-arrow" style={{ borderTop: `25px solid ${this._flyout ? "#121721" : "transparent"}`, @@ -161,38 +156,31 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen } loaded = (nw: number, nh: number, np: number) => { + // nh *= .33.33333; + // nw *= 1.33333; this.dataDoc.numPages = np; if (!this.Document.nativeWidth || !this.Document.nativeHeight || !this.Document.scrollHeight) { let oldaspect = (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1); this.Document.nativeWidth = nw; this.Document.nativeHeight = this.Document.nativeHeight ? nw * oldaspect : nh; this.Document.height = this.Document[WidthSym]() * (nh / nw); - this.Document.scrollHeight = np * this.Document.nativeHeight; - } - } - - @action - onScroll = (e: React.UIEvent<HTMLDivElement>) => { - if (e.currentTarget && this.props.ContainingCollectionDoc) { - this.props.Document.panTransformType = "None"; - this.Document.panY = e.currentTarget.scrollTop; } } - render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (!(pdfUrl instanceof PdfField) || !this._pdf ? <div>{`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}</div> : - <div className={classname} - onScroll={this.onScroll} - style={{ marginTop: `${(this.Document.panY || 0)}px` }} - ref={this._mainCont}> - <div className="pdfBox-scrollHack" style={{ height: NumCast(this.props.Document.scrollHeight) + ((this.Document.nativeHeight || 0) - (this.Document.nativeHeight || 0) / (this.Document.scale || 1)) }} /> - <PDFViewer pdf={this._pdf} url={pdfUrl.url.pathname} active={this.props.active} scrollTo={this.scrollTo} loaded={this.loaded} panY={this.Document.panY || 0} + <div className={classname} ref={this._mainCont} onWheel={(e: React.WheelEvent) => e.stopPropagation()} onPointerDown={(e: React.PointerEvent) => { + let hit = document.elementFromPoint(e.clientX, e.clientY); + if (hit && hit.localName === "span") { + e.button === 0 && e.stopPropagation(); + } + }}> + <PDFViewer pdf={this._pdf} url={pdfUrl.url.pathname} active={this.props.active} scrollTo={this.scrollTo} loaded={this.loaded} Document={this.props.Document} DataDoc={this.dataDoc} - addDocTab={this.props.addDocTab} setPanY={this.setPanY} GoToPage={this.GotoPage} + addDocTab={this.props.addDocTab} GoToPage={this.GotoPage} pinToPres={this.props.pinToPres} addDocument={this.props.addDocument} fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} /> {this.settingsPanel()} diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index a2f3911c5..fdfbde457 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -2,6 +2,9 @@ .pdfViewer-viewer { pointer-events:inherit; width: 100%; + height:100%; + position: absolute; + overflow: scroll; .pdfViewer-visibleElements { .pdfPage-cont { .pdfPage-textLayer { @@ -19,8 +22,10 @@ } } } + .page { + position: relative; + } .pdfViewer-text { - transform: scale(1.5); transform-origin: top left; } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index c94b4e3a4..4f1d3f07f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -5,7 +5,7 @@ import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import * as rp from "request-promise"; import { Dictionary } from "typescript-collections"; -import { Doc, DocListCast, FieldResult } from "../../../new_fields/Doc"; +import { Doc, DocListCast, FieldResult, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { ScriptField } from "../../../new_fields/ScriptField"; @@ -32,10 +32,8 @@ interface IViewerProps { fieldExtensionDoc: Doc; fieldKey: string; loaded: (nw: number, nh: number, np: number) => void; - panY: number; scrollTo: (y: number) => void; active: () => boolean; - setPanY?: (n: number) => void; GoToPage?: (n: number) => void; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; pinToPres: (document: Doc) => void; @@ -47,16 +45,13 @@ interface IViewerProps { */ @observer export class PDFViewer extends React.Component<IViewerProps> { - @observable.shallow private _visibleElements: JSX.Element[] = []; // _visibleElements is the array of JSX elements that gets rendered - @observable private _isPage: string[] = [];// _isPage is an array that tells us whether or not an index is rendered as a page or as a placeholder - @observable private _pageSizes: { width: number, height: number }[] = []; + @observable public _pageSizes: { width: number, height: number }[] = []; @observable private _annotations: Doc[] = []; @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>(); @observable private _script: CompiledScript = CompileScript("return true") as CompiledScript; @observable private _searching: boolean = false; @observable private Index: number = -1; - private _pageBuffer: number = 1; private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; @@ -68,16 +63,6 @@ export class PDFViewer extends React.Component<IViewerProps> { private _searchString: string = ""; private _selectionText: string = ""; - @computed get panY(): number { return this.props.panY; } - - // startIndex: where to start rendering pages - @computed get startIndex(): number { return Math.max(0, this.getPageFromScroll(this.panY) - this._pageBuffer); } - - // endIndex: where to end rendering pages - @computed get endIndex(): number { - return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.panY + (this._pageSizes[0] ? this._pageSizes[0].height : 0)) + this._pageBuffer); - } - @computed get allAnnotations() { return DocListCast(this.props.fieldExtensionDoc.annotations).filter( anno => this._script.run({ this: anno }, console.log, true).result); @@ -87,16 +72,9 @@ export class PDFViewer extends React.Component<IViewerProps> { return this._annotations.filter(anno => this._script.run({ this: anno }, console.log, true).result); } - componentDidUpdate = (prevProps: IViewerProps) => this.panY !== prevProps.panY && this.renderPages(); - componentDidMount = async () => { await this.initialLoad(); - this._reactionDisposer = reaction( - () => [this.props.active(), this.startIndex, this._pageSizes.length ? this.endIndex : 0], - () => this.renderPages(), - { fireImmediately: true }); - this._annotationReactionDisposer = reaction( () => this.props.fieldExtensionDoc && DocListCast(this.props.fieldExtensionDoc.annotations), annotations => annotations && annotations.length && this.renderAnnotations(annotations, true), @@ -117,6 +95,8 @@ export class PDFViewer extends React.Component<IViewerProps> { document.removeEventListener("copy", this.copy); document.addEventListener("copy", this.copy); + + setTimeout(() => this.toggleSearch(undefined as any), 1000); } componentWillUnmount = () => { @@ -154,27 +134,16 @@ export class PDFViewer extends React.Component<IViewerProps> { @action initialLoad = async () => { if (this._pageSizes.length === 0) { - this._isPage = Array<string>(this.props.pdf.numPages); this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); - this._visibleElements = Array<JSX.Element>(this.props.pdf.numPages); await Promise.all(this._pageSizes.map<Pdfjs.PDFPromise<any>>((val, i) => this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => { this._pageSizes.splice(i, 1, { - width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]) * scale, - height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]) * scale + width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]), + height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]) }); - this._visibleElements.splice(i, 1, - <div key={`${this.props.url}-placeholder-${i + 1}`} className="pdfviewer-placeholder" - style={{ width: this._pageSizes[i].width, height: this._pageSizes[i].height }}> - "PAGE IS LOADING... " - </div>); - this.getPlaceholderPage(i); + i === this.props.pdf.numPages - 1 && this.props.loaded((page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]), + (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]), i); })))); - this.props.loaded(Math.max(...this._pageSizes.map(i => i.width)), this._pageSizes[0].height, this.props.pdf.numPages); - - let startY = NumCast(this.props.Document.startY, NumCast(this.props.Document.panY)); - this.props.setPanY && this.props.setPanY(startY); - this.props.scrollTo(startY); } } @@ -231,61 +200,6 @@ export class PDFViewer extends React.Component<IViewerProps> { } @action - getPlaceholderPage = (page: number) => { - if (this._isPage[page] !== "none") { - this._isPage[page] = "none"; - this._visibleElements[page] = ( - <div key={`${this.props.url}-placeholder-${page + 1}`} className="pdfviewer-placeholder" - style={{ width: this._pageSizes[page].width, height: this._pageSizes[page].height }}> - "PAGE IS LOADING... " - </div>); - } - } - - @action - getRenderedPage = (page: number) => { - if (this._isPage[page] !== "page") { - this._isPage[page] = "page"; - this._visibleElements[page] = (<Page {...this.props} - size={this._pageSizes[page]} - numPages={this.props.pdf.numPages} - setSelectionText={this.setSelectionText} - page={page} - key={`${this.props.url}-rendered-${page + 1}`} - name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`} - pageLoaded={this.pageLoaded} - renderAnnotations={this.renderAnnotations} - createAnnotation={this.createAnnotation} - sendAnnotations={this.receiveAnnotations} - makeAnnotationDocuments={this.makeAnnotationDocument} - getScrollFromPage={this.getScrollFromPage} />); - } - } - - // change the address to be the file address of the PNG version of each page - // file address of the pdf - @action - getPageImage = async (page: number) => { - if (this._isPage[page] !== "image") { - this._isPage[page] = "image"; - try { - let res = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${page + 1}.PNG`))); - runInAction(() => this._visibleElements[page] = - <img key={res.path} src={res.path} onError={() => this.getRenderedPage(page)} - style={{ width: `${parseInt(res.width) * scale}px`, height: `${parseInt(res.height) * scale}px` }} />); - } catch (e) { - console.log(e); - } - } - } - - renderPages = () => { - numberRange(this.props.pdf.numPages).filter(p => this._isPage[p] !== undefined).map(i => - (i < this.startIndex || i > this.endIndex) ? this.getPlaceholderPage(i) : // pages outside of the pdf use empty stand-in divs - this.props.active() ? this.getRenderedPage(i) : this.getPageImage(i)); - } - - @action renderAnnotations = (annotations: Doc[], removeOldAnnotations: boolean): void => { if (removeOldAnnotations) { this._annotations = annotations; @@ -391,43 +305,44 @@ export class PDFViewer extends React.Component<IViewerProps> { @action toggleSearch = (e: React.MouseEvent) => { - e.stopPropagation(); + e && e.stopPropagation(); this._searching = !this._searching; if (this._searching) { if (!this._pdfFindController && this._mainCont.current && this._viewer.current && !this._pdfViewer) { - let simpleLinkService = new SimpleLinkService(this); + document.addEventListener("pagesinit", () => this._pdfViewer.currentScaleValue = this.props.Document[WidthSym]() / this._pageSizes[0].width); + document.addEventListener("pagerendered", () => console.log("rendered")); + var pdfLinkService = new PDFJSViewer.PDFLinkService(); + this._pdfFindController = new PDFJSViewer.PDFFindController({ + linkService: pdfLinkService, + }); this._pdfViewer = new PDFJSViewer.PDFViewer({ container: this._mainCont.current, viewer: this._viewer.current, - linkService: simpleLinkService - }) - simpleLinkService.setPDFJSview(this._pdfViewer); - this._mainCont.current.addEventListener("pagesinit", () => this._pdfViewer.currentScaleValue = 1); - this._mainCont.current.addEventListener("pagerendered", () => console.log("rendered")); + linkService: pdfLinkService, + findController: this._pdfFindController, + renderer: "svg" + }); + pdfLinkService.setViewer(this._pdfViewer); + pdfLinkService.setDocument(this.props.pdf, null); this._pdfViewer.setDocument(this.props.pdf); - this._pdfFindController = new PDFJSViewer.PDFFindController(this._pdfViewer); - this._pdfViewer.findController = this._pdfFindController; } } } - @computed get visibleElementWrapper() { - trace(); - return this._visibleElements; - } render() { - return (<div className="pdfViewer-viewer" ref={this._mainCont} > - <div className="pdfViewer-visibleElements" style={this._searching ? { position: "absolute", top: 0 } : {}}> - {this.visibleElementWrapper} + trace(); + let scaling = this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width / this.props.Document[WidthSym]() : 1; + return (<div className="pdfViewer-viewer" ref={this._mainCont} > + <div className="pdfViewer-text" key="viewerText" style={{ transform: `scale(${scaling})` }} > + <div key="viewerReal" ref={this._viewer} /> </div> - <div className="pdfViewer-text" ref={this._viewer} /> <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`} />)} </div> <div className="pdfViewer-overlayCont" onPointerDown={(e) => e.stopPropagation()} - style={{ bottom: -this.props.panY, left: `${this._searching ? 0 : 100}%` }}> + style={{ bottom: 0, left: `${this._searching ? 0 : 100}%` }}> <button className="pdfViewer-overlayButton" title="Open Search Bar" /> <input className="pdfViewer-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} /> @@ -435,17 +350,17 @@ export class PDFViewer extends React.Component<IViewerProps> { <FontAwesomeIcon icon="search" size="3x" color="white" /></button> </div> <button className="pdfViewer-overlayButton" onClick={this.prevAnnotation} title="Previous Annotation" - style={{ bottom: -this.props.panY + 280, right: 10, display: this.props.active() ? "flex" : "none" }}> + style={{ bottom: 280, right: 10, display: this.props.active() ? "flex" : "none" }}> <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="3x" /></div> </button> <button className="pdfViewer-overlayButton" onClick={this.nextAnnotation} title="Next Annotation" - style={{ bottom: -this.props.panY + 200, right: 10, display: this.props.active() ? "flex" : "none" }}> + style={{ bottom: 200, right: 10, display: this.props.active() ? "flex" : "none" }}> <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="3x" /></div> </button> <button className="pdfViewer-overlayButton" onClick={this.toggleSearch} title="Open Search Bar" - style={{ bottom: -this.props.panY + 10, right: 0, display: this.props.active() ? "flex" : "none" }}> + style={{ bottom: 10, right: 0, display: this.props.active() ? "flex" : "none" }}> <div className="pdfViewer-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div> <div className="pdfViewer-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}> <FontAwesomeIcon style={{ color: "white" }} icon={this._searching ? "times" : "search"} size="3x" /></div> @@ -455,42 +370,3 @@ export class PDFViewer extends React.Component<IViewerProps> { } export enum AnnotationTypes { Region } - -class SimpleLinkService { - _viewer: PDFViewer; - _pdfjsView: any; - - constructor(viewer: PDFViewer) { - this._viewer = viewer; - } - setPDFJSview(v: any) { this._pdfjsView = v; } - - navigateTo() { } - - getDestinationHash() { return "#"; } - - getAnchorUrl() { return "#"; } - - setHash() { } - - isPageVisible(page: number) { return true; } - - executeNamedAction() { } - - cachePageRef() { } - - get pagesCount() { return this._viewer._pdfViewer.pagesCount; } - - get page() { return NumCast(this._viewer.props.Document.curPage); } - set page(p: number) { - this._pdfjsView._ensurePdfPageLoaded(this._pdfjsView._pages[p - 1]).then(() => { - this._pdfjsView.renderingQueue.renderView(this._pdfjsView._pages[p - 1]); - if (this._viewer.props.GoToPage) - this._viewer.props.GoToPage(p); - }); - } - - - get rotation() { return 0; } - set rotation(value: any) { } -}
\ No newline at end of file |