From 0e5891eab7f53697b764b7e9da5163db0351a0a2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 8 Feb 2021 19:25:48 -0500
Subject: overhaul of link anchors on text boxes to use actual Documents to
 represent selected text.  Also got rid of _scrollY and _scrollPreviewY so
 that all document regions can be focused on using focus() and the new
 scrollFocus() mechanisim

---
 .../views/nodes/formattedText/FormattedTextBox.tsx | 207 ++++++++++-----------
 1 file changed, 96 insertions(+), 111 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index b3abcf90a..2409f36a0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -92,6 +92,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     public static Instance: FormattedTextBox;
     public ProseRef?: HTMLDivElement;
     public get EditorView() { return this._editorView; }
+    public get SidebarKey() { return this.fieldKey + "-sidebar"; }
     private _boxRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _ref: React.RefObject<HTMLDivElement> = React.createRef();
     private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -104,15 +105,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _recordingStart: number = 0;
     private _pause: boolean = false;
-    private _animatingScroll: number = 0; // hack to prevent scroll values from being written to document when scroll is animating
+    private _ignoreScroll = false;
 
     @computed get _recording() { return this.dataDoc?.audioState === "recording"; }
     set _recording(value) {
         this.dataDoc.audioState = value ? "recording" : undefined;
     }
 
-    @observable private _entered = false;
-
     public static FocusedBox: FormattedTextBox | undefined;
     public static SelectOnLoad = "";
     public static PasteOnLoad: ClipboardEvent | undefined;
@@ -164,29 +163,35 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     // TODO: bcz: Argh... if a section of text has multiple anchors, this should just remove the intended one. 
     // but since removing one anchor from the list of attr anchors isn't implemented, this will end up removing nothing.
     public RemoveLinkFromDoc(linkDoc?: Doc) {
+        this.unhighlightSearchTerms();
         const state = this._editorView?.state;
-        if (state && linkDoc && this._editorView) {
-            var allLinks: any[] = [];
+        const a1 = linkDoc?.anchor1 as Doc;
+        const a2 = linkDoc?.anchor2 as Doc;
+        if (state && a1 && a2 && this._editorView) {
+            this.removeDocument(a1);
+            this.removeDocument(a2);
+            var allFoundLinkAnchors: any[] = [];
             state.doc.nodesBetween(0, state.doc.nodeSize - 2, (node: any, pos: number, parent: any) => {
-                const foundMark = findLinkMark(node.marks);
-                const newHrefs = foundMark?.attrs.allLinks.filter((a: any) => a.href.includes(linkDoc[Id])) || [];
-                allLinks = newHrefs.length ? newHrefs : allLinks;
+                const foundLinkAnchors = findLinkMark(node.marks)?.attrs.allAnchors.filter((a: any) => a.anchorId === a1[Id] || a.anchorId === a2[Id]) || [];
+                allFoundLinkAnchors = foundLinkAnchors.length ? foundLinkAnchors : allFoundLinkAnchors;
                 return true;
             });
-            if (allLinks.length) {
-                this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allLinks }));
+            if (allFoundLinkAnchors.length) {
+                this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allAnchors: allFoundLinkAnchors }));
             }
         }
     }
-    // removes all the specified link referneces from the selection. 
+    // removes all the specified link references from the selection. 
     // NOTE: as above, this won't work correctly if there are marks with overlapping but not exact sets of link references.
-    public RemoveLinkFromSelection(allLinks: { href: string, title: string, linkId: string, targetId: string }[]) {
+    public RemoveAnchorFromSelection(allAnchors: { href: string, title: string, linkId: string, targetId: string }[]) {
         const state = this._editorView?.state;
         if (state && this._editorView) {
-            this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allLinks }));
+            this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allAnchors }));
         }
     }
 
