From 972f76a34e3c1a1aa5f0be59639fbd5763c9c16f Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 6 Jun 2019 17:45:26 -0400 Subject: links get saved to global table --- src/client/documents/Documents.ts | 30 +++++----- src/client/util/DocumentManager.ts | 54 ++++++++++++------ src/client/util/DragManager.ts | 19 +++++-- src/client/views/DocumentDecorations.tsx | 8 ++- .../CollectionFreeFormLinksView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/LinkBox.tsx | 17 +++--- src/client/views/nodes/LinkManager.tsx | 51 +++++++++++++++++ src/client/views/nodes/LinkMenu.scss | 65 +++++++++++++++++++++- src/client/views/nodes/LinkMenu.tsx | 51 ++++++++++++++--- 10 files changed, 247 insertions(+), 56 deletions(-) create mode 100644 src/client/views/nodes/LinkManager.tsx (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ab61b915c..87b831663 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -35,6 +35,7 @@ import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; +import { LinkManager } from "../views/nodes/LinkManager"; var requestImageSize = require('request-image-size'); var path = require('path'); @@ -70,12 +71,12 @@ const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; export namespace DocUtils { export function MakeLink(source: Doc, target: Doc) { - let protoSrc = source.proto ? source.proto : source; - let protoTarg = target.proto ? target.proto : target; + // let protoSrc = source.proto ? source.proto : source; + // let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; - linkDoc.proto!.title = "-link name-"; + linkDoc.proto!.title = source.proto!.title + " and " + target.proto!.title; linkDoc.proto!.linkDescription = ""; linkDoc.proto!.linkTags = "Default"; @@ -84,17 +85,20 @@ export namespace DocUtils { linkDoc.proto!.linkedFrom = source; linkDoc.proto!.linkedFromPage = source.curPage; - let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); - if (!linkedFrom) { - protoTarg.linkedFromDocs = linkedFrom = new List(); - } - linkedFrom.push(linkDoc); + // let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); + // if (!linkedFrom) { + // protoTarg.linkedFromDocs = linkedFrom = new List(); + // } + // linkedFrom.push(linkDoc); + + // let linkedTo = Cast(protoSrc.linkedToDocs, listSpec(Doc)); + // if (!linkedTo) { + // protoSrc.linkedToDocs = linkedTo = new List(); + // } + // linkedTo.push(linkDoc); + + LinkManager.Instance.allLinks.push(linkDoc); - let linkedTo = Cast(protoSrc.linkedToDocs, listSpec(Doc)); - if (!linkedTo) { - protoSrc.linkedToDocs = linkedTo = new List(); - } - linkedTo.push(linkDoc); return linkDoc; }, "make link"); } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 65c4b9e4b..712529745 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -9,6 +9,7 @@ import { CollectionView } from '../views/collections/CollectionView'; import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; import { Id } from '../../new_fields/FieldSymbols'; +import { LinkManager } from '../views/nodes/LinkManager'; export class DocumentManager { @@ -83,12 +84,16 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { - return DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { - let linksList = DocListCast(dv.props.Document.linkedToDocs); + let linked = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { + + + let linksList = LinkManager.Instance.findAllRelatedLinks(dv.props.Document); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { if (link) { - let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); + let destination = (link["linkedTo"] === dv.props.Document) ? link["linkedFrom"] : link["linkedTo"]; + let linkToDoc = FieldValue(Cast(destination, Doc)); + // let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); if (linkToDoc) { DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => pairs.push({ a: dv, b: docView1, l: link })); @@ -97,21 +102,38 @@ export class DocumentManager { return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); } - linksList = DocListCast(dv.props.Document.linkedFromDocs); - if (linksList && linksList.length) { - pairs.push(...linksList.reduce((pairs, link) => { - if (link) { - let linkFromDoc = FieldValue(Cast(link.linkedFrom, Doc)); - if (linkFromDoc) { - DocumentManager.Instance.getDocumentViews(linkFromDoc).map(docView1 => - pairs.push({ a: dv, b: docView1, l: link })); - } - } - return pairs; - }, pairs)); - } + + // let linksList = DocListCast(dv.props.Document.linkedToDocs); + // console.log("to links", linksList.length); + // if (linksList && linksList.length) { + // pairs.push(...linksList.reduce((pairs, link) => { + // if (link) { + // let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); + // if (linkToDoc) { + // DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => + // pairs.push({ a: dv, b: docView1, l: link })); + // } + // } + // return pairs; + // }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); + // } + // linksList = DocListCast(dv.props.Document.linkedFromDocs); + // console.log("from links", linksList.length); + // if (linksList && linksList.length) { + // pairs.push(...linksList.reduce((pairs, link) => { + // if (link) { + // let linkFromDoc = FieldValue(Cast(link.linkedFrom, Doc)); + // if (linkFromDoc) { + // DocumentManager.Instance.getDocumentViews(linkFromDoc).map(docView1 => + // pairs.push({ a: dv, b: docView1, l: link })); + // } + // } + // return pairs; + // }, pairs)); + // } return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); + return linked; } @undoBatch diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 1e84a0db0..7854cc080 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -4,6 +4,7 @@ import { Cast } from "../../new_fields/Types"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import * as globalCssVariables from "../views/globalCssVariables.scss"; +import { LinkManager } from "../views/nodes/LinkManager"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc | Promise, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType) { @@ -41,14 +42,20 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) { let srcTarg = sourceDoc.proto; let draggedDocs: Doc[] = []; - let draggedFromDocs: Doc[] = []; + // let draggedFromDocs: Doc[] = []; if (srcTarg) { - let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs); - let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs); - if (linkToDocs) draggedDocs = linkToDocs.map(linkDoc => Cast(linkDoc.linkedTo, Doc) as Doc); - if (linkFromDocs) draggedFromDocs = linkFromDocs.map(linkDoc => Cast(linkDoc.linkedFrom, Doc) as Doc); + // let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs); + // let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs); + let linkDocs = LinkManager.Instance.findAllRelatedLinks(srcTarg); + if (linkDocs) draggedDocs = linkDocs.map(link => { + return LinkManager.Instance.findOppositeAnchor(link, sourceDoc); + }); + + + // if (linkToDocs) draggedDocs = linkToDocs.map(linkDoc => Cast(linkDoc.linkedTo, Doc) as Doc); + // if (linkFromDocs) draggedFromDocs = linkFromDocs.map(linkDoc => Cast(linkDoc.linkedFrom, Doc) as Doc); } - draggedDocs.push(...draggedFromDocs); + // draggedDocs.push(...draggedFromDocs); if (draggedDocs.length) { let moddrag: Doc[] = []; for (const draggedDoc of draggedDocs) { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index da9b1253e..2aae9bce6 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -28,6 +28,7 @@ import { CollectionView } from "./collections/CollectionView"; import { DocumentManager } from "../util/DocumentManager"; import { FormattedTextBox } from "./nodes/FormattedTextBox"; import { FieldView } from "./nodes/FieldView"; +import { LinkManager } from "./nodes/LinkManager"; library.add(faLink); @@ -510,9 +511,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let linkButton = null; if (SelectionManager.SelectedDocuments().length > 0) { let selFirst = SelectionManager.SelectedDocuments()[0]; - let linkToSize = Cast(selFirst.props.Document.linkedToDocs, listSpec(Doc), []).length; - let linkFromSize = Cast(selFirst.props.Document.linkedFromDocs, listSpec(Doc), []).length; - let linkCount = linkToSize + linkFromSize; + // let linkToSize = Cast(selFirst.props.Document.linkedToDocs, listSpec(Doc), []).length; + // let linkFromSize = Cast(selFirst.props.Document.linkedFromDocs, listSpec(Doc), []).length; + // let linkCount = linkToSize + linkFromSize; + let linkCount = LinkManager.Instance.findAllRelatedLinks(selFirst.props.Document).length; linkButton = ( { @undoBatch - onViewButtonPressed = async (e: React.PointerEvent): Promise => { + followLink = async (e: React.PointerEvent): Promise => { e.stopPropagation(); DocumentManager.Instance.jumpToDocument(this.props.pairedDoc, e.altKey); } @@ -62,8 +63,8 @@ export class LinkBox extends React.Component { return ( // -
-
+
+

{this.props.linkName}

@@ -72,13 +73,15 @@ export class LinkBox extends React.Component {
-
+
{/*
*/} +
+
-
+
-
+
); diff --git a/src/client/views/nodes/LinkManager.tsx b/src/client/views/nodes/LinkManager.tsx new file mode 100644 index 000000000..1064eaa22 --- /dev/null +++ b/src/client/views/nodes/LinkManager.tsx @@ -0,0 +1,51 @@ +import { observable, computed, action } from "mobx"; +import React = require("react"); +import { SelectionManager } from "../../util/SelectionManager"; +import { observer } from "mobx-react"; +import './LinkEditor.scss'; +import { props } from "bluebird"; +import { DocumentView } from "./DocumentView"; +import { link } from "fs"; +import { StrCast, Cast } from "../../../new_fields/Types"; +import { Doc } from "../../../new_fields/Doc"; +import { listSpec } from "../../../new_fields/Schema"; + + +export class LinkManager { + private static _instance: LinkManager; + public static get Instance(): LinkManager { + return this._instance || (this._instance = new this()); + } + private constructor() { + } + + @observable + public allLinks: Array = []; + + public findAllRelatedLinks(source: Doc): Array { + let related = LinkManager.Instance.allLinks.filter( + link => Doc.AreProtosEqual(source, Cast(link.linkedFrom, Doc, new Doc)) || Doc.AreProtosEqual(source, Cast(link.linkedTo, Doc, new Doc))); + return related; + } + + public findRelatedGroupedLinks(source: Doc): Map> { + let related = this.findAllRelatedLinks(source); + let categories = new Map(); + related.forEach(link => { + let group = categories.get(link.linkTags); + if (group) group.push(link); + else group = [link]; + categories.set(link.linkTags, group); + }) + return categories; + } + + public findOppositeAnchor(link: Doc, source: Doc): Doc { + if (Doc.AreProtosEqual(source, Cast(link.linkedFrom, Doc, new Doc))) { + return Cast(link.linkedTo, Doc, new Doc); + } else { + return Cast(link.linkedFrom, Doc, new Doc); + } + } + +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index dedcce6ef..860f31d8a 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -1,3 +1,5 @@ +@import "../globalCssVariables"; + #linkMenu-container { width: 100%; height: auto; @@ -18,4 +20,65 @@ width: 100%; height: 100px; overflow-y: scroll; -} \ No newline at end of file +} + +.link-menu-group { + .link-menu-item { + border-top: 0.5px solid $light-color-secondary; + padding: 6px; + position: relative; + display: flex; + + .link-menu-item-content { + width: 100%; + } + + &:last-child { + border-bottom: 0.5px solid $light-color-secondary; + } + &:hover .link-menu-item-buttons { + display: flex; + } + &:hover .link-menu-item-content { + width: calc(100% - 72px); + } + } + .link-menu-item-buttons { + display: none; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + + .button { + width: 20px; + height: 20px; + margin: 0; + margin-right: 6px; + border-radius: 50%; + cursor: pointer; + pointer-events: auto; + background-color: $dark-color; + color: $light-color; + font-size: 65%; + transition: transform 0.2s; + text-align: center; + position: relative; + + .fa-icon { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + &:last-child { + margin-right: 0; + } + &:hover { + background: $main-accent; + } + } + } +} + diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 3f09d6214..6dc5623d1 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -8,6 +8,7 @@ import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; +import { LinkManager } from "./LinkManager"; interface Props { docView: DocumentView; @@ -19,26 +20,62 @@ export class LinkMenu extends React.Component { @observable private _editingLink?: Doc; - renderLinkItems(links: Doc[], key: string, type: string) { + // renderLinkItems(links: Doc[], key: string, type: string) { + // return links.map(link => { + // let doc = FieldValue(Cast(link[key], Doc)); + // if (doc) { + // return this._editingLink = link)} type={type} />; + // } + // }); + // } + + renderLinkGroup(links: Doc[]) { + console.log("render link group"); + let source = this.props.docView.Document; + console.log("num links", links.length, typeof links); return links.map(link => { - let doc = FieldValue(Cast(link[key], Doc)); + let destination = (link["linkedTo"] === source) ? link["linkedFrom"] : link["linkedTo"]; + let doc = FieldValue(Cast(destination, Doc)); if (doc) { - return this._editingLink = link)} type={type} />; + console.log(doc[Id] + source[Id], "source is", source[Id]); + return this._editingLink = link)} type={""} />; } }); } + renderLinkItems(links: Map>) { + console.log("render link items"); + + let linkItems: Array = []; + + links.forEach((links, group) => { + console.log("category is ", group); + linkItems.push( +
+

{group}:

+
+ {this.renderLinkGroup(links)} +
+
+ ) + }); + + return linkItems; + } + render() { //get list of links from document - let linkFrom = DocListCast(this.props.docView.props.Document.linkedFromDocs); - let linkTo = DocListCast(this.props.docView.props.Document.linkedToDocs); + // let linkFrom = DocListCast(this.props.docView.props.Document.linkedFromDocs); + // let linkTo = DocListCast(this.props.docView.props.Document.linkedToDocs); + let related = LinkManager.Instance.findRelatedGroupedLinks(this.props.docView.props.Document); if (this._editingLink === undefined) { return (
{/* */}
- {this.renderLinkItems(linkTo, "linkedTo", "Destination: ")} - {this.renderLinkItems(linkFrom, "linkedFrom", "Source: ")} + {/* {this.renderLinkItems(linkTo, "linkedTo", "Destination: ")} + {this.renderLinkItems(linkFrom, "linkedFrom", "Source: ")} */} + {this.renderLinkItems(related)}
); -- cgit v1.2.3-70-g09d2 From 70996f3f19d408e819e081ed03bd7ccf0de44503 Mon Sep 17 00:00:00 2001 From: Vellichora Date: Fri, 7 Jun 2019 17:52:31 -0400 Subject: links can be categorized under a group --- src/client/documents/Documents.ts | 40 +++++++++--- src/client/util/DocumentManager.ts | 5 +- src/client/util/DragManager.ts | 2 +- src/client/util/LinkManager.ts | 84 +++++++++++++++++++++++++ src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/LinkEditor.tsx | 103 +++++++++++++++++++++++++------ src/client/views/nodes/LinkManager.tsx | 51 --------------- src/client/views/nodes/LinkMenu.tsx | 33 ++++++---- 9 files changed, 227 insertions(+), 95 deletions(-) create mode 100644 src/client/util/LinkManager.ts delete mode 100644 src/client/views/nodes/LinkManager.tsx (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 87b831663..5ec19f987 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -35,7 +35,7 @@ import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; -import { LinkManager } from "../views/nodes/LinkManager"; +import { LinkManager } from "../util/LinkManager"; var requestImageSize = require('request-image-size'); var path = require('path'); @@ -69,21 +69,45 @@ export interface DocumentOptions { } const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; +// export interface LinkData { +// anchor1: Doc; +// anchor1Page: number; +// anchor1Tags: Array<{ tag: string, name: string, description: string }>; +// anchor2: Doc; +// anchor2Page: number; +// anchor2Tags: Array<{ tag: string, name: string, description: string }>; +// } + +// export interface TagData { +// tag: string; +// name: string; +// description: string; +// } + export namespace DocUtils { export function MakeLink(source: Doc, target: Doc) { // let protoSrc = source.proto ? source.proto : source; // let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { + let groupDoc = Docs.TextDocument(); + groupDoc.proto!.type = "*"; + let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; - linkDoc.proto!.title = source.proto!.title + " and " + target.proto!.title; - linkDoc.proto!.linkDescription = ""; - linkDoc.proto!.linkTags = "Default"; + // linkDoc.proto!.title = source.proto!.title + " and " + target.proto!.title; + // linkDoc.proto!.linkDescription = ""; + linkDoc.proto!.anchor1 = source; + linkDoc.proto!.anchor1Page = source.curPage; + linkDoc.proto!.anchor1Groups = new List([groupDoc]); + + linkDoc.proto!.anchor2 = target; + linkDoc.proto!.anchor2Page = target.curPage; + linkDoc.proto!.anchor2Groups = new List([groupDoc]); - linkDoc.proto!.linkedTo = target; - linkDoc.proto!.linkedToPage = target.curPage; - linkDoc.proto!.linkedFrom = source; - linkDoc.proto!.linkedFromPage = source.curPage; + // linkDoc.proto!.linkedTo = target; + // linkDoc.proto!.linkedToPage = target.curPage; + // linkDoc.proto!.linkedFrom = source; + // linkDoc.proto!.linkedFromPage = source.curPage; // let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); // if (!linkedFrom) { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 712529745..52f0fe3f6 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -9,7 +9,7 @@ import { CollectionView } from '../views/collections/CollectionView'; import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; import { Id } from '../../new_fields/FieldSymbols'; -import { LinkManager } from '../views/nodes/LinkManager'; +import { LinkManager } from './LinkManager'; export class DocumentManager { @@ -91,7 +91,8 @@ export class DocumentManager { if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { if (link) { - let destination = (link["linkedTo"] === dv.props.Document) ? link["linkedFrom"] : link["linkedTo"]; + // let destination = (link["linkedTo"] === dv.props.Document) ? link["linkedFrom"] : link["linkedTo"]; + let destination = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); let linkToDoc = FieldValue(Cast(destination, Doc)); // let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); if (linkToDoc) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 7854cc080..809368aff 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -4,7 +4,7 @@ import { Cast } from "../../new_fields/Types"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import * as globalCssVariables from "../views/globalCssVariables.scss"; -import { LinkManager } from "../views/nodes/LinkManager"; +import { LinkManager } from "./LinkManager"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc | Promise, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType) { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts new file mode 100644 index 000000000..a6d649395 --- /dev/null +++ b/src/client/util/LinkManager.ts @@ -0,0 +1,84 @@ +import { observable, computed, action } from "mobx"; +import React = require("react"); +import { SelectionManager } from "./SelectionManager"; +import { observer } from "mobx-react"; +import { props } from "bluebird"; +import { DocumentView } from "../views/nodes/DocumentView"; +import { link } from "fs"; +import { StrCast, Cast } from "../../new_fields/Types"; +import { Doc } from "../../new_fields/Doc"; +import { listSpec } from "../../new_fields/Schema"; +import { List } from "../../new_fields/List"; + + +export class LinkManager { + private static _instance: LinkManager; + public static get Instance(): LinkManager { + return this._instance || (this._instance = new this()); + } + private constructor() { + } + + @observable + public allLinks: Array = []; + + public findAllRelatedLinks(source: Doc): Array { + let related = LinkManager.Instance.allLinks.filter( + link => Doc.AreProtosEqual(source, Cast(link.anchor1, Doc, new Doc)) || Doc.AreProtosEqual(source, Cast(link.anchor2, Doc, new Doc))); + return related; + } + + public findRelatedGroupedLinks(source: Doc): Map> { + let related = this.findAllRelatedLinks(source); + + let categories = new Map(); + related.forEach(link => { + // get groups of given doc + let groups = (Doc.AreProtosEqual(source, Cast(link.anchor1, Doc, new Doc))) ? Cast(link.anchor1Groups, listSpec(Doc), []) : Cast(link.anchor2Groups, listSpec(Doc), []); + if (groups) { + if (groups.length > 0) { + groups.forEach(groupDoc => { + if (groupDoc instanceof Doc) { + let groupType = StrCast(groupDoc.proto!.type); + let group = categories.get(groupType); // TODO: clean this up lol + if (group) group.push(link); + else group = [link]; + categories.set(groupType, group); + } else { + // promise doc + } + + }) + } + else { + // if link is in no groups then put it in default group + let group = categories.get("*"); + if (group) group.push(link); + else group = [link]; + categories.set("*", group); + } + } + + + // let anchor = this.findOppositeAnchor(link, source); + // let group = categories.get(link.linkTags); + // if (group) group.push(link); + // else group = [link]; + // categories.set(link.linkTags, group); + }) + return categories; + } + + public findOppositeAnchor(link: Doc, source: Doc): Doc { + if (Doc.AreProtosEqual(source, Cast(link.anchor1, Doc, new Doc))) { + return Cast(link.anchor2, Doc, new Doc); + } else { + return Cast(link.anchor1, Doc, new Doc); + } + } + + // public findAnchorTags(link: Doc, source: Doc): Doc[] { + // if (source) + // } + +} \ No newline at end of file diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 2aae9bce6..eb45c770e 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -28,7 +28,7 @@ import { CollectionView } from "./collections/CollectionView"; import { DocumentManager } from "../util/DocumentManager"; import { FormattedTextBox } from "./nodes/FormattedTextBox"; import { FieldView } from "./nodes/FieldView"; -import { LinkManager } from "./nodes/LinkManager"; +import { LinkManager } from "../util/LinkManager"; library.add(faLink); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0baa061ab..fc6974e6c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -52,7 +52,7 @@ library.add(faDesktop); const linkSchema = createSchema({ title: "string", linkDescription: "string", - linkTags: listSpec("string"), + linkTags: "string", linkedTo: Doc, linkedFrom: Doc }); diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 71a423338..2ab8c3460 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -6,52 +6,115 @@ import './LinkEditor.scss'; import { props } from "bluebird"; import { DocumentView } from "./DocumentView"; import { link } from "fs"; -import { StrCast } from "../../../new_fields/Types"; +import { StrCast, Cast } from "../../../new_fields/Types"; import { Doc } from "../../../new_fields/Doc"; +import { List } from "../../../new_fields/List"; +import { listSpec } from "../../../new_fields/Schema"; +import { LinkManager } from "../../util/LinkManager"; interface Props { + sourceDoc: Doc; linkDoc: Doc; + groups: Map; showLinks: () => void; } @observer export class LinkEditor extends React.Component { - @observable private _nameInput: string = StrCast(this.props.linkDoc.title); - @observable private _descriptionInput: string = StrCast(this.props.linkDoc.linkDescription); + // @observable private _title: string = StrCast(this.props.linkDoc.title); + // @observable private _description: string = StrCast(this.props.linkDoc.linkDescription); + // @observable private _tags: Array = Cast(this.props.linkDoc.linkTags, List); + // @action + // onTitleChanged = (e: React.ChangeEvent) => { + // this._title = e.target.value; + // } - onSaveButtonPressed = (e: React.PointerEvent): void => { - e.stopPropagation(); + // @action + // onDescriptionChanged = (e: React.ChangeEvent) => { + // this._description = e.target.value; + // } + + // renderTags() { + // return this._tags.map(tag => { + // if (tag === "") { + // return ; + // } else { + // return ; + // } + // }) + // } + + // addTag = (): void => { + // this._tags.push(""); + // } + @action + editGroup(groupId: number, value: string) { let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - linkDoc.title = this._nameInput; - linkDoc.linkDescription = this._descriptionInput; + let groupDoc = this.props.groups.get(groupId); + if (groupDoc) { + groupDoc.proto!.type = value; + if (Doc.AreProtosEqual(this.props.sourceDoc, Cast(linkDoc.anchor1, Doc, new Doc))) { + // let groups = Cast(linkDoc.anchor1Groups, listSpec(Doc), []); + // groups.push(groupDoc); + linkDoc.anchor1Groups = new List([groupDoc]); + + } else { + linkDoc.anchor2Groups = new List([groupDoc]); + } + } - this.props.showLinks(); } + renderGroup(groupId: number, groupDoc: Doc) { + return ( +
+

type:

+ this.editGroup(groupId, e.target.value)}> +
+ ) + } + renderGroups() { + let groups: Array = []; + this.props.groups.forEach((groupDoc, groupId) => { + groups.push( +
+ {this.renderGroup(groupId, groupDoc)} +
+ ) + }); + return groups; + } + + onSaveButtonPressed = (e: React.PointerEvent): void => { + e.stopPropagation(); + + // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; + // // linkDoc.title = this._title; + // // linkDoc.linkDescription = this._description; + + this.props.showLinks(); + } render() { + let destination = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); return (
- - +

linked to: {destination.proto!.title}

+ Groups: + {this.renderGroups()} + + {/* + */} + {/* {this.renderTags()} + */}
SAVE
); } - - @action - onNameChanged = (e: React.ChangeEvent) => { - this._nameInput = e.target.value; - } - - @action - onDescriptionChanged = (e: React.ChangeEvent) => { - this._descriptionInput = e.target.value; - } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkManager.tsx b/src/client/views/nodes/LinkManager.tsx deleted file mode 100644 index 1064eaa22..000000000 --- a/src/client/views/nodes/LinkManager.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { observable, computed, action } from "mobx"; -import React = require("react"); -import { SelectionManager } from "../../util/SelectionManager"; -import { observer } from "mobx-react"; -import './LinkEditor.scss'; -import { props } from "bluebird"; -import { DocumentView } from "./DocumentView"; -import { link } from "fs"; -import { StrCast, Cast } from "../../../new_fields/Types"; -import { Doc } from "../../../new_fields/Doc"; -import { listSpec } from "../../../new_fields/Schema"; - - -export class LinkManager { - private static _instance: LinkManager; - public static get Instance(): LinkManager { - return this._instance || (this._instance = new this()); - } - private constructor() { - } - - @observable - public allLinks: Array = []; - - public findAllRelatedLinks(source: Doc): Array { - let related = LinkManager.Instance.allLinks.filter( - link => Doc.AreProtosEqual(source, Cast(link.linkedFrom, Doc, new Doc)) || Doc.AreProtosEqual(source, Cast(link.linkedTo, Doc, new Doc))); - return related; - } - - public findRelatedGroupedLinks(source: Doc): Map> { - let related = this.findAllRelatedLinks(source); - let categories = new Map(); - related.forEach(link => { - let group = categories.get(link.linkTags); - if (group) group.push(link); - else group = [link]; - categories.set(link.linkTags, group); - }) - return categories; - } - - public findOppositeAnchor(link: Doc, source: Doc): Doc { - if (Doc.AreProtosEqual(source, Cast(link.linkedFrom, Doc, new Doc))) { - return Cast(link.linkedTo, Doc, new Doc); - } else { - return Cast(link.linkedFrom, Doc, new Doc); - } - } - -} \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 6dc5623d1..affe35e2a 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -8,7 +8,9 @@ import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; -import { LinkManager } from "./LinkManager"; +import { LinkManager } from "../../util/LinkManager"; +import { number } from "prop-types"; +import { listSpec } from "../../../new_fields/Schema"; interface Props { docView: DocumentView; @@ -29,23 +31,20 @@ export class LinkMenu extends React.Component { // }); // } - renderLinkGroup(links: Doc[]) { - console.log("render link group"); + renderLinkGroupItems(links: Doc[]) { let source = this.props.docView.Document; - console.log("num links", links.length, typeof links); return links.map(link => { - let destination = (link["linkedTo"] === source) ? link["linkedFrom"] : link["linkedTo"]; + // let destination = (link["linkedTo"] === source) ? link["linkedFrom"] : link["linkedTo"]; + let destination = LinkManager.Instance.findOppositeAnchor(link, source); let doc = FieldValue(Cast(destination, Doc)); if (doc) { console.log(doc[Id] + source[Id], "source is", source[Id]); - return this._editingLink = link)} type={""} />; + return this._editingLink = link)} type={""} />; } }); } - renderLinkItems(links: Map>) { - console.log("render link items"); - + renderLinkItems = (links: Map>): Array => { let linkItems: Array = []; links.forEach((links, group) => { @@ -54,7 +53,7 @@ export class LinkMenu extends React.Component {

{group}:

- {this.renderLinkGroup(links)} + {this.renderLinkGroupItems(links)}
) @@ -80,8 +79,20 @@ export class LinkMenu extends React.Component { ); } else { + let counter = 0; + let groups = new Map(); + let groupList = (Doc.AreProtosEqual(this.props.docView.props.Document, Cast(this._editingLink.anchor1, Doc, new Doc))) ? + Cast(this._editingLink.anchor1Groups, listSpec(Doc), []) : Cast(this._editingLink.anchor2Groups, listSpec(Doc), []); + groupList.forEach(group => { + if (group instanceof Doc) { + console.log(counter); + groups.set(counter, group); + counter++; + } + }) + return ( - this._editingLink = undefined)}> + this._editingLink = undefined)}> ); } -- cgit v1.2.3-70-g09d2 From d429898b5337331450e46c223380e5d00967b2d6 Mon Sep 17 00:00:00 2001 From: Fawn Date: Mon, 10 Jun 2019 20:02:32 -0400 Subject: added set up for metadata on links --- src/client/documents/Documents.ts | 1 + src/client/util/DocumentManager.ts | 5 +- src/client/util/DragManager.ts | 4 +- src/client/util/LinkManager.ts | 81 ++++++++---- src/client/views/nodes/LinkEditor.scss | 42 ++++++ src/client/views/nodes/LinkEditor.tsx | 229 +++++++++++++++++++++++++++------ src/client/views/nodes/LinkMenu.tsx | 34 +++-- 7 files changed, 313 insertions(+), 83 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5ec19f987..731490d97 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -91,6 +91,7 @@ export namespace DocUtils { UndoManager.RunInBatch(() => { let groupDoc = Docs.TextDocument(); groupDoc.proto!.type = "*"; + groupDoc.proto!.metadata = new List([]); let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 52f0fe3f6..2acbb3ad4 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -9,7 +9,7 @@ import { CollectionView } from '../views/collections/CollectionView'; import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; import { Id } from '../../new_fields/FieldSymbols'; -import { LinkManager } from './LinkManager'; +import { LinkManager, LinkUtils } from './LinkManager'; export class DocumentManager { @@ -92,7 +92,8 @@ export class DocumentManager { pairs.push(...linksList.reduce((pairs, link) => { if (link) { // let destination = (link["linkedTo"] === dv.props.Document) ? link["linkedFrom"] : link["linkedTo"]; - let destination = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); + + let destination = LinkUtils.findOppositeAnchor(link, dv.props.Document); let linkToDoc = FieldValue(Cast(destination, Doc)); // let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); if (linkToDoc) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 809368aff..9ac421fbf 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -4,7 +4,7 @@ import { Cast } from "../../new_fields/Types"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import * as globalCssVariables from "../views/globalCssVariables.scss"; -import { LinkManager } from "./LinkManager"; +import { LinkManager, LinkUtils } from "./LinkManager"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc | Promise, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType) { @@ -48,7 +48,7 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n // let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs); let linkDocs = LinkManager.Instance.findAllRelatedLinks(srcTarg); if (linkDocs) draggedDocs = linkDocs.map(link => { - return LinkManager.Instance.findOppositeAnchor(link, sourceDoc); + return LinkUtils.findOppositeAnchor(link, sourceDoc); }); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index a6d649395..5e8e0475b 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -10,6 +10,43 @@ import { Doc } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; +export namespace LinkUtils { + export function findOppositeAnchor(link: Doc, anchor: Doc): Doc { + if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { + return Cast(link.anchor2, Doc, new Doc); + } else { + return Cast(link.anchor1, Doc, new Doc); + } + } + + // export function getAnchorGroups(link: Doc, anchor: Doc): Doc[] { + // let groups; + // if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { + // groups = Cast(link.anchor1Groups, listSpec(Doc), []); + // } else { + // groups = Cast(link.anchor2Groups, listSpec(Doc), []); + // } + + // if (groups instanceof Doc[]) { + // return groups; + // } else { + // return []; + // } + // // if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { + // // returnCast(link.anchor1Groups, listSpec(Doc), []); + // // } else { + // // return Cast(link.anchor2Groups, listSpec(Doc), []); + // // } + // } + + export function setAnchorGroups(link: Doc, anchor: Doc, groups: Doc[]) { + if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { + link.anchor1Groups = new List(groups); + } else { + link.anchor2Groups = new List(groups); + } + } +} export class LinkManager { private static _instance: LinkManager; @@ -19,31 +56,30 @@ export class LinkManager { private constructor() { } - @observable - public allLinks: Array = []; + @observable public allLinks: Array = []; + @observable public allGroups: Map = new Map(); - public findAllRelatedLinks(source: Doc): Array { - let related = LinkManager.Instance.allLinks.filter( - link => Doc.AreProtosEqual(source, Cast(link.anchor1, Doc, new Doc)) || Doc.AreProtosEqual(source, Cast(link.anchor2, Doc, new Doc))); - return related; + public findAllRelatedLinks(anchor: Doc): Array { + return LinkManager.Instance.allLinks.filter( + link => Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)) || Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc))); } - public findRelatedGroupedLinks(source: Doc): Map> { - let related = this.findAllRelatedLinks(source); + public findRelatedGroupedLinks(anchor: Doc): Map> { + let related = this.findAllRelatedLinks(anchor); - let categories = new Map(); + let anchorGroups = new Map(); related.forEach(link => { // get groups of given doc - let groups = (Doc.AreProtosEqual(source, Cast(link.anchor1, Doc, new Doc))) ? Cast(link.anchor1Groups, listSpec(Doc), []) : Cast(link.anchor2Groups, listSpec(Doc), []); - if (groups) { - if (groups.length > 0) { - groups.forEach(groupDoc => { + let oppGroups = (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) ? Cast(link.anchor1Groups, listSpec(Doc), []) : Cast(link.anchor2Groups, listSpec(Doc), []); + if (oppGroups) { + if (oppGroups.length > 0) { + oppGroups.forEach(groupDoc => { if (groupDoc instanceof Doc) { let groupType = StrCast(groupDoc.proto!.type); - let group = categories.get(groupType); // TODO: clean this up lol + let group = anchorGroups.get(groupType); // TODO: clean this up lol if (group) group.push(link); else group = [link]; - categories.set(groupType, group); + anchorGroups.set(groupType, group); } else { // promise doc } @@ -52,10 +88,10 @@ export class LinkManager { } else { // if link is in no groups then put it in default group - let group = categories.get("*"); + let group = anchorGroups.get("*"); if (group) group.push(link); else group = [link]; - categories.set("*", group); + anchorGroups.set("*", group); } } @@ -66,16 +102,11 @@ export class LinkManager { // else group = [link]; // categories.set(link.linkTags, group); }) - return categories; + return anchorGroups; } - public findOppositeAnchor(link: Doc, source: Doc): Doc { - if (Doc.AreProtosEqual(source, Cast(link.anchor1, Doc, new Doc))) { - return Cast(link.anchor2, Doc, new Doc); - } else { - return Cast(link.anchor1, Doc, new Doc); - } - } + + // public findAnchorTags(link: Doc, source: Doc): Doc[] { // if (source) diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index 9629585d7..52ed26442 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -39,4 +39,46 @@ .save-button:hover { background: $main-accent; cursor: pointer; +} + +.linkEditor { + font-size: 12px; // TODO + + .linkEditor-back { + // background-color: $dark-color; + // color: $light-color; + margin-bottom: 6px; + } + + .linkEditor-groupsLabel { + display: flex; + justify-content: space-between; + button { + width: 20px; + height: 20px; + margin-left: 6px; + padding: 0; + font-size: 14px; + } + } + .linkEditor-linkedTo { + border-bottom: 0.5px solid $light-color-secondary; + padding-bottom: 6px; + margin-bottom: 6px; + } + .linkEditor-group { + background-color: $light-color-secondary; + padding: 6px; + margin: 3px 0; + border-radius: 3px; + } + .linkEditor-group-row { + display: flex; + .linkEditor-group-row-label { + margin-right: 6px; + } + } + .linkEditor-metadata-row { + display: flex; + } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 2ab8c3460..14fdd26df 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -10,17 +10,57 @@ import { StrCast, Cast } from "../../../new_fields/Types"; import { Doc } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; -import { LinkManager } from "../../util/LinkManager"; +import { LinkManager, LinkUtils } from "../../util/LinkManager"; +import { Docs } from "../../documents/Documents"; +import { Utils } from "../../../Utils"; +import { faArrowLeft, faEllipsisV } from '@fortawesome/free-solid-svg-icons'; +import { library } from "@fortawesome/fontawesome-svg-core"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { string } from "prop-types"; -interface Props { +library.add(faArrowLeft); +library.add(faEllipsisV); + +interface LinkEditorProps { sourceDoc: Doc; linkDoc: Doc; - groups: Map; + groups: Map; + metadata: Map>; showLinks: () => void; } @observer -export class LinkEditor extends React.Component { +export class LinkEditor extends React.Component { + + // @observable private _groups: Map = new Map(); + // @observable private _metadata: Map> = new Map(); + + // // componentDidMount() { + + // // } + // constructor(props: LinkEditorProps) { + // super(props); + + // let groups = new Map(); + // let metadata: Map> = new Map(); + // let groupList = (Doc.AreProtosEqual(props.docView.props.Document, Cast(this._editingLink.anchor1, Doc, new Doc))) ? + // Cast(this._editingLink.anchor1Groups, listSpec(Doc), []) : Cast(this._editingLink.anchor2Groups, listSpec(Doc), []); + // groupList.forEach(groupDoc => { + // if (groupDoc instanceof Doc) { + // let id = Utils.GenerateGuid(); + // groups.set(id, groupDoc); + + // let metadataMap = new Map(); + // let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []); + // metadataDocs.forEach(mdDoc => { + // if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc + // metadataMap.set(Utils.GenerateGuid(), mdDoc); + // } + // }) + // metadata.set(id, metadataMap); + // } + // }) + // } // @observable private _title: string = StrCast(this.props.linkDoc.title); // @observable private _description: string = StrCast(this.props.linkDoc.linkDescription); @@ -51,68 +91,175 @@ export class LinkEditor extends React.Component { // } @action - editGroup(groupId: number, value: string) { + editGroup(groupId: string, value: string) { let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; let groupDoc = this.props.groups.get(groupId); if (groupDoc) { groupDoc.proto!.type = value; - if (Doc.AreProtosEqual(this.props.sourceDoc, Cast(linkDoc.anchor1, Doc, new Doc))) { - // let groups = Cast(linkDoc.anchor1Groups, listSpec(Doc), []); - // groups.push(groupDoc); - linkDoc.anchor1Groups = new List([groupDoc]); - - } else { - linkDoc.anchor2Groups = new List([groupDoc]); - } + LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, [groupDoc]); } + } + + @action + addGroup = (e: React.MouseEvent): void => { + // create new document for group + let groupDoc = Docs.TextDocument(); + groupDoc.proto!.title = ""; + groupDoc.proto!.metadata = new List([]); + this.props.groups.set(Utils.GenerateGuid(), groupDoc); + + let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; + LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this.props.groups.values())); } - renderGroup(groupId: number, groupDoc: Doc) { + renderGroup(groupId: string, groupDoc: Doc) { + // let metadata = this.props.metadata.get(groupId); + // if (!metadata) { + // metadata = new Map(); + // } return ( -
-

type:

- this.editGroup(groupId, e.target.value)}> + //
+
+

type:

+ this.editGroup(groupId, e.target.value)}>
+ // {/* {this.renderMetadata(groupId)} */ } + // {/* */ } + // //
) } + @action + addMetadata = (groupId: string): void => { + // create new metadata doc + let mdDoc = Docs.TextDocument(); + mdDoc.proto!.title = ""; + mdDoc.proto!.value = ""; + + // append to map + let mdMap = this.props.metadata.get(groupId); + if (mdMap) { + mdMap.set(Utils.GenerateGuid(), mdDoc); + } else { + mdMap = new Map(); + mdMap.set(Utils.GenerateGuid(), mdDoc); + } + + // add to internal representation of metadata + this.props.metadata.set(groupId, mdMap); + + // add to internatal representation of group + let groupDoc = this.props.groups.get(groupId); + if (groupDoc) { + groupDoc.proto!.metadata = new List(Array.from(mdMap.values())); + this.props.groups.set(groupId, groupDoc); + } + + // add to link doc + let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; + LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this.props.groups.values())); + + } + + // @action + // addMetadata = (groupId: string): void => { + // let groupDoc = this.props.groups.get(groupId); + // if (groupDoc) { + // // create new document for metadata row + // let metadata = Cast(groupDoc.metadata, listSpec(Doc), []); + // let metadataDoc = Docs.TextDocument(); + // metadataDoc.proto!.title = ""; + // metadataDoc.proto!.value = ""; + // let metadataMap = new Map([metadataDoc]); // TODO: append to metadata + // } + // } + + // @action + // editMetadataTitle = (groupId: string, mdId: string, value: string) => { + // let group = this.props.metadata.get(groupId); + // if (group) { + // let mdDoc = group.get(mdId); + // if (mdDoc) { + // mdDoc.proto!.title = value; + // } + // } + // } + + // @action + // editMetadataValue = (groupId: string, mdId: string, value: string) => { + // let group = this.props.metadata.get(groupId); + // if (group) { + // let mdDoc = group.get(mdId); + // if (mdDoc) { + // mdDoc.proto!.value = value; + // } + // } + // } + + @action + editMetadataTitle(groupId: string, mdId: string, value: string) { + + } + + @action + editMetadataValue(groupId: string, mdId: string, value: string) { + + } + + renderMetadata(groupId: string) { + let metadata: Array = []; + let metadataMap = this.props.metadata.get(groupId); + if (metadataMap) { + metadataMap.forEach((mdDoc, mdId) => { + metadata.push( +
+ this.editMetadataTitle(groupId, mdId, e.target.value)}> + : + this.editMetadataValue(groupId, mdId, e.target.value)}> +
+ ) + }) + } + + return metadata; + + // let metadataList: Array = []; + // metadata.forEach((mdDoc, mdId) => { + // metadataList.push( + //
+ // this.editMetadataTitle(groupId, mdId, e.target.value)}>: + // this.editMetadataValue(groupId, mdId, e.target.value)}> + //
+ // ) + // }) + } + renderGroups() { let groups: Array = []; this.props.groups.forEach((groupDoc, groupId) => { - groups.push( -
- {this.renderGroup(groupId, groupDoc)} -
- ) + groups.push(this.renderGroup(groupId, groupDoc)) }); return groups; } - onSaveButtonPressed = (e: React.PointerEvent): void => { - e.stopPropagation(); - - // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - // // linkDoc.title = this._title; - // // linkDoc.linkDescription = this._description; - - this.props.showLinks(); - } - render() { - let destination = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); + let destination = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); return ( -
-

linked to: {destination.proto!.title}

- Groups: +
+ +

editing link to: {destination.proto!.title}

+
+ Groups: + +
{this.renderGroups()} - {/* - */} - {/* {this.renderTags()} - */} -
SAVE
); diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index affe35e2a..ab478feae 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -8,9 +8,10 @@ import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; -import { LinkManager } from "../../util/LinkManager"; -import { number } from "prop-types"; +import { LinkManager, LinkUtils } from "../../util/LinkManager"; +import { number, string } from "prop-types"; import { listSpec } from "../../../new_fields/Schema"; +import { Utils } from "../../../Utils"; interface Props { docView: DocumentView; @@ -34,12 +35,11 @@ export class LinkMenu extends React.Component { renderLinkGroupItems(links: Doc[]) { let source = this.props.docView.Document; return links.map(link => { - // let destination = (link["linkedTo"] === source) ? link["linkedFrom"] : link["linkedTo"]; - let destination = LinkManager.Instance.findOppositeAnchor(link, source); + let destination = LinkUtils.findOppositeAnchor(link, source); let doc = FieldValue(Cast(destination, Doc)); if (doc) { console.log(doc[Id] + source[Id], "source is", source[Id]); - return this._editingLink = link)} type={""} />; + return this._editingLink = link)} type={""} />; } }); } @@ -79,20 +79,28 @@ export class LinkMenu extends React.Component {
); } else { - let counter = 0; - let groups = new Map(); + let groups = new Map(); + let metadata: Map> = new Map(); let groupList = (Doc.AreProtosEqual(this.props.docView.props.Document, Cast(this._editingLink.anchor1, Doc, new Doc))) ? Cast(this._editingLink.anchor1Groups, listSpec(Doc), []) : Cast(this._editingLink.anchor2Groups, listSpec(Doc), []); - groupList.forEach(group => { - if (group instanceof Doc) { - console.log(counter); - groups.set(counter, group); - counter++; + groupList.forEach(groupDoc => { + if (groupDoc instanceof Doc) { + let id = Utils.GenerateGuid(); + groups.set(id, groupDoc); + + let metadataMap = new Map(); + let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []); + metadataDocs.forEach(mdDoc => { + if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc + metadataMap.set(Utils.GenerateGuid(), mdDoc); + } + }) + metadata.set(id, metadataMap); } }) return ( - this._editingLink = undefined)}> + this._editingLink = undefined)}> ); } -- cgit v1.2.3-70-g09d2 From 074b4b2a3246ae86fda334629d40540dd2bbf633 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 11 Jun 2019 15:27:02 -0400 Subject: links can choose group type from preexisting group types --- src/client/util/LinkManager.ts | 2 +- src/client/views/nodes/LinkEditor.scss | 26 +++ src/client/views/nodes/LinkEditor.tsx | 370 ++++++++++++++++++--------------- src/client/views/nodes/LinkMenu.tsx | 43 ++-- 4 files changed, 252 insertions(+), 189 deletions(-) (limited to 'src') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 5e8e0475b..99fb4c6c0 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -57,7 +57,7 @@ export class LinkManager { } @observable public allLinks: Array = []; - @observable public allGroups: Map = new Map(); + @observable public allGroups: Map> = new Map(); public findAllRelatedLinks(anchor: Doc): Array { return LinkManager.Instance.allLinks.filter( diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index 52ed26442..d3ac8cf19 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -80,5 +80,31 @@ } .linkEditor-metadata-row { display: flex; + justify-content: space-between; + input { + width: calc(50% - 2px); + } + } +} + +.linkEditor-dropdown { + width: 100%; + position: relative; + .linkEditor-options-wrapper { + width: 100%; + position: absolute; + top: 19px; + left: 0; + display: flex; + flex-direction: column; + } + .linkEditor-option { + background-color: red; + border: 1px solid $intermediate-color; + border-top: 0; + cursor: pointer; + &:hover { + background-color: $light-color-secondary; + } } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 14fdd26df..481d3bf37 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -21,85 +21,119 @@ import { string } from "prop-types"; library.add(faArrowLeft); library.add(faEllipsisV); +// this dropdown could be generalizeds +@observer +class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: string, setGroup: (groupId: string, group: string) => void }> { + @observable private _searchTerm: string = ""; + @observable private _groupType: string = this.props.groupType; + + @action + setSearchTerm(value: string) { + this._searchTerm = value; + } + + @action + setGroupType(value: string) { + this._groupType = value; + } + + createGroup(value: string) { + LinkManager.Instance.allGroups.set(value, []); + this.props.setGroup(this.props.groupId, value); + } + + renderOptions = (): JSX.Element[] => { + let allGroups: string[], searchTerm: string, results: string[], exactFound: boolean; + if (this._searchTerm !== "") { + allGroups = Array.from(LinkManager.Instance.allGroups.keys()); + searchTerm = this._searchTerm.toUpperCase(); + results = allGroups.filter(group => group.toUpperCase().indexOf(searchTerm) > -1); + exactFound = results.findIndex(group => group.toUpperCase() === searchTerm) > -1; + } else { + results = []; + exactFound = false; + } + + let options = []; + results.forEach(result => { + options.push(
{ this.props.setGroup(this.props.groupId, result); this.setGroupType(result); this.setSearchTerm("") }}>{result}
) + }); + + if (!exactFound && this._searchTerm !== "") { + options.push(
{ this.createGroup(this._searchTerm); this.setGroupType(this._searchTerm); this.setSearchTerm("") }}>Create new "{this._searchTerm}" group
) + } + + return options; + } + + render() { + return ( +
+ { this.setSearchTerm(e.target.value); this.setGroupType(e.target.value) }}> +
+ {this.renderOptions()} +
+
+ ) + } +} + + interface LinkEditorProps { sourceDoc: Doc; linkDoc: Doc; - groups: Map; - metadata: Map>; + // groups: Map; + // metadata: Map>; showLinks: () => void; } @observer export class LinkEditor extends React.Component { - // @observable private _groups: Map = new Map(); - // @observable private _metadata: Map> = new Map(); - - // // componentDidMount() { - - // // } - // constructor(props: LinkEditorProps) { - // super(props); - - // let groups = new Map(); - // let metadata: Map> = new Map(); - // let groupList = (Doc.AreProtosEqual(props.docView.props.Document, Cast(this._editingLink.anchor1, Doc, new Doc))) ? - // Cast(this._editingLink.anchor1Groups, listSpec(Doc), []) : Cast(this._editingLink.anchor2Groups, listSpec(Doc), []); - // groupList.forEach(groupDoc => { - // if (groupDoc instanceof Doc) { - // let id = Utils.GenerateGuid(); - // groups.set(id, groupDoc); - - // let metadataMap = new Map(); - // let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []); - // metadataDocs.forEach(mdDoc => { - // if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc - // metadataMap.set(Utils.GenerateGuid(), mdDoc); - // } - // }) - // metadata.set(id, metadataMap); - // } - // }) - // } - - // @observable private _title: string = StrCast(this.props.linkDoc.title); - // @observable private _description: string = StrCast(this.props.linkDoc.linkDescription); - // @observable private _tags: Array = Cast(this.props.linkDoc.linkTags, List); - - // @action - // onTitleChanged = (e: React.ChangeEvent) => { - // this._title = e.target.value; - // } + @observable private _groups: Map = new Map(); + @observable private _metadata: Map> = new Map(); + + constructor(props: LinkEditorProps) { + super(props); + + let groups = new Map(); + let metadata: Map> = new Map(); + let groupList = (Doc.AreProtosEqual(props.sourceDoc, Cast(props.linkDoc.anchor1, Doc, new Doc))) ? + Cast(props.linkDoc.anchor1Groups, listSpec(Doc), []) : Cast(props.linkDoc.anchor2Groups, listSpec(Doc), []); + groupList.forEach(groupDoc => { + if (groupDoc instanceof Doc) { + let id = Utils.GenerateGuid(); + groups.set(id, groupDoc); + + let metadataMap = new Map(); + let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []); + metadataDocs.forEach(mdDoc => { + if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc + metadataMap.set(Utils.GenerateGuid(), mdDoc); + } + }) + metadata.set(id, metadataMap); + } else { + // promise doc + } + }) + this._groups = groups; + this._metadata = metadata; + } // @action - // onDescriptionChanged = (e: React.ChangeEvent) => { - // this._description = e.target.value; - // } - - // renderTags() { - // return this._tags.map(tag => { - // if (tag === "") { - // return ; - // } else { - // return ; - // } - // }) - // } - - // addTag = (): void => { - // this._tags.push(""); + // editGroup(groupId: string, value: string) { + // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; + // let groupDoc = this._groups.get(groupId); + // if (groupDoc) { + // groupDoc.proto!.type = value; + // LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, [groupDoc]); + // } // } - @action - editGroup(groupId: string, value: string) { - let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - let groupDoc = this.props.groups.get(groupId); - if (groupDoc) { - groupDoc.proto!.type = value; - LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, [groupDoc]); - } - } - @action addGroup = (e: React.MouseEvent): void => { // create new document for group @@ -107,149 +141,150 @@ export class LinkEditor extends React.Component { groupDoc.proto!.title = ""; groupDoc.proto!.metadata = new List([]); - this.props.groups.set(Utils.GenerateGuid(), groupDoc); + this._groups.set(Utils.GenerateGuid(), groupDoc); + + let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; + LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); + } + @action + setGroup = (groupId: string, group: string): void => { let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this.props.groups.values())); + let groupDoc = this._groups.get(groupId); + if (groupDoc) { + groupDoc.proto!.type = group; + LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, [groupDoc]); + } } renderGroup(groupId: string, groupDoc: Doc) { - // let metadata = this.props.metadata.get(groupId); - // if (!metadata) { - // metadata = new Map(); - // } + console.log("testing", groupDoc["strawberry"], groupDoc["type"]); return ( - //
-
-

type:

- this.editGroup(groupId, e.target.value)}> +
+
+

type:

+ + {/* this.editGroup(groupId, e.target.value)}> */} + {/* this.editGroup(groupId, e.target.value)}> */} +
+ {this.renderMetadata(groupId)} +
- // {/* {this.renderMetadata(groupId)} */ } - // {/* */ } - // //
) } @action - addMetadata = (groupId: string): void => { - // create new metadata doc - let mdDoc = Docs.TextDocument(); - mdDoc.proto!.title = ""; - mdDoc.proto!.value = ""; - - // append to map - let mdMap = this.props.metadata.get(groupId); - if (mdMap) { - mdMap.set(Utils.GenerateGuid(), mdDoc); + addMetadata = (groupType: string): void => { + let mdKeys = LinkManager.Instance.allGroups.get(groupType); + if (mdKeys) { + if (mdKeys.indexOf("new key") > -1) { + mdKeys.push("new key"); + } } else { - mdMap = new Map(); - mdMap.set(Utils.GenerateGuid(), mdDoc); + mdKeys = ["new key"]; } + LinkManager.Instance.allGroups.set(groupType, mdKeys); + console.log("md added", groupType, LinkManager.Instance.allGroups.get(groupType), LinkManager.Instance.allGroups); + + // // create new metadata doc + // let mdDoc = Docs.TextDocument(); + // mdDoc.proto!.title = ""; + // mdDoc.proto!.value = ""; + + // // append to map + // let mdMap = this._metadata.get(groupId); + // if (mdMap) { + // mdMap.set(Utils.GenerateGuid(), mdDoc); + // } else { + // mdMap = new Map(); + // mdMap.set(Utils.GenerateGuid(), mdDoc); + // } - // add to internal representation of metadata - this.props.metadata.set(groupId, mdMap); - - // add to internatal representation of group - let groupDoc = this.props.groups.get(groupId); - if (groupDoc) { - groupDoc.proto!.metadata = new List(Array.from(mdMap.values())); - this.props.groups.set(groupId, groupDoc); - } + // // add to internal representation of metadata + // this._metadata.set(groupId, mdMap); - // add to link doc - let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this.props.groups.values())); + // // add to internatal representation of group + // let groupDoc = this._groups.get(groupId); + // if (groupDoc) { + // groupDoc.proto!.metadata = new List(Array.from(mdMap.values())); + // this._groups.set(groupId, groupDoc); + // } + // // add to link doc + // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; + // LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); } // @action - // addMetadata = (groupId: string): void => { - // let groupDoc = this.props.groups.get(groupId); - // if (groupDoc) { - // // create new document for metadata row - // let metadata = Cast(groupDoc.metadata, listSpec(Doc), []); - // let metadataDoc = Docs.TextDocument(); - // metadataDoc.proto!.title = ""; - // metadataDoc.proto!.value = ""; - // let metadataMap = new Map([metadataDoc]); // TODO: append to metadata - // } - // } - - // @action - // editMetadataTitle = (groupId: string, mdId: string, value: string) => { - // let group = this.props.metadata.get(groupId); - // if (group) { - // let mdDoc = group.get(mdId); + // editMetadataTitle(groupId: string, mdId: string, value: string) { + // let groupMd = this._metadata.get(groupId); + // if (groupMd) { + // let mdDoc = groupMd.get(mdId); // if (mdDoc) { // mdDoc.proto!.title = value; // } // } + // // set group and link? // } // @action - // editMetadataValue = (groupId: string, mdId: string, value: string) => { - // let group = this.props.metadata.get(groupId); - // if (group) { - // let mdDoc = group.get(mdId); + // editMetadataValue(groupId: string, mdId: string, value: string) { + // let groupMd = this._metadata.get(groupId); + // if (groupMd) { + // let mdDoc = groupMd.get(mdId); // if (mdDoc) { // mdDoc.proto!.value = value; // } // } + // // set group and link? // } - @action - editMetadataTitle(groupId: string, mdId: string, value: string) { - - } - - @action - editMetadataValue(groupId: string, mdId: string, value: string) { - - } - renderMetadata(groupId: string) { let metadata: Array = []; - let metadataMap = this.props.metadata.get(groupId); - if (metadataMap) { - metadataMap.forEach((mdDoc, mdId) => { - metadata.push( -
- this.editMetadataTitle(groupId, mdId, e.target.value)}> - : - this.editMetadataValue(groupId, mdId, e.target.value)}> -
- ) - }) + let groupDoc = this._groups.get(groupId); + if (groupDoc) { + let mdDoc = Cast(groupDoc.proto!.metadata, Doc, new Doc); + let groupType = StrCast(groupDoc.proto!.type); + let groupMdKeys = LinkManager.Instance.allGroups.get(groupType); + console.log("rendering md", groupType, groupMdKeys, LinkManager.Instance.allGroups); + if (groupMdKeys) { + groupMdKeys.forEach(key => { + metadata.push( +
+ + : + +
+ ) + }) + } } - return metadata; - // let metadataList: Array = []; - // metadata.forEach((mdDoc, mdId) => { - // metadataList.push( - //
- // this.editMetadataTitle(groupId, mdId, e.target.value)}>: - // this.editMetadataValue(groupId, mdId, e.target.value)}> - //
- // ) - // }) - } + // let metadataMap = this._metadata.get(groupId); + // if (metadataMap) { + // metadataMap.forEach((mdDoc, mdId) => { + // metadata.push( + //
+ // this.editMetadataTitle(groupId, mdId, e.target.value)}> + // : + // this.editMetadataValue(groupId, mdId, e.target.value)}> + //
+ // ) + // }) + // } - renderGroups() { - let groups: Array = []; - this.props.groups.forEach((groupDoc, groupId) => { - groups.push(this.renderGroup(groupId, groupDoc)) - }); - return groups; + return metadata; } render() { let destination = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); + let groups: Array = []; + this._groups.forEach((groupDoc, groupId) => { + groups.push(this.renderGroup(groupId, groupDoc)) + }); + return (
@@ -258,8 +293,7 @@ export class LinkEditor extends React.Component { Groups:
- {this.renderGroups()} - + {groups}
); diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index ab478feae..e378ee1cb 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -48,7 +48,6 @@ export class LinkMenu extends React.Component { let linkItems: Array = []; links.forEach((links, group) => { - console.log("category is ", group); linkItems.push(

{group}:

@@ -59,6 +58,10 @@ export class LinkMenu extends React.Component { ) }); + if (linkItems.length === 0) { + linkItems.push(

no links have been created yet

); + } + return linkItems; } @@ -79,28 +82,28 @@ export class LinkMenu extends React.Component {
); } else { - let groups = new Map(); - let metadata: Map> = new Map(); - let groupList = (Doc.AreProtosEqual(this.props.docView.props.Document, Cast(this._editingLink.anchor1, Doc, new Doc))) ? - Cast(this._editingLink.anchor1Groups, listSpec(Doc), []) : Cast(this._editingLink.anchor2Groups, listSpec(Doc), []); - groupList.forEach(groupDoc => { - if (groupDoc instanceof Doc) { - let id = Utils.GenerateGuid(); - groups.set(id, groupDoc); + // let groups = new Map(); + // let metadata: Map> = new Map(); + // let groupList = (Doc.AreProtosEqual(this.props.docView.props.Document, Cast(this._editingLink.anchor1, Doc, new Doc))) ? + // Cast(this._editingLink.anchor1Groups, listSpec(Doc), []) : Cast(this._editingLink.anchor2Groups, listSpec(Doc), []); + // groupList.forEach(groupDoc => { + // if (groupDoc instanceof Doc) { + // let id = Utils.GenerateGuid(); + // groups.set(id, groupDoc); - let metadataMap = new Map(); - let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []); - metadataDocs.forEach(mdDoc => { - if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc - metadataMap.set(Utils.GenerateGuid(), mdDoc); - } - }) - metadata.set(id, metadataMap); - } - }) + // let metadataMap = new Map(); + // let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []); + // metadataDocs.forEach(mdDoc => { + // if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc + // metadataMap.set(Utils.GenerateGuid(), mdDoc); + // } + // }) + // metadata.set(id, metadataMap); + // } + // }) return ( - this._editingLink = undefined)}> + this._editingLink = undefined)}> ); } -- cgit v1.2.3-70-g09d2 From 2c3b54d6e07c37a4f5fd52a49d6d60e7a8e5d2d2 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 11 Jun 2019 18:17:28 -0400 Subject: metadata types can be assigned to groups of links --- src/client/documents/Documents.ts | 2 +- src/client/views/nodes/LinkEditor.tsx | 76 +++++++++++++++++++++++++++++------ src/client/views/nodes/LinkMenu.tsx | 1 - 3 files changed, 64 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 731490d97..9f1501265 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -91,7 +91,7 @@ export namespace DocUtils { UndoManager.RunInBatch(() => { let groupDoc = Docs.TextDocument(); groupDoc.proto!.type = "*"; - groupDoc.proto!.metadata = new List([]); + groupDoc.proto!.metadata = Docs.TextDocument(); let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 481d3bf37..8d12bc30f 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -37,6 +37,7 @@ class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: s this._groupType = value; } + @action createGroup(value: string) { LinkManager.Instance.allGroups.set(value, []); this.props.setGroup(this.props.groupId, value); @@ -81,12 +82,49 @@ class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: s } } +@observer +class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc, mdKey: string, mdValue: string }> { + @observable private _key: string = this.props.mdKey; + @observable private _value: string = this.props.mdValue; + + @action + editMetadataKey = (value: string): void => { + // TODO: check that metadata doesnt already exist in group + let groupMdKeys = new Array(...LinkManager.Instance.allGroups.get(this.props.groupType)!); + if (groupMdKeys) { + let index = groupMdKeys.indexOf(this._key); + if (index > -1) { + groupMdKeys[index] = value; + } + else { + console.log("OLD KEY WAS NOT FOUND", ...groupMdKeys) + } + } + + this._key = value; + LinkManager.Instance.allGroups.set(this.props.groupType, groupMdKeys); + } + + @action + editMetadataValue = (value: string): void => { + this.props.mdDoc[this._key] = value; + this._value = value; + } + + render() { + return ( +
+ this.editMetadataKey(e.target.value)}>: + this.editMetadataValue(e.target.value)}> +
+ ) + } +} + interface LinkEditorProps { sourceDoc: Doc; linkDoc: Doc; - // groups: Map; - // metadata: Map>; showLinks: () => void; } @@ -158,7 +196,6 @@ export class LinkEditor extends React.Component { } renderGroup(groupId: string, groupDoc: Doc) { - console.log("testing", groupDoc["strawberry"], groupDoc["type"]); return (
@@ -168,23 +205,28 @@ export class LinkEditor extends React.Component { {/* this.editGroup(groupId, e.target.value)}> */}
{this.renderMetadata(groupId)} - + {groupDoc["type"] === "*" ? <> : } + + {/* */}
) } + viewGroupAsTable = (): void => { + + } + @action addMetadata = (groupType: string): void => { let mdKeys = LinkManager.Instance.allGroups.get(groupType); if (mdKeys) { - if (mdKeys.indexOf("new key") > -1) { + if (mdKeys.indexOf("new key") === -1) { mdKeys.push("new key"); } } else { mdKeys = ["new key"]; } LinkManager.Instance.allGroups.set(groupType, mdKeys); - console.log("md added", groupType, LinkManager.Instance.allGroups.get(groupType), LinkManager.Instance.allGroups); // // create new metadata doc // let mdDoc = Docs.TextDocument(); @@ -239,6 +281,14 @@ export class LinkEditor extends React.Component { // // set group and link? // } + @action + editMetadataKey(groupId: string, value: string) { + let groupDoc = this._groups.get(groupId); + if (groupDoc) { + + } + } + renderMetadata(groupId: string) { let metadata: Array = []; let groupDoc = this._groups.get(groupId); @@ -246,15 +296,15 @@ export class LinkEditor extends React.Component { let mdDoc = Cast(groupDoc.proto!.metadata, Doc, new Doc); let groupType = StrCast(groupDoc.proto!.type); let groupMdKeys = LinkManager.Instance.allGroups.get(groupType); - console.log("rendering md", groupType, groupMdKeys, LinkManager.Instance.allGroups); if (groupMdKeys) { - groupMdKeys.forEach(key => { + groupMdKeys.forEach((key, index) => { metadata.push( -
- - : - -
+ + //
+ // + // : + // + //
) }) } diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index e378ee1cb..21b5807ae 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -38,7 +38,6 @@ export class LinkMenu extends React.Component { let destination = LinkUtils.findOppositeAnchor(link, source); let doc = FieldValue(Cast(destination, Doc)); if (doc) { - console.log(doc[Id] + source[Id], "source is", source[Id]); return this._editingLink = link)} type={""} />; } }); -- cgit v1.2.3-70-g09d2 From e1fd270f1806ffd51174c835b335ceb4ebb2fe56 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 12 Jun 2019 16:20:46 -0400 Subject: created remove handlers for link metadata and groups --- src/client/documents/Documents.ts | 14 +- src/client/util/LinkManager.ts | 110 ++++++++++----- src/client/views/nodes/LinkBox.tsx | 38 ++--- src/client/views/nodes/LinkEditor.scss | 32 ++++- src/client/views/nodes/LinkEditor.tsx | 250 +++++++++++++++------------------ src/client/views/nodes/LinkMenu.scss | 2 +- src/client/views/nodes/LinkMenu.tsx | 33 +---- 7 files changed, 251 insertions(+), 228 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9f1501265..9517cbbda 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -89,9 +89,13 @@ export namespace DocUtils { // let protoSrc = source.proto ? source.proto : source; // let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { - let groupDoc = Docs.TextDocument(); - groupDoc.proto!.type = "*"; - groupDoc.proto!.metadata = Docs.TextDocument(); + // let groupDoc1 = Docs.TextDocument(); + // groupDoc1.proto!.type = "*"; + // groupDoc1.proto!.metadata = Docs.TextDocument(); + + // let groupDoc2 = Docs.TextDocument(); + // groupDoc2.proto!.type = "*"; + // groupDoc2.proto!.metadata = Docs.TextDocument(); let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; @@ -99,11 +103,11 @@ export namespace DocUtils { // linkDoc.proto!.linkDescription = ""; linkDoc.proto!.anchor1 = source; linkDoc.proto!.anchor1Page = source.curPage; - linkDoc.proto!.anchor1Groups = new List([groupDoc]); + linkDoc.proto!.anchor1Groups = new List([]); linkDoc.proto!.anchor2 = target; linkDoc.proto!.anchor2Page = target.curPage; - linkDoc.proto!.anchor2Groups = new List([groupDoc]); + linkDoc.proto!.anchor2Groups = new List([]); // linkDoc.proto!.linkedTo = target; // linkDoc.proto!.linkedToPage = target.curPage; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 99fb4c6c0..cc8617052 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -9,6 +9,7 @@ import { StrCast, Cast } from "../../new_fields/Types"; import { Doc } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; +import { string } from "prop-types"; export namespace LinkUtils { export function findOppositeAnchor(link: Doc, anchor: Doc): Doc { @@ -40,14 +41,48 @@ export namespace LinkUtils { // } export function setAnchorGroups(link: Doc, anchor: Doc, groups: Doc[]) { + // console.log("setting groups for anchor", anchor["title"]); if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { link.anchor1Groups = new List(groups); } else { link.anchor2Groups = new List(groups); } } + + export function removeGroupFromAnchor(link: Doc, anchor: Doc, groupType: string) { + let groups = []; + if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { + groups = Cast(link["anchor1Groups"], listSpec(Doc), []); + } else { + groups = Cast(link["anchor2Groups"], listSpec(Doc), []); + } + + let newGroups: Doc[] = []; + groups.forEach(groupDoc => { + if (groupDoc instanceof Doc && StrCast(groupDoc["type"]) !== groupType) { + newGroups.push(groupDoc); + } + }) + LinkUtils.setAnchorGroups(link, anchor, newGroups); + } } +/* + * link doc: + * - anchor1: doc + * - anchor1page: number + * - anchor1groups: list of group docs representing the groups anchor1 categorizes this link/anchor2 in + * - anchor2: doc + * - anchor2page: number + * - anchor2groups: list of group docs representing the groups anchor2 categorizes this link/anchor1 in + * + * group doc: + * - type: string representing the group type/name/category + * - metadata: doc representing the metadata kvps + * + * metadata doc: + * - user defined kvps + */ export class LinkManager { private static _instance: LinkManager; public static get Instance(): LinkManager { @@ -56,60 +91,57 @@ export class LinkManager { private constructor() { } - @observable public allLinks: Array = []; - @observable public allGroups: Map> = new Map(); + @observable public allLinks: Array = []; // list of link docs + @observable public allGroups: Map> = new Map(); // map of group type to list of its metadata keys public findAllRelatedLinks(anchor: Doc): Array { return LinkManager.Instance.allLinks.filter( link => Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)) || Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc))); } + // returns map of group type to anchor's links in that group type public findRelatedGroupedLinks(anchor: Doc): Map> { let related = this.findAllRelatedLinks(anchor); - let anchorGroups = new Map(); + let anchorGroups = new Map>(); related.forEach(link => { - // get groups of given doc - let oppGroups = (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) ? Cast(link.anchor1Groups, listSpec(Doc), []) : Cast(link.anchor2Groups, listSpec(Doc), []); - if (oppGroups) { - if (oppGroups.length > 0) { - oppGroups.forEach(groupDoc => { - if (groupDoc instanceof Doc) { - let groupType = StrCast(groupDoc.proto!.type); - let group = anchorGroups.get(groupType); // TODO: clean this up lol - if (group) group.push(link); - else group = [link]; - anchorGroups.set(groupType, group); - } else { - // promise doc - } - - }) - } - else { - // if link is in no groups then put it in default group - let group = anchorGroups.get("*"); - if (group) group.push(link); - else group = [link]; - anchorGroups.set("*", group); - } - } + // get groups of given anchor categorizes this link/opposite anchor in + let groups = (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) ? Cast(link.anchor1Groups, listSpec(Doc), []) : Cast(link.anchor2Groups, listSpec(Doc), []); + if (groups.length > 0) { + groups.forEach(groupDoc => { + if (groupDoc instanceof Doc) { + let groupType = StrCast(groupDoc["type"]); + let group = anchorGroups.get(groupType); // TODO: clean this up lol + if (group) group.push(link); + else group = [link]; + anchorGroups.set(groupType, group); + } else { + // promise doc + } + + }) + } + else { + // if link is in no groups then put it in default group + let group = anchorGroups.get("*"); + if (group) group.push(link); + else group = [link]; + anchorGroups.set("*", group); + } - // let anchor = this.findOppositeAnchor(link, source); - // let group = categories.get(link.linkTags); - // if (group) group.push(link); - // else group = [link]; - // categories.set(link.linkTags, group); }) return anchorGroups; } - - - - // public findAnchorTags(link: Doc, source: Doc): Doc[] { - // if (source) - // } + public deleteGroup(groupType: string) { + let deleted = LinkManager.Instance.allGroups.delete(groupType); + if (deleted) { + LinkManager.Instance.allLinks.forEach(linkDoc => { + LinkUtils.removeGroupFromAnchor(linkDoc, Cast(linkDoc["anchor1"], Doc, new Doc), groupType); + LinkUtils.removeGroupFromAnchor(linkDoc, Cast(linkDoc["anchor2"], Doc, new Doc), groupType); + }) + } + } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index ebca1dc69..5597bb1aa 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -41,23 +41,23 @@ export class LinkBox extends React.Component { this.props.showEditor(); } - @action - onDeleteButtonPressed = async (e: React.PointerEvent): Promise => { - e.stopPropagation(); - const [linkedFrom, linkedTo] = await Promise.all([Cast(this.props.linkDoc.linkedFrom, Doc), Cast(this.props.linkDoc.linkedTo, Doc)]); - if (linkedFrom) { - const linkedToDocs = Cast(linkedFrom.linkedToDocs, listSpec(Doc)); - if (linkedToDocs) { - linkedToDocs.splice(linkedToDocs.indexOf(this.props.linkDoc), 1); - } - } - if (linkedTo) { - const linkedFromDocs = Cast(linkedTo.linkedFromDocs, listSpec(Doc)); - if (linkedFromDocs) { - linkedFromDocs.splice(linkedFromDocs.indexOf(this.props.linkDoc), 1); - } - } - } + // @action + // onDeleteButtonPressed = async (e: React.PointerEvent): Promise => { + // e.stopPropagation(); + // const [linkedFrom, linkedTo] = await Promise.all([Cast(this.props.linkDoc.linkedFrom, Doc), Cast(this.props.linkDoc.linkedTo, Doc)]); + // if (linkedFrom) { + // const linkedToDocs = Cast(linkedFrom.linkedToDocs, listSpec(Doc)); + // if (linkedToDocs) { + // linkedToDocs.splice(linkedToDocs.indexOf(this.props.linkDoc), 1); + // } + // } + // if (linkedTo) { + // const linkedFromDocs = Cast(linkedTo.linkedFromDocs, listSpec(Doc)); + // if (linkedFromDocs) { + // linkedFromDocs.splice(linkedFromDocs.indexOf(this.props.linkDoc), 1); + // } + // } + // } render() { @@ -80,8 +80,8 @@ export class LinkBox extends React.Component {
-
-
+ {/*
+
*/} ); diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index d3ac8cf19..b56f3046a 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -74,6 +74,7 @@ } .linkEditor-group-row { display: flex; + margin-bottom: 6px; .linkEditor-group-row-label { margin-right: 6px; } @@ -81,8 +82,17 @@ .linkEditor-metadata-row { display: flex; justify-content: space-between; + margin-bottom: 6px; input { - width: calc(50% - 2px); + width: calc(50% - 18px); + height: 20px; + } + button { + width: 20px; + height: 20px; + margin-left: 6px; + padding: 0; + font-size: 14px; } } } @@ -90,6 +100,7 @@ .linkEditor-dropdown { width: 100%; position: relative; + z-index: 999; .linkEditor-options-wrapper { width: 100%; position: absolute; @@ -107,4 +118,23 @@ background-color: $light-color-secondary; } } +} + +.linkEditor-group-buttons { + height: 20px; + display: flex; + justify-content: space-between; + button { + width: 31%; + height: 20px; + margin-left: 6px; + padding: 0; + font-size: 10px; + &:first-child { // delete + font-size: 14px; + } + &:disabled { // delete + background-color: gray; + } + } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 8d12bc30f..05e05c2ee 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -1,11 +1,7 @@ import { observable, computed, action } from "mobx"; import React = require("react"); -import { SelectionManager } from "../../util/SelectionManager"; import { observer } from "mobx-react"; import './LinkEditor.scss'; -import { props } from "bluebird"; -import { DocumentView } from "./DocumentView"; -import { link } from "fs"; import { StrCast, Cast } from "../../../new_fields/Types"; import { Doc } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; @@ -21,7 +17,7 @@ import { string } from "prop-types"; library.add(faArrowLeft); library.add(faEllipsisV); -// this dropdown could be generalizeds +// this dropdown could be generalized @observer class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: string, setGroup: (groupId: string, group: string) => void }> { @observable private _searchTerm: string = ""; @@ -63,7 +59,7 @@ class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: s if (!exactFound && this._searchTerm !== "") { options.push(
{ this.createGroup(this._searchTerm); this.setGroupType(this._searchTerm); this.setSearchTerm("") }}>Create new "{this._searchTerm}" group
) + onClick={() => { this.createGroup(this._searchTerm); this.setGroupType(this._searchTerm); this.setSearchTerm("") }}>Define new "{this._searchTerm}" relationship) } return options; @@ -82,6 +78,8 @@ class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: s } } + + @observer class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc, mdKey: string, mdValue: string }> { @observable private _key: string = this.props.mdKey; @@ -111,11 +109,28 @@ class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc this._value = value; } + @action + removeMetadata = (): void => { + let groupMdKeys = new Array(...LinkManager.Instance.allGroups.get(this.props.groupType)!); + if (groupMdKeys) { + let index = groupMdKeys.indexOf(this._key); + if (index > -1) { + groupMdKeys.splice(index, 1); + } + else { + console.log("OLD KEY WAS NOT FOUND", ...groupMdKeys) + } + } + this._key = ""; + LinkManager.Instance.allGroups.set(this.props.groupType, groupMdKeys); + } + render() { return (
this.editMetadataKey(e.target.value)}>: this.editMetadataValue(e.target.value)}> +
) } @@ -131,85 +146,129 @@ interface LinkEditorProps { @observer export class LinkEditor extends React.Component { - @observable private _groups: Map = new Map(); - @observable private _metadata: Map> = new Map(); + @observable private _groups: Map = new Map(); // map of temp group id to the corresponding group doc constructor(props: LinkEditorProps) { super(props); let groups = new Map(); - let metadata: Map> = new Map(); let groupList = (Doc.AreProtosEqual(props.sourceDoc, Cast(props.linkDoc.anchor1, Doc, new Doc))) ? Cast(props.linkDoc.anchor1Groups, listSpec(Doc), []) : Cast(props.linkDoc.anchor2Groups, listSpec(Doc), []); groupList.forEach(groupDoc => { if (groupDoc instanceof Doc) { let id = Utils.GenerateGuid(); groups.set(id, groupDoc); - - let metadataMap = new Map(); - let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []); - metadataDocs.forEach(mdDoc => { - if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc - metadataMap.set(Utils.GenerateGuid(), mdDoc); - } - }) - metadata.set(id, metadataMap); } else { // promise doc } }) this._groups = groups; - this._metadata = metadata; } - // @action - // editGroup(groupId: string, value: string) { - // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - // let groupDoc = this._groups.get(groupId); - // if (groupDoc) { - // groupDoc.proto!.type = value; - // LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, [groupDoc]); - // } - // } - @action - addGroup = (e: React.MouseEvent): void => { - // create new document for group - let groupDoc = Docs.TextDocument(); - groupDoc.proto!.title = ""; - groupDoc.proto!.metadata = new List([]); + addGroup = (): void => { + console.log("before adding", ...Array.from(this._groups.keys())); + + let index = Array.from(this._groups.values()).findIndex(groupDoc => { + return groupDoc["type"] === "New Group"; + }) + if (index === -1) { + // create new document for group + let groupDoc = Docs.TextDocument(); + groupDoc.proto!.type = "New Group"; + groupDoc.proto!.metadata = Docs.TextDocument(); - this._groups.set(Utils.GenerateGuid(), groupDoc); + this._groups.set(Utils.GenerateGuid(), groupDoc); - let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); + let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; + LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); + } + + + // console.log("set anchor groups for", this.props.sourceDoc["title"]); + console.log("after adding", ...Array.from(this._groups.keys())); } @action - setGroup = (groupId: string, group: string): void => { + setGroupType = (groupId: string, groupType: string): void => { + console.log("setting for ", groupId); let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; let groupDoc = this._groups.get(groupId); if (groupDoc) { - groupDoc.proto!.type = group; - LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, [groupDoc]); + console.log("found group doc"); + groupDoc.proto!.type = groupType; + + this._groups.set(groupId, groupDoc); + + let gd = this._groups.get(groupId); + if (gd) + console.log("just created", StrCast(gd["type"])); + + LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); + console.log("set", Array.from(this._groups.values()).length) + } + + let anchor1groups: string[] = []; + Cast(this.props.linkDoc.anchor1Groups, listSpec(Doc), []).forEach(doc => { + if (doc instanceof Doc) { + anchor1groups.push(StrCast(doc.proto!.type)); + } else { + console.log("promise"); + } + }) + let anchor2groups: string[] = []; + Cast(this.props.linkDoc.anchor2Groups, listSpec(Doc), []).forEach(doc => { + if (doc instanceof Doc) { + anchor2groups.push(StrCast(doc.proto!.type)); + } else { + console.log("promise"); + } + }) + console.log("groups for anchors; anchor1: [", ...anchor1groups, "] ; anchor2: [", ...anchor2groups, "]") + } + + removeGroupFromLink = (groupId: string, groupType: string) => { + // this._groups.delete(groupId); + let groupDoc = this._groups.get(groupId); + if (groupDoc) { + LinkUtils.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType); + this._groups.delete(groupId); + } + // LinkUtils.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); + console.log("\nremoved", groupId, "remaining", ...Array.from(this._groups.keys()), "\n"); + } + + deleteGroup = (groupId: string, groupType: string) => { + let groupDoc = this._groups.get(groupId); + if (groupDoc) { + LinkManager.Instance.deleteGroup(groupType); + this._groups.delete(groupId); } } renderGroup(groupId: string, groupDoc: Doc) { - return ( -
-
-

type:

- - {/* this.editGroup(groupId, e.target.value)}> */} - {/* this.editGroup(groupId, e.target.value)}> */} + let type = StrCast(groupDoc["type"]); + if ((type && LinkManager.Instance.allGroups.get(type)) || type === "New Group") { + return ( +
+
+

type:

+ +
+ {this.renderMetadata(groupId)} +
+ {groupDoc["type"] === "New Group" ? : + } + {/* */} + + + +
- {this.renderMetadata(groupId)} - {groupDoc["type"] === "*" ? <> : } - - {/* */} -
- ) + ) + } else { + return <> + } } viewGroupAsTable = (): void => { @@ -228,65 +287,6 @@ export class LinkEditor extends React.Component { } LinkManager.Instance.allGroups.set(groupType, mdKeys); - // // create new metadata doc - // let mdDoc = Docs.TextDocument(); - // mdDoc.proto!.title = ""; - // mdDoc.proto!.value = ""; - - // // append to map - // let mdMap = this._metadata.get(groupId); - // if (mdMap) { - // mdMap.set(Utils.GenerateGuid(), mdDoc); - // } else { - // mdMap = new Map(); - // mdMap.set(Utils.GenerateGuid(), mdDoc); - // } - - // // add to internal representation of metadata - // this._metadata.set(groupId, mdMap); - - // // add to internatal representation of group - // let groupDoc = this._groups.get(groupId); - // if (groupDoc) { - // groupDoc.proto!.metadata = new List(Array.from(mdMap.values())); - // this._groups.set(groupId, groupDoc); - // } - - // // add to link doc - // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - // LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); - } - - // @action - // editMetadataTitle(groupId: string, mdId: string, value: string) { - // let groupMd = this._metadata.get(groupId); - // if (groupMd) { - // let mdDoc = groupMd.get(mdId); - // if (mdDoc) { - // mdDoc.proto!.title = value; - // } - // } - // // set group and link? - // } - - // @action - // editMetadataValue(groupId: string, mdId: string, value: string) { - // let groupMd = this._metadata.get(groupId); - // if (groupMd) { - // let mdDoc = groupMd.get(mdId); - // if (mdDoc) { - // mdDoc.proto!.value = value; - // } - // } - // // set group and link? - // } - - @action - editMetadataKey(groupId: string, value: string) { - let groupDoc = this._groups.get(groupId); - if (groupDoc) { - - } } renderMetadata(groupId: string) { @@ -300,30 +300,10 @@ export class LinkEditor extends React.Component { groupMdKeys.forEach((key, index) => { metadata.push( - //
- // - // : - // - //
) }) } } - - - // let metadataMap = this._metadata.get(groupId); - // if (metadataMap) { - // metadataMap.forEach((mdDoc, mdId) => { - // metadata.push( - //
- // this.editMetadataTitle(groupId, mdId, e.target.value)}> - // : - // this.editMetadataValue(groupId, mdId, e.target.value)}> - //
- // ) - // }) - // } - return metadata; } @@ -340,10 +320,10 @@ export class LinkEditor extends React.Component {

editing link to: {destination.proto!.title}

- Groups: - + Relationships: +
- {groups} + {groups.length > 0 ? groups :
There are currently no relationships associated with this link.
}
); diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index 860f31d8a..7e031b897 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -40,7 +40,7 @@ display: flex; } &:hover .link-menu-item-content { - width: calc(100% - 72px); + width: calc(100% - 42px); } } .link-menu-item-buttons { diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 21b5807ae..47bd6c3eb 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -32,7 +32,7 @@ export class LinkMenu extends React.Component { // }); // } - renderLinkGroupItems(links: Doc[]) { + renderGroup(links: Doc[]) { let source = this.props.docView.Document; return links.map(link => { let destination = LinkUtils.findOppositeAnchor(link, source); @@ -43,7 +43,7 @@ export class LinkMenu extends React.Component { }); } - renderLinkItems = (links: Map>): Array => { + renderLinks = (links: Map>): Array => { let linkItems: Array = []; links.forEach((links, group) => { @@ -51,7 +51,7 @@ export class LinkMenu extends React.Component {

{group}:

- {this.renderLinkGroupItems(links)} + {this.renderGroup(links)}
) @@ -65,10 +65,7 @@ export class LinkMenu extends React.Component { } render() { - //get list of links from document - // let linkFrom = DocListCast(this.props.docView.props.Document.linkedFromDocs); - // let linkTo = DocListCast(this.props.docView.props.Document.linkedToDocs); - let related = LinkManager.Instance.findRelatedGroupedLinks(this.props.docView.props.Document); + let related: Map = LinkManager.Instance.findRelatedGroupedLinks(this.props.docView.props.Document); if (this._editingLink === undefined) { return (
@@ -76,31 +73,11 @@ export class LinkMenu extends React.Component {
{/* {this.renderLinkItems(linkTo, "linkedTo", "Destination: ")} {this.renderLinkItems(linkFrom, "linkedFrom", "Source: ")} */} - {this.renderLinkItems(related)} + {this.renderLinks(related)}
); } else { - // let groups = new Map(); - // let metadata: Map> = new Map(); - // let groupList = (Doc.AreProtosEqual(this.props.docView.props.Document, Cast(this._editingLink.anchor1, Doc, new Doc))) ? - // Cast(this._editingLink.anchor1Groups, listSpec(Doc), []) : Cast(this._editingLink.anchor2Groups, listSpec(Doc), []); - // groupList.forEach(groupDoc => { - // if (groupDoc instanceof Doc) { - // let id = Utils.GenerateGuid(); - // groups.set(id, groupDoc); - - // let metadataMap = new Map(); - // let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []); - // metadataDocs.forEach(mdDoc => { - // if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc - // metadataMap.set(Utils.GenerateGuid(), mdDoc); - // } - // }) - // metadata.set(id, metadataMap); - // } - // }) - return ( this._editingLink = undefined)}> ); -- cgit v1.2.3-70-g09d2 From 2843baddfa1fe4f46762c63b732eedb5ea7c6e41 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 12 Jun 2019 18:23:22 -0400 Subject: link group can be viewed as a table --- src/client/views/nodes/LinkEditor.tsx | 69 ++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 05e05c2ee..3886687f1 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -13,6 +13,7 @@ import { faArrowLeft, faEllipsisV } from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { string } from "prop-types"; +import { SetupDrag } from "../../util/DragManager"; library.add(faArrowLeft); library.add(faEllipsisV); @@ -176,7 +177,7 @@ export class LinkEditor extends React.Component { // create new document for group let groupDoc = Docs.TextDocument(); groupDoc.proto!.type = "New Group"; - groupDoc.proto!.metadata = Docs.TextDocument(); + // groupDoc.proto!.metadata = Docs.TextDocument(); this._groups.set(Utils.GenerateGuid(), groupDoc); @@ -246,6 +247,55 @@ export class LinkEditor extends React.Component { } } + copyGroup = (groupId: string, groupType: string) => { + let oppAnchor = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); + let groupList = (Doc.AreProtosEqual(oppAnchor, Cast(this.props.linkDoc.anchor1, Doc, new Doc))) ? + Cast(this.props.linkDoc.anchor1Groups, listSpec(Doc), []) : Cast(this.props.linkDoc.anchor2Groups, listSpec(Doc), []); + // if group already exists on opposite anchor, copy value + let index = groupList.findIndex(groupDoc => { + if (groupDoc instanceof Doc) { + return StrCast(groupDoc["type"]) === groupType; + } else { + return false; + } + }) + // TODO: clean + // if (index > 0) { + // let thisGroupDoc = this._groups.get(groupId); + // let oppGroupDoc = groupList[index]; + // let keys = LinkManager.Instance.allGroups.get(groupType); + // if (keys) { + // keys.forEach(key => { + // if (thisGroupDoc && oppGroupDoc instanceof Doc) { // TODO: clean + // let val = thisGroupDoc[key] === undefined ? "" : StrCast(thisGroupDoc[key]); + // oppGroupDoc[key] = val; + // } + // // mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key]) + // // oppGroupDoc[key] = thisGroupDoc[key]; + // }) + // } + // // LinkUtils.setAnchorGroups(this.props.linkDoc, oppAnchor, [oppGroupDoc]); + // } else { + let thisGroupDoc = this._groups.get(groupId); + let newGroupDoc = Docs.TextDocument(); + newGroupDoc.proto!.type = groupType; + let keys = LinkManager.Instance.allGroups.get(groupType); + if (keys) { + keys.forEach(key => { + if (thisGroupDoc) { // TODO: clean + let val = thisGroupDoc[key] === undefined ? "" : StrCast(thisGroupDoc[key]); + newGroupDoc[key] = val; + } + // mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key]) + // oppGroupDoc[key] = thisGroupDoc[key]; + }) + } + LinkUtils.setAnchorGroups(this.props.linkDoc, oppAnchor, [newGroupDoc]); // TODO: fix to append to list + // } + + // else create group on opposite anchor + } + renderGroup(groupId: string, groupDoc: Doc) { let type = StrCast(groupDoc["type"]); if ((type && LinkManager.Instance.allGroups.get(type)) || type === "New Group") { @@ -259,10 +309,10 @@ export class LinkEditor extends React.Component {
{groupDoc["type"] === "New Group" ? : } - {/* */} + {this.viewGroupAsTable(groupId, type)}
) @@ -271,10 +321,20 @@ export class LinkEditor extends React.Component { } } - viewGroupAsTable = (): void => { - + viewGroupAsTable(groupId: string, groupType: string) { + let keys = LinkManager.Instance.allGroups.get(groupType); + let groupDoc = this._groups.get(groupId); + if (keys && groupDoc) { + console.log("keys:", ...keys); + let createTable = action(() => Docs.SchemaDocument(keys!, [Cast(groupDoc!["metadata"], Doc, new Doc)], { width: 200, height: 200, title: groupType + " table" })); + let ref = React.createRef(); + return
+ } else { + return <> + } } + @action addMetadata = (groupType: string): void => { let mdKeys = LinkManager.Instance.allGroups.get(groupType); @@ -286,7 +346,6 @@ export class LinkEditor extends React.Component { mdKeys = ["new key"]; } LinkManager.Instance.allGroups.set(groupType, mdKeys); - } renderMetadata(groupId: string) { -- cgit v1.2.3-70-g09d2 From bd829aa067912baa08c18c09f5dcfcd3853e45ad Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 13 Jun 2019 11:47:50 -0400 Subject: anchors show up in link group table and metadata values get copied on transfer --- src/client/util/LinkManager.ts | 27 +++++++++++++++++++++++++++ src/client/views/nodes/LinkEditor.tsx | 20 +++++++++++++++----- 2 files changed, 42 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index cc8617052..02ecec88a 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -134,6 +134,33 @@ export class LinkManager { return anchorGroups; } + public findMetadataInGroup(groupType: string) { + let md: Doc[] = []; + let allLinks = LinkManager.Instance.allLinks; + // for every link find its groups + // allLinks.forEach(linkDoc => { + // let anchor1groups = LinkManager.Instance.findRelatedGroupedLinks(Cast(linkDoc["anchor1"], Doc, new Doc)); + // if (anchor1groups.get(groupType)) { + // md.push(linkDoc["anchor1"]["group"]) + // } + // }) + allLinks.forEach(linkDoc => { + let anchor1Groups = Cast(linkDoc["anchor1Groups"], listSpec(Doc), []); + let anchor2Groups = Cast(linkDoc["anchor2Groups"], listSpec(Doc), []); + [...anchor1Groups, ...anchor2Groups].forEach(groupDoc => { + if (groupDoc instanceof Doc) { + if (StrCast(groupDoc["type"]) === groupType) { + md.push(Cast(groupDoc["metadata"], Doc, new Doc)); + } + } else { + // TODO: promise + } + }) + + }) + return md; + } + public deleteGroup(groupType: string) { let deleted = LinkManager.Instance.allGroups.delete(groupType); if (deleted) { diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 3886687f1..8860ac582 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -175,9 +175,14 @@ export class LinkEditor extends React.Component { }) if (index === -1) { // create new document for group + let mdDoc = Docs.TextDocument(); + mdDoc.proto!.anchor1 = this.props.sourceDoc["title"]; + mdDoc.proto!.anchor2 = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc)["title"]; + let groupDoc = Docs.TextDocument(); groupDoc.proto!.type = "New Group"; - // groupDoc.proto!.metadata = Docs.TextDocument(); + groupDoc.proto!.metadata = mdDoc; + this._groups.set(Utils.GenerateGuid(), groupDoc); @@ -277,19 +282,23 @@ export class LinkEditor extends React.Component { // // LinkUtils.setAnchorGroups(this.props.linkDoc, oppAnchor, [oppGroupDoc]); // } else { let thisGroupDoc = this._groups.get(groupId); + let thisMdDoc = Cast(thisGroupDoc!["metadata"], Doc, new Doc); let newGroupDoc = Docs.TextDocument(); - newGroupDoc.proto!.type = groupType; + let newMdDoc = Docs.TextDocument(); let keys = LinkManager.Instance.allGroups.get(groupType); if (keys) { keys.forEach(key => { if (thisGroupDoc) { // TODO: clean - let val = thisGroupDoc[key] === undefined ? "" : StrCast(thisGroupDoc[key]); - newGroupDoc[key] = val; + let val = thisMdDoc[key] === undefined ? "" : StrCast(thisMdDoc[key]); + newMdDoc[key] = val; } // mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key]) // oppGroupDoc[key] = thisGroupDoc[key]; }) } + newGroupDoc.proto!.type = groupType; + newGroupDoc.proto!.metadata = newMdDoc; + LinkUtils.setAnchorGroups(this.props.linkDoc, oppAnchor, [newGroupDoc]); // TODO: fix to append to list // } @@ -326,7 +335,8 @@ export class LinkEditor extends React.Component { let groupDoc = this._groups.get(groupId); if (keys && groupDoc) { console.log("keys:", ...keys); - let createTable = action(() => Docs.SchemaDocument(keys!, [Cast(groupDoc!["metadata"], Doc, new Doc)], { width: 200, height: 200, title: groupType + " table" })); + let docs: Doc[] = LinkManager.Instance.findMetadataInGroup(groupType); + let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" })); let ref = React.createRef(); return
} else { -- cgit v1.2.3-70-g09d2 From 8c70b822ddd6f2a92d3f3d30cd54c47efad38605 Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 13 Jun 2019 12:05:06 -0400 Subject: updated styling on link editor --- .../collectionFreeForm/CollectionFreeFormLinkView.tsx | 3 ++- src/client/views/nodes/LinkEditor.scss | 18 +++++++++++++----- src/client/views/nodes/LinkEditor.tsx | 17 +++++++++-------- 3 files changed, 24 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 301b769af..a1a222675 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -46,7 +46,8 @@ export class CollectionFreeFormLinkView extends React.Component text += StrCast(l.title) + "(" + StrCast(l.linkDescription) + "), "); - text = text.substr(0, text.length - 2); + // text = text.substr(0, text.length - 2); + text = ""; return ( <> { {this.renderMetadata(groupId)}
- {groupDoc["type"] === "New Group" ? : - } - - - + {groupDoc["type"] === "New Group" ? : + } + + {/* + */} {this.viewGroupAsTable(groupId, type)}
@@ -338,9 +339,9 @@ export class LinkEditor extends React.Component { let docs: Doc[] = LinkManager.Instance.findMetadataInGroup(groupType); let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" })); let ref = React.createRef(); - return
+ return
} else { - return <> + return } } -- cgit v1.2.3-70-g09d2 From 15d2dbc6935df7667733c267de07961e51df5f00 Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 13 Jun 2019 12:37:47 -0400 Subject: link table minor fixes --- src/client/util/LinkManager.ts | 11 ++++++++++- src/client/views/nodes/LinkEditor.tsx | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 02ecec88a..fab0efb4e 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -147,7 +147,16 @@ export class LinkManager { allLinks.forEach(linkDoc => { let anchor1Groups = Cast(linkDoc["anchor1Groups"], listSpec(Doc), []); let anchor2Groups = Cast(linkDoc["anchor2Groups"], listSpec(Doc), []); - [...anchor1Groups, ...anchor2Groups].forEach(groupDoc => { + anchor1Groups.forEach(groupDoc => { + if (groupDoc instanceof Doc) { + if (StrCast(groupDoc["type"]) === groupType) { + md.push(Cast(groupDoc["metadata"], Doc, new Doc)); + } + } else { + // TODO: promise + } + }) + anchor2Groups.forEach(groupDoc => { if (groupDoc instanceof Doc) { if (StrCast(groupDoc["type"]) === groupType) { md.push(Cast(groupDoc["metadata"], Doc, new Doc)); diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index bab87fc12..370fdee1d 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -286,6 +286,8 @@ export class LinkEditor extends React.Component { let thisMdDoc = Cast(thisGroupDoc!["metadata"], Doc, new Doc); let newGroupDoc = Docs.TextDocument(); let newMdDoc = Docs.TextDocument(); + newMdDoc.proto!.anchor1 = StrCast(thisMdDoc["anchor2"]); + newMdDoc.proto!.anchor2 = StrCast(thisMdDoc["anchor1"]); let keys = LinkManager.Instance.allGroups.get(groupType); if (keys) { keys.forEach(key => { -- cgit v1.2.3-70-g09d2 From bd864051d6bbec3f1ac09ab6c66f9bb62e02411b Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 13 Jun 2019 15:41:08 -0400 Subject: fixed lint issues --- src/client/util/LinkManager.ts | 38 ++++++++++---------- src/client/views/nodes/LinkEditor.tsx | 67 +++++++++++++++++------------------ 2 files changed, 51 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index fab0efb4e..c0c607b01 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -52,17 +52,17 @@ export namespace LinkUtils { export function removeGroupFromAnchor(link: Doc, anchor: Doc, groupType: string) { let groups = []; if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { - groups = Cast(link["anchor1Groups"], listSpec(Doc), []); + groups = Cast(link.proto!.anchor1Groups, listSpec(Doc), []); } else { - groups = Cast(link["anchor2Groups"], listSpec(Doc), []); + groups = Cast(link.proto!.anchor2Groups, listSpec(Doc), []); } let newGroups: Doc[] = []; groups.forEach(groupDoc => { - if (groupDoc instanceof Doc && StrCast(groupDoc["type"]) !== groupType) { + if (groupDoc instanceof Doc && StrCast(groupDoc.type) !== groupType) { newGroups.push(groupDoc); } - }) + }); LinkUtils.setAnchorGroups(link, anchor, newGroups); } } @@ -111,7 +111,7 @@ export class LinkManager { if (groups.length > 0) { groups.forEach(groupDoc => { if (groupDoc instanceof Doc) { - let groupType = StrCast(groupDoc["type"]); + let groupType = StrCast(groupDoc.type); let group = anchorGroups.get(groupType); // TODO: clean this up lol if (group) group.push(link); else group = [link]; @@ -120,7 +120,7 @@ export class LinkManager { // promise doc } - }) + }); } else { // if link is in no groups then put it in default group @@ -130,7 +130,7 @@ export class LinkManager { anchorGroups.set("*", group); } - }) + }); return anchorGroups; } @@ -145,28 +145,28 @@ export class LinkManager { // } // }) allLinks.forEach(linkDoc => { - let anchor1Groups = Cast(linkDoc["anchor1Groups"], listSpec(Doc), []); - let anchor2Groups = Cast(linkDoc["anchor2Groups"], listSpec(Doc), []); + let anchor1Groups = Cast(linkDoc.anchor1Groups, listSpec(Doc), []); + let anchor2Groups = Cast(linkDoc.anchor2Groups, listSpec(Doc), []); anchor1Groups.forEach(groupDoc => { if (groupDoc instanceof Doc) { - if (StrCast(groupDoc["type"]) === groupType) { - md.push(Cast(groupDoc["metadata"], Doc, new Doc)); + if (StrCast(groupDoc.type) === groupType) { + md.push(Cast(groupDoc.metadata, Doc, new Doc)); } } else { // TODO: promise } - }) + }); anchor2Groups.forEach(groupDoc => { if (groupDoc instanceof Doc) { - if (StrCast(groupDoc["type"]) === groupType) { - md.push(Cast(groupDoc["metadata"], Doc, new Doc)); + if (StrCast(groupDoc.type) === groupType) { + md.push(Cast(groupDoc.metadata, Doc, new Doc)); } } else { // TODO: promise } - }) + }); - }) + }); return md; } @@ -174,9 +174,9 @@ export class LinkManager { let deleted = LinkManager.Instance.allGroups.delete(groupType); if (deleted) { LinkManager.Instance.allLinks.forEach(linkDoc => { - LinkUtils.removeGroupFromAnchor(linkDoc, Cast(linkDoc["anchor1"], Doc, new Doc), groupType); - LinkUtils.removeGroupFromAnchor(linkDoc, Cast(linkDoc["anchor2"], Doc, new Doc), groupType); - }) + LinkUtils.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType); + LinkUtils.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc), groupType); + }); } } diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 370fdee1d..386d0cc3e 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -56,12 +56,12 @@ class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: s let options = []; results.forEach(result => { options.push(
{ this.props.setGroup(this.props.groupId, result); this.setGroupType(result); this.setSearchTerm("") }}>{result}
) + onClick={() => { this.props.setGroup(this.props.groupId, result); this.setGroupType(result); this.setSearchTerm(""); }}>{result}); }); if (!exactFound && this._searchTerm !== "") { options.push(
{ this.createGroup(this._searchTerm); this.setGroupType(this._searchTerm); this.setSearchTerm("") }}>Define new "{this._searchTerm}" relationship
) + onClick={() => { this.createGroup(this._searchTerm); this.setGroupType(this._searchTerm); this.setSearchTerm(""); }}>Define new "{this._searchTerm}" relationship); } return options; @@ -71,12 +71,12 @@ class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: s return (
{ this.setSearchTerm(e.target.value); this.setGroupType(e.target.value) }}> + onChange={e => { this.setSearchTerm(e.target.value); this.setGroupType(e.target.value); }}>
{this.renderOptions()}
- ) + ); } } @@ -97,7 +97,7 @@ class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc groupMdKeys[index] = value; } else { - console.log("OLD KEY WAS NOT FOUND", ...groupMdKeys) + console.log("OLD KEY WAS NOT FOUND", ...groupMdKeys); } } @@ -120,7 +120,7 @@ class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc groupMdKeys.splice(index, 1); } else { - console.log("OLD KEY WAS NOT FOUND", ...groupMdKeys) + console.log("OLD KEY WAS NOT FOUND", ...groupMdKeys); } } this._key = ""; @@ -134,7 +134,7 @@ class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc this.editMetadataValue(e.target.value)}> - ) + ); } } @@ -163,7 +163,7 @@ export class LinkEditor extends React.Component { } else { // promise doc } - }) + }); this._groups = groups; } @@ -171,20 +171,20 @@ export class LinkEditor extends React.Component { addGroup = (): void => { console.log("before adding", ...Array.from(this._groups.keys())); + // new group only gets added if there is not already a group with type "new group" let index = Array.from(this._groups.values()).findIndex(groupDoc => { - return groupDoc["type"] === "New Group"; - }) + return groupDoc.type === "New Group"; + }); if (index === -1) { // create new document for group let mdDoc = Docs.TextDocument(); - mdDoc.proto!.anchor1 = this.props.sourceDoc["title"]; - mdDoc.proto!.anchor2 = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc)["title"]; + mdDoc.proto!.anchor1 = this.props.sourceDoc.title; + mdDoc.proto!.anchor2 = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title; let groupDoc = Docs.TextDocument(); groupDoc.proto!.type = "New Group"; groupDoc.proto!.metadata = mdDoc; - this._groups.set(Utils.GenerateGuid(), groupDoc); let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; @@ -207,12 +207,9 @@ export class LinkEditor extends React.Component { this._groups.set(groupId, groupDoc); - let gd = this._groups.get(groupId); - if (gd) - console.log("just created", StrCast(gd["type"])); LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); - console.log("set", Array.from(this._groups.values()).length) + console.log("set", Array.from(this._groups.values()).length); } let anchor1groups: string[] = []; @@ -222,7 +219,7 @@ export class LinkEditor extends React.Component { } else { console.log("promise"); } - }) + }); let anchor2groups: string[] = []; Cast(this.props.linkDoc.anchor2Groups, listSpec(Doc), []).forEach(doc => { if (doc instanceof Doc) { @@ -230,8 +227,8 @@ export class LinkEditor extends React.Component { } else { console.log("promise"); } - }) - console.log("groups for anchors; anchor1: [", ...anchor1groups, "] ; anchor2: [", ...anchor2groups, "]") + }); + console.log("groups for anchors; anchor1: [", ...anchor1groups, "] ; anchor2: [", ...anchor2groups, "]"); } removeGroupFromLink = (groupId: string, groupType: string) => { @@ -260,11 +257,11 @@ export class LinkEditor extends React.Component { // if group already exists on opposite anchor, copy value let index = groupList.findIndex(groupDoc => { if (groupDoc instanceof Doc) { - return StrCast(groupDoc["type"]) === groupType; + return StrCast(groupDoc.type) === groupType; } else { return false; } - }) + }); // TODO: clean // if (index > 0) { // let thisGroupDoc = this._groups.get(groupId); @@ -283,11 +280,11 @@ export class LinkEditor extends React.Component { // // LinkUtils.setAnchorGroups(this.props.linkDoc, oppAnchor, [oppGroupDoc]); // } else { let thisGroupDoc = this._groups.get(groupId); - let thisMdDoc = Cast(thisGroupDoc!["metadata"], Doc, new Doc); + let thisMdDoc = Cast(thisGroupDoc!.metadata, Doc, new Doc); let newGroupDoc = Docs.TextDocument(); let newMdDoc = Docs.TextDocument(); - newMdDoc.proto!.anchor1 = StrCast(thisMdDoc["anchor2"]); - newMdDoc.proto!.anchor2 = StrCast(thisMdDoc["anchor1"]); + newMdDoc.proto!.anchor1 = StrCast(thisMdDoc.anchor2); + newMdDoc.proto!.anchor2 = StrCast(thisMdDoc.anchor1); let keys = LinkManager.Instance.allGroups.get(groupType); if (keys) { keys.forEach(key => { @@ -297,7 +294,7 @@ export class LinkEditor extends React.Component { } // mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key]) // oppGroupDoc[key] = thisGroupDoc[key]; - }) + }); } newGroupDoc.proto!.type = groupType; newGroupDoc.proto!.metadata = newMdDoc; @@ -309,7 +306,7 @@ export class LinkEditor extends React.Component { } renderGroup(groupId: string, groupDoc: Doc) { - let type = StrCast(groupDoc["type"]); + let type = StrCast(groupDoc.type); if ((type && LinkManager.Instance.allGroups.get(type)) || type === "New Group") { return (
@@ -319,7 +316,7 @@ export class LinkEditor extends React.Component {
{this.renderMetadata(groupId)}
- {groupDoc["type"] === "New Group" ? : + {groupDoc.type === "New Group" ? : } {/* @@ -327,9 +324,9 @@ export class LinkEditor extends React.Component { {this.viewGroupAsTable(groupId, type)}
- ) + ); } else { - return <> + return <>; } } @@ -341,9 +338,9 @@ export class LinkEditor extends React.Component { let docs: Doc[] = LinkManager.Instance.findMetadataInGroup(groupType); let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" })); let ref = React.createRef(); - return
+ return
; } else { - return + return ; } } @@ -372,8 +369,8 @@ export class LinkEditor extends React.Component { groupMdKeys.forEach((key, index) => { metadata.push( - ) - }) + ); + }); } } return metadata; @@ -384,7 +381,7 @@ export class LinkEditor extends React.Component { let groups: Array = []; this._groups.forEach((groupDoc, groupId) => { - groups.push(this.renderGroup(groupId, groupDoc)) + groups.push(this.renderGroup(groupId, groupDoc)); }); return ( -- cgit v1.2.3-70-g09d2 From 0d0782362d4549b80c27c3ce5d8439c2f6fa4d7b Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 13 Jun 2019 16:05:26 -0400 Subject: fixed delete handlers for link groups --- src/client/util/LinkManager.ts | 57 +++++++++++++++++----------------- src/client/views/nodes/LinkEditor.scss | 2 +- src/client/views/nodes/LinkEditor.tsx | 34 ++++++++++---------- 3 files changed, 46 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index c0c607b01..2f40c77e7 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -20,49 +20,48 @@ export namespace LinkUtils { } } - // export function getAnchorGroups(link: Doc, anchor: Doc): Doc[] { - // let groups; - // if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { - // groups = Cast(link.anchor1Groups, listSpec(Doc), []); - // } else { - // groups = Cast(link.anchor2Groups, listSpec(Doc), []); - // } - - // if (groups instanceof Doc[]) { - // return groups; - // } else { - // return []; - // } - // // if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { - // // returnCast(link.anchor1Groups, listSpec(Doc), []); - // // } else { - // // return Cast(link.anchor2Groups, listSpec(Doc), []); - // // } - // } - export function setAnchorGroups(link: Doc, anchor: Doc, groups: Doc[]) { // console.log("setting groups for anchor", anchor["title"]); if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { link.anchor1Groups = new List(groups); + + let print: string[] = []; + Cast(link.anchor1Groups, listSpec(Doc), []).forEach(doc => { + if (doc instanceof Doc) { + print.push(StrCast(doc.type)); + } + }); + console.log("set anchor's groups as", print); } else { link.anchor2Groups = new List(groups); + + let print: string[] = []; + Cast(link.anchor2Groups, listSpec(Doc), []).forEach(doc => { + if (doc instanceof Doc) { + print.push(StrCast(doc.type)); + } + }); + console.log("set anchor's groups as", print); } } export function removeGroupFromAnchor(link: Doc, anchor: Doc, groupType: string) { - let groups = []; - if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { - groups = Cast(link.proto!.anchor1Groups, listSpec(Doc), []); - } else { - groups = Cast(link.proto!.anchor2Groups, listSpec(Doc), []); - } + let groups = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)) ? + Cast(link.proto!.anchor1Groups, listSpec(Doc), []) : Cast(link.proto!.anchor2Groups, listSpec(Doc), []); let newGroups: Doc[] = []; groups.forEach(groupDoc => { if (groupDoc instanceof Doc && StrCast(groupDoc.type) !== groupType) { newGroups.push(groupDoc); - } + } // TODO: promise }); + + // let grouptypes: string[] = []; + // newGroups.forEach(groupDoc => { + // grouptypes.push(StrCast(groupDoc.type)); + // }); + // console.log("remove anchor's groups as", grouptypes); + LinkUtils.setAnchorGroups(link, anchor, newGroups); } } @@ -92,7 +91,7 @@ export class LinkManager { } @observable public allLinks: Array = []; // list of link docs - @observable public allGroups: Map> = new Map(); // map of group type to list of its metadata keys + @observable public groupMetadataKeys: Map> = new Map(); // map of group type to list of its metadata keys public findAllRelatedLinks(anchor: Doc): Array { return LinkManager.Instance.allLinks.filter( @@ -171,7 +170,7 @@ export class LinkManager { } public deleteGroup(groupType: string) { - let deleted = LinkManager.Instance.allGroups.delete(groupType); + let deleted = LinkManager.Instance.groupMetadataKeys.delete(groupType); if (deleted) { LinkManager.Instance.allLinks.forEach(linkDoc => { LinkUtils.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType); diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index df7bd6086..01f0cb82a 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -127,7 +127,7 @@ display: flex; justify-content: space-between; .linkEditor-groupOpts { - width: calc(33% - 3px); + width: calc(20% - 3px); height: 20px; // margin-left: 6px; padding: 0; diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 386d0cc3e..5097d625e 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -37,14 +37,14 @@ class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: s @action createGroup(value: string) { - LinkManager.Instance.allGroups.set(value, []); + LinkManager.Instance.groupMetadataKeys.set(value, []); this.props.setGroup(this.props.groupId, value); } renderOptions = (): JSX.Element[] => { let allGroups: string[], searchTerm: string, results: string[], exactFound: boolean; if (this._searchTerm !== "") { - allGroups = Array.from(LinkManager.Instance.allGroups.keys()); + allGroups = Array.from(LinkManager.Instance.groupMetadataKeys.keys()); searchTerm = this._searchTerm.toUpperCase(); results = allGroups.filter(group => group.toUpperCase().indexOf(searchTerm) > -1); exactFound = results.findIndex(group => group.toUpperCase() === searchTerm) > -1; @@ -90,7 +90,7 @@ class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc @action editMetadataKey = (value: string): void => { // TODO: check that metadata doesnt already exist in group - let groupMdKeys = new Array(...LinkManager.Instance.allGroups.get(this.props.groupType)!); + let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!); if (groupMdKeys) { let index = groupMdKeys.indexOf(this._key); if (index > -1) { @@ -102,7 +102,7 @@ class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc } this._key = value; - LinkManager.Instance.allGroups.set(this.props.groupType, groupMdKeys); + LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys); } @action @@ -113,7 +113,7 @@ class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc @action removeMetadata = (): void => { - let groupMdKeys = new Array(...LinkManager.Instance.allGroups.get(this.props.groupType)!); + let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!); if (groupMdKeys) { let index = groupMdKeys.indexOf(this._key); if (index > -1) { @@ -124,7 +124,7 @@ class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc } } this._key = ""; - LinkManager.Instance.allGroups.set(this.props.groupType, groupMdKeys); + LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys); } render() { @@ -132,7 +132,7 @@ class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc
this.editMetadataKey(e.target.value)}>: this.editMetadataValue(e.target.value)}> - +
); } @@ -199,7 +199,7 @@ export class LinkEditor extends React.Component { @action setGroupType = (groupId: string, groupType: string): void => { console.log("setting for ", groupId); - let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; + // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; let groupDoc = this._groups.get(groupId); if (groupDoc) { console.log("found group doc"); @@ -208,7 +208,7 @@ export class LinkEditor extends React.Component { this._groups.set(groupId, groupDoc); - LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); + LinkUtils.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); console.log("set", Array.from(this._groups.values()).length); } @@ -285,7 +285,7 @@ export class LinkEditor extends React.Component { let newMdDoc = Docs.TextDocument(); newMdDoc.proto!.anchor1 = StrCast(thisMdDoc.anchor2); newMdDoc.proto!.anchor2 = StrCast(thisMdDoc.anchor1); - let keys = LinkManager.Instance.allGroups.get(groupType); + let keys = LinkManager.Instance.groupMetadataKeys.get(groupType); if (keys) { keys.forEach(key => { if (thisGroupDoc) { // TODO: clean @@ -307,7 +307,7 @@ export class LinkEditor extends React.Component { renderGroup(groupId: string, groupDoc: Doc) { let type = StrCast(groupDoc.type); - if ((type && LinkManager.Instance.allGroups.get(type)) || type === "New Group") { + if ((type && LinkManager.Instance.groupMetadataKeys.get(type)) || type === "New Group") { return (
@@ -319,8 +319,8 @@ export class LinkEditor extends React.Component { {groupDoc.type === "New Group" ? : } - {/* - */} + + {this.viewGroupAsTable(groupId, type)}
@@ -331,7 +331,7 @@ export class LinkEditor extends React.Component { } viewGroupAsTable(groupId: string, groupType: string) { - let keys = LinkManager.Instance.allGroups.get(groupType); + let keys = LinkManager.Instance.groupMetadataKeys.get(groupType); let groupDoc = this._groups.get(groupId); if (keys && groupDoc) { console.log("keys:", ...keys); @@ -347,7 +347,7 @@ export class LinkEditor extends React.Component { @action addMetadata = (groupType: string): void => { - let mdKeys = LinkManager.Instance.allGroups.get(groupType); + let mdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType); if (mdKeys) { if (mdKeys.indexOf("new key") === -1) { mdKeys.push("new key"); @@ -355,7 +355,7 @@ export class LinkEditor extends React.Component { } else { mdKeys = ["new key"]; } - LinkManager.Instance.allGroups.set(groupType, mdKeys); + LinkManager.Instance.groupMetadataKeys.set(groupType, mdKeys); } renderMetadata(groupId: string) { @@ -364,7 +364,7 @@ export class LinkEditor extends React.Component { if (groupDoc) { let mdDoc = Cast(groupDoc.proto!.metadata, Doc, new Doc); let groupType = StrCast(groupDoc.proto!.type); - let groupMdKeys = LinkManager.Instance.allGroups.get(groupType); + let groupMdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType); if (groupMdKeys) { groupMdKeys.forEach((key, index) => { metadata.push( -- cgit v1.2.3-70-g09d2 From 8bcd3567df7c49523638f0b935ecd09b1acad45d Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 13 Jun 2019 18:07:35 -0400 Subject: cannot create the same link twice --- src/client/documents/Documents.ts | 1 + src/client/util/LinkManager.ts | 10 ++++++++++ 2 files changed, 11 insertions(+) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9517cbbda..9cb41aecc 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -86,6 +86,7 @@ const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; export namespace DocUtils { export function MakeLink(source: Doc, target: Doc) { + if (LinkManager.Instance.doesLinkExist(source, target)) return; // let protoSrc = source.proto ? source.proto : source; // let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 2f40c77e7..929fcbf21 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -10,6 +10,7 @@ import { Doc } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; import { string } from "prop-types"; +import { Docs } from "../documents/Documents"; export namespace LinkUtils { export function findOppositeAnchor(link: Doc, anchor: Doc): Doc { @@ -179,4 +180,13 @@ export class LinkManager { } } + public doesLinkExist(anchor1: Doc, anchor2: Doc) { + let allLinks = LinkManager.Instance.allLinks; + let index = allLinks.findIndex(linkDoc => { + return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, new Doc), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, new Doc), anchor2)) || + (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, new Doc), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, new Doc), anchor1)); + }); + return index !== -1; + } + } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From f54496c7aa930e385e77aaf37df8d51db733e3a2 Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 14 Jun 2019 15:24:35 -0400 Subject: cleaned up link code --- src/client/util/LinkManager.ts | 166 ++++++----------- src/client/views/nodes/LinkBox.scss | 122 ++++++------- src/client/views/nodes/LinkBox.tsx | 52 +----- src/client/views/nodes/LinkEditor.scss | 106 +++++------ src/client/views/nodes/LinkEditor.tsx | 319 +++++++++++++-------------------- src/client/views/nodes/LinkMenu.scss | 76 +------- src/client/views/nodes/LinkMenu.tsx | 54 ++---- 7 files changed, 311 insertions(+), 584 deletions(-) (limited to 'src') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 929fcbf21..aaed7388d 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,71 +1,9 @@ -import { observable, computed, action } from "mobx"; -import React = require("react"); -import { SelectionManager } from "./SelectionManager"; -import { observer } from "mobx-react"; -import { props } from "bluebird"; -import { DocumentView } from "../views/nodes/DocumentView"; -import { link } from "fs"; +import { observable } from "mobx"; import { StrCast, Cast } from "../../new_fields/Types"; -import { Doc } from "../../new_fields/Doc"; +import { Doc, DocListCast } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; -import { string } from "prop-types"; -import { Docs } from "../documents/Documents"; -export namespace LinkUtils { - export function findOppositeAnchor(link: Doc, anchor: Doc): Doc { - if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { - return Cast(link.anchor2, Doc, new Doc); - } else { - return Cast(link.anchor1, Doc, new Doc); - } - } - - export function setAnchorGroups(link: Doc, anchor: Doc, groups: Doc[]) { - // console.log("setting groups for anchor", anchor["title"]); - if (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) { - link.anchor1Groups = new List(groups); - - let print: string[] = []; - Cast(link.anchor1Groups, listSpec(Doc), []).forEach(doc => { - if (doc instanceof Doc) { - print.push(StrCast(doc.type)); - } - }); - console.log("set anchor's groups as", print); - } else { - link.anchor2Groups = new List(groups); - - let print: string[] = []; - Cast(link.anchor2Groups, listSpec(Doc), []).forEach(doc => { - if (doc instanceof Doc) { - print.push(StrCast(doc.type)); - } - }); - console.log("set anchor's groups as", print); - } - } - - export function removeGroupFromAnchor(link: Doc, anchor: Doc, groupType: string) { - let groups = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)) ? - Cast(link.proto!.anchor1Groups, listSpec(Doc), []) : Cast(link.proto!.anchor2Groups, listSpec(Doc), []); - - let newGroups: Doc[] = []; - groups.forEach(groupDoc => { - if (groupDoc instanceof Doc && StrCast(groupDoc.type) !== groupType) { - newGroups.push(groupDoc); - } // TODO: promise - }); - - // let grouptypes: string[] = []; - // newGroups.forEach(groupDoc => { - // grouptypes.push(StrCast(groupDoc.type)); - // }); - // console.log("remove anchor's groups as", grouptypes); - - LinkUtils.setAnchorGroups(link, anchor, newGroups); - } -} /* * link doc: @@ -94,6 +32,7 @@ export class LinkManager { @observable public allLinks: Array = []; // list of link docs @observable public groupMetadataKeys: Map> = new Map(); // map of group type to list of its metadata keys + // finds all links that contain the given anchor public findAllRelatedLinks(anchor: Doc): Array { return LinkManager.Instance.allLinks.filter( link => Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)) || Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc))); @@ -102,27 +41,19 @@ export class LinkManager { // returns map of group type to anchor's links in that group type public findRelatedGroupedLinks(anchor: Doc): Map> { let related = this.findAllRelatedLinks(anchor); - let anchorGroups = new Map>(); related.forEach(link => { + let groups = LinkManager.Instance.getAnchorGroups(link, anchor); - // get groups of given anchor categorizes this link/opposite anchor in - let groups = (Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc))) ? Cast(link.anchor1Groups, listSpec(Doc), []) : Cast(link.anchor2Groups, listSpec(Doc), []); if (groups.length > 0) { groups.forEach(groupDoc => { - if (groupDoc instanceof Doc) { - let groupType = StrCast(groupDoc.type); - let group = anchorGroups.get(groupType); // TODO: clean this up lol - if (group) group.push(link); - else group = [link]; - anchorGroups.set(groupType, group); - } else { - // promise doc - } - + let groupType = StrCast(groupDoc.type); + let group = anchorGroups.get(groupType); + if (group) group.push(link); + else group = [link]; + anchorGroups.set(groupType, group); }); - } - else { + } else { // if link is in no groups then put it in default group let group = anchorGroups.get("*"); if (group) group.push(link); @@ -134,52 +65,38 @@ export class LinkManager { return anchorGroups; } - public findMetadataInGroup(groupType: string) { + // returns a list of all metadata docs associated with the given group type + public findAllMetadataDocsInGroup(groupType: string): Array { let md: Doc[] = []; let allLinks = LinkManager.Instance.allLinks; - // for every link find its groups - // allLinks.forEach(linkDoc => { - // let anchor1groups = LinkManager.Instance.findRelatedGroupedLinks(Cast(linkDoc["anchor1"], Doc, new Doc)); - // if (anchor1groups.get(groupType)) { - // md.push(linkDoc["anchor1"]["group"]) - // } - // }) allLinks.forEach(linkDoc => { - let anchor1Groups = Cast(linkDoc.anchor1Groups, listSpec(Doc), []); - let anchor2Groups = Cast(linkDoc.anchor2Groups, listSpec(Doc), []); - anchor1Groups.forEach(groupDoc => { - if (groupDoc instanceof Doc) { - if (StrCast(groupDoc.type) === groupType) { - md.push(Cast(groupDoc.metadata, Doc, new Doc)); - } - } else { - // TODO: promise - } - }); - anchor2Groups.forEach(groupDoc => { - if (groupDoc instanceof Doc) { - if (StrCast(groupDoc.type) === groupType) { - md.push(Cast(groupDoc.metadata, Doc, new Doc)); - } - } else { - // TODO: promise - } - }); - + let anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc)); + let anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc)); + anchor1Groups.forEach(groupDoc => { if (StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase()) md.push(Cast(groupDoc.metadata, Doc, new Doc)); }); + anchor2Groups.forEach(groupDoc => { if (StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase()) md.push(Cast(groupDoc.metadata, Doc, new Doc)); }); }); return md; } - public deleteGroup(groupType: string) { + // removes all group docs from all links with the given group type + public deleteGroup(groupType: string): void { let deleted = LinkManager.Instance.groupMetadataKeys.delete(groupType); if (deleted) { LinkManager.Instance.allLinks.forEach(linkDoc => { - LinkUtils.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType); - LinkUtils.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc), groupType); + LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType); + LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc), groupType); }); } } + // removes group doc of given group type only from given anchor on given link + public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { + let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); + let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase()); + LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); + } + + // checks if a link with the given anchors exists public doesLinkExist(anchor1: Doc, anchor2: Doc) { let allLinks = LinkManager.Instance.allLinks; let index = allLinks.findIndex(linkDoc => { @@ -189,4 +106,31 @@ export class LinkManager { return index !== -1; } + // finds the opposite anchor of a given anchor in a link + public findOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc { + if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { + return Cast(linkDoc.anchor2, Doc, new Doc); + } else { + return Cast(linkDoc.anchor1, Doc, new Doc); + } + } + + // gets the groups associates with an anchor in a link + public getAnchorGroups(linkDoc: Doc, anchor: Doc): Array { + if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { + return DocListCast(linkDoc.anchor1Groups); + } else { + return DocListCast(linkDoc.anchor2Groups); + } + } + + // sets the groups of the given anchor in the given link + public setAnchorGroups(linkDoc: Doc, anchor: Doc, groups: Doc[]) { + if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { + linkDoc.anchor1Groups = new List(groups); + } else { + linkDoc.anchor2Groups = new List(groups); + } + } + } \ No newline at end of file diff --git a/src/client/views/nodes/LinkBox.scss b/src/client/views/nodes/LinkBox.scss index 639f83b38..08fefaf4d 100644 --- a/src/client/views/nodes/LinkBox.scss +++ b/src/client/views/nodes/LinkBox.scss @@ -1,66 +1,62 @@ @import "../globalCssVariables"; -.link-container { - width: 100%; - height: 50px; +.link-menu-item { + border-top: 0.5px solid $light-color-secondary; + padding: 6px; + position: relative; display: flex; - flex-direction: row; - border-top: 0.5px solid #bababa; -} - -.info-container { - width: 65%; - padding-top: 5px; - padding-left: 5px; - display: flex; - flex-direction: column -} - -.link-name { - font-size: 11px; -} - -.doc-name { - font-size: 8px; -} - -.button-container { - width: 35%; - padding-top: 8px; - display: flex; - flex-direction: row; -} - -.button { - height: 20px; - width: 20px; - margin: 8px 4px; - border-radius: 50%; - opacity: 0.9; - pointer-events: auto; - background-color: $dark-color; - color: $light-color; - text-transform: uppercase; - letter-spacing: 2px; - font-size: 60%; - transition: transform 0.2s; -} - -.button:hover { - background: $main-accent; - cursor: pointer; -} - -// .fa-icon-view { -// margin-left: 3px; -// margin-top: 5px; -// } - -.fa-icon-edit { - margin-left: 6px; - margin-top: 6px; -} - -.fa-icon-delete { - margin-left: 7px; - margin-top: 6px; + font-size: 12px; + + .link-menu-item-content { + width: 100%; + } + + &:last-child { + border-bottom: 0.5px solid $light-color-secondary; + } + &:hover { + .link-menu-item-buttons { + display: flex; + } + .link-menu-item-content { + width: calc(100% - 42px); + } + } +} + +.link-menu-item-buttons { + display: none; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + + .button { + width: 20px; + height: 20px; + margin: 0; + margin-right: 6px; + border-radius: 50%; + cursor: pointer; + pointer-events: auto; + background-color: $dark-color; + color: $light-color; + font-size: 65%; + transition: transform 0.2s; + text-align: center; + position: relative; + + .fa-icon { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + &:last-child { + margin-right: 0; + } + &:hover { + background: $main-accent; + } + } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 5597bb1aa..1a7cce4a3 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -4,13 +4,10 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { observer } from "mobx-react"; import { DocumentManager } from "../../util/DocumentManager"; import { undoBatch } from "../../util/UndoManager"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; import './LinkBox.scss'; import React = require("react"); import { Doc } from '../../../new_fields/Doc'; -import { Cast, NumCast } from '../../../new_fields/Types'; -import { listSpec } from '../../../new_fields/Schema'; -import { action } from 'mobx'; +import { StrCast } from '../../../new_fields/Types'; library.add(faEye); @@ -20,9 +17,8 @@ library.add(faArrowRight); interface Props { linkDoc: Doc; - linkName: String; - pairedDoc: Doc; - type: String; + sourceDoc: Doc; + destinationDoc: Doc; showEditor: () => void; } @@ -30,58 +26,28 @@ interface Props { export class LinkBox extends React.Component { @undoBatch - followLink = async (e: React.PointerEvent): Promise => { + onFollowLink = async (e: React.PointerEvent): Promise => { e.stopPropagation(); - DocumentManager.Instance.jumpToDocument(this.props.pairedDoc, e.altKey); + DocumentManager.Instance.jumpToDocument(this.props.destinationDoc, e.altKey); } - onEditButtonPressed = (e: React.PointerEvent): void => { + onEdit = (e: React.PointerEvent): void => { e.stopPropagation(); - this.props.showEditor(); } - // @action - // onDeleteButtonPressed = async (e: React.PointerEvent): Promise => { - // e.stopPropagation(); - // const [linkedFrom, linkedTo] = await Promise.all([Cast(this.props.linkDoc.linkedFrom, Doc), Cast(this.props.linkDoc.linkedTo, Doc)]); - // if (linkedFrom) { - // const linkedToDocs = Cast(linkedFrom.linkedToDocs, listSpec(Doc)); - // if (linkedToDocs) { - // linkedToDocs.splice(linkedToDocs.indexOf(this.props.linkDoc), 1); - // } - // } - // if (linkedTo) { - // const linkedFromDocs = Cast(linkedTo.linkedFromDocs, listSpec(Doc)); - // if (linkedFromDocs) { - // linkedFromDocs.splice(linkedFromDocs.indexOf(this.props.linkDoc), 1); - // } - // } - // } - render() { - return ( - //
-

{this.props.linkName}

-
-
-

{this.props.type}{this.props.pairedDoc.Title}

+

{StrCast(this.props.destinationDoc.title)}

- {/*
-
*/} -
-
-
-
- {/*
-
*/} +
+
); diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index 01f0cb82a..6aac39f45 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -1,92 +1,63 @@ @import "../globalCssVariables"; -.edit-container { + +.linkEditor { width: 100%; height: auto; - display: flex; - flex-direction: column; + font-size: 12px; // TODO } -.name-input { - margin-bottom: 10px; - padding: 5px; - font-size: 12px; - border: 1px solid #bababa; +.linkEditor-back { + margin-bottom: 6px; } -.description-input { - font-size: 11px; - padding: 5px; - margin-bottom: 10px; - border: 1px solid #bababa; -} +.linkEditor-groupsLabel { + display: flex; + justify-content: space-between; -.save-button { - width: 50px; - height: 22px; - pointer-events: auto; - background-color: $dark-color; - color: $light-color; - text-transform: uppercase; - letter-spacing: 2px; - padding: 2px; - font-size: 10px; - margin: 0 auto; - transition: transform 0.2s; - text-align: center; - line-height: 20px; + button { + width: 20px; + height: 20px; + margin-left: 6px; + padding: 0; + font-size: 14px; + } } -.save-button:hover { - background: $main-accent; - cursor: pointer; +.linkEditor-linkedTo { + border-bottom: 0.5px solid $light-color-secondary; + padding-bottom: 6px; + margin-bottom: 6px; } -.linkEditor { - font-size: 12px; // TODO - - .linkEditor-back { - // background-color: $dark-color; - // color: $light-color; - margin-bottom: 6px; - } +.linkEditor-group { + background-color: $light-color-secondary; + padding: 6px; + margin: 3px 0; + border-radius: 3px; - .linkEditor-groupsLabel { - display: flex; - justify-content: space-between; - button { - width: 20px; - height: 20px; - margin-left: 6px; - padding: 0; - font-size: 14px; - } - } - .linkEditor-linkedTo { - border-bottom: 0.5px solid $light-color-secondary; - padding-bottom: 6px; - margin-bottom: 6px; - } - .linkEditor-group { - background-color: $light-color-secondary; - padding: 6px; - margin: 3px 0; - border-radius: 3px; - } .linkEditor-group-row { display: flex; margin-bottom: 6px; + .linkEditor-group-row-label { margin-right: 6px; } } + .linkEditor-metadata-row { display: flex; justify-content: space-between; margin-bottom: 6px; + + .linkEditor-error { + border-color: red; + } + input { width: calc(50% - 18px); height: 20px; } + button { width: 20px; height: 20px; @@ -97,10 +68,12 @@ } } + .linkEditor-dropdown { width: 100%; position: relative; z-index: 999; + .linkEditor-options-wrapper { width: 100%; position: absolute; @@ -109,12 +82,14 @@ display: flex; flex-direction: column; } + .linkEditor-option { background-color: $light-color-secondary; border: 1px solid $intermediate-color; border-top: 0; padding: 3px; cursor: pointer; + &:hover { background-color: $intermediate-color; font-weight: bold; @@ -126,19 +101,18 @@ height: 20px; display: flex; justify-content: space-between; + .linkEditor-groupOpts { width: calc(20% - 3px); height: 20px; - // margin-left: 6px; padding: 0; font-size: 10px; - &:first-child { // delete - font-size: 14px; - } - &:disabled { // delete + + &:disabled { background-color: gray; } } + .linkEditor-groupOpts button { width: 100%; height: 20px; diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 5097d625e..484682c22 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -4,61 +4,52 @@ import { observer } from "mobx-react"; import './LinkEditor.scss'; import { StrCast, Cast } from "../../../new_fields/Types"; import { Doc } from "../../../new_fields/Doc"; -import { List } from "../../../new_fields/List"; -import { listSpec } from "../../../new_fields/Schema"; -import { LinkManager, LinkUtils } from "../../util/LinkManager"; +import { LinkManager } from "../../util/LinkManager"; import { Docs } from "../../documents/Documents"; import { Utils } from "../../../Utils"; import { faArrowLeft, faEllipsisV, faTable } from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { string } from "prop-types"; import { SetupDrag } from "../../util/DragManager"; library.add(faArrowLeft); library.add(faEllipsisV); library.add(faTable); + +interface GroupTypesDropdownProps { + groupId: string; + groupType: string; + setGroup: (groupId: string, group: string) => void; +} // this dropdown could be generalized @observer -class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: string, setGroup: (groupId: string, group: string) => void }> { +class GroupTypesDropdown extends React.Component { @observable private _searchTerm: string = ""; @observable private _groupType: string = this.props.groupType; - @action - setSearchTerm(value: string) { - this._searchTerm = value; - } + @action setSearchTerm = (value: string): void => { this._searchTerm = value; }; + @action setGroupType = (value: string): void => { this._groupType = value; }; @action - setGroupType(value: string) { - this._groupType = value; + createGroup = (groupType: string): void => { + this.props.setGroup(this.props.groupId, groupType); + LinkManager.Instance.groupMetadataKeys.set(groupType, []); } - @action - createGroup(value: string) { - LinkManager.Instance.groupMetadataKeys.set(value, []); - this.props.setGroup(this.props.groupId, value); - } + renderOptions = (): JSX.Element[] | JSX.Element => { + if (this._searchTerm === "") return <>; - renderOptions = (): JSX.Element[] => { - let allGroups: string[], searchTerm: string, results: string[], exactFound: boolean; - if (this._searchTerm !== "") { - allGroups = Array.from(LinkManager.Instance.groupMetadataKeys.keys()); - searchTerm = this._searchTerm.toUpperCase(); - results = allGroups.filter(group => group.toUpperCase().indexOf(searchTerm) > -1); - exactFound = results.findIndex(group => group.toUpperCase() === searchTerm) > -1; - } else { - results = []; - exactFound = false; - } + let allGroupTypes = Array.from(LinkManager.Instance.groupMetadataKeys.keys()); + let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); + let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase()) > -1; - let options = []; - results.forEach(result => { - options.push(
{ this.props.setGroup(this.props.groupId, result); this.setGroupType(result); this.setSearchTerm(""); }}>{result}
); + let options = groupOptions.map(groupType => { + return
{ this.props.setGroup(this.props.groupId, groupType); this.setGroupType(groupType); this.setSearchTerm(""); }}>{groupType}
; }); + // if search term does not already exist as a group type, give option to create new group type if (!exactFound && this._searchTerm !== "") { options.push(
{ this.createGroup(this._searchTerm); this.setGroupType(this._searchTerm); this.setSearchTerm(""); }}>Define new "{this._searchTerm}" relationship
); @@ -81,57 +72,66 @@ class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: s } - +interface LinkMetadataEditorProps { + groupType: string; + mdDoc: Doc; + mdKey: string; + mdValue: string; +} @observer -class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc, mdKey: string, mdValue: string }> { +class LinkMetadataEditor extends React.Component { @observable private _key: string = this.props.mdKey; @observable private _value: string = this.props.mdValue; + @observable private _keyError: boolean = false; @action - editMetadataKey = (value: string): void => { - // TODO: check that metadata doesnt already exist in group + setMetadataKey = (value: string): void => { let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!); - if (groupMdKeys) { - let index = groupMdKeys.indexOf(this._key); - if (index > -1) { - groupMdKeys[index] = value; - } - else { - console.log("OLD KEY WAS NOT FOUND", ...groupMdKeys); - } + + // don't allow user to create existing key + let newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase()); + if (newIndex > -1) { + this._keyError = true; + this._key = value; + return; + } else { + this._keyError = false; } + // set new value for key + let currIndex = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase()); + if (currIndex === -1) console.error("LinkMetadataEditor: key was not found"); + groupMdKeys[currIndex] = value; + this._key = value; LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys); } @action - editMetadataValue = (value: string): void => { - this.props.mdDoc[this._key] = value; - this._value = value; + setMetadataValue = (value: string): void => { + if (!this._keyError) { + this._value = value; + this.props.mdDoc[this._key] = value; + } } @action removeMetadata = (): void => { let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!); - if (groupMdKeys) { - let index = groupMdKeys.indexOf(this._key); - if (index > -1) { - groupMdKeys.splice(index, 1); - } - else { - console.log("OLD KEY WAS NOT FOUND", ...groupMdKeys); - } - } - this._key = ""; + + let index = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase()); + if (index === -1) console.error("LinkMetadataEditor: key was not found"); + groupMdKeys.splice(index, 1); + LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys); + this._key = ""; } render() { return (
- this.editMetadataKey(e.target.value)}>: - this.editMetadataValue(e.target.value)}> + this.setMetadataKey(e.target.value)}>: + this.setMetadataValue(e.target.value)}>
); @@ -144,175 +144,127 @@ interface LinkEditorProps { linkDoc: Doc; showLinks: () => void; } - @observer export class LinkEditor extends React.Component { - @observable private _groups: Map = new Map(); // map of temp group id to the corresponding group doc + // map of temporary group id to the corresponding group doc + @observable private _groups: Map = new Map(); constructor(props: LinkEditorProps) { super(props); let groups = new Map(); - let groupList = (Doc.AreProtosEqual(props.sourceDoc, Cast(props.linkDoc.anchor1, Doc, new Doc))) ? - Cast(props.linkDoc.anchor1Groups, listSpec(Doc), []) : Cast(props.linkDoc.anchor2Groups, listSpec(Doc), []); + let groupList = LinkManager.Instance.getAnchorGroups(props.linkDoc, props.sourceDoc); groupList.forEach(groupDoc => { - if (groupDoc instanceof Doc) { - let id = Utils.GenerateGuid(); - groups.set(id, groupDoc); - } else { - // promise doc - } + let id = Utils.GenerateGuid(); + groups.set(id, groupDoc); }); this._groups = groups; } @action addGroup = (): void => { - console.log("before adding", ...Array.from(this._groups.keys())); - // new group only gets added if there is not already a group with type "new group" let index = Array.from(this._groups.values()).findIndex(groupDoc => { return groupDoc.type === "New Group"; }); - if (index === -1) { - // create new document for group - let mdDoc = Docs.TextDocument(); - mdDoc.proto!.anchor1 = this.props.sourceDoc.title; - mdDoc.proto!.anchor2 = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title; + if (index > -1) return; - let groupDoc = Docs.TextDocument(); - groupDoc.proto!.type = "New Group"; - groupDoc.proto!.metadata = mdDoc; + // create new metadata document for group + let mdDoc = Docs.TextDocument(); + mdDoc.proto!.anchor1 = this.props.sourceDoc.title; + mdDoc.proto!.anchor2 = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title; - this._groups.set(Utils.GenerateGuid(), groupDoc); + // create new group document + let groupDoc = Docs.TextDocument(); + groupDoc.proto!.type = "New Group"; + groupDoc.proto!.metadata = mdDoc; - let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); - } + this._groups.set(Utils.GenerateGuid(), groupDoc); - - // console.log("set anchor groups for", this.props.sourceDoc["title"]); - console.log("after adding", ...Array.from(this._groups.keys())); + let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; + LinkManager.Instance.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); } @action setGroupType = (groupId: string, groupType: string): void => { - console.log("setting for ", groupId); - // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; let groupDoc = this._groups.get(groupId); if (groupDoc) { - console.log("found group doc"); groupDoc.proto!.type = groupType; - this._groups.set(groupId, groupDoc); - - - LinkUtils.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); - console.log("set", Array.from(this._groups.values()).length); + LinkManager.Instance.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); } - - let anchor1groups: string[] = []; - Cast(this.props.linkDoc.anchor1Groups, listSpec(Doc), []).forEach(doc => { - if (doc instanceof Doc) { - anchor1groups.push(StrCast(doc.proto!.type)); - } else { - console.log("promise"); - } - }); - let anchor2groups: string[] = []; - Cast(this.props.linkDoc.anchor2Groups, listSpec(Doc), []).forEach(doc => { - if (doc instanceof Doc) { - anchor2groups.push(StrCast(doc.proto!.type)); - } else { - console.log("promise"); - } - }); - console.log("groups for anchors; anchor1: [", ...anchor1groups, "] ; anchor2: [", ...anchor2groups, "]"); } - removeGroupFromLink = (groupId: string, groupType: string) => { - // this._groups.delete(groupId); + removeGroupFromLink = (groupId: string, groupType: string): void => { let groupDoc = this._groups.get(groupId); - if (groupDoc) { - LinkUtils.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType); - this._groups.delete(groupId); - } - // LinkUtils.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); - console.log("\nremoved", groupId, "remaining", ...Array.from(this._groups.keys()), "\n"); + if (!groupDoc) console.error("LinkEditor: group not found"); + LinkManager.Instance.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType); + this._groups.delete(groupId); } - deleteGroup = (groupId: string, groupType: string) => { + deleteGroup = (groupId: string, groupType: string): void => { let groupDoc = this._groups.get(groupId); - if (groupDoc) { - LinkManager.Instance.deleteGroup(groupType); - this._groups.delete(groupId); - } + if (!groupDoc) console.error("LinkEditor: group not found"); + LinkManager.Instance.deleteGroup(groupType); + this._groups.delete(groupId); } - copyGroup = (groupId: string, groupType: string) => { - let oppAnchor = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); - let groupList = (Doc.AreProtosEqual(oppAnchor, Cast(this.props.linkDoc.anchor1, Doc, new Doc))) ? - Cast(this.props.linkDoc.anchor1Groups, listSpec(Doc), []) : Cast(this.props.linkDoc.anchor2Groups, listSpec(Doc), []); - // if group already exists on opposite anchor, copy value - let index = groupList.findIndex(groupDoc => { - if (groupDoc instanceof Doc) { - return StrCast(groupDoc.type) === groupType; - } else { - return false; - } - }); - // TODO: clean - // if (index > 0) { - // let thisGroupDoc = this._groups.get(groupId); - // let oppGroupDoc = groupList[index]; - // let keys = LinkManager.Instance.allGroups.get(groupType); - // if (keys) { - // keys.forEach(key => { - // if (thisGroupDoc && oppGroupDoc instanceof Doc) { // TODO: clean - // let val = thisGroupDoc[key] === undefined ? "" : StrCast(thisGroupDoc[key]); - // oppGroupDoc[key] = val; - // } - // // mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key]) - // // oppGroupDoc[key] = thisGroupDoc[key]; - // }) - // } - // // LinkUtils.setAnchorGroups(this.props.linkDoc, oppAnchor, [oppGroupDoc]); - // } else { - let thisGroupDoc = this._groups.get(groupId); - let thisMdDoc = Cast(thisGroupDoc!.metadata, Doc, new Doc); - let newGroupDoc = Docs.TextDocument(); - let newMdDoc = Docs.TextDocument(); - newMdDoc.proto!.anchor1 = StrCast(thisMdDoc.anchor2); - newMdDoc.proto!.anchor2 = StrCast(thisMdDoc.anchor1); + copyGroup = (groupId: string, groupType: string): void => { + let sourceGroupDoc = this._groups.get(groupId); + let sourceMdDoc = Cast(sourceGroupDoc!.metadata, Doc, new Doc); + let destDoc = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); + let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc); let keys = LinkManager.Instance.groupMetadataKeys.get(groupType); + + // create new metadata doc with copied kvp + let destMdDoc = Docs.TextDocument(); + destMdDoc.proto!.anchor1 = StrCast(sourceMdDoc.anchor2); + destMdDoc.proto!.anchor2 = StrCast(sourceMdDoc.anchor1); if (keys) { keys.forEach(key => { - if (thisGroupDoc) { // TODO: clean - let val = thisMdDoc[key] === undefined ? "" : StrCast(thisMdDoc[key]); - newMdDoc[key] = val; - } - // mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key]) - // oppGroupDoc[key] = thisGroupDoc[key]; + let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]); + destMdDoc[key] = val; }); } - newGroupDoc.proto!.type = groupType; - newGroupDoc.proto!.metadata = newMdDoc; - LinkUtils.setAnchorGroups(this.props.linkDoc, oppAnchor, [newGroupDoc]); // TODO: fix to append to list - // } + // create new group doc with new metadata doc + let destGroupDoc = Docs.TextDocument(); + destGroupDoc.proto!.type = groupType; + destGroupDoc.proto!.metadata = destMdDoc; - // else create group on opposite anchor + // if group does not already exist on opposite anchor, create group doc + let index = destGroupList.findIndex(groupDoc => { StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase(); }); + if (index > -1) { + destGroupList[index] = destGroupDoc; + } else { + destGroupList.push(destGroupDoc); + } + + LinkManager.Instance.setAnchorGroups(this.props.linkDoc, destDoc, destGroupList); + } + + viewGroupAsTable = (groupId: string, groupType: string): JSX.Element => { + let keys = LinkManager.Instance.groupMetadataKeys.get(groupType); + let groupDoc = this._groups.get(groupId); + if (keys && groupDoc) { + let docs: Doc[] = LinkManager.Instance.findAllMetadataDocsInGroup(groupType); + let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" })); + let ref = React.createRef(); + return
; + } else { + return ; + } } - renderGroup(groupId: string, groupDoc: Doc) { + renderGroup = (groupId: string, groupDoc: Doc): JSX.Element => { let type = StrCast(groupDoc.type); if ((type && LinkManager.Instance.groupMetadataKeys.get(type)) || type === "New Group") { return (

type:

- +
{this.renderMetadata(groupId)}
@@ -330,35 +282,20 @@ export class LinkEditor extends React.Component { } } - viewGroupAsTable(groupId: string, groupType: string) { - let keys = LinkManager.Instance.groupMetadataKeys.get(groupType); - let groupDoc = this._groups.get(groupId); - if (keys && groupDoc) { - console.log("keys:", ...keys); - let docs: Doc[] = LinkManager.Instance.findMetadataInGroup(groupType); - let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" })); - let ref = React.createRef(); - return
; - } else { - return ; - } - } - @action addMetadata = (groupType: string): void => { let mdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType); if (mdKeys) { - if (mdKeys.indexOf("new key") === -1) { - mdKeys.push("new key"); - } + // only add "new key" if there is no other key with value "new key"; prevents spamming + if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key"); } else { mdKeys = ["new key"]; } LinkManager.Instance.groupMetadataKeys.set(groupType, mdKeys); } - renderMetadata(groupId: string) { + renderMetadata = (groupId: string): JSX.Element[] => { let metadata: Array = []; let groupDoc = this._groups.get(groupId); if (groupDoc) { @@ -377,7 +314,7 @@ export class LinkEditor extends React.Component { } render() { - let destination = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); + let destination = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); let groups: Array = []; this._groups.forEach((groupDoc, groupId) => { diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index 7e031b897..09a830c9e 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -1,84 +1,14 @@ @import "../globalCssVariables"; -#linkMenu-container { +.linkMenu { width: 100%; height: auto; - display: flex; - flex-direction: column; } -#linkMenu-searchBar { - width: 100%; - padding: 5px; - margin-bottom: 10px; - font-size: 12px; - border: 1px solid #bababa; -} - -#linkMenu-list { - margin-top: 5px; - width: 100%; - height: 100px; +.linkMenu-list { + max-height: 200px; overflow-y: scroll; } -.link-menu-group { - .link-menu-item { - border-top: 0.5px solid $light-color-secondary; - padding: 6px; - position: relative; - display: flex; - - .link-menu-item-content { - width: 100%; - } - &:last-child { - border-bottom: 0.5px solid $light-color-secondary; - } - &:hover .link-menu-item-buttons { - display: flex; - } - &:hover .link-menu-item-content { - width: calc(100% - 42px); - } - } - .link-menu-item-buttons { - display: none; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - - .button { - width: 20px; - height: 20px; - margin: 0; - margin-right: 6px; - border-radius: 50%; - cursor: pointer; - pointer-events: auto; - background-color: $dark-color; - color: $light-color; - font-size: 65%; - transition: transform 0.2s; - text-align: center; - position: relative; - - .fa-icon { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - - &:last-child { - margin-right: 0; - } - &:hover { - background: $main-accent; - } - } - } -} diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 47bd6c3eb..ebca54c92 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -6,12 +6,8 @@ import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; -import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; import { LinkManager, LinkUtils } from "../../util/LinkManager"; -import { number, string } from "prop-types"; -import { listSpec } from "../../../new_fields/Schema"; -import { Utils } from "../../../Utils"; interface Props { docView: DocumentView; @@ -23,57 +19,42 @@ export class LinkMenu extends React.Component { @observable private _editingLink?: Doc; - // renderLinkItems(links: Doc[], key: string, type: string) { - // return links.map(link => { - // let doc = FieldValue(Cast(link[key], Doc)); - // if (doc) { - // return this._editingLink = link)} type={type} />; - // } - // }); - // } - - renderGroup(links: Doc[]) { + renderGroup = (group: Doc[]): Array => { let source = this.props.docView.Document; - return links.map(link => { - let destination = LinkUtils.findOppositeAnchor(link, source); - let doc = FieldValue(Cast(destination, Doc)); - if (doc) { - return this._editingLink = link)} type={""} />; - } + return group.map(linkDoc => { + let destination = LinkUtils.findOppositeAnchor(linkDoc, source); + return this._editingLink = linkDoc)} />; }); } - renderLinks = (links: Map>): Array => { + renderAllGroups = (groups: Map>): Array => { let linkItems: Array = []; - - links.forEach((links, group) => { + groups.forEach((group, groupType) => { linkItems.push( -
-

{group}:

+
+

{groupType}:

- {this.renderGroup(links)} + {this.renderGroup(group)}
- ) + ); }); - if (linkItems.length === 0) { - linkItems.push(

no links have been created yet

); - } + // source doc has no links + if (linkItems.length === 0) linkItems.push(

No links have been created yet. Drag the linking button onto another document to create a link.

); return linkItems; } render() { - let related: Map = LinkManager.Instance.findRelatedGroupedLinks(this.props.docView.props.Document); + let sourceDoc = this.props.docView.props.Document; + let groups: Map = LinkManager.Instance.findRelatedGroupedLinks(sourceDoc); if (this._editingLink === undefined) { return ( -
+
{/* */} -
- {/* {this.renderLinkItems(linkTo, "linkedTo", "Destination: ")} - {this.renderLinkItems(linkFrom, "linkedFrom", "Source: ")} */} - {this.renderLinks(related)} +
+ {this.renderAllGroups(groups)}
); @@ -82,6 +63,5 @@ export class LinkMenu extends React.Component { this._editingLink = undefined)}> ); } - } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 05f0f145269fffc5dfada98a5f20bbc8e204bd28 Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 14 Jun 2019 15:30:22 -0400 Subject: cleaned up more link code --- src/client/util/DocumentManager.ts | 42 ++++--------------------------------- src/client/util/DragManager.ts | 17 ++++++--------- src/client/views/nodes/LinkMenu.tsx | 4 ++-- 3 files changed, 12 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 2acbb3ad4..6271220e4 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -9,7 +9,7 @@ import { CollectionView } from '../views/collections/CollectionView'; import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; import { Id } from '../../new_fields/FieldSymbols'; -import { LinkManager, LinkUtils } from './LinkManager'; +import { LinkManager } from './LinkManager'; export class DocumentManager { @@ -86,53 +86,19 @@ export class DocumentManager { public get LinkedDocumentViews() { let linked = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { - let linksList = LinkManager.Instance.findAllRelatedLinks(dv.props.Document); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { if (link) { - // let destination = (link["linkedTo"] === dv.props.Document) ? link["linkedFrom"] : link["linkedTo"]; - - let destination = LinkUtils.findOppositeAnchor(link, dv.props.Document); - let linkToDoc = FieldValue(Cast(destination, Doc)); - // let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); - if (linkToDoc) { - DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => + let destination = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); + if (destination) { + DocumentManager.Instance.getDocumentViews(destination).map(docView1 => pairs.push({ a: dv, b: docView1, l: link })); } } return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); } - - // let linksList = DocListCast(dv.props.Document.linkedToDocs); - // console.log("to links", linksList.length); - // if (linksList && linksList.length) { - // pairs.push(...linksList.reduce((pairs, link) => { - // if (link) { - // let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); - // if (linkToDoc) { - // DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => - // pairs.push({ a: dv, b: docView1, l: link })); - // } - // } - // return pairs; - // }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); - // } - // linksList = DocListCast(dv.props.Document.linkedFromDocs); - // console.log("from links", linksList.length); - // if (linksList && linksList.length) { - // pairs.push(...linksList.reduce((pairs, link) => { - // if (link) { - // let linkFromDoc = FieldValue(Cast(link.linkedFrom, Doc)); - // if (linkFromDoc) { - // DocumentManager.Instance.getDocumentViews(linkFromDoc).map(docView1 => - // pairs.push({ a: dv, b: docView1, l: link })); - // } - // } - // return pairs; - // }, pairs)); - // } return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); return linked; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 9ac421fbf..b4acbcffa 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -4,7 +4,7 @@ import { Cast } from "../../new_fields/Types"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import * as globalCssVariables from "../views/globalCssVariables.scss"; -import { LinkManager, LinkUtils } from "./LinkManager"; +import { LinkManager } from "./LinkManager"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc | Promise, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType) { @@ -42,18 +42,13 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) { let srcTarg = sourceDoc.proto; let draggedDocs: Doc[] = []; - // let draggedFromDocs: Doc[] = []; if (srcTarg) { - // let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs); - // let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs); let linkDocs = LinkManager.Instance.findAllRelatedLinks(srcTarg); - if (linkDocs) draggedDocs = linkDocs.map(link => { - return LinkUtils.findOppositeAnchor(link, sourceDoc); - }); - - - // if (linkToDocs) draggedDocs = linkToDocs.map(linkDoc => Cast(linkDoc.linkedTo, Doc) as Doc); - // if (linkFromDocs) draggedFromDocs = linkFromDocs.map(linkDoc => Cast(linkDoc.linkedFrom, Doc) as Doc); + if (linkDocs) { + draggedDocs = linkDocs.map(link => { + return LinkManager.Instance.findOppositeAnchor(link, sourceDoc); + }); + } } // draggedDocs.push(...draggedFromDocs); if (draggedDocs.length) { diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index ebca54c92..2fcbd25fa 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -7,7 +7,7 @@ import './LinkMenu.scss'; import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; -import { LinkManager, LinkUtils } from "../../util/LinkManager"; +import { LinkManager } from "../../util/LinkManager"; interface Props { docView: DocumentView; @@ -22,7 +22,7 @@ export class LinkMenu extends React.Component { renderGroup = (group: Doc[]): Array => { let source = this.props.docView.Document; return group.map(linkDoc => { - let destination = LinkUtils.findOppositeAnchor(linkDoc, source); + let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, source); return this._editingLink = linkDoc)} />; }); } -- cgit v1.2.3-70-g09d2 From 1afb8d18e0f63e7e9ab05ccf79f5f34533fdec05 Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 14 Jun 2019 16:38:25 -0400 Subject: links can be expanded in menu to view metadata --- src/client/views/nodes/LinkBox.scss | 30 ++++++++++++++++------ src/client/views/nodes/LinkBox.tsx | 51 +++++++++++++++++++++++++++---------- src/client/views/nodes/LinkMenu.tsx | 8 +++--- 3 files changed, 64 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/LinkBox.scss b/src/client/views/nodes/LinkBox.scss index 08fefaf4d..77462f611 100644 --- a/src/client/views/nodes/LinkBox.scss +++ b/src/client/views/nodes/LinkBox.scss @@ -1,29 +1,43 @@ @import "../globalCssVariables"; -.link-menu-item { - border-top: 0.5px solid $light-color-secondary; +.linkMenu-item { + border-top: 0.5px solid $main-accent; padding: 6px; position: relative; display: flex; font-size: 12px; - .link-menu-item-content { + .linkMenu-item-content { width: 100%; } + + .link-metadata { + margin-top: 6px; + padding: 3px; + border-top: 0.5px solid $light-color-secondary; + .link-metadata-row { + margin-left: 6px; + } + } &:last-child { - border-bottom: 0.5px solid $light-color-secondary; + border-bottom: 0.5px solid $main-accent; } &:hover { - .link-menu-item-buttons { + .linkMenu-item-buttons { display: flex; } - .link-menu-item-content { - width: calc(100% - 42px); + .linkMenu-item-content { + &.expand-two { + width: calc(100% - 46px); + } + &.expand-three { + width: calc(100% - 78px); + } } } } -.link-menu-item-buttons { +.linkMenu-item-buttons { display: none; position: absolute; top: 50%; diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 1a7cce4a3..9fbe83126 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEdit, faEye, faTimes, faArrowRight } from '@fortawesome/free-solid-svg-icons'; +import { faEdit, faEye, faTimes, faArrowRight, faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { observer } from "mobx-react"; import { DocumentManager } from "../../util/DocumentManager"; @@ -7,15 +7,14 @@ import { undoBatch } from "../../util/UndoManager"; import './LinkBox.scss'; import React = require("react"); import { Doc } from '../../../new_fields/Doc'; -import { StrCast } from '../../../new_fields/Types'; +import { StrCast, Cast } from '../../../new_fields/Types'; +import { observable, action } from 'mobx'; +import { LinkManager } from '../../util/LinkManager'; +library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); -library.add(faEye); -library.add(faEdit); -library.add(faTimes); -library.add(faArrowRight); - interface Props { + groupType: string; linkDoc: Doc; sourceDoc: Doc; destinationDoc: Doc; @@ -24,6 +23,8 @@ interface Props { @observer export class LinkBox extends React.Component { + @observable private _showMore: boolean = false; + @action toggleShowMore() { this._showMore = !this._showMore; } @undoBatch onFollowLink = async (e: React.PointerEvent): Promise => { @@ -36,19 +37,43 @@ export class LinkBox extends React.Component { this.props.showEditor(); } + renderMetadata = (): JSX.Element => { + let groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); + let index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase()); + let groupDoc = index > -1 ? groups[index] : undefined; + + let mdRows: Array = []; + if (groupDoc) { + let mdDoc = Cast(groupDoc.metadata, Doc, new Doc); + let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); + mdRows = keys!.map(key => { + return (
{key}: {StrCast(mdDoc[key])}
); + }); + } + + return (
{mdRows}
); + } + render() { + + let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); + let canExpand = keys ? keys.length > 0 : false; + return ( -
-
+
+

{StrCast(this.props.destinationDoc.title)}

+
+ {canExpand ?
this.toggleShowMore()}> +
: <>} +
+
+
+ {this._showMore ? this.renderMetadata() : <>}
-
-
-
-
); } diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 2fcbd25fa..aef56df67 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -19,11 +19,11 @@ export class LinkMenu extends React.Component { @observable private _editingLink?: Doc; - renderGroup = (group: Doc[]): Array => { + renderGroup = (group: Doc[], groupType: string): Array => { let source = this.props.docView.Document; return group.map(linkDoc => { let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, source); - return this._editingLink = linkDoc)} />; + return this._editingLink = linkDoc)} />; }); } @@ -34,13 +34,13 @@ export class LinkMenu extends React.Component {

{groupType}:

- {this.renderGroup(group)} + {this.renderGroup(group, groupType)}
); }); - // source doc has no links + // if source doc has no links push message if (linkItems.length === 0) linkItems.push(

No links have been created yet. Drag the linking button onto another document to create a link.

); return linkItems; -- cgit v1.2.3-70-g09d2 From d3e734140c52e69f3d07f1ebe145e554a062addb Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 14 Jun 2019 17:08:05 -0400 Subject: can drag follow button on link to move anchor --- src/client/views/nodes/LinkBox.tsx | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 9fbe83126..09df1eac5 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -10,6 +10,8 @@ import { Doc } from '../../../new_fields/Doc'; import { StrCast, Cast } from '../../../new_fields/Types'; import { observable, action } from 'mobx'; import { LinkManager } from '../../util/LinkManager'; +import { DragLinksAsDocuments } from '../../util/DragManager'; +import { SelectionManager } from '../../util/SelectionManager'; library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); @@ -23,6 +25,7 @@ interface Props { @observer export class LinkBox extends React.Component { + private _drag = React.createRef(); @observable private _showMore: boolean = false; @action toggleShowMore() { this._showMore = !this._showMore; } @@ -54,6 +57,30 @@ export class LinkBox extends React.Component { return (
{mdRows}
); } + 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); + + DragLinksAsDocuments(this._drag.current, e.x, e.y, SelectionManager.SelectedDocuments()[0].props.Document); + } + e.stopPropagation(); + } + render() { let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); @@ -68,13 +95,13 @@ export class LinkBox extends React.Component { {canExpand ?
this.toggleShowMore()}>
: <>}
-
+
{this._showMore ? this.renderMetadata() : <>}
-
+
); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From b4bd43f6e79c9dec30842262f270ca6122f1184a Mon Sep 17 00:00:00 2001 From: Fawn Date: Sat, 15 Jun 2019 17:17:45 -0400 Subject: cleaned up some leftover linkedfromdocs/linkedtodocs --- src/client/documents/Documents.ts | 31 ++--------------- src/client/util/LinkManager.ts | 3 +- src/client/views/DocumentDecorations.tsx | 3 -- .../views/collections/CollectionDockingView.tsx | 5 +-- src/client/views/nodes/DocumentView.tsx | 40 +++++++++------------- 5 files changed, 24 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9cb41aecc..268dbc927 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -87,21 +87,11 @@ const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; export namespace DocUtils { export function MakeLink(source: Doc, target: Doc) { if (LinkManager.Instance.doesLinkExist(source, target)) return; - // let protoSrc = source.proto ? source.proto : source; - // let protoTarg = target.proto ? target.proto : target; - UndoManager.RunInBatch(() => { - // let groupDoc1 = Docs.TextDocument(); - // groupDoc1.proto!.type = "*"; - // groupDoc1.proto!.metadata = Docs.TextDocument(); - // let groupDoc2 = Docs.TextDocument(); - // groupDoc2.proto!.type = "*"; - // groupDoc2.proto!.metadata = Docs.TextDocument(); + UndoManager.RunInBatch(() => { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); - //let linkDoc = new Doc; - // linkDoc.proto!.title = source.proto!.title + " and " + target.proto!.title; - // linkDoc.proto!.linkDescription = ""; + linkDoc.proto!.anchor1 = source; linkDoc.proto!.anchor1Page = source.curPage; linkDoc.proto!.anchor1Groups = new List([]); @@ -110,23 +100,6 @@ export namespace DocUtils { linkDoc.proto!.anchor2Page = target.curPage; linkDoc.proto!.anchor2Groups = new List([]); - // linkDoc.proto!.linkedTo = target; - // linkDoc.proto!.linkedToPage = target.curPage; - // linkDoc.proto!.linkedFrom = source; - // linkDoc.proto!.linkedFromPage = source.curPage; - - // let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); - // if (!linkedFrom) { - // protoTarg.linkedFromDocs = linkedFrom = new List(); - // } - // linkedFrom.push(linkDoc); - - // let linkedTo = Cast(protoSrc.linkedToDocs, listSpec(Doc)); - // if (!linkedTo) { - // protoSrc.linkedToDocs = linkedTo = new List(); - // } - // linkedTo.push(linkDoc); - LinkManager.Instance.allLinks.push(linkDoc); return linkDoc; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index aaed7388d..23ba9d2e4 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -30,7 +30,8 @@ export class LinkManager { } @observable public allLinks: Array = []; // list of link docs - @observable public groupMetadataKeys: Map> = new Map(); // map of group type to list of its metadata keys + @observable public groupMetadataKeys: Map> = new Map(); + // map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it hodls // finds all links that contain the given anchor public findAllRelatedLinks(anchor: Doc): Array { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index eb45c770e..6ed263ac8 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -511,9 +511,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let linkButton = null; if (SelectionManager.SelectedDocuments().length > 0) { let selFirst = SelectionManager.SelectedDocuments()[0]; - // let linkToSize = Cast(selFirst.props.Document.linkedToDocs, listSpec(Doc), []).length; - // let linkFromSize = Cast(selFirst.props.Document.linkedFromDocs, listSpec(Doc), []).length; - // let linkCount = linkToSize + linkFromSize; let linkCount = LinkManager.Instance.findAllRelatedLinks(selFirst.props.Document).length; linkButton = ( { @@ -338,9 +339,9 @@ export class CollectionDockingView extends React.Component [doc.linkedFromDocs, doc.LinkedToDocs, doc.title], + tab.reactionDisposer = reaction(() => [LinkManager.Instance.findAllRelatedLinks(doc), doc.title], () => { - counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length; + counter.innerHTML = LinkManager.Instance.findAllRelatedLinks(doc).length; tab.titleElement[0].textContent = doc.title; }, { fireImmediately: true }); tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index fc6974e6c..c1a32ad82 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -31,6 +31,7 @@ import React = require("react"); import { Id, Copy } from '../../../new_fields/FieldSymbols'; import { ContextMenuProps } from '../ContextMenuItem'; import { list, object, createSimpleSchema } from 'serializr'; +import { LinkManager } from '../../util/LinkManager'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(faTrash); @@ -49,16 +50,16 @@ library.add(faCrosshairs); library.add(faDesktop); -const linkSchema = createSchema({ - title: "string", - linkDescription: "string", - linkTags: "string", - linkedTo: Doc, - linkedFrom: Doc -}); +// const linkSchema = createSchema({ +// title: "string", +// linkDescription: "string", +// linkTags: "string", +// linkedTo: Doc, +// linkedFrom: Doc +// }); -type LinkDoc = makeInterface<[typeof linkSchema]>; -const LinkDoc = makeInterface(linkSchema); +// type LinkDoc = makeInterface<[typeof linkSchema]>; +// const LinkDoc = makeInterface(linkSchema); export interface DocumentViewProps { ContainingCollectionView: Opt; @@ -216,8 +217,7 @@ export class DocumentView extends DocComponent(Docu let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs); let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs); - let linkedToDocs = await DocListCastAsync(this.props.Document.linkedToDocs, []); - let linkedFromDocs = await DocListCastAsync(this.props.Document.linkedFromDocs, []); + let linkedDocs = LinkManager.Instance.findAllRelatedLinks(this.props.Document); let expandedDocs: Doc[] = []; expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs; expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs; @@ -252,18 +252,12 @@ export class DocumentView extends DocComponent(Docu this.props.collapseToPoint && this.props.collapseToPoint(scrpt, expandedProtoDocs); } } - else if (linkedToDocs.length || linkedFromDocs.length) { - let linkedFwdDocs = [ - linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : expandedDocs[0], - linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : expandedDocs[0]]; - - let linkedFwdPage = [ - linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : undefined, - linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : undefined]; - if (!linkedFwdDocs.some(l => l instanceof Promise)) { - let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab"); - DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0]); - } + else if (linkedDocs.length) { + let linkedDoc = linkedDocs.length ? linkedDocs[0] : expandedDocs[0]; + let linkedPages = [linkedDocs.length ? NumCast(linkedDocs[0].anchor1Page, undefined) : NumCast(linkedDocs[0].anchor2Page, undefined), + linkedDocs.length ? NumCast(linkedDocs[0].anchor2Page, undefined) : NumCast(linkedDocs[0].anchor1Page, undefined)]; + let maxLocation = StrCast(linkedDoc.maximizeLocation, "inTab"); + DocumentManager.Instance.jumpToDocument(linkedDoc, ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedPages[altKey ? 1 : 0]); } } } -- cgit v1.2.3-70-g09d2 From 70eaadb2773ae78f99d856c4986b8f27ebbb36ad Mon Sep 17 00:00:00 2001 From: Fawn Date: Mon, 17 Jun 2019 11:32:38 -0400 Subject: single link can be dragged from link menu --- src/client/util/DragManager.ts | 12 ++++++++++++ src/client/views/nodes/LinkBox.tsx | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index b32c25b0a..82d30e0e6 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -43,6 +43,18 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () return onItemDown; } +export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: number, linkDoc: Doc, sourceDoc: Doc) { + let draggeddoc = LinkManager.Instance.findOppositeAnchor(linkDoc, sourceDoc); + let moddrag = await Cast(draggeddoc.annotationOn, Doc); + let dragData = new DragManager.DocumentDragData(moddrag ? [moddrag] : [draggeddoc]); + DragManager.StartDocumentDrag([dragEle], dragData, x, y, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); +} + export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) { let srcTarg = sourceDoc.proto; let draggedDocs: Doc[] = []; diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 09df1eac5..8d07547ed 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -10,7 +10,7 @@ import { Doc } from '../../../new_fields/Doc'; import { StrCast, Cast } from '../../../new_fields/Types'; import { observable, action } from 'mobx'; import { LinkManager } from '../../util/LinkManager'; -import { DragLinksAsDocuments } from '../../util/DragManager'; +import { DragLinksAsDocuments, DragLinkAsDocument } from '../../util/DragManager'; import { SelectionManager } from '../../util/SelectionManager'; library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); @@ -76,7 +76,7 @@ export class LinkBox extends React.Component { document.removeEventListener("pointermove", this.onLinkButtonMoved); document.removeEventListener("pointerup", this.onLinkButtonUp); - DragLinksAsDocuments(this._drag.current, e.x, e.y, SelectionManager.SelectedDocuments()[0].props.Document); + DragLinkAsDocument(this._drag.current, e.x, e.y, this.props.linkDoc, this.props.sourceDoc); } e.stopPropagation(); } -- cgit v1.2.3-70-g09d2 From d91e7eec9a62363b383b929166cdf600b124334c Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 18 Jun 2019 15:09:21 -0400 Subject: links to nodes in different contexts render as a circle --- src/client/util/DocumentManager.ts | 16 ++- .../CollectionFreeFormLinkView.tsx | 97 +++++++++------ .../CollectionFreeFormLinksView.tsx | 135 +++++++++++++++++---- src/client/views/nodes/DocumentView.tsx | 4 +- 4 files changed, 181 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index fc78993b8..85f8bf751 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,7 +1,7 @@ import { computed, observable } from 'mobx'; import { DocumentView } from '../views/nodes/DocumentView'; import { Doc, DocListCast, Opt } from '../../new_fields/Doc'; -import { FieldValue, Cast, NumCast, BoolCast } from '../../new_fields/Types'; +import { FieldValue, Cast, NumCast, BoolCast, StrCast } from '../../new_fields/Types'; import { listSpec } from '../../new_fields/Schema'; import { undoBatch } from './UndoManager'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; @@ -85,22 +85,26 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { 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)); let linksList = LinkManager.Instance.findAllRelatedLinks(dv.props.Document); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { if (link) { let destination = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); if (destination) { - DocumentManager.Instance.getDocumentViews(destination).map(docView1 => - pairs.push({ a: dv, b: docView1, l: link })); + DocumentManager.Instance.getDocumentViews(destination).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 }); + }); } } return pairs; - }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); + }, [] as { anchor1View: DocumentView, anchor2View: DocumentView, linkDoc: Doc }[])); } return pairs; - }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); + }, [] as { anchor1View: DocumentView, anchor2View: DocumentView, linkDoc: Doc }[]); return linked; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index ddde8ece8..36ffac9c8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -5,58 +5,81 @@ import { InkingControl } from "../../InkingControl"; import "./CollectionFreeFormLinkView.scss"; import React = require("react"); import v5 = require("uuid/v5"); +import { DocumentView } from "../../nodes/DocumentView"; export interface CollectionFreeFormLinkViewProps { - A: Doc; - B: Doc; - LinkDocs: Doc[]; - addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; - removeDocument: (document: Doc) => boolean; + // anchor1: Doc; + // anchor2: Doc; + // LinkDocs: Doc[]; + // addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + // removeDocument: (document: Doc) => boolean; + // sameContext: boolean; + + sourceView: DocumentView; + targetView: DocumentView; + sameContext: boolean; } @observer export class CollectionFreeFormLinkView extends React.Component { - 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 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 = ""; - this.props.LinkDocs.map(l => text += StrCast(l.title) + "(" + StrCast(l.linkDescription) + "), "); - text = ""; + // 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.Document.x) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.width) / NumCast(a1.Document.zoomBasis, 1) / 2); + let y1 = NumCast(a1.Document.y) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.height) / NumCast(a1.Document.zoomBasis, 1) / 2); + + let x2 = NumCast(a2.Document.x) + (BoolCast(a2.Document.isMinimized, false) ? 5 : NumCast(a2.Document.width) / NumCast(a2.Document.zoomBasis, 1) / 2); + let y2 = NumCast(a2.Document.y) + (BoolCast(a2.Document.isMinimized, false) ? 5 : NumCast(a2.Document.height) / NumCast(a2.Document.zoomBasis, 1) / 2); + if (!this.props.sameContext) { + x2 = x1 + 300; + y2 = y1 - 300; + } + + // if (!this.props.sameContext) { + // console.log("not same context", StrCast(a1.title), StrCast(a2.title)); + // x2 = x1 + 300; + // y2 = y2 + 300; + // } else { + // console.log("same context", StrCast(a1.title), StrCast(a2.title)); + // } + // let text = ""; + // this.props.LinkDocs.map(l => text += StrCast(l.title) + "(" + StrCast(l.linkDescription) + "), "); + // text = ""; return ( <> - + {!this.props.sameContext ? : <>} {/* */} - + {/* {text} - + */} ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index c4dd534ed..fc92c81d5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -92,40 +92,123 @@ export class CollectionFreeFormLinksView extends React.Component sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document); } - @computed - get uniqueConnections() { - 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 => { - if (!drawnPairs.reduce((found, drawnPair) => { - 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.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) { - drawnPair.l.push(connection.l); - } - return match || found; - }, false)) { - drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] }) + // @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); + + // // 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); + // 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 match = match1 || match2; + // if (match && !drawnPair.linkDocs.reduce((found, link) => found || link[Id] === connection.linkDoc[Id], false)) { + // drawnPair.linkDocs.push(connection.linkDoc); + // } + // return match || found; + // }, false)) { + // drawnPairs.push({ anchor1: possiblePair.anchor1, anchor2: possiblePair.anchor2, linkDocs: [connection.linkDoc] }); + // } + // }); + // return drawnPairs; + // }, [] as { anchor1: Doc, anchor2: Doc, linkDocs: Doc[] }[]); + // return connections.map(c => { + // let x = c.linkDocs.reduce((p, l) => p + l[Id], ""); + // return ; + // }); + // } + + findUniquePairs = (): JSX.Element[] => { + // console.log("FIND UNIQUE PAIRS"); + let connections = DocumentManager.Instance.LinkedDocumentViews; + + let unique: Array<{ sourceView: DocumentView, targetView: DocumentView, sameContext: boolean }> = []; + 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 sameContext = c.anchor1View.props.ContainingCollectionView === c.anchor2View.props.ContainingCollectionView; + + if (!(match1Index > -1 || match2Index > -1)) { + // if docview pair does not already exist in unique, push + unique.push({ sourceView: c.anchor1View, targetView: c.anchor2View, sameContext: sameContext }); + } else { + // if docview pair exists in unique, push if not in same context + if (!sameContext) { + match1Index > -1 ? unique.push({ sourceView: c.anchor2View, targetView: c.anchor1View, sameContext: sameContext }) + : unique.push({ sourceView: c.anchor1View, targetView: c.anchor2View, sameContext: sameContext }); } - }); - return drawnPairs; - }, [] as { a: Doc, b: Doc, l: Doc[] }[]); - return connections.map(c => { - let x = c.l.reduce((p, l) => p + l[Id], ""); - return ; + } + }); + + console.log("\n UNIQUE"); + unique.forEach(u => { + console.log(StrCast(u.sourceView.Document.title), StrCast(u.targetView.Document.title), u.sameContext); + }); + + // console.log("\n"); + + return unique.map(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!.props.Document === this.props.Document; + let targetIn = u.targetView.props.ContainingCollectionView!.props.Document === this.props.Document; + let inContainer = u.sameContext ? sourceIn || targetIn : sourceIn; + if (inContainer) { + // console.log("key", key, StrCast(u.sourceView.Document.title), StrCast(u.targetView.Document.title)); + return ; + } else { + return
; + } }); } render() { + this.findUniquePairs(); return (
- {this.uniqueConnections} + {/* {this.uniqueConnections} */} + {this.findUniquePairs()} {this.props.children}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c998b8ea6..e98392a18 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -319,8 +319,8 @@ export class DocumentView extends DocComponent(Docu this._lastTap = Date.now(); } - deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); } - fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight") }; + deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); }; + fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight"); }; makeBtnClicked = (): void => { let doc = Doc.GetProto(this.props.Document); doc.isButton = !BoolCast(doc.isButton, false); -- cgit v1.2.3-70-g09d2 From 6fcd0d8d6fb1471b8af460f6d80bdf0d0e681566 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 18 Jun 2019 15:17:27 -0400 Subject: added button to delete a link --- src/client/views/nodes/LinkEditor.scss | 21 +++++++++++++++------ src/client/views/nodes/LinkEditor.tsx | 18 +++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index 6aac39f45..154b4abe3 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -10,6 +10,21 @@ margin-bottom: 6px; } +.linKEditor-info { + border-bottom: 0.5px solid $light-color-secondary; + padding-bottom: 6px; + margin-bottom: 6px; + display: flex; + justify-content: space-between; + + .linkEditor-delete { + width: 20px; + height: 20px; + margin-left: 6px; + padding: 0; + } +} + .linkEditor-groupsLabel { display: flex; justify-content: space-between; @@ -23,12 +38,6 @@ } } -.linkEditor-linkedTo { - border-bottom: 0.5px solid $light-color-secondary; - padding-bottom: 6px; - margin-bottom: 6px; -} - .linkEditor-group { background-color: $light-color-secondary; padding: 6px; diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 484682c22..29ead7388 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -7,14 +7,12 @@ import { Doc } from "../../../new_fields/Doc"; import { LinkManager } from "../../util/LinkManager"; import { Docs } from "../../documents/Documents"; import { Utils } from "../../../Utils"; -import { faArrowLeft, faEllipsisV, faTable } from '@fortawesome/free-solid-svg-icons'; +import { faArrowLeft, faEllipsisV, faTable, faTrash } from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SetupDrag } from "../../util/DragManager"; -library.add(faArrowLeft); -library.add(faEllipsisV); -library.add(faTable); +library.add(faArrowLeft, faEllipsisV, faTable, faTrash); interface GroupTypesDropdownProps { @@ -162,6 +160,13 @@ export class LinkEditor extends React.Component { this._groups = groups; } + @action + deleteLink = (): void => { + let index = LinkManager.Instance.allLinks.indexOf(this.props.linkDoc); + LinkManager.Instance.allLinks.splice(index, 1); + this.props.showLinks(); + } + @action addGroup = (): void => { // new group only gets added if there is not already a group with type "new group" @@ -324,7 +329,10 @@ export class LinkEditor extends React.Component { return (
-

editing link to: {destination.proto!.title}

+
+

editing link to: {destination.proto!.title}

+ +
Relationships: -- cgit v1.2.3-70-g09d2 From 8e5afb5bbb47324a381b5184254e77eba7bd8536 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 18 Jun 2019 16:30:24 -0400 Subject: can click on button link to node in different context than source --- .../CollectionFreeFormLinkView.scss | 1 + .../CollectionFreeFormLinkView.tsx | 28 +++++++++++++--------- .../CollectionFreeFormLinksView.tsx | 6 ++--- 3 files changed, 21 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index 7a0fd2b31..fc5212edd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -9,6 +9,7 @@ opacity: 0.5; transform: translate(10000px,10000px); pointer-events: all; + cursor: pointer; } .collectionfreeformlinkview-linkText { stroke: rgb(0,0,0); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 36ffac9c8..4d477454b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -6,6 +6,7 @@ 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; @@ -18,6 +19,7 @@ export interface CollectionFreeFormLinkViewProps { sourceView: DocumentView; targetView: DocumentView; sameContext: boolean; + addDocTab: (document: Doc, where: string) => void; } @observer @@ -42,6 +44,13 @@ export class CollectionFreeFormLinkView extends React.Component { + // TODO: would be nicer to open docview in context + e.stopPropagation(); + console.log("follow"); + this.props.addDocTab(this.props.targetView.props.Document, "onRight"); + } + render() { // let l = this.props.LinkDocs; // let a = this.props.A; @@ -58,23 +67,20 @@ export class CollectionFreeFormLinkView extends React.Component text += StrCast(l.title) + "(" + StrCast(l.linkDescription) + "), "); - // text = ""; + let containing = ""; + if (this.props.targetView.props.ContainingCollectionView) { + containing = StrCast(this.props.targetView.props.ContainingCollectionView.props.Document.title); + } + + let text = "link to " + StrCast(this.props.targetView.props.Document.title) + (containing === "" ? "" : (" in the context of " + containing)); return ( <> - {!this.props.sameContext ? : <>} + {!this.props.sameContext ? : <>} + {!this.props.sameContext ? {text} : <>} {/* */} {/* diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index fc92c81d5..86625863f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -190,12 +190,12 @@ export class CollectionFreeFormLinksView extends React.Component { // 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!.props.Document === this.props.Document; - let targetIn = u.targetView.props.ContainingCollectionView!.props.Document === this.props.Document; + 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 inContainer = u.sameContext ? sourceIn || targetIn : sourceIn; if (inContainer) { // console.log("key", key, StrCast(u.sourceView.Document.title), StrCast(u.targetView.Document.title)); - return ; + return ; } else { return
; } -- cgit v1.2.3-70-g09d2 From f362dbfc237536c6c4a8c6d088c3dc818080f7c2 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 19 Jun 2019 12:50:58 -0400 Subject: both tail ends of a cut link appear on hover/focus of an anchor --- src/client/documents/Documents.ts | 12 ++++- src/client/util/DragManager.ts | 9 ++++ .../CollectionFreeFormLinkView.tsx | 21 +------- .../CollectionFreeFormLinkWithProxyView.tsx | 60 ++++++++++++++++++++++ .../CollectionFreeFormLinksView.tsx | 59 ++++++++++++--------- 5 files changed, 116 insertions(+), 45 deletions(-) create mode 100644 src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 002de8b5f..79ba433c8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -99,8 +99,18 @@ export namespace DocUtils { linkDocProto.anchor2 = target; linkDocProto.anchor2Page = target.curPage; linkDocProto.anchor2Groups = new List([]); + linkDocProto.context = targetContext; + let proxy1 = Docs.TextDocument({ width: 300, height: 100, borderRounding: 0 }); + let proxy1Proto = Doc.GetProto(proxy1); + let proxy2 = Docs.TextDocument({ width: 300, height: 100, borderRounding: 0 }); + let proxy2Proto = Doc.GetProto(proxy2); + + linkDocProto.proxy1 = proxy1; // src: 1 targ: 2 + linkDocProto.proxy2 = proxy2; // src: 2 targ: 1 + + LinkManager.Instance.allLinks.push(linkDoc); return linkDoc; @@ -377,7 +387,7 @@ export namespace Docs { } /* - + this template requires an additional style setting on the collectionView-cont to make the layout relative .collectionView-cont { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 82d30e0e6..4787ac40f 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -45,6 +45,9 @@ export function SetupDrag(_reference: React.RefObject, 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]); DragManager.StartDocumentDrag([dragEle], dragData, x, y, { @@ -58,6 +61,9 @@ export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: num export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) { let srcTarg = sourceDoc.proto; let draggedDocs: Doc[] = []; + + // TODO: if not in same context then don't drag + if (srcTarg) { let linkDocs = LinkManager.Instance.findAllRelatedLinks(srcTarg); if (linkDocs) { @@ -74,6 +80,9 @@ 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.moveDocument = (document, targetCollection, addDocument) => { + // return false; + // }; DragManager.StartDocumentDrag([dragEle], dragData, x, y, { handlers: { dragComplete: action(emptyFunction), diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 4d477454b..f995a35e3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -18,8 +18,6 @@ export interface CollectionFreeFormLinkViewProps { sourceView: DocumentView; targetView: DocumentView; - sameContext: boolean; - addDocTab: (document: Doc, where: string) => void; } @observer @@ -44,12 +42,6 @@ export class CollectionFreeFormLinkView extends React.Component { - // TODO: would be nicer to open docview in context - e.stopPropagation(); - console.log("follow"); - this.props.addDocTab(this.props.targetView.props.Document, "onRight"); - } render() { // let l = this.props.LinkDocs; @@ -62,25 +54,14 @@ export class CollectionFreeFormLinkView extends React.Component - {!this.props.sameContext ? : <>} - {!this.props.sameContext ? {text} : <>} + {/* */} {/* diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx new file mode 100644 index 000000000..81a00ba95 --- /dev/null +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx @@ -0,0 +1,60 @@ +import { observer } from "mobx-react"; +import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc"; +import { BoolCast, NumCast, StrCast } from "../../../../new_fields/Types"; +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"; +import { observable } from "mobx"; + +export interface CollectionFreeFormLinkViewProps { + sourceView: DocumentView; + targetView: DocumentView; + proxyDoc: Doc; + addDocTab: (document: Doc, where: string) => void; +} + +@observer +export class CollectionFreeFormLinkWithProxyView extends React.Component { + + // @observable private _proxyX: number = NumCast(this.props.proxyDoc.x); + // @observable private _proxyY: number = NumCast(this.props.proxyDoc.y); + + followButton = (e: React.PointerEvent): void => { + // TODO: would be nicer to open docview in context + e.stopPropagation(); + console.log("follow"); + this.props.addDocTab(this.props.targetView.props.Document, "onRight"); + } + + render() { + let a1 = this.props.sourceView; + let a2 = this.props.proxyDoc; + let x1 = NumCast(a1.Document.x) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.width) / NumCast(a1.Document.zoomBasis, 1) / 2); + let y1 = NumCast(a1.Document.y) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.height) / NumCast(a1.Document.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 containing = ""; + // if (this.props.targetView.props.ContainingCollectionView) { + // containing = StrCast(this.props.targetView.props.ContainingCollectionView.props.Document.title); + // } + + // let text = "link to " + StrCast(this.props.targetView.props.Document.title) + (containing === "" ? "" : (" in the context of " + containing)); + return ( + <> + + {/* + {text} */} + + ); + } +} + +//onPointerDown={this.followButton} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 86625863f..eaef1f32a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -11,6 +11,8 @@ import { CollectionViewProps } from "../CollectionSubView"; import "./CollectionFreeFormLinksView.scss"; import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); +import { CollectionFreeFormLinkWithProxyView } from "./CollectionFreeFormLinkWithProxyView"; +import { Docs } from "../../../documents/Documents"; @observer export class CollectionFreeFormLinksView extends React.Component { @@ -159,47 +161,56 @@ export class CollectionFreeFormLinksView extends React.Component { - // console.log("FIND UNIQUE PAIRS"); let connections = DocumentManager.Instance.LinkedDocumentViews; - let unique: Array<{ sourceView: DocumentView, targetView: DocumentView, sameContext: boolean }> = []; + 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 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; - if (!(match1Index > -1 || match2Index > -1)) { - // if docview pair does not already exist in unique, push - unique.push({ sourceView: c.anchor1View, targetView: c.anchor2View, sameContext: sameContext }); + // 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 { - // if docview pair exists in unique, push if not in same context - if (!sameContext) { - match1Index > -1 ? unique.push({ sourceView: c.anchor2View, targetView: c.anchor1View, sameContext: sameContext }) - : unique.push({ sourceView: c.anchor1View, targetView: c.anchor2View, sameContext: sameContext }); - } + unique.add({ sourceView: c.anchor1View, targetView: c.anchor2View, linkDoc: c.linkDoc }); + unique.add({ sourceView: c.anchor2View, targetView: c.anchor1View, linkDoc: c.linkDoc }); } }); - console.log("\n UNIQUE"); + let uniqueList: JSX.Element[] = []; unique.forEach(u => { - console.log(StrCast(u.sourceView.Document.title), StrCast(u.targetView.Document.title), u.sameContext); - }); - - // console.log("\n"); - - return unique.map(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 inContainer = u.sameContext ? sourceIn || targetIn : sourceIn; + let sameContext = u.sourceView.props.ContainingCollectionView === u.targetView.props.ContainingCollectionView; + let inContainer = sameContext ? sourceIn || targetIn : sourceIn; + if (inContainer) { - // console.log("key", key, StrCast(u.sourceView.Document.title), StrCast(u.targetView.Document.title)); - return ; - } else { - return
; + // let alias = Doc.MakeAlias(proxy); + if (sameContext) { + uniqueList.push(); + } else { + 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); + + let context = u.targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(u.targetView.props.ContainingCollectionView.props.Document.title)) : ""; + let text = proxyKey + " link to " + StrCast(u.targetView.props.Document.title) + context; + + let proxyProto = Doc.GetProto(proxy); + proxyProto.data = text; + + this.props.addDocument(proxy, false); + uniqueList.push(); + } } }); + return uniqueList; } render() { -- cgit v1.2.3-70-g09d2 From c5e401cb0a7fec2279ceecbc8d1429dcdd2f04b9 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 19 Jun 2019 22:27:21 -0400 Subject: buttons on cut links functional except for when dragged from link menu --- src/client/documents/Documents.ts | 47 +++++++-- src/client/util/DragManager.ts | 7 ++ src/client/util/LinkManager.ts | 15 ++- .../CollectionFreeFormLinkView.scss | 59 ++++++++--- .../CollectionFreeFormLinkView.tsx | 2 +- .../CollectionFreeFormLinkWithProxyView.tsx | 109 +++++++++++++++++---- .../CollectionFreeFormLinksView.tsx | 42 +++++--- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 14 ++- src/client/views/nodes/FieldView.tsx | 6 ++ src/client/views/nodes/LinkBox.scss | 76 -------------- src/client/views/nodes/LinkBox.tsx | 107 -------------------- src/client/views/nodes/LinkButtonBox.scss | 18 ++++ src/client/views/nodes/LinkButtonBox.tsx | 63 ++++++++++++ src/client/views/nodes/LinkMenu.tsx | 4 +- src/client/views/nodes/LinkMenuItem.scss | 76 ++++++++++++++ src/client/views/nodes/LinkMenuItem.tsx | 107 ++++++++++++++++++++ src/new_fields/LinkButtonField.ts | 35 +++++++ 18 files changed, 548 insertions(+), 242 deletions(-) delete mode 100644 src/client/views/nodes/LinkBox.scss delete mode 100644 src/client/views/nodes/LinkBox.tsx create mode 100644 src/client/views/nodes/LinkButtonBox.scss create mode 100644 src/client/views/nodes/LinkButtonBox.tsx create mode 100644 src/client/views/nodes/LinkMenuItem.scss create mode 100644 src/client/views/nodes/LinkMenuItem.tsx create mode 100644 src/new_fields/LinkButtonField.ts (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 79ba433c8..4d4fa2645 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -26,7 +26,7 @@ import { OmitKeys } from "../../Utils"; import { ImageField, VideoField, AudioField, PdfField, WebField } from "../../new_fields/URLField"; import { HtmlField } from "../../new_fields/HtmlField"; import { List } from "../../new_fields/List"; -import { Cast, NumCast } from "../../new_fields/Types"; +import { Cast, NumCast, StrCast } from "../../new_fields/Types"; import { IconField } from "../../new_fields/IconField"; import { listSpec } from "../../new_fields/Schema"; import { DocServer } from "../DocServer"; @@ -36,6 +36,10 @@ import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; +import { LinkButtonBox } from "../views/nodes/LinkButtonBox"; +import { LinkButtonField, LinkButtonData } from "../../new_fields/LinkButtonField"; +import { DocumentManager } from "../util/DocumentManager"; +import { Id } from "../../new_fields/FieldSymbols"; var requestImageSize = require('request-image-size'); var path = require('path'); @@ -102,14 +106,32 @@ export namespace DocUtils { linkDocProto.context = targetContext; - let proxy1 = Docs.TextDocument({ width: 300, height: 100, borderRounding: 0 }); - let proxy1Proto = Doc.GetProto(proxy1); - let proxy2 = Docs.TextDocument({ width: 300, height: 100, borderRounding: 0 }); - let proxy2Proto = Doc.GetProto(proxy2); + let sourceViews = DocumentManager.Instance.getDocumentViews(source); + let targetViews = DocumentManager.Instance.getDocumentViews(target); + sourceViews.forEach(sv => { + targetViews.forEach(tv => { - linkDocProto.proxy1 = proxy1; // src: 1 targ: 2 - linkDocProto.proxy2 = proxy2; // src: 2 targ: 1 + // TODO: do only for when diff contexts + let proxy1 = Docs.LinkButtonDocument( + { sourceViewId: StrCast(sv.props.Document[Id]), targetViewId: StrCast(tv.props.Document[Id]) }, + { width: 200, height: 100, borderRounding: 0 }); + let proxy1Proto = Doc.GetProto(proxy1); + proxy1Proto.sourceViewId = StrCast(sv.props.Document[Id]); + proxy1Proto.targetViewId = StrCast(tv.props.Document[Id]); + proxy1Proto.isLinkButton = true; + let proxy2 = Docs.LinkButtonDocument( + { sourceViewId: StrCast(tv.props.Document[Id]), targetViewId: StrCast(sv.props.Document[Id]) }, + { width: 200, height: 100, borderRounding: 0 }); + let proxy2Proto = Doc.GetProto(proxy2); + proxy2Proto.sourceViewId = StrCast(tv.props.Document[Id]); + proxy2Proto.targetViewId = StrCast(sv.props.Document[Id]); + proxy2Proto.isLinkButton = true; + + LinkManager.Instance.linkProxies.push(proxy1); + LinkManager.Instance.linkProxies.push(proxy2); + }); + }); LinkManager.Instance.allLinks.push(linkDoc); @@ -131,6 +153,7 @@ export namespace Docs { let audioProto: Doc; let pdfProto: Doc; let iconProto: Doc; + let linkProto: Doc; const textProtoId = "textProto"; const histoProtoId = "histoProto"; const pdfProtoId = "pdfProto"; @@ -141,6 +164,7 @@ export namespace Docs { const videoProtoId = "videoProto"; const audioProtoId = "audioProto"; const iconProtoId = "iconProto"; + const linkProtoId = "linkProto"; export function initProtos(): Promise { return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId]).then(fields => { @@ -154,6 +178,7 @@ export namespace Docs { audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); + linkProto = fields[linkProtoId] as Doc || CreateLinkPrototype(); }); } @@ -186,6 +211,11 @@ export namespace Docs { { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }); return iconProto; } + function CreateLinkPrototype(): Doc { + let linkProto = setupPrototypeOptions(linkProtoId, "LINK_PROTO", LinkButtonBox.LayoutString(), + { x: 0, y: 0, width: 300 }); + return linkProto; + } function CreateTextPrototype(): Doc { let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), { x: 0, y: 0, width: 300, backgroundColor: "#f1efeb" }); @@ -272,6 +302,9 @@ export namespace Docs { export function IconDocument(icon: string, options: DocumentOptions = {}) { return CreateInstance(iconProto, new IconField(icon), options); } + export function LinkButtonDocument(data: LinkButtonData, options: DocumentOptions = {}) { + return CreateInstance(linkProto, new LinkButtonField(data), options); + } export function PdfDocument(url: string, options: DocumentOptions = {}) { return CreateInstance(pdfProto, new PdfField(new URL(url)), options); } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 4787ac40f..be00778e7 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -7,6 +7,8 @@ import * as globalCssVariables from "../views/globalCssVariables.scss"; import { LinkManager } from "./LinkManager"; import { URLField } from "../../new_fields/URLField"; import { SelectionManager } from "./SelectionManager"; +import { Docs } from "../documents/Documents"; +import { DocumentManager } from "./DocumentManager"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc | Promise, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, options?: any, dontHideOnDrop?: boolean) { @@ -223,6 +225,11 @@ export namespace DragManager { StartDrag([ele], dragData, downX, downY, options); } + export function StartLinkProxyDrag(ele: HTMLElement, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + runInAction(() => StartDragFunctions.map(func => func())); + StartDrag([ele], dragData, downX, downY, options); + } + export let AbortDrag: () => void = emptyFunction; function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 23ba9d2e4..544f2edda 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,4 +1,4 @@ -import { observable } from "mobx"; +import { observable, action } from "mobx"; import { StrCast, Cast } from "../../new_fields/Types"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; @@ -32,6 +32,7 @@ export class LinkManager { @observable public allLinks: Array = []; // list of link docs @observable public groupMetadataKeys: Map> = new Map(); // map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it hodls + @observable public linkProxies: Array = []; // list of linkbutton docs - used to visualize link when an anchors are not in the same context // finds all links that contain the given anchor public findAllRelatedLinks(anchor: Doc): Array { @@ -134,4 +135,16 @@ export class LinkManager { } } + @action + public addLinkProxy(proxy: Doc) { + LinkManager.Instance.linkProxies.push(proxy); + } + + public findLinkProxy(sourceViewId: string, targetViewId: string): Doc | undefined { + let index = LinkManager.Instance.linkProxies.findIndex(p => { + return StrCast(p.sourceViewId) === sourceViewId && StrCast(p.targetViewId) === targetViewId; + }); + return index > -1 ? LinkManager.Instance.linkProxies[index] : undefined; + } + } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index fc5212edd..d8d518147 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -1,19 +1,50 @@ -.collectionfreeformlinkview-linkLine { - stroke: black; +// .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); - opacity: 0.5; pointer-events: all; + + &.linkview-line { + stroke: black; + stroke-width: 2px; + opacity: 0.5; + } } -.collectionfreeformlinkview-linkCircle { - stroke: rgb(0,0,0); - opacity: 0.5; - transform: translate(10000px,10000px); - pointer-events: all; + +.linkview-button { + width: 200px; + height: 100px; + border-radius: 5px; + padding: 10px; + position: relative; + background-color: black; cursor: pointer; -} -.collectionfreeformlinkview-linkText { - stroke: rgb(0,0,0); - opacity: 0.5; - transform: translate(10000px,10000px); - pointer-events: all; + + p { + width: calc(100% - 20px); + color: white; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index f995a35e3..13b5dc575 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -57,7 +57,7 @@ export class CollectionFreeFormLinkView extends React.Component - diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx index 81a00ba95..a4d122af2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx @@ -7,13 +7,17 @@ import React = require("react"); import v5 = require("uuid/v5"); import { DocumentView } from "../../nodes/DocumentView"; import { Docs } from "../../../documents/Documents"; -import { observable } from "mobx"; +import { observable, action } from "mobx"; +import { CollectionDockingView } from "../CollectionDockingView"; +import { dropActionType, DragManager } from "../../../util/DragManager"; +import { emptyFunction } from "../../../../Utils"; +import { DocumentManager } from "../../../util/DocumentManager"; export interface CollectionFreeFormLinkViewProps { sourceView: DocumentView; targetView: DocumentView; proxyDoc: Doc; - addDocTab: (document: Doc, where: string) => void; + // addDocTab: (document: Doc, where: string) => void; } @observer @@ -21,37 +25,104 @@ export class CollectionFreeFormLinkWithProxyView extends React.Component(); + private _downX: number = 0; + private _downY: number = 0; + @observable _x: number = 0; + @observable _y: number = 0; + // @observable private _proxyDoc: Doc = Docs.TextDocument(); // used for positioning + + @action + componentDidMount() { + let a2 = this.props.proxyDoc; + this._x = NumCast(a2.x) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2); + this._y = NumCast(a2.y) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2); + } + followButton = (e: React.PointerEvent): void => { - // TODO: would be nicer to open docview in context e.stopPropagation(); - console.log("follow"); - this.props.addDocTab(this.props.targetView.props.Document, "onRight"); + let open = this.props.targetView.props.ContainingCollectionView ? this.props.targetView.props.ContainingCollectionView.props.Document : this.props.targetView.props.Document; + CollectionDockingView.Instance.AddRightSplit(open); + DocumentManager.Instance.jumpToDocument(this.props.targetView.props.Document, e.altKey); + } + + @action + setPosition(x: number, y: number) { + this._x = x; + this._y = y; + } + + startDragging(x: number, y: number) { + if (this._ref.current) { + let dragData = new DragManager.DocumentDragData([this.props.proxyDoc]); + + DragManager.StartLinkProxyDrag(this._ref.current, dragData, x, y, { + handlers: { + dragComplete: action(() => { + let a2 = this.props.proxyDoc; + let offset = NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2; + let x = NumCast(a2.x);// + NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2; + let y = NumCast(a2.y);// + NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2; + this.setPosition(x, y); + + // this is a hack :'( theres prob a better way to make the input doc not render + let views = DocumentManager.Instance.getDocumentViews(this.props.proxyDoc); + views.forEach(dv => { + dv.props.removeDocument && dv.props.removeDocument(dv.props.Document); + }); + }), + }, + hideSource: true //? + }); + } + } + + onPointerDown = (e: React.PointerEvent): void => { + this._downX = e.clientX; + this._downY = e.clientY; + + e.stopPropagation(); + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + } + + onPointerMove = (e: PointerEvent): void => { + if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + this.startDragging(this._downX, this._downY); + } + e.stopPropagation(); + e.preventDefault(); + } + onPointerUp = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); } render() { let a1 = this.props.sourceView; - let a2 = this.props.proxyDoc; let x1 = NumCast(a1.Document.x) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.width) / NumCast(a1.Document.zoomBasis, 1) / 2); let y1 = NumCast(a1.Document.y) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.height) / NumCast(a1.Document.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 containing = ""; - // if (this.props.targetView.props.ContainingCollectionView) { - // containing = StrCast(this.props.targetView.props.ContainingCollectionView.props.Document.title); - // } + let context = this.props.targetView.props.ContainingCollectionView ? + (" in the context of " + StrCast(this.props.targetView.props.ContainingCollectionView.props.Document.title)) : ""; + let text = "link to " + StrCast(this.props.targetView.props.Document.title) + context; - // let text = "link to " + StrCast(this.props.targetView.props.Document.title) + (containing === "" ? "" : (" in the context of " + containing)); return ( <> - - {/* - {text} */} + x2={`${this._x}`} y2={`${this._y}`} /> + +
+

{text}

+
+
); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index eaef1f32a..bde68001b 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 } from "mobx"; +import { computed, IReactionDisposer, reaction, action } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; @@ -13,6 +13,8 @@ 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 { @@ -195,18 +197,32 @@ export class CollectionFreeFormLinksView extends React.Component); } else { - 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); - - let context = u.targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(u.targetView.props.ContainingCollectionView.props.Document.title)) : ""; - let text = proxyKey + " link to " + StrCast(u.targetView.props.Document.title) + context; - - let proxyProto = Doc.GetProto(proxy); - proxyProto.data = text; - - this.props.addDocument(proxy, false); - uniqueList.push(); + 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(); + + // 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(); + // } + // 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(); } } }); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 02396c3af..940b00a90 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -12,6 +12,7 @@ import "./DocumentView.scss"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; import { IconBox } from "./IconBox"; +import { LinkButtonBox } from "./LinkButtonBox"; import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { VideoBox } from "./VideoBox"; @@ -103,7 +104,7 @@ export class DocumentContentsView extends React.Component(Docu var scaling = this.props.ContentScaling(); var nativeHeight = this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + + // // for linkbutton docs + // let isLinkButton = BoolCast(this.props.Document.isLinkButton); + // let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); + // let display = isLinkButton ? activeDvs.reduce((found, dv) => { + // let matchSv = this.props.Document.sourceViewId === StrCast(dv.props.Document[Id]); + // let matchTv = this.props.Document.targetViewId === StrCast(dv.props.Document[Id]); + // let match = matchSv || matchTv; + // return match || found; + // }, false) : true; + return (
(Docu background: this.Document.backgroundColor || "", width: nativeWidth, height: nativeHeight, - transform: `scale(${scaling}, ${scaling})` + transform: `scale(${scaling}, ${scaling})`, + // display: display ? "block" : "none" }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 5a83de8e3..6fecb34d7 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -18,6 +18,8 @@ import { FormattedTextBox } from "./FormattedTextBox"; import { IconBox } from "./IconBox"; import { ImageBox } from "./ImageBox"; import { VideoBox } from "./VideoBox"; +import { LinkButtonBox } from "./LinkButtonBox"; +import { LinkButtonField } from "../../../new_fields/LinkButtonField"; // @@ -49,6 +51,7 @@ export interface FieldViewProps { @observer export class FieldView extends React.Component { public static LayoutString(fieldType: { name: string }, fieldStr: string = "data") { + console.log("LAYOUT STRING", fieldType.name, fieldStr); return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"} />`; } @@ -74,6 +77,9 @@ export class FieldView extends React.Component { else if (field instanceof IconField) { return ; } + else if (field instanceof LinkButtonField) { + return ; + } else if (field instanceof VideoField) { return ; } diff --git a/src/client/views/nodes/LinkBox.scss b/src/client/views/nodes/LinkBox.scss deleted file mode 100644 index 77462f611..000000000 --- a/src/client/views/nodes/LinkBox.scss +++ /dev/null @@ -1,76 +0,0 @@ -@import "../globalCssVariables"; -.linkMenu-item { - border-top: 0.5px solid $main-accent; - padding: 6px; - position: relative; - display: flex; - font-size: 12px; - - .linkMenu-item-content { - width: 100%; - } - - .link-metadata { - margin-top: 6px; - padding: 3px; - border-top: 0.5px solid $light-color-secondary; - .link-metadata-row { - margin-left: 6px; - } - } - - &:last-child { - border-bottom: 0.5px solid $main-accent; - } - &:hover { - .linkMenu-item-buttons { - display: flex; - } - .linkMenu-item-content { - &.expand-two { - width: calc(100% - 46px); - } - &.expand-three { - width: calc(100% - 78px); - } - } - } -} - -.linkMenu-item-buttons { - display: none; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - - .button { - width: 20px; - height: 20px; - margin: 0; - margin-right: 6px; - border-radius: 50%; - cursor: pointer; - pointer-events: auto; - background-color: $dark-color; - color: $light-color; - font-size: 65%; - transition: transform 0.2s; - text-align: center; - position: relative; - - .fa-icon { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - - &:last-child { - margin-right: 0; - } - &:hover { - background: $main-accent; - } - } -} \ No newline at end of file diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx deleted file mode 100644 index 8d07547ed..000000000 --- a/src/client/views/nodes/LinkBox.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEdit, faEye, faTimes, faArrowRight, faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { observer } from "mobx-react"; -import { DocumentManager } from "../../util/DocumentManager"; -import { undoBatch } from "../../util/UndoManager"; -import './LinkBox.scss'; -import React = require("react"); -import { Doc } from '../../../new_fields/Doc'; -import { StrCast, Cast } from '../../../new_fields/Types'; -import { observable, action } from 'mobx'; -import { LinkManager } from '../../util/LinkManager'; -import { DragLinksAsDocuments, DragLinkAsDocument } from '../../util/DragManager'; -import { SelectionManager } from '../../util/SelectionManager'; -library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); - - -interface Props { - groupType: string; - linkDoc: Doc; - sourceDoc: Doc; - destinationDoc: Doc; - showEditor: () => void; -} - -@observer -export class LinkBox extends React.Component { - private _drag = React.createRef(); - @observable private _showMore: boolean = false; - @action toggleShowMore() { this._showMore = !this._showMore; } - - @undoBatch - onFollowLink = async (e: React.PointerEvent): Promise => { - e.stopPropagation(); - DocumentManager.Instance.jumpToDocument(this.props.destinationDoc, e.altKey); - } - - onEdit = (e: React.PointerEvent): void => { - e.stopPropagation(); - this.props.showEditor(); - } - - renderMetadata = (): JSX.Element => { - let groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); - let index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase()); - let groupDoc = index > -1 ? groups[index] : undefined; - - let mdRows: Array = []; - if (groupDoc) { - let mdDoc = Cast(groupDoc.metadata, Doc, new Doc); - let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); - mdRows = keys!.map(key => { - return (
{key}: {StrCast(mdDoc[key])}
); - }); - } - - return (
{mdRows}
); - } - - 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); - - DragLinkAsDocument(this._drag.current, e.x, e.y, this.props.linkDoc, this.props.sourceDoc); - } - e.stopPropagation(); - } - - render() { - - let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); - let canExpand = keys ? keys.length > 0 : false; - - return ( -
-
-
-

{StrCast(this.props.destinationDoc.title)}

-
- {canExpand ?
this.toggleShowMore()}> -
: <>} -
-
-
-
- {this._showMore ? this.renderMetadata() : <>} -
- -
- ); - } -} \ No newline at end of file diff --git a/src/client/views/nodes/LinkButtonBox.scss b/src/client/views/nodes/LinkButtonBox.scss new file mode 100644 index 000000000..24bfd2c9f --- /dev/null +++ b/src/client/views/nodes/LinkButtonBox.scss @@ -0,0 +1,18 @@ +.linkBox-cont { + width: 200px; + height: 100px; + background-color: black; + text-align: center; + color: white; + padding: 10px; + border-radius: 5px; + position: relative; + + .linkBox-cont-wrapper { + width: calc(100% - 20px); + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + } +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkButtonBox.tsx b/src/client/views/nodes/LinkButtonBox.tsx new file mode 100644 index 000000000..8a7c1ed8b --- /dev/null +++ b/src/client/views/nodes/LinkButtonBox.tsx @@ -0,0 +1,63 @@ +import React = require("react"); +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { computed, observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { FieldView, FieldViewProps } from './FieldView'; +import "./LinkButtonBox.scss"; +import { DocumentView } from "./DocumentView"; +import { Doc } from "../../../new_fields/Doc"; +import { LinkButtonField } from "../../../new_fields/LinkButtonField"; +import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { DocumentManager } from "../../util/DocumentManager"; +import { Id } from "../../../new_fields/FieldSymbols"; + +library.add(faCaretUp); +library.add(faObjectGroup); +library.add(faStickyNote); +library.add(faFilePdf); +library.add(faFilm); + +@observer +export class LinkButtonBox extends React.Component { + public static LayoutString() { return FieldView.LayoutString(LinkButtonBox); } + + followLink = (): void => { + console.log("follow link???"); + let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); + let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); + if (targetView && targetView.props.ContainingCollectionView) { + CollectionDockingView.Instance.AddRightSplit(targetView.props.ContainingCollectionView.props.Document); + } + } + + render() { + + let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); + let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); + + let text = "Could not find link"; + if (targetView) { + let context = targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(targetView.props.ContainingCollectionView.props.Document.title)) : ""; + text = "Link to " + StrCast(targetView.props.Document.title) + context; + } + + let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); + let display = activeDvs.reduce((found, dv) => { + let matchSv = field.data.sourceViewId === StrCast(dv.props.Document[Id]); + let matchTv = field.data.targetViewId === StrCast(dv.props.Document[Id]); + let match = matchSv || matchTv; + return match || found; + }, false); + + return ( +
+
+

{text}

+
+
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index aef56df67..7e4c15312 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -1,7 +1,7 @@ import { action, observable } from "mobx"; import { observer } from "mobx-react"; import { DocumentView } from "./DocumentView"; -import { LinkBox } from "./LinkBox"; +import { LinkMenuItem } from "./LinkMenuItem"; import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); @@ -23,7 +23,7 @@ export class LinkMenu extends React.Component { let source = this.props.docView.Document; return group.map(linkDoc => { let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, source); - return this._editingLink = linkDoc)} />; + return this._editingLink = linkDoc)} />; }); } diff --git a/src/client/views/nodes/LinkMenuItem.scss b/src/client/views/nodes/LinkMenuItem.scss new file mode 100644 index 000000000..77462f611 --- /dev/null +++ b/src/client/views/nodes/LinkMenuItem.scss @@ -0,0 +1,76 @@ +@import "../globalCssVariables"; +.linkMenu-item { + border-top: 0.5px solid $main-accent; + padding: 6px; + position: relative; + display: flex; + font-size: 12px; + + .linkMenu-item-content { + width: 100%; + } + + .link-metadata { + margin-top: 6px; + padding: 3px; + border-top: 0.5px solid $light-color-secondary; + .link-metadata-row { + margin-left: 6px; + } + } + + &:last-child { + border-bottom: 0.5px solid $main-accent; + } + &:hover { + .linkMenu-item-buttons { + display: flex; + } + .linkMenu-item-content { + &.expand-two { + width: calc(100% - 46px); + } + &.expand-three { + width: calc(100% - 78px); + } + } + } +} + +.linkMenu-item-buttons { + display: none; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + + .button { + width: 20px; + height: 20px; + margin: 0; + margin-right: 6px; + border-radius: 50%; + cursor: pointer; + pointer-events: auto; + background-color: $dark-color; + color: $light-color; + font-size: 65%; + transition: transform 0.2s; + text-align: center; + position: relative; + + .fa-icon { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + &:last-child { + margin-right: 0; + } + &:hover { + background: $main-accent; + } + } +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx new file mode 100644 index 000000000..c68365584 --- /dev/null +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -0,0 +1,107 @@ +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faEdit, faEye, faTimes, faArrowRight, faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { observer } from "mobx-react"; +import { DocumentManager } from "../../util/DocumentManager"; +import { undoBatch } from "../../util/UndoManager"; +import './LinkMenuItem.scss'; +import React = require("react"); +import { Doc } from '../../../new_fields/Doc'; +import { StrCast, Cast } from '../../../new_fields/Types'; +import { observable, action } from 'mobx'; +import { LinkManager } from '../../util/LinkManager'; +import { DragLinksAsDocuments, DragLinkAsDocument } from '../../util/DragManager'; +import { SelectionManager } from '../../util/SelectionManager'; +library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); + + +interface LinkMenuItemProps { + groupType: string; + linkDoc: Doc; + sourceDoc: Doc; + destinationDoc: Doc; + showEditor: () => void; +} + +@observer +export class LinkMenuItem extends React.Component { + private _drag = React.createRef(); + @observable private _showMore: boolean = false; + @action toggleShowMore() { this._showMore = !this._showMore; } + + @undoBatch + onFollowLink = async (e: React.PointerEvent): Promise => { + e.stopPropagation(); + DocumentManager.Instance.jumpToDocument(this.props.destinationDoc, e.altKey); + } + + onEdit = (e: React.PointerEvent): void => { + e.stopPropagation(); + this.props.showEditor(); + } + + renderMetadata = (): JSX.Element => { + let groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); + let index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase()); + let groupDoc = index > -1 ? groups[index] : undefined; + + let mdRows: Array = []; + if (groupDoc) { + let mdDoc = Cast(groupDoc.metadata, Doc, new Doc); + let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); + mdRows = keys!.map(key => { + return (
{key}: {StrCast(mdDoc[key])}
); + }); + } + + return (
{mdRows}
); + } + + 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); + + DragLinkAsDocument(this._drag.current, e.x, e.y, this.props.linkDoc, this.props.sourceDoc); + } + e.stopPropagation(); + } + + render() { + + let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); + let canExpand = keys ? keys.length > 0 : false; + + return ( +
+
+
+

{StrCast(this.props.destinationDoc.title)}

+
+ {canExpand ?
this.toggleShowMore()}> +
: <>} +
+
+
+
+ {this._showMore ? this.renderMetadata() : <>} +
+ +
+ ); + } +} \ No newline at end of file diff --git a/src/new_fields/LinkButtonField.ts b/src/new_fields/LinkButtonField.ts new file mode 100644 index 000000000..92e1ed922 --- /dev/null +++ b/src/new_fields/LinkButtonField.ts @@ -0,0 +1,35 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, primitive, createSimpleSchema, object } from "serializr"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString } from "./FieldSymbols"; +import { Doc } from "./Doc"; +import { DocumentView } from "../client/views/nodes/DocumentView"; + +export type LinkButtonData = { + sourceViewId: string, + targetViewId: string +}; + +const LinkButtonSchema = createSimpleSchema({ + sourceViewId: true, + targetViewId: true +}); + +@Deserializable("linkButton") +export class LinkButtonField extends ObjectField { + @serializable(object(LinkButtonSchema)) + readonly data: LinkButtonData; + + constructor(data: LinkButtonData) { + super(); + this.data = data; + } + + [Copy]() { + return new LinkButtonField(this.data); + } + + [ToScriptString]() { + return "invalid"; + } +} -- cgit v1.2.3-70-g09d2 From 73f03785f938542a91b28b35043f2feda2bc1432 Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 20 Jun 2019 11:26:33 -0400 Subject: merge --- .../collections/collectionFreeForm/CollectionFreeFormLinksView.tsx | 4 ++++ src/client/views/nodes/DocumentView.tsx | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index bde68001b..3c2ccecfc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -167,12 +167,16 @@ export class CollectionFreeFormLinksView extends React.Component = 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) { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c424384a4..1d74b47a1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -447,6 +447,11 @@ export class DocumentView extends DocComponent(Docu onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; }; onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; }; + onDragOver = (e: React.DragEvent): void => { + this.props.Document.libraryBrush = true; + console.log("dragOver"); + }; + onDragLeave = (e: React.DragEvent): void => { this.props.Document.libraryBrush = false; }; isSelected = () => SelectionManager.IsSelected(this); @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); } @@ -491,7 +496,7 @@ export class DocumentView extends DocComponent(Docu // display: display ? "block" : "none" }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} - + onDragOver={this.onDragOver} onDragLeave={this.onDragLeave} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} > {this.contents} -- cgit v1.2.3-70-g09d2 From a0246ef84396545f79fc4a8b21de1a56cbf06aca Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 20 Jun 2019 11:34:28 -0400 Subject: merge --- .../collections/collectionFreeForm/CollectionFreeFormLinksView.tsx | 6 ------ src/client/views/nodes/DocumentView.tsx | 6 ------ 2 files changed, 12 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 211c90c29..3c2ccecfc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -214,7 +214,6 @@ export class CollectionFreeFormLinksView extends React.Component); // let proxy = LinkManager.Instance.findLinkProxy(StrCast(u.sourceView.props.Document[Id]), StrCast(u.targetView.props.Document[Id])); @@ -228,11 +227,6 @@ export class CollectionFreeFormLinksView extends React.Component); -======= - return match || found; - }, false)) { - drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] }); ->>>>>>> e9d62f4ca0dbeb57e46239047041a8a04da7b504 } } }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1d87205ab..dbbae2d59 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -31,12 +31,9 @@ import "./DocumentView.scss"; import React = require("react"); import { Id, Copy } from '../../../new_fields/FieldSymbols'; import { ContextMenuProps } from '../ContextMenuItem'; -<<<<<<< HEAD import { list, object, createSimpleSchema } from 'serializr'; import { LinkManager } from '../../util/LinkManager'; -======= import { RouteStore } from '../../../server/RouteStore'; ->>>>>>> e9d62f4ca0dbeb57e46239047041a8a04da7b504 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(fa.faTrash); @@ -556,7 +553,6 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; -<<<<<<< HEAD // // for linkbutton docs // let isLinkButton = BoolCast(this.props.Document.isLinkButton); @@ -568,9 +564,7 @@ export class DocumentView extends DocComponent(Docu // return match || found; // }, false) : true; -======= var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; ->>>>>>> e9d62f4ca0dbeb57e46239047041a8a04da7b504 return (
Date: Thu, 20 Jun 2019 12:56:13 -0400 Subject: styling of link proxy --- src/client/util/DocumentManager.ts | 17 +++++++++++++---- .../collectionFreeForm/CollectionFreeFormLinkView.scss | 6 ++++-- 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index bb87d09ec..84f4d7fa2 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -89,17 +89,26 @@ export class DocumentManager { let linksList = LinkManager.Instance.findAllRelatedLinks(dv.props.Document); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { - if (link) { - let destination = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); - if (destination) { - DocumentManager.Instance.getDocumentViews(destination).map(docView1 => { + // 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"); } } + // } return pairs; }, [] as { anchor1View: DocumentView, anchor2View: DocumentView, linkDoc: Doc }[])); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index d8d518147..0cd4338e5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -33,14 +33,16 @@ width: 200px; height: 100px; border-radius: 5px; + border: 2px solid black; padding: 10px; position: relative; - background-color: black; + background-color: white; + text-align: center; cursor: pointer; p { width: calc(100% - 20px); - color: white; + color: black; position: absolute; top: 50%; left: 50%; -- cgit v1.2.3-70-g09d2 From e6ebed17e6ddb2ccee81d65fcb451a9b54302762 Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 20 Jun 2019 17:12:48 -0400 Subject: links can be made from freeform view to treeview --- src/client/views/collections/CollectionTreeView.tsx | 8 +++++++- .../collectionFreeForm/CollectionFreeFormLinkView.scss | 6 ++---- .../collections/collectionFreeForm/CollectionFreeFormLinkView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormLinksView.tsx | 3 +++ src/client/views/nodes/LinkMenuItem.scss | 8 ++++++-- 5 files changed, 19 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c51c16883..ba2dac1c3 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -9,7 +9,7 @@ import { List } from '../../../new_fields/List'; import { Document, listSpec } from '../../../new_fields/Schema'; import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types'; import { emptyFunction, Utils } from '../../../Utils'; -import { Docs } from '../../documents/Documents'; +import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; import { SelectionManager } from '../../util/SelectionManager'; @@ -232,6 +232,12 @@ class TreeView extends React.Component { let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; let inside = x[0] > bounds[0] + 75 || (!before && !this._collapsed); + if (de.data instanceof DragManager.LinkDragData) { + let sourceDoc = de.data.linkSourceDocument; + let destDoc = this.props.document; + DocUtils.MakeLink(sourceDoc, destDoc); + e.stopPropagation(); + } if (de.data instanceof DragManager.DocumentDragData) { let addDoc = (doc: Doc) => this.props.addDocument(doc, this.props.document, before); if (inside) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index 0cd4338e5..d8d518147 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -33,16 +33,14 @@ width: 200px; height: 100px; border-radius: 5px; - border: 2px solid black; padding: 10px; position: relative; - background-color: white; - text-align: center; + background-color: black; cursor: pointer; p { width: calc(100% - 20px); - color: black; + color: white; position: absolute; top: 50%; left: 50%; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 13b5dc575..ca55b0ff0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -58,7 +58,7 @@ export class CollectionFreeFormLinkView extends React.Component diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 3c2ccecfc..6868e8187 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -165,6 +165,9 @@ export class CollectionFreeFormLinksView extends React.Component { 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 => { diff --git a/src/client/views/nodes/LinkMenuItem.scss b/src/client/views/nodes/LinkMenuItem.scss index 77462f611..16a1df331 100644 --- a/src/client/views/nodes/LinkMenuItem.scss +++ b/src/client/views/nodes/LinkMenuItem.scss @@ -6,6 +6,10 @@ display: flex; font-size: 12px; + .link-name { + position: relative; + } + .linkMenu-item-content { width: 100%; } @@ -27,10 +31,10 @@ display: flex; } .linkMenu-item-content { - &.expand-two { + &.expand-two p { width: calc(100% - 46px); } - &.expand-three { + &.expand-three p { width: calc(100% - 78px); } } -- cgit v1.2.3-70-g09d2 From fbfe9faca199b6dedd6844f1fa20cc02060a3c5a Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 20 Jun 2019 18:25:49 -0400 Subject: can see what docs are linked to in treeview: --- .../views/collections/CollectionTreeView.scss | 6 ++++++ .../views/collections/CollectionTreeView.tsx | 25 ++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index a85604e58..e5611ba03 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -59,6 +59,12 @@ background: lightgray; } +.collectionTreeView-subtitle { + font-style: italic; + font-size: 8pt; + color: $intermediate-color; +} + .docContainer { position: relative; text-overflow: ellipsis; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ba2dac1c3..0b922b3c4 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -25,6 +25,7 @@ import { CollectionSchemaPreview } from './CollectionSchemaView'; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); +import { LinkManager } from '../../util/LinkManager'; export interface TreeViewProps { @@ -166,6 +167,7 @@ class TreeView extends React.Component { keyList.push(key); } }); + if (LinkManager.Instance.findAllRelatedLinks(this.props.document).length > 0) keyList.push("links"); if (keyList.indexOf("data") !== -1) { keyList.splice(keyList.indexOf("data"), 1); } @@ -275,6 +277,24 @@ class TreeView extends React.Component { return finalXf; } + renderLinks = () => { + let ele: JSX.Element[] = []; + let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); + let groups = LinkManager.Instance.findRelatedGroupedLinks(this.props.document); + groups.forEach((groupLinkDocs, groupType) => { + let destLinks = groupLinkDocs.map(d => LinkManager.Instance.findOppositeAnchor(d, this.props.document)); + ele.push( +
+
{groupType}:
+ {TreeView.GetChildElements(destLinks, this.props.treeViewId, "treeviewlink-" + groupType, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)} +
+ ); + }); + return ele; + } + render() { let contentElement: (JSX.Element | null) = null; let docList = Cast(this.props.document[this._chosenKey], listSpec(Doc)); @@ -285,8 +305,9 @@ class TreeView extends React.Component { if (!this._collapsed) { if (!this.props.document.embed) { contentElement =
    - {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this._chosenKey, addDoc, remDoc, this.move, - this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)} + {this._chosenKey === "links" ? this.renderLinks() : + TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this._chosenKey, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)}
; } else { contentElement =
-- cgit v1.2.3-70-g09d2 From b1a2871fcca57ce934b8613b315a08eede188669 Mon Sep 17 00:00:00 2001 From: Fawn Date: Thu, 20 Jun 2019 19:03:16 -0400 Subject: link menu styling --- src/client/views/nodes/LinkMenu.scss | 13 +++++++++++++ src/client/views/nodes/LinkMenu.tsx | 6 +++--- src/client/views/nodes/LinkMenuItem.scss | 30 +++++++++++++++++------------- 3 files changed, 33 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index 09a830c9e..d2e411c3d 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -10,5 +10,18 @@ overflow-y: scroll; } +.linkMenu-group { + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + + .linkMenu-group-name { + border-bottom: 0.5px solid lightgray; + margin-bottom: 4px; + } +} + diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 7e4c15312..8aca130a5 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -31,9 +31,9 @@ export class LinkMenu extends React.Component { let linkItems: Array = []; groups.forEach((group, groupType) => { linkItems.push( -
-

{groupType}:

-
+
+

{groupType}:

+
{this.renderGroup(group, groupType)}
diff --git a/src/client/views/nodes/LinkMenuItem.scss b/src/client/views/nodes/LinkMenuItem.scss index 16a1df331..25d167231 100644 --- a/src/client/views/nodes/LinkMenuItem.scss +++ b/src/client/views/nodes/LinkMenuItem.scss @@ -1,13 +1,19 @@ @import "../globalCssVariables"; .linkMenu-item { - border-top: 0.5px solid $main-accent; - padding: 6px; + // border-top: 0.5px solid $main-accent; position: relative; display: flex; font-size: 12px; + .link-name { position: relative; + + p { + padding: 4px 6px; + line-height: 12px; + border-radius: 5px; + } } .linkMenu-item-content { @@ -15,27 +21,25 @@ } .link-metadata { - margin-top: 6px; - padding: 3px; - border-top: 0.5px solid $light-color-secondary; - .link-metadata-row { - margin-left: 6px; - } + padding: 0 10px 0 16px; + margin-bottom: 4px; + color: $main-accent; + font-style: italic; + font-size: 10.5px; } - &:last-child { - border-bottom: 0.5px solid $main-accent; - } &:hover { .linkMenu-item-buttons { display: flex; } .linkMenu-item-content { &.expand-two p { - width: calc(100% - 46px); + width: calc(100% - 52px); + background-color: lightgray; } &.expand-three p { - width: calc(100% - 78px); + width: calc(100% - 84px); + background-color: lightgray; } } } -- cgit v1.2.3-70-g09d2 From d78c651322ad228152b862eaa378946fe65cc9f9 Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 21 Jun 2019 13:32:23 -0400 Subject: dragged links from menu are aliases --- src/client/util/DocumentManager.ts | 13 + src/client/util/DragManager.ts | 12 +- .../CollectionFreeFormLinkView.tsx | 14 +- .../CollectionFreeFormLinksView.tsx | 267 ++++++++++----------- 4 files changed, 163 insertions(+), 143 deletions(-) (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 84f4d7fa2..325f4894d 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -10,6 +10,7 @@ import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; import { Id } from '../../new_fields/FieldSymbols'; import { LinkManager } from './LinkManager'; +import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; export class DocumentManager { @@ -84,6 +85,18 @@ 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)); let linksList = LinkManager.Instance.findAllRelatedLinks(dv.props.Document); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 01193cab5..78cae4ff7 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,6 +1,6 @@ import { action, runInAction, observable } from "mobx"; import { Doc, DocListCastAsync } from "../../new_fields/Doc"; -import { Cast } from "../../new_fields/Types"; +import { Cast, StrCast } from "../../new_fields/Types"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import * as globalCssVariables from "../views/globalCssVariables.scss"; @@ -52,6 +52,7 @@ export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: num 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, { handlers: { dragComplete: action(emptyFunction), @@ -82,6 +83,7 @@ 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; // }; @@ -201,14 +203,18 @@ export namespace DragManager { export let StartDragFunctions: (() => void)[] = []; export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + console.log("outside", dragData.userDropAction, dragData.dropAction); runInAction(() => StartDragFunctions.map(func => func())); StartDrag(eles, dragData, downX, downY, options, - (dropData: { [id: string]: any }) => + (dropData: { [id: string]: any }) => { + console.log("DRAG", dragData.userDropAction, dragData.dropAction); (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)); + dragData.draggedDocuments + ); + }); } export function StartAnnotationDrag(eles: HTMLElement[], dragData: AnnotationDragData, downX: number, downY: number, options?: DragOptions) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index ca55b0ff0..65d5ac474 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -16,8 +16,10 @@ export interface CollectionFreeFormLinkViewProps { // removeDocument: (document: Doc) => boolean; // sameContext: boolean; - sourceView: DocumentView; - targetView: DocumentView; + // sourceView: DocumentView; + // targetView: DocumentView; + sourceView: Doc; + targetView: Doc; } @observer @@ -49,11 +51,11 @@ export class CollectionFreeFormLinkView extends React.Component diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 6868e8187..9d2f8946b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -96,153 +96,152 @@ export class CollectionFreeFormLinksView extends React.Component sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document); } - // @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); - - // // 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); - // 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 match = match1 || match2; - // if (match && !drawnPair.linkDocs.reduce((found, link) => found || link[Id] === connection.linkDoc[Id], false)) { - // drawnPair.linkDocs.push(connection.linkDoc); - // } - // return match || found; - // }, false)) { - // drawnPairs.push({ anchor1: possiblePair.anchor1, anchor2: possiblePair.anchor2, linkDocs: [connection.linkDoc] }); - // } - // }); - // return drawnPairs; - // }, [] as { anchor1: Doc, anchor2: Doc, linkDocs: Doc[] }[]); - // return connections.map(c => { - // let x = c.linkDocs.reduce((p, l) => p + l[Id], ""); - // return ; - // }); - // } + @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); + + // 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); + 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 match = match1 || match2; + if (match && !drawnPair.linkDocs.reduce((found, link) => found || link[Id] === connection.linkDoc[Id], false)) { + drawnPair.linkDocs.push(connection.linkDoc); + } + return match || found; + }, false)) { + drawnPairs.push({ anchor1: possiblePair.anchor1, anchor2: possiblePair.anchor2, linkDocs: [connection.linkDoc] }); + } + }); + return drawnPairs; + }, [] as { anchor1: Doc, anchor2: Doc, linkDocs: Doc[] }[]); + return connections.map(c => { + let x = c.linkDocs.reduce((p, l) => p + l[Id], ""); + return ; + }); + } - findUniquePairs = (): JSX.Element[] => { - let connections = DocumentManager.Instance.LinkedDocumentViews; + // 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))); + // // 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 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; + // // 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); + // // 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 }); - } - }); + // // 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(); - } 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(); - - // 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(); - // } - // 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(); - } - } - }); - return uniqueList; - } + // 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(); + // } 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(); + + // // 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(); + // // } + // // 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(); + // } + // } + // }); + // return uniqueList; + // } render() { - this.findUniquePairs(); return (
- {/* {this.uniqueConnections} */} - {this.findUniquePairs()} + {this.uniqueConnections} + {/* {this.findUniquePairs()} */} {this.props.children}
-- cgit v1.2.3-70-g09d2 From 60f9122ea31d660d60d5429890c4eb0ef6d8613b Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 21 Jun 2019 13:41:25 -0400 Subject: following link without viewdoc opens it to right --- src/client/util/DragManager.ts | 2 -- src/client/views/nodes/LinkMenuItem.tsx | 7 ++++++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 78cae4ff7..11530ef09 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -203,11 +203,9 @@ export namespace DragManager { export let StartDragFunctions: (() => void)[] = []; export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { - console.log("outside", dragData.userDropAction, dragData.dropAction); runInAction(() => StartDragFunctions.map(func => func())); StartDrag(eles, dragData, downX, downY, options, (dropData: { [id: string]: any }) => { - console.log("DRAG", dragData.userDropAction, dragData.dropAction); (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") ? diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index c68365584..1ef4690b0 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -12,6 +12,7 @@ import { observable, action } from 'mobx'; import { LinkManager } from '../../util/LinkManager'; import { DragLinksAsDocuments, DragLinkAsDocument } from '../../util/DragManager'; import { SelectionManager } from '../../util/SelectionManager'; +import { CollectionDockingView } from '../collections/CollectionDockingView'; library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); @@ -32,7 +33,11 @@ export class LinkMenuItem extends React.Component { @undoBatch onFollowLink = async (e: React.PointerEvent): Promise => { e.stopPropagation(); - DocumentManager.Instance.jumpToDocument(this.props.destinationDoc, e.altKey); + if (DocumentManager.Instance.getDocumentView(this.props.destinationDoc)) { + DocumentManager.Instance.jumpToDocument(this.props.destinationDoc, e.altKey); + } else { + CollectionDockingView.Instance.AddRightSplit(this.props.destinationDoc); + } } onEdit = (e: React.PointerEvent): void => { -- cgit v1.2.3-70-g09d2 From a4b34adcb34184728be0b69b33a561f6d10f0a98 Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 21 Jun 2019 16:27:03 -0400 Subject: can drag just a group of links on a doc --- src/client/documents/Documents.ts | 4 +- src/client/util/DocumentManager.ts | 46 ++----- src/client/util/DragManager.ts | 47 ++++++- .../CollectionFreeFormLinkView.scss | 38 +++--- .../CollectionFreeFormLinkView.tsx | 88 ++++++------ .../CollectionFreeFormLinksView.tsx | 148 +++------------------ src/client/views/nodes/DocumentView.tsx | 2 + src/client/views/nodes/LinkMenu.scss | 15 ++- src/client/views/nodes/LinkMenu.tsx | 18 +-- src/client/views/nodes/LinkMenuGroup.tsx | 74 +++++++++++ src/client/views/nodes/LinkMenuItem.tsx | 8 +- src/new_fields/Doc.ts | 16 ++- 12 files changed, 243 insertions(+), 261 deletions(-) create mode 100644 src/client/views/nodes/LinkMenuGroup.tsx (limited to 'src') 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 => { 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, 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 { - // 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 ( <> - - {/* */} - {/* + {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 { @@ -98,150 +94,42 @@ export class CollectionFreeFormLinksView extends React.Component { - // 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 ; + let x = c.l.reduce((p, l) => p + l[Id], ""); + return ; }); } - // 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(); - // } 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(); - - // // 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(); - // // } - // // 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(); - // } - // } - // }); - // return uniqueList; - // } - render() { return (
{this.uniqueConnections} - {/* {this.findUniquePairs()} */} {this.props.children}
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(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 { @observable private _editingLink?: Doc; - renderGroup = (group: Doc[], groupType: string): Array => { - let source = this.props.docView.Document; - return group.map(linkDoc => { - let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, source); - return this._editingLink = linkDoc)} />; - }); - } - renderAllGroups = (groups: Map>): Array => { let linkItems: Array = []; groups.forEach((group, groupType) => { linkItems.push( -
-

{groupType}:

-
- {this.renderGroup(group, groupType)} -
-
+ 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 { + + private _drag = React.createRef(); + + 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 ; + }); + + return ( +
+

{this.props.groupType}:

+
+ {groupItems} +
+
+ ) + } +} \ 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 { 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 {
-

{StrCast(this.props.destinationDoc.title)}

+

{StrCast(this.props.destinationDoc.title)}

{canExpand ?
this.toggleShowMore()}>
: <>}
-
+
{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; } -- cgit v1.2.3-70-g09d2 From 7962aff8431b692af5229cd8e6c390bbe1110336 Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 21 Jun 2019 16:29:31 -0400 Subject: link menu styling --- src/client/views/nodes/LinkMenu.scss | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index c01ed23c4..1ca669d00 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -11,27 +11,29 @@ } .linkMenu-group { - margin-bottom: 10px; + // margin-bottom: 10px; + border-bottom: 0.5px solid lightgray; + padding: 5px 0; + &:last-child { - margin-bottom: 0; + border-bottom: none; } .linkMenu-group-name { padding: 4px 6px; line-height: 12px; border-radius: 5px; - margin-bottom: 2px; + font-weight: bold; &:hover { background-color: lightgray; } } - .linkMenu-group-wrapper { - border-top: 0.5px solid lightgray; - padding-top: 4px; - } + // .linkMenu-group-wrapper { + // padding-top: 4px; + // } } -- cgit v1.2.3-70-g09d2 From 32ef8d83d5829e2faadbebaf6f9b694df5d7ea02 Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 21 Jun 2019 17:41:20 -0400 Subject: link menu styling --- src/client/documents/Documents.ts | 9 +++--- src/client/util/LinkManager.ts | 10 +++++-- src/client/views/nodes/LinkEditor.scss | 50 ++++++++++++++++++---------------- src/client/views/nodes/LinkEditor.tsx | 32 ++++++++++++++-------- src/client/views/nodes/LinkMenu.scss | 5 ---- src/new_fields/Doc.ts | 17 ++++-------- 6 files changed, 66 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7cef48b98..64032e096 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -90,7 +90,7 @@ export namespace DocUtils { // export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { // let protoSrc = source.proto ? source.proto : source; // let protoTarg = target.proto ? target.proto : target; - export function MakeLink(source: Doc, target: Doc, targetContext?: Doc) { + export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { if (LinkManager.Instance.doesLinkExist(source, target)) { console.log("LINK EXISTS"); return; } @@ -99,9 +99,10 @@ export namespace DocUtils { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); let linkDocProto = Doc.GetProto(linkDoc); - // linkDocProto.title = title === "" ? source.title + " to " + target.title : title; - // linkDocProto.linkDescription = description; - // linkDocProto.linkTags = tags; + + linkDocProto.title = title; //=== "" ? source.title + " to " + target.title : title; + linkDocProto.linkDescription = description; + linkDocProto.linkTags = tags; linkDocProto.anchor1 = source; linkDocProto.anchor1Page = source.curPage; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 544f2edda..fef996bb9 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -3,6 +3,7 @@ import { StrCast, Cast } from "../../new_fields/Types"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; +import { Id } from "../../new_fields/FieldSymbols"; /* @@ -36,8 +37,13 @@ export class LinkManager { // finds all links that contain the given anchor public findAllRelatedLinks(anchor: Doc): Array { - return LinkManager.Instance.allLinks.filter( - link => Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)) || Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc))); + return LinkManager.Instance.allLinks.filter(link => { + let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)); + let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc)); + let idmatch1 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor1, Doc, new Doc)[Id]); + let idmatch2 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor2, Doc, new Doc)[Id]); + return protomatch1 || protomatch2 || idmatch1 || idmatch2; + }); } // returns map of group type to anchor's links in that group type diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index 154b4abe3..760887fa2 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -25,17 +25,22 @@ } } +.linkEditor-button { + width: 20px; + height: 20px; + margin-left: 6px; + padding: 0; + font-size: 12px; + border-radius: 10px; + + &:disabled { + background-color: gray; + } +} + .linkEditor-groupsLabel { display: flex; justify-content: space-between; - - button { - width: 20px; - height: 20px; - margin-left: 6px; - padding: 0; - font-size: 14px; - } } .linkEditor-group { @@ -109,23 +114,20 @@ .linkEditor-group-buttons { height: 20px; display: flex; - justify-content: space-between; - - .linkEditor-groupOpts { - width: calc(20% - 3px); - height: 20px; - padding: 0; - font-size: 10px; + justify-content: flex-end; - &:disabled { - background-color: gray; - } + .linkEditor-button { + margin-left: 6px; } - .linkEditor-groupOpts button { - width: 100%; - height: 20px; - font-size: 10px; - padding: 0; - } + // .linkEditor-groupOpts { + // width: calc(20% - 3px); + // height: 20px; + // padding: 0; + // font-size: 10px; + + // &:disabled { + // background-color: gray; + // } + // } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 29ead7388..0d13c6cc8 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -7,12 +7,13 @@ import { Doc } from "../../../new_fields/Doc"; import { LinkManager } from "../../util/LinkManager"; import { Docs } from "../../documents/Documents"; import { Utils } from "../../../Utils"; -import { faArrowLeft, faEllipsisV, faTable, faTrash } from '@fortawesome/free-solid-svg-icons'; +import { faArrowLeft, faEllipsisV, faTable, faTrash, faCog } from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SetupDrag } from "../../util/DragManager"; +import { anchorPoints, Flyout } from "../DocumentDecorations"; -library.add(faArrowLeft, faEllipsisV, faTable, faTrash); +library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog); interface GroupTypesDropdownProps { @@ -256,9 +257,9 @@ export class LinkEditor extends React.Component { let docs: Doc[] = LinkManager.Instance.findAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" })); let ref = React.createRef(); - return
; + return
; } else { - return ; + return ; } } @@ -273,12 +274,20 @@ export class LinkEditor extends React.Component {
{this.renderMetadata(groupId)}
- {groupDoc.type === "New Group" ? : - } - - - + {groupDoc.type === "New Group" ? : + } {this.viewGroupAsTable(groupId, type)} + + + + + + }> + +
); @@ -290,6 +299,7 @@ export class LinkEditor extends React.Component { @action addMetadata = (groupType: string): void => { + console.log("ADD MD"); let mdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType); if (mdKeys) { // only add "new key" if there is no other key with value "new key"; prevents spamming @@ -331,11 +341,11 @@ export class LinkEditor extends React.Component {

editing link to: {destination.proto!.title}

- +
Relationships: - +
{groups.length > 0 ? groups :
There are currently no relationships associated with this link.
}
diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index 1ca669d00..ae3446e25 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -11,7 +11,6 @@ } .linkMenu-group { - // margin-bottom: 10px; border-bottom: 0.5px solid lightgray; padding: 5px 0; @@ -30,10 +29,6 @@ background-color: lightgray; } } - - // .linkMenu-group-wrapper { - // padding-top: 4px; - // } } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 5ce47fc2f..fda788f2d 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -234,32 +234,27 @@ export namespace Doc { export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc { const copy = new Doc; Object.keys(doc).forEach(key => { + console.log(key); const field = doc[key]; if (key === "proto" && copyProto) { + console.log(1); if (field instanceof Doc) { + console.log(2); copy[key] = Doc.MakeCopy(field); } } else { if (field instanceof RefField) { + console.log(3); copy[key] = field; } else if (field instanceof ObjectField) { + console.log(4); copy[key] = ObjectField.MakeCopy(field); } else { + console.log(5); copy[key] = field; } } }); - 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; } -- cgit v1.2.3-70-g09d2 From 522970375fe0227f9221a7e8be02875afd74ca63 Mon Sep 17 00:00:00 2001 From: Fawn Date: Mon, 24 Jun 2019 14:01:29 -0400 Subject: link menu styling --- src/client/util/DocumentManager.ts | 23 ++++++----- src/client/util/DragManager.ts | 12 ++---- src/client/util/LinkManager.ts | 6 +-- .../CollectionFreeFormLinkView.tsx | 24 +++++------ .../CollectionFreeFormLinksView.tsx | 19 ++++++++- src/client/views/nodes/LinkEditor.scss | 2 +- src/client/views/nodes/LinkEditor.tsx | 46 +++++++++++++--------- 7 files changed, 77 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index c4cb6721a..89e6183d6 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -85,20 +85,19 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { + console.log("link"); 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 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 { a: DocumentView, b: DocumentView, l: Doc }[])); - } + pairs.push(...linksList.reduce((pairs, link) => { + if (link) { + let linkToDoc = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); + DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => { + pairs.push({ a: dv, b: docView1, l: link }); + }); + } + return pairs; + }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); + // } return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 2abcff4f7..f4c8adc8e 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -7,7 +7,7 @@ import * as globalCssVariables from "../views/globalCssVariables.scss"; import { LinkManager } from "./LinkManager"; import { URLField } from "../../new_fields/URLField"; import { SelectionManager } from "./SelectionManager"; -import { Docs } from "../documents/Documents"; +import { Docs, DocUtils } from "../documents/Documents"; import { DocumentManager } from "./DocumentManager"; export type dropActionType = "alias" | "copy" | undefined; @@ -51,7 +51,7 @@ export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: num let moddrag = await Cast(draggeddoc.annotationOn, Doc); let dragData = new DragManager.DocumentDragData(moddrag ? [moddrag] : [draggeddoc]); dragData.dropAction = "alias" as dropActionType; - DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, { + DragManager.StartLinkedDocumentDrag([dragEle], sourceDoc, dragData, x, y, { handlers: { dragComplete: action(emptyFunction), }, @@ -95,7 +95,7 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n // dragData.draggedDocuments // ); // }); - DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, { + DragManager.StartLinkedDocumentDrag([dragEle], sourceDoc, dragData, x, y, { handlers: { dragComplete: action(emptyFunction), }, @@ -223,7 +223,7 @@ export namespace DragManager { }); } - export function StartLinkedDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + export function StartLinkedDocumentDrag(eles: HTMLElement[], sourceDoc: Doc, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { runInAction(() => StartDragFunctions.map(func => func())); StartDrag(eles, dragData, downX, downY, options, @@ -233,16 +233,12 @@ export namespace DragManager { // 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 ? diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index fef996bb9..745255f31 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -40,9 +40,9 @@ export class LinkManager { return LinkManager.Instance.allLinks.filter(link => { let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)); let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc)); - let idmatch1 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor1, Doc, new Doc)[Id]); - let idmatch2 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor2, Doc, new Doc)[Id]); - return protomatch1 || protomatch2 || idmatch1 || idmatch2; + // let idmatch1 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor1, Doc, new Doc)[Id]); + // let idmatch2 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor2, Doc, new Doc)[Id]); + return protomatch1 || protomatch2;// || idmatch1 || idmatch2; }); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 5c7f080e0..5dc3b5c16 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -9,6 +9,7 @@ import v5 = require("uuid/v5"); export interface CollectionFreeFormLinkViewProps { A: Doc; B: Doc; + // LinkDoc: Doc; LinkDocs: Doc[]; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; removeDocument: (document: Doc) => boolean; @@ -25,18 +26,18 @@ export class CollectionFreeFormLinkView extends React.Component { - 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); - }); + // 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 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); @@ -44,14 +45,13 @@ export class CollectionFreeFormLinkView extends React.Component {/* { let srcViews = this.documentAnchors(connection.a); let targetViews = this.documentAnchors(connection.b); + + console.log(srcViews.length, targetViews.length); let possiblePairs: { a: Doc, b: Doc, }[] = []; - srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }))); + srcViews.map(sv => { + targetViews.map(tv => { + console.log("PUSH", StrCast(sv.props.Document.title), StrCast(sv.props.Document.id), StrCast(tv.props.Document.title), StrCast(tv.props.Document.id)); + possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }); + }); + }); possiblePairs.map(possiblePair => { if (!drawnPairs.reduce((found, drawnPair) => { let match1 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.a) && Doc.AreProtosEqual(possiblePair.b, drawnPair.b)); @@ -123,9 +132,17 @@ export class CollectionFreeFormLinksView extends React.Component; }); + + // return DocumentManager.Instance.LinkedDocumentViews.map(c => { + // // let x = c.l.reduce((p, l) => p + l[Id], ""); + // let x = c.a.Document[Id] + c.b.Document[Id]; + // return ; + // }); } render() { + console.log("\n"); return (
diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index 760887fa2..2602b8816 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -30,7 +30,7 @@ height: 20px; margin-left: 6px; padding: 0; - font-size: 12px; + // font-size: 12px; border-radius: 10px; &:disabled { diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 0d13c6cc8..95199bae2 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -7,13 +7,13 @@ import { Doc } from "../../../new_fields/Doc"; import { LinkManager } from "../../util/LinkManager"; import { Docs } from "../../documents/Documents"; import { Utils } from "../../../Utils"; -import { faArrowLeft, faEllipsisV, faTable, faTrash, faCog } from '@fortawesome/free-solid-svg-icons'; +import { faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTimes, faPlus } from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SetupDrag } from "../../util/DragManager"; import { anchorPoints, Flyout } from "../DocumentDecorations"; -library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog); +library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTimes, faPlus); interface GroupTypesDropdownProps { @@ -131,7 +131,7 @@ class LinkMetadataEditor extends React.Component {
this.setMetadataKey(e.target.value)}>: this.setMetadataValue(e.target.value)}> - +
); } @@ -266,6 +266,29 @@ export class LinkEditor extends React.Component { renderGroup = (groupId: string, groupDoc: Doc): JSX.Element => { let type = StrCast(groupDoc.type); if ((type && LinkManager.Instance.groupMetadataKeys.get(type)) || type === "New Group") { + let buttons; + if (type === "New Group") { + buttons = ( + <> + + + + + + + ); + } else { + buttons = ( + <> + + + + + {this.viewGroupAsTable(groupId, type)} + + ); + } + return (
@@ -274,20 +297,7 @@ export class LinkEditor extends React.Component {
{this.renderMetadata(groupId)}
- {groupDoc.type === "New Group" ? : - } - {this.viewGroupAsTable(groupId, type)} - - - - - - }> - - + {buttons}
); @@ -345,7 +355,7 @@ export class LinkEditor extends React.Component {
Relationships: - +
{groups.length > 0 ? groups :
There are currently no relationships associated with this link.
}
-- cgit v1.2.3-70-g09d2 From 41cf1e8536964764f18ab752140e484e36cbe464 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 25 Jun 2019 17:09:36 -0400 Subject: links can save --- src/client/documents/Documents.ts | 58 +--- src/client/util/DocumentManager.ts | 14 +- src/client/util/DragManager.ts | 24 +- src/client/util/LinkManager.ts | 239 +++++++++++---- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainView.tsx | 7 + .../views/collections/CollectionDockingView.tsx | 4 +- .../views/collections/CollectionTreeView.tsx | 18 +- .../CollectionFreeFormLinksView.tsx | 7 +- src/client/views/nodes/DocumentView.tsx | 8 +- src/client/views/nodes/LinkButtonBox.scss | 34 +-- src/client/views/nodes/LinkButtonBox.tsx | 126 ++++---- src/client/views/nodes/LinkEditor.tsx | 323 +++++++++------------ src/client/views/nodes/LinkMenu.tsx | 2 +- src/client/views/nodes/LinkMenuGroup.tsx | 8 +- src/client/views/nodes/LinkMenuItem.tsx | 6 +- src/new_fields/Doc.ts | 9 +- src/new_fields/LinkButtonField.ts | 58 ++-- 18 files changed, 503 insertions(+), 444 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 64032e096..fbd96fb66 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -35,8 +35,8 @@ import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; -import { LinkButtonBox } from "../views/nodes/LinkButtonBox"; -import { LinkButtonField, LinkButtonData } from "../../new_fields/LinkButtonField"; +// import { LinkButtonBox } from "../views/nodes/LinkButtonBox"; +// import { LinkButtonField, LinkButtonData } from "../../new_fields/LinkButtonField"; import { DocumentManager } from "../util/DocumentManager"; import { Id } from "../../new_fields/FieldSymbols"; var requestImageSize = require('request-image-size'); @@ -100,6 +100,7 @@ export namespace DocUtils { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); let linkDocProto = Doc.GetProto(linkDoc); + linkDocProto.context = targetContext; linkDocProto.title = title; //=== "" ? source.title + " to " + target.title : title; linkDocProto.linkDescription = description; linkDocProto.linkTags = tags; @@ -111,36 +112,7 @@ export namespace DocUtils { linkDocProto.anchor2Page = target.curPage; linkDocProto.anchor2Groups = new List([]); - linkDocProto.context = targetContext; - - let sourceViews = DocumentManager.Instance.getDocumentViews(source); - let targetViews = DocumentManager.Instance.getDocumentViews(target); - sourceViews.forEach(sv => { - targetViews.forEach(tv => { - - // TODO: do only for when diff contexts - let proxy1 = Docs.LinkButtonDocument( - { sourceViewId: StrCast(sv.props.Document[Id]), targetViewId: StrCast(tv.props.Document[Id]) }, - { width: 200, height: 100, borderRounding: 0 }); - let proxy1Proto = Doc.GetProto(proxy1); - proxy1Proto.sourceViewId = StrCast(sv.props.Document[Id]); - proxy1Proto.targetViewId = StrCast(tv.props.Document[Id]); - proxy1Proto.isLinkButton = true; - - let proxy2 = Docs.LinkButtonDocument( - { sourceViewId: StrCast(tv.props.Document[Id]), targetViewId: StrCast(sv.props.Document[Id]) }, - { width: 200, height: 100, borderRounding: 0 }); - let proxy2Proto = Doc.GetProto(proxy2); - proxy2Proto.sourceViewId = StrCast(tv.props.Document[Id]); - proxy2Proto.targetViewId = StrCast(sv.props.Document[Id]); - proxy2Proto.isLinkButton = true; - - LinkManager.Instance.linkProxies.push(proxy1); - LinkManager.Instance.linkProxies.push(proxy2); - }); - }); - - LinkManager.Instance.allLinks.push(linkDoc); + LinkManager.Instance.addLink(linkDoc); return linkDoc; }, "make link"); @@ -160,7 +132,7 @@ export namespace Docs { let audioProto: Doc; let pdfProto: Doc; let iconProto: Doc; - let linkProto: Doc; + // let linkProto: Doc; const textProtoId = "textProto"; const histoProtoId = "histoProto"; const pdfProtoId = "pdfProto"; @@ -171,7 +143,7 @@ export namespace Docs { const videoProtoId = "videoProto"; const audioProtoId = "audioProto"; const iconProtoId = "iconProto"; - const linkProtoId = "linkProto"; + // const linkProtoId = "linkProto"; export function initProtos(): Promise { return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId]).then(fields => { @@ -185,7 +157,7 @@ export namespace Docs { audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); - linkProto = fields[linkProtoId] as Doc || CreateLinkPrototype(); + // linkProto = fields[linkProtoId] as Doc || CreateLinkPrototype(); }); } @@ -218,11 +190,11 @@ export namespace Docs { { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }); return iconProto; } - function CreateLinkPrototype(): Doc { - let linkProto = setupPrototypeOptions(linkProtoId, "LINK_PROTO", LinkButtonBox.LayoutString(), - { x: 0, y: 0, width: 300 }); - return linkProto; - } + // function CreateLinkPrototype(): Doc { + // let linkProto = setupPrototypeOptions(linkProtoId, "LINK_PROTO", LinkButtonBox.LayoutString(), + // { x: 0, y: 0, width: 300 }); + // return linkProto; + // } function CreateTextPrototype(): Doc { let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), { x: 0, y: 0, width: 300, backgroundColor: "#f1efeb" }); @@ -309,9 +281,9 @@ export namespace Docs { export function IconDocument(icon: string, options: DocumentOptions = {}) { return CreateInstance(iconProto, new IconField(icon), options); } - export function LinkButtonDocument(data: LinkButtonData, options: DocumentOptions = {}) { - return CreateInstance(linkProto, new LinkButtonField(data), options); - } + // export function LinkButtonDocument(data: LinkButtonData, options: DocumentOptions = {}) { + // return CreateInstance(linkProto, new LinkButtonField(data), options); + // } export function PdfDocument(url: string, options: DocumentOptions = {}) { return CreateInstance(pdfProto, new PdfField(new URL(url)), options); } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 89e6183d6..767abe63f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -85,12 +85,11 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { - console.log("link"); - 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 pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { + let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document); pairs.push(...linksList.reduce((pairs, link) => { if (link) { - let linkToDoc = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); + let linkToDoc = LinkManager.Instance.getOppositeAnchor(link, dv.props.Document); DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => { pairs.push({ a: dv, b: docView1, l: link }); }); @@ -100,6 +99,13 @@ export class DocumentManager { // } return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); + + // console.log("LINKED DOCUMENT VIEWS"); + // pairs.forEach(p => { + // console.log(StrCast(p.a.Document.title), p.a.props.Document[Id], StrCast(p.b.Document.title), p.b.props.Document[Id]); + // }); + + return pairs; } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index f4c8adc8e..1aacf2c53 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -46,7 +46,7 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () } export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: number, linkDoc: Doc, sourceDoc: Doc) { - let draggeddoc = LinkManager.Instance.findOppositeAnchor(linkDoc, sourceDoc); + let draggeddoc = LinkManager.Instance.getOppositeAnchor(linkDoc, sourceDoc); let moddrag = await Cast(draggeddoc.annotationOn, Doc); let dragData = new DragManager.DocumentDragData(moddrag ? [moddrag] : [draggeddoc]); @@ -66,10 +66,10 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n // TODO: if not in same context then don't drag if (srcTarg) { - let linkDocs = LinkManager.Instance.findAllRelatedLinks(srcTarg); + let linkDocs = LinkManager.Instance.getAllRelatedLinks(srcTarg); if (linkDocs) { draggedDocs = linkDocs.map(link => { - return LinkManager.Instance.findOppositeAnchor(link, sourceDoc); + return LinkManager.Instance.getOppositeAnchor(link, sourceDoc); }); } } @@ -236,10 +236,16 @@ export namespace DragManager { if (dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) { return d; } else { - return Doc.MakeAlias(d); + // return d; + let r = Doc.MakeAlias(d); + // DocUtils.MakeLink(sourceDoc, r); + return r; } } else { - return Doc.MakeAlias(d); + // return d; + let r = Doc.MakeAlias(d); + // DocUtils.MakeLink(sourceDoc, r); + return r; } // return (dv && dv.props.ContainingCollectionView !== SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) || !dv ? // Doc.MakeAlias(d) : d; @@ -282,10 +288,10 @@ export namespace DragManager { StartDrag([ele], dragData, downX, downY, options); } - export function StartLinkProxyDrag(ele: HTMLElement, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { - runInAction(() => StartDragFunctions.map(func => func())); - StartDrag([ele], dragData, downX, downY, options); - } + // export function StartLinkProxyDrag(ele: HTMLElement, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + // runInAction(() => StartDragFunctions.map(func => func())); + // StartDrag([ele], dragData, downX, downY, options); + // } export let AbortDrag: () => void = emptyFunction; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 745255f31..82c3a9acd 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,9 +1,10 @@ import { observable, action } from "mobx"; -import { StrCast, Cast } from "../../new_fields/Types"; +import { StrCast, Cast, FieldValue } from "../../new_fields/Types"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; import { Id } from "../../new_fields/FieldSymbols"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; /* @@ -23,6 +24,11 @@ import { Id } from "../../new_fields/FieldSymbols"; * - user defined kvps */ export class LinkManager { + // static Instance: LinkManager; + // private constructor() { + // LinkManager.Instance = this; + // } + private static _instance: LinkManager; public static get Instance(): LinkManager { return this._instance || (this._instance = new this()); @@ -30,25 +36,138 @@ export class LinkManager { private constructor() { } - @observable public allLinks: Array = []; // list of link docs - @observable public groupMetadataKeys: Map> = new Map(); - // map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it hodls - @observable public linkProxies: Array = []; // list of linkbutton docs - used to visualize link when an anchors are not in the same context + public get LinkManagerDoc(): Doc | undefined { + return FieldValue(Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc)); + } + // @observable public allLinks: Array = []; //List = new List([]); // list of link docs + // @observable public groupMetadataKeys: Map> = new Map(); + // map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it holds + + public getAllLinks(): Doc[] { + return LinkManager.Instance.LinkManagerDoc ? LinkManager.Instance.LinkManagerDoc.allLinks ? DocListCast(LinkManager.Instance.LinkManagerDoc.allLinks) : [] : []; + } + + public addLink(linkDoc: Doc): boolean { + let linkList = LinkManager.Instance.getAllLinks(); + linkList.push(linkDoc); + console.log("link man doc", LinkManager.Instance.LinkManagerDoc); + if (LinkManager.Instance.LinkManagerDoc) { + LinkManager.Instance.LinkManagerDoc.allLinks = new List(linkList); + return true; + } + return false; + } + + public deleteLink(linkDoc: Doc): boolean { + let linkList = LinkManager.Instance.getAllLinks(); + let index = LinkManager.Instance.getAllLinks().indexOf(linkDoc); + if (index > -1) { + linkList.splice(index, 1); + if (LinkManager.Instance.LinkManagerDoc) { + LinkManager.Instance.LinkManagerDoc.allLinks = new List(linkList); + return true; + } + } + return false; + } // finds all links that contain the given anchor - public findAllRelatedLinks(anchor: Doc): Array { - return LinkManager.Instance.allLinks.filter(link => { + public getAllRelatedLinks(anchor: Doc): Doc[] {//List { + let related = LinkManager.Instance.getAllLinks().filter(link => { let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)); let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc)); - // let idmatch1 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor1, Doc, new Doc)[Id]); - // let idmatch2 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor2, Doc, new Doc)[Id]); - return protomatch1 || protomatch2;// || idmatch1 || idmatch2; + return protomatch1 || protomatch2; }); + return related; + } + + public addGroupType(groupType: string): boolean { + if (LinkManager.Instance.LinkManagerDoc) { + LinkManager.Instance.LinkManagerDoc[groupType] = new List([]); + let groupTypes = LinkManager.Instance.getAllGroupTypes(); + groupTypes.push(groupType); + LinkManager.Instance.LinkManagerDoc.allGroupTypes = new List(groupTypes); + return true; + } + return false; + } + + // removes all group docs from all links with the given group type + public deleteGroupType(groupType: string): boolean { + if (LinkManager.Instance.LinkManagerDoc) { + if (LinkManager.Instance.LinkManagerDoc[groupType]) { + LinkManager.Instance.LinkManagerDoc[groupType] = undefined; + LinkManager.Instance.getAllLinks().forEach(linkDoc => { + LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType); + LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc), groupType); + }); + } + return true; + } else return false; + } + + public getAllGroupTypes(): string[] { + if (LinkManager.Instance.LinkManagerDoc) { + if (LinkManager.Instance.LinkManagerDoc.allGroupTypes) { + return Cast(LinkManager.Instance.LinkManagerDoc.allGroupTypes, listSpec("string"), []); + } else { + LinkManager.Instance.LinkManagerDoc.allGroupTypes = new List([]); + return []; + } + } + return []; + } + + // gets the groups associates with an anchor in a link + public getAnchorGroups(linkDoc: Doc, anchor: Doc): Array { + if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { + return DocListCast(linkDoc.anchor1Groups); + } else { + return DocListCast(linkDoc.anchor2Groups); + } + } + + // sets the groups of the given anchor in the given link + public setAnchorGroups(linkDoc: Doc, anchor: Doc, groups: Doc[]) { + if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { + linkDoc.anchor1Groups = new List(groups); + } else { + linkDoc.anchor2Groups = new List(groups); + } + } + + public addGroupToAnchor(linkDoc: Doc, anchor: Doc, groupDoc: Doc, replace: boolean = false) { + let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); + let index = groups.findIndex(gDoc => { + return StrCast(groupDoc.type).toUpperCase() === StrCast(gDoc.type).toUpperCase(); + }); + if (index > -1 && replace) { + groups[index] = groupDoc; + } + if (index === -1) { + groups.push(groupDoc); + } + LinkManager.Instance.setAnchorGroups(linkDoc, anchor, groups); + } + + // removes group doc of given group type only from given anchor on given link + public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { + let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); + let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase()); + LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); } + // public doesAnchorHaveGroup(linkDoc: Doc, anchor: Doc, groupDoc: Doc): boolean { + // let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); + // let index = groups.findIndex(gDoc => { + // return StrCast(groupDoc.type).toUpperCase() === StrCast(gDoc.type).toUpperCase(); + // }); + // return index > -1; + // } + // returns map of group type to anchor's links in that group type - public findRelatedGroupedLinks(anchor: Doc): Map> { - let related = this.findAllRelatedLinks(anchor); + public getRelatedGroupedLinks(anchor: Doc): Map> { + let related = this.getAllRelatedLinks(anchor); let anchorGroups = new Map>(); related.forEach(link => { let groups = LinkManager.Instance.getAnchorGroups(link, anchor); @@ -73,10 +192,41 @@ export class LinkManager { return anchorGroups; } + // public addMetadataKeyToGroup(groupType: string, key: string): boolean { + // if (LinkManager.Instance.LinkManagerDoc) { + // if (LinkManager.Instance.LinkManagerDoc[groupType]) { + // let keyList = LinkManager.Instance.findMetadataKeysInGroup(groupType); + // keyList.push(key); + // LinkManager.Instance.LinkManagerDoc[groupType] = new List(keyList); + // return true; + // } + // return false; + // } + // return false; + // } + + public getMetadataKeysInGroup(groupType: string): string[] { + if (LinkManager.Instance.LinkManagerDoc) { + return LinkManager.Instance.LinkManagerDoc[groupType] ? Cast(LinkManager.Instance.LinkManagerDoc[groupType], listSpec("string"), []) : []; + } + return []; + } + + public setMetadataKeysForGroup(groupType: string, keys: string[]): boolean { + if (LinkManager.Instance.LinkManagerDoc) { + // if (LinkManager.Instance.LinkManagerDoc[groupType]) { + LinkManager.Instance.LinkManagerDoc[groupType] = new List(keys); + return true; + // } + // return false; + } + return false; + } + // returns a list of all metadata docs associated with the given group type - public findAllMetadataDocsInGroup(groupType: string): Array { + public getAllMetadataDocsInGroup(groupType: string): Array { let md: Doc[] = []; - let allLinks = LinkManager.Instance.allLinks; + let allLinks = LinkManager.Instance.getAllLinks(); allLinks.forEach(linkDoc => { let anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc)); let anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc)); @@ -86,27 +236,9 @@ export class LinkManager { return md; } - // removes all group docs from all links with the given group type - public deleteGroup(groupType: string): void { - let deleted = LinkManager.Instance.groupMetadataKeys.delete(groupType); - if (deleted) { - LinkManager.Instance.allLinks.forEach(linkDoc => { - LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType); - LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc), groupType); - }); - } - } - - // removes group doc of given group type only from given anchor on given link - public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { - let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); - let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase()); - LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); - } - // checks if a link with the given anchors exists - public doesLinkExist(anchor1: Doc, anchor2: Doc) { - let allLinks = LinkManager.Instance.allLinks; + public doesLinkExist(anchor1: Doc, anchor2: Doc): boolean { + let allLinks = LinkManager.Instance.getAllLinks(); let index = allLinks.findIndex(linkDoc => { return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, new Doc), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, new Doc), anchor2)) || (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, new Doc), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, new Doc), anchor1)); @@ -115,7 +247,7 @@ export class LinkManager { } // finds the opposite anchor of a given anchor in a link - public findOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc { + public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc { if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { return Cast(linkDoc.anchor2, Doc, new Doc); } else { @@ -123,34 +255,17 @@ export class LinkManager { } } - // gets the groups associates with an anchor in a link - public getAnchorGroups(linkDoc: Doc, anchor: Doc): Array { - if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { - return DocListCast(linkDoc.anchor1Groups); - } else { - return DocListCast(linkDoc.anchor2Groups); - } - } - // sets the groups of the given anchor in the given link - public setAnchorGroups(linkDoc: Doc, anchor: Doc, groups: Doc[]) { - if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { - linkDoc.anchor1Groups = new List(groups); - } else { - linkDoc.anchor2Groups = new List(groups); - } - } - - @action - public addLinkProxy(proxy: Doc) { - LinkManager.Instance.linkProxies.push(proxy); - } + // @action + // public addLinkProxy(proxy: Doc) { + // LinkManager.Instance.linkProxies.push(proxy); + // } - public findLinkProxy(sourceViewId: string, targetViewId: string): Doc | undefined { - let index = LinkManager.Instance.linkProxies.findIndex(p => { - return StrCast(p.sourceViewId) === sourceViewId && StrCast(p.targetViewId) === targetViewId; - }); - return index > -1 ? LinkManager.Instance.linkProxies[index] : undefined; - } + // public findLinkProxy(sourceViewId: string, targetViewId: string): Doc | undefined { + // let index = LinkManager.Instance.linkProxies.findIndex(p => { + // return StrCast(p.sourceViewId) === sourceViewId && StrCast(p.targetViewId) === targetViewId; + // }); + // return index > -1 ? LinkManager.Instance.linkProxies[index] : undefined; + // } } \ No newline at end of file diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 926273633..3a2752d7e 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -573,7 +573,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let linkButton = null; if (SelectionManager.SelectedDocuments().length > 0) { let selFirst = SelectionManager.SelectedDocuments()[0]; - let linkCount = LinkManager.Instance.findAllRelatedLinks(selFirst.props.Document).length; + let linkCount = LinkManager.Instance.getAllRelatedLinks(selFirst.props.Document).length; linkButton = (([]); + CurrentUserUtils.UserDocument.linkManagerDoc = linkManagerDoc; + } list.push(mainDoc); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) setTimeout(() => { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c82027da5..4140f8029 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -354,9 +354,9 @@ export class CollectionDockingView extends React.Component [LinkManager.Instance.findAllRelatedLinks(doc), doc.title], + tab.reactionDisposer = reaction(() => [LinkManager.Instance.getAllRelatedLinks(doc), doc.title], () => { - counter.innerHTML = LinkManager.Instance.findAllRelatedLinks(doc).length; + counter.innerHTML = LinkManager.Instance.getAllRelatedLinks(doc).length; tab.titleElement[0].textContent = doc.title; }, { fireImmediately: true }); tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 0b922b3c4..7bc3ad124 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -73,7 +73,7 @@ class TreeView extends React.Component { @undoBatch delete = () => this.props.deleteDoc(this.props.document); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight"); - onPointerDown = (e: React.PointerEvent) => e.stopPropagation() + onPointerDown = (e: React.PointerEvent) => e.stopPropagation(); onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); if (e.buttons === 1 && SelectionManager.GetIsDragging()) { @@ -115,7 +115,7 @@ class TreeView extends React.Component { return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc); } @action - indent = () => this.props.addDocument(this.props.document) && this.delete(); + indent = () => this.props.addDocument(this.props.document) && this.delete() renderBullet() { let docList = Cast(this.props.document["data"], listSpec(Doc)); @@ -167,7 +167,7 @@ class TreeView extends React.Component { keyList.push(key); } }); - if (LinkManager.Instance.findAllRelatedLinks(this.props.document).length > 0) keyList.push("links"); + if (LinkManager.Instance.getAllRelatedLinks(this.props.document).length > 0) keyList.push("links"); if (keyList.indexOf("data") !== -1) { keyList.splice(keyList.indexOf("data"), 1); } @@ -281,9 +281,9 @@ class TreeView extends React.Component { let ele: JSX.Element[] = []; let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); - let groups = LinkManager.Instance.findRelatedGroupedLinks(this.props.document); + let groups = LinkManager.Instance.getRelatedGroupedLinks(this.props.document); groups.forEach((groupLinkDocs, groupType) => { - let destLinks = groupLinkDocs.map(d => LinkManager.Instance.findOppositeAnchor(d, this.props.document)); + let destLinks = groupLinkDocs.map(d => LinkManager.Instance.getOppositeAnchor(d, this.props.document)); ele.push(
{groupType}:
@@ -325,7 +325,7 @@ class TreeView extends React.Component { addDocTab={this.props.addDocTab} setPreviewScript={emptyFunction}> -
+
; } } return
@@ -364,14 +364,14 @@ class TreeView extends React.Component { TreeView.AddDocToList(docList[i - 1], fieldKey, child); remove(child); } - } + }; let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => { return add(doc, relativeTo ? relativeTo : docList[i], before !== undefined ? before : false); - } + }; let rowHeight = () => { let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0); return aspect ? Math.min(child[WidthSym](), rowWidth()) / aspect : child[HeightSym](); - } + }; return { let srcViews = this.documentAnchors(connection.a); let targetViews = this.documentAnchors(connection.b); - console.log(srcViews.length, targetViews.length); + // console.log(srcViews.length, targetViews.length); let possiblePairs: { a: Doc, b: Doc, }[] = []; srcViews.map(sv => { targetViews.map(tv => { - console.log("PUSH", StrCast(sv.props.Document.title), StrCast(sv.props.Document.id), StrCast(tv.props.Document.title), StrCast(tv.props.Document.id)); + // console.log("PUSH", StrCast(sv.props.Document.title), StrCast(sv.props.Document.id), StrCast(tv.props.Document.title), StrCast(tv.props.Document.id)); possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }); }); }); @@ -142,7 +142,6 @@ export class CollectionFreeFormLinksView extends React.Component diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1fc2cf770..7b185336b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -285,7 +285,7 @@ export class DocumentView extends DocComponent(Docu let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs); let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs); - let linkedDocs = LinkManager.Instance.findAllRelatedLinks(this.props.Document); + let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document); let expandedDocs: Doc[] = []; expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs; expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs; @@ -536,11 +536,6 @@ export class DocumentView extends DocComponent(Docu onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; }; onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; }; - onDragOver = (e: React.DragEvent): void => { - this.props.Document.libraryBrush = true; - console.log("dragOver"); - }; - onDragLeave = (e: React.DragEvent): void => { this.props.Document.libraryBrush = false; }; isSelected = () => SelectionManager.IsSelected(this); @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }; @@ -585,7 +580,6 @@ export class DocumentView extends DocComponent(Docu // display: display ? "block" : "none" }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} - onDragOver={this.onDragOver} onDragLeave={this.onDragLeave} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} > {this.contents} diff --git a/src/client/views/nodes/LinkButtonBox.scss b/src/client/views/nodes/LinkButtonBox.scss index 24bfd2c9f..6be2dcf60 100644 --- a/src/client/views/nodes/LinkButtonBox.scss +++ b/src/client/views/nodes/LinkButtonBox.scss @@ -1,18 +1,18 @@ -.linkBox-cont { - width: 200px; - height: 100px; - background-color: black; - text-align: center; - color: white; - padding: 10px; - border-radius: 5px; - position: relative; +// .linkBox-cont { +// width: 200px; +// height: 100px; +// background-color: black; +// text-align: center; +// color: white; +// padding: 10px; +// border-radius: 5px; +// position: relative; - .linkBox-cont-wrapper { - width: calc(100% - 20px); - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - } -} \ No newline at end of file +// .linkBox-cont-wrapper { +// width: calc(100% - 20px); +// position: absolute; +// left: 50%; +// top: 50%; +// transform: translate(-50%, -50%); +// } +// } \ No newline at end of file diff --git a/src/client/views/nodes/LinkButtonBox.tsx b/src/client/views/nodes/LinkButtonBox.tsx index 8a7c1ed8b..440847ead 100644 --- a/src/client/views/nodes/LinkButtonBox.tsx +++ b/src/client/views/nodes/LinkButtonBox.tsx @@ -1,63 +1,63 @@ -import React = require("react"); -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { computed, observable, runInAction } from "mobx"; -import { observer } from "mobx-react"; -import { FieldView, FieldViewProps } from './FieldView'; -import "./LinkButtonBox.scss"; -import { DocumentView } from "./DocumentView"; -import { Doc } from "../../../new_fields/Doc"; -import { LinkButtonField } from "../../../new_fields/LinkButtonField"; -import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { DocumentManager } from "../../util/DocumentManager"; -import { Id } from "../../../new_fields/FieldSymbols"; - -library.add(faCaretUp); -library.add(faObjectGroup); -library.add(faStickyNote); -library.add(faFilePdf); -library.add(faFilm); - -@observer -export class LinkButtonBox extends React.Component { - public static LayoutString() { return FieldView.LayoutString(LinkButtonBox); } - - followLink = (): void => { - console.log("follow link???"); - let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); - let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); - if (targetView && targetView.props.ContainingCollectionView) { - CollectionDockingView.Instance.AddRightSplit(targetView.props.ContainingCollectionView.props.Document); - } - } - - render() { - - let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); - let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); - - let text = "Could not find link"; - if (targetView) { - let context = targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(targetView.props.ContainingCollectionView.props.Document.title)) : ""; - text = "Link to " + StrCast(targetView.props.Document.title) + context; - } - - let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); - let display = activeDvs.reduce((found, dv) => { - let matchSv = field.data.sourceViewId === StrCast(dv.props.Document[Id]); - let matchTv = field.data.targetViewId === StrCast(dv.props.Document[Id]); - let match = matchSv || matchTv; - return match || found; - }, false); - - return ( -
-
-

{text}

-
-
- ); - } -} \ No newline at end of file +// import React = require("react"); +// import { library } from '@fortawesome/fontawesome-svg-core'; +// import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; +// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +// import { computed, observable, runInAction } from "mobx"; +// import { observer } from "mobx-react"; +// import { FieldView, FieldViewProps } from './FieldView'; +// import "./LinkButtonBox.scss"; +// import { DocumentView } from "./DocumentView"; +// import { Doc } from "../../../new_fields/Doc"; +// import { LinkButtonField } from "../../../new_fields/LinkButtonField"; +// import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; +// import { CollectionDockingView } from "../collections/CollectionDockingView"; +// import { DocumentManager } from "../../util/DocumentManager"; +// import { Id } from "../../../new_fields/FieldSymbols"; + +// library.add(faCaretUp); +// library.add(faObjectGroup); +// library.add(faStickyNote); +// library.add(faFilePdf); +// library.add(faFilm); + +// @observer +// export class LinkButtonBox extends React.Component { +// public static LayoutString() { return FieldView.LayoutString(LinkButtonBox); } + +// followLink = (): void => { +// console.log("follow link???"); +// let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); +// let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); +// if (targetView && targetView.props.ContainingCollectionView) { +// CollectionDockingView.Instance.AddRightSplit(targetView.props.ContainingCollectionView.props.Document); +// } +// } + +// render() { + +// let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); +// let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); + +// let text = "Could not find link"; +// if (targetView) { +// let context = targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(targetView.props.ContainingCollectionView.props.Document.title)) : ""; +// text = "Link to " + StrCast(targetView.props.Document.title) + context; +// } + +// let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); +// let display = activeDvs.reduce((found, dv) => { +// let matchSv = field.data.sourceViewId === StrCast(dv.props.Document[Id]); +// let matchTv = field.data.targetViewId === StrCast(dv.props.Document[Id]); +// let match = matchSv || matchTv; +// return match || found; +// }, false); + +// return ( +//
+//
+//

{text}

+//
+//
+// ); +// } +// } \ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 95199bae2..5f4f7d4f0 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -17,9 +17,8 @@ library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, fa interface GroupTypesDropdownProps { - groupId: string; groupType: string; - setGroup: (groupId: string, group: string) => void; + setGroupType: (group: string) => void; } // this dropdown could be generalized @observer @@ -32,20 +31,20 @@ class GroupTypesDropdown extends React.Component { @action createGroup = (groupType: string): void => { - this.props.setGroup(this.props.groupId, groupType); - LinkManager.Instance.groupMetadataKeys.set(groupType, []); + this.props.setGroupType(groupType); + LinkManager.Instance.addGroupType(groupType); } renderOptions = (): JSX.Element[] | JSX.Element => { if (this._searchTerm === "") return <>; - let allGroupTypes = Array.from(LinkManager.Instance.groupMetadataKeys.keys()); + let allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes()); let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase()) > -1; let options = groupOptions.map(groupType => { return
{ this.props.setGroup(this.props.groupId, groupType); this.setGroupType(groupType); this.setSearchTerm(""); }}>{groupType}
; + onClick={() => { this.props.setGroupType(groupType); this.setGroupType(groupType); this.setSearchTerm(""); }}>{groupType}
; }); // if search term does not already exist as a group type, give option to create new group type @@ -85,7 +84,7 @@ class LinkMetadataEditor extends React.Component { @action setMetadataKey = (value: string): void => { - let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!); + let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType); // don't allow user to create existing key let newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase()); @@ -98,12 +97,15 @@ class LinkMetadataEditor extends React.Component { } // set new value for key - let currIndex = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase()); + let currIndex = groupMdKeys.findIndex(key => { + console.log("finding index this", key.toUpperCase(), "that", this._key.toUpperCase()); + return StrCast(key).toUpperCase() === this._key.toUpperCase(); + }); if (currIndex === -1) console.error("LinkMetadataEditor: key was not found"); groupMdKeys[currIndex] = value; this._key = value; - LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys); + LinkManager.Instance.setMetadataKeysForGroup(this.props.groupType, groupMdKeys); } @action @@ -116,13 +118,13 @@ class LinkMetadataEditor extends React.Component { @action removeMetadata = (): void => { - let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!); + let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType); let index = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase()); if (index === -1) console.error("LinkMetadataEditor: key was not found"); groupMdKeys.splice(index, 1); - LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys); + LinkManager.Instance.setMetadataKeysForGroup(this.props.groupType, groupMdKeys); this._key = ""; } @@ -137,215 +139,176 @@ class LinkMetadataEditor extends React.Component { } } - -interface LinkEditorProps { +interface LinkGroupEditorProps { sourceDoc: Doc; linkDoc: Doc; - showLinks: () => void; + groupDoc: Doc; } @observer -export class LinkEditor extends React.Component { +export class LinkGroupEditor extends React.Component { - // map of temporary group id to the corresponding group doc - @observable private _groups: Map = new Map(); - - constructor(props: LinkEditorProps) { - super(props); + @action + setGroupType = (groupType: string): void => { + console.log("SET GROUP TYPE TO", groupType); + this.props.groupDoc.type = groupType; + console.log("GROUP TYPE HAS BEEN SET TO ", StrCast(this.props.groupDoc.type)); + } - let groups = new Map(); - let groupList = LinkManager.Instance.getAnchorGroups(props.linkDoc, props.sourceDoc); - groupList.forEach(groupDoc => { - let id = Utils.GenerateGuid(); - groups.set(id, groupDoc); - }); - this._groups = groups; + removeGroupFromLink = (groupType: string): void => { + LinkManager.Instance.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType); } - @action - deleteLink = (): void => { - let index = LinkManager.Instance.allLinks.indexOf(this.props.linkDoc); - LinkManager.Instance.allLinks.splice(index, 1); - this.props.showLinks(); + deleteGroup = (groupType: string): void => { + LinkManager.Instance.deleteGroupType(groupType); } - @action - addGroup = (): void => { - // new group only gets added if there is not already a group with type "new group" - let index = Array.from(this._groups.values()).findIndex(groupDoc => { - return groupDoc.type === "New Group"; - }); - if (index > -1) return; + copyGroup = (groupType: string): void => { + let sourceGroupDoc = this.props.groupDoc; + let sourceMdDoc = Cast(sourceGroupDoc.metadata, Doc, new Doc); - // create new metadata document for group - let mdDoc = Docs.TextDocument(); - mdDoc.proto!.anchor1 = this.props.sourceDoc.title; - mdDoc.proto!.anchor2 = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title; + let destDoc = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); + // let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc); + let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); - // create new group document - let groupDoc = Docs.TextDocument(); - groupDoc.proto!.type = "New Group"; - groupDoc.proto!.metadata = mdDoc; + // create new metadata doc with copied kvp + let destMdDoc = new Doc(); + destMdDoc.anchor1 = StrCast(sourceMdDoc.anchor2); + destMdDoc.anchor2 = StrCast(sourceMdDoc.anchor1); + keys.forEach(key => { + let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]); + destMdDoc[key] = val; + }); - this._groups.set(Utils.GenerateGuid(), groupDoc); + // create new group doc with new metadata doc + let destGroupDoc = new Doc(); + destGroupDoc.type = groupType; + destGroupDoc.metadata = destMdDoc; - let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - LinkManager.Instance.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); + LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, destDoc, destGroupDoc, true); } @action - setGroupType = (groupId: string, groupType: string): void => { - let groupDoc = this._groups.get(groupId); - if (groupDoc) { - groupDoc.proto!.type = groupType; - this._groups.set(groupId, groupDoc); - LinkManager.Instance.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); - } - } - - removeGroupFromLink = (groupId: string, groupType: string): void => { - let groupDoc = this._groups.get(groupId); - if (!groupDoc) console.error("LinkEditor: group not found"); - LinkManager.Instance.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType); - this._groups.delete(groupId); - } - - deleteGroup = (groupId: string, groupType: string): void => { - let groupDoc = this._groups.get(groupId); - if (!groupDoc) console.error("LinkEditor: group not found"); - LinkManager.Instance.deleteGroup(groupType); - this._groups.delete(groupId); + addMetadata = (groupType: string): void => { + let mdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); + // only add "new key" if there is no other key with value "new key"; prevents spamming + if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key"); + LinkManager.Instance.setMetadataKeysForGroup(groupType, mdKeys); } - copyGroup = (groupId: string, groupType: string): void => { - let sourceGroupDoc = this._groups.get(groupId); - let sourceMdDoc = Cast(sourceGroupDoc!.metadata, Doc, new Doc); - let destDoc = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); - let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc); - let keys = LinkManager.Instance.groupMetadataKeys.get(groupType); - - // create new metadata doc with copied kvp - let destMdDoc = Docs.TextDocument(); - destMdDoc.proto!.anchor1 = StrCast(sourceMdDoc.anchor2); - destMdDoc.proto!.anchor2 = StrCast(sourceMdDoc.anchor1); - if (keys) { - keys.forEach(key => { - let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]); - destMdDoc[key] = val; + renderMetadata = (): JSX.Element[] => { + let metadata: Array = []; + let groupDoc = this.props.groupDoc; + let mdDoc = Cast(groupDoc.metadata, Doc, new Doc); + let groupType = StrCast(groupDoc.type); + let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); + if (groupMdKeys) { + groupMdKeys.forEach((key, index) => { + metadata.push( + + ); }); } - - // create new group doc with new metadata doc - let destGroupDoc = Docs.TextDocument(); - destGroupDoc.proto!.type = groupType; - destGroupDoc.proto!.metadata = destMdDoc; - - // if group does not already exist on opposite anchor, create group doc - let index = destGroupList.findIndex(groupDoc => { StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase(); }); - if (index > -1) { - destGroupList[index] = destGroupDoc; - } else { - destGroupList.push(destGroupDoc); - } - - LinkManager.Instance.setAnchorGroups(this.props.linkDoc, destDoc, destGroupList); + return metadata; } - viewGroupAsTable = (groupId: string, groupType: string): JSX.Element => { - let keys = LinkManager.Instance.groupMetadataKeys.get(groupType); - let groupDoc = this._groups.get(groupId); - if (keys && groupDoc) { - let docs: Doc[] = LinkManager.Instance.findAllMetadataDocsInGroup(groupType); - let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" })); - let ref = React.createRef(); - return
; - } else { - return ; - } + viewGroupAsTable = (groupType: string): JSX.Element => { + let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); + let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); + let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys], docs, { width: 500, height: 300, title: groupType + " table" })); + let ref = React.createRef(); + return
; } - renderGroup = (groupId: string, groupDoc: Doc): JSX.Element => { - let type = StrCast(groupDoc.type); - if ((type && LinkManager.Instance.groupMetadataKeys.get(type)) || type === "New Group") { - let buttons; - if (type === "New Group") { - buttons = ( - <> - - - - - - - ); - } else { - buttons = ( - <> - - - - - {this.viewGroupAsTable(groupId, type)} - - ); - } - - return ( -
-
-

type:

- -
- {this.renderMetadata(groupId)} -
- {buttons} -
-
+ render() { + let groupType = StrCast(this.props.groupDoc.type); + // if ((groupType && LinkManager.Instance.getMetadataKeysInGroup(groupType).length > 0) || groupType === "") { + let buttons; + if (groupType === "") { + buttons = ( + <> + + + + + + ); } else { - return <>; + buttons = ( + <> + + + + + {this.viewGroupAsTable(groupType)} + + ); } + + return ( +
+
+

type:

+ +
+ {this.renderMetadata()} +
+ {buttons} +
+
+ ); } + // else { + // return <>; + // } + // } +} + +interface LinkEditorProps { + sourceDoc: Doc; + linkDoc: Doc; + showLinks: () => void; +} +@observer +export class LinkEditor extends React.Component { @action - addMetadata = (groupType: string): void => { - console.log("ADD MD"); - let mdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType); - if (mdKeys) { - // only add "new key" if there is no other key with value "new key"; prevents spamming - if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key"); - } else { - mdKeys = ["new key"]; - } - LinkManager.Instance.groupMetadataKeys.set(groupType, mdKeys); + deleteLink = (): void => { + LinkManager.Instance.deleteLink(this.props.linkDoc); + this.props.showLinks(); } - renderMetadata = (groupId: string): JSX.Element[] => { - let metadata: Array = []; - let groupDoc = this._groups.get(groupId); - if (groupDoc) { - let mdDoc = Cast(groupDoc.proto!.metadata, Doc, new Doc); - let groupType = StrCast(groupDoc.proto!.type); - let groupMdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType); - if (groupMdKeys) { - groupMdKeys.forEach((key, index) => { - metadata.push( - - ); - }); - } - } - return metadata; + @action + addGroup = (): void => { + // create new metadata document for group + let mdDoc = new Doc(); + mdDoc.anchor1 = this.props.sourceDoc.title; + mdDoc.anchor2 = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title; + + // create new group document + let groupDoc = new Doc(); + groupDoc.type = ""; + groupDoc.metadata = mdDoc; + + LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, this.props.sourceDoc, groupDoc); } render() { - let destination = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); + let destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); - let groups: Array = []; - this._groups.forEach((groupDoc, groupId) => { - groups.push(this.renderGroup(groupId, groupDoc)); + let groupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); + console.log("NUM GROUPS", groupList.length); + let groups = groupList.map(groupDoc => { + return ; }); + + // let groups: Array = []; + // this._groups.forEach((groupDoc, groupId) => { + // groups.push(this.renderGroup(groupId, groupDoc)); + // }); + return (
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index f96c7d2e4..04ca47db3 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -38,7 +38,7 @@ export class LinkMenu extends React.Component { render() { let sourceDoc = this.props.docView.props.Document; - let groups: Map = LinkManager.Instance.findRelatedGroupedLinks(sourceDoc); + let groups: Map = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc); if (this._editingLink === undefined) { return (
diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx index 229143d99..71326f703 100644 --- a/src/client/views/nodes/LinkMenuGroup.tsx +++ b/src/client/views/nodes/LinkMenuGroup.tsx @@ -42,10 +42,10 @@ export class LinkMenuGroup extends React.Component { 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 draggedDocs = this.props.group.map(linkDoc => LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc)); let dragData = new DragManager.DocumentDragData(draggedDocs); - DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, { + DragManager.StartLinkedDocumentDrag([this._drag.current], this.props.sourceDoc, dragData, e.x, e.y, { handlers: { dragComplete: action(emptyFunction), }, @@ -57,7 +57,7 @@ export class LinkMenuGroup extends React.Component { render() { let groupItems = this.props.group.map(linkDoc => { - let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, this.props.sourceDoc); + let destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc); return ; }); @@ -69,6 +69,6 @@ export class LinkMenuGroup extends React.Component { {groupItems}
- ) + ); } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index 42ef353b7..28694721d 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -53,8 +53,8 @@ export class LinkMenuItem extends React.Component { let mdRows: Array = []; if (groupDoc) { let mdDoc = Cast(groupDoc.metadata, Doc, new Doc); - let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); - mdRows = keys!.map(key => { + let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType); + mdRows = keys.map(key => { return (
{key}: {StrCast(mdDoc[key])}
); }); } @@ -88,7 +88,7 @@ export class LinkMenuItem extends React.Component { render() { - let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); + let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType); let canExpand = keys ? keys.length > 0 : false; return ( diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index fda788f2d..9b104184f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -234,23 +234,20 @@ export namespace Doc { export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc { const copy = new Doc; Object.keys(doc).forEach(key => { - console.log(key); const field = doc[key]; if (key === "proto" && copyProto) { - console.log(1); if (field instanceof Doc) { - console.log(2); copy[key] = Doc.MakeCopy(field); } } else { if (field instanceof RefField) { - console.log(3); + console.log("equals field, ref", key); copy[key] = field; } else if (field instanceof ObjectField) { - console.log(4); + console.log("copy field, object", key); copy[key] = ObjectField.MakeCopy(field); } else { - console.log(5); + console.log("equals field", key); copy[key] = field; } } diff --git a/src/new_fields/LinkButtonField.ts b/src/new_fields/LinkButtonField.ts index 92e1ed922..e6d1de749 100644 --- a/src/new_fields/LinkButtonField.ts +++ b/src/new_fields/LinkButtonField.ts @@ -1,35 +1,35 @@ -import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, primitive, createSimpleSchema, object } from "serializr"; -import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString } from "./FieldSymbols"; -import { Doc } from "./Doc"; -import { DocumentView } from "../client/views/nodes/DocumentView"; +// import { Deserializable } from "../client/util/SerializationHelper"; +// import { serializable, primitive, createSimpleSchema, object } from "serializr"; +// import { ObjectField } from "./ObjectField"; +// import { Copy, ToScriptString } from "./FieldSymbols"; +// import { Doc } from "./Doc"; +// import { DocumentView } from "../client/views/nodes/DocumentView"; -export type LinkButtonData = { - sourceViewId: string, - targetViewId: string -}; +// export type LinkButtonData = { +// sourceViewId: string, +// targetViewId: string +// }; -const LinkButtonSchema = createSimpleSchema({ - sourceViewId: true, - targetViewId: true -}); +// const LinkButtonSchema = createSimpleSchema({ +// sourceViewId: true, +// targetViewId: true +// }); -@Deserializable("linkButton") -export class LinkButtonField extends ObjectField { - @serializable(object(LinkButtonSchema)) - readonly data: LinkButtonData; +// @Deserializable("linkButton") +// export class LinkButtonField extends ObjectField { +// @serializable(object(LinkButtonSchema)) +// readonly data: LinkButtonData; - constructor(data: LinkButtonData) { - super(); - this.data = data; - } +// constructor(data: LinkButtonData) { +// super(); +// this.data = data; +// } - [Copy]() { - return new LinkButtonField(this.data); - } +// [Copy]() { +// return new LinkButtonField(this.data); +// } - [ToScriptString]() { - return "invalid"; - } -} +// [ToScriptString]() { +// return "invalid"; +// } +// } -- cgit v1.2.3-70-g09d2 From 2a698e88da5ef0a9fee1ff4ee69746f1242798c9 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 25 Jun 2019 18:32:17 -0400 Subject: fixed render links in treeview --- src/client/views/collections/CollectionTreeView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index f5c5219a7..ca77f7c45 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -270,7 +270,7 @@ class TreeView extends React.Component { renderLinks = () => { let ele: JSX.Element[] = []; let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); let groups = LinkManager.Instance.getRelatedGroupedLinks(this.props.document); groups.forEach((groupLinkDocs, groupType) => { let destLinks = groupLinkDocs.map(d => LinkManager.Instance.getOppositeAnchor(d, this.props.document)); -- cgit v1.2.3-70-g09d2 From 2d300b0cd3d02c900865c61eacd539efed5289e6 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 25 Jun 2019 20:18:14 -0400 Subject: fixed link metadata rendering bug --- src/client/documents/Documents.ts | 11 +- src/client/util/LinkManager.ts | 2 +- .../CollectionFreeFormLinkView.scss | 31 ----- .../CollectionFreeFormLinkView.tsx | 1 - .../CollectionFreeFormLinkWithProxyView.tsx | 131 --------------------- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 11 -- src/client/views/nodes/LinkButtonBox.scss | 18 --- src/client/views/nodes/LinkButtonBox.tsx | 63 ---------- src/client/views/nodes/LinkEditor.tsx | 45 ++++--- src/new_fields/LinkButtonField.ts | 35 ------ 11 files changed, 35 insertions(+), 316 deletions(-) delete mode 100644 src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx delete mode 100644 src/client/views/nodes/LinkButtonBox.scss delete mode 100644 src/client/views/nodes/LinkButtonBox.tsx delete mode 100644 src/new_fields/LinkButtonField.ts (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d2300e4d2..547687921 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -88,7 +88,7 @@ export namespace DocUtils { // let protoTarg = target.proto ? target.proto : target; export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { if (LinkManager.Instance.doesLinkExist(source, target)) { - console.log("LINK EXISTS"); return; + return; } UndoManager.RunInBatch(() => { @@ -186,11 +186,6 @@ export namespace Docs { { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }); return iconProto; } - // function CreateLinkPrototype(): Doc { - // let linkProto = setupPrototypeOptions(linkProtoId, "LINK_PROTO", LinkButtonBox.LayoutString(), - // { x: 0, y: 0, width: 300 }); - // return linkProto; - // } function CreateTextPrototype(): Doc { let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), { x: 0, y: 0, width: 300, backgroundColor: "#f1efeb" }); @@ -277,9 +272,7 @@ export namespace Docs { export function IconDocument(icon: string, options: DocumentOptions = {}) { return CreateInstance(iconProto, new IconField(icon), options); } - // export function LinkButtonDocument(data: LinkButtonData, options: DocumentOptions = {}) { - // return CreateInstance(linkProto, new LinkButtonField(data), options); - // } + export function PdfDocument(url: string, options: DocumentOptions = {}) { return CreateInstance(pdfProto, new PdfField(new URL(url)), options); } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 60de7fc52..db814082f 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -156,7 +156,7 @@ export class LinkManager { // removes group doc of given group type only from given anchor on given link public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); - let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase()); + let newGroups = groups.filter(groupDoc => { StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase() }); LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index 239c2ce56..fc5212edd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -17,34 +17,3 @@ transform: translate(10000px,10000px); pointer-events: all; } - -.linkview-ele { - transform: translate(10000px,10000px); - pointer-events: all; - - &.linkview-line { - stroke: black; - stroke-width: 2px; - opacity: 0.5; - } -} - -.linkview-button { - width: 200px; - height: 100px; - border-radius: 5px; - padding: 10px; - position: relative; - background-color: black; - cursor: pointer; - - p { - width: calc(100% - 20px); - color: white; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - -} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 5dc3b5c16..b546d1b78 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -9,7 +9,6 @@ import v5 = require("uuid/v5"); export interface CollectionFreeFormLinkViewProps { A: Doc; B: Doc; - // LinkDoc: Doc; LinkDocs: Doc[]; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; removeDocument: (document: Doc) => boolean; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx deleted file mode 100644 index a4d122af2..000000000 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { observer } from "mobx-react"; -import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc"; -import { BoolCast, NumCast, StrCast } from "../../../../new_fields/Types"; -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"; -import { observable, action } from "mobx"; -import { CollectionDockingView } from "../CollectionDockingView"; -import { dropActionType, DragManager } from "../../../util/DragManager"; -import { emptyFunction } from "../../../../Utils"; -import { DocumentManager } from "../../../util/DocumentManager"; - -export interface CollectionFreeFormLinkViewProps { - sourceView: DocumentView; - targetView: DocumentView; - proxyDoc: Doc; - // addDocTab: (document: Doc, where: string) => void; -} - -@observer -export class CollectionFreeFormLinkWithProxyView extends React.Component { - - // @observable private _proxyX: number = NumCast(this.props.proxyDoc.x); - // @observable private _proxyY: number = NumCast(this.props.proxyDoc.y); - private _ref = React.createRef(); - private _downX: number = 0; - private _downY: number = 0; - @observable _x: number = 0; - @observable _y: number = 0; - // @observable private _proxyDoc: Doc = Docs.TextDocument(); // used for positioning - - @action - componentDidMount() { - let a2 = this.props.proxyDoc; - this._x = NumCast(a2.x) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2); - this._y = NumCast(a2.y) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2); - } - - - followButton = (e: React.PointerEvent): void => { - e.stopPropagation(); - let open = this.props.targetView.props.ContainingCollectionView ? this.props.targetView.props.ContainingCollectionView.props.Document : this.props.targetView.props.Document; - CollectionDockingView.Instance.AddRightSplit(open); - DocumentManager.Instance.jumpToDocument(this.props.targetView.props.Document, e.altKey); - } - - @action - setPosition(x: number, y: number) { - this._x = x; - this._y = y; - } - - startDragging(x: number, y: number) { - if (this._ref.current) { - let dragData = new DragManager.DocumentDragData([this.props.proxyDoc]); - - DragManager.StartLinkProxyDrag(this._ref.current, dragData, x, y, { - handlers: { - dragComplete: action(() => { - let a2 = this.props.proxyDoc; - let offset = NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2; - let x = NumCast(a2.x);// + NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2; - let y = NumCast(a2.y);// + NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2; - this.setPosition(x, y); - - // this is a hack :'( theres prob a better way to make the input doc not render - let views = DocumentManager.Instance.getDocumentViews(this.props.proxyDoc); - views.forEach(dv => { - dv.props.removeDocument && dv.props.removeDocument(dv.props.Document); - }); - }), - }, - hideSource: true //? - }); - } - } - - onPointerDown = (e: React.PointerEvent): void => { - this._downX = e.clientX; - this._downY = e.clientY; - - e.stopPropagation(); - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - } - - onPointerMove = (e: PointerEvent): void => { - if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - this.startDragging(this._downX, this._downY); - } - e.stopPropagation(); - e.preventDefault(); - } - onPointerUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - } - - render() { - let a1 = this.props.sourceView; - let x1 = NumCast(a1.Document.x) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.width) / NumCast(a1.Document.zoomBasis, 1) / 2); - let y1 = NumCast(a1.Document.y) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.height) / NumCast(a1.Document.zoomBasis, 1) / 2); - - let context = this.props.targetView.props.ContainingCollectionView ? - (" in the context of " + StrCast(this.props.targetView.props.ContainingCollectionView.props.Document.title)) : ""; - let text = "link to " + StrCast(this.props.targetView.props.Document.title) + context; - - return ( - <> - - -
-

{text}

-
-
- - ); - } -} - -//onPointerDown={this.followButton} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 940b00a90..02396c3af 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -12,7 +12,6 @@ import "./DocumentView.scss"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; import { IconBox } from "./IconBox"; -import { LinkButtonBox } from "./LinkButtonBox"; import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { VideoBox } from "./VideoBox"; @@ -104,7 +103,7 @@ export class DocumentContentsView extends React.Component(Docu var scaling = this.props.ContentScaling(); var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - // // for linkbutton docs - // let isLinkButton = BoolCast(this.props.Document.isLinkButton); - // let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); - // let display = isLinkButton ? activeDvs.reduce((found, dv) => { - // let matchSv = this.props.Document.sourceViewId === StrCast(dv.props.Document[Id]); - // let matchTv = this.props.Document.targetViewId === StrCast(dv.props.Document[Id]); - // let match = matchSv || matchTv; - // return match || found; - // }, false) : true; - var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
(Docu width: nativeWidth, height: nativeHeight, transform: `scale(${scaling}, ${scaling})`, - // display: display ? "block" : "none" }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} diff --git a/src/client/views/nodes/LinkButtonBox.scss b/src/client/views/nodes/LinkButtonBox.scss deleted file mode 100644 index 6be2dcf60..000000000 --- a/src/client/views/nodes/LinkButtonBox.scss +++ /dev/null @@ -1,18 +0,0 @@ -// .linkBox-cont { -// width: 200px; -// height: 100px; -// background-color: black; -// text-align: center; -// color: white; -// padding: 10px; -// border-radius: 5px; -// position: relative; - -// .linkBox-cont-wrapper { -// width: calc(100% - 20px); -// position: absolute; -// left: 50%; -// top: 50%; -// transform: translate(-50%, -50%); -// } -// } \ No newline at end of file diff --git a/src/client/views/nodes/LinkButtonBox.tsx b/src/client/views/nodes/LinkButtonBox.tsx deleted file mode 100644 index 440847ead..000000000 --- a/src/client/views/nodes/LinkButtonBox.tsx +++ /dev/null @@ -1,63 +0,0 @@ -// import React = require("react"); -// import { library } from '@fortawesome/fontawesome-svg-core'; -// import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; -// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -// import { computed, observable, runInAction } from "mobx"; -// import { observer } from "mobx-react"; -// import { FieldView, FieldViewProps } from './FieldView'; -// import "./LinkButtonBox.scss"; -// import { DocumentView } from "./DocumentView"; -// import { Doc } from "../../../new_fields/Doc"; -// import { LinkButtonField } from "../../../new_fields/LinkButtonField"; -// import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; -// import { CollectionDockingView } from "../collections/CollectionDockingView"; -// import { DocumentManager } from "../../util/DocumentManager"; -// import { Id } from "../../../new_fields/FieldSymbols"; - -// library.add(faCaretUp); -// library.add(faObjectGroup); -// library.add(faStickyNote); -// library.add(faFilePdf); -// library.add(faFilm); - -// @observer -// export class LinkButtonBox extends React.Component { -// public static LayoutString() { return FieldView.LayoutString(LinkButtonBox); } - -// followLink = (): void => { -// console.log("follow link???"); -// let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); -// let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); -// if (targetView && targetView.props.ContainingCollectionView) { -// CollectionDockingView.Instance.AddRightSplit(targetView.props.ContainingCollectionView.props.Document); -// } -// } - -// render() { - -// let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); -// let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); - -// let text = "Could not find link"; -// if (targetView) { -// let context = targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(targetView.props.ContainingCollectionView.props.Document.title)) : ""; -// text = "Link to " + StrCast(targetView.props.Document.title) + context; -// } - -// let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); -// let display = activeDvs.reduce((found, dv) => { -// let matchSv = field.data.sourceViewId === StrCast(dv.props.Document[Id]); -// let matchTv = field.data.targetViewId === StrCast(dv.props.Document[Id]); -// let match = matchSv || matchTv; -// return match || found; -// }, false); - -// return ( -//
-//
-//

{text}

-//
-//
-// ); -// } -// } \ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index a6511c3fe..eed34b21f 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -71,10 +71,12 @@ class GroupTypesDropdown extends React.Component { interface LinkMetadataEditorProps { + id: string; groupType: string; mdDoc: Doc; mdKey: string; mdValue: string; + changeMdIdKey: (id: string, newKey: string) => void; } @observer class LinkMetadataEditor extends React.Component { @@ -86,8 +88,6 @@ class LinkMetadataEditor extends React.Component { setMetadataKey = (value: string): void => { let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType); - // console.log("set", ...groupMdKeys, typeof (groupMdKeys[0])); - // don't allow user to create existing key let newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase()); if (newIndex > -1) { @@ -105,6 +105,7 @@ class LinkMetadataEditor extends React.Component { if (currIndex === -1) console.error("LinkMetadataEditor: key was not found"); groupMdKeys[currIndex] = value; + this.props.changeMdIdKey(this.props.id, value); this._key = value; LinkManager.Instance.setMetadataKeysForGroup(this.props.groupType, [...groupMdKeys]); } @@ -133,7 +134,7 @@ class LinkMetadataEditor extends React.Component { return (
this.setMetadataKey(e.target.value)}>: - this.setMetadataValue(e.target.value)}> + this.setMetadataValue(e.target.value)}>
); @@ -148,6 +149,17 @@ interface LinkGroupEditorProps { @observer export class LinkGroupEditor extends React.Component { + private _metadataIds: Map = new Map(); + + constructor(props: LinkGroupEditorProps) { + super(props); + + let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.type)); + groupMdKeys.forEach(key => { + this._metadataIds.set(key, Utils.GenerateGuid()); + }); + } + @action setGroupType = (groupType: string): void => { this.props.groupDoc.type = groupType; @@ -188,12 +200,18 @@ export class LinkGroupEditor extends React.Component { @action addMetadata = (groupType: string): void => { + this._metadataIds.set("new key", Utils.GenerateGuid()); let mdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); // only add "new key" if there is no other key with value "new key"; prevents spamming if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key"); LinkManager.Instance.setMetadataKeysForGroup(groupType, mdKeys); } + // for key rendering purposes + changeMdIdKey = (id: string, newKey: string) => { + this._metadataIds.set(newKey, id); + } + renderMetadata = (): JSX.Element[] => { let metadata: Array = []; let groupDoc = this.props.groupDoc; @@ -201,25 +219,24 @@ export class LinkGroupEditor extends React.Component { let groupType = StrCast(groupDoc.type); let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); - if (groupMdKeys) { - groupMdKeys.forEach((key, index) => { - metadata.push( - - ); - }); - } + groupMdKeys.forEach((key) => { + let val = StrCast(mdDoc[key]); + metadata.push( + + ); + }); return metadata; } viewGroupAsTable = (groupType: string): JSX.Element => { let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); + let index = keys.indexOf(""); + if (index > -1) keys.splice(index, 1); let cols = ["anchor1", "anchor2", ...[...keys]]; - // keys.forEach(k => cols.push(k)); - // console.log("COLS", ...cols); let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); let ref = React.createRef(); - return
; + return
; } render() { @@ -233,7 +250,7 @@ export class LinkGroupEditor extends React.Component { - + ); } else { diff --git a/src/new_fields/LinkButtonField.ts b/src/new_fields/LinkButtonField.ts deleted file mode 100644 index e6d1de749..000000000 --- a/src/new_fields/LinkButtonField.ts +++ /dev/null @@ -1,35 +0,0 @@ -// import { Deserializable } from "../client/util/SerializationHelper"; -// import { serializable, primitive, createSimpleSchema, object } from "serializr"; -// import { ObjectField } from "./ObjectField"; -// import { Copy, ToScriptString } from "./FieldSymbols"; -// import { Doc } from "./Doc"; -// import { DocumentView } from "../client/views/nodes/DocumentView"; - -// export type LinkButtonData = { -// sourceViewId: string, -// targetViewId: string -// }; - -// const LinkButtonSchema = createSimpleSchema({ -// sourceViewId: true, -// targetViewId: true -// }); - -// @Deserializable("linkButton") -// export class LinkButtonField extends ObjectField { -// @serializable(object(LinkButtonSchema)) -// readonly data: LinkButtonData; - -// constructor(data: LinkButtonData) { -// super(); -// this.data = data; -// } - -// [Copy]() { -// return new LinkButtonField(this.data); -// } - -// [ToScriptString]() { -// return "invalid"; -// } -// } -- cgit v1.2.3-70-g09d2 From ca8a78de9957ad27d345ad51fdaee9dae3f096bd Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 25 Jun 2019 20:44:34 -0400 Subject: can't link to containing collection --- src/client/documents/Documents.ts | 7 ++++--- src/client/util/DragManager.ts | 16 ++-------------- src/client/views/nodes/LinkEditor.tsx | 5 +++-- src/new_fields/Doc.ts | 3 --- 4 files changed, 9 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 547687921..b11b5fdf2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -35,6 +35,7 @@ import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; +import { DocumentManager } from "../util/DocumentManager"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -87,9 +88,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, title: string = "", description: string = "", tags: string = "Default") { - if (LinkManager.Instance.doesLinkExist(source, target)) { - return; - } + if (LinkManager.Instance.doesLinkExist(source, target)) return; + let sv = DocumentManager.Instance.getDocumentView(source); + if (sv && sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === target) return; UndoManager.RunInBatch(() => { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 4be3d82d3..55d8c570f 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -63,8 +63,6 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n let srcTarg = sourceDoc.proto; let draggedDocs: Doc[] = []; - // TODO: if not in same context then don't drag - if (srcTarg) { let linkDocs = LinkManager.Instance.getAllRelatedLinks(srcTarg); if (linkDocs) { @@ -232,25 +230,20 @@ export namespace DragManager { (dropData: { [id: string]: any }) => { dropData.droppedDocuments = dragData.draggedDocuments.map(d => { let dv = DocumentManager.Instance.getDocumentView(d); - // console.log("DRAG", StrCast(d.title)); if (dv) { if (dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) { return d; } else { - // return d; let r = Doc.MakeAlias(d); - // DocUtils.MakeLink(sourceDoc, r); + // DocUtils.MakeLink(r, sourceDoc); return r; } } else { - // return d; let r = Doc.MakeAlias(d); - // DocUtils.MakeLink(sourceDoc, r); + // DocUtils.MakeLink(r, sourceDoc); return r; } - // return (dv && dv.props.ContainingCollectionView !== SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) || !dv ? - // Doc.MakeAlias(d) : d; }); }); @@ -290,11 +283,6 @@ export namespace DragManager { StartDrag([ele], dragData, downX, downY, options); } - // export function StartLinkProxyDrag(ele: HTMLElement, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { - // runInAction(() => StartDragFunctions.map(func => func())); - // StartDrag([ele], dragData, downX, downY, options); - // } - export let AbortDrag: () => void = emptyFunction; function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) { diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index eed34b21f..232331204 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -133,8 +133,8 @@ class LinkMetadataEditor extends React.Component { render() { return (
- this.setMetadataKey(e.target.value)}>: - this.setMetadataValue(e.target.value)}> + this.setMetadataKey(e.target.value)}>: + this.setMetadataValue(e.target.value)}>
); @@ -221,6 +221,7 @@ export class LinkGroupEditor extends React.Component { groupMdKeys.forEach((key) => { let val = StrCast(mdDoc[key]); + console.log(key, val); metadata.push( ); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index f6fd44d61..d7411e63e 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -260,13 +260,10 @@ export namespace Doc { } } else { if (field instanceof RefField) { - console.log("equals field, ref", key); copy[key] = field; } else if (field instanceof ObjectField) { - console.log("copy field, object", key); copy[key] = ObjectField.MakeCopy(field); } else { - console.log("equals field", key); copy[key] = field; } } -- cgit v1.2.3-70-g09d2 From a3c4aa24a9e9074da8f2421954f610c8178e10b1 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 25 Jun 2019 21:28:15 -0400 Subject: link metadata values appear on first load --- src/client/documents/Documents.ts | 19 ------------------- src/client/util/DocumentManager.ts | 8 +++----- src/client/util/DragManager.ts | 2 +- src/client/views/nodes/LinkEditor.tsx | 11 +++++++---- 4 files changed, 11 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b11b5fdf2..ddbf8f753 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -68,25 +68,7 @@ export interface DocumentOptions { } const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; -// export interface LinkData { -// anchor1: Doc; -// anchor1Page: number; -// anchor1Tags: Array<{ tag: string, name: string, description: string }>; -// anchor2: Doc; -// anchor2Page: number; -// anchor2Tags: Array<{ tag: string, name: string, description: string }>; -// } - -// export interface TagData { -// tag: string; -// name: string; -// description: string; -// } - export namespace DocUtils { - // export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { - // let protoSrc = source.proto ? source.proto : source; - // let protoTarg = target.proto ? target.proto : target; export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { if (LinkManager.Instance.doesLinkExist(source, target)) return; let sv = DocumentManager.Instance.getDocumentView(source); @@ -154,7 +136,6 @@ export namespace Docs { audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); - // linkProto = fields[linkProtoId] as Doc || CreateLinkPrototype(); }); } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 767abe63f..d7798ebfd 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -73,6 +73,9 @@ export class DocumentManager { if (doc === toFind) { toReturn.push(view); } else { + // if (Doc.AreProtosEqual(doc, toFind)) { + // toReturn.push(view); + let docSrc = FieldValue(doc.proto); if (docSrc && Object.is(docSrc, toFind)) { toReturn.push(view); @@ -100,11 +103,6 @@ export class DocumentManager { return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); - // console.log("LINKED DOCUMENT VIEWS"); - // pairs.forEach(p => { - // console.log(StrCast(p.a.Document.title), p.a.props.Document[Id], StrCast(p.b.Document.title), p.b.props.Document[Id]); - // }); - return pairs; } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 55d8c570f..27063d1c2 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -230,7 +230,7 @@ export namespace DragManager { (dropData: { [id: string]: any }) => { dropData.droppedDocuments = dragData.draggedDocuments.map(d => { let dv = DocumentManager.Instance.getDocumentView(d); - + // return d; if (dv) { if (dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) { return d; diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 232331204..80eadf668 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -1,8 +1,8 @@ -import { observable, computed, action } from "mobx"; +import { observable, computed, action, trace } from "mobx"; import React = require("react"); import { observer } from "mobx-react"; import './LinkEditor.scss'; -import { StrCast, Cast } from "../../../new_fields/Types"; +import { StrCast, Cast, FieldValue } from "../../../new_fields/Types"; import { Doc } from "../../../new_fields/Doc"; import { LinkManager } from "../../util/LinkManager"; import { Docs } from "../../documents/Documents"; @@ -215,7 +215,10 @@ export class LinkGroupEditor extends React.Component { renderMetadata = (): JSX.Element[] => { let metadata: Array = []; let groupDoc = this.props.groupDoc; - let mdDoc = Cast(groupDoc.metadata, Doc, new Doc); + const mdDoc = FieldValue(Cast(groupDoc.metadata, Doc)); + if (!mdDoc) { + return []; + } let groupType = StrCast(groupDoc.type); let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); @@ -265,7 +268,7 @@ export class LinkGroupEditor extends React.Component { ); } - + trace(); return (
-- cgit v1.2.3-70-g09d2 From d0ff42632f8a155303e11945a1a974a15052f0db Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 26 Jun 2019 11:40:36 -0400 Subject: link menu styling --- src/client/util/LinkManager.ts | 44 +--------------------- .../CollectionFreeFormLinksView.tsx | 21 +---------- src/client/views/nodes/DocumentView.tsx | 2 - src/client/views/nodes/LinkEditor.tsx | 1 - src/client/views/nodes/LinkMenu.scss | 25 +++++++++--- src/client/views/nodes/LinkMenuGroup.tsx | 21 ++++++++++- src/client/views/nodes/LinkMenuItem.scss | 2 + 7 files changed, 44 insertions(+), 72 deletions(-) (limited to 'src') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index db814082f..97c816001 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -24,10 +24,6 @@ import { CurrentUserUtils } from "../../server/authentication/models/current_use * - user defined kvps */ export class LinkManager { - // static Instance: LinkManager; - // private constructor() { - // LinkManager.Instance = this; - // } private static _instance: LinkManager; public static get Instance(): LinkManager { @@ -36,12 +32,11 @@ export class LinkManager { private constructor() { } + // the linkmanagerdoc stores a list of docs representing all linkdocs in 'allLinks' and a list of strings representing all group types in 'allGroupTypes' + // lists of strings representing the metadata keys for each group type is stored under a key that is the same as the group type public get LinkManagerDoc(): Doc | undefined { return FieldValue(Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc)); } - // @observable public allLinks: Array = []; //List = new List([]); // list of link docs - // @observable public groupMetadataKeys: Map> = new Map(); - // map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it holds public getAllLinks(): Doc[] { return LinkManager.Instance.LinkManagerDoc ? LinkManager.Instance.LinkManagerDoc.allLinks ? DocListCast(LinkManager.Instance.LinkManagerDoc.allLinks) : [] : []; @@ -160,14 +155,6 @@ export class LinkManager { LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); } - // public doesAnchorHaveGroup(linkDoc: Doc, anchor: Doc, groupDoc: Doc): boolean { - // let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); - // let index = groups.findIndex(gDoc => { - // return StrCast(groupDoc.type).toUpperCase() === StrCast(gDoc.type).toUpperCase(); - // }); - // return index > -1; - // } - // returns map of group type to anchor's links in that group type public getRelatedGroupedLinks(anchor: Doc): Map> { let related = this.getAllRelatedLinks(anchor); @@ -195,19 +182,6 @@ export class LinkManager { return anchorGroups; } - // public addMetadataKeyToGroup(groupType: string, key: string): boolean { - // if (LinkManager.Instance.LinkManagerDoc) { - // if (LinkManager.Instance.LinkManagerDoc[groupType]) { - // let keyList = LinkManager.Instance.findMetadataKeysInGroup(groupType); - // keyList.push(key); - // LinkManager.Instance.LinkManagerDoc[groupType] = new List(keyList); - // return true; - // } - // return false; - // } - // return false; - // } - public getMetadataKeysInGroup(groupType: string): string[] { if (LinkManager.Instance.LinkManagerDoc) { return LinkManager.Instance.LinkManagerDoc[groupType] ? Cast(LinkManager.Instance.LinkManagerDoc[groupType], listSpec("string"), []) : []; @@ -254,18 +228,4 @@ export class LinkManager { return Cast(linkDoc.anchor1, Doc, new Doc); } } - - - // @action - // public addLinkProxy(proxy: Doc) { - // LinkManager.Instance.linkProxies.push(proxy); - // } - - // public findLinkProxy(sourceViewId: string, targetViewId: string): Doc | undefined { - // let index = LinkManager.Instance.linkProxies.findIndex(p => { - // return StrCast(p.sourceViewId) === sourceViewId && StrCast(p.targetViewId) === targetViewId; - // }); - // return index > -1 ? LinkManager.Instance.linkProxies[index] : undefined; - // } - } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index bb8e8a5c2..ebeb1fcee 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -94,24 +94,12 @@ export class CollectionFreeFormLinksView extends React.Component { - // console.log("CONNECTION", StrCast(d.a.props.Document.title), StrCast(d.b.props.Document.title)); - // }); - - // console.log("CONNECTIONS"); - let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => { let srcViews = this.documentAnchors(connection.a); let targetViews = this.documentAnchors(connection.b); - // console.log(srcViews.length, targetViews.length); let possiblePairs: { a: Doc, b: Doc, }[] = []; - srcViews.map(sv => { - targetViews.map(tv => { - // console.log("PUSH", StrCast(sv.props.Document.title), StrCast(sv.props.Document.id), StrCast(tv.props.Document.title), StrCast(tv.props.Document.id)); - possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }); - }); - }); + srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }))); possiblePairs.map(possiblePair => { if (!drawnPairs.reduce((found, drawnPair) => { let match1 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.a) && Doc.AreProtosEqual(possiblePair.b, drawnPair.b)); @@ -132,13 +120,6 @@ export class CollectionFreeFormLinksView extends React.Component; }); - - // return DocumentManager.Instance.LinkedDocumentViews.map(c => { - // // let x = c.l.reduce((p, l) => p + l[Id], ""); - // let x = c.a.Document[Id] + c.b.Document[Id]; - // return ; - // }); } render() { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c71d7ed68..d77e08089 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -162,9 +162,7 @@ export class DocumentView extends DocComponent(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/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 80eadf668..87ebeefdb 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -224,7 +224,6 @@ export class LinkGroupEditor extends React.Component { groupMdKeys.forEach((key) => { let val = StrCast(mdDoc[key]); - console.log(key, val); metadata.push( ); diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index ae3446e25..1dd933c32 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -20,13 +20,28 @@ } .linkMenu-group-name { - padding: 4px 6px; - line-height: 12px; - border-radius: 5px; - font-weight: bold; + display: flex; &:hover { - background-color: lightgray; + p { + background-color: lightgray; + width: calc(100% - 26px); + } + .linkEditor-tableButton { + display: block; + } + } + + p { + width: 100%; + padding: 4px 6px; + line-height: 12px; + border-radius: 5px; + font-weight: bold; + } + + .linkEditor-tableButton { + display: none; } } } diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx index 71326f703..732e76997 100644 --- a/src/client/views/nodes/LinkMenuGroup.tsx +++ b/src/client/views/nodes/LinkMenuGroup.tsx @@ -8,8 +8,10 @@ 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 { DragLinksAsDocuments, DragManager, SetupDrag } from "../../util/DragManager"; import { emptyFunction } from "../../../Utils"; +import { Docs } from "../../documents/Documents"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; interface LinkMenuGroupProps { sourceDoc: Doc; @@ -22,6 +24,7 @@ interface LinkMenuGroupProps { export class LinkMenuGroup extends React.Component { private _drag = React.createRef(); + private _table = React.createRef(); onLinkButtonDown = (e: React.PointerEvent): void => { e.stopPropagation(); @@ -55,6 +58,17 @@ export class LinkMenuGroup extends React.Component { e.stopPropagation(); } + viewGroupAsTable = (groupType: string): JSX.Element => { + let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); + let index = keys.indexOf(""); + if (index > -1) keys.splice(index, 1); + let cols = ["anchor1", "anchor2", ...[...keys]]; + let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); + let createTable = action(() => Docs.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); + let ref = React.createRef(); + return
; + } + render() { let groupItems = this.props.group.map(linkDoc => { let destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc); @@ -64,7 +78,10 @@ export class LinkMenuGroup extends React.Component { return (
-

{this.props.groupType}:

+
+

{this.props.groupType}:

+ {this.viewGroupAsTable(this.props.groupType)} +
{groupItems}
diff --git a/src/client/views/nodes/LinkMenuItem.scss b/src/client/views/nodes/LinkMenuItem.scss index 25d167231..175a93cb2 100644 --- a/src/client/views/nodes/LinkMenuItem.scss +++ b/src/client/views/nodes/LinkMenuItem.scss @@ -1,4 +1,5 @@ @import "../globalCssVariables"; + .linkMenu-item { // border-top: 0.5px solid $main-accent; position: relative; @@ -13,6 +14,7 @@ padding: 4px 6px; line-height: 12px; border-radius: 5px; + overflow-wrap: break-word; } } -- cgit v1.2.3-70-g09d2 From 69e37491908b5c189b94f780994c1f142c69be2e Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 26 Jun 2019 14:15:40 -0400 Subject: minor changes --- src/client/util/DocumentManager.ts | 1 - src/client/util/DragManager.ts | 24 +-------- src/client/util/LinkManager.ts | 18 ++++--- src/client/views/nodes/LinkEditor.scss | 12 ++--- src/client/views/nodes/LinkEditor.tsx | 14 ++++-- src/client/views/nodes/LinkMenu.scss | 84 +++++++++++++++++++++++++++++++ src/client/views/nodes/LinkMenu.tsx | 11 ++-- src/client/views/nodes/LinkMenuItem.scss | 86 -------------------------------- src/client/views/nodes/LinkMenuItem.tsx | 5 +- 9 files changed, 117 insertions(+), 138 deletions(-) delete mode 100644 src/client/views/nodes/LinkMenuItem.scss (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index d7798ebfd..877475347 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -68,7 +68,6 @@ export class DocumentManager { //gets document view that is in a freeform canvas collection DocumentManager.Instance.DocumentViews.map(view => { let doc = view.props.Document; - // if (view.props.ContainingCollectionView instanceof CollectionFreeFormView) { if (doc === toFind) { toReturn.push(view); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 27063d1c2..8e6abe18e 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -71,7 +71,6 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n }); } } - // draggedDocs.push(...draggedFromDocs); if (draggedDocs.length) { let moddrag: Doc[] = []; for (const draggedDoc of draggedDocs) { @@ -79,20 +78,6 @@ 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.moveDocument = (document, targetCollection, addDocument) => { - // return false; - // }; - - // 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], sourceDoc, dragData, x, y, { handlers: { dragComplete: action(emptyFunction), @@ -230,19 +215,14 @@ export namespace DragManager { (dropData: { [id: string]: any }) => { dropData.droppedDocuments = dragData.draggedDocuments.map(d => { let dv = DocumentManager.Instance.getDocumentView(d); - // return d; if (dv) { if (dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) { return d; } else { - let r = Doc.MakeAlias(d); - // DocUtils.MakeLink(r, sourceDoc); - return r; + return Doc.MakeAlias(d); } } else { - let r = Doc.MakeAlias(d); - // DocUtils.MakeLink(r, sourceDoc); - return r; + return Doc.MakeAlias(d); } }); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 97c816001..1db686751 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -151,7 +151,7 @@ export class LinkManager { // removes group doc of given group type only from given anchor on given link public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); - let newGroups = groups.filter(groupDoc => { StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase() }); + let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase()); LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); } @@ -165,23 +165,25 @@ export class LinkManager { if (groups.length > 0) { groups.forEach(groupDoc => { let groupType = StrCast(groupDoc.type); - let group = anchorGroups.get(groupType); - if (group) group.push(link); - else group = [link]; - anchorGroups.set(groupType, group); + if (groupType === "") { + let group = anchorGroups.get("*"); + anchorGroups.set("*", group ? [...group, link] : [link]); + } else { + let group = anchorGroups.get(groupType); + anchorGroups.set(groupType, group ? [...group, link] : [link]); + } }); } else { // if link is in no groups then put it in default group let group = anchorGroups.get("*"); - if (group) group.push(link); - else group = [link]; - anchorGroups.set("*", group); + anchorGroups.set("*", group ? [...group, link] : [link]); } }); return anchorGroups; } + // gets a list of strings representing the keys of the metadata associated with the given group type public getMetadataKeysInGroup(groupType: string): string[] { if (LinkManager.Instance.LinkManagerDoc) { return LinkManager.Instance.LinkManagerDoc[groupType] ? Cast(LinkManager.Instance.LinkManagerDoc[groupType], listSpec("string"), []) : []; diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index 2602b8816..1424d7633 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -10,18 +10,15 @@ margin-bottom: 6px; } -.linKEditor-info { +.linkEditor-info { border-bottom: 0.5px solid $light-color-secondary; padding-bottom: 6px; margin-bottom: 6px; display: flex; justify-content: space-between; - .linkEditor-delete { - width: 20px; - height: 20px; - margin-left: 6px; - padding: 0; + .linkEditor-linkedTo { + width: calc(100% - 26px); } } @@ -105,8 +102,7 @@ cursor: pointer; &:hover { - background-color: $intermediate-color; - font-weight: bold; + background-color: lightgray; } } } diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 87ebeefdb..51efcc36d 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -11,7 +11,6 @@ import { faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTim import { library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SetupDrag } from "../../util/DragManager"; -import { anchorPoints, Flyout } from "../DocumentDecorations"; library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTimes, faPlus); @@ -35,6 +34,11 @@ class GroupTypesDropdown extends React.Component { LinkManager.Instance.addGroupType(groupType); } + onChange = (val: string): void => { + this.setSearchTerm(val); + this.setGroupType(val); + } + renderOptions = (): JSX.Element[] | JSX.Element => { if (this._searchTerm === "") return <>; @@ -59,12 +63,12 @@ class GroupTypesDropdown extends React.Component { render() { return (
- { this.setSearchTerm(e.target.value); this.setGroupType(e.target.value); }}> + this.onChange(e.target.value)}>
{this.renderOptions()}
-
+
); } } @@ -326,7 +330,7 @@ export class LinkEditor extends React.Component {

editing link to: {destination.proto!.title}

- +
Relationships: diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index 1dd933c32..429977326 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -46,5 +46,89 @@ } } +.linkMenu-item { + // border-top: 0.5px solid $main-accent; + position: relative; + display: flex; + font-size: 12px; + + + .link-name { + position: relative; + + p { + padding: 4px 6px; + line-height: 12px; + border-radius: 5px; + overflow-wrap: break-word; + } + } + + .linkMenu-item-content { + width: 100%; + } + + .link-metadata { + padding: 0 10px 0 16px; + margin-bottom: 4px; + color: $main-accent; + font-style: italic; + font-size: 10.5px; + } + + &:hover { + .linkMenu-item-buttons { + display: flex; + } + .linkMenu-item-content { + &.expand-two p { + width: calc(100% - 52px); + background-color: lightgray; + } + &.expand-three p { + width: calc(100% - 84px); + background-color: lightgray; + } + } + } +} + +.linkMenu-item-buttons { + display: none; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + + .button { + width: 20px; + height: 20px; + margin: 0; + margin-right: 6px; + border-radius: 50%; + cursor: pointer; + pointer-events: auto; + background-color: $dark-color; + color: $light-color; + font-size: 65%; + transition: transform 0.2s; + text-align: center; + position: relative; + + .fa-icon { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + &:last-child { + margin-right: 0; + } + &:hover { + background: $main-accent; + } + } +} diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 04ca47db3..8ef899cfc 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -1,15 +1,11 @@ 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 { Doc } from "../../../new_fields/Doc"; import { LinkManager } from "../../util/LinkManager"; -import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; -import { emptyFunction } from "../../../Utils"; import { LinkMenuGroup } from "./LinkMenuGroup"; interface Props { @@ -22,6 +18,11 @@ export class LinkMenu extends React.Component { @observable private _editingLink?: Doc; + @action + componentWillReceiveProps() { + this._editingLink = undefined; + } + renderAllGroups = (groups: Map>): Array => { let linkItems: Array = []; groups.forEach((group, groupType) => { diff --git a/src/client/views/nodes/LinkMenuItem.scss b/src/client/views/nodes/LinkMenuItem.scss deleted file mode 100644 index 175a93cb2..000000000 --- a/src/client/views/nodes/LinkMenuItem.scss +++ /dev/null @@ -1,86 +0,0 @@ -@import "../globalCssVariables"; - -.linkMenu-item { - // border-top: 0.5px solid $main-accent; - position: relative; - display: flex; - font-size: 12px; - - - .link-name { - position: relative; - - p { - padding: 4px 6px; - line-height: 12px; - border-radius: 5px; - overflow-wrap: break-word; - } - } - - .linkMenu-item-content { - width: 100%; - } - - .link-metadata { - padding: 0 10px 0 16px; - margin-bottom: 4px; - color: $main-accent; - font-style: italic; - font-size: 10.5px; - } - - &:hover { - .linkMenu-item-buttons { - display: flex; - } - .linkMenu-item-content { - &.expand-two p { - width: calc(100% - 52px); - background-color: lightgray; - } - &.expand-three p { - width: calc(100% - 84px); - background-color: lightgray; - } - } - } -} - -.linkMenu-item-buttons { - display: none; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - - .button { - width: 20px; - height: 20px; - margin: 0; - margin-right: 6px; - border-radius: 50%; - cursor: pointer; - pointer-events: auto; - background-color: $dark-color; - color: $light-color; - font-size: 65%; - transition: transform 0.2s; - text-align: center; - position: relative; - - .fa-icon { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - - &:last-child { - margin-right: 0; - } - &:hover { - background: $main-accent; - } - } -} \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index 28694721d..486e3dc9b 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -4,14 +4,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { observer } from "mobx-react"; import { DocumentManager } from "../../util/DocumentManager"; import { undoBatch } from "../../util/UndoManager"; -import './LinkMenuItem.scss'; +import './LinkMenu.scss'; import React = require("react"); import { Doc } from '../../../new_fields/Doc'; import { StrCast, Cast } from '../../../new_fields/Types'; import { observable, action } from 'mobx'; import { LinkManager } from '../../util/LinkManager'; -import { DragLinksAsDocuments, DragLinkAsDocument } from '../../util/DragManager'; -import { SelectionManager } from '../../util/SelectionManager'; +import { DragLinkAsDocument } from '../../util/DragManager'; import { CollectionDockingView } from '../collections/CollectionDockingView'; library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); -- cgit v1.2.3-70-g09d2 From a81677c7dffafa5134d4c5cbe893f7a886eaab63 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 26 Jun 2019 14:48:16 -0400 Subject: can clear links on a doc --- src/client/util/LinkManager.ts | 5 +++++ src/client/views/nodes/LinkEditor.scss | 14 +++++++++----- src/client/views/nodes/LinkEditor.tsx | 1 + src/client/views/nodes/LinkMenu.scss | 4 ++++ src/client/views/nodes/LinkMenu.tsx | 10 ++++++++++ 5 files changed, 29 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 1db686751..f2f3e51dd 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -75,6 +75,11 @@ export class LinkManager { return related; } + public deleteAllLinksOnAnchor(anchor: Doc) { + let related = LinkManager.Instance.getAllRelatedLinks(anchor); + related.forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); + } + public addGroupType(groupType: string): boolean { if (LinkManager.Instance.LinkManagerDoc) { LinkManager.Instance.LinkManagerDoc[groupType] = new List([]); diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index 1424d7633..3c49c2212 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -47,8 +47,8 @@ border-radius: 3px; .linkEditor-group-row { - display: flex; - margin-bottom: 6px; + // display: flex; + margin-bottom: 3px; .linkEditor-group-row-label { margin-right: 6px; @@ -65,16 +65,16 @@ } input { - width: calc(50% - 18px); + width: calc(50% - 16px); height: 20px; } button { width: 20px; height: 20px; - margin-left: 6px; + margin-left: 3px; padding: 0; - font-size: 14px; + font-size: 10px; } } } @@ -85,6 +85,10 @@ position: relative; z-index: 999; + input { + width: 100%; + } + .linkEditor-options-wrapper { width: 100%; position: absolute; diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 51efcc36d..22da732cf 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -278,6 +278,7 @@ export class LinkGroupEditor extends React.Component {

type:

+ {this.renderMetadata().length > 0 ?

metadata:

: <>} {this.renderMetadata()}
{buttons} diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index 429977326..7cc11172b 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -131,4 +131,8 @@ } } +.linkEditor-clearButton { + float: right; +} + diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 8ef899cfc..71384c368 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -7,6 +7,11 @@ import React = require("react"); import { Doc } from "../../../new_fields/Doc"; import { LinkManager } from "../../util/LinkManager"; import { LinkMenuGroup } from "./LinkMenuGroup"; +import { faTrash } from '@fortawesome/free-solid-svg-icons'; +import { library } from "@fortawesome/fontawesome-svg-core"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +library.add(faTrash) interface Props { docView: DocumentView; @@ -23,6 +28,10 @@ export class LinkMenu extends React.Component { this._editingLink = undefined; } + clearAllLinks = () => { + LinkManager.Instance.deleteAllLinksOnAnchor(this.props.docView.props.Document); + } + renderAllGroups = (groups: Map>): Array => { let linkItems: Array = []; groups.forEach((group, groupType) => { @@ -43,6 +52,7 @@ export class LinkMenu extends React.Component { if (this._editingLink === undefined) { return (
+ {/* */}
{this.renderAllGroups(groups)} -- cgit v1.2.3-70-g09d2 From 681ba524496d40aecb832fc79d68d7695435aed8 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 26 Jun 2019 16:05:24 -0400 Subject: fixed link alias dragging --- src/client/util/DocumentManager.ts | 6 +- src/client/util/DragManager.ts | 25 +- src/client/views/nodes/LinkMenu.scss | 5 +- src/client/views/nodes/LinkMenu.tsx | 2 +- src/client/views/nodes/LinkMenuGroup.tsx | 7 +- ...357\200\277 1 \357\200\272 0], targetContext);" | 792 +++++++++++++++++++++ 6 files changed, 816 insertions(+), 21 deletions(-) create mode 100644 "tance.jumpToDocument(linkedFwdDocs[altKey \357\200\277 1 \357\200\272 0], ctrlKey, false, document =\357\200\276 this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey \357\200\277 1 \357\200\272 0], targetContext);" (limited to 'src') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 877475347..fed30bbdc 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -72,11 +72,7 @@ export class DocumentManager { if (doc === toFind) { toReturn.push(view); } else { - // if (Doc.AreProtosEqual(doc, toFind)) { - // toReturn.push(view); - - let docSrc = FieldValue(doc.proto); - if (docSrc && Object.is(docSrc, toFind)) { + if (Doc.AreProtosEqual(doc, toFind)) { toReturn.push(view); } } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 8e6abe18e..5c75c8fe5 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -9,6 +9,7 @@ import { URLField } from "../../new_fields/URLField"; import { SelectionManager } from "./SelectionManager"; import { Docs, DocUtils } from "../documents/Documents"; import { DocumentManager } from "./DocumentManager"; +import { Id } from "../../new_fields/FieldSymbols"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc | Promise, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, options?: any, dontHideOnDrop?: boolean) { @@ -213,19 +214,25 @@ export namespace DragManager { 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); - if (dv) { - if (dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) { - return d; + // dropData.droppedDocuments = + console.log(dragData.draggedDocuments.length); + let droppedDocuments: Doc[] = dragData.draggedDocuments.reduce((droppedDocs: Doc[], d) => { + let dvs = DocumentManager.Instance.getDocumentViews(d); + console.log(StrCast(d.title), dvs.length); + + if (dvs.length) { + let inContext = dvs.filter(dv => dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView); + if (inContext.length) { + inContext.forEach(dv => droppedDocs.push(dv.props.Document)); } else { - return Doc.MakeAlias(d); + droppedDocs.push(Doc.MakeAlias(d)); } } else { - return Doc.MakeAlias(d); + droppedDocs.push(Doc.MakeAlias(d)); } - }); - + return droppedDocs; + }, []); + dropData.droppedDocuments = droppedDocuments; }); } diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index 7cc11172b..a4018bd2d 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -25,6 +25,8 @@ &:hover { p { background-color: lightgray; + } + p.expand-one { width: calc(100% - 26px); } .linkEditor-tableButton { @@ -131,8 +133,5 @@ } } -.linkEditor-clearButton { - float: right; -} diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 71384c368..68fde17a0 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -11,7 +11,7 @@ import { faTrash } from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -library.add(faTrash) +library.add(faTrash); interface Props { docView: DocumentView; diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx index 732e76997..f4e0b8931 100644 --- a/src/client/views/nodes/LinkMenuGroup.tsx +++ b/src/client/views/nodes/LinkMenuGroup.tsx @@ -79,13 +79,14 @@ export class LinkMenuGroup extends React.Component { return (
-

{this.props.groupType}:

- {this.viewGroupAsTable(this.props.groupType)} +

{this.props.groupType}:

+ {this.props.groupType === "*" || this.props.groupType === "" ? <> : this.viewGroupAsTable(this.props.groupType)}
{groupItems}
-
+
); } } \ No newline at end of file diff --git "a/tance.jumpToDocument(linkedFwdDocs[altKey \357\200\277 1 \357\200\272 0], ctrlKey, false, document =\357\200\276 this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey \357\200\277 1 \357\200\272 0], targetContext);" "b/tance.jumpToDocument(linkedFwdDocs[altKey \357\200\277 1 \357\200\272 0], ctrlKey, false, document =\357\200\276 this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey \357\200\277 1 \357\200\272 0], targetContext);" new file mode 100644 index 000000000..0aa3ad47b --- /dev/null +++ "b/tance.jumpToDocument(linkedFwdDocs[altKey \357\200\277 1 \357\200\272 0], ctrlKey, false, document =\357\200\276 this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey \357\200\277 1 \357\200\272 0], targetContext);" @@ -0,0 +1,792 @@ +commit cc1f3b32d60786b56280a8b3c00059aa7823af89 +Merge: a81677c deb8576 +Author: Fawn +Date: Wed Jun 26 14:54:46 2019 -0400 + + merge + +commit a81677c7dffafa5134d4c5cbe893f7a886eaab63 +Author: Fawn +Date: Wed Jun 26 14:48:16 2019 -0400 + + can clear links on a doc + +commit 69e37491908b5c189b94f780994c1f142c69be2e +Author: Fawn +Date: Wed Jun 26 14:15:40 2019 -0400 + + minor changes + +commit deb85766ac5648cc8e3ab4bf9d182ac5bbbbe144 +Merge: 219cabb 5e47775 +Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> +Date: Wed Jun 26 12:51:18 2019 -0400 + + Merge pull request #170 from browngraphicslab/presentation-selection-mohammad + + Presentation selection mohammad + +commit 5e477755b392128ab8b39c082f16dd67708be0d2 +Merge: 444f970 6d1f161 +Author: Sam Wilkins +Date: Wed Jun 26 12:48:45 2019 -0400 + + Merge branch 'presentation-selection-mohammad' of https://github.com/browngraphicslab/Dash-Web into presentation-selection-mohammad + +commit 444f970365a4280376e929e78c16090f6ae92739 +Merge: 64ffa0a 219cabb +Author: Sam Wilkins +Date: Wed Jun 26 12:48:40 2019 -0400 + + merged with master + +commit 6d1f161de3c27ec07673b5e48a915961177b57b6 +Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> +Date: Wed Jun 26 12:39:54 2019 -0400 + + long line wrap + +commit f0632e4f6b608d05ef6d9f77d93da259c58c1e8d +Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> +Date: Wed Jun 26 12:33:16 2019 -0400 + + long line wrap + +commit 0d5e2537520ca1e6a6b52f4d0f03aa2bcfc6c5c6 +Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> +Date: Wed Jun 26 12:30:16 2019 -0400 + + cleanup + +commit 8954bac59b50aa3618625379a17dbefe9aceca72 +Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> +Date: Wed Jun 26 12:29:07 2019 -0400 + + removed console.logs + +commit d0ff42632f8a155303e11945a1a974a15052f0db +Author: Fawn +Date: Wed Jun 26 11:40:36 2019 -0400 + + link menu styling + +commit a3c4aa24a9e9074da8f2421954f610c8178e10b1 +Author: Fawn +Date: Tue Jun 25 21:28:15 2019 -0400 + + link metadata values appear on first load + +commit ca8a78de9957ad27d345ad51fdaee9dae3f096bd +Author: Fawn +Date: Tue Jun 25 20:44:34 2019 -0400 + + can't link to containing collection + +commit 2d300b0cd3d02c900865c61eacd539efed5289e6 +Author: Fawn +Date: Tue Jun 25 20:18:14 2019 -0400 + + fixed link metadata rendering bug + +commit 2a698e88da5ef0a9fee1ff4ee69746f1242798c9 +Author: Fawn +Date: Tue Jun 25 18:32:17 2019 -0400 + + fixed render links in treeview + +commit 7abe170ce5bd0c415e23456eb2bed26e8fdee7aa +Merge: 41cf1e8 219cabb +Author: Fawn +Date: Tue Jun 25 18:23:26 2019 -0400 + + merge + +commit 41cf1e8536964764f18ab752140e484e36cbe464 +Author: Fawn +Date: Tue Jun 25 17:09:36 2019 -0400 + + links can save + +commit 64ffa0accfc872c81035079527952aabaf56c6f6 +Author: Mohammad Amoush +Date: Tue Jun 25 13:16:45 2019 -0400 + + Small Css Fix On weight + +commit 219cabb3fe42ab199550efc3423b7aaed4e1ee93 +Author: Tyler Schicke +Date: Mon Jun 24 22:45:19 2019 -0400 + + Switched shift drag of tabs to normal drag and added drag target for document drag + +commit d475b19e9ba7bc8870ec7bc1e10b5cc88decea0b +Author: Tyler Schicke +Date: Mon Jun 24 15:56:42 2019 -0400 + + fixed crash + +commit 522970375fe0227f9221a7e8be02875afd74ca63 +Author: Fawn +Date: Mon Jun 24 14:01:29 2019 -0400 + + link menu styling + +commit addf0e443f64951a437701f0d5a087c1d5968faf +Merge: c9f77d5 d01039b +Author: tschicke-brown +Date: Mon Jun 24 13:57:02 2019 -0400 + + Merge pull request #167 from browngraphicslab/schema_fixes + + Schema and scripting fixes + +commit d01039b10f0ebd328224c0b1a190b0f884a7c727 +Merge: 6abf829 c9f77d5 +Author: Tyler Schicke +Date: Mon Jun 24 13:56:30 2019 -0400 + + Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web into schema_fixes + +commit c9f77d5aab98e6e7865cdcad957d5c937631775d +Author: Tyler Schicke +Date: Mon Jun 24 13:41:39 2019 -0400 + + Added ReadOnly mode for docs and changed computed values a bit + +commit e18662f2fa9e1d3dd1b0eb3b5531092258d05972 +Author: Mohammad Amoush +Date: Mon Jun 24 12:42:44 2019 -0400 + + Refactoring + +commit 52051829373bc4acfe9d705b64c30e3fddebf439 +Author: Tyler Schicke +Date: Mon Jun 24 10:49:05 2019 -0400 + + Fixed image size stuff + +commit ac781d2fb714ca26fb364d00d5aeb7a20b008655 +Author: Tyler Schicke +Date: Mon Jun 24 10:26:57 2019 -0400 + + Changed how zooming works + +commit 6e5cd0e991e2e6d7ae8de1d73ff273ba0737355c +Author: Tyler Schicke +Date: Sun Jun 23 17:23:33 2019 -0400 + + Fixed shift dragging with no open panes + +commit 32ef8d83d5829e2faadbebaf6f9b694df5d7ea02 +Author: Fawn +Date: Fri Jun 21 17:41:20 2019 -0400 + + link menu styling + +commit 7962aff8431b692af5229cd8e6c390bbe1110336 +Author: Fawn +Date: Fri Jun 21 16:29:31 2019 -0400 + + link menu styling + +commit a4b34adcb34184728be0b69b33a561f6d10f0a98 +Author: Fawn +Date: Fri Jun 21 16:27:03 2019 -0400 + + can drag just a group of links on a doc + +commit e1f5f341854944c533efdb7d36306edd1e1dc747 +Author: Mohammad Amoush +Date: Fri Jun 21 14:53:08 2019 -0400 + + Some More documentation + +commit 542f25d4af36cf0948696d45afba2e9e19f5bc37 +Author: Mohammad Amoush +Date: Fri Jun 21 14:47:11 2019 -0400 + + Redo Grouping Fixed + +commit 60f9122ea31d660d60d5429890c4eb0ef6d8613b +Author: Fawn +Date: Fri Jun 21 13:41:25 2019 -0400 + + following link without viewdoc opens it to right + +commit d78c651322ad228152b862eaa378946fe65cc9f9 +Author: Fawn +Date: Fri Jun 21 13:32:23 2019 -0400 + + dragged links from menu are aliases + +commit 179afa6e80631fcb8899408c3961bf1757e5b19b +Merge: ca5e29f a40e7bb +Author: Bob Zeleznik +Date: Thu Jun 20 22:23:40 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit ca5e29fdc7c238274eaf90682a8fa2ddc90e4e17 +Author: Bob Zeleznik +Date: Thu Jun 20 22:22:57 2019 -0400 + + fix to open on right, fix to image drag fro web, and layout fixes for stacking view multi-column + +commit a40e7bb5e9d1256002083d7e3f3c4db60cd8e9df +Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> +Date: Thu Jun 20 19:41:39 2019 -0400 + + Fixed missed pointer up event + +commit f4b75a7c921181faeeee04fbd57cd24fbd57523e +Author: Mohammad Amoush +Date: Thu Jun 20 19:16:42 2019 -0400 + + Undo/Redo First Version + +commit b1a2871fcca57ce934b8613b315a08eede188669 +Author: Fawn +Date: Thu Jun 20 19:03:16 2019 -0400 + + link menu styling + +commit f2b54dc49205f8ea8944e26e43662a0c8dd08ed0 +Merge: 0cab79a 7d0f6c1 +Author: Tyler Schicke +Date: Thu Jun 20 18:36:04 2019 -0400 + + Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web + +commit 0cab79a50719719e1dade40520a6967f7aa8f951 +Author: Tyler Schicke +Date: Thu Jun 20 18:35:45 2019 -0400 + + Added debug and release modes to server and client + +commit fbfe9faca199b6dedd6844f1fa20cc02060a3c5a +Author: Fawn +Date: Thu Jun 20 18:25:49 2019 -0400 + + can see what docs are linked to in treeview: + +commit 7d0f6c18489f7155818611721985d9610b08d8e7 +Merge: d2dfc0f 46a2a9e +Author: yipstanley +Date: Thu Jun 20 17:50:46 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit 1f172642d12c4669960b8526324e4bd034994be4 +Author: Tyler Schicke +Date: Thu Jun 20 17:44:24 2019 -0400 + + Added arrange documents in grid command + +commit d2dfc0f9d35f0084a7c0dea73215f5d21055f2f3 +Author: yipstanley +Date: Thu Jun 20 17:17:14 2019 -0400 + + pdf page sizes loading error + +commit e6ebed17e6ddb2ccee81d65fcb451a9b54302762 +Author: Fawn +Date: Thu Jun 20 17:12:48 2019 -0400 + + links can be made from freeform view to treeview + +commit 46a2a9e1f10b63feeb21a1e186daeaef2ccbcda4 +Merge: a39b285 a5dc0e0 +Author: bob +Date: Thu Jun 20 17:11:29 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit a39b2854b848006c19460685d7bf4005a9f650ae +Author: bob +Date: Thu Jun 20 17:09:50 2019 -0400 + + moved AddDocToList to Doc utils + +commit a5dc0e04add05f2f5bf1e17f1ac0a5e0aba1ea41 +Author: Tyler Schicke +Date: Thu Jun 20 16:27:44 2019 -0400 + + Added hidden flag to documents + +commit e88538bb8af2ba648da2326d0f6edd3e0186766e +Author: Mohammad Amoush +Date: Thu Jun 20 15:45:07 2019 -0400 + + Title changing to presentations added + +commit 9b3e80def0be6c09c31b5176817a54323d217d81 +Author: Tyler Schicke +Date: Thu Jun 20 15:06:41 2019 -0400 + + Handled more events in editable view + +commit 1f24c5010a1cf6365265ea1f02327bb81a98134a +Author: Tyler Schicke +Date: Thu Jun 20 14:54:55 2019 -0400 + + Doc.GetProto change and swapped KVP syntax + +commit 4360287e6cafcb59af1ae62fc31ddc161bcf2e51 +Author: Fawn +Date: Thu Jun 20 12:56:13 2019 -0400 + + styling of link proxy + +commit 711abbeba69e4d9afc634b8edf019b12b6dff915 +Author: Mohammad Amoush +Date: Thu Jun 20 12:54:41 2019 -0400 + + Documentation and reset Presentation at removal fixed + +commit a0246ef84396545f79fc4a8b21de1a56cbf06aca +Author: Fawn +Date: Thu Jun 20 11:34:28 2019 -0400 + + merge + +commit 8dbfb3029a99eaf37a5234e9d9e33cc64f779b03 +Merge: af8e5cf e9d62f4 +Author: Tyler Schicke +Date: Thu Jun 20 11:33:01 2019 -0400 + + Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web + +commit af8e5cf1bfbfa2d57b4fd89c72306a71d8cabe1d +Author: Tyler Schicke +Date: Thu Jun 20 11:32:54 2019 -0400 + + Fixed context menu search + +commit cd2db5bf11fb89e3cd7016f7f798d65698c74c5e +Merge: 73f0378 e9d62f4 +Author: Fawn +Date: Thu Jun 20 11:31:15 2019 -0400 + + merge + +commit 73f03785f938542a91b28b35043f2feda2bc1432 +Author: Fawn +Date: Thu Jun 20 11:26:33 2019 -0400 + + merge + +commit e9d62f4ca0dbeb57e46239047041a8a04da7b504 +Author: bob +Date: Thu Jun 20 11:26:16 2019 -0400 + + changed color picker. fixed delting selected docs. fixed scaling items in nested panels. + +commit a5478b2d4cc3b66c6b58471cbb05c623d0109724 +Author: Tyler Schicke +Date: Thu Jun 20 10:04:51 2019 -0400 + + "Fixed" search + +commit 01aee875e626c695fe208addaaa6f58aad387dd6 +Author: Tyler Schicke +Date: Thu Jun 20 10:02:08 2019 -0400 + + Mostly keep context menu on screen + +commit 38de022621175bda7410df4444fcd2bbee0919cb +Author: Bob Zeleznik +Date: Wed Jun 19 23:43:47 2019 -0400 + + slight tweaks. + +commit 9e55bfaad39aa47ab0594c6af7f1aa68e2a8db7a +Merge: 118ecb1 827c589 +Author: Bob Zeleznik +Date: Wed Jun 19 22:40:57 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit 118ecb14ce519bcbade12b3d52e11b22fcc371b3 +Author: Bob Zeleznik +Date: Wed Jun 19 22:40:54 2019 -0400 + + cleaned up and enhanced tree view + +commit c5e401cb0a7fec2279ceecbc8d1429dcdd2f04b9 +Author: Fawn +Date: Wed Jun 19 22:27:21 2019 -0400 + + buttons on cut links functional except for when dragged from link menu + +commit 6fc6054dc7aea144fd967a8cb3fe7d8fe5ec6d6d +Author: Mohammad Amoush +Date: Wed Jun 19 19:13:30 2019 -0400 + + Width of the presentations fixed, removal of presentations option added, backUP group and normal groups updated when a doc is removed from presentation by removing it from both + +commit 827c58950b649629c84211d41fdd4d041287801e +Merge: 05e50f2 96c26c5 +Author: Tyler Schicke +Date: Wed Jun 19 18:49:50 2019 -0400 + + Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web + +commit 96c26c57527d443784bde9752551bfa10b3ce4d2 +Author: Bob Zeleznik +Date: Wed Jun 19 18:34:45 2019 -0400 + + removed marquee summarizing icon + +commit 05e50f27a15e8a02ffb27606c51026d1b85bc677 +Author: Tyler Schicke +Date: Wed Jun 19 17:36:52 2019 -0400 + + Added basic keyboard controls to context menu + +commit fa37e023b88127cb8a6b393a848200361a396fb4 +Merge: 565b27c 5b2a498 +Author: Tyler Schicke +Date: Wed Jun 19 16:21:09 2019 -0400 + + Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web + +commit 565b27cca8953a60067de367cae4c0a99beb3cab +Author: Tyler Schicke +Date: Wed Jun 19 16:21:03 2019 -0400 + + started adding selection to context menu + +commit 5b2a498aca75bd53ffab61f998218bec546b8154 +Merge: 358437e 39e8a7a +Author: bob +Date: Wed Jun 19 16:17:21 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit 358437eeafe42e029ffe27702bde15a3fad54a3b +Author: bob +Date: Wed Jun 19 16:17:18 2019 -0400 + + working version of embedded tree view docs. + +commit 4c1383e47f2203a00bc7f3d73c209f3149d6a772 +Author: Mohammad Amoush +Date: Wed Jun 19 15:53:05 2019 -0400 + + ... + +commit a288a2fd0a30a3a16dd01bc4e12dcf6bc117c766 +Author: Mohammad Amoush +Date: Wed Jun 19 15:25:24 2019 -0400 + + Navigation and Zoom Option For Manual Selection Added and New Presentation TItle Naming Added + + Now, You can manually click on navigate or zoom and navigate to that document if current was their index. A way to manually disregard groups, and just navigate to that doc. + +commit 39e8a7a365442cdc11024c4de8019184fd0057ac +Merge: 5b6f13d 9ab4739 +Author: Stanley Yip <33562077+yipstanley@users.noreply.github.com> +Date: Wed Jun 19 15:05:38 2019 -0400 + + Merge pull request #163 from browngraphicslab/pdf_fixes + + deleting annotations + +commit 5b6f13d64e9e38b94df0ae61ffedcb0b34290045 +Merge: 35e73f3 4ebbdd8 +Author: Tyler Schicke +Date: Wed Jun 19 15:04:46 2019 -0400 + + Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web + +commit 35e73f369a2145d8a042e0011a43e71763d57998 +Author: Tyler Schicke +Date: Wed Jun 19 15:02:48 2019 -0400 + + added better search to context menu + +commit 9ab47393a2ce3d174ad3238422c2c310764be9af +Author: yipstanley +Date: Wed Jun 19 14:40:28 2019 -0400 + + interaction improvements with delete button + +commit b9849810231e540a5898a56012abd32c197b23b5 +Author: yipstanley +Date: Wed Jun 19 14:39:15 2019 -0400 + + anna + +commit b960a876d6a31b3eaebb0ac6eca6f191a0d4c900 +Author: yipstanley +Date: Wed Jun 19 14:38:43 2019 -0400 + + oop + +commit 46d57bc21cda4703855b85a4603bd471975d845b +Author: yipstanley +Date: Wed Jun 19 14:25:47 2019 -0400 + + deleting annotations + +commit f362dbfc237536c6c4a8c6d088c3dc818080f7c2 +Author: Fawn +Date: Wed Jun 19 12:50:58 2019 -0400 + + both tail ends of a cut link appear on hover/focus of an anchor + +commit fb62f3b2e39bbe2dd3da5eaffedbaa8e60f06dbb +Author: Mohammad Amoush +Date: Wed Jun 19 12:35:54 2019 -0400 + + Grouping for different presentations fixed + +commit 4ebbdd803cdf83806902509dfa0432ce3a139403 +Merge: 0bb2052 c056ade +Author: Stanley Yip <33562077+yipstanley@users.noreply.github.com> +Date: Wed Jun 19 11:48:16 2019 -0400 + + Merge pull request #162 from browngraphicslab/pdf_fixes + + Pdf fixes + +commit c056adeca11f35972b5f75c6b1cc31292d5765d4 +Author: yipstanley +Date: Wed Jun 19 11:47:20 2019 -0400 + + push + +commit 37f327ab659e6fa1221f9f4ed7649402c5dedc00 +Author: yipstanley +Date: Wed Jun 19 11:24:32 2019 -0400 + + aspect ratio, dragging, and full screen scrolling fixed + +commit 0bb20528c8167b3ba1c4c88d97586d50ae183b4c +Author: bob +Date: Wed Jun 19 10:37:36 2019 -0400 + + added highlight for expanded tree view items + +commit f60398d5db9041e09c809c16a0b885936ac11a3d +Author: bob +Date: Wed Jun 19 10:21:37 2019 -0400 + + fixed multi-column stacking + +commit 0674331f3611d297028526c888c718a75b012e0a +Author: bob +Date: Wed Jun 19 09:36:21 2019 -0400 + + fixed resizing stacking views. changed defaults for new docs in treeView + +commit 1472d2b56aa64896f0a93f172322121d19cd1592 +Author: bob +Date: Wed Jun 19 09:11:35 2019 -0400 + + fixed lint errors. + +commit 8c94bb92b23dea138fa752929b6134e7214dfb60 +Merge: 3b880d7 13e301d +Author: Bob Zeleznik +Date: Tue Jun 18 22:51:48 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit 3b880d7b15b7107049ae27601b9f759b17f7fde9 +Author: Bob Zeleznik +Date: Tue Jun 18 22:51:46 2019 -0400 + + added initial keyboard shortcuts for adding and moving docs in TreeView. fixed image drag bug. + +commit 13e301dea2f537b67b338cc6a98d3f3b5a8e1f36 +Author: Tyler Schicke +Date: Tue Jun 18 20:58:32 2019 -0400 + + Fixed linter errors + +commit 464fa03d6ebb2a7aaef1d7622afa3e1e7ee816a3 +Author: Tyler Schicke +Date: Tue Jun 18 20:11:31 2019 -0400 + + Context menu improvements and error fixes + +commit 4ffcff69a2fc767c6a03d46d7296b6a8c7ffd281 +Author: madelinegr +Date: Tue Jun 18 19:13:45 2019 -0400 + + Presentations Listed, Option to Change Added, and + +commit ca126adda9e4def83fb5c2e07e382917ca0b4ee0 +Author: Tyler Schicke +Date: Tue Jun 18 17:24:59 2019 -0400 + + Fixed docking view? + +commit b0ac30172019713e1c75083c1199485d902e0eed +Author: Tyler Schicke +Date: Tue Jun 18 16:37:28 2019 -0400 + + Fixed zoomBasis stuff and added deletion handling for reponse from server + +commit 8e5afb5bbb47324a381b5184254e77eba7bd8536 +Author: Fawn +Date: Tue Jun 18 16:30:24 2019 -0400 + + can click on button link to node in different context than source + +commit 6fcd0d8d6fb1471b8af460f6d80bdf0d0e681566 +Author: Fawn +Date: Tue Jun 18 15:17:27 2019 -0400 + + added button to delete a link + +commit d91e7eec9a62363b383b929166cdf600b124334c +Author: Fawn +Date: Tue Jun 18 15:09:21 2019 -0400 + + links to nodes in different contexts render as a circle + +commit d3cad099d49690810166d0342f7c371bda0f007e +Merge: 04668e2 b1af251 +Author: bob +Date: Tue Jun 18 13:30:55 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit 04668e21313f6e62e5ab35ac737fc54191769a5a +Author: bob +Date: Tue Jun 18 13:30:41 2019 -0400 + + fixed cleanup of marquee keyhandler. + +commit b1af251b058743798aa3fa3895d22327c8560dfc +Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> +Date: Tue Jun 18 13:19:50 2019 -0400 + + Added pointer down flag for tab focus + +commit 9544576ec0167d64f564ae4c87d392eba07ff467 +Author: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> +Date: Tue Jun 18 13:18:34 2019 -0400 + + Added tab focusing on hover + +commit 2633f61d311528e62d50d4ff56f5884b3b51ac61 +Author: bob +Date: Tue Jun 18 13:12:15 2019 -0400 + + added undo/redo bindings for app. + +commit 3a25bad918c72f5d6de9a720de9e0d316c00f2fe +Author: bob +Date: Tue Jun 18 13:03:28 2019 -0400 + + fixed issues with expanding text boxes that have a dynamic title + +commit f4fcf306e2579b7479610899a01c06fb157d47de +Author: bob +Date: Tue Jun 18 12:03:14 2019 -0400 + + fixed goldenlayout nesting + +commit 4f0086f6ea948c1c5254db2acc93f6735987daa5 +Merge: 749eef1 d7ebe7b +Author: bob +Date: Tue Jun 18 11:31:49 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit 749eef13af1338225b2bec4dbcd7a50a5650d285 +Author: bob +Date: Tue Jun 18 11:31:46 2019 -0400 + + fixed image drag drop when not selected. + +commit d7ebe7b7d19cf7dc797443aa485293670c3ee4e2 +Merge: 66d4cc9 08872de +Author: yipstanley +Date: Tue Jun 18 11:08:44 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit 66d4cc94bcc69f590d90dd35823f93b8e2fb90d8 +Author: yipstanley +Date: Tue Jun 18 10:52:10 2019 -0400 + + selection fixes + +commit 08872def596af073c5f14336c8faf07f44561bbc +Merge: 8d00265 c50ba1c +Author: bob +Date: Tue Jun 18 10:28:31 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit 8d0026573ad9a196f864490bcf07c78f54082bad +Author: bob +Date: Tue Jun 18 10:28:29 2019 -0400 + + fixed selection within multicolumn stacking view. added drop of html image selections. + +commit c50ba1c4cc01d5cd085dee0dae6f633164efeb80 +Merge: cc032e2 64e6a94 +Author: yipstanley +Date: Tue Jun 18 10:10:58 2019 -0400 + + Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web + +commit cc032e2f60015728f64f46ef009c9306e36746a0 +Author: yipstanley +Date: Tue Jun 18 10:05:49 2019 -0400 + + fixes + +commit 64e6a941639aab8d7109178aa151a50909547309 +Author: Bob Zeleznik +Date: Tue Jun 18 09:05:41 2019 -0400 + + fixed index out of range + +commit 4b8324fcf44c5d3c3a4b3f6e98a4d1dfce84811b +Author: Bob Zeleznik +Date: Tue Jun 18 08:53:01 2019 -0400 + + removed trace + +commit a3b8a57027d7c45ea19d259e1ec18fa6a8648c24 +Author: Bob Zeleznik +Date: Tue Jun 18 08:49:02 2019 -0400 + + looked like wrong code... + +commit 2f5c38c6a0a5220c2a31931c34d94e199854d703 +Author: Bob Zeleznik +Date: Tue Jun 18 08:36:37 2019 -0400 + + more streamlining + +commit 62c781c0c79ac395c5e117d208a90485ff1ba599 +Author: Bob Zeleznik +Date: Tue Jun 18 02:19:07 2019 -0400 + + faster loading of PDFs + +commit 4dc8c03562a0473becb895824740da487e16e771 +Author: Bob Zeleznik +Date: Tue Jun 18 00:17:58 2019 -0400 + + added dropping of Dash urls from gmail + +commit 9c7ff72a8ad249c05b672a46e3fbbb69ffca3a2a +Merge: 8c64ffd 71b1cfb +Author: Tyler Schicke +Date: Mon Jun 17 23:04:22 2019 -0400 + + Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web + +commit 8c64ffd92e382050bc8727981cf9fb830e4f02a7 +Author: Tyler Schicke +Date: Mon Jun 17 23:04:07 2019 -0400 + + Added share with user functionality -- cgit v1.2.3-70-g09d2 From fcba28b1f826da50729e3a005f5fcac7a3c4316c Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 26 Jun 2019 17:27:48 -0400 Subject: cant link to user doc --- src/client/documents/Documents.ts | 1 + src/client/util/DragManager.ts | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ddbf8f753..7b14ae037 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -73,6 +73,7 @@ export namespace DocUtils { if (LinkManager.Instance.doesLinkExist(source, target)) return; let sv = DocumentManager.Instance.getDocumentView(source); if (sv && sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === target) return; + if (target === CurrentUserUtils.UserDocument) return; UndoManager.RunInBatch(() => { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 5c75c8fe5..ddc10d38a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -215,10 +215,8 @@ export namespace DragManager { StartDrag(eles, dragData, downX, downY, options, (dropData: { [id: string]: any }) => { // dropData.droppedDocuments = - console.log(dragData.draggedDocuments.length); let droppedDocuments: Doc[] = dragData.draggedDocuments.reduce((droppedDocs: Doc[], d) => { let dvs = DocumentManager.Instance.getDocumentViews(d); - console.log(StrCast(d.title), dvs.length); if (dvs.length) { let inContext = dvs.filter(dv => dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView); -- cgit v1.2.3-70-g09d2