aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf/PDFViewer.tsx
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
committerSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
commitf3dab2a56db5e4a6a3dca58185d94e1ff7d1dc32 (patch)
treea7bc895266b53bb620dbd2dd71bad2e83b555446 /src/client/views/pdf/PDFViewer.tsx
parentb5c5410b4af5d2c68d2107d3f064f6e3ec4ac3f2 (diff)
parent136f3d9f349d54e8bdd73b6380ea47c19e5edebf (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.tsx252
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}
/>
)}