+    getAnchor = () => this.makeLinkAnchor(undefined, "add:right", undefined, "Anchored Selection");
+
     linkOnDeselect: Map<string, string> = new Map();
 
     doLinkOnDeselect() {
@@ -234,11 +239,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
             DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.rootDoc, () => this.rootDoc, targetCreator), e.pageX, e.pageY, {
                 dragComplete: e => {
+                    const anchor = this.makeLinkAnchor(undefined, "add:right", undefined, "a link");
                     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.linkDocument = DocUtils.MakeLink({ doc: anchor }, { 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
                 }
             });
@@ -270,8 +275,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     this.linkOnDeselect.set(key, value);
 
                     const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
-                    const allLinks = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }];
-                    const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "add:right", title: value });
+                    const allAnchors = [{ href: Utils.prepend("/doc/" + id), title: value, anchorId: id }];
+                    const link = this._editorView.state.schema.marks.linkAnchor.create({ allAnchors, location: "add:right", title: value });
                     const mval = this._editorView.state.schema.marks.metadataVal.create();
                     const offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
                     tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval);
@@ -407,9 +412,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             res.map(r => r.map(h => flattened.push(h)));
             const lastSel = Math.min(flattened.length - 1, this._searchIndex);
             this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
-            const alink = DocUtils.MakeLink({ doc: this.rootDoc }, { doc: target }, "automatic")!;
-            const allLinks = [{ href: Utils.prepend("/doc/" + alink[Id]), title: "a link", targetId: target[Id], linkId: alink[Id] }];
-            const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, title: "a link", location });
+            const anchor = new Doc();
+            const alink = DocUtils.MakeLink({ doc: anchor }, { doc: target }, "automatic")!;
+            const allAnchors = [{ href: Utils.prepend("/doc/" + anchor[Id]), title: "a link", anchorId: anchor[Id] }];
+            const link = this._editorView.state.schema.marks.linkAnchor.create({ allAnchors, title: "a link", location });
             this._editorView.dispatch(tr.addMark(flattened[lastSel].from, flattened[lastSel].to, link));
         }
     }
@@ -507,8 +513,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
     linkDrop = (data: { linkDocument: Doc }) => {
         const anchor1Title = data.linkDocument.anchor1 instanceof Doc ? StrCast(data.linkDocument.anchor1.title) : "-untitled-";
-        const anchor1Id = data.linkDocument.anchor1 instanceof Doc ? data.linkDocument.anchor1[Id] : "";
-        this.makeLinkToSelection(data.linkDocument[Id], anchor1Title, "add:right", anchor1Id);
+        const anchor1 = data.linkDocument.anchor1 instanceof Doc ? data.linkDocument.anchor1 : undefined;
+        this.makeLinkAnchor(anchor1, "add:right", undefined, anchor1Title);
     }
 
     getNodeEndpoints(context: Node, node: Node): { from: number, to: number } | null {
@@ -825,37 +831,46 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         };
     }
 
-    makeLinkToSelection(linkId: string, title: string, location: string, targetId: string, targetHref?: string) {
+    makeLinkAnchor(anchorDoc?: Doc, location?: string, targetHref?: string, title?: string) {
         const state = this._editorView?.state;
         if (state) {
-            const href = targetHref ?? Utils.prepend("/doc/" + linkId);
             const sel = state.selection;
             const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
             let tr = state.tr.addMark(sel.from, sel.to, splitter);
-            sel.from !== sel.to && tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
-                if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
-                    const allLinks = [{ href, title, targetId, linkId }];
-                    allLinks.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.linkAnchor.name)?.attrs.allLinks ?? []));
-                    const link = state.schema.marks.linkAnchor.create({ allLinks, title, location, linkId });
-                    tr = tr.addMark(pos, pos + node.nodeSize, link);
-                }
-            });
-            this.dataDoc[ForceServerWrite] = this.dataDoc[UpdatingFromServer] = true;  // need to allow permissions for adding links to readonly/augment only documents
-            this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
-            this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
+            if (sel.from !== sel.to) {
+                const anchor = anchorDoc ?? Docs.Create.TextanchorDocument();
+                const href = targetHref ?? Utils.prepend("/doc/" + anchor[Id]);
+                if (anchor !== anchorDoc) this.addDocument(anchor);
+                tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
+                    if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
+                        const allAnchors = [{ href, title, anchorId: anchor[Id] }];
+                        allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.linkAnchor.name)?.attrs.allAnchors ?? []));
+                        const link = state.schema.marks.linkAnchor.create({ allAnchors, title, location });
+                        tr = tr.addMark(pos, pos + node.nodeSize, link);
+                    }
+                });
+                this.dataDoc[ForceServerWrite] = this.dataDoc[UpdatingFromServer] = true;  // need to allow permissions for adding links to readonly/augment only documents
+                this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
+                this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
+                Doc.GetProto(anchor).title = this._editorView?.state.doc.textBetween(sel.from, sel.to);
+                return anchor;
+            }
+            return anchorDoc ?? this.rootDoc;
         }
