diff options
-rw-r--r-- | src/client/views/MarqueeAnnotator.scss | 12 | ||||
-rw-r--r-- | src/client/views/MarqueeAnnotator.tsx | 198 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 196 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.scss | 16 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 272 |
5 files changed, 273 insertions, 421 deletions
diff --git a/src/client/views/MarqueeAnnotator.scss b/src/client/views/MarqueeAnnotator.scss new file mode 100644 index 000000000..c90d48a65 --- /dev/null +++ b/src/client/views/MarqueeAnnotator.scss @@ -0,0 +1,12 @@ + +.marqueeAnnotator-annotationBox { + position: absolute; + background-color: rgba(245, 230, 95, 0.616); +} + + +.marqueeAnnotator-dragBox { + position:absolute; + background-color: transparent; + opacity: 0.1; +}
\ No newline at end of file diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx new file mode 100644 index 000000000..bcc03bea1 --- /dev/null +++ b/src/client/views/MarqueeAnnotator.tsx @@ -0,0 +1,198 @@ +import { action, observable } from "mobx"; +import { observer } from "mobx-react"; +import { Dictionary } from "typescript-collections"; +import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../fields/Doc"; +import { Id } from "../../fields/FieldSymbols"; +import { GetEffectiveAcl } from "../../fields/util"; +import { DocUtils, Docs } from "../documents/Documents"; +import { CurrentUserUtils } from "../util/CurrentUserUtils"; +import { DragManager } from "../util/DragManager"; +import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox"; +import { PDFMenu } from "./pdf/PDFMenu"; +import "./MarqueeAnnotator.scss"; +import React = require("react"); +import { undoBatch } from "../util/UndoManager"; +import { NumCast } from "../../fields/Types"; +import { DocumentType } from "../documents/DocumentTypes"; +import { List } from "../../fields/List"; +const _global = (window /* browser */ || global /* node */) as any; + +export interface MarqueeAnnotatorProps { + rootDoc: Doc; + clientX: number; + clientY: number; + scaling?: () => number; + mainCont: HTMLDivElement; + savedAnnotations: Dictionary<number, HTMLDivElement[]>; + annotationLayer: HTMLDivElement; + addDocument: (doc: Doc) => boolean; + getPageFromScroll?: (top: number) => number; + finishMarquee: () => void; +} +@observer +export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { + private _startX: number = 0; + private _startY: number = 0; + @observable private _left: number = 0; + @observable private _top: number = 0; + @observable private _width: number = 0; + @observable private _height: number = 0; + + @action componentDidMount() { + // set marquee x and y positions to the spatially transformed position + const boundingRect = this.props.mainCont.getBoundingClientRect(); + this._startX = this._left = (this.props.clientX - boundingRect.left) * (this.props.mainCont.offsetWidth / boundingRect.width); + this._startY = this._top = (this.props.clientY - boundingRect.top) * (this.props.mainCont.offsetHeight / boundingRect.height) + this.props.mainCont.scrollTop; + this._height = this._width = 0; + document.addEventListener("pointermove", this.onSelectMove); + document.addEventListener("pointerup", this.onSelectEnd); + } + componentWillUnmount() { + document.removeEventListener("pointermove", this.onSelectMove); + document.removeEventListener("pointerup", this.onSelectEnd); + } + + @undoBatch + @action + makeAnnotationDocument = (color: string): Opt<Doc> => { + if (this.props.savedAnnotations.size() === 0) return undefined; + if ((this.props.savedAnnotations.values()[0][0] as any).marqueeing) { + const scale = this.props.scaling?.() || 1; + const anno = this.props.savedAnnotations.values()[0][0]; + const mainAnnoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title }); + if (anno.style.left) mainAnnoDoc.x = parseInt(anno.style.left) / scale; + if (anno.style.top) mainAnnoDoc.y = (NumCast(this.props.rootDoc._scrollTop) + parseInt(anno.style.top)) / scale; + if (anno.style.height) mainAnnoDoc._height = parseInt(anno.style.height) / scale; + if (anno.style.width) mainAnnoDoc._width = parseInt(anno.style.width) / scale; + mainAnnoDoc.group = mainAnnoDoc; + anno.remove(); + this.props.savedAnnotations.clear(); + return mainAnnoDoc; + } else { + const mainAnnoDoc = Docs.Create.FreeformDocument([], { type: DocumentType.PDFANNO, annotationOn: this.props.rootDoc, title: "Selection on " + this.props.rootDoc.title, _width: 1, _height: 1 }); + const mainAnnoDocProto = Doc.GetProto(mainAnnoDoc); + + let maxX = -Number.MAX_VALUE; + let minY = Number.MAX_VALUE; + const annoDocs: Doc[] = []; + this.props.savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => value.map(anno => { + const annoDoc = new Doc(); + if (anno.style.left) annoDoc.x = parseInt(anno.style.left); + if (anno.style.top) annoDoc.y = parseInt(anno.style.top); + if (anno.style.height) annoDoc._height = parseInt(anno.style.height); + if (anno.style.width) annoDoc._width = parseInt(anno.style.width); + annoDoc.group = mainAnnoDoc; + annoDoc.backgroundColor = color; + annoDocs.push(annoDoc); + anno.remove(); + (annoDoc.y !== undefined) && (minY = Math.min(NumCast(annoDoc.y), minY)); + (annoDoc.x !== undefined) && (maxX = Math.max(NumCast(annoDoc.x) + NumCast(annoDoc._width), maxX)); + })); + + mainAnnoDocProto.y = Math.max(minY, 0); + mainAnnoDocProto.x = Math.max(maxX, 0); + // mainAnnoDocProto.text = this._selectionText; + mainAnnoDocProto.annotations = new List<Doc>(annoDocs); + this.props.savedAnnotations.clear(); + return mainAnnoDoc; + } + } + @action + highlight = (color: string) => { + // creates annotation documents for current highlights + const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]); + const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color); + annotationDoc && this.props.addDocument(annotationDoc); + return annotationDoc as Doc ?? undefined; + } + + public static previewNewAnnotation = action((savedAnnotations: Dictionary<number, HTMLDivElement[]>, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => { + if (div.style.top) { + div.style.top = (parseInt(div.style.top)/*+ this.getScrollFromPage(page)*/).toString(); + } + annotationLayer.append(div); + div.style.backgroundColor = "#ACCEF7"; + div.style.opacity = "0.5"; + const savedPage = savedAnnotations.getValue(page); + if (savedPage) { + savedPage.push(div); + savedAnnotations.setValue(page, savedPage); + } + else { + savedAnnotations.setValue(page, [div]); + } + }); + + @action + onSelectMove = (e: PointerEvent) => { + // transform positions and find the width and height to set the marquee to + const boundingRect = this.props.mainCont.getBoundingClientRect(); + this._width = ((e.clientX - boundingRect.left) * (this.props.mainCont.offsetWidth / boundingRect.width)) - this._startX; + this._height = ((e.clientY - boundingRect.top) * (this.props.mainCont.offsetHeight / boundingRect.height)) - this._startY + this.props.mainCont.scrollTop; + this._left = Math.min(this._startX, this._startX + this._width); + this._top = Math.min(this._startY, this._startY + this._height); + this._width = Math.abs(this._width); + this._height = Math.abs(this._height); + e.stopPropagation(); + e.preventDefault(); + } + + onSelectEnd = (e: PointerEvent) => { + if (!e.ctrlKey) { + PDFMenu.Instance.Marquee = { left: this._left, top: this._top, width: this._width, height: this._height }; + } + + const marquees = this.props.mainCont.getElementsByClassName("marqueeAnnotator-dragBox"); + if (marquees?.length) { // copy the temporary marquee to allow for multiple selections (not currently available though). + const copy = document.createElement("div"); + ["left", "top", "width", "height", "border", "opacity"].forEach(prop => copy.style[prop as any] = (marquees[0] as HTMLDivElement).style[prop as any]); + copy.className = "marqueeAnnotator-annotationBox"; + (copy as any).marqueeing = true; + MarqueeAnnotator.previewNewAnnotation(this.props.savedAnnotations, this.props.annotationLayer, copy, this.props.getPageFromScroll?.(this._top) || 0); + } + PDFMenu.Instance.Highlight = this.highlight; + /** + * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation. + * It also initiates a Drag/Drop interaction to place the text annotation. + */ + PDFMenu.Instance.StartDrag = action(async (e: PointerEvent, ele: HTMLElement) => { + e.preventDefault(); + e.stopPropagation(); + const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.rootDoc.title, 0, 0, 100, 100); + FormattedTextBox.SelectOnLoad = targetDoc[Id]; + const anchorHighlightDoc = this.highlight("rgba(173, 216, 230, 0.75)"); // hyperlink color + if (anchorHighlightDoc) { + DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.rootDoc, anchorHighlightDoc, targetDoc), e.pageX, e.pageY, { + dragComplete: e => { + if (!e.aborted && e.annoDragData && !e.linkDocument) { + e.linkDocument = DocUtils.MakeLink({ doc: anchorHighlightDoc }, { doc: e.annoDragData.dropDocument }, "Annotation"); + anchorHighlightDoc.isLinkButton = true; // prevents link button from showing up --- maybe not a good thing? + anchorHighlightDoc.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.props.rootDoc; + } + e.linkDocument && e.annoDragData?.linkDropCallback?.(e as { linkDocument: Doc });// bcz: typescript can't figure out that this is valid even though we tested e.linkDocument + } + }); + } + }); + + if (this._width > 10 || this._height > 10) { // configure and show the annotation/link menu if a the drag region is big enough + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); + + if (PDFMenu.Instance.Highlighting) {// when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up + this.highlight("rgba(245, 230, 95, 0.75)"); // yellowish highlight color for highlighted text (should match PDFMenu's highlight color) + } + } + this.props.finishMarquee(); + } + + render() { + return <div className="marqueeAnnotator-dragBox" + style={{ + left: `${this._left}px`, top: `${this._top}px`, + width: `${this._width}px`, height: `${this._height}px`, + border: `${this._width === 0 ? "" : "2px dashed black"}`, + opacity: 0.2 + }}> + </div>; + } +} diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 4c4f3f15a..f9e71bc92 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,9 +1,10 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Dictionary } from "typescript-collections"; import * as WebRequest from 'web-request'; -import { Doc, DocListCast, Opt, AclAddonly, AclEdit, AclAdmin, DataSym, HeightSym, WidthSym } from "../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { HtmlField } from "../../../fields/HtmlField"; @@ -12,9 +13,9 @@ import { List } from "../../../fields/List"; import { listSpec, makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { WebField } from "../../../fields/URLField"; -import { TraceMobx, GetEffectiveAcl } from "../../../fields/util"; -import { addStyleSheet, clearStyleSheetRules, emptyFunction, returnOne, returnZero, Utils, returnTrue, OmitKeys, smoothScroll } from "../../../Utils"; -import { Docs, DocUtils } from "../../documents/Documents"; +import { TraceMobx } from "../../../fields/util"; +import { addStyleSheet, clearStyleSheetRules, emptyFunction, OmitKeys, returnOne, smoothScroll, Utils } from "../../../Utils"; +import { Docs } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { undoBatch } from "../../util/UndoManager"; @@ -25,14 +26,10 @@ import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; import { Annotation } from "../pdf/Annotation"; import { PDFMenu } from "../pdf/PDFMenu"; -import { PdfViewerMarquee } from "../pdf/PDFViewer"; import { FieldView, FieldViewProps } from './FieldView'; import "./WebBox.scss"; -import "../pdf/PDFViewer.scss"; import React = require("react"); -import { Tooltip } from '@material-ui/core'; -import { CurrentUserUtils } from '../../util/CurrentUserUtils'; -import { FormattedTextBox } from './formattedText/FormattedTextBox'; +import { MarqueeAnnotator } from "../MarqueeAnnotator"; const htmlToText = require("html-to-text"); type WebDocument = makeInterface<[typeof documentSchema]>; @@ -40,17 +37,12 @@ const WebDocument = makeInterface(documentSchema); @observer export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocument>(WebDocument) { - private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); static _annotationStyle: any = addStyleSheet(); public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); - private _startX: number = 0; - private _startY: number = 0; - @observable private _marqueeX: number = 0; - @observable private _marqueeY: number = 0; - @observable private _marqueeWidth: number = 0; - @observable private _marqueeHeight: number = 0; + private _downX: number = 0; + private _downY: number = 0; @observable private _marqueeing: boolean = false; @observable private _url: string = "hello"; @observable private _pressX: number = 0; @@ -211,9 +203,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum this._iframe?.removeEventListener('wheel', this.iframeWheel); } - onUrlDragover = (e: React.DragEvent) => { - e.preventDefault(); - } + onUrlDragover = (e: React.DragEvent) => { e.preventDefault(); } @undoBatch @action @@ -256,6 +246,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum urlHash(s: string) { return s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0); } + @action submitURL = () => { if (!this._url.startsWith("http")) this._url = "http://" + this._url; @@ -283,9 +274,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } onValueKeyDown = async (e: React.KeyboardEvent) => { - if (e.key === "Enter") { - this.submitURL(); - } + if (e.key === "Enter") this.submitURL(); e.stopPropagation(); } @@ -300,22 +289,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } _ignore = 0; - onPreWheel = (e: React.WheelEvent) => { - this._ignore = e.timeStamp; - } - onPrePointer = (e: React.PointerEvent) => { - this._ignore = e.timeStamp; - } + onPreWheel = (e: React.WheelEvent) => { this._ignore = e.timeStamp; } + onPrePointer = (e: React.PointerEvent) => { this._ignore = e.timeStamp; } onPostPointer = (e: React.PointerEvent) => { - if (this._ignore !== e.timeStamp) { - e.stopPropagation(); - } + if (this._ignore !== e.timeStamp) e.stopPropagation(); } onPostWheel = (e: React.WheelEvent) => { - if (this._ignore !== e.timeStamp) { - e.stopPropagation(); - } + if (this._ignore !== e.timeStamp) e.stopPropagation(); } onLongPressDown = (e: React.PointerEvent) => { @@ -431,7 +412,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum @computed get urlContent() { - const field = this.dataDoc[this.props.fieldKey]; let view; if (field instanceof HtmlField) { @@ -449,6 +429,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } return view; } + @computed get content() { const view = this.urlContent; @@ -476,26 +457,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum </>); } - - @computed get allAnnotations() { return DocListCast(this.dataDoc[this.props.fieldKey + "-annotations"]); } @computed get nonDocAnnotations() { return this.allAnnotations.filter(a => a.annotations); } - @undoBatch - @action - makeAnnotationDocument = (color: string): Opt<Doc> => { - const scale = this.props.scaling?.() || 1; - if (this._savedAnnotations.size() === 0) return undefined; - const anno = this._savedAnnotations.values()[0][0]; - const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, annotationOn: this.props.Document, title: "Annotation on " + this.Document.title }); - if (anno.style.left) annoDoc.x = parseInt(anno.style.left) / scale; - if (anno.style.top) annoDoc.y = (NumCast(this.layoutDoc._scrollTop) + parseInt(anno.style.top)) / scale; - if (anno.style.height) annoDoc._height = parseInt(anno.style.height) / scale; - if (anno.style.width) annoDoc._width = parseInt(anno.style.width) / scale; - anno.remove(); - this._savedAnnotations.clear(); - return annoDoc; - } @computed get annotationLayer() { TraceMobx(); return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}> @@ -504,65 +468,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum } </div>; } - @action - createAnnotation = (div: HTMLDivElement, page: number) => { - if (this._annotationLayer.current) { - if (div.style.top) { - div.style.top = (parseInt(div.style.top)).toString(); - } - this._annotationLayer.current.append(div); - div.style.backgroundColor = "#ACCEF7"; - div.style.opacity = "0.5"; - const savedPage = this._savedAnnotations.getValue(page); - if (savedPage) { - savedPage.push(div); - this._savedAnnotations.setValue(page, savedPage); - } - else { - this._savedAnnotations.setValue(page, [div]); - } - } - } - - @action - highlight = (color: string) => { - // creates annotation documents for current highlights - const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); - const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) ? this.makeAnnotationDocument(color.replace(/[0-9.]*\)/, "0.3)")) : undefined; - annotationDoc && this.addDocument?.(annotationDoc); - return annotationDoc ?? undefined; - } - /** - * This is temporary for creating annotations from highlights. It will - * start a drag event and create or put the necessary info into the drag event. - */ - @action - startDrag = async (e: PointerEvent, ele: HTMLElement) => { - e.preventDefault(); - e.stopPropagation(); - const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.Document.title, 0, 0, 125, 125); - FormattedTextBox.SelectOnLoad = targetDoc[Id]; - const annotationDoc = this.highlight("rgba(173, 216, 230, 0.35)"); // hyperlink color - if (annotationDoc) { - DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, { - dragComplete: e => { - if (!e.aborted && e.annoDragData && !e.linkDocument) { - e.linkDocument = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation"); - annotationDoc.isLinkButton = true; - annotationDoc.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.props.Document; - } - } - }); - } - } @action onMarqueeDown = (e: React.PointerEvent) => { - this._marqueeing = false; + this._downX = e.clientX; + this._downY = e.clientY; if (!e.altKey && e.button === 0 && this.active(true)) { // clear out old marquees and initialize menu for new selection - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; PDFMenu.Instance.Status = "pdf"; PDFMenu.Instance.fadeOut(true); this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); @@ -570,79 +482,30 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum if ((e.target as any)?.parentElement.className === "textLayer") { // start selecting text if mouse down on textLayer spans } - else if (this._mainCont.current) { - // set marquee x and y positions to the spatially transformed position - const boundingRect = this._mainCont.current.getBoundingClientRect(); - this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width); - this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height) + this._mainCont.current.scrollTop; - this._marqueeHeight = this._marqueeWidth = 0; - this._marqueeing = true; - } + else this._marqueeing = true; document.addEventListener("pointermove", this.onSelectMove); document.addEventListener("pointerup", this.onSelectEnd); } } + @action onSelectMove = (e: PointerEvent): void => { - if (this._marqueeing && this._mainCont.current) { - // transform positions and find the width and height to set the marquee to - const boundingRect = this._mainCont.current.getBoundingClientRect(); - this._marqueeWidth = ((e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width)) - this._startX; - this._marqueeHeight = ((e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height)) - this._startY + this._mainCont.current.scrollTop; - this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth); - this._marqueeY = Math.min(this._startY, this._startY + this._marqueeHeight); - this._marqueeWidth = Math.abs(this._marqueeWidth); - this._marqueeHeight = Math.abs(this._marqueeHeight); - e.stopPropagation(); - e.preventDefault(); - } - else if (e.target && (e.target as any).parentElement === this._mainCont.current) { - e.stopPropagation(); - } + if (e.target && (e.target as any).parentElement === this._mainCont.current) e.stopPropagation(); } @action + finishMarquee = () => { this._marqueeing = false; } + + @action onSelectEnd = (e: PointerEvent): void => { clearStyleSheetRules(WebBox._annotationStyle); + this.props.select(true); this._savedAnnotations.clear(); - if (this._marqueeWidth > 10 || this._marqueeHeight > 10) { - const marquees = this._mainCont.current!.getElementsByClassName("pdfViewerDash-dragAnnotationBox"); - if (marquees?.length) { // copy the marquee and convert it to a permanent annotation. - const style = (marquees[0] as HTMLDivElement).style; - const copy = document.createElement("div"); - copy.style.left = style.left; - copy.style.top = style.top; - copy.style.width = style.width; - copy.style.height = style.height; - copy.style.border = style.border; - copy.style.opacity = style.opacity; - (copy as any).marqueeing = true; - copy.className = "webBox-annotationBox"; - this.createAnnotation(copy, 0); - } - if (!e.ctrlKey) { - PDFMenu.Instance.Marquee = { left: this._marqueeX, top: this._marqueeY, width: this._marqueeWidth, height: this._marqueeHeight }; - } - PDFMenu.Instance.jumpTo(e.clientX, e.clientY); - } - //this._marqueeing = false; - - if (PDFMenu.Instance.Highlighting) {// when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up - this.highlight("rgba(245, 230, 95, 0.616)"); // yellowish highlight color for highlighted text (should match PDFMenu's highlight color) - } - else { - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; - } document.removeEventListener("pointermove", this.onSelectMove); document.removeEventListener("pointerup", this.onSelectEnd); } - marqueeWidth = () => this._marqueeWidth; - marqueeHeight = () => this._marqueeHeight; - marqueeX = () => this._marqueeX; - marqueeY = () => this._marqueeY; - marqueeing = () => this._marqueeing; + scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc._scrollLeft), NumCast(this.layoutDoc._scrollTop)); render() { const inactiveLayer = this.props.layerProvider?.(this.layoutDoc) === false; @@ -655,9 +518,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum {this.content} <div className={"webBox-outerContent"} ref={this._outerRef} style={{ - width: `${100 / scale}%`, - height: `${100 / scale}%`, - transform: `scale(${scale})`, + width: `${100 / scale}%`, height: `${100 / scale}%`, transform: `scale(${scale})`, pointerEvents: this.layoutDoc.isAnnotating && !inactiveLayer ? "all" : "none" }} onWheel={e => { @@ -700,7 +561,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum </div> </div> {this.annotationLayer} - <PdfViewerMarquee isMarqueeing={this.marqueeing} width={this.marqueeWidth} height={this.marqueeHeight} x={this.marqueeX} y={this.marqueeY} /> + {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : + <MarqueeAnnotator rootDoc={this.rootDoc} scaling={this.props.scaling} addDocument={this.addDocument} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} clientX={this._downX} clientY={this._downY} mainCont={this._mainCont.current} />} </div > {this.props.isSelected() ? this.editToggleBtn() : null} </div>); diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 0ecb4dba4..d17b2b5ef 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -34,6 +34,8 @@ border: unset; } .pdfViewerDash-text-selected { + position: relative; + z-index: -1; .textLayer{ pointer-events: all; user-select: text; @@ -46,12 +48,6 @@ } } - .pdfViewerDash-dragAnnotationBox { - position:absolute; - background-color: transparent; - opacity: 0.1; - } - .pdfViewerDash-overlay, .pdfViewerDash-overlay-inking { transform-origin: left top; position: absolute; @@ -83,11 +79,6 @@ width: 100%; pointer-events: none; mix-blend-mode: multiply; // bcz: makes text fuzzy! - - .pdfViewerDash-annotationBox { - position: absolute; - background-color: rgba(245, 230, 95, 0.616); - } } .pdfViewerDash-waiting { width: 70%; @@ -102,5 +93,4 @@ .pdfViewerDash-interactive { pointer-events: all; -} -
\ No newline at end of file +}
\ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 8700a5739..03ffdb2db 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -3,31 +3,26 @@ import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import { Dictionary } from "typescript-collections"; -import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { InkTool } from "../../../fields/InkField"; -import { List } from "../../../fields/List"; import { createSchema, makeInterface } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { PdfField } from "../../../fields/URLField"; -import { GetEffectiveAcl, TraceMobx } from "../../../fields/util"; +import { TraceMobx } from "../../../fields/util"; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from "../../../Utils"; -import { Docs, DocUtils } from "../../documents/Documents"; -import { DocumentType } from "../../documents/DocumentTypes"; +import { DocUtils } from "../../documents/Documents"; import { Networking } from "../../Network"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { DragManager } from "../../util/DragManager"; import { CompiledScript, CompileScript } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; import { SharingManager } from "../../util/SharingManager"; import { SnappingManager } from "../../util/SnappingManager"; -import { undoBatch } from "../../util/UndoManager"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; +import { MarqueeAnnotator } from "../MarqueeAnnotator"; import { FieldViewProps } from "../nodes/FieldView"; -import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox"; import { FormattedTextBoxComment } from "../nodes/formattedText/FormattedTextBoxComment"; import { LinkDocPreview } from "../nodes/LinkDocPreview"; import { Annotation } from "./Annotation"; @@ -73,10 +68,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>(); @observable private _script: CompiledScript = CompileScript("return true") as CompiledScript; @observable private Index: number = -1; - @observable private _marqueeX: number = 0; - @observable private _marqueeY: number = 0; - @observable private _marqueeWidth: number = 0; - @observable private _marqueeHeight: number = 0; @observable private _marqueeing: boolean = false; @observable private _showWaiting = true; @observable private _showCover = false; @@ -92,8 +83,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu private _viewer: React.RefObject<HTMLDivElement> = React.createRef(); private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); private _selectionText: string = ""; - private _startX: number = 0; - private _startY: number = 0; private _downX: number = 0; private _downY: number = 0; private _coverPath: any; @@ -196,13 +185,13 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu } copy = (e: ClipboardEvent) => { - if (this.props.active(true) && e.clipboardData) { - const annoDoc = this.makeAnnotationDocument("rgba(3,144,152,0.3)"); // copied text markup color (blueish) - if (annoDoc) { - e.clipboardData.setData("text/plain", this._selectionText); - e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]); - e.clipboardData.setData("dash/pdfRegion", annoDoc[Id]); - } + if (this.active() && e.clipboardData) { + //const annoDoc = this.makeAnnotationDocument("rgba(3,144,152,0.3)"); // copied text markup color (blueish) + //if (annoDoc) { + e.clipboardData.setData("text/plain", this._selectionText); + // e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]); + // e.clipboardData.setData("dash/pdfRegion", annoDoc[Id]); + //} e.preventDefault(); } } @@ -292,57 +281,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu this._pdfViewer.setDocument(this.props.pdf); } - @undoBatch - @action - makeAnnotationDocument = (color: string): Opt<Doc> => { - if (this._savedAnnotations.size() === 0) return undefined; - // let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {}); - let mainAnnoDoc = Docs.Create.FreeformDocument([], { title: "anno", _width: 1, _height: 1 }); - let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc); - const annoDocs: Doc[] = []; - let maxX = -Number.MAX_VALUE; - let minY = Number.MAX_VALUE; - if ((this._savedAnnotations.values()[0][0] as any).marqueeing) { - const anno = this._savedAnnotations.values()[0][0]; - const annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color.replace(/[0-9.]*\)/, ".3)"), title: "Annotation on " + this.Document.title }); - if (anno.style.left) annoDoc.x = parseInt(anno.style.left); - if (anno.style.top) annoDoc.y = parseInt(anno.style.top); - if (anno.style.height) annoDoc._height = parseInt(anno.style.height); - if (anno.style.width) annoDoc._width = parseInt(anno.style.width); - annoDoc.group = mainAnnoDoc; - annoDocs.push(annoDoc); - anno.remove(); - mainAnnoDoc = annoDoc; - mainAnnoDocProto.type = DocumentType.COL; - mainAnnoDocProto = Doc.GetProto(mainAnnoDoc); - mainAnnoDocProto.y = annoDoc.y; - } else { - this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => value.map(anno => { - const annoDoc = new Doc(); - if (anno.style.left) annoDoc.x = parseInt(anno.style.left); - if (anno.style.top) annoDoc.y = parseInt(anno.style.top); - if (anno.style.height) annoDoc._height = parseInt(anno.style.height); - if (anno.style.width) annoDoc._width = parseInt(anno.style.width); - annoDoc.group = mainAnnoDoc; - annoDoc.backgroundColor = color; - annoDocs.push(annoDoc); - anno.remove(); - (annoDoc.y !== undefined) && (minY = Math.min(NumCast(annoDoc.y), minY)); - (annoDoc.x !== undefined) && (maxX = Math.max(NumCast(annoDoc.x) + NumCast(annoDoc._width), maxX)); - })); - - mainAnnoDocProto.y = Math.max(minY, 0); - mainAnnoDocProto.x = Math.max(maxX, 0); - mainAnnoDocProto.type = DocumentType.PDFANNO; - mainAnnoDocProto.text = this._selectionText; - mainAnnoDocProto.annotations = new List<Doc>(annoDocs); - } - mainAnnoDocProto.title = "Annotation on " + this.Document.title; - mainAnnoDocProto.annotationOn = this.props.Document; - this._savedAnnotations.clear(); - this.Index = -1; - return mainAnnoDoc; - } + @action prevAnnotation = () => { this.Index = Math.max(this.Index - 1, 0); @@ -400,25 +339,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu return index; } - @action - createAnnotation = (div: HTMLDivElement, page: number) => { - if (this._annotationLayer.current) { - if (div.style.top) { - div.style.top = (parseInt(div.style.top)/*+ this.getScrollFromPage(page)*/).toString(); - } - this._annotationLayer.current.append(div); - div.style.backgroundColor = "#ACCEF7"; - div.style.opacity = "0.5"; - const savedPage = this._savedAnnotations.getValue(page); - if (savedPage) { - savedPage.push(div); - this._savedAnnotations.setValue(page, savedPage); - } - else { - this._savedAnnotations.setValue(page, [div]); - } - } - } @action search = (searchString: string, fwd: boolean, clear: boolean = false) => { @@ -448,7 +368,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu this._mainCont.current.addEventListener("pagesloaded", executeFind); this._mainCont.current.addEventListener("pagerendered", executeFind); } - } @action @@ -465,11 +384,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu if ((e.button !== 0 || e.altKey) && this.active(true)) { this._setPreviewCursor?.(e.clientX, e.clientY, true); } - this._marqueeing = false; if (!e.altKey && e.button === 0 && this.active(true)) { // clear out old marquees and initialize menu for new selection - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; PDFMenu.Instance.Status = "pdf"; PDFMenu.Instance.fadeOut(true); this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); @@ -477,14 +393,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu if (e.target && (e.target as any).parentElement.className === "textLayer") { // start selecting text if mouse down on textLayer spans } - else if (this._mainCont.current) { - // set marquee x and y positions to the spatially transformed position - const boundingRect = this._mainCont.current.getBoundingClientRect(); - this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width); - this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height) + this._mainCont.current.scrollTop; - this._marqueeHeight = this._marqueeWidth = 0; - this._marqueeing = true; - } + else this._marqueeing = true; document.addEventListener("pointermove", this.onSelectMove); document.addEventListener("pointerup", this.onSelectEnd); document.addEventListener("pointerup", this.removeStyle, true); @@ -497,21 +406,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu @action onSelectMove = (e: PointerEvent): void => { - if (this._marqueeing && this._mainCont.current) { - // transform positions and find the width and height to set the marquee to - const boundingRect = this._mainCont.current.getBoundingClientRect(); - this._marqueeWidth = ((e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width)) - this._startX; - this._marqueeHeight = ((e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height)) - this._startY + this._mainCont.current.scrollTop; - this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth); - this._marqueeY = Math.min(this._startY, this._startY + this._marqueeHeight); - this._marqueeWidth = Math.abs(this._marqueeWidth); - this._marqueeHeight = Math.abs(this._marqueeHeight); - e.stopPropagation(); - e.preventDefault(); - } - else if (e.target && (e.target as any).parentElement === this._mainCont.current) { - e.stopPropagation(); - } + if (e.target && (e.target as any).parentElement === this._mainCont.current) e.stopPropagation(); } @action @@ -522,17 +417,16 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu for (let i = 0; i < clientRects.length; i++) { const rect = clientRects.item(i); if (rect) { - const scaleY = this._mainCont.current.offsetHeight / boundingRect.height; const scaleX = this._mainCont.current.offsetWidth / boundingRect.width; if (rect.width !== this._mainCont.current.clientWidth) { const annoBox = document.createElement("div"); - annoBox.className = "pdfViewerDash-annotationBox"; + annoBox.className = "marqueeAnnotator-annotationBox"; // transforms the positions from screen onto the pdf div annoBox.style.top = ((rect.top - boundingRect.top) * scaleX / this._zoomed + this._mainCont.current.scrollTop).toString(); annoBox.style.left = ((rect.left - boundingRect.left) * scaleX / this._zoomed).toString(); annoBox.style.width = (rect.width * this._mainCont.current.offsetWidth / boundingRect.width / this._zoomed).toString(); annoBox.style.height = (rect.height * this._mainCont.current.offsetHeight / boundingRect.height / this._zoomed).toString(); - this.createAnnotation(annoBox, this.getPageFromScroll(rect.top)); + this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, this.getPageFromScroll(rect.top)); } } } @@ -548,116 +442,38 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu } @action + finishMarquee = () => { this._marqueeing = false; } + + @action onSelectEnd = (e: PointerEvent): void => { clearStyleSheetRules(PDFViewer._annotationStyle); this.props.select(false); this._savedAnnotations.clear(); - if (this._marqueeing) { - if (this._marqueeWidth > 10 || this._marqueeHeight > 10) { - const marquees = this._mainCont.current!.getElementsByClassName("pdfViewerDash-dragAnnotationBox"); - if (marquees?.length) { // copy the marquee and convert it to a permanent annotation. - const style = (marquees[0] as HTMLDivElement).style; - const copy = document.createElement("div"); - copy.style.left = style.left; - copy.style.top = style.top; - copy.style.width = style.width; - copy.style.height = style.height; - copy.style.border = style.border; - copy.style.opacity = style.opacity; - (copy as any).marqueeing = true; - copy.className = "pdfViewerDash-annotationBox"; - this.createAnnotation(copy, this.getPageFromScroll(this._marqueeY)); - } - - if (!e.ctrlKey) { - PDFMenu.Instance.Marquee = { left: this._marqueeX, top: this._marqueeY, width: this._marqueeWidth, height: this._marqueeHeight }; - } - PDFMenu.Instance.jumpTo(e.clientX, e.clientY); - } - this._marqueeing = false; - } - else { - const sel = window.getSelection(); - if (sel?.type === "Range") { - const selRange = sel.getRangeAt(0); - this.createTextAnnotation(sel, selRange); - PDFMenu.Instance.jumpTo(e.clientX, e.clientY); - } - } - - if (PDFMenu.Instance.Highlighting) {// when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up - this.highlight("rgba(245, 230, 95, 0.75)"); // yellowish highlight color for highlighted text (should match PDFMenu's highlight color) - } - else { - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; - } document.removeEventListener("pointermove", this.onSelectMove); document.removeEventListener("pointerup", this.onSelectEnd); - } - - @action - highlight = (color: string) => { - // creates annotation documents for current highlights - const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); - const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color); - annotationDoc && this.addDocument?.(annotationDoc); - return annotationDoc as Doc ?? undefined; - } - /** - * This is temporary for creating annotations from highlights. It will - * start a drag event and create or put the necessary info into the drag event. - */ - @action - startDrag = async (e: PointerEvent, ele: HTMLElement) => { - e.preventDefault(); - e.stopPropagation(); - - const clipDoc = Doc.MakeAlias(this.dataDoc); - clipDoc._fitWidth = true; - clipDoc._width = this.marqueeWidth(); - clipDoc._height = this.marqueeHeight(); - clipDoc._scrollTop = this.marqueeY(); - const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.Document.title, 0, 0, 100, 100); - FormattedTextBox.SelectOnLoad = targetDoc[Id]; - Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]); - clipDoc.rootDocument = targetDoc; - // DocUtils.makeCustomViewClicked(targetDoc, Docs.Create.StackingDocument, "slideView", undefined); - // targetDoc.layoutKey = "layout"; - // const targetDoc = Docs.Create.TextDocument("", { _width: 200, _height: 200, title: "Note linked to " + this.props.Document.title }); - // Doc.GetProto(targetDoc).snipped = this.dataDoc[this.props.fieldKey][Copy](); - // const snipLayout = Docs.Create.PdfDocument("http://www.msn.com", { title: "snippetView", isTemplateDoc: true, isTemplateForField: "snipped", _fitWidth: true, _width: this.marqueeWidth(), _height: this.marqueeHeight(), _scrollTop: this.marqueeY() }); - // Doc.GetProto(snipLayout).layout = PDFBox.LayoutString("snipped"); - const annotationDoc = this.highlight("rgba(173, 216, 230, 0.75)"); // hyperlink color - if (annotationDoc) { - DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, { - dragComplete: e => { - if (!e.aborted && e.annoDragData && !e.linkDocument) { - e.linkDocument = DocUtils.MakeLink({ doc: annotationDoc }, { doc: e.annoDragData.dropDocument }, "Annotation"); - } - annotationDoc.isLinkButton = true; // prevents link button fro showing up --- maybe not a good thing? - annotationDoc.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.props.Document; - e.linkDocument && e.annoDragData?.linkDropCallback?.(e as { linkDocument: Doc });// bcz: typescript can't figure out that this is valid even though we tested e.linkDocument above - } - }); + const sel = window.getSelection(); + if (sel?.type === "Range") { + const selRange = sel.getRangeAt(0); + this.createTextAnnotation(sel, selRange); + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); } } scrollXf = () => { return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this.layoutDoc._scrollTop || 0) : this.props.ScreenToLocalTransform(); } + onClick = (e: React.MouseEvent) => { - this._setPreviewCursor && - e.button === 0 && + if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < 3 && - Math.abs(e.clientY - this._downY) < 3 && + Math.abs(e.clientY - this._downY) < 3) { this._setPreviewCursor(e.clientX, e.clientY, false); + } } setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; - getCoverImage = () => { if (!this.props.Document[HeightSym]() || !Doc.NativeHeight(this.props.Document)) { setTimeout((() => { @@ -745,11 +561,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu {this._showWaiting ? <img className="pdfViewerDash-waiting" key="waiting" src={"/assets/loading.gif"} /> : (null)} </>; } - marqueeWidth = () => this._marqueeWidth; - marqueeHeight = () => this._marqueeHeight; - marqueeX = () => this._marqueeX; - marqueeY = () => this._marqueeY; - marqueeing = () => this._marqueeing; contentZoom = () => this._zoomed; render() { TraceMobx(); @@ -766,29 +577,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu {this.overlayLayer} {this.overlayInfo} {this.standinViews} - <PdfViewerMarquee isMarqueeing={this.marqueeing} width={this.marqueeWidth} height={this.marqueeHeight} x={this.marqueeX} y={this.marqueeY} /> + {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : + <MarqueeAnnotator rootDoc={this.rootDoc} addDocument={this.addDocument} finishMarquee={this.finishMarquee} getPageFromScroll={this.getPageFromScroll} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} clientX={this._downX} clientY={this._downY} mainCont={this._mainCont.current} />} </div >; } -} - -export interface PdfViewerMarqueeProps { - isMarqueeing: () => boolean; - width: () => number; - height: () => number; - x: () => number; - y: () => number; -} - -@observer -export class PdfViewerMarquee extends React.Component<PdfViewerMarqueeProps> { - render() { - return !this.props.isMarqueeing() ? (null) : <div className="pdfViewerDash-dragAnnotationBox" - style={{ - left: `${this.props.x()}px`, top: `${this.props.y()}px`, - width: `${this.props.width()}px`, height: `${this.props.height()}px`, - border: `${this.props.width() === 0 ? "" : "2px dashed black"}`, - opacity: 0.2 - }}> - </div>; - } -} +}
\ No newline at end of file |