aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/DragManager.ts15
-rw-r--r--src/client/util/ProsemirrorCopy/prompt.js179
-rw-r--r--src/client/util/ProsemirrorExampleTransfer.ts (renamed from src/client/util/ProsemirrorKeymap.ts)2
-rw-r--r--src/client/util/RichTextSchema.tsx26
-rw-r--r--src/client/util/TooltipTextMenu.tsx77
-rw-r--r--src/client/views/DocumentDecorations.tsx50
-rw-r--r--src/client/views/PresentationView.tsx1
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx52
-rw-r--r--src/debug/Test.tsx1
-rw-r--r--src/server/index.ts1
10 files changed, 385 insertions, 19 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 8ee1ad2af..ab00fabe1 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -4,6 +4,7 @@ import { Cast } from "../../new_fields/Types";
import { emptyFunction } from "../../Utils";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import * as globalCssVariables from "../views/globalCssVariables.scss";
+import { URLField } from "../../new_fields/URLField";
import { SelectionManager } from "./SelectionManager";
export type dropActionType = "alias" | "copy" | undefined;
@@ -181,10 +182,24 @@ export namespace DragManager {
[id: string]: any;
}
+ export class EmbedDragData {
+ constructor(embeddableSourceDoc: Doc) {
+ this.embeddableSourceDoc = embeddableSourceDoc;
+ this.urlField = embeddableSourceDoc.data instanceof URLField ? embeddableSourceDoc.data : undefined;
+ }
+ embeddableSourceDoc: Doc;
+ urlField?: URLField;
+ [id: string]: any;
+ }
+
export function StartLinkDrag(ele: HTMLElement, dragData: LinkDragData, downX: number, downY: number, options?: DragOptions) {
StartDrag([ele], dragData, downX, downY, options);
}
+ export function StartEmbedDrag(ele: HTMLElement, dragData: EmbedDragData, downX: number, downY: number, options?: DragOptions) {
+ StartDrag([ele], dragData, downX, downY, options);
+ }
+
export let AbortDrag: () => void = emptyFunction;
function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) {
diff --git a/src/client/util/ProsemirrorCopy/prompt.js b/src/client/util/ProsemirrorCopy/prompt.js
new file mode 100644
index 000000000..b9068195f
--- /dev/null
+++ b/src/client/util/ProsemirrorCopy/prompt.js
@@ -0,0 +1,179 @@
+const prefix = "ProseMirror-prompt"
+
+export function openPrompt(options) {
+ let wrapper = document.body.appendChild(document.createElement("div"))
+ wrapper.className = prefix
+ wrapper.style.zIndex = 1000;
+ wrapper.style.width = 250;
+ wrapper.style.textAlign = "center";
+
+ let mouseOutside = e => { if (!wrapper.contains(e.target)) close() }
+ setTimeout(() => window.addEventListener("mousedown", mouseOutside), 50)
+ let close = () => {
+ window.removeEventListener("mousedown", mouseOutside)
+ if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper)
+ }
+
+ let domFields = []
+ for (let name in options.fields) domFields.push(options.fields[name].render())
+
+ let submitButton = document.createElement("button")
+ submitButton.type = "submit"
+ submitButton.className = prefix + "-submit"
+ submitButton.textContent = "OK"
+ let cancelButton = document.createElement("button")
+ cancelButton.type = "button"
+ cancelButton.className = prefix + "-cancel"
+ cancelButton.textContent = "Cancel"
+ cancelButton.addEventListener("click", close)
+
+ let form = wrapper.appendChild(document.createElement("form"))
+ let title = document.createElement("h5")
+ title.style.marginBottom = 15
+ title.style.marginTop = 10
+ if (options.title) form.appendChild(title).textContent = options.title
+ domFields.forEach(field => {
+ form.appendChild(document.createElement("div")).appendChild(field)
+ })
+ let b = document.createElement("div");
+ b.style.marginTop = 15;
+ let buttons = form.appendChild(b)
+ // buttons.className = prefix + "-buttons"
+ buttons.appendChild(submitButton)
+ buttons.appendChild(document.createTextNode(" "))
+ buttons.appendChild(cancelButton)
+
+ let box = wrapper.getBoundingClientRect()
+ wrapper.style.top = options.flyout_top + "px"
+ wrapper.style.left = options.flyout_left + "px"
+
+ let submit = () => {
+ let params = getValues(options.fields, domFields)
+ if (params) {
+ close()
+ options.callback(params)
+ }
+ }
+
+ form.addEventListener("submit", e => {
+ e.preventDefault()
+ submit()
+ })
+
+ form.addEventListener("keydown", e => {
+ if (e.keyCode == 27) {
+ e.preventDefault()
+ close()
+ } else if (e.keyCode == 13 && !(e.ctrlKey || e.metaKey || e.shiftKey)) {
+ e.preventDefault()
+ submit()
+ } else if (e.keyCode == 9) {
+ window.setTimeout(() => {
+ if (!wrapper.contains(document.activeElement)) close()
+ }, 500)
+ }
+ })
+
+ let input = form.elements[0]
+ if (input) input.focus()
+}
+
+function getValues(fields, domFields) {
+ let result = Object.create(null), i = 0
+ for (let name in fields) {
+ let field = fields[name], dom = domFields[i++]
+ let value = field.read(dom), bad = field.validate(value)
+ if (bad) {
+ reportInvalid(dom, bad)
+ return null
+ }
+ result[name] = field.clean(value)
+ }
+ return result
+}
+
+function reportInvalid(dom, message) {
+ // FIXME this is awful and needs a lot more work
+ let parent = dom.parentNode
+ let msg = parent.appendChild(document.createElement("div"))
+ msg.style.left = (dom.offsetLeft + dom.offsetWidth + 2) + "px"
+ msg.style.top = (dom.offsetTop - 5) + "px"
+ msg.className = "ProseMirror-invalid"
+ msg.textContent = message
+ setTimeout(() => parent.removeChild(msg), 1500)
+}
+
+// ::- The type of field that `FieldPrompt` expects to be passed to it.
+export class Field {
+ // :: (Object)
+ // Create a field with the given options. Options support by all
+ // field types are:
+ //
+ // **`value`**`: ?any`
+ // : The starting value for the field.
+ //
+ // **`label`**`: string`
+ // : The label for the field.
+ //
+ // **`required`**`: ?bool`
+ // : Whether the field is required.
+ //
+ // **`validate`**`: ?(any) → ?string`
+ // : A function to validate the given value. Should return an
+ // error message if it is not valid.
+ constructor(options) { this.options = options }
+
+ // render:: (state: EditorState, props: Object) → dom.Node
+ // Render the field to the DOM. Should be implemented by all subclasses.
+
+ // :: (dom.Node) → any
+ // Read the field's value from its DOM node.
+ read(dom) { return dom.value }
+
+ // :: (any) → ?string
+ // A field-type-specific validation function.
+ validateType(_value) { }
+
+ validate(value) {
+ if (!value && this.options.required)
+ return "Required field"
+ return this.validateType(value) || (this.options.validate && this.options.validate(value))
+ }
+
+ clean(value) {
+ return this.options.clean ? this.options.clean(value) : value
+ }
+}
+
+// ::- A field class for single-line text fields.
+export class TextField extends Field {
+ render() {
+ let input = document.createElement("input")
+ input.type = "text"
+ input.placeholder = this.options.label
+ input.value = this.options.value || ""
+ input.autocomplete = "off"
+ input.style.marginBottom = 4
+ input.style.border = "1px solid black"
+ input.style.padding = "4px 4px"
+ return input
+ }
+}
+
+
+// ::- A field class for dropdown fields based on a plain `<select>`
+// tag. Expects an option `options`, which should be an array of
+// `{value: string, label: string}` objects, or a function taking a
+// `ProseMirror` instance and returning such an array.
+export class SelectField extends Field {
+ render() {
+ let select = document.createElement("select")
+ this.options.options.forEach(o => {
+ let opt = select.appendChild(document.createElement("option"))
+ opt.value = o.value
+ opt.selected = o.value == this.options.value
+ opt.label = o.label
+ })
+ return select
+ }
+}
diff --git a/src/client/util/ProsemirrorKeymap.ts b/src/client/util/ProsemirrorExampleTransfer.ts
index cf656dc17..091926d0a 100644
--- a/src/client/util/ProsemirrorKeymap.ts
+++ b/src/client/util/ProsemirrorExampleTransfer.ts
@@ -97,4 +97,4 @@ export default function buildKeymap<S extends Schema<any>>(schema: S, mapKeys?:
}
return keys;
-} \ No newline at end of file
+}
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 61ca4af5e..e1e595925 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -139,6 +139,32 @@ export const nodes: { [index: string]: NodeSpec } = {
}
},
+ video: {
+ inline: true,
+ attrs: {
+ src: {},
+ width: { default: "100px" },
+ alt: { default: null },
+ title: { default: null }
+ },
+ group: "inline",
+ draggable: true,
+ parseDOM: [{
+ tag: "video[src]", getAttrs(dom: any) {
+ return {
+ src: dom.getAttribute("src"),
+ title: dom.getAttribute("title"),
+ alt: dom.getAttribute("alt"),
+ width: Math.min(100, Number(dom.getAttribute("width"))),
+ }
+ }
+ }],
+ toDOM(node) {
+ const attrs = { style: `width: ${node.attrs.width}` }
+ return ["video", { ...node.attrs, ...attrs }]
+ }
+ },
+
// :: NodeSpec A hard line break, represented in the DOM as `<br>`.
hard_break: {
inline: true,
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index e12d4ed3c..0a61b7e7d 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -14,11 +14,12 @@ import { library } from '@fortawesome/fontawesome-svg-core';
import { wrapInList, bulletList, liftListItem, listItem, } from 'prosemirror-schema-list';
import { liftTarget, RemoveMarkStep, AddMarkStep } from 'prosemirror-transform';
import {
- faListUl,
+ faListUl, faGrinTongueSquint,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FieldViewProps } from "../views/nodes/FieldView";
import { throwStatement } from "babel-types";
+const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js");
import { View } from "@react-pdf/renderer";
import { DragManager } from "./DragManager";
import { Doc, Opt, Field } from "../../new_fields/Doc";
@@ -47,6 +48,8 @@ export class TooltipTextMenu {
private fontStylesToName: Map<MarkType, string>;
private listTypeToIcon: Map<NodeType, string>;
private fontSizeIndicator: HTMLSpanElement = document.createElement("span");
+ private link: HTMLAnchorElement;
+
private linkEditor?: HTMLDivElement;
private linkText?: HTMLDivElement;
private linkDrag?: HTMLImageElement;
@@ -125,6 +128,13 @@ export class TooltipTextMenu {
this.listTypeToIcon.set(schema.nodes.ordered_list, "1)");
this.listTypes = Array.from(this.listTypeToIcon.keys());
+ this.link = document.createElement("a");
+ this.link.target = "_blank";
+ this.link.style.color = "white";
+ this.tooltip.appendChild(this.link);
+
+ this.tooltip.appendChild(this.createLink().render(this.view).dom);
+
this.update(view, undefined);
}
@@ -363,6 +373,43 @@ export class TooltipTextMenu {
});
}
+ createLink() {
+ let markType = schema.marks.link;
+ return new MenuItem({
+ title: "Add or remove link",
+ label: "Add or remove link",
+ execEvent: "",
+ icon: icons.link,
+ css: "color:white;",
+ class: "menuicon",
+ enable(state) { return !state.selection.empty; },
+ run: (state, dispatch, view) => {
+ // to remove link
+ if (this.markActive(state, markType)) {
+ toggleMark(markType)(state, dispatch);
+ return true;
+ }
+ // to create link
+ openPrompt({
+ title: "Create a link",
+ fields: {
+ href: new TextField({
+ label: "Link target",
+ required: true
+ }),
+ title: new TextField({ label: "Title" })
+ },
+ callback(attrs: any) {
+ toggleMark(markType, attrs)(view.state, view.dispatch);
+ view.focus();
+ },
+ flyout_top: 0,
+ flyout_left: 0
+ });
+ }
+ });
+ }
+
//makes a button for the drop down FOR NODE TYPES
//css is the style you want applied to the button
dropdownNodeBtn(label: string, css: string, nodeType: NodeType | undefined, view: EditorView, groupNodes: NodeType[], changeToNodeInGroup: (nodeType: NodeType<any> | undefined, view: EditorView, groupNodes: NodeType[]) => any) {
@@ -379,6 +426,12 @@ export class TooltipTextMenu {
});
}
+ markActive = function (state: EditorState<any>, type: MarkType<Schema<string, string>>) {
+ let { from, $from, to, empty } = state.selection;
+ if (empty) return type.isInSet(state.storedMarks || $from.marks());
+ else return state.doc.rangeHasMark(from, to, type);
+ };
+
// Helper function to create menu icons
icon(text: string, name: string, title: string = name) {
let span = document.createElement("span");
@@ -426,6 +479,20 @@ export class TooltipTextMenu {
};
}
+ getMarksInSelection(state: EditorState<any>, targets: MarkType<any>[]) {
+ let found: Mark<any>[] = [];
+ let { from, to } = state.selection as TextSelection;
+ state.doc.nodesBetween(from, to, (node) => {
+ let marks = node.marks;
+ if (marks) {
+ marks.forEach(m => {
+ if (targets.includes(m.type)) found.push(m);
+ });
+ }
+ });
+ return found;
+ }
+
//updates the tooltip menu when the selection changes
update(view: EditorView, lastState: EditorState | undefined) {
let state = view.state;
@@ -439,6 +506,14 @@ export class TooltipTextMenu {
return;
}
+ let linksInSelection = this.activeMarksOnSelection([schema.marks.link]);
+ if (linksInSelection.length > 0) {
+ let attributes = this.getMarksInSelection(this.view.state, [schema.marks.link])[0].attrs;
+ this.link.href = attributes.href;
+ this.link.textContent = attributes.title;
+ this.link.style.visibility = "visible";
+ } else this.link.style.visibility = "hidden";
+
// Otherwise, reposition it and update its content
this.tooltip.style.display = "";
let { from, to } = state.selection;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 1111cd2f5..ceca940b6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -24,6 +24,7 @@ import { LinkMenu } from "./nodes/LinkMenu";
import { TemplateMenu } from "./TemplateMenu";
import { Template, Templates } from "./Templates";
import React = require("react");
+import { URLField } from '../../new_fields/URLField';
const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -41,6 +42,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
private _titleHeight = 20;
private _linkButton = React.createRef<HTMLDivElement>();
private _linkerButton = React.createRef<HTMLDivElement>();
+ private _embedButton = React.createRef<HTMLDivElement>();
private _downX = 0;
private _downY = 0;
private _iconDoc?: Doc = undefined;
@@ -329,12 +331,27 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
document.removeEventListener("pointerup", this.onLinkerButtonUp);
document.addEventListener("pointerup", this.onLinkerButtonUp);
}
+
+ onEmbedButtonDown = (e: React.PointerEvent): void => {
+ e.stopPropagation();
+ document.removeEventListener("pointermove", this.onEmbedButtonMoved);
+ document.addEventListener("pointermove", this.onEmbedButtonMoved);
+ document.removeEventListener("pointerup", this.onEmbedButtonUp);
+ document.addEventListener("pointerup", this.onEmbedButtonUp);
+ }
+
onLinkerButtonUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onLinkerButtonMoved);
document.removeEventListener("pointerup", this.onLinkerButtonUp);
e.stopPropagation();
}
+ onEmbedButtonUp = (e: PointerEvent): void => {
+ document.removeEventListener("pointermove", this.onEmbedButtonMoved);
+ document.removeEventListener("pointerup", this.onEmbedButtonUp);
+ e.stopPropagation();
+ }
+
@action
onLinkerButtonMoved = (e: PointerEvent): void => {
if (this._linkerButton.current !== null) {
@@ -354,6 +371,25 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
e.stopPropagation();
}
+ @action
+ onEmbedButtonMoved = (e: PointerEvent): void => {
+ if (this._embedButton.current !== null) {
+ document.removeEventListener("pointermove", this.onEmbedButtonMoved);
+ document.removeEventListener("pointerup", this.onEmbedButtonUp);
+
+ let dragDocView = SelectionManager.SelectedDocuments()[0];
+ let dragData = new DragManager.EmbedDragData(dragDocView.props.Document);
+
+ DragManager.StartEmbedDrag(dragDocView.ContentDiv!, dragData, e.x, e.y, {
+ handlers: {
+ dragComplete: action(emptyFunction),
+ },
+ hideSource: false
+ });
+ }
+ e.stopPropagation();
+ }
+
onLinkButtonDown = (e: React.PointerEvent): void => {
e.stopPropagation();
document.removeEventListener("pointermove", this.onLinkButtonMoved);
@@ -511,6 +547,19 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
// e.stopPropagation();
// }
+ considerEmbed = () => {
+ let thisDoc = SelectionManager.SelectedDocuments()[0].props.Document;
+ let canEmbed = thisDoc.data && thisDoc.data instanceof URLField;
+ if (!canEmbed) return (null);
+ return (
+ <div className="linkButtonWrapper">
+ <div style={{ paddingTop: 3, marginLeft: 30 }} title="Drag Embed" className="linkButton-linker" ref={this._embedButton} onPointerDown={this.onEmbedButtonDown}>
+ <FontAwesomeIcon className="fa-image" icon="image" size="sm" />
+ </div>
+ </div>
+ );
+ }
+
render() {
var bounds = this.Bounds;
let seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined;
@@ -605,6 +654,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
</div>
</div>
<TemplateMenu docs={SelectionManager.ViewsSortedVertically()} templates={templates} />
+ {this.considerEmbed()}
</div>
</div >
</div>
diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx
index b0c93ee26..d2d41a4ba 100644
--- a/src/client/views/PresentationView.tsx
+++ b/src/client/views/PresentationView.tsx
@@ -18,7 +18,6 @@ interface PresListProps extends PresViewProps {
gotoDocument(index: number): void;
}
-
@observer
/**
* Component that takes in a document prop and a boolean whether it's collapsed or not.
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 6a490109f..0a4a67211 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -5,34 +5,31 @@ 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 { RichTextField } from "../../../new_fields/RichTextField";
import { createSchema, 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 { 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 { 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 +57,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 +96,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 +119,29 @@ 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();
+ }
+ }
+
componentDidMount() {
const config = {
schema,
@@ -172,8 +192,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: {
@@ -261,7 +281,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
onClick = (e: React.MouseEvent): void => {
- this._proseRef.current!.focus();
+ this._proseRef!.focus();
+ if (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
@@ -366,7 +390,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>
);
}
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index 57221aa39..0dca4b4b1 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -6,7 +6,6 @@ import { ImageField } from '../new_fields/URLField';
import { Doc } from '../new_fields/Doc';
import { List } from '../new_fields/List';
-
const schema1 = createSchema({
hello: "number",
test: "string",
diff --git a/src/server/index.ts b/src/server/index.ts
index fd66c90b4..eda1ab422 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -9,7 +9,6 @@ import * as fs from 'fs';
import * as sharp from 'sharp';
const imageDataUri = require('image-data-uri');
import * as mobileDetect from 'mobile-detect';
-import { ObservableMap } from 'mobx';
import * as passport from 'passport';
import * as path from 'path';
import * as request from 'request';