+        return anchorDoc ?? this.rootDoc;
     }
 
     IsActive = () => {
         return this.active();//this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0;
     }
 
-    scrollToLinkId = (linkId: string) => {
-        const findLinkFrag = (frag: Fragment, editor: EditorView) => {
+    scrollFocus = (doc: Doc, smooth: boolean) => {
+        const anchorId = doc[Id];
+        const findAnchorFrag = (frag: Fragment, editor: EditorView) => {
             const nodes: Node[] = [];
             let hadStart = start !== 0;
             frag.forEach((node, index) => {
-                const examinedNode = findLinkNode(node, editor);
+                const examinedNode = findAnchorNode(node, editor);
                 if (examinedNode?.node.textContent) {
                     nodes.push(examinedNode.node);
                     !hadStart && (start = index + examinedNode.start);
@@ -864,20 +879,20 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             });
             return { frag: Fragment.fromArray(nodes), start };
         };
-        const findLinkNode = (node: Node, editor: EditorView) => {
+        const findAnchorNode = (node: Node, editor: EditorView) => {
             if (!node.isText) {
-                const content = findLinkFrag(node.content, editor);
+                const content = findAnchorFrag(node.content, editor);
                 return { node: node.copy(content.frag), start: content.start };
             }
             const marks = [...node.marks];
             const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.linkAnchor);
-            return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => linkId === item.href.replace(/.*\/doc\//, "")) ? { node, start: 0 } : undefined;
+            return linkIndex !== -1 && marks[linkIndex].attrs.allAnchors.find((item: { href: string }) => anchorId === item.href.replace(/.*\/doc\//, "")) ? { node, start: 0 } : undefined;
         };
 
         let start = 0;
-        if (this._editorView && linkId) {
+        if (this._editorView && anchorId) {
             const editor = this._editorView;
-            const ret = findLinkFrag(editor.state.doc.content, editor);
+            const ret = findAnchorFrag(editor.state.doc.content, editor);
 
             if (ret.frag.size > 2 && ret.start >= 0) {
                 let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
@@ -885,16 +900,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     selection = TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
                 }
                 editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
-                const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
-                setTimeout(() => editor.dispatch(editor.state.tr.addMark(selection.from, selection.to, mark)), 0);
-                setTimeout(() => this.unhighlightSearchTerms(), 2000);
+                const escAnchorId = anchorId[0] > '0' && anchorId[0] <= '9' ? `\\3${anchorId[0]} ${anchorId.substr(1)}` : anchorId;
+                addStyleSheetRule(FormattedTextBox._highlightStyleSheet, `${escAnchorId}`, { background: "yellow" });
+                setTimeout(() => clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), 1500);
             }
-            Doc.SetInPlace(this.layoutDoc, "scrollToLinkID", undefined, false);
-            Doc.SetInPlace(this.layoutDoc, "_scrollToPreviewLinkID", undefined, false);
         }
     }
 
     componentDidMount() {
+        this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
         this.props.contentsActive?.(this.IsActive);
         this._cachedLinks = DocListCast(this.Document.links);
         this._disposers.sidebarheight = reaction(
@@ -916,27 +930,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }
             }
         );
-        this._disposers.linkMaker = reaction(
-            () => this.props.makeLink?.(),
-            (linkDoc: Opt<Doc>) => {
-                if (linkDoc) {
-                    const a1 = Cast(linkDoc.anchor1, Doc, null);
-                    const a2 = Cast(linkDoc.anchor2, Doc, null);
-                    const otherAnchor = Doc.AreProtosEqual(a1, this.rootDoc) ? a2 : a1;
-                    const anchor2Title = StrCast(otherAnchor.title, "-untitled-");
-                    const anchor2Id = otherAnchor?.[Id] || "";
-                    this.makeLinkToSelection(linkDoc[Id], anchor2Title, "add:right", anchor2Id);
-                }
-            },
-            { fireImmediately: true }
-        );
         this._disposers.editorState = reaction(
             () => {
-                if (!this.dataDoc || !this.layoutDoc) return undefined;
-                if (this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey]) {
-                    return { data: Cast(this.dataDoc[this.props.fieldKey], RichTextField, null), str: StrCast(this.dataDoc[this.props.fieldKey]) };
-                }
-                return { data: Cast(this.layoutDoc[this.props.fieldKey], RichTextField, null), str: StrCast(this.layoutDoc[this.props.fieldKey]) };
+                const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined :
+                    this.dataDoc?.[this.props.fieldKey + "-noTemplate"] || !this.layoutDoc[this.props.fieldKey] ?
+                        this.dataDoc : this.layoutDoc;
+                return !whichDoc ? undefined : { data: Cast(whichDoc[this.props.fieldKey], RichTextField, null), str: StrCast(whichDoc[this.props.fieldKey]) };
             },
             incomingValue => {
                 if (this._editorView && this._applyingChange !== this.fieldKey) {
@@ -1013,38 +1012,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }
             );
         }
-        this._disposers.previewScrollToRegion = reaction(() => StrCast(this.layoutDoc._scrollToPreviewLinkID),
-            scrollToLinkID => this.props.renderDepth === -1 && scrollToLinkID && this.scrollToLinkId(scrollToLinkID), { fireImmediately: true }
-        );
-        this._disposers.scrollToRegion = reaction(() => StrCast(this.layoutDoc.scrollToLinkID),
-            scrollToLinkID => scrollToLinkID && this.scrollToLinkId(scrollToLinkID), { fireImmediately: true }
-        );
+        var quickScroll: string | undefined = "";
         this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop),
             pos => {
-                const durationMiliStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/);
-                const durationSecStr = StrCast(this.Document._viewTransition).match(/([0-9.]*)s/);
-                const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
-                if (duration) {
-                    this._animatingScroll++;
-                    this._scrollRef.current && smoothScroll(duration, this._scrollRef.current, Math.abs(pos || 0));
-                    setTimeout(() => this._animatingScroll--, duration);
-                } else {
-                    this._scrollRef.current?.scrollTo({ top: pos });
+                if (!this._ignoreScroll && this._scrollRef.current) {
+                    const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition);
+                    const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
+                    const durationSecStr = viewTrans.match(/([0-9.]*)s/);
+                    const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
+                    if (duration) {
+                        smoothScroll(duration, this._scrollRef.current, Math.abs(pos || 0));
+                    } else {
+                        this._scrollRef.current.scrollTo({ top: pos });
+                    }
                 }
             }, { fireImmediately: true }
         );
