aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorusodhi <61431818+usodhi@users.noreply.github.com>2021-01-20 20:07:48 -0500
committerusodhi <61431818+usodhi@users.noreply.github.com>2021-01-20 20:07:48 -0500
commit7b49bca26f95e7bf1e878d4987f061511a5def39 (patch)
treeb160034ebfd770c73071203410b468778fa93697
parente4c9218e217b617e513259cac90b077431890eaa (diff)
parent68785a97178d229935c0429791081d7c09312dc3 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into filters
-rw-r--r--src/client/util/DragManager.ts24
-rw-r--r--src/client/views/GlobalKeyHandler.ts4
-rw-r--r--src/client/views/MainView.tsx4
-rw-r--r--src/client/views/MarqueeAnnotator.scss12
-rw-r--r--src/client/views/MarqueeAnnotator.tsx215
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx3
-rw-r--r--src/client/views/collections/CollectionSubView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx14
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx1
-rw-r--r--src/client/views/nodes/DocumentView.tsx14
-rw-r--r--src/client/views/nodes/ImageBox.scss10
-rw-r--r--src/client/views/nodes/ImageBox.tsx43
-rw-r--r--src/client/views/nodes/VideoBox.tsx45
-rw-r--r--src/client/views/nodes/WebBox.tsx248
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx40
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx2
-rw-r--r--src/client/views/pdf/AnchorMenu.scss (renamed from src/client/views/pdf/PDFMenu.scss)2
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx (renamed from src/client/views/pdf/PDFMenu.tsx)16
-rw-r--r--src/client/views/pdf/Annotation.tsx20
-rw-r--r--src/client/views/pdf/PDFViewer.scss16
-rw-r--r--src/client/views/pdf/PDFViewer.tsx326
21 files changed, 510 insertions, 553 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 52ccfda74..7b4d43793 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -105,14 +105,14 @@ export namespace DragManager {
constructor(aborted: boolean, dragData: { [id: string]: any }) {
this.aborted = aborted;
this.docDragData = dragData instanceof DocumentDragData ? dragData : undefined;
- this.annoDragData = dragData instanceof PdfAnnoDragData ? dragData : undefined;
+ this.annoDragData = dragData instanceof AnchorAnnoDragData ? dragData : undefined;
this.linkDragData = dragData instanceof LinkDragData ? dragData : undefined;
this.columnDragData = dragData instanceof ColumnDragData ? dragData : undefined;
}
linkDocument?: Doc;
aborted: boolean;
docDragData?: DocumentDragData;
- annoDragData?: PdfAnnoDragData;
+ annoDragData?: AnchorAnnoDragData;
linkDragData?: LinkDragData;
columnDragData?: ColumnDragData;
}
@@ -152,19 +152,21 @@ export namespace DragManager {
}
colKey: SchemaHeaderField;
}
- // used by PDFs to conditionally (if the drop completes) create a text annotation when dragging from the PDF toolbar when a text region has been selected.
+ // used by PDFs,Text,Image,Video,Web to conditionally (if the drop completes) create a text annotation when dragging the annotate button from the AnchorMenu when a text/region selection has been made.
// this is pretty clunky and should be rethought out using linkDrag or DocumentDrag
- export class PdfAnnoDragData {
- constructor(dragDoc: Doc, annotationDoc: Doc, dropDoc: Doc) {
+ export class AnchorAnnoDragData {
+ constructor(dragDoc: Doc, annotationDocCreator: () => Doc, dropDocCreator: () => Doc) {
this.dragDocument = dragDoc;
- this.dropDocument = dropDoc;
- this.annotationDocument = annotationDoc;
+ this.dropDocCreator = dropDocCreator;
+ this.annotationDocCreator = annotationDocCreator;
this.offset = [0, 0];
}
targetContext: Doc | undefined;
dragDocument: Doc;
- annotationDocument: Doc;
- dropDocument: Doc;
+ annotationDocCreator: () => Doc;
+ dropDocCreator: () => Doc;
+ dropDocument?: Doc;
+ annotationDocument?: Doc;
offset: number[];
dropAction: dropActionType;
userDropAction: dropActionType;
@@ -250,7 +252,7 @@ export namespace DragManager {
}
// drag&drop the pdf annotation anchor which will create a text note on drop via a dropCompleted() DragOption
- export function StartPdfAnnoDrag(eles: HTMLElement[], dragData: PdfAnnoDragData, downX: number, downY: number, options?: DragOptions) {
+ export function StartAnchorAnnoDrag(eles: HTMLElement[], dragData: AnchorAnnoDragData, downX: number, downY: number, options?: DragOptions) {
StartDrag(eles, dragData, downX, downY, options);
}
@@ -353,7 +355,7 @@ export namespace DragManager {
const xs: number[] = [];
const ys: number[] = [];
- docsBeingDragged = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof PdfAnnoDragData ? [dragData.dragDocument] : [];
+ docsBeingDragged = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnchorAnnoDragData ? [dragData.dragDocument] : [];
const elesCont = {
left: Number.MAX_SAFE_INTEGER,
top: Number.MAX_SAFE_INTEGER,
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 9cad3b9f6..6dc927877 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -22,7 +22,7 @@ import { DocumentDecorations } from "./DocumentDecorations";
import { InkStrokeProperties } from "./InkStrokeProperties";
import { MainView } from "./MainView";
import { DocumentLinksButton } from "./nodes/DocumentLinksButton";
-import { PDFMenu } from "./pdf/PDFMenu";
+import { AnchorMenu } from "./pdf/AnchorMenu";
import { SnappingManager } from "../util/SnappingManager";
import { SearchBox } from "./search/SearchBox";
import { random } from "lodash";
@@ -263,7 +263,7 @@ export class KeyManager {
}
break;
case "c":
- if (!PDFMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) {
+ if (!AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) {
const bds = DocumentDecorations.Instance.Bounds;
const pt = SelectionManager.Views()[0].props.ScreenToLocalTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
const text = `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views().map(dv => dv.Document[Id]).join(":");
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6c0b85f4b..a0161b81e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -56,7 +56,7 @@ import { RadialMenu } from './nodes/RadialMenu';
import { TaskCompletionBox } from './nodes/TaskCompletedBox';
import { WebBox } from './nodes/WebBox';
import { OverlayView } from './OverlayView';
-import { PDFMenu } from './pdf/PDFMenu';
+import { AnchorMenu } from './pdf/AnchorMenu';
import { PreviewCursor } from './PreviewCursor';
import { PropertiesView } from './PropertiesView';
import { SearchBox } from './search/SearchBox';
@@ -617,7 +617,7 @@ export class MainView extends React.Component {
<TaskCompletionBox />
<ContextMenu />
<RadialMenu />
- <PDFMenu />
+ <AnchorMenu />
<MarqueeOptionsMenu />
<OverlayView />
<TimelineMenu />
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..0ab2d1ecf
--- /dev/null
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -0,0 +1,215 @@
+import { action, observable, runInAction } 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 { AnchorMenu } from "./pdf/AnchorMenu";
+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;
+ down: 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;
+
+ constructor(props: any) {
+ super(props);
+ runInAction(() => {
+ AnchorMenu.Instance.Status = "marquee";
+ AnchorMenu.Instance.fadeOut(true);
+ // clear out old marquees and initialize menu for new selection
+ this.props.savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+ this.props.savedAnnotations.clear();
+ });
+ }
+
+ @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.down[0] - boundingRect.left) * (this.props.mainCont.offsetWidth / boundingRect.width);
+ this._startY = this._top = (this.props.down[1] - 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();
+ }
+
+ onSelectEnd = (e: PointerEvent) => {
+ if (!e.ctrlKey) {
+ AnchorMenu.Instance.Marquee = { left: this._left, top: this._top, width: this._width, height: this._height };
+ }
+
+ AnchorMenu.Instance.Highlight = this.highlight;
+ /**
+ * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.
+ * It also initiates a Drag/Drop interaction to place the text annotation.
+ */
+ AnchorMenu.Instance.StartDrag = action(async (e: PointerEvent, ele: HTMLElement) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const targetCreator = () => {
+ const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.rootDoc.title, 0, 0, 100, 100);
+ FormattedTextBox.SelectOnLoad = target[Id];
+ return target;
+ }
+ const anchorCreator = () => {
+ const annoDoc = this.highlight("rgba(173, 216, 230, 0.75)"); // hyperlink color
+ annoDoc.isLinkButton = true; // prevents link button from showing up --- maybe not a good thing?
+ this.props.addDocument(annoDoc);
+ return annoDoc;
+ }
+ DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.rootDoc, anchorCreator, targetCreator), e.pageX, e.pageY, {
+ dragComplete: e => {
+ if (!e.aborted && e.annoDragData && e.annoDragData.annotationDocument && e.annoDragData.dropDocument && !e.linkDocument) {
+ e.linkDocument = DocUtils.MakeLink({ doc: e.annoDragData.annotationDocument }, { doc: e.annoDragData.dropDocument }, "Annotation");
+ e.annoDragData.annotationDocument.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
+ 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);
+ }
+
+ AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
+
+ if (AnchorMenu.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 AnchorMenu's highlight color)
+ }
+ } else {
+ runInAction(() => this._width = this._height = 0);
+ }
+ 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/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 093310755..66afad0ac 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -12,6 +12,7 @@ import "./Timeline.scss";
import { TimelineOverview } from "./TimelineOverview";
import { Track } from "./Track";
import clamp from "../../util/clamp";
+import { DocumentType } from "../../documents/DocumentTypes";
/**
* Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are
@@ -75,7 +76,7 @@ export class Timeline extends React.Component<FieldViewProps> {
*/
@computed
private get children(): Doc[] {
- const annotatedDoc = ["image", "video", "pdf"].includes(StrCast(this.props.Document.type));
+ const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF].includes(StrCast(this.props.Document.type) as any);
if (annotatedDoc) {
return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + "-annotations"]);
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d7b9d9745..26cb4d156 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -224,7 +224,9 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
}
else if (de.complete.annoDragData && (!this.props.isAnnotationOverlay || de.complete.annoDragData.dragDocument === this.props.Document)) {
e.stopPropagation();
- return this.addDocument(de.complete.annoDragData.dropDocument);
+ de.complete.annoDragData.annotationDocument = de.complete.annoDragData.annotationDocCreator();
+ de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator();
+ return de.complete.annoDragData.dropDocument && this.addDocument(de.complete.annoDragData.dropDocument);
}
return false;
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f934fcd92..2bc716928 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -43,7 +43,6 @@ import { CollectionViewType } from "../CollectionView";
import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
import "./CollectionFreeFormView.scss";
-import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
@@ -267,8 +266,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@undoBatch
@action
- internalPdfAnnoDrop(e: Event, annoDragData: DragManager.PdfAnnoDragData, xp: number, yp: number) {
- const dragDoc = annoDragData.dropDocument;
+ internalPdfAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData, xp: number, yp: number) {
+ const dragDoc = annoDragData.dropDocument!;
const dropPos = [NumCast(dragDoc.x), NumCast(dragDoc.y)];
dragDoc.x = xp - annoDragData.offset[0] + (NumCast(dragDoc.x) - dropPos[0]);
dragDoc.y = yp - annoDragData.offset[1] + (NumCast(dragDoc.y) - dropPos[1]);
@@ -299,7 +298,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
const [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
if (this.isAnnotationOverlay !== true && de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp);
- if (de.complete.annoDragData?.dropDocument && super.onInternalDrop(e, de)) return this.internalPdfAnnoDrop(e, de.complete.annoDragData, xp, yp);
+ if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalPdfAnnoDrop(e, de.complete.annoDragData, xp, yp);
if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, xp, yp);
return false;
}
@@ -813,7 +812,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
else if (this.props.active(true)) {
e.stopPropagation();
if (!e.ctrlKey && MarqueeView.DragMarquee) this.setPan(this.panX() + e.deltaX, this.panY() + e.deltaY, "None", true);
- else this.zoom(e.clientX, e.clientY, e.deltaY);
+ else if (!this.props.isAnnotationOverlay) this.zoom(e.clientX, e.clientY, e.deltaY);
}
this.props.Document.targetScale = NumCast(this.props.Document[this.scaleFieldKey]);
}
@@ -945,17 +944,18 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const newAfterFocus = (didFocus: boolean) => {
afterFocus && setTimeout(() => {
// @ts-ignore
- if (afterFocus?.(didFocus || (newPanX !== savedState.px || newPanY !== savedState.py))) {
+ if (afterFocus?.(!dontCenter && (didFocus || (newPanX !== savedState.px || newPanY !== savedState.py)))) {
this.Document._panX = savedState.px;
this.Document._panY = savedState.py;
this.Document[this.scaleFieldKey] = savedState.s;
this.Document._viewTransition = savedState.pt;
}
+ doc.hidden && Doc.UnHighlightDoc(doc);
}, newPanX !== savedState.px || newPanY !== savedState.py ? 500 : 0);
return false;
};
this.props.focus(this.props.Document, undefined, undefined, newAfterFocus, undefined, newDidFocus);
- Doc.linkFollowHighlight(doc);
+ !doc.hidden && Doc.linkFollowHighlight(doc);
}
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index d20d1abfc..258e839d0 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -217,6 +217,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// bcz: do we need this? it kills the context menu on the main collection if !altKey
// e.stopPropagation();
}
+ else PreviewCursor.Visible = false;
}
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 721418ca8..546eca427 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -553,10 +553,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
"Linking to document tabs not yet supported. Drop link on document content.");
return;
}
+ if (de.complete.annoDragData) de.complete.annoDragData.annotationDocument = de.complete.annoDragData.annotationDocCreator();
const linkSource = de.complete.annoDragData ? de.complete.annoDragData.annotationDocument : de.complete.linkDragData ? de.complete.linkDragData.linkSourceDocument : undefined;
if (linkSource && linkSource !== this.props.Document) {
e.stopPropagation();
- de.complete.linkDocument = DocUtils.MakeLink({ doc: linkSource }, { doc: this._componentView?.getAnchor() || this.rootDoc }, "link", undefined, undefined, undefined, [de.x, de.y]);
+ const dropDoc = this._componentView?.getAnchor() || this.rootDoc;
+ if (de.complete.annoDragData) de.complete.annoDragData.dropDocument = dropDoc;
+ de.complete.linkDocument = DocUtils.MakeLink({ doc: linkSource }, { doc: dropDoc }, "link", undefined, undefined, undefined, [de.x, de.y]);
}
}
@@ -892,10 +895,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
@computed get panelWidth() { return this.nativeWidth ? this.nativeWidth * this.nativeScaling : this.props.PanelWidth(); }
@computed get panelHeight() {
if (this.nativeHeight) {
- if (this.props.Document._fitWidth) {
- return Math.min(this.props.PanelHeight(), NumCast(this.props.Document.scrollHeight, this.props.PanelHeight()));
- }
- return Math.min(this.props.PanelHeight(), this.nativeHeight * this.nativeScaling);
+ return Math.min(this.props.PanelHeight(),
+ this.props.Document._fitWidth ?
+ Math.max(NumCast(this.props.Document._height), NumCast((this.props.Document.scrollHeight as number) * this.props.PanelWidth() / this.nativeWidth, this.props.PanelHeight())) :
+ this.nativeHeight * this.nativeScaling
+ );
}
return this.props.PanelHeight();
}
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index c1b95b308..41055e2db 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -5,6 +5,16 @@
position: relative;
transform-origin: top left;
+
+ .imageBox-annotationLayer {
+ position: absolute;
+ transform-origin: left top;
+ top: 0;
+ width: 100%;
+ pointer-events: none;
+ mix-blend-mode: multiply; // bcz: makes text fuzzy!
+ }
+
.imageBox-fader {
pointer-events: inherit;
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index b0e7f4ce5..47fa25951 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -27,6 +27,10 @@ import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
import { StyleProp } from '../StyleProvider';
+import { AnchorMenu } from '../pdf/AnchorMenu';
+import { Dictionary } from 'typescript-collections';
+import { MarqueeAnnotator } from '../MarqueeAnnotator';
+import { Annotation } from '../pdf/Annotation';
const path = require('path');
const { Howl } = require('howler');
@@ -63,7 +67,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
private _dropDisposer?: DragManager.DragDropDisposer;
- private _pathDisposer?: IReactionDisposer;
+ private _disposers: { [name: string]: IReactionDisposer } = {};
@observable private _audioState = 0;
@observable static _showControls: boolean;
@observable uploadIcon = uploadIcons.idle;
@@ -74,7 +78,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
}
componentDidMount() {
- this._pathDisposer = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
+ this._disposers.selection = reaction(() => this.props.isSelected(),
+ selected => {
+ if (!selected) {
+ this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+ this._savedAnnotations.clear();
+ AnchorMenu.Instance.fadeOut(true);
+ }
+ },
+ { fireImmediately: true });
+ this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
action(({ nativeSize, width }) => {
if (!this.layoutDoc._height) {
this.layoutDoc._height = width * nativeSize.nativeHeight / nativeSize.nativeWidth;
@@ -84,7 +97,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
}
componentWillUnmount() {
- this._pathDisposer?.();
+ Object.values(this._disposers).forEach(disposer => disposer?.());
}
@undoBatch
@@ -356,7 +369,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
}
- return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget}>
+ return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
<div className="imageBox-fader" >
<img key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
src={srcpath}
@@ -402,11 +415,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.ycenter);
contentFunc = () => [this.content];
+ private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+ private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+ @observable _marqueeing: number[] | undefined;
+ @observable _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
+ @computed get annotationLayer() {
+ return <div className="imageBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer} />;
+ }
+ @action
+ marqueeDown = (e: React.PointerEvent) => {
+ if (!e.altKey && e.button === 0 && this.active(true)) this._marqueeing = [e.clientX, e.clientY];
+ }
+ @action
+ finishMarquee = () => {
+ this._marqueeing = undefined;
+ this.props.select(true);
+ }
+
render() {
TraceMobx();
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
- return (<div className={`imageBox`} onContextMenu={this.specificContextMenu}
+ return (<div className={`imageBox`} onContextMenu={this.specificContextMenu} ref={this._mainCont}
style={{
width: this.props.PanelWidth() ? undefined : `100%`,
height: this.props.PanelWidth() ? undefined : `100%`,
@@ -437,6 +467,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
whenActiveChanged={this.whenActiveChanged}>
{this.contentFunc}
</CollectionFreeFormView>
+ {this.annotationLayer}
+ {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
+ <MarqueeAnnotator rootDoc={this.rootDoc} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocument} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />}
</div >);
}
}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index c2c159e4b..55a9818ad 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -25,6 +25,9 @@ import { LinkDocPreview } from "./LinkDocPreview";
import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment";
import { Transform } from "../../util/Transform";
import { StyleProp } from "../StyleProvider";
+import { Dictionary } from "typescript-collections";
+import { MarqueeAnnotator } from "../MarqueeAnnotator";
+import { AnchorMenu } from "../pdf/AnchorMenu";
const path = require('path');
export const timeSchema = createSchema({
@@ -36,14 +39,16 @@ const VideoDocument = makeInterface(documentSchema, timeSchema);
@observer
export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) {
static _youtubeIframeCounter: number = 0;
- // private _reactionDisposer?: IReactionDisposer;
- // private _youtubeReactionDisposer?: IReactionDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _youtubePlayer: YT.Player | undefined = undefined;
private _videoRef: HTMLVideoElement | null = null;
private _youtubeIframeId: number = -1;
private _youtubeContentCreated = false;
private _isResetClick = 0;
+ private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+ private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+ @observable _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
+ @observable _marqueeing: number[] | undefined;
@observable _forceCreateYouTubeIFrame = false;
@observable _playTimer?: NodeJS.Timeout = undefined;
@observable _fullScreen = false;
@@ -185,6 +190,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
}
componentDidMount() {
+ this._disposers.selection = reaction(() => this.props.isSelected(),
+ selected => {
+ if (!selected) {
+ this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+ this._savedAnnotations.clear();
+ AnchorMenu.Instance.fadeOut(true);
+ }
+ },
+ { fireImmediately: true });
this._disposers.videoStart = reaction(
() => this.Document._videoStart,
(videoStart) => {
@@ -225,9 +239,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
componentWillUnmount() {
this.Pause();
- this._disposers.reactionDisposer?.();
- this._disposers.youtubeReactionDisposer?.();
- this._disposers.videoStart?.();
+ Object.values(this._disposers).forEach(disposer => disposer?.());
}
@action
@@ -417,17 +429,29 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
screenToLocalTransform = () => this.props.ScreenToLocalTransform();
contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
+
+ @computed get annotationLayer() {
+ return <div className="imageBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer} />;
+ }
+
+ marqueeDown = action((e: React.PointerEvent) => {
+ if (!e.altKey && e.button === 0 && this.active(true)) this._marqueeing = [e.clientX, e.clientY];
+ })
+
+ finishMarquee = action(() => {
+ this._marqueeing = undefined;
+ this.props.select(true);
+ })
+
render() {
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
- return (<div className="videoBox" onContextMenu={this.specificContextMenu}
+ return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
style={{
- width: "100%",
- height: "100%",
pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
borderRadius
}} >
- <div className="videoBox-viewer" >
+ <div className="videoBox-viewer" onPointerDown={this.marqueeDown}>
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
forceScaling={true}
fieldKey={this.annotationKey}
@@ -446,6 +470,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
</CollectionFreeFormView>
</div>
{this.uIButtons}
+ {this.annotationLayer}
+ {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
+ <MarqueeAnnotator rootDoc={this.rootDoc} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocumentWithTimestamp} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />}
</div >);
}
}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 4c4f3f15a..c9c4ed159 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 { 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";
@@ -24,15 +25,11 @@ import { ContextMenuProps } from "../ContextMenuItem";
import { ViewBoxAnnotatableComponent } from "../DocComponent";
import { DocumentDecorations } from "../DocumentDecorations";
import { Annotation } from "../pdf/Annotation";
-import { PDFMenu } from "../pdf/PDFMenu";
-import { PdfViewerMarquee } from "../pdf/PDFViewer";
+import { AnchorMenu } from "../pdf/AnchorMenu";
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,32 +37,22 @@ 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;
- @observable private _marqueeing: boolean = false;
+ private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
+ private _disposers: { [name: string]: IReactionDisposer } = {};
+ private _longPressSecondsHack?: NodeJS.Timeout;
+ private _outerRef = React.createRef<HTMLDivElement>();
+ private _iframeIndicatorRef = React.createRef<HTMLDivElement>();
+ private _iframeDragRef = React.createRef<HTMLDivElement>();
+ @observable private _marqueeing: number[] | undefined;
@observable private _url: string = "hello";
@observable private _pressX: number = 0;
@observable private _pressY: number = 0;
@observable private _iframe: HTMLIFrameElement | null = null;
@observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
- private _selectionReactionDisposer?: IReactionDisposer;
- private _scrollReactionDisposer?: IReactionDisposer;
- private _scrollTopReactionDisposer?: IReactionDisposer;
- private _moveReactionDisposer?: IReactionDisposer;
- private _longPressSecondsHack?: NodeJS.Timeout;
- private _outerRef = React.createRef<HTMLDivElement>();
- private _iframeIndicatorRef = React.createRef<HTMLDivElement>();
- private _iframeDragRef = React.createRef<HTMLDivElement>();
- private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
+
get scrollHeight() { return this.webpage?.scrollHeight || 1000; }
get _collapsed() { return StrCast(this.layoutDoc._chromeStatus) !== "enabled"; }
set _collapsed(value) { this.layoutDoc._chromeStatus = !value ? "enabled" : "disabled"; }
@@ -99,8 +86,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
this.webpage.scrollLeft = NumCast(this.layoutDoc._scrollLeft);
}
}
- this._scrollReactionDisposer?.();
- this._scrollReactionDisposer = reaction(() => ({ scrollY: this.layoutDoc._scrollY, scrollX: this.layoutDoc._scrollX }),
+ this._disposers.scrollReaction?.();
+ this._disposers.scrollReaction = reaction(() => ({ scrollY: this.layoutDoc._scrollY, scrollX: this.layoutDoc._scrollX }),
({ scrollY, scrollX }) => {
const delay = this._outerRef.current ? 0 : 250; // wait for mainCont and try again to scroll
const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/);
@@ -118,7 +105,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
},
{ fireImmediately: true }
);
- this._scrollTopReactionDisposer = reaction(() => this.layoutDoc._scrollTop,
+ this._disposers.scrollTop = reaction(() => this.layoutDoc._scrollTop,
scrollTop => {
const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/);
const duration = durationStr ? Number(durationStr[1]) : 1000;
@@ -166,15 +153,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
const urlField = Cast(this.dataDoc[this.props.fieldKey], WebField);
runInAction(() => this._url = urlField?.url.toString() || "");
- this._moveReactionDisposer = reaction(() => this.layoutDoc.x || this.layoutDoc.y,
+ this._disposers.scrollMove = reaction(() => this.layoutDoc.x || this.layoutDoc.y,
() => this.updateScroll(this.layoutDoc._scrollLeft, this.layoutDoc._scrollTop));
- this._selectionReactionDisposer = reaction(() => this.props.isSelected(),
+ this._disposers.selection = reaction(() => this.props.isSelected(),
selected => {
if (!selected) {
this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
- this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, []));
- PDFMenu.Instance.fadeOut(true);
+ this._savedAnnotations.clear();
+ AnchorMenu.Instance.fadeOut(true);
}
},
{ fireImmediately: true });
@@ -202,18 +189,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
componentWillUnmount() {
- this._moveReactionDisposer?.();
- this._selectionReactionDisposer?.();
- this._scrollTopReactionDisposer?.();
- this._scrollReactionDisposer?.();
+ Object.values(this._disposers).forEach(disposer => disposer?.());
document.removeEventListener("pointerup", this.onLongPressUp);
document.removeEventListener("pointermove", this.onLongPressMove);
this._iframe?.removeEventListener('wheel', this.iframeWheel);
}
- onUrlDragover = (e: React.DragEvent) => {
- e.preventDefault();
- }
+ onUrlDragover = (e: React.DragEvent) => { e.preventDefault(); }
@undoBatch
@action
@@ -256,6 +238,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 +266,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 +281,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 +404,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 +421,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
return view;
}
+
@computed
get content() {
const view = this.urlContent;
@@ -476,26 +449,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,145 +460,20 @@ 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;
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()));
- this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, []));
- 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;
- }
- 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();
+ this._marqueeing = [e.clientX, e.clientY];
}
}
@action
- onSelectEnd = (e: PointerEvent): void => {
- clearStyleSheetRules(WebBox._annotationStyle);
- 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);
+ finishMarquee = () => {
+ this._marqueeing = undefined;
+ this.props.select(true);
}
- 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 +486,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 +529,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} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocument} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />}
</div >
{this.props.isSelected() ? this.editToggleBtn() : null}
</div>);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c129d0204..f9982f747 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -64,6 +64,8 @@ import { SnappingManager } from '../../../util/SnappingManager';
import { LinkDocPreview } from '../LinkDocPreview';
import { SubCollectionViewProps } from '../../collections/CollectionSubView';
import { StyleProp } from '../../StyleProvider';
+import { AnchorMenu } from '../../pdf/AnchorMenu';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
export interface FormattedTextBoxProps {
makeLink?: () => Opt<Doc>; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text
@@ -252,6 +254,43 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
let unchanged = true;
const effectiveAcl = GetEffectiveAcl(this.dataDoc);
+ if (!this._editorView.state.selection.empty) {
+ runInAction(() => {
+ AnchorMenu.Instance.Status = "marquee";
+ AnchorMenu.Instance.Highlight = action((color: string) => {
+ this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView?.state, this._editorView?.dispatch);
+ return undefined;
+ });
+ /**
+ * 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.
+ */
+ AnchorMenu.Instance.StartDrag = action(async (e: PointerEvent, ele: HTMLElement) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const targetCreator = () => {
+ const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.rootDoc.title, 0, 0, 100, 100);
+ FormattedTextBox.SelectOnLoad = target[Id];
+ return target;
+ }
+
+ DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.rootDoc, () => this.rootDoc, targetCreator), e.pageX, e.pageY, {
+ dragComplete: e => {
+ if (!e.aborted && e.annoDragData && e.annoDragData.annotationDocument && e.annoDragData.dropDocument && !e.linkDocument) {
+ e.linkDocument = DocUtils.MakeLink({ doc: e.annoDragData.annotationDocument }, { doc: e.annoDragData.dropDocument }, "hyperlink", "link to note");
+ e.annoDragData.annotationDocument.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.rootDoc;
+ }
+ e.linkDocument && e.annoDragData?.dropDocument && this.makeLinkToSelection(e.linkDocument[Id], "a link", "add:right", e.annoDragData.dropDocument[Id]);
+ 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
+ }
+ });
+ });
+ });
+ const coordsT = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
+ const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to);
+ AnchorMenu.Instance.jumpTo(Math.min(coordsT.left, coordsB.left), Math.max(coordsT.bottom, coordsB.bottom));
+ }
+
const removeSelection = (json: string | undefined) => {
return json?.indexOf("\"storedMarks\"") === -1 ? json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\"");
};
@@ -960,6 +999,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this._disposers.selected = reaction(() => this.props.isSelected(),
action((selected) => {
this._recording = false;
+ AnchorMenu.Instance.fadeOut(true);
if (RichTextMenu.Instance?.view === this._editorView && !selected) {
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 038a91aa3..8867595ff 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -255,7 +255,7 @@ export class FormattedTextBoxComment {
docTarget && DocServer.GetRefField(docTarget).then(async linkDoc => {
if (linkDoc instanceof Doc) {
(FormattedTextBoxComment.tooltipText as any).href = href;
- FormattedTextBoxComment.linkDoc = DocListCast(textBox.props.Document.links).find(link => link.anchor1 === textBox.props.Document || link.anchor2 === textBox.props.Document ? link : undefined) || linkDoc;
+ FormattedTextBoxComment.linkDoc = linkDoc;
const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.dataDoc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
if (anchor !== target && anchor && target) {
diff --git a/src/client/views/pdf/PDFMenu.scss b/src/client/views/pdf/AnchorMenu.scss
index fa43a99b2..b7afb26a5 100644
--- a/src/client/views/pdf/PDFMenu.scss
+++ b/src/client/views/pdf/AnchorMenu.scss
@@ -1,4 +1,4 @@
-.pdfMenu-addTag {
+.anchorMenu-addTag {
display: grid;
width: 200px;
padding: 5px;
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 1f8872e3d..e2bd5a73d 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -8,11 +8,11 @@ import { Doc, Opt } from "../../../fields/Doc";
import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from "../../../Utils";
import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
import { ButtonDropdown } from "../nodes/formattedText/RichTextMenu";
-import "./PDFMenu.scss";
+import "./AnchorMenu.scss";
@observer
-export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
- static Instance: PDFMenu;
+export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
+ static Instance: AnchorMenu;
private _commentCont = React.createRef<HTMLButtonElement>();
private _palette = [
@@ -39,7 +39,7 @@ export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable public _colorBtn = false;
@observable public Highlighting: boolean = false;
- @observable public Status: "pdf" | "annotation" | "" = "";
+ @observable public Status: "marquee" | "annotation" | "" = "";
public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public Highlight: (color: string) => Opt<Doc> = (color: string) => undefined;
@@ -54,8 +54,8 @@ export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
constructor(props: Readonly<{}>) {
super(props);
- PDFMenu.Instance = this;
- PDFMenu.Instance._canFade = false;
+ AnchorMenu.Instance = this;
+ AnchorMenu.Instance._canFade = false;
}
pointerDown = (e: React.PointerEvent) => {
@@ -119,7 +119,7 @@ export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
}
render() {
- const buttons = this.Status === "pdf" ?
+ const buttons = this.Status === "marquee" ?
[
this.highlighter,
@@ -144,7 +144,7 @@ export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> {
<FontAwesomeIcon icon="thumbtack" size="lg" />
</button>
</Tooltip>,
- // <div key="7" className="pdfMenu-addTag" >
+ // <div key="7" className="anchorMenu-addTag" >
// <input onChange={this.keyChanged} placeholder="Key" style={{ gridColumn: 1 }} />
// <input onChange={this.valueChanged} placeholder="Value" style={{ gridColumn: 3 }} />
// </div>,
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 5ef57f986..85dd65901 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -8,7 +8,7 @@ import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast } from "../.
import { LinkManager } from "../../util/LinkManager";
import { undoBatch } from "../../util/UndoManager";
import "./Annotation.scss";
-import { PDFMenu } from "./PDFMenu";
+import { AnchorMenu } from "./AnchorMenu";
interface IAnnotationProps {
anno: Doc;
@@ -84,7 +84,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
DocListCast(group.annotations).forEach(anno => anno.delete = true);
}
- PDFMenu.Instance.fadeOut(true);
+ AnchorMenu.Instance.fadeOut(true);
}
@undoBatch
@@ -105,14 +105,14 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
@action
onPointerDown = (e: React.PointerEvent) => {
if (e.button === 2 || e.ctrlKey) {
- PDFMenu.Instance.Status = "annotation";
- PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this);
- PDFMenu.Instance.Pinned = false;
- PDFMenu.Instance.AddTag = this.addTag.bind(this);
- PDFMenu.Instance.PinToPres = this.pinToPres;
- PDFMenu.Instance.MakePushpin = this.makePushpin;
- PDFMenu.Instance.IsPushpin = this.isPushpin;
- PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+ AnchorMenu.Instance.Status = "annotation";
+ AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+ AnchorMenu.Instance.Pinned = false;
+ AnchorMenu.Instance.AddTag = this.addTag.bind(this);
+ AnchorMenu.Instance.PinToPres = this.pinToPres;
+ AnchorMenu.Instance.MakePushpin = this.makePushpin;
+ AnchorMenu.Instance.IsPushpin = this.isPushpin;
+ AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
e.stopPropagation();
}
else if (e.button === 0) {
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..f2052d454 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -3,35 +3,30 @@ 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";
-import { PDFMenu } from "./PDFMenu";
+import { AnchorMenu } from "./AnchorMenu";
import "./PDFViewer.scss";
const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
import React = require("react");
@@ -73,11 +68,7 @@ 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 _marqueeing: number[] | undefined;
@observable private _showWaiting = true;
@observable private _showCover = false;
@observable private _zoomed = 1;
@@ -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;
@@ -148,7 +137,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
if (!selected) {
this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, []));
- PDFMenu.Instance.fadeOut(true);
+ AnchorMenu.Instance.fadeOut(true);
}
(SelectionManager.Views().length === 1) && this.setupPdfJsViewer();
},
@@ -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
@@ -460,57 +379,51 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
// if alt+left click, drag and annotate
this._downX = e.clientX;
this._downY = e.clientY;
- (e.target as any).tagName === "SPAN" && (this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" }));
if ((this.Document._viewScale || 1) !== 1) return;
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()));
- this._savedAnnotations.keys().forEach(k => this._savedAnnotations.setValue(k, []));
- 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;
+ if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) {
+ this._marqueeing = [e.clientX, e.clientY]; // if texLayer is hit, then we select text instead of using a marquee
+ } else {
+ // clear out old marquees and initialize menu for new selection
+ AnchorMenu.Instance.Status = "marquee";
+ AnchorMenu.Instance.fadeOut(true);
+ this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove()));
+ this._savedAnnotations.clear();
+ this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" });
+ document.addEventListener("pointerup", this.onSelectEnd);
}
document.addEventListener("pointermove", this.onSelectMove);
- document.addEventListener("pointerup", this.onSelectEnd);
- document.addEventListener("pointerup", this.removeStyle, true);
}
}
- removeStyle = () => {
- clearStyleSheetRules(PDFViewer._annotationStyle);
- document.removeEventListener("pointerup", this.removeStyle);
+
+ @action
+ finishMarquee = () => {
+ this._marqueeing = undefined;
+ document.removeEventListener("pointermove", this.onSelectMove);
+ this.props.select(false);
}
@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
+ onSelectEnd = (e: PointerEvent): void => {
+ clearStyleSheetRules(PDFViewer._annotationStyle);
+ this.props.select(false);
+ document.removeEventListener("pointermove", this.onSelectMove);
+ document.removeEventListener("pointerup", this.onSelectEnd);
+
+ const sel = window.getSelection();
+ if (sel?.type === "Range") {
+ const selRange = sel.getRangeAt(0);
+ this.createTextAnnotation(sel, selRange);
+ AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
}
}
@@ -522,17 +435,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));
}
}
}
@@ -547,117 +459,21 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
}
}
- @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
- }
- });
- }
- }
-
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 &&
- Math.abs(e.clientX - this._downX) < 3 &&
- Math.abs(e.clientY - this._downY) < 3 &&
+ if (this._setPreviewCursor && e.button === 0 &&
+ Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
+ Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
this._setPreviewCursor(e.clientX, e.clientY, false);
+ }
+ e.stopPropagation();
}
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} down={this._marqueeing} addDocument={this.addDocument} finishMarquee={this.finishMarquee} getPageFromScroll={this.getPageFromScroll} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} 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