diff options
Diffstat (limited to 'src/client/views/pdf/PDFViewer.tsx')
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 455 |
1 files changed, 249 insertions, 206 deletions
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 510c5c385..837734edf 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,36 +1,35 @@ -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx"; -import { observer } from "mobx-react"; -import * as Pdfjs from "pdfjs-dist"; -import "pdfjs-dist/web/pdf_viewer.css"; -import { Doc, DocListCast, Field, HeightSym, Opt } from "../../../fields/Doc"; -import { Id } from "../../../fields/FieldSymbols"; -import { InkTool } from "../../../fields/InkField"; -import { Cast, NumCast, StrCast } from "../../../fields/Types"; -import { TraceMobx } from "../../../fields/util"; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, returnFalse, smoothScroll, Utils } from "../../../Utils"; -import { DocUtils } from "../../documents/Documents"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { SelectionManager } from "../../util/SelectionManager"; -import { SharingManager } from "../../util/SharingManager"; -import { SnappingManager } from "../../util/SnappingManager"; -import { MarqueeOptionsMenu } from "../collections/collectionFreeForm"; -import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; -import { MarqueeAnnotator } from "../MarqueeAnnotator"; -import { DocumentViewProps } from "../nodes/DocumentView"; -import { FieldViewProps } from "../nodes/FieldView"; -import { LinkDocPreview } from "../nodes/LinkDocPreview"; -import { StyleProp } from "../StyleProvider"; -import { AnchorMenu } from "./AnchorMenu"; -import { Annotation } from "./Annotation"; -import "./PDFViewer.scss"; -import React = require("react"); -const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); -const pdfjsLib = require("pdfjs-dist"); -const _global = (window /* browser */ || global /* node */) as any; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; +import * as Pdfjs from 'pdfjs-dist'; +import 'pdfjs-dist/web/pdf_viewer.css'; +import { Doc, DocListCast, Field, HeightSym, Opt } from '../../../fields/Doc'; +import { Id } from '../../../fields/FieldSymbols'; +import { InkTool } from '../../../fields/InkField'; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { TraceMobx } from '../../../fields/util'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from '../../../Utils'; +import { DocUtils } from '../../documents/Documents'; +import { SelectionManager } from '../../util/SelectionManager'; +import { SharingManager } from '../../util/SharingManager'; +import { SnappingManager } from '../../util/SnappingManager'; +import { MarqueeOptionsMenu } from '../collections/collectionFreeForm'; +import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; +import { MarqueeAnnotator } from '../MarqueeAnnotator'; +import { DocumentViewProps } from '../nodes/DocumentView'; +import { FieldViewProps } from '../nodes/FieldView'; +import { LinkDocPreview } from '../nodes/LinkDocPreview'; +import { StyleProp } from '../StyleProvider'; +import { AnchorMenu } from './AnchorMenu'; +import { Annotation } from './Annotation'; +import './PDFViewer.scss'; +import React = require('react'); +const PDFJSViewer = require('pdfjs-dist/web/pdf_viewer'); +const pdfjsLib = require('pdfjs-dist'); +const _global = (window /* browser */ || global) /* node */ as any; //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`; // The workerSrc property shall be specified. -pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.14.305/build/pdf.worker.js"; +pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@2.14.305/build/pdf.worker.js'; interface IViewerProps extends FieldViewProps { Document: Doc; @@ -53,7 +52,7 @@ interface IViewerProps extends FieldViewProps { @observer export class PDFViewer extends React.Component<IViewerProps> { static _annotationStyle: any = addStyleSheet(); - @observable private _pageSizes: { width: number, height: number }[] = []; + @observable private _pageSizes: { width: number; height: number }[] = []; @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); @observable private _marqueeing: number[] | undefined; @observable private _textSelecting = true; @@ -70,7 +69,7 @@ export class PDFViewer extends React.Component<IViewerProps> { private _viewer: React.RefObject<HTMLDivElement> = React.createRef(); public _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined; _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); - private _selectionText: string = ""; + private _selectionText: string = ''; private _downX: number = 0; private _downY: number = 0; private _lastSearch = false; @@ -82,69 +81,80 @@ export class PDFViewer extends React.Component<IViewerProps> { @observable isAnnotating = false; // key where data is stored @computed get allAnnotations() { - return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + "-annotations"]), this.props.docFilters(), this.props.docRangeFilters(), undefined); + return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '-annotations']), this.props.docFilters(), this.props.docRangeFilters(), undefined); + } + @computed get inlineTextAnnotations() { + return this.allAnnotations.filter(a => a.textInlineAnnotations); } - @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); } componentDidMount = async () => { - runInAction(() => this._showWaiting = true); + runInAction(() => (this._showWaiting = true)); this.setupPdfJsViewer(); - this._mainCont.current?.addEventListener("scroll", e => (e.target as any).scrollLeft = 0); + this._mainCont.current?.addEventListener('scroll', e => ((e.target as any).scrollLeft = 0)); - this._disposers.autoHeight = reaction(() => this.props.layoutDoc._autoHeight, + this._disposers.autoHeight = reaction( + () => this.props.layoutDoc._autoHeight, autoHeight => { if (autoHeight) { - this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]); - this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1)); + this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']); + this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.scaling?.() || 1)); } - }); + } + ); - this._disposers.selected = reaction(() => this.props.isSelected(), + this._disposers.selected = reaction( + () => this.props.isSelected(), selected => { // if (!selected) { // Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); // Array.from(this._savedAnnotations.keys()).forEach(k => this._savedAnnotations.set(k, [])); // } - (SelectionManager.Views().length === 1) && this.setupPdfJsViewer(); + SelectionManager.Views().length === 1 && this.setupPdfJsViewer(); }, - { fireImmediately: true }); - this._disposers.curPage = reaction(() => Cast(this.props.Document._curPage, "number", null), - (page) => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page), { fireImmediately: true } ); - } + this._disposers.curPage = reaction( + () => Cast(this.props.Document._curPage, 'number', null), + page => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page), + { fireImmediately: true } + ); + }; componentWillUnmount = () => { Object.values(this._disposers).forEach(disposer => disposer?.()); - document.removeEventListener("copy", this.copy); - } + document.removeEventListener('copy', this.copy); + }; copy = (e: ClipboardEvent) => { if (this.props.isContentActive() && e.clipboardData) { - e.clipboardData.setData("text/plain", this._selectionText); + e.clipboardData.setData('text/plain', this._selectionText); e.preventDefault(); } - } + }; @action initialLoad = async () => { if (this._pageSizes.length === 0) { - this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); - await Promise.all(this._pageSizes.map((val, i) => - this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => { - const page0or180 = page.rotate === 0 || page.rotate === 180; - this._pageSizes.splice(i, 1, { - width: (page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1]), - height: (page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0]) - }); - if (i === this.props.pdf.numPages - 1) { - this.props.loaded?.(page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1], - page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], this.props.pdf.numPages); - } - })))); - this.props.Document.scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0) * 96 / 72; + this._pageSizes = Array<{ width: number; height: number }>(this.props.pdf.numPages); + await Promise.all( + this._pageSizes.map((val, i) => + this.props.pdf.getPage(i + 1).then( + action((page: Pdfjs.PDFPageProxy) => { + const page0or180 = page.rotate === 0 || page.rotate === 180; + this._pageSizes.splice(i, 1, { + width: page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1], + height: page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], + }); + if (i === this.props.pdf.numPages - 1) { + this.props.loaded?.(page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1], page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], this.props.pdf.numPages); + } + }) + ) + ) + ); + this.props.Document.scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72; } - } + }; // scrolls to focus on a nested annotation document. if this is part a link preview then it will jump to the scroll location, // otherwise it will scroll smoothly. @@ -153,7 +163,7 @@ export class PDFViewer extends React.Component<IViewerProps> { let focusSpeed: Opt<number>; if (doc !== this.props.rootDoc && mainCont) { const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1); - const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight, NumCast(this.props.Document.scrollHeight)); + const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, NumCast(this.props.Document.scrollHeight)); if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) { focusSpeed = 500; @@ -165,10 +175,10 @@ export class PDFViewer extends React.Component<IViewerProps> { this._initialScroll = NumCast(this.props.layoutDoc._scrollTop); } return focusSpeed; - } + }; crop = (region: Doc | undefined, addCrop?: boolean) => { return this.props.crop(region, addCrop); - } + }; @action setupPdfJsViewer = async () => { @@ -179,18 +189,18 @@ export class PDFViewer extends React.Component<IViewerProps> { await this.initialLoad(); this.createPdfViewer(); - } + }; pagesinit = () => { if (this._pdfViewer._setDocumentViewerElement?.offsetParent) { - runInAction(() => this._pdfViewer.currentScaleValue = this.props.layoutDoc._viewScale = 1); + runInAction(() => (this._pdfViewer.currentScaleValue = this.props.layoutDoc._viewScale = 1)); this.gotoPage(NumCast(this.props.Document._curPage, 1)); } - document.removeEventListener("pagesinit", this.pagesinit); - var quickScroll: string | undefined = this._initialScroll ? this._initialScroll.toString() : ""; + document.removeEventListener('pagesinit', this.pagesinit); + var quickScroll: string | undefined = this._initialScroll ? this._initialScroll.toString() : ''; this._disposers.scroll = reaction( () => Math.abs(NumCast(this.props.Document._scrollTop)), - (pos) => { + pos => { if (!this._ignoreScroll) { this._showWaiting && this.setupPdfJsViewer(); const viewTrans = quickScroll ?? StrCast(this.props.Document._viewTransition); @@ -199,10 +209,13 @@ export class PDFViewer extends React.Component<IViewerProps> { const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0; this._forcedScroll = true; if (duration) { - setTimeout(() => { - this._mainCont.current && smoothScroll(duration, this._mainCont.current, pos); - setTimeout(() => this._forcedScroll = false, duration); - }, this._mainCont.current ? 0 : 250); // wait for mainCont and try again to scroll + setTimeout( + () => { + this._mainCont.current && smoothScroll(duration, this._mainCont.current, pos); + setTimeout(() => (this._forcedScroll = false), duration); + }, + this._mainCont.current ? 0 : 250 + ); // wait for mainCont and try again to scroll } else { this._mainCont.current?.scrollTo({ top: pos }); this._forcedScroll = false; @@ -216,23 +229,27 @@ export class PDFViewer extends React.Component<IViewerProps> { this._mainCont.current?.scrollTo({ top: Math.abs(this._initialScroll || 0) }); this._initialScroll = undefined; } - } + }; createPdfViewer() { - if (!this._mainCont.current) { // bcz: I don't think this is ever triggered or needed - console.log("PDFViewer- I guess we got here"); + if (!this._mainCont.current) { + // bcz: I don't think this is ever triggered or needed + console.log('PDFViewer- I guess we got here'); if (this._retries < 5) { this._retries++; - console.log("PDFViewer- retry num:" + this._retries); + console.log('PDFViewer- retry num:' + this._retries); setTimeout(() => this.createPdfViewer(), 1000); } return; } - document.removeEventListener("copy", this.copy); - document.addEventListener("copy", this.copy); + document.removeEventListener('copy', this.copy); + document.addEventListener('copy', this.copy); const eventBus = new PDFJSViewer.EventBus(true); - eventBus._on("pagesinit", this.pagesinit); - eventBus._on("pagerendered", action(() => this._showWaiting = false)); + eventBus._on('pagesinit', this.pagesinit); + eventBus._on( + 'pagerendered', + action(() => (this._showWaiting = false)) + ); const pdfLinkService = new PDFJSViewer.PDFLinkService({ eventBus }); const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, eventBus }); this._pdfViewer = new PDFJSViewer.PDFViewer({ @@ -240,31 +257,30 @@ export class PDFViewer extends React.Component<IViewerProps> { viewer: this._viewer.current, linkService: pdfLinkService, findController: pdfFindController, - renderer: "canvas", - eventBus + renderer: 'canvas', + eventBus, }); pdfLinkService.setViewer(this._pdfViewer); pdfLinkService.setDocument(this.props.pdf, null); this._pdfViewer.setDocument(this.props.pdf); } - @action prevAnnotation = () => { this.Index = Math.max(this.Index - 1, 0); 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); this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]); - } + }; @action gotoPage = (p: number) => { this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) }); - } + }; @action scrollToAnnotation = (scrollToAnnotation: Doc) => { @@ -272,7 +288,7 @@ export class PDFViewer extends React.Component<IViewerProps> { this.scrollFocus(scrollToAnnotation, true); Doc.linkFollowHighlight(scrollToAnnotation); } - } + }; @observable private _scrollTimer: any; @@ -289,7 +305,7 @@ export class PDFViewer extends React.Component<IViewerProps> { this._scrollTimer = undefined; }, 200); } - } + }; // get the page index that the vertical offset passed in is on getPageFromScroll = (vOffset: number) => { @@ -299,7 +315,7 @@ export class PDFViewer extends React.Component<IViewerProps> { currOffset -= this._pageSizes[index++].height; } return index; - } + }; @action search = (searchString: string, bwd?: boolean, clear: boolean = false) => { @@ -308,22 +324,21 @@ export class PDFViewer extends React.Component<IViewerProps> { findPrevious: bwd, highlightAll: true, phraseSearch: true, - query: searchString + query: searchString, }; if (clear) { - this._pdfViewer?.findController.executeCommand('reset', { query: "" }); + this._pdfViewer?.findController.executeCommand('reset', { query: '' }); } else if (!searchString) { bwd ? this.prevAnnotation() : this.nextAnnotation(); } else if (this._pdfViewer?.pageViewsReady) { this._pdfViewer.findController.executeCommand('findagain', findOpts); - } - else if (this._mainCont.current) { + } else if (this._mainCont.current) { const executeFind = () => this._pdfViewer.findController.executeCommand('find', findOpts); - this._mainCont.current.addEventListener("pagesloaded", executeFind); - this._mainCont.current.addEventListener("pagerendered", executeFind); + this._mainCont.current.addEventListener('pagesloaded', executeFind); + this._mainCont.current.addEventListener('pagerendered', executeFind); } return true; - } + }; @action onPointerDown = (e: React.PointerEvent): void => { @@ -340,24 +355,27 @@ export class PDFViewer extends React.Component<IViewerProps> { if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) { this._setPreviewCursor?.(e.clientX, e.clientY, true, false); } - if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { this.props.select(false); MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; this.isAnnotating = true; - if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) { + if (e.target && ((e.target as any).className.includes('endOfContent') || (e.target as any).parentElement.className !== 'textLayer')) { this._textSelecting = false; - document.addEventListener("pointermove", this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called + document.addEventListener('pointermove', this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called } else { // if textLayer is hit, then we select text instead of using a marquee so clear out the marquee. - setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it. - - this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "htmlAnnotation", { "pointer-events": "none" }); - document.addEventListener("pointerup", this.onSelectEnd); - document.addEventListener("pointermove", this.onSelectMove); + setTimeout( + action(() => (this._marqueeing = undefined)), + 100 + ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it. + + this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, 'htmlAnnotation', { 'pointer-events': 'none' }); + document.addEventListener('pointerup', this.onSelectEnd); + document.addEventListener('pointermove', this.onSelectMove); } } - } + }; @action finishMarquee = (x?: number, y?: number) => { @@ -365,8 +383,8 @@ export class PDFViewer extends React.Component<IViewerProps> { this.isAnnotating = false; this._marqueeing = undefined; this._textSelecting = true; - document.removeEventListener("pointermove", this.onSelectMove); - } + document.removeEventListener('pointermove', this.onSelectMove); + }; onSelectMove = (e: PointerEvent) => e.stopPropagation(); @@ -375,15 +393,15 @@ export class PDFViewer extends React.Component<IViewerProps> { this.isAnnotating = false; clearStyleSheetRules(PDFViewer._annotationStyle); this.props.select(false); - document.removeEventListener("pointermove", this.onSelectMove); - document.removeEventListener("pointerup", this.onSelectEnd); + document.removeEventListener('pointermove', this.onSelectMove); + document.removeEventListener('pointerup', this.onSelectEnd); const sel = window.getSelection(); - if (sel?.type === "Range") { + if (sel?.type === 'Range') { this.createTextAnnotation(sel, sel.getRangeAt(0)); AnchorMenu.Instance.jumpTo(e.clientX, e.clientY); } - } + }; @action createTextAnnotation = (sel: Selection, selRange: Range) => { @@ -395,41 +413,41 @@ export class PDFViewer extends React.Component<IViewerProps> { if (rect && rect.width !== this._mainCont.current.clientWidth && rect.width) { const scaleX = this._mainCont.current.offsetWidth / boundingRect.width; const pdfScale = NumCast(this.props.layoutDoc._viewScale, 1); - const annoBox = document.createElement("div"); - annoBox.className = "marqueeAnnotator-annotationBox"; + const annoBox = document.createElement('div'); + annoBox.className = 'marqueeAnnotator-annotationBox'; // transforms the positions from screen onto the pdf div - annoBox.style.top = ((rect.top - boundingRect.top) * scaleX / pdfScale + this._mainCont.current.scrollTop).toString(); - annoBox.style.left = ((rect.left - boundingRect.left) * scaleX / pdfScale).toString(); - annoBox.style.width = (rect.width * this._mainCont.current.offsetWidth / boundingRect.width / pdfScale).toString(); - annoBox.style.height = (rect.height * this._mainCont.current.offsetHeight / boundingRect.height / pdfScale).toString(); + annoBox.style.top = (((rect.top - boundingRect.top) * scaleX) / pdfScale + this._mainCont.current.scrollTop).toString(); + annoBox.style.left = (((rect.left - boundingRect.left) * scaleX) / pdfScale).toString(); + annoBox.style.width = ((rect.width * this._mainCont.current.offsetWidth) / boundingRect.width / pdfScale).toString(); + annoBox.style.height = ((rect.height * this._mainCont.current.offsetHeight) / boundingRect.height / pdfScale).toString(); this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, this.getPageFromScroll(rect.top)); } } } - this._selectionText = selRange.cloneContents().textContent || ""; + this._selectionText = selRange.cloneContents().textContent || ''; // clear selection - if (sel.empty) { // Chrome + if (sel.empty) { + // Chrome sel.empty(); - } else if (sel.removeAllRanges) { // Firefox + } else if (sel.removeAllRanges) { + // Firefox sel.removeAllRanges(); } - } + }; scrollXf = () => { return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._scrollTop)) : this.props.ScreenToLocalTransform(); - } + }; onClick = (e: React.MouseEvent) => { - if (this._setPreviewCursor && e.button === 0 && - Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && - Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { + if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { this._setPreviewCursor(e.clientX, e.clientY, false, false); } // e.stopPropagation(); // bcz: not sure why this was here. We need to allow the DocumentView to get clicks to process doubleClicks - } + }; - setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func; + setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func); @action onZoomWheel = (e: React.WheelEvent) => { @@ -437,52 +455,59 @@ export class PDFViewer extends React.Component<IViewerProps> { e.stopPropagation(); if (e.ctrlKey) { const curScale = Number(this._pdfViewer.currentScaleValue); - this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - curScale * e.deltaY / 1000)); + this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - (curScale * e.deltaY) / 1000)); this.props.layoutDoc._viewScale = Number(this._pdfViewer.currentScaleValue); } } - } + }; - pointerEvents = () => this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none"; + pointerEvents = () => (this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'); @computed get annotationLayer() { - return <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` }} ref={this._annotationLayer}> - {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno => - <Annotation {...this.props} fieldKey={this.props.fieldKey + "-annotations"} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)} - </div>; + return ( + <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` }} ref={this._annotationLayer}> + {this.inlineTextAnnotations + .sort((a, b) => NumCast(a.y) - NumCast(b.y)) + .map(anno => ( + <Annotation {...this.props} fieldKey={this.props.fieldKey + '-annotations'} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} /> + ))} + </div> + ); } @computed get overlayInfo() { - return !this._overlayAnnoInfo ? (null) : + return !this._overlayAnnoInfo ? null : ( <div className="pdfViewerDash-overlayAnno" style={{ top: NumCast(this._overlayAnnoInfo.y), left: NumCast(this._overlayAnnoInfo.x) }}> <div className="pdfViewerDash-overlayAnno" style={{ right: -50, background: SharingManager.Instance.users.find(users => users.user.email === this._overlayAnnoInfo!.author)?.userColor }}> - {this._overlayAnnoInfo.author + " " + Field.toString(this._overlayAnnoInfo.creationDate as Field)} + {this._overlayAnnoInfo.author + ' ' + Field.toString(this._overlayAnnoInfo.creationDate as Field)} </div> - </div>; + </div> + ); } - showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno); + showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno)); overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1)); panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); - basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter("textInlineAnnotations")]; + basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')]; transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; - childStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => { + childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { - if (doc.textInlineAnnotations) return "none"; - return "all"; + if (doc.textInlineAnnotations) return 'none'; + return 'all'; } return this.props.styleProvider?.(doc, props, property); - } + }; - renderAnnotations = (docFilters?: () => string[], dontRender?: boolean) => - <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} + renderAnnotations = (docFilters?: () => string[], dontRender?: boolean) => ( + <CollectionFreeFormView + {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit} isAnnotationOverlay={true} - fieldKey={this.props.fieldKey + "-annotations"} + fieldKey={this.props.fieldKey + '-annotations'} setPreviewCursor={this.setPreviewCursor} PanelHeight={this.panelHeight} PanelWidth={this.panelWidth} - dropAction={"alias"} + dropAction={'alias'} select={emptyFunction} ContentScaling={this.contentZoom} bringToFront={emptyFunction} @@ -493,70 +518,88 @@ export class PDFViewer extends React.Component<IViewerProps> { ScreenToLocalTransform={this.overlayTransform} renderDepth={this.props.renderDepth + 1} /> - @computed get overlayTransparentAnnotations() { return this.renderAnnotations(this.transparentFilter, false); } - @computed get overlayOpaqueAnnotations() { return this.renderAnnotations(this.opaqueFilter, false); } + ); + @computed get overlayTransparentAnnotations() { + return this.renderAnnotations(this.transparentFilter, false); + } + @computed get overlayOpaqueAnnotations() { + return this.renderAnnotations(this.opaqueFilter, false); + } @computed get overlayClickableAnnotations() { - return <div style={{ height: NumCast(this.props.rootDoc.scrollHeight) }}> - {this.renderAnnotations(undefined, true)} - </div>; + return <div style={{ height: NumCast(this.props.rootDoc.scrollHeight) }}>{this.renderAnnotations(undefined, true)}</div>; } @computed get overlayLayer() { - return <div style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none" }}> - <div className="pdfViewerDash-overlay" - style={{ - pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none", - mixBlendMode: "multiply", - transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` - }}> - {this.overlayTransparentAnnotations} - </div> - <div className="pdfViewerDash-overlay" - style={{ - pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none", - mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined, - transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` - }}> - {this.overlayOpaqueAnnotations} - {this.overlayClickableAnnotations} + return ( + <div style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none' }}> + <div + className="pdfViewerDash-overlay" + style={{ + pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none', + mixBlendMode: 'multiply', + transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`, + }}> + {this.overlayTransparentAnnotations} + </div> + <div + className="pdfViewerDash-overlay" + style={{ + pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none', + mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined, + transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`, + }}> + {this.overlayOpaqueAnnotations} + {this.overlayClickableAnnotations} + </div> </div> - </div>; + ); } @computed get pdfViewerDiv() { - return <div className={"pdfViewerDash-text" + (this.props.pointerEvents?.() !== "none" && this._textSelecting && this.props.isContentActive() ? "-selected" : "")} ref={this._viewer} />; + return <div className={'pdfViewerDash-text' + (this.props.pointerEvents?.() !== 'none' && this._textSelecting && this.props.isContentActive() ? '-selected' : '')} ref={this._viewer} />; + } + @computed get contentScaling() { + return this.props.ContentScaling?.() || 1; } - @computed get contentScaling() { return this.props.ContentScaling?.() || 1; } contentZoom = () => NumCast(this.props.layoutDoc._viewScale, 1); savedAnnotations = () => this._savedAnnotations; render() { TraceMobx(); - return <div className="pdfViewer-content"> - <div className={`pdfViewerDash${this.props.isContentActive() && this.props.pointerEvents?.() !== "none" ? "-interactive" : ""}`} ref={this._mainCont} - onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick} - style={{ - overflowX: NumCast(this.props.layoutDoc._viewScale, 1) !== 1 ? "scroll" : undefined, - height: !this.props.Document._fitWidth && (window.screen.width > 600) ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`, - transform: `scale(${this.contentScaling})` - }} > - {this.pdfViewerDiv} - {this.annotationLayer} - {this.overlayLayer} - {this.overlayInfo} - {this._showWaiting ? <img className="pdfViewerDash-waiting" src={"/assets/loading.gif"} /> : (null)} - {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : - <MarqueeAnnotator rootDoc={this.props.rootDoc} - getPageFromScroll={this.getPageFromScroll} - anchorMenuClick={this.props.anchorMenuClick} - scrollTop={0} - down={this._marqueeing} - addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)} - docView={this.props.docViewPath().lastElement()} - finishMarquee={this.finishMarquee} - savedAnnotations={this.savedAnnotations} - annotationLayer={this._annotationLayer.current} - mainCont={this._mainCont.current} - anchorMenuCrop={this._textSelecting ? undefined : this.crop} - />} + return ( + <div className="pdfViewer-content"> + <div + className={`pdfViewerDash${this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' ? '-interactive' : ''}`} + ref={this._mainCont} + onScroll={this.onScroll} + onWheel={this.onZoomWheel} + onPointerDown={this.onPointerDown} + onClick={this.onClick} + style={{ + overflowX: NumCast(this.props.layoutDoc._viewScale, 1) !== 1 ? 'scroll' : undefined, + height: !this.props.Document._fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`, + transform: `scale(${this.contentScaling})`, + }}> + {this.pdfViewerDiv} + {this.annotationLayer} + {this.overlayLayer} + {this.overlayInfo} + {this._showWaiting ? <img className="pdfViewerDash-waiting" src={'/assets/loading.gif'} /> : null} + {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : ( + <MarqueeAnnotator + rootDoc={this.props.rootDoc} + getPageFromScroll={this.getPageFromScroll} + anchorMenuClick={this.props.anchorMenuClick} + scrollTop={0} + down={this._marqueeing} + addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)} + docView={this.props.docViewPath().lastElement()} + finishMarquee={this.finishMarquee} + savedAnnotations={this.savedAnnotations} + annotationLayer={this._annotationLayer.current} + mainCont={this._mainCont.current} + anchorMenuCrop={this._textSelecting ? undefined : this.crop} + /> + )} + </div> </div> - </div>; + ); } -}
\ No newline at end of file +} |