diff options
author | bob <bcz@cs.brown.edu> | 2019-10-03 17:52:35 -0400 |
---|---|---|
committer | bob <bcz@cs.brown.edu> | 2019-10-03 17:52:35 -0400 |
commit | 460cec4d786e026dabdb8fb873284f6574799367 (patch) | |
tree | afa8353737106480940bff2c78fe6297d4a56ef5 /src | |
parent | 48ac0b54cfe29b97a7add72b2369bfc2896f98f7 (diff) |
start of embedding documents in text notes.
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/RichTextSchema.tsx | 143 | ||||
-rw-r--r-- | src/client/views/DocumentButtonBar.tsx | 2 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 36 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 24 |
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(); }, |