diff options
Diffstat (limited to 'src/client/util')
| -rw-r--r-- | src/client/util/DocumentManager.ts | 38 | ||||
| -rw-r--r-- | src/client/util/DragManager.ts | 42 | ||||
| -rw-r--r-- | src/client/util/LinkManager.ts | 150 |
3 files changed, 202 insertions, 28 deletions
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 862395d74..bb87d09ec 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'; @@ -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 './LinkManager'; export class DocumentManager { @@ -83,35 +84,28 @@ 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) => { + // 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 linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); - if (linkToDoc) { - DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => - pairs.push({ a: dv, b: docView1, l: link })); + let destination = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); + if (destination) { + 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 }[])); - } - 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)); + }, [] 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; } @undoBatch diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c3c92daa5..01193cab5 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -4,8 +4,11 @@ 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 { 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<HTMLElement>, docFunc: () => Doc | Promise<Doc>, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, options?: any, dontHideOnDrop?: boolean) { @@ -42,17 +45,36 @@ export function SetupDrag(_reference: React.RefObject<HTMLElement>, docFunc: () return onItemDown; } +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, { + 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[] = []; - let draggedFromDocs: Doc[] = []; + + // TODO: if not in same context then don't drag + 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 linkDocs = LinkManager.Instance.findAllRelatedLinks(srcTarg); + if (linkDocs) { + draggedDocs = linkDocs.map(link => { + return LinkManager.Instance.findOppositeAnchor(link, sourceDoc); + }); + } } - draggedDocs.push(...draggedFromDocs); + // draggedDocs.push(...draggedFromDocs); if (draggedDocs.length) { let moddrag: Doc[] = []; for (const draggedDoc of draggedDocs) { @@ -60,6 +82,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), @@ -220,6 +245,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 new file mode 100644 index 000000000..544f2edda --- /dev/null +++ b/src/client/util/LinkManager.ts @@ -0,0 +1,150 @@ +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"; +import { List } from "../../new_fields/List"; + + +/* + * 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 { + return this._instance || (this._instance = new this()); + } + private constructor() { + } + + @observable public allLinks: Array<Doc> = []; // list of link docs + @observable public groupMetadataKeys: Map<string, Array<string>> = 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<Doc> = []; // 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<Doc> { + 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<string, Array<Doc>> { + let related = this.findAllRelatedLinks(anchor); + let anchorGroups = new Map<string, Array<Doc>>(); + related.forEach(link => { + let groups = LinkManager.Instance.getAnchorGroups(link, anchor); + + 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); + }); + } 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); + } + + }); + return anchorGroups; + } + + // returns a list of all metadata docs associated with the given group type + public findAllMetadataDocsInGroup(groupType: string): Array<Doc> { + let md: Doc[] = []; + let allLinks = LinkManager.Instance.allLinks; + 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)); + 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; + } + + // 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; + 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; + } + + // 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<Doc> { + 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<Doc>(groups); + } else { + linkDoc.anchor2Groups = new List<Doc>(groups); + } + } + + @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 |
