aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx39
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx4
-rw-r--r--src/client/views/nodes/DocumentView.tsx35
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss94
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx292
-rw-r--r--src/client/views/nodes/FormattedTextBoxComment.tsx104
-rw-r--r--src/client/views/nodes/LinkEditor.scss145
-rw-r--r--src/client/views/nodes/LinkEditor.tsx400
-rw-r--r--src/client/views/nodes/LinkMenu.scss137
-rw-r--r--src/client/views/nodes/LinkMenu.tsx75
-rw-r--r--src/client/views/nodes/LinkMenuGroup.tsx105
-rw-r--r--src/client/views/nodes/LinkMenuItem.tsx152
-rw-r--r--src/client/views/nodes/PDFBox.tsx15
-rw-r--r--src/client/views/nodes/WebBox.scss1
-rw-r--r--src/client/views/nodes/WebBox.tsx23
15 files changed, 397 insertions, 1224 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 7631ecc6c..9692dd8a9 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,20 +1,21 @@
import { computed } from "mobx";
import { observer } from "mobx-react";
import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { BoolCast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
+import { BoolCast, FieldValue, NumCast, StrCast, Cast } from "../../../new_fields/Types";
import { Transform } from "../../util/Transform";
import { DocComponent } from "../DocComponent";
import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView";
import "./DocumentView.scss";
import React = require("react");
import { Doc } from "../../../new_fields/Doc";
-import { returnEmptyString } from "../../../Utils";
+import { random } from "animejs";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
x?: number;
y?: number;
width?: number;
height?: number;
+ jitterRotation: number;
}
const schema = createSchema({
@@ -28,7 +29,7 @@ const FreeformDocument = makeInterface(schema, positionSchema);
@observer
export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, FreeformDocument>(FreeformDocument) {
- @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}) `; }
+ @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg) scale(${this.zoom}) `; }
@computed get X() { return this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
@computed get Y() { return this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
@computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.width !== undefined ? this.props.width : this.Document.width || 0; }
@@ -76,6 +77,21 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
clusterColorFunc = (doc: Doc) => this.clusterColor;
render() {
+ let txf = this.transform;
+ let w = this.width;
+ let h = this.height;
+ let renderScript = this.Document.renderScript;
+ if (renderScript) {
+ let someView = Cast(this.Document.someView, Doc);
+ let minimap = Cast(this.Document.minimap, Doc);
+ if (someView instanceof Doc && minimap instanceof Doc) {
+ let x = (NumCast(someView.panX) - NumCast(someView.width) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitX) - NumCast(minimap.fitW) / 2)) / NumCast(minimap.fitW) * NumCast(minimap.width) - NumCast(minimap.width) / 2;
+ let y = (NumCast(someView.panY) - NumCast(someView.height) / 2 / NumCast(someView.scale) - (NumCast(minimap.fitY) - NumCast(minimap.fitH) / 2)) / NumCast(minimap.fitH) * NumCast(minimap.height) - NumCast(minimap.height) / 2;
+ w = NumCast(someView.width) / NumCast(someView.scale) / NumCast(minimap.fitW) * NumCast(minimap.width);
+ h = NumCast(someView.height) / NumCast(someView.scale) / NumCast(minimap.fitH) * NumCast(minimap.height);
+ txf = `translate(${x}px,${y}px)`;
+ }
+ }
const hasPosition = this.props.x !== undefined || this.props.y !== undefined;
return (
<div className="collectionFreeFormDocumentView-container"
@@ -83,15 +99,18 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
transformOrigin: "left top",
position: "absolute",
backgroundColor: "transparent",
- boxShadow: this.props.Document.opacity === 0 ? undefined : this.props.Document.z ? `#9c9396 ${StrCast(this.props.Document.boxShadow, "10px 10px 0.9vw")}` :
- this.clusterColor ? (
- this.props.Document.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` :
- `${this.clusterColor} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : undefined,
+ boxShadow:
+ this.props.Document.opacity === 0 ? undefined : // if it's not visible, then no shadow
+ this.props.Document.z ? `#9c9396 ${StrCast(this.props.Document.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow
+ this.clusterColor ? (
+ this.props.Document.isBackground ? `0px 0px 50px 50px ${this.clusterColor}` : // if it's a background & has a cluster color, make the shadow spread really big
+ `${this.clusterColor} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent
+ undefined,
borderRadius: this.borderRounding(),
- transform: this.transform,
+ transform: txf,
transition: hasPosition ? "transform 1s" : StrCast(this.props.Document.transition),
- width: this.width,
- height: this.height,
+ width: w,
+ height: h,
zIndex: this.Document.zIndex || 0,
}} >
<DocumentView {...this.props}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index d77662355..d0e117fe4 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -14,6 +14,7 @@ import { ImageBox } from "./ImageBox";
import { DragBox } from "./DragBox";
import { ButtonBox } from "./ButtonBox";
import { PresBox } from "./PresBox";
+import { LinkFollowBox } from "../linking/LinkFollowBox";
import { IconBox } from "./IconBox";
import { KeyValueBox } from "./KeyValueBox";
import { PDFBox } from "./PDFBox";
@@ -30,6 +31,7 @@ import { List } from "../../../new_fields/List";
import { Doc } from "../../../new_fields/Doc";
import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
import { ScriptField } from "../../../new_fields/ScriptField";
+import { fromPromise } from "mobx-utils";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -108,7 +110,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
return <ObserverJsxParser
blacklistedAttrs={[]}
- components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox }}
+ components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox }}
bindings={this.CreateBindings()}
jsx={this.finalLayout}
showWarnings={true}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4d5307c88..633455f63 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -375,8 +375,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false,
document => { // open up target if it's not already in view ...
+ let cv = this.props.ContainingCollectionView; // bcz: ugh --- maybe need to have a props.unfocus() method so that we leave things in the state we found them??
+ let px = cv && cv.props.Document.panX;
+ let py = cv && cv.props.Document.panY;
+ let s = cv && cv.props.Document.scale;
this.props.focus(this.props.Document, true, 1); // by zooming into the button document first
- setTimeout(() => this.props.addDocTab(document, undefined, maxLocation), 1000); // then after the 1sec animation, open up the target in a new tab
+ setTimeout(() => {
+ this.props.addDocTab(document, undefined, maxLocation);
+ cv && (cv.props.Document.panX = px);
+ cv && (cv.props.Document.panY = py);
+ cv && (cv.props.Document.scale = s);
+ }, 1000); // then after the 1sec animation, open up the target in a new tab
},
linkedFwdPage[altKey ? 1 : 0], targetContext);
}
@@ -593,11 +602,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" });
makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Into Button", event: this.makeBtnClicked, icon: "concierge-bell" });
makes.push({ description: "OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") });
+ makes.push({ description: "OnClick foreach doc", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick", "docList(this.collectionContext.data).map(d => {", "});\n") });
makes.push({
description: "Into Portal", event: () => {
- let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
- DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal");
- this.makeBtnClicked();
+ if (!DocListCast(this.props.Document.links).find(doc => {
+ if (Cast(doc.anchor2, Doc) instanceof Doc && (Cast(doc.anchor2, Doc) as Doc)!.title === this.props.Document.title + ".portal") return true;
+ return false;
+ })) {
+ let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" });
+ DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal");
+ Doc.GetProto(this.props.Document).isButton = true;
+ }
}, icon: "window-restore"
});
makes.push({ description: this.props.Document.ignoreClick ? "Selectable" : "Unselectable", event: () => this.props.Document.ignoreClick = !this.props.Document.ignoreClick, icon: this.props.Document.ignoreClick ? "unlock" : "lock" });
@@ -758,19 +773,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
});
}
let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined;
- let brushDegree = Doc.IsBrushedDegree(this.props.Document);
+ let fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
let borderRounding = StrCast(Doc.GetProto(this.props.Document).borderRounding);
- let localScale = this.props.ScreenToLocalTransform().Scale * brushDegree;
+ let localScale = this.props.ScreenToLocalTransform().Scale * fullDegree;
return (
<div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
ref={this._mainCont}
style={{
pointerEvents: this.layoutDoc.isBackground && !this.isSelected() ? "none" : "all",
color: foregroundColor,
- outlineColor: ["transparent", "maroon", "maroon"][brushDegree],
- outlineStyle: ["none", "dashed", "solid"][brushDegree],
- outlineWidth: brushDegree && !borderRounding ? `${localScale}px` : "0px",
- border: brushDegree && borderRounding ? `${["none", "dashed", "solid"][brushDegree]} ${["transparent", "maroon", "maroon"][brushDegree]} ${localScale}px` : undefined,
+ outlineColor: ["transparent", "maroon", "maroon", "yellow"][fullDegree],
+ outlineStyle: ["none", "dashed", "solid", "solid"][fullDegree],
+ outlineWidth: fullDegree && !borderRounding ? `${localScale}px` : "0px",
+ border: fullDegree && borderRounding ? `${["none", "dashed", "solid", "solid"][fullDegree]} ${["transparent", "maroon", "maroon", "yellow"][fullDegree]} ${localScale}px` : undefined,
borderRadius: "inherit",
background: backgroundColor,
width: nativeWidth,
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 03e81bfca..d7ac7a9c5 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -84,18 +84,94 @@
align-content: center;
}
-ol { counter-reset: deci 0;}
-.decimal-ol { counter-reset: deci 0; p { display: inline }; font-size: 24 }
+footnote {
+ display: inline-block;
+ position: relative;
+ cursor: pointer;
+ div {
+ padding : 0 !important;
+ }
+}
+
+footnote::after {
+ content: counter(prosemirror-footnote);
+ vertical-align: super;
+ font-size: 75%;
+ counter-increment: prosemirror-footnote;
+}
+
+.ProseMirror {
+ counter-reset: prosemirror-footnote;
+ }
+
+.footnote-tooltip {
+ cursor: auto;
+ font-size: 75%;
+ position: absolute;
+ left: -30px;
+ top: calc(100% + 10px);
+ background: silver;
+ padding: 3px;
+ border-radius: 2px;
+ max-width: 100px;
+ min-width: 50px;
+ width: max-content;
+}
+
+.prosemirror-attribution {
+ font-size: 8px;
+}
+.footnote-tooltip::before {
+ border: 5px solid silver;
+ border-top-width: 0px;
+ border-left-color: transparent;
+ border-right-color: transparent;
+ position: absolute;
+ top: -5px;
+ left: 27px;
+ content: " ";
+ height: 0;
+ width: 0;
+}
+
+.formattedTextBox-summarizer {
+ opacity :0.5;
+ position: relative;
+ width:40px;
+ height:20px;
+}
+.formattedTextBox-summarizer::after{
+ content: "←" ;
+}
+
+.formattedTextBox-summarizer-collapsed {
+ opacity :0.5;
+ position: relative;
+ width:40px;
+ height:20px;
+}
+.formattedTextBox-summarizer-collapsed::after {
+ content: "...";
+}
+
+ol { counter-reset: deci1 0;}
+.decimal1-ol {counter-reset: deci1; p { display: inline }; font-size: 24 }
.decimal2-ol {counter-reset: deci2; p { display: inline }; font-size: 18 }
.decimal3-ol {counter-reset: deci3; p { display: inline }; font-size: 14 }
.decimal4-ol {counter-reset: deci4; p { display: inline }; font-size: 10 }
+.decimal5-ol {counter-reset: deci5; p { display: inline }; font-size: 10 }
+.decimal6-ol {counter-reset: deci6; p { display: inline }; font-size: 10 }
+.decimal7-ol {counter-reset: deci7; p { display: inline }; font-size: 10 }
.upper-alpha-ol {counter-reset: ualph; p { display: inline }; font-size: 18 }
.lower-roman-ol {counter-reset: lroman; p { display: inline }; font-size: 14; }
.lower-alpha-ol {counter-reset: lalpha; p { display: inline }; font-size: 10;}
-.decimal:before { content: counter(deci) " "; counter-increment: deci; display:inline-block; width: 30}
-.decimal2:before { content: counter(deci) "." counter(deci2) " "; counter-increment: deci2; display:inline-block; width: 35}
-.decimal3:before { content: counter(deci) "." counter(deci2) "." counter(deci3) " "; counter-increment: deci3; display:inline-block; width: 35}
-.decimal4:before { content: counter(deci) "." counter(deci2) "." counter(deci3) "." counter(deci4) " "; counter-increment: deci4; display:inline-block; width: 40}
-.upper-alpha:before { content: counter(deci) "." counter(ualph, upper-alpha) " "; counter-increment: ualph; display:inline-block; width: 35 }
-.lower-roman:before { content: counter(deci) "." counter(ualph, upper-alpha) "." counter(lroman, lower-roman) " "; counter-increment: lroman;display:inline-block; width: 50 }
-.lower-alpha:before { content: counter(deci) "." counter(ualph, upper-alpha) "." counter(lroman, lower-roman) "." counter(lalpha, lower-alpha)" "; counter-increment: lalpha; display:inline-block; width: 35}
+.decimal1:before { content: counter(deci1) ")"; counter-increment: deci1; display:inline-block; width: 30}
+.decimal2:before { content: counter(deci1) "." counter(deci2) ")"; counter-increment: deci2; display:inline-block; width: 35}
+.decimal3:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) ")"; counter-increment: deci3; display:inline-block; width: 35}
+.decimal4:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) ")"; counter-increment: deci4; display:inline-block; width: 40}
+.decimal5:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) "." counter(deci5) ")"; counter-increment: deci5; display:inline-block; width: 40}
+.decimal6:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) "." counter(deci5) "." counter(deci6) ")"; counter-increment: deci6; display:inline-block; width: 45}
+.decimal7:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) "." counter(deci5) "." counter(deci6) "." counter(deci7) ")"; counter-increment: deci7; display:inline-block; width: 50}
+.upper-alpha:before { content: counter(deci1) "." counter(ualph, upper-alpha) ")"; counter-increment: ualph; display:inline-block; width: 35 }
+.lower-roman:before { content: counter(deci1) "." counter(ualph, upper-alpha) "." counter(lroman, lower-roman) ")"; counter-increment: lroman;display:inline-block; width: 50 }
+.lower-alpha:before { content: counter(deci1) "." counter(ualph, upper-alpha) "." counter(lroman, lower-roman) "." counter(lalpha, lower-alpha) ")"; counter-increment: lalpha; display:inline-block; width: 35}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index ec1ddbdb7..985075a52 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -13,16 +13,16 @@ import { Doc, DocListCast, Opt, WidthSym } from "../../../new_fields/Doc";
import { Copy, Id } from '../../../new_fields/FieldSymbols';
import { List } from '../../../new_fields/List';
import { RichTextField, ToPlainText, FromPlainText } from "../../../new_fields/RichTextField";
-import { BoolCast, Cast, NumCast, StrCast, DateCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, NumCast, StrCast, DateCast, PromiseValue } from "../../../new_fields/Types";
import { createSchema, makeInterface } from "../../../new_fields/Schema";
-import { Utils, numberRange } from '../../../Utils';
+import { Utils, numberRange, timenow } from '../../../Utils';
import { DocServer } from "../../DocServer";
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from "../../util/DragManager";
import buildKeymap from "../../util/ProsemirrorExampleTransfer";
import { inpRules } from "../../util/RichTextRules";
-import { ImageResizeView, schema, SummarizedView } from "../../util/RichTextSchema";
+import { ImageResizeView, schema, SummarizedView, OrderedListView, FootnoteView } from "../../util/RichTextSchema";
import { SelectionManager } from "../../util/SelectionManager";
import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
import { TooltipTextMenu } from "../../util/TooltipTextMenu";
@@ -37,9 +37,9 @@ import { DocumentDecorations } from '../DocumentDecorations';
import { DictationManager } from '../../util/DictationManager';
import { ReplaceStep } from 'prosemirror-transform';
import { DocumentType } from '../../documents/DocumentTypes';
-import { link } from 'fs';
-import { selectionSizePlugin, findStartOfMark, findUserMark, findEndOfMark, findOtherUserMark, SelectionSizeTooltip } from './FormattedTextBoxComment';
-import { date } from 'serializr';
+import { formattedTextBoxCommentPlugin, FormattedTextBoxComment } from './FormattedTextBoxComment';
+import { inputRules } from 'prosemirror-inputrules';
+import { select } from 'async';
library.add(faEdit);
library.add(faSmile, faTextHeight, faUpload);
@@ -77,6 +77,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
private _editorView: Opt<EditorView>;
private _applyingChange: boolean = false;
private _linkClicked = "";
+ private _nodeClicked: any;
private _undoTyping?: UndoManager.Batch;
private _reactionDisposer: Opt<IReactionDisposer>;
private _searchReactionDisposer?: Lambda;
@@ -118,6 +119,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
@undoBatch
public setFontColor(color: string) {
+ this._editorView!.state.storedMarks
if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false;
if (this._editorView!.state.selection.to - this._editorView!.state.selection.from > this._editorView!.state.doc.nodeSize - 3) {
this.props.Document.color = color;
@@ -201,40 +203,62 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
@computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
- paste = (e: ClipboardEvent) => {
- if (e.clipboardData && this._editorView) {
- // let pdfPasteText = `${Utils.GenerateDeterministicGuid("pdf paste")}`;
- // for (let i = 0; i < e.clipboardData.items.length; i++) {
- // let item = e.clipboardData.items.item(i);
- // console.log(item)
- // if (item.type === "text/plain") {
- // console.log("plain")
- // item.getAsString((text) => {
- // let pdfPasteIndex = text.indexOf(pdfPasteText);
- // if (pdfPasteIndex > -1) {
- // let insertText = text.substr(0, pdfPasteIndex);
- // const tx = this._editorView!.state.tr.insertText(insertText);
- // // tx.setSelection(new Selection(tx.))
- // const state = this._editorView!.state;
- // this._editorView!.dispatch(tx);
- // if (FormattedTextBox._toolTipTextMenu) {
- // // this._toolTipTextMenu.makeLinkWithState(state)
- // }
- // e.stopPropagation();
- // e.preventDefault();
- // }
- // });
- // }
- // }
+ // this should be internal to prosemirror, but is needed
+ // here to make sure that footnote view nodes in the overlay editor
+ // get removed when they're not selected.
+
+ syncNodeSelection(view: any, sel: any) {
+ if (sel instanceof NodeSelection) {
+ var desc = view.docView.descAt(sel.from);
+ if (desc !== view.lastSelectedViewDesc) {
+ if (view.lastSelectedViewDesc) {
+ view.lastSelectedViewDesc.deselectNode();
+ view.lastSelectedViewDesc = null;
+ }
+ if (desc) { desc.selectNode(); }
+ view.lastSelectedViewDesc = desc;
+ }
+ } else {
+ if (view.lastSelectedViewDesc) {
+ view.lastSelectedViewDesc.deselectNode();
+ view.lastSelectedViewDesc = null;
+ }
}
}
dispatchTransaction = (tx: Transaction) => {
if (this._editorView) {
+ let metadata = tx.selection.$from.marks().find((m: Mark) => m.type === schema.marks.metadata);
+ if (metadata) {
+ let range = tx.selection.$from.blockRange(tx.selection.$to);
+ let text = range ? tx.doc.textBetween(range.start, range.end) : "";
+ let textEndSelection = tx.selection.to;
+ for (; textEndSelection < range!.end && text[textEndSelection - range!.start] != " "; textEndSelection++) { }
+ text = text.substr(0, textEndSelection - range!.start);
+ text = text.split(" ")[text.split(" ").length - 1];
+ let split = text.split("::");
+ if (split.length > 1 && split[1]) {
+ let key = split[0];
+ let value = split[split.length - 1];
+
+ let id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key);
+ DocServer.GetRefField(value).then(doc => {
+ DocServer.GetRefField(id).then(linkDoc => {
+ this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value);
+ if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; }
+ else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id);
+ })
+ });
+ const link = this._editorView!.state.schema.marks.link.create({ href: `http://localhost:1050/doc/${id}`, location: "onRight", title: value });
+ const mval = this._editorView!.state.schema.marks.metadataVal.create();
+ let offset = (tx.selection.to === range!.end - 1 ? -1 : 0);
+ tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval);
+ this.dataDoc[key] = value;
+ }
+ }
const state = this._editorView.state.apply(tx);
- FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = true);
this._editorView.updateState(state);
- FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = false);
+ this.syncNodeSelection(this._editorView, this._editorView.state.selection); // bcz: ugh -- shouldn't be needed but without this the overlay view's footnote popup doesn't get deselected
if (state.selection.empty && FormattedTextBox._toolTipTextMenu && tx.storedMarks) {
FormattedTextBox._toolTipTextMenu.mark_key_pressed(tx.storedMarks);
}
@@ -244,12 +268,16 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this.extensionDoc && (this.extensionDoc.lastModified = new DateField(new Date(Date.now())));
this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON()));
this._applyingChange = false;
- let title = StrCast(this.dataDoc.title);
- if (title && title.startsWith("-") && this._editorView && !this.Document.customTitle) {
- let str = this._editorView.state.doc.textContent;
- let titlestr = str.substr(0, Math.min(40, str.length));
- this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
- }
+ this.updateTitle();
+ this.tryUpdateHeight();
+ }
+ }
+
+ updateTitle = () => {
+ if (StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.Document.customTitle) {
+ let str = this._editorView.state.doc.textContent;
+ let titlestr = str.substr(0, Math.min(40, str.length));
+ this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
}
}
@@ -292,7 +320,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
// }
}
}
-
+ setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => {
+ let view = this._editorView!;
+ let mid = view.state.doc.resolve(Math.round((start + end) / 2));
+ let nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: keep ? Doc.CurrentUserEmail : mark.attrs.userid, opened: opened });
+ view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark).setSelection(new TextSelection(mid)));
+ }
protected createDropTarget = (ele: HTMLDivElement) => {
this._proseRef = ele;
this.dropDisposer && this.dropDisposer();
@@ -304,10 +337,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
drop = async (e: Event, de: DragManager.DropEvent) => {
// We're dealing with a link to a document
if (de.data instanceof DragManager.EmbedDragData && de.data.urlField) {
+ let target = de.data.embeddableSourceDoc;
// We're dealing with an internal document drop
let url = de.data.urlField.url.href;
let model: NodeType = (url.includes(".mov") || url.includes(".mp4")) ? schema.nodes.video : schema.nodes.image;
- this._editorView!.dispatch(this._editorView!.state.tr.insert(0, model.create({ src: url })));
+ let pos = this._editorView!.posAtCoords({ left: de.x, top: de.y });
+ this._editorView!.dispatch(this._editorView!.state.tr.insert(pos!.pos, model.create({ src: url, docid: target[Id] })));
+ DocUtils.MakeLink(this.dataDoc, target, undefined, "ImgRef:" + target.title, undefined, undefined, target[Id]);
e.stopPropagation();
} else if (de.data instanceof DragManager.DocumentDragData) {
const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0];
@@ -372,14 +408,16 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
return numberRange(count).map(x => schema.nodes.list_item.create(undefined, schema.nodes.paragraph.create()));
}
+ _keymap: any = undefined;
@computed get config() {
+ this._keymap = buildKeymap(schema);
return {
schema,
- inpRules, //these currently don't do anything, but could eventually be helpful
plugins: this.props.isOverlay ? [
+ inputRules(inpRules),
this.tooltipTextMenuPlugin(),
history(),
- keymap(buildKeymap(schema)),
+ keymap(this._keymap),
keymap(baseKeymap),
// this.tooltipLinkingMenuPlugin(),
new Plugin({
@@ -387,23 +425,16 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
attributes: { class: "ProseMirror-example-setup-style" }
}
}),
- selectionSizePlugin
+ formattedTextBoxCommentPlugin
] : [
history(),
- keymap(buildKeymap(schema)),
+ keymap(this._keymap),
keymap(baseKeymap),
]
};
}
- @action
- rebuildEditor() {
- this.setupEditor(this.config, this.dataDoc, this.props.fieldKey);
- }
-
componentDidMount() {
- document.addEventListener("paste", this.paste);
-
if (!this.props.isOverlay) {
this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
() => {
@@ -623,9 +654,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (link) {
cbe.clipboardData!.setData("dash/linkDoc", link[Id]);
linkId = link[Id];
- let guid = StrCast(link.guid);
- link.guid = guid;
- let frag = addMarkToFrag(slice.content);
+ let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(doc.title)));
slice = new Slice(frag, slice.openStart, slice.openEnd);
var tr = view.state.tr.replaceSelection(slice);
@@ -636,24 +665,20 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
return true;
- function addMarkToFrag(frag: Fragment) {
+ function addMarkToFrag(frag: Fragment, marker: (node: Node) => Node) {
const nodes: Node[] = [];
- frag.forEach(node => nodes.push(addLinkMark(node)));
+ frag.forEach(node => nodes.push(marker(node)));
return Fragment.fromArray(nodes);
}
- function addLinkMark(node: Node) {
+ function addLinkMark(node: Node, title: string) {
if (!node.isText) {
- const content = addMarkToFrag(node.content);
+ const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title));
return node.copy(content);
}
const marks = [...node.marks];
const linkIndex = marks.findIndex(mark => mark.type.name === "link");
- const link = view.state.schema.mark(view.state.schema.marks.link, { href: `http://localhost:1050/doc/${linkId}`, location: "onRight" });
- if (linkIndex !== -1) {
- marks.splice(linkIndex, 1, link);
- } else {
- marks.push(link);
- }
+ const link = view.state.schema.mark(view.state.schema.marks.link, { href: `http://localhost:1050/doc/${linkId}`, location: "onRight", title: title, docref: true });
+ marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link);
return node.mark(marks);
}
}
@@ -671,32 +696,68 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
if (this._proseRef) {
+ let self = this;
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),
dispatchTransaction: this.dispatchTransaction,
nodeViews: {
- image(node, view, getPos) { return new ImageResizeView(node, view, getPos); },
+ image(node, view, getPos) { return new ImageResizeView(node, view, getPos, self.props.addDocTab); },
star(node, view, getPos) { return new SummarizedView(node, view, getPos); },
+ ordered_list(node, view, getPos) { return new OrderedListView(); },
+ footnote(node, view, getPos) { return new FootnoteView(node, view, getPos); }
},
clipboardTextSerializer: this.clipboardTextSerializer,
handlePaste: this.handlePaste,
});
+ (this._editorView as any).isOverlay = this.props.isOverlay;
if (startup) {
Doc.GetProto(doc).documentText = undefined;
this._editorView.dispatch(this._editorView.state.tr.insertText(startup));
}
}
- if (this.props.Document[Id] === FormattedTextBox.SelectOnLoad) {
+ let selectOnLoad = this.props.Document[Id] === FormattedTextBox.SelectOnLoad;
+ if (selectOnLoad) {
FormattedTextBox.SelectOnLoad = "";
this.props.select(false);
}
else if (this.props.isOverlay) this._editorView!.focus();
- var markerss = this._editorView!.state.storedMarks || (this._editorView!.state.selection.$to.parentOffset && this._editorView!.state.selection.$from.marks());
- let newMarks = [...(markerss ? markerss.filter(m => m.type !== schema.marks.user_mark) : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail })];
- this._editorView!.state.storedMarks = newMarks;
-
+ // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
+ this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })];
+ let heading = this.props.Document.heading;
+ if (heading && selectOnLoad) {
+ PromiseValue(Cast(this.props.Document.ruleProvider, Doc)).then(ruleProvider => {
+ if (ruleProvider) {
+ let align = StrCast(ruleProvider["ruleAlign_" + heading]);
+ let font = StrCast(ruleProvider["ruleFont_" + heading]);
+ let size = NumCast(ruleProvider["ruleSize_" + heading]);
+ if (align) {
+ let tr = this._editorView!.state.tr;
+ tr = tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(2))).
+ replaceSelectionWith(this._editorView!.state.schema.nodes.paragraph.create({ align: align }), true).
+ setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(0)));
+ this._editorView!.dispatch(tr);
+ }
+ let sm = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })];
+ size && (sm = [...sm, schema.marks.pFontSize.create({ fontSize: size })]);
+ font && (sm = [...sm, this.getFont(font)]);
+ this._editorView!.dispatch(this._editorView!.state.tr.setStoredMarks(sm));
+ }
+ });
+ }
+ }
+ getFont(font: string) {
+ switch (font) {
+ case "Arial": return schema.marks.arial.create();
+ case "Times New Roman": return schema.marks.timesNewRoman.create();
+ case "Georgia": return schema.marks.georgia.create();
+ case "Comic Sans MS": return schema.marks.comicSans.create();
+ case "Tahoma": return schema.marks.tahoma.create();
+ case "Impact": return schema.marks.impact.create();
+ case "ACrimson Textrial": return schema.marks.crimson.create();
+ }
+ return schema.marks.arial.create();
}
componentWillUnmount() {
@@ -707,19 +768,18 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._pullReactionDisposer && this._pullReactionDisposer();
this._heightReactionDisposer && this._heightReactionDisposer();
this._searchReactionDisposer && this._searchReactionDisposer();
- document.removeEventListener("paste", this.paste);
this._editorView && this._editorView.destroy();
}
+
onPointerDown = (e: React.PointerEvent): void => {
+ let pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
+ pos && (this._nodeClicked = this._editorView!.state.doc.nodeAt(pos.pos));
if (this.props.onClick && e.button === 0) {
e.preventDefault();
}
if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) {
e.stopPropagation();
- if (FormattedTextBox._toolTipTextMenu && FormattedTextBox._toolTipTextMenu.tooltip) {
- //this._toolTipTextMenu.tooltip.style.opacity = "0";
- }
}
let ctrlKey = e.ctrlKey;
if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) {
@@ -732,6 +792,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
for (let parent = (e.target as any).parentNode; !href && parent; parent = parent.parentNode) {
href = parent.childNodes[0].href ? parent.childNodes[0].href : parent.href;
}
+ let pcords = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
+ let node = pcords && this._editorView!.state.doc.nodeAt(pcords.pos);
+ if (node) {
+ let link = node.marks.find(m => m.type === this._editorView!.state.schema.marks.link);
+ href = link && link.attrs.href;
+ location = link && link.attrs.location;
+ }
if (href) {
if (href.indexOf(Utils.prepend("/doc/")) === 0) {
this._linkClicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0];
@@ -757,7 +824,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
DocumentManager.Instance.jumpToDocument(targetContext, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
} else if (jumpToDoc) {
DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
-
+ } else {
+ DocumentManager.Instance.jumpToDocument(linkDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
}
}
});
@@ -780,36 +848,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
onPointerUp = (e: React.PointerEvent): void => {
- let view = this._editorView!;
- const pos = view.posAtCoords({ left: e.clientX, top: e.clientY });
- const rpos = pos && view.state.doc.resolve(pos.pos);
- let noselection = view.state.selection.$from === view.state.selection.$to;
- let set = false;
- if (pos && rpos) {
- let nbef = findStartOfMark(rpos, view, findOtherUserMark);
- let naft = findEndOfMark(rpos, view, findOtherUserMark);
- const spos = pos.pos - nbef;
- const epos = pos.pos + naft;
- let child = rpos.nodeBefore || rpos.nodeAfter;
- let mark = child && findOtherUserMark(child.marks);
- if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
- SelectionSizeTooltip.SetState(this, mark.attrs.opened, spos, epos, mark);
- set = true;
- }
- }
- !set && SelectionSizeTooltip.Hide();
+ FormattedTextBoxComment.textBox = this;
if (e.buttons === 1 && this.props.isSelected() && !e.altKey) {
e.stopPropagation();
}
}
- setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => {
- let view = this._editorView!;
- let mid = view.state.doc.resolve(Math.round((start + end) / 2));
- let nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: keep ? Doc.CurrentUserEmail : mark.attrs.userid, opened: opened });
- view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark).setSelection(new TextSelection(mid, mid)));
- }
-
@action
onFocused = (e: React.FocusEvent): void => {
document.removeEventListener("keypress", this.recordKeyHandler);
@@ -831,6 +875,17 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
onClick = (e: React.MouseEvent): void => {
+ // this hackiness handles clicking on the list item bullets to do expand/collapse. the bullets are ::before pseudo elements so there's no real way to hit test against them.
+ if (this.props.isSelected() && e.nativeEvent.offsetX < 40) {
+ let pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY });
+ if (pos && pos.pos > 0) {
+ let node = this._editorView!.state.doc.nodeAt(pos.pos);
+ let node2 = node && node.type === schema.nodes.paragraph ? this._editorView!.state.doc.nodeAt(pos.pos - 1) : undefined;
+ if (node === this._nodeClicked && node2 && (node2.type === schema.nodes.ordered_list || node2.type === schema.nodes.list_item)) {
+ this._editorView!.dispatch(this._editorView!.state.tr.setNodeMarkup(pos.pos - 1, node2.type, { ...node2.attrs, visibility: !node2.attrs.visibility }));
+ }
+ }
+ }
this._proseRef!.focus();
if (this._linkClicked) {
this._linkClicked = "";
@@ -874,44 +929,14 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
SelectionManager.DeselectAll();
}
e.stopPropagation();
- if (e.key === "Tab" || e.key === "Enter") { // bullets typically change "levels" when tab or enter is used. sometimes backspcae, so maybe that should be added.
+ if (e.key === "Tab" || e.key === "Enter") {
e.preventDefault();
- setTimeout(() => { // force re-rendering of bullet numbers that may have had their bullet labels change. (Our prosemirrior code re-"marks" the changed bullets, but nothing causes the Dom to be re-rendered which is where the nubering takes place)
- SelectionManager.DeselectAll();
- SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(this.props.Document, this.props.ContainingCollectionView)!, false);
- }, 0);
- }
- function timenow() {
- var now = new Date();
- let ampm = 'am';
- let h = now.getHours();
- let m: any = now.getMinutes();
- let s: any = now.getSeconds();
- if (h >= 12) {
- if (h > 12) h -= 12;
- ampm = 'pm';
- }
-
- if (m < 10) m = '0' + m;
- if (s < 10) s = '0' + s;
- return now.toLocaleDateString() + ' ' + h + ':' + m + ':' + s + ' ' + ampm;
}
- var markerss = this._editorView!.state.storedMarks || (this._editorView!.state.selection.$to.parentOffset && this._editorView!.state.selection.$from.marks());
- let newMarks = [...(markerss ? markerss.filter(m => m.type !== schema.marks.user_mark) : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })];
- this._editorView!.state.storedMarks = newMarks;
+ this._editorView!.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() }));
- // stop propagation doesn't seem to stop propagation of native keyboard events.
- // so we set a flag on the native event that marks that the event's been handled.
- (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
- if (StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.Document.customTitle) {
- let str = this._editorView.state.doc.textContent;
- let titlestr = str.substr(0, Math.min(40, str.length));
- this.dataDoc.title = "-" + titlestr + (str.length > 40 ? "..." : "");
- }
if (!this._undoTyping) {
this._undoTyping = UndoManager.StartBatch("undoTyping");
}
- this.tryUpdateHeight();
}
@action
@@ -928,7 +953,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
render() {
- let self = this;
let style = this.props.isOverlay ? "scroll" : "hidden";
let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : "";
let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.props.Document.isBackground ||
diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx
index 9123f8aed..2d2fff06d 100644
--- a/src/client/views/nodes/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/FormattedTextBoxComment.tsx
@@ -1,18 +1,25 @@
-import { Plugin, EditorState } from "prosemirror-state"
-import './FormattedTextBoxComment.scss'
+import { Plugin, EditorState } from "prosemirror-state";
+import './FormattedTextBoxComment.scss';
import { ResolvedPos, Mark } from "prosemirror-model";
import { EditorView } from "prosemirror-view";
import { Doc } from "../../../new_fields/Doc";
+import { schema } from "../../util/RichTextSchema";
+import { DocServer } from "../../DocServer";
+import { Utils } from "../../../Utils";
+import { StrCast } from "../../../new_fields/Types";
-export let selectionSizePlugin = new Plugin({
- view(editorView) { return new SelectionSizeTooltip(editorView); }
-})
+export let formattedTextBoxCommentPlugin = new Plugin({
+ view(editorView) { return new FormattedTextBoxComment(editorView); }
+});
export function findOtherUserMark(marks: Mark[]): Mark | undefined {
return marks.find(m => m.attrs.userid && m.attrs.userid !== Doc.CurrentUserEmail);
}
export function findUserMark(marks: Mark[]): Mark | undefined {
return marks.find(m => m.attrs.userid);
}
+export function findLinkMark(marks: Mark[]): Mark | undefined {
+ return marks.find(m => m.type === schema.marks.link);
+}
export function findStartOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: Mark[]) => Mark | undefined) {
let before = 0;
let nbef = rpos.nodeBefore;
@@ -35,7 +42,7 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark
}
-export class SelectionSizeTooltip {
+export class FormattedTextBoxComment {
static tooltip: HTMLElement;
static tooltipText: HTMLElement;
static start: number;
@@ -44,39 +51,39 @@ export class SelectionSizeTooltip {
static opened: boolean;
static textBox: any;
constructor(view: any) {
- if (!SelectionSizeTooltip.tooltip) {
+ if (!FormattedTextBoxComment.tooltip) {
const root = document.getElementById("root");
let input = document.createElement("input");
input.type = "checkbox";
- SelectionSizeTooltip.tooltip = document.createElement("div");
- SelectionSizeTooltip.tooltipText = document.createElement("div");
- SelectionSizeTooltip.tooltip.appendChild(SelectionSizeTooltip.tooltipText);
- SelectionSizeTooltip.tooltip.className = "FormattedTextBox-tooltip";
- SelectionSizeTooltip.tooltip.style.pointerEvents = "all";
- SelectionSizeTooltip.tooltip.appendChild(input);
- SelectionSizeTooltip.tooltip.onpointerdown = (e: PointerEvent) => {
+ FormattedTextBoxComment.tooltip = document.createElement("div");
+ FormattedTextBoxComment.tooltipText = document.createElement("div");
+ FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText);
+ FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip";
+ FormattedTextBoxComment.tooltip.style.pointerEvents = "all";
+ FormattedTextBoxComment.tooltip.appendChild(input);
+ FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => {
let keep = e.target && (e.target as any).type === "checkbox";
- SelectionSizeTooltip.opened = keep || !SelectionSizeTooltip.opened;
- SelectionSizeTooltip.textBox.setAnnotation(
- SelectionSizeTooltip.start, SelectionSizeTooltip.end, SelectionSizeTooltip.mark,
- SelectionSizeTooltip.opened, keep);
+ FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened;
+ FormattedTextBoxComment.textBox && FormattedTextBoxComment.textBox.setAnnotation(
+ FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark,
+ FormattedTextBoxComment.opened, keep);
};
- root && root.appendChild(SelectionSizeTooltip.tooltip);
+ root && root.appendChild(FormattedTextBoxComment.tooltip);
}
this.update(view, undefined);
}
public static Hide() {
- SelectionSizeTooltip.textBox = undefined;
- SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = "none");
+ FormattedTextBoxComment.textBox = undefined;
+ FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none");
}
public static SetState(textBox: any, opened: boolean, start: number, end: number, mark: Mark) {
- SelectionSizeTooltip.textBox = textBox;
- SelectionSizeTooltip.start = start;
- SelectionSizeTooltip.end = end;
- SelectionSizeTooltip.mark = mark;
- SelectionSizeTooltip.opened = opened;
- SelectionSizeTooltip.textBox && SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = "");
+ FormattedTextBoxComment.textBox = textBox;
+ FormattedTextBoxComment.start = start;
+ FormattedTextBoxComment.end = end;
+ FormattedTextBoxComment.mark = mark;
+ FormattedTextBoxComment.opened = opened;
+ FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "");
}
update(view: EditorView, lastState?: EditorState) {
@@ -85,13 +92,46 @@ export class SelectionSizeTooltip {
if (lastState && lastState.doc.eq(state.doc) &&
lastState.selection.eq(state.selection)) return;
+ let set = "none";
if (state.selection.$from) {
let nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark);
let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark);
+ const spos = state.selection.$from.pos - nbef;
+ const epos = state.selection.$from.pos + naft;
let child = state.selection.$from.nodeBefore;
let mark = child && findOtherUserMark(child.marks);
+ let noselection = view.state.selection.$from === view.state.selection.$to;
+ if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) {
+ FormattedTextBoxComment.SetState(this, mark.attrs.opened, spos, epos, mark);
+ }
+ if (mark && child && nbef && naft) {
+ FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified;
+ // These are in screen coordinates
+ // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
+ let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
+ // The box in which the tooltip is positioned, to use as base
+ let box = (document.getElementById("main-div") as any).getBoundingClientRect();
+ // Find a center-ish x position from the selection endpoints (when
+ // crossing lines, end may be more to the left)
+ let left = Math.max((start.left + end.left) / 2, start.left + 3);
+ FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
+ FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
+ set = "";
+ }
+ }
+ if (set === "none" && state.selection.$from) {
+ FormattedTextBoxComment.textBox = undefined;
+ let nbef = findStartOfMark(state.selection.$from, view, findLinkMark);
+ let naft = findEndOfMark(state.selection.$from, view, findLinkMark);
+ let child = state.selection.$from.nodeBefore;
+ let mark = child && findLinkMark(child.marks);
if (mark && child && nbef && naft) {
- SelectionSizeTooltip.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified;
+ FormattedTextBoxComment.tooltipText.textContent = "link : " + (mark.attrs.title || mark.attrs.href);
+ if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) {
+ let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0];
+ docTarget && DocServer.GetRefField(docTarget).then(linkDoc =>
+ (linkDoc as Doc) && (FormattedTextBoxComment.tooltipText.textContent = "link :" + StrCast((linkDoc as Doc).title)));
+ }
// These are in screen coordinates
// let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to);
let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef);
@@ -100,11 +140,13 @@ export class SelectionSizeTooltip {
// Find a center-ish x position from the selection endpoints (when
// crossing lines, end may be more to the left)
let left = Math.max((start.left + end.left) / 2, start.left + 3);
- SelectionSizeTooltip.tooltip.style.left = (left - box.left) + "px";
- SelectionSizeTooltip.tooltip.style.bottom = (box.bottom - start.top) + "px";
+ FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px";
+ FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px";
+ set = "";
}
}
+ FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set);
}
- destroy() { SelectionSizeTooltip.tooltip.style.display = "none" }
+ destroy() { FormattedTextBoxComment.tooltip.style.display = "none"; }
}
diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss
deleted file mode 100644
index fc5f2410c..000000000
--- a/src/client/views/nodes/LinkEditor.scss
+++ /dev/null
@@ -1,145 +0,0 @@
-@import "../globalCssVariables";
-
-.linkEditor {
- width: 100%;
- height: auto;
- font-size: 12px; // TODO
-}
-
-.linkEditor-back {
- margin-bottom: 6px;
-}
-
-.linkEditor-info {
- border-bottom: 0.5px solid $light-color-secondary;
- padding-bottom: 6px;
- margin-bottom: 6px;
- display: flex;
- justify-content: space-between;
-
- .linkEditor-linkedTo {
- width: calc(100% - 26px);
- }
-}
-
-.linkEditor-button {
- width: 20px;
- height: 20px;
- margin-left: 6px;
- padding: 0;
- // font-size: 12px;
- border-radius: 10px;
-
- &:disabled {
- background-color: gray;
- }
-}
-
-.linkEditor-groupsLabel {
- display: flex;
- justify-content: space-between;
-}
-
-.linkEditor-group {
- background-color: $light-color-secondary;
- padding: 6px;
- margin: 3px 0;
- border-radius: 3px;
-
- .linkEditor-group-row {
- display: flex;
- margin-bottom: 3px;
-
- .linkEditor-group-row-label {
- margin-right: 6px;
- }
- }
-
- .linkEditor-metadata-row {
- display: flex;
- justify-content: space-between;
- margin-bottom: 6px;
-
- .linkEditor-error {
- border-color: red;
- }
-
- input {
- width: calc(50% - 16px);
- height: 20px;
- }
-
- button {
- width: 20px;
- height: 20px;
- margin-left: 3px;
- padding: 0;
- font-size: 10px;
- }
- }
-}
-
-
-.linkEditor-dropdown {
- width: 100%;
- position: relative;
- z-index: 999;
-
- input {
- width: 100%;
- }
-
- .linkEditor-options-wrapper {
- width: 100%;
- position: absolute;
- top: 19px;
- left: 0;
- display: flex;
- flex-direction: column;
- }
-
- .linkEditor-option {
- background-color: $light-color-secondary;
- border: 1px solid $intermediate-color;
- border-top: 0;
- padding: 3px;
- cursor: pointer;
-
- &:hover {
- background-color: lightgray;
- }
-
- &.onDown {
- background-color: gray;
- }
- }
-}
-
-.linkEditor-typeButton {
- background-color: transparent;
- color: $dark-color;
- width: 100%;
- height: 20px;
- padding: 0 3px;
- padding-bottom: 2px;
- text-align: left;
- text-transform: none;
- letter-spacing: normal;
- font-size: 12px;
- font-weight: bold;
-
- &:hover {
- background-color: $light-color;
- }
-}
-
-.linkEditor-group-buttons {
- height: 20px;
- display: flex;
- justify-content: flex-end;
- margin-top: 5px;
-
- .linkEditor-button {
- margin-left: 6px;
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx
deleted file mode 100644
index ecb3e9db4..000000000
--- a/src/client/views/nodes/LinkEditor.tsx
+++ /dev/null
@@ -1,400 +0,0 @@
-import { observable, computed, action, trace } from "mobx";
-import React = require("react");
-import { observer } from "mobx-react";
-import './LinkEditor.scss';
-import { StrCast, Cast, FieldValue } from "../../../new_fields/Types";
-import { Doc } from "../../../new_fields/Doc";
-import { LinkManager } from "../../util/LinkManager";
-import { Docs } from "../../documents/Documents";
-import { Utils } from "../../../Utils";
-import { faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTimes, faPlus } from '@fortawesome/free-solid-svg-icons';
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { SetupDrag } from "../../util/DragManager";
-import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField";
-
-library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTimes, faPlus);
-
-
-interface GroupTypesDropdownProps {
- groupType: string;
- setGroupType: (group: string) => void;
-}
-// this dropdown could be generalized
-@observer
-class GroupTypesDropdown extends React.Component<GroupTypesDropdownProps> {
- @observable private _searchTerm: string = this.props.groupType;
- @observable private _groupType: string = this.props.groupType;
- @observable private _isEditing: boolean = false;
-
- @action
- createGroup = (groupType: string): void => {
- this.props.setGroupType(groupType);
- LinkManager.Instance.addGroupType(groupType);
- }
-
- @action
- onChange = (val: string): void => {
- this._searchTerm = val;
- this._groupType = val;
- this._isEditing = true;
- }
-
- @action
- onKeyDown = (e: React.KeyboardEvent): void => {
- if (e.key === "Enter") {
- let allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
- let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase());
-
- if (exactFound > -1) {
- let groupType = groupOptions[exactFound];
- this.props.setGroupType(groupType);
- this._groupType = groupType;
- } else {
- this.createGroup(this._searchTerm);
- this._groupType = this._searchTerm;
- }
-
- this._searchTerm = this._groupType;
- this._isEditing = false;
- }
- }
-
- @action
- onOptionClick = (value: string, createNew: boolean): void => {
- if (createNew) {
- this.createGroup(this._searchTerm);
- this._groupType = this._searchTerm;
-
- } else {
- this.props.setGroupType(value);
- this._groupType = value;
-
- }
- this._searchTerm = this._groupType;
- this._isEditing = false;
- }
-
- @action
- onButtonPointerDown = (): void => {
- this._isEditing = true;
- }
-
- renderOptions = (): JSX.Element[] | JSX.Element => {
- if (this._searchTerm === "") return <></>;
-
- let allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes());
- let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
-
- let options = groupOptions.map(groupType => {
- let ref = React.createRef<HTMLDivElement>();
- return <div key={groupType} ref={ref} className="linkEditor-option"
- onClick={() => this.onOptionClick(groupType, false)}>{groupType}</div>;
- });
-
- // if search term does not already exist as a group type, give option to create new group type
- if (!exactFound && this._searchTerm !== "") {
- let ref = React.createRef<HTMLDivElement>();
- options.push(<div key={""} ref={ref} className="linkEditor-option"
- onClick={() => this.onOptionClick(this._searchTerm, true)}>Define new "{this._searchTerm}" relationship</div>);
- }
-
- return options;
- }
-
- render() {
- if (this._isEditing || this._groupType === "") {
- return (
- <div className="linkEditor-dropdown">
- <input type="text" value={this._groupType} placeholder="Search for or create a new group"
- onChange={e => this.onChange(e.target.value)} onKeyDown={this.onKeyDown} autoFocus></input>
- <div className="linkEditor-options-wrapper">
- {this.renderOptions()}
- </div>
- </div >
- );
- } else {
- return <button className="linkEditor-typeButton" onClick={() => this.onButtonPointerDown()}>{this._groupType}</button>;
- }
- }
-}
-
-
-interface LinkMetadataEditorProps {
- id: string;
- groupType: string;
- mdDoc: Doc;
- mdKey: string;
- mdValue: string;
- changeMdIdKey: (id: string, newKey: string) => void;
-}
-@observer
-class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
- @observable private _key: string = this.props.mdKey;
- @observable private _value: string = this.props.mdValue;
- @observable private _keyError: boolean = false;
-
- @action
- setMetadataKey = (value: string): void => {
- let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
-
- // don't allow user to create existing key
- let newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase());
- if (newIndex > -1) {
- this._keyError = true;
- this._key = value;
- return;
- } else {
- this._keyError = false;
- }
-
- // set new value for key
- let currIndex = groupMdKeys.findIndex(key => {
- return StrCast(key).toUpperCase() === this._key.toUpperCase();
- });
- if (currIndex === -1) console.error("LinkMetadataEditor: key was not found");
- groupMdKeys[currIndex] = value;
-
- this.props.changeMdIdKey(this.props.id, value);
- this._key = value;
- LinkManager.Instance.setMetadataKeysForGroup(this.props.groupType, [...groupMdKeys]);
- }
-
- @action
- setMetadataValue = (value: string): void => {
- if (!this._keyError) {
- this._value = value;
- this.props.mdDoc[this._key] = value;
- }
- }
-
- @action
- removeMetadata = (): void => {
- let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);
-
- let index = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase());
- if (index === -1) console.error("LinkMetadataEditor: key was not found");
- groupMdKeys.splice(index, 1);
-
- LinkManager.Instance.setMetadataKeysForGroup(this.props.groupType, groupMdKeys);
- this._key = "";
- }
-
- render() {
- return (
- <div className="linkEditor-metadata-row">
- <input className={this._keyError ? "linkEditor-error" : ""} type="text" value={this._key === "new key" ? "" : this._key} placeholder="key" onChange={e => this.setMetadataKey(e.target.value)}></input>:
- <input type="text" value={this._value} placeholder="value" onChange={e => this.setMetadataValue(e.target.value)}></input>
- <button onClick={() => this.removeMetadata()}><FontAwesomeIcon icon="times" size="sm" /></button>
- </div>
- );
- }
-}
-
-interface LinkGroupEditorProps {
- sourceDoc: Doc;
- linkDoc: Doc;
- groupDoc: Doc;
-}
-@observer
-export class LinkGroupEditor extends React.Component<LinkGroupEditorProps> {
-
- private _metadataIds: Map<string, string> = new Map();
-
- constructor(props: LinkGroupEditorProps) {
- super(props);
-
- let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.type));
- groupMdKeys.forEach(key => {
- this._metadataIds.set(key, Utils.GenerateGuid());
- });
- }
-
- @action
- setGroupType = (groupType: string): void => {
- this.props.groupDoc.type = groupType;
- }
-
- removeGroupFromLink = (groupType: string): void => {
- LinkManager.Instance.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType);
- }
-
- deleteGroup = (groupType: string): void => {
- LinkManager.Instance.deleteGroupType(groupType);
- }
-
- copyGroup = async (groupType: string): Promise<void> => {
- let sourceGroupDoc = this.props.groupDoc;
- const sourceMdDoc = await Cast(sourceGroupDoc.metadata, Doc);
- if (!sourceMdDoc) return;
-
- let destDoc = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
- // let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc);
- let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
-
- // create new metadata doc with copied kvp
- let destMdDoc = new Doc();
- destMdDoc.anchor1 = StrCast(sourceMdDoc.anchor2);
- destMdDoc.anchor2 = StrCast(sourceMdDoc.anchor1);
- keys.forEach(key => {
- let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]);
- destMdDoc[key] = val;
- });
-
- // create new group doc with new metadata doc
- let destGroupDoc = new Doc();
- destGroupDoc.type = groupType;
- destGroupDoc.metadata = destMdDoc;
-
- if (destDoc) {
- LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, destDoc, destGroupDoc, true);
- }
- }
-
- @action
- addMetadata = (groupType: string): void => {
- this._metadataIds.set("new key", Utils.GenerateGuid());
- let mdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
- // only add "new key" if there is no other key with value "new key"; prevents spamming
- if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key");
- LinkManager.Instance.setMetadataKeysForGroup(groupType, mdKeys);
- }
-
- // for key rendering purposes
- changeMdIdKey = (id: string, newKey: string) => {
- this._metadataIds.set(newKey, id);
- }
-
- renderMetadata = (): JSX.Element[] => {
- let metadata: Array<JSX.Element> = [];
- let groupDoc = this.props.groupDoc;
- const mdDoc = FieldValue(Cast(groupDoc.metadata, Doc));
- if (!mdDoc) {
- return [];
- }
- let groupType = StrCast(groupDoc.type);
- let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
-
- groupMdKeys.forEach((key) => {
- let val = StrCast(mdDoc[key]);
- metadata.push(
- <LinkMetadataEditor key={"mded-" + this._metadataIds.get(key)} id={this._metadataIds.get(key)!} groupType={groupType} mdDoc={mdDoc} mdKey={key} mdValue={val} changeMdIdKey={this.changeMdIdKey} />
- );
- });
- return metadata;
- }
-
- viewGroupAsTable = (groupType: string): JSX.Element => {
- let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
- let index = keys.indexOf("");
- if (index > -1) keys.splice(index, 1);
- let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb"));
- let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
- let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" }));
- let ref = React.createRef<HTMLDivElement>();
- return <div ref={ref}><button className="linkEditor-button" onPointerDown={SetupDrag(ref, createTable)} title="Drag to view relationship table"><FontAwesomeIcon icon="table" size="sm" /></button></div>;
- }
-
- render() {
- let groupType = StrCast(this.props.groupDoc.type);
- // if ((groupType && LinkManager.Instance.getMetadataKeysInGroup(groupType).length > 0) || groupType === "") {
- let buttons;
- if (groupType === "") {
- buttons = (
- <>
- <button className="linkEditor-button" disabled={true} title="Add KVP"><FontAwesomeIcon icon="plus" size="sm" /></button>
- <button className="linkEditor-button" disabled title="Copy group to opposite anchor"><FontAwesomeIcon icon="exchange-alt" size="sm" /></button>
- <button className="linkEditor-button" onClick={() => this.removeGroupFromLink(groupType)} title="Remove group from link"><FontAwesomeIcon icon="times" size="sm" /></button>
- <button className="linkEditor-button" disabled title="Delete group"><FontAwesomeIcon icon="trash" size="sm" /></button>
- <button className="linkEditor-button" disabled title="Drag to view relationship table"><FontAwesomeIcon icon="table" size="sm" /></button>
- </>
- );
- } else {
- buttons = (
- <>
- <button className="linkEditor-button" onClick={() => this.addMetadata(groupType)} title="Add KVP"><FontAwesomeIcon icon="plus" size="sm" /></button>
- <button className="linkEditor-button" onClick={() => this.copyGroup(groupType)} title="Copy group to opposite anchor"><FontAwesomeIcon icon="exchange-alt" size="sm" /></button>
- <button className="linkEditor-button" onClick={() => this.removeGroupFromLink(groupType)} title="Remove group from link"><FontAwesomeIcon icon="times" size="sm" /></button>
- <button className="linkEditor-button" onClick={() => this.deleteGroup(groupType)} title="Delete group"><FontAwesomeIcon icon="trash" size="sm" /></button>
- {this.viewGroupAsTable(groupType)}
- </>
- );
- }
- return (
- <div className="linkEditor-group">
- <div className="linkEditor-group-row ">
- <p className="linkEditor-group-row-label">type:</p>
- <GroupTypesDropdown groupType={groupType} setGroupType={this.setGroupType} />
- </div>
- {this.renderMetadata().length > 0 ? <p className="linkEditor-group-row-label">metadata:</p> : <></>}
- {this.renderMetadata()}
- <div className="linkEditor-group-buttons">
- {buttons}
- </div>
- </div>
- );
- }
-}
-
-
-interface LinkEditorProps {
- sourceDoc: Doc;
- linkDoc: Doc;
- showLinks: () => void;
-}
-@observer
-export class LinkEditor extends React.Component<LinkEditorProps> {
-
- @action
- deleteLink = (): void => {
- LinkManager.Instance.deleteLink(this.props.linkDoc);
- this.props.showLinks();
- }
-
- @action
- addGroup = (): void => {
- // create new metadata document for group
- let mdDoc = new Doc();
- mdDoc.anchor1 = this.props.sourceDoc.title;
- let opp = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
- if (opp) {
- mdDoc.anchor2 = opp.title;
- }
-
- // create new group document
- let groupDoc = new Doc();
- groupDoc.type = "";
- groupDoc.metadata = mdDoc;
-
- LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, this.props.sourceDoc, groupDoc);
- }
-
- render() {
- let destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
-
- let groupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
- let groups = groupList.map(groupDoc => {
- return <LinkGroupEditor key={"gred-" + StrCast(groupDoc.type)} linkDoc={this.props.linkDoc} sourceDoc={this.props.sourceDoc} groupDoc={groupDoc} />;
- });
-
- if (destination) {
- return (
- <div className="linkEditor">
- <button className="linkEditor-back" onPointerDown={() => this.props.showLinks()}><FontAwesomeIcon icon="arrow-left" size="sm" /></button>
- <div className="linkEditor-info">
- <p className="linkEditor-linkedTo">editing link to: <b>{destination.proto!.title}</b></p>
- <button className="linkEditor-button" onPointerDown={() => this.deleteLink()} title="Delete link"><FontAwesomeIcon icon="trash" size="sm" /></button>
- </div>
- <div className="linkEditor-groupsLabel">
- <b>Relationships:</b>
- <button className="linkEditor-button" onClick={() => this.addGroup()} title=" Add Group"><FontAwesomeIcon icon="plus" size="sm" /></button>
- </div>
- {groups.length > 0 ? groups : <div className="linkEditor-group">There are currently no relationships associated with this link.</div>}
- </div>
-
- );
- }
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss
deleted file mode 100644
index a4018bd2d..000000000
--- a/src/client/views/nodes/LinkMenu.scss
+++ /dev/null
@@ -1,137 +0,0 @@
-@import "../globalCssVariables";
-
-.linkMenu {
- width: 100%;
- height: auto;
-}
-
-.linkMenu-list {
- max-height: 200px;
- overflow-y: scroll;
-}
-
-.linkMenu-group {
- border-bottom: 0.5px solid lightgray;
- padding: 5px 0;
-
-
- &:last-child {
- border-bottom: none;
- }
-
- .linkMenu-group-name {
- display: flex;
-
- &:hover {
- p {
- background-color: lightgray;
- }
- p.expand-one {
- width: calc(100% - 26px);
- }
- .linkEditor-tableButton {
- display: block;
- }
- }
-
- p {
- width: 100%;
- padding: 4px 6px;
- line-height: 12px;
- border-radius: 5px;
- font-weight: bold;
- }
-
- .linkEditor-tableButton {
- display: none;
- }
- }
-}
-
-.linkMenu-item {
- // border-top: 0.5px solid $main-accent;
- position: relative;
- display: flex;
- font-size: 12px;
-
-
- .link-name {
- position: relative;
-
- p {
- padding: 4px 6px;
- line-height: 12px;
- border-radius: 5px;
- overflow-wrap: break-word;
- }
- }
-
- .linkMenu-item-content {
- width: 100%;
- }
-
- .link-metadata {
- padding: 0 10px 0 16px;
- margin-bottom: 4px;
- color: $main-accent;
- font-style: italic;
- font-size: 10.5px;
- }
-
- &:hover {
- .linkMenu-item-buttons {
- display: flex;
- }
- .linkMenu-item-content {
- &.expand-two p {
- width: calc(100% - 52px);
- background-color: lightgray;
- }
- &.expand-three p {
- width: calc(100% - 84px);
- background-color: lightgray;
- }
- }
- }
-}
-
-.linkMenu-item-buttons {
- display: none;
- position: absolute;
- top: 50%;
- right: 0;
- transform: translateY(-50%);
-
- .button {
- width: 20px;
- height: 20px;
- margin: 0;
- margin-right: 6px;
- border-radius: 50%;
- cursor: pointer;
- pointer-events: auto;
- background-color: $dark-color;
- color: $light-color;
- font-size: 65%;
- transition: transform 0.2s;
- text-align: center;
- position: relative;
-
- .fa-icon {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
-
- &:last-child {
- margin-right: 0;
- }
- &:hover {
- background: $main-accent;
- }
- }
-}
-
-
-
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
deleted file mode 100644
index 1908889e9..000000000
--- a/src/client/views/nodes/LinkMenu.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import { DocumentView } from "./DocumentView";
-import { LinkEditor } from "./LinkEditor";
-import './LinkMenu.scss';
-import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
-import { LinkManager } from "../../util/LinkManager";
-import { LinkMenuGroup } from "./LinkMenuGroup";
-import { faTrash } from '@fortawesome/free-solid-svg-icons';
-import { library } from "@fortawesome/fontawesome-svg-core";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-
-library.add(faTrash);
-
-interface Props {
- docView: DocumentView;
- changeFlyout: () => void;
- addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
-}
-
-@observer
-export class LinkMenu extends React.Component<Props> {
-
- @observable private _editingLink?: Doc;
-
- @action
- componentWillReceiveProps() {
- this._editingLink = undefined;
- }
-
- clearAllLinks = () => {
- LinkManager.Instance.deleteAllLinksOnAnchor(this.props.docView.props.Document);
- }
-
- renderAllGroups = (groups: Map<string, Array<Doc>>): Array<JSX.Element> => {
- let linkItems: Array<JSX.Element> = [];
- groups.forEach((group, groupType) => {
- linkItems.push(
- <LinkMenuGroup
- key={groupType}
- sourceDoc={this.props.docView.props.Document}
- group={group}
- groupType={groupType}
- showEditor={action((linkDoc: Doc) => this._editingLink = linkDoc)}
- addDocTab={this.props.addDocTab} />
- );
- });
-
- // if source doc has no links push message
- if (linkItems.length === 0) linkItems.push(<p key="">No links have been created yet. Drag the linking button onto another document to create a link.</p>);
-
- return linkItems;
- }
-
- render() {
- let sourceDoc = this.props.docView.props.Document;
- let groups: Map<string, Doc[]> = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc);
- if (this._editingLink === undefined) {
- return (
- <div className="linkMenu">
- <button className="linkEditor-button linkEditor-clearButton" onClick={() => this.clearAllLinks()} title="Clear all links"><FontAwesomeIcon icon="trash" size="sm" /></button>
- {/* <input id="linkMenu-searchBar" type="text" placeholder="Search..."></input> */}
- <div className="linkMenu-list">
- {this.renderAllGroups(groups)}
- </div>
- </div>
- );
- } else {
- return (
- <LinkEditor sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)}></LinkEditor>
- );
- }
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx
deleted file mode 100644
index e04044266..000000000
--- a/src/client/views/nodes/LinkMenuGroup.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import { DocumentView } from "./DocumentView";
-import { LinkMenuItem } from "./LinkMenuItem";
-import { LinkEditor } from "./LinkEditor";
-import './LinkMenu.scss';
-import React = require("react");
-import { Doc, DocListCast } from "../../../new_fields/Doc";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { LinkManager } from "../../util/LinkManager";
-import { DragLinksAsDocuments, DragManager, SetupDrag } from "../../util/DragManager";
-import { emptyFunction } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { UndoManager } from "../../util/UndoManager";
-import { StrCast } from "../../../new_fields/Types";
-import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField";
-
-interface LinkMenuGroupProps {
- sourceDoc: Doc;
- group: Doc[];
- groupType: string;
- showEditor: (linkDoc: Doc) => void;
- addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
-}
-
-@observer
-export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
-
- private _drag = React.createRef<HTMLDivElement>();
- private _table = React.createRef<HTMLDivElement>();
-
- onLinkButtonDown = (e: React.PointerEvent): void => {
- e.stopPropagation();
- document.removeEventListener("pointermove", this.onLinkButtonMoved);
- document.addEventListener("pointermove", this.onLinkButtonMoved);
- document.removeEventListener("pointerup", this.onLinkButtonUp);
- document.addEventListener("pointerup", this.onLinkButtonUp);
- }
-
- onLinkButtonUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onLinkButtonMoved);
- document.removeEventListener("pointerup", this.onLinkButtonUp);
- e.stopPropagation();
- }
-
-
- onLinkButtonMoved = async (e: PointerEvent) => {
- UndoManager.RunInBatch(() => {
- if (this._drag.current !== null && (e.movementX > 1 || e.movementY > 1)) {
- document.removeEventListener("pointermove", this.onLinkButtonMoved);
- document.removeEventListener("pointerup", this.onLinkButtonUp);
-
- let draggedDocs = this.props.group.map(linkDoc => {
- let opp = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
- if (opp) return opp;
- }) as Doc[];
- let dragData = new DragManager.DocumentDragData(draggedDocs, draggedDocs.map(d => undefined));
-
- DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, {
- handlers: {
- dragComplete: action(emptyFunction),
- },
- hideSource: false
- });
- }
- }, "drag links");
- e.stopPropagation();
- }
-
- viewGroupAsTable = (groupType: string): JSX.Element => {
- let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType);
- let index = keys.indexOf("");
- if (index > -1) keys.splice(index, 1);
- let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb"));
- let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType);
- let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" }));
- let ref = React.createRef<HTMLDivElement>();
- return <div ref={ref}><button className="linkEditor-button linkEditor-tableButton" onPointerDown={SetupDrag(ref, createTable)} title="Drag to view relationship table"><FontAwesomeIcon icon="table" size="sm" /></button></div>;
- }
-
- render() {
- let groupItems = this.props.group.map(linkDoc => {
- let destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc);
- if (destination && this.props.sourceDoc) {
- return <LinkMenuItem key={destination[Id] + this.props.sourceDoc[Id]} groupType={this.props.groupType}
- addDocTab={this.props.addDocTab}
- linkDoc={linkDoc} sourceDoc={this.props.sourceDoc} destinationDoc={destination} showEditor={this.props.showEditor} />;
- }
- });
-
- return (
- <div className="linkMenu-group">
- <div className="linkMenu-group-name">
- <p ref={this._drag} onPointerDown={this.onLinkButtonDown}
- className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"} > {this.props.groupType}:</p>
- {this.props.groupType === "*" || this.props.groupType === "" ? <></> : this.viewGroupAsTable(this.props.groupType)}
- </div>
- <div className="linkMenu-group-wrapper">
- {groupItems}
- </div>
- </div >
- );
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx
deleted file mode 100644
index 15f8c0786..000000000
--- a/src/client/views/nodes/LinkMenuItem.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEdit, faEye, faTimes, faArrowRight, faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { observer } from "mobx-react";
-import { DocumentManager } from "../../util/DocumentManager";
-import { undoBatch } from "../../util/UndoManager";
-import './LinkMenu.scss';
-import React = require("react");
-import { Doc, DocListCastAsync } from '../../../new_fields/Doc';
-import { StrCast, Cast, FieldValue, NumCast } from '../../../new_fields/Types';
-import { observable, action } from 'mobx';
-import { LinkManager } from '../../util/LinkManager';
-import { DragLinkAsDocument } from '../../util/DragManager';
-import { CollectionDockingView } from '../collections/CollectionDockingView';
-import { SelectionManager } from '../../util/SelectionManager';
-import { Utils } from '../../../Utils';
-import { Id } from '../../../new_fields/FieldSymbols';
-library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp);
-
-
-interface LinkMenuItemProps {
- groupType: string;
- linkDoc: Doc;
- sourceDoc: Doc;
- destinationDoc: Doc;
- showEditor: (linkDoc: Doc) => void;
- addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => void;
-}
-
-@observer
-export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
- private _drag = React.createRef<HTMLDivElement>();
- @observable private _showMore: boolean = false;
- @action toggleShowMore() { this._showMore = !this._showMore; }
-
- @undoBatch
- onFollowLink = async (e: React.PointerEvent): Promise<void> => {
- e.stopPropagation();
- e.persist();
- let jumpToDoc = this.props.destinationDoc;
- let pdfDoc = FieldValue(Cast(this.props.destinationDoc, Doc));
- if (pdfDoc) {
- jumpToDoc = pdfDoc;
- }
- let proto = Doc.GetProto(this.props.linkDoc);
- let targetContext = await Cast(proto.targetContext, Doc);
- let sourceContext = await Cast(proto.sourceContext, Doc);
- let guid = StrCast(this.props.linkDoc.guid);
- let self = this;
-
- let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
- if (e.ctrlKey) {
- dockingFunc = (document: Doc) => CollectionDockingView.Instance.AddRightSplit(document, undefined);
- }
-
- if (this.props.destinationDoc === self.props.linkDoc.anchor2 && targetContext) {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, async document => dockingFunc(document), undefined, targetContext);
- }
- else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!));
- if (guid) {
- console.log('wegotthis', StrCast(self.props.linkDoc.anchor2), jumpToDoc[Id]);
- jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(this.props.linkDoc.anchor2));
- jumpToDoc.guid = guid;
- } else { // retroactively fixing old in-text links by adding guid
- console.log('wegotthis', self.props.linkDoc.anchor2, jumpToDoc[Id]);
- jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(this.props.linkDoc.anchor2));
- let newguid = Utils.GenerateGuid();
- this.props.linkDoc.guid = newguid;
- jumpToDoc.guid = newguid;
- }
- }
- else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((this.props.destinationDoc === self.props.linkDoc.anchor2 ? self.props.linkDoc.anchor2Page : self.props.linkDoc.anchor1Page)));
- }
- else {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, dockingFunc);
- }
- }
-
- onEdit = (e: React.PointerEvent): void => {
- e.stopPropagation();
- this.props.showEditor(this.props.linkDoc);
- }
-
- renderMetadata = (): JSX.Element => {
- let groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc);
- let index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase());
- let groupDoc = index > -1 ? groups[index] : undefined;
-
- let mdRows: Array<JSX.Element> = [];
- if (groupDoc) {
- let mdDoc = Cast(groupDoc.metadata, Doc, null);
- if (mdDoc) {
- let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
- mdRows = keys.map(key => {
- return (<div key={key} className="link-metadata-row"><b>{key}</b>: {StrCast(mdDoc[key])}</div>);
- });
- }
- }
-
- return (<div className="link-metadata">{mdRows}</div>);
- }
-
- onLinkButtonDown = (e: React.PointerEvent): void => {
- e.stopPropagation();
- document.removeEventListener("pointermove", this.onLinkButtonMoved);
- document.addEventListener("pointermove", this.onLinkButtonMoved);
- document.removeEventListener("pointerup", this.onLinkButtonUp);
- document.addEventListener("pointerup", this.onLinkButtonUp);
- }
-
- onLinkButtonUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onLinkButtonMoved);
- document.removeEventListener("pointerup", this.onLinkButtonUp);
- e.stopPropagation();
- }
-
- onLinkButtonMoved = async (e: PointerEvent) => {
- if (this._drag.current !== null && (e.movementX > 1 || e.movementY > 1)) {
- document.removeEventListener("pointermove", this.onLinkButtonMoved);
- document.removeEventListener("pointerup", this.onLinkButtonUp);
-
- DragLinkAsDocument(this._drag.current, e.x, e.y, this.props.linkDoc, this.props.sourceDoc);
- }
- e.stopPropagation();
- }
-
- render() {
-
- let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType);
- let canExpand = keys ? keys.length > 0 : false;
-
- return (
- <div className="linkMenu-item">
- <div className={canExpand ? "linkMenu-item-content expand-three" : "linkMenu-item-content expand-two"}>
- <div className="link-name">
- <p ref={this._drag} onPointerDown={this.onLinkButtonDown}>{StrCast(this.props.destinationDoc.title)}</p>
- <div className="linkMenu-item-buttons">
- {canExpand ? <div title="Show more" className="button" onPointerDown={() => this.toggleShowMore()}>
- <FontAwesomeIcon className="fa-icon" icon={this._showMore ? "chevron-up" : "chevron-down"} size="sm" /></div> : <></>}
- <div title="Edit link" className="button" onPointerDown={this.onEdit}><FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div>
- <div title="Follow link" className="button" onPointerDown={this.onFollowLink}><FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" /></div>
- </div>
- </div>
- {this._showMore ? this.renderMetadata() : <></>}
- </div>
-
- </div >
- );
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 18f82ff47..df35b603c 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -33,7 +33,9 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
@observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
@computed get containingCollectionDocument() { return this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document; }
- @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; }
+ @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
+
+
@computed get fieldExtensionDoc() { return Doc.resolvedFieldDataDoc(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, "true"); }
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
@@ -48,7 +50,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
componentDidMount() {
this.props.setPdfBox && this.props.setPdfBox(this);
- const pdfUrl = Cast(this.props.Document.data, PdfField);
+ const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
if (pdfUrl instanceof PdfField) {
Pdfjs.getDocument(pdfUrl.url.pathname).promise.then(pdf => runInAction(() => this._pdf = pdf));
}
@@ -78,7 +80,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
@action
public GotoPage(p: number) {
- if (p > 0 && p <= NumCast(this.props.Document.numPages)) {
+ if (p > 0 && p <= NumCast(this.dataDoc.numPages)) {
this.props.Document.curPage = p;
this.props.Document.panY = (p - 1) * NumCast(this.dataDoc.nativeHeight);
}
@@ -87,7 +89,7 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
@action
public ForwardPage() {
let cp = this.GetPage() + 1;
- if (cp <= NumCast(this.props.Document.numPages)) {
+ if (cp <= NumCast(this.dataDoc.numPages)) {
this.props.Document.curPage = cp;
this.props.Document.panY = (cp - 1) * NumCast(this.dataDoc.nativeHeight);
}
@@ -185,11 +187,12 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
}
}
+
render() {
- const pdfUrl = Cast(this.props.Document.data, PdfField);
+ const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
return (!(pdfUrl instanceof PdfField) || !this._pdf ?
- <div>{`pdf, ${this.props.Document.data}, not found`}</div> :
+ <div>{`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}</div> :
<div className={classname}
onScroll={this.onScroll}
style={{ marginTop: `${this.containingCollectionDocument ? NumCast(this.containingCollectionDocument.panY) : 0}px` }}
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 43220df71..fbe9bf063 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -30,6 +30,7 @@
width: 100%;
height: 100%;
position: absolute;
+ pointer-events: all;
}
.webBox-button {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 642f58daf..29eef27a0 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,22 +1,27 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable } from "mobx";
import { observer } from "mobx-react";
-import { FieldResult, Doc } from "../../../new_fields/Doc";
+import { FieldResult, Doc, Field } from "../../../new_fields/Doc";
import { HtmlField } from "../../../new_fields/HtmlField";
-import { InkTool } from "../../../new_fields/InkField";
-import { Cast, NumCast } from "../../../new_fields/Types";
import { WebField } from "../../../new_fields/URLField";
-import { Utils } from "../../../Utils";
import { DocumentDecorations } from "../DocumentDecorations";
import { InkingControl } from "../InkingControl";
import { FieldView, FieldViewProps } from './FieldView';
-import { KeyValueBox } from "./KeyValueBox";
import "./WebBox.scss";
import React = require("react");
+import { InkTool } from "../../../new_fields/InkField";
+import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types";
+import { Utils } from "../../../Utils";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faStickyNote } from '@fortawesome/free-solid-svg-icons';
+import { observable, action, computed } from "mobx";
+import { listSpec } from "../../../new_fields/Schema";
+import { RefField } from "../../../new_fields/RefField";
+import { ObjectField } from "../../../new_fields/ObjectField";
+import { updateSourceFile } from "typescript";
+import { KeyValueBox } from "./KeyValueBox";
+import { setReactionScheduler } from "mobx/lib/internal";
+import { library } from "@fortawesome/fontawesome-svg-core";
import { SelectionManager } from "../../util/SelectionManager";
import { Docs } from "../../documents/Documents";
-import { faStickyNote } from "@fortawesome/free-solid-svg-icons";
-import { library } from "@fortawesome/fontawesome-svg-core";
library.add(faStickyNote);