From 7dbd1417fe12b54cdab44fd25adb6c5a9c52bac9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 11 Feb 2021 18:11:57 -0500
Subject: more focus refactoring and some bug fixes - resetting target document
 when recursively focusing.

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

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

diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 6155ed493..150a549cb 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -99,6 +99,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
 
     followLink = (e: React.PointerEvent) => {
         if (this._linkDoc && this._linkSrc) {
+            LinkDocPreview.Clear();
             LinkManager.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
         } else if (this.props.hrefs?.length) {
             this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _width: 200, _height: 400, useCors: true }), "add:right");
-- 
cgit v1.2.3-70-g09d2


From 4a9d6d1409327fd99c5f554caebd917a316db32b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 12 Feb 2021 11:00:29 -0500
Subject: changed lightbox to navigate within frame if next target is alreay
 there, otherwise create it.  changed focus default to call afterFocus(). 
 fixed bug of zooming on target doc to compute scale correctly.

---
 src/client/documents/Documents.ts                  |  5 +++
 src/client/util/DocumentManager.ts                 | 13 ++----
 src/client/util/LinkManager.ts                     | 25 ++++++-----
 src/client/views/DocumentDecorations.tsx           |  5 +--
 src/client/views/GestureOverlay.tsx                |  2 +-
 src/client/views/GlobalKeyHandler.ts               |  2 +-
 src/client/views/LightboxView.tsx                  | 52 +++++++++++++++-------
 src/client/views/MainView.tsx                      | 14 +++---
 src/client/views/OverlayView.tsx                   |  3 +-
 src/client/views/collections/CollectionMenu.tsx    |  7 ++-
 .../views/collections/CollectionSchemaView.tsx     |  3 +-
 src/client/views/collections/SchemaTable.tsx       |  4 +-
 src/client/views/collections/TabDocView.tsx        |  5 ++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 src/client/views/nodes/DocumentContentsView.tsx    |  6 +--
 src/client/views/nodes/LinkDocPreview.tsx          |  4 +-
 src/client/views/nodes/PresBox.tsx                 |  6 ++-
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 .../views/presentationview/PresElementBox.tsx      |  3 +-
 src/mobile/MobileInterface.tsx                     |  4 +-
 21 files changed, 97 insertions(+), 72 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index de4a8f43c..2d370ca8d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -55,6 +55,7 @@ import { PresElementBox } from "../views/presentationview/PresElementBox";
 import { SearchBox } from "../views/search/SearchBox";
 import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { DocumentType } from "./DocumentTypes";
+import { DocAfterFocusFunc } from "../views/nodes/DocumentView";
 const path = require('path');
 
 const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
@@ -1017,6 +1018,10 @@ export namespace DocUtils {
         });
     }
 
+    export function DefaultFocus(doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) {
+        afterFocus?.(false);
+    }
+
     export let ActiveRecordings: AudioBox[] = [];
 
     export function MakeLinkToActiveAudio(doc: Doc) {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 816f7f6be..5b4917a30 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -10,8 +10,6 @@ import { LightboxView } from '../views/LightboxView';
 import { DocumentView } from '../views/nodes/DocumentView';
 import { Scripting } from './Scripting';
 
-export type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
-
 export class DocumentManager {
 
     //global holds all of the nodes (regardless of which collection they're in)
@@ -217,14 +215,9 @@ export class DocumentManager {
                         findView(0);
                     }
                 } else {  // there's no context view so we need to create one first and try again when that finishes
-                    const finishFunc = () => this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished);
-                    if (LightboxView.LightboxDoc) {
-                        runInAction(() => LightboxView.LightboxDoc = targetDocContext);
-                        setTimeout(() => finishFunc, 250);
-                    } else {
-                        createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
-                            finishFunc);
-                    }
+                    const finishFunc = () => this.jumpToDocument(targetDoc, true, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished);
+                    createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
+                        finishFunc);
                 }
             }
         }
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 58ccfe645..d433605f1 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -4,10 +4,11 @@ import { Doc, DocListCast, Opt } from "../../fields/Doc";
 import { BoolCast, Cast, StrCast } from "../../fields/Types";
 import { LightboxView } from "../views/LightboxView";
 import { DocumentViewSharedProps } from "../views/nodes/DocumentView";
-import { CreateViewFunc, DocumentManager } from "./DocumentManager";
+import { DocumentManager } from "./DocumentManager";
 import { SharingManager } from "./SharingManager";
 import { UndoManager } from "./UndoManager";
 
