aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
authorusodhi <61431818+usodhi@users.noreply.github.com>2020-09-18 09:26:31 +0530
committerusodhi <61431818+usodhi@users.noreply.github.com>2020-09-18 09:26:31 +0530
commitc322504b7160d39a4da7ff40f5c67023be1bf6ed (patch)
tree43dffa00be1d78a91342233b23a109a404a88c9a /src/client/views/nodes
parent5639cf97dbeff337e9661c874da4e46b9ed1a0b4 (diff)
parent88fbc391b3dd3f36a2e7015bc59c3a306d3e7d57 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into acls_uv
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/AudioBox.tsx8
-rw-r--r--src/client/views/nodes/DocumentView.scss2
-rw-r--r--src/client/views/nodes/DocumentView.tsx36
-rw-r--r--src/client/views/nodes/WebBox.tsx11
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.scss1
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx4
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx22
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx63
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts21
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts23
-rw-r--r--src/client/views/nodes/formattedText/prosemirrorPatches.js6
11 files changed, 104 insertions, 93 deletions
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 88f6a65be..cba65f663 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -26,6 +26,7 @@ import { Scripting } from "../../util/Scripting";
import Waveform from "react-audio-waveform";
import axios from "axios";
import { SnappingManager } from "../../util/SnappingManager";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
declare class MediaRecorder {
// whatever MediaRecorder has
@@ -292,11 +293,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
// creates a text document for dictation
onFile = (e: any) => {
- const newDoc = Docs.Create.TextDocument("", {
- _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, title: "", _chromeStatus: "disabled",
- x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10,
- _width: NumCast(this.props.Document._width), _height: 2 * NumCast(this.props.Document._height)
- });
+ const newDoc = CurrentUserUtils.GetNewTextDoc("", NumCast(this.props.Document.x), NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10,
+ NumCast(this.props.Document._width), 2 * NumCast(this.props.Document._height));
Doc.GetProto(newDoc).recordingSource = this.dataDoc;
Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
Doc.GetProto(newDoc).audioState = ComputedField.MakeFunction("self.recordingSource.audioState");
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 430a1aa45..c31172e22 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -110,7 +110,7 @@
transform-origin: top left;
top: 0;
width: 100%;
- height: 25;
+ height: 14;
background: rgba(0, 0, 0, .4);
text-align: center;
text-overflow: ellipsis;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5ca57a193..7ff8b635c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -118,10 +118,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
private get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); }
public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
public get ContentDiv() { return this._mainCont.current; }
+ public get LayoutFieldKey() { return this.props.layoutKey || Doc.LayoutFieldKey(this.layoutDoc); }
+ @computed get ShowTitle() {
+ return StrCast(this.layoutDoc._showTitle,
+ !Doc.IsSystem(this.layoutDoc) && this.rootDoc.type === DocumentType.RTF && !this.props.treeViewDoc ?
+ (this.dataDoc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : "author;creationDate") :
+ undefined);
+ }
@computed get topMost() { return this.props.renderDepth === 0; }
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
- @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeWidth"], (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0))); }
- @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? Doc.LayoutFieldKey(this.layoutDoc) + "-" : "_") + "nativeHeight"], (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0))); }
+ @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeWidth"], (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0))); }
+ @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeHeight"], (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0))); }
@computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
@computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
@computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
@@ -737,8 +744,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.props.bringToFront(this.props.Document, true);
const wid = this.Document[WidthSym](); // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
const hgt = this.Document[HeightSym]();
- this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeWidth"] = wid;
- this.props.Document[DataSym][Doc.LayoutFieldKey(this.Document) + "-nativeHeight"] = hgt;
+ this.props.Document[DataSym][this.LayoutFieldKey + "-nativeWidth"] = wid;
+ this.props.Document[DataSym][this.LayoutFieldKey + "-nativeHeight"] = hgt;
}
}
@@ -848,7 +855,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
}
moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
- Doc.AreProtosEqual(this.props.Document, Cast(Doc.UserDoc().myUserDoc, Doc, null)) && moreItems.push({ description: "Toggle Alternate Button Bar", event: () => Doc.UserDoc()["documentLinksButton-hideEnd"] = !Doc.UserDoc()["documentLinksButton-hideEnd"], icon: "eye" });
}
}
@@ -887,8 +893,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); };
chromeHeight = () => {
- const showTitle = StrCast(this.layoutDoc._showTitle);
- const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("PresBox") !== -1 || StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
+ const excluded = ["PresBox", "FormattedTextBox", "FontIconBox"];
+ const showTextTitle = this.ShowTitle && !excluded.includes(StrCast(this.layoutDoc.layout)) ? this.ShowTitle : undefined;
return showTextTitle ? 25 : 1;
}
@@ -1014,10 +1020,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
</div>;
}
- const showTitle = StrCast(this.layoutDoc._showTitle);
const showTitleHover = StrCast(this.layoutDoc._showTitleHover);
const showCaption = StrCast(this.layoutDoc._showCaption);
- const showTextTitle = showTitle && (StrCast(this.layoutDoc.layout).indexOf("FormattedTextBox") !== -1) ? showTitle : undefined;
+ const showTextTitle = this.ShowTitle && this.rootDoc.type === DocumentType.RTF ? this.ShowTitle : undefined;
const captionView = (!showCaption ? (null) :
<div className="documentView-captionWrapper" style={{ backgroundColor: StrCast(this.layoutDoc["caption-backgroundColor"]), color: StrCast(this.layoutDoc["caption-color"]) }}>
<DocumentContentsView {...OmitKeys(this.props, ['children']).omit}
@@ -1033,19 +1038,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onClick={this.onClickFunc}
layoutKey={this.finalLayoutKey} />
</div>);
- const titleView = (!showTitle ? (null) :
+ const titleView = (!this.ShowTitle ? (null) :
<div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
position: showTextTitle ? "relative" : "absolute",
+ background: SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.userColor || (this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.UserDoc().userColor) : "rgba(0,0,0,0.4)"),
pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
}}>
<EditableView ref={this._titleRef}
- contents={(this.props.DataDoc || this.props.Document)[showTitle]?.toString()}
- display={"block"} height={72} fontSize={12}
- GetValue={() => (this.props.DataDoc || this.props.Document)[showTitle]?.toString()}
- SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[showTitle] = value) ? true : true)}
+ contents={this.ShowTitle.split(";").map(field => field + ":" + (this.props.DataDoc || this.props.Document)[field]?.toString()).join(" ")}
+ display={"block"} fontSize={10}
+ GetValue={() => ""}
+ SetValue={undoBatch((value: string) => (Doc.GetProto(this.props.DataDoc || this.props.Document)[this.ShowTitle] = value) ? true : true)}
/>
</div>);
- return !showTitle && !showCaption ?
+ return !this.ShowTitle && !showCaption ?
this.contents :
<div className="documentView-styleWrapper" >
{this.Document.type !== DocumentType.RTF ? <> {this.contents} {titleView} </> : <> {titleView} {this.contents} </>}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 41622dbc9..dac6d3bf8 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -32,6 +32,7 @@ import "./WebBox.scss";
import "../pdf/PDFViewer.scss";
import React = require("react");
import { Tooltip } from '@material-ui/core';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
const htmlToText = require("html-to-text");
type WebDocument = makeInterface<[typeof documentSchema]>;
@@ -498,15 +499,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
e.preventDefault();
e.stopPropagation();
- const clipDoc = Doc.MakeAlias(this.dataDoc);
- clipDoc._fitWidth = true;
- clipDoc._width = this.marqueeWidth();
- clipDoc._height = this.marqueeHeight();
- clipDoc._scrollTop = this.marqueeY();
- const targetDoc = Docs.Create.TextDocument("", { _width: 125, _height: 125, title: "Note linked to " + this.props.Document.title });
- Doc.GetProto(targetDoc).data = new List<Doc>([clipDoc]);
- clipDoc.rootDocument = targetDoc;
- targetDoc.layoutKey = "layout";
+ const targetDoc = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.Document.title, 0, 0, 125, 125);
const annotationDoc = this.highlight("rgba(173, 216, 230, 0.35)"); // hyperlink color
if (annotationDoc) {
DragManager.StartPdfAnnoDrag([ele], new DragManager.PdfAnnoDragData(this.props.Document, annotationDoc, targetDoc), e.pageX, e.pageY, {
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index 09dfb349f..e16036000 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -19,6 +19,7 @@
position: relative;
display: inline-block;
font-weight: normal;
+ background: rgba(0,0,0,0.1);
}
.dashFieldView-fieldSpan {
min-width: 20px;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 0332bb4fd..a4114ed2c 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -1,5 +1,5 @@
-import { IReactionDisposer, observable, runInAction, computed, action } from "mobx";
-import { Doc, DocListCast, Field, LayoutSym } from "../../../../fields/Doc";
+import { IReactionDisposer, observable, computed, action } from "mobx";
+import { Doc, DocListCast, Field } from "../../../../fields/Doc";
import { List } from "../../../../fields/List";
import { listSpec } from "../../../../fields/Schema";
import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c0c3bdd80..83012bab5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -15,7 +15,7 @@ import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, Wid
import { documentSchema } from '../../../../fields/documentSchemas';
import applyDevTools = require("prosemirror-dev-tools");
import { removeMarkWithAttrs } from "./prosemirrorPatches";
-import { Id } from '../../../../fields/FieldSymbols';
+import { Id, Copy } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { PrefetchProxy } from '../../../../fields/Proxy';
import { RichTextField } from "../../../../fields/RichTextField";
@@ -577,6 +577,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const cm = ContextMenu.Instance;
const changeItems: ContextMenuProps[] = [];
+ changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" });
const noteTypesDoc = Cast(Doc.UserDoc()["template-notes"], Doc, null);
DocListCast(noteTypesDoc?.data).forEach(note => {
changeItems.push({
@@ -586,7 +587,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}), icon: "eye"
});
});
- changeItems.push({ description: "plain", event: undoBatch(() => Doc.setNativeView(this.rootDoc)), icon: "eye" });
!Doc.UserDoc().noviceMode && changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
const highlighting: ContextMenuProps[] = [];
["My Text", "Text from Others", "Todo Items", "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"].forEach(option =>
@@ -618,7 +618,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
// this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
- !Doc.UserDoc().noviceMode && appearanceItems.push({ description: "Reset default note style", event: () => this.rootDoc.layoutKey = "layout", icon: "eye" });
!Doc.UserDoc().noviceMode && appearanceItems.push({
description: "Make Default Layout", event: () => {
if (!this.layoutDoc.isTemplateDoc) {
@@ -639,6 +638,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template
this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root. This should probably apply to all '_' fields
this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null);
+ this.rootDoc.backgroundColor = Cast(this.layoutDoc.backgroundColor, "string", null);
}, 10);
}
Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
@@ -1233,7 +1233,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
FormattedTextBoxComment.textBox = this;
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
!this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0))));
- FormattedTextBoxComment.update(editor, undefined, (e.target as any)?.className === "prosemirror-dropdownlink" ? (e.target as any).href : "");
+ const target = (e.target as any).parentElement; // hrefs are store don the database of the <a> node that wraps the hyerlink <span>
+ FormattedTextBoxComment.update(editor, undefined, target?.dataset?.targethrefs);
}
(e.nativeEvent as any).formattedHandled = true;
@@ -1487,7 +1488,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
@action
tryUpdateHeight(limitHeight?: number) {
let scrollHeight = this.ProseRef?.scrollHeight || 0;
- if (this.props.renderDepth && this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
+ if (this.props.renderDepth && this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
scrollHeight = scrollHeight * NumCast(this.layoutDoc._viewScale, 1);
if (limitHeight && scrollHeight > limitHeight) {
scrollHeight = limitHeight;
@@ -1506,18 +1507,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}, 10);
} else {
try {
- // let ele = this._boxRef.current;
- // while (ele && ele.className !== "documentView-contentsView") ele = ele.parentElement as any;
- // if (ele) {
- // const docHeight = Number(getComputedStyle(ele).height.replace("px", ""));
- // const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", ""));
- // const outer = docHeight - boxHeight - (this.props.ChromeHeight ? this.props.ChromeHeight() : 0);
- // this.rootDoc._height = newHeight + outer;
- // this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
- // }
const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", ""));
const outer = this.rootDoc[HeightSym]() - boxHeight - (this.props.ChromeHeight ? this.props.ChromeHeight() : 0);
- this.rootDoc._height = newHeight + outer;
+ this.rootDoc._height = newHeight + Math.max(0, outer);
this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
} catch (e) { console.log("Error in tryUpdateHeight"); }
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 1bf885636..f015d329c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from "@material-ui/core";
-import { action } from "mobx";
+import { action, observable } from "mobx";
import { Mark, ResolvedPos } from "prosemirror-model";
import { EditorState, Plugin } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
@@ -60,6 +60,7 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark
// this view appears when clicking on text that has a hyperlink which is configured to show a preview of its target.
// this will also display metadata information about text when the view is configured to display things like other people who authored text.
//
+
export class FormattedTextBoxComment {
static tooltip: HTMLElement;
static tooltipText: HTMLElement;
@@ -72,6 +73,13 @@ export class FormattedTextBoxComment {
static _deleteRef: Opt<HTMLDivElement | null>;
static _followRef: Opt<HTMLDivElement | null>;
+ static _nextRef: Opt<HTMLDivElement | null>;
+
+ static _lastState?: EditorState;
+ static _lastView?: EditorView;
+
+ @observable static _hrefInd = 0;
+ static _hrefs: string[] | undefined = [];
constructor(view: any) {
if (!FormattedTextBoxComment.tooltip) {
@@ -86,7 +94,7 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
- FormattedTextBoxComment.tooltip.style.maxWidth = "200px";
+ FormattedTextBoxComment.tooltip.style.maxWidth = "400px";
FormattedTextBoxComment.tooltip.style.maxHeight = "235px";
FormattedTextBoxComment.tooltip.style.width = "100%";
FormattedTextBoxComment.tooltip.style.height = "100%";
@@ -101,6 +109,8 @@ export class FormattedTextBoxComment {
if (linkDoc.author) {
if (FormattedTextBoxComment._deleteRef?.contains(e.target as any)) {
this.deleteLink();
+ } else if (FormattedTextBoxComment._nextRef?.contains(e.target as any)) {
+ FormattedTextBoxComment.showPreview(FormattedTextBoxComment._lastView!, FormattedTextBoxComment._lastState, FormattedTextBoxComment._hrefs?.[(++FormattedTextBoxComment._hrefInd) % FormattedTextBoxComment._hrefs?.length]);
} else {
FormattedTextBoxComment.linkDoc = undefined;
if (linkDoc.type !== DocumentType.LINK) {
@@ -128,7 +138,6 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.linkDoc ? LinkManager.Instance.deleteLink(FormattedTextBoxComment.linkDoc) : null;
LinkDocPreview.LinkInfo = undefined;
DocumentLinksButton.EditLink = undefined;
- //FormattedTextBoxComment.tooltipText = undefined;
FormattedTextBoxComment.Hide();
});
@@ -164,14 +173,20 @@ export class FormattedTextBoxComment {
}
static update(view: EditorView, lastState?: EditorState, forceUrl: string = "") {
- const state = view.state;
// Don't do anything if the document/selection didn't change
- if (lastState && lastState.doc.eq(state.doc) &&
- lastState.selection.eq(state.selection)) {
+ if (!forceUrl && lastState?.doc.eq(view.state.doc) && lastState?.selection.eq(view.state.selection)) {
return;
}
+ FormattedTextBoxComment._lastState = lastState;
+ FormattedTextBoxComment._lastView = view;
+ FormattedTextBoxComment._hrefs = forceUrl ? forceUrl.trim().split(" ") : undefined;
+ FormattedTextBoxComment._hrefInd = 0;
FormattedTextBoxComment.linkDoc = undefined;
+ FormattedTextBoxComment.showPreview(view, lastState, FormattedTextBoxComment._hrefs?.[FormattedTextBoxComment._hrefInd]);
+ }
+ static showPreview(view: EditorView, lastState?: EditorState, forceUrl: string = "") {
+ const state = view.state;
const textBox = FormattedTextBoxComment.textBox;
if (!textBox || !textBox.props) {
return;
@@ -209,7 +224,7 @@ export class FormattedTextBoxComment {
state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node));
child = child || (nbef && state.selection.$from.nodeBefore);
const mark = child ? findLinkMark(child.marks) : undefined;
- const href = (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allLinks.find((item: { href: string }) => item.href)?.href || forceUrl;
+ const href = forceUrl || (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allLinks.find((item: { href: string }) => item.href)?.href;
if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) {
try {
ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText);
@@ -219,9 +234,10 @@ export class FormattedTextBoxComment {
FormattedTextBoxComment.tooltipText.style.width = "100%";
FormattedTextBoxComment.tooltipText.style.height = "100%";
FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis";
+ FormattedTextBoxComment.tooltipText.style.cursor = "pointer";
FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
- FormattedTextBoxComment.tooltipText.textContent = "external => " + href;
+ FormattedTextBoxComment.tooltipText.textContent = "URL: " + href;
(FormattedTextBoxComment.tooltipText as any).href = href;
if (href.startsWith("https://en.wikipedia.org/wiki/")) {
wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(summary => FormattedTextBoxComment.tooltipText.textContent = summary.substring(0, 500)));
@@ -245,34 +261,35 @@ export class FormattedTextBoxComment {
if (target?.author) {
FormattedTextBoxComment.showCommentbox("", view, nbef);
- const title = StrCast(target.title).length > 16 ?
- StrCast(target.title).substr(0, 16) + "..." : target.title;
-
+ const title = StrCast(target.title).length > 16 ? StrCast(target.title).substr(0, 16) + "..." : target.title;
const docPreview = <div className="FormattedTextBoxComment">
<div className="FormattedTextBoxComment-info">
<div className="FormattedTextBoxComment-title">
{title}
- {FormattedTextBoxComment.linkDoc.description !== "" ? <p className="FormattedTextBoxComment-description">
- {StrCast(FormattedTextBoxComment.linkDoc.description)}</p> : null}
+ {FormattedTextBoxComment.linkDoc.description === "" ? (null) :
+ <p className="FormattedTextBoxComment-description"> {StrCast(FormattedTextBoxComment.linkDoc.description)}</p>}
</div>
<div className="wrapper" style={{ float: "right" }}>
+ {(FormattedTextBoxComment._hrefs?.length || 0) <= 1 ? (null) : <Tooltip title={<><div className="dash-tooltip">Next Link</div></>} placement="top">
+ <div className="FormattedTextBoxComment-button" ref={(r) => this._nextRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="chevron-right" color="white" size="sm" />
+ </div>
+ </Tooltip>}
<Tooltip title={<><div className="dash-tooltip">Delete Link</div></>} placement="top">
- <div className="FormattedTextBoxComment-button"
- ref={(r) => this._deleteRef = r}>
- <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="trash" color="white"
- size="sm" /></div>
+ <div className="FormattedTextBoxComment-button" ref={(r) => this._deleteRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="trash" color="white" size="sm" />
+ </div>
</Tooltip>
<Tooltip title={<><div className="dash-tooltip">Follow Link</div></>} placement="top">
- <div className="FormattedTextBoxComment-button"
- ref={(r) => this._followRef = r}>
- <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="arrow-right" color="white"
- size="sm" />
+ <div className="FormattedTextBoxComment-button" ref={(r) => this._followRef = r}>
+ <FontAwesomeIcon className="FormattedTextBoxComment-fa-icon" icon="arrow-right" color="white" size="sm" />
</div>
</Tooltip>
- </div> </div>
+ </div>
+ </div>
<div className="FormattedTextBoxComment-preview-wrapper">
<ContentFittingDocumentView
Document={target}
@@ -304,8 +321,6 @@ export class FormattedTextBoxComment {
</div>
</div>;
-
-
FormattedTextBoxComment.showCommentbox("", view, nbef);
ReactDOM.render(docPreview, FormattedTextBoxComment.tooltipText);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 10d679e45..921c0e128 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -267,19 +267,21 @@ export class RichTextRules {
// [[fieldKey]] => show field
// [[fieldKey:Doc]] => show field of doc
new InputRule(
- new RegExp(/\[\[([a-zA-Z_@\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@\? \-0-9]+)?\]\]$/),
+ new RegExp(/\[\[([a-zA-Z_@\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@\.\? \-0-9]+)?\]\]$/),
(state, match, start, end) => {
const fieldKey = match[1];
- const docid = match[3]?.substring(1);
+ const rawdocid = match[3]?.substring(1);
+ const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined;
const value = match[2]?.substring(1);
if (!fieldKey) {
+ const linkId = Utils.GenerateGuid();
if (docid) {
DocServer.GetRefField(docid).then(docx => {
- const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500, }, docid);
- DocUtils.Publish(target, docid, returnFalse, returnFalse);
- DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to");
+ const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500, }, docid);
+ DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to", undefined, linkId);
});
- const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "add:right", title: docid, targetId: docid });
+ const allLinks = [{ href: Utils.prepend("/doc/" + docid), title: docid, targetId: docid, linkId }];
+ const link = state.schema.marks.linkAnchor.create({ allLinks, title: rawdocid, location: "add:right" });
return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link);
}
return state.tr;
@@ -297,15 +299,16 @@ export class RichTextRules {
// {{<layout>}} => show layout for this doc
// {{<layout> : Doc}} => show layout for another doc
new InputRule(
- new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._/\-]*\))?(:[a-zA-Z_ \-0-9]+)?\}\}$/),
+ new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(\([a-zA-Z0-9…._/\-]*\))?(:[a-zA-Z_@\.\? \-0-9]+)?\}\}$/),
(state, match, start, end) => {
const fieldKey = match[1] || "";
const fieldParam = match[2]?.replace("…", "...") || "";
- const docid = match[3]?.substring(1);
+ const rawdocid = match[3]?.substring(1);
+ const docid = rawdocid ? (!rawdocid.includes("@") ? Doc.CurrentUserEmail + "@" + rawdocid : rawdocid).replace(".", "_") : undefined;
if (!fieldKey && !docid) return state.tr;
docid && DocServer.GetRefField(docid).then(docx => {
if (!(docx instanceof Doc && docx)) {
- const docx = Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid);
+ const docx = Docs.Create.FreeformDocument([], { title: rawdocid, _width: 500, _height: 500 }, docid);
DocUtils.Publish(docx, docid, returnFalse, returnFalse);
}
});
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 066ceb5c8..cca7ea013 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -36,20 +36,21 @@ export const marks: { [index: string]: MarkSpec } = {
}],
toDOM(node: any) {
const targetids = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.targetId, "");
+ const targethrefs = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.href, "");
const linkids = node.attrs.allLinks.reduce((p: string, item: { href: string, title: string, targetId: string, linkId: string }) => p + " " + item.linkId, "");
return node.attrs.docref && node.attrs.title ?
["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, href: node.attrs.allLinks[0].href, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] :
- node.attrs.allLinks.length === 1 ?
- ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}`, href: node.attrs.allLinks[0].href, style: `text-decoration: ${linkids === " " ? "underline" : undefined}` }, 0] :
- ["div", { class: "prosemirror-anchor" },
- ["span", { class: "prosemirror-linkBtn" },
- ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0],
- ["input", { class: "prosemirror-hrefoptions" }],
- ],
- ["div", { class: "prosemirror-links" }, ...node.attrs.allLinks.map((item: { href: string, title: string }) =>
- ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
- )]
- ];
+ //node.attrs.allLinks.length === 1 ?
+ ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, "data-targethrefs": targethrefs, title: `${node.attrs.title}`, href: node.attrs.allLinks[0].href, style: `text-decoration: ${linkids === " " ? "underline" : undefined}` }, 0];
+ // ["div", { class: "prosemirror-anchor" },
+ // ["span", { class: "prosemirror-linkBtn" },
+ // ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0],
+ // ["input", { class: "prosemirror-hrefoptions" }],
+ // ],
+ // ["div", { class: "prosemirror-links" }, ...node.attrs.allLinks.map((item: { href: string, title: string }) =>
+ // ["a", { class: "prosemirror-dropdownlink", href: item.href }, item.title]
+ // )]
+ // ];
}
},
diff --git a/src/client/views/nodes/formattedText/prosemirrorPatches.js b/src/client/views/nodes/formattedText/prosemirrorPatches.js
index 0969ea4ef..746c93868 100644
--- a/src/client/views/nodes/formattedText/prosemirrorPatches.js
+++ b/src/client/views/nodes/formattedText/prosemirrorPatches.js
@@ -146,7 +146,9 @@ function isInSetWithAttrs(mark, set, attrs) {
for (var i = 0; i < set.length; i++) {
if (set[i].type == mark) {
if (Array.from(Object.keys(attrs)).reduce((p, akey) => {
- return p && JSON.stringify(set[i].attrs[akey]) === JSON.stringify(attrs[akey]);
+ if (p && JSON.stringify(set[i].attrs[akey]) === JSON.stringify(attrs[akey])) return true;
+ set[i].attrs.allLinks = Array.from(set[i].attrs.allLinks).filter(a => !Array.from(attrs.allLinks.map(al => al.targetId)).includes(a.targetId) || !Array.from(attrs.allLinks.map(al => al.linkId).includes(a.linkId)))
+ return false;
}, true)) {
return set[i];
}
@@ -178,7 +180,7 @@ function removeMarkWithAttrs(tr, from, to, mark, attrs) {
var style = toRemove[i], found$1 = (void 0);
for (var j = 0; j < matched.length; j++) {
var m = matched[j];
- if (m.step == step - 1 && style.eq(matched[j].style)) { found$1 = m; }
+ if (m.step == step - 1 /*&& style.eq(matched[j].style)*/) { found$1 = m; } // bcz: not sure what this is even trying to do, but style.eq doesn't seem to exist anymore
}
if (found$1) {
found$1.to = end;