diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/DragManager.ts | 24 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 4 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/MarqueeAnnotator.tsx | 53 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 9 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 14 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 40 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx | 2 | ||||
-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) | 12 | ||||
-rw-r--r-- | src/client/views/pdf/Annotation.tsx | 20 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 18 |
16 files changed, 136 insertions, 82 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 8a07c5321..1c5277de0 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"; @@ -261,7 +261,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 c1fafe3e6..bd5db62ef 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -55,7 +55,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'; @@ -615,7 +615,7 @@ export class MainView extends React.Component { <TaskCompletionBox /> <ContextMenu /> <RadialMenu /> - <PDFMenu /> + <AnchorMenu /> <MarqueeOptionsMenu /> <OverlayView /> <TimelineMenu /> diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 21048ebe2..0ab2d1ecf 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -8,7 +8,7 @@ import { DocUtils, Docs } from "../documents/Documents"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { DragManager } from "../util/DragManager"; import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox"; -import { PDFMenu } from "./pdf/PDFMenu"; +import { AnchorMenu } from "./pdf/AnchorMenu"; import "./MarqueeAnnotator.scss"; import React = require("react"); import { undoBatch } from "../util/UndoManager"; @@ -40,8 +40,8 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { constructor(props: any) { super(props); runInAction(() => { - PDFMenu.Instance.Status = "marquee"; - PDFMenu.Instance.fadeOut(true); + 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(); @@ -148,32 +148,37 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { onSelectEnd = (e: PointerEvent) => { if (!e.ctrlKey) { - PDFMenu.Instance.Marquee = { left: this._left, top: this._top, width: this._width, height: this._height }; + AnchorMenu.Instance.Marquee = { left: this._left, top: this._top, width: this._width, height: this._height }; } - PDFMenu.Instance.Highlight = this.highlight; + AnchorMenu.Instance.Highlight = this.highlight; /** - * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation. + * 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. */ - PDFMenu.Instance.StartDrag = action(async (e: PointerEvent, ele: HTMLElement) => { + AnchorMenu.Instance.StartDrag = action(async (e: PointerEvent, ele: HTMLElement) => { e.preventDefault(); e.stopPropagation(); - const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.rootDoc.title, 0, 0, 100, 100); - FormattedTextBox.SelectOnLoad = targetDoc[Id]; - const anchorHighlightDoc = this.highlight("rgba(173, 216, 230, 0.75)"); // hyperlink color - if (anchorHighlightDoc) { - DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.rootDoc, anchorHighlightDoc, targetDoc), e.pageX, e.pageY, { - dragComplete: e => { - if (!e.aborted && e.annoDragData && !e.linkDocument) { - e.linkDocument = DocUtils.MakeLink({ doc: anchorHighlightDoc }, { doc: e.annoDragData.dropDocument }, "Annotation"); - anchorHighlightDoc.isLinkButton = true; // prevents link button from showing up --- maybe not a good thing? - anchorHighlightDoc.isPushpin = e.annoDragData?.dropDocument.annotationOn === this.props.rootDoc; - } - e.linkDocument && e.annoDragData?.linkDropCallback?.(e as { linkDocument: Doc });// bcz: typescript can't figure out that this is valid even though we tested e.linkDocument - } - }); + 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 @@ -186,10 +191,10 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { MarqueeAnnotator.previewNewAnnotation(this.props.savedAnnotations, this.props.annotationLayer, copy, this.props.getPageFromScroll?.(this._top) || 0); } - PDFMenu.Instance.jumpTo(e.clientX, e.clientY); + AnchorMenu.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) + 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); 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 588ba6922..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]); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6371ae5fb..13e1f3d54 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -550,10 +550,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]); } } @@ -873,10 +876,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.tsx b/src/client/views/nodes/ImageBox.tsx index e202749aa..47fa25951 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -27,7 +27,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); import { StyleProp } from '../StyleProvider'; -import { PDFMenu } from '../pdf/PDFMenu'; +import { AnchorMenu } from '../pdf/AnchorMenu'; import { Dictionary } from 'typescript-collections'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { Annotation } from '../pdf/Annotation'; @@ -83,7 +83,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD if (!selected) { this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); this._savedAnnotations.clear(); - PDFMenu.Instance.fadeOut(true); + AnchorMenu.Instance.fadeOut(true); } }, { fireImmediately: true }); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index f1ef6d10b..55a9818ad 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -27,7 +27,7 @@ import { Transform } from "../../util/Transform"; import { StyleProp } from "../StyleProvider"; import { Dictionary } from "typescript-collections"; import { MarqueeAnnotator } from "../MarqueeAnnotator"; -import { PDFMenu } from "../pdf/PDFMenu"; +import { AnchorMenu } from "../pdf/AnchorMenu"; const path = require('path'); export const timeSchema = createSchema({ @@ -195,7 +195,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD if (!selected) { this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); this._savedAnnotations.clear(); - PDFMenu.Instance.fadeOut(true); + AnchorMenu.Instance.fadeOut(true); } }, { fireImmediately: true }); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index ca3d4448d..c9c4ed159 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -25,7 +25,7 @@ import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; import { Annotation } from "../pdf/Annotation"; -import { PDFMenu } from "../pdf/PDFMenu"; +import { AnchorMenu } from "../pdf/AnchorMenu"; import { FieldView, FieldViewProps } from './FieldView'; import "./WebBox.scss"; import React = require("react"); @@ -161,7 +161,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum if (!selected) { this._savedAnnotations.values().forEach(v => v.forEach(a => a.remove())); this._savedAnnotations.clear(); - PDFMenu.Instance.fadeOut(true); + AnchorMenu.Instance.fadeOut(true); } }, { fireImmediately: true }); 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 603e26021..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 = [ @@ -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) => { @@ -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.tsx b/src/client/views/pdf/PDFViewer.tsx index f9139220b..f2052d454 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -26,7 +26,7 @@ import { FieldViewProps } from "../nodes/FieldView"; 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"); @@ -137,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(); }, @@ -384,30 +384,32 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu this._setPreviewCursor?.(e.clientX, e.clientY, true); } if (!e.altKey && e.button === 0 && this.active(true)) { - if (e.target && (e.target as any).parentElement.className !== "textLayer") { + 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 - PDFMenu.Instance.Status = "marquee"; - PDFMenu.Instance.fadeOut(true); + 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("pointermove", this.onSelectMove); document.addEventListener("pointerup", this.onSelectEnd); } + document.addEventListener("pointermove", this.onSelectMove); } } @action finishMarquee = () => { this._marqueeing = undefined; + document.removeEventListener("pointermove", this.onSelectMove); this.props.select(false); } @action onSelectMove = (e: PointerEvent): void => { - 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 @@ -421,7 +423,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu if (sel?.type === "Range") { const selRange = sel.getRangeAt(0); this.createTextAnnotation(sel, selRange); - PDFMenu.Instance.jumpTo(e.clientX, e.clientY); + AnchorMenu.Instance.jumpTo(e.clientX, e.clientY); } } |