aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbob <bcz@cs.brown.edu>2019-10-03 17:52:35 -0400
committerbob <bcz@cs.brown.edu>2019-10-03 17:52:35 -0400
commit460cec4d786e026dabdb8fb873284f6574799367 (patch)
treeafa8353737106480940bff2c78fe6297d4a56ef5 /src
parent48ac0b54cfe29b97a7add72b2369bfc2896f98f7 (diff)
start of embedding documents in text notes.
Diffstat (limited to 'src')
-rw-r--r--src/client/util/RichTextSchema.tsx143
-rw-r--r--src/client/views/DocumentButtonBar.tsx2
-rw-r--r--src/client/views/DocumentDecorations.tsx36
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx1
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx24
5 files changed, 158 insertions, 48 deletions
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 066266873..7be773475 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -9,11 +9,16 @@ import { EditorView } from "prosemirror-view";
import { Doc } from "../../new_fields/Doc";
import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
import { DocServer } from "../DocServer";
-import { Cast, NumCast } from "../../new_fields/Types";
import { DocumentManager } from "./DocumentManager";
import ParagraphNodeSpec from "./ParagraphNodeSpec";
-import { times } from "async";
-import { LinkManager } from "./LinkManager";
+import React = require("react");
+import { action, Lambda, observable, reaction, computed, runInAction, trace } from "mobx";
+import { observer } from "mobx-react";
+import * as ReactDOM from 'react-dom';
+import { DocumentView } from "../views/nodes/DocumentView";
+import { returnFalse, emptyFunction, returnEmptyString, returnOne } from "../../Utils";
+import { Transform } from "./Transform";
+import { NumCast } from "../../new_fields/Types";
const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -158,6 +163,35 @@ export const nodes: { [index: string]: NodeSpec } = {
}
},
+ dashDoc: {
+ inline: true,
+ attrs: {
+ width: { default: 200 },
+ height: { default: 100 },
+ title: { default: null },
+ float: { default: "left" },
+ location: { default: "onRight" },
+ docid: { default: "" }
+ },
+ group: "inline",
+ draggable: true,
+ // parseDOM: [{
+ // tag: "img[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"))),
+ // };
+ // }
+ // }],
+ // TODO if we don't define toDom, dragging the image crashes. Why?
+ toDOM(node) {
+ const attrs = { style: `width: ${node.attrs.width}, height: ${node.attrs.height}` };
+ return ["div", { ...node.attrs, ...attrs }];
+ }
+ },
+
video: {
inline: true,
attrs: {
@@ -671,6 +705,109 @@ export class ImageResizeView {
}
}
+export class DashDocView {
+ _handle: HTMLElement;
+ _dashSpan: HTMLDivElement;
+ _outer: HTMLElement;
+ constructor(node: any, view: any, getPos: any, addDocTab: any) {
+ this._handle = document.createElement("span");
+ this._dashSpan = document.createElement("div");
+ this._outer = document.createElement("span");
+ this._outer.style.position = "relative";
+ this._outer.style.width = node.attrs.width;
+ this._outer.style.height = node.attrs.height;
+ this._outer.style.display = "inline-block";
+ this._outer.style.overflow = "hidden";
+ (this._outer.style as any).float = node.attrs.float;
+
+ this._dashSpan.style.width = "100%";
+ this._dashSpan.style.height = "100%";
+ this._dashSpan.style.position = "absolute";
+ this._dashSpan.style.display = "inline-block"
+ this._handle.style.position = "absolute";
+ this._handle.style.width = "20px";
+ this._handle.style.height = "20px";
+ this._handle.style.backgroundColor = "blue";
+ this._handle.style.borderRadius = "15px";
+ this._handle.style.display = "none";
+ this._handle.style.bottom = "-10px";
+ this._handle.style.right = "-10px";
+ DocServer.GetRefField(node.attrs.docid).then(async dashDoc => {
+ if (dashDoc instanceof Doc) {
+ let scale = () => 100 / NumCast(dashDoc.nativeWidth, 100);
+ ReactDOM.render(<DocumentView
+ fitToBox={true}
+ Document={dashDoc}
+ addDocument={returnFalse}
+ removeDocument={returnFalse}
+ ruleProvider={undefined}
+ ScreenToLocalTransform={Transform.Identity}
+ addDocTab={returnFalse}
+ pinToPres={returnFalse}
+ renderDepth={1}
+ PanelWidth={() => 100}
+ PanelHeight={() => 100}
+ focus={emptyFunction}
+ backgroundColor={returnEmptyString}
+ parentActive={returnFalse}
+ whenActiveChanged={returnFalse}
+ bringToFront={emptyFunction}
+ zoomToScale={emptyFunction}
+ getScale={returnOne}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ ContentScaling={scale}
+ ></DocumentView>, this._dashSpan);
+ }
+ });
+ let self = this;
+ this._dashSpan.onpointerdown = function (e: any) {
+ };
+ this._handle.onpointerdown = function (e: any) {
+ e.preventDefault();
+ e.stopPropagation();
+ const startX = e.pageX;
+ const startWidth = parseFloat(node.attrs.width);
+ const onpointermove = (e: any) => {
+ const currentX = e.pageX;
+ const diffInPx = currentX - startX;
+ self._outer.style.width = `${startWidth + diffInPx}`;
+ //Array.from(FormattedTextBox.InputBoxOverlay!.CurrentDiv.getElementsByTagName("img")).map((img: any) => img.opacity = "0.1");
+ FormattedTextBox.InputBoxOverlay!.CurrentDiv.style.opacity = "0";
+ };
+
+ const onpointerup = () => {
+ document.removeEventListener("pointermove", onpointermove);
+ document.removeEventListener("pointerup", onpointerup);
+ view.dispatch(
+ view.state.tr.setSelection(view.state.selection).setNodeMarkup(getPos(), null,
+ { ...node.attrs, width: self._outer.style.width })
+ );
+ FormattedTextBox.InputBoxOverlay!.CurrentDiv.style.opacity = "1";
+ };
+
+ document.addEventListener("pointermove", onpointermove);
+ document.addEventListener("pointerup", onpointerup);
+ };
+
+ this._outer.appendChild(this._handle);
+ this._outer.appendChild(this._dashSpan);
+ (this as any).dom = this._outer;
+ }
+
+ selectNode() {
+ this._dashSpan.classList.add("ProseMirror-selectednode");
+
+ this._handle.style.display = "";
+ }
+
+ deselectNode() {
+ this._dashSpan.classList.remove("ProseMirror-selectednode");
+
+ this._handle.style.display = "none";
+ }
+}
+
export class OrderedListView {
update(node: any) {
return false; // if attr's of an ordered_list (e.g., bulletStyle) change, return false forces the dom node to be recreated which is necessary for the bullet labels to update
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 338f6b83e..e57745b86 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -203,7 +203,7 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[],
considerEmbed = () => {
let thisDoc = this.props.views[0].props.Document;
let canEmbed = thisDoc.data && thisDoc.data instanceof URLField;
- if (!canEmbed) return (null);
+ // if (!canEmbed) return (null);
return (
<div className="linkButtonWrapper">
<div title="Drag Embed" className="linkButton-linker" ref={this._embedButton} onPointerDown={this.onEmbedButtonDown}>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9d42eb719..26ffaf3a6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -48,7 +48,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
private _resizeBorderWidth = 16;
private _linkBoxHeight = 20 + 3; // link button height + margin
private _titleHeight = 20;
- private _embedButton = React.createRef<HTMLDivElement>();
private _downX = 0;
private _downY = 0;
private _iconDoc?: Doc = undefined;
@@ -414,41 +413,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
}
- 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);
- }
-
-
-
- onEmbedButtonUp = (e: PointerEvent): void => {
- document.removeEventListener("pointermove", this.onEmbedButtonMoved);
- document.removeEventListener("pointerup", this.onEmbedButtonUp);
- 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();
- }
-
onPointerMove = (e: PointerEvent): void => {
e.stopPropagation();
e.preventDefault();
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 14e513157..fe805a980 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -31,7 +31,6 @@ import { SubCollectionViewProps } from "./CollectionSubView";
import React = require("react");
import { ButtonSelector } from './ParentDocumentSelector';
import { DocumentType } from '../../documents/DocumentTypes';
-import { compileFunction } from 'vm';
import { ComputedField } from '../../../new_fields/ScriptField';
library.add(faFile);
const _global = (window /* browser */ || global /* node */) as any;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index f84301c10..819accf20 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -12,7 +12,7 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "
import { ReplaceStep } from 'prosemirror-transform';
import { EditorView } from "prosemirror-view";
import { DateField } from '../../../new_fields/DateField';
-import { Doc, DocListCastAsync, Opt, WidthSym } from "../../../new_fields/Doc";
+import { Doc, DocListCastAsync, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc";
import { Copy, Id } from '../../../new_fields/FieldSymbols';
import { RichTextField } from "../../../new_fields/RichTextField";
import { RichTextUtils } from '../../../new_fields/RichTextUtils';
@@ -28,7 +28,7 @@ import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from "../../util/DragManager";
import buildKeymap from "../../util/ProsemirrorExampleTransfer";
import { inpRules } from "../../util/RichTextRules";
-import { FootnoteView, ImageResizeView, OrderedListView, schema, SummarizedView } from "../../util/RichTextSchema";
+import { FootnoteView, ImageResizeView, DashDocView, OrderedListView, schema, SummarizedView } from "../../util/RichTextSchema";
import { SelectionManager } from "../../util/SelectionManager";
import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu";
import { TooltipTextMenu } from "../../util/TooltipTextMenu";
@@ -289,14 +289,23 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
@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) {
+ if (de.data instanceof DragManager.EmbedDragData) {
let target = de.data.embeddableSourceDoc;
// We're dealing with an internal document drop
- let url = de.data.urlField.url.href;
- let model: NodeType = [".mov", ".mp4"].includes(url) ? schema.nodes.video : schema.nodes.image;
+ const link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "ImgRef:" + target.title);
+ let node: Node<any>;
+ if (de.data.urlField && link) {
+ let url: string = de.data.urlField.url.href;
+ let model: NodeType = [".mov", ".mp4"].includes(url) ? schema.nodes.video : schema.nodes.image;
+ node = model.create({ src: url, docid: link[Id] })
+ } else {
+ node = schema.nodes.dashDoc.create({
+ width: this.props.Document[WidthSym](), height: this.props.Document[HeightSym](),
+ title: "dashDoc", docid: target[Id]
+ });
+ }
let pos = this._editorView!.posAtCoords({ left: de.x, top: de.y });
- let link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "ImgRef:" + target.title);
- link && this._editorView!.dispatch(this._editorView!.state.tr.insert(pos!.pos, model.create({ src: url, docid: link[Id] })));
+ link && this._editorView!.dispatch(this._editorView!.state.tr.insert(pos!.pos, node));
this.tryUpdateHeight();
e.stopPropagation();
} else if (de.data instanceof DragManager.DocumentDragData) {
@@ -736,6 +745,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
},
dispatchTransaction: this.dispatchTransaction,
nodeViews: {
+ dashDoc(node, view, getPos) { return new DashDocView(node, view, getPos, self.props.addDocTab); },
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(); },