aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/goldenLayout.js2
-rw-r--r--src/client/util/RichTextSchema.tsx2
-rw-r--r--src/client/util/TooltipTextMenu.tsx32
-rw-r--r--src/client/views/linking/LinkFollowBox.tsx10
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx4
-rw-r--r--src/client/views/nodes/DocumentView.tsx2
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx58
8 files changed, 94 insertions, 19 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2fa0d2dcb..4ae770e25 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -638,7 +638,6 @@ export namespace DocUtils {
});
}
export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", sourceContext?: Doc, id?: string, anchored1?: boolean) {
- if (LinkManager.Instance.doesLinkExist(source, target)) return undefined;
let sv = DocumentManager.Instance.getDocumentView(source);
if (sv && sv.props.ContainingCollectionDoc === target) return;
if (target === CurrentUserUtils.UserDocument) return undefined;
@@ -651,7 +650,6 @@ export namespace DocUtils {
linkDocProto.sourceContext = sourceContext;
linkDocProto.title = title === "" ? source.title + " to " + target.title : title;
linkDocProto.linkDescription = description;
- linkDocProto.type = DocumentType.LINK;
linkDocProto.anchor1 = source;
linkDocProto.anchor1Page = source.curPage;
@@ -665,6 +663,7 @@ export namespace DocUtils {
Doc.GetProto(source).links = ComputedField.MakeFunction("links(this)");
Doc.GetProto(target).links = ComputedField.MakeFunction("links(this)");
+
}, "make link");
return linkDocProto;
}
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index ad78139c1..29b750720 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -377,7 +377,7 @@
this._nOriginalY = coordinates.y;
this._oDocument.on('mousemove touchmove', this._fMove);
- this._oDocument.one('mouseup touchend', this._fUp);
+ this._oDocument.on('mouseup touchend', this._fUp);
this._timeout = setTimeout(lm.utils.fnBind(this._startDrag, this), this._nDelay);
}
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index ba4b92a25..9d5ccffe9 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -253,7 +253,7 @@ export const marks: { [index: string]: MarkSpec } = {
href: {},
location: { default: null },
title: { default: null },
- docref: { default: false }
+ docref: { default: false } // flags whether the linked text comes from a document within Dash. If so, an attribution label is appended after the text
},
inclusive: false,
parseDOM: [{
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index b6de048e4..987bc4f58 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -302,12 +302,17 @@ export class TooltipTextMenu {
{
handlers: {
dragComplete: action(() => {
- let linkDoc = dragData.linkDocument;
- let proto = Doc.GetProto(linkDoc);
- if (proto && docView) {
- proto.sourceContext = docView.props.ContainingCollectionDoc;
+ if (dragData.linkDocument) {
+ let linkDoc = dragData.linkDocument;
+ let proto = Doc.GetProto(linkDoc);
+ if (proto && docView) {
+ proto.sourceContext = docView.props.ContainingCollectionDoc;
+ }
+ let text = this.makeLink(linkDoc, ctrlKey ? "onRight" : "inTab");
+ if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) {
+ proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link
+ }
}
- linkDoc instanceof Doc && this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab");
}),
},
hideSource: false
@@ -389,17 +394,24 @@ export class TooltipTextMenu {
}
}
- makeLinkWithState = (state: EditorState, target: string, location: string) => {
- let link = state.schema.mark(state.schema.marks.link, { href: target, location: location });
- }
+ // makeLinkWithState = (state: EditorState, target: string, location: string) => {
+ // let link = state.schema.mark(state.schema.marks.link, { href: target, location: location });
+ // }
- makeLink = (target: string, location: string) => {
+ makeLink = (targetDoc: Doc, location: string): string => {
+ let target = Utils.prepend("/doc/" + targetDoc[Id]);
let node = this.view.state.selection.$from.nodeAfter;
- let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location });
+ let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location, guid: targetDoc[Id] });
this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link));
this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link));
node = this.view.state.selection.$from.nodeAfter;
link = node && node.marks.find(m => m.type.name === "link");
+ if (node) {
+ if (node.text) {
+ return node.text;
+ }
+ }
+ return "";
}
deleteLink = () => {
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index 81b0249dd..cad404d1f 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -18,6 +18,7 @@ import { DocServer } from "../../DocServer";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { docs_v1 } from "googleapis";
+import { Utils } from "../../../Utils";
enum FollowModes {
OPENTAB = "Open in Tab",
@@ -242,6 +243,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
let proto = Doc.GetProto(LinkFollowBox.linkDoc);
let targetContext = await Cast(proto.targetContext, Doc);
let sourceContext = await Cast(proto.sourceContext, Doc);
+ let guid = StrCast(LinkFollowBox.linkDoc[Id]);
const shouldZoom = options ? options.shouldZoom : false;
let dockingFunc = (document: Doc) => { (this._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
@@ -251,6 +253,14 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
}
else if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor1 && sourceContext) {
DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!));
+ if (LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc) {
+ if (guid) {
+ let views = DocumentManager.Instance.getDocumentViews(jumpToDoc);
+ views.length && (views[0].props.Document.scrollToLinkID = guid);
+ } else {
+ jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.linkDoc[Id]));
+ }
+ }
}
else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, undefined, undefined,
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 82fe3df23..835554ac0 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -28,9 +28,7 @@ interface LinkMenuItemProps {
export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
private _drag = React.createRef<HTMLDivElement>();
@observable private _showMore: boolean = false;
- @action toggleShowMore() {
- this._showMore = !this._showMore;
- }
+ @action toggleShowMore() { this._showMore = !this._showMore; }
onEdit = (e: React.PointerEvent): void => {
e.stopPropagation();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index d8cfff973..e89fddd25 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -350,7 +350,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
// const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
de.data.linkSourceDocument !== this.props.Document &&
- (de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc));
+ (de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc, undefined, "in-text link being created")); // TODODO this is where in text links get passed
}
}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index eb4718581..cc73c32a1 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -80,6 +80,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
private _nodeClicked: any;
private _undoTyping?: UndoManager.Batch;
private _searchReactionDisposer?: Lambda;
+ private _scroolToRegionReactionDisposer: Opt<IReactionDisposer>;
private _reactionDisposer: Opt<IReactionDisposer>;
private _textReactionDisposer: Opt<IReactionDisposer>;
private _heightReactionDisposer: Opt<IReactionDisposer>;
@@ -138,6 +139,53 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (this.props.isOverlay) {
DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined);
}
+
+ this._scroolToRegionReactionDisposer = reaction(
+ () => StrCast(this.props.Document.scrollToLinkID),
+ async (scrollToLinkID) => {
+ let findLinkFrag = (frag: Fragment, editor: EditorView) => {
+ const nodes: Node[] = [];
+ frag.forEach((node, index) => {
+ let examinedNode = findLinkNode(node, editor);
+ if (examinedNode && examinedNode.textContent) {
+ nodes.push(examinedNode);
+ start += index;
+ }
+ });
+ return { frag: Fragment.fromArray(nodes), start: start };
+ }
+ let findLinkNode = (node: Node, editor: EditorView) => {
+ if (!node.isText) {
+ const content = findLinkFrag(node.content, editor);
+ return node.copy(content.frag);
+ }
+ const marks = [...node.marks];
+ const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.link);
+ return linkIndex !== -1 && scrollToLinkID === marks[linkIndex].attrs.href.replace(/.*\/doc\//, "") ? node : undefined;
+ }
+
+ let start = -1;
+
+ if (this._editorView && scrollToLinkID) {
+ let editor = this._editorView;
+ let ret = findLinkFrag(editor.state.doc.content, editor);
+
+ if (ret.frag.size > 2 && ((!this.props.isOverlay && !this.props.isSelected()) || (this.props.isSelected() && this.props.isOverlay))) {
+ let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
+ if (ret.frag.firstChild) {
+ selection = TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
+ }
+ editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
+ const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
+ setTimeout(() => editor.dispatch(editor.state.tr.addMark(selection.from, selection.to, mark)), 0);
+ setTimeout(() => this.unhighlightSearchTerms(), 2000);
+
+ this.props.Document.scrollToLinkID = undefined;
+ }
+ }
+
+ }
+ );
}
public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
@@ -696,6 +744,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._editorView && this._editorView.destroy();
this._editorView = new EditorView(this._proseRef, {
state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config),
+ handleScrollToSelection: (editorView) => {
+ let ref = editorView.domAtPos(editorView.state.selection.from);
+ let r1 = (ref.node as any).getBoundingClientRect();
+ let r3 = self._ref.current!.getBoundingClientRect();
+ self._ref.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale;
+ return true;
+ },
dispatchTransaction: this.dispatchTransaction,
nodeViews: {
image(node, view, getPos) { return new ImageResizeView(node, view, getPos, self.props.addDocTab); },
@@ -736,6 +791,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
componentWillUnmount() {
+ this._scroolToRegionReactionDisposer && this._scroolToRegionReactionDisposer();
this._rulesReactionDisposer && this._rulesReactionDisposer();
this._reactionDisposer && this._reactionDisposer();
this._proxyReactionDisposer && this._proxyReactionDisposer();
@@ -783,9 +839,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let proto = Doc.GetProto(linkDoc);
let targetContext = await Cast(proto.targetContext, Doc);
let jumpToDoc = await Cast(linkDoc.anchor2, Doc);
+
if (jumpToDoc) {
if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
-
DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page)));
return;
}