diff options
author | Sophie Zhang <sophie_zhang@brown.edu> | 2024-01-25 11:35:26 -0500 |
---|---|---|
committer | Sophie Zhang <sophie_zhang@brown.edu> | 2024-01-25 11:35:26 -0500 |
commit | f3dab2a56db5e4a6a3dca58185d94e1ff7d1dc32 (patch) | |
tree | a7bc895266b53bb620dbd2dd71bad2e83b555446 /src/client/views/pdf/PDFViewer.tsx | |
parent | b5c5410b4af5d2c68d2107d3f064f6e3ec4ac3f2 (diff) | |
parent | 136f3d9f349d54e8bdd73b6380ea47c19e5edebf (diff) |
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/views/pdf/PDFViewer.tsx')
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 252 |
1 files changed, 117 insertions, 135 deletions
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 939928c1c..0d4cfda88 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,43 +1,40 @@ -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, 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, Opt } from '../../../fields/Doc'; +import * as PDFJSViewer from 'pdfjs-dist/web/pdf_viewer.mjs'; +import * as React from 'react'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { Height } from '../../../fields/DocSymbols'; 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, returnAll, returnFalse, returnNone, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, returnAll, returnFalse, returnNone, returnZero, smoothScroll, Utils } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; -import { DragManager } from '../../util/DragManager'; 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 { DocFocusOptions, DocumentViewProps } from '../nodes/DocumentView'; -import { FieldViewProps } from '../nodes/FieldView'; -import { LinkDocPreview } from '../nodes/LinkDocPreview'; +import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView'; +import { LinkInfo } from '../nodes/LinkDocPreview'; +import { PDFBox } from '../nodes/PDFBox'; +import { ObservableReactComponent } from '../ObservableReactComponent'; import { StyleProp } from '../StyleProvider'; import { AnchorMenu } from './AnchorMenu'; import { Annotation } from './Annotation'; -import './PDFViewer.scss'; -import React = require('react'); import { GPTPopup } from './GPTPopup/GPTPopup'; -import { InkingStroke } from '../InkingStroke'; -const PDFJSViewer = require('pdfjs-dist/web/pdf_viewer'); -const pdfjsLib = require('pdfjs-dist'); +import './PDFViewer.scss'; 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.16.105/build/pdf.worker.js'; +Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.0.379/build/pdf.worker.mjs'; interface IViewerProps extends FieldViewProps { + pdfBox: PDFBox; Document: Doc; - rootDoc: Doc; dataDoc: Doc; layoutDoc: Doc; fieldKey: string; @@ -54,21 +51,25 @@ interface IViewerProps extends FieldViewProps { * Handles rendering and virtualization of the pdf */ @observer -export class PDFViewer extends React.Component<IViewerProps> { +export class PDFViewer extends ObservableReactComponent<IViewerProps> { static _annotationStyle: any = addStyleSheet(); - @observable private _pageSizes: { width: number; height: number }[] = []; - @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); - @observable private _marqueeing: number[] | undefined; - @observable private _textSelecting = true; - @observable private _showWaiting = true; - @observable private _overlayAnnoInfo: Opt<Doc>; - @observable private Index: number = -1; + + constructor(props: IViewerProps) { + super(props); + makeObservable(this); + } + + @observable _pageSizes: { width: number; height: number }[] = []; + @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>(); + @observable _textSelecting = true; + @observable _showWaiting = true; + @observable Index: number = -1; private _pdfViewer: any; private _styleRule: any; // stylesheet rule for making hyperlinks clickable private _retries = 0; // number of times tried to create the PDF viewer private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void); - private _setBrushViewer: undefined | ((view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void); + private _marqueeref = React.createRef<MarqueeAnnotator>(); private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewer: React.RefObject<HTMLDivElement> = React.createRef(); @@ -90,44 +91,38 @@ 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.childFilters(), this.props.childFiltersByRanges()); + return DocUtils.FilterDocs(DocListCast(this._props.dataDoc[this._props.fieldKey + '_annotations']), this._props.childFilters(), this._props.childFiltersByRanges()); } @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.text_inlineAnnotations); } - componentDidMount = async () => { + componentDidMount() { runInAction(() => (this._showWaiting = true)); this.setupPdfJsViewer(); this._mainCont.current?.addEventListener('scroll', e => ((e.target as any).scrollLeft = 0)); this._disposers.layout_autoHeight = reaction( - () => this.props.layoutDoc._layout_autoHeight, + () => this._props.layoutDoc._layout_autoHeight, layout_autoHeight => { if (layout_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.NativeDimScaling?.() || 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.NativeDimScaling?.() || 1)); } } ); 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(); - }, + () => this._props.isSelected(), + selected => SelectionManager.Views.length === 1 && this.setupPdfJsViewer(), { fireImmediately: true } ); this._disposers.curPage = reaction( - () => Cast(this.props.Document._layout_curPage, 'number', null), + () => Cast(this._props.Document._layout_curPage, 'number', null), page => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page), { fireImmediately: true } ); - }; + } componentWillUnmount = () => { Object.values(this._disposers).forEach(disposer => disposer?.()); @@ -135,7 +130,7 @@ export class PDFViewer extends React.Component<IViewerProps> { }; copy = (e: ClipboardEvent) => { - if (this.props.isContentActive() && e.clipboardData) { + if (this._props.isContentActive() && e.clipboardData) { e.clipboardData.setData('text/plain', this._selectionText); const anchor = this._getAnchor(undefined, false); if (anchor) { @@ -151,18 +146,18 @@ export class PDFViewer extends React.Component<IViewerProps> { @action initialLoad = async () => { if (this._pageSizes.length === 0) { - this._pageSizes = Array<{ width: number; height: number }>(this.props.pdf.numPages); + 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( + 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); + 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); } }) ) @@ -176,31 +171,30 @@ export class PDFViewer extends React.Component<IViewerProps> { // 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. - scrollFocus = (doc: Doc, scrollTop: number, options: DocFocusOptions) => { + scrollFocus = (doc: Doc, scrollTop: number, options: FocusViewOptions) => { const mainCont = this._mainCont.current; let focusSpeed: Opt<number>; - if (doc !== this.props.rootDoc && mainCont) { - const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - const scrollTo = Utils.scrollIntoView(scrollTop, doc[Height](), NumCast(this.props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight); - if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._layout_scrollTop) { + if (doc !== this._props.Document && mainCont) { + const windowHeight = this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1); + const scrollTo = Utils.scrollIntoView(scrollTop, doc[Height](), NumCast(this._props.layoutDoc._layout_scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight); + if (scrollTo !== undefined && scrollTo !== this._props.layoutDoc._layout_scrollTop) { if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc }; else if (!options.instant) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper); else this._mainCont.current?.scrollTo({ top: Math.abs(scrollTo || 0) }); } } else { - this._initialScroll = { loc: NumCast(this.props.layoutDoc._layout_scrollTop), easeFunc: options.easeFunc }; + this._initialScroll = { loc: NumCast(this._props.layoutDoc._layout_scrollTop), easeFunc: options.easeFunc }; } return focusSpeed; }; - crop = (region: Doc | undefined, addCrop?: boolean) => this.props.crop(region, addCrop); - brushView = (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => this._setBrushViewer?.(view, transTime); + crop = (region: Doc | undefined, addCrop?: boolean) => this._props.crop(region, addCrop); @action setupPdfJsViewer = async () => { if (this._viewerIsSetup) return; this._viewerIsSetup = true; this._showWaiting = true; - this.props.setPdfViewer(this); + this._props.setPdfViewer(this); await this.initialLoad(); this.createPdfViewer(); @@ -208,22 +202,22 @@ export class PDFViewer extends React.Component<IViewerProps> { pagesinit = () => { if (this._pdfViewer._setDocumentViewerElement?.offsetParent) { - runInAction(() => (this._pdfViewer.currentScaleValue = this.props.layoutDoc._freeform_scale = 1)); - this.gotoPage(NumCast(this.props.Document._layout_curPage, 1)); + runInAction(() => (this._pdfViewer.currentScaleValue = this._props.layoutDoc._freeform_scale = 1)); + this.gotoPage(NumCast(this._props.Document._layout_curPage, 1)); } document.removeEventListener('pagesinit', this.pagesinit); var quickScroll: { loc?: string; easeFunc?: 'ease' | 'linear' } | undefined = { loc: this._initialScroll ? this._initialScroll.loc?.toString() : '', easeFunc: this._initialScroll ? this._initialScroll.easeFunc : undefined }; this._disposers.scale = reaction( - () => NumCast(this.props.layoutDoc._freeform_scale, 1), + () => NumCast(this._props.layoutDoc._freeform_scale, 1), scale => (this._pdfViewer.currentScaleValue = scale), { fireImmediately: true } ); this._disposers.scroll = reaction( - () => Math.abs(NumCast(this.props.Document._layout_scrollTop)), + () => Math.abs(NumCast(this._props.Document._layout_scrollTop)), pos => { if (!this._ignoreScroll) { this._showWaiting && this.setupPdfJsViewer(); - const viewTrans = quickScroll?.loc ?? StrCast(this.props.Document._viewTransition); + const viewTrans = quickScroll?.loc ?? StrCast(this._props.Document._viewTransition); const durationMiliStr = viewTrans.match(/([0-9]*)ms/); const durationSecStr = viewTrans.match(/([0-9.]*)s/); const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0; @@ -264,7 +258,7 @@ export class PDFViewer extends React.Component<IViewerProps> { } document.removeEventListener('copy', this.copy); document.addEventListener('copy', this.copy); - const eventBus = new PDFJSViewer.EventBus(true); + const eventBus = new PDFJSViewer.EventBus(); eventBus._on('pagesinit', this.pagesinit); eventBus._on( 'pagerendered', @@ -274,15 +268,14 @@ export class PDFViewer extends React.Component<IViewerProps> { const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, eventBus }); this._pdfViewer = new PDFJSViewer.PDFViewer({ container: this._mainCont.current, - viewer: this._viewer.current, + viewer: this._viewer.current || undefined, linkService: pdfLinkService, findController: pdfFindController, - renderer: 'canvas', eventBus, }); pdfLinkService.setViewer(this._pdfViewer); - pdfLinkService.setDocument(this.props.pdf, null); - this._pdfViewer.setDocument(this.props.pdf); + pdfLinkService.setDocument(this._props.pdf, null); + this._pdfViewer.setDocument(this._props.pdf); } @action @@ -310,18 +303,18 @@ export class PDFViewer extends React.Component<IViewerProps> { } }; - @observable private _scrollTimer: any; + @observable private _scrollTimer: any = undefined; onScroll = (e: React.UIEvent<HTMLElement>) => { if (this._mainCont.current && !this._forcedScroll) { this._ignoreScroll = true; // the pdf scrolled, so we need to tell the Doc to scroll but we don't want the doc to then try to set the PDF scroll pos (which would interfere with the smooth scroll animation) - if (!LinkDocPreview.LinkInfo) { - this.props.layoutDoc._layout_scrollTop = this._mainCont.current.scrollTop; + if (!LinkInfo.Instance?.LinkInfo) { + this._props.layoutDoc._layout_scrollTop = this._mainCont.current.scrollTop; } this._ignoreScroll = false; if (this._scrollTimer) clearTimeout(this._scrollTimer); // wait until a scrolling pause, then create an anchor to audio this._scrollTimer = setTimeout(() => { - DocUtils.MakeLinkToActiveAudio(() => this.props.DocumentView?.().ComponentView?.getAnchor!(true)!, false); + DocUtils.MakeLinkToActiveAudio(() => this._props.pdfBox.getAnchor(true)!, false); this._scrollTimer = undefined; }, 200); } @@ -371,24 +364,21 @@ export class PDFViewer extends React.Component<IViewerProps> { // if alt+left click, drag and annotate this._downX = e.clientX; this._downY = e.clientY; - if ((this.props.Document._freeform_scale || 1) !== 1) return; - if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) { - this._setPreviewCursor?.(e.clientX, e.clientY, true, false, this.props.Document); + if ((this._props.Document._freeform_scale || 1) !== 1) return; + if ((e.button !== 0 || e.altKey) && this._props.isContentActive()) { + this._setPreviewCursor?.(e.clientX, e.clientY, true, false, this._props.Document); } - if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { - this.props.select(false); + if (!e.altKey && e.button === 0 && this._props.isContentActive() && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { + this._props.select(false); MarqueeAnnotator.clearAnnotations(this._savedAnnotations); - this._marqueeing = [e.clientX, e.clientY]; + this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]); this.isAnnotating = true; const target = e.target as any; if (e.target && (target.className.includes('endOfContent') || (target.parentElement.className !== 'textLayer' && target.parentElement.parentElement?.className !== 'textLayer'))) { this._textSelecting = false; } 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. + setTimeout(() => this._marqueeref.current?.onTerminateSelection(), 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); @@ -400,15 +390,16 @@ export class PDFViewer extends React.Component<IViewerProps> { finishMarquee = (x?: number, y?: number) => { this._getAnchor = AnchorMenu.Instance?.GetAnchor; this.isAnnotating = false; - this._marqueeing = undefined; + this._marqueeref.current?.onTerminateSelection(); this._textSelecting = true; }; @action onSelectEnd = (e: PointerEvent): void => { + this._getAnchor = AnchorMenu.Instance?.GetAnchor; this.isAnnotating = false; clearStyleSheetRules(PDFViewer._annotationStyle); - this.props.select(false); + this._props.select(false); document.removeEventListener('pointerup', this.onSelectEnd); const sel = window.getSelection(); @@ -423,29 +414,32 @@ export class PDFViewer extends React.Component<IViewerProps> { // Changing which document to add the annotation to (the currently selected PDF) GPTPopup.Instance.setSidebarId('data_sidebar'); - GPTPopup.Instance.addDoc = this.props.sidebarAddDoc; + GPTPopup.Instance.addDoc = this._props.sidebarAddDoc; }; @action createTextAnnotation = (sel: Selection, selRange: Range) => { if (this._mainCont.current) { + this._mainCont.current.style.transform = `rotate(${NumCast(this._props.pdfBox.ScreenToLocalBoxXf().RotateDeg)}deg)`; const boundingRect = this._mainCont.current.getBoundingClientRect(); const clientRects = selRange.getClientRects(); for (let i = 0; i < clientRects.length; i++) { const rect = clientRects.item(i); - if (rect?.width && rect.width < this._mainCont.current.clientWidth / this.props.ScreenToLocalTransform().Scale) { + if (rect && rect?.width && rect.width < this._mainCont.current.clientWidth / this._props.ScreenToLocalTransform().Scale) { const scaleX = this._mainCont.current.offsetWidth / boundingRect.width; - const pdfScale = NumCast(this.props.layoutDoc._freeform_scale, 1); + const scaleY = this._mainCont.current.offsetHeight / boundingRect.height; + const pdfScale = NumCast(this._props.layoutDoc._freeform_scale, 1); 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) * scaleY) / pdfScale + this._mainCont.current.scrollTop).toString(); + annoBox.style.width = ((rect.width * scaleX) / pdfScale).toString(); + annoBox.style.height = ((rect.height * scaleY) / pdfScale).toString(); this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, this.getPageFromScroll(rect.top)); } } + this._mainCont.current!.style.transform = ''; } this._selectionContent = selRange.cloneContents(); this._selectionText = this._selectionContent?.textContent || ''; @@ -463,69 +457,57 @@ export class PDFViewer extends React.Component<IViewerProps> { onClick = (e: React.MouseEvent) => { this._scrollStopper?.(); 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, this.props.Document); + this._setPreviewCursor(e.clientX, e.clientY, false, false, this._props.Document); } // 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, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func); - setBrushViewer = (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => (this._setBrushViewer = func); @action onZoomWheel = (e: React.WheelEvent) => { - if (this.props.isContentActive(true)) { + if (this._props.isContentActive()) { 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.props.layoutDoc._freeform_scale = Number(this._pdfViewer.currentScaleValue); + this._props.layoutDoc._freeform_scale = Number(this._pdfViewer.currentScaleValue); } } }; pointerEvents = () => - this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() + this._props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' // : 'none'; @computed get annotationLayer() { const inlineAnnos = this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).filter(anno => !anno.hidden); return ( - <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}> + <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this._props.Document), transform: `scale(${NumCast(this._props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}> {inlineAnnos.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`} /> + <Annotation {...this._props} fieldKey={this._props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} dataDoc={this._props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} /> ))} </div> ); } - @computed get overlayInfo() { - 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.author_date as Field)} - </div> - </div> - ); - } - getScrollHeight = () => this._scrollHeight; - showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno)); - scrollXf = () => (this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._layout_scrollTop)) : this.props.ScreenToLocalTransform()); - overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._freeform_scale, 1)); - panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); - panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()]; - opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length && this.props.isContentActive() ? [] : [Utils.IsOpaqueFilter()])]; - childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => { + scrollXf = () => this._props.ScreenToLocalTransform().translate(0, this._mainCont.current ? NumCast(this._props.layoutDoc._layout_scrollTop) : 0); + overlayTransform = () => this.scrollXf().scale(1 / NumCast(this._props.layoutDoc._freeform_scale, 1)); + panelWidth = () => this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1); + panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1); + transparentFilter = () => [...this._props.childFilters(), Utils.TransparentBackgroundFilter]; + opaqueFilter = () => [...this._props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.CanEmbed && this._props.isContentActive() ? [] : [Utils.OpaqueBackgroundFilter])]; + childStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { - if (this.inlineTextAnnotations.includes(doc) || this.props.isContentActive() === false) return 'none'; + if (this.inlineTextAnnotations.includes(doc) || this._props.isContentActive() === false) return 'none'; const isInk = doc.layout_isSvg && !props?.LayoutTemplateString; return isInk ? 'visiblePainted' : 'all'; } - return this.props.styleProvider?.(doc, props, property); + return this._props.styleProvider?.(doc, props, property); }; - childPointerEvents = () => (this.props.isContentActive() !== false ? 'all' : 'none'); + childPointerEvents = () => (this._props.isContentActive() !== false ? 'all' : 'none'); renderAnnotations = (childFilters: () => string[], mixBlendMode?: any, display?: string) => ( <div className="pdfViewerDash-overlay" @@ -535,18 +517,17 @@ export class PDFViewer extends React.Component<IViewerProps> { pointerEvents: Doc.ActiveTool !== InkTool.None ? 'all' : undefined, }}> <CollectionFreeFormView - {...this.props} + {...this._props} NativeWidth={returnZero} NativeHeight={returnZero} - setContentView={emptyFunction} // override setContentView to do nothing - pointerEvents={this.props.isContentActive() && (SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it. + setContentViewBox={emptyFunction} // override setContentView to do nothing + pointerEvents={this._props.isContentActive() && (SnappingManager.IsDragging || Doc.ActiveTool !== InkTool.None) ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it. childPointerEvents={this.childPointerEvents} // but freeform children need to get events to allow text editing, etc - renderDepth={this.props.renderDepth + 1} + renderDepth={this._props.renderDepth + 1} isAnnotationOverlay={true} - fieldKey={this.props.fieldKey + '_annotations'} + fieldKey={this._props.fieldKey + '_annotations'} getScrollHeight={this.getScrollHeight} setPreviewCursor={this.setPreviewCursor} - setBrushViewer={this.setBrushViewer} PanelHeight={this.panelHeight} PanelWidth={this.panelWidth} ScreenToLocalTransform={this.overlayTransform} @@ -554,64 +535,65 @@ export class PDFViewer extends React.Component<IViewerProps> { isAnnotationOverlayScrollable={true} childFilters={childFilters} select={emptyFunction} - bringToFront={emptyFunction} styleProvider={this.childStyleProvider} /> </div> ); @computed get overlayTransparentAnnotations() { - const transparentChildren = DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), this.transparentFilter(), []); - return !transparentChildren.length ? null : this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length && this.props.isContentActive() ? 'none' : undefined); + const transparentChildren = DocUtils.FilterDocs(DocListCast(this._props.dataDoc[this._props.fieldKey + '_annotations']), this.transparentFilter(), []); + return !transparentChildren.length ? null : this.renderAnnotations(this.transparentFilter, 'multiply', SnappingManager.CanEmbed && this._props.isContentActive() ? 'none' : undefined); } @computed get overlayOpaqueAnnotations() { return this.renderAnnotations(this.opaqueFilter, this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined); } @computed get overlayLayer() { return ( - <div style={{ pointerEvents: this.props.isContentActive() && SnappingManager.GetIsDragging() ? 'all' : 'none' }}> + <div style={{ pointerEvents: this._props.isContentActive() && SnappingManager.IsDragging ? 'all' : 'none' }}> {this.overlayTransparentAnnotations} {this.overlayOpaqueAnnotations} </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} />; } savedAnnotations = () => this._savedAnnotations; + addDocumentWrapper = (doc: Doc | Doc[]) => this._props.addDocument!(doc); render() { TraceMobx(); return ( <div className="pdfViewer-content"> <div - className={`pdfViewerDash${this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' ? '-interactive' : ''}`} + 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._freeform_scale, 1) !== 1 ? 'scroll' : undefined, - height: !this.props.Document._layout_fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `100%`, + overflowX: NumCast(this._props.layoutDoc._freeform_scale, 1) !== 1 ? 'scroll' : undefined, + height: !this._props.Document._layout_fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this._props.Document) : `100%`, }}> {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 : ( + {!this._mainCont.current || !this._annotationLayer.current ? null : ( <MarqueeAnnotator - rootDoc={this.props.rootDoc} + ref={this._marqueeref} + Document={this._props.Document} getPageFromScroll={this.getPageFromScroll} - anchorMenuClick={this.props.anchorMenuClick} + anchorMenuClick={this._props.anchorMenuClick} scrollTop={0} - down={this._marqueeing} - addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)} - docView={this.props.docViewPath().lastElement()} + isNativeScaled={true} + annotationLayerScrollTop={NumCast(this._props.Document._layout_scrollTop)} + addDocument={this.addDocumentWrapper} + docView={this._props.pdfBox.DocumentView!} finishMarquee={this.finishMarquee} savedAnnotations={this.savedAnnotations} selectionText={this.selectionText} annotationLayer={this._annotationLayer.current} - mainCont={this._mainCont.current} + marqueeContainer={this._mainCont.current} anchorMenuCrop={this._textSelecting ? undefined : this.crop} /> )} |