aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/DocumentManager.ts36
-rw-r--r--src/client/util/LinkManager.ts73
-rw-r--r--src/client/views/MainView.tsx5
-rw-r--r--src/client/views/StyleProvider.tsx1
-rw-r--r--src/client/views/collections/CollectionMapView.tsx12
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx46
-rw-r--r--src/client/views/nodes/DocumentView.tsx44
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx30
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx18
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx2
-rw-r--r--src/client/views/pdf/Annotation.tsx12
12 files changed, 123 insertions, 161 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 1c81cf26c..1609e0af7 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -1,14 +1,12 @@
import { action, observable } from 'mobx';
import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc';
import { Id } from '../../fields/FieldSymbols';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { Cast, NumCast } from '../../fields/Types';
import { returnFalse } from '../../Utils';
import { DocumentType } from '../documents/DocumentTypes';
import { CollectionDockingView } from '../views/collections/CollectionDockingView';
import { CollectionView } from '../views/collections/CollectionView';
import { DocumentView } from '../views/nodes/DocumentView';
-import { FormattedTextBoxComment } from '../views/nodes/formattedText/FormattedTextBoxComment';
-import { LinkDocPreview } from '../views/nodes/LinkDocPreview';
import { LinkManager } from './LinkManager';
import { Scripting } from './Scripting';
@@ -223,37 +221,5 @@ export class DocumentManager {
}
}
- public async FollowLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
- LinkDocPreview.TargetDoc = undefined;
- FormattedTextBoxComment.linkDoc = undefined;
- 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
- const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor2
- const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
- const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
- const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
- const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs);
- const followLinks = linkDocList.length ? (doc.isPushpin ? linkDocList : [linkDocList[0]]) : [];
- followLinks.forEach(async linkDoc => {
- if (linkDoc) {
- const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 :
- (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc;
- const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") :
- 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) {
- const containerDoc = (await Cast(target.annotationOn, Doc)) || target;
- containerDoc._currentTimecode = targetTimecode;
- const targetContext = await target?.context as Doc;
- 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?.();
- }
- } else {
- finished?.();
- }
- });
- }
}
Scripting.addGlobal(function DocFocus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); }); \ No newline at end of file
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 38e81cf99..accf53676 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,7 +1,12 @@
+import { computedFn } from "mobx-utils";
import { Doc, DocListCast, Opt } from "../../fields/Doc";
-import { Cast, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, StrCast } from "../../fields/Types";
+import { DocFocusFunc, DocumentViewSharedProps } from "../views/nodes/DocumentView";
+import { FormattedTextBoxComment } from "../views/nodes/formattedText/FormattedTextBoxComment";
+import { LinkDocPreview } from "../views/nodes/LinkDocPreview";
+import { CreateViewFunc, DocumentManager } from "./DocumentManager";
import { SharingManager } from "./SharingManager";
-import { computedFn } from "mobx-utils";
+import { UndoManager } from "./UndoManager";
/*
* link doc:
@@ -89,4 +94,68 @@ export class LinkManager {
if (Doc.AreProtosEqual(anchor, a2.annotationOn as Doc)) return a1;
if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
}
+
+
+ // follows a link - if the target is on screen, it highlights/pans to it.
+ // if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place
+ // depending on the followLinkLocation property of the source (or the link itself as a fallback);
+ public static FollowLink = async (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean) => {
+ const batch = UndoManager.StartBatch("follow link click");
+ // open up target if it's not already in view ...
+ const createViewFunc = (doc: Doc, followLoc: string, finished: Opt<() => void>) => {
+ const targetFocusAfterDocFocus = () => {
+ const where = StrCast(sourceDoc.followLinkLocation) || followLoc;
+ const hackToCallFinishAfterFocus = () => {
+ finished && setTimeout(finished, 0); // finished() needs to be called right after hackToCallFinishAfterFocus(), but there's no callback for that so we use the hacky timeout.
+ return false; // we must return false here so that the zoom to the document is not reversed. If it weren't for needing to call finished(), we wouldn't need this function at all since not having it is equivalent to returning false
+ };
+ const addTab = docViewProps.addDocTab(doc, where);
+ addTab && setTimeout(() => {
+ const targDocView = DocumentManager.Instance.getFirstDocumentView(doc);
+ targDocView?.props.focus(doc, BoolCast(sourceDoc.followLinkZoom, false), undefined, hackToCallFinishAfterFocus);
+ }); // add the target and focus on it.
+ return where !== "inPlace" || addTab; // return true to reset the initial focus&zoom (return false for 'inPlace' since resetting the initial focus&zoom will negate the zoom into the target)
+ };
+ if (!sourceDoc.followLinkZoom) {
+ targetFocusAfterDocFocus();
+ } else {
+ // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target
+ docViewProps.focus(sourceDoc, BoolCast(sourceDoc.followLinkZoom, true), 1, targetFocusAfterDocFocus);
+ }
+ };
+ await LinkManager.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, false), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
+ }
+ public static async traverseLink(link: Opt<Doc>, doc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
+ LinkDocPreview.TargetDoc = undefined;
+ FormattedTextBoxComment.linkDoc = undefined;
+ 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
+ const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, doc)); // link docs where 'doc' is anchor2
+ const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
+ const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
+ const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
+ const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs);
+ const followLinks = linkDocList.length ? (doc.isPushpin ? linkDocList : [linkDocList[0]]) : [];
+ followLinks.forEach(async linkDoc => {
+ if (linkDoc) {
+ const target = (doc === linkDoc.anchor1 ? linkDoc.anchor2 : doc === linkDoc.anchor2 ? linkDoc.anchor1 :
+ (Doc.AreProtosEqual(doc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, doc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc;
+ const targetTimecode = (doc === linkDoc.anchor1 ? Cast(linkDoc.anchor2_timecode, "number") :
+ 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) {
+ const containerDoc = (await Cast(target.annotationOn, Doc)) || target;
+ containerDoc._currentTimecode = targetTimecode;
+ const targetContext = await target?.context as Doc;
+ 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?.();
+ }
+ } else {
+ finished?.();
+ }
+ });
+ }
+
} \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 23c65fa53..0390bdf52 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -578,9 +578,8 @@ export class MainView extends React.Component {
<CollectionMenu />
{LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
{DocumentLinksButton.EditLink ? <LinkMenu docView={DocumentLinksButton.EditLink} addDocTab={DocumentLinksButton.EditLink.props.addDocTab} changeFlyout={emptyFunction} /> : (null)}
- {LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} styleProvider={DefaultStyleProvider}
- linkDoc={LinkDocPreview.LinkInfo.linkDoc} linkSrc={LinkDocPreview.LinkInfo.linkSrc} href={LinkDocPreview.LinkInfo.href}
- addDocTab={LinkDocPreview.LinkInfo.addDocTab} /> : (null)}
+ {LinkDocPreview.LinkInfo ? <LinkDocPreview location={LinkDocPreview.LinkInfo.Location} docprops={LinkDocPreview.LinkInfo.docprops}
+ linkDoc={LinkDocPreview.LinkInfo.linkDoc} linkSrc={LinkDocPreview.LinkInfo.linkSrc} href={LinkDocPreview.LinkInfo.href} /> : (null)}
<GestureOverlay >
{this.mainContent}
</GestureOverlay>
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 83192164f..dd406abed 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -71,7 +71,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
switch (property.split(":")[0]) {
case StyleProp.DocContents: return undefined;
- case StyleProp.LinkSource: return isAnchor && docProps?.Document; // pass the LinkSource to the LinkAnchorBox
case StyleProp.WidgetColor: return darkScheme() ? "lightgrey" : "dimgrey";
case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null));
case StyleProp.HideLinkButton: return isAnchor || props?.dontRegisterView || (!selected && (doc?.isLinkButton || doc?.hideLinkButton));
diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx
index 1af1a05aa..92288c1f2 100644
--- a/src/client/views/collections/CollectionMapView.tsx
+++ b/src/client/views/collections/CollectionMapView.tsx
@@ -1,16 +1,16 @@
-import { GoogleApiWrapper, Map as GeoMap, IMapProps, Marker } from "google-maps-react";
+import { GoogleApiWrapper, IMapProps, Map as GeoMap, Marker } from "google-maps-react";
+import { action, computed, Lambda, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../fields/Doc";
+import { Doc, DocListCast, Field, FieldResult, Opt } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { makeInterface } from "../../../fields/Schema";
import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
+import { LinkManager } from "../../util/LinkManager";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
import "./CollectionMapView.scss";
import { CollectionSubView } from "./CollectionSubView";
import React = require("react");
-import { DocumentManager } from "../../util/DocumentManager";
-import { UndoManager, undoBatch } from "../../util/UndoManager";
-import { computed, runInAction, Lambda, action } from "mobx";
import requestPromise = require("request-promise");
type MapSchema = makeInterface<[typeof documentSchema]>;
@@ -88,7 +88,7 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap
zoom && (this.layoutDoc[`${fieldKey}-mapCenter-zoom`] = zoom);
});
if (layout.isLinkButton && DocListCast(layout.links).length) {
- await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => {
+ await LinkManager.traverseLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => {
this.props.addDocTab(doc, where);
finished?.();
}, false, this.props.ContainingCollectionDoc, batch.end, undefined);
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 8e0004c87..50cb064b9 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -13,9 +13,8 @@ import { DragManager } from '../../util/DragManager';
import { Hypothesis } from '../../util/HypothesisUtils';
import { LinkManager } from '../../util/LinkManager';
import { undoBatch } from '../../util/UndoManager';
-import { ContextMenu } from '../ContextMenu';
import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewSharedProps } from '../nodes/DocumentView';
import { LinkDocPreview } from '../nodes/LinkDocPreview';
import './LinkMenuItem.scss';
import React = require("react");
@@ -28,7 +27,7 @@ interface LinkMenuItemProps {
sourceDoc: Doc;
destinationDoc: Doc;
showEditor: (linkDoc: Doc) => void;
- addDocTab: (document: Doc, where: string) => boolean;
+ docprops: DocumentViewSharedProps,
menuRef: React.Ref<HTMLDivElement>;
}
@@ -110,7 +109,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
onLinkButtonUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onLinkButtonMoved);
document.removeEventListener("pointerup", this.onLinkButtonUp);
- DocumentView.followLinkClick(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
+ LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
e.stopPropagation();
}
@@ -126,41 +125,6 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
e.stopPropagation();
}
- @action
- onContextMenu = (e: React.MouseEvent) => {
- DocumentLinksButton.EditLink = undefined;
- LinkDocPreview.LinkInfo = undefined;
- e.preventDefault();
- ContextMenu.Instance.addItem({ description: "Follow Default Link", event: () => LinkMenuItem.followDefault(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc, this.props.addDocTab), icon: "arrow-right" });
- ContextMenu.Instance.displayMenu(e.clientX, e.clientY);
- }
-
-
- @action
- public static followDefault(linkDoc: Doc, sourceDoc: Doc, destinationDoc: Doc, addDocTab: (doc: Doc, where: string) => void) {
- DocumentLinksButton.EditLink = undefined;
- LinkDocPreview.LinkInfo = undefined;
-
- if (linkDoc.followLinkLocation === "openExternal" && destinationDoc.type === DocumentType.WEB) {
- window.open(`${StrCast(linkDoc.annotationUri)}#annotations:${StrCast(linkDoc.annotationId)}`, '_blank');
- }
-
- if (linkDoc.followLinkLocation && linkDoc.followLinkLocation !== "default") {
- const annotationOn = destinationDoc.annotationOn as Doc;
- addDocTab(annotationOn instanceof Doc ? annotationOn : destinationDoc, StrCast(linkDoc.followLinkLocation));
- if (annotationOn) {
- setTimeout(() => {
- const dv = DocumentManager.Instance.getFirstDocumentView(destinationDoc);
- dv?.props.focus(destinationDoc, false);
- });
- }
- } else {
- DocumentManager.Instance.FollowLink(linkDoc, sourceDoc, addDocTab, false);
- }
-
- linkDoc.linksToAnnotation && Hypothesis.scrollToAnnotation(StrCast(linkDoc.annotationId), destinationDoc);
- }
-
@undoBatch
@action
deleteLink = (): void => {
@@ -215,7 +179,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
<div ref={this._drag} className="linkMenu-name" //title="drag to view target. click to customize."
onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
onPointerEnter={action(e => this.props.linkDoc && (LinkDocPreview.LinkInfo = {
- addDocTab: this.props.addDocTab,
+ docprops: this.props.docprops,
linkSrc: this.props.sourceDoc,
linkDoc: this.props.linkDoc,
Location: [e.clientX, e.clientY + 20]
@@ -260,8 +224,6 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
<div className="button" onPointerDown={this.deleteLink}>
<FontAwesomeIcon className="fa-icon" icon="trash" size="sm" /></div>
</Tooltip>
- {/* <div title="Follow link" className="button" onPointerDown={this.followDefault} onContextMenu={this.onContextMenu}>
- <FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" /></div> */}
</div>
</div>
</div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 56982bd89..15ba62e23 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -425,7 +425,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
} else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself
this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "add:right");
} else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
- this.allLinks.length && DocumentView.followLinkClick(undefined, this.props.Document, this.props, e.altKey);
+ this.allLinks.length && LinkManager.FollowLink(undefined, this.props.Document, this.props, e.altKey);
} else {
if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
@@ -785,7 +785,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
hideLinkAnchor = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && (doc.hidden = true), true)
anchorPanelWidth = () => this.props.PanelWidth() || 1;
anchorPanelHeight = () => this.props.PanelHeight() || 1;
- anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => this.props.styleProvider?.(doc, props, property + ":anchor");
+ anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => {
+ switch (property.split(":")[0]) {
+ case StyleProp.LinkSource: return this.props.Document; // pass the LinkSource to the LinkAnchorBox
+ }
+ return this.props.styleProvider?.(doc, props, property + ":anchor");
+ }
@computed get directLinks() { TraceMobx(); return LinkManager.Instance.getAllDirectLinks(this.rootDoc); }
@computed get allLinks() { TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc); }
@@ -930,40 +935,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
toggleNativeDimensions = () => this.docView?.toggleNativeDimensions();
contentsActive = () => this.docView?.contentsActive();
- // follows a link - if the target is on screen, it highlights/pans to it.
- // if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place
- // depending on the followLinkLocation property of the source (or the link itself as a fallback);
- public static followLinkClick = async (linkDoc: Opt<Doc>, sourceDoc: Doc, docView: {
- focus: DocFocusFunc,
- addDocTab: (doc: Doc, where: string) => boolean,
- ContainingCollectionDoc?: Doc
- }, altKey: boolean) => {
- const batch = UndoManager.StartBatch("follow link click");
- // open up target if it's not already in view ...
- const createViewFunc = (doc: Doc, followLoc: string, finished: Opt<() => void>) => {
- const targetFocusAfterDocFocus = () => {
- const where = StrCast(sourceDoc.followLinkLocation) || followLoc;
- const hackToCallFinishAfterFocus = () => {
- finished && setTimeout(finished, 0); // finished() needs to be called right after hackToCallFinishAfterFocus(), but there's no callback for that so we use the hacky timeout.
- return false; // we must return false here so that the zoom to the document is not reversed. If it weren't for needing to call finished(), we wouldn't need this function at all since not having it is equivalent to returning false
- };
- const addTab = docView.addDocTab(doc, where);
- addTab && setTimeout(() => {
- const targDocView = DocumentManager.Instance.getFirstDocumentView(doc);
- targDocView?.props.focus(doc, BoolCast(sourceDoc.followLinkZoom, false), undefined, hackToCallFinishAfterFocus);
- }); // add the target and focus on it.
- return where !== "inPlace" || addTab; // return true to reset the initial focus&zoom (return false for 'inPlace' since resetting the initial focus&zoom will negate the zoom into the target)
- };
- if (!sourceDoc.followLinkZoom) {
- targetFocusAfterDocFocus();
- } else {
- // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target
- docView.focus(sourceDoc, BoolCast(sourceDoc.followLinkZoom, true), 1, targetFocusAfterDocFocus);
- }
- };
- await DocumentManager.Instance.FollowLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, false), docView.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
- }
-
@action public float = () => {
const { Document: topDoc, ContainingCollectionView: container } = this.props;
const screenXf = container?.screenToLocalTransform();
@@ -1106,7 +1077,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
}
}
-
Scripting.addGlobal(function toggleDetail(doc: any, layoutKey: string, otherKey: string = "layout") {
const dv = DocumentManager.Instance.getDocumentView(doc);
if (dv?.props.Document.layoutKey === layoutKey) dv?.switchViews(otherKey !== "layout", otherKey.replace("layout_", ""));
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index f035fba33..e347d1304 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -1,25 +1,25 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../fields/Doc";
+import { Doc } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
+import { Id } from "../../../fields/FieldSymbols";
import { makeInterface } from "../../../fields/Schema";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { Utils, setupMoveUpEvents, emptyFunction, OmitKeys } from '../../../Utils';
-import { DocumentManager } from "../../util/DocumentManager";
+import { TraceMobx } from "../../../fields/util";
+import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
import { DragManager } from "../../util/DragManager";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import "./LinkAnchorBox.scss";
-import { FieldView, FieldViewProps } from "./FieldView";
-import React = require("react");
-import { ContextMenuProps } from "../ContextMenuItem";
+import { LinkManager } from "../../util/LinkManager";
+import { SelectionManager } from "../../util/SelectionManager";
import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { ViewBoxBaseComponent } from "../DocComponent";
import { LinkEditor } from "../linking/LinkEditor";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { SelectionManager } from "../../util/SelectionManager";
-import { TraceMobx } from "../../../fields/util";
-import { Id } from "../../../fields/FieldSymbols";
-import { LinkDocPreview } from "./LinkDocPreview";
import { StyleProp } from "../StyleProvider";
+import { FieldView, FieldViewProps } from "./FieldView";
+import "./LinkAnchorBox.scss";
+import { LinkDocPreview } from "./LinkDocPreview";
+import React = require("react");
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -74,7 +74,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
this._timeout = setTimeout(action(() => {
- DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, (doc, where) => this.props.addDocTab(doc, where), false);
+ LinkManager.FollowLink(this.rootDoc, anchorContainerDoc, this.props, false);
this._editing = false;
}), 300 - (Date.now() - this._lastTap));
}
@@ -136,7 +136,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch
return <div className={`linkAnchorBox-cont${small ? "-small" : ""} ${this.rootDoc[Id]}`}
onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
onPointerEnter={action(e => LinkDocPreview.LinkInfo = {
- addDocTab: this.props.addDocTab,
+ docprops: this.props,
linkSrc: linkSource,
linkDoc: this.rootDoc,
Location: [e.clientX, e.clientY + 20]
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index c69fb5b33..07b2b6338 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -6,25 +6,24 @@ import { Id } from '../../../fields/FieldSymbols';
import { Cast, FieldValue, NumCast } from "../../../fields/Types";
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse } from "../../../Utils";
import { Docs } from "../../documents/Documents";
-import { DocumentManager } from "../../util/DocumentManager";
+import { LinkManager } from '../../util/LinkManager';
import { Transform } from "../../util/Transform";
import { ContextMenu } from '../ContextMenu';
import { DocumentLinksButton } from './DocumentLinksButton';
-import { DocumentView, StyleProviderFunc } from "./DocumentView";
+import { DocumentView, StyleProviderFunc, DocumentViewSharedProps } from "./DocumentView";
import React = require("react");
interface Props {
linkDoc?: Doc;
linkSrc?: Doc;
href?: string;
- styleProvider?: StyleProviderFunc;
- addDocTab: (document: Doc, where: string) => boolean;
+ docprops: DocumentViewSharedProps;
location: number[];
}
@observer
export class LinkDocPreview extends React.Component<Props> {
static TargetDoc: Doc | undefined;
- @observable public static LinkInfo: Opt<{ linkDoc?: Doc; addDocTab: (document: Doc, where: string) => boolean, linkSrc: Doc; href?: string; Location: number[] }>;
+ @observable public static LinkInfo: Opt<{ linkDoc?: Doc; linkSrc: Doc; href?: string; Location: number[], docprops: DocumentViewSharedProps }>;
@observable _targetDoc: Opt<Doc>;
@observable _toolTipText = "";
_editRef = React.createRef<HTMLDivElement>();
@@ -42,7 +41,7 @@ export class LinkDocPreview extends React.Component<Props> {
async followDefault() {
DocumentLinksButton.EditLink = undefined;
LinkDocPreview.LinkInfo = undefined;
- this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, (doc, where) => this.props.addDocTab(doc, where), false) : null;
+ this._targetDoc && LinkManager.FollowLink(this.props.linkDoc, this._targetDoc, this.props.docprops, false);
}
componentWillUnmount() { LinkDocPreview.TargetDoc = undefined; }
@@ -75,10 +74,9 @@ export class LinkDocPreview extends React.Component<Props> {
}
pointerDown = (e: React.PointerEvent) => {
if (this.props.linkDoc && this.props.linkSrc) {
- DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.linkSrc,
- (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
+ LinkManager.FollowLink(this.props.linkDoc, this.props.linkSrc, this.props.docprops, false);
} else if (this.props.href) {
- this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { _fitWidth: true, title: this.props.href, _width: 200, _height: 400, useCors: true }), "add:right");
+ this.props.docprops?.addDocTab(Docs.Create.WebDocument(this.props.href, { _fitWidth: true, title: this.props.href, _width: 200, _height: 400, useCors: true }), "add:right");
}
}
width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
@@ -113,7 +111,7 @@ export class LinkDocPreview extends React.Component<Props> {
focus={emptyFunction}
whenActiveChanged={returnFalse}
bringToFront={returnFalse}
- styleProvider={this.props.styleProvider} />;
+ styleProvider={this.props.docprops?.styleProvider} />;
}
render() {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0bb483b41..2b9910dfb 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -57,7 +57,7 @@ import { FieldView, FieldViewProps } from "../FieldView";
import "./FormattedTextBox.scss";
import { FormattedTextBoxComment, formattedTextBoxCommentPlugin, findLinkMark } from './FormattedTextBoxComment';
import React = require("react");
-import { DocumentManager } from '../../../util/DocumentManager';
+import { LinkManager } from '../../../util/LinkManager';
import { CollectionStackingView } from '../../collections/CollectionStackingView';
import { CollectionViewType, CollectionViewProps } from '../../collections/CollectionView';
import { SnappingManager } from '../../../util/SnappingManager';
@@ -1331,8 +1331,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) {
this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right");
} else {
- DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, this.props.Document,
- (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation));
+ LinkManager.FollowLink(FormattedTextBoxComment.linkDoc, this.props.Document, this.props, false);
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index c6be25dc2..038a91aa3 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -116,7 +116,7 @@ export class FormattedTextBoxComment {
textBox.props.addDocTab(linkDoc, e.ctrlKey ? "add" : "add:right");
} else {
const target = LinkManager.getOppositeAnchor(linkDoc, textBox.dataDoc);
- target && DocumentView.followLinkClick(linkDoc, textBox.dataDoc, textBox.props, e.altKey);
+ target && LinkManager.FollowLink(linkDoc, textBox.dataDoc, textBox.props, e.altKey);
}
}
}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 20ea7bfe4..5ef57f986 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,14 +1,14 @@
import React = require("react");
import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, WidthSym, Field, Opt } from "../../../fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
-import { Cast, FieldValue, BoolCast, NumCast, StrCast, PromiseValue } from "../../../fields/Types";
-import { DocumentManager } from "../../util/DocumentManager";
-import { PDFMenu } from "./PDFMenu";
-import "./Annotation.scss";
+import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast } from "../../../fields/Types";
+import { LinkManager } from "../../util/LinkManager";
import { undoBatch } from "../../util/UndoManager";
+import "./Annotation.scss";
+import { PDFMenu } from "./PDFMenu";
interface IAnnotationProps {
anno: Doc;
@@ -119,7 +119,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
e.persist();
e.stopPropagation();
PromiseValue(this.props.document.group).then(annoGroup => annoGroup instanceof Doc &&
- DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined,
+ LinkManager.traverseLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined,
() => this.props.select(false))
);
}