From 4fd53320c87313c9add8f551154f8df3e2522b5f Mon Sep 17 00:00:00 2001 From: madelinegr Date: Tue, 4 Jun 2019 18:15:54 -0400 Subject: end of day 6/4 --- src/client/util/SearchUtil.ts | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/client/util') diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 28ec8ca14..27d27a3b8 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -23,4 +23,8 @@ export namespace SearchUtil { return Search(`proto_i:"${protoId}"`, true); // return Search(`{!join from=id to=proto_i}id:${protoId}`, true); } + + export async function GetViewsOfDocument(doc: Doc): Promise { + return Search(`proto_i:"${doc[Id]}"`, true); + } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 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/client/util') 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/client/util') 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 6abf829099e4f61f2f557078f645fb9f2aa2414c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 8 Jun 2019 22:58:23 -0400 Subject: Moved ScriptField to new_fields --- src/client/util/Scripting.ts | 2 +- src/fields/ScriptField.ts | 93 ------------------------------------------- src/new_fields/ScriptField.ts | 93 +++++++++++++++++++++++++++++++++++++++++++ src/new_fields/util.ts | 2 +- 4 files changed, 95 insertions(+), 95 deletions(-) delete mode 100644 src/fields/ScriptField.ts create mode 100644 src/new_fields/ScriptField.ts (limited to 'src/client/util') diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 40e2ad6bb..688716d5f 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -12,7 +12,7 @@ import { Doc, Field } from '../../new_fields/Doc'; import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField'; import { List } from '../../new_fields/List'; import { RichTextField } from '../../new_fields/RichTextField'; -import { ScriptField, ComputedField } from '../../fields/ScriptField'; +import { ScriptField, ComputedField } from '../../new_fields/ScriptField'; export interface ScriptSucccess { success: true; diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts deleted file mode 100644 index d4fed1a1d..000000000 --- a/src/fields/ScriptField.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { ObjectField } from "../new_fields/ObjectField"; -import { CompiledScript, CompileScript } from "../client/util/Scripting"; -import { Copy, ToScriptString, Parent, SelfProxy } from "../new_fields/FieldSymbols"; -import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; -import { Deserializable } from "../client/util/SerializationHelper"; -import { computed } from "mobx"; - -function optional(propSchema: PropSchema) { - return custom(value => { - if (value !== undefined) { - return propSchema.serializer(value); - } - return SKIP; - }, (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => { - if (jsonValue !== undefined) { - return propSchema.deserializer(jsonValue, callback, context, oldValue); - } - return SKIP; - }); -} - -const optionsSchema = createSimpleSchema({ - requiredType: true, - addReturn: true, - typecheck: true, - params: optional(map(primitive())) -}); - -const scriptSchema = createSimpleSchema({ - options: object(optionsSchema), - originalScript: true -}); - -function deserializeScript(script: ScriptField) { - const comp = CompileScript(script.script.originalScript, script.script.options); - if (!comp.compiled) { - throw new Error("Couldn't compile loaded script"); - } - (script as any).script = comp; -} - -@Deserializable("script", deserializeScript) -export class ScriptField extends ObjectField { - @serializable(object(scriptSchema)) - readonly script: CompiledScript; - - constructor(script: CompiledScript) { - super(); - - this.script = script; - } - - // init(callback: (res: Field) => any) { - // const options = this.options!; - // const keys = Object.keys(options.options.capturedIds); - // Server.GetFields(keys).then(fields => { - // let captured: { [name: string]: Field } = {}; - // keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]); - // const opts: ScriptOptions = { - // addReturn: options.options.addReturn, - // params: options.options.params, - // requiredType: options.options.requiredType, - // capturedVariables: captured - // }; - // const script = CompileScript(options.script, opts); - // if (!script.compiled) { - // throw new Error("Can't compile script"); - // } - // this._script = script; - // callback(this); - // }); - // } - - [Copy](): ObjectField { - return new ScriptField(this.script); - } - - [ToScriptString]() { - return "script field"; - } -} - -@Deserializable("computed", deserializeScript) -export class ComputedField extends ScriptField { - @computed - get value() { - const val = this.script.run({ this: (this[Parent] as any)[SelfProxy] }); - if (val.success) { - return val.result; - } - return undefined; - } -} \ No newline at end of file diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts new file mode 100644 index 000000000..0839a823d --- /dev/null +++ b/src/new_fields/ScriptField.ts @@ -0,0 +1,93 @@ +import { ObjectField } from "./ObjectField"; +import { CompiledScript, CompileScript } from "../client/util/Scripting"; +import { Copy, ToScriptString, Parent, SelfProxy } from "./FieldSymbols"; +import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; +import { Deserializable } from "../client/util/SerializationHelper"; +import { computed } from "mobx"; + +function optional(propSchema: PropSchema) { + return custom(value => { + if (value !== undefined) { + return propSchema.serializer(value); + } + return SKIP; + }, (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => { + if (jsonValue !== undefined) { + return propSchema.deserializer(jsonValue, callback, context, oldValue); + } + return SKIP; + }); +} + +const optionsSchema = createSimpleSchema({ + requiredType: true, + addReturn: true, + typecheck: true, + params: optional(map(primitive())) +}); + +const scriptSchema = createSimpleSchema({ + options: object(optionsSchema), + originalScript: true +}); + +function deserializeScript(script: ScriptField) { + const comp = CompileScript(script.script.originalScript, script.script.options); + if (!comp.compiled) { + throw new Error("Couldn't compile loaded script"); + } + (script as any).script = comp; +} + +@Deserializable("script", deserializeScript) +export class ScriptField extends ObjectField { + @serializable(object(scriptSchema)) + readonly script: CompiledScript; + + constructor(script: CompiledScript) { + super(); + + this.script = script; + } + + // init(callback: (res: Field) => any) { + // const options = this.options!; + // const keys = Object.keys(options.options.capturedIds); + // Server.GetFields(keys).then(fields => { + // let captured: { [name: string]: Field } = {}; + // keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]); + // const opts: ScriptOptions = { + // addReturn: options.options.addReturn, + // params: options.options.params, + // requiredType: options.options.requiredType, + // capturedVariables: captured + // }; + // const script = CompileScript(options.script, opts); + // if (!script.compiled) { + // throw new Error("Can't compile script"); + // } + // this._script = script; + // callback(this); + // }); + // } + + [Copy](): ObjectField { + return new ScriptField(this.script); + } + + [ToScriptString]() { + return "script field"; + } +} + +@Deserializable("computed", deserializeScript) +export class ComputedField extends ScriptField { + @computed + get value() { + const val = this.script.run({ this: this[Parent] }); + if (val.success) { + return val.result; + } + return undefined; + } +} \ No newline at end of file diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index d9c2a9866..a37abb732 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -7,7 +7,7 @@ import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; import { action } from "mobx"; import { Parent, OnUpdate, Update, Id, SelfProxy } from "./FieldSymbols"; -import { ComputedField } from "../fields/ScriptField"; +import { ComputedField } from "./ScriptField"; export const setter = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { if (SerializationHelper.IsSerializing()) { -- 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/client/util') 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/client/util') 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 e65fba03470cc38bc610baf4ff77fcc13cc08b74 Mon Sep 17 00:00:00 2001 From: madelinegr Date: Tue, 11 Jun 2019 16:47:15 -0400 Subject: lint errors fixed --- src/client/northstar/dash-nodes/HistogramBox.tsx | 2 +- src/client/util/DocumentManager.ts | 2 +- src/client/util/DragManager.ts | 3 +- src/client/views/ContextMenu.tsx | 4 +-- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainOverlayTextBox.tsx | 2 +- src/client/views/MainView.tsx | 2 +- src/client/views/PresentationView.tsx | 4 +-- .../views/collections/CollectionBaseView.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 3 +- .../views/collections/CollectionSchemaView.tsx | 11 ++++--- .../views/collections/CollectionStackingView.tsx | 4 +-- .../views/collections/CollectionTreeView.tsx | 10 +++---- .../CollectionFreeFormLinksView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 6 ++-- src/client/views/nodes/ImageBox.tsx | 4 +-- src/client/views/nodes/PDFBox.tsx | 6 ++-- src/client/views/search/IconBar.tsx | 35 +++++++++++----------- src/client/views/search/SearchBox.tsx | 8 ++--- src/client/views/search/ToggleBar.tsx | 4 +-- 22 files changed, 59 insertions(+), 61 deletions(-) (limited to 'src/client/util') diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index d7732ee86..ce678a9d2 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -126,7 +126,7 @@ export class HistogramBox extends React.Component { brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]; let brushed = DocListCast(brush.brushingDocs); if (!brushed.length) - return null; + {return null;} return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] }; }); runInAction(() => this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...mapped.filter(m => m) as { l: Doc, b: Doc }[])); diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index ff0c1560b..76ca4b99b 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -35,7 +35,7 @@ export class DocumentManager { let toReturn: DocumentView | null = null; let passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; - for (let i = 0; i < passes.length; i++) { + for(let i = 0; i < passes.length; i++) { DocumentManager.Instance.DocumentViews.map(view => { if (view.props.Document[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) { toReturn = view; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 8ee1ad2af..475bb9237 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -246,11 +246,12 @@ export namespace DragManager { // } // } let set = dragElement.getElementsByTagName('*'); - for (let i = 0; i < set.length; i++) + for (let i = 0; i < set.length; i++) { if (set[i].hasAttribute("style")) { let s = set[i]; (s as any).style.pointerEvents = "none"; } + } dragDiv.appendChild(dragElement); diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index da374455e..eb1937683 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,8 +1,8 @@ import React = require("react"); import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem"; import { observable, action } from "mobx"; -import { observer } from "mobx-react" -import "./ContextMenu.scss" +import { observer } from "mobx-react"; +import "./ContextMenu.scss"; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSearch, faCircle } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 7260b00cf..5ac02a78d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -430,7 +430,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> SelectionManager.SelectedDocuments().forEach(element => { const rect = element.ContentDiv ? element.ContentDiv.getBoundingClientRect() : new DOMRect(); - if (rect.width !== 0 && (dX != 0 || dY != 0 || dW != 0 || dH != 0)) { + if (rect.width !== 0 && (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0)) { let doc = PositionDocument(element.props.Document); let nwidth = doc.nativeWidth || 0; let nheight = doc.nativeHeight || 0; diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index a0359419b..8f6d16e1f 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -34,7 +34,7 @@ export class MainOverlayTextBox extends React.Component this.TextDoc = box.props.Document; let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined); let xf = () => { box.props.ScreenToLocalTransform(); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); }; - this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf) + this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf); } else { this.TextDoc = undefined; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 307f23df1..f30755711 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -344,7 +344,7 @@ export class MainView extends React.Component { this.globalDisplayFlags.jumpToVisible = true; break; case "escape": - _.mapValues(this.globalDisplayFlags, () => false) + _.mapValues(this.globalDisplayFlags, () => false); break; case "f": this.isSearchVisible = !this.isSearchVisible; diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index b0c93ee26..db689e1b6 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -41,8 +41,8 @@ class PresentationViewList extends React.Component { //this doc is selected className += " presentationView-selected"; } - let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; } - let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; } + let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; }; + let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; }; return (
{ @action.bound removeDocument(doc: Doc): boolean { - let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView) + let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView); docView && SelectionManager.DeselectDoc(docView); const props = this.props; //TODO This won't create the field if it doesn't already exist diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 51e29cb54..2bce6103b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -419,8 +419,7 @@ export class DockedFrameRenderer extends React.Component { @observable private _document: Opt; get _stack(): any { let parent = (this.props as any).glContainer.parent.parent; - if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) - return parent.parent.contentItems[1]; + if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) { return parent.parent.contentItems[1]; } return parent; } constructor(props: any) { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b9e5a5b65..c886d0f35 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -90,8 +90,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { let columnDocs = DocListCast(schemaDoc.data); if (columnDocs) { let ddoc = columnDocs.find(doc => doc.title === columnName); - if (ddoc) - return ddoc; + if (ddoc) { return ddoc; } } } return this.props.Document; @@ -288,7 +287,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } getPreviewTransform = (): Transform => this.props.ScreenToLocalTransform().translate( - - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth); + - this.borderWidth - this.DIVIDER_WIDTH - this.tableWidth, - this.borderWidth) get documentKeysCheckList() { @@ -337,7 +336,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { columns={this.tableColumns} column={{ ...ReactTableDefaults.column, Cell: this.renderCell, }} getTrProps={this.getTrProps} - /> + />; } @computed @@ -362,7 +361,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { addDocTab={this.props.addDocTab} setPreviewScript={this.setPreviewScript} previewScript={this.previewScript} - /> + />; } @action setPreviewScript = (script: string) => { @@ -410,7 +409,7 @@ export class CollectionSchemaPreview extends React.Component this.nativeWidth * this.contentScaling(); private PanelHeight = () => this.nativeHeight * this.contentScaling(); - private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling()) + private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling()); get centeringOffset() { return (this.props.width() - this.nativeWidth * this.contentScaling()) / 2; } @action onPreviewScriptChange = (e: React.ChangeEvent) => { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index cad7cd50c..0c5d42769 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -116,7 +116,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let childFocus = (doc: Doc) => { doc.libraryBrush = true; this.props.focus(this.props.Document); // just focus on this collection, not the underlying document because the API doesn't support adding an offset to focus on and we can't pan zoom our contents to be centered. - } + }; return (
doc) { collapseToPoint={this.collapseToPoint} />
); - }) + }); } onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 8a6764c58..f83fa3b4b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -67,8 +67,8 @@ class TreeView extends React.Component { @undoBatch delete = () => this.props.deleteDoc(this.props.document); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight"); - @action onMouseEnter = () => { this._isOver = true; } - @action onMouseLeave = () => { this._isOver = false; } + @action onMouseEnter = () => { this._isOver = true; }; + @action onMouseLeave = () => { this._isOver = false; }; onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); @@ -89,8 +89,8 @@ 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._bulletType === BulletType.Collapsible); - this._header!.current!.className = "treeViewItem-header" - if (inside && this._bulletType != BulletType.List) this._header!.current!.className = "treeViewItem-header-inside"; + this._header!.current!.className = "treeViewItem-header"; + if (inside && this._bulletType !== BulletType.List) this._header!.current!.className = "treeViewItem-header-inside"; else if (before) this._header!.current!.className = "treeViewItem-header-above"; else if (!before) this._header!.current!.className = "treeViewItem-header-below"; e.stopPropagation(); @@ -191,7 +191,7 @@ class TreeView extends React.Component { if (inside) { let docList = Cast(this.props.document.data, listSpec(Doc)); if (docList !== undefined) { - addDoc = (doc: Doc) => { docList && docList.push(doc); return true; } + addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }; } } let added = false; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index c4dd534ed..be75c6c5c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -109,7 +109,7 @@ export class CollectionFreeFormLinksView extends React.Component [ , ...this.views - ]; + ] render() { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; const easing = () => this.props.Document.panTransformType === "Ease"; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b6c566964..b93b90c72 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -312,7 +312,7 @@ export class MarqueeView extends React.Component summary.imageSummary = imageSummary; this.props.addDocument(imageSummary, false); } - }) + }); newCollection.proto!.summaryDoc = summary; selected = [newCollection]; newCollection.x = bounds.left + bounds.width; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 19f5c7d36..71bb3a8dc 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -312,8 +312,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); @@ -442,7 +442,7 @@ export class DocumentView extends DocComponent(Docu onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; }; isSelected = () => SelectionManager.IsSelected(this); - @action select = (ctrlPressed: boolean) => { this.selectOnLoad = false; SelectionManager.SelectDoc(this, ctrlPressed); } + @action select = (ctrlPressed: boolean) => { this.selectOnLoad = false; SelectionManager.SelectDoc(this, ctrlPressed); }; @observable selectOnLoad: boolean = false; @computed get nativeWidth() { return this.Document.nativeWidth || 0; } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0d19508fa..c93b84949 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -88,7 +88,7 @@ export class ImageBox extends DocComponent(ImageD onPointerDown = (e: React.PointerEvent): void => { if (e.shiftKey && e.ctrlKey) - e.stopPropagation(); + {e.stopPropagation();} // if (Date.now() - this._lastTap < 300) { // if (e.buttons === 1) { // this._downX = e.clientX; @@ -189,7 +189,7 @@ export class ImageBox extends DocComponent(ImageD @action onError = () => { let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; if (timeout < 10) - setTimeout(this.retryPath, Math.min(10000, timeout * 5)); + { setTimeout(this.retryPath, Math.min(10000, timeout * 5));} } _curSuffix = "_m"; render() { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 05088f688..1fb3090b8 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -262,7 +262,7 @@ export class PDFBox extends DocComponent(PdfDocumen this.props.Document.thumbnail = new ImageField(new URL(url)); } runInAction(() => this._renderAsSvg = true); - }) + }); })) .catch(function (error: any) { console.error('oops, something went wrong!', error); @@ -336,7 +336,7 @@ export class PDFBox extends DocComponent(PdfDocumen choosePath(url: URL) { if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1) - return url.href; + {return url.href;} let ext = path.extname(url.href); return url.href.replace(ext, this._curSuffix + ext); } @@ -351,7 +351,7 @@ export class PDFBox extends DocComponent(PdfDocumen @action onError = () => { let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; if (timeout < 10) - setTimeout(this.retryPath, Math.min(10000, timeout * 5)); + {setTimeout(this.retryPath, Math.min(10000, timeout * 5));} } _curSuffix = "_m"; diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx index 3f5d11d8f..3f77d64eb 100644 --- a/src/client/views/search/IconBar.tsx +++ b/src/client/views/search/IconBar.tsx @@ -95,6 +95,7 @@ export class IconBar extends React.Component { @action.bound unselectAllRefs() { + this.allRefs.forEach(element => { if (element.current) { element.current.setAttribute("data-selected", "false"); @@ -105,16 +106,16 @@ export class IconBar extends React.Component { @action.bound alternateRef(ref: any) { if (ref.getAttribute("data-selected") === "true") { - ref.setAttribute("data-selected", "false") + ref.setAttribute("data-selected", "false"); } else { - ref.setAttribute("data-selected", "true") + ref.setAttribute("data-selected", "true"); } } @action.bound onClick = (value: string) => { - let icons: string[] = this.props.getIcons() + let icons: string[] = this.props.getIcons(); let ref = this.getRef(value); this.alternateRef(ref); if (value === DocTypes.NONE) { @@ -150,15 +151,13 @@ export class IconBar extends React.Component { } getInitialStatus = (type: string) => { - console.log(this.getRef(type)) if (this.originalFilteredNodes.includes(type)) { - return "true" + return "true"; } - return "false" + return "false"; } render() { - console.log("rendering") return (
Filter by type of node
@@ -167,7 +166,7 @@ export class IconBar extends React.Component {
{ this.onClick(DocTypes.NONE) }}> + onClick={() => { this.onClick(DocTypes.NONE); }}>
Clear
@@ -176,7 +175,7 @@ export class IconBar extends React.Component {
{ this.onClick(DocTypes.PDF) }}> + onClick={() => { this.onClick(DocTypes.PDF); }}>
PDF
@@ -185,7 +184,7 @@ export class IconBar extends React.Component {
{ this.onClick(DocTypes.HIST) }}> + onClick={() => { this.onClick(DocTypes.HIST); }}>
Histogram
@@ -194,7 +193,7 @@ export class IconBar extends React.Component {
{ this.onClick(DocTypes.COL) }}> + onClick={() => { this.onClick(DocTypes.COL); }}>
Collection
@@ -203,7 +202,7 @@ export class IconBar extends React.Component {
{ this.onClick(DocTypes.IMG) }}> + onClick={() => { this.onClick(DocTypes.IMG); }}>
Image
@@ -212,7 +211,7 @@ export class IconBar extends React.Component {
{ this.onClick(DocTypes.VID) }}> + onClick={() => { this.onClick(DocTypes.VID); }}>
Video
@@ -221,7 +220,7 @@ export class IconBar extends React.Component {
{ this.onClick(DocTypes.WEB) }}> + onClick={() => { this.onClick(DocTypes.WEB); }}>
Web
@@ -230,7 +229,7 @@ export class IconBar extends React.Component {
{ this.onClick(DocTypes.LINK) }}> + onClick={() => { this.onClick(DocTypes.LINK); }}>
Link
@@ -239,7 +238,7 @@ export class IconBar extends React.Component {
{ this.onClick(DocTypes.AUDIO) }}> + onClick={() => { this.onClick(DocTypes.AUDIO); }}>
Audio
@@ -248,13 +247,13 @@ export class IconBar extends React.Component {
{ this.onClick(DocTypes.TEXT) }}> + onClick={() => { this.onClick(DocTypes.TEXT); }}>
Text
- ) + ); } } diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index e9146a6d1..deeb56762 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -47,14 +47,14 @@ export class SearchBox extends React.Component { if(!this._wordStatus){ let oldWords = query.split(" "); let newWords: string[] = []; - console.log(oldWords) + console.log(oldWords); oldWords.forEach(word => { let newWrd = "+" + word; newWords.push(newWrd); - }) - console.log(newWords) + }); + console.log(newWords); - query = newWords.join(" ") + query = newWords.join(" "); } //gets json result into a list of documents that can be used diff --git a/src/client/views/search/ToggleBar.tsx b/src/client/views/search/ToggleBar.tsx index 7dbb3b767..e7fd86d30 100644 --- a/src/client/views/search/ToggleBar.tsx +++ b/src/client/views/search/ToggleBar.tsx @@ -64,7 +64,7 @@ export class ToggleBar extends React.Component{ translateX: -totalWidth, easing: "easeInOutQuad", duration: 500 - }) + }); } } @@ -88,5 +88,5 @@ export class ToggleBar extends React.Component{ ); - }; + } } \ No newline at end of file -- 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/client/util') 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 09e40959ec0d47ae3bff2e9f1797ea2b0cbc1034 Mon Sep 17 00:00:00 2001 From: madelinegr Date: Wed, 12 Jun 2019 22:45:20 -0400 Subject: end of day 6/12 --- src/client/documents/Documents.ts | 6 +++--- src/client/util/DocumentManager.ts | 26 +++++++++++++++++++++++++- src/client/views/MainView.tsx | 4 ++-- src/client/views/search/IconBar.scss | 15 ++++++++++++++- src/client/views/search/IconBar.tsx | 16 +++++++++++++++- src/client/views/search/SearchBox.tsx | 25 +++++++++++++++++-------- src/client/views/search/SearchItem.tsx | 22 +++++++++++++++++----- src/client/views/search/ToggleBar.tsx | 16 ++++++++++++++-- 8 files changed, 107 insertions(+), 23 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9d83f0e36..2da5eed43 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -89,11 +89,13 @@ export namespace DocUtils { 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 = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1}); + // linkDoc.type = DocTypes.LINK; let linkDocProto = Doc.GetProto(linkDoc); linkDocProto.title = source.title + " to " + target.title; linkDocProto.linkDescription = ""; linkDocProto.linkTags = "Default"; + linkDocProto.type = DocTypes.LINK; linkDocProto.linkedTo = target; linkDocProto.linkedFrom = source; @@ -110,8 +112,6 @@ export namespace DocUtils { return linkDoc; }, "make link"); } - - } export namespace Docs { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 76ca4b99b..40928ecae 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -30,12 +30,36 @@ export class DocumentManager { // this.DocumentViews = new Array(); } + //gets all views + public getDocumentViewsById(id: string) { + let toReturn: DocumentView[] = []; + DocumentManager.Instance.DocumentViews.map(view => { + if (view.props.Document[Id] === id) { + toReturn.push(view); + } + }); + if (toReturn.length === 0) { + DocumentManager.Instance.DocumentViews.map(view => { + let doc = view.props.Document.proto; + if (doc && doc[Id]) { + if(doc[Id] === id) + {toReturn.push(view);} + } + }); + } + return toReturn; + } + + public getAllDocumentViews(doc: Doc){ + return this.getDocumentViewsById(doc[Id]); + } + public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null { let toReturn: DocumentView | null = null; let passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; - for(let i = 0; i < passes.length; i++) { + for (let i = 0; i < passes.length; i++) { DocumentManager.Instance.DocumentViews.map(view => { if (view.props.Document[Id] === id && (!passes[i] || view.props.ContainingCollectionView === preferredCollection)) { toReturn = view; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f30755711..c612d4b00 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -10,7 +10,7 @@ import * as request from 'request'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; -import { Docs } from '../documents/Documents'; +import { Docs, DocTypes } from '../documents/Documents'; import { SetupDrag, DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; @@ -243,7 +243,7 @@ export class MainView extends React.Component { let audiourl = "http://techslides.com/demos/samples/sample.mp3"; let videourl = "http://techslides.com/demos/sample-videos/small.mp4"; - let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" })); + let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note", type: DocTypes.TEXT })); let addColNode = action(() => Docs.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); let addSchemaNode = action(() => Docs.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" })); let addTreeNode = action(() => CurrentUserUtils.UserDocument); diff --git a/src/client/views/search/IconBar.scss b/src/client/views/search/IconBar.scss index 98b4e50a6..e08e09702 100644 --- a/src/client/views/search/IconBar.scss +++ b/src/client/views/search/IconBar.scss @@ -34,10 +34,12 @@ .type-icon.selected { background-color: $alt-accent; + opacity: 1; } .type-icon.not-selected { background-color: transparent; + opacity: .4; } .fontawesome-icon { @@ -45,9 +47,20 @@ width: 28px; } -.type-icon:hover { +.type-icon.filter:hover { transform: scale(1.1); background-color: $alt-accent; + opacity: 1; + + +.filter-description { + opacity: 1; + } +} + +.type-icon.none:hover { + transform: scale(1.1); + // background-color: $alt-accent; + opacity: 1; +.filter-description { opacity: 1; diff --git a/src/client/views/search/IconBar.tsx b/src/client/views/search/IconBar.tsx index f3741a5de..8fb7d0959 100644 --- a/src/client/views/search/IconBar.tsx +++ b/src/client/views/search/IconBar.tsx @@ -31,6 +31,8 @@ export interface IconBarProps { @observer export class IconBar extends React.Component { + static Instance: IconBar; + @observable noneRef = React.createRef(); @observable colRef = React.createRef(); @observable imgRef = React.createRef(); @@ -45,11 +47,23 @@ export class IconBar extends React.Component { @observable originalFilteredNodes: string[] = this.props.getIcons(); + constructor(props: IconBarProps){ + super(props); + IconBar.Instance = this; + } + componentDidMount = () => { //i KNOW this is bad i just can't get this to re render eeeeeeeek this.forceUpdate(); } + @action.bound + resetIconFilters = () => { + this.unselectAllRefs(); + // lmao sorry + this.forceUpdate(); + } + //gets ref associated with given string @action.bound getRef = (value: string) => { @@ -163,7 +177,7 @@ export class IconBar extends React.Component {
Filter by type of node
-
{ this.onClick(DocTypes.NONE); }}> diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 319573adf..622fa27f0 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -53,6 +53,14 @@ export class SearchBox extends React.Component { }); } + @action.bound + resetFilters = () => { + ToggleBar.Instance.resetToggle(); + IconBar.Instance.resetIconFilters(); + // this._wordStatus = true; + this._icons = []; + } + @action.bound onChange(e: React.ChangeEvent) { this._searchString = e.target.value; @@ -66,22 +74,24 @@ export class SearchBox extends React.Component { @action submitSearch = async () => { let query = this._searchString; + let results: Doc[]; if (!this._wordStatus) { let oldWords = query.split(" "); let newWords: string[] = []; - console.log(oldWords); oldWords.forEach(word => { let newWrd = "+" + word; newWords.push(newWrd); }); - console.log(newWords); - query = newWords.join(" "); } - //gets json result into a list of documents that can be used - const results = await this.getResults(query); + if(query === ""){ + results = []; + } + else{ + //gets json result into a list of documents that can be used + results = await this.getResults(query);} runInAction(() => { this._resultsOpen = true; @@ -151,6 +161,7 @@ export class SearchBox extends React.Component { closeSearch = () => { this._open = false; this._resultsOpen = false; + this._results = []; } @action @@ -227,14 +238,12 @@ export class SearchBox extends React.Component { stopProp = (e: React.PointerEvent) => { e.stopPropagation(); - console.log('stopping prop') this._pointerTime = e.timeStamp; } @action.bound openSearch(e: React.PointerEvent) { e.stopPropagation(); - this._results = []; this._openNoResults = false; this._open = false; this._resultsOpen = true; @@ -285,7 +294,7 @@ export class SearchBox extends React.Component { temp for filtering where in doc the keywords are found
- +
) : undefined} diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 65f4dfd31..fb0eade4b 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -3,7 +3,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faMusic, faLink, faChartBar, faGlobeAsia } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Cast, NumCast } from "../../../new_fields/Types"; -import { observable, runInAction, computed } from "mobx"; +import { observable, runInAction, computed, action } from "mobx"; import { listSpec } from "../../../new_fields/Schema"; import { Doc } from "../../../new_fields/Doc"; import { DocumentManager } from "../../util/DocumentManager"; @@ -16,6 +16,7 @@ import "./SearchItem.scss"; import { CollectionViewType } from "../collections/CollectionBaseView"; import { DocTypes } from "../../documents/Documents"; import { SearchBox } from "./SearchBox"; +import { DocumentView } from "../nodes/DocumentView"; export interface SearchItemProps { doc: Doc; @@ -84,11 +85,8 @@ export class SelectorContextMenu extends React.Component { export class SearchItem extends React.Component { @observable _selected: boolean = false; - @observable hover = false; onClick = () => { - // DocumentManager.Instance.jumpToDocument(this.props.doc); - console.log(Cast(this.props.doc.type, "string", "")) CollectionDockingView.Instance.AddRightSplit(this.props.doc); } @@ -138,10 +136,24 @@ export class SearchItem extends React.Component { SearchBox.Instance.openSearch(e); } + highlightDoc = (e: React.PointerEvent) => { + let docViews: DocumentView[] = DocumentManager.Instance.getAllDocumentViews(this.props.doc); + docViews.forEach(element => { + element.props.Document.libraryBrush = true; + }); + } + + unHighlightDoc = (e: React.PointerEvent) => { + let docViews: DocumentView[] = DocumentManager.Instance.getAllDocumentViews(this.props.doc); + docViews.forEach(element => { + element.props.Document.libraryBrush = false; + }); + } + render() { return (
-
{ +
{ this.pointerDown; SetupDrag(this.collectionRef, this.startDocDrag);}} >
diff --git a/src/client/views/search/ToggleBar.tsx b/src/client/views/search/ToggleBar.tsx index e7fd86d30..32f7a63dd 100644 --- a/src/client/views/search/ToggleBar.tsx +++ b/src/client/views/search/ToggleBar.tsx @@ -15,6 +15,7 @@ export interface ToggleBarProps { @observer export class ToggleBar extends React.Component{ + static Instance: ToggleBar; @observable forwardTimeline: anime.AnimeTimelineInstance; @observable _toggleButton: React.RefObject; @@ -23,6 +24,7 @@ export class ToggleBar extends React.Component{ constructor(props: ToggleBarProps) { super(props); + ToggleBar.Instance = this; this._toggleButton = React.createRef(); this.forwardTimeline = anime.timeline({ loop: false, @@ -76,12 +78,22 @@ export class ToggleBar extends React.Component{ this.props.changeStatus(); } + @action.bound + public resetToggle = () => { + if (!this._curStatus) { + this.forwardTimeline.play() + this.forwardTimeline.reverse(); + this.props.changeStatus(); + this._curStatus = true; + } + } + render() { return (
-
{this.props.optionOne}
-
{this.props.optionTwo}
+
{this.props.optionOne}
+
{this.props.optionTwo}
-- cgit v1.2.3-70-g09d2 From 74dee5f76ffc5bbdd07caafb4273aaf485dec1b9 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 12 Jun 2019 23:22:58 -0400 Subject: beginnings of nested golden layout --- src/client/documents/Documents.ts | 18 +++ src/client/util/DocumentManager.ts | 4 +- src/client/util/DragManager.ts | 8 +- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/views/MainOverlayTextBox.tsx | 4 +- src/client/views/MainView.tsx | 20 ++- .../views/collections/CollectionBaseView.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 162 ++++++++++++++------- .../views/collections/CollectionTreeView.tsx | 2 +- .../views/collections/ParentDocumentSelector.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 12 +- 11 files changed, 158 insertions(+), 80 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ab61b915c..b346e1570 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 { CollectionDockingView } from "../views/collections/CollectionDockingView"; var requestImageSize = require('request-image-size'); var path = require('path'); @@ -315,6 +316,23 @@ export namespace Docs { export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } + export type DocConfig = { + doc: Doc, + initialWidth?: number + } + export function StandardCollectionDockingDocument(configs: Array, options: DocumentOptions, id?: string, type: string = "row") { + let layoutConfig = { + content: [ + { + type: type, + content: [ + ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, config.initialWidth)) + ] + } + ] + }; + return DockDocument(configs.map(c => c.doc), JSON.stringify(layoutConfig), options, id); + } export function CaptionDocument(doc: Doc) { const captionDoc = Doc.MakeAlias(doc); diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 65c4b9e4b..72d9f7e76 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -134,7 +134,7 @@ export class DocumentManager { const actualDoc = Doc.MakeAlias(docDelegate); actualDoc.libraryBrush = true; if (linkPage !== undefined) actualDoc.curPage = linkPage; - (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc); + (dockFunc || CollectionDockingView.AddRightSplit)(actualDoc); } else { let contextView: DocumentView | null; docDelegate.libraryBrush = true; @@ -142,7 +142,7 @@ export class DocumentManager { contextDoc.panTransformType = "Ease"; contextView.props.focus(contextDoc); } else { - (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc); + (dockFunc || CollectionDockingView.AddRightSplit)(contextDoc); } } } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 1e84a0db0..7625b0463 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -26,8 +26,8 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () // if (this.props.isSelected() || this.props.isTopMost) { if (e.button === 0) { e.stopPropagation(); - if (e.shiftKey && CollectionDockingView.Instance) { - CollectionDockingView.Instance.StartOtherDrag([await docFunc()], e); + if (e.shiftKey && CollectionDockingView.TopLevel) { + CollectionDockingView.TopLevel.StartOtherDrag([await docFunc()], e); } else { document.addEventListener("pointermove", onRowMove); document.addEventListener("pointerup", onRowUp); @@ -264,9 +264,9 @@ export namespace DragManager { if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey || e.altKey ? "alias" : undefined; } - if (e.shiftKey && CollectionDockingView.Instance) { + if (e.shiftKey && CollectionDockingView.TopLevel) { AbortDrag(); - CollectionDockingView.Instance.StartOtherDrag(docs, { + CollectionDockingView.TopLevel.StartOtherDrag(docs, { pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index f517f757a..fa2483db5 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -194,7 +194,7 @@ export class TooltipTextMenu { if (DocumentManager.Instance.getDocumentView(f)) { DocumentManager.Instance.getDocumentView(f)!.props.focus(f); } - else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f); + else if (CollectionDockingView.TopLevel) CollectionDockingView.AddRightSplit(f); } })); } diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 24327b995..718979123 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -86,7 +86,7 @@ export class MainOverlayTextBox extends React.Component addDocTab = (doc: Doc, location: string) => { if (true) { // location === "onRight") { need to figure out stack to add "inTab" - CollectionDockingView.Instance.AddRightSplit(doc); + CollectionDockingView.AddRightSplit(doc); } } render() { @@ -102,6 +102,6 @@ export class MainOverlayTextBox extends React.Component
; } - else return (null); Z + else return (null); } } \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 67a026897..426e2440a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell } from '@fortawesome/free-solid-svg-icons'; +import { faClone, faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faTree, faUndoAlt, faBell } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; @@ -106,6 +106,7 @@ export class MainView extends React.Component { library.add(faFilm); library.add(faMusic); library.add(faTree); + library.add(faClone) this.initEventListeners(); this.initAuthenticationRouters(); } @@ -151,8 +152,11 @@ export class MainView extends React.Component { const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); if (list) { let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` }); - var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; - let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }, id); + let configs = [ + { doc: CurrentUserUtils.UserDocument, initialWidth: 150 }, + { doc: freeformDoc, initialWidth: 600 } + ] + let mainDoc = Docs.StandardCollectionDockingDocument(configs, { title: `Workspace ${list.length + 1}` }, id); list.push(mainDoc); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) setTimeout(() => { @@ -183,8 +187,8 @@ export class MainView extends React.Component { } openNotifsCol = () => { - if (this._notifsCol && CollectionDockingView.Instance) { - CollectionDockingView.Instance.AddRightSplit(this._notifsCol); + if (this._notifsCol && CollectionDockingView.TopLevel) { + CollectionDockingView.AddRightSplit(this._notifsCol); } } @@ -240,6 +244,7 @@ export class MainView extends React.Component { let addTextNode = action(() => Docs.TextDocument({ borderRounding: -1, width: 200, height: 200, title: "a text note" })); let addColNode = action(() => Docs.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); + let addDockingNode = action(() => Docs.StandardCollectionDockingDocument([{ doc: addColNode(), initialWidth: 200 }], { width: 200, height: 200, title: "a nested docking freeform collection" })); let addSchemaNode = action(() => Docs.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" })); let addTreeNode = action(() => CurrentUserUtils.UserDocument); //let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); @@ -260,6 +265,7 @@ export class MainView extends React.Component { [React.createRef(), "object-group", "Add Collection", addColNode], [React.createRef(), "tree", "Add Tree", addTreeNode], [React.createRef(), "table", "Add Schema", addSchemaNode], + [React.createRef(), "clone", "Add Docking Frame", addDockingNode] ]; return < div id="add-nodes-menu" > @@ -327,12 +333,12 @@ export class MainView extends React.Component { switch (e.key) { case "ArrowRight": if (this.mainFreeform) { - CollectionDockingView.Instance.AddRightSplit(this.mainFreeform!); + CollectionDockingView.AddRightSplit(this.mainFreeform!); } break; case "ArrowLeft": if (this.mainFreeform) { - CollectionDockingView.Instance.CloseRightSplit(this.mainFreeform!); + CollectionDockingView.CloseRightSplit(this.mainFreeform!); } break; case "o": diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 4d6721dc1..096c65092 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -16,7 +16,7 @@ export enum CollectionViewType { Schema, Docking, Tree, - Stacking + Stacking, } export interface CollectionRenderProps { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 1adb73bcf..dfb8fac35 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,9 +1,9 @@ import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { action, observable, reaction, Lambda } from "mobx"; +import { action, observable, reaction, Lambda, IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; -import Measure from "react-measure"; +import Measure, { ContentRect } from "react-measure"; import * as GoldenLayout from "../../../client/goldenLayout"; import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc"; import { FieldId } from "../../../new_fields/RefField"; @@ -26,7 +26,7 @@ import { CurrentUserUtils } from '../../../server/authentication/models/current_ @observer export class CollectionDockingView extends React.Component { - public static Instance: CollectionDockingView; + public static TopLevel: CollectionDockingView; public static makeDocumentConfig(document: Doc, width?: number) { return { type: 'react-component', @@ -35,11 +35,16 @@ export class CollectionDockingView extends React.Component { + const config = CollectionDockingView.makeDocumentConfig(document, width); + (config.props as any).parent = this; + return config; + } + private _goldenLayout: any = null; private _containerRef = React.createRef(); private _flush: boolean = false; @@ -48,7 +53,7 @@ export class CollectionDockingView extends React.Component - this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener. + CollectionDockingView.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener. onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 })); } - @action - public OpenFullScreen(document: Doc) { + private openFullScreen = (document: Doc) => { let newItemStackConfig = { type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document)] + content: [this.makeDocConfig(document)] }; var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout); this._goldenLayout.root.contentItems[0].addChild(docconfig); docconfig.callDownwards('_$init'); this._goldenLayout._$maximiseItem(docconfig); - this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); + this._ignoreStateChange = this.retrieveConfiguration(); this.stateChanged(); } + @action + public static OpenFullScreen(document: Doc, dockingView: CollectionDockingView = CollectionDockingView.TopLevel) { + dockingView.openFullScreen(document); + } + + initializeConfiguration = (configText: string) => { + let configuration: any = JSON.parse(configText); + this.injectParentProp(configuration.content); + this._goldenLayout = new GoldenLayout(configuration); + } + + retrieveConfiguration = () => { + let configuration: any = this._goldenLayout.toConfig(); + this.injectParentProp(configuration.content, true); + return JSON.stringify(configuration); + } + + injectParentProp = (contentArray: any[], reverse: boolean = false) => { + if (!contentArray || contentArray.length == 0) return; + contentArray.forEach(member => { + let baseCase = Object.keys(member).includes("props"); + if (!baseCase) { + this.injectParentProp(member.content, reverse) + } else { + reverse ? delete member.props.parent : member.props.parent = this; + } + }); + } + @undoBatch @action - public CloseRightSplit = (document: Doc): boolean => { + public static CloseRightSplit = (document: Doc, dockingView: CollectionDockingView = CollectionDockingView.TopLevel): boolean => { let retVal = false; - if (this._goldenLayout.root.contentItems[0].isRow) { - retVal = Array.from(this._goldenLayout.root.contentItems[0].contentItems).some((child: any) => { + if (dockingView._goldenLayout.root.contentItems[0].isRow) { + retVal = Array.from(dockingView._goldenLayout.root.contentItems[0].contentItems).some((child: any) => { if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" && Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)!.Document, document)) { child.contentItems[0].remove(); - this.layoutChanged(document); + dockingView.layoutChanged(document); return true; } else { Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => { if (Doc.AreProtosEqual(DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)!.Document, document)) { child.contentItems[j].remove(); child.config.activeItemIndex = Math.max(child.contentItems.length - 1, 0); - let docs = Cast(this.props.Document.data, listSpec(Doc)); + let docs = Cast(dockingView.props.Document.data, listSpec(Doc)); docs && docs.indexOf(document) !== -1 && docs.splice(docs.indexOf(document), 1); return true; } @@ -103,7 +136,7 @@ export class CollectionDockingView extends React.Component { + public static AddRightSplit = (document: Doc, minimize: boolean = false, dockingView: CollectionDockingView = CollectionDockingView.TopLevel) => { + return dockingView.addRightSplit(document, minimize); + } + + private addRightSplit(document: Doc, minimize = false) { let docs = Cast(this.props.Document.data, listSpec(Doc)); if (docs) { docs.push(document); } let newItemStackConfig = { type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document)] + content: [this.makeDocConfig(document)] }; var newContentItem = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout); @@ -157,13 +193,17 @@ export class CollectionDockingView extends React.Component { + + public static AddTab = (stack: any, document: Doc, dockingView: CollectionDockingView = CollectionDockingView.TopLevel) => { + dockingView.addTab(stack, document); + } + + private addTab = (stack: any, document: Doc) => { let docs = Cast(this.props.Document.data, listSpec(Doc)); if (docs) { docs.push(document); } - let docContentConfig = CollectionDockingView.makeDocumentConfig(document); + let docContentConfig = this.makeDocConfig(document); var newContentItem = stack.layoutManager.createContentItem(docContentConfig, this._goldenLayout); stack.addChild(newContentItem.contentItems[0], undefined); this.layoutChanged(); @@ -173,10 +213,10 @@ export class CollectionDockingView extends React.Component void = () => { if (this._containerRef.current) { this.reactionDisposer = reaction( () => StrCast(this.props.Document.dockingConfig), () => { - if (!this._goldenLayout || this._ignoreStateChange !== JSON.stringify(this._goldenLayout.toConfig())) { + if (!this._goldenLayout || this._ignoreStateChange !== this.retrieveConfiguration()) { // Because this is in a set timeout, if this component unmounts right after mounting, // we will leak a GoldenLayout, because we try to destroy it before we ever create it setTimeout(() => this.setupGoldenLayout(), 1); @@ -218,7 +258,7 @@ export class CollectionDockingView extends React.Component void = () => { @@ -232,18 +272,19 @@ export class CollectionDockingView extends React.Component { - var cur = this._containerRef.current; - + onResize = (size: ContentRect) => { // bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed - this._goldenLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height); + // this._goldenLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height); + if (this._goldenLayout) { + this._goldenLayout.updateSize(size.offset!.width, size.offset!.height); + } } @action @@ -301,12 +342,12 @@ export class CollectionDockingView extends React.Component { - let docs = Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc)); - CollectionDockingView.Instance._removedDocs.map(theDoc => + let docs = Cast(CollectionDockingView.TopLevel.props.Document.data, listSpec(Doc)); + CollectionDockingView.TopLevel._removedDocs.map(theDoc => docs && docs.indexOf(theDoc) !== -1 && docs.splice(docs.indexOf(theDoc), 1)); - CollectionDockingView.Instance._removedDocs.length = 0; - var json = JSON.stringify(this._goldenLayout.toConfig()); + CollectionDockingView.TopLevel._removedDocs.length = 0; + var json = this.retrieveConfiguration(); this.props.Document.dockingConfig = json; if (this.undohack && !this.hack) { this.undohack.end(); @@ -315,18 +356,18 @@ export class CollectionDockingView extends React.Component { + private itemDropped = () => { this.stateChanged(); } - htmlToElement(html: string) { + private htmlToElement(html: string) { var template = document.createElement('template'); html = html.trim(); // Never return a text node of whitespace as the result template.innerHTML = html; return template.content.firstChild; } - tabCreated = async (tab: any) => { + private tabCreated = async (tab: any) => { if (tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") { if (tab.contentItem.config.fixed) { tab.contentItem.parent.config.fixed = true; @@ -346,7 +387,7 @@ export class CollectionDockingView extends React.Component CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); + ReactDOM.render( CollectionDockingView.AddTab(stack, doc)} />, upDiv); tab.reactComponents = [upDiv]; tab.element.append(upDiv); counter.DashDocId = tab.contentItem.config.props.documentId; @@ -368,13 +409,13 @@ export class CollectionDockingView extends React.Component { + private tabDestroyed = (tab: any) => { if (tab.reactComponents) { for (const ele of tab.reactComponents) { ReactDOM.unmountComponentAtNode(ele); @@ -383,7 +424,7 @@ export class CollectionDockingView extends React.Component { + private stackCreated = (stack: any) => { //stack.header.controlsContainer.find('.lm_popout').hide(); stack.header.controlsContainer.find('.lm_close') //get the close icon .off('click') //unbind the current click handler @@ -394,7 +435,7 @@ export class CollectionDockingView extends React.Component + + {({ measureRef }) => ( +
+ + )} + ); } @@ -419,16 +466,23 @@ export class CollectionDockingView extends React.Component { _mainCont = React.createRef(); @observable private _panelWidth = 0; @observable private _panelHeight = 0; @observable private _document: Opt; + private get parentProps(): SubCollectionViewProps { + return this.props.parent.props; + } + get _stack(): any { - let parent = (this.props as any).glContainer.parent.parent; + let parent = this.props.glContainer.parent.parent; if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) return parent.parent.contentItems[1]; return parent; @@ -451,7 +505,7 @@ export class DockedFrameRenderer extends React.Component { if (this._mainCont.current && this._mainCont.current.children) { let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current.children[0].firstChild as HTMLElement); scale = Utils.GetScreenTransform(this._mainCont.current).scale; - return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale); + return this.parentProps.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale); } return Transform.Identity(); } @@ -469,9 +523,9 @@ export class DockedFrameRenderer extends React.Component { addDocTab = (doc: Doc, location: string) => { if (location === "onRight") { - CollectionDockingView.Instance.AddRightSplit(doc); + CollectionDockingView.AddRightSplit(doc); } else { - CollectionDockingView.Instance.AddTab(this._stack, doc); + CollectionDockingView.AddTab(this._stack, doc); } } get content() { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 48da52ffa..b0a310ec1 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -123,7 +123,7 @@ class TreeView extends React.Component { return true; }} />); - let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : []; + let dataDocs = CollectionDockingView.TopLevel ? Cast(CollectionDockingView.TopLevel.props.Document.data, listSpec(Doc), []) : []; let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index f11af04a3..95183bd2c 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -29,8 +29,8 @@ export class SelectorContextMenu extends React.Component { allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index]))); docs.forEach(doc => map.delete(doc)); runInAction(() => { - this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: this.props.Document })); - this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target })); + this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.TopLevel.props.Document)).map(doc => ({ col: doc, target: this.props.Document })); + this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.TopLevel.props.Document)).map(([col, target]) => ({ col, target })); }); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7750b9173..efba26c2c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -238,11 +238,11 @@ export class DocumentView extends DocComponent(Docu expandedProtoDocs.forEach(maxDoc => maxDoc.isMinimized = wasMinimized); } } - if (maxLocation && maxLocation !== "inPlace" && CollectionDockingView.Instance) { - let dataDocs = DocListCast(CollectionDockingView.Instance.props.Document.data); + if (maxLocation && maxLocation !== "inPlace" && CollectionDockingView.TopLevel) { + let dataDocs = DocListCast(CollectionDockingView.TopLevel.props.Document.data); if (dataDocs) { expandedDocs.forEach(maxDoc => - (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) && + (!CollectionDockingView.CloseRightSplit(Doc.GetProto(maxDoc)) && this.props.addDocTab(getDispDoc(maxDoc), maxLocation))); } } else { @@ -270,8 +270,8 @@ export class DocumentView extends DocComponent(Docu this._downX = e.clientX; this._downY = e.clientY; this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0; - if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) { - CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e); + if (e.shiftKey && e.buttons === 1 && CollectionDockingView.TopLevel) { + CollectionDockingView.TopLevel.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e); e.stopPropagation(); } else { if (this.active) e.stopPropagation(); // events stop at the lowest document that is active. @@ -316,7 +316,7 @@ export class DocumentView extends DocComponent(Docu } } fullScreenClicked = (): void => { - CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeCopy(this.props.Document, false)); + CollectionDockingView.TopLevel && CollectionDockingView.OpenFullScreen(Doc.MakeCopy(this.props.Document, false)); SelectionManager.DeselectAll(); } -- 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/client/util') 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 0a6c03f109254e36556482e75fa5fb14491d1626 Mon Sep 17 00:00:00 2001 From: ab Date: Thu, 13 Jun 2019 12:03:39 -0400 Subject: demo --- src/client/util/RichTextSchema.tsx | 12 ++++++++---- src/client/util/TooltipTextMenu.scss | 11 +++++++++-- src/client/util/TooltipTextMenu.tsx | 15 ++++++--------- src/client/views/nodes/FormattedTextBox.scss | 2 +- 4 files changed, 24 insertions(+), 16 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 61ca4af5e..c0225f1f9 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -259,7 +259,7 @@ export const marks: { [index: string]: MarkSpec } = { toDOM() { return ['span', { style: 'color: blue' - }] + }]; } }, @@ -461,6 +461,7 @@ export class SummarizedView { this._collapsed.style.position = "relative"; this._collapsed.style.width = "40px"; this._collapsed.style.height = "20px"; + let self = this; this._collapsed.onpointerdown = function (e: any) { console.log("star pressed!"); if (node.attrs.visibility) { @@ -469,16 +470,19 @@ export class SummarizedView { let y = getPos(); view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1 + node.attrs.oldtextlen))); view.dispatch(view.state.tr.deleteSelection(view.state, () => { })); - //this._collapsed.textContent = "㊀"; + self._collapsed.textContent = "㊉"; } else { node.attrs.visibility = !node.attrs.visibility; console.log("content is invisible"); let y = getPos(); + console.log(y); let mark = view.state.schema.mark(view.state.schema.marks.underline); console.log("PASTING " + node.attrs.oldtext.toString()); view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1))); - view.dispatch(view.state.tr.replaceSelection(node.attrs.oldtext).addMark(view.state.selection.from, view.state.selection.from + node.attrs.oldtextlen, mark)); - //this._collapsed.textContent = "㊉"; + const from = view.state.selection.from; + view.dispatch(view.state.tr.replaceSelection(node.attrs.oldtext).addMark(from, from + node.attrs.oldtextlen, mark)); + //view.dispatch(view.state.tr.setSelection(view.state.doc, from + node.attrs.oldtextlen + 1, from + node.attrs.oldtextlen + 1)); + self._collapsed.textContent = "㊀"; } e.preventDefault(); e.stopPropagation(); diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index 4d4eb386d..af449071e 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -244,8 +244,8 @@ //transform: translateX(-50%); transform: translateY(-50%); pointer-events: all; - height: auto; - width:inherit; + height: 100; + width:250; .ProseMirror-example-setup-style hr { padding: 2px 10px; border: none; @@ -306,3 +306,10 @@ font-size: 12px; padding-right: 0px; } + .summarize{ + margin-left: 15px; + color: black; + height: 20px; + background-color: white; + text-align: center; + } diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index e12d4ed3c..7d4d5566c 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -234,19 +234,16 @@ export class TooltipTextMenu { this.linkEditor.appendChild(linkBtn); this.tooltip.appendChild(this.linkEditor); + // SUMMARIZE BUTTON + let starButton = document.createElement("span"); - // starButton.style.width = '10px'; - // starButton.style.height = '10px'; - starButton.style.marginLeft = '10px'; - starButton.textContent = "Summarize"; - starButton.style.color = 'black'; - starButton.style.height = '20px'; - starButton.style.backgroundColor = 'white'; - starButton.style.textAlign = 'center'; + starButton.className = "summarize"; + starButton.textContent = "★"; + starButton.title = 'Summarize'; starButton.onclick = () => { let state = this.view.state; this.insertStar(state, this.view.dispatch); - } + }; this.tooltip.appendChild(starButton); } diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 4a29c1949..e3e40860c 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -1,7 +1,7 @@ @import "../globalCssVariables"; .ProseMirror { width: 100%; - height: auto; + height: 100; min-height: 100%; font-family: $serif; } -- 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/client/util') 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/client/util') 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/client/util') 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 552e7b558f6627d91471ca1ee33d3505a94a3a86 Mon Sep 17 00:00:00 2001 From: ab Date: Thu, 13 Jun 2019 16:07:03 -0400 Subject: highlighting --- src/client/util/RichTextSchema.tsx | 18 ++++++++++++++---- src/client/util/TooltipTextMenu.scss | 8 ++++---- src/client/util/TooltipTextMenu.tsx | 6 +++--- 3 files changed, 21 insertions(+), 11 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index c0225f1f9..789f3e0cf 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -5,6 +5,7 @@ import { redo, undo } from 'prosemirror-history'; import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list'; import { EditorState, Transaction, NodeSelection, TextSelection, Selection, } from "prosemirror-state"; import { EditorView, } from "prosemirror-view"; +import { View } from '@react-pdf/renderer'; const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"], preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]; @@ -254,11 +255,11 @@ export const marks: { [index: string]: MarkSpec } = { toDOM: () => ['sup'] }, - collapse: { - parseDOM: [{ style: 'color: blue' }], + highlight: { + parseDOM: [{ style: 'background: #9aa8a4' }], toDOM() { return ['span', { - style: 'color: blue' + style: 'background: #9aa8a4' }]; } }, @@ -453,6 +454,7 @@ export class ImageResizeView { export class SummarizedView { // TODO: highlight text that is summarized. to find end of region, walk along mark _collapsed: HTMLElement; + _view: any; constructor(node: any, view: any, getPos: any) { this._collapsed = document.createElement("span"); this._collapsed.textContent = "㊉"; @@ -462,6 +464,7 @@ export class SummarizedView { this._collapsed.style.width = "40px"; this._collapsed.style.height = "20px"; let self = this; + this._view = view; this._collapsed.onpointerdown = function (e: any) { console.log("star pressed!"); if (node.attrs.visibility) { @@ -471,17 +474,19 @@ export class SummarizedView { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1 + node.attrs.oldtextlen))); view.dispatch(view.state.tr.deleteSelection(view.state, () => { })); self._collapsed.textContent = "㊉"; + self.updateSummarizedText(); } else { node.attrs.visibility = !node.attrs.visibility; console.log("content is invisible"); let y = getPos(); console.log(y); - let mark = view.state.schema.mark(view.state.schema.marks.underline); + let mark = view.state.schema.mark(view.state.schema.marks.highlight); console.log("PASTING " + node.attrs.oldtext.toString()); view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1))); const from = view.state.selection.from; view.dispatch(view.state.tr.replaceSelection(node.attrs.oldtext).addMark(from, from + node.attrs.oldtextlen, mark)); //view.dispatch(view.state.tr.setSelection(view.state.doc, from + node.attrs.oldtextlen + 1, from + node.attrs.oldtextlen + 1)); + view.dispatch(view.state.tr.removeStoredMark(mark)); self._collapsed.textContent = "㊀"; } e.preventDefault(); @@ -493,6 +498,11 @@ export class SummarizedView { selectNode() { } + updateSummarizedText(mark?: any) { + console.log(this._view.state.doc.marks); + console.log(this._view.state.doc.childCount); + } + deselectNode() { } } diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index af449071e..676411535 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -235,7 +235,7 @@ .tooltipMenu { position: absolute; z-index: 50; - background: whitesmoke; + background: black; border: 1px solid silver; border-radius: 15px; padding: 2px 10px; @@ -308,8 +308,8 @@ } .summarize{ margin-left: 15px; - color: black; + color: white; height: 20px; - background-color: white; + // background-color: white; text-align: center; - } + } \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 7d4d5566c..f3f27335f 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -179,13 +179,13 @@ export class TooltipTextMenu { this.linkText.style.whiteSpace = "nowrap"; this.linkText.style.width = "150px"; this.linkText.style.overflow = "hidden"; - this.linkText.style.color = "black"; + this.linkText.style.color = "white"; this.linkText.onpointerdown = (e: PointerEvent) => { e.stopPropagation(); }; let linkBtn = document.createElement("div"); linkBtn.textContent = ">>"; linkBtn.style.width = "10px"; linkBtn.style.height = "10px"; - linkBtn.style.color = "black"; + linkBtn.style.color = "white"; linkBtn.style.cssFloat = "left"; linkBtn.onpointerdown = (e: PointerEvent) => { let node = this.view.state.selection.$from.nodeAfter; @@ -382,7 +382,7 @@ export class TooltipTextMenu { span.className = name + " menuicon"; span.title = title; span.textContent = text; - span.style.color = "black"; + span.style.color = "white"; return span; } -- 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/client/util') 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/client/util') 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/client/util') 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 c22feda3d2df8f6814c0837ea18ad293975ae2e4 Mon Sep 17 00:00:00 2001 From: ab Date: Fri, 14 Jun 2019 17:20:46 -0400 Subject: f this --- src/client/util/RichTextSchema.tsx | 41 +++++++++++++++++++++++++----------- src/client/util/TooltipTextMenu.scss | 2 +- src/client/util/TooltipTextMenu.tsx | 4 ++-- 3 files changed, 32 insertions(+), 15 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 789f3e0cf..8c56a32e8 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType, Slice } from "prosemirror-model"; +import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType, Slice, Mark } from "prosemirror-model"; import { joinUp, lift, setBlockType, toggleMark, wrapIn, selectNodeForward, deleteSelection } from 'prosemirror-commands'; import { redo, undo } from 'prosemirror-history'; import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list'; @@ -90,7 +90,7 @@ export const nodes: { [index: string]: NodeSpec } = { inline: true, attrs: { visibility: { default: false }, - oldtext: { default: undefined }, + text: { default: undefined }, oldtextslice: { default: undefined }, oldtextlen: { default: 0 } @@ -256,10 +256,10 @@ export const marks: { [index: string]: MarkSpec } = { }, highlight: { - parseDOM: [{ style: 'background: #9aa8a4' }], + parseDOM: [{ style: 'background: #d9dbdd' }], toDOM() { return ['span', { - style: 'background: #9aa8a4' + style: 'background: #d9dbdd' }]; } }, @@ -471,20 +471,23 @@ export class SummarizedView { node.attrs.visibility = !node.attrs.visibility; console.log("content is visible"); let y = getPos(); - view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1 + node.attrs.oldtextlen))); + let { from, to } = self.updateSummarizedText(y + 1, view.state.schema.marks.highlight); + let length = to - from; + let newSelection = TextSelection.create(view.state.doc, y + 1, y + 1 + length); + node.attrs.text = newSelection.content(); + view.dispatch(view.state.tr.setSelection(newSelection)); view.dispatch(view.state.tr.deleteSelection(view.state, () => { })); self._collapsed.textContent = "㊉"; - self.updateSummarizedText(); } else { node.attrs.visibility = !node.attrs.visibility; console.log("content is invisible"); let y = getPos(); console.log(y); let mark = view.state.schema.mark(view.state.schema.marks.highlight); - console.log("PASTING " + node.attrs.oldtext.toString()); + console.log("PASTING " + node.attrs.text.toString()); view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1))); const from = view.state.selection.from; - view.dispatch(view.state.tr.replaceSelection(node.attrs.oldtext).addMark(from, from + node.attrs.oldtextlen, mark)); + view.dispatch(view.state.tr.replaceSelection(node.attrs.text).addMark(from, from + node.attrs.oldtextlen, mark)); //view.dispatch(view.state.tr.setSelection(view.state.doc, from + node.attrs.oldtextlen + 1, from + node.attrs.oldtextlen + 1)); view.dispatch(view.state.tr.removeStoredMark(mark)); self._collapsed.textContent = "㊀"; @@ -498,9 +501,23 @@ export class SummarizedView { selectNode() { } - updateSummarizedText(mark?: any) { - console.log(this._view.state.doc.marks); - console.log(this._view.state.doc.childCount); + updateSummarizedText(start?: any, mark?: any) { + let $start = this._view.state.doc.resolve(start); + let first_startPos = $start.start(), endPos = first_startPos; + while ($start.nodeAfter !== null) { + let startIndex = $start.index(), endIndex = $start.indexAfter(); + while (startIndex > 0 && mark.isInSet($start.parent.child(startIndex - 1).marks)) startIndex--; + while (endIndex < $start.parent.childCount && mark.isInSet($start.parent.child(endIndex).marks)) endIndex++; + let startPos = $start.start(), endPos = startPos; + for (let i = 0; i < endIndex; i++) { + let size = $start.parent.child(i).nodeSize; + console.log($start.parent.child(i).childCount); + if (i < startIndex) startPos += size; + endPos += size; + } + $start = this._view.state.doc.resolve(start + (endPos - startPos) + 1); + } + return { from: first_startPos, to: endPos }; } deselectNode() { @@ -524,4 +541,4 @@ schema.nodeFromJSON = (json: any) => { node.attrs.oldtext = Slice.fromJSON(schema, node.attrs.oldtextslice); } return node; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index 676411535..c10a61558 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -235,7 +235,7 @@ .tooltipMenu { position: absolute; z-index: 50; - background: black; + background: #121721; border: 1px solid silver; border-radius: 15px; padding: 2px 10px; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index f3f27335f..b8ca299dd 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -273,9 +273,9 @@ export class TooltipTextMenu { insertStar(state: EditorState, dispatch: any) { console.log("creating star..."); - let newNode = schema.nodes.star.create({ visibility: false, oldtext: state.selection.content(), oldtextslice: state.selection.content().toJSON(), oldtextlen: state.selection.to - state.selection.from }); + let newNode = schema.nodes.star.create({ visibility: false, text: state.selection.content(), oldtextslice: state.selection.content().toJSON(), oldtextlen: state.selection.to - state.selection.from }); if (dispatch) { - console.log(newNode.attrs.oldtext.toString()); + console.log(newNode.attrs.text.toString()); dispatch(state.tr.replaceSelectionWith(newNode)); } return true; -- cgit v1.2.3-70-g09d2 From 218c8b6476621ef0ffe151014f77bb1d506705a3 Mon Sep 17 00:00:00 2001 From: madelinegr Date: Fri, 14 Jun 2019 18:24:01 -0400 Subject: Zooming added to presentation, and done --- src/client/util/DocumentManager.ts | 31 +++++++- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/views/MainOverlayTextBox.tsx | 2 +- src/client/views/MainView.tsx | 5 +- src/client/views/SearchItem.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 6 +- .../views/collections/CollectionSchemaView.tsx | 4 +- .../views/collections/CollectionStackingView.tsx | 2 + .../views/collections/CollectionTreeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 31 +++++--- src/client/views/nodes/DocumentView.tsx | 10 ++- src/client/views/nodes/FieldView.tsx | 5 +- .../views/presentationview/PresentationElement.tsx | 31 +++++++- .../views/presentationview/PresentationView.tsx | 93 +++++++++++++++++++--- 14 files changed, 184 insertions(+), 42 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 65c4b9e4b..a613625cb 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,4 +1,4 @@ -import { computed, observable } from 'mobx'; +import { computed, observable, action } 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'; @@ -115,7 +115,7 @@ export class DocumentManager { } @undoBatch - public jumpToDocument = async (docDelegate: Doc, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number): Promise => { + public jumpToDocument = async (docDelegate: Doc, willZoom: boolean, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number): Promise => { let doc = Doc.GetProto(docDelegate); const contextDoc = await Cast(doc.annotationOn, Doc); if (contextDoc) { @@ -128,7 +128,7 @@ export class DocumentManager { if (!forceDockFunc && (docView = DocumentManager.Instance.getDocumentView(doc))) { docView.props.Document.libraryBrush = true; if (linkPage !== undefined) docView.props.Document.curPage = linkPage; - docView.props.focus(docView.props.Document); + docView.props.focus(docView.props.Document, willZoom); } else { if (!contextDoc) { const actualDoc = Doc.MakeAlias(docDelegate); @@ -140,11 +140,34 @@ export class DocumentManager { docDelegate.libraryBrush = true; if (!forceDockFunc && (contextView = DocumentManager.Instance.getDocumentView(contextDoc))) { contextDoc.panTransformType = "Ease"; - contextView.props.focus(contextDoc); + contextView.props.focus(contextDoc, willZoom); } else { (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc); } } } } + + @action + zoomIntoScale = (docDelegate: Doc, scale: number) => { + let doc = Doc.GetProto(docDelegate); + + let docView: DocumentView | null; + docView = DocumentManager.Instance.getDocumentView(doc); + if (docView) { + docView.props.zoomToScale(scale); + } + } + + getScaleOfDocView = (docDelegate: Doc) => { + let doc = Doc.GetProto(docDelegate); + + let docView: DocumentView | null; + docView = DocumentManager.Instance.getDocumentView(doc); + if (docView) { + return docView.props.getScale(); + } else { + return 1; + } + } } \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index f517f757a..34785446b 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -192,7 +192,7 @@ export class TooltipTextMenu { DocServer.GetRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { if (DocumentManager.Instance.getDocumentView(f)) { - DocumentManager.Instance.getDocumentView(f)!.props.focus(f); + DocumentManager.Instance.getDocumentView(f)!.props.focus(f, false); } else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f); } diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 24327b995..d1224febe 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -102,6 +102,6 @@ export class MainOverlayTextBox extends React.Component
; } - else return (null); Z + else return (null); } } \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 3d772916e..f78879efe 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -199,7 +199,10 @@ export class MainView extends React.Component { parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} - ContainingCollectionView={undefined} />; + ContainingCollectionView={undefined} + zoomToScale={emptyFunction} + getScale={returnOne} + />; let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, Doc)) : undefined; console.log("GETTING mainContent()"); console.log(castRes instanceof Promise); diff --git a/src/client/views/SearchItem.tsx b/src/client/views/SearchItem.tsx index 01c7316d6..101d893de 100644 --- a/src/client/views/SearchItem.tsx +++ b/src/client/views/SearchItem.tsx @@ -24,7 +24,7 @@ library.add(faFilm); export class SearchItem extends React.Component { onClick = () => { - DocumentManager.Instance.jumpToDocument(this.props.doc); + DocumentManager.Instance.jumpToDocument(this.props.doc, false); } //needs help diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index dcc1bd95d..81f574a6c 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -9,7 +9,7 @@ import { Doc, Field, Opt, DocListCast } from "../../../new_fields/Doc"; import { FieldId } from "../../../new_fields/RefField"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, returnTrue, Utils } from "../../../Utils"; +import { emptyFunction, returnTrue, Utils, returnOne } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; import { Transform } from '../../util/Transform'; @@ -483,7 +483,9 @@ export class DockedFrameRenderer extends React.Component { whenActiveChanged={emptyFunction} focus={emptyFunction} addDocTab={this.addDocTab} - ContainingCollectionView={undefined} /> + ContainingCollectionView={undefined} + zoomToScale={emptyFunction} + getScale={returnOne} />
); } diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 11d71d023..715faafd0 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -7,7 +7,7 @@ import { observer } from "mobx-react"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'; import "react-table/react-table.css"; -import { emptyFunction, returnFalse, returnZero } from "../../../Utils"; +import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils"; import { SetupDrag } from "../../util/DragManager"; import { CompileScript } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; @@ -451,6 +451,8 @@ export class CollectionSchemaPreview extends React.Component
)} {input} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index da7ea50c6..e1453c658 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -146,6 +146,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { bringToFront={emptyFunction} whenActiveChanged={this.props.whenActiveChanged} collapseToPoint={this.collapseToPoint} + zoomToScale={emptyFunction} + getScale={returnOne} />
); }) diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2814c0502..c80bd8fce 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -147,7 +147,7 @@ class TreeView extends React.Component { ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab"), icon: "folder" }); ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight"), icon: "caret-square-right" }); if (DocumentManager.Instance.getDocumentViews(this.props.document).length) { - ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) }); + ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document, false)) }); } ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } else { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5d26cb0c2..419d95b5f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -244,7 +244,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { doc.zIndex = docs.length + 1; } - focusDocument = (doc: Doc) => { + focusDocument = (doc: Doc, willZoom: boolean) => { const panX = this.Document.panX; const panY = this.Document.panY; const id = this.Document[Id]; @@ -271,9 +271,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { newState.initializers[id] = { panX: newPanX, panY: newPanY }; HistoryUtil.pushState(newState); this.setPan(newPanX, newPanY); + this.props.Document.panTransformType = "Ease"; this.props.focus(this.props.Document); - //this.setScaleToZoom(doc); + if (willZoom) { + this.setScaleToZoom(doc); + } } @@ -281,21 +284,29 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let p = this.props; let PanelHeight = p.PanelHeight(); let panelWidth = p.PanelWidth(); - // let heightDif: number = PanelHeight - NumCast(doc.height); - // let widthDif: number = panelWidth - NumCast(doc.width); + let docHeight = NumCast(doc.height); let docWidth = NumCast(doc.width); - let targetHeight = 0.8 * PanelHeight; - let targetWidth = 0.8 * panelWidth; + let targetHeight = 0.5 * PanelHeight; + let targetWidth = 0.5 * panelWidth; let maxScaleX: number = targetWidth / docWidth; let maxScaleY: number = targetHeight / docHeight; - // let maxScaleX: number = NumCast(doc.width) / (panelWidth - 10); - // let maxScaleY: number = NumCast(doc.height) / (PanelHeight - 10); let maxApplicableScale = Math.min(maxScaleX, maxScaleY); this.Document.scale = maxApplicableScale; } + zoomToScale = (scale: number) => { + this.Document.scale = scale; + } + + getScale = () => { + if (this.Document.scale) { + return this.Document.scale; + } + return 1; + } + getDocumentViewProps(document: Doc): DocumentViewProps { return { @@ -315,6 +326,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { whenActiveChanged: this.props.whenActiveChanged, bringToFront: this.bringToFront, addDocTab: this.props.addDocTab, + zoomToScale: this.zoomToScale, + getScale: this.getScale }; } @@ -346,7 +359,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private childViews = () => [ , ...this.views - ]; + ] render() { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; const easing = () => this.props.Document.panTransformType === "Ease"; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 449fa76a7..fb7657b68 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -69,13 +69,15 @@ export interface DocumentViewProps { ContentScaling: () => number; PanelWidth: () => number; PanelHeight: () => number; - focus: (doc: Doc) => void; + focus: (doc: Doc, willZoom: boolean) => void; selectOnLoad: boolean; parentActive: () => boolean; whenActiveChanged: (isActive: boolean) => void; bringToFront: (doc: Doc) => void; addDocTab: (doc: Doc, where: string) => void; collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void; + zoomToScale: (scale: number) => void; + getScale: () => number; } const schema = createSchema({ @@ -302,8 +304,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); @@ -422,7 +424,7 @@ export class DocumentView extends DocComponent(Docu this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), "onRight"); }, icon: "search" }); - cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document), icon: "crosshairs" }); + cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" }); cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 7b642b299..3047d55a3 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -103,7 +103,10 @@ export class FieldView extends React.Component { ContainingCollectionView={this.props.ContainingCollectionView} parentActive={this.props.active} whenActiveChanged={this.props.whenActiveChanged} - bringToFront={emptyFunction} /> + bringToFront={emptyFunction} + zoomToScale={emptyFunction} + getScale={returnOne} + /> ); } else if (field instanceof List) { diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx index c58570798..00dc07921 100644 --- a/src/client/views/presentationview/PresentationElement.tsx +++ b/src/client/views/presentationview/PresentationElement.tsx @@ -12,6 +12,7 @@ import { faFile as fileSolid, faLocationArrow, faArrowUp, faSearch } from '@fort import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons'; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; +import { DocumentManager } from "../../util/DocumentManager"; @@ -27,7 +28,7 @@ interface PresentationElementProps { document: Doc; index: number; deleteDocument(index: number): void; - gotoDocument(index: number): void; + gotoDocument(index: number, fromDoc: number): Promise; allListElements: Doc[]; groupMappings: Map; presStatus: boolean; @@ -300,6 +301,9 @@ export default class PresentationElement extends React.Component { + e.stopPropagation(); + if (this.selectedButtons[buttonIndex.Show]) { + this.selectedButtons[buttonIndex.Show] = false; + this.props.document.viewScale = 1; + + } else { + if (this.selectedButtons[buttonIndex.Navigate]) { + this.selectedButtons[buttonIndex.Navigate] = false; + } + this.selectedButtons[buttonIndex.Show] = true; + } + + this.autoSaveButtonChange(buttonIndex.Show); + + } + render() { let p = this.props; @@ -330,13 +355,13 @@ export default class PresentationElement extends React.Component { p.gotoDocument(p.index); e.stopPropagation(); }}> + onClick={e => { p.gotoDocument(p.index, NumCast(this.props.mainDocument.selectedDoc)); e.stopPropagation(); }}> {`${p.index + 1}. ${title}`}

- + diff --git a/src/client/views/presentationview/PresentationView.tsx b/src/client/views/presentationview/PresentationView.tsx index d3365725b..6cf5fad7e 100644 --- a/src/client/views/presentationview/PresentationView.tsx +++ b/src/client/views/presentationview/PresentationView.tsx @@ -26,7 +26,7 @@ export interface PresViewProps { interface PresListProps extends PresViewProps { deleteDocument(index: number): void; - gotoDocument(index: number): void; + gotoDocument(index: number, fromDoc: number): Promise; groupMappings: Map; presElementsMappings: Map; setChildrenDocs: (docList: Doc[]) => void; @@ -71,9 +71,24 @@ class PresentationViewList extends React.Component { }); } + /** + * Initially every document starts with a viewScale 1, which means + * that they will be displayed in a canvas with scale 1. + */ + @action + initializeScaleViews = (docList: Doc[]) => { + docList.forEach((doc: Doc) => { + let curScale = NumCast(doc.viewScale, null); + if (curScale === undefined) { + doc.viewScale = 1; + } + }); + } + render() { const children = DocListCast(this.props.Document.data); this.initializeGroupIds(children); + this.initializeScaleViews(children); this.props.setChildrenDocs(children); return ( @@ -102,7 +117,6 @@ export class PresentationView extends React.Component { @observable presButtonBackUp: Doc = new Doc(); - componentDidMount() { //getting both backUp documents let castedGroupBackUp = Cast(this.props.Document.presGroupBackUp, Doc); @@ -195,7 +209,7 @@ export class PresentationView extends React.Component { if (nextSelected === current) nextSelected = current + 1; } - this.gotoDocument(nextSelected); + this.gotoDocument(nextSelected, current); } back = async () => { @@ -209,6 +223,7 @@ export class PresentationView extends React.Component { //asking for its presentation id. let curPresId = StrCast(docAtCurrent.presentId); let prevSelected = current - 1; + let zoomOut: boolean = false; //checking if this presentation id is mapped to a group, if so chosing the first element in group if (this.groupMappings.has(curPresId)) { @@ -217,11 +232,29 @@ export class PresentationView extends React.Component { //end of grup so go beyond if (prevSelected === current) prevSelected = current - 1; + //checking if any of the group members had used zooming in + currentsArray.forEach((doc: Doc) => { + if (this.presElementsMappings.get(doc)!.selected[buttonIndex.Show]) { + zoomOut = true; + return; + } + }); } + // if a group set that flag to zero or a single element + //If so making sure to zoom out, which goes back to state before zooming action + if (zoomOut || this.presElementsMappings.get(docAtCurrent)!.selected[buttonIndex.Show]) { + let prevScale = NumCast(this.childrenDocs[prevSelected].viewScale, null); + let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[current]); + if (prevScale !== undefined) { + if (prevScale !== curScale) { + DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale); + } + } + } + this.gotoDocument(prevSelected, current); - this.gotoDocument(prevSelected); } /** @@ -285,9 +318,10 @@ export class PresentationView extends React.Component { * has the option open and last in the group. If not in the group, and it has * te option open, navigates to that element. */ - navigateToElement = (curDoc: Doc) => { + navigateToElement = async (curDoc: Doc, fromDoc: number) => { let docToJump: Doc = curDoc; let curDocPresId = StrCast(curDoc.presentId, null); + let willZoom: boolean = false; //checking if in group if (curDocPresId !== undefined) { @@ -297,6 +331,11 @@ export class PresentationView extends React.Component { let selectedButtons: boolean[] = this.presElementsMappings.get(doc)!.selected; if (selectedButtons[buttonIndex.Navigate]) { docToJump = doc; + willZoom = false; + } + if (selectedButtons[buttonIndex.Show]) { + docToJump = doc; + willZoom = true; } }); } @@ -305,13 +344,35 @@ export class PresentationView extends React.Component { //docToJump stayed same meaning, it was not in the group or was the last element in the group if (docToJump === curDoc) { //checking if curDoc has navigation open - if (this.presElementsMappings.get(curDoc)!.selected[buttonIndex.Navigate]) { - DocumentManager.Instance.jumpToDocument(curDoc); - } else { - return; + let curDocButtons = this.presElementsMappings.get(curDoc)!.selected; + if (curDocButtons[buttonIndex.Navigate]) { + DocumentManager.Instance.jumpToDocument(curDoc, false); + } else if (curDocButtons[buttonIndex.Show]) { + let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]); + //awaiting jump so that new scale can be found, since jumping is async + await DocumentManager.Instance.jumpToDocument(curDoc, true); + let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc); + curDoc.viewScale = newScale; + + //saving the scale user was on before zooming in + if (curScale !== 1) { + this.childrenDocs[fromDoc].viewScale = curScale; + } + } + return; + } + let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]); + + //awaiting jump so that new scale can be found, since jumping is async + await DocumentManager.Instance.jumpToDocument(docToJump, willZoom); + let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc); + curDoc.viewScale = newScale; + //saving the scale that user was on + if (curScale !== 1) { + this.childrenDocs[fromDoc].viewScale = curScale; } - DocumentManager.Instance.jumpToDocument(docToJump); + } /** @@ -340,7 +401,7 @@ export class PresentationView extends React.Component { } } @action - public gotoDocument = async (index: number) => { + public gotoDocument = async (index: number, fromDoc: number) => { const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); if (!list) { return; @@ -357,7 +418,7 @@ export class PresentationView extends React.Component { const doc = await list[index]; if (this.presStatus) { - this.navigateToElement(doc); + this.navigateToElement(doc, fromDoc); this.hideIfNotPresented(index); this.showAfterPresented(index); } @@ -407,7 +468,8 @@ export class PresentationView extends React.Component { } else { this.presStatus = true; this.startPresentation(0); - this.gotoDocument(0); + const current = NumCast(this.props.Document.selectedDoc); + this.gotoDocument(0, current); } this.props.Document.presStatus = this.presStatus; } @@ -423,6 +485,11 @@ export class PresentationView extends React.Component { doc.opacity = 1; }); this.props.Document.selectedDoc = 0; + if (this.childrenDocs.length === 0) { + return; + } + DocumentManager.Instance.zoomIntoScale(this.childrenDocs[0], 1); + this.childrenDocs[0].viewScale = 1; } -- 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/client/util') 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/client/util') 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 a3e1f7332e0cb96dae0abd80a2972ae74ac31104 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 17 Jun 2019 13:30:37 -0400 Subject: Doc server documentation completed --- src/Utils.ts | 10 +- src/client/DocServer.ts | 285 +++++++++++++++++++++++++-------- src/client/util/SerializationHelper.ts | 4 +- src/new_fields/Doc.ts | 4 +- src/new_fields/util.ts | 4 +- src/server/index.ts | 12 +- 6 files changed, 235 insertions(+), 84 deletions(-) (limited to 'src/client/util') diff --git a/src/Utils.ts b/src/Utils.ts index 611c61135..657bd673e 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -73,14 +73,14 @@ export class Utils { }; } - public static Emit(socket: Socket | SocketIOClient.Socket, message: Message, args: T) { + public static emit(socket: Socket | SocketIOClient.Socket, message: Message, args: T) { this.log("Emit", message.Name, args, false); socket.emit(message.Message, args); } - public static EmitCallback(socket: Socket | SocketIOClient.Socket, message: Message, args: T): Promise; - public static EmitCallback(socket: Socket | SocketIOClient.Socket, message: Message, args: T, fn: (args: any) => any): void; - public static EmitCallback(socket: Socket | SocketIOClient.Socket, message: Message, args: T, fn?: (args: any) => any): void | Promise { + public static emitCallback(socket: Socket | SocketIOClient.Socket, message: Message, args: T): Promise; + public static emitCallback(socket: Socket | SocketIOClient.Socket, message: Message, args: T, fn: (args: any) => any): void; + public static emitCallback(socket: Socket | SocketIOClient.Socket, message: Message, args: T, fn?: (args: any) => any): void | Promise { this.log("Emit", message.Name, args, false); if (fn) { socket.emit(message.Message, args, this.loggingCallback('Receiving', fn, message.Name)); @@ -89,7 +89,7 @@ export class Utils { } } - public static AddServerHandler(socket: Socket | SocketIOClient.Socket, message: Message, handler: (args: T) => any) { + public static addServerHandler(socket: Socket | SocketIOClient.Socket, message: Message, handler: (args: T) => any) { socket.on(message.Message, this.loggingCallback('Incoming', handler, message.Name)); } diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index d759b4757..ad7c706b6 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,35 +1,116 @@ import * as OpenSocket from 'socket.io-client'; -import { MessageStore } from "./../server/Message"; +import { MessageStore, Diff } from "./../server/Message"; import { Opt } from '../new_fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; import { RefField } from '../new_fields/RefField'; import { Id, HandleUpdate } from '../new_fields/FieldSymbols'; +/** + * This class encapsulates the transfer and cross-client synchronization of + * data stored only in documents (RefFields). In the process, it also + * creates and maintains a cache of documents so that they can be accessed + * more efficiently. Currently, there is no cache eviction scheme in place. + * + * NOTE: while this class is technically abstracted to work with any [RefField], because + * [Doc] instances are the only [RefField] we need / have implemented at the moment, the documentation + * will treat all data used here as [Doc]s + * + * Any time we want to write a new field to the database (via the server) + * or update ourselves based on the server's update message, that occurs here + */ export namespace DocServer { + // a document cache for efficient document retrieval const _cache: { [id: string]: RefField | Promise> } = {}; + // the handle / client side endpoint of the web socket (https://bit.ly/2TeALea for more info) connection established with the server const _socket = OpenSocket(`${window.location.protocol}//${window.location.hostname}:4321`); + // this client's distinct GUID created at initialization const GUID: string = Utils.GenerateGuid(); + // indicates whether or not a document is currently being udpated, and, if so, its id + let updatingId: string | undefined; - export function makeReadOnly() { - _CreateField = emptyFunction; - _UpdateField = emptyFunction; - _respondToUpdate = emptyFunction; - } + export namespace Util { - export function prepend(extension: string): string { - return window.location.origin + extension; - } + /** + * Whenever the server sends us its handshake message on our + * websocket, we use the above function to return the handshake. + */ + Utils.addServerHandler(_socket, MessageStore.Foo, onConnection); + + /** + * This function emits a message (with this client's + * unique GUID) to the server + * indicating that this client has connected + */ + function onConnection() { + _socket.emit(MessageStore.Bar.Message, GUID); + } + + /** + * A convenience method. Prepends the full path (i.e. http://localhost:1050) to the + * requested extension + * @param extension the specified sub-path to append to the window origin + */ + export function prepend(extension: string): string { + return window.location.origin + extension; + } + + /** + * Emits a message to the server that wipes + * all documents in the database. + */ + export function deleteDatabase() { + Utils.emit(_socket, MessageStore.DeleteAll, {}); + } + + /** + * This disables this client's ability to write new fields, + * update existing fields, and update and reflect the changes if + * other clients update shared fields. Thus, the client can only read + * a static snapshot of their workspaces + * + * Currently this is conditionally called in MainView.tsx when analyzing + * the document's url. + */ + export function makeReadOnly() { + // replaces default functionality with no-ops + _createField = emptyFunction; + _emitFieldUpdate = emptyFunction; + _respondToUpdate = emptyFunction; + } - export function DeleteDatabase() { - Utils.Emit(_socket, MessageStore.DeleteAll, {}); } - export async function GetRefField(id: string): Promise> { + // RETRIEVE DOCS FROM SERVER + + /** + * Given a single Doc GUID, this utility function will asynchronously attempt to fetch the id's associated + * field, first looking in the RefField cache and then communicating with + * the server if the document has not been cached. + * @param id the id of the requested document + */ + export async function getRefField(id: string): Promise> { + // an initial pass through the cache to determine whether the document needs to be fetched, + // is already in the process of being fetched or already exists in the + // cache let cached = _cache[id]; if (cached === undefined) { - const prom = Utils.EmitCallback(_socket, MessageStore.GetRefField, id).then(async fieldJson => { + // NOT CACHED => we'll have to send a request to the server + + // synchronously, we emit a single callback to the server requesting the serialized (i.e. represented by a string) + // field for the given ids. This returns a promise, which, when resolved, indicates the the JSON serialized version of + // the field has been returned from the server + const getSerializedField = Utils.emitCallback(_socket, MessageStore.GetRefField, id); + + // when the serialized RefField has been received, go head and begin deserializing it into an object. + // Here, once deserialized, we also invoke .proto to 'load' the document's prototype, which ensures that all + // future .proto calls on the Doc won't have to go farther than the cache to get their actual value. + const deserializeField = getSerializedField.then(async fieldJson => { + // deserialize const field = SerializationHelper.Deserialize(fieldJson); + // either way, overwrite or delete any promises cached at this id (that we inserted as flags + // to indicate that the field was in the process of being fetched). Now everything + // should be an actual value within or entirely absent from the cache. if (field !== undefined) { await field.proto; _cache[id] = field; @@ -38,41 +119,45 @@ export namespace DocServer { } return field; }); - _cache[id] = prom; - return prom; + // here, indicate that the document associated with this id is currently + // being retrieved and cached + _cache[id] = deserializeField; + return deserializeField; } else if (cached instanceof Promise) { + // BEING RETRIEVED AND CACHED => some other caller previously (likely recently) called GetRefField(s), + // and requested the document I'm looking for. Shouldn't fetch again, just + // return this promise which will resolve to the field itself (see 7) return cached; } else { + // CACHED => great, let's just return the cached field we have return cached; } } /** - * Given a list of Doc GUIDs, this utility function will asynchronously attempt to fetch each document - * associated with a given input id, first looking in the RefField cache and then communicating with - * the server if the document was not found there. - * + * Given a list of Doc GUIDs, this utility function will asynchronously attempt to each id's associated + * field, first looking in the RefField cache and then communicating with + * the server if the document has not been cached. * @param ids the ids that map to the reqested documents */ - export async function GetRefFields(ids: string[]): Promise<{ [id: string]: Opt }> { + export async function getRefFields(ids: string[]): Promise<{ [id: string]: Opt }> { const requestedIds: string[] = []; const waitingIds: string[] = []; const promises: Promise>[] = []; const map: { [id: string]: Opt } = {}; - // 1) An initial pass through the cache to determine which documents need to be fetched, + // 1) an initial pass through the cache to determine which documents need to be fetched, // which are already in the process of being fetched and which already exist in the // cache for (const id of ids) { const cached = _cache[id]; - if (cached === undefined) { // NOT CACHED => we'll have to send a request to the server requestedIds.push(id); } else if (cached instanceof Promise) { - // BEING CACHED => someone else previously (likely recently) called GetRefFields, + // BEING RETRIEVED AND CACHED => some other caller previously (likely recently) called GetRefField(s), // and requested one of the documents I'm looking for. Shouldn't fetch again, just - // wait until this promise is resolved (see the second to last line of the function) + // wait until this promise is resolved (see 7) promises.push(cached); waitingIds.push(id); } else { @@ -81,46 +166,49 @@ export namespace DocServer { } } - // 2) Synchronously, we emit a single callback to the server requesting the documents for the given ids. - // This returns a promise, which, when resolved, indicates that all the JSON serialized versions of + // 2) synchronously, we emit a single callback to the server requesting the serialized (i.e. represented by a string) + // fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of // the fields have been returned from the server - const fieldsReceived: Promise = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds); + const getSerializedFields: Promise = Utils.emitCallback(_socket, MessageStore.GetRefFields, requestedIds); - // 3) When the serialized RefFields have been received, go head and begin deserializing them into objects. + // 3) when the serialized RefFields have been received, go head and begin deserializing them into objects. // Here, once deserialized, we also invoke .proto to 'load' the documents' prototypes, which ensures that all - // future .proto calls won't have to go farther than the cache to get their actual value. - const fieldsDeserialized = fieldsReceived.then(async fields => { + // future .proto calls on the Doc won't have to go farther than the cache to get their actual value. + const deserializeFields = getSerializedFields.then(async fields => { const fieldMap: { [id: string]: RefField } = {}; - const deserializedFields: any = []; + const protosToLoad: any = []; for (const field of fields) { if (field !== undefined) { // deserialize let deserialized: any = SerializationHelper.Deserialize(field); fieldMap[field.id] = deserialized; - deserializedFields.push(deserialized.proto); + // adds to a list of promises that will be awaited asynchronously + protosToLoad.push(deserialized.proto); } } - // this actually handles the loeading of prototypes - await Promise.all(deserializedFields); + // this actually handles the loading of prototypes + await Promise.all(protosToLoad); return fieldMap; }); - // 4) Here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) + // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) // we set the value at the field's id to a promise that will resolve to the field. // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method). - requestedIds.forEach(id => _cache[id] = fieldsDeserialized.then(fields => fields[id])); + // The mapping in the .then call ensures that when other callers await these promises, they'll + // get the resolved field + requestedIds.forEach(id => _cache[id] = deserializeFields.then(fields => fields[id])); - // 5) At this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose + // 5) at this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose // prototype documents, if any, have also been fetched and cached. - const fields = await fieldsDeserialized; + const fields = await deserializeFields; - // 6) With this confidence, we can now go through and update the cache at the ids of the fields that + // 6) with this confidence, we can now go through and update the cache at the ids of the fields that // we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given // id to the soon to be returned field mapping. requestedIds.forEach(id => { const field = fields[id]; - // either way, overwrite or delete any promises that we inserted as flags - // to indicate that the field was in the process of being fetched. Now everything + // either way, overwrite or delete any promises (that we inserted as flags + // to indicate that the field was in the process of being fetched). Now everything // should be an actual value within or entirely absent from the cache. if (field !== undefined) { _cache[id] = field; @@ -130,78 +218,141 @@ export namespace DocServer { map[id] = field; }); - // 7) Those promises we encountered in the else if of 1), which represent + // 7) those promises we encountered in the else if of 1), which represent // other callers having already submitted a request to the server for (a) document(s) // in which we're interested, must still be awaited so that we can return the proper // values for those as well. // - // Fortunately, those other callers will also hit their own version of 6) and clean up + // fortunately, those other callers will also hit their own version of 6) and clean up // the shared cache when these promises resolve, so all we have to do is... const otherCallersFetching = await Promise.all(promises); // ...extract the RefFields returned from the resolution of those promises and add them to our // own map. waitingIds.forEach((id, index) => map[id] = otherCallersFetching[index]); - // Now, we return our completed mapping from all of the ids that were passed into the method + // now, we return our completed mapping from all of the ids that were passed into the method // to their actual RefField | undefined values. This return value either becomes the input // argument to the caller's promise (i.e. GetRefFields.then(map => //do something with map...)) // or it is the direct return result if the promise is awaited. return map; } - let _UpdateField = (id: string, diff: any) => { + // WRITE A NEW DOCUMENT TO THE SERVER + + /** + * A wrapper around the function local variable _createField. + * This allows us to swap in different executions while comfortably + * calling the same function throughout the code base (such as in Util.makeReadonly()) + * @param field the [RefField] to be serialized and sent to the server to be stored in the database + */ + export function createField(field: RefField) { + _createField(field); + } + + /** + * The default behavior for field creation. This inserts the [Doc] instance + * in the cache at its id, serializes the [Doc]'s initial state + * and finally sends that seruialized data to the server. + * @param field the [RefField] to be serialized and sent to the server to be stored in the database + */ + let _createField = (field: RefField) => { + _cache[field[Id]] = field; + const initialState = SerializationHelper.serialize(field); + Utils.emit(_socket, MessageStore.CreateField, initialState); + }; + + // NOTIFY THE SERVER OF AN UPDATE TO A DOC'S STATE + + /** + * A wrapper around the function local variable _emitFieldUpdate. + * This allows us to swap in different executions while comfortably + * calling the same function throughout the code base (such as in Util.makeReadonly()) + * @param id the id of the [Doc] whose state has been updated in our client + * @param updatedState the new value of the document. At some point, this + * should actually be a proper diff, to improve efficiency + */ + export function emitFieldUpdate(id: string, updatedState: any) { + _emitFieldUpdate(id, updatedState); + } + + /** + * The default behavior for indicating to the server that we've locally updated + * a document. + * @param id the id of the [Doc] whose state has been updated in our client + * @param updatedState the new value of the document. At some point, this + * should actually be a proper diff, to improve efficiency + */ + let _emitFieldUpdate = (id: string, updatedState: any) => { + // don't emit a duplicate message if the server is already + // (asynchronously) still updating this document's state. if (id === updatingId) { return; } - Utils.Emit(_socket, MessageStore.UpdateField, { id, diff }); + // creates the diff object to send to the server + let diff: Diff = { id, diff: updatedState }; + // emit this diff to notify server + Utils.emit(_socket, MessageStore.UpdateField, diff); }; - export function UpdateField(id: string, diff: any) { - _UpdateField(id, diff); - } + // RESPOND TO THE SERVER'S INDICATION THAT A DOC'S STATE HAS BEEN UPDATED - let _CreateField = (field: RefField) => { - _cache[field[Id]] = field; - const initialState = SerializationHelper.Serialize(field); - Utils.Emit(_socket, MessageStore.CreateField, initialState); - }; + /** + * Whenever the client receives an update, execute the + * current behavior. + */ + Utils.addServerHandler(_socket, MessageStore.UpdateField, respondToUpdate); - export function CreateField(field: RefField) { - _CreateField(field); + /** + * A wrapper around the function local variable _respondToUpdate. + * This allows us to swap in different executions while comfortably + * calling the same function throughout the code base (such as in Util.makeReadonly()) + * @param diff kept as [any], but actually the [Diff] object sent from the server containing + * the [Doc]'s id and its new state + */ + function respondToUpdate(diff: any) { + _respondToUpdate(diff); } - let updatingId: string | undefined; + /** + * The default behavior for responding to another client's indication + * that it has updated the state of a [Doc] that is also in use by + * this client + * @param diff kept as [any], but actually the [Diff] object sent from the server containing + * the [Doc]'s id and its new state + */ let _respondToUpdate = (diff: any) => { const id = diff.id; + // to be valid, the Diff object must reference + // a document's id if (id === undefined) { return; } - const field = _cache[id]; const update = (f: Opt) => { + // if the RefField is absent from the cache or + // its promise in the cache resolves to undefined, there + // can't be anything to update if (f === undefined) { return; } + // extract this Doc's update handler const handler = f[HandleUpdate]; if (handler) { + // set the 'I'm currently updating this Doc' flag updatingId = id; handler.call(f, diff.diff); + // reset to indicate no ongoing updates updatingId = undefined; } }; + // check the cache for the field + const field = _cache[id]; if (field instanceof Promise) { + // if the field is still being retrieved, update when the promise is resolved field.then(update); } else { + // otherwise, just execute the update update(field); } }; - function respondToUpdate(diff: any) { - _respondToUpdate(diff); - } - - function connected() { - _socket.emit(MessageStore.Bar.Message, GUID); - } - Utils.AddServerHandler(_socket, MessageStore.Foo, connected); - Utils.AddServerHandler(_socket, MessageStore.UpdateField, respondToUpdate); } \ No newline at end of file diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index 7ded85e43..ea8af3834 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -7,7 +7,7 @@ export namespace SerializationHelper { return serializing > 0; } - export function Serialize(obj: Field): any { + export function serialize(obj: Field): any { if (obj === undefined || obj === null) { return undefined; } @@ -124,7 +124,7 @@ export namespace Deserializable { export function autoObject(): PropSchema { return custom( - (s) => SerializationHelper.Serialize(s), + (s) => SerializationHelper.serialize(s), (s) => SerializationHelper.Deserialize(s) ); } \ No newline at end of file diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index af65f5482..9da8912fe 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -80,7 +80,7 @@ export class Doc extends RefField { }); this[SelfProxy] = doc; if (!id || forceSave) { - DocServer.CreateField(doc); + DocServer.createField(doc); } return doc; } @@ -108,7 +108,7 @@ export class Doc extends RefField { private ___fields: any = {}; private [Update] = (diff: any) => { - DocServer.UpdateField(this[Id], diff); + DocServer.emitFieldUpdate(this[Id], diff); } private [Self] = this; diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 8caceb063..7709d6c24 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -43,7 +43,7 @@ export const setter = action(function (target: any, prop: string | symbol | numb } else { target.__fields[prop] = value; } - target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) } }); + target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.serialize(value) : (value === undefined ? null : value) } }); UndoManager.AddEvent({ redo: () => receiver[prop] = value, undo: () => receiver[prop] = curValue @@ -103,7 +103,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any let current = ObjectField.MakeCopy(value); return (diff?: any) => { if (true || !diff) { - diff = { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; + diff = { '$set': { ["fields." + prop]: SerializationHelper.serialize(value) } }; const oldValue = current; const newValue = ObjectField.MakeCopy(value); current = newValue; diff --git a/src/server/index.ts b/src/server/index.ts index fd66c90b4..d9fe9d93d 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -301,16 +301,16 @@ let clients: Map = {}; server.on("connection", function (socket: Socket) { console.log("a user has connected"); - Utils.Emit(socket, MessageStore.Foo, "handshooken"); + Utils.emit(socket, MessageStore.Foo, "handshooken"); - Utils.AddServerHandler(socket, MessageStore.Bar, barReceived); - Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args)); + Utils.addServerHandler(socket, MessageStore.Bar, barReceived); + Utils.addServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args)); Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField); Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields); - Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields); + Utils.addServerHandler(socket, MessageStore.DeleteAll, deleteFields); - Utils.AddServerHandler(socket, MessageStore.CreateField, CreateField); - Utils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff)); + Utils.addServerHandler(socket, MessageStore.CreateField, CreateField); + Utils.addServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff)); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); }); -- cgit v1.2.3-70-g09d2 From de0304b2966ebdede9d9db8c510e19020046115c Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 17 Jun 2019 13:38:15 -0400 Subject: peripheral renaming fixes --- src/client/documents/Documents.ts | 4 ++-- src/client/util/History.ts | 6 +++--- src/client/util/SearchUtil.ts | 4 ++-- src/client/util/TooltipTextMenu.tsx | 8 ++++---- src/client/views/MainView.tsx | 10 +++++----- src/client/views/SearchBox.tsx | 6 +++--- src/client/views/collections/CollectionDockingView.tsx | 10 +++++----- src/client/views/collections/CollectionSubView.tsx | 8 ++++---- src/client/views/collections/CollectionVideoView.tsx | 2 +- src/client/views/collections/DockedFrameRenderer.tsx | 2 +- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- .../views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 6 +++--- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 2 +- src/debug/Viewer.tsx | 2 +- src/mobile/ImageUpload.tsx | 4 ++-- src/new_fields/Proxy.ts | 2 +- src/server/authentication/models/current_user_utils.ts | 6 +++--- 20 files changed, 46 insertions(+), 46 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b10954636..758291b9b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -98,7 +98,7 @@ export namespace Docs { // non-guid string ids for each document prototype let protoIds = [textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId] // fetch the actual prototype documents from the server - let actualProtos = await DocServer.GetRefFields(protoIds); + let actualProtos = await DocServer.getRefFields(protoIds); // initialize prototype documents textProto = actualProtos[textProtoId] as Doc || CreateTextProto(); @@ -363,7 +363,7 @@ export namespace Docs { CurrentUserUtils.AddNorthstarSchema(schema, schemaDoc); const docs = schemaDocuments; CurrentUserUtils.GetAllNorthstarColumnAttributes(schema).map(attr => { - DocServer.GetRefField(attr.displayName! + ".alias").then(action((field: Opt) => { + DocServer.getRefField(attr.displayName! + ".alias").then(action((field: Opt) => { if (field instanceof Doc) { docs.push(field); } else { diff --git a/src/client/util/History.ts b/src/client/util/History.ts index 545ea8629..94bfcbe09 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -88,7 +88,7 @@ export namespace HistoryUtil { } export function createUrl(params: ParsedUrl): string { - let baseUrl = DocServer.prepend(`/${params.type}`); + let baseUrl = DocServer.Util.prepend(`/${params.type}`); switch (params.type) { case "doc": const initializers = encodeURIComponent(JSON.stringify(params.initializers)); @@ -103,7 +103,7 @@ export namespace HistoryUtil { } export async function initDoc(id: string, initializer: DocInitializerList) { - const doc = await DocServer.GetRefField(id); + const doc = await DocServer.getRefField(id); if (!(doc instanceof Doc)) { return; } @@ -111,7 +111,7 @@ export namespace HistoryUtil { } async function onDocUrl(url: DocUrl) { - const field = await DocServer.GetRefField(url.docId); + const field = await DocServer.getRefField(url.docId); await Promise.all(Object.keys(url.initializers).map(id => initDoc(id, url.initializers[id]))); if (field instanceof Doc) { MainView.Instance.openWorkspace(field, true); diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 28ec8ca14..9dd9acbb7 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -7,13 +7,13 @@ export namespace SearchUtil { export function Search(query: string, returnDocs: true): Promise; export function Search(query: string, returnDocs: false): Promise; export async function Search(query: string, returnDocs: boolean) { - const ids = JSON.parse(await rp.get(DocServer.prepend("/search"), { + const ids = JSON.parse(await rp.get(DocServer.Util.prepend("/search"), { qs: { query } })); if (!returnDocs) { return ids; } - const docMap = await DocServer.GetRefFields(ids); + const docMap = await DocServer.getRefFields(ids); return ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc); } diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index fa2483db5..36219a99e 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -187,9 +187,9 @@ export class TooltipTextMenu { let link = node && node.marks.find(m => m.type.name === "link"); if (link) { let href: string = link.attrs.href; - if (href.indexOf(DocServer.prepend("/doc/")) === 0) { - let docid = href.replace(DocServer.prepend("/doc/"), ""); - DocServer.GetRefField(docid).then(action((f: Opt) => { + if (href.indexOf(DocServer.Util.prepend("/doc/")) === 0) { + let docid = href.replace(DocServer.Util.prepend("/doc/"), ""); + DocServer.getRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { if (DocumentManager.Instance.getDocumentView(f)) { DocumentManager.Instance.getDocumentView(f)!.props.focus(f); @@ -218,7 +218,7 @@ export class TooltipTextMenu { handlers: { dragComplete: action(() => { let m = dragData.droppedDocuments; - this.makeLink(DocServer.prepend("/doc/" + m[0][Id])); + this.makeLink(DocServer.Util.prepend("/doc/" + m[0][Id])); }), }, hideSource: false diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 984db0426..734961b56 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -76,11 +76,11 @@ export class MainView extends React.Component { // causes errors to be generated when modifying an observable outside of an action configure({ enforceActions: "observed" }); if (window.location.search.includes("readonly")) { - DocServer.makeReadOnly(); + DocServer.Util.makeReadOnly(); } if (window.location.search.includes("safe")) { if (!window.location.search.includes("nro")) { - DocServer.makeReadOnly(); + DocServer.Util.makeReadOnly(); } CollectionBaseView.SetSafeMode(true); } @@ -141,7 +141,7 @@ export class MainView extends React.Component { this.createNewWorkspace(); } } else { - DocServer.GetRefField(CurrentUserUtils.MainDocId).then(field => + DocServer.getRefField(CurrentUserUtils.MainDocId).then(field => field instanceof Doc ? this.openWorkspace(field) : this.createNewWorkspace(CurrentUserUtils.MainDocId)); } @@ -294,7 +294,7 @@ export class MainView extends React.Component { let logoutRef = React.createRef(); return [ - , + ,
+
]; } diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx index 7164d98a4..973715876 100644 --- a/src/client/views/SearchBox.tsx +++ b/src/client/views/SearchBox.tsx @@ -56,13 +56,13 @@ export class SearchBox extends React.Component { @action getResults = async (query: string) => { - let response = await rp.get(DocServer.prepend('/search'), { + let response = await rp.get(DocServer.Util.prepend('/search'), { qs: { query } }); let res: string[] = JSON.parse(response); - const fields = await DocServer.GetRefFields(res); + const fields = await DocServer.getRefFields(res); const docs: Doc[] = []; for (const id of res) { const field = fields[id]; @@ -74,7 +74,7 @@ export class SearchBox extends React.Component { } public static async convertDataUri(imageUri: string, returnedFilename: string) { try { - let posting = DocServer.prepend(RouteStore.dataUriToImage); + let posting = DocServer.Util.prepend(RouteStore.dataUriToImage); const returnedUri = await rp.post(posting, { body: { uri: imageUri, diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index e2bcb10ec..4f5837590 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -306,7 +306,7 @@ export class CollectionDockingView extends React.Component) => + DocServer.getRefField(docid).then(action(async (sourceDoc: Opt) => (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc))); } else if ((className === "lm_title" || className === "lm_tab lm_active") && !e.shiftKey) { @@ -320,7 +320,7 @@ export class CollectionDockingView extends React.Component) => { + DocServer.getRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y, { @@ -372,7 +372,7 @@ export class CollectionDockingView extends React.Component { + DocServer.getRefField(tab.contentItem.config.props.documentId).then(async doc => { if (doc instanceof Doc) { let counter: any = this.htmlToElement(`0
`); tab.element.append(counter); @@ -409,7 +409,7 @@ export class CollectionDockingView extends React.Component { - let doc = await DocServer.GetRefField(contentItem.config.props.documentId); + let doc = await DocServer.getRefField(contentItem.config.props.documentId); if (doc instanceof Doc) { let theDoc = doc; CollectionDockingView.TopLevel._removedDocs.push(theDoc); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 440a2410b..36e276d13 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -133,7 +133,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (path.includes(window.location.hostname)) { let s = path.split('/'); let id = s[s.length - 1]; - DocServer.GetRefField(id).then(field => { + DocServer.getRefField(id).then(field => { if (field instanceof Doc) { let alias = Doc.MakeAlias(field); alias.x = options.x || 0; @@ -170,8 +170,8 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (html && html.indexOf(document.location.origin)) { // prosemirror text containing link to dash document let start = html.indexOf(window.location.origin); let path = html.substr(start, html.length - start); - let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.prepend("/doc/"), "").split("?")[0]; - DocServer.GetRefField(docid).then(f => (f instanceof Doc) && this.props.addDocument(f, false)); + let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.Util.prepend("/doc/"), "").split("?")[0]; + DocServer.getRefField(docid).then(f => (f instanceof Doc) && this.props.addDocument(f, false)); return; } if (html && html.indexOf("(schemaCtor: (doc: Doc) => T) { if (item.kind === "string" && item.type.indexOf("uri") !== -1) { let str: string; let prom = new Promise(resolve => e.dataTransfer.items[i].getAsString(resolve)) - .then(action((s: string) => rp.head(DocServer.prepend(RouteStore.corsProxy + "/" + (str = s))))) + .then(action((s: string) => rp.head(DocServer.Util.prepend(RouteStore.corsProxy + "/" + (str = s))))) .then(result => { let type = result["content-type"]; if (type) { diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index bd5cd5450..ccbac9915 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -97,7 +97,7 @@ export class CollectionVideoView extends React.Component { let filename = encodeURIComponent("snapshot" + this.props.Document.title + "_" + this.props.Document.curPage).replace(/\./g, ""); SearchBox.convertDataUri(dataUrl, filename).then((returnedFilename) => { if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); + let url = DocServer.Util.prepend(returnedFilename); let imageSummary = Docs.Create.ImageDocument(url, { x: NumCast(this.props.Document.x) + width, y: NumCast(this.props.Document.y), width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-" diff --git a/src/client/views/collections/DockedFrameRenderer.tsx b/src/client/views/collections/DockedFrameRenderer.tsx index 25d4b2a49..1e7c5661b 100644 --- a/src/client/views/collections/DockedFrameRenderer.tsx +++ b/src/client/views/collections/DockedFrameRenderer.tsx @@ -42,7 +42,7 @@ export class DockedFrameRenderer extends React.Component { } constructor(props: any) { super(props); - DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); + DocServer.getRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); } nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9d19df540..cd613e6ab 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -235,8 +235,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { e.preventDefault(); let start = html.indexOf(window.location.origin); let path = html.substr(start, html.length - start); - let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.prepend("/doc/"), "").split("?")[0]; - DocServer.GetRefField(docid).then(f => { + let docid = path.substr(0, path.indexOf("\">")).replace(DocServer.Util.prepend("/doc/"), "").split("?")[0]; + DocServer.getRefField(docid).then(f => { if (f instanceof Doc) { f.x = pt[0]; f.y = pt[1]; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index cd386abfa..07a58ed64 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -306,7 +306,7 @@ export class MarqueeView extends React.Component let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" }); SearchBox.convertDataUri(dataUrl, "icon" + summary[Id] + "_image").then((returnedFilename) => { if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); + let url = DocServer.Util.prepend(returnedFilename); let imageSummary = Docs.Create.ImageDocument(url, { x: bounds.left, y: bounds.top + 100 / zoomBasis, width: 150, height: bounds.height / bounds.width * 150, title: "-summary image-" diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 16e40000d..fdcb20e9a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -422,7 +422,7 @@ export class DocumentView extends DocComponent(Docu }, icon: "search" }); cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document), icon: "crosshairs" }); - cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); + cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.Util.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); if (!this.topMost) { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index d00a4b928..6a14a04f7 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -237,9 +237,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe href = parent.childNodes[0].href; } if (href) { - if (href.indexOf(DocServer.prepend("/doc/")) === 0) { - let docid = href.replace(DocServer.prepend("/doc/"), "").split("?")[0]; - DocServer.GetRefField(docid).then(f => { + if (href.indexOf(DocServer.Util.prepend("/doc/")) === 0) { + let docid = href.replace(DocServer.Util.prepend("/doc/"), "").split("?")[0]; + DocServer.getRefField(docid).then(f => { (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, "inTab")) }); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index aa29a7170..df9e49b64 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -257,7 +257,7 @@ export class PDFBox extends DocComponent(PdfDocumen .then(action((dataUrl: string) => { SearchBox.convertDataUri(dataUrl, "icon" + this.Document[Id] + "_" + this.curPage).then((returnedFilename) => { if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); + let url = DocServer.Util.prepend(returnedFilename); this.props.Document.thumbnail = new ImageField(new URL(url)); } runInAction(() => this._renderAsSvg = true); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 35ecf12f6..9ab607e91 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -97,7 +97,7 @@ export class VideoBox extends DocComponent(VideoD }; try { let responseSchema: any = {}; - const videoInfoResponse = await rp.get(DocServer.prepend(RouteStore.corsProxy + "/" + `https://www.youtube.com/watch?v=${videoId}`), videoInfoRequestConfig); + const videoInfoResponse = await rp.get(DocServer.Util.prepend(RouteStore.corsProxy + "/" + `https://www.youtube.com/watch?v=${videoId}`), videoInfoRequestConfig); const dataHtml = videoInfoResponse; const start = dataHtml.indexOf('ytplayer.config = ') + 18; const end = dataHtml.indexOf(';ytplayer.load'); diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index b22300d0b..753149756 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -146,7 +146,7 @@ class Viewer extends React.Component { @action onKeyPress = (e: React.KeyboardEvent) => { if (e.key === "Enter") { - DocServer.GetRefField(this.idToAdd).then(action((field: any) => { + DocServer.getRefField(this.idToAdd).then(action((field: any) => { if (field !== undefined) { this.fields.push(field); } diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index a8f94b746..df597e0a9 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -57,11 +57,11 @@ class Uploader extends React.Component { this.status = "getting user document"; - const res = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)); + const res = await rp.get(DocServer.Util.prepend(RouteStore.getUserDocumentId)); if (!res) { throw new Error("No user id returned"); } - const field = await DocServer.GetRefField(res); + const field = await DocServer.getRefField(res); let pending: Opt; if (field instanceof Doc) { pending = await Cast(field.optionalRightCollection, Doc); diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index 130ec066e..230e4ab8b 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -57,7 +57,7 @@ export class ProxyField extends ObjectField { return undefined; } if (!this.promise) { - this.promise = DocServer.GetRefField(this.fieldId).then(action((field: any) => { + this.promise = DocServer.getRefField(this.fieldId).then(action((field: any) => { this.promise = undefined; this.cache = field; if (field === undefined) this.failed = true; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 169be3b99..95c20d2db 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -40,7 +40,7 @@ export class CurrentUserUtils { } public static async loadCurrentUser(): Promise { - let userPromise = rp.get(DocServer.prepend(RouteStore.getCurrUser)).then(response => { + let userPromise = rp.get(DocServer.Util.prepend(RouteStore.getCurrUser)).then(response => { if (response) { let obj = JSON.parse(response); CurrentUserUtils.curr_id = obj.id as string; @@ -49,9 +49,9 @@ export class CurrentUserUtils { throw new Error("There should be a user! Why does Dash think there isn't one?"); } }); - let userDocPromise = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => { + let userDocPromise = await rp.get(DocServer.Util.prepend(RouteStore.getUserDocumentId)).then(id => { if (id) { - return DocServer.GetRefField(id).then(field => + return DocServer.getRefField(id).then(field => runInAction(() => this.user_document = field instanceof Doc ? field : this.createUserDocument(id))); } else { throw new Error("There should be a user id! Why does Dash think there isn't one?"); -- cgit v1.2.3-70-g09d2 From 01330e3c72828094077e718c8c8d08acf8e81a77 Mon Sep 17 00:00:00 2001 From: ab Date: Mon, 17 Jun 2019 14:55:56 -0400 Subject: nodesBetween showing some promise... --- src/client/util/RichTextSchema.tsx | 39 ++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 8c56a32e8..b130ace2a 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType, Slice, Mark } from "prosemirror-model"; +import { Schema, NodeSpec, MarkSpec, DOMOutputSpecArray, NodeType, Slice, Mark, Node } from "prosemirror-model"; import { joinUp, lift, setBlockType, toggleMark, wrapIn, selectNodeForward, deleteSelection } from 'prosemirror-commands'; import { redo, undo } from 'prosemirror-history'; import { orderedList, bulletList, listItem, } from 'prosemirror-schema-list'; @@ -503,19 +503,30 @@ export class SummarizedView { updateSummarizedText(start?: any, mark?: any) { let $start = this._view.state.doc.resolve(start); - let first_startPos = $start.start(), endPos = first_startPos; - while ($start.nodeAfter !== null) { - let startIndex = $start.index(), endIndex = $start.indexAfter(); - while (startIndex > 0 && mark.isInSet($start.parent.child(startIndex - 1).marks)) startIndex--; - while (endIndex < $start.parent.childCount && mark.isInSet($start.parent.child(endIndex).marks)) endIndex++; - let startPos = $start.start(), endPos = startPos; - for (let i = 0; i < endIndex; i++) { - let size = $start.parent.child(i).nodeSize; - console.log($start.parent.child(i).childCount); - if (i < startIndex) startPos += size; - endPos += size; - } - $start = this._view.state.doc.resolve(start + (endPos - startPos) + 1); + let first_startPos = $start.start();//, endPos = first_startPos; + let startIndex = $start.index(), endIndex = $start.indexAfter(); + let nodeAfter = $start.nodeAfter; + while (startIndex > 0 && mark.isInSet($start.parent.child(startIndex - 1).marks)) startIndex--; + while (endIndex < $start.parent.childCount && mark.isInSet($start.parent.child(endIndex).marks)) endIndex++; + let startPos = $start.start(), endPos = startPos; + for (let i = 0; i < endIndex; i++) { + let size = $start.parent.child(i).nodeSize; + console.log($start.parent.child(i).childCount); + if (i < startIndex) startPos += size; + endPos += size; + } + for (let i: number = start + 1; i < this._view.state.doc.nodeSize - 1; i++) { + console.log("ITER:", i); + this._view.state.doc.nodesBetween(start, i, (node: Node, pos: number, parent: Node, index: number) => { + if (node === undefined || parent === undefined) { + console.log('undefined dawg'); + return false; + } + if (node.isLeaf) { + console.log(node.textContent); + console.log(node.marks); + } + }); } return { from: first_startPos, to: endPos }; } -- cgit v1.2.3-70-g09d2 From c2da1676379817fb977a7233fadfb96ab67dc16f Mon Sep 17 00:00:00 2001 From: ab Date: Tue, 18 Jun 2019 13:20:47 -0400 Subject: highlighting using nodesbetween, still some index bugs --- src/client/util/RichTextSchema.tsx | 47 +++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index b130ace2a..391a4fa04 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -503,32 +503,41 @@ export class SummarizedView { updateSummarizedText(start?: any, mark?: any) { let $start = this._view.state.doc.resolve(start); - let first_startPos = $start.start();//, endPos = first_startPos; - let startIndex = $start.index(), endIndex = $start.indexAfter(); - let nodeAfter = $start.nodeAfter; - while (startIndex > 0 && mark.isInSet($start.parent.child(startIndex - 1).marks)) startIndex--; - while (endIndex < $start.parent.childCount && mark.isInSet($start.parent.child(endIndex).marks)) endIndex++; - let startPos = $start.start(), endPos = startPos; - for (let i = 0; i < endIndex; i++) { - let size = $start.parent.child(i).nodeSize; - console.log($start.parent.child(i).childCount); - if (i < startIndex) startPos += size; - endPos += size; - } + let endPos = start; + //let first_startPos = $start.start(), endPos = first_startPos; + // let startIndex = $start.index(), endIndex = $start.indexAfter(); + // let nodeAfter = $start.nodeAfter; + // while (startIndex > 0 && mark.isInSet($start.parent.child(startIndex - 1).marks)) startIndex--; + // while (endIndex < $start.parent.childCount && mark.isInSet($start.parent.child(endIndex).marks)) endIndex++; + // let startPos = $start.start(), endPos = startPos; + // for (let i = 0; i < endIndex; i++) { + // let size = $start.parent.child(i).nodeSize; + // console.log($start.parent.child(i).childCount); + // if (i < startIndex) startPos += size; + // endPos += size; + // } + let _mark = this._view.state.schema.mark(this._view.state.schema.marks.highlight); + // first_startPos = start; + // endPos = first_startPos; + let visited = new Set(); for (let i: number = start + 1; i < this._view.state.doc.nodeSize - 1; i++) { console.log("ITER:", i); this._view.state.doc.nodesBetween(start, i, (node: Node, pos: number, parent: Node, index: number) => { - if (node === undefined || parent === undefined) { - console.log('undefined dawg'); - return false; - } if (node.isLeaf) { - console.log(node.textContent); - console.log(node.marks); + if (node.marks.includes(_mark) && !visited.has(node)) { + visited.add(node); + //endPos += node.nodeSize + 1; + endPos = i + node.nodeSize - 1; + console.log("node contains mark!"); + } + else { } + } }); } - return { from: first_startPos, to: endPos }; + console.log(start); + console.log(endPos); + return { from: start, to: endPos }; } deselectNode() { -- 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/client/util') 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 4bec1d89eff45d6dcbb4041bc211db88d9da1c8f Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 18 Jun 2019 22:02:58 -0400 Subject: fixed serialization typo and added draft of python word doc scraper to git directory for safety --- src/buxton/scraper.py | 128 +++++++++++++++++++++++++++++++++ src/client/DocServer.ts | 2 +- src/client/util/SerializationHelper.ts | 4 +- src/new_fields/util.ts | 4 +- 4 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 src/buxton/scraper.py (limited to 'src/client/util') diff --git a/src/buxton/scraper.py b/src/buxton/scraper.py new file mode 100644 index 000000000..0abebb485 --- /dev/null +++ b/src/buxton/scraper.py @@ -0,0 +1,128 @@ +import os +import docx2txt +from docx import Document +from docx.opc.constants import RELATIONSHIP_TYPE as RT +import re +from pymongo import MongoClient +import shutil +import uuid + +source = "./source" +dist = "./Dash-Web/src/server/public/files" + +collection_handle = MongoClient("localhost", 27017)["Dash"]["buxton"] + +def extract_links(fileName): + links = [] + doc = Document(fileName) + rels = doc.part.rels + for rel in rels: + item = rels[rel] + if item.reltype == RT.HYPERLINK and ".aspx" not in item._target: + links.append(item._target) + return links + +def extract_value(kv_string): + return kv_string.split(":")[1].strip() + +def mkdir_if_absent(path): + try: + if not os.path.exists(path): + os.mkdir(path) + except OSError: + print("Failed to create the appropriate directory structures for %s" % file_name) + +def parse_document(file_name: str): + result = {} + pure_name = file_name.split(".")[0] + + dir_path = dist + "/" + pure_name + mkdir_if_absent(dir_path) + + raw = str(docx2txt.process(source + "/" + file_name, dir_path)) + + sanitize = lambda line: re.sub("[\n\t]+", "", line).strip().replace(u"\u00A0", " ").replace(u"\u2013", "-").replace(u"\u201c", '''"''').replace(u"\u201d", '''"''') + remove_empty = lambda line: len(line) > 1 + + lines = list(map(sanitize, raw.split("\n"))) + lines = list(filter(remove_empty, lines)) + + result["file_name"] = file_name + result["title"] = lines[2] + result["short_description"] = lines[3].replace("Short Description: ", "") + + cur = 5 + notes = "" + while lines[cur] != "Device Details": + notes += lines[cur] + " " + cur += 1 + result["buxton_notes"] = notes.strip() + + cur += 1 + clean = list(map(lambda data: data.strip().split(":"), lines[cur].split("|"))) + result["company"] = clean[0][1].strip() + result["year"] = clean[1][1].strip() + result["original_price"] = clean[2][1].strip() + + cur += 1 + result["degrees_of_freedom"] = extract_value(lines[cur]) + cur += 1 + result["dimensions"] = extract_value(lines[cur]) + + cur += 2 + result["primary_key"] = extract_value(lines[cur]) + cur += 1 + result["secondary_key"] = extract_value(lines[cur]) + + result["hyperlinks"] = extract_links(source + "/" + file_name) + + cur += 2 + link_descriptions = [] + while lines[cur] != "Image": + link_descriptions.append(lines[cur]) + cur += 1 + result["link_descriptions"] = link_descriptions + + images = [] + captions = [] + cur += 3 + while cur + 1 < len(lines) and lines[cur] != "NOTES:": + images.append(lines[cur]) + captions.append(lines[cur + 1]) + cur += 2 + result["images"] = images + result["captions"] = captions + + notes = [] + if (cur < len(lines) and lines[cur] == "NOTES:"): + cur += 1 + while cur < len(lines): + notes.append(lines[cur]) + cur += 1 + result["notes"] = notes + + return result + +def upload(document): + wrapper = {} + wrapper["_id"] = str(uuid.uuid4()) + wrapper["fields"] = document + wrapper["__type"] = "Doc" + collection_handle.insert_one(wrapper) + +if os.path.exists(dist): + shutil.rmtree(dist) +while (os.path.exists(dist)): + pass +os.mkdir(dist) + +for file_name in os.listdir(source): + if file_name.endswith('.docx'): + upload(parse_document(file_name)) + +lines = ['*', '!.gitignore'] +with open(dist + "/.gitignore", 'w') as f: + f.write('\n'.join(lines)) + + + diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index ad7c706b6..3b33657eb 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -257,7 +257,7 @@ export namespace DocServer { */ let _createField = (field: RefField) => { _cache[field[Id]] = field; - const initialState = SerializationHelper.serialize(field); + const initialState = SerializationHelper.Serialize(field); Utils.emit(_socket, MessageStore.CreateField, initialState); }; diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index ea8af3834..7ded85e43 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -7,7 +7,7 @@ export namespace SerializationHelper { return serializing > 0; } - export function serialize(obj: Field): any { + export function Serialize(obj: Field): any { if (obj === undefined || obj === null) { return undefined; } @@ -124,7 +124,7 @@ export namespace Deserializable { export function autoObject(): PropSchema { return custom( - (s) => SerializationHelper.serialize(s), + (s) => SerializationHelper.Serialize(s), (s) => SerializationHelper.Deserialize(s) ); } \ No newline at end of file diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 7709d6c24..8caceb063 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -43,7 +43,7 @@ export const setter = action(function (target: any, prop: string | symbol | numb } else { target.__fields[prop] = value; } - target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.serialize(value) : (value === undefined ? null : value) } }); + target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) } }); UndoManager.AddEvent({ redo: () => receiver[prop] = value, undo: () => receiver[prop] = curValue @@ -103,7 +103,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any let current = ObjectField.MakeCopy(value); return (diff?: any) => { if (true || !diff) { - diff = { '$set': { ["fields." + prop]: SerializationHelper.serialize(value) } }; + diff = { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; const oldValue = current; const newValue = ObjectField.MakeCopy(value); current = newValue; -- cgit v1.2.3-70-g09d2 From 90e8adc8578aa29a153cd41256b2f546a429a5bb Mon Sep 17 00:00:00 2001 From: ab Date: Wed, 19 Jun 2019 12:03:13 -0400 Subject: ui changes --- src/client/util/TooltipTextMenu.scss | 14 +++--- src/client/util/TooltipTextMenu.tsx | 72 ++++++++++++++++------------- src/client/views/nodes/FormattedTextBox.tsx | 4 +- 3 files changed, 49 insertions(+), 41 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index c10a61558..1867a697a 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -233,19 +233,19 @@ } .tooltipMenu { - position: absolute; - z-index: 50; + // position: absolute; + // z-index: 50; background: #121721; border: 1px solid silver; border-radius: 15px; - padding: 2px 10px; + //padding: 2px 10px; //margin-bottom: 100px; //-webkit-transform: translateX(-50%); //transform: translateX(-50%); - transform: translateY(-50%); + //transform: translateY(-50%); pointer-events: all; - height: 100; - width:250; + // height: 100; + // width:250; .ProseMirror-example-setup-style hr { padding: 2px 10px; border: none; @@ -307,7 +307,7 @@ padding-right: 0px; } .summarize{ - margin-left: 15px; + //margin-left: 15px; color: white; height: 20px; // background-color: white; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index a19b6c1d8..06a916832 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -66,7 +66,7 @@ export class TooltipTextMenu { this.tooltip.className = "tooltipMenu"; //add the div which is the tooltip - view.dom.parentNode!.parentNode!.appendChild(this.tooltip); + //view.dom.parentNode!.parentNode!.appendChild(this.tooltip); //add additional icons library.add(faListUl); @@ -131,11 +131,17 @@ export class TooltipTextMenu { this.link = document.createElement("a"); this.link.target = "_blank"; this.link.style.color = "white"; - this.tooltip.appendChild(this.link); + //this.tooltip.appendChild(this.link); this.tooltip.appendChild(this.createLink().render(this.view).dom); + this.tooltip.appendChild(this.createStar().render(this.view).dom); + this.update(view, undefined); + + if (view.dom.parentNode !== null) { + view.dom.parentNode.insertBefore(this.tooltip, view.dom); + } } //label of dropdown will change to given label @@ -239,23 +245,10 @@ export class TooltipTextMenu { hideSource: false }); }; - this.linkEditor.appendChild(this.linkDrag); - this.linkEditor.appendChild(this.linkText); - this.linkEditor.appendChild(linkBtn); - this.tooltip.appendChild(this.linkEditor); - - // SUMMARIZE BUTTON - - let starButton = document.createElement("span"); - starButton.className = "summarize"; - starButton.textContent = "★"; - starButton.title = 'Summarize'; - starButton.onclick = () => { - let state = this.view.state; - this.insertStar(state, this.view.dispatch); - }; - - this.tooltip.appendChild(starButton); + // this.linkEditor.appendChild(this.linkDrag); + // this.linkEditor.appendChild(this.linkText); + // this.linkEditor.appendChild(linkBtn); + //this.tooltip.appendChild(this.linkEditor); } let node = this.view.state.selection.$from.nodeAfter; @@ -370,6 +363,21 @@ export class TooltipTextMenu { }); } + createStar() { + return new MenuItem({ + title: "Summarize", + label: "Summarize", + icon: icons.join, + css: "color:white;", + class: "summarize", + execEvent: "", + run: (state, dispatch, view) => { + this.insertStar(state, dispatch); + } + + }); + } + createLink() { let markType = schema.marks.link; return new MenuItem({ @@ -499,34 +507,34 @@ export class TooltipTextMenu { // Hide the tooltip if the selection is empty if (state.selection.empty) { - this.tooltip.style.display = "none"; - return; + //this.tooltip.style.display = "none"; + //return; } let linksInSelection = this.activeMarksOnSelection([schema.marks.link]); - if (linksInSelection.length > 0) { - let attributes = this.getMarksInSelection(this.view.state, [schema.marks.link])[0].attrs; - this.link.href = attributes.href; - this.link.textContent = attributes.title; - this.link.style.visibility = "visible"; - } else this.link.style.visibility = "hidden"; + // if (linksInSelection.length > 0) { + // let attributes = this.getMarksInSelection(this.view.state, [schema.marks.link])[0].attrs; + // this.link.href = attributes.href; + // this.link.textContent = attributes.title; + // this.link.style.visibility = "visible"; + // } else this.link.style.visibility = "hidden"; // Otherwise, reposition it and update its content - this.tooltip.style.display = ""; + //this.tooltip.style.display = ""; let { from, to } = state.selection; let start = view.coordsAtPos(from), end = view.coordsAtPos(to); // The box in which the tooltip is positioned, to use as base - let box = this.tooltip.offsetParent!.getBoundingClientRect(); + //let box = this.tooltip.offsetParent!.getBoundingClientRect(); // Find a center-ish x position from the selection endpoints (when // crossing lines, end may be more to the left) let left = Math.max((start.left + end.left) / 2, start.left + 3); - this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px"; + //this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px"; let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale; let mid = Math.min(start.left, end.left) + width; //this.tooltip.style.width = 225 + "px"; - this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px"; - this.tooltip.style.top = "-100px"; + // this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px"; + // this.tooltip.style.top = "-100px"; //this.tooltip.style.height = "100px"; // let transform = this.editorProps.ScreenToLocalTransform(); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index df12f261b..223e6df0a 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -254,7 +254,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { e.stopPropagation(); if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip) { - this._toolTipTextMenu.tooltip.style.opacity = "0"; + //this._toolTipTextMenu.tooltip.style.opacity = "0"; } } let ctrlKey = e.ctrlKey; @@ -289,7 +289,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } onPointerUp = (e: React.PointerEvent): void => { if (this._toolTipTextMenu && this._toolTipTextMenu.tooltip) { - this._toolTipTextMenu.tooltip.style.opacity = "1"; + //this._toolTipTextMenu.tooltip.style.opacity = "1"; } if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { e.stopPropagation(); -- 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/client/util') 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 47e125b9c7a8a61d820c013a5dd5f9cf654205af Mon Sep 17 00:00:00 2001 From: ab Date: Wed, 19 Jun 2019 14:01:05 -0400 Subject: moved tooltip inside textbox --- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 06a916832..06862a79a 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -278,7 +278,7 @@ export class TooltipTextMenu { console.log("creating star..."); let newNode = schema.nodes.star.create({ visibility: false, text: state.selection.content(), oldtextslice: state.selection.content().toJSON(), oldtextlen: state.selection.to - state.selection.from }); if (dispatch) { - console.log(newNode.attrs.text.toString()); + //console.log(newNode.attrs.text.toString()); dispatch(state.tr.replaceSelectionWith(newNode)); } return true; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 05dc6f534..b8bcfcc89 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -50,7 +50,7 @@ export class MarqueeView extends React.Component document.removeEventListener("pointerup", this.onPointerUp, true); document.removeEventListener("pointermove", this.onPointerMove, true); } - if (all) { + if (rem_keydown) { document.removeEventListener("keydown", this.marqueeCommand, true); } this._visible = false; -- 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/client/util') 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 6259738cf77c2749f556e6d57addae8dac1b32d2 Mon Sep 17 00:00:00 2001 From: ab Date: Thu, 20 Jun 2019 11:17:22 -0400 Subject: moving div --- src/client/util/TooltipTextMenu.scss | 7 ++++--- src/client/util/TooltipTextMenu.tsx | 6 +++--- src/client/views/MainOverlayTextBox.scss | 2 +- src/client/views/MainOverlayTextBox.tsx | 7 +++++-- src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.scss | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 2 ++ 7 files changed, 17 insertions(+), 11 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index 1867a697a..456953fb6 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -233,16 +233,17 @@ } .tooltipMenu { - // position: absolute; + position: absolute; // z-index: 50; background: #121721; border: 1px solid silver; border-radius: 15px; + //height: 60px; //padding: 2px 10px; - //margin-bottom: 100px; + //margin-top: 100px; //-webkit-transform: translateX(-50%); //transform: translateX(-50%); - //transform: translateY(-50%); + transform: translateY(-85px); pointer-events: all; // height: 100; // width:250; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 06862a79a..641514af9 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -139,9 +139,9 @@ export class TooltipTextMenu { this.update(view, undefined); - if (view.dom.parentNode !== null) { - view.dom.parentNode.insertBefore(this.tooltip, view.dom); - } + view.dom.parentNode!.parentNode!.insertBefore(this.tooltip, view.dom.parentNode); + + //console.log("hi"); } //label of dropdown will change to given label diff --git a/src/client/views/MainOverlayTextBox.scss b/src/client/views/MainOverlayTextBox.scss index f6a746e63..fd6ca3471 100644 --- a/src/client/views/MainOverlayTextBox.scss +++ b/src/client/views/MainOverlayTextBox.scss @@ -1,7 +1,7 @@ @import "globalCssVariables"; .mainOverlayTextBox-textInput { background-color: rgba(248, 6, 6, 0.001); - width: 200px; + width: 400px; height: 200px; position:absolute; overflow: visible; diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index b4ad5f4d7..f1c4b9708 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -24,6 +24,7 @@ export class MainOverlayTextBox extends React.Component private _textHideOnLeave?: boolean; private _textTargetDiv: HTMLDivElement | undefined; private _textProxyDiv: React.RefObject; + private _outerdiv: React.RefObject; private _textBottom: boolean | undefined; private _textAutoHeight: boolean | undefined; private _textBox: FormattedTextBox | undefined; @@ -32,6 +33,7 @@ export class MainOverlayTextBox extends React.Component constructor(props: MainOverlayTextBoxProps) { super(props); this._textProxyDiv = React.createRef(); + this._outerdiv = React.createRef(); MainOverlayTextBox.Instance = this; reaction(() => FormattedTextBox.InputBoxOverlay, (box?: FormattedTextBox) => { @@ -114,16 +116,17 @@ export class MainOverlayTextBox extends React.Component let s = this._textXf().Scale; let location = this._textBottom ? textRect.bottom : textRect.top; let hgt = this._textAutoHeight || this._textBottom ? "auto" : this._textTargetDiv.clientHeight; - return
+ return
+ ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} outer_div={this._outerdiv} />
+
; } else return (null); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b8bcfcc89..b1e0e62ea 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -95,7 +95,7 @@ export class MarqueeView extends React.Component } }); } else if (!e.ctrlKey) { - let newBox = Docs.TextDocument({ width: 200, height: 30, x: x, y: y, title: "-typed text-" }); + let newBox = Docs.TextDocument({ width: 200, height: 50, x: x, y: y, title: "-typed text-" }); newBox.proto!.autoHeight = true; this.props.addLiveTextDocument(newBox); } diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index e3e40860c..d3045ae2f 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -1,7 +1,7 @@ @import "../globalCssVariables"; .ProseMirror { width: 100%; - height: 100; + height: 100%; min-height: 100%; font-family: $serif; } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 223e6df0a..370b24e95 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -45,6 +45,7 @@ export interface FormattedTextBoxProps { hideOnLeave?: boolean; height?: string; color?: string; + outer_div?: React.RefObject; } const richTextSchema = createSchema({ @@ -97,6 +98,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe constructor(props: FieldViewProps) { super(props); + this.props.outer_div ? console.log("smd") : console.log("bye"); this._ref = React.createRef(); if (this.props.isOverlay) { -- cgit v1.2.3-70-g09d2 From 4360287e6cafcb59af1ae62fc31ddc161bcf2e51 Mon Sep 17 00:00:00 2001 From: Fawn 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/client/util') 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 7ef1f39703a378df3f1116d046f9b57a4427d64d Mon Sep 17 00:00:00 2001 From: ab Date: Thu, 20 Jun 2019 16:54:16 -0400 Subject: div moved, but z-index is fd --- src/client/util/TooltipTextMenu.scss | 12 +++++++----- src/client/util/TooltipTextMenu.tsx | 13 +++++++++---- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainOverlayTextBox.scss | 4 ++++ src/client/views/MainOverlayTextBox.tsx | 8 ++++---- src/client/views/nodes/FormattedTextBox.tsx | 8 ++++++-- 6 files changed, 31 insertions(+), 16 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index 456953fb6..d19ded68f 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -18,6 +18,7 @@ .ProseMirror-menuitem { margin-right: 3px; display: inline-block; + z-index: 100000; } .ProseMirror-menuseparator { @@ -67,7 +68,7 @@ } .ProseMirror-menu-dropdown-menu { - z-index: 15; + z-index: 100000; min-width: 6em; background: white; } @@ -80,6 +81,7 @@ cursor: pointer; padding: 2px 8px 2px 4px; width: auto; + z-index: 100000; } .ProseMirror-menu-dropdown-item:hover { @@ -233,8 +235,8 @@ } .tooltipMenu { - position: absolute; - // z-index: 50; + position: relative; + z-index: 2000; background: #121721; border: 1px solid silver; border-radius: 15px; @@ -245,8 +247,8 @@ //transform: translateX(-50%); transform: translateY(-85px); pointer-events: all; - // height: 100; - // width:250; + height: 30px; + width:500px; .ProseMirror-example-setup-style hr { padding: 2px 10px; border: none; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 641514af9..06c8bbc1a 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -29,6 +29,7 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie import { DocumentManager } from "./DocumentManager"; import { Id } from "../../new_fields/FieldSymbols"; import { Utils } from "../../Utils"; +import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox"; // import { wrap } from "module"; const SVG = "http://www.w3.org/2000/svg"; @@ -42,7 +43,7 @@ export class TooltipTextMenu { private fontStyles: MarkType[]; private fontSizes: MarkType[]; private listTypes: NodeType[]; - private editorProps: FieldViewProps; + private editorProps: FieldViewProps & FormattedTextBoxProps; private state: EditorState; private fontSizeToNum: Map; private fontStylesToName: Map; @@ -58,7 +59,7 @@ export class TooltipTextMenu { private fontStyleDom?: Node; private listTypeBtnDom?: Node; - constructor(view: EditorView, editorProps: FieldViewProps) { + constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) { this.view = view; this.state = view.state; this.editorProps = editorProps; @@ -139,9 +140,13 @@ export class TooltipTextMenu { this.update(view, undefined); - view.dom.parentNode!.parentNode!.insertBefore(this.tooltip, view.dom.parentNode); + //view.dom.parentNode!.parentNode!.insertBefore(this.tooltip, view.dom.parentNode); - //console.log("hi"); + // quick and dirty null check + const outer_div = this.editorProps.outer_div; + outer_div && outer_div(this.tooltip); + + console.log("hi"); } //label of dropdown will change to given label diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index d8642d675..3d802964a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -619,7 +619,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> left: bounds.x - this._resizeBorderWidth / 2, top: bounds.y - this._resizeBorderWidth / 2, pointerEvents: this.Interacting ? "none" : "all", - zIndex: SelectionManager.SelectedDocuments().length > 1 ? 1000 : 0, + zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0, }} onPointerDown={this.onBackgroundDown} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); }} >
private _textHideOnLeave?: boolean; private _textTargetDiv: HTMLDivElement | undefined; private _textProxyDiv: React.RefObject; - private _outerdiv: React.RefObject; + private _outerdiv = (dominus: HTMLElement | null) => this._dominus && dominus && dominus.appendChild(this._dominus); private _textBottom: boolean | undefined; private _textAutoHeight: boolean | undefined; private _textBox: FormattedTextBox | undefined; + private _dominus?: HTMLElement; @observable public TextDoc?: Doc; constructor(props: MainOverlayTextBoxProps) { super(props); this._textProxyDiv = React.createRef(); - this._outerdiv = React.createRef(); MainOverlayTextBox.Instance = this; reaction(() => FormattedTextBox.InputBoxOverlay, (box?: FormattedTextBox) => { @@ -116,14 +116,14 @@ export class MainOverlayTextBox extends React.Component let s = this._textXf().Scale; let location = this._textBottom ? textRect.bottom : textRect.top; let hgt = this._textAutoHeight || this._textBottom ? "auto" : this._textTargetDiv.clientHeight; - return
+ return
+ ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} outer_div={(dominus: HTMLElement) => this._dominus = dominus} />
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 370b24e95..421267b2a 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -45,7 +45,7 @@ export interface FormattedTextBoxProps { hideOnLeave?: boolean; height?: string; color?: string; - outer_div?: React.RefObject; + outer_div?: (domminus: HTMLElement) => void; } const richTextSchema = createSchema({ @@ -61,6 +61,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe return FieldView.LayoutString(FormattedTextBox, fieldStr); } private _ref: React.RefObject; + private _outerdiv?: (dominus: HTMLElement) => void; private _proseRef?: HTMLDivElement; private _editorView: Opt; private _toolTipTextMenu: TooltipTextMenu | undefined = undefined; @@ -98,7 +99,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe constructor(props: FieldViewProps) { super(props); - this.props.outer_div ? console.log("smd") : console.log("bye"); + if (this.props.outer_div) { + this._outerdiv = this.props.outer_div; + console.log("yay"); + } this._ref = React.createRef(); if (this.props.isOverlay) { -- cgit v1.2.3-70-g09d2 From 0cab79a50719719e1dade40520a6967f7aa8f951 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 18:35:45 -0400 Subject: Added debug and release modes to server and client --- .gitignore | 2 ++ package.json | 2 ++ src/client/util/ClientUtils.ts.temp | 3 ++ .../views/collections/CollectionStackingView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 16 +++++----- src/server/index.ts | 34 ++++++++++++++++++---- 6 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 src/client/util/ClientUtils.ts.temp (limited to 'src/client/util') diff --git a/.gitignore b/.gitignore index a499c39a3..5d3100dc6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ node_modules package-lock.json dist/ .DS_Store +.env +ClientUtils.ts \ No newline at end of file diff --git a/package.json b/package.json index 7fd6c4ba9..91d3a3853 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@types/cookie-parser": "^1.4.1", "@types/cookie-session": "^2.0.36", "@types/d3-format": "^1.3.1", + "@types/dotenv": "^6.1.1", "@types/express": "^4.16.1", "@types/express-flash": "0.0.0", "@types/express-session": "^1.15.12", @@ -110,6 +111,7 @@ "cookie-session": "^2.0.0-beta.3", "crypto-browserify": "^3.11.0", "d3-format": "^1.3.2", + "dotenv": "^8.0.0", "express": "^4.16.4", "express-flash": "0.0.2", "express-session": "^1.15.6", diff --git a/src/client/util/ClientUtils.ts.temp b/src/client/util/ClientUtils.ts.temp new file mode 100644 index 000000000..f9fad5ed9 --- /dev/null +++ b/src/client/util/ClientUtils.ts.temp @@ -0,0 +1,3 @@ +export namespace ClientUtils { + export const RELEASE = "mode"; +} \ No newline at end of file diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 368e94a8c..521e8d616 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -101,7 +101,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let renderScale = this.columnWidth / NumCast(d.nativeWidth, this.columnWidth); let aspect = NumCast(d.nativeWidth) / NumCast(d.nativeHeight); let width = () => this.columnWidth; - let height = () => aspect ? width() / aspect : d[HeightSym]() + let height = () => aspect ? width() / aspect : d[HeightSym](); let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap)); let childFocus = (doc: Doc) => { doc.libraryBrush = true; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c51c16883..c7ed2eb38 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -72,7 +72,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()) { @@ -114,11 +114,11 @@ 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)); - let doc = Cast(this.props.document["data"], Doc); + let docList = Cast(this.props.document.data, listSpec(Doc)); + let doc = Cast(this.props.document.data, Doc); let isDoc = doc instanceof Doc || docList; return
this._collapsed = !this._collapsed)}> {} @@ -298,7 +298,7 @@ class TreeView extends React.Component { addDocTab={this.props.addDocTab} setPreviewScript={emptyFunction}> -
+
; } } return
@@ -337,14 +337,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 request.get(url).pipe(fs.createWriteStream(dest)); +const release = process.env.RELEASE === "true"; +if (process.env.RELEASE === "true") { + console.log("Running server in release mode"); +} else { + console.log("Running server in debug mode"); +} +console.log(process.env.PWD); +let clientUtils = fs.readFileSync("./src/client/util/ClientUtils.ts.temp", "utf8"); +clientUtils = `//AUTO-GENERATED FILE: DO NOT EDIT\n${clientUtils.replace('"mode"', String(release))}`; +fs.writeFileSync("./src/client/util/ClientUtils.ts", clientUtils, "utf8"); + const mongoUrl = 'mongodb://localhost:27017/Dash'; mongoose.connect(mongoUrl); mongoose.connection.on('connected', () => console.log("connected")); @@ -406,11 +418,21 @@ app.post(RouteStore.reset, postReset); app.use(RouteStore.corsProxy, (req, res) => req.pipe(request(req.url.substring(1))).pipe(res)); -app.get(RouteStore.delete, (req, res) => - deleteFields().then(() => res.redirect(RouteStore.home))); +app.get(RouteStore.delete, (req, res) => { + if (release) { + res.send("no"); + return; + } + deleteFields().then(() => res.redirect(RouteStore.home)); +}); -app.get(RouteStore.deleteAll, (req, res) => - deleteAll().then(() => res.redirect(RouteStore.home))); +app.get(RouteStore.deleteAll, (req, res) => { + if (release) { + res.send("no"); + return; + } + deleteAll().then(() => res.redirect(RouteStore.home)); +}); app.use(wdm(compiler, { publicPath: config.output.publicPath })); @@ -435,7 +457,9 @@ server.on("connection", function (socket: Socket) { Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args)); Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField); Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields); - Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields); + if (!release) { + Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields); + } Utils.AddServerHandler(socket, MessageStore.CreateField, CreateField); Utils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff)); -- cgit v1.2.3-70-g09d2 From 542501fc3247dafa5879482f62a28147ec4b3793 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 21 Jun 2019 13:13:18 -0400 Subject: lots more template stuff. --- src/client/util/DocumentManager.ts | 6 +- src/client/util/DragManager.ts | 18 ++-- src/client/views/DocumentDecorations.tsx | 35 ++++---- src/client/views/MainOverlayTextBox.tsx | 8 +- .../views/collections/CollectionDockingView.tsx | 100 +++++++++++---------- .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 33 +++++-- src/client/views/collections/CollectionView.tsx | 4 +- .../views/collections/ParentDocumentSelector.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 27 +++--- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 43 +++++---- src/client/views/pdf/PDFViewer.tsx | 1 - src/new_fields/Doc.ts | 8 ++ 15 files changed, 174 insertions(+), 119 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 862395d74..8421c902e 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -138,7 +138,7 @@ export class DocumentManager { docContext.panTransformType = "Ease"; targetContextView.props.focus(docDelegate); } else { - (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext); + (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext, docContext); setTimeout(() => { this.jumpToDocument(docDelegate, forceDockFunc, dockFunc, linkPage); }, 10); @@ -147,7 +147,7 @@ export class DocumentManager { const actualDoc = Doc.MakeAlias(docDelegate); actualDoc.libraryBrush = true; if (linkPage !== undefined) actualDoc.curPage = linkPage; - (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc); + (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc, actualDoc); } } else { let contextView: DocumentView | null; @@ -156,7 +156,7 @@ export class DocumentManager { contextDoc.panTransformType = "Ease"; contextView.props.focus(docDelegate); } else { - (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc); + (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc, contextDoc); setTimeout(() => { this.jumpToDocument(docDelegate, forceDockFunc, dockFunc, linkPage); }, 10); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c3c92daa5..cc83b7346 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -15,7 +15,8 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); - var dragData = new DragManager.DocumentDragData([await docFunc()]); + let doc = await docFunc(); + var dragData = new DragManager.DocumentDragData([doc], [doc]); dragData.dropAction = dropAction; dragData.moveDocument = moveFunc; dragData.options = options; @@ -31,7 +32,7 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () if (e.button === 0) { e.stopPropagation(); if (e.shiftKey && CollectionDockingView.Instance) { - CollectionDockingView.Instance.StartOtherDrag([await docFunc()], e); + CollectionDockingView.Instance.StartOtherDrag(e, [await docFunc()]); } else { document.addEventListener("pointermove", onRowMove); document.addEventListener("pointerup", onRowUp); @@ -59,7 +60,8 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n let doc = await Cast(draggedDoc.annotationOn, Doc); if (doc) moddrag.push(doc); } - let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); + let linkDocs = moddrag.length ? moddrag : draggedDocs; + let dragData = new DragManager.DocumentDragData(linkDocs, linkDocs); // bcz: dataDocs? DragManager.StartDocumentDrag([dragEle], dragData, x, y, { handlers: { dragComplete: action(emptyFunction), @@ -141,13 +143,15 @@ export namespace DragManager { export type MoveFunction = (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; export class DocumentDragData { - constructor(dragDoc: Doc[]) { + constructor(dragDoc: Doc[], dragDataDocs: Doc[]) { this.draggedDocuments = dragDoc; + this.draggedDataDocs = dragDataDocs; this.droppedDocuments = dragDoc; this.xOffset = 0; this.yOffset = 0; } draggedDocuments: Doc[]; + draggedDataDocs: Doc[]; droppedDocuments: Doc[]; xOffset: number; yOffset: number; @@ -238,6 +242,8 @@ export namespace DragManager { const docs: Doc[] = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : []; + const datadocs: Doc[] = + dragData instanceof DocumentDragData ? dragData.draggedDataDocs : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : []; let dragElements = eles.map(ele => { const w = ele.offsetWidth, h = ele.offsetHeight; @@ -314,12 +320,12 @@ export namespace DragManager { } if (e.shiftKey && CollectionDockingView.Instance) { AbortDrag(); - CollectionDockingView.Instance.StartOtherDrag(docs, { + CollectionDockingView.Instance.StartOtherDrag({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 - }); + }, docs, datadocs); } //TODO: Why can't we use e.movementX and e.movementY? let moveX = e.pageX - lastX; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3a366b4d2..8f02f04d7 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -166,7 +166,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let dragDocView = SelectionManager.SelectedDocuments()[0]; const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0); const [xoff, yoff] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top); - let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); + let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document), SelectionManager.SelectedDocuments().map(dv => dv.props.DataDoc)); dragData.xOffset = xoff; dragData.yOffset = yoff; dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument; @@ -485,9 +485,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let doc = PositionDocument(element.props.Document); let nwidth = doc.nativeWidth || 0; let nheight = doc.nativeHeight || 0; - let zoomBasis = NumCast(doc.zoomBasis, 1); - let width = (doc.width || 0) / zoomBasis; - let height = (doc.height || (nheight / nwidth * width)) / zoomBasis; + let width = (doc.width || 0); + let height = (doc.height || (nheight / nwidth * width)); let scale = element.props.ScreenToLocalTransform().Scale; let actualdW = Math.max(width + (dW * scale), 20); let actualdH = Math.max(height + (dH * scale), 20); @@ -502,25 +501,27 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } if (nwidth > 0 && nheight > 0) { if (Math.abs(dW) > Math.abs(dH)) { - if (!fixedAspect) proto.nativeWidth = zoomBasis * actualdW / (doc.width || 1) * NumCast(proto.nativeWidth); - doc.width = zoomBasis * actualdW; - // doc.zoomBasis = zoomBasis * width / actualdW; + if (!fixedAspect) { + Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true); + } + doc.width = actualdW; if (fixedAspect) doc.height = nheight / nwidth * doc.width; - else doc.height = zoomBasis * actualdH; - proto.nativeHeight = (doc.height || 0) / doc.width * NumCast(proto.nativeWidth); + else doc.height = actualdH; + Doc.SetInPlace(element.props.Document, "nativeHeight", (doc.height || 0) / doc.width * (doc.nativeWidth || 0), true); } else { - if (!fixedAspect) proto.nativeHeight = zoomBasis * actualdH / (doc.height || 1) * NumCast(proto.nativeHeight); - doc.height = zoomBasis * actualdH; - //doc.zoomBasis = zoomBasis * height / actualdH; + if (!fixedAspect) { + Doc.SetInPlace(element.props.Document, "nativeHeight", actualdH / (doc.height || 1) * (doc.nativeHeight || 0), true); + } + doc.height = actualdH; if (fixedAspect) doc.width = nwidth / nheight * doc.height; - else doc.width = zoomBasis * actualdW; - proto.nativeWidth = (doc.width || 0) / doc.height * NumCast(proto.nativeHeight); + else doc.width = actualdW; + Doc.SetInPlace(element.props.Document, "nativeWidth", (doc.width || 0) / doc.height * (doc.nativeHeight || 0), true); } } else { - dW && (doc.width = zoomBasis * actualdW); - dH && (doc.height = zoomBasis * actualdH); - proto.autoHeight = undefined; + dW && (doc.width = actualdW); + dH && (doc.height = actualdH); + Doc.SetInPlace(element.props.Document, "autoHeight", undefined, true); } } }); diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 64271650b..73d59ce31 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -82,10 +82,10 @@ export class MainOverlayTextBox extends React.Component } @action textBoxMove = (e: PointerEvent) => { - if (e.movementX > 1 || e.movementY > 1) { + if ((e.movementX > 1 || e.movementY > 1) && FormattedTextBox.InputBoxOverlay) { document.removeEventListener("pointermove", this.textBoxMove); document.removeEventListener('pointerup', this.textBoxUp); - let dragData = new DragManager.DocumentDragData(FormattedTextBox.InputBoxOverlay ? [FormattedTextBox.InputBoxOverlay.props.Document] : []); + let dragData = new DragManager.DocumentDragData([FormattedTextBox.InputBoxOverlay.props.Document], [FormattedTextBox.InputBoxOverlay.props.DataDoc]); const [left, top] = this._textXf().inverse().transformPoint(0, 0); dragData.xOffset = e.clientX - left; dragData.yOffset = e.clientY - top; @@ -102,9 +102,9 @@ export class MainOverlayTextBox extends React.Component document.removeEventListener('pointerup', this.textBoxUp); } - addDocTab = (doc: Doc, location: string) => { + addDocTab = (doc: Doc, dataDoc: Doc, location: string) => { if (true) { // location === "onRight") { need to figure out stack to add "inTab" - CollectionDockingView.Instance.AddRightSplit(doc); + CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); } } render() { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 79a0d18df..89fadab57 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -28,7 +28,7 @@ import { MainView } from '../MainView'; @observer export class CollectionDockingView extends React.Component { public static Instance: CollectionDockingView; - public static makeDocumentConfig(document: Doc, width?: number) { + public static makeDocumentConfig(document: Doc, dataDoc: Doc, width?: number) { return { type: 'react-component', component: 'DocumentFrameRenderer', @@ -36,6 +36,7 @@ export class CollectionDockingView extends React.Component - this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener. + dragDocs.map((dragDoc, i) => + this.AddRightSplit(dragDoc, dragDataDocs ? dragDataDocs[i] : dragDoc, true).contentItems[0].tab._dragListener. onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 })); } @action - public OpenFullScreen(document: Doc) { + public OpenFullScreen(document: Doc, dataDoc: Doc) { let newItemStackConfig = { type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document)] + content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)] }; var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout); this._goldenLayout.root.contentItems[0].addChild(docconfig); @@ -123,14 +124,14 @@ export class CollectionDockingView extends React.Component { + public AddRightSplit = (document: Doc, dataDoc: Doc, minimize: boolean = false) => { let docs = Cast(this.props.Document.data, listSpec(Doc)); if (docs) { docs.push(document); } let newItemStackConfig = { type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document)] + content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)] }; var newContentItem = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout); @@ -160,12 +161,12 @@ export class CollectionDockingView extends React.Component { + public AddTab = (stack: any, document: Doc, dataDocument: Doc) => { let docs = Cast(this.props.Document.data, listSpec(Doc)); if (docs) { docs.push(document); } - let docContentConfig = CollectionDockingView.makeDocumentConfig(document); + let docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument); var newContentItem = stack.layoutManager.createContentItem(docContentConfig, this._goldenLayout); stack.addChild(newContentItem.contentItems[0], undefined); this.layoutChanged(); @@ -281,14 +282,16 @@ export class CollectionDockingView extends React.Component) => { + DocServer.GetRefField(docid).then(action(async (f: Opt) => { if (f instanceof Doc) { - DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y, + let dataDoc = (datadocid !== docid) ? await DocServer.GetRefField(datadocid) : f; + DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f], [dataDoc instanceof Doc ? dataDoc : f]), x, y, { handlers: { dragComplete: emptyFunction, @@ -338,33 +341,34 @@ export class CollectionDockingView extends React.Component { - if (doc instanceof Doc) { - let counter: any = this.htmlToElement(`0
`); - tab.element.append(counter); - let upDiv = document.createElement("span"); - const stack = tab.contentItem.parent; - // shifts the focus to this tab when another tab is dragged over it - tab.element[0].onmouseenter = (e: any) => { - if (!this._isPointerDown) return; - var activeContentItem = tab.header.parent.getActiveContentItem(); - if (tab.contentItem !== activeContentItem) { - tab.header.parent.setActiveContentItem(tab.contentItem); - } - tab.setActive(true); - }; - ReactDOM.render( CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); - tab.reactComponents = [upDiv]; - tab.element.append(upDiv); - counter.DashDocId = tab.contentItem.config.props.documentId; - tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title], - () => { - counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length; - tab.titleElement[0].textContent = doc.title; - }, { fireImmediately: true }); - tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; - } - }); + let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc; + let dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc; + if (doc instanceof Doc && dataDoc instanceof Doc) { + let counter: any = this.htmlToElement(`0
`); + tab.element.append(counter); + let upDiv = document.createElement("span"); + const stack = tab.contentItem.parent; + // shifts the focus to this tab when another tab is dragged over it + tab.element[0].onmouseenter = (e: any) => { + if (!this._isPointerDown) return; + var activeContentItem = tab.header.parent.getActiveContentItem(); + if (tab.contentItem !== activeContentItem) { + tab.header.parent.setActiveContentItem(tab.contentItem); + } + tab.setActive(true); + }; + ReactDOM.render( CollectionDockingView.Instance.AddTab(stack, doc, dataDoc)} />, upDiv); + tab.reactComponents = [upDiv]; + tab.element.append(upDiv); + counter.DashDocId = tab.contentItem.config.props.documentId; + counter.DashDataDocId = tab.contentItem.config.props.dataDocumentId; + tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title], + () => { + counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length; + tab.titleElement[0].textContent = doc.title; + }, { fireImmediately: true }); + tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; + } } tab.titleElement[0].Tab = tab; tab.closeElement.off('click') //unbind the current click handler @@ -427,6 +431,7 @@ export class CollectionDockingView extends React.Component { @observable private _panelWidth = 0; @observable private _panelHeight = 0; @observable private _document: Opt; + @observable private _dataDoc: Opt; get _stack(): any { let parent = (this.props as any).glContainer.parent.parent; if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) { @@ -444,7 +450,11 @@ export class DockedFrameRenderer extends React.Component { } constructor(props: any) { super(props); - DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); + DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => { + this._document = f as Doc; + if (!this.props.dataDocumentId || this.props.documentId === this.props.dataDocumentId) this._dataDoc = this._document; + })); + if (this.props.dataDocumentId && this.props.documentId !== this.props.dataDocumentId) DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt) => this._dataDoc = f as Doc)); } nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth); @@ -481,17 +491,17 @@ export class DockedFrameRenderer extends React.Component { } get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; } - addDocTab = (doc: Doc, location: string) => { + addDocTab = (doc: Doc, dataDoc: Doc, location: string) => { if (doc.dockingConfig) { MainView.Instance.openWorkspace(doc); } else if (location === "onRight") { - CollectionDockingView.Instance.AddRightSplit(doc); + CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); } else { - CollectionDockingView.Instance.AddTab(this._stack, doc); + CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc); } } get content() { - if (!this._document) { + if (!this._document || !this._dataDoc) { return (null); } return ( @@ -499,7 +509,7 @@ export class DockedFrameRenderer extends React.Component { style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier}, ${this.scaleToFitMultiplier})` }}> boolean; active: () => boolean; whenActiveChanged: (isActive: boolean) => void; - addDocTab: (document: Doc, where: string) => void; + addDocTab: (document: Doc, dataDoc: Doc, where: string) => void; setPreviewScript: (script: string) => void; previewScript?: string; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 18d6751c6..83a7c9e3a 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -26,6 +26,8 @@ import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); import { FormattedTextBox } from '../nodes/FormattedTextBox'; +import { ImageField } from '../../../new_fields/URLField'; +import { ImageBox } from '../nodes/ImageBox'; export interface TreeViewProps { @@ -35,7 +37,7 @@ export interface TreeViewProps { deleteDoc: (doc: Doc) => boolean; moveDocument: DragManager.MoveFunction; dropAction: "alias" | "copy" | undefined; - addDocTab: (doc: Doc, where: string) => void; + addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void; panelWidth: () => number; panelHeight: () => number; addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; @@ -73,7 +75,7 @@ class TreeView extends React.Component { } @undoBatch delete = () => this.props.deleteDoc(this.props.document); - @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "onRight"); + @undoBatch openRight = async () => this.props.addDocTab(this.props.document, this.props.dataDoc, "onRight"); onPointerDown = (e: React.PointerEvent) => e.stopPropagation(); onPointerEnter = (e: React.PointerEvent): void => { @@ -148,6 +150,25 @@ class TreeView extends React.Component { SetValue={(value: string) => { let res = (Doc.GetProto(this.props.document)[key] = value) ? true : true; + if (value.startsWith(">>")) { + let metaKey = value.slice(2, value.length); + let collection = this.props.containingCollection; + Doc.GetProto(collection)[metaKey] = new ImageField("http://www.cs.brown.edu/~bcz/face.gif"); + let template = Doc.MakeAlias(collection); + template.title = metaKey; + template.embed = true; + template.layout = ImageBox.LayoutString(metaKey); + template.x = 0; + template.y = 0; + template.nativeWidth = 300; + template.nativeHeight = 300; + template.width = 300; + template.height = 300; + template.isTemplate = true; + template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); + Doc.AddDocToList(collection, "data", template); + this.delete(); + } if (value.startsWith(">")) { let metaKey = value.slice(1, value.length); let collection = this.props.containingCollection; @@ -236,10 +257,10 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.props.document)) }); - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.KVPDocument(this.props.document, { width: 300, height: 300 }), "onRight"), icon: "layer-group" }); + ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" }); if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { - ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab"), icon: "folder" }); - ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight"), icon: "caret-square-right" }); + ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.props.dataDoc, "inTab"), icon: "folder" }); + ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.props.dataDoc, "onRight"), icon: "caret-square-right" }); if (DocumentManager.Instance.getDocumentViews(this.props.document).length) { ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) }); } @@ -339,7 +360,7 @@ class TreeView extends React.Component { remove: ((doc: Doc) => boolean), move: DragManager.MoveFunction, dropAction: dropActionType, - addDocTab: (doc: Doc, where: string) => void, + addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void, screenToLocalXf: () => Transform, outerXf: () => { translateX: number, translateY: number }, active: () => boolean, diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 89e5ad74c..10e6fb885 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -18,6 +18,7 @@ import { Doc } from '../../../new_fields/Doc'; import { FormattedTextBox } from '../nodes/FormattedTextBox'; import { Docs } from '../../documents/Documents'; import { List } from '../../../new_fields/List'; +import { ImageField } from '../../../new_fields/URLField'; export const COLLECTION_BORDER_WIDTH = 2; library.add(faTh); @@ -63,8 +64,9 @@ export class CollectionView extends React.Component { let otherdoc = Docs.TextDocument({ width: 100, height: 50, title: "applied template" }); Doc.GetProto(otherdoc).description = "THIS DESCRIPTION IS REALLY IMPORTANT!"; Doc.GetProto(otherdoc).summary = "THIS SUMMARY IS MEANINGFUL!"; + Doc.GetProto(otherdoc).photo = new ImageField("http://www.cs.brown.edu/~bcz/snowbeast.JPG"); Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document); - this.props.addDocTab && this.props.addDocTab(otherdoc, "onRight"); + this.props.addDocTab && this.props.addDocTab(otherdoc, otherdoc, "onRight"); }), icon: "project-diagram" }); } diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index f11af04a3..fa7058d5a 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -9,7 +9,7 @@ import { CollectionDockingView } from "./CollectionDockingView"; import { NumCast } from "../../../new_fields/Types"; import { CollectionViewType } from "./CollectionBaseView"; -type SelectorProps = { Document: Doc, addDocTab(doc: Doc, location: string): void }; +type SelectorProps = { Document: Doc, addDocTab(doc: Doc, dataDoc: Doc, location: string): void }; @observer export class SelectorContextMenu extends React.Component { @observable private _docs: { col: Doc, target: Doc }[] = []; @@ -43,7 +43,7 @@ export class SelectorContextMenu extends React.Component { col.panX = newPanX; col.panY = newPanY; } - this.props.addDocTab(col, "inTab"); + this.props.addDocTab(col, col, "inTab"); // bcz: dataDoc? }; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index da50d0b53..08f58dcd6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -81,7 +81,7 @@ export interface DocumentViewProps { parentActive: () => boolean; whenActiveChanged: (isActive: boolean) => void; bringToFront: (doc: Doc) => void; - addDocTab: (doc: Doc, where: string) => void; + addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void; animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void; } @@ -212,8 +212,9 @@ export class DocumentView extends DocComponent(Docu startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) { if (this._mainCont.current) { let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; + let alldataConnected = [this.props.DataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); - let dragData = new DragManager.DocumentDragData(allConnected); + let dragData = new DragManager.DocumentDragData(allConnected, alldataConnected); const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); dragData.dropAction = dropAction; dragData.xOffset = xoff; @@ -268,7 +269,7 @@ export class DocumentView extends DocComponent(Docu let altKey = e.altKey; let ctrlKey = e.ctrlKey; if (this._doubleTap && !this.props.isTopMost) { - this.props.addDocTab(this.props.Document, "inTab"); + this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"); SelectionManager.DeselectAll(); this.props.Document.libraryBrush = false; } @@ -311,7 +312,7 @@ export class DocumentView extends DocComponent(Docu if (dataDocs) { expandedDocs.forEach(maxDoc => (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) && - this.props.addDocTab(getDispDoc(maxDoc), maxLocation))); + this.props.addDocTab(getDispDoc(maxDoc), getDispDoc(maxDoc), maxLocation))); } } else { let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2); @@ -334,7 +335,7 @@ export class DocumentView extends DocComponent(Docu if (!linkedFwdDocs.some(l => l instanceof Promise)) { let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab"); let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined; - DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext); + DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, document, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext); } } } @@ -345,7 +346,7 @@ export class DocumentView extends DocComponent(Docu this._downY = e.clientY; this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0; if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) { - CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e); + CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.props.DataDoc]); e.stopPropagation(); } else { if (this.active) e.stopPropagation(); // events stop at the lowest document that is active. @@ -376,7 +377,7 @@ export class DocumentView extends DocComponent(Docu } 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"); }; + fieldsClicked = (): void => { let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }; makeBtnClicked = (): void => { let doc = Doc.GetProto(this.props.Document); doc.isButton = !BoolCast(doc.isButton, false); @@ -390,7 +391,7 @@ export class DocumentView extends DocComponent(Docu } } fullScreenClicked = (): void => { - CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeCopy(this.props.Document, false)); + CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeAlias(this.props.Document), this.props.DataDoc); SelectionManager.DeselectAll(); } @@ -473,10 +474,10 @@ export class DocumentView extends DocComponent(Docu const cm = ContextMenu.Instance; let subitems: ContextMenuProps[] = []; subitems.push({ description: "Open Full Screen", event: this.fullScreenClicked, icon: "desktop" }); - subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "inTab"), icon: "folder" }); - subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "inTab"), icon: "folder" }); - subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "onRight"), icon: "caret-square-right" }); - subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" }); + subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"), icon: "folder" }); + subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" }); + subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight"), icon: "caret-square-right" }); + subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" }); @@ -486,7 +487,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Find aliases", event: async () => { const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); - this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), "onRight"); + this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), Docs.SchemaDocument(["title"], aliases, {}), "onRight"); // bcz: dataDoc? }, icon: "search" }); cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document), icon: "crosshairs" }); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 35608af86..8879da55f 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -36,7 +36,7 @@ export interface FieldViewProps { isTopMost: boolean; selectOnLoad: boolean; addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean; - addDocTab: (document: Doc, where: string) => void; + addDocTab: (document: Doc, dataDoc: Doc, where: string) => void; removeDocument?: (document: Doc) => boolean; moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; ScreenToLocalTransform: () => Transform; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f59af226d..39444fd6a 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -270,7 +270,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this._linkClicked = href.replace(DocServer.prepend("/doc/"), "").split("?")[0]; if (this._linkClicked) { DocServer.GetRefField(this._linkClicked).then(f => { - (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, "inTab")); + (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, document, "inTab")); }); e.stopPropagation(); e.preventDefault(); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index f56a2d926..241a593b0 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,13 +1,13 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faImage } from '@fortawesome/free-solid-svg-icons'; -import { action, observable } from 'mobx'; +import { action, observable, computed } from 'mobx'; import { observer } from "mobx-react"; import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app import { Doc, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { List } from '../../../new_fields/List'; import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; -import { Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types'; +import { Cast, FieldValue, NumCast, StrCast, BoolCast } from '../../../new_fields/Types'; import { ImageField } from '../../../new_fields/URLField'; import { Utils } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; @@ -36,7 +36,7 @@ const ImageDocument = makeInterface(pageSchema, positionSchema); @observer export class ImageBox extends DocComponent(ImageDocument) { - public static LayoutString() { return FieldView.LayoutString(ImageBox); } + public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ImageBox, fieldKey); } private _imgRef: React.RefObject = React.createRef(); private _downX: number = 0; private _downY: number = 0; @@ -46,6 +46,8 @@ export class ImageBox extends DocComponent(ImageD private dropDisposer?: DragManager.DragDropDisposer; + @computed get dataDoc() { return this.props.DataDoc && BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; } + protected createDropTarget = (ele: HTMLDivElement) => { if (this.dropDisposer) { @@ -66,19 +68,24 @@ export class ImageBox extends DocComponent(ImageD drop = (e: Event, de: DragManager.DropEvent) => { if (de.data instanceof DragManager.DocumentDragData) { de.data.droppedDocuments.forEach(action((drop: Doc) => { - let layout = StrCast(drop.backgroundLayout); - if (layout.indexOf(ImageBox.name) !== -1) { - let imgData = this.props.Document[this.props.fieldKey]; - if (imgData instanceof ImageField) { - Doc.SetOnPrototype(this.props.Document, "data", new List([imgData])); - } - let imgList = Cast(this.props.Document[this.props.fieldKey], listSpec(ImageField), [] as any[]); - if (imgList) { - let field = drop.data; - if (field instanceof ImageField) imgList.push(field); - else if (field instanceof List) imgList.concat(field); - } + if (this.dataDoc !== this.props.Document && drop.data instanceof ImageField) { + this.dataDoc[this.props.fieldKey] = new ImageField(drop.data.url); e.stopPropagation(); + } else { + let layout = StrCast(drop.backgroundLayout); + if (layout.indexOf(ImageBox.name) !== -1) { + let imgData = this.dataDoc[this.props.fieldKey]; + if (imgData instanceof ImageField) { + Doc.SetOnPrototype(this.dataDoc, this.props.fieldKey, new List([imgData])); + } + let imgList = Cast(this.dataDoc[this.props.fieldKey], listSpec(ImageField), [] as any[]); + if (imgList) { + let field = drop.data; + if (field instanceof ImageField) imgList.push(field); + else if (field instanceof List) imgList.concat(field); + } + e.stopPropagation(); + } } })); // de.data.removeDocument() bcz: need to implement @@ -206,7 +213,7 @@ export class ImageBox extends DocComponent(ImageD let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"]; // this._curSuffix = ""; // if (w > 20) { - let field = this.Document[this.props.fieldKey]; + let field = this.dataDoc[this.props.fieldKey]; // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m"; // else if (this._largeRetryCount < 10) this._curSuffix = "_l"; @@ -214,8 +221,8 @@ export class ImageBox extends DocComponent(ImageD else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => this.choosePath((p as ImageField).url)); // } let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; - let rotation = NumCast(this.props.Document.rotation, 0); - let aspect = (rotation % 180) ? this.props.Document[HeightSym]() / this.props.Document[WidthSym]() : 1; + let rotation = NumCast(this.dataDoc.rotation, 0); + let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1; let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0; return (
{ this._isPage[page] = "page"; this._visibleElements[page] = ( 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/client/util') 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 30fd6dc9431cd8121a6d761ffdfba5d0dae641a6 Mon Sep 17 00:00:00 2001 From: ab Date: Fri, 21 Jun 2019 13:39:36 -0400 Subject: marks now reflect cursor position, not selection. Still need to consider when there is no nodeAfter --- src/client/util/TooltipTextMenu.scss | 2 +- src/client/util/TooltipTextMenu.tsx | 54 +++++++++++++++++++++++++------- src/client/views/MainOverlayTextBox.scss | 1 + 3 files changed, 45 insertions(+), 12 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss index d19ded68f..b10573b3e 100644 --- a/src/client/util/TooltipTextMenu.scss +++ b/src/client/util/TooltipTextMenu.scss @@ -60,7 +60,6 @@ } .ProseMirror-menu-dropdown-menu, .ProseMirror-menu-submenu { - position: absolute; background: $dark-color; color:white; border: 1px solid rgb(223, 223, 223); @@ -71,6 +70,7 @@ z-index: 100000; min-width: 6em; background: white; + position: absolute; } .linking { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 06c8bbc1a..c9216199b 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -7,6 +7,7 @@ import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemir import { EditorView } from "prosemirror-view"; import { schema } from "./RichTextSchema"; import { Schema, NodeType, MarkType, Mark } from "prosemirror-model"; +import { Node as ProsNode } from "prosemirror-model" import React = require("react"); import "./TooltipTextMenu.scss"; const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands"); @@ -30,6 +31,7 @@ import { DocumentManager } from "./DocumentManager"; import { Id } from "../../new_fields/FieldSymbols"; import { Utils } from "../../Utils"; import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox"; +import { text } from "body-parser"; // import { wrap } from "module"; const SVG = "http://www.w3.org/2000/svg"; @@ -290,7 +292,7 @@ export class TooltipTextMenu { } //will display a remove-list-type button if selection is in list, otherwise will show list type dropdown - updateListItemDropdown(label: string, listTypeBtn: Node) { + updateListItemDropdown(label: string, listTypeBtn: any) { //remove old btn if (listTypeBtn) { this.tooltip.removeChild(listTypeBtn); } @@ -516,7 +518,7 @@ export class TooltipTextMenu { //return; } - let linksInSelection = this.activeMarksOnSelection([schema.marks.link]); + //let linksInSelection = this.activeMarksOnSelection([schema.marks.link]); // if (linksInSelection.length > 0) { // let attributes = this.getMarksInSelection(this.view.state, [schema.marks.link])[0].attrs; // this.link.href = attributes.href; @@ -583,17 +585,47 @@ export class TooltipTextMenu { let { empty, $cursor, ranges } = this.view.state.selection as TextSelection; let state = this.view.state; let dispatch = this.view.dispatch; - - let activeMarks = markGroup.filter(mark => { - if (dispatch) { - let has = false, tr = state.tr; - for (let i = 0; !has && i < ranges.length; i++) { - let { $from, $to } = ranges[i]; - return state.doc.rangeHasMark($from.pos, $to.pos, mark); + let activeMarks: MarkType[]; + if (!empty) { + activeMarks = markGroup.filter(mark => { + if (dispatch) { + let has = false, tr = state.tr; + for (let i = 0; !has && i < ranges.length; i++) { + let { $from, $to } = ranges[i]; + return state.doc.rangeHasMark($from.pos, $to.pos, mark); + } } + return false; + }); + } + else { + let pos = this.view.state.selection.$from; + let ref_node: ProsNode; + if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) { + ref_node = pos.nodeAfter; } - return false; - }); + else if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) { + ref_node = pos.nodeBefore; + } + else { + return []; + } + let text_node_type: NodeType; + if (ref_node.isText) { + text_node_type = ref_node.type; + } + else { + return []; + } + + activeMarks = markGroup.filter(mark_type => { + if (dispatch) { + let mark = state.schema.mark(mark_type); + return ref_node.marks.includes(mark); + } + return false; + }); + } return activeMarks; } diff --git a/src/client/views/MainOverlayTextBox.scss b/src/client/views/MainOverlayTextBox.scss index c294e1b35..1093ff671 100644 --- a/src/client/views/MainOverlayTextBox.scss +++ b/src/client/views/MainOverlayTextBox.scss @@ -21,4 +21,5 @@ .unscaled_div{ width: 500px; z-index: 10000; + position: absolute; } \ No newline at end of file -- 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/client/util') 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/client/util') 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 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/client/util') 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 73b3304d0865fc34ad1f21af2bbca20a3eca8a8a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 23 Jun 2019 14:08:08 -0400 Subject: fixed issues with summarizing blocks --- src/client/util/RichTextSchema.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 60481f1f9..820d17a14 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -501,8 +501,7 @@ export class SummarizedView { let length = to - from; let newSelection = TextSelection.create(view.state.doc, y + 1, y + 1 + length); node.attrs.text = newSelection.content(); - view.dispatch(view.state.tr.setSelection(newSelection)); - view.dispatch(view.state.tr.deleteSelection(view.state, () => { })); + view.dispatch(view.state.tr.setSelection(newSelection).deleteSelection(view.state, () => { })); self._collapsed.textContent = "㊉"; } else { node.attrs.visibility = !node.attrs.visibility; @@ -513,9 +512,8 @@ export class SummarizedView { console.log("PASTING " + node.attrs.text.toString()); view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1))); const from = view.state.selection.from; - view.dispatch(view.state.tr.replaceSelection(node.attrs.text).addMark(from, from + node.attrs.oldtextlen, mark)); - //view.dispatch(view.state.tr.setSelection(view.state.doc, from + node.attrs.oldtextlen + 1, from + node.attrs.oldtextlen + 1)); - view.dispatch(view.state.tr.removeStoredMark(mark)); + let size = node.attrs.text.size; + view.dispatch(view.state.tr.replaceSelection(node.attrs.text).addMark(from, from + size, mark).removeStoredMark(mark)); self._collapsed.textContent = "㊀"; } e.preventDefault(); @@ -548,16 +546,16 @@ export class SummarizedView { let visited = new Set(); for (let i: number = start + 1; i < this._view.state.doc.nodeSize - 1; i++) { console.log("ITER:", i); + let skip = false; this._view.state.doc.nodesBetween(start, i, (node: Node, pos: number, parent: Node, index: number) => { - if (node.isLeaf) { - if (node.marks.includes(_mark) && !visited.has(node)) { + if (node.isLeaf && !visited.has(node) && !skip) { + if (node.marks.includes(_mark)) { visited.add(node); //endPos += node.nodeSize + 1; endPos = i + node.nodeSize - 1; console.log("node contains mark!"); } - else { } - + else skip = true; } }); } -- cgit v1.2.3-70-g09d2 From 818d3a367648f7dbb279e13aa197ffbae412fbd0 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 23 Jun 2019 21:14:34 -0400 Subject: added arbitrary font size mark --- src/client/util/RichTextSchema.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/client/util') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 820d17a14..f3f6655af 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -350,6 +350,16 @@ export const marks: { [index: string]: MarkSpec } = { /** FONT SIZES */ + pFontSize: { + attrs: { + fontSize: { default: 10 } + }, + inclusive: false, + parseDOM: [{ style: 'font-size: 10px;' }], + toDOM: (node) => ['span', { + style: `font-size: ${node.attrs.fontSize}px;` + }] + }, p10: { parseDOM: [{ style: 'font-size: 10px;' }], -- cgit v1.2.3-70-g09d2 From 52051829373bc4acfe9d705b64c30e3fddebf439 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 24 Jun 2019 10:49:05 -0400 Subject: Fixed image size stuff --- package.json | 3 +- src/client/documents/Documents.ts | 2 +- src/client/util/request-image-size.js | 73 +++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 src/client/util/request-image-size.js (limited to 'src/client/util') diff --git a/package.json b/package.json index 91d3a3853..713c5d585 100644 --- a/package.json +++ b/package.json @@ -124,6 +124,7 @@ "html-to-image": "^0.1.0", "i": "^0.3.6", "image-data-uri": "^2.0.0", + "image-size": "^0.7.4", "imagesloaded": "^4.1.4", "jsonwebtoken": "^8.5.0", "jsx-to-string": "^1.4.0", @@ -175,13 +176,13 @@ "react-split-pane": "^0.1.85", "react-table": "^6.9.2", "request": "^2.88.0", - "request-image-size": "^2.1.0", "request-promise": "^4.2.4", "serializr": "^1.5.1", "sharp": "^0.22.1", "socket.io": "^2.2.0", "socket.io-client": "^2.2.0", "solr-node": "^1.1.3", + "standard-http-error": "^2.0.1", "typescript-collections": "^1.3.2", "url-loader": "^1.1.2", "uuid": "^3.3.2", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index de6c5bc6a..b04fc401a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -34,7 +34,7 @@ import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; -var requestImageSize = require('request-image-size'); +var requestImageSize = require('../util/request-image-size'); var path = require('path'); export interface DocumentOptions { diff --git a/src/client/util/request-image-size.js b/src/client/util/request-image-size.js new file mode 100644 index 000000000..0f9328872 --- /dev/null +++ b/src/client/util/request-image-size.js @@ -0,0 +1,73 @@ +/** + * request-image-size: Detect image dimensions via request. + * Licensed under the MIT license. + * + * https://github.com/FdezRomero/request-image-size + * © 2017 Rodrigo Fernández Romero + * + * Based on the work of Johannes J. Schmidt + * https://github.com/jo/http-image-size + */ + +const request = require('request'); +const imageSize = require('image-size'); +const HttpError = require('standard-http-error'); + +module.exports = function requestImageSize(options) { + let opts = { + encoding: null + }; + + if (options && typeof options === 'object') { + opts = Object.assign(options, opts); + } else if (options && typeof options === 'string') { + opts = Object.assign({ uri: options }, opts); + } else { + return Promise.reject(new Error('You should provide an URI string or a "request" options object.')); + } + + opts.encoding = null; + + return new Promise((resolve, reject) => { + const req = request(opts); + + req.on('response', res => { + if (res.statusCode >= 400) { + return reject(new HttpError(res.statusCode, res.statusMessage)); + } + + let buffer = new Buffer([]); + let size; + let imageSizeError; + + res.on('data', chunk => { + buffer = Buffer.concat([buffer, chunk]); + + try { + size = imageSize(buffer); + } catch (err) { + imageSizeError = err; + return; + } + + if (size) { + resolve(size); + return req.abort(); + } + }); + + res.on('error', err => reject(err)); + + res.on('end', () => { + if (!size) { + return reject(imageSizeError); + } + + size.downloaded = buffer.length; + return resolve(size); + }); + }); + + req.on('error', err => reject(err)); + }); +}; -- cgit v1.2.3-70-g09d2 From 6df09d7d646c16e6469b198e7d270b6a1e45b0c7 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 24 Jun 2019 13:23:28 -0400 Subject: fixes for templates with annotations and more. --- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 55 +++++------ src/client/views/InkingCanvas.tsx | 7 +- src/client/views/InkingControl.tsx | 3 +- src/client/views/MainView.tsx | 4 +- .../views/collections/CollectionBaseView.tsx | 1 - .../views/collections/CollectionTreeView.tsx | 106 +++++++++------------ src/client/views/collections/CollectionView.tsx | 17 +--- .../collectionFreeForm/CollectionFreeFormView.tsx | 5 +- src/client/views/pdf/PDFViewer.tsx | 1 + src/new_fields/Doc.ts | 2 +- 11 files changed, 82 insertions(+), 121 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index c9216199b..048fb7133 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -222,7 +222,7 @@ export class TooltipTextMenu { if (DocumentManager.Instance.getDocumentView(f)) { DocumentManager.Instance.getDocumentView(f)!.props.focus(f); } - else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f); + else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f, f); } })); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 0d5cca9f1..9be5c9cd6 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -24,9 +24,10 @@ import { LinkMenu } from "./nodes/LinkMenu"; import { TemplateMenu } from "./TemplateMenu"; import { Template, Templates } from "./Templates"; import React = require("react"); -import { URLField } from '../../new_fields/URLField'; +import { URLField, ImageField } from '../../new_fields/URLField'; import { templateLiteral } from 'babel-types'; import { CollectionViewType } from './collections/CollectionBaseView'; +import { ImageBox } from './nodes/ImageBox'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -75,41 +76,29 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (text[0] === '#') { this._fieldKey = text.slice(1, text.length); this._title = this.selectionTitle; - } else if (text.startsWith(">>>")) { - let metaKey = text.slice(3, text.length); - let collection = SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView!.props.Document; - Doc.GetProto(collection)[metaKey] = new List([ - Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), - Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }), - ]); - let template = Doc.MakeAlias(collection); + } else if (text.startsWith(">")) { + let metaKey = text.slice(text.startsWith(">>>") ? 3 : text.startsWith(">>") ? 2 : 1, text.length); + let field = SelectionManager.SelectedDocuments()[0]; + let collectionKey = field.props.ContainingCollectionView!.props.fieldKey; + let collection = field.props.ContainingCollectionView!.props.Document; + let collectionKeyProp = `fieldKey={"${collectionKey}"}`; + let collectionAnnotationsKeyProp = `fieldKey={"annotations"}`; + let metaKeyProp = `fieldKey={"${metaKey}"}`; + let metaAnnotationsKeyProp = `fieldKey={"${metaKey}_annotations"}`; + let template = Doc.MakeAlias(field.props.Document); + template.proto = collection; template.title = metaKey; + template.nativeWidth = Cast(field.nativeWidth, "number"); + template.nativeHeight = Cast(field.nativeHeight, "number"); template.embed = true; - template.layout = CollectionView.LayoutString(metaKey); - template.viewType = CollectionViewType.Freeform; - template.x = 0; - template.y = 0; - template.width = 300; - template.height = 300; template.isTemplate = true; - template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); - Doc.AddDocToList(collection, "data", template); - SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument && dv.props.removeDocument(dv.props.Document)); - } else if (text[0] === ">") { - let metaKey = text.slice(1, text.length); - let first = SelectionManager.SelectedDocuments()[0].props.Document!; - let collection = SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView!.props.Document; - Doc.GetProto(collection)[metaKey] = "-empty field-"; - let template = Doc.MakeAlias(collection); - template.title = metaKey; - template.layout = FormattedTextBox.LayoutString(metaKey); - template.isTemplate = true; - template.x = NumCast(first.x); - template.y = NumCast(first.y); - template.width = first[WidthSym](); - template.height = first[HeightSym](); - template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); - Doc.AddDocToList(collection, "data", template); + template.templates = new List([Templates.TitleBar(metaKey)]); + template.layout = StrCast(field.props.Document.layout).replace(collectionKeyProp, metaKeyProp); + if (field.props.Document.backgroundLayout) { + template.layout = StrCast(field.props.Document.layout).replace(collectionAnnotationsKeyProp, metaAnnotationsKeyProp); + template.backgroundLayout = StrCast(field.props.Document.backgroundLayout).replace(collectionKeyProp, metaKeyProp); + } + Doc.AddDocToList(collection, collectionKey, template); SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument && dv.props.removeDocument(dv.props.Document)); } else { diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 5d4ea76cd..fd7e5b07d 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -14,6 +14,7 @@ import { Cast, PromiseValue, NumCast } from "../../new_fields/Types"; interface InkCanvasProps { getScreenTransform: () => Transform; Document: Doc; + inkFieldKey: string; children: () => JSX.Element[]; } @@ -40,7 +41,7 @@ export class InkingCanvas extends React.Component { } componentDidMount() { - PromiseValue(Cast(this.props.Document.ink, InkField)).then(ink => runInAction(() => { + PromiseValue(Cast(this.props.Document[this.props.inkFieldKey], InkField)).then(ink => runInAction(() => { if (ink) { let bounds = Array.from(ink.inkData).reduce(([mix, max, miy, may], [id, strokeData]) => strokeData.pathData.reduce(([mix, max, miy, may], p) => @@ -55,12 +56,12 @@ export class InkingCanvas extends React.Component { @computed get inkData(): Map { - let map = Cast(this.props.Document.ink, InkField); + let map = Cast(this.props.Document[this.props.inkFieldKey], InkField); return !map ? new Map : new Map(map.inkData); } set inkData(value: Map) { - Doc.GetProto(this.props.Document).ink = new InkField(value); + Doc.GetProto(this.props.Document)[this.props.inkFieldKey] = new InkField(value); } @action diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 0837e07a9..6cde73933 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -8,6 +8,7 @@ import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-s import { SelectionManager } from "../util/SelectionManager"; import { InkTool } from "../../new_fields/InkField"; import { Doc } from "../../new_fields/Doc"; +import { InkingCanvas } from "./InkingCanvas"; library.add(faPen, faHighlighter, faEraser, faBan); @@ -39,7 +40,7 @@ export class InkingControl extends React.Component { @action switchColor = (color: ColorResult): void => { this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); - SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = this._selectedColor); + if (InkingControl.Instance.selectedTool === InkTool.None) SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = this._selectedColor); } @action diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8198b88d2..a72f25b99 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -144,7 +144,7 @@ export class MainView extends React.Component { const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); if (list) { let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` }); - var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; + var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] }; let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }, id); list.push(mainDoc); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) @@ -177,7 +177,7 @@ export class MainView extends React.Component { openNotifsCol = () => { if (this._notifsCol && CollectionDockingView.Instance) { - CollectionDockingView.Instance.AddRightSplit(this._notifsCol); + CollectionDockingView.Instance.AddRightSplit(this._notifsCol, this._notifsCol); } } diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 75bdf755c..79a9f3be0 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -100,7 +100,6 @@ export class CollectionBaseView extends React.Component { } return false; } - @computed get isAnnotationOverlay() { return this.props.fieldKey === "annotations"; } @action.bound addDocument(doc: Doc, allowDuplicates: boolean = false): boolean { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b3f1b1c88..f5f323269 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -28,7 +28,6 @@ import React = require("react"); import { FormattedTextBox } from '../nodes/FormattedTextBox'; import { ImageField } from '../../../new_fields/URLField'; import { ImageBox } from '../nodes/ImageBox'; -import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionView } from './CollectionView'; @@ -71,11 +70,23 @@ class TreeView extends React.Component { @observable _collapsed: boolean = true; @computed get fieldKey() { + let keys = Array.from(Object.keys(this.dataDoc)); + if (this.dataDoc.proto instanceof Doc) { + keys.push(...Array.from(Object.keys(this.dataDoc.proto))); + while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); + } + let keyList: string[] = []; + keys.map(key => { + let docList = Cast(this.dataDoc[key], listSpec(Doc)); + if (docList && docList.length > 0) { + keyList.push(key); + } + }); let layout = StrCast(this.props.document.layout); if (layout.indexOf("fieldKey={\"") !== -1) { return layout.split("fieldKey={\"")[1].split("\"")[0]; } - return "data"; + return keyList.length ? keyList[0] : "data"; } @computed get dataDoc() { return (BoolCast(this.props.document.isTemplate) ? this.props.dataDoc : this.props.document); } @@ -163,63 +174,39 @@ class TreeView extends React.Component { SetValue={(value: string) => { let res = (Doc.GetProto(this.dataDoc)[key] = value) ? true : true; - if (value.startsWith(">>>")) { - let metaKey = value.slice(3, value.length); + if (value.startsWith(">")) { + let metaKey = value.slice(value.startsWith(">>>") ? 3 : value.startsWith(">>") ? 2 : 1, value.length); let collection = this.props.containingCollection; - Doc.GetProto(collection)[metaKey] = new List([ - Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), - Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }), - ]); let template = Doc.MakeAlias(collection); template.title = metaKey; template.embed = true; - template.layout = CollectionView.LayoutString(metaKey); - template.viewType = CollectionViewType.Freeform; template.x = 0; template.y = 0; template.width = 300; template.height = 300; template.isTemplate = true; template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); - Doc.AddDocToList(collection, "data", template); - this.delete(); - } else - if (value.startsWith(">>")) { - let metaKey = value.slice(2, value.length); - let collection = this.props.containingCollection; + if (value.startsWith(">>>")) { // Collection + Doc.GetProto(collection)[metaKey] = new List([ + Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), + Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }), + ]); + template.layout = CollectionView.LayoutString(metaKey); + template.viewType = CollectionViewType.Freeform; + } else if (value.startsWith(">>")) { // Image Doc.GetProto(collection)[metaKey] = new ImageField("http://www.cs.brown.edu/~bcz/face.gif"); - let template = Doc.MakeAlias(collection); - template.title = metaKey; - template.embed = true; template.layout = ImageBox.LayoutString(metaKey); - template.x = 0; - template.y = 0; template.nativeWidth = 300; template.nativeHeight = 300; - template.width = 300; - template.height = 300; - template.isTemplate = true; - template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); - Doc.AddDocToList(collection, "data", template); - this.delete(); - } else - if (value.startsWith(">")) { - let metaKey = value.slice(1, value.length); - let collection = this.props.containingCollection; - Doc.GetProto(collection)[metaKey] = "-empty field-"; - let template = Doc.MakeAlias(collection); - template.title = metaKey; - template.embed = true; - template.layout = FormattedTextBox.LayoutString(metaKey); - template.x = 0; - template.y = 0; - template.width = 100; - template.height = 50; - template.isTemplate = true; - template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); - Doc.AddDocToList(collection, "data", template); - this.delete(); - } + } else if (value.startsWith(">")) { // Text + Doc.GetProto(collection)[metaKey] = "-empty field-"; + template.layout = FormattedTextBox.LayoutString(metaKey); + template.width = 100; + template.height = 50; + } + Doc.AddDocToList(collection, "data", template); + this.delete(); + } return res; }} @@ -238,14 +225,8 @@ class TreeView extends React.Component { keys.push(...Array.from(Object.keys(this.dataDoc.proto))); while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } - let keyList: string[] = []; - keys.map(key => { - let docList = Cast(this.dataDoc[key], listSpec(Doc)); - let doc = Cast(this.dataDoc[key], Doc); - if (doc instanceof Doc || docList) { - keyList.push(key); - } - }); + let keyList: string[] = keys.reduce((l, key) => Cast(this.dataDoc[key], listSpec(Doc)) ? [...l, key] : l, [] as string[]); + keys.map(key => Cast(this.dataDoc[key], Doc) instanceof Doc && keyList.push(key)); if (keyList.indexOf(this.fieldKey) !== -1) { keyList.splice(keyList.indexOf(this.fieldKey), 1); } @@ -291,16 +272,16 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.dataDoc)) }); - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.dataDoc, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" }); + ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" }); if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { - ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.props.document, "inTab"), icon: "folder" }); - ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.props.document, "onRight"), icon: "caret-square-right" }); + ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.dataDoc, "inTab"), icon: "folder" }); + ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.dataDoc, "onRight"), icon: "caret-square-right" }); if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) { ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.dataDoc).map(view => view.props.focus(this.props.document)) }); } - ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.dataDoc)) }); + ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } else { - ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.dataDoc)) }); + ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); e.stopPropagation(); @@ -349,14 +330,14 @@ 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.props.document, this.props.dataDoc, this._chosenKey, addDoc, remDoc, this.move, + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.dataDoc, 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 =
{ active: () => boolean, panelWidth: () => number, ) { - let docList = docs.filter(child => !child.excludeFromLibrary && (key !== this.fieldKey || !child.isMinimized)); + let docList = docs.filter(child => !child.excludeFromLibrary); let rowWidth = () => panelWidth() - 20; return docList.map((child, i) => { let indent = i === 0 ? undefined : () => { @@ -475,6 +456,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { outerXf = () => Utils.GetScreenTransform(this._mainEle!); onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {}); + @computed get dataDoc() { return (BoolCast(this.props.DataDoc.isTemplate) ? this.props.DataDoc : this.props.Document); } render() { let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; @@ -502,7 +484,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { }} />
    { - TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove, + TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.dataDoc, this.props.fieldKey, addDoc, this.remove, moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth) }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 872cb3f1c..cc097f371 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -2,8 +2,10 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; import { observer } from "mobx-react"; import * as React from 'react'; +import { Doc } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; +import { Docs } from '../../documents/Documents'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; @@ -14,11 +16,6 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV import { CollectionSchemaView } from "./CollectionSchemaView"; import { CollectionStackingView } from './CollectionStackingView'; import { CollectionTreeView } from "./CollectionTreeView"; -import { Doc } from '../../../new_fields/Doc'; -import { FormattedTextBox } from '../nodes/FormattedTextBox'; -import { Docs } from '../../documents/Documents'; -import { List } from '../../../new_fields/List'; -import { ImageField } from '../../../new_fields/URLField'; export const COLLECTION_BORDER_WIDTH = 2; library.add(faTh); @@ -62,17 +59,7 @@ export class CollectionView extends React.Component { ContextMenu.Instance.addItem({ description: "Apply Template", event: undoBatch(() => { let otherdoc = Docs.TextDocument({ width: 100, height: 50, title: "applied template" }); - Doc.GetProto(otherdoc).description = "THIS DESCRIPTION IS REALLY IMPORTANT!"; - Doc.GetProto(otherdoc).summary = "THIS SUMMARY IS MEANINGFUL!"; - Doc.GetProto(otherdoc).photo = new ImageField("http://www.cs.brown.edu/~bcz/snowbeast.JPG"); Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document); - Doc.GetProto(otherdoc).publication = new List([ - Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }), - Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), - Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), - Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), - Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }), - ]); this.props.addDocTab && this.props.addDocTab(otherdoc, otherdoc, "onRight"); }), icon: "project-diagram" }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 71964ef82..c6f003a81 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -370,7 +370,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } private childViews = () => [ - , + , ...this.views ] render() { @@ -387,7 +387,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { easing={easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}> - + {this.childViews} @@ -414,6 +414,7 @@ class CollectionFreeFormOverlayView extends React.Component boolean }> { @computed get backgroundView() { + let props = this.props; return (); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 7000352e7..6adead626 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -203,6 +203,7 @@ class Viewer extends React.Component { this._isPage[page] = "page"; this._visibleElements[page] = ( Date: Mon, 24 Jun 2019 13:41:39 -0400 Subject: Added ReadOnly mode for docs and changed computed values a bit --- src/client/util/Scripting.ts | 12 +++++++++++- src/client/views/nodes/KeyValueBox.tsx | 6 +++--- src/fields/ScriptField.ts | 9 +++++---- src/new_fields/Doc.ts | 11 ++++++++++- src/new_fields/Proxy.ts | 4 +--- src/new_fields/util.ts | 36 +++++++++++++++++++++++++++------- 6 files changed, 59 insertions(+), 19 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 40e2ad6bb..a59d9f201 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -39,7 +39,6 @@ export interface CompileError { } export type CompileResult = CompiledScript | CompileError; - function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult { const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error); if ((options.typecheck !== false && errors) || !script) { @@ -64,10 +63,20 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an } } let thisParam = args.this || capturedVariables.this; + let batch: { end(): void } | undefined = undefined; try { + if (!options.editable) { + batch = Doc.MakeReadOnly(); + } const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray); + if (batch) { + batch.end(); + } return { success: true, result }; } catch (error) { + if (batch) { + batch.end(); + } return { success: false, error }; } }; @@ -133,6 +142,7 @@ export interface ScriptOptions { params?: { [name: string]: string }; capturedVariables?: { [name: string]: Field }; typecheck?: boolean; + editable?: boolean; } export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult { diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 3d626eef0..a4c14ae38 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -38,10 +38,11 @@ export class KeyValueBox extends React.Component { } public static SetField(doc: Doc, key: string, value: string) { let eq = value.startsWith("="); + let target = eq ? doc : Doc.GetProto(doc); value = eq ? value.substr(1) : value; let dubEq = value.startsWith(":="); value = dubEq ? value.substr(2) : value; - let options: ScriptOptions = { addReturn: true }; + let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } }; if (dubEq) options.typecheck = false; let script = CompileScript(value, options); if (!script.compiled) { @@ -49,12 +50,11 @@ export class KeyValueBox extends React.Component { } let field = new ComputedField(script); if (!dubEq) { - let res = script.run(); + let res = script.run({ this: target }); if (!res.success) return false; field = res.result; } if (Field.IsField(field, true)) { - let target = eq ? doc : Doc.GetProto(doc); target[key] = field; return true; } diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index ac46ccf90..dbca74720 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -3,7 +3,7 @@ import { CompiledScript, CompileScript } from "../client/util/Scripting"; import { Copy, ToScriptString, Parent, SelfProxy } from "../new_fields/FieldSymbols"; import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; import { Deserializable } from "../client/util/SerializationHelper"; -import { computed } from "mobx"; +import { Doc } from "../new_fields/Doc"; function optional(propSchema: PropSchema) { return custom(value => { @@ -23,6 +23,7 @@ const optionsSchema = createSimpleSchema({ requiredType: true, addReturn: true, typecheck: true, + readonly: true, params: optional(map(primitive())) }); @@ -86,9 +87,9 @@ export class ScriptField extends ObjectField { @Deserializable("computed", deserializeScript) export class ComputedField extends ScriptField { - @computed - get value() { - const val = this._script.run({ this: (this[Parent] as any)[SelfProxy] }); + //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc + value(doc: Doc) { + const val = this._script.run({ this: doc }); if (val.success) { return val.result; } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 9bacf49ba..cce4fff5d 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -2,7 +2,7 @@ import { observable, action } from "mobx"; 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 { setter, getter, getField, updateFunction, deleteProperty, makeEditable, makeReadOnly } from "./util"; import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast } from "./Types"; import { listSpec } from "./Schema"; import { ObjectField } from "./ObjectField"; @@ -156,6 +156,15 @@ export namespace Doc { // return Cast(field, ctor); // }); // } + export function MakeReadOnly(): { end(): void } { + makeReadOnly(); + return { + end() { + makeEditable(); + } + }; + } + export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult { const self = doc[Self]; return getField(self, key, ignoreProto); diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index 130ec066e..38d874a68 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -48,9 +48,8 @@ export class ProxyField extends ObjectField { private failed = false; private promise?: Promise; - value(callback?: ((field: T | undefined) => void)): T | undefined | FieldWaiting { + value(): T | undefined | FieldWaiting { if (this.cache) { - callback && callback(this.cache); return this.cache; } if (this.failed) { @@ -64,7 +63,6 @@ export class ProxyField extends ObjectField { return field; })); } - callback && this.promise.then(callback); return this.promise; } } diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 8cb1db953..cc937c567 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -6,10 +6,13 @@ import { FieldValue } from "./Types"; import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; import { action } from "mobx"; -import { Parent, OnUpdate, Update, Id, SelfProxy } from "./FieldSymbols"; +import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols"; import { ComputedField } from "../fields/ScriptField"; -export const setter = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { +function _readOnlySetter(): never { + throw new Error("Documents can't be modified in read-only mode"); +} +const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { if (SerializationHelper.IsSerializing()) { target[prop] = value; return true; @@ -53,6 +56,20 @@ export const setter = action(function (target: any, prop: string | symbol | numb return true; }); +let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl; + +export function makeReadOnly() { + _setter = _readOnlySetter; +} + +export function makeEditable() { + _setter = _setterImpl; +} + +export function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean { + return _setter(target, prop, value, receiver); +} + export function getter(target: any, prop: string | symbol | number, receiver: any): any { if (typeof prop === "symbol") { return target.__fields[prop] || target[prop]; @@ -60,25 +77,30 @@ export function getter(target: any, prop: string | symbol | number, receiver: an if (SerializationHelper.IsSerializing()) { return target[prop]; } - return getField(target, prop); + return getFieldImpl(target, prop, receiver); } -export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any { +function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any { + receiver = receiver || target[SelfProxy]; const field = target.__fields[prop]; if (field instanceof ProxyField) { return field.value(); } if (field instanceof ComputedField) { - return field.value; + return field.value(receiver); } if (field === undefined && !ignoreProto && prop !== "proto") { - const proto = getField(target, "proto", true); + const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters if (proto instanceof Doc) { - return proto[prop]; + return getFieldImpl(proto[Self], prop, receiver, ignoreProto); } return undefined; } return field; + +} +export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any { + return getFieldImpl(target, prop, undefined, ignoreProto); } export function deleteProperty(target: any, prop: string | number | symbol) { -- 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/client/util') 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 4270bd26e1b28f429ca11e43504b1a39a0a77baa Mon Sep 17 00:00:00 2001 From: ab Date: Mon, 24 Jun 2019 16:11:29 -0400 Subject: idrk --- src/client/util/RichTextSchema.tsx | 33 ++----- src/client/util/TooltipTextMenu.tsx | 168 ++++++++++++++++++++++-------------- 2 files changed, 110 insertions(+), 91 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index f3f6655af..936dcbfd6 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -91,8 +91,8 @@ export const nodes: { [index: string]: NodeSpec } = { attrs: { visibility: { default: false }, text: { default: undefined }, - oldtextslice: { default: undefined }, - oldtextlen: { default: 0 } + textslice: { default: undefined }, + textlen: { default: 0 } }, group: "inline", @@ -495,31 +495,26 @@ export class SummarizedView { this._collapsed = document.createElement("span"); this._collapsed.textContent = "㊉"; this._collapsed.style.opacity = "0.5"; - // this._collapsed.style.background = "yellow"; this._collapsed.style.position = "relative"; this._collapsed.style.width = "40px"; this._collapsed.style.height = "20px"; let self = this; this._view = view; this._collapsed.onpointerdown = function (e: any) { - console.log("star pressed!"); if (node.attrs.visibility) { node.attrs.visibility = !node.attrs.visibility; - console.log("content is visible"); let y = getPos(); let { from, to } = self.updateSummarizedText(y + 1, view.state.schema.marks.highlight); let length = to - from; let newSelection = TextSelection.create(view.state.doc, y + 1, y + 1 + length); + // update attrs of node node.attrs.text = newSelection.content(); view.dispatch(view.state.tr.setSelection(newSelection).deleteSelection(view.state, () => { })); self._collapsed.textContent = "㊉"; } else { node.attrs.visibility = !node.attrs.visibility; - console.log("content is invisible"); let y = getPos(); - console.log(y); let mark = view.state.schema.mark(view.state.schema.marks.highlight); - console.log("PASTING " + node.attrs.text.toString()); view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1))); const from = view.state.selection.from; let size = node.attrs.text.size; @@ -538,39 +533,21 @@ export class SummarizedView { updateSummarizedText(start?: any, mark?: any) { let $start = this._view.state.doc.resolve(start); let endPos = start; - //let first_startPos = $start.start(), endPos = first_startPos; - // let startIndex = $start.index(), endIndex = $start.indexAfter(); - // let nodeAfter = $start.nodeAfter; - // while (startIndex > 0 && mark.isInSet($start.parent.child(startIndex - 1).marks)) startIndex--; - // while (endIndex < $start.parent.childCount && mark.isInSet($start.parent.child(endIndex).marks)) endIndex++; - // let startPos = $start.start(), endPos = startPos; - // for (let i = 0; i < endIndex; i++) { - // let size = $start.parent.child(i).nodeSize; - // console.log($start.parent.child(i).childCount); - // if (i < startIndex) startPos += size; - // endPos += size; - // } + let _mark = this._view.state.schema.mark(this._view.state.schema.marks.highlight); - // first_startPos = start; - // endPos = first_startPos; let visited = new Set(); for (let i: number = start + 1; i < this._view.state.doc.nodeSize - 1; i++) { - console.log("ITER:", i); let skip = false; this._view.state.doc.nodesBetween(start, i, (node: Node, pos: number, parent: Node, index: number) => { if (node.isLeaf && !visited.has(node) && !skip) { if (node.marks.includes(_mark)) { visited.add(node); - //endPos += node.nodeSize + 1; endPos = i + node.nodeSize - 1; - console.log("node contains mark!"); } else skip = true; } }); } - console.log(start); - console.log(endPos); return { from: start, to: endPos }; } @@ -592,7 +569,7 @@ const fromJson = schema.nodeFromJSON; schema.nodeFromJSON = (json: any) => { let node = fromJson(json); if (json.type === "star") { - node.attrs.oldtext = Slice.fromJSON(schema, node.attrs.oldtextslice); + node.attrs.text = Slice.fromJSON(schema, node.attrs.textslice); } return node; }; \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index c9216199b..c42d5fab8 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -6,7 +6,7 @@ import { keymap } from "prosemirror-keymap"; import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { schema } from "./RichTextSchema"; -import { Schema, NodeType, MarkType, Mark } from "prosemirror-model"; +import { Schema, NodeType, MarkType, Mark, ResolvedPos } from "prosemirror-model"; import { Node as ProsNode } from "prosemirror-model" import React = require("react"); import "./TooltipTextMenu.scss"; @@ -32,6 +32,7 @@ import { Id } from "../../new_fields/FieldSymbols"; import { Utils } from "../../Utils"; import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox"; import { text } from "body-parser"; +import { type } from "os"; // import { wrap } from "module"; const SVG = "http://www.w3.org/2000/svg"; @@ -61,6 +62,8 @@ export class TooltipTextMenu { private fontStyleDom?: Node; private listTypeBtnDom?: Node; + private _activeMarks: Mark[] = []; + constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) { this.view = view; this.state = view.state; @@ -123,6 +126,7 @@ export class TooltipTextMenu { this.fontSizeToNum.set(schema.marks.p32, 32); this.fontSizeToNum.set(schema.marks.p48, 48); this.fontSizeToNum.set(schema.marks.p72, 72); + //this.fontSizeToNum.set(schema.marks.pFontSize,schema.marks.pFontSize.) this.fontSizes = Array.from(this.fontSizeToNum.keys()); //list types @@ -282,8 +286,20 @@ export class TooltipTextMenu { } insertStar(state: EditorState, dispatch: any) { - console.log("creating star..."); - let newNode = schema.nodes.star.create({ visibility: false, text: state.selection.content(), oldtextslice: state.selection.content().toJSON(), oldtextlen: state.selection.to - state.selection.from }); + if (state.selection.empty) { + let mark = state.schema.mark(state.schema.marks.highlight) + if (this._activeMarks.includes(mark)) { + const ind = this._activeMarks.indexOf(mark); + this._activeMarks.splice(ind, 1); + } + else { + this._activeMarks.push(mark); + } + dispatch(state.tr.insertText(" ").setStoredMarks(this._activeMarks)); + //dispatch(state.tr.setStoredMarks(this._activeMarks)); + return true; + } + let newNode = schema.nodes.star.create({ visibility: false, text: state.selection.content(), textslice: state.selection.content().toJSON(), textlen: state.selection.to - state.selection.from }); if (dispatch) { //console.log(newNode.attrs.text.toString()); dispatch(state.tr.replaceSelectionWith(newNode)); @@ -315,7 +331,7 @@ export class TooltipTextMenu { } //for a specific grouping of marks (passed in), remove all and apply the passed-in one to the selected text - changeToMarkInGroup(markType: MarkType, view: EditorView, fontMarks: MarkType[]) { + changeToMarkInGroup = (markType: MarkType, view: EditorView, fontMarks: MarkType[]) => { let { empty, $cursor, ranges } = view.state.selection as TextSelection; let state = view.state; let dispatch = view.dispatch; @@ -341,7 +357,18 @@ export class TooltipTextMenu { } } } - }); //actually apply font + }); + // fontsize + // if (markType.name[0] === 'p') { + // let size = this.fontSizeToNum.get(markType); + // if (size) { this.updateFontSizeDropdown(String(size) + " pt"); } + // } + // else { + // let fontName = this.fontStylesToName.get(markType); + // if (fontName) { this.updateFontStyleDropdown(fontName); } + // } + //this.update(view, undefined); + //actually apply font return toggleMark(markType)(view.state, view.dispatch, view); } @@ -529,53 +556,46 @@ export class TooltipTextMenu { // Otherwise, reposition it and update its content //this.tooltip.style.display = ""; let { from, to } = state.selection; - let start = view.coordsAtPos(from), end = view.coordsAtPos(to); - // The box in which the tooltip is positioned, to use as base - //let box = this.tooltip.offsetParent!.getBoundingClientRect(); - // Find a center-ish x position from the selection endpoints (when - // crossing lines, end may be more to the left) - let left = Math.max((start.left + end.left) / 2, start.left + 3); - //this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px"; - let width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale; - let mid = Math.min(start.left, end.left) + width; - - //this.tooltip.style.width = 225 + "px"; - // this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px"; - // this.tooltip.style.top = "-100px"; - //this.tooltip.style.height = "100px"; - - // let transform = this.editorProps.ScreenToLocalTransform(); - // this.tooltip.style.width = `${225 / transform.Scale}px`; - // Utils //UPDATE LIST ITEM DROPDOWN this.listTypeBtnDom = this.updateListItemDropdown(":", this.listTypeBtnDom!); + //this._activeMarks = []; //UPDATE FONT STYLE DROPDOWN let activeStyles = this.activeMarksOnSelection(this.fontStyles); - if (activeStyles.length === 1) { - // if we want to update something somewhere with active font name - let fontName = this.fontStylesToName.get(activeStyles[0]); - if (fontName) { this.updateFontStyleDropdown(fontName); } - } else if (activeStyles.length === 0) { - //crimson on default - this.updateFontStyleDropdown("Crimson Text"); - } else { - this.updateFontStyleDropdown("Various"); + if (activeStyles !== undefined) { + // activeStyles.forEach((markType) => { + // this._activeMarks.push(this.view.state.schema.mark(markType)); + // }); + if (activeStyles.length === 1) { + // if we want to update something somewhere with active font name + let fontName = this.fontStylesToName.get(activeStyles[0]); + if (fontName) { this.updateFontStyleDropdown(fontName); } + } else if (activeStyles.length === 0) { + //crimson on default + this.updateFontStyleDropdown("Crimson Text"); + } else { + this.updateFontStyleDropdown("Various"); + } } //UPDATE FONT SIZE DROPDOWN let activeSizes = this.activeMarksOnSelection(this.fontSizes); - if (activeSizes.length === 1) { //if there's only one active font size - let size = this.fontSizeToNum.get(activeSizes[0]); - if (size) { this.updateFontSizeDropdown(String(size) + " pt"); } - } else if (activeSizes.length === 0) { - //should be 14 on default - this.updateFontSizeDropdown("14 pt"); - } else { //multiple font sizes selected - this.updateFontSizeDropdown("Various"); + if (activeSizes !== undefined) { + if (activeSizes.length === 1) { //if there's only one active font size + // activeSizes.forEach((markType) => { + // this._activeMarks.push(this.view.state.schema.mark(markType)); + // }); + let size = this.fontSizeToNum.get(activeSizes[0]); + if (size) { this.updateFontSizeDropdown(String(size) + " pt"); } + } else if (activeSizes.length === 0) { + //should be 14 on default + this.updateFontSizeDropdown("14 pt"); + } else { //multiple font sizes selected + this.updateFontSizeDropdown("Various"); + } } - + this.view.dispatch(this.view.state.tr.setStoredMarks(this._activeMarks)); this.updateLinkMenu(); } @@ -599,34 +619,56 @@ export class TooltipTextMenu { }); } else { - let pos = this.view.state.selection.$from; - let ref_node: ProsNode; - if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) { - ref_node = pos.nodeAfter; - } - else if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) { - ref_node = pos.nodeBefore; - } - else { - return []; - } - let text_node_type: NodeType; - if (ref_node.isText) { - text_node_type = ref_node.type; + const pos = this.view.state.selection.$from; + const ref_node: ProsNode = this.reference_node(pos); + if (ref_node !== null && ref_node !== this.view.state.doc) { + let text_node_type: NodeType; + if (ref_node.isText) { + text_node_type = ref_node.type; + } + else { + return []; + } + + this._activeMarks = ref_node.marks; + + activeMarks = markGroup.filter(mark_type => { + if (dispatch) { + let mark = state.schema.mark(mark_type); + return ref_node.marks.includes(mark); + } + return false; + }); + return activeMarks; } else { return []; } - activeMarks = markGroup.filter(mark_type => { - if (dispatch) { - let mark = state.schema.mark(mark_type); - return ref_node.marks.includes(mark); - } - return false; - }); } - return activeMarks; + } + + reference_node(pos: ResolvedPos): ProsNode { + let ref_node: ProsNode = this.view.state.doc; + if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) { + ref_node = pos.nodeAfter; + } + else if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) { + ref_node = pos.nodeBefore; + } + else if (pos.pos > 0) { + let skip = false; + for (let i: number = pos.pos - 1; i > 0; i--) { + this.view.state.doc.nodesBetween(i, pos.pos, (node: ProsNode, pos: number, parent: ProsNode, index: number) => { + if (node.isLeaf && !skip) { + ref_node = node; + skip = true; + } + + }); + } + } + return ref_node; } destroy() { this.tooltip.remove(); } -- cgit v1.2.3-70-g09d2 From 219cabb3fe42ab199550efc3423b7aaed4e1ee93 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 24 Jun 2019 22:45:19 -0400 Subject: Switched shift drag of tabs to normal drag and added drag target for document drag --- src/client/util/DragManager.ts | 4 ++- .../views/collections/CollectionDockingView.tsx | 41 +++++++++++++++------- 2 files changed, 32 insertions(+), 13 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c3c92daa5..b707dbe57 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -90,6 +90,8 @@ export namespace DragManager { handlers: DragHandlers; hideSource: boolean | (() => boolean); + + withoutShiftDrag?: boolean; } export interface DragDropDisposer { @@ -312,7 +314,7 @@ export namespace DragManager { if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey || e.altKey ? "alias" : undefined; } - if (e.shiftKey && CollectionDockingView.Instance) { + if (((options && !options.withoutShiftDrag) || !options) && e.shiftKey && CollectionDockingView.Instance) { AbortDrag(); CollectionDockingView.Instance.StartOtherDrag(docs, { pageX: e.pageX, diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 5f8862c43..f1473139c 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -24,6 +24,10 @@ import { SubCollectionViewProps } from "./CollectionSubView"; import { ParentDocSelector } from './ParentDocumentSelector'; import React = require("react"); import { MainView } from '../MainView'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faFile } from '@fortawesome/free-solid-svg-icons'; +library.add(faFile); @observer export class CollectionDockingView extends React.Component { @@ -261,9 +265,9 @@ export class CollectionDockingView extends React.Component { this._isPointerDown = true; let onPointerUp = action(() => { - window.removeEventListener("pointerup", onPointerUp) - this._isPointerDown = false - }) + window.removeEventListener("pointerup", onPointerUp); + this._isPointerDown = false; + }); window.addEventListener("pointerup", onPointerUp); var className = (e.target as any).className; if (className === "messageCounter") { @@ -276,7 +280,7 @@ export class CollectionDockingView extends React.Component) => (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc))); } else - if ((className === "lm_title" || className === "lm_tab lm_active") && !e.shiftKey) { + if ((className === "lm_title" || className === "lm_tab lm_active") && e.shiftKey) { e.stopPropagation(); e.preventDefault(); let x = e.clientX; @@ -294,7 +298,8 @@ export class CollectionDockingView extends React.Component { if (doc instanceof Doc) { - let counter: any = this.htmlToElement(`0
`); - tab.element.append(counter); + let dragSpan = document.createElement("span"); + dragSpan.style.position = "relative"; + dragSpan.style.bottom = "6px"; + dragSpan.style.paddingLeft = "4px"; + dragSpan.style.paddingRight = "2px"; let upDiv = document.createElement("span"); const stack = tab.contentItem.parent; // shifts the focus to this tab when another tab is dragged over it @@ -354,15 +362,24 @@ export class CollectionDockingView extends React.Component CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); - tab.reactComponents = [upDiv]; + ReactDOM.render( { + e.preventDefault(); + e.stopPropagation(); + DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, { + handlers: { dragComplete: emptyFunction }, + hideSource: false + }); + }}>, dragSpan); + ReactDOM.render( CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); + tab.reactComponents = [dragSpan, upDiv]; + tab.element.append(dragSpan); tab.element.append(upDiv); - counter.DashDocId = tab.contentItem.config.props.documentId; - tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title], + tab.reactionDisposer = reaction(() => [doc.title], () => { - counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length; tab.titleElement[0].textContent = doc.title; }, { fireImmediately: true }); + //TODO why can't this just be doc instead of the id? tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; } }); -- cgit v1.2.3-70-g09d2 From 45ded4da2592bc2a8201e1260dfb6a80485684c7 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 25 Jun 2019 09:35:09 -0400 Subject: converted isTopMost into renderDepth and capped renderDepth at 7. removed previous cycle detection. --- src/client/documents/Documents.ts | 6 +-- src/client/util/DragManager.ts | 2 - src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainOverlayTextBox.tsx | 2 +- src/client/views/MainView.tsx | 2 +- .../views/collections/CollectionBaseView.tsx | 48 ++++------------------ .../views/collections/CollectionDockingView.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 8 ++-- src/client/views/nodes/DocumentContentsView.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 11 ++--- src/client/views/nodes/FieldView.tsx | 4 +- src/client/views/nodes/KeyValuePair.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 2 +- 14 files changed, 31 insertions(+), 65 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b04fc401a..ac8a892b8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -344,7 +344,7 @@ export namespace Docs { {layout}
- +
`); @@ -356,7 +356,7 @@ export namespace Docs { {layout}
- +
`); @@ -379,7 +379,7 @@ export namespace Docs { {layout}
- +
`); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 3143924fc..442187912 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -28,7 +28,6 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () document.removeEventListener('pointerup', onRowUp); }; let onItemDown = async (e: React.PointerEvent) => { - // if (this.props.isSelected() || this.props.isTopMost) { if (e.button === 0) { e.stopPropagation(); if (e.shiftKey && CollectionDockingView.Instance) { @@ -38,7 +37,6 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () document.addEventListener("pointerup", onRowUp); } } - //} }; return onItemDown; } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 9d5844426..042306997 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -153,7 +153,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @computed get Bounds(): { x: number, y: number, b: number, r: number } { return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => { - if (documentView.props.isTopMost) { + if (documentView.props.renderDepth === 0) { return bounds; } let transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse(); diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index b2cfc82c4..7363b08ef 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -123,7 +123,7 @@ export class MainOverlayTextBox extends React.Component this._dominus = dominus} />
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a72f25b99..d26e24748 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -211,7 +211,7 @@ export class MainView extends React.Component { ContentScaling={returnOne} PanelWidth={this.getPWidth} PanelHeight={this.getPHeight} - isTopMost={true} + renderDepth={0} selectOnLoad={false} focus={emptyFunction} parentActive={returnTrue} diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 79a9f3be0..50d1a5071 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -64,8 +64,7 @@ export class CollectionBaseView extends React.Component { active = (): boolean => { var isSelected = this.props.isSelected(); - var topMost = this.props.isTopMost; - return isSelected || this._isChildActive || topMost; + return isSelected || this._isChildActive || this.props.renderDepth === 0; } //TODO should this be observable? @@ -75,32 +74,6 @@ export class CollectionBaseView extends React.Component { this.props.whenActiveChanged(isActive); } - createsCycle(documentToAdd: Doc, containerDocument: Doc): boolean { - if (!(documentToAdd instanceof Doc)) { - return false; - } - if (StrCast(documentToAdd.layout).indexOf("CollectionView") !== -1) { - let data = DocListCast(documentToAdd.data); - for (const doc of data) { - if (this.createsCycle(doc, containerDocument)) { - return true; - } - } - } - let annots = DocListCast(documentToAdd.annotations); - for (const annot of annots) { - if (this.createsCycle(annot, containerDocument)) { - return true; - } - } - for (let containerProto: Opt = containerDocument; containerProto !== undefined; containerProto = FieldValue(containerProto.proto)) { - if (containerProto[Id] === documentToAdd[Id]) { - return true; - } - } - return false; - } - @action.bound addDocument(doc: Doc, allowDuplicates: boolean = false): boolean { var curPage = NumCast(this.props.Document.curPage, -1); @@ -109,19 +82,16 @@ export class CollectionBaseView extends React.Component { Doc.GetProto(doc).annotationOn = this.props.Document; } allowDuplicates = true; - if (!this.createsCycle(doc, this.dataDoc)) { - //TODO This won't create the field if it doesn't already exist - const value = Cast(this.dataDoc[this.props.fieldKey], listSpec(Doc)); - if (value !== undefined) { - if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) { - value.push(doc); - } - } else { - Doc.SetOnPrototype(this.dataDoc, this.props.fieldKey, new List([doc])); + //TODO This won't create the field if it doesn't already exist + const value = Cast(this.dataDoc[this.props.fieldKey], listSpec(Doc)); + if (value !== undefined) { + if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) { + value.push(doc); } - return true; + } else { + Doc.SetOnPrototype(this.dataDoc, this.props.fieldKey, new List([doc])); } - return false; + return true; } @action.bound diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 82cb5dbbb..6d2f61173 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -535,7 +535,7 @@ export class DockedFrameRenderer extends React.Component { PanelWidth={this.nativeWidth} PanelHeight={this.nativeHeight} ScreenToLocalTransform={this.ScreenToLocalTransform} - isTopMost={true} + renderDepth={0} selectOnLoad={false} parentActive={returnTrue} whenActiveChanged={emptyFunction} diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b4158a5b1..f03afe7e0 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -104,7 +104,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { ContainingCollectionView: this.props.CollectionView, isSelected: returnFalse, select: emptyFunction, - isTopMost: false, + renderDepth: this.props.renderDepth + 1, selectOnLoad: false, ScreenToLocalTransform: Transform.Identity, focus: emptyFunction, @@ -454,7 +454,7 @@ export class CollectionSchemaPreview extends React.Component 0) { return; } e.stopPropagation(); @@ -303,7 +303,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { removeDocument: this.props.removeDocument, moveDocument: this.props.moveDocument, ScreenToLocalTransform: this.getTransform, - isTopMost: false, + renderDepth: this.props.renderDepth + 1, selectOnLoad: layoutDoc[Id] === this._selectOnLoaded, PanelWidth: layoutDoc[WidthSym], PanelHeight: layoutDoc[HeightSym], @@ -404,7 +404,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { class CollectionFreeFormOverlayView extends React.Component boolean }> { @computed get overlayView() { return (); + renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />); } render() { return this.overlayView; @@ -416,7 +416,7 @@ class CollectionFreeFormBackgroundView extends React.Component); + renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />); } render() { return this.props.Document.backgroundLayout ? this.backgroundView : (null); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index a4316c143..0da4888a1 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -103,6 +103,7 @@ export class DocumentContentsView extends React.Component 7) return (null); if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null); return boolean; moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; ScreenToLocalTransform: () => Transform; - isTopMost: boolean; + renderDepth: number; ContentScaling: () => number; PanelWidth: () => number; PanelHeight: () => number; @@ -120,7 +120,7 @@ export class DocumentView extends DocComponent(Docu public get ContentDiv() { return this._mainCont.current; } @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); } - @computed get topMost(): boolean { return this.props.isTopMost; } + @computed get topMost(): boolean { return this.props.renderDepth === 0; } @computed get templates(): List { let field = this.props.Document.templates; if (field && field instanceof List) { @@ -268,7 +268,7 @@ export class DocumentView extends DocComponent(Docu e.stopPropagation(); let altKey = e.altKey; let ctrlKey = e.ctrlKey; - if (this._doubleTap && !this.props.isTopMost) { + if (this._doubleTap && !this.props.renderDepth) { this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"); SelectionManager.DeselectAll(); this.props.Document.libraryBrush = false; @@ -436,7 +436,6 @@ export class DocumentView extends DocComponent(Docu @action addTemplate = (template: Template) => { this.templates.push(template.Layout); - this.templates = this.templates; } @action @@ -447,7 +446,6 @@ export class DocumentView extends DocComponent(Docu break; } } - this.templates = this.templates; } freezeNativeDimensions = (): void => { @@ -549,7 +547,7 @@ export class DocumentView extends DocComponent(Docu var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return ( -
(Docu transform: `scale(${scaling}, ${scaling})` }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} - onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} > {this.contents} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 8879da55f..374a523cb 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -33,7 +33,7 @@ export interface FieldViewProps { DataDoc: Doc; isSelected: () => boolean; select: (isCtrlPressed: boolean) => void; - isTopMost: boolean; + renderDepth: number; selectOnLoad: boolean; addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean; addDocTab: (document: Doc, dataDoc: Doc, where: string) => void; @@ -97,7 +97,7 @@ export class FieldView extends React.Component { // ContentScaling={returnOne} // PanelWidth={returnHundred} // PanelHeight={returnHundred} - // isTopMost={true} //TODO Why is this top most? + // renderDepth={0} //TODO Why is renderDepth reset? // selectOnLoad={false} // focus={emptyFunction} // isSelected={this.props.isSelected} diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 3e03e7e75..3dda81db7 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -32,7 +32,7 @@ export class KeyValuePair extends React.Component { fieldKey: this.props.keyName, isSelected: returnFalse, select: emptyFunction, - isTopMost: false, + renderDepth: 1, selectOnLoad: false, active: returnFalse, whenActiveChanged: emptyFunction, diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index b9e93b4da..10c66d318 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -492,7 +492,7 @@ interface IAnnotationProps { // 1} // PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)} // PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)} -- cgit v1.2.3-70-g09d2 From c2c04325e0f29c01cd5ab0617d17a62183a53611 Mon Sep 17 00:00:00 2001 From: ab Date: Tue, 25 Jun 2019 14:00:42 -0400 Subject: fixed most stuff, still the zooming and focus bugs --- src/client/util/RichTextSchema.tsx | 1 + src/client/util/TooltipTextMenu.tsx | 54 ++++++++++------------ .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- 3 files changed, 26 insertions(+), 31 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 936dcbfd6..63c879d67 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -509,6 +509,7 @@ export class SummarizedView { let newSelection = TextSelection.create(view.state.doc, y + 1, y + 1 + length); // update attrs of node node.attrs.text = newSelection.content(); + node.attrs.textslice = newSelection.content().toJSON(); view.dispatch(view.state.tr.setSelection(newSelection).deleteSelection(view.state, () => { })); self._collapsed.textContent = "㊉"; } else { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index c42d5fab8..d08048e21 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -84,7 +84,7 @@ export class TooltipTextMenu { { command: toggleMark(schema.marks.strikethrough), dom: this.icon("S", "strikethrough", "Strikethrough") }, { command: toggleMark(schema.marks.superscript), dom: this.icon("s", "superscript", "Superscript") }, { command: toggleMark(schema.marks.subscript), dom: this.icon("s", "subscript", "Subscript") }, - { command: deleteSelection, dom: this.icon("C", 'collapse', 'Collapse') } + { command: toggleMark(schema.marks.highlight), dom: this.icon("H", 'blue', 'Blue') } // { command: wrapInList(schema.nodes.bullet_list), dom: this.icon(":", "bullets") }, // { command: wrapInList(schema.nodes.ordered_list), dom: this.icon("1)", "bullets") }, // { command: lift, dom: this.icon("<", "lift") }, @@ -144,6 +144,8 @@ export class TooltipTextMenu { this.tooltip.appendChild(this.createStar().render(this.view).dom); + this.updateListItemDropdown(":", this.listTypeBtnDom); + this.update(view, undefined); //view.dom.parentNode!.parentNode!.insertBefore(this.tooltip, view.dom.parentNode); @@ -166,12 +168,15 @@ export class TooltipTextMenu { fontSizeBtns.push(this.dropdownMarkBtn(String(number), "color: black; width: 50px;", mark, this.view, this.changeToMarkInGroup, this.fontSizes)); }); - if (this.fontSizeDom) { this.tooltip.removeChild(this.fontSizeDom); } - this.fontSizeDom = (new Dropdown(cut(fontSizeBtns), { + let newfontSizeDom = (new Dropdown(cut(fontSizeBtns), { label: label, css: "color:black; min-width: 60px; padding-left: 5px; margin-right: 0;" }) as MenuItem).render(this.view).dom; - this.tooltip.appendChild(this.fontSizeDom); + if (this.fontSizeDom) { this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom); } + else { + this.tooltip.appendChild(newfontSizeDom); + } + this.fontSizeDom = newfontSizeDom; } //label of dropdown will change to given label @@ -185,13 +190,16 @@ export class TooltipTextMenu { fontBtns.push(this.dropdownMarkBtn(name, "color: black; font-family: " + name + ", sans-serif; width: 125px;", mark, this.view, this.changeToMarkInGroup, this.fontStyles)); }); - if (this.fontStyleDom) { this.tooltip.removeChild(this.fontStyleDom); } - this.fontStyleDom = (new Dropdown(cut(fontBtns), { + let newfontStyleDom = (new Dropdown(cut(fontBtns), { label: label, css: "color:black; width: 125px; margin-left: -3px; padding-left: 2px;" }) as MenuItem).render(this.view).dom; + if (this.fontStyleDom) { this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom); } + else { + this.tooltip.appendChild(newfontStyleDom); + } + this.fontStyleDom = newfontStyleDom; - this.tooltip.appendChild(this.fontStyleDom); } updateLinkMenu() { @@ -286,19 +294,6 @@ export class TooltipTextMenu { } insertStar(state: EditorState, dispatch: any) { - if (state.selection.empty) { - let mark = state.schema.mark(state.schema.marks.highlight) - if (this._activeMarks.includes(mark)) { - const ind = this._activeMarks.indexOf(mark); - this._activeMarks.splice(ind, 1); - } - else { - this._activeMarks.push(mark); - } - dispatch(state.tr.insertText(" ").setStoredMarks(this._activeMarks)); - //dispatch(state.tr.setStoredMarks(this._activeMarks)); - return true; - } let newNode = schema.nodes.star.create({ visibility: false, text: state.selection.content(), textslice: state.selection.content().toJSON(), textlen: state.selection.to - state.selection.from }); if (dispatch) { //console.log(newNode.attrs.text.toString()); @@ -359,15 +354,14 @@ export class TooltipTextMenu { } }); // fontsize - // if (markType.name[0] === 'p') { - // let size = this.fontSizeToNum.get(markType); - // if (size) { this.updateFontSizeDropdown(String(size) + " pt"); } - // } - // else { - // let fontName = this.fontStylesToName.get(markType); - // if (fontName) { this.updateFontStyleDropdown(fontName); } - // } - //this.update(view, undefined); + if (markType.name[0] === 'p') { + let size = this.fontSizeToNum.get(markType); + if (size) { this.updateFontSizeDropdown(String(size) + " pt"); } + } + else { + let fontName = this.fontStylesToName.get(markType); + if (fontName) { this.updateFontStyleDropdown(fontName); } + } //actually apply font return toggleMark(markType)(view.state, view.dispatch, view); } @@ -558,7 +552,7 @@ export class TooltipTextMenu { let { from, to } = state.selection; //UPDATE LIST ITEM DROPDOWN - this.listTypeBtnDom = this.updateListItemDropdown(":", this.listTypeBtnDom!); + //this.listTypeBtnDom = this.updateListItemDropdown(":", this.listTypeBtnDom!); //this._activeMarks = []; //UPDATE FONT STYLE DROPDOWN diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 796ff029c..d2a6ceafa 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -95,7 +95,7 @@ export class MarqueeView extends React.Component } }); } else if (!e.ctrlKey) { - let newBox = Docs.TextDocument({ width: 200, height: 50, x: x, y: y, title: "-typed text-" }); + let newBox = Docs.TextDocument({ width: 200, height: 100, x: x, y: y, title: "-typed text-" }); newBox.proto!.autoHeight = true; this.props.addLiveTextDocument(newBox); } -- cgit v1.2.3-70-g09d2 From 76e444f3016fa2d550f664520ee27ead3eb39837 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 25 Jun 2019 14:45:46 -0400 Subject: fixed tooltip appearing. --- src/client/util/TooltipTextMenu.tsx | 4 +++- src/client/views/MainOverlayTextBox.tsx | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index c42d5fab8..5ff244b5f 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -671,5 +671,7 @@ export class TooltipTextMenu { return ref_node; } - destroy() { this.tooltip.remove(); } + destroy() { + this.tooltip.remove(); + } } diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 4488d1046..58946845c 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -24,13 +24,18 @@ export class MainOverlayTextBox extends React.Component private _textHideOnLeave?: boolean; private _textTargetDiv: HTMLDivElement | undefined; private _textProxyDiv: React.RefObject; - private _outerdiv = (dominus: HTMLElement | null) => this._dominus && dominus && dominus.appendChild(this._dominus); private _textBottom: boolean | undefined; private _textAutoHeight: boolean | undefined; + private _setouterdiv = (outerdiv: HTMLElement | null) => { this._outerdiv = outerdiv; this.updateTooltip(); } + private _outerdiv: HTMLElement | null = null; private _textBox: FormattedTextBox | undefined; - private _dominus?: HTMLElement; + private _tooltip?: HTMLElement; @observable public TextDoc?: Doc; + updateTooltip = () => { + this._outerdiv && this._tooltip && !this._outerdiv.contains(this._tooltip) && this._outerdiv.appendChild(this._tooltip) + } + constructor(props: MainOverlayTextBoxProps) { super(props); this._textProxyDiv = React.createRef(); @@ -116,14 +121,14 @@ export class MainOverlayTextBox extends React.Component let s = this._textXf().Scale; let location = this._textBottom ? textRect.bottom : textRect.top; let hgt = this._textAutoHeight || this._textBottom ? "auto" : this._textTargetDiv.clientHeight; - return
+ return
this._dominus = dominus} /> + ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyFunction} addDocTab={this.addDocTab} outer_div={(tooltip: HTMLElement) => { this._tooltip = tooltip; this.updateTooltip(); }} />
-- 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/client/util') 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 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/client/util') 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/client/util') 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 06bd486c72342b4d979245c9f4051156e6492541 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 25 Jun 2019 21:22:29 -0400 Subject: scraping progress --- src/buxton/scraper.py | 331 --------------------- src/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx | Bin 1675500 -> 0 bytes src/buxton/source/Bill_Notes_Braun_T3.docx | Bin 1671968 -> 0 bytes src/buxton/source/Bill_Notes_CasioC801.docx | Bin 574664 -> 0 bytes src/buxton/source/Bill_Notes_Casio_Mini.docx | Bin 581069 -> 0 bytes .../source/Bill_Notes_FingerWorks_Prototype.docx | Bin 585090 -> 0 bytes .../source/Bill_Notes_Fingerworks_TouchStream.docx | Bin 1722555 -> 0 bytes src/buxton/source/Bill_Notes_FrogPad.docx | Bin 840173 -> 0 bytes src/buxton/source/Bill_Notes_Gavilan_SC.docx | Bin 1695290 -> 0 bytes .../source/Bill_Notes_Grandjean_Stenotype.docx | Bin 2094142 -> 0 bytes src/buxton/source/Bill_Notes_Matias.docx | Bin 590407 -> 0 bytes src/buxton/source/Bill_Notes_MousePen.docx | Bin 505322 -> 0 bytes src/buxton/source/Bill_Notes_NewO.docx | Bin 2264571 -> 0 bytes src/buxton/source/Bill_Notes_OLPC.docx | Bin 6883659 -> 0 bytes src/buxton/source/Bill_Notes_PARCkbd.docx | Bin 631959 -> 0 bytes .../source/Bill_Notes_Philco_Mystery_Control.docx | Bin 1994439 -> 0 bytes src/buxton/source/Bill_Notes_TASA_Kbd.docx | Bin 461199 -> 0 bytes src/buxton/source/Bill_Notes_The_Tap.docx | Bin 711321 -> 0 bytes src/client/util/ClientUtils.ts | 4 + src/scraping/acm/chromedriver.exe | Bin 0 -> 7477760 bytes src/scraping/acm/citations.txt | 2 + src/scraping/acm/debug.log | 38 +++ src/scraping/acm/index.js | 88 ++++++ src/scraping/acm/package.json | 17 ++ src/scraping/acm/results.txt | 64 ++++ src/scraping/buxton/scraper.py | 331 +++++++++++++++++++++ .../buxton/source/Bill_Notes_Bill_Notes_CyKey.docx | Bin 0 -> 1675500 bytes .../buxton/source/Bill_Notes_Braun_T3.docx | Bin 0 -> 1671968 bytes .../buxton/source/Bill_Notes_CasioC801.docx | Bin 0 -> 574664 bytes .../buxton/source/Bill_Notes_Casio_Mini.docx | Bin 0 -> 581069 bytes .../source/Bill_Notes_FingerWorks_Prototype.docx | Bin 0 -> 585090 bytes .../source/Bill_Notes_Fingerworks_TouchStream.docx | Bin 0 -> 1722555 bytes src/scraping/buxton/source/Bill_Notes_FrogPad.docx | Bin 0 -> 840173 bytes .../buxton/source/Bill_Notes_Gavilan_SC.docx | Bin 0 -> 1695290 bytes .../source/Bill_Notes_Grandjean_Stenotype.docx | Bin 0 -> 2094142 bytes src/scraping/buxton/source/Bill_Notes_Matias.docx | Bin 0 -> 590407 bytes .../buxton/source/Bill_Notes_MousePen.docx | Bin 0 -> 505322 bytes src/scraping/buxton/source/Bill_Notes_NewO.docx | Bin 0 -> 2264571 bytes src/scraping/buxton/source/Bill_Notes_OLPC.docx | Bin 0 -> 6883659 bytes src/scraping/buxton/source/Bill_Notes_PARCkbd.docx | Bin 0 -> 631959 bytes .../source/Bill_Notes_Philco_Mystery_Control.docx | Bin 0 -> 1994439 bytes .../buxton/source/Bill_Notes_TASA_Kbd.docx | Bin 0 -> 461199 bytes src/scraping/buxton/source/Bill_Notes_The_Tap.docx | Bin 0 -> 711321 bytes 43 files changed, 544 insertions(+), 331 deletions(-) delete mode 100644 src/buxton/scraper.py delete mode 100644 src/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx delete mode 100644 src/buxton/source/Bill_Notes_Braun_T3.docx delete mode 100644 src/buxton/source/Bill_Notes_CasioC801.docx delete mode 100644 src/buxton/source/Bill_Notes_Casio_Mini.docx delete mode 100644 src/buxton/source/Bill_Notes_FingerWorks_Prototype.docx delete mode 100644 src/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx delete mode 100644 src/buxton/source/Bill_Notes_FrogPad.docx delete mode 100644 src/buxton/source/Bill_Notes_Gavilan_SC.docx delete mode 100644 src/buxton/source/Bill_Notes_Grandjean_Stenotype.docx delete mode 100644 src/buxton/source/Bill_Notes_Matias.docx delete mode 100644 src/buxton/source/Bill_Notes_MousePen.docx delete mode 100644 src/buxton/source/Bill_Notes_NewO.docx delete mode 100644 src/buxton/source/Bill_Notes_OLPC.docx delete mode 100644 src/buxton/source/Bill_Notes_PARCkbd.docx delete mode 100644 src/buxton/source/Bill_Notes_Philco_Mystery_Control.docx delete mode 100644 src/buxton/source/Bill_Notes_TASA_Kbd.docx delete mode 100644 src/buxton/source/Bill_Notes_The_Tap.docx create mode 100644 src/client/util/ClientUtils.ts create mode 100644 src/scraping/acm/chromedriver.exe create mode 100644 src/scraping/acm/citations.txt create mode 100644 src/scraping/acm/debug.log create mode 100644 src/scraping/acm/index.js create mode 100644 src/scraping/acm/package.json create mode 100644 src/scraping/acm/results.txt create mode 100644 src/scraping/buxton/scraper.py create mode 100644 src/scraping/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_Braun_T3.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_CasioC801.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_Casio_Mini.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_FingerWorks_Prototype.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_FrogPad.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_Gavilan_SC.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_Grandjean_Stenotype.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_Matias.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_MousePen.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_NewO.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_OLPC.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_PARCkbd.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_Philco_Mystery_Control.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_TASA_Kbd.docx create mode 100644 src/scraping/buxton/source/Bill_Notes_The_Tap.docx (limited to 'src/client/util') diff --git a/src/buxton/scraper.py b/src/buxton/scraper.py deleted file mode 100644 index 043fd3cf4..000000000 --- a/src/buxton/scraper.py +++ /dev/null @@ -1,331 +0,0 @@ -import os -import docx2txt -from docx import Document -from docx.opc.constants import RELATIONSHIP_TYPE as RT -import re -from pymongo import MongoClient -import shutil -import uuid -import datetime -from PIL import Image -import math -import sys - -source = "./source" -dist = "../server/public/files" - -db = MongoClient("localhost", 27017)["Dash"] -schema_guids = [] - - -def extract_links(fileName): - links = [] - doc = Document(fileName) - rels = doc.part.rels - for rel in rels: - item = rels[rel] - if item.reltype == RT.HYPERLINK and ".aspx" not in item._target: - links.append(item._target) - return listify(links) - - -def extract_value(kv_string): - pieces = kv_string.split(":") - return (pieces[1] if len(pieces) > 1 else kv_string).strip() - - -def mkdir_if_absent(path): - try: - if not os.path.exists(path): - os.mkdir(path) - except OSError: - print("failed to create the appropriate directory structures for %s" % file_name) - - -def guid(): - return str(uuid.uuid4()) - - -def listify(list): - return { - "fields": list, - "__type": "list" - } - - -def protofy(fieldId): - return { - "fieldId": fieldId, - "__type": "proxy" - } - - -def write_schema(parse_results, display_fields): - view_guids = parse_results["child_guids"] - - data_doc = parse_results["schema"] - fields = data_doc["fields"] - - view_doc_guid = guid() - - view_doc = { - "_id": view_doc_guid, - "fields": { - "proto": protofy(data_doc["_id"]), - "x": 10, - "y": 10, - "width": 900, - "height": 600, - "panX": 0, - "panY": 0, - "zoomBasis": 0.5, - "zIndex": 2, - "libraryBrush": False, - "viewType": 2 - }, - "__type": "Doc" - } - - fields["proto"] = protofy("collectionProto") - fields["data"] = listify(proxify_guids(view_guids)) - fields["schemaColumns"] = listify(display_fields) - fields["backgroundColor"] = "white" - fields["scale"] = 0.5 - fields["viewType"] = 2 - fields["author"] = "Bill Buxton" - fields["creationDate"] = { - "date": datetime.datetime.utcnow().microsecond, - "__type": "date" - } - fields["isPrototype"] = True - fields["page"] = -1 - - db.newDocuments.insert_one(data_doc) - db.newDocuments.insert_one(view_doc) - - data_doc_guid = data_doc["_id"] - print(f"inserted view document ({view_doc_guid})") - print(f"inserted data document ({data_doc_guid})\n") - - return view_doc_guid - - -def write_image(folder, name): - path = f"http://localhost:1050/files/{folder}/{name}" - - data_doc_guid = guid() - view_doc_guid = guid() - - view_doc = { - "_id": view_doc_guid, - "fields": { - "proto": protofy(data_doc_guid), - "x": 10, - "y": 10, - "width": 300, - "zIndex": 2, - "libraryBrush": False - }, - "__type": "Doc" - } - - image = Image.open(f"{dist}/{folder}/{name}") - native_width, native_height = image.size - - data_doc = { - "_id": data_doc_guid, - "fields": { - "proto": protofy("imageProto"), - "data": { - "url": path, - "__type": "image" - }, - "title": name, - "nativeWidth": native_width, - "author": "Bill Buxton", - "creationDate": { - "date": datetime.datetime.utcnow().microsecond, - "__type": "date" - }, - "isPrototype": True, - "page": -1, - "nativeHeight": native_height, - "height": native_height - }, - "__type": "Doc" - } - - db.newDocuments.insert_one(view_doc) - db.newDocuments.insert_one(data_doc) - - return view_doc_guid - - -def parse_document(file_name: str): - print(f"parsing {file_name}...") - pure_name = file_name.split(".")[0] - - result = {} - - dir_path = dist + "/" + pure_name - mkdir_if_absent(dir_path) - - raw = str(docx2txt.process(source + "/" + file_name, dir_path)) - - view_guids = [] - count = 0 - for image in os.listdir(dir_path): - count += 1 - view_guids.append(write_image(pure_name, image)) - os.rename(dir_path + "/" + image, dir_path + - "/" + image.replace(".", "_m.", 1)) - print(f"extracted {count} images...") - - def sanitize(line): return re.sub("[\n\t]+", "", line).replace(u"\u00A0", " ").replace( - u"\u2013", "-").replace(u"\u201c", '''"''').replace(u"\u201d", '''"''').strip() - - def sanitize_price(raw: str): - raw = raw.replace(",", "") - start = raw.find("$") - if start > -1: - i = start + 1 - while (i < len(raw) and re.match(r"[0-9\.]", raw[i])): - i += 1 - price = raw[start + 1: i + 1] - return float(price) - elif (raw.lower().find("nfs")): - return -1 - else: - return math.nan - - def remove_empty(line): return len(line) > 1 - - lines = list(map(sanitize, raw.split("\n"))) - lines = list(filter(remove_empty, lines)) - - result["file_name"] = file_name - result["title"] = lines[2].strip() - result["short_description"] = lines[3].strip().replace( - "Short Description: ", "") - - cur = 5 - notes = "" - while lines[cur] != "Device Details": - notes += lines[cur] + " " - cur += 1 - result["buxton_notes"] = notes.strip() - - cur += 1 - clean = list( - map(lambda data: data.strip().split(":"), lines[cur].split("|"))) - result["company"] = clean[0][len(clean[0]) - 1].strip() - result["year"] = clean[1][len(clean[1]) - 1].strip() - result["original_price"] = sanitize_price( - clean[2][len(clean[2]) - 1].strip()) - - cur += 1 - result["degrees_of_freedom"] = extract_value( - lines[cur]).replace("NA", "N/A") - cur += 1 - - dimensions = lines[cur].lower() - if dimensions.startswith("dimensions"): - dim_concat = dimensions[11:].strip() - cur += 1 - while lines[cur] != "Key Words": - dim_concat += (" " + lines[cur].strip()) - cur += 1 - result["dimensions"] = dim_concat - else: - result["dimensions"] = "N/A" - - cur += 1 - result["primary_key"] = extract_value(lines[cur]) - cur += 1 - result["secondary_key"] = extract_value(lines[cur]) - - while lines[cur] != "Links": - result["secondary_key"] += (" " + extract_value(lines[cur]).strip()) - cur += 1 - - cur += 1 - link_descriptions = [] - while lines[cur] != "Image": - link_descriptions.append(lines[cur].strip()) - cur += 1 - result["link_descriptions"] = listify(link_descriptions) - - result["hyperlinks"] = extract_links(source + "/" + file_name) - - images = [] - captions = [] - cur += 3 - while cur + 1 < len(lines) and lines[cur] != "NOTES:": - images.append(lines[cur]) - captions.append(lines[cur + 1]) - cur += 2 - result["images"] = listify(images) - result["captions"] = listify(captions) - - notes = [] - if (cur < len(lines) and lines[cur] == "NOTES:"): - cur += 1 - while cur < len(lines): - notes.append(lines[cur]) - cur += 1 - if len(notes) > 0: - result["notes"] = listify(notes) - - print("writing child schema...") - - return { - "schema": { - "_id": guid(), - "fields": result, - "__type": "Doc" - }, - "child_guids": view_guids - } - - -def proxify_guids(guids): - return list(map(lambda guid: {"fieldId": guid, "__type": "proxy"}, guids)) - - -if os.path.exists(dist): - shutil.rmtree(dist) -while os.path.exists(dist): - pass -os.mkdir(dist) -mkdir_if_absent(source) - -candidates = 0 -for file_name in os.listdir(source): - if file_name.endswith('.docx'): - candidates += 1 - schema_guids.append(write_schema( - parse_document(file_name), ["title", "data"])) - -print("writing parent schema...") -parent_guid = write_schema({ - "schema": { - "_id": guid(), - "fields": {}, - "__type": "Doc" - }, - "child_guids": schema_guids -}, ["title", "short_description", "original_price"]) - -print("appending parent schema to main workspace...\n") -db.newDocuments.update_one( - {"fields.title": "WS collection 1"}, - {"$push": {"fields.data.fields": {"fieldId": parent_guid, "__type": "proxy"}}} -) - -print("rewriting .gitignore...\n") -lines = ['*', '!.gitignore'] -with open(dist + "/.gitignore", 'w') as f: - f.write('\n'.join(lines)) - -suffix = "" if candidates == 1 else "s" -print(f"conversion complete. {candidates} candidate{suffix} processed.") diff --git a/src/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx b/src/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx deleted file mode 100644 index 06094b4d3..000000000 Binary files a/src/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_Braun_T3.docx b/src/buxton/source/Bill_Notes_Braun_T3.docx deleted file mode 100644 index 356697092..000000000 Binary files a/src/buxton/source/Bill_Notes_Braun_T3.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_CasioC801.docx b/src/buxton/source/Bill_Notes_CasioC801.docx deleted file mode 100644 index cd89fb97b..000000000 Binary files a/src/buxton/source/Bill_Notes_CasioC801.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_Casio_Mini.docx b/src/buxton/source/Bill_Notes_Casio_Mini.docx deleted file mode 100644 index a503cddfc..000000000 Binary files a/src/buxton/source/Bill_Notes_Casio_Mini.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_FingerWorks_Prototype.docx b/src/buxton/source/Bill_Notes_FingerWorks_Prototype.docx deleted file mode 100644 index 4d13a8cf5..000000000 Binary files a/src/buxton/source/Bill_Notes_FingerWorks_Prototype.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx b/src/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx deleted file mode 100644 index 578a1be08..000000000 Binary files a/src/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_FrogPad.docx b/src/buxton/source/Bill_Notes_FrogPad.docx deleted file mode 100644 index d01e1bf5c..000000000 Binary files a/src/buxton/source/Bill_Notes_FrogPad.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_Gavilan_SC.docx b/src/buxton/source/Bill_Notes_Gavilan_SC.docx deleted file mode 100644 index 7bd28b376..000000000 Binary files a/src/buxton/source/Bill_Notes_Gavilan_SC.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_Grandjean_Stenotype.docx b/src/buxton/source/Bill_Notes_Grandjean_Stenotype.docx deleted file mode 100644 index 0615c4953..000000000 Binary files a/src/buxton/source/Bill_Notes_Grandjean_Stenotype.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_Matias.docx b/src/buxton/source/Bill_Notes_Matias.docx deleted file mode 100644 index 547603256..000000000 Binary files a/src/buxton/source/Bill_Notes_Matias.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_MousePen.docx b/src/buxton/source/Bill_Notes_MousePen.docx deleted file mode 100644 index 4e1056636..000000000 Binary files a/src/buxton/source/Bill_Notes_MousePen.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_NewO.docx b/src/buxton/source/Bill_Notes_NewO.docx deleted file mode 100644 index a514926d2..000000000 Binary files a/src/buxton/source/Bill_Notes_NewO.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_OLPC.docx b/src/buxton/source/Bill_Notes_OLPC.docx deleted file mode 100644 index bfca0a9bb..000000000 Binary files a/src/buxton/source/Bill_Notes_OLPC.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_PARCkbd.docx b/src/buxton/source/Bill_Notes_PARCkbd.docx deleted file mode 100644 index c0cf6ba9a..000000000 Binary files a/src/buxton/source/Bill_Notes_PARCkbd.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_Philco_Mystery_Control.docx b/src/buxton/source/Bill_Notes_Philco_Mystery_Control.docx deleted file mode 100644 index ad06903f3..000000000 Binary files a/src/buxton/source/Bill_Notes_Philco_Mystery_Control.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_TASA_Kbd.docx b/src/buxton/source/Bill_Notes_TASA_Kbd.docx deleted file mode 100644 index e4c659de9..000000000 Binary files a/src/buxton/source/Bill_Notes_TASA_Kbd.docx and /dev/null differ diff --git a/src/buxton/source/Bill_Notes_The_Tap.docx b/src/buxton/source/Bill_Notes_The_Tap.docx deleted file mode 100644 index 8ceebc71e..000000000 Binary files a/src/buxton/source/Bill_Notes_The_Tap.docx and /dev/null differ diff --git a/src/client/util/ClientUtils.ts b/src/client/util/ClientUtils.ts new file mode 100644 index 000000000..425bde14a --- /dev/null +++ b/src/client/util/ClientUtils.ts @@ -0,0 +1,4 @@ +//AUTO-GENERATED FILE: DO NOT EDIT +export namespace ClientUtils { + export const RELEASE = false; +} \ No newline at end of file diff --git a/src/scraping/acm/chromedriver.exe b/src/scraping/acm/chromedriver.exe new file mode 100644 index 000000000..6a362fd43 Binary files /dev/null and b/src/scraping/acm/chromedriver.exe differ diff --git a/src/scraping/acm/citations.txt b/src/scraping/acm/citations.txt new file mode 100644 index 000000000..e5018ddef --- /dev/null +++ b/src/scraping/acm/citations.txt @@ -0,0 +1,2 @@ +321046 +2412979 \ No newline at end of file diff --git a/src/scraping/acm/debug.log b/src/scraping/acm/debug.log new file mode 100644 index 000000000..8c0a148f4 --- /dev/null +++ b/src/scraping/acm/debug.log @@ -0,0 +1,38 @@ +[0625/170004.768:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/170004.769:ERROR:exception_snapshot_win.cc(98)] thread ID 17604 not found in process +[0625/171124.644:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/171124.645:ERROR:exception_snapshot_win.cc(98)] thread ID 14348 not found in process +[0625/171853.989:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/171853.990:ERROR:exception_snapshot_win.cc(98)] thread ID 12080 not found in process +[0625/171947.744:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/171947.745:ERROR:exception_snapshot_win.cc(98)] thread ID 16160 not found in process +[0625/172007.424:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/172007.425:ERROR:exception_snapshot_win.cc(98)] thread ID 13472 not found in process +[0625/172059.353:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/172059.354:ERROR:exception_snapshot_win.cc(98)] thread ID 6396 not found in process +[0625/172402.795:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/172402.796:ERROR:exception_snapshot_win.cc(98)] thread ID 10720 not found in process +[0625/172618.850:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/172618.850:ERROR:exception_snapshot_win.cc(98)] thread ID 21136 not found in process +[0625/172819.875:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/172819.876:ERROR:exception_snapshot_win.cc(98)] thread ID 17624 not found in process +[0625/172953.674:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/172953.675:ERROR:exception_snapshot_win.cc(98)] thread ID 15180 not found in process +[0625/173412.182:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/173412.182:ERROR:exception_snapshot_win.cc(98)] thread ID 13952 not found in process +[0625/173447.806:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/173447.807:ERROR:exception_snapshot_win.cc(98)] thread ID 1572 not found in process +[0625/173516.188:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/173516.189:ERROR:exception_snapshot_win.cc(98)] thread ID 5472 not found in process +[0625/173528.446:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/173528.447:ERROR:exception_snapshot_win.cc(98)] thread ID 20420 not found in process +[0625/173539.436:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/173539.437:ERROR:exception_snapshot_win.cc(98)] thread ID 16192 not found in process +[0625/173643.139:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/173643.140:ERROR:exception_snapshot_win.cc(98)] thread ID 15716 not found in process +[0625/173659.376:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/173659.377:ERROR:exception_snapshot_win.cc(98)] thread ID 11828 not found in process +[0625/201137.209:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/201137.210:ERROR:exception_snapshot_win.cc(98)] thread ID 7688 not found in process +[0625/210240.476:ERROR:process_reader_win.cc(123)] NtOpenThread: {Access Denied} A process has requested access to an object, but has not been granted those access rights. (0xc0000022) +[0625/210240.477:ERROR:exception_snapshot_win.cc(98)] thread ID 20828 not found in process diff --git a/src/scraping/acm/index.js b/src/scraping/acm/index.js new file mode 100644 index 000000000..81f775617 --- /dev/null +++ b/src/scraping/acm/index.js @@ -0,0 +1,88 @@ +const { Builder, By, Key, until } = require('selenium-webdriver'); +const fs = require("fs"); + +let driver; +fs.readFile("./citations.txt", { encoding: "utf8" }, scrapeTargets); +results = [] + +async function scrapeTargets(error, data) { + if (error) { + console.log("\nUnable to collect target citations from a citations.txt file stored in this directory.\nPlease make sure one is provided.") + return; + } + + driver = await new Builder().forBrowser('chrome').build(); + + let references = data.split("\n").map(entry => entry.replace("\r", "")).filter(line => line.match(/\d+/g)); + + let results = [] + let pdfs = [] + for (let id of references) { + let result = {} + let lines = [] + try { + let url = `https://dl.acm.org/citation.cfm?id=${id}`; + await driver.get(url); + await driver.sleep(500) + let candidates = await driver.findElements(By.xpath('.//a[contains(@href, "ft_gateway.cfm?id=")]')); + if (candidates.length > 0) { + pdfs.push(candidates[0]) + } + let webElements = await driver.findElements(By.id("abstract-body")) + for (let el of webElements) { + let text = await el.getText() + lines.push(text) + } + result.url = url + result.abstract = lines.join(" "); + await driver.findElement(By.xpath(`//*[@id="tab-1014-btnInnerEl"]/span`)).click() + await driver.sleep(500) + let authors = await driver.findElement(By.xpath('//*[@id="tabpanel-1009-body"]')).getText() + let sanitize = line => line.length > 0 && !(line.startsWith("No contact information") || line.startsWith("View colleagues of") || line.startsWith("Bibliometrics:")) + authorLines = authors.split("\n").map(line => line.trim()).filter(sanitize) + + let i = 0; + let allAuthors = [] + while (i < authorLines.length) { + let individual = []; + while (!authorLines[i].startsWith("Average citations")) { + individual.push(authorLines[i]) + i++ + } + individual.push(authorLines[i]) + allAuthors.push(individual); + i++ + } + result.authors = allAuthors.map(metadata => { + let publicationYears = metadata[1].substring(18).split("-"); + author = { + name: metadata[0], + publication_start: parseInt(publicationYears[0]), + publication_end: parseInt(publicationYears[1]) + }; + for (let count = 2; count < metadata.length; count++) { + let attr = metadata[count] + let char = attr.length - 1; + while (attr[char] != " ") { + char-- + } + let key = attr.substring(0, char).toLowerCase().replace(/ /g, "_").replace(/[\(\)]/g, ""); + let value = parseFloat(attr.substring(char + 1).replace(/,/g, "")); + author[key] = value + } + return author + }) + } catch (e) { + console.log(e) + await driver.quit(); + } + results.push(result) + } + + let output = ""; + results.forEach(res => output += (JSON.stringify(res, null, 4) + "\n")); + + fs.writeFile("./results.txt", output, function errorHandler(exception) { console.log(exception || "results successfully written") }) + + await driver.quit(); +} \ No newline at end of file diff --git a/src/scraping/acm/package.json b/src/scraping/acm/package.json new file mode 100644 index 000000000..10f4d2156 --- /dev/null +++ b/src/scraping/acm/package.json @@ -0,0 +1,17 @@ +{ + "name": "scraper", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^0.19.0", + "cheerio": "^1.0.0-rc.3", + "selenium-webdriver": "^4.0.0-alpha.4" + } +} diff --git a/src/scraping/acm/results.txt b/src/scraping/acm/results.txt new file mode 100644 index 000000000..05bb2be8b --- /dev/null +++ b/src/scraping/acm/results.txt @@ -0,0 +1,64 @@ +{ + "url": "https://dl.acm.org/citation.cfm?id=321046", + "abstract": "It has been observed by many people that a striking number of quite diverse mathematical problems can be formulated as problems in integer programming, that is, linear programming problems in which some or all of the variables are required to assume integral values. This fact is rendered quite interesting by recent research on such problems, notably by R. E. Gomory [2, 3], which gives promise of yielding efficient computational techniques for their solution. The present paper provides yet another example of the versatility of integer programming as a mathematical modeling device by representing a generalization of the well-known “Travelling Salesman Problem†in integer programming terms. The authors have developed several such models, of which the one presented here is the most efficient in terms of generality, number of variables, and number of constraints. This model is due to the second author [4] and was presented briefly at the Symposium on Combinatorial Problems held at Princeton University, April 1960, sponsored by SIAM and IBM. The problem treated is: (1) A salesman is required to visit each of n cities, indexed by 1, … , n. He leaves from a “base city†indexed by 0, visits each of the n other cities exactly once, and returns to city 0. During his travels he must return to 0 exactly t times, including his final return (here t may be allowed to vary), and he must visit no more than p cities in one tour. (By a tour we mean a succession of visits to cities without stopping at city 0.) It is required to find such an itinerary which minimizes the total distance traveled by the salesman. Note that if t is fixed, then for the problem to have a solution we must have tp ≧ n. For t = 1, p ≧ n, we have the standard traveling salesman problem. Let dij (i ≠ j = 0, 1, … , n) be the distance covered in traveling from city i to city j. The following integer programming problem will be shown to be equivalent to (1): (2) Minimize the linear form ∑0≦i≠j≦n∑ dijxij over the set determined by the relations ∑ni=0i≠j xij = 1 (j = 1, … , n) ∑nj=0j≠i xij = 1 (i = 1, … , n) ui - uj + pxij ≦ p - 1 (1 ≦ i ≠ j ≦ n) where the xij are non-negative integers and the ui (i = 1, …, n) are arbitrary real numbers. (We shall see that it is permissible to restrict the ui to be non-negative integers as well.) If t is fixed it is necessary to add the additional relation: ∑nu=1 xi0 = t Note that the constraints require that xij = 0 or 1, so that a natural correspondence between these two problems exists if the xij are interpreted as follows: The salesman proceeds from city i to city j if and only if xij = 1. Under this correspondence the form to be minimized in (2) is the total distance to be traveled by the salesman in (1), so the burden of proof is to show that the two feasible sets correspond; i.e., a feasible solution to (2) has xij which do define a legitimate itinerary in (1), and, conversely a legitimate itinerary in (1) defines xij, which, together with appropriate ui, satisfy the constraints of (2). Consider a feasible solution to (2). The number of returns to city 0 is given by ∑ni=1 xi0. The constraints of the form ∑ xij = 1, all xij non-negative integers, represent the conditions that each city (other than zero) is visited exactly once. The ui play a role similar to node potentials in a network and the inequalities involving them serve to eliminate tours that do not begin and end at city 0 and tours that visit more than p cities. Consider any xr0r1 = 1 (r1 ≠ 0). There exists a unique r2 such that xr1r2 = 1. Unless r2 = 0, there is a unique r3 with xr2r3 = 1. We proceed in this fashion until some rj = 0. This must happen since the alternative is that at some point we reach an rk = rj, j + 1 < k. Since none of the r's are zero we have uri - uri + 1 + pxriri + 1 ≦ p - 1 or uri - uri + 1 ≦ - 1. Summing from i = j to k - 1, we have urj - urk = 0 ≦ j + 1 - k, which is a contradiction. Thus all tours include city 0. It remains to observe that no tours is of length greater than p. Suppose such a tour exists, x0r1 , xr1r2 , … , xrprp+1 = 1 with all ri ≠ 0. Then, as before, ur1 - urp+1 ≦ - p or urp+1 - ur1 ≧ p. But we have urp+1 - ur1 + pxrp+1r1 ≦ p - 1 or urp+1 - ur1 ≦ p (1 - xrp+1r1) - 1 ≦ p - 1, which is a contradiction. Conversely, if the xij correspond to a legitimate itinerary, it is clear that the ui can be adjusted so that ui = j if city i is the jth city visited in the tour which includes city i, for we then have ui - uj = - 1 if xij = 1, and always ui - uj ≦ p - 1. The above integer program involves n2 + n constraints (if t is not fixed) in n2 + 2n variables. Since the inequality form of constraint is fundamental for integer programming calculations, one may eliminate 2n variables, say the xi0 and x0j, by means of the equation constraints and produce an equivalent problem with n2 + n inequalities and n2 variables. The currently known integer programming procedures are sufficiently regular in their behavior to cast doubt on the heuristic value of machine experiments with our model. However, it seems appropriate to report the results of the five machine experiments we have conducted so far. The solution procedure used was the all-integer algorithm of R. E. Gomory [3] without the ranking procedure he describes. The first three experiments were simple model verification tests on a four-city standard traveling salesman problem with distance matrix [ 20 23 4 30 7 27 25 5 25 3 21 26 ] The first experiment was with a model, now obsolete, using roughly twice as many constraints and variables as the current model (for this problem, 28 constraints in 21 variables). The machine was halted after 4000 pivot steps had failed to produce a solution. The second experiment used the earlier model with the xi0 and x0j eliminated, resulting in a 28-constraint, 15-variable problem. Here the machine produced the optimal solution in 41 pivot steps. The third experiment used the current formulation with the xi0 and x0j eliminated, yielding 13 constraints and 9 variables. The optimal solution was reached in 7 pivot steps. The fourth and fifth experiments were used on a standard ten-city problem, due to Barachet, solved by Dantzig, Johnson and Fulkerson [1]. The current formulation was used, yielding 91 constraints in 81 variables. The fifth problem differed from the fourth only in that the ordering of the rows was altered to attempt to introduce more favorable pivot choices. In each case the machine was stopped after over 250 pivot steps had failed to produce the solution. In each case the last 100 pivot steps had failed to change the value of the objective function. It seems hopeful that more efficient integer programming procedures now under development will yield a satisfactory algorithmic solution to the traveling salesman problem, when applied to this model. In any case, the model serves to illustrate how problems of this sort may be succinctly formulated in integer programming terms.", + "authors": [ + { + "name": "C. E. Miller", + "publication_start": 1960, + "publication_end": 1960, + "publication_count": 1, + "citation_count": 179, + "available_for_download": 1, + "downloads_6_weeks": 132, + "downloads_12_months": 993, + "downloads_cumulative": 9781, + "average_downloads_per_article": 9781, + "average_citations_per_article": 179 + }, + { + "name": "A. W. Tucker", + "publication_start": 1960, + "publication_end": 1993, + "publication_count": 5, + "citation_count": 196, + "available_for_download": 1, + "downloads_6_weeks": 132, + "downloads_12_months": 993, + "downloads_cumulative": 9781, + "average_downloads_per_article": 9781, + "average_citations_per_article": 39.2 + }, + { + "name": "R. A. Zemlin", + "publication_start": 1960, + "publication_end": 1964, + "publication_count": 2, + "citation_count": 188, + "available_for_download": 2, + "downloads_6_weeks": 132, + "downloads_12_months": 998, + "downloads_cumulative": 10012, + "average_downloads_per_article": 5006, + "average_citations_per_article": 94 + } + ] +} +{ + "url": "https://dl.acm.org/citation.cfm?id=2412979", + "abstract": "The STRUCT system utilizes the flexibility of a powerful graphics display system to provide a set of tools for program analysis. These tools allow the analysis of the static prograin structure and the dynamic execution behavior. of programs within the entire operating system/user program environment of the Brown University Graphics System (BUGS). Information is collected and presented in a manner which fully exploits two aspects of this environment. First, the operating system has been developed in a well-structured hierarcal manner following principles laid down by other researchers (2), (3). Second the programs under analysis have been written in a structured programming language following coding conventions which make available, at the source code level, valuable program control information. A new set of pictorial constructs is introduced for presenting a. program structure (static or dynamic) for inspection. These constructs combine the best features of an indented structured source code listing and the box odented nature of traditional flow charts. The graphical tools available are USed to provide for swift changes in. the desired level of detail displayed within a program structure, for traveling linearly through a program structure, for traveling through a complex program structure (following subroutine or system calls), for concurrently viewing multiple related program structures, and for presenting dynamic program behavior data using three-dimensional projections, The volume of a three-dimensional box representing a program block is proportional to the block's resource utilization. The scope of this paper is limited to a description of the STRUCT system. This system is currently being used to predict and analyze the performance advantages available through the migration of function (program modules) between levels of software and between software and firmware within BUGS. The results of this research on migration will be included in a doctoral dissertation currently being written.", + "authors": [ + { + "name": "Andries Van Dam", + "publication_start": 1975, + "publication_end": 1975, + "publication_count": 1, + "citation_count": 0, + "available_for_download": 0, + "downloads_6_weeks": 8, + "downloads_12_months": 97, + "downloads_cumulative": 97, + "average_downloads_per_article": 0, + "average_citations_per_article": 0 + } + ] +} diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py new file mode 100644 index 000000000..97af10519 --- /dev/null +++ b/src/scraping/buxton/scraper.py @@ -0,0 +1,331 @@ +import os +import docx2txt +from docx import Document +from docx.opc.constants import RELATIONSHIP_TYPE as RT +import re +from pymongo import MongoClient +import shutil +import uuid +import datetime +from PIL import Image +import math +import sys + +source = "./source" +dist = "../../server/public/files" + +db = MongoClient("localhost", 27017)["Dash"] +schema_guids = [] + + +def extract_links(fileName): + links = [] + doc = Document(fileName) + rels = doc.part.rels + for rel in rels: + item = rels[rel] + if item.reltype == RT.HYPERLINK and ".aspx" not in item._target: + links.append(item._target) + return listify(links) + + +def extract_value(kv_string): + pieces = kv_string.split(":") + return (pieces[1] if len(pieces) > 1 else kv_string).strip() + + +def mkdir_if_absent(path): + try: + if not os.path.exists(path): + os.mkdir(path) + except OSError: + print("failed to create the appropriate directory structures for %s" % file_name) + + +def guid(): + return str(uuid.uuid4()) + + +def listify(list): + return { + "fields": list, + "__type": "list" + } + + +def protofy(fieldId): + return { + "fieldId": fieldId, + "__type": "proxy" + } + + +def write_schema(parse_results, display_fields): + view_guids = parse_results["child_guids"] + + data_doc = parse_results["schema"] + fields = data_doc["fields"] + + view_doc_guid = guid() + + view_doc = { + "_id": view_doc_guid, + "fields": { + "proto": protofy(data_doc["_id"]), + "x": 10, + "y": 10, + "width": 900, + "height": 600, + "panX": 0, + "panY": 0, + "zoomBasis": 0.5, + "zIndex": 2, + "libraryBrush": False, + "viewType": 2 + }, + "__type": "Doc" + } + + fields["proto"] = protofy("collectionProto") + fields["data"] = listify(proxify_guids(view_guids)) + fields["schemaColumns"] = listify(display_fields) + fields["backgroundColor"] = "white" + fields["scale"] = 0.5 + fields["viewType"] = 2 + fields["author"] = "Bill Buxton" + fields["creationDate"] = { + "date": datetime.datetime.utcnow().microsecond, + "__type": "date" + } + fields["isPrototype"] = True + fields["page"] = -1 + + db.newDocuments.insert_one(data_doc) + db.newDocuments.insert_one(view_doc) + + data_doc_guid = data_doc["_id"] + print(f"inserted view document ({view_doc_guid})") + print(f"inserted data document ({data_doc_guid})\n") + + return view_doc_guid + + +def write_image(folder, name): + path = f"http://localhost:1050/files/{folder}/{name}" + + data_doc_guid = guid() + view_doc_guid = guid() + + view_doc = { + "_id": view_doc_guid, + "fields": { + "proto": protofy(data_doc_guid), + "x": 10, + "y": 10, + "width": 300, + "zIndex": 2, + "libraryBrush": False + }, + "__type": "Doc" + } + + image = Image.open(f"{dist}/{folder}/{name}") + native_width, native_height = image.size + + data_doc = { + "_id": data_doc_guid, + "fields": { + "proto": protofy("imageProto"), + "data": { + "url": path, + "__type": "image" + }, + "title": name, + "nativeWidth": native_width, + "author": "Bill Buxton", + "creationDate": { + "date": datetime.datetime.utcnow().microsecond, + "__type": "date" + }, + "isPrototype": True, + "page": -1, + "nativeHeight": native_height, + "height": native_height + }, + "__type": "Doc" + } + + db.newDocuments.insert_one(view_doc) + db.newDocuments.insert_one(data_doc) + + return view_doc_guid + + +def parse_document(file_name: str): + print(f"parsing {file_name}...") + pure_name = file_name.split(".")[0] + + result = {} + + dir_path = dist + "/" + pure_name + mkdir_if_absent(dir_path) + + raw = str(docx2txt.process(source + "/" + file_name, dir_path)) + + view_guids = [] + count = 0 + for image in os.listdir(dir_path): + count += 1 + view_guids.append(write_image(pure_name, image)) + os.rename(dir_path + "/" + image, dir_path + + "/" + image.replace(".", "_m.", 1)) + print(f"extracted {count} images...") + + def sanitize(line): return re.sub("[\n\t]+", "", line).replace(u"\u00A0", " ").replace( + u"\u2013", "-").replace(u"\u201c", '''"''').replace(u"\u201d", '''"''').strip() + + def sanitize_price(raw: str): + raw = raw.replace(",", "") + start = raw.find("$") + if start > -1: + i = start + 1 + while (i < len(raw) and re.match(r"[0-9\.]", raw[i])): + i += 1 + price = raw[start + 1: i + 1] + return float(price) + elif (raw.lower().find("nfs")): + return -1 + else: + return math.nan + + def remove_empty(line): return len(line) > 1 + + lines = list(map(sanitize, raw.split("\n"))) + lines = list(filter(remove_empty, lines)) + + result["file_name"] = file_name + result["title"] = lines[2].strip() + result["short_description"] = lines[3].strip().replace( + "Short Description: ", "") + + cur = 5 + notes = "" + while lines[cur] != "Device Details": + notes += lines[cur] + " " + cur += 1 + result["buxton_notes"] = notes.strip() + + cur += 1 + clean = list( + map(lambda data: data.strip().split(":"), lines[cur].split("|"))) + result["company"] = clean[0][len(clean[0]) - 1].strip() + result["year"] = clean[1][len(clean[1]) - 1].strip() + result["original_price"] = sanitize_price( + clean[2][len(clean[2]) - 1].strip()) + + cur += 1 + result["degrees_of_freedom"] = extract_value( + lines[cur]).replace("NA", "N/A") + cur += 1 + + dimensions = lines[cur].lower() + if dimensions.startswith("dimensions"): + dim_concat = dimensions[11:].strip() + cur += 1 + while lines[cur] != "Key Words": + dim_concat += (" " + lines[cur].strip()) + cur += 1 + result["dimensions"] = dim_concat + else: + result["dimensions"] = "N/A" + + cur += 1 + result["primary_key"] = extract_value(lines[cur]) + cur += 1 + result["secondary_key"] = extract_value(lines[cur]) + + while lines[cur] != "Links": + result["secondary_key"] += (" " + extract_value(lines[cur]).strip()) + cur += 1 + + cur += 1 + link_descriptions = [] + while lines[cur] != "Image": + link_descriptions.append(lines[cur].strip()) + cur += 1 + result["link_descriptions"] = listify(link_descriptions) + + result["hyperlinks"] = extract_links(source + "/" + file_name) + + images = [] + captions = [] + cur += 3 + while cur + 1 < len(lines) and lines[cur] != "NOTES:": + images.append(lines[cur]) + captions.append(lines[cur + 1]) + cur += 2 + result["images"] = listify(images) + result["captions"] = listify(captions) + + notes = [] + if (cur < len(lines) and lines[cur] == "NOTES:"): + cur += 1 + while cur < len(lines): + notes.append(lines[cur]) + cur += 1 + if len(notes) > 0: + result["notes"] = listify(notes) + + print("writing child schema...") + + return { + "schema": { + "_id": guid(), + "fields": result, + "__type": "Doc" + }, + "child_guids": view_guids + } + + +def proxify_guids(guids): + return list(map(lambda guid: {"fieldId": guid, "__type": "proxy"}, guids)) + + +if os.path.exists(dist): + shutil.rmtree(dist) +while os.path.exists(dist): + pass +os.mkdir(dist) +mkdir_if_absent(source) + +candidates = 0 +for file_name in os.listdir(source): + if file_name.endswith('.docx'): + candidates += 1 + schema_guids.append(write_schema( + parse_document(file_name), ["title", "data"])) + +print("writing parent schema...") +parent_guid = write_schema({ + "schema": { + "_id": guid(), + "fields": {}, + "__type": "Doc" + }, + "child_guids": schema_guids +}, ["title", "short_description", "original_price"]) + +print("appending parent schema to main workspace...\n") +db.newDocuments.update_one( + {"fields.title": "WS collection 1"}, + {"$push": {"fields.data.fields": {"fieldId": parent_guid, "__type": "proxy"}}} +) + +print("rewriting .gitignore...\n") +lines = ['*', '!.gitignore'] +with open(dist + "/.gitignore", 'w') as f: + f.write('\n'.join(lines)) + +suffix = "" if candidates == 1 else "s" +print(f"conversion complete. {candidates} candidate{suffix} processed.") diff --git a/src/scraping/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx b/src/scraping/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx new file mode 100644 index 000000000..06094b4d3 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_Bill_Notes_CyKey.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_Braun_T3.docx b/src/scraping/buxton/source/Bill_Notes_Braun_T3.docx new file mode 100644 index 000000000..356697092 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_Braun_T3.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_CasioC801.docx b/src/scraping/buxton/source/Bill_Notes_CasioC801.docx new file mode 100644 index 000000000..cd89fb97b Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_CasioC801.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_Casio_Mini.docx b/src/scraping/buxton/source/Bill_Notes_Casio_Mini.docx new file mode 100644 index 000000000..a503cddfc Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_Casio_Mini.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_FingerWorks_Prototype.docx b/src/scraping/buxton/source/Bill_Notes_FingerWorks_Prototype.docx new file mode 100644 index 000000000..4d13a8cf5 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_FingerWorks_Prototype.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx b/src/scraping/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx new file mode 100644 index 000000000..578a1be08 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_Fingerworks_TouchStream.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_FrogPad.docx b/src/scraping/buxton/source/Bill_Notes_FrogPad.docx new file mode 100644 index 000000000..d01e1bf5c Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_FrogPad.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_Gavilan_SC.docx b/src/scraping/buxton/source/Bill_Notes_Gavilan_SC.docx new file mode 100644 index 000000000..7bd28b376 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_Gavilan_SC.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_Grandjean_Stenotype.docx b/src/scraping/buxton/source/Bill_Notes_Grandjean_Stenotype.docx new file mode 100644 index 000000000..0615c4953 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_Grandjean_Stenotype.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_Matias.docx b/src/scraping/buxton/source/Bill_Notes_Matias.docx new file mode 100644 index 000000000..547603256 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_Matias.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_MousePen.docx b/src/scraping/buxton/source/Bill_Notes_MousePen.docx new file mode 100644 index 000000000..4e1056636 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_MousePen.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_NewO.docx b/src/scraping/buxton/source/Bill_Notes_NewO.docx new file mode 100644 index 000000000..a514926d2 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_NewO.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_OLPC.docx b/src/scraping/buxton/source/Bill_Notes_OLPC.docx new file mode 100644 index 000000000..bfca0a9bb Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_OLPC.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_PARCkbd.docx b/src/scraping/buxton/source/Bill_Notes_PARCkbd.docx new file mode 100644 index 000000000..c0cf6ba9a Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_PARCkbd.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_Philco_Mystery_Control.docx b/src/scraping/buxton/source/Bill_Notes_Philco_Mystery_Control.docx new file mode 100644 index 000000000..ad06903f3 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_Philco_Mystery_Control.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_TASA_Kbd.docx b/src/scraping/buxton/source/Bill_Notes_TASA_Kbd.docx new file mode 100644 index 000000000..e4c659de9 Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_TASA_Kbd.docx differ diff --git a/src/scraping/buxton/source/Bill_Notes_The_Tap.docx b/src/scraping/buxton/source/Bill_Notes_The_Tap.docx new file mode 100644 index 000000000..8ceebc71e Binary files /dev/null and b/src/scraping/buxton/source/Bill_Notes_The_Tap.docx differ -- 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/client/util') 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/client/util') 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 4b69ee75fbc98d81eac1bbad0749cf266ccd4c3c Mon Sep 17 00:00:00 2001 From: ab Date: Wed, 26 Jun 2019 13:12:39 -0400 Subject: idk --- src/client/util/TooltipTextMenu.tsx | 7 ++----- src/client/views/nodes/FormattedTextBox.tsx | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index d08048e21..c42a29806 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -153,8 +153,6 @@ export class TooltipTextMenu { // quick and dirty null check const outer_div = this.editorProps.outer_div; outer_div && outer_div(this.tooltip); - - console.log("hi"); } //label of dropdown will change to given label @@ -552,8 +550,6 @@ export class TooltipTextMenu { let { from, to } = state.selection; //UPDATE LIST ITEM DROPDOWN - //this.listTypeBtnDom = this.updateListItemDropdown(":", this.listTypeBtnDom!); - //this._activeMarks = []; //UPDATE FONT STYLE DROPDOWN let activeStyles = this.activeMarksOnSelection(this.fontStyles); @@ -606,6 +602,7 @@ export class TooltipTextMenu { let has = false, tr = state.tr; for (let i = 0; !has && i < ranges.length; i++) { let { $from, $to } = ranges[i]; + let hasmark: boolean = state.doc.rangeHasMark($from.pos, $to.pos, mark); return state.doc.rangeHasMark($from.pos, $to.pos, mark); } } @@ -633,13 +630,13 @@ export class TooltipTextMenu { } return false; }); - return activeMarks; } else { return []; } } + return activeMarks; } reference_node(pos: ResolvedPos): ProsNode { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 391e42b57..ca2a58cfe 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -101,7 +101,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe super(props); if (this.props.outer_div) { this._outerdiv = this.props.outer_div; - console.log("yay"); } this._ref = React.createRef(); -- 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/client/util') 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/client/util') 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/client/util') 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/client/util') 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 From cad1871a1a8860b67eec61df225b7abf99900029 Mon Sep 17 00:00:00 2001 From: ab Date: Thu, 27 Jun 2019 12:37:04 -0400 Subject: keyboard shortcut for summarize --- src/client/util/ProsemirrorExampleTransfer.ts | 4 ++ src/client/util/TooltipTextMenu.tsx | 53 +++++++++++++++++++++- src/client/views/MainOverlayTextBox.scss | 2 +- src/client/views/MainOverlayTextBox.tsx | 4 +- .../collections/collectionFreeForm/MarqueeView.tsx | 6 +-- 5 files changed, 61 insertions(+), 8 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index 091926d0a..fa9e2e5af 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -7,6 +7,8 @@ import { wrapInList, splitListItem, liftListItem, sinkListItem } from "prosemirr import { undo, redo } from "prosemirror-history"; import { undoInputRule } from "prosemirror-inputrules"; import { Transaction, EditorState } from "prosemirror-state"; +import { TooltipTextMenu } from "./TooltipTextMenu"; +import { Statement } from "../northstar/model/idea/idea"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; @@ -96,5 +98,7 @@ export default function buildKeymap>(schema: S, mapKeys?: }); } + bind("Mod-s", TooltipTextMenu.insertStar); + return keys; } diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 385c9a2a9..3a72a1ede 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -64,6 +64,8 @@ export class TooltipTextMenu { private _activeMarks: Mark[] = []; + private _collapseBtn?: MenuItem; + constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) { this.view = view; this.state = view.state; @@ -71,6 +73,10 @@ export class TooltipTextMenu { this.tooltip = document.createElement("div"); this.tooltip.className = "tooltipMenu"; + // this.createCollapse(); + // if (this._collapseBtn) { + // this.tooltip.appendChild(this._collapseBtn.render(this.view).dom); + // } //add the div which is the tooltip //view.dom.parentNode!.parentNode!.appendChild(this.tooltip); @@ -144,6 +150,8 @@ export class TooltipTextMenu { this.tooltip.appendChild(this.createStar().render(this.view).dom); + + this.updateListItemDropdown(":", this.listTypeBtnDom); this.update(view, undefined); @@ -291,7 +299,7 @@ export class TooltipTextMenu { link = node && node.marks.find(m => m.type.name === "link"); } - insertStar(state: EditorState, dispatch: any) { + public static insertStar(state: EditorState, dispatch: any) { let newNode = schema.nodes.star.create({ visibility: false, text: state.selection.content(), textslice: state.selection.content().toJSON(), textlen: state.selection.to - state.selection.from }); if (dispatch) { //console.log(newNode.attrs.text.toString()); @@ -398,12 +406,53 @@ export class TooltipTextMenu { class: "summarize", execEvent: "", run: (state, dispatch, view) => { - this.insertStar(state, dispatch); + TooltipTextMenu.insertStar(state, dispatch); } }); } + createCollapse() { + this._collapseBtn = new MenuItem({ + title: "Collapse", + //label: "Collapse", + icon: icons.join, + execEvent: "", + css: "color:white;", + class: "summarize", + run: (state, dispatch, view) => { + this.collapseToolTip(); + } + }); + } + + collapseToolTip() { + if (this._collapseBtn) { + if (this._collapseBtn.spec.title === "Collapse") { + // const newcollapseBtn = new MenuItem({ + // title: "Expand", + // icon: icons.join, + // execEvent: "", + // css: "color:white;", + // class: "summarize", + // run: (state, dispatch, view) => { + // this.collapseToolTip(); + // } + // }); + // this.tooltip.replaceChild(newcollapseBtn.render(this.view).dom, this._collapseBtn.render(this.view).dom); + // this._collapseBtn = newcollapseBtn; + this.tooltip.style.width = "30px"; + this._collapseBtn.spec.title = "Expand"; + this._collapseBtn.render(this.view); + } + else { + this._collapseBtn.spec.title = "Collapse"; + this.tooltip.style.width = "550px"; + this._collapseBtn.render(this.view); + } + } + } + createLink() { let markType = schema.marks.link; return new MenuItem({ diff --git a/src/client/views/MainOverlayTextBox.scss b/src/client/views/MainOverlayTextBox.scss index 1093ff671..5381a63b3 100644 --- a/src/client/views/MainOverlayTextBox.scss +++ b/src/client/views/MainOverlayTextBox.scss @@ -19,7 +19,7 @@ } } .unscaled_div{ - width: 500px; + // width: 500px; z-index: 10000; position: absolute; } \ No newline at end of file diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 58946845c..479713fbd 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -26,14 +26,14 @@ export class MainOverlayTextBox extends React.Component private _textProxyDiv: React.RefObject; private _textBottom: boolean | undefined; private _textAutoHeight: boolean | undefined; - private _setouterdiv = (outerdiv: HTMLElement | null) => { this._outerdiv = outerdiv; this.updateTooltip(); } + private _setouterdiv = (outerdiv: HTMLElement | null) => { this._outerdiv = outerdiv; this.updateTooltip(); }; private _outerdiv: HTMLElement | null = null; private _textBox: FormattedTextBox | undefined; private _tooltip?: HTMLElement; @observable public TextDoc?: Doc; updateTooltip = () => { - this._outerdiv && this._tooltip && !this._outerdiv.contains(this._tooltip) && this._outerdiv.appendChild(this._tooltip) + this._outerdiv && this._tooltip && !this._outerdiv.contains(this._tooltip) && this._outerdiv.appendChild(this._tooltip); } constructor(props: MainOverlayTextBoxProps) { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index d2a6ceafa..5a25a4f9a 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -184,9 +184,9 @@ export class MarqueeView extends React.Component @action onPointerUp = (e: PointerEvent): void => { - console.log("pointer up!"); + // console.log("pointer up!"); if (this._visible) { - console.log("visible"); + // console.log("visible"); let mselect = this.marqueeSelect(); if (!e.shiftKey) { SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document); @@ -195,7 +195,7 @@ export class MarqueeView extends React.Component mselect.length ? this.cleanupInteractions(true, false) : this.cleanupInteractions(true); } else { - console.log("invisible"); + //console.log("invisible"); this.cleanupInteractions(true); } -- cgit v1.2.3-70-g09d2 From 79f301a2f74e88f1cf59064de320c199b5154827 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Fri, 28 Jun 2019 21:47:26 -0400 Subject: implemented global key handler --- src/client/util/SelectionManager.ts | 5 +- src/client/views/GlobalKeyHandler.ts | 141 +++++++++++++++++++++ src/client/views/MainView.tsx | 62 ++------- .../views/collections/CollectionDockingView.tsx | 20 ++- src/client/views/nodes/DocumentView.tsx | 4 +- 5 files changed, 176 insertions(+), 56 deletions(-) create mode 100644 src/client/views/GlobalKeyHandler.ts (limited to 'src/client/util') diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 7dbb81e76..3bc71ad42 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,11 +1,13 @@ -import { observable, action, runInAction } from "mobx"; +import { observable, action, runInAction, IReactionDisposer, reaction, autorun } from "mobx"; import { Doc } from "../../new_fields/Doc"; import { DocumentView } from "../views/nodes/DocumentView"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; import { NumCast } from "../../new_fields/Types"; export namespace SelectionManager { + class Manager { + @observable IsDragging: boolean = false; @observable SelectedDocuments: Array = []; @@ -18,6 +20,7 @@ export namespace SelectionManager { } manager.SelectedDocuments.push(docView); + // console.log(manager.SelectedDocuments); docView.props.whenActiveChanged(true); } } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts new file mode 100644 index 000000000..bb10f27cf --- /dev/null +++ b/src/client/views/GlobalKeyHandler.ts @@ -0,0 +1,141 @@ +import { UndoManager } from "../util/UndoManager"; +import { SelectionManager } from "../util/SelectionManager"; +import { CollectionDockingView } from "./collections/CollectionDockingView"; +import { MainView } from "./MainView"; +import { DragManager } from "../util/DragManager"; +import { action } from "mobx"; + +const modifiers = ["Control", "Meta", "Shift", "Alt"]; +type KeyHandler = (keycode: string) => KeyControlInfo; +type KeyControlInfo = { + preventDefault: boolean, + stopPropagation: boolean +}; + +export default class KeyManager { + public static Handler: KeyManager; + private mainView: MainView; + private router = new Map(); + + constructor(mainView: MainView) { + this.mainView = mainView; + this.router.set("0000", this.unmodified); + this.router.set("0100", this.ctrl); + this.router.set("0010", this.alt); + this.router.set("1100", this.ctrl_shift); + } + + public handle = (e: KeyboardEvent) => { + let keyname = e.key.toLowerCase(); + this.handleGreedy(keyname); + + if (modifiers.includes(keyname)) { + return; + } + + let bit = (value: boolean) => value ? "1" : "0"; + let modifierIndex = bit(e.shiftKey) + bit(e.ctrlKey) + bit(e.altKey) + bit(e.metaKey); + + let handleConstrained = this.router.get(modifierIndex); + if (!handleConstrained) { + return; + } + + let control = handleConstrained(keyname); + + control.stopPropagation && e.stopPropagation(); + control.preventDefault && e.preventDefault(); + } + + private handleGreedy = action((keyname: string) => { + switch (keyname) { + } + }); + + private unmodified = action((keyname: string) => { + switch (keyname) { + case "escape": + if (CollectionDockingView.Instance.HasFullScreen()) { + CollectionDockingView.Instance.CloseFullScreen(); + } else { + SelectionManager.DeselectAll(); + } + DragManager.AbortDrag(); + break; + } + + return { + stopPropagation: false, + preventDefault: false + }; + }); + + private alt = action((keyname: string) => { + let stopPropagation = true; + let preventDefault = true; + + switch (keyname) { + case "n": + let toggle = this.mainView.addMenuToggle.current!; + toggle.checked = !toggle.checked; + break; + } + + return { + stopPropagation: stopPropagation, + preventDefault: preventDefault + }; + }); + + private ctrl = action((keyname: string) => { + let stopPropagation = true; + let preventDefault = true; + + switch (keyname) { + case "arrowright": + this.mainView.mainFreeform && CollectionDockingView.Instance.AddRightSplit(this.mainView.mainFreeform, undefined); + break; + case "arrowleft": + this.mainView.mainFreeform && CollectionDockingView.Instance.CloseRightSplit(this.mainView.mainFreeform); + break; + case "f": + this.mainView.isSearchVisible = !this.mainView.isSearchVisible; + break; + case "o": + let target = SelectionManager.SelectedDocuments()[0]; + target && target.fullScreenClicked(); + break; + case "r": + preventDefault = false; + break; + case "y": + UndoManager.Redo(); + break; + case "z": + UndoManager.Undo(); + break; + } + + return { + stopPropagation: stopPropagation, + preventDefault: preventDefault + }; + }); + + private ctrl_shift = action((keyname: string) => { + let stopPropagation = true; + let preventDefault = true; + + switch (keyname) { + case "z": + UndoManager.Redo(); + break; + } + + return { + stopPropagation: stopPropagation, + preventDefault: preventDefault + }; + }); + +} \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 6290d8985..157512aa0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -36,18 +36,20 @@ import { CollectionBaseView } from './collections/CollectionBaseView'; import { List } from '../../new_fields/List'; import PDFMenu from './pdf/PDFMenu'; import { InkTool } from '../../new_fields/InkField'; -import * as _ from "lodash"; +import _ from "lodash"; +import KeyManager from './GlobalKeyHandler'; @observer export class MainView extends React.Component { public static Instance: MainView; + @observable addMenuToggle = React.createRef(); @observable private _workspacesShown: boolean = false; @observable public pwidth: number = 0; @observable public pheight: number = 0; @computed private get mainContainer(): Opt { return FieldValue(Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc)); } - @computed private get mainFreeform(): Opt { + @computed get mainFreeform(): Opt { let docs = DocListCast(this.mainContainer!.data); return (docs && docs.length > 1) ? docs[1] : undefined; } @@ -64,12 +66,13 @@ export class MainView extends React.Component { } componentWillMount() { - document.removeEventListener("keydown", this.globalKeyHandler); - document.addEventListener("keydown", this.globalKeyHandler); + KeyManager.Handler = new KeyManager(this); + document.removeEventListener("keydown", KeyManager.Handler.handle); + document.addEventListener("keydown", KeyManager.Handler.handle); } componentWillUnMount() { - document.removeEventListener("keydown", this.globalKeyHandler); + document.removeEventListener("keydown", KeyManager.Handler.handle); } constructor(props: Readonly<{}>) { @@ -122,18 +125,6 @@ export class MainView extends React.Component { // window.addEventListener("pointermove", (e) => this.reportLocation(e)) window.addEventListener("drop", (e) => e.preventDefault(), false); // drop event handler window.addEventListener("dragover", (e) => e.preventDefault(), false); // drag event handler - window.addEventListener("keydown", (e) => { - if (e.key === "Escape") { - DragManager.AbortDrag(); - SelectionManager.DeselectAll(); - } else if (e.key === "z" && e.ctrlKey) { - e.preventDefault(); - UndoManager.Undo(); - } else if ((e.key === "y" && e.ctrlKey) || (e.key === "z" && e.ctrlKey && e.shiftKey)) { - e.preventDefault(); - UndoManager.Redo(); - } - }, false); // drag event handler // click interactions for the context menu document.addEventListener("pointerdown", action(function (e: PointerEvent) { @@ -292,7 +283,7 @@ export class MainView extends React.Component { ]; return < div id="add-nodes-menu" > - +
@@ -300,8 +291,7 @@ export class MainView extends React.Component {
  • -
  • )} +
  • diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 927aa363f..f5dd76b71 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -251,6 +251,8 @@ class TreeView extends React.Component { e.stopPropagation(); } } + + @undoBatch treeDrop = (e: Event, de: DragManager.DropEvent) => { let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); let rect = this._header!.current!.getBoundingClientRect(); -- cgit v1.2.3-70-g09d2 From 9334c8d12df76df60eedcc093b986f42d1f7f67d Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 2 Jul 2019 14:03:36 -0400 Subject: Refactored some search stuff and fixed treeview flyout min X --- src/client/util/SearchUtil.ts | 28 ++++++++++++++++------ src/client/views/MainView.tsx | 2 +- .../views/collections/ParentDocumentSelector.tsx | 4 ++-- src/client/views/search/SearchBox.tsx | 15 +----------- src/client/views/search/SearchItem.tsx | 4 ++-- src/server/Search.ts | 8 +++---- 6 files changed, 31 insertions(+), 30 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 27d27a3b8..ac2eb72e7 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -4,27 +4,41 @@ import { Doc } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; export namespace SearchUtil { - export function Search(query: string, returnDocs: true): Promise; - export function Search(query: string, returnDocs: false): Promise; + export interface IdSearchResult { + ids: string[]; + numFound: number; + } + + export interface DocSearchResult { + docs: Doc[]; + numFound: number; + } + + export function Search(query: string, returnDocs: true): Promise; + export function Search(query: string, returnDocs: false): Promise; export async function Search(query: string, returnDocs: boolean) { - const ids = JSON.parse(await rp.get(DocServer.prepend("/search"), { + const result: IdSearchResult = JSON.parse(await rp.get(DocServer.prepend("/search"), { qs: { query } })); if (!returnDocs) { - return ids; + return result; } + const { ids, numFound } = result; const docMap = await DocServer.GetRefFields(ids); - return ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc); + const docs = ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc); + return { docs, numFound }; } export async function GetAliasesOfDocument(doc: Doc): Promise { const proto = await Doc.GetT(doc, "proto", Doc, true); const protoId = (proto || doc)[Id]; - return Search(`proto_i:"${protoId}"`, true); + const result = await Search(`proto_i:"${protoId}"`, true); + return result.docs; // return Search(`{!join from=id to=proto_i}id:${protoId}`, true); } export async function GetViewsOfDocument(doc: Doc): Promise { - return Search(`proto_i:"${doc[Id]}"`, true); + const results = await Search(`proto_i:"${doc[Id]}"`, true); + return results.docs; } } \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 787e240b9..04ecc47cf 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -272,7 +272,7 @@ export class MainView extends React.Component { } @action onPointerMove = (e: PointerEvent) => { - this.flyoutWidth = e.clientX; + this.flyoutWidth = Math.max(e.clientX, 0); } @action onPointerUp = (e: PointerEvent) => { diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index b29a30069..c0f489cd8 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -23,9 +23,9 @@ export class SelectorContextMenu extends React.Component { async fetchDocuments() { let aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document); - const docs = await SearchUtil.Search(`data_l:"${this.props.Document[Id]}"`, true); + const { docs } = await SearchUtil.Search(`data_l:"${this.props.Document[Id]}"`, true); const map: Map = new Map; - const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true))); + const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true).then(result => result.docs))); allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index]))); docs.forEach(doc => map.delete(doc)); runInAction(() => { diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index a2556133b..1f6835c26 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -102,20 +102,7 @@ export class SearchBox extends React.Component { @action getResults = async (query: string) => { - let response = await rp.get(DocServer.prepend('/search'), { - qs: { - query - } - }); - let res: string[] = JSON.parse(response); - const fields = await DocServer.GetRefFields(res); - const docs: Doc[] = []; - for (const id of res) { - const field = fields[id]; - if (field instanceof Doc) { - docs.push(field); - } - } + const { docs } = await SearchUtil.Search(query, true); return FilterBox.Instance.filterDocsByType(docs); } diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index f8a0c7b16..5a7bdd24d 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -48,9 +48,9 @@ export class SelectorContextMenu extends React.Component { async fetchDocuments() { let aliases = (await SearchUtil.GetViewsOfDocument(this.props.doc)).filter(doc => doc !== this.props.doc); - const docs = await SearchUtil.Search(`data_l:"${this.props.doc[Id]}"`, true); + const { docs } = await SearchUtil.Search(`data_l:"${this.props.doc[Id]}"`, true); const map: Map = new Map; - const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true))); + const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true).then(result => result.docs))); allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index]))); docs.forEach(doc => map.delete(doc)); runInAction(() => { diff --git a/src/server/Search.ts b/src/server/Search.ts index 2db2b242c..e2a74b737 100644 --- a/src/server/Search.ts +++ b/src/server/Search.ts @@ -27,11 +27,11 @@ export class Search { start: start } })); - const fields = searchResults.response.docs; - const ids = fields.map((field: any) => field.id); - return ids; + const { docs, numFound } = searchResults.response; + const ids = docs.map((field: any) => field.id); + return { ids, numFound }; } catch { - return []; + return { ids: [], numFound: -1 }; } } -- cgit v1.2.3-70-g09d2 From 9b6a8b6685e1bb98960f650997663e5bb8501c4a Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 2 Jul 2019 15:17:52 -0400 Subject: made color selection undoable and display color updates based on first item in selected documents --- src/client/util/SelectionManager.ts | 14 ++++++++++++-- src/client/views/GlobalKeyHandler.ts | 1 + src/client/views/InkingControl.tsx | 33 ++++++++++++++++++++++++++++----- src/client/views/MainView.tsx | 6 +++--- 4 files changed, 44 insertions(+), 10 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 3bc71ad42..41e6ec786 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,8 +1,9 @@ import { observable, action, runInAction, IReactionDisposer, reaction, autorun } from "mobx"; -import { Doc } from "../../new_fields/Doc"; +import { Doc, Opt } from "../../new_fields/Doc"; import { DocumentView } from "../views/nodes/DocumentView"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; -import { NumCast } from "../../new_fields/Types"; +import { NumCast, StrCast } from "../../new_fields/Types"; +import { InkingControl } from "../views/InkingControl"; export namespace SelectionManager { @@ -11,6 +12,7 @@ export namespace SelectionManager { @observable IsDragging: boolean = false; @observable SelectedDocuments: Array = []; + @action SelectDoc(docView: DocumentView, ctrlPressed: boolean): void { // if doc is not in SelectedDocuments, add it @@ -41,6 +43,14 @@ export namespace SelectionManager { } const manager = new Manager(); + reaction(() => manager.SelectedDocuments, sel => { + let firstView = sel[0]; + let doc = firstView.props.Document; + let targetDoc = doc.isTemplate ? doc : Doc.GetProto(doc); + let targetColor = StrCast(targetDoc.backgroundColor); + targetColor = targetColor.length === 0 ? "#FFFFFFFF" : targetColor; + InkingControl.Instance.updateSelectedColor(targetColor); + }); export function DeselectDoc(docView: DocumentView): void { manager.DeselectDoc(docView); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 574e43ba3..fb4a107ad 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -65,6 +65,7 @@ export default class KeyManager { SelectionManager.DeselectAll(); } } + MainView.Instance.toggleColorPicker(true); break; } diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index ec228ce98..0461d7299 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -1,4 +1,4 @@ -import { observable, action, computed } from "mobx"; +import { observable, action, computed, runInAction } from "mobx"; import { ColorResult } from 'react-color'; import React = require("react"); import { observer } from "mobx-react"; @@ -8,6 +8,8 @@ import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-s import { SelectionManager } from "../util/SelectionManager"; import { InkTool } from "../../new_fields/InkField"; import { Doc } from "../../new_fields/Doc"; +import { undoBatch, UndoManager } from "../util/UndoManager"; +import { StrCast } from "../../new_fields/Types"; library.add(faPen, faHighlighter, faEraser, faBan); @@ -36,11 +38,27 @@ export class InkingControl extends React.Component { return number.toString(16).toUpperCase(); } - @action - switchColor = (color: ColorResult): void => { + @undoBatch + switchColor = action((color: ColorResult): void => { this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); - if (InkingControl.Instance.selectedTool === InkTool.None) SelectionManager.SelectedDocuments().forEach(doc => (doc.props.Document.isTemplate ? doc.props.Document : Doc.GetProto(doc.props.Document)).backgroundColor = this._selectedColor); - } + if (InkingControl.Instance.selectedTool === InkTool.None) { + let selected = SelectionManager.SelectedDocuments(); + let oldColors = selected.map(view => { + let targetDoc = view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document); + let oldColor = StrCast(targetDoc.backgroundColor); + targetDoc.backgroundColor = this._selectedColor; + return { + target: targetDoc, + previous: oldColor + }; + }); + let captured = this._selectedColor; + UndoManager.AddEvent({ + undo: () => oldColors.forEach(pair => pair.target.backgroundColor = pair.previous), + redo: () => oldColors.forEach(pair => pair.target.backgroundColor = captured) + }); + } + }); @action switchWidth = (width: string): void => { @@ -57,6 +75,11 @@ export class InkingControl extends React.Component { return this._selectedColor; } + @action + updateSelectedColor(value: string) { + this._selectedColor = value; + } + @computed get selectedWidth() { return this._selectedWidth; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 787e240b9..62377a3b0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -291,7 +291,7 @@ export class MainView extends React.Component { } else { CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); } - } + }; let flyout = { - this._colorPickerDisplay = !this._colorPickerDisplay; + toggleColorPicker = (close = false) => { + this._colorPickerDisplay = close ? false : !this._colorPickerDisplay; } /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */ -- cgit v1.2.3-70-g09d2 From 5edfd42cea089c0691e5568c604a220d5abab301 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 2 Jul 2019 18:45:17 -0400 Subject: fixed color changing bug on empty selection --- src/client/util/SelectionManager.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 41e6ec786..3c396362e 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -44,11 +44,14 @@ export namespace SelectionManager { const manager = new Manager(); reaction(() => manager.SelectedDocuments, sel => { - let firstView = sel[0]; - let doc = firstView.props.Document; - let targetDoc = doc.isTemplate ? doc : Doc.GetProto(doc); - let targetColor = StrCast(targetDoc.backgroundColor); - targetColor = targetColor.length === 0 ? "#FFFFFFFF" : targetColor; + let targetColor = "#FFFFFF"; + if (sel.length > 0) { + let firstView = sel[0]; + let doc = firstView.props.Document; + let targetDoc = doc.isTemplate ? doc : Doc.GetProto(doc); + let stored = StrCast(targetDoc.backgroundColor); + stored.length > 0 && (targetColor = stored); + } InkingControl.Instance.updateSelectedColor(targetColor); }); -- cgit v1.2.3-70-g09d2 From 25fdbe3b9159473a9249246f6c208b6e1220dbbb Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 3 Jul 2019 14:46:44 -0400 Subject: Changed how scripting works --- src/client/documents/Documents.ts | 25 +++------------------- src/client/util/Scripting.ts | 45 +++++++++++++++++++++++++++++++-------- src/new_fields/Doc.ts | 2 ++ src/new_fields/List.ts | 5 ++++- src/new_fields/RichTextField.ts | 2 ++ src/new_fields/ScriptField.ts | 4 +++- src/new_fields/URLField.ts | 11 +++++----- 7 files changed, 56 insertions(+), 38 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7d7a1f02a..7a976e7d7 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -36,6 +36,7 @@ import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; import { DocumentManager } from "../util/DocumentManager"; +import { Scripting } from "../util/Scripting"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -385,26 +386,6 @@ export namespace Docs { `); } - /* - - this template requires an additional style setting on the collectionView-cont to make the layout relative - -.collectionView-cont { - position: relative; - width: 100%; - height: 100%; } - */ - function Percentaption() { - return (` -
    -
    - {layout} -
    -
    - -
    -
    - `); - } -} \ No newline at end of file + +Scripting.addGlobal("Docs", Docs); \ No newline at end of file diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 30a05154a..3156c4f43 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -7,12 +7,7 @@ let ts = (window as any).ts; // @ts-ignore import * as typescriptlib from '!!raw-loader!./type_decls.d'; -import { Docs } from "../documents/Documents"; import { Doc, Field } from '../../new_fields/Doc'; -import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField'; -import { List } from '../../new_fields/List'; -import { RichTextField } from '../../new_fields/RichTextField'; -import { ScriptField, ComputedField } from '../../new_fields/ScriptField'; export interface ScriptSucccess { success: true; @@ -38,6 +33,34 @@ export interface CompileError { errors: any[]; } +export namespace Scripting { + export function addGlobal(global: { name: string }): void; + export function addGlobal(name: string, global: any): void; + export function addGlobal(nameOrGlobal: any, global?: any) { + let n: string; + let obj: any; + if (global !== undefined && typeof nameOrGlobal === "string") { + n = nameOrGlobal; + obj = global; + } else if (nameOrGlobal && typeof nameOrGlobal.name === "string") { + n = nameOrGlobal.name; + obj = nameOrGlobal; + } else { + throw new Error("Must either register an object with a name, or give a name and an object"); + } + if (scriptingGlobals.hasOwnProperty(n)) { + throw new Error(`Global with name ${n} is already registered, choose another name`); + } + scriptingGlobals[n] = obj; + } +} + +export function scriptingGlobal(constructor: { new(...args: any[]): any }) { + Scripting.addGlobal(constructor); +} + +const scriptingGlobals: { [name: string]: any } = {}; + export type CompileResult = CompiledScript | CompileError; function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult { const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error); @@ -45,9 +68,11 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an return { compiled: false, errors: diagnostics }; } - let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField, ScriptField, ComputedField, CompileScript]; - let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)]; - let params: any[] = [Docs, ...fieldTypes]; + let paramNames = Object.keys(scriptingGlobals); + let params = paramNames.map(key => scriptingGlobals[key]); + // let fieldTypes = [Doc, ImageField, PdfField, VideoField, AudioField, List, RichTextField, ScriptField, ComputedField, CompileScript]; + // let paramNames = ["Docs", ...fieldTypes.map(fn => fn.name)]; + // let params: any[] = [Docs, ...fieldTypes]; let compiledFunction = new Function(...paramNames, `return ${script}`); let { capturedVariables = {} } = options; let run = (args: { [name: string]: any } = {}): ScriptResult => { @@ -178,4 +203,6 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp let diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics); return Run(outputText, paramNames, diagnostics, script, options); -} \ No newline at end of file +} + +Scripting.addGlobal(CompileScript); \ No newline at end of file diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 29d35e19f..c361e3032 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -8,6 +8,7 @@ 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 { scriptingGlobal } from "../client/util/Scripting"; export namespace Field { export function toScriptString(field: Field): string { @@ -55,6 +56,7 @@ export function DocListCast(field: FieldResult): Doc[] { export const WidthSym = Symbol("Width"); export const HeightSym = Symbol("Height"); +@scriptingGlobal @Deserializable("doc").withFields(["id"]) export class Doc extends RefField { constructor(id?: FieldId, forceSave?: boolean) { diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts index f1e4c4721..a2133a990 100644 --- a/src/new_fields/List.ts +++ b/src/new_fields/List.ts @@ -7,6 +7,7 @@ import { ObjectField } from "./ObjectField"; import { RefField } from "./RefField"; import { ProxyField } from "./Proxy"; import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, Copy } from "./FieldSymbols"; +import { Scripting } from "../client/util/Scripting"; const listHandlers: any = { /// Mutator methods @@ -294,4 +295,6 @@ class ListImpl extends ObjectField { } } export type List = ListImpl & (T | (T extends RefField ? Promise : never))[]; -export const List: { new (fields?: T[]): List } = ListImpl as any; \ No newline at end of file +export const List: { new (fields?: T[]): List } = ListImpl as any; + +Scripting.addGlobal("List", List); \ No newline at end of file diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts index 89d077a47..78a3a4067 100644 --- a/src/new_fields/RichTextField.ts +++ b/src/new_fields/RichTextField.ts @@ -2,7 +2,9 @@ import { ObjectField } from "./ObjectField"; import { serializable } from "serializr"; import { Deserializable } from "../client/util/SerializationHelper"; import { Copy, ToScriptString } from "./FieldSymbols"; +import { scriptingGlobal } from "../client/util/Scripting"; +@scriptingGlobal @Deserializable("RichTextField") export class RichTextField extends ObjectField { @serializable(true) diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts index 3d56e9374..e2994ed70 100644 --- a/src/new_fields/ScriptField.ts +++ b/src/new_fields/ScriptField.ts @@ -1,5 +1,5 @@ import { ObjectField } from "./ObjectField"; -import { CompiledScript, CompileScript } from "../client/util/Scripting"; +import { CompiledScript, CompileScript, scriptingGlobal } from "../client/util/Scripting"; import { Copy, ToScriptString, Parent, SelfProxy } from "./FieldSymbols"; import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; import { Deserializable } from "../client/util/SerializationHelper"; @@ -40,6 +40,7 @@ function deserializeScript(script: ScriptField) { (script as any).script = comp; } +@scriptingGlobal @Deserializable("script", deserializeScript) export class ScriptField extends ObjectField { @serializable(object(scriptSchema)) @@ -81,6 +82,7 @@ export class ScriptField extends ObjectField { } } +@scriptingGlobal @Deserializable("computed", deserializeScript) export class ComputedField extends ScriptField { //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts index 4a2841fb6..d935a61af 100644 --- a/src/new_fields/URLField.ts +++ b/src/new_fields/URLField.ts @@ -2,6 +2,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom } from "serializr"; import { ObjectField } from "./ObjectField"; import { ToScriptString, Copy } from "./FieldSymbols"; +import { Scripting, scriptingGlobal } from "../client/util/Scripting"; function url() { return custom( @@ -37,8 +38,8 @@ export abstract class URLField extends ObjectField { } } -@Deserializable("audio") export class AudioField extends URLField { } -@Deserializable("image") export class ImageField extends URLField { } -@Deserializable("video") export class VideoField extends URLField { } -@Deserializable("pdf") export class PdfField extends URLField { } -@Deserializable("web") export class WebField extends URLField { } \ No newline at end of file +@scriptingGlobal @Deserializable("audio") export class AudioField extends URLField { } +@scriptingGlobal @Deserializable("image") export class ImageField extends URLField { } +@scriptingGlobal @Deserializable("video") export class VideoField extends URLField { } +@scriptingGlobal @Deserializable("pdf") export class PdfField extends URLField { } +@scriptingGlobal @Deserializable("web") export class WebField extends URLField { } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 74e4909a77ac143ecdb1d038ad182aae9c710129 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 3 Jul 2019 22:15:25 -0400 Subject: implemented directory import routine --- src/client/documents/Documents.ts | 57 ++++++++- src/client/util/Import & Export/ImageImporter.tsx | 67 +++++++++++ src/client/util/Import & Export/ImportBox.tsx | 134 +++++++++++++++++++++ src/client/util/SelectionManager.ts | 2 +- src/client/views/MainView.tsx | 3 + src/client/views/collections/CollectionSubView.tsx | 46 +------ src/client/views/nodes/DocumentContentsView.tsx | 3 +- 7 files changed, 265 insertions(+), 47 deletions(-) create mode 100644 src/client/util/Import & Export/ImageImporter.tsx create mode 100644 src/client/util/Import & Export/ImportBox.tsx (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7d7a1f02a..5d637dd3a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -36,6 +36,7 @@ import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; import { DocumentManager } from "../util/DocumentManager"; +import ImportBox from "../util/Import & Export/ImportBox"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -51,7 +52,8 @@ export enum DocTypes { KVP = "kvp", VID = "video", AUDIO = "audio", - LINK = "link" + LINK = "link", + IMPORT = "import" } export interface DocumentOptions { @@ -127,6 +129,7 @@ export namespace Docs { let audioProto: Doc; let pdfProto: Doc; let iconProto: Doc; + let importProto: Doc; // let linkProto: Doc; const textProtoId = "textProto"; const histoProtoId = "histoProto"; @@ -138,6 +141,7 @@ export namespace Docs { const videoProtoId = "videoProto"; const audioProtoId = "audioProto"; const iconProtoId = "iconProto"; + const importProtoId = "importProto"; // const linkProtoId = "linkProto"; export function initProtos(): Promise { @@ -152,6 +156,7 @@ export namespace Docs { audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); + importProto = fields[importProtoId] as Doc || CreateImportPrototype(); }); } @@ -174,6 +179,11 @@ export namespace Docs { return imageProto; } + function CreateImportPrototype(): Doc { + let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", ImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT }); + return importProto; + } + function CreateHistogramPrototype(): Doc { let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"), { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString(), type: DocTypes.HIST }); @@ -261,6 +271,10 @@ export namespace Docs { return CreateInstance(audioProto, new AudioField(new URL(url)), options); } + export function DirectoryImportDocument(options: DocumentOptions = {}) { + return CreateInstance(importProto, "", options); + } + export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) { return CreateInstance(histoProto, new HistogramField(histoOp), options); } @@ -333,6 +347,47 @@ export namespace Docs { return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } + export async function getDocumentFromType(type: string, path: string, options: DocumentOptions, addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean): Promise> { + let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined; + if (type.indexOf("image") !== -1) { + ctor = Docs.ImageDocument; + } + if (type.indexOf("video") !== -1) { + ctor = Docs.VideoDocument; + } + if (type.indexOf("audio") !== -1) { + ctor = Docs.AudioDocument; + } + if (type.indexOf("pdf") !== -1) { + ctor = Docs.PdfDocument; + options.nativeWidth = 1200; + } + if (type.indexOf("excel") !== -1) { + ctor = Docs.DBDocument; + options.dropAction = "copy"; + } + if (type.indexOf("html") !== -1) { + if (path.includes(window.location.hostname)) { + let s = path.split('/'); + let id = s[s.length - 1]; + DocServer.GetRefField(id).then(field => { + if (field instanceof Doc) { + let alias = Doc.MakeAlias(field); + alias.x = options.x || 0; + alias.y = options.y || 0; + alias.width = options.width || 300; + alias.height = options.height || options.width || 300; + addDocument && addDocument(alias, false); + } + }); + return undefined; + } + ctor = Docs.WebDocument; + options = { height: options.width, ...options, title: path, nativeWidth: undefined }; + } + return ctor ? ctor(path, options) : undefined; + } + export function CaptionDocument(doc: Doc) { const captionDoc = Doc.MakeAlias(doc); captionDoc.overlayLayout = FixedCaption(); diff --git a/src/client/util/Import & Export/ImageImporter.tsx b/src/client/util/Import & Export/ImageImporter.tsx new file mode 100644 index 000000000..d664f6487 --- /dev/null +++ b/src/client/util/Import & Export/ImageImporter.tsx @@ -0,0 +1,67 @@ +import "fs"; +import React = require("react"); +import { Doc } from "../../../new_fields/Doc"; +import { DocServer } from "../../DocServer"; +import { RouteStore } from "../../../server/RouteStore"; +import { action } from "mobx"; +import { Docs } from "../../documents/Documents"; +import { FieldViewProps } from "../../views/nodes/FieldView"; + +interface ImageImporterProps { + addSchema: (imageDocs: Doc[]) => void; +} + +export default class BulkImporter extends React.Component { + private selector = React.createRef(); + + handleSelection = async (e: React.ChangeEvent) => { + let promises: Promise[] = []; + let docs: Doc[] = []; + + let files = e.target.files; + if (!files) return; + + for (let i = 0; i < files.length; i++) { + let target = files.item(i); + + if (target === null) { + continue; + } + + let type = target.type; + let formData = new FormData(); + formData.append('file', target); + let dropFileName = target ? target.name : "-empty-"; + + let prom = fetch(DocServer.prepend(RouteStore.upload), { + method: 'POST', + body: formData + }).then(async (res: Response) => { + (await res.json()).map(action((file: any) => { + let path = window.location.origin + file; + let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName }); + docPromise.then(doc => doc && docs.push(doc)); + })); + }); + promises.push(prom); + } + + await Promise.all(promises); + + let parent = Docs.SchemaDocument(["title", "data"], docs, { width: 300, height: 300, title: "Bulk Import from Directory" }); + } + + componentDidMount() { + this.selector.current!.setAttribute("directory", "true"); + this.selector.current!.setAttribute("webkitdirectory", "true"); + } + + render() { + return ( +
    + +
    + ); + } + +} \ No newline at end of file diff --git a/src/client/util/Import & Export/ImportBox.tsx b/src/client/util/Import & Export/ImportBox.tsx new file mode 100644 index 000000000..630911710 --- /dev/null +++ b/src/client/util/Import & Export/ImportBox.tsx @@ -0,0 +1,134 @@ +import "fs"; +import React = require("react"); +import { Doc } from "../../../new_fields/Doc"; +import { DocServer } from "../../DocServer"; +import { RouteStore } from "../../../server/RouteStore"; +import { action, observable } from "mobx"; +import { FieldViewProps, FieldView } from "../../views/nodes/FieldView"; +import Measure, { ContentRect } from "react-measure"; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; +import { Docs, DocumentOptions } from "../../documents/Documents"; + +interface ImageImporterProps { + addSchema: (imageDocs: Doc[]) => void; +} + +export default class ImportBox extends React.Component { + @observable private top = 0; + @observable private left = 0; + private dimensions = 50; + + constructor(props: FieldViewProps) { + super(props); + library.add(faArrowUp); + } + + public static LayoutString() { return FieldView.LayoutString(ImportBox); } + + private selector = React.createRef(); + + handleSelection = async (e: React.ChangeEvent) => { + let promises: Promise[] = []; + let docs: Doc[] = []; + + let files = e.target.files; + if (!files || files.length === 0) return; + + let directory = (files.item(0) as any).webkitRelativePath.split("/", 1); + + for (let i = 0; i < files.length; i++) { + let uploaded_file = files.item(i); + + if (!uploaded_file) { + continue; + } + + let formData = new FormData(); + formData.append('file', uploaded_file); + let dropFileName = uploaded_file ? uploaded_file.name : "-empty-"; + let type = uploaded_file.type; + + let prom = fetch(DocServer.prepend(RouteStore.upload), { + method: 'POST', + body: formData + }).then(async (res: Response) => { + (await res.json()).map(action((file: any) => { + let path = DocServer.prepend(file); + let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName }); + docPromise.then(doc => doc && docs.push(doc)); + })); + }); + promises.push(prom); + } + + await Promise.all(promises); + + let doc = this.props.Document; + let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") }; + let parent = this.props.ContainingCollectionView; + if (parent) { + let importContainer = Docs.StackingDocument(docs, options); + Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer); + this.props.removeDocument && this.props.removeDocument(doc); + } + } + + componentDidMount() { + this.selector.current!.setAttribute("directory", "true"); + this.selector.current!.setAttribute("webkitdirectory", "true"); + } + + @action + preserveCentering = (rect: ContentRect) => { + let bounds = rect.offset!; + if (bounds.width === 0 || bounds.height === 0) { + return; + } + let offset = this.dimensions / 2; + this.left = bounds.width / 2 - offset; + this.top = bounds.height / 2 - offset; + } + + render() { + let dimensions = 50; + return ( + + {({ measureRef }) => +
    + +