-        this._disposers.scrollY = reaction(() => Cast(this.layoutDoc._scrollY, "number", null),
-            pos => {
-                this.Document._scrollY = undefined;
-                pos !== undefined && setTimeout(() => this.layoutDoc._scrollTop = pos, this._scrollRef.current ? 0 : 250);
-            }, { fireImmediately: true }
-        );
-        this._disposers.scrollPreviewY = reaction(() => Cast(this.layoutDoc.scrollPreviewY, "number", null),
-            pos => {
-                this.Document.scrollPreviewY = undefined;
-                pos !== undefined && setTimeout(() => this.layoutDoc._scrollTop = pos, this._scrollRef.current ? 0 : 250);
-            }, { fireImmediately: true }
-        );
+        quickScroll = undefined;
 
         setTimeout(() => this.tryUpdateHeight(NumCast(this.layoutDoc.limitHeight)));
     }
@@ -1228,8 +1212,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 handleScrollToSelection: (editorView) => {
                     const docPos = editorView.coordsAtPos(editorView.state.selection.from);
                     const viewRect = self._ref.current!.getBoundingClientRect();
-                    if (docPos.top < viewRect.top || docPos.top > viewRect.bottom) {
-                        docPos && (self._scrollRef.current!.scrollTop += (docPos.top - viewRect.top) * self.props.ScreenToLocalTransform().Scale);
+                    const scrollRef = self._scrollRef.current;
+                    if ((docPos.top < viewRect.top || docPos.top > viewRect.bottom) && scrollRef) {
+                        const scrollPos = scrollRef.scrollTop + (docPos.top - viewRect.top) * self.props.ScreenToLocalTransform().Scale;
+                        if (!LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) {
+                            scrollPos && smoothScroll(500, scrollRef, scrollPos);
+                        } else {
+                            scrollRef.scrollTo({ top: scrollPos });
+                        }
                     }
                     return true;
                 },
@@ -1423,6 +1413,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
     }
 
