diff options
-rw-r--r-- | src/client/documents/Documents.ts | 4 | ||||
-rw-r--r-- | src/client/util/DocumentManager.ts | 46 | ||||
-rw-r--r-- | src/client/util/DragManager.ts | 47 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss | 38 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx | 88 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx | 148 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/LinkMenu.scss | 15 | ||||
-rw-r--r-- | src/client/views/nodes/LinkMenu.tsx | 18 | ||||
-rw-r--r-- | src/client/views/nodes/LinkMenuGroup.tsx | 74 | ||||
-rw-r--r-- | src/client/views/nodes/LinkMenuItem.tsx | 8 | ||||
-rw-r--r-- | src/new_fields/Doc.ts | 16 |
12 files changed, 243 insertions, 261 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index df5c39562..7cef48b98 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -91,7 +91,9 @@ export namespace DocUtils { // let protoSrc = source.proto ? source.proto : source; // let protoTarg = target.proto ? target.proto : target; export function MakeLink(source: Doc, target: Doc, targetContext?: Doc) { - if (LinkManager.Instance.doesLinkExist(source, target)) return; + if (LinkManager.Instance.doesLinkExist(source, target)) { + console.log("LINK EXISTS"); return; + } UndoManager.RunInBatch(() => { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 325f4894d..c4cb6721a 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -85,51 +85,25 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { - // console.log("linked"); - // let docs = DocListCast(CurrentUserUtils.UserDocument.data); - // docs.forEach(d => { - // console.log("d", StrCast(d.title)); - - // }); - - // let d = Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc, new Doc); - // console.log("DOC", StrCast(d.title)); - - - - let linked = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { - // console.log("FINDING LINKED DVs FOR", StrCast(dv.props.Document.title)); + return DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { let linksList = LinkManager.Instance.findAllRelatedLinks(dv.props.Document); + // let linksList = DocListCast(dv.props.Document.linkedToDocs); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { - // if (link) { - let destination = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); - // console.log("FINDING FOR", StrCast(dv.Document.title), StrCast(destination.title)); - - if (destination) { - let dvs = DocumentManager.Instance.getDocumentViews(destination); - if (dvs.length > 0) { - dvs.map(docView1 => { - // console.log("PUSHING LINK BETWEEN", StrCast(dv.props.Document.title), StrCast(docView1.props.Document.title)); - // TODO: if any docviews are not in the same context, draw a proxy - // let sameContent = dv.props.ContainingCollectionView === docView1.props.ContainingCollectionView; - pairs.push({ anchor1View: dv, anchor2View: docView1, linkDoc: link }); - // console.log("PUSHED", StrCast(dv.props.Document.title), StrCast(docView1.Document.title)); - }); - } else { - let dv = DocumentManager.Instance.getDocumentView(destination); - dv ? console.log(StrCast(dv.props.Document.title)) : console.log("cant find"); - } + if (link) { + let linkToDoc = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); + // console.log("FOUND ", DocumentManager.Instance.getDocumentViews(linkToDoc).length, "DOCVIEWS FOR", StrCast(linkToDoc.title), "WITH SOURCE", StrCast(dv.props.Document.title)); + DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => + pairs.push({ a: dv, b: docView1, l: link })); } - // } return pairs; - }, [] as { anchor1View: DocumentView, anchor2View: DocumentView, linkDoc: Doc }[])); + }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); } return pairs; - }, [] as { anchor1View: DocumentView, anchor2View: DocumentView, linkDoc: Doc }[]); - return linked; + }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); } + @undoBatch public jumpToDocument = async (docDelegate: Doc, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number, docContext?: Doc): Promise<void> => { let doc = Doc.GetProto(docDelegate); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 11530ef09..2abcff4f7 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -48,12 +48,10 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: number, linkDoc: Doc, sourceDoc: Doc) { let draggeddoc = LinkManager.Instance.findOppositeAnchor(linkDoc, sourceDoc); - // TODO: if not in same context then don't drag - let moddrag = await Cast(draggeddoc.annotationOn, Doc); let dragData = new DragManager.DocumentDragData(moddrag ? [moddrag] : [draggeddoc]); dragData.dropAction = "alias" as dropActionType; - DragManager.StartDocumentDrag([dragEle], dragData, x, y, { + DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, { handlers: { dragComplete: action(emptyFunction), }, @@ -83,11 +81,21 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n if (doc) moddrag.push(doc); } let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); - dragData.dropAction = "alias" as dropActionType; // dragData.moveDocument = (document, targetCollection, addDocument) => { // return false; // }; - DragManager.StartDocumentDrag([dragEle], dragData, x, y, { + + // runInAction(() => StartDragFunctions.map(func => func())); + // (eles, dragData, downX, downY, options, + // (dropData: { [id: string]: any }) => { + // (dropData.droppedDocuments = dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? + // dragData.draggedDocuments.map(d => Doc.MakeAlias(d)) : + // dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? + // dragData.draggedDocuments.map(d => Doc.MakeCopy(d, true)) : + // dragData.draggedDocuments + // ); + // }); + DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, { handlers: { dragComplete: action(emptyFunction), }, @@ -215,6 +223,35 @@ export namespace DragManager { }); } + export function StartLinkedDocumentDrag(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.draggedDocuments.map(d => { + let dv = DocumentManager.Instance.getDocumentView(d); + // console.log("DRAG", StrCast(d.title)); + + if (dv) { + console.log("DRAG", StrCast(d.title), "has view"); + if (dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) { + console.log("DRAG", StrCast(d.title), "same"); + return d; + } else { + console.log("DRAG", StrCast(d.title), "diff"); + return Doc.MakeAlias(d); + } + } else { + console.log("DRAG", StrCast(d.title), "has no view"); + return Doc.MakeAlias(d); + } + // return (dv && dv.props.ContainingCollectionView !== SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) || !dv ? + // Doc.MakeAlias(d) : d; + }); + + }); + } + export function StartAnnotationDrag(eles: HTMLElement[], dragData: AnnotationDragData, downX: number, downY: number, options?: DragOptions) { StartDrag(eles, dragData, downX, downY, options); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index d8d518147..239c2ce56 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -1,22 +1,22 @@ -// .collectionfreeformlinkview-linkLine { -// stroke: black; -// transform: translate(10000px,10000px); -// opacity: 0.5; -// pointer-events: all; -// } -// .collectionfreeformlinkview-linkCircle { -// stroke: rgb(0,0,0); -// opacity: 0.5; -// transform: translate(10000px,10000px); -// pointer-events: all; -// cursor: pointer; -// } -// .collectionfreeformlinkview-linkText { -// stroke: rgb(0,0,0); -// opacity: 0.5; -// transform: translate(10000px,10000px); -// pointer-events: all; -// } +.collectionfreeformlinkview-linkLine { + stroke: black; + transform: translate(10000px,10000px); + opacity: 0.5; + pointer-events: all; +} +.collectionfreeformlinkview-linkCircle { + stroke: rgb(0,0,0); + opacity: 0.5; + transform: translate(10000px,10000px); + pointer-events: all; + cursor: pointer; +} +.collectionfreeformlinkview-linkText { + stroke: rgb(0,0,0); + opacity: 0.5; + transform: translate(10000px,10000px); + pointer-events: all; +} .linkview-ele { transform: translate(10000px,10000px); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 65d5ac474..5c7f080e0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -5,70 +5,60 @@ import { InkingControl } from "../../InkingControl"; import "./CollectionFreeFormLinkView.scss"; import React = require("react"); import v5 = require("uuid/v5"); -import { DocumentView } from "../../nodes/DocumentView"; -import { Docs } from "../../../documents/Documents"; export interface CollectionFreeFormLinkViewProps { - // anchor1: Doc; - // anchor2: Doc; - // LinkDocs: Doc[]; - // addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; - // removeDocument: (document: Doc) => boolean; - // sameContext: boolean; - - // sourceView: DocumentView; - // targetView: DocumentView; - sourceView: Doc; - targetView: Doc; + A: Doc; + B: Doc; + LinkDocs: Doc[]; + addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + removeDocument: (document: Doc) => boolean; } @observer export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> { - // onPointerDown = (e: React.PointerEvent) => { - // if (e.button === 0 && !InkingControl.Instance.selectedTool) { - // let a = this.props.A; - // let b = this.props.B; - // let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : a[WidthSym]() / 2); - // let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : a[HeightSym]() / 2); - // let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : b[WidthSym]() / 2); - // let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : b[HeightSym]() / 2); - // this.props.LinkDocs.map(l => { - // let width = l[WidthSym](); - // l.x = (x1 + x2) / 2 - width / 2; - // l.y = (y1 + y2) / 2 + 10; - // if (!this.props.removeDocument(l)) this.props.addDocument(l, false); - // }); - // e.stopPropagation(); - // e.preventDefault(); - // } - // } - - + onPointerDown = (e: React.PointerEvent) => { + if (e.button === 0 && !InkingControl.Instance.selectedTool) { + let a = this.props.A; + let b = this.props.B; + let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : a[WidthSym]() / 2); + let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : a[HeightSym]() / 2); + let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : b[WidthSym]() / 2); + let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : b[HeightSym]() / 2); + this.props.LinkDocs.map(l => { + let width = l[WidthSym](); + l.x = (x1 + x2) / 2 - width / 2; + l.y = (y1 + y2) / 2 + 10; + if (!this.props.removeDocument(l)) this.props.addDocument(l, false); + }); + e.stopPropagation(); + e.preventDefault(); + } + } render() { - // let l = this.props.LinkDocs; - // let a = this.props.A; - // let b = this.props.B; - let a1 = this.props.sourceView; - let a2 = this.props.targetView; - let x1 = NumCast(a1.x) + (BoolCast(a1.isMinimized, false) ? 5 : NumCast(a1.width) / NumCast(a1.zoomBasis, 1) / 2); - let y1 = NumCast(a1.y) + (BoolCast(a1.isMinimized, false) ? 5 : NumCast(a1.height) / NumCast(a1.zoomBasis, 1) / 2); - - let x2 = NumCast(a2.x) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2); - let y2 = NumCast(a2.y) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2); - + let l = this.props.LinkDocs; + let a = this.props.A; + let b = this.props.B; + let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / NumCast(a.zoomBasis, 1) / 2); + let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / NumCast(a.zoomBasis, 1) / 2); + let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / NumCast(b.zoomBasis, 1) / 2); + let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / NumCast(b.zoomBasis, 1) / 2); + let text = ""; + let first = this.props.LinkDocs[0]; + if (this.props.LinkDocs.length === 1) text += first.title + (first.linkDescription ? "(" + StrCast(first.linkDescription) + ")" : ""); + else text = "-multiple-"; + text = ""; return ( <> - <line className="linkview-line linkview-ele" - // style={{ strokeWidth: `${2 * 1 / 2}` }} + <line key="linkLine" className="collectionfreeformlinkview-linkLine" + style={{ strokeWidth: `${2 * l.length / 2}` }} x1={`${x1}`} y1={`${y1}`} x2={`${x2}`} y2={`${y2}`} /> - {/* <circle key="linkCircle" className="collectionfreeformlinkview-linkCircle" cx={(x1 + x2) / 2} cy={(y1 + y2) / 2} r={8} onPointerDown={this.onPointerDown} /> */} - {/* <text key="linkText" textAnchor="middle" className="collectionfreeformlinkview-linkText" x={`${(x1 + x2) / 2}`} y={`${(y1 + y2) / 2}`}> + <text key="linkText" textAnchor="middle" className="collectionfreeformlinkview-linkText" x={`${(x1 + x2) / 2}`} y={`${(y1 + y2) / 2}`}> {text} - </text> */} + </text> </> ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 9d2f8946b..854122592 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -1,4 +1,4 @@ -import { computed, IReactionDisposer, reaction, action } from "mobx"; +import { computed, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; @@ -11,10 +11,6 @@ import { CollectionViewProps } from "../CollectionSubView"; import "./CollectionFreeFormLinksView.scss"; import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); -import { CollectionFreeFormLinkWithProxyView } from "./CollectionFreeFormLinkWithProxyView"; -import { Docs } from "../../../documents/Documents"; -import { LinkButtonField } from "../../../../new_fields/LinkButtonField"; -import { LinkManager } from "../../../util/LinkManager"; @observer export class CollectionFreeFormLinksView extends React.Component<CollectionViewProps> { @@ -98,150 +94,42 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP @computed get uniqueConnections() { - // console.log("\n"); - let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => { - // console.log("CONNECTION BETWEEN", StrCast(connection.anchor1View.props.Document.title), StrCast(connection.anchor2View.props.Document.title)); - let srcViews = this.documentAnchors(connection.anchor1View); - // srcViews.forEach(sv => { - // console.log("DOCANCHORS SRC", StrCast(connection.anchor1View.Document.title), StrCast(sv.Document.title)); - // }); - - let targetViews = this.documentAnchors(connection.anchor2View); - // targetViews.forEach(sv => { - // console.log("DOCANCHORS TARG", StrCast(connection.anchor2View.Document.title), StrCast(sv.Document.title)); - // }); - - // console.log("lengths", srcViews.length, targetViews.length); + // DocumentManager.Instance.LinkedDocumentViews.forEach(d => { + // console.log("CONNECTION", StrCast(d.a.props.Document.title), StrCast(d.b.props.Document.title)); + // }); - // srcViews.forEach(v => { - // console.log("SOURCE VIEW", StrCast(v.props.Document.title)); - // }); - // targetViews.forEach(v => { - // console.log("TARGET VIEW", StrCast(v.Document.title)); - // }); - - let possiblePairs: { anchor1: Doc, anchor2: Doc }[] = []; - // srcViews.map(sv => { - // console.log("SOURCE VIEW", StrCast(sv.props.Document.title)); - // targetViews.map(tv => { - // console.log("TARGET VIEW", StrCast(tv.props.Document.title)); - // // console.log("PUSHING PAIR", StrCast(sv.props.Document.title), StrCast(tv.props.Document.title)); - // possiblePairs.push({ anchor1: sv.props.Document, anchor2: tv.props.Document }); - // }); - // console.log("END\n"); - // }); - srcViews.forEach(sv => { - // console.log("SOURCE VIEW", StrCast(sv.props.Document.title)); - targetViews.forEach(tv => { - // console.log("TARGET VIEW", StrCast(tv.props.Document.title)); - // console.log("PUSHING PAIR", StrCast(sv.props.Document.title), StrCast(tv.props.Document.title)); - possiblePairs.push({ anchor1: sv.props.Document, anchor2: tv.props.Document }); - }); - // console.log("END\n"); - }); - // console.log("POSSIBLE PAIRS LENGTH", possiblePairs.length); + let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => { + let srcViews = this.documentAnchors(connection.a); + let targetViews = this.documentAnchors(connection.b); + let possiblePairs: { a: Doc, b: Doc, }[] = []; + srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }))); possiblePairs.map(possiblePair => { - // console.log("POSSIBLEPAIR", StrCast(possiblePair.anchor1.title), StrCast(possiblePair.anchor2.title)); if (!drawnPairs.reduce((found, drawnPair) => { - let match1 = (Doc.AreProtosEqual(possiblePair.anchor1, drawnPair.anchor1) && Doc.AreProtosEqual(possiblePair.anchor2, drawnPair.anchor2)); - let match2 = (Doc.AreProtosEqual(possiblePair.anchor1, drawnPair.anchor2) && Doc.AreProtosEqual(possiblePair.anchor2, drawnPair.anchor1)); + let match1 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.a) && Doc.AreProtosEqual(possiblePair.b, drawnPair.b)); + let match2 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.b) && Doc.AreProtosEqual(possiblePair.b, drawnPair.a)); let match = match1 || match2; - if (match && !drawnPair.linkDocs.reduce((found, link) => found || link[Id] === connection.linkDoc[Id], false)) { - drawnPair.linkDocs.push(connection.linkDoc); + if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) { + drawnPair.l.push(connection.l); } return match || found; }, false)) { - drawnPairs.push({ anchor1: possiblePair.anchor1, anchor2: possiblePair.anchor2, linkDocs: [connection.linkDoc] }); + drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] }); } }); return drawnPairs; - }, [] as { anchor1: Doc, anchor2: Doc, linkDocs: Doc[] }[]); + }, [] as { a: Doc, b: Doc, l: Doc[] }[]); return connections.map(c => { - let x = c.linkDocs.reduce((p, l) => p + l[Id], ""); - return <CollectionFreeFormLinkView key={x} sourceView={c.anchor1} targetView={c.anchor2} />; + let x = c.l.reduce((p, l) => p + l[Id], ""); + return <CollectionFreeFormLinkView key={x} A={c.a} B={c.b} LinkDocs={c.l} + removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />; }); } - // findUniquePairs = (): JSX.Element[] => { - // let connections = DocumentManager.Instance.LinkedDocumentViews; - - // // console.log("CONNECTIONS"); - // // connections.forEach(c => console.log(StrCast(c.anchor1View.Document.title), StrCast(c.anchor2View.Document.title))); - - // let unique: Set<{ sourceView: DocumentView, targetView: DocumentView, linkDoc: Doc }> = new Set(); - // connections.forEach(c => { - - // // let match1Index = unique.findIndex(u => (c.anchor1View === u.sourceView) && (c.anchor2View === u.targetView)); - // // let match2Index = unique.findIndex(u => (c.anchor1View === u.targetView) && (c.anchor2View === u.sourceView)); - // let match1 = unique.has({ sourceView: c.anchor1View, targetView: c.anchor2View, linkDoc: c.linkDoc }); - // let match2 = unique.has({ sourceView: c.anchor2View, targetView: c.anchor1View, linkDoc: c.linkDoc }); - // let sameContext = c.anchor1View.props.ContainingCollectionView === c.anchor2View.props.ContainingCollectionView; - - // // console.log("CONNECTION", StrCast(c.anchor1View.props.Document.title), StrCast(c.anchor2View.props.Document.title), match1, match2); - - - // // if in same context, push if docview pair does not already exist - // // else push both directions of pair - // if (sameContext) { - // if (!(match1 || match2)) unique.add({ sourceView: c.anchor1View, targetView: c.anchor2View, linkDoc: c.linkDoc }); - // } else { - // unique.add({ sourceView: c.anchor1View, targetView: c.anchor2View, linkDoc: c.linkDoc }); - // unique.add({ sourceView: c.anchor2View, targetView: c.anchor1View, linkDoc: c.linkDoc }); - // } - // }); - - // let uniqueList: JSX.Element[] = []; - // unique.forEach(u => { - // // TODO: make better key - // let key = StrCast(u.sourceView.Document[Id]) + "-link-" + StrCast(u.targetView.Document[Id]) + "-" + Date.now() + Math.random(); - // let sourceIn = u.sourceView.props.ContainingCollectionView ? u.sourceView.props.ContainingCollectionView.props.Document === this.props.Document : false; - // let targetIn = u.targetView.props.ContainingCollectionView ? u.targetView.props.ContainingCollectionView.props.Document === this.props.Document : false; - // let sameContext = u.sourceView.props.ContainingCollectionView === u.targetView.props.ContainingCollectionView; - // let inContainer = sameContext ? sourceIn || targetIn : sourceIn; - - // if (inContainer) { - // // let alias = Doc.MakeAlias(proxy); - // if (sameContext) { - // uniqueList.push(<CollectionFreeFormLinkView key={key} sourceView={u.sourceView} targetView={u.targetView} />); - // } else { - // let proxy = LinkManager.Instance.findLinkProxy(StrCast(u.sourceView.props.Document[Id]), StrCast(u.targetView.props.Document[Id])); - // if (!proxy) { - // proxy = Docs.LinkButtonDocument( - // { sourceViewId: StrCast(u.sourceView.props.Document[Id]), targetViewId: StrCast(u.targetView.props.Document[Id]) }, - // { width: 200, height: 100, borderRounding: 0 }); - // let proxy1Proto = Doc.GetProto(proxy); - // proxy1Proto.sourceViewId = StrCast(u.sourceView.props.Document[Id]); - // proxy1Proto.targetViewId = StrCast(u.targetView.props.Document[Id]); - // proxy1Proto.isLinkButton = true; - - // // LinkManager.Instance.linkProxies.push(proxy); - // LinkManager.Instance.addLinkProxy(proxy); - // } - // uniqueList.push(<CollectionFreeFormLinkWithProxyView key={key} sourceView={u.sourceView} targetView={u.targetView} proxyDoc={proxy} />); - - // // let proxy = LinkManager.Instance.findLinkProxy(StrCast(u.sourceView.props.Document[Id]), StrCast(u.targetView.props.Document[Id])); - // // if (proxy) { - // // this.props.addDocument(proxy, false); - // // uniqueList.push(<CollectionFreeFormLinkWithProxyView key={key} sourceView={u.sourceView} targetView={u.targetView} />); - // // } - // // let proxyKey = Doc.AreProtosEqual(u.sourceView.Document, Cast(u.linkDoc.anchor1, Doc, new Doc)) ? "proxy1" : "proxy2"; - // // let proxy = Cast(u.linkDoc[proxyKey], Doc, new Doc); - // // this.props.addDocument(proxy, false); - - // // uniqueList.push(<CollectionFreeFormLinkWithProxyView key={key} sourceView={u.sourceView} targetView={u.targetView} - // // proxyDoc={proxy} addDocTab={this.props.addDocTab} />); - // } - // } - // }); - // return uniqueList; - // } - render() { return ( <div className="collectionfreeformlinksview-container"> <svg className="collectionfreeformlinksview-svgCanvas"> {this.uniqueConnections} - {/* {this.findUniquePairs()} */} </svg> {this.props.children} </div> diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index dbbae2d59..1fc2cf770 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -161,7 +161,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu this._animateToIconDisposer = reaction(() => this.props.Document.isIconAnimating, (values) => (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false) , { fireImmediately: true }); + // console.log("CREATED NEW DOC VIEW", StrCast(this.props.Document.title), DocumentManager.Instance.DocumentViews.length); DocumentManager.Instance.DocumentViews.push(this); + // console.log("ADDED TO DOC MAN", StrCast(this.props.Document.title), DocumentManager.Instance.DocumentViews.length); } animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => { diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index d2e411c3d..c01ed23c4 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -18,8 +18,19 @@ } .linkMenu-group-name { - border-bottom: 0.5px solid lightgray; - margin-bottom: 4px; + padding: 4px 6px; + line-height: 12px; + border-radius: 5px; + margin-bottom: 2px; + + &:hover { + background-color: lightgray; + } + } + + .linkMenu-group-wrapper { + border-top: 0.5px solid lightgray; + padding-top: 4px; } } diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 8aca130a5..f96c7d2e4 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -8,6 +8,9 @@ import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { LinkManager } from "../../util/LinkManager"; +import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; +import { emptyFunction } from "../../../Utils"; +import { LinkMenuGroup } from "./LinkMenuGroup"; interface Props { docView: DocumentView; @@ -19,24 +22,11 @@ export class LinkMenu extends React.Component<Props> { @observable private _editingLink?: Doc; - renderGroup = (group: Doc[], groupType: string): Array<JSX.Element> => { - let source = this.props.docView.Document; - return group.map(linkDoc => { - let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, source); - return <LinkMenuItem key={destination[Id] + source[Id]} groupType={groupType} linkDoc={linkDoc} sourceDoc={source} destinationDoc={destination} showEditor={action(() => this._editingLink = linkDoc)} />; - }); - } - renderAllGroups = (groups: Map<string, Array<Doc>>): Array<JSX.Element> => { let linkItems: Array<JSX.Element> = []; groups.forEach((group, groupType) => { linkItems.push( - <div key={groupType} className="linkMenu-group"> - <p className="linkMenu-group-name">{groupType}:</p> - <div className="linkMenu-group-wrapper"> - {this.renderGroup(group, groupType)} - </div> - </div> + <LinkMenuGroup key={groupType} sourceDoc={this.props.docView.props.Document} group={group} groupType={groupType} showEditor={action((linkDoc: Doc) => this._editingLink = linkDoc)} /> ); }); diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx new file mode 100644 index 000000000..229143d99 --- /dev/null +++ b/src/client/views/nodes/LinkMenuGroup.tsx @@ -0,0 +1,74 @@ +import { action, observable } from "mobx"; +import { observer } from "mobx-react"; +import { DocumentView } from "./DocumentView"; +import { LinkMenuItem } from "./LinkMenuItem"; +import { LinkEditor } from "./LinkEditor"; +import './LinkMenu.scss'; +import React = require("react"); +import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { LinkManager } from "../../util/LinkManager"; +import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; +import { emptyFunction } from "../../../Utils"; + +interface LinkMenuGroupProps { + sourceDoc: Doc; + group: Doc[]; + groupType: string; + showEditor: (linkDoc: Doc) => void; +} + +@observer +export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> { + + private _drag = React.createRef<HTMLDivElement>(); + + onLinkButtonDown = (e: React.PointerEvent): void => { + e.stopPropagation(); + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.addEventListener("pointermove", this.onLinkButtonMoved); + document.removeEventListener("pointerup", this.onLinkButtonUp); + document.addEventListener("pointerup", this.onLinkButtonUp); + } + + onLinkButtonUp = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.removeEventListener("pointerup", this.onLinkButtonUp); + e.stopPropagation(); + } + + onLinkButtonMoved = async (e: PointerEvent) => { + if (this._drag.current !== null && (e.movementX > 1 || e.movementY > 1)) { + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.removeEventListener("pointerup", this.onLinkButtonUp); + + let draggedDocs = this.props.group.map(linkDoc => LinkManager.Instance.findOppositeAnchor(linkDoc, this.props.sourceDoc)); + let dragData = new DragManager.DocumentDragData(draggedDocs); + + DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + } + e.stopPropagation(); + } + + render() { + let groupItems = this.props.group.map(linkDoc => { + let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, this.props.sourceDoc); + return <LinkMenuItem key={destination[Id] + this.props.sourceDoc[Id]} groupType={this.props.groupType} + linkDoc={linkDoc} sourceDoc={this.props.sourceDoc} destinationDoc={destination} showEditor={this.props.showEditor} />; + }); + + return ( + <div className="linkMenu-group"> + <p className="linkMenu-group-name" ref={this._drag} onPointerDown={this.onLinkButtonDown} >{this.props.groupType}:</p> + <div className="linkMenu-group-wrapper"> + {groupItems} + </div> + </div> + ) + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index 1ef4690b0..42ef353b7 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -21,7 +21,7 @@ interface LinkMenuItemProps { linkDoc: Doc; sourceDoc: Doc; destinationDoc: Doc; - showEditor: () => void; + showEditor: (linkDoc: Doc) => void; } @observer @@ -42,7 +42,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { onEdit = (e: React.PointerEvent): void => { e.stopPropagation(); - this.props.showEditor(); + this.props.showEditor(this.props.linkDoc); } renderMetadata = (): JSX.Element => { @@ -95,12 +95,12 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { <div className="linkMenu-item"> <div className={canExpand ? "linkMenu-item-content expand-three" : "linkMenu-item-content expand-two"}> <div className="link-name"> - <p>{StrCast(this.props.destinationDoc.title)}</p> + <p ref={this._drag} onPointerDown={this.onLinkButtonDown}>{StrCast(this.props.destinationDoc.title)}</p> <div className="linkMenu-item-buttons"> {canExpand ? <div title="Show more" className="button" onPointerDown={() => this.toggleShowMore()}> <FontAwesomeIcon className="fa-icon" icon={this._showMore ? "chevron-up" : "chevron-down"} size="sm" /></div> : <></>} <div title="Edit link" className="button" onPointerDown={this.onEdit}><FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div> - <div title="Follow link" className="button" ref={this._drag} onPointerDown={this.onLinkButtonDown} onPointerUp={this.onFollowLink}><FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" /></div> + <div title="Follow link" className="button" onPointerDown={this.onFollowLink}><FontAwesomeIcon className="fa-icon" icon="arrow-right" size="sm" /></div> </div> </div> {this._showMore ? this.renderMetadata() : <></>} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 1b0ff812f..5ce47fc2f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -3,11 +3,13 @@ import { serializable, primitive, map, alias, list } from "serializr"; import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper"; import { DocServer } from "../client/DocServer"; import { setter, getter, getField, updateFunction, deleteProperty } from "./util"; -import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast } from "./Types"; +import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, StrCast } from "./Types"; import { listSpec } from "./Schema"; import { ObjectField } from "./ObjectField"; import { RefField, FieldId } from "./RefField"; import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols"; +import { LinkManager } from "../client/util/LinkManager"; +import { DocUtils } from "../client/documents/Documents"; export namespace Field { export function toScriptString(field: Field): string { @@ -247,6 +249,18 @@ export namespace Doc { } } }); + console.log("COPY", StrCast(doc.title)); + let links = LinkManager.Instance.findAllRelatedLinks(doc); + links.forEach(linkDoc => { + let opp = LinkManager.Instance.findOppositeAnchor(linkDoc, doc); + console.log("OPP", StrCast(opp.title)); + DocUtils.MakeLink(opp, copy); + }); + + LinkManager.Instance.allLinks.forEach(l => { + console.log("LINK", StrCast(Cast(l.anchor1, Doc, new Doc).title), StrCast(Cast(l.anchor2, Doc, new Doc).title)); + }); + return copy; } |