aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/FormattedTextBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/FormattedTextBox.tsx')
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx95
1 files changed, 76 insertions, 19 deletions
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 6a490109f..df12f261b 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -1,38 +1,38 @@
import { library } from '@fortawesome/fontawesome-svg-core';
import { faEdit, faSmile } from '@fortawesome/free-solid-svg-icons';
-import { action, IReactionDisposer, observable, reaction } from "mobx";
+import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { baseKeymap } from "prosemirror-commands";
import { history } from "prosemirror-history";
import { keymap } from "prosemirror-keymap";
+import { NodeType } from 'prosemirror-model';
import { EditorState, Plugin, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
-import { Doc, Field, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
+import { Doc, Opt } from "../../../new_fields/Doc";
import { Id } from '../../../new_fields/FieldSymbols';
+import { List } from '../../../new_fields/List';
import { RichTextField } from "../../../new_fields/RichTextField";
-import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
import { DocServer } from "../../DocServer";
import { Docs } from '../../documents/Documents';
-import { DocumentManager } from "../../util/DocumentManager";
+import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from "../../util/DragManager";
-import buildKeymap from "../../util/ProsemirrorKeymap";
+import buildKeymap from "../../util/ProsemirrorExampleTransfer";
import { inpRules } from "../../util/RichTextRules";
-import { SummarizedView, ImageResizeView, schema } from "../../util/RichTextSchema";
+import { ImageResizeView, schema, SummarizedView } from "../../util/RichTextSchema";
import { SelectionManager } from "../../util/SelectionManager";
import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
import { TooltipTextMenu } from "../../util/TooltipTextMenu";
import { undoBatch, UndoManager } from "../../util/UndoManager";
import { ContextMenu } from "../../views/ContextMenu";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
+import { ContextMenuProps } from '../ContextMenuItem';
import { DocComponent } from "../DocComponent";
import { InkingControl } from "../InkingControl";
+import { Templates } from '../Templates';
import { FieldView, FieldViewProps } from "./FieldView";
import "./FormattedTextBox.scss";
import React = require("react");
-import { DocUtils } from '../../documents/Documents';
-import { start } from 'repl';
-import { ContextMenuProps } from '../ContextMenuItem';
library.add(faEdit);
library.add(faSmile);
@@ -60,13 +60,14 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
private _ref: React.RefObject<HTMLDivElement>;
- private _proseRef: React.RefObject<HTMLDivElement>;
+ private _proseRef?: HTMLDivElement;
private _editorView: Opt<EditorView>;
private _toolTipTextMenu: TooltipTextMenu | undefined = undefined;
private _applyingChange: boolean = false;
private _linkClicked = "";
private _reactionDisposer: Opt<IReactionDisposer>;
private _proxyReactionDisposer: Opt<IReactionDisposer>;
+ private dropDisposer?: DragManager.DragDropDisposer;
public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
@observable _entered = false;
@@ -98,7 +99,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
super(props);
this._ref = React.createRef();
- this._proseRef = React.createRef();
if (this.props.isOverlay) {
DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined);
}
@@ -122,6 +122,51 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
}
+ protected createDropTarget = (ele: HTMLDivElement) => {
+ this._proseRef = ele;
+ if (this.dropDisposer) {
+ this.dropDisposer();
+ }
+ if (ele) {
+ this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+ }
+ }
+
+ @undoBatch
+ @action
+ 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) {
+ // 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 })));
+ e.stopPropagation();
+ } else {
+ if (de.data instanceof DragManager.DocumentDragData) {
+ let ldocs = Cast(this.props.Document.subBulletDocs, listSpec(Doc));
+ if (!ldocs) {
+ this.props.Document.subBulletDocs = new List<Doc>([]);
+ }
+ ldocs = Cast(this.props.Document.subBulletDocs, listSpec(Doc));
+ if (!ldocs) return;
+ if (!ldocs || !ldocs[0] || ldocs[0] instanceof Promise || StrCast((ldocs[0] as Doc).layout).indexOf("CollectionView") === -1) {
+ ldocs.splice(0, 0, Docs.StackingDocument([], { title: StrCast(this.props.Document.title) + "-subBullets", x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y) + NumCast(this.props.Document.height), width: 300, height: 300 }));
+ this.props.addDocument && this.props.addDocument(ldocs[0] as Doc);
+ this.props.Document.templates = new List<string>([Templates.Bullet.Layout]);
+ this.props.Document.isBullet = true;
+ }
+ let stackDoc = (ldocs[0] as Doc);
+ if (de.data.moveDocument) {
+ de.data.moveDocument(de.data.draggedDocuments[0], stackDoc, (doc) => {
+ Cast(stackDoc.data, listSpec(Doc))!.push(doc);
+ return true;
+ });
+ }
+ }
+ }
+ }
+
componentDidMount() {
const config = {
schema,
@@ -151,7 +196,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
FormattedTextBox.InputBoxOverlay = this;
FormattedTextBox.InputBoxOverlayScroll = this._ref.current!.scrollTop;
}
- });
+ }, { fireImmediately: true });
}
this._reactionDisposer = reaction(
@@ -172,8 +217,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (!startup && !field && doc) {
startup = StrCast(doc[fieldKey]);
}
- if (this._proseRef.current) {
- this._editorView = new EditorView(this._proseRef.current, {
+ if (this._proseRef) {
+ this._editorView = new EditorView(this._proseRef, {
state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config),
dispatchTransaction: this.dispatchTransaction,
nodeViews: {
@@ -221,6 +266,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (href) {
if (href.indexOf(DocServer.prepend("/doc/")) === 0) {
this._linkClicked = href.replace(DocServer.prepend("/doc/"), "").split("?")[0];
+ if (this._linkClicked) {
+ DocServer.GetRefField(this._linkClicked).then(f => {
+ (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, "inTab"));
+ });
+ e.stopPropagation();
+ e.preventDefault();
+ }
} else {
let webDoc = Docs.WebDocument(href, { x: NumCast(this.props.Document.x, 0) + NumCast(this.props.Document.width, 0), y: NumCast(this.props.Document.y) });
this.props.addDocument && this.props.addDocument(webDoc);
@@ -261,7 +313,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
onClick = (e: React.MouseEvent): void => {
- this._proseRef.current!.focus();
+ this._proseRef!.focus();
+ if (this._linkClicked) {
+ this._linkClicked = "";
+ e.preventDefault();
+ e.stopPropagation();
+ }
}
onMouseDown = (e: React.MouseEvent): void => {
if (!this.props.isSelected()) { // preventing default allows the onClick to be generated instead of being swallowed by the text box itself
@@ -336,7 +393,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let subitems: ContextMenuProps[] = [];
subitems.push({
description: BoolCast(this.props.Document.autoHeight, false) ? "Manual Height" : "Auto Height",
- event: action(() => this.props.Document.autoHeight = !BoolCast(this.props.Document.autoHeight, false)), icon: "expand-arrows-alt"
+ event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight, false)), icon: "expand-arrows-alt"
});
ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems });
}
@@ -353,7 +410,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
color: this.props.color ? this.props.color : this.props.hideOnLeave ? "white" : "initial",
pointerEvents: interactive ? "all" : "none",
}}
- onKeyPress={this.onKeyPress}
+ onKeyDown={this.onKeyPress}
onFocus={this.onFocused}
onClick={this.onClick}
onContextMenu={this.specificContextMenu}
@@ -366,7 +423,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
onPointerEnter={this.onPointerEnter}
onPointerLeave={this.onPointerLeave}
>
- <div className={`formattedTextBox-inner${rounded}`} style={{ whiteSpace: "pre-wrap", pointerEvents: this.props.Document.isButton && !this.props.isSelected() ? "none" : "all" }} ref={this._proseRef} />
+ <div className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget} style={{ whiteSpace: "pre-wrap", pointerEvents: this.props.Document.isButton && !this.props.isSelected() ? "none" : "all" }} />
</div>
);
}