From 59c52553a942d327b3cdf7377eb85f4b0b19c2dd Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 8 May 2019 23:52:13 -0400 Subject: added imageResize handle in text boxes. --- src/client/util/TooltipTextMenu.tsx | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 68a73375e..e33c53a1a 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -94,6 +94,7 @@ export class TooltipTextMenu { this.fontSizeToNum = new Map(); this.fontSizeToNum.set(schema.marks.p10, 10); this.fontSizeToNum.set(schema.marks.p12, 12); + this.fontSizeToNum.set(schema.marks.p14, 14); this.fontSizeToNum.set(schema.marks.p16, 16); this.fontSizeToNum.set(schema.marks.p24, 24); this.fontSizeToNum.set(schema.marks.p32, 32); -- cgit v1.2.3-70-g09d2 From 9015421a227ab58f309906eabf884654d3a31a17 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 9 May 2019 11:58:36 -0400 Subject: added start of hyperlinks to text boxes --- src/client/util/DragManager.ts | 3 +- src/client/util/TooltipTextMenu.tsx | 101 +++++++++++++++++++++++++++++++- src/client/views/nodes/DocumentView.tsx | 1 + 3 files changed, 102 insertions(+), 3 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index ec192eaff..fbf20e244 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -172,6 +172,7 @@ export namespace DragManager { droppedDocuments: Doc[] = []; linkSourceDocument: Doc; blacklist: Doc[]; + dontClearTextBox?: boolean; [id: string]: any; } @@ -188,7 +189,7 @@ export namespace DragManager { dragDiv.style.pointerEvents = "none"; DragManager.Root().appendChild(dragDiv); } - MainOverlayTextBox.Instance.SetTextDoc(); + if (!dragData.dontClearTextBox) MainOverlayTextBox.Instance.SetTextDoc(); let scaleXs: number[] = []; let scaleYs: number[] = []; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index e33c53a1a..15895fd1c 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -6,19 +6,28 @@ import { keymap } from "prosemirror-keymap"; import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { schema } from "./RichTextSchema"; -import { Schema, NodeType, MarkType } from "prosemirror-model"; +import { Schema, NodeType, MarkType, Mark } from "prosemirror-model"; import React = require("react"); import "./TooltipTextMenu.scss"; const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands"); import { library } from '@fortawesome/fontawesome-svg-core'; import { wrapInList, bulletList, liftListItem, listItem, } from 'prosemirror-schema-list'; -import { liftTarget } from 'prosemirror-transform'; +import { liftTarget, RemoveMarkStep, AddMarkStep } from 'prosemirror-transform'; import { faListUl, } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FieldViewProps } from "../views/nodes/FieldView"; import { throwStatement } from "babel-types"; +import { View } from "@react-pdf/renderer"; +import { DragManager } from "./DragManager"; +import { Doc, Opt, Field } from "../../new_fields/Doc"; +import { Id } from "../../new_fields/RefField"; +import { Utils } from "../northstar/utils/Utils"; +import { DocServer } from "../DocServer"; +import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; +import { CollectionDockingView } from "../views/collections/CollectionDockingView"; +import { DocumentManager } from "./DocumentManager"; const SVG = "http://www.w3.org/2000/svg"; @@ -37,6 +46,8 @@ export class TooltipTextMenu { private fontStylesToName: Map; private listTypeToIcon: Map; private fontSizeIndicator: HTMLSpanElement = document.createElement("span"); + private linkEditor?: HTMLDivElement; + private linkText?: HTMLDivElement; //dropdown doms private fontSizeDom?: Node; private fontStyleDom?: Node; @@ -150,6 +161,90 @@ export class TooltipTextMenu { this.tooltip.appendChild(this.fontStyleDom); } + updateLinkMenu() { + if (!this.linkEditor || !this.linkText) { + this.linkEditor = document.createElement("div"); + this.linkEditor.style.color = "white"; + this.linkText = document.createElement("div"); + this.linkText.style.cssFloat = "left"; + this.linkText.style.marginRight = "5px"; + this.linkText.setAttribute("contenteditable", "true"); + this.linkText.style.color = "white"; + let linkBtn = document.createElement("div"); + linkBtn.textContent = ">>"; + linkBtn.style.width = "20px"; + linkBtn.style.height = "20px"; + linkBtn.style.color = "white"; + linkBtn.style.cssFloat = "left"; + linkBtn.onpointerdown = (e: PointerEvent) => { + let node = this.view.state.selection.$from.nodeAfter; + let link = node && node.marks.find(m => m.type.name === "link"); + if (link) { + console.log("Link to : " + link.attrs.href); + let href: string = link.attrs.href; + if (href.indexOf(DocServer.prepend("/doc/")) === 0) { + let docid = href.replace(DocServer.prepend("/doc/"), ""); + DocServer.GetRefField(docid).then(action((f: Opt) => { + if (f instanceof Doc) { + if (DocumentManager.Instance.getDocumentView(f)) + DocumentManager.Instance.getDocumentView(f)!.props.focus(f); + else CollectionDockingView.Instance.AddRightSplit(f); + } + })); + } + e.stopPropagation(); + e.preventDefault(); + } + } + let linkDrag = document.createElement("div"); + linkDrag.textContent = "O"; + linkDrag.style.width = "20px"; + linkDrag.style.height = "20px"; + linkDrag.style.color = "white"; + linkDrag.style.cssFloat = "left"; + linkDrag.onpointerdown = (e: PointerEvent) => { + let dragData = new DragManager.LinkDragData(this.editorProps.Document); + dragData.dontClearTextBox = true; + DragManager.StartLinkDrag(this.linkEditor!, dragData, e.clientX, e.clientY, + { + handlers: { + dragComplete: action(() => { + let m = dragData.droppedDocuments as Doc[]; + this.makeLink(DocServer.prepend("/doc/" + m[0][Id])); + }), + }, + hideSource: false + }) + }; + this.linkEditor.appendChild(this.linkText); + this.linkEditor.appendChild(linkBtn); + this.linkEditor.appendChild(linkDrag) + this.tooltip.appendChild(this.linkEditor); + } + + let node = this.view.state.selection.$from.nodeAfter; + let link = node && node.marks.find(m => m.type.name === "link"); + this.linkText.textContent = link ? link.attrs.href : "-empty-"; + + this.linkText.onkeydown = (e: KeyboardEvent) => { + if (e.key === "Enter") { + this.makeLink(this.linkText!.textContent!); + e.stopPropagation(); + e.preventDefault(); + } + } + this.tooltip.appendChild(this.linkEditor); + } + + makeLink = (target: string) => { + let node = this.view.state.selection.$from.nodeAfter; + let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target }); + this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link)); + this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link)); + node = this.view.state.selection.$from.nodeAfter; + link = node && node.marks.find(m => m.type.name === "link"); + } + //will display a remove-list-type button if selection is in list, otherwise will show list type dropdown updateListItemDropdown(label: string, listTypeBtn: Node) { //remove old btn @@ -348,6 +443,8 @@ export class TooltipTextMenu { } else { //multiple font sizes selected this.updateFontSizeDropdown("Various"); } + + this.updateLinkMenu(); } //finds all active marks on selection in given group diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 38efeeba5..c3bf36553 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -238,6 +238,7 @@ export class DocumentView extends DocComponent(Docu const protoDest = destDoc.proto; const protoSrc = sourceDoc.proto; Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc); + de.data.droppedDocuments.push(destDoc); e.stopPropagation(); } } -- cgit v1.2.3-70-g09d2 From 244d4d127e7e1a0faadbc5a8baed7922ef03522c Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 9 May 2019 14:11:35 -0400 Subject: cleaned up textlinking a little. --- src/client/util/DragManager.ts | 13 ++-- src/client/util/SelectionManager.ts | 4 +- src/client/util/TooltipTextMenu.tsx | 25 +++--- src/client/views/DocumentDecorations.tsx | 8 +- src/client/views/MainOverlayTextBox.tsx | 30 ++++--- .../views/collections/CollectionSchemaView.tsx | 3 +- src/client/views/collections/CollectionSubView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 8 +- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 91 ++++++++++++++++++---- 10 files changed, 122 insertions(+), 64 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index fbf20e244..eaf851a75 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,11 +1,9 @@ -import { action } from "mobx"; +import { action, runInAction } from "mobx"; +import { Doc, DocListCast } from "../../new_fields/Doc"; +import { Cast } from "../../new_fields/Types"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import * as globalCssVariables from "../views/globalCssVariables.scss"; -import { MainOverlayTextBox } from "../views/MainOverlayTextBox"; -import { Doc, DocListCast } from "../../new_fields/Doc"; -import { Cast } from "../../new_fields/Types"; -import { listSpec } from "../../new_fields/Schema"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType) { @@ -154,7 +152,10 @@ export namespace DragManager { [id: string]: any; } + export let StartDragFunctions: (() => void)[] = []; + export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + runInAction(() => StartDragFunctions.map(func => func())); StartDrag(eles, dragData, downX, downY, options, (dropData: { [id: string]: any }) => (dropData.droppedDocuments = dragData.userDropAction == "alias" || (!dragData.userDropAction && dragData.dropAction == "alias") ? @@ -189,7 +190,6 @@ export namespace DragManager { dragDiv.style.pointerEvents = "none"; DragManager.Root().appendChild(dragDiv); } - if (!dragData.dontClearTextBox) MainOverlayTextBox.Instance.SetTextDoc(); let scaleXs: number[] = []; let scaleYs: number[] = []; @@ -217,6 +217,7 @@ export namespace DragManager { dragElement.style.top = "0"; dragElement.style.bottom = ""; dragElement.style.left = "0"; + dragElement.style.color = "black"; dragElement.style.transformOrigin = "0 0"; dragElement.style.zIndex = globalCssVariables.contextMenuZindex;// "1000"; dragElement.style.transform = `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`; diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index fe5acf4b4..a3a8172c7 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,7 +1,7 @@ import { observable, action } from "mobx"; import { Doc } from "../../new_fields/Doc"; -import { MainOverlayTextBox } from "../views/MainOverlayTextBox"; import { DocumentView } from "../views/nodes/DocumentView"; +import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; export namespace SelectionManager { class Manager { @@ -25,7 +25,7 @@ export namespace SelectionManager { DeselectAll(): void { manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false)); manager.SelectedDocuments = []; - MainOverlayTextBox.Instance.SetTextDoc(); + FormattedTextBox.InputBoxOverlay = undefined; } @action ReselectAll() { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 15895fd1c..6eb654319 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -48,6 +48,7 @@ export class TooltipTextMenu { private fontSizeIndicator: HTMLSpanElement = document.createElement("span"); private linkEditor?: HTMLDivElement; private linkText?: HTMLDivElement; + private linkDrag?: HTMLImageElement; //dropdown doms private fontSizeDom?: Node; private fontStyleDom?: Node; @@ -168,8 +169,13 @@ export class TooltipTextMenu { this.linkText = document.createElement("div"); this.linkText.style.cssFloat = "left"; this.linkText.style.marginRight = "5px"; + this.linkText.style.marginLeft = "5px"; this.linkText.setAttribute("contenteditable", "true"); + this.linkText.style.whiteSpace = "nowrap"; + this.linkText.style.width = "150px"; + this.linkText.style.overflow = "hidden"; this.linkText.style.color = "white"; + this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); } let linkBtn = document.createElement("div"); linkBtn.textContent = ">>"; linkBtn.style.width = "20px"; @@ -196,16 +202,17 @@ export class TooltipTextMenu { e.preventDefault(); } } - let linkDrag = document.createElement("div"); - linkDrag.textContent = "O"; - linkDrag.style.width = "20px"; - linkDrag.style.height = "20px"; - linkDrag.style.color = "white"; - linkDrag.style.cssFloat = "left"; - linkDrag.onpointerdown = (e: PointerEvent) => { + this.linkDrag = document.createElement("img"); + this.linkDrag.src = "https://seogurusnyc.com/wp-content/uploads/2016/12/link-1.png"; + this.linkDrag.style.width = "20px"; + this.linkDrag.style.height = "20px"; + this.linkDrag.style.color = "white"; + this.linkDrag.style.background = "black"; + this.linkDrag.style.cssFloat = "left"; + this.linkDrag.onpointerdown = (e: PointerEvent) => { let dragData = new DragManager.LinkDragData(this.editorProps.Document); dragData.dontClearTextBox = true; - DragManager.StartLinkDrag(this.linkEditor!, dragData, e.clientX, e.clientY, + DragManager.StartLinkDrag(this.linkDrag!, dragData, e.clientX, e.clientY, { handlers: { dragComplete: action(() => { @@ -216,9 +223,9 @@ export class TooltipTextMenu { hideSource: false }) }; + this.linkEditor.appendChild(this.linkDrag); this.linkEditor.appendChild(this.linkText); this.linkEditor.appendChild(linkBtn); - this.linkEditor.appendChild(linkDrag) this.tooltip.appendChild(this.linkEditor); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e3eb034fa..85ddadd64 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -5,7 +5,6 @@ import { DragLinksAsDocuments, DragManager } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; import { undoBatch } from "../util/UndoManager"; import './DocumentDecorations.scss'; -import { MainOverlayTextBox } from "./MainOverlayTextBox"; import { DocumentView, PositionDocument } from "./nodes/DocumentView"; import { LinkMenu } from "./nodes/LinkMenu"; import { TemplateMenu } from "./TemplateMenu"; @@ -25,11 +24,9 @@ import { faLink } from '@fortawesome/free-solid-svg-icons'; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; -import { CollectionFreeFormView } from "./collections/collectionFreeForm/CollectionFreeFormView"; import { CollectionView } from "./collections/CollectionView"; -import { createCipher } from "crypto"; -import { FieldView } from "./nodes/FieldView"; import { DocumentManager } from "../util/DocumentManager"; +import { FormattedTextBox } from "./nodes/FormattedTextBox"; library.add(faLink); @@ -328,6 +325,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let selDoc = SelectionManager.SelectedDocuments()[0]; let container = selDoc.props.ContainingCollectionView ? selDoc.props.ContainingCollectionView.props.Document.proto : undefined; let dragData = new DragManager.LinkDragData(selDoc.props.Document, container ? [container] : []); + FormattedTextBox.InputBoxOverlay = undefined; DragManager.StartLinkDrag(this._linkerButton.current, dragData, e.pageX, e.pageY, { handlers: { dragComplete: action(emptyFunction), @@ -410,7 +408,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> break; } - MainOverlayTextBox.Instance.SetTextDoc(); + FormattedTextBox.InputBoxOverlay = undefined; SelectionManager.SelectedDocuments().forEach(element => { const rect = element.ContentDiv ? element.ContentDiv.getBoundingClientRect() : new DOMRect(); diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 3b75c248a..91f626737 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -1,16 +1,12 @@ -import { action, observable, trace } from 'mobx'; +import { action, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import "normalize.css"; import * as React from 'react'; import { emptyFunction, returnTrue, returnZero } from '../../Utils'; -import '../northstar/model/ModelExtensions'; -import '../northstar/utils/Extensions'; import { DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; +import "normalize.css"; import "./MainOverlayTextBox.scss"; import { FormattedTextBox } from './nodes/FormattedTextBox'; -import { Doc } from '../../new_fields/Doc'; -import { NumCast } from '../../new_fields/Types'; interface MainOverlayTextBoxProps { } @@ -18,8 +14,6 @@ interface MainOverlayTextBoxProps { @observer export class MainOverlayTextBox extends React.Component { public static Instance: MainOverlayTextBox; - @observable public TextDoc?: Doc = undefined; - public TextScroll: number = 0; @observable _textXf: () => Transform = () => Transform.Identity(); private _textFieldKey: string = "data"; private _textColor: string | null = null; @@ -30,15 +24,18 @@ export class MainOverlayTextBox extends React.Component super(props); this._textProxyDiv = React.createRef(); MainOverlayTextBox.Instance = this; + reaction(() => FormattedTextBox.InputBoxOverlay, + (box?: FormattedTextBox) => { + if (box) this.setTextDoc(box.props.fieldKey, box.CurrentDiv, box.props.ScreenToLocalTransform); + else this.setTextDoc(); + }); } @action - SetTextDoc(textDoc?: Doc, textFieldKey?: string, div?: HTMLDivElement, tx?: () => Transform) { + private setTextDoc(textFieldKey?: string, div?: HTMLDivElement, tx?: () => Transform) { if (this._textTargetDiv) { this._textTargetDiv.style.color = this._textColor; } - - this.TextDoc = textDoc; this._textFieldKey = textFieldKey!; this._textXf = tx ? tx : () => Transform.Identity(); this._textTargetDiv = div; @@ -46,15 +43,13 @@ export class MainOverlayTextBox extends React.Component if (div.parentElement && div.parentElement instanceof HTMLDivElement && div.parentElement.id === "screenSpace") this._textXf = () => Transform.Identity(); this._textColor = div.style.color; div.style.color = "transparent"; - this.TextScroll = div.scrollTop; } } @action textScroll = (e: React.UIEvent) => { if (this._textProxyDiv.current && this._textTargetDiv) { - this.TextScroll = (e as any)._targetInst.stateNode.scrollTop;// this._textProxyDiv.current.children[0].scrollTop; - this._textTargetDiv.scrollTop = this.TextScroll; + this._textTargetDiv.scrollTop = (e as any)._targetInst.stateNode.scrollTop; } } @@ -64,11 +59,12 @@ export class MainOverlayTextBox extends React.Component document.addEventListener('pointerup', this.textBoxUp); } } + @action textBoxMove = (e: PointerEvent) => { if (e.movementX > 1 || e.movementY > 1) { document.removeEventListener("pointermove", this.textBoxMove); document.removeEventListener('pointerup', this.textBoxUp); - let dragData = new DragManager.DocumentDragData([this.TextDoc!]); + let dragData = new DragManager.DocumentDragData(FormattedTextBox.InputBoxOverlay ? [FormattedTextBox.InputBoxOverlay.props.Document] : []); const [left, top] = this._textXf().inverse().transformPoint(0, 0); dragData.xOffset = e.clientX - left; dragData.yOffset = e.clientY - top; @@ -86,13 +82,13 @@ export class MainOverlayTextBox extends React.Component } render() { - if (this.TextDoc && this._textTargetDiv) { + if (FormattedTextBox.InputBoxOverlay && this._textTargetDiv) { let textRect = this._textTargetDiv.getBoundingClientRect(); let s = this._textXf().Scale; return
-
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 16818affd..6dd0e5935 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -322,8 +322,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { render() { library.add(faCog); library.add(faPlus); - //This can't just pass FieldValue to filter because filter passes other arguments to the passed in function, which end up as default values in FieldValue - const children = (this.children || []).filter(doc => FieldValue(doc)); + const children = this.children; return (
this.onDrop(e, {})} ref={this.createTarget}> diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 828ac880a..082692d8d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -50,7 +50,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { get children() { //TODO tfs: This might not be what we want? //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) - return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => FieldValue(doc)); + return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []).filter(doc => FieldValue(doc)).map(doc => doc as Doc); } @action diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 797f94d5f..c1d149098 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -70,7 +70,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } public getActiveDocuments = () => { const curPage = FieldValue(this.Document.curPage, -1); - return FieldValue(this.children, [] as Doc[]).filter(doc => { + return this.children.filter(doc => { var page = NumCast(doc.page, -1); return page === curPage || page === -1; }); @@ -169,7 +169,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // if (!this.props.active()) { // return; // } - let childSelected = (this.children || []).filter(doc => doc).some(doc => { + let childSelected = this.children.some(doc => { var dv = DocumentManager.Instance.getDocumentView(doc); return dv && SelectionManager.IsSelected(dv) ? true : false; }); @@ -222,7 +222,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } bringToFront = (doc: Doc) => { - const docs = (this.children || []); + const docs = this.children; docs.slice().sort((doc1, doc2) => { if (doc1 === doc) return 1; if (doc2 === doc) return -1; @@ -268,7 +268,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed.struct get views() { let curPage = FieldValue(this.Document.curPage, -1); - let docviews = (this.children || []).filter(doc => doc).reduce((prev, doc) => { + let docviews = this.children.reduce((prev, doc) => { if (!(doc instanceof Doc)) return prev; var page = NumCast(doc.page, -1); if (page === curPage || page === -1) { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 613c24fa4..cdc1bdc85 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -1,6 +1,6 @@ import React = require("react"); import { observer } from "mobx-react"; -import { computed } from "mobx"; +import { computed, observable } from "mobx"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; import { VideoBox } from "./VideoBox"; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 445a834ee..e7fb94777 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -1,29 +1,31 @@ -import { action, IReactionDisposer, reaction, trace, computed, _allowStateChangesInsideComputed } from "mobx"; +import { action, IReactionDisposer, observable, reaction } from "mobx"; +import { observer } from "mobx-react"; import { baseKeymap } from "prosemirror-commands"; import { history } from "prosemirror-history"; import { keymap } from "prosemirror-keymap"; import { EditorState, Plugin, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; +import { Doc, Field, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; +import { RichTextField } from "../../../new_fields/RichTextField"; +import { createSchema, makeInterface } from "../../../new_fields/Schema"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { DocServer } from "../../DocServer"; +import { DocumentManager } from "../../util/DocumentManager"; +import { DragManager } from "../../util/DragManager"; import buildKeymap from "../../util/ProsemirrorKeymap"; import { inpRules } from "../../util/RichTextRules"; -import { schema, ImageResizeView } from "../../util/RichTextSchema"; +import { ImageResizeView, schema } 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 { MainOverlayTextBox } from "../MainOverlayTextBox"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { DocComponent } from "../DocComponent"; +import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import React = require("react"); -import { SelectionManager } from "../../util/SelectionManager"; -import { DocComponent } from "../DocComponent"; -import { createSchema, makeInterface } from "../../../new_fields/Schema"; -import { Opt, Doc, WidthSym, HeightSym } from "../../../new_fields/Doc"; -import { observer } from "mobx-react"; -import { InkingControl } from "../InkingControl"; -import { StrCast, Cast, NumCast, BoolCast } from "../../../new_fields/Types"; -import { RichTextField } from "../../../new_fields/RichTextField"; -import { Id } from "../../../new_fields/RefField"; -import { UndoManager } from "../../util/UndoManager"; // FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document // @@ -62,15 +64,23 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe private _proseRef: React.RefObject; private _editorView: Opt; private _gotDown: boolean = false; + private _dropDisposer?: DragManager.DragDropDisposer; private _reactionDisposer: Opt; private _inputReactionDisposer: Opt; private _proxyReactionDisposer: Opt; + public get CurrentDiv(): HTMLDivElement { return this._ref.current!; } + + @observable public static InputBoxOverlay?: FormattedTextBox = undefined; + public static InputBoxOverlayScroll: number = 0; constructor(props: FieldViewProps) { super(props); this._ref = React.createRef(); this._proseRef = React.createRef(); + if (this.props.isOverlay) { + DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined); + } } _applyingChange: boolean = false; @@ -94,7 +104,27 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } + @undoBatch + @action + drop = async (e: Event, de: DragManager.DropEvent) => { + if (de.data instanceof DragManager.LinkDragData) { + let sourceDoc = de.data.linkSourceDocument; + let destDoc = this.props.Document; + + const protoDest = destDoc.proto; + const protoSrc = sourceDoc.proto; + Doc.MakeLink(protoSrc ? protoSrc : sourceDoc, protoDest ? protoDest : destDoc); + de.data.droppedDocuments.push(destDoc); + e.stopPropagation(); + } + } + componentDidMount() { + if (this._ref.current) { + this._dropDisposer = DragManager.MakeDropTarget(this._ref.current, { + handlers: { drop: this.drop.bind(this) } + }); + } const config = { schema, inpRules, //these currently don't do anything, but could eventually be helpful @@ -117,7 +147,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe }; if (this.props.isOverlay) { - this._inputReactionDisposer = reaction(() => MainOverlayTextBox.Instance.TextDoc && MainOverlayTextBox.Instance.TextDoc[Id], + this._inputReactionDisposer = reaction(() => FormattedTextBox.InputBoxOverlay, () => { if (this._editorView) { this._editorView.destroy(); @@ -127,7 +157,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe ); } else { this._proxyReactionDisposer = reaction(() => this.props.isSelected(), - () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform)); + () => { + if (this.props.isSelected()) { + FormattedTextBox.InputBoxOverlay = this; + FormattedTextBox.InputBoxOverlayScroll = this._ref.current!.scrollTop; + } + }); } @@ -178,6 +213,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (this._proxyReactionDisposer) { this._proxyReactionDisposer(); } + if (this._dropDisposer) { + this._dropDisposer(); + } } onPointerDown = (e: React.PointerEvent): void => { @@ -186,6 +224,24 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip) this._toolTipTextMenu.tooltip.style.opacity = "0"; } + if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey) { + if (e.target && (e.target as any).href) { + let href = (e.target as any).href; + if (href.indexOf(DocServer.prepend("/doc/")) === 0) { + let docid = href.replace(DocServer.prepend("/doc/"), ""); + DocServer.GetRefField(docid).then(action((f: Opt) => { + if (f instanceof Doc) { + if (DocumentManager.Instance.getDocumentView(f)) + DocumentManager.Instance.getDocumentView(f)!.props.focus(f); + else CollectionDockingView.Instance.AddRightSplit(f); + } + })); + } + e.stopPropagation(); + e.preventDefault(); + } + + } if (e.button === 2 || (e.button === 0 && e.ctrlKey)) { this._gotDown = true; e.preventDefault(); @@ -199,12 +255,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } + @action onFocused = (e: React.FocusEvent): void => { if (!this.props.isOverlay) { - MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform); + FormattedTextBox.InputBoxOverlay = this; } else { if (this._proseRef.current) { - this._proseRef.current.scrollTop = MainOverlayTextBox.Instance.TextScroll; + this._proseRef.current.scrollTop = FormattedTextBox.InputBoxOverlayScroll; } } } -- cgit v1.2.3-70-g09d2