aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/pdf/PDFViewer.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/pdf/PDFViewer.tsx')
-rw-r--r--src/client/views/pdf/PDFViewer.tsx92
1 files changed, 57 insertions, 35 deletions
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index aaff2a342..45b1d727e 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,34 +1,37 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
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 * as PDFJSViewer from 'pdfjs-dist/web/pdf_viewer.mjs';
import * as React from 'react';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, returnAll, returnFalse, returnNone, returnZero, smoothScroll } from '../../../ClientUtils';
+import { CreateLinkToActiveAudio, Doc, DocListCast, Opt } from '../../../fields/Doc';
import { DocData, 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, returnZero, smoothScroll, Utils } from '../../../Utils';
-import { DocUtils } from '../../documents/Documents';
-import { SelectionManager } from '../../util/SelectionManager';
+import { emptyFunction } from '../../../Utils';
+import { DocUtils } from '../../documents/DocUtils';
import { SnappingManager } from '../../util/SnappingManager';
import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { MarqueeAnnotator } from '../MarqueeAnnotator';
-import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
import { LinkInfo } from '../nodes/LinkDocPreview';
import { PDFBox } from '../nodes/PDFBox';
import { ObservableReactComponent } from '../ObservableReactComponent';
-import { StyleProp } from '../StyleProvider';
+import { StyleProp } from '../StyleProp';
import { AnchorMenu } from './AnchorMenu';
import { Annotation } from './Annotation';
import { GPTPopup } from './GPTPopup/GPTPopup';
import './PDFViewer.scss';
-const _global = (window /* browser */ || global) /* node */ as any;
-//pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
+// pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
// The workerSrc property shall be specified.
Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.1.392/build/pdf.worker.mjs';
@@ -42,6 +45,7 @@ interface IViewerProps extends FieldViewProps {
url: string;
sidebarAddDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean;
loaded?: (nw: number, nh: number, np: number) => void;
+ // eslint-disable-next-line no-use-before-define
setPdfViewer: (view: PDFViewer) => void;
anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
crop: (region: Doc | undefined, addCrop?: boolean) => Doc | undefined;
@@ -98,14 +102,18 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
}
componentDidMount() {
- 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.layout_autoHeight = reaction(
() => this._props.layoutDoc._layout_autoHeight,
- layout_autoHeight => {
- if (layout_autoHeight) {
+ layoutAutoHeight => {
+ if (layoutAutoHeight) {
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));
}
@@ -114,7 +122,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
this._disposers.selected = reaction(
() => this._props.isSelected(),
- selected => SelectionManager.Views.length === 1 && this.setupPdfJsViewer(),
+ () => DocumentView.Selected().length === 1 && this.setupPdfJsViewer(),
{ fireImmediately: true }
);
this._disposers.curPage = reaction(
@@ -164,7 +172,9 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
)
);
}
- runInAction(() => (this._scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72));
+ runInAction(() => {
+ this._scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72;
+ });
};
_scrollStopper: undefined | (() => void);
@@ -176,7 +186,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
let focusSpeed: Opt<number>;
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);
+ const scrollTo = ClientUtils.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);
@@ -202,14 +212,18 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
pagesinit = () => {
if (this._pdfViewer._setDocumentViewerElement?.offsetParent) {
- runInAction(() => (this._pdfViewer.currentScaleValue = this._props.layoutDoc._freeform_scale = 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 };
+ let 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),
- scale => (this._pdfViewer.currentScaleValue = scale),
+ scale => {
+ this._pdfViewer.currentScaleValue = scale;
+ },
{ fireImmediately: true }
);
this._disposers.scroll = reaction(
@@ -226,7 +240,9 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
setTimeout(
() => {
this._mainCont.current && (this._scrollStopper = smoothScroll(duration, this._mainCont.current, pos, this._initialScroll?.easeFunc ?? 'ease', this._scrollStopper));
- setTimeout(() => (this._forcedScroll = false), duration);
+ setTimeout(() => {
+ this._forcedScroll = false;
+ }, duration);
},
this._mainCont.current ? 0 : 250
); // wait for mainCont and try again to scroll
@@ -262,7 +278,9 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
eventBus._on('pagesinit', this.pagesinit);
eventBus._on(
'pagerendered',
- action(() => (this._showWaiting = false))
+ action(() => {
+ this._showWaiting = false;
+ })
);
const pdfLinkService = new PDFJSViewer.PDFLinkService({ eventBus });
const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, eventBus });
@@ -305,7 +323,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
@observable private _scrollTimer: any = undefined;
- onScroll = (e: React.UIEvent<HTMLElement>) => {
+ onScroll = () => {
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 (!LinkInfo.Instance?.LinkInfo) {
@@ -314,7 +332,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
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.pdfBox.getAnchor(true)!, false);
+ CreateLinkToActiveAudio(() => this._props.pdfBox.getAnchor(true)!, false);
this._scrollTimer = undefined;
}, 200);
}
@@ -340,7 +358,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
query: searchString,
};
if (clear) {
- this._pdfViewer?.eventBus.dispatch('reset', {});
+ this._pdfViewer?.eventBus.dispatch('findbarclose', {});
} else if (!searchString) {
bwd ? this.prevAnnotation() : this.nextAnnotation();
} else if (this._pdfViewer?.pageViewsReady) {
@@ -371,7 +389,6 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
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._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'))) {
@@ -383,11 +400,12 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, 'htmlAnnotation', { 'pointer-events': 'none' });
document.addEventListener('pointerup', this.onSelectEnd);
}
+ this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]);
}
};
@action
- finishMarquee = (x?: number, y?: number) => {
+ finishMarquee = (/* x?: number, y?: number */) => {
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
this.isAnnotating = false;
this._marqueeref.current?.onTerminateSelection();
@@ -456,13 +474,15 @@ export class PDFViewer extends ObservableReactComponent<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) {
+ if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < ClientUtils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < ClientUtils.DRAG_THRESHOLD) {
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);
+ setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => {
+ this._setPreviewCursor = func;
+ };
@action
onZoomWheel = (e: React.WheelEvent) => {
@@ -485,7 +505,8 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
return (
<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} dataDoc={this._props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ <Annotation {...this._props} fieldKey={this._props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} containerDataDoc={this._props.dataDoc} annoDoc={anno} key={`${anno[Id]}-annotation`} />
))}
</div>
);
@@ -496,8 +517,8 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
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])];
+ transparentFilter = () => [...this._props.childFilters(), ClientUtils.TransparentBackgroundFilter];
+ opaqueFilter = () => [...this._props.childFilters(), ClientUtils.noDragDocsFilter, ...(SnappingManager.CanEmbed && this._props.isContentActive() ? [] : [ClientUtils.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';
@@ -517,6 +538,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
pointerEvents: Doc.ActiveTool !== InkTool.None ? 'all' : undefined,
}}>
<CollectionFreeFormView
+ // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
NativeWidth={returnZero}
NativeHeight={returnZero}
@@ -524,7 +546,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
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}
- isAnnotationOverlay={true}
+ isAnnotationOverlay
fieldKey={this._props.fieldKey + '_annotations'}
getScrollHeight={this.getScrollHeight}
setPreviewCursor={this.setPreviewCursor}
@@ -532,7 +554,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
PanelWidth={this.panelWidth}
ScreenToLocalTransform={this.overlayTransform}
isAnyChildContentActive={returnFalse}
- isAnnotationOverlayScrollable={true}
+ isAnnotationOverlayScrollable
childFilters={childFilters}
select={emptyFunction}
styleProvider={this.childStyleProvider}
@@ -577,7 +599,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
{this.pdfViewerDiv}
{this.annotationLayer}
{this.overlayLayer}
- {this._showWaiting ? <img className="pdfViewerDash-waiting" src={'/assets/loading.gif'} /> : null}
+ {this._showWaiting ? <img alt="" className="pdfViewerDash-waiting" src="/assets/loading.gif" /> : null}
{!this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
ref={this._marqueeref}
@@ -585,7 +607,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
getPageFromScroll={this.getPageFromScroll}
anchorMenuClick={this._props.anchorMenuClick}
scrollTop={0}
- isNativeScaled={true}
+ isNativeScaled
annotationLayerScrollTop={NumCast(this._props.Document._layout_scrollTop)}
addDocument={this.addDocumentWrapper}
docView={this._props.pdfBox.DocumentView!}
@@ -594,7 +616,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> {
selectionText={this.selectionText}
annotationLayer={this._annotationLayer.current}
marqueeContainer={this._mainCont.current}
- anchorMenuCrop={this._textSelecting ? undefined : this.crop}
+ anchorMenuCrop={this.crop}
/>
)}
</div>