+    static _highlightStyleSheet: any = addStyleSheet();
     static _bulletStyleSheet: any = addStyleSheet();
     static _userStyleSheet: any = addStyleSheet();
     _forceUncollapse = true; // if the cursor doesn't move between clicks, then the selection will disappear for some reason.  This flags the 2nd click as happening on a selection which allows bullet points to toggle
@@ -1609,9 +1600,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         this._editorView!.dispatch(updateBullets(this._editorView!.state.tr, this._editorView!.state.schema));
         eve.stopPropagation(); // drag n drop of text within text note will generate a new note if not caughst, as will dragging in from outside of Dash.
     }
-    onscrolled = (ev: React.UIEvent) => {
-        if (!LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc && !this._animatingScroll) {
-            this.layoutDoc._scrollTop = this._scrollRef.current!.scrollTop;
+    onScroll = (ev: React.UIEvent) => {
+        if (!LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc && this._scrollRef.current) {
+            this._ignoreScroll = true;
+            this.layoutDoc._scrollTop = this._scrollRef.current.scrollTop;
+            this._ignoreScroll = false;
         }
     }
 
@@ -1655,7 +1648,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     @computed get sidebarHandle() {
-        const annotated = DocListCast(this.dataDoc[this.annotationKey]).filter(d => d?.author).length;
+        const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
         return this.props.noSidebar || (!this.props.isSelected() && !(annotated && !this.sidebarWidth())) ? (null) :
             <div className="formattedTextBox-sidebar-handle"
                 style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`, background: annotated ? "lightblue" : this.props.styleProvider?.(this.props.Document, this.props, StyleProp.WidgetColor) }}
@@ -1675,9 +1668,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             xMargin: 0,
             yMargin: 0,
             chromeStatus: "enabled",
-            scaleField: this.annotationKey + "-scale",
+            scaleField: this.SidebarKey + "-scale",
             isAnnotationOverlay: true,
-            fieldKey: this.annotationKey,
+            fieldKey: this.SidebarKey,
             fitContentsToDoc: fitToBox,
             select: emptyFunction,
             active: this.annotationsActive,
@@ -1750,14 +1743,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     onPointerDown={this.onPointerDown}
                     onMouseUp={this.onMouseUp}
                     onWheel={this.onPointerWheel}
-                    onPointerEnter={action(() => this._entered = true)}
-                    onPointerLeave={action(e => {
-                        this._entered = false;
-                        const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y);
-                        for (let child: any = target; child; child = child?.parentElement) {
-                            child === this._ref.current! && (this._entered = true);
-                        }
-                    })}
                     onDoubleClick={this.onDoubleClick}
                 >
                     <div className={`formattedTextBox-outer${selected ? "-selected" : ""}`} ref={this._scrollRef}
@@ -1766,7 +1751,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                             pointerEvents: !active && !SnappingManager.GetIsDragging() ? "none" : undefined,
                             overflow: this.layoutDoc._singleLine ? "hidden" : undefined,
                         }}
-                        onScroll={this.onscrolled} onDrop={this.ondrop} >
+                        onScroll={this.onScroll} onDrop={this.ondrop} >
                         <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selPaddingClass}`} ref={this.createDropTarget}
                             style={{
                                 padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${padding}px`,
-- 
cgit v1.2.3-70-g09d2