+type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
 /* 
  * link doc: 
  * - anchor1: doc
@@ -106,7 +107,11 @@ export class LinkManager {
         const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => {
             const createTabForTarget = (didFocus: boolean) => new Promise<boolean>(res => {
                 const where = StrCast(sourceDoc.followLinkLocation) || followLoc;
-                docViewProps.addDocTab(doc, where);
+                if (LightboxView.LightboxDoc) {
+                    LightboxView.SetLightboxDoc(doc);
+                } else {
+                    docViewProps.addDocTab(doc, where);
+                }
                 setTimeout(() => {
                     const targDocView = DocumentManager.Instance.getFirstDocumentView(doc);
                     if (targDocView) {
@@ -130,6 +135,7 @@ export class LinkManager {
         };
         LinkManager.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, false), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
     }
+
     public static traverseLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
         const linkDocs = link ? [link] : DocListCast(doc.links);
         const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor1
@@ -147,16 +153,11 @@ export class LinkManager {
                     doc === linkDoc.anchor2 ? Cast(linkDoc.anchor1_timecode, "number") :
                         (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? Cast(linkDoc.anchor2_timecode, "number") : Cast(linkDoc.anchor1_timecode, "number")));
                 if (target) {
-                    if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(doc)) {
-                        runInAction(() => LightboxView.LightboxDoc = (target.annotationOn as Doc) ?? target);
-                        finished?.();
-                    } else {
-                        const containerDoc = Cast(target.annotationOn, Doc, null) || target;
-                        targetTimecode !== undefined && (containerDoc._currentTimecode = targetTimecode);
-                        const targetContext = Cast(containerDoc?.context, Doc, null);
-                        const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
-                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished);
-                    }
+                    const containerDoc = Cast(target.annotationOn, Doc, null) || target;
+                    targetTimecode !== undefined && (containerDoc._currentTimecode = targetTimecode);
+                    const targetContext = Cast(containerDoc?.context, Doc, null);
+                    const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
+                    DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished);
                 } else {
                     finished?.();
                 }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 77b43db9b..87ed142f8 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -175,10 +175,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b
                 } else if (e.altKey) {    // open same document in new tab
                     CollectionDockingView.ToggleSplit(Cast(selectedDocs[0].props.Document._fullScreenView, Doc, null) || selectedDocs[0].props.Document, "right");
                 } else {
-                    runInAction(() => {
-                        LightboxView.LightboxDoc = selectedDocs[0].props.Document;
-                        LightboxView.LightboxFuture = selectedDocs.slice(1).map(view => view.props.Document);
-                    });
+                    LightboxView.SetLightboxDoc(selectedDocs[0].props.Document, selectedDocs.slice(1).map(view => view.props.Document));
                 }
             }
         }
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index d7e7e055f..b4051194f 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -873,7 +873,7 @@ export class GestureOverlay extends Touchable {
                 styleProvider={returnEmptyString}
                 layerProvider={undefined}
                 docViewPath={returnEmptyDoclist}
-                focus={emptyFunction}
+                focus={DocUtils.DefaultFocus}
                 parentActive={returnTrue}
                 whenActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 09b95315c..3ebcda219 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -130,7 +130,7 @@ export class KeyManager {
                 }
                 if (doDeselect) {
                     SelectionManager.DeselectAll();
-                    LightboxView.LightboxDoc = undefined;
+                    LightboxView.SetLightboxDoc(undefined);
                 }
                 DictationManager.Controls.stop();
                 GoogleAuthenticationManager.Instance.cancel();
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 4e9491ec6..fbfc9672f 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -9,6 +9,8 @@ import { Transform } from '../util/Transform';
 import "./LightboxView.scss";
 import { DocumentView } from './nodes/DocumentView';
 import { DefaultStyleProvider } from './StyleProvider';
+import { DocUtils } from '../documents/Documents';
+import { DocumentManager } from '../util/DocumentManager';
 
 interface LightboxViewProps {
     PanelWidth: number;
@@ -18,7 +20,16 @@ interface LightboxViewProps {
 
 @observer
 export class LightboxView extends React.Component<LightboxViewProps> {
-    @observable public static LightboxDoc: Opt<Doc>;
+    @observable static LightboxDoc: Opt<Doc>;
+    @action public static SetLightboxDoc(doc: Opt<Doc>, future?: Doc[]) {
+        LightboxView.LightboxDoc = doc;
+        if (!doc) {
+            LightboxView.LightboxFuture = LightboxView.LightboxHistory = [];
+        } else if (future) {
+            LightboxView.LightboxFuture = future;
+        }
+        return true;
+    }
     public static IsLightboxDocView(path: DocumentView[]) { return path.includes(LightboxView.LightboxDocView.current!); }
     public static LightboxHistory: (Opt<Doc>)[] = [];
     public static LightboxFuture: (Opt<Doc>)[] = [];
@@ -47,13 +58,11 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         return !LightboxView.LightboxDoc ? (null) :
             <div className="lightboxView-frame"
                 onPointerDown={e => { downx = e.clientX; downy = e.clientY; }}
-                onClick={action(e => {
+                onClick={e => {
                     if (Math.abs(downx - e.clientX) < 4 && Math.abs(downy - e.clientY) < 4) {
-                        LightboxView.LightboxHistory = [];
-                        LightboxView.LightboxFuture = [];
-                        LightboxView.LightboxDoc = undefined;
+                        LightboxView.SetLightboxDoc(undefined);
                     }
-                })}  >
+                }}  >
                 <div className="lightboxView-contents" style={{
                     left: this.leftBorder,
                     top: this.topBorder,
@@ -74,7 +83,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         ScreenToLocalTransform={this.lightboxScreenToLocal}
                         PanelWidth={this.lightboxWidth}
                         PanelHeight={this.lightboxHeight}
-                        focus={emptyFunction}
+                        focus={DocUtils.DefaultFocus}
                         parentActive={returnTrue}
                         whenActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
@@ -87,18 +96,31 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                 </div>
                 {this.navBtn(undefined, "chevron-left",
                     () => LightboxView.LightboxDoc && LightboxView.LightboxHistory.length ? "" : "none",
-                    action(e => {
+                    e => {
                         e.stopPropagation();
-                        const popped = LightboxView.LightboxHistory.pop();
-                        if (LightboxView.LightboxHistory.lastElement() !== LightboxView.LightboxFuture.lastElement()) LightboxView.LightboxFuture.push(popped);
-                        LightboxView.LightboxDoc = LightboxView.LightboxHistory.lastElement();
-                    }))}
+                        const previous = LightboxView.LightboxHistory.pop();
+                        const target = LightboxView.LightboxHistory.lastElement();
+                        const docView = target && DocumentManager.Instance.getLightboxDocumentView(target);
+                        if (docView && target) {
+                            if (LightboxView.LightboxFuture.lastElement() !== previous) LightboxView.LightboxFuture.push(previous);
+                            docView.focus(target, true, 0.9);
+                        } else {
+                            LightboxView.SetLightboxDoc(target);
+                        }
+                    })}
                 {this.navBtn(this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]), "chevron-right",
                     () => LightboxView.LightboxDoc && LightboxView.LightboxFuture.length ? "" : "none",
-                    action(e => {
+                    e => {
                         e.stopPropagation();
-                        LightboxView.LightboxDoc = LightboxView.LightboxFuture.pop();
-                    }))}
+                        const target = LightboxView.LightboxFuture.pop();
+                        const docView = target && DocumentManager.Instance.getLightboxDocumentView(target);
+                        if (docView && target) {
+                            docView.focus(target, true, 0.9);
+                            if (LightboxView.LightboxHistory.lastElement() !== target) LightboxView.LightboxHistory.push(target);
+                        } else {
+                            LightboxView.SetLightboxDoc(target);
+                        }
+                    })}
 
             </div>;
     }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ca07fa879..314922df8 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -16,7 +16,7 @@ import { TraceMobx } from '../../fields/util';
 import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
-import { Docs } from '../documents/Documents';
+import { Docs, DocUtils } from '../documents/Documents';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { DocumentManager } from '../util/DocumentManager';
 import { GroupManager } from '../util/GroupManager';
@@ -47,7 +47,7 @@ import { LinkMenu } from './linking/LinkMenu';
 import "./MainView.scss";
 import { AudioBox } from './nodes/AudioBox';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
-import { DocumentView, DocumentViewProps } from './nodes/DocumentView';
+import { DocumentView, DocumentViewProps, DocAfterFocusFunc } from './nodes/DocumentView';
 import { FieldViewProps } from './nodes/FieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
@@ -257,7 +257,7 @@ export class MainView extends React.Component {
             ScreenToLocalTransform={Transform.Identity}
             PanelWidth={this.getPWidth}
             PanelHeight={this.getPHeight}
-            focus={emptyFunction}
+            focus={DocUtils.DefaultFocus}
             parentActive={returnTrue}
             whenActiveChanged={emptyFunction}
             bringToFront={emptyFunction}
@@ -350,7 +350,7 @@ export class MainView extends React.Component {
                         PanelHeight={this.getContentsHeight}
                         renderDepth={0}
                         scriptContext={CollectionDockingView.Instance.props.Document}
-                        focus={emptyFunction}
+                        focus={DocUtils.DefaultFocus}
                         parentActive={returnTrue}
                         whenActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
@@ -380,7 +380,7 @@ export class MainView extends React.Component {
                 PanelHeight={this.getContentsHeight}
                 renderDepth={0}
                 docViewPath={returnEmptyDoclist}
-                focus={emptyFunction}
+                focus={DocUtils.DefaultFocus}
                 styleProvider={DefaultStyleProvider}
                 layerProvider={undefined}
                 parentActive={returnTrue}
@@ -501,7 +501,7 @@ export class MainView extends React.Component {
                     PanelWidth={this.flyoutWidthFunc}
                     PanelHeight={this.getContentsHeight}
                     renderDepth={0}
-                    focus={emptyFunction}
+                    focus={DocUtils.DefaultFocus}
                     whenActiveChanged={emptyFunction}
                     docFilters={returnEmptyFilter}
                     docRangeFilters={returnEmptyFilter}
@@ -563,7 +563,7 @@ export class MainView extends React.Component {
                 PanelWidth={this.getPWidth}
                 PanelHeight={this.getPHeight}
                 renderDepth={0}
-                focus={emptyFunction}
+                focus={DocUtils.DefaultFocus}
                 docViewPath={returnEmptyDoclist}
                 parentActive={returnFalse}
                 whenActiveChanged={emptyFunction}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 64f907f4c..5696b16e9 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -15,6 +15,7 @@ import { DocumentView } from "./nodes/DocumentView";
 import './OverlayView.scss';
 import { ScriptingRepl } from './ScriptingRepl';
 import { DefaultStyleProvider } from "./StyleProvider";
+import { DocUtils } from "../documents/Documents";
 
 export type OverlayDisposer = () => void;
 
@@ -191,7 +192,7 @@ export class OverlayView extends React.Component {
                     renderDepth={1}
                     parentActive={returnTrue}
                     whenActiveChanged={emptyFunction}
-                    focus={emptyFunction}
+                    focus={DocUtils.DefaultFocus}
                     styleProvider={DefaultStyleProvider}
                     layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index d6e4b01c4..5fa988f06 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -489,13 +489,12 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
     @computed get lightboxButton() {
         const targetDoc = this.selectedDoc;
         return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Lightbox of Documents"}</div>} placement="top">
-            <button className="antimodeMenu-button" onPointerDown={action(() => {
+            <button className="antimodeMenu-button" onPointerDown={() => {
                 const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
                 if (docs.length) {
-                    LightboxView.LightboxDoc = docs[0];
-                    LightboxView.LightboxFuture = docs.slice(1);
+                    LightboxView.SetLightboxDoc(targetDoc, docs);
                 }
-            })}>
+            }}>
                 <FontAwesomeIcon className="documentdecorations-icon" icon="desktop" size="lg" />
             </button>
         </Tooltip>;
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 66064e354..d2ed5427b 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -25,6 +25,7 @@ import { DefaultStyleProvider } from "../StyleProvider";
 import "./CollectionSchemaView.scss";
 import { CollectionSubView } from "./CollectionSubView";
 import { SchemaTable } from "./SchemaTable";
+import { DocUtils } from "../../documents/Documents";
 // bcz: need to add drag and drop of rows and columns.  This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
 
 export enum ColumnType {
@@ -404,7 +405,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
                     fitContentsToDoc={true}
                     freezeDimensions={true}
                     dontCenter={"y"}
-                    focus={emptyFunction}
+                    focus={DocUtils.DefaultFocus}
                     renderDepth={this.props.renderDepth}
                     rootSelected={this.rootSelected}
                     PanelWidth={this.previewWidth}
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index d4b4cf333..53801eef1 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -16,7 +16,7 @@ import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
 import { ImageField } from "../../../fields/URLField";
 import { GetEffectiveAcl } from "../../../fields/util";
 import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse } from "../../../Utils";
-import { Docs, DocumentOptions } from "../../documents/Documents";
+import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CompileScript, Transformer, ts } from "../../util/Scripting";
 import { Transform } from "../../util/Transform";
@@ -575,7 +575,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         layerProvider={undefined}
                         docViewPath={returnEmptyDoclist}
                         freezeDimensions={true}
-                        focus={emptyFunction}
+                        focus={DocUtils.DefaultFocus}
                         renderDepth={this.props.renderDepth}
                         rootSelected={() => false}
                         PanelWidth={() => 150}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index e1e1c8656..0fb140231 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -34,6 +34,7 @@ import { CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
 import React = require("react");
 import Color = require('color');
+import { DocUtils } from '../../documents/Documents';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -278,7 +279,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
             case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
-            case "lightbox": return runInAction(() => LightboxView.LightboxDoc = doc) ? true : false;
+            case "lightbox": return LightboxView.SetLightboxDoc(doc);
             case "inPlace":
             case "add":
             default:
@@ -333,7 +334,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     ScreenToLocalTransform={Transform.Identity}
                     renderDepth={0}
                     whenActiveChanged={emptyFunction}
-                    focus={emptyFunction}
+                    focus={DocUtils.DefaultFocus}
                     styleProvider={TabDocView.miniStyleProvider}
                     layerProvider={undefined}
                     addDocTab={this.addDocTab}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c18ef7a3b..74175c0b2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -963,7 +963,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1] };
 
         if (scale) {
-            this.Document[this.scaleFieldKey] = scale * Math.min(this.props.PanelWidth() / Math.abs(pt2[0] - pt[0])), this.props.PanelHeight() / Math.abs(pt2[1] - pt[1]);
+            this.Document[this.scaleFieldKey] = scale * Math.min(this.props.PanelWidth() / Math.abs(pt2[0] - pt[0]), this.props.PanelHeight() / Math.abs(pt2[1] - pt[1]));
             return { px: (bounds.left + bounds.right) / 2, py: (bounds.top + bounds.bot) / 2 };
         } else {
             const cx = NumCast(this.layoutDoc._panX);
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index e24a671d0..b681054fc 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -356,7 +356,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
             renderDepth={this.props.renderDepth + 1}
             startTag={"audioStart"}
             endTag={"audioEnd"}
-            focus={emptyFunction}
+            focus={DocUtils.DefaultFocus}
             bringToFront={emptyFunction}
             CollectionView={undefined}
             duration={this.duration}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 4b4720d58..7a8eb5628 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -166,9 +166,9 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
         return { props: list };
     }
 
-    componentWillUpdate(oldProps: any, newState: any) {
-        // console.log("willupdate", oldProps, this.props); // bcz: if you get a message saying something invalidated because reactive props changed, then this method allows you to figure out which prop changed
-    }
+    // componentWillUpdate(oldProps: any, newState: any) {
+    //     // console.log("willupdate", oldProps, this.props); // bcz: if you get a message saying something invalidated because reactive props changed, then this method allows you to figure out which prop changed
+    // }
 
     @computed get renderData() {
         TraceMobx();
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 150a549cb..488ce493c 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -7,7 +7,7 @@ import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"
 import { NumCast, StrCast } from "../../../fields/Types";
 import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, setupMoveUpEvents, Utils } from "../../../Utils";
 import { DocServer } from '../../DocServer';
-import { Docs } from "../../documents/Documents";
+import { Docs, DocUtils } from "../../documents/Documents";
 import { LinkManager } from '../../util/LinkManager';
 import { Transform } from "../../util/Transform";
 import { undoBatch } from '../../util/UndoManager';
@@ -162,7 +162,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             renderDepth={-1}
                             PanelWidth={this.width}
                             PanelHeight={this.height}
-                            focus={emptyFunction}
+                            focus={DocUtils.DefaultFocus}
                             whenActiveChanged={returnFalse}
                             bringToFront={returnFalse}
                             NativeWidth={Doc.NativeWidth(this._targetDoc) ? () => Doc.NativeWidth(this._targetDoc) : undefined}
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 589a1c2ae..d736dc583 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -422,7 +422,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             this.layoutDoc.presCollection = targetDoc;
             // this still needs some fixing
             setTimeout(resetSelection, 500);
-            doc !== targetDoc && setTimeout(() => finished?.(), 100); /// give it some time to create the targetDoc if we're opening up its context
+            if (doc !== targetDoc) {
+                setTimeout(() => finished?.(), 100); /// give it some time to create the targetDoc if we're opening up its context
+            } else {
+                finished?.();
+            }
         };
         // If openDocument is selected then it should open the document for the user
         if (activeItem.openDocument) {
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index c0247c226..a99853aac 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -497,7 +497,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
                 startTag={"videoStart"}
                 endTag={"videoEnd"}
                 fieldKeySuffix={"-timeline"}
-                focus={emptyFunction}
+                focus={DocUtils.DefaultFocus}
                 bringToFront={emptyFunction}
                 CollectionView={undefined}
                 duration={this.duration}
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 01a55660a..c794a4132 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -22,6 +22,7 @@ import { PresBox, PresColor, PresMovement } from "../nodes/PresBox";
 import { StyleProp } from "../StyleProvider";
 import "./PresElementBox.scss";
 import React = require("react");
+import { DocUtils } from "../../documents/Documents";
 
 export const presSchema = createSchema({
     presentationTargetDoc: Doc,
@@ -105,7 +106,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
                     parentActive={this.props.active}
                     moveDocument={this.props.moveDocument!}
                     renderDepth={this.props.renderDepth + 1}
-                    focus={emptyFunction}
+                    focus={DocUtils.DefaultFocus}
                     whenActiveChanged={returnFalse}
                     bringToFront={returnFalse}
                     docFilters={this.props.docFilters}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 058c61a6d..da3dbecbe 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -13,7 +13,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from "react";
-import { Docs, DocumentOptions } from '../client/documents/Documents';
+import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents';
 import { DocumentType } from "../client/documents/DocumentTypes";
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { Scripting } from '../client/util/Scripting';
@@ -210,7 +210,7 @@ export class MobileInterface extends React.Component {
                     PanelWidth={this.returnWidth}
                     PanelHeight={this.returnHeight}
                     renderDepth={0}
-                    focus={emptyFunction}
+                    focus={DocUtils.DefaultFocus}
                     styleProvider={this.whitebackground}
                     layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
-- 
cgit v1.2.3-70-g09d2


From 926665e2367ff6a201a48618df6f7985ddbcb4b0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 12 Feb 2021 16:45:06 -0500
Subject: fixed previewing/scrollingto targets in web boxes and PDFs.  fixed
 following link to textanchor when rtf doc is not displayed.

---
 src/client/util/DocumentManager.ts        | 20 +++++++++-----------
 src/client/views/nodes/LinkDocPreview.tsx |  4 ++--
 src/client/views/nodes/PDFBox.tsx         | 14 ++++++++++++--
 src/client/views/nodes/WebBox.tsx         |  5 ++++-
 src/client/views/pdf/PDFViewer.tsx        | 18 ++++++++++++++----
 5 files changed, 41 insertions(+), 20 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 5b4917a30..d2251583c 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -141,7 +141,7 @@ export class DocumentManager {
         finished?: () => void,
     ): Promise<void> => {
         const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
-        const docView = getFirstDocView(targetDoc, originatingDoc);
+        let docView = getFirstDocView(targetDoc, originatingDoc);
         const highlight = () => {
             const finalDocView = getFirstDocView(targetDoc);
             finalDocView && Doc.linkFollowHighlight(finalDocView.rootDoc);
@@ -159,19 +159,17 @@ export class DocumentManager {
             finished?.();
             return false;
         };
-        let annotatedDoc = Cast(targetDoc.annotationOn, Doc, null);
+        const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null);
+        const rtfView = annotatedDoc && getFirstDocView(annotatedDoc);
         const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
         const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined;
         const targetDocContext = contextDoc || annotatedDoc;
         const targetDocContextView = targetDocContext && getFirstDocView(targetDocContext);
-        if (!docView && annotatedDoc && annotatedDoc !== originatingDoc?.context && targetDoc.type === DocumentType.TEXTANCHOR) {
-            const first = getFirstDocView(annotatedDoc);
-            if (first) {
-                annotatedDoc = first.rootDoc;
-                first.focus(targetDoc, false);
-            }
-        } else if (docView && (targetDocContextView || !targetDocContext)) {  // we have a docView already and aren't forced to create a new one ... just focus on the document.  TODO move into view if necessary otherwise just highlight?      
-            docView.props.focus(docView.rootDoc, willZoom, undefined, (didFocus: boolean) =>
+        if (!docView && targetDoc.type === DocumentType.TEXTANCHOR && rtfView) {
+            rtfView.focus(targetDoc, false);
+        }
+        else if (docView) {
+            docView.props.focus(targetDoc, willZoom, undefined, (didFocus: boolean) =>
                 new Promise<boolean>(res => {
                     focusAndFinish(didFocus);
                     res();
@@ -205,7 +203,7 @@ export class DocumentManager {
                                 // we didn't find the target, so it must have moved out of the context.  Go back to just creating it.
                                 if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.rootDoc);
                                 if (targetDoc.layout) { // there will no layout for a TEXTANCHOR type document
-                                    Doc.SetInPlace(targetDoc, "annotationOn", undefined, false);
+                                    // Doc.SetInPlace(targetDoc, "annotationOn", undefined, false);
                                     createViewFunc(Doc.BrushDoc(targetDoc), finished); //  create a new view of the target
                                 }
                             } else {
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 488ce493c..7bd5d14d2 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -3,7 +3,7 @@ import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from 'mobx';
 import { observer } from "mobx-react";
 import wiki from "wikijs";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocCastAsync } from "../../../fields/Doc";
 import { NumCast, StrCast } from "../../../fields/Types";
 import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, setupMoveUpEvents, Utils } from "../../../Utils";
 import { DocServer } from '../../DocServer';
@@ -45,7 +45,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         if (anchor1 && anchor2) {
             linkTarget = Doc.AreProtosEqual(anchor1, this._linkSrc) || Doc.AreProtosEqual(anchor1?.annotationOn as Doc, this._linkSrc) ? anchor2 : anchor1;
         }
-        this._targetDoc = linkTarget?.annotationOn as Doc ?? linkTarget;
+        linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => this._targetDoc = anno));
         this._toolTipText = "";
     }
     componentDidUpdate(props: any) {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index c20d958ff..b941c07f6 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -80,7 +80,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
         }
     }
 
-    scrollFocus = (doc: Doc, smooth: boolean) => this._pdfViewer?.scrollFocus(doc, smooth);
+    initialScrollTarget: Opt<Doc>;
+    scrollFocus = (doc: Doc, smooth: boolean) => {
+        this.initialScrollTarget = doc;
+        return this._pdfViewer?.scrollFocus(doc, smooth);
+    }
     getAnchor = () => this.rootDoc;
     componentWillUnmount() { this._selectReactionDisposer?.(); }
     componentDidMount() {
@@ -128,7 +132,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
     });
 
     whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive));
-    setPdfViewer = (pdfViewer: PDFViewer) => { this._pdfViewer = pdfViewer; };
+    setPdfViewer = (pdfViewer: PDFViewer) => {
+        this._pdfViewer = pdfViewer;
+        if (this.initialScrollTarget) {
+            this.scrollFocus(this.initialScrollTarget, false);
+            this.initialScrollTarget = undefined;
+        }
+    };
     searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
 
     settingsPanel() {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index abda0ed3a..663a8b8e4 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -30,6 +30,7 @@ import { DocAfterFocusFunc } from "./DocumentView";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./WebBox.scss";
 import React = require("react");
+import { LinkDocPreview } from "./LinkDocPreview";
 const htmlToText = require("html-to-text");
 
 type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -119,7 +120,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
                 this._initialScroll !== undefined && (this._initialScroll = scrollTo);
                 this._ignoreScroll = true;
                 this.goTo(scrollTo, focusSpeed = smooth ? 500 : 0);
-                this.layoutDoc._scrollTop = scrollTo;
+                if (!LinkDocPreview.LinkInfo) {
+                    this.layoutDoc._scrollTop = scrollTo;
+                }
                 this._ignoreScroll = false;
             }
         } else {
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 17936847e..7687690b2 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -28,6 +28,7 @@ import { AnchorMenu } from "./AnchorMenu";
 import "./PDFViewer.scss";
 const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
 import React = require("react");
+import { LinkDocPreview } from "../nodes/LinkDocPreview";
 const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
 const pdfjsLib = require("pdfjs-dist");
 const _global = (window /* browser */ || global /* node */) as any;
