aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-11-22 11:52:57 -0500
committerbobzel <zzzman@gmail.com>2023-11-22 11:52:57 -0500
commitcc75a03d89a4b553a53b55404464cd2ca93d9b48 (patch)
treed4c455549245a287656d7e03ff9e459231251829
parent52241c5a42c0fa2d92eca8110523081ce9f353af (diff)
fixed more issues with rotation. restrutured how MarqueeAnnotator works to be simpler and more correct.
-rw-r--r--src/client/views/DocumentDecorations.tsx12
-rw-r--r--src/client/views/MarqueeAnnotator.tsx284
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx4
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx14
-rw-r--r--src/client/views/nodes/ImageBox.tsx15
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx46
-rw-r--r--src/client/views/nodes/MapBox/MapBox2.tsx46
-rw-r--r--src/client/views/nodes/VideoBox.scss1
-rw-r--r--src/client/views/nodes/VideoBox.tsx23
-rw-r--r--src/client/views/nodes/WebBox.tsx167
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx4
-rw-r--r--src/client/views/pdf/Annotation.tsx30
-rw-r--r--src/client/views/pdf/PDFViewer.tsx50
13 files changed, 294 insertions, 402 deletions
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3782a8878..d4b474de9 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -69,7 +69,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
const center = {x: (this.Bounds.x+this.Bounds.r)/2, y: (this.Bounds.y+this.Bounds.b)/2};
const {x,y} = Utils.rotPt(e.clientX - center.x,
e.clientY - center.y,
- -NumCast(SelectionManager.Views().lastElement()?.screenToLocalTransform().RotateDeg));
+ NumCast(SelectionManager.Views().lastElement()?.screenToLocalTransform().Rotate));
(this._showNothing = !(this.Bounds.x !== Number.MAX_VALUE && //
(this.Bounds.x > center.x+x + this._resizeBorderWidth / 2 ||
this.Bounds.r < center.x+x - this._resizeBorderWidth / 2 ||
@@ -630,12 +630,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
@computed get rotCenter() {
const lastView = SelectionManager.Views().lastElement();
if (lastView) {
+ const invXf = lastView.props.ScreenToLocalTransform().inverse();
const seldoc = lastView.layoutDoc;
- const loccenter = Utils.rotPt(NumCast(seldoc.rotation_centerX) * NumCast(seldoc._width), NumCast(seldoc.rotation_centerY) * NumCast(seldoc._height), lastView.props.ScreenToLocalTransform().Rotate);
- return lastView.props
- .ScreenToLocalTransform()
- .inverse()
- .transformPoint(loccenter.x + NumCast(seldoc._width) / 2, loccenter.y + NumCast(seldoc._height) / 2);
+ const loccenter = Utils.rotPt(NumCast(seldoc.rotation_centerX) * NumCast(seldoc._width), NumCast(seldoc.rotation_centerY) * NumCast(seldoc._height), invXf.Rotate);
+ return invXf.transformPoint(loccenter.x + NumCast(seldoc._width) / 2, loccenter.y + NumCast(seldoc._height) / 2);
}
return this._rotCenter;
}
@@ -691,7 +689,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
const bounds = this.ClippedBounds;
const useLock = bounds.r - bounds.x > 135 && seldocview.CollectionFreeFormDocumentView;
const useRotation = !hideResizers && seldocview.rootDoc.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate?
- const rotation = SelectionManager.Views().length == 1 ? seldocview.screenToLocalTransform().RotateDeg : 0;
+ const rotation = SelectionManager.Views().length == 1 ? seldocview.screenToLocalTransform().inverse().RotateDeg : 0;
// Radius constants
const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 10d2d8568..20c7a08fa 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,4 +1,4 @@
-import { action, observable, ObservableMap, runInAction } from 'mobx';
+import { action, computed, observable, ObservableMap, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, Opt } from '../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData } from '../../fields/DocSymbols';
@@ -21,13 +21,13 @@ const _global = (window /* browser */ || global) /* node */ as any;
export interface MarqueeAnnotatorProps {
rootDoc: Doc;
down?: number[];
- iframe?: () => undefined | HTMLIFrameElement;
scrollTop: number;
scaling?: () => number;
- iframeScaling?: () => number;
+ annotationLayerScaling?: () => number;
+ annotationLayerScrollTop: number;
containerOffset?: () => number[];
mainCont: HTMLDivElement;
- docView: DocumentView;
+ docView: () => DocumentView;
savedAnnotations: () => ObservableMap<number, HTMLDivElement[]>;
selectionText: () => string;
annotationLayer: HTMLDivElement;
@@ -40,27 +40,11 @@ export interface MarqueeAnnotatorProps {
}
@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;
+ private _start: { x: number; y: number } = { x: 0, y: 0 };
@observable private _width: number = 0;
@observable private _height: number = 0;
-
- constructor(props: any) {
- super(props);
-
- AnchorMenu.Instance.OnCrop = (e: PointerEvent) => {
- if (this.props.anchorMenuCrop) {
- UndoManager.RunInBatch(() => this.props.anchorMenuCrop?.(this.highlight('', true, undefined, false), true), 'cropping');
- }
- };
- AnchorMenu.Instance.OnClick = undoable((e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation');
- AnchorMenu.Instance.OnAudio = unimplementedFunction;
- AnchorMenu.Instance.Highlight = this.highlight;
- AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true);
- AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true);
- }
+ @computed get top() { return Math.min(this._start.y, this._start.y + this._height); } // prettier-ignore
+ @computed get left() { return Math.min(this._start.x, this._start.x + this._width);} // prettier-ignore
@action
static clearAnnotations(savedAnnotations: ObservableMap<number, HTMLDivElement[]>) {
@@ -71,81 +55,15 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
savedAnnotations.clear();
}
- @action gotDownPoint() {
- if (!this._width && !this._height) {
- const downPt = this.props.down!;
- // set marquee x and y positions to the spatially transformed position
- const boundingRect = this.props.mainCont.getBoundingClientRect();
- this._startX = this._left = (downPt[0] - boundingRect.left) * (this.props.mainCont.offsetWidth / boundingRect.width);
- this._startY = this._top = (downPt[1] - boundingRect.top) * (this.props.mainCont.offsetHeight / boundingRect.height) + this.props.mainCont.scrollTop;
- }
-
- const doc = this.props.iframe?.()?.contentDocument ?? document;
- doc.removeEventListener('pointermove', this.onSelectMove);
- doc.removeEventListener('pointerup', this.onSelectEnd);
- doc.addEventListener('pointermove', this.onSelectMove);
- doc.addEventListener('pointerup', this.onSelectEnd);
-
- /**
- * 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((e: PointerEvent, ele: HTMLElement) => {
- e.preventDefault();
- e.stopPropagation();
- const sourceAnchorCreator = () => this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color
-
- const targetCreator = (annotationOn: Doc | undefined) => {
- const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
- FormattedTextBox.SelectOnLoad = target[Id];
- return target;
- };
- DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
- dragComplete: e => {
- if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
- e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
- e.annoDragData.linkSourceDoc.followLinkZoom = false;
- }
- },
- });
- });
- /**
- * 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.StartCropDrag = !this.props.anchorMenuCrop
- ? unimplementedFunction
- : action((e: PointerEvent, ele: HTMLElement) => {
- e.preventDefault();
- e.stopPropagation();
- var cropRegion: Doc | undefined;
- const sourceAnchorCreator = () => (cropRegion = this.highlight('', true, undefined, true)); // hyperlink color
- const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!;
- DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
- dragComplete: e => {
- if (!e.aborted && e.linkDocument) {
- Doc.GetProto(e.linkDocument).link_relationship = 'cropped image';
- Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView.rootDoc.title;
- Doc.GetProto(e.linkDocument).link_displayLine = false;
- }
- },
- });
- });
- }
- releaseDownPt() {
- const doc = this.props.iframe?.()?.contentDocument ?? document;
- doc.removeEventListener('pointermove', this.onSelectMove);
- doc.removeEventListener('pointerup', this.onSelectEnd);
- }
-
@undoBatch
@action
makeAnnotationDocument = (color: string, isLinkButton?: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>): Opt<Doc> => {
const savedAnnoMap = savedAnnotations?.values() && Array.from(savedAnnotations?.values()).length ? savedAnnotations : this.props.savedAnnotations();
if (savedAnnoMap.size === 0) return undefined;
const savedAnnos = Array.from(savedAnnoMap.values())[0];
+ const doc = this.props.docView().rootDoc;
+ const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1);
if (savedAnnos.length && (savedAnnos[0] as any).marqueeing) {
- const scale = this.props.scaling?.() || 1;
const anno = savedAnnos[0];
const containerOffset = this.props.containerOffset?.() || [0, 0];
const marqueeAnno = Docs.Create.FreeformDocument([], {
@@ -154,10 +72,10 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
annotationOn: this.props.rootDoc,
title: 'Annotation on ' + this.props.rootDoc.title,
});
- marqueeAnno.x = NumCast(this.props.docView.props.Document.freeform_panX_min) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1);
- marqueeAnno.y = NumCast(this.props.docView.props.Document.freeform_panY_min) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1) + NumCast(this.props.scrollTop);
- marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1);
- marqueeAnno._width = parseInt(anno.style.width || '0') / scale / NumCast(this.props.docView.props.Document._freeform_scale, 1);
+ marqueeAnno.x = NumCast(doc.freeform_panX_min) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale;
+ marqueeAnno.y = NumCast(doc.freeform_panY_min) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale;
+ marqueeAnno._height = parseInt(anno.style.height || '0') / scale;
+ marqueeAnno._width = parseInt(anno.style.width || '0') / scale;
anno.remove();
savedAnnoMap.clear();
return marqueeAnno;
@@ -215,83 +133,143 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
};
public static previewNewAnnotation = action((savedAnnotations: ObservableMap<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';
+ annotationLayer.append(div);
const savedPage = savedAnnotations.get(page);
- if (savedPage) {
- savedPage.push(div);
- savedAnnotations.set(page, savedPage);
- } else {
- savedAnnotations.set(page, [div]);
- }
+ if (savedPage) savedPage.push(div);
+ savedAnnotations.set(page, savedPage ?? [div]);
});
+ getTransformedScreenPt = (down: number[]) => {
+ const boundingRect = this.props.mainCont.getBoundingClientRect();
+ const center = { x: boundingRect.x + boundingRect.width / 2, y: boundingRect.y + boundingRect.height / 2 };
+ const downPt = Utils.rotPt(down[0] - center.x, down[1] - center.y, NumCast(this.props.docView().screenToLocalTransform().Rotate));
+ const scale = this.props.docView().props.ScreenToLocalTransform().Scale;
+ const scalex = this.props.mainCont.offsetWidth / NumCast(this.props.rootDoc.width);
+ const scaley = this.props.mainCont.offsetHeight / NumCast(this.props.rootDoc.height);
+ // set marquee x and y positions to the spatially transformed position
+ return { x: scalex * (downPt.x + NumCast(this.props.rootDoc.width) / scale / 2) * scale,
+ y: scaley * (downPt.y + NumCast(this.props.rootDoc.height) / scale / 2) * scale + this.props.annotationLayerScrollTop }; // prettier-ignore
+ };
+
+ @action
+ public onInitiateSelection(down: number[]) {
+ this._width = this._height = 0;
+ this._start = this.getTransformedScreenPt(down);
+
+ document.removeEventListener('pointermove', this.onSelectMove);
+ document.removeEventListener('pointerup', this.onSelectEnd);
+ document.addEventListener('pointermove', this.onSelectMove);
+ document.addEventListener('pointerup', this.onSelectEnd);
+
+ AnchorMenu.Instance.OnCrop = (e: PointerEvent) => {
+ if (this.props.anchorMenuCrop) {
+ UndoManager.RunInBatch(() => this.props.anchorMenuCrop?.(this.highlight('', true, undefined, false), true), 'cropping');
+ }
+ };
+ AnchorMenu.Instance.OnClick = undoable((e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation');
+ AnchorMenu.Instance.OnAudio = unimplementedFunction;
+ AnchorMenu.Instance.Highlight = this.highlight;
+ AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true);
+ AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true);
+
+ /**
+ * 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((e: PointerEvent, ele: HTMLElement) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const sourceAnchorCreator = () => this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color
+
+ const targetCreator = (annotationOn: Doc | undefined) => {
+ const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
+ FormattedTextBox.SelectOnLoad = target[Id];
+ return target;
+ };
+ DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
+ dragComplete: e => {
+ if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
+ e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
+ e.annoDragData.linkSourceDoc.followLinkZoom = false;
+ }
+ },
+ });
+ });
+ /**
+ * 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.StartCropDrag = !this.props.anchorMenuCrop
+ ? unimplementedFunction
+ : action((e: PointerEvent, ele: HTMLElement) => {
+ e.preventDefault();
+ e.stopPropagation();
+ var cropRegion: Doc | undefined;
+ const sourceAnchorCreator = () => (cropRegion = this.highlight('', true, undefined, true)); // hyperlink color
+ const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!;
+ DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
+ dragComplete: e => {
+ if (!e.aborted && e.linkDocument) {
+ Doc.GetProto(e.linkDocument).link_relationship = 'cropped image';
+ Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView().rootDoc.title;
+ Doc.GetProto(e.linkDocument).link_displayLine = false;
+ }
+ },
+ });
+ });
+ }
+ public onTerminateSelection() {
+ document.removeEventListener('pointermove', this.onSelectMove);
+ document.removeEventListener('pointerup', this.onSelectEnd);
+ }
+
@action
onSelectMove = (e: PointerEvent) => {
- // transform positions and find the width and height to set the marquee to
- const boundingRect = (this.props.iframe?.()?.contentDocument?.body || this.props.mainCont).getBoundingClientRect();
- const mainRect = this.props.mainCont.getBoundingClientRect();
- const cliX = e.clientX * (this.props.iframeScaling?.() || 1) - boundingRect.left;
- const cliY = e.clientY * (this.props.iframeScaling?.() || 1) - boundingRect.top;
- this._width = cliX * (this.props.mainCont.offsetWidth / mainRect.width) - this._startX;
- this._height = cliY * (this.props.mainCont.offsetHeight / mainRect.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);
+ const movLoc = this.getTransformedScreenPt([e.clientX, e.clientY]);
+ this._width = movLoc.x - this._start.x;
+ this._height = movLoc.y - this._start.y;
//e.stopPropagation(); // overlay documents are all 'active', yet they can be dragged. if we stop propagation, then they can be marqueed but not dragged. if we don't stop, then they will be marqueed and dragged, but the marquee will be zero width since the doc will move along with the cursor.
};
+ @action
onSelectEnd = (e: PointerEvent) => {
- const mainRect = this.props.mainCont.getBoundingClientRect();
- const cliX = e.clientX * (this.props.iframeScaling?.() || 1) + (this.props.iframe ? mainRect.left : 0);
- const cliY = e.clientY * (this.props.iframeScaling?.() || 1) + (this.props.iframe ? mainRect.top : 0);
- if (this._width > 10 || this._height > 10) {
+ e.stopPropagation();
+ const marquees = this.props.mainCont.getElementsByClassName('marqueeAnnotator-dragBox');
+ const marqueeStyle = (Array.from(marquees).lastElement() as HTMLDivElement)?.style;
+ if (!this.isEmpty && marqueeStyle) {
// 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');
- ['border', 'opacity'].forEach(prop => (copy.style[prop as any] = (marquees[0] as HTMLDivElement).style[prop as any]));
- const bounds = (marquees[0] as HTMLDivElement).getBoundingClientRect();
- const uitls = Utils.GetScreenTransform(marquees[0] as HTMLDivElement);
- const rbounds = { top: uitls.translateY, left: uitls.translateX, width: bounds.right - bounds.left, height: bounds.bottom - bounds.top };
- const otls = Utils.GetScreenTransform(this.props.annotationLayer);
- const fbounds = { top: (rbounds.top - otls.translateY) / otls.scale, left: (rbounds.left - otls.translateX) / otls.scale, width: rbounds.width / otls.scale, height: rbounds.height / otls.scale };
- copy.style.top = fbounds.top.toString() + 'px';
- copy.style.left = fbounds.left.toString() + 'px';
- copy.style.width = fbounds.width.toString() + 'px';
- copy.style.height = fbounds.height.toString() + 'px';
- 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(cliX, cliY);
-
- this.props.finishMarquee(undefined, undefined, e);
- runInAction(() => (this._width = this._height = 0));
- } else {
- runInAction(() => (this._width = this._height = 0));
- this.props.finishMarquee(cliX, cliY, e);
+ // copy the temporary marquee to allow for multiple selections (not currently available though).
+ const copy = document.createElement('div');
+ const scale = (this.props.scaling?.() || 1) * NumCast(this.props.docView().rootDoc._freeform_scale, 1);
+ ['border', 'opacity', 'top', 'left', 'width', 'height'].forEach(prop => (copy.style[prop as any] = marqueeStyle[prop as any]));
+ copy.className = 'marqueeAnnotator-annotationBox';
+ copy.style.top = parseInt(marqueeStyle.top.toString().replace('px', '')) / scale + this.props.scrollTop + 'px';
+ copy.style.left = parseInt(marqueeStyle.left.toString().replace('px', '')) / scale + 'px';
+ copy.style.width = parseInt(marqueeStyle.width.toString().replace('px', '')) / scale + 'px';
+ copy.style.height = parseInt(marqueeStyle.height.toString().replace('px', '')) / scale + 'px';
+ (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);
}
+ this.props.finishMarquee(this.isEmpty ? e.clientX : undefined, this.isEmpty ? e.clientY : undefined, e);
+ this._width = this._height = 0;
};
+ get isEmpty() {
+ return Math.abs(this._width) <= 10 && Math.abs(this._height) <= 10;
+ }
+
render() {
- return !this.props.down ? null : (
+ return (
<div
- ref={r => (r ? this.gotDownPoint() : this.releaseDownPt())}
className="marqueeAnnotator-dragBox"
style={{
- left: `${this._left}px`,
- top: `${this._top}px`,
- width: `${this._width}px`,
- height: `${this._height}px`,
+ left: `${this.left}px`,
+ top: `${this.top}px`,
+ width: `${Math.abs(this._width)}px`,
+ height: `${Math.abs(this._height)}px`,
border: `${this._width === 0 ? '' : '2px dashed black'}`,
}}
/>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1b3bbdd18..e350c35cc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -341,7 +341,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
const d = docDragData.droppedDocuments[i];
const layoutDoc = Doc.Layout(d);
- const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], -this.props.ScreenToLocalTransform().Rotate);
+ const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], this.props.ScreenToLocalTransform().Rotate);
if (this.Document._currentFrame !== undefined) {
CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
const pvals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000)); // get filled in values (uses defaults when not value is specified) for position
@@ -732,7 +732,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
PresBox.Instance?.pauseAutoPres();
this.props.DocumentView?.().clearViewTransition();
const [dxi, dyi] = this.screenToLocalXf.transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
- const { x: dx, y: dy } = Utils.rotPt(dxi, dyi, -this.props.ScreenToLocalTransform().Rotate);
+ const { x: dx, y: dy } = Utils.rotPt(dxi, dyi, this.props.ScreenToLocalTransform().Rotate);
this.setPan(NumCast(this.Document[this.panXFieldKey]) - (ctrlKey ? 0 : dx), NumCast(this.Document[this.panYFieldKey]) - (shiftKey ? 0 : dy), 0, true);
this._lastX = e.clientX;
this._lastY = e.clientY;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 465bbcf2f..421d431b3 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -131,9 +131,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static animStringFields = ['backgroundColor', 'color', 'fillColor']; // fields that are configured to be animatable using animation frames
public static animDataFields = (doc: Doc) => (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames
- @observable _animPos: number[] | undefined = undefined;
- @observable _contentView: DocumentView | undefined | null;
-
get CollectionFreeFormView() {
return this.props.CollectionFreeFormView;
}
@@ -231,7 +228,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
this.props
.ScreenToLocalTransform()
.translate(-this.props.w_X(), -this.props.w_Y())
- .rotateDeg(this.props.w_Rotation?.() || 0);
+ .rotateDeg(-(this.props.w_Rotation?.() || 0));
returnThis = () => this;
/// this indicates whether the doc view is activated because of its relationshop to a group
@@ -262,14 +259,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
{this.props.RenderCutoffProvider(this.props.Document) ? (
<div style={{ position: 'absolute', width: this.props.PanelWidth(), height: this.props.PanelHeight(), background: 'lightGreen' }} />
) : (
- <DocumentView
- ref={action((r: DocumentView | null) => (this._contentView = r))}
- {...passOnProps}
- CollectionFreeFormDocumentView={this.returnThis}
- styleProvider={this.styleProvider}
- ScreenToLocalTransform={this.screenToLocalTransform}
- isGroupActive={this.isGroupActive}
- />
+ <DocumentView {...passOnProps} CollectionFreeFormDocumentView={this.returnThis} styleProvider={this.styleProvider} ScreenToLocalTransform={this.screenToLocalTransform} isGroupActive={this.isGroupActive} />
)}
</div>
);
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index bca062190..103843046 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -70,6 +70,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
private _disposers: { [name: string]: IReactionDisposer } = {};
private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined;
private _overlayIconRef = React.createRef<HTMLDivElement>();
+ private _marqueeref = React.createRef<MarqueeAnnotator>();
@observable _curSuffix = '';
@observable _uploadIcon = uploadIcons.idle;
@@ -473,7 +474,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
- @observable _marqueeing: number[] | undefined;
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@computed get annotationLayer() {
TraceMobx();
@@ -487,7 +487,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
e,
action(e => {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
+ this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]);
return true;
}),
returnFalse,
@@ -499,7 +499,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action
finishMarquee = () => {
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
- this._marqueeing = undefined;
+ this._marqueeref.current?.onTerminateSelection();
this.props.select(false);
};
focus = (anchor: Doc, options: DocFocusOptions) => (anchor.type === DocumentType.CONFIG ? undefined : this._ffref.current?.focus(anchor, options));
@@ -557,13 +557,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{this.content}
</CollectionFreeFormView>
{this.annotationLayer}
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+ {!this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
rootDoc={this.rootDoc}
scrollTop={0}
- down={this._marqueeing}
- scaling={this.props.NativeDimScaling}
- docView={this.props.docViewPath().slice(-1)[0]}
+ annotationLayerScrollTop={0}
+ scaling={returnOne}
+ annotationLayerScaling={this.props.NativeDimScaling}
+ docView={this.props.DocumentView!}
addDocument={this.addDocument}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotations}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 37935c513..9b75ca7e3 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -7,9 +7,8 @@ import * as React from 'react';
import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
import { DocCss, Highlight } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
-import { InkTool } from '../../../../fields/InkField';
import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
import { Docs, DocUtils } from '../../../documents/Documents';
import { DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
@@ -21,7 +20,6 @@ import { undoable, UndoManager } from '../../../util/UndoManager';
import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
-import { MarqueeAnnotator } from '../../MarqueeAnnotator';
import { SidebarAnnos } from '../../SidebarAnnos';
import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
@@ -67,14 +65,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return FieldView.LayoutString(MapBox, fieldKey);
}
private _dragRef = React.createRef<HTMLDivElement>();
- private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
- private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _sidebarRef = React.createRef<SidebarAnnos>();
private _ref: React.RefObject<HTMLDivElement> = React.createRef();
private _disposers: { [key: string]: IReactionDisposer } = {};
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
- @observable private _marqueeing: number[] | undefined;
@observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@computed get allSidebarDocs() {
return DocListCast(this.dataDoc[this.SidebarKey]);
@@ -278,28 +273,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func);
- @action
- onMarqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
- setupMoveUpEvents(
- this,
- e,
- action(e => {
- MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
- return true;
- }),
- returnFalse,
- () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
- false
- );
- }
- };
- @action finishMarquee = (x?: number, y?: number) => {
- this._marqueeing = undefined;
- x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false, this.rootDoc);
- };
-
addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => this.addDocument(doc, annotationKey);
pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none');
@@ -832,23 +805,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
isAnyChildContentActive={this.isAnyChildContentActive}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
/> */}
-
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
- <MarqueeAnnotator
- rootDoc={this.rootDoc}
- anchorMenuClick={this.anchorMenuClick}
- scrollTop={0}
- down={this._marqueeing}
- scaling={returnOne}
- addDocument={this.addDocumentWrapper}
- docView={this.props.docViewPath().lastElement()}
- finishMarquee={this.finishMarquee}
- savedAnnotations={this.savedAnnotations}
- annotationLayer={this._annotationLayer.current}
- selectionText={returnEmptyString}
- mainCont={this._mainCont.current}
- />
- )}
</div>
{/* </LoadScript > */}
<div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index c42664abe..6bad7d724 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -5,9 +5,8 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
-import { InkTool } from '../../../../fields/InkField';
import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { SnappingManager } from '../../../util/SnappingManager';
@@ -15,7 +14,6 @@ import { UndoManager } from '../../../util/UndoManager';
import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
-import { MarqueeAnnotator } from '../../MarqueeAnnotator';
import { AnchorMenu } from '../../pdf/AnchorMenu';
import { Annotation } from '../../pdf/Annotation';
import { SidebarAnnos } from '../../SidebarAnnos';
@@ -106,8 +104,6 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable private selectedPlace: Doc | undefined;
@observable private markerMap: { [id: string]: google.maps.Marker } = {};
@observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
- @observable private _marqueeing: number[] | undefined;
- @observable private _isAnnotating = false;
@observable private inputRef = React.createRef<HTMLInputElement>();
@observable private searchMarkers: google.maps.Marker[] = [];
@observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
@@ -119,7 +115,6 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
return DocListCast(this.dataDoc[this.annotationKey]);
}
@observable private toggleAddMarker = false;
- private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
@observable _showSidebar = false;
@computed get SidebarShown() {
@@ -481,29 +476,6 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
- @action
- onMarqueeDown = (e: React.PointerEvent) => {
- if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
- setupMoveUpEvents(
- this,
- e,
- action(e => {
- MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
- return true;
- }),
- returnFalse,
- () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
- false
- );
- }
- };
- @action finishMarquee = (x?: number, y?: number) => {
- this._marqueeing = undefined;
- this._isAnnotating = false;
- x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false, this.props.Document);
- };
-
addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
return this.addDocument(doc, annotationKey);
};
@@ -598,22 +570,6 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
{/* {this.handleMapCenter(this._map)} */}
</GoogleMap>
</div>
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
- <MarqueeAnnotator
- rootDoc={this.rootDoc}
- anchorMenuClick={this.anchorMenuClick}
- scrollTop={0}
- down={this._marqueeing}
- scaling={returnOne}
- addDocument={this.addDocumentWrapper}
- docView={this.props.docViewPath().lastElement()}
- finishMarquee={this.finishMarquee}
- savedAnnotations={this.savedAnnotations}
- annotationLayer={this._annotationLayer.current}
- selectionText={returnEmptyString}
- mainCont={this._mainCont.current}
- />
- )}
</div>
{/* </LoadScript > */}
<div className="MapBox2-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index f803715ad..f90c19050 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -100,6 +100,7 @@
padding: 0 10px 0 7px;
transition: opacity 0.3s;
z-index: 10001;
+ transform-origin: top left;
.timecode-controls {
display: flex;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 6ebf84738..8bf2f4ce5 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -64,6 +64,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
private _youtubeIframeId: number = -1;
private _youtubeContentCreated = false;
private _audioPlayer: HTMLAudioElement | null = null;
+ private _marqueeref = React.createRef<MarqueeAnnotator>();
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); // outermost div
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _playRegionTimer: any = null; // timeout for playback
@@ -71,7 +72,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@observable _stackedTimeline: any; // CollectionStackedTimeline ref
@observable static _nativeControls: boolean; // default html controls
- @observable _marqueeing: number[] | undefined; // coords for marquee selection
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@observable _screenCapture = false;
@observable _clicking = false; // used for transition between showing/hiding timeline
@@ -867,7 +867,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
e,
action(e => {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
+ this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]);
return true;
}),
returnFalse,
@@ -881,7 +881,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// ends marquee selection
@action
finishMarquee = () => {
- this._marqueeing = undefined;
+ this._marqueeref.current?.onTerminateSelection();
this.props.select(true);
};
@@ -912,7 +912,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
.scale(100 / this.heightPercent);
};
- marqueeFitScaling = () => ((this.props.NativeDimScaling?.() || 1) * this.heightPercent) / 100;
marqueeOffset = () => [((this.panelWidth() / 2) * (1 - this.heightPercent / 100)) / (this.heightPercent / 100), 0];
timelineDocFilter = () => [`_isTimelineLabel:true,${Utils.noRecursionHack}:x`];
@@ -937,8 +936,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div
className="videoBox-ui"
style={{
- transformOrigin: 'top left',
- transform: `rotate(${this.props.ScreenToLocalTransform().RotateDeg}deg) translate(${-(xRight - xPos) + 10}px, ${yBot - yMid - uiHeight - uiMargin}px)`,
+ transform: `rotate(${this.props.ScreenToLocalTransform().inverse().RotateDeg}deg) translate(${-(xRight - xPos) + 10}px, ${yBot - yMid - uiHeight - uiMargin}px)`,
left: xPos,
top: yMid,
height: uiHeight,
@@ -952,6 +950,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
);
};
+ thumbnails = () => StrListCast(this.dataDoc[this.fieldKey + '_thumbnails']);
// renders CollectionStackedTimeline
@computed get renderTimeline() {
return (
@@ -963,7 +962,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
fieldKey={this.annotationKey}
dictationKey={this.fieldKey + '_dictation'}
mediaPath={this.audiopath}
- thumbnails={() => StrListCast(this.dataDoc[this.fieldKey + '_thumbnails'])}
+ thumbnails={this.thumbnails}
renderDepth={this.props.renderDepth + 1}
startTag={'_timecodeToShow' /* videoStart */}
endTag={'_timecodeToHide' /* videoEnd */}
@@ -1095,13 +1094,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
</CollectionFreeFormView>
</div>
{this.annotationLayer}
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+ {!this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
+ ref={this._marqueeref}
rootDoc={this.rootDoc}
scrollTop={0}
- down={this._marqueeing}
- scaling={this.marqueeFitScaling}
- docView={this.props.docViewPath().slice(-1)[0]}
+ annotationLayerScrollTop={0}
+ scaling={returnOne}
+ annotationLayerScaling={this.props.NativeDimScaling}
+ docView={this.props.DocumentView!}
containerOffset={this.marqueeOffset}
addDocument={this.addDocWithTimecode}
finishMarquee={this.finishMarquee}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 524a3cc4a..e42fb4a03 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as WebRequest from 'web-request';
import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
@@ -51,6 +51,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
+ private _marqueeref = React.createRef<MarqueeAnnotator>();
private _disposers: { [name: string]: IReactionDisposer } = {};
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _keyInput = React.createRef<HTMLInputElement>();
@@ -66,10 +67,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@observable private _searching: boolean = false;
@observable private _showSidebar = false;
@observable private _webPageHasBeenRendered = false;
- @observable private _overlayAnnoInfo: Opt<Doc>;
@observable private _marqueeing: number[] | undefined;
- @observable private _isAnnotating = false;
- @observable private _iframeClick: HTMLIFrameElement | undefined = undefined;
+ get marqueeing() {
+ return this._marqueeing;
+ }
+ set marqueeing(val) {
+ val && this._marqueeref.current?.onInitiateSelection(val);
+ !val && this._marqueeref.current?.onTerminateSelection();
+ this._marqueeing = val;
+ }
@observable private _iframe: HTMLIFrameElement | null = null;
@observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@observable private _scrollHeight = NumCast(this.layoutDoc.scrollHeight);
@@ -245,6 +251,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
@action
createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
if (this._mainCont.current && selRange) {
+ if (this.rootDoc[this.props.fieldKey] instanceof HtmlField) this._mainCont.current.style.transform = `rotate(${NumCast(this.props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`;
const clientRects = selRange.getClientRects();
for (let i = 0; i < clientRects.length; i++) {
const rect = clientRects.item(i);
@@ -261,12 +268,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, 1);
}
}
+ this._mainCont.current.style.transform = '';
}
- //this._selectionText = selRange.cloneContents().textContent || "";
this._selectionContent = selRange?.cloneContents();
this._selectionText = this._selectionContent?.textContent || '';
// clear selection
+ this._textAnnotationCreator = undefined;
if (sel.empty) sel.empty(); // Chrome
else if (sel.removeAllRanges) sel.removeAllRanges(); // Firefox
return this._savedAnnotations;
@@ -354,26 +362,27 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
@action
webClipDown = (e: React.PointerEvent) => {
- const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
+ e.stopPropagation();
+ const sel = window.getSelection();
+ this._textAnnotationCreator = undefined;
+ if (sel?.empty) sel.empty(); // Chrome
+ else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
+ // bcz: NEED TO unrotate e.clientX and e.clientY
const word = getWordAtPoint(e.target, e.clientX, e.clientY);
this._setPreviewCursor?.(e.clientX, e.clientY, false, true, this.rootDoc);
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- e.button !== 2 && (this._marqueeing = [e.clientX, e.clientY]);
- if (word || (e.target as any)?.className?.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
- e.stopPropagation();
- setTimeout(
- action(() => (this._marqueeing = undefined)),
- 100
- ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
- } else {
- this._isAnnotating = true;
- this.props.select(false);
- e.stopPropagation();
+
+ if (!word && !(e.target as any)?.className?.includes('rangeslider') && !(e.target as any)?.onclick && !(e.target as any)?.parentNode?.onclick) {
+ if (e.button !== 2) this.marqueeing = [e.clientX, e.clientY];
e.preventDefault();
}
document.addEventListener('pointerup', this.webClipUp);
};
+ @action
webClipUp = (e: PointerEvent) => {
+ if (window.getSelection()?.isCollapsed && this._marqueeref.current?.isEmpty) {
+ this.marqueeing = undefined;
+ }
document.removeEventListener('pointerup', this.webClipUp);
this._getAnchor = AnchorMenu.Instance?.GetAnchor; // need to save AnchorMenu's getAnchor since a subsequent selection on another doc will overwrite this value
const sel = window.getSelection();
@@ -382,7 +391,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this._selectionText = sel.toString();
AnchorMenu.Instance.setSelectedText(sel.toString());
this._textAnnotationCreator = () => this.createTextAnnotation(sel, selRange);
- AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
+ (!sel.isCollapsed || this.marqueeing) && AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
// Changing which document to add the annotation to (the currently selected WebBox)
GPTPopup.Instance.setSidebarId(`${this.props.fieldKey}_${this._urlHash ? this._urlHash + '_' : ''}sidebar`);
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
@@ -390,36 +399,26 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
};
@action
iframeDown = (e: PointerEvent) => {
- const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
- const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
- const word = getWordAtPoint(e.target, e.clientX, e.clientY);
- this._setPreviewCursor?.(e.clientX, e.clientY, false, true, this.rootDoc);
+ this.props.select(false);
+ const locpt = {
+ x: (e.clientX / NumCast(this.rootDoc.nativeWidth)) * this.props.PanelWidth(),
+ y: ((e.clientY - NumCast(this.rootDoc.layout_scrollTop))/ NumCast(this.rootDoc.nativeHeight)) * this.props.PanelHeight() }; // prettier-ignore
+ const scrclick = this.props.DocumentView?.().props.ScreenToLocalTransform().inverse().transformPoint(locpt.x, locpt.y)!;
+ const scrcent = this.props
+ .DocumentView?.()
+ .props.ScreenToLocalTransform()
+ .inverse()
+ .transformPoint(NumCast(this.rootDoc.width) / 2, NumCast(this.rootDoc.height) / 2)!;
+ const theclickoff = Utils.rotPt(scrclick[0] - scrcent[0], scrclick[1] - scrcent[1], -this.props.ScreenToLocalTransform().Rotate);
+ const theclick = [theclickoff.x + scrcent[0], theclickoff.y + scrcent[1]];
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- e.button !== 2 && (this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._layout_scrollTop) * scale]);
- if (word || (e.target as any)?.className?.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
- setTimeout(
- action(() => (this._marqueeing = undefined)),
- 100
- ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
- } else {
- this._iframeClick = this._iframe ?? undefined;
- this._isAnnotating = true;
- this.props.select(false);
- e.stopPropagation();
- e.preventDefault();
- }
-
- // bcz: hack - iframe grabs all events which messes up how we handle contextMenus. So this super naively simulates the event stack to get the specific menu items and the doc view menu items.
- if (e.button === 2 || (e.button === 0 && e.altKey)) {
+ const word = getWordAtPoint(e.target, e.clientX, e.clientY);
+ if (!word && !(e.target as any)?.className?.includes('rangeslider') && !(e.target as any)?.onclick && !(e.target as any)?.parentNode?.onclick) {
+ this.marqueeing = theclick;
e.preventDefault();
- //e.stopPropagation();
- ContextMenu.Instance.closeMenu();
- ContextMenu.Instance.setIgnoreEvents(true);
}
};
isFirefox = () => 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1;
- iframeClick = () => this._iframeClick;
- iframeScaling = () => 1 / this.props.ScreenToLocalTransform().Scale;
addWebStyleSheet(document: any, styleType: string = 'text/css') {
if (document) {
@@ -723,38 +722,56 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}
};
+ /**
+ * This gets called when some other child of the webbox is selected and a pointer down occurs on the webbox.
+ * it's also called for html clippings when you click outside the bounds of the clipping
+ * @param e
+ */
@action
onMarqueeDown = (e: React.PointerEvent) => {
+ const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
+ this._textAnnotationCreator = undefined;
+ if (sel?.empty) sel.empty(); // Chrome
+ else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
+ this.marqueeing = [e.clientX, e.clientY];
if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
setupMoveUpEvents(
this,
e,
action(e => {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
return true;
}),
returnFalse,
- () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+ action(() => {
+ this.marqueeing = undefined;
+ MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+ }),
false
);
+ } else {
+ this.marqueeing = undefined;
}
};
@action finishMarquee = (x?: number, y?: number, e?: PointerEvent) => {
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
- this._marqueeing = undefined;
- this._isAnnotating = false;
- this._iframeClick = undefined;
+ this.marqueeing = undefined;
+ this._textAnnotationCreator = undefined;
const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
if (sel?.empty) sel.empty(); // Chrome
else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
+ this._setPreviewCursor?.(x ?? 0, y ?? 0, false, !this._marqueeref.current?.isEmpty, this.rootDoc);
if (x !== undefined && y !== undefined) {
- this._setPreviewCursor?.(x, y, false, false, this.rootDoc);
ContextMenu.Instance.closeMenu();
ContextMenu.Instance.setIgnoreEvents(false);
if (e?.button === 2 || e?.altKey) {
- this.specificContextMenu(undefined as any);
- this.props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
+ e?.preventDefault();
+ e?.stopPropagation();
+ setTimeout(() => {
+ // if menu comes up right away, the down event can still be active causing a menu item to be selected
+ this.specificContextMenu(undefined as any);
+ this.props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
+ });
}
}
};
@@ -797,7 +814,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
key={this._warning}
className="webBox-iframe"
ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))}
- style={{ pointerEvents: this._isAnyChildContentActive || DocumentView.Interacting ? 'none' : undefined }}
+ style={{ pointerEvents: DocumentView.Interacting ? 'none' : undefined }}
src={url}
onLoad={this.iframeLoaded}
scrolling="no" // ugh.. on windows, I get an inner scroll bar for the iframe's body even though the scrollHeight should be set to the full height of the document.
@@ -942,7 +959,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
{this.inlineTextAnnotations
.sort((a, b) => NumCast(a.y) - NumCast(b.y))
.map(anno => (
- <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+ <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
))}
</div>
);
@@ -1004,7 +1021,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
onWheel={this.onZoomWheel}
onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
onPointerDown={this.onMarqueeDown}>
- <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight) || '100%', pointerEvents }}>
+ <div className="webBox-innerContent" style={{ height: (this._webPageHasBeenRendered && this._scrollHeight > this.props.PanelHeight() && this._scrollHeight) || '100%', pointerEvents }}>
{this.content}
<div style={{ display: SnappingManager.GetCanEmbed() ? 'none' : undefined, mixBlendMode: 'multiply' }}>{this.renderTransparentAnnotations}</div>
{this.renderOpaqueAnnotations}
@@ -1049,7 +1066,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
);
}
searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
- showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func);
panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth;
panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
@@ -1067,7 +1083,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
!this._draggingSidebar && this.props.isContentActive() && !MarqueeOptionsMenu.Instance?.isShown()
? 'all' //
: 'none';
- annotationPointerEvents = () => (this.props.isContentActive() && (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
+ annotationPointerEvents = () => (this.props.isContentActive() && (SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? 'all' : 'none');
render() {
const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
@@ -1090,27 +1106,26 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
}}
onContextMenu={this.specificContextMenu}>
{this.webpage}
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
- <div style={{ transformOrigin: 'top left', transform: `scale(${1 / scale})` }}>
- <MarqueeAnnotator
- rootDoc={this.rootDoc}
- iframe={this.isFirefox() ? this.iframeClick : undefined}
- iframeScaling={this.isFirefox() ? this.iframeScaling : undefined}
- anchorMenuClick={this.anchorMenuClick}
- scrollTop={0}
- down={this._marqueeing}
- scaling={returnOne}
- addDocument={this.addDocumentWrapper}
- docView={this.props.docViewPath().lastElement()}
- finishMarquee={this.finishMarquee}
- savedAnnotations={this.savedAnnotationsCreator}
- selectionText={this.selectionText}
- annotationLayer={this._annotationLayer.current}
- mainCont={this._mainCont.current}
- />
- </div>
- )}
</div>
+ {!this._mainCont.current || !this._annotationLayer.current ? null : (
+ <div style={{ position: 'absolute', height: '100%', width: '100%', pointerEvents: this.marqueeing ? 'all' : 'none' }}>
+ <MarqueeAnnotator
+ ref={this._marqueeref}
+ rootDoc={this.rootDoc}
+ anchorMenuClick={this.anchorMenuClick}
+ scrollTop={NumCast(this.layoutDoc.layout_scrollTop)}
+ annotationLayerScrollTop={0}
+ scaling={this.props.NativeDimScaling}
+ addDocument={this.addDocumentWrapper}
+ docView={this.props.DocumentView!}
+ finishMarquee={this.finishMarquee}
+ savedAnnotations={this.savedAnnotationsCreator}
+ selectionText={this.selectionText}
+ annotationLayer={this._annotationLayer.current}
+ mainCont={this._mainCont.current}
+ />
+ </div>
+ )}
<div
className="webBox-sideResizer"
style={{
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index f3f07cc21..789509784 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1640,8 +1640,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
let dataField = Doc.LayoutFieldKey(tagDoc);
if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField = dataField + '_annotations';
- if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc.annotationOn["${dataField}"]`);
- else activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc["${dataField}"]`);
+ if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc.annotationOn?.["${dataField}"]`);
+ else activeItem.data = ComputedField.MakeFunction(`self.presentation_targetDoc?.["${dataField}"]`);
}}
checked={Cast(activeItem.presentation_indexed, 'number', null) !== undefined ? true : false}
/>
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 38e4f65b6..e1e87763c 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -12,12 +12,13 @@ import { FieldViewProps } from '../nodes/FieldView';
import { AnchorMenu } from './AnchorMenu';
import './Annotation.scss';
import { LinkManager } from '../../util/LinkManager';
+import { Rect } from 'react-measure';
interface IAnnotationProps extends FieldViewProps {
anno: Doc;
dataDoc: Doc;
fieldKey: string;
- showInfo: (anno: Opt<Doc>) => void;
+ showInfo?: (anno: Opt<Doc>) => void;
pointerEvents?: () => Opt<string>;
}
@observer
@@ -70,17 +71,23 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
};
@action
+ onContextMenu = (e: React.MouseEvent) => {
+ AnchorMenu.Instance.Status = 'annotation';
+ AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
+ AnchorMenu.Instance.Pinned = false;
+ AnchorMenu.Instance.PinToPres = this.pinToPres;
+ AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
+ AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
+ AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion);
+ AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
+ e.stopPropagation();
+ e.preventDefault();
+ };
+ @action
onPointerDown = (e: React.PointerEvent) => {
if (e.button === 2 || e.ctrlKey) {
- AnchorMenu.Instance.Status = 'annotation';
- AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
- AnchorMenu.Instance.Pinned = false;
- AnchorMenu.Instance.PinToPres = this.pinToPres;
- AnchorMenu.Instance.MakeTargetToggle = this.makeTargretToggle;
- AnchorMenu.Instance.IsTargetToggler = this.isTargetToggler;
- AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(this.annoTextRegion);
- AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
e.stopPropagation();
+ e.preventDefault();
} else if (e.button === 0) {
e.stopPropagation();
LinkFollower.FollowLink(undefined, this.annoTextRegion, false);
@@ -102,13 +109,14 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
ref={this._mainCont}
onPointerEnter={action(() => {
Doc.BrushDoc(this.props.anno);
- this.props.showInfo(this.props.anno);
+ this.props.showInfo?.(this.props.anno);
})}
onPointerLeave={action(() => {
Doc.UnBrushDoc(this.props.anno);
- this.props.showInfo(undefined);
+ this.props.showInfo?.(undefined);
})}
onPointerDown={this.onPointerDown}
+ onContextMenu={this.onContextMenu}
style={{
left: NumCast(this.props.document.x),
top: NumCast(this.props.document.y),
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index a66e519c9..ca9bf7bd2 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -2,7 +2,7 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
import { observer } from 'mobx-react';
import * as Pdfjs from 'pdfjs-dist';
import 'pdfjs-dist/web/pdf_viewer.css';
-import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Height } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
@@ -11,7 +11,6 @@ import { TraceMobx } from '../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, returnAll, returnFalse, returnNone, returnZero, smoothScroll, Utils } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
import { SelectionManager } from '../../util/SelectionManager';
-import { SharingManager } from '../../util/SharingManager';
import { SnappingManager } from '../../util/SnappingManager';
import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
@@ -56,16 +55,15 @@ export class PDFViewer extends React.Component<IViewerProps> {
static _annotationStyle: any = addStyleSheet();
@observable private _pageSizes: { width: number; height: number }[] = [];
@observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
- @observable private _marqueeing: number[] | undefined;
@observable private _textSelecting = true;
@observable private _showWaiting = true;
- @observable private _overlayAnnoInfo: Opt<Doc>;
@observable private Index: number = -1;
private _pdfViewer: any;
private _styleRule: any; // stylesheet rule for making hyperlinks clickable
private _retries = 0; // number of times tried to create the PDF viewer
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);
+ private _marqueeref = React.createRef<MarqueeAnnotator>();
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
private _disposers: { [name: string]: IReactionDisposer } = {};
private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
@@ -368,17 +366,14 @@ export class PDFViewer extends React.Component<IViewerProps> {
if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
this.props.select(false);
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeing = [e.clientX, e.clientY];
+ this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]);
this.isAnnotating = true;
const target = e.target as any;
if (e.target && (target.className.includes('endOfContent') || (target.parentElement.className !== 'textLayer' && target.parentElement.parentElement?.className !== 'textLayer'))) {
this._textSelecting = false;
} else {
// if textLayer is hit, then we select text instead of using a marquee so clear out the marquee.
- setTimeout(
- action(() => (this._marqueeing = undefined)),
- 100
- ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
+ setTimeout(() => this._marqueeref.current?.onTerminateSelection(), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, 'htmlAnnotation', { 'pointer-events': 'none' });
document.addEventListener('pointerup', this.onSelectEnd);
@@ -390,7 +385,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
finishMarquee = (x?: number, y?: number) => {
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
this.isAnnotating = false;
- this._marqueeing = undefined;
+ this._marqueeref.current?.onTerminateSelection();
this._textSelecting = true;
};
@@ -420,23 +415,26 @@ export class PDFViewer extends React.Component<IViewerProps> {
@action
createTextAnnotation = (sel: Selection, selRange: Range) => {
if (this._mainCont.current) {
+ this._mainCont.current.style.transform = `rotate(${NumCast(this.props.DocumentView!().screenToLocalTransform().RotateDeg)}deg)`;
const boundingRect = this._mainCont.current.getBoundingClientRect();
const clientRects = selRange.getClientRects();
for (let i = 0; i < clientRects.length; i++) {
const rect = clientRects.item(i);
- if (rect?.width && rect.width < this._mainCont.current.clientWidth / this.props.ScreenToLocalTransform().Scale) {
+ if (rect && rect?.width && rect.width < this._mainCont.current.clientWidth / this.props.ScreenToLocalTransform().Scale) {
const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
+ const scaleY = this._mainCont.current.offsetHeight / boundingRect.height;
const pdfScale = NumCast(this.props.layoutDoc._freeform_scale, 1);
const annoBox = document.createElement('div');
annoBox.className = 'marqueeAnnotator-annotationBox';
// transforms the positions from screen onto the pdf div
- annoBox.style.top = (((rect.top - boundingRect.top) * scaleX) / pdfScale + this._mainCont.current.scrollTop).toString();
annoBox.style.left = (((rect.left - boundingRect.left) * scaleX) / pdfScale).toString();
- annoBox.style.width = ((rect.width * this._mainCont.current.offsetWidth) / boundingRect.width / pdfScale).toString();
- annoBox.style.height = ((rect.height * this._mainCont.current.offsetHeight) / boundingRect.height / pdfScale).toString();
+ annoBox.style.top = (((rect.top - boundingRect.top) * scaleY) / pdfScale + this._mainCont.current.scrollTop).toString();
+ annoBox.style.width = ((rect.width * scaleX) / pdfScale).toString();
+ annoBox.style.height = ((rect.height * scaleY) / pdfScale).toString();
this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, this.getPageFromScroll(rect.top));
}
}
+ this._mainCont.current!.style.transform = '';
}
this._selectionContent = selRange.cloneContents();
this._selectionText = this._selectionContent?.textContent || '';
@@ -482,24 +480,13 @@ export class PDFViewer extends React.Component<IViewerProps> {
return (
<div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._freeform_scale, 1)})` }} ref={this._annotationLayer}>
{inlineAnnos.map(anno => (
- <Annotation {...this.props} fieldKey={this.props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+ <Annotation {...this.props} fieldKey={this.props.fieldKey + '_annotations'} pointerEvents={this.pointerEvents} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
))}
</div>
);
}
- @computed get overlayInfo() {
- return !this._overlayAnnoInfo ? null : (
- <div className="pdfViewerDash-overlayAnno" style={{ top: NumCast(this._overlayAnnoInfo.y), left: NumCast(this._overlayAnnoInfo.x) }}>
- <div className="pdfViewerDash-overlayAnno" style={{ right: -50, background: SharingManager.Instance.users.find(users => users.user.email === this._overlayAnnoInfo!.author)?.userColor }}>
- {this._overlayAnnoInfo.author + ' ' + Field.toString(this._overlayAnnoInfo.author_date as Field)}
- </div>
- </div>
- );
- }
-
getScrollHeight = () => this._scrollHeight;
- showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
scrollXf = () => (this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._layout_scrollTop)) : this.props.ScreenToLocalTransform());
overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._freeform_scale, 1));
panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1);
@@ -567,6 +554,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
return <div className={'pdfViewerDash-text' + (this.props.pointerEvents?.() !== 'none' && this._textSelecting && this.props.isContentActive() ? '-selected' : '')} ref={this._viewer} />;
}
savedAnnotations = () => this._savedAnnotations;
+ addDocumentWrapper = (doc: Doc | Doc[]) => this.props.addDocument!(doc);
render() {
TraceMobx();
return (
@@ -585,17 +573,17 @@ export class PDFViewer extends React.Component<IViewerProps> {
{this.pdfViewerDiv}
{this.annotationLayer}
{this.overlayLayer}
- {this.overlayInfo}
{this._showWaiting ? <img className="pdfViewerDash-waiting" src={'/assets/loading.gif'} /> : null}
- {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+ {!this._mainCont.current || !this._annotationLayer.current ? null : (
<MarqueeAnnotator
+ ref={this._marqueeref}
rootDoc={this.props.rootDoc}
getPageFromScroll={this.getPageFromScroll}
anchorMenuClick={this.props.anchorMenuClick}
scrollTop={0}
- down={this._marqueeing}
- addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)}
- docView={this.props.docViewPath().lastElement()}
+ annotationLayerScrollTop={NumCast(this.props.Document._layout_scrollTop)}
+ addDocument={this.addDocumentWrapper}
+ docView={this.props.DocumentView!}
finishMarquee={this.finishMarquee}
savedAnnotations={this.savedAnnotations}
selectionText={this.selectionText}