diff options
| author | bobzel <zzzman@gmail.com> | 2023-11-22 11:52:57 -0500 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2023-11-22 11:52:57 -0500 |
| commit | cc75a03d89a4b553a53b55404464cd2ca93d9b48 (patch) | |
| tree | d4c455549245a287656d7e03ff9e459231251829 /src/client/views/pdf | |
| parent | 52241c5a42c0fa2d92eca8110523081ce9f353af (diff) | |
fixed more issues with rotation. restrutured how MarqueeAnnotator works to be simpler and more correct.
Diffstat (limited to 'src/client/views/pdf')
| -rw-r--r-- | src/client/views/pdf/Annotation.tsx | 30 | ||||
| -rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 50 |
2 files changed, 38 insertions, 42 deletions
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 38e4f65b6..e1e87763c 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -12,12 +12,13 @@ import { FieldViewProps } from '../nodes/FieldView'; import { AnchorMenu } from './AnchorMenu'; import './Annotation.scss'; import { LinkManager } from '../../util/LinkManager'; +import { Rect } from 'react-measure'; interface IAnnotationProps extends FieldViewProps { anno: Doc; dataDoc: Doc; fieldKey: string; - showInfo: (anno: Opt<Doc>) => void; + showInfo?: (anno: Opt<Doc>) => void; pointerEvents?: () => Opt<string>; } @observer @@ -70,17 +71,23 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> { }; @action + onContextMenu = (e: React.MouseEvent) => { + AnchorMenu.Instance.Status = 'annotation'; + AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this); + AnchorMenu.Instance.Pinned = false; + AnchorMenu.Instance.PinToPres = this.pinToPres; + AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle; + AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler; + AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion); + AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true); + e.stopPropagation(); + e.preventDefault(); + }; + @action onPointerDown = (e: React.PointerEvent) => { if (e.button === 2 || e.ctrlKey) { - AnchorMenu.Instance.Status = 'annotation'; - AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this); - AnchorMenu.Instance.Pinned = false; - AnchorMenu.Instance.PinToPres = this.pinToPres; - AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle; - AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler; - AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion); - AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true); e.stopPropagation(); + e.preventDefault(); } else if (e.button === 0) { e.stopPropagation(); LinkFollower.FollowLink(undefined, this.annoTextRegion, false); @@ -102,13 +109,14 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> { ref={this._mainCont} onPointerEnter={action(() => { Doc.BrushDoc(this.props.anno); - this.props.showInfo(this.props.anno); + this.props.showInfo?.(this.props.anno); })} onPointerLeave={action(() => { Doc.UnBrushDoc(this.props.anno); - this.props.showInfo(undefined); + this.props.showInfo?.(undefined); })} onPointerDown={this.onPointerDown} + onContextMenu={this.onContextMenu} style={{ left: NumCast(this.props.document.x), top: NumCast(this.props.document.y), diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index a66e519c9..ca9bf7bd2 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -2,7 +2,7 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio 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 { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { Height } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; @@ -11,7 +11,6 @@ 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 { SharingManager } from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; import { MarqueeOptionsMenu } from '../collections/collectionFreeForm'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; @@ -56,16 +55,15 @@ export class PDFViewer extends React.Component<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; 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 _marqueeref = React.createRef<MarqueeAnnotator>(); private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewer: React.RefObject<HTMLDivElement> = React.createRef(); @@ -368,17 +366,14 @@ export class PDFViewer extends React.Component<IViewerProps> { if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) { this.props.select(false); MarqueeAnnotator.clearAnnotations(this._savedAnnotations); - this._marqueeing = [e.clientX, e.clientY]; + this._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); @@ -390,7 +385,7 @@ 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; }; @@ -420,23 +415,26 @@ export class PDFViewer extends React.Component<IViewerProps> { @action createTextAnnotation = (sel: Selection, selRange: Range) => { if (this._mainCont.current) { + this._mainCont.current.style.transform = `rotate(${NumCast(this.props.DocumentView!().screenToLocalTransform().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 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 || ''; @@ -482,24 +480,13 @@ export class PDFViewer extends React.Component<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} 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); @@ -567,6 +554,7 @@ export class PDFViewer extends React.Component<IViewerProps> { 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 ( @@ -585,17 +573,17 @@ export class PDFViewer extends React.Component<IViewerProps> { {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 + ref={this._marqueeref} rootDoc={this.props.rootDoc} getPageFromScroll={this.getPageFromScroll} anchorMenuClick={this.props.anchorMenuClick} scrollTop={0} - down={this._marqueeing} - addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)} - docView={this.props.docViewPath().lastElement()} + annotationLayerScrollTop={NumCast(this.props.Document._layout_scrollTop)} + addDocument={this.addDocumentWrapper} + docView={this.props.DocumentView!} finishMarquee={this.finishMarquee} savedAnnotations={this.savedAnnotations} selectionText={this.selectionText} |