@@ -87,6 +88,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     private _lastSearch = false;
     private _viewerIsSetup = false;
     private _ignoreScroll = false;
+    private _initialScroll: Opt<number>;
     private _smoothScrolling = true;
 
     @computed get allAnnotations() {
@@ -182,14 +184,16 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     scrollFocus = (doc: Doc, smooth: boolean) => {
         const mainCont = this._mainCont.current;
         let focusSpeed: Opt<number>;
-        if (doc !== this.rootDoc && mainCont) {
+        if (doc !== this.rootDoc && mainCont && this._pdfViewer) {
             const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1));
             if (scrollTo !== undefined) {
                 focusSpeed = 500;
 
                 if (smooth) smoothScroll(focusSpeed, mainCont, scrollTo);
-                else mainCont.scrollTop = scrollTo;
+                else this._mainCont.current?.scrollTo({ top: Math.abs(scrollTo || 0) });
             }
+        } else {
+            this._initialScroll = NumCast(doc.y);
         }
         return focusSpeed;
     }
@@ -222,7 +226,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
             this.gotoPage(this.Document._curPage || 1);
         }
         document.removeEventListener("pagesinit", this.pagesinit);
-        var quickScroll: string | undefined = "";
+        var quickScroll: string | undefined = this._initialScroll ? this._initialScroll.toString() : "";
         this._disposers.scroll = reaction(
             () => NumCast(this.Document._scrollTop),
             (pos) => {
@@ -249,6 +253,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
             { fireImmediately: true }
         );
         quickScroll = undefined;
+        if (this._initialScroll !== undefined && this._mainCont.current) {
+            this._mainCont.current?.scrollTo({ top: Math.abs(this._initialScroll || 0) });
+            this._initialScroll = undefined;
+        }
     }
 
     createPdfViewer() {
@@ -313,7 +321,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     onScroll = (e: React.UIEvent<HTMLElement>) => {
         if (this._mainCont.current && !this._smoothScrolling) {
             this._ignoreScroll = true;
-            this.layoutDoc._scrollTop = this._mainCont.current.scrollTop;
+            if (!LinkDocPreview.LinkInfo) {
+                this.layoutDoc._scrollTop = this._mainCont.current.scrollTop;
+            }
             this._ignoreScroll = false;
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 3dae4d3d8ac10f6f3c9f937282dbfa5882584aad Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 16 Feb 2021 14:39:41 -0500
Subject: added getAnchor() to freeformView to capture viewtype & filters. 
 fixed setPanIntoView bug.  added setViewSpec componentView method to allow
 anchor to set view specs.

---
 .../views/collections/CollectionSchemaCells.tsx    |  2 +-
 .../views/collections/CollectionSchemaView.tsx     |  4 +-
 .../views/collections/CollectionTimeView.tsx       | 65 ++++++++++++----------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 38 ++++++++++++-
 src/client/views/nodes/DocumentView.tsx            | 17 ++++--
 src/client/views/nodes/LinkDocPreview.tsx          |  6 +-
 6 files changed, 91 insertions(+), 41 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 904b4c761..2e6186680 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -216,7 +216,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
         const placeholder = type === "number" ? "0" : contents === "" ? "--" : "undefined";
         return (
             <div className="collectionSchemaView-cellContainer" style={{ cursor: field instanceof Doc ? "grab" : "auto" }}
-                ref={dragRef} onPointerDown={this.onPointerDown} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave}>
+                ref={dragRef} onPointerDown={this.onPointerDown} onClick={action(e => this._isEditing = true)} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave}>
                 <div className={className} ref={this._focusRef} tabIndex={-1}>
                     <div className="collectionSchemaView-cellContents" ref={type === undefined || type === "document" ? this.dropRef : null}>
                         {!this.props.Document._searchDoc ?
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 528cdc8b7..504cf32a0 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -103,7 +103,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
     @action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing;
 
     @undoBatch
-    setColumnType = (columnField: SchemaHeaderField, type: ColumnType): void => {
+    setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => {
         this._openTypes = false;
         if (columnTypes.get(columnField.heading)) return;
 
@@ -114,7 +114,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             columns[index] = columnField;
             this.columns = columns;
         }
-    }
+    });
 
     @undoBatch
     setColumnColor = (columnField: SchemaHeaderField, color: string): void => {
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 8c3e53dfd..426eced1d 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,16 +1,21 @@
+import { toUpper } from "lodash";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, Opt, DocCastAsync, StrListCast } from "../../../fields/Doc";
+import { Doc, DocCastAsync, Opt, StrListCast } from "../../../fields/Doc";
 import { List } from "../../../fields/List";
 import { ObjectField } from "../../../fields/ObjectField";
 import { RichTextField } from "../../../fields/RichTextField";
+import { listSpec } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { NumCast, StrCast, BoolCast, Cast } from "../../../fields/Types";
-import { emptyFunction, returnFalse, setupMoveUpEvents, returnTrue } from "../../../Utils";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils";
+import { Docs, DocUtils } from "../../documents/Documents";
+import { DocumentManager } from "../../util/DocumentManager";
 import { Scripting } from "../../util/Scripting";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { EditableView } from "../EditableView";
+import { ViewSpecPrefix } from "../nodes/DocumentView";
 import { ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngines";
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { CollectionSubView } from "./CollectionSubView";
@@ -19,12 +24,6 @@ const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 import React = require("react");
-import { DocUtils, Docs } from "../../documents/Documents";
-import { toUpper } from "lodash";
-import { DocumentManager } from "../../util/DocumentManager";
-import { listSpec } from "../../../fields/Schema";
-import { LinkManager } from "../../util/LinkManager";
-import { LinkDocPreview } from "../nodes/LinkDocPreview";
 
 @observer
 export class CollectionTimeView extends CollectionSubView(doc => doc) {
@@ -33,6 +32,9 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     @observable _collapsed: boolean = false;
     @observable _childClickedScript: Opt<ScriptField>;
     @observable _viewDefDivClick: Opt<ScriptField>;
+    @observable _focusDocFilters: Opt<string[]>; // fields that get overridden by a focus anchor
+    @observable _focusPivotField: Opt<string>;
+    @observable _focusRangeFilters: Opt<string[]>;
 
     getAnchor = () => {
         const anchor = Docs.Create.TextanchorDocument({
@@ -41,9 +43,15 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
             hideLinkButton: true,
             annotationOn: this.rootDoc
         });
-        Doc.GetProto(anchor).pivotField = this.pivotField;
-        Doc.GetProto(anchor).docFilters = ObjectField.MakeCopy(this.layoutDoc._docFilters as ObjectField);
-        Doc.GetProto(anchor)._docRangeFilters = ObjectField.MakeCopy(this.layoutDoc._docRangeFilters as ObjectField);
+
+        // save view spec information for anchor
+        const proto = Doc.GetProto(anchor);
+        proto.pivotField = this.pivotField;
+        proto.docFilters = ObjectField.MakeCopy(this.layoutDoc._docFilters as ObjectField) || new List<string>([]);
+        proto.docRangeFilters = ObjectField.MakeCopy(this.layoutDoc._docRangeFilters as ObjectField) || new List<string>([]);
+        proto[ViewSpecPrefix + "_viewType"] = this.layoutDoc._viewType;
+
+        // store anchor in annotations list of document (not technically needed since these anchors are never drawn)
         if (Cast(this.dataDoc[this.props.fieldKey + "-annotations"], listSpec(Doc), null) !== undefined) {
             Cast(this.dataDoc[this.props.fieldKey + "-annotations"], listSpec(Doc), []).push(anchor);
         } else {
@@ -63,26 +71,24 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         });
     }
 
-    @observable _scrollFilters: Opt<string[]>;
-    @observable _scrollPivotField: Opt<string>;
-    timeDocFilters = () => this._scrollFilters || ([] as string[]);
-    get pivotField() { return this._scrollPivotField || StrCast(this.layoutDoc._pivotField); }
+    get pivotField() { return this._focusPivotField || StrCast(this.layoutDoc._pivotField); }
     @action
-    scrollFocus = (anchor: Doc, smooth: boolean) => {
-        if (!LinkDocPreview.LinkInfo) {
-            if (anchor.pivotField !== undefined) {
-                this.layoutDoc._prevFilterIndex = 1;
-                this.layoutDoc._pivotField = StrCast(anchor.pivotField);
-                this.layoutDoc._docFilters = ObjectField.MakeCopy(Doc.GetProto(anchor).docFilters as ObjectField);
-                this.layoutDoc._docRangeFilters = ObjectField.MakeCopy(Doc.GetProto(anchor).docRangeFilters as ObjectField);
-            }
-        } else {
-            this._scrollPivotField = StrCast(anchor.pivotField);
-            this._scrollFilters = StrListCast(Doc.GetProto(anchor).docFilters);
+    setViewSpec = (anchor: Doc, preview: boolean) => {
+        if (preview) {   // if in preview, then override document's fields with view spec
+            this._focusPivotField = StrCast(anchor.pivotField);
+            this._focusDocFilters = StrListCast(anchor.docFilters);
+            this._focusRangeFilters = StrListCast(anchor.docRangeFilters);
+        } else if (anchor.pivotField !== undefined) {  // otherwise set document's fields based on anchor view spec
+            this.layoutDoc._prevFilterIndex = 1;
+            this.layoutDoc._pivotField = StrCast(anchor.pivotField);
+            this.layoutDoc._docFilters = new List<string>(this.pivotDocFilters());
+            this.layoutDoc._docRangeFilters = new List<string>(this.pivotDocRangeFilters());
         }
         return 0;
     }
 
+    pivotDocFilters = () => this._focusDocFilters || this.props.docFilters();
+    pivotDocRangeFilters = () => this._focusRangeFilters || this.props.docRangeFilters();
     layoutEngine = () => this._layoutEngine;
     toggleVisibility = action(() => this._collapsed = !this._collapsed);
 
@@ -136,9 +142,10 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
         return <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this.props.active() ? undefined : "none" }}
             onClick={this.contentsDown}>
             <CollectionFreeFormView {...this.props}
-                engineProps={{ pivotField: this.pivotField, docFilters: this.docFilters }}
+                engineProps={{ pivotField: this.pivotField, docFilters: this.docFilters, docRangeFilters: this.docRangeFilters }}
                 fitContentsToDoc={returnTrue}
-                docFilters={this.timeDocFilters}
+                docFilters={this.pivotDocFilters}
+                docRangeFilters={this.pivotDocRangeFilters}
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
                 childFreezeDimensions={true}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b2ee6bbd0..c54596a9e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -6,6 +6,7 @@ import { collectionSchema, documentSchema } from "../../../../fields/documentSch
 import { Id } from "../../../../fields/FieldSymbols";
 import { InkData, InkField, InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
+import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
 import { createSchema, listSpec, makeInterface } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
@@ -35,7 +36,7 @@ import { DocumentDecorations } from "../../DocumentDecorations";
 import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkingStroke";
 import { LightboxView } from "../../LightboxView";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
-import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../../nodes/DocumentView";
+import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
 import { FieldViewProps } from "../../nodes/FieldView";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { pageSchema } from "../../nodes/ImageBox";
@@ -114,6 +115,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @observable _clusterSets: (Doc[])[] = [];
     @observable _timelineRef = React.createRef<Timeline>();
     @observable _marqueeRef = React.createRef<HTMLDivElement>();
+    @observable _focusFilters: Opt<string[]>; // fields that get overriden by focus anchor
 
     @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.active() || this.props.active()); }
     @computed get fitToContentVals() {
@@ -985,7 +987,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 panY: (bounds.top + bounds.bot) / 2,
                 scale: Math.min(maxZoom, scale * Math.min(this.props.PanelWidth() / Math.abs(pt2[0] - pt[0]), this.props.PanelHeight() / Math.abs(pt2[1] - pt[1])))
             };
-        } else if ((screen.right - screen.left) < (bounds.right - bounds.left)) {
+        } else if ((screen.right - screen.left) < (bounds.right - bounds.left) ||
+            (screen.bot - screen.top) < (bounds.bot - bounds.top)) {
             return {
                 panX: (bounds.left + bounds.right) / 2,
                 panY: (bounds.top + bounds.bot) / 2,
@@ -1023,7 +1026,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             PanelWidth: childLayout[WidthSym],
             PanelHeight: childLayout[HeightSym],
-            docFilters: this.docFilters,
+            docFilters: this.freeformDocFilters,
             docRangeFilters: this.docRangeFilters,
             searchFilterDocs: this.searchFilterDocs,
             focus: this.focusDocument,
@@ -1202,6 +1205,35 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
         return elements;
     }
+
+    freeformDocFilters = () => this._focusFilters || this.docFilters();
+    @action
+    setViewSpec = (anchor: Doc, preview: boolean) => {
+        if (preview) {
+            this._focusFilters = StrListCast(Doc.GetProto(anchor).docFilters);
+        } else if (anchor.pivotField !== undefined) {
+            this.layoutDoc._docFilters = ObjectField.MakeCopy(Doc.GetProto(anchor).docFilters as ObjectField);
+        }
+        return 0;
+    }
+
+    getAnchor = () => {
+        const anchor = Docs.Create.TextanchorDocument({
+            title: StrCast(this.layoutDoc._viewType),
+            useLinkSmallAnchor: true,
+            hideLinkButton: true,
+            annotationOn: this.rootDoc
+        });
+        const proto = Doc.GetProto(anchor);
+        proto[ViewSpecPrefix + "_viewType"] = this.layoutDoc._viewType;
+        proto[ViewSpecPrefix + "_docFilters"] = ObjectField.MakeCopy(this.layoutDoc.docFilters as ObjectField) || new List<string>([]);
+        if (Cast(this.dataDoc[this.props.fieldKey + "-annotations"], listSpec(Doc), null) !== undefined) {
+            Cast(this.dataDoc[this.props.fieldKey + "-annotations"], listSpec(Doc), []).push(anchor);
+        } else {
+            this.dataDoc[this.props.fieldKey + "-annotations"] = new List<Doc>([anchor]);
+        }
+        return anchor;
+    }
     freeformData = (force?: boolean) => this.fitToContent || force ? this.fitToContentVals : undefined;
     @action
     componentDidMount() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4aa424af3..e52791901 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -45,6 +45,7 @@ import { LinkDocPreview } from "./LinkDocPreview";
 import { PresBox } from './PresBox';
 import { RadialMenu } from './RadialMenu';
 import React = require("react");
+import { ObjectField } from "../../../fields/ObjectField";
 const { Howl } = require('howler');
 
 interface Window {
@@ -56,12 +57,13 @@ declare class MediaRecorder {
     constructor(e: any);
 }
 
-
 export enum ViewAdjustment {
     resetView = 1,
     doNothing = 0
-
 }
+
+export const ViewSpecPrefix = "_VIEW";  // field prefix for anchor fields that are immediately copied over to the target document when link is followed.  Other anchor properties will be copied over in the specific setViewSpec() method on their view (which allows for seting preview values instead of writing to the document)
+
 export interface DocFocusOptions {
     originalTarget?: Doc; // set in JumpToDocument, used by TabDocView to determine whether to fit contents to tab
     willZoom?: boolean;   // determines whether to zoom in on target document
@@ -75,6 +77,7 @@ export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps | F
 export interface DocComponentView {
     getAnchor?: () => Doc;
     scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus
+    setViewSpec?: (anchor: Doc, preview: boolean) => void;
     back?: () => boolean;
     forward?: () => boolean;
     url?: () => string;
@@ -405,10 +408,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
     }
 
-    focus = (doc: Doc, options?: DocFocusOptions) => {
-        const focusSpeed = this._componentView?.scrollFocus?.(doc, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here      
+    focus = (anchor: Doc, options?: DocFocusOptions) => {
+        // copying over _VIEW fields immediately allows the view type to switch to create the right _componentView
+        Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]));
+        // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
+        setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
+        const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here      
         const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing;
-        this.props.focus(options?.docTransform ? doc : this.rootDoc, {
+        this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
             ...options, afterFocus: (didFocus: boolean) =>
                 new Promise<ViewAdjustment>(res => setTimeout(async () => res(endFocus ? await endFocus(didFocus) : ViewAdjustment.doNothing), focusSpeed ?? 0))
         });
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 7bd5d14d2..716810536 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -45,7 +45,11 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         if (anchor1 && anchor2) {
             linkTarget = Doc.AreProtosEqual(anchor1, this._linkSrc) || Doc.AreProtosEqual(anchor1?.annotationOn as Doc, this._linkSrc) ? anchor2 : anchor1;
         }
-        linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => this._targetDoc = anno));
+        if (linkTarget?.annotationOn) {
+            linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => this._targetDoc = anno));
+        } else {
+            this._targetDoc = linkTarget;
+        }
         this._toolTipText = "";
     }
     componentDidUpdate(props: any) {
-- 
cgit v1.2.3-70-g09d2

