diff options
Diffstat (limited to 'src/client/views/pdf')
| -rw-r--r-- | src/client/views/pdf/Annotation.tsx | 89 | ||||
| -rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 54 |
2 files changed, 88 insertions, 55 deletions
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 053c88e17..38578837a 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -1,18 +1,20 @@ +/* eslint-disable react/jsx-props-no-spreading */ import { action, computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; -import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types'; +import { BoolCast, DocCast, NumCast, StrCast } from '../../../fields/Types'; import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; -import { undoBatch } from '../../util/UndoManager'; +import { undoable } from '../../util/UndoManager'; +import { ObservableReactComponent } from '../ObservableReactComponent'; import { OpenWhere } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { AnchorMenu } from './AnchorMenu'; import './Annotation.scss'; -import { ObservableReactComponent } from '../ObservableReactComponent'; +import { Highlight } from '../../../fields/DocSymbols'; interface IAnnotationProps extends FieldViewProps { anno: Doc; @@ -27,11 +29,41 @@ export class Annotation extends ObservableReactComponent<IAnnotationProps> { super(props); makeObservable(this); } + + @computed get linkHighlighted() { + const found = LinkManager.Instance.getAllDirectLinks(this._props.anno).find(link => { + const a1 = LinkManager.getOppositeAnchor(link, this._props.anno); + return a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, a1)); + }); + return found; + } + linkHighlightedFunc = () => this.linkHighlighted; + highlightedFunc = () => this._props.anno[Highlight]; + + deleteAnnotation = undoable(() => { + const docAnnotations = DocListCast(this._props.dataDoc[this._props.fieldKey]); + this._props.dataDoc[this._props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this._props.anno)); + AnchorMenu.Instance.fadeOut(true); + this._props.select(false); + }, 'delete annotation'); + + pinToPres = undoable(() => this._props.pinToPres(this._props.anno, {}), 'pin to pres'); + render() { return ( <div style={{ display: this._props.anno.textCopied && !Doc.GetBrushHighlightStatus(this._props.anno) ? 'none' : undefined }}> {DocListCast(this._props.anno.text_inlineAnnotations).map(a => ( - <RegionAnnotation pointerEvents={this._props.pointerEvents} {...this._props} document={a} key={a[Id]} /> + // eslint-disable-next-line no-use-before-define + <RegionAnnotation + pointerEvents={this._props.pointerEvents} + {...this._props} + highlighted={this.highlightedFunc} + linkHighlighted={this.linkHighlightedFunc} + pinToPres={this.pinToPres} + deleteAnnotation={this.deleteAnnotation} + document={a} + key={a[Id]} + /> ))} </div> ); @@ -40,49 +72,41 @@ export class Annotation extends ObservableReactComponent<IAnnotationProps> { interface IRegionAnnotationProps extends IAnnotationProps { document: Doc; + linkHighlighted: () => Doc | undefined; + highlighted: () => any; + deleteAnnotation: () => void; + pinToPres: (...args: any[]) => void; pointerEvents?: () => Opt<string>; } @observer class RegionAnnotation extends ObservableReactComponent<IRegionAnnotationProps> { private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); - @computed get annoTextRegion() { - return Cast(this._props.document.annoTextRegion, Doc, null) || this._props.document; + @computed get regionDoc() { + return DocCast(this._props.document.embedContainer, this._props.document); } - @undoBatch - deleteAnnotation = () => { - const docAnnotations = DocListCast(this._props.dataDoc[this._props.fieldKey]); - this._props.dataDoc[this._props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion)); - AnchorMenu.Instance.fadeOut(true); - this._props.select(false); - }; - - @undoBatch - pinToPres = () => this._props.pinToPres(this.annoTextRegion, {}); + makeTargetToggle = undoable(() => { this.regionDoc.followLinkToggle = !this.regionDoc.followLinkToggle }, "set link toggle"); // prettier-ignore - @undoBatch - makeTargetToggle = () => { this.annoTextRegion.followLinkToggle = !this.annoTextRegion.followLinkToggle }; // prettier-ignore + isTargetToggler = () => BoolCast(this.regionDoc.followLinkToggle); - isTargetToggler = () => BoolCast(this.annoTextRegion.followLinkToggle); - @undoBatch - showTargetTrail = (anchor: Doc) => { + showTargetTrail = undoable((anchor: Doc) => { const trail = DocCast(anchor.presentationTrail); if (trail) { Doc.ActivePresentation = trail; this._props.addDocTab(trail, OpenWhere.replaceRight); } - }; + }, 'show target trail'); @action onContextMenu = (e: React.MouseEvent) => { AnchorMenu.Instance.Status = 'annotation'; - AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this); + AnchorMenu.Instance.Delete = this._props.deleteAnnotation; AnchorMenu.Instance.Pinned = false; - AnchorMenu.Instance.PinToPres = this.pinToPres; + AnchorMenu.Instance.PinToPres = this._props.pinToPres; AnchorMenu.Instance.MakeTargetToggle = this.makeTargetToggle; AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler; - AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion); + AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.regionDoc); AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true); e.stopPropagation(); e.preventDefault(); @@ -94,19 +118,12 @@ class RegionAnnotation extends ObservableReactComponent<IRegionAnnotationProps> e.preventDefault(); } else if (e.button === 0) { e.stopPropagation(); - LinkFollower.FollowLink(undefined, this.annoTextRegion, false); + LinkFollower.FollowLink(undefined, this.regionDoc, false); } }; - @computed get linkHighlighted() { - for (const link of LinkManager.Instance.getAllDirectLinks(this._props.document)) { - const a1 = LinkManager.getOppositeAnchor(link, this._props.document); - if (a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, this._props.document))) return true; - } - } - render() { - const brushed = this.annoTextRegion && Doc.GetBrushHighlightStatus(this.annoTextRegion); + const brushed = this.regionDoc && Doc.GetBrushHighlightStatus(this.regionDoc); return ( <div className="htmlAnnotation" @@ -128,8 +145,8 @@ class RegionAnnotation extends ObservableReactComponent<IRegionAnnotationProps> height: NumCast(this._props.document._height), opacity: brushed === Doc.DocBrushStatus.highlighted ? 0.5 : undefined, pointerEvents: this._props.pointerEvents?.() as any, - outline: brushed === Doc.DocBrushStatus.unbrushed && this.linkHighlighted ? 'solid 1px lightBlue' : undefined, - backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? 'orange' : StrCast(this._props.document.backgroundColor), + outline: this._props.linkHighlighted() ? 'solid 1px lightBlue' : undefined, + backgroundColor: this._props.highlighted() ? 'orange' : StrCast(this._props.document.backgroundColor), }} /> ); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0ab952e84..a3fd192f7 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,3 +1,5 @@ +/* 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'; @@ -10,7 +12,7 @@ import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, Utils } from '../../../Utils'; +import { emptyFunction } from '../../../Utils'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, returnAll, returnFalse, returnNone, returnZero, smoothScroll } from '../../../ClientUtils'; import { DocUtils } from '../../documents/Documents'; import { SelectionManager } from '../../util/SelectionManager'; @@ -27,9 +29,8 @@ 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'; @@ -43,6 +44,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; @@ -109,8 +111,8 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { 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)); } @@ -119,7 +121,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { this._disposers.selected = reaction( () => this._props.isSelected(), - selected => SelectionManager.Views.length === 1 && this.setupPdfJsViewer(), + () => SelectionManager.Views.length === 1 && this.setupPdfJsViewer(), { fireImmediately: true } ); this._disposers.curPage = reaction( @@ -169,7 +171,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); @@ -207,14 +211,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( @@ -231,7 +239,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 @@ -267,7 +277,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 }); @@ -310,7 +322,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) { @@ -345,7 +357,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) { @@ -392,7 +404,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { }; @action - finishMarquee = (x?: number, y?: number) => { + finishMarquee = (/* x?: number, y?: number */) => { this._getAnchor = AnchorMenu.Instance?.GetAnchor; this.isAnnotating = false; this._marqueeref.current?.onTerminateSelection(); @@ -467,7 +479,9 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { // 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) => { @@ -490,6 +504,7 @@ 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 => ( + // eslint-disable-next-line react/jsx-props-no-spreading <Annotation {...this._props} fieldKey={this._props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} dataDoc={this._props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} /> ))} </div> @@ -522,6 +537,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} @@ -529,7 +545,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} @@ -582,7 +598,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} @@ -590,7 +606,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!} |
