From 464bca333ba97185dcbc93409001adeb26711902 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 1 Jul 2021 22:43:47 -0400
Subject: added ability to create anchors in PDF/Webboxes by making a selection
 and clicking the start link button

---
 src/client/views/pdf/AnchorMenu.tsx | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/client/views/pdf/AnchorMenu.tsx')

diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 1e2d72254..86b124de5 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -46,6 +46,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     public OnClick: (e: PointerEvent) => void = unimplementedFunction;
     public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
     public Highlight: (color: string, isPushpin: boolean) => Opt<Doc> = (color: string, isPushpin: boolean) => undefined;
+    public GetAnchor: () => Opt<Doc> = () => undefined;
     public Delete: () => void = unimplementedFunction;
     public AddTag: (key: string, value: string) => boolean = returnFalse;
     public PinToPres: () => void = unimplementedFunction;
-- 
cgit v1.2.3-70-g09d2


From 7995c8b9b810df4ed5ade1db71fcb0e2de3d3da2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 14 Jul 2021 09:47:18 -0400
Subject: fixed selections so that they persist on deselect to allow linking a
 selection on one document directly to a selection on another document.   Also
 fixed recently closed to move closed document to top of list.

---
 src/client/views/DocComponent.tsx     |  5 ++++-
 src/client/views/MarqueeAnnotator.tsx | 23 ++++++++++++-----------
 src/client/views/nodes/ImageBox.tsx   | 15 ++++++++++++---
 src/client/views/nodes/WebBox.tsx     |  2 +-
 src/client/views/pdf/AnchorMenu.tsx   |  4 ++--
 5 files changed, 31 insertions(+), 18 deletions(-)

(limited to 'src/client/views/pdf/AnchorMenu.tsx')

diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index a878a7afb..0b54e3cd6 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -154,7 +154,10 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
                         leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
                         Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         doc.context = undefined;
-                        recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
+                        if (recent) {
+                            Doc.RemoveDocFromList(recent, "data", doc);
+                            Doc.AddDocToList(recent, "data", doc, undefined, true, true);
+                        }
                     });
                     this.props.select(false);
                     return true;
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index b5df7b79b..717bd0768 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -67,7 +67,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
 
         AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight("rgba(173, 216, 230, 0.75)", true));
         AnchorMenu.Instance.Highlight = this.highlight;
-        AnchorMenu.Instance.GetAnchor = () => this.highlight("rgba(173, 216, 230, 0.75)", true);
+        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight("rgba(173, 216, 230, 0.75)", true, savedAnnotations);
         /**
          * 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.
@@ -102,12 +102,13 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
 
     @undoBatch
     @action
-    makeAnnotationDocument = (color: string, isLinkButton?: boolean): Opt<Doc> => {
-        if (this.props.savedAnnotations.size === 0) return undefined;
-        const savedAnnos = Array.from(this.props.savedAnnotations.values())[0];
+    makeAnnotationDocument = (color: string, isLinkButton?: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>): Opt<Doc> => {
+        const savedAnnoMap = savedAnnotations ?? this.props.savedAnnotations;
+        if (savedAnnoMap.size === 0) return undefined;
+        const savedAnnos = Array.from(savedAnnoMap.values())[0];
         if (savedAnnos.length && (savedAnnos[0] as any).marqueeing) {
             const scale = this.props.scaling?.() || 1;
-            const anno = Array.from(this.props.savedAnnotations.values())[0][0];
+            const anno = savedAnnos[0];
             const containerOffset = this.props.containerOffset?.() || [0, 0];
             const marqueeAnno = Docs.Create.FreeformDocument([], { _isLinkButton: isLinkButton, backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title });
             marqueeAnno.x = (parseInt(anno.style.left || "0") - containerOffset[0]) / scale;
@@ -115,7 +116,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
             marqueeAnno._height = parseInt(anno.style.height || "0") / scale;
             marqueeAnno._width = parseInt(anno.style.width || "0") / scale;
             anno.remove();
-            this.props.savedAnnotations.clear();
+            savedAnnoMap.clear();
             return marqueeAnno;
         }
 
@@ -123,7 +124,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         let maxX = -Number.MAX_VALUE;
         let minY = Number.MAX_VALUE;
         const annoDocs: Doc[] = [];
-        this.props.savedAnnotations.forEach((value: HTMLDivElement[], key: number) => value.map(anno => {
+        savedAnnoMap.forEach((value: HTMLDivElement[], key: number) => value.map(anno => {
             const textRegion = new Doc();
             textRegion.x = parseInt(anno.style.left ?? "0");
             textRegion.y = parseInt(anno.style.top ?? "0");
@@ -142,15 +143,15 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         textRegionAnnoProto.x = Math.max(maxX, 0);
         // mainAnnoDocProto.text = this._selectionText;
         textRegionAnnoProto.textInlineAnnotations = new List<Doc>(annoDocs);
-        this.props.savedAnnotations.clear();
+        savedAnnoMap.clear();
         return textRegionAnno;
     }
     @action
-    highlight = (color: string, isLinkButton: boolean) => {
+    highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => {
         // creates annotation documents for current highlights
         const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
-        const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton);
-        annotationDoc && this.props.addDocument(annotationDoc);
+        const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
+        !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
         return annotationDoc as Doc ?? undefined;
     }
 
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 13dfad0fe..b803c94ba 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -7,7 +7,7 @@ import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
 import { createSchema, makeInterface } from '../../../fields/Schema';
 import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, NumCast } from '../../../fields/Types';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, OmitKeys, returnOne, Utils } from '../../../Utils';
@@ -28,6 +28,8 @@ import "./ImageBox.scss";
 import React = require("react");
 import { InkTool } from '../../../fields/InkField';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { AnchorMenu } from '../pdf/AnchorMenu';
+import { Docs } from '../../documents/Documents';
 const path = require('path');
 
 export const pageSchema = createSchema({
@@ -62,6 +64,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     } // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
 
+
+    getAnchor = () => {
+        const anchor = AnchorMenu.Instance?.GetAnchor(this._savedAnnotations);
+        anchor && this.addDocument(anchor);
+        return anchor ?? this.rootDoc;
+    }
+
     componentDidMount() {
         this.props.setContentView?.(this); // bcz: do not remove this.  without it, stepping into an image in the lightbox causes an infinite loop....
         this._disposers.sizer = reaction(() => (
@@ -74,8 +83,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             { fireImmediately: true, delay: 1000 });
         this._disposers.selection = reaction(() => this.props.isSelected(),
             selected => !selected && setTimeout(() => {
-                Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
-                this._savedAnnotations.clear();
+                // Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
+                // this._savedAnnotations.clear();
             }));
         this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
             ({ nativeSize, width }) => {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index a16881b66..5791e2310 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -176,7 +176,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     getAnchor = () => {
         const anchor =
-            AnchorMenu.Instance?.GetAnchor() ??
+            AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ??
             Docs.Create.TextanchorDocument({
                 title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
                 annotationOn: this.rootDoc,
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index 86b124de5..c24c4eaaf 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -1,7 +1,7 @@
 import React = require("react");
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Tooltip } from "@material-ui/core";
-import { action, computed, observable, IReactionDisposer, reaction } from "mobx";
+import { action, computed, observable, IReactionDisposer, reaction, ObservableMap } from "mobx";
 import { observer } from "mobx-react";
 import { ColorState } from "react-color";
 import { Doc, Opt } from "../../../fields/Doc";
@@ -46,7 +46,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
     public OnClick: (e: PointerEvent) => void = unimplementedFunction;
     public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
     public Highlight: (color: string, isPushpin: boolean) => Opt<Doc> = (color: string, isPushpin: boolean) => undefined;
-    public GetAnchor: () => Opt<Doc> = () => undefined;
+    public GetAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     public Delete: () => void = unimplementedFunction;
     public AddTag: (key: string, value: string) => boolean = returnFalse;
     public PinToPres: () => void = unimplementedFunction;
-- 
cgit v1.2.3-70-g09d2

