From 8ef6948b1b3788044d3d53ad4966297ab5256f09 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 19 Feb 2020 21:52:25 -0500 Subject: added titles to group type docs --- src/client/views/linking/LinkEditor.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/client/views/linking/LinkEditor.tsx') diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index e3bf6b5f8..6df52c126 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -215,6 +215,7 @@ export class LinkGroupEditor extends React.Component { @action setGroupType = (groupType: string): void => { this.props.groupDoc.type = groupType; + this.props.groupDoc.title = groupType; } removeGroupFromLink = (groupType: string): void => { @@ -246,6 +247,7 @@ export class LinkGroupEditor extends React.Component { // create new group doc with new metadata doc const destGroupDoc = new Doc(); destGroupDoc.type = groupType; + destGroupDoc.title = groupType; destGroupDoc.metadata = destMdDoc; if (destDoc) { -- cgit v1.2.3-70-g09d2 From 7a7105d8867e4b1c91e020f54dc8e9b3be563587 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 19 Feb 2020 22:21:56 -0500 Subject: fixed some link grouping structures --- src/client/util/LinkManager.ts | 10 ++-- src/client/views/linking/LinkEditor.tsx | 80 ++++++++++++------------------- src/client/views/linking/LinkMenuItem.tsx | 4 +- 3 files changed, 38 insertions(+), 56 deletions(-) (limited to 'src/client/views/linking/LinkEditor.tsx') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 5f3667acc..1a793b524 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -148,7 +148,7 @@ export class LinkManager { public addGroupToAnchor(linkDoc: Doc, anchor: Doc, groupDoc: Doc, replace: boolean = false) { const groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); const index = groups.findIndex(gDoc => { - return StrCast(groupDoc.type).toUpperCase() === StrCast(gDoc.type).toUpperCase(); + return StrCast(groupDoc.title).toUpperCase() === StrCast(gDoc.title).toUpperCase(); }); if (index > -1 && replace) { groups[index] = groupDoc; @@ -162,7 +162,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) { const groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); - const newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase()); + const newGroups = groups.filter(groupDoc => StrCast(groupDoc.title).toUpperCase() !== groupType.toUpperCase()); LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); } @@ -175,7 +175,7 @@ export class LinkManager { if (groups.length > 0) { groups.forEach(groupDoc => { - const groupType = StrCast(groupDoc.type); + const groupType = StrCast(groupDoc.title); if (groupType === "") { const group = anchorGroups.get("*"); anchorGroups.set("*", group ? [...group, link] : [link]); @@ -217,8 +217,8 @@ export class LinkManager { allLinks.forEach(linkDoc => { const anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, null)); const anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, null)); - anchor1Groups.forEach(groupDoc => { if (StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase()) { const meta = Cast(groupDoc.metadata, Doc, null); meta && md.push(meta); } }); - anchor2Groups.forEach(groupDoc => { if (StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase()) { const meta = Cast(groupDoc.metadata, Doc, null); meta && md.push(meta); } }); + anchor1Groups.forEach(groupDoc => { if (StrCast(groupDoc.title).toUpperCase() === groupType.toUpperCase()) { md.push(groupDoc); } }); + anchor2Groups.forEach(groupDoc => { if (StrCast(groupDoc.title).toUpperCase() === groupType.toUpperCase()) { md.push(groupDoc); } }); }); return md; } diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 6df52c126..3a5cba81b 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -206,15 +206,12 @@ export class LinkGroupEditor extends React.Component { constructor(props: LinkGroupEditorProps) { super(props); - const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.type)); - groupMdKeys.forEach(key => { - this._metadataIds.set(key, Utils.GenerateGuid()); - }); + const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.title)); + groupMdKeys.forEach(key => this._metadataIds.set(key, Utils.GenerateGuid())); } @action setGroupType = (groupType: string): void => { - this.props.groupDoc.type = groupType; this.props.groupDoc.title = groupType; } @@ -228,27 +225,22 @@ export class LinkGroupEditor extends React.Component { copyGroup = async (groupType: string): Promise => { const sourceGroupDoc = this.props.groupDoc; - const sourceMdDoc = await Cast(sourceGroupDoc.metadata, Doc); - if (!sourceMdDoc) return; const destDoc = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); // let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc); const keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); - // create new metadata doc with copied kvp - const destMdDoc = new Doc(); - destMdDoc.anchor1 = StrCast(sourceMdDoc.anchor2); - destMdDoc.anchor2 = StrCast(sourceMdDoc.anchor1); - keys.forEach(key => { - const val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]); - destMdDoc[key] = val; - }); // create new group doc with new metadata doc const destGroupDoc = new Doc(); - destGroupDoc.type = groupType; destGroupDoc.title = groupType; - destGroupDoc.metadata = destMdDoc; + // create new metadata doc with copied kvp + destGroupDoc.anchor1 = sourceGroupDoc.anchor2; + destGroupDoc.anchor2 = sourceGroupDoc.anchor1; + keys.forEach(key => { + const val = sourceGroupDoc[key] === undefined ? "" : StrCast(sourceGroupDoc[key]); + destGroupDoc[key] = val; + }); if (destDoc) { LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, destDoc, destGroupDoc, true); @@ -272,17 +264,13 @@ export class LinkGroupEditor extends React.Component { renderMetadata = (): JSX.Element[] => { const metadata: Array = []; const groupDoc = this.props.groupDoc; - const mdDoc = FieldValue(Cast(groupDoc.metadata, Doc)); - if (!mdDoc) { - return []; - } - const groupType = StrCast(groupDoc.type); + const groupType = StrCast(groupDoc.title); const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); groupMdKeys.forEach((key) => { - const val = StrCast(mdDoc[key]); + const val = StrCast(groupDoc[key]); metadata.push( - + ); }); return metadata; @@ -300,7 +288,7 @@ export class LinkGroupEditor extends React.Component { } render() { - const groupType = StrCast(this.props.groupDoc.type); + const groupType = StrCast(this.props.groupDoc.title); // if ((groupType && LinkManager.Instance.getMetadataKeysInGroup(groupType).length > 0) || groupType === "") { let buttons; if (groupType === "") { @@ -358,18 +346,14 @@ export class LinkEditor extends React.Component { @action addGroup = (): void => { // create new metadata document for group - const mdDoc = new Doc(); - mdDoc.anchor1 = this.props.sourceDoc.title; + // create new group document + const groupDoc = new Doc(); + groupDoc.anchor1 = this.props.sourceDoc; const opp = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); if (opp) { - mdDoc.anchor2 = opp.title; + groupDoc.anchor2 = opp; } - // create new group document - const groupDoc = new Doc(); - groupDoc.type = ""; - groupDoc.metadata = mdDoc; - LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, this.props.sourceDoc, groupDoc); } @@ -378,25 +362,23 @@ export class LinkEditor extends React.Component { const groupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); const groups = groupList.map(groupDoc => { - return ; + return ; }); - if (destination) { - return ( -
- -
-

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

- -
-
- Relationships: - -
- {groups.length > 0 ? groups :
There are currently no relationships associated with this link.
} + return !destination ? (null) : ( +
+ +
+

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

+
+
+ Relationships: + +
+ {groups.length > 0 ? groups :
There are currently no relationships associated with this link.
} +
- ); - } + ); } } \ No newline at end of file diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 54f5636e4..43a7daf00 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -40,12 +40,12 @@ export class LinkMenuItem extends React.Component { renderMetadata = (): JSX.Element => { const groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); - const index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase()); + const index = groups.findIndex(groupDoc => StrCast(groupDoc.title).toUpperCase() === this.props.groupType.toUpperCase()); const groupDoc = index > -1 ? groups[index] : undefined; let mdRows: Array = []; if (groupDoc) { - const mdDoc = Cast(groupDoc.metadata, Doc, null); + const mdDoc = groupDoc; if (mdDoc) { const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType); mdRows = keys.map(key => { -- cgit v1.2.3-70-g09d2 From 6e5abc3052f4df09b156deccdfe6a7b0b62f492b Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 20 Feb 2020 15:59:53 -0500 Subject: cleaned up a whole bunch of linking stuff. link menu / link types / link default behaviors / added LinkBox --- src/client/documents/Documents.ts | 68 ++++++++++++---------- src/client/util/LinkManager.ts | 48 +++------------ src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 13 +++-- src/client/views/MainView.tsx | 2 + .../CollectionSchemaMovableTableHOC.tsx | 7 ++- .../views/collections/CollectionSchemaView.tsx | 3 +- src/client/views/collections/CollectionSubView.tsx | 2 + .../views/collections/CollectionTreeView.tsx | 24 ++++---- src/client/views/linking/LinkEditor.tsx | 20 +------ src/client/views/linking/LinkMenuGroup.tsx | 2 +- src/client/views/linking/LinkMenuItem.tsx | 5 +- src/client/views/nodes/DocuLinkBox.tsx | 23 +++++++- src/client/views/nodes/DocumentBox.tsx | 6 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 9 ++- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/LinkBox.scss | 3 + src/client/views/nodes/LinkBox.tsx | 35 +++++++++++ src/client/views/nodes/PresBox.tsx | 2 +- src/new_fields/ObjectField.ts | 1 - 21 files changed, 152 insertions(+), 128 deletions(-) create mode 100644 src/client/views/nodes/LinkBox.scss create mode 100644 src/client/views/nodes/LinkBox.tsx (limited to 'src/client/views/linking/LinkEditor.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 650538e93..2d2de54b7 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -26,7 +26,6 @@ import { listSpec } from "../../new_fields/Schema"; import { DocServer } from "../DocServer"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; -import { UndoManager, undoBatch } from "../util/UndoManager"; import { YoutubeBox } from "../apis/youtube/YoutubeBox"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { LinkManager } from "../util/LinkManager"; @@ -56,6 +55,7 @@ import { extname } from "path"; import { MessageStore } from "../../server/Message"; import { ContextMenuProps } from "../views/ContextMenuItem"; import { ContextMenu } from "../views/ContextMenu"; +import { LinkBox } from "../views/nodes/LinkBox"; const requestImageSize = require('../util/request-image-size'); const path = require('path'); @@ -85,6 +85,7 @@ export interface DocumentOptions { y?: number; z?: number; dropAction?: dropActionType; + chilDropAction?: dropActionType; layoutKey?: string; type?: string; title?: string; @@ -103,6 +104,8 @@ export interface DocumentOptions { lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed opacity?: number; defaultBackgroundColor?: string; + isBackground?: boolean; + isButton?: boolean; columnWidth?: number; fontSize?: number; curPage?: number; @@ -220,6 +223,10 @@ export namespace Docs { layout: { view: DirectoryImportBox, dataField: data }, options: { _height: 150 } }], + [DocumentType.LINK, { + layout: { view: LinkBox, dataField: data }, + options: { _height: 75 } + }], [DocumentType.LINKDOC, { data: new List(), layout: { view: EmptyBox, dataField: data }, @@ -399,8 +406,8 @@ export namespace Docs { Scripting.addGlobal(Buxton); - const delegateKeys = ["x", "y", "layoutKey", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "dropAction", "_annotationOn", - "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "_showTitle", "_showCaption", "_showTitleHover"]; + const delegateKeys = ["x", "y", "layoutKey", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "dropAction", "childDropAction", "_annotationOn", + "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "_showTitle", "_showCaption", "_showTitleHover", "isButton", "isBackground", "removeDropProperties"]; /** * This function receives the relevant document prototype and uses @@ -516,6 +523,30 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.TEXT), text, options); } + export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) { + const doc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, { isBackground: true, isButton: true, removeDropProperties: new List(["isBackground", "isButton"]), ...options }); + const linkDocProto = Doc.GetProto(doc); + linkDocProto.anchor1 = source.doc; + linkDocProto.anchor2 = target.doc; + linkDocProto.anchor1Context = source.ctx; + linkDocProto.anchor2Context = target.ctx; + linkDocProto.anchor1Timecode = source.doc.currentTimecode; + linkDocProto.anchor2Timecode = target.doc.currentTimecode; + + if (linkDocProto.layout_key1 === undefined) { + Cast(linkDocProto.proto, Doc, null)!.layout_key1 = DocuLinkBox.LayoutString("anchor1"); + Cast(linkDocProto.proto, Doc, null)!.layout_key2 = DocuLinkBox.LayoutString("anchor2"); + Cast(linkDocProto.proto, Doc, null)!.linkBoxExcludedKeys = new List(["treeViewExpandedView", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "proto", "aliasNumber", "title", "isPrototype", "lastOpened", "creationDate", "author"]); + Cast(linkDocProto.proto, Doc, null)!.layoutKey = undefined; + } + + LinkManager.Instance.addLink(doc); + + Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)"); + Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)"); + return doc; + } + export function InkDocument(color: string, tool: number, strokeWidth: number, points: { X: number, Y: number }[], options: DocumentOptions = {}) { const doc = InstanceFromProto(Prototypes.get(DocumentType.INK), new InkField(points), options); doc.color = color; @@ -870,34 +901,11 @@ export namespace DocUtils { if (sv && sv.props.ContainingCollectionDoc === target.doc) return; if (target.doc === CurrentUserUtils.UserDocument) return undefined; - const linkDocProto = new Doc(id, true); - UndoManager.RunInBatch(() => { - linkDocProto.type = DocumentType.LINK; + const linkDoc = Docs.Create.LinkDocument(source, target, { title }, id); - linkDocProto.title = title === "" ? source.doc.title + " to " + target.doc.title : title; - linkDocProto.linkDescription = description; - linkDocProto.isPrototype = true; - - linkDocProto.anchor1 = source.doc; - linkDocProto.anchor2 = target.doc; - linkDocProto.anchor1Context = source.ctx; - linkDocProto.anchor2Context = target.ctx; - linkDocProto.anchor1Groups = new List([]); - linkDocProto.anchor2Groups = new List([]); - linkDocProto.anchor1Timecode = source.doc.currentTimecode; - linkDocProto.anchor2Timecode = target.doc.currentTimecode; - linkDocProto.layout_key1 = DocuLinkBox.LayoutString("anchor1"); - linkDocProto.layout_key2 = DocuLinkBox.LayoutString("anchor2"); - linkDocProto.width = linkDocProto.height = 0; - linkDocProto.isBackground = true; - linkDocProto.isButton = true; - - LinkManager.Instance.addLink(linkDocProto); - - Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)"); - Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)"); - }, "make link"); - return linkDocProto; + Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)"); + Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)"); + return linkDoc; } export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number): void { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 1a793b524..1c328c04e 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -135,35 +135,13 @@ export class LinkManager { 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, null))) { - linkDoc.anchor1Groups = new List(groups); - } else { - linkDoc.anchor2Groups = new List(groups); - } - } - public addGroupToAnchor(linkDoc: Doc, anchor: Doc, groupDoc: Doc, replace: boolean = false) { - const groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); - const index = groups.findIndex(gDoc => { - return StrCast(groupDoc.title).toUpperCase() === StrCast(gDoc.title).toUpperCase(); - }); - if (index > -1 && replace) { - groups[index] = groupDoc; - } - if (index === -1) { - groups.push(groupDoc); - } - LinkManager.Instance.setAnchorGroups(linkDoc, anchor, groups); + linkDoc.title = groupDoc.title; } // removes group doc of given group type only from given anchor on given link public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { - const groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); - const newGroups = groups.filter(groupDoc => StrCast(groupDoc.title).toUpperCase() !== groupType.toUpperCase()); - LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); + linkDoc.title = "-ungrouped-"; } // returns map of group type to anchor's links in that group type @@ -171,19 +149,10 @@ export class LinkManager { const related = this.getAllRelatedLinks(anchor); const anchorGroups = new Map>(); related.forEach(link => { - const groups = LinkManager.Instance.getAnchorGroups(link, anchor); - - if (groups.length > 0) { - groups.forEach(groupDoc => { - const groupType = StrCast(groupDoc.title); - if (groupType === "") { - const group = anchorGroups.get("*"); - anchorGroups.set("*", group ? [...group, link] : [link]); - } else { - const group = anchorGroups.get(groupType); - anchorGroups.set(groupType, group ? [...group, link] : [link]); - } - }); + if (link.title && link.title !== "-ungrouped-") { + const group = anchorGroups.get(StrCast(link.title)); + anchorGroups.set(StrCast(link.title), group ? [...group, link] : [link]); + } else { // if link is in no groups then put it in default group const group = anchorGroups.get("*"); @@ -215,10 +184,7 @@ export class LinkManager { const md: Doc[] = []; const allLinks = LinkManager.Instance.getAllLinks(); allLinks.forEach(linkDoc => { - const anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, null)); - const anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, null)); - anchor1Groups.forEach(groupDoc => { if (StrCast(groupDoc.title).toUpperCase() === groupType.toUpperCase()) { md.push(groupDoc); } }); - anchor2Groups.forEach(groupDoc => { if (StrCast(groupDoc.title).toUpperCase() === groupType.toUpperCase()) { md.push(groupDoc); } }); + if (StrCast(linkDoc.title).toUpperCase() === groupType.toUpperCase()) { md.push(linkDoc); } }); return md; } diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index c8ad78f47..b1cc8f26a 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -120,7 +120,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | const linkDoc = dropEv.linkDragData?.linkDocument as Doc; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop if (this.view0 && linkDoc) { const proto = Doc.GetProto(linkDoc); - proto.sourceContext = this.view0.props.ContainingCollectionDoc; + proto.anchor1Context = this.view0.props.ContainingCollectionDoc; const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-"; const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : ""; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 07cc1d984..61c199c1d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -77,11 +77,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> var [sptX, sptY] = transform.transformPoint(0, 0); let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight()); if (documentView.props.Document.type === DocumentType.LINK) { - const rect = documentView.ContentDiv!.getElementsByClassName("docuLinkBox-cont")[0].getBoundingClientRect(); - sptX = rect.left; - sptY = rect.top; - bptX = rect.right; - bptY = rect.bottom; + const docuBox = documentView.ContentDiv!.getElementsByClassName("docuLinkBox-cont"); + if (docuBox.length) { + const rect = docuBox[0].getBoundingClientRect(); + sptX = rect.left; + sptY = rect.top; + bptX = rect.right; + bptY = rect.bottom; + } } return { x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index cc75a68fe..8697c601a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -278,6 +278,7 @@ export class MainView extends React.Component { if (this.darkScheme) { switch (doc.type) { case DocumentType.TEXT || DocumentType.BUTTON: return "#2d2d2d"; + case DocumentType.LINK: case DocumentType.COL: { if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)"; } @@ -287,6 +288,7 @@ export class MainView extends React.Component { switch (doc.type) { case DocumentType.TEXT: return "#f1efeb"; case DocumentType.BUTTON: return "lightgray"; + case DocumentType.LINK: case DocumentType.COL: { if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "lightgray"; } diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index 153bbd410..670d6dbb2 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -3,9 +3,9 @@ import { ReactTableDefaults, TableCellRenderer, RowInfo } from "react-table"; import "./CollectionSchemaView.scss"; import { Transform } from "../../util/Transform"; import { Doc } from "../../../new_fields/Doc"; -import { DragManager, SetupDrag } from "../../util/DragManager"; +import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager"; import { SelectionManager } from "../../util/SelectionManager"; -import { Cast, FieldValue } from "../../../new_fields/Types"; +import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { ContextMenu } from "../ContextMenu"; import { action } from "mobx"; import { library } from '@fortawesome/fontawesome-svg-core'; @@ -135,6 +135,7 @@ export interface MovableRowProps { rowFocused: boolean; textWrapRow: (doc: Doc) => void; rowWrapped: boolean; + dropAction: string; } export class MovableRow extends React.Component { @@ -219,7 +220,7 @@ export class MovableRow extends React.Component { if (!doc) return <>; const reference = React.createRef(); - const onItemDown = SetupDrag(reference, () => doc, this.move); + const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType); let className = "collectionSchema-row"; if (this.props.rowFocused) className += " row-focused"; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 7dee7f18f..9486d195a 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -408,7 +408,8 @@ export class SchemaTable extends React.Component { rowInfo, rowFocused: !this._headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document), textWrapRow: this.toggleTextWrapRow, - rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1 + rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1, + dropAction: StrCast(this.props.Document.childDropAction) }; } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index bebd99a3a..27a1c6367 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -41,6 +41,8 @@ export interface CollectionViewProps extends FieldViewProps { export interface SubCollectionViewProps extends CollectionViewProps { CollectionView: Opt; children?: never | (() => JSX.Element[]) | React.ReactNode; + overrideDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explict list (see LinkBox) + ignoreFields?: string[]; // used in TreeView to ignore specified fields (see LinkBox) isAnnotationOverlay?: boolean; annotationsKey: string; layoutEngine?: () => string; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index dd622bf9d..15ad49e26 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -34,7 +34,6 @@ import "./CollectionTreeView.scss"; import React = require("react"); import { CollectionViewType } from './CollectionView'; import { RichTextField } from '../../../new_fields/RichTextField'; -import { ObjectField } from '../../../new_fields/ObjectField'; export interface TreeViewProps { @@ -65,6 +64,7 @@ export interface TreeViewProps { treeViewPreventOpen: boolean; renderedIds: string[]; onCheckedClick?: ScriptField; + ignoreFields?: string[]; } library.add(faTrashAlt); @@ -283,6 +283,7 @@ class TreeView extends React.Component { const rows: JSX.Element[] = []; for (const key of Object.keys(ids).slice().sort()) { + if (this.props.ignoreFields?.includes(key)) continue; const contents = doc[key]; let contentElement: (JSX.Element | null)[] | JSX.Element = []; @@ -293,11 +294,11 @@ class TreeView extends React.Component { DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, - [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick); + [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.ignoreFields); } else { contentElement = Field.toKeyValueString(doc, key)} @@ -336,7 +337,7 @@ class TreeView extends React.Component { this.templateDataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, - [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick)} + [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.ignoreFields)} ; } else if (this.treeViewExpandedView === "fields") { return
    @@ -470,7 +471,8 @@ class TreeView extends React.Component { treeViewPreventOpen: boolean, renderedIds: string[], libraryPath: Doc[] | undefined, - onCheckedClick: ScriptField | undefined + onCheckedClick: ScriptField | undefined, + ignoreFields: string[] | undefined ) { const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField); if (viewSpecScript) { @@ -579,7 +581,8 @@ class TreeView extends React.Component { active={active} treeViewHideHeaderFields={treeViewHideHeaderFields} treeViewPreventOpen={treeViewPreventOpen} - renderedIds={renderedIds} />; + renderedIds={renderedIds} + ignoreFields={ignoreFields} />; }); } } @@ -717,7 +720,8 @@ export class CollectionTreeView extends CollectionSubView(Document) { const dropAction = StrCast(this.props.Document.dropAction) as dropActionType; const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); - return !this.childDocs ? (null) : ( + const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs; + return !childDocs ? (null) : (
    ([Templates.Title.Layout]) }); EditableView.loadId = doc[Id]; - this.addDoc(doc, this.childDocs.length ? this.childDocs[0] : undefined, true); + this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true); })} />)} {this.props.Document.allowClear ? this.renderClearButton : (null)}
      { - TreeView.GetChildElements(this.childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, + TreeView.GetChildElements(childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => BoolCast(this.props.Document.treeViewHideHeaderFields), - BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick)) + BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick), this.props.ignoreFields) }
    diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 3a5cba81b..3c3767832 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -166,7 +166,7 @@ class LinkMetadataEditor extends React.Component { setMetadataValue = (value: string): void => { if (!this._keyError) { this._value = value; - this.props.mdDoc[this._key] = value; + Doc.GetProto(this.props.mdDoc)[this._key] = value; } } @@ -343,25 +343,10 @@ export class LinkEditor extends React.Component { this.props.showLinks(); } - @action - addGroup = (): void => { - // create new metadata document for group - // create new group document - const groupDoc = new Doc(); - groupDoc.anchor1 = this.props.sourceDoc; - const opp = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); - if (opp) { - groupDoc.anchor2 = opp; - } - - LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, this.props.sourceDoc, groupDoc); - } - render() { const destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); - const groupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); - const groups = groupList.map(groupDoc => { + const groups = [this.props.linkDoc].map(groupDoc => { return ; }); @@ -374,7 +359,6 @@ export class LinkEditor extends React.Component {
    Relationships: -
    {groups.length > 0 ? groups :
    There are currently no relationships associated with this link.
    }
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index 78c77e106..88f837a03 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -58,7 +58,7 @@ export class LinkMenuGroup extends React.Component { if (index > -1) keys.splice(index, 1); const cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb")); const docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); - const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { _width: 500, _height: 300, title: groupType + " table" })); + const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { _width: 500, _height: 300, title: groupType + " table", childDropAction: "alias" })); const ref = React.createRef(); return
; } diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 43a7daf00..7dd2c0fa8 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -39,9 +39,8 @@ export class LinkMenuItem extends React.Component { } renderMetadata = (): JSX.Element => { - const groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); - const index = groups.findIndex(groupDoc => StrCast(groupDoc.title).toUpperCase() === this.props.groupType.toUpperCase()); - const groupDoc = index > -1 ? groups[index] : undefined; + const index = StrCast(this.props.linkDoc.title).toUpperCase() === this.props.groupType.toUpperCase() ? 0 : -1; + const groupDoc = index > -1 ? this.props.linkDoc : undefined; let mdRows: Array = []; if (groupDoc) { diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx index a0b5cd8ec..1983ae529 100644 --- a/src/client/views/nodes/DocuLinkBox.tsx +++ b/src/client/views/nodes/DocuLinkBox.tsx @@ -1,6 +1,6 @@ import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; import { documentSchema } from "../../../new_fields/documentSchemas"; import { makeInterface } from "../../../new_fields/Schema"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; @@ -11,6 +11,8 @@ import { DocComponent } from "../DocComponent"; import "./DocuLinkBox.scss"; import { FieldView, FieldViewProps } from "./FieldView"; import React = require("react"); +import { ContextMenuProps } from "../ContextMenuItem"; +import { ContextMenu } from "../ContextMenu"; type DocLinkSchema = makeInterface<[typeof documentSchema]>; const DocLinkDocument = makeInterface(documentSchema); @@ -61,12 +63,27 @@ export class DocuLinkBox extends DocComponent(Doc onClick = (e: React.MouseEvent) => { if (!this.props.Document.onClick) { if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey && this.props.Document.isButton)) { - DocumentManager.Instance.FollowLink(this.props.Document, this.props.ContainingCollectionDoc as Doc, document => this.props.addDocTab(document, "inTab"), false); + if (this.props.Document.linkTarget === "doc") { + const alias = Doc.MakeAlias(this.props.Document); + alias.isButton = undefined; + alias.isBackground = undefined; + this.props.addDocTab(alias, StrCast(this.props.Document.linkOpenLocation, "inTab")); + } else { + DocumentManager.Instance.FollowLink(this.props.Document, this.props.ContainingCollectionDoc as Doc, document => this.props.addDocTab(document, StrCast(this.props.Document.linkOpenLocation, "inTab")), false); + } } e.stopPropagation(); } } + specificContextMenu = (e: React.MouseEvent): void => { + const funcs: ContextMenuProps[] = []; + funcs.push({ description: "Open Target " + (this.props.Document.linkOpenLocation !== "onRight" ? "on Right" : "in Tab"), event: () => { e.stopPropagation(); this.props.Document.linkOpenLocation = this.props.Document.linkOpenLocation !== "onRight" ? "onRight" : "inTab" }, icon: "eye" }); + funcs.push({ description: this.props.Document.linkTarget === "doc" ? "Open Link Target" : "Open Link Doc", event: () => { e.stopPropagation(); this.props.Document.linkTarget = this.props.Document.linkTarget === "doc" ? "anchor" : "doc" }, icon: "eye" }); + + ContextMenu.Instance.addItem({ description: "Link Funcs...", subitems: funcs, icon: "asterisk" }); + } + render() { const x = NumCast(this.props.Document[this.props.fieldKey + "_x"], 100); const y = NumCast(this.props.Document[this.props.fieldKey + "_y"], 100); @@ -76,7 +93,7 @@ export class DocuLinkBox extends DocComponent(Doc const timecode = this.props.Document[anchor + "Timecode"]; const targetTitle = StrCast((this.props.Document[anchor]! as Doc).title) + (timecode !== undefined ? ":" + timecode : ""); - return
; const DocBoxDocument = makeInterface(documentSchema); @observer -export class DocumentBox extends DocComponent(DocBoxDocument) { +export class DocumentBox extends DocAnnotatableComponent(DocBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocumentBox, fieldKey); } _prevSelectionDisposer: IReactionDisposer | undefined; _selections: Doc[] = []; @@ -80,7 +80,7 @@ export class DocumentBox extends DocComponent(DocB pheight = () => this.props.PanelHeight() - 30; getTransform = () => this.props.ScreenToLocalTransform().translate(-15, -15); render() { - const containedDoc = this.props.Document[this.props.fieldKey] as Doc; + const containedDoc = this.dataDoc[this.props.fieldKey] as Doc; return
(Docu dragData.dropAction = dropAction; dragData.moveDocument = this.props.moveDocument;// this.Document.onDragStart ? undefined : this.props.moveDocument; dragData.dragDivName = this.props.dragDivName; - this.props.Document.sourceContext = this.props.ContainingCollectionDoc; // bcz: !! shouldn't need this ... use search find the document's context dynamically + this.props.Document.anchor1Context = this.props.ContainingCollectionDoc; // bcz: !! shouldn't need this ... use search find the document's context dynamically DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.Document.onDragStart }); } } @@ -534,7 +534,7 @@ export class DocumentView extends DocComponent(Docu // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true); // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView); de.complete.linkDragData.linkSourceDocument !== this.props.Document && - (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "in-text link being created")); // TODODO this is where in text links get passed + (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "-ungrouped-")); // TODODO this is where in text links get passed } } @@ -762,9 +762,8 @@ export class DocumentView extends DocComponent(Docu } @computed get finalLayoutKey() { - const { layoutKey } = this.props; - if (typeof layoutKey === "string") { - return layoutKey; + if (typeof this.props.layoutKey === "string") { + return this.props.layoutKey; } const fallback = Cast(this.props.Document.layoutKey, "string"); return typeof fallback === "string" ? fallback : "layout"; diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 55d23da12..033511af4 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -114,7 +114,7 @@ export class FieldView extends React.Component { // return // } else if (!(field instanceof Promise)) { - return

{field.toString()}

; + return

{Field.toString(field)}

; } else { return

{"Waiting for server..."}

; diff --git a/src/client/views/nodes/LinkBox.scss b/src/client/views/nodes/LinkBox.scss new file mode 100644 index 000000000..b5b8e660f --- /dev/null +++ b/src/client/views/nodes/LinkBox.scss @@ -0,0 +1,3 @@ +.linkBox-container-interactive { + pointer-events: all; +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx new file mode 100644 index 000000000..0e327e130 --- /dev/null +++ b/src/client/views/nodes/LinkBox.tsx @@ -0,0 +1,35 @@ +import React = require("react"); +import { observer } from "mobx-react"; +import { documentSchema } from "../../../new_fields/documentSchemas"; +import { makeInterface, listSpec } from "../../../new_fields/Schema"; +import { returnFalse, returnZero } from "../../../Utils"; +import { CollectionTreeView } from "../collections/CollectionTreeView"; +import { DocExtendableComponent } from "../DocComponent"; +import { FieldView, FieldViewProps } from './FieldView'; +import "./LinkBox.scss"; +import { Cast } from "../../../new_fields/Types"; + +type LinkDocument = makeInterface<[typeof documentSchema]>; +const LinkDocument = makeInterface(documentSchema); + +@observer +export class LinkBox extends DocExtendableComponent(LinkDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); } + render() { + return
e.button === 0 && !e.ctrlKey && e.stopPropagation()} + style={{ background: this.props.backgroundColor?.(this.props.Document) }} > + + + +
; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 8d20bbe59..791ed5ef1 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -187,7 +187,7 @@ export class PresBox extends React.Component { //docToJump stayed same meaning, it was not in the group or was the last element in the group const aliasOf = await Cast(docToJump.aliasOf, Doc); - const srcContext = aliasOf && await Cast(aliasOf.sourceContext, Doc); + const srcContext = aliasOf && await Cast(aliasOf.anchor1Context, Doc); if (docToJump === curDoc) { //checking if curDoc has navigation open const target = await Cast(curDoc.presentationTargetDoc, Doc); diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts index 566104b40..9aa1c9b04 100644 --- a/src/new_fields/ObjectField.ts +++ b/src/new_fields/ObjectField.ts @@ -1,4 +1,3 @@ -import { Doc } from "./Doc"; import { RefField } from "./RefField"; import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols"; import { Scripting } from "../client/util/Scripting"; -- cgit v1.2.3-70-g09d2 From 72892f0811f03dae096ebea2dcac24b56d661b07 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 20 Feb 2020 23:45:59 -0500 Subject: more link editor fixes. fixed border rounding. --- src/client/documents/Documents.ts | 2 +- src/client/views/DocumentDecorations.tsx | 2 +- .../CollectionFreeFormLinkView.tsx | 9 ++- src/client/views/linking/LinkEditor.scss | 22 ++++--- src/client/views/linking/LinkEditor.tsx | 73 +++------------------- src/client/views/linking/LinkMenu.tsx | 2 +- src/client/views/linking/LinkMenuItem.tsx | 38 +++-------- 7 files changed, 39 insertions(+), 109 deletions(-) (limited to 'src/client/views/linking/LinkEditor.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2d2de54b7..9c83a6a9e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -85,7 +85,7 @@ export interface DocumentOptions { y?: number; z?: number; dropAction?: dropActionType; - chilDropAction?: dropActionType; + childDropAction?: dropActionType; layoutKey?: string; type?: string; title?: string; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 61c199c1d..dcd8dd113 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -256,7 +256,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let dist = Math.sqrt((e.clientX - down[0]) * (e.clientX - down[0]) + (e.clientY - down[1]) * (e.clientY - down[1])); dist = dist < 3 ? 0 : dist; SelectionManager.SelectedDocuments().map(dv => dv.props.Document).map(doc => doc.layout instanceof Doc ? doc.layout : doc.isTemplateForField ? doc : Doc.GetProto(doc)). - map(d => d.borderRounding = `${Math.min(100, dist)}%`); + map(d => d.borderRounding = `${Math.max(0, dist)}px`); return false; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index f04b79ea4..f0c706c1e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -95,10 +95,15 @@ export class CollectionFreeFormLinkView extends React.Component + + {text !== "-ungrouped-" ? text : ""} + ; + x2={`${pt2[0]}`} y2={`${pt2[1]}`} /> + ); } } \ No newline at end of file diff --git a/src/client/views/linking/LinkEditor.scss b/src/client/views/linking/LinkEditor.scss index fc5f2410c..0807216c9 100644 --- a/src/client/views/linking/LinkEditor.scss +++ b/src/client/views/linking/LinkEditor.scss @@ -22,10 +22,9 @@ } } -.linkEditor-button { - width: 20px; - height: 20px; - margin-left: 6px; +.linkEditor-button, .linkEditor-addbutton { + width: 18px; + height: 18px; padding: 0; // font-size: 12px; border-radius: 10px; @@ -34,6 +33,9 @@ background-color: gray; } } +.linkEditor-addbutton{ + margin-left: 0px; +} .linkEditor-groupsLabel { display: flex; @@ -49,10 +51,11 @@ .linkEditor-group-row { display: flex; margin-bottom: 3px; + } - .linkEditor-group-row-label { - margin-right: 6px; - } + .linkEditor-group-row-label { + margin-right: 6px; + display:inline-block; } .linkEditor-metadata-row { @@ -118,7 +121,6 @@ .linkEditor-typeButton { background-color: transparent; color: $dark-color; - width: 100%; height: 20px; padding: 0 3px; padding-bottom: 2px; @@ -127,6 +129,8 @@ letter-spacing: normal; font-size: 12px; font-weight: bold; + display: inline-block; + width: calc(100% - 40px); &:hover { background-color: $light-color; @@ -140,6 +144,6 @@ margin-top: 5px; .linkEditor-button { - margin-left: 6px; + margin-left: 3px; } } \ No newline at end of file diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 3c3767832..6cdd11430 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -108,7 +108,7 @@ class GroupTypesDropdown extends React.Component { if (this._isEditing || this._groupType === "") { return (
- this.onChange(e.target.value)} onKeyDown={this.onKeyDown} autoFocus>
{this.renderOptions()} @@ -187,7 +187,7 @@ class LinkMetadataEditor extends React.Component {
this.setMetadataKey(e.target.value)}>: this.setMetadataValue(e.target.value)}> - +
); } @@ -223,29 +223,6 @@ export class LinkGroupEditor extends React.Component { LinkManager.Instance.deleteGroupType(groupType); } - copyGroup = async (groupType: string): Promise => { - const sourceGroupDoc = this.props.groupDoc; - - const destDoc = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); - // let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc); - const keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); - - - // create new group doc with new metadata doc - const destGroupDoc = new Doc(); - destGroupDoc.title = groupType; - // create new metadata doc with copied kvp - destGroupDoc.anchor1 = sourceGroupDoc.anchor2; - destGroupDoc.anchor2 = sourceGroupDoc.anchor1; - keys.forEach(key => { - const val = sourceGroupDoc[key] === undefined ? "" : StrCast(sourceGroupDoc[key]); - destGroupDoc[key] = val; - }); - - if (destDoc) { - LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, destDoc, destGroupDoc, true); - } - } @action addMetadata = (groupType: string): void => { @@ -276,53 +253,22 @@ export class LinkGroupEditor extends React.Component { return metadata; } - viewGroupAsTable = (groupType: string): JSX.Element => { - const keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); - const index = keys.indexOf(""); - if (index > -1) keys.splice(index, 1); - const cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb")); - const docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); - const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { _width: 500, _height: 300, title: groupType + " table" })); - const ref = React.createRef(); - return
; - } - render() { const groupType = StrCast(this.props.groupDoc.title); // if ((groupType && LinkManager.Instance.getMetadataKeysInGroup(groupType).length > 0) || groupType === "") { - let buttons; - if (groupType === "") { - buttons = ( - <> - - - - - - - ); - } else { - buttons = ( - <> - - - - - {this.viewGroupAsTable(groupType)} - - ); - } + let buttons = ; + let addButton = ; + return (
-

type:

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

metadata:

: <>} + {addButton} {this.renderMetadata()} -
- {buttons} -
); } @@ -357,9 +303,6 @@ 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/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index a8aa01306..b768eacc3 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -60,7 +60,7 @@ export class LinkMenu extends React.Component { if (this._editingLink === undefined) { return (
- + {/* */} {/* */}
{this.renderAllGroups(groups)} diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 7dd2c0fa8..5bf2e4547 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -11,6 +11,7 @@ import { ContextMenu } from '../ContextMenu'; import { LinkFollowBox } from './LinkFollowBox'; import './LinkMenuItem.scss'; import React = require("react"); +import { DocumentManager } from '../../util/DocumentManager'; library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); @@ -30,7 +31,7 @@ export class LinkMenuItem extends React.Component { private _downY = 0; private _eleClone: any; @observable private _showMore: boolean = false; - @action toggleShowMore() { this._showMore = !this._showMore; } + @action toggleShowMore(e: React.PointerEvent) { e.stopPropagation(); this._showMore = !this._showMore; } onEdit = (e: React.PointerEvent): void => { e.stopPropagation(); @@ -40,17 +41,12 @@ export class LinkMenuItem extends React.Component { renderMetadata = (): JSX.Element => { const index = StrCast(this.props.linkDoc.title).toUpperCase() === this.props.groupType.toUpperCase() ? 0 : -1; - const groupDoc = index > -1 ? this.props.linkDoc : undefined; + const mdDoc = index > -1 ? this.props.linkDoc : undefined; let mdRows: Array = []; - if (groupDoc) { - const mdDoc = groupDoc; - if (mdDoc) { - const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType); - mdRows = keys.map(key => { - return (
{key}: {StrCast(mdDoc[key])}
); - }); - } + if (mdDoc) { + const keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType); + mdRows = keys.map(key =>
{key}: {StrCast(mdDoc[key])}
); } return (
{mdRows}
); @@ -71,11 +67,6 @@ export class LinkMenuItem extends React.Component { document.removeEventListener("pointermove", this.onLinkButtonMoved); document.removeEventListener("pointerup", this.onLinkButtonUp); - if (LinkFollowBox.Instance !== undefined) { - LinkFollowBox.Instance.props.Document.isMinimized = false; - LinkFollowBox.Instance.setLinkDocs(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc); - LinkFollowBox.setAddDocTab(this.props.addDocTab); - } e.stopPropagation(); } @@ -92,26 +83,13 @@ export class LinkMenuItem extends React.Component { onContextMenu = (e: React.MouseEvent) => { e.preventDefault(); - ContextMenu.Instance.addItem({ description: "Open in Link Follower", event: () => this.openLinkFollower(), icon: "link" }); ContextMenu.Instance.addItem({ description: "Follow Default Link", event: () => this.followDefault(), icon: "arrow-right" }); ContextMenu.Instance.displayMenu(e.clientX, e.clientY); } @action.bound async followDefault() { - if (LinkFollowBox.Instance !== undefined) { - LinkFollowBox.setAddDocTab(this.props.addDocTab); - LinkFollowBox.Instance.setLinkDocs(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc); - LinkFollowBox.Instance.defaultLinkBehavior(); - } - } - - @action.bound - async openLinkFollower() { - if (LinkFollowBox.Instance !== undefined) { - LinkFollowBox.Instance.props.Document.isMinimized = false; - LinkFollowBox.Instance.setLinkDocs(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc); - } + DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false); } render() { @@ -124,7 +102,7 @@ export class LinkMenuItem extends React.Component {

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

- {canExpand ?
this.toggleShowMore()}> + {canExpand ?
this.toggleShowMore(e)}>
: <>}
-- cgit v1.2.3-70-g09d2 From 8274233572d5ff299e13868c86bd2436f4c276fc Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 21 Feb 2020 09:32:23 -0500 Subject: fixed link naming and exposed link database (at least for testing) --- src/client/documents/Documents.ts | 16 ++++++++++------ src/client/util/LinkManager.ts | 18 +++++++++--------- src/client/views/DocumentButtonBar.tsx | 5 +++-- src/client/views/GestureOverlay.tsx | 2 +- src/client/views/collections/CollectionTreeView.tsx | 4 ++-- src/client/views/linking/LinkEditor.tsx | 10 +++++----- src/client/views/nodes/DocuLinkBox.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 5 +++-- src/server/authentication/models/current_user_utils.ts | 2 +- 9 files changed, 35 insertions(+), 28 deletions(-) (limited to 'src/client/views/linking/LinkEditor.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9c83a6a9e..4bad24be9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -119,6 +119,7 @@ export interface DocumentOptions { annotationOn?: Doc; removeDropProperties?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document dbDoc?: Doc; + linkRelationship?: string; // type of relatinoship a link represents ischecked?: ScriptField; // returns whether a font icon box is checked activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts) onClick?: ScriptField; @@ -225,11 +226,12 @@ export namespace Docs { }], [DocumentType.LINK, { layout: { view: LinkBox, dataField: data }, - options: { _height: 75 } + options: { _height: 150 } }], [DocumentType.LINKDOC, { data: new List(), layout: { view: EmptyBox, dataField: data }, + options: { childDropAction: "alias", title: "LINK DB" } }], [DocumentType.YOUTUBE, { layout: { view: YoutubeBox, dataField: data } @@ -407,7 +409,8 @@ export namespace Docs { Scripting.addGlobal(Buxton); const delegateKeys = ["x", "y", "layoutKey", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "dropAction", "childDropAction", "_annotationOn", - "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "_showTitle", "_showCaption", "_showTitleHover", "isButton", "isBackground", "removeDropProperties"]; + "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "_showTitle", "_showCaption", "_showTitleHover", + "isButton", "isBackground", "removeDropProperties", "treeViewOpen"]; /** * This function receives the relevant document prototype and uses @@ -524,7 +527,7 @@ export namespace Docs { } export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) { - const doc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, { isBackground: true, isButton: true, removeDropProperties: new List(["isBackground", "isButton"]), ...options }); + const doc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, { isButton: true, treeViewHideTitle: true, treeViewOpen: false, removeDropProperties: new List(["isBackground", "isButton"]), ...options }); const linkDocProto = Doc.GetProto(doc); linkDocProto.anchor1 = source.doc; linkDocProto.anchor2 = target.doc; @@ -536,7 +539,7 @@ export namespace Docs { if (linkDocProto.layout_key1 === undefined) { Cast(linkDocProto.proto, Doc, null)!.layout_key1 = DocuLinkBox.LayoutString("anchor1"); Cast(linkDocProto.proto, Doc, null)!.layout_key2 = DocuLinkBox.LayoutString("anchor2"); - Cast(linkDocProto.proto, Doc, null)!.linkBoxExcludedKeys = new List(["treeViewExpandedView", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "proto", "aliasNumber", "title", "isPrototype", "lastOpened", "creationDate", "author"]); + Cast(linkDocProto.proto, Doc, null)!.linkBoxExcludedKeys = new List(["treeViewExpandedView", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "proto", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]); Cast(linkDocProto.proto, Doc, null)!.layoutKey = undefined; } @@ -896,12 +899,13 @@ export namespace DocUtils { }); } - export function MakeLink(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, title: string = "", description: string = "", id?: string) { + export function MakeLink(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, title: string = "", linkRelationship: string = "", id?: string) { const sv = DocumentManager.Instance.getDocumentView(source.doc); if (sv && sv.props.ContainingCollectionDoc === target.doc) return; if (target.doc === CurrentUserUtils.UserDocument) return undefined; - const linkDoc = Docs.Create.LinkDocument(source, target, { title }, id); + const linkDoc = Docs.Create.LinkDocument(source, target, { title, linkRelationship }, id); + Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('this.anchor1.title +" " + (this.linkRelationship||"to") +" " + this.anchor2.title'); Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)"); Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)"); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 1c328c04e..4457f41e2 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -40,7 +40,7 @@ export class LinkManager { public getAllLinks(): Doc[] { const ldoc = LinkManager.Instance.LinkManagerDoc; if (ldoc) { - const docs = DocListCast(ldoc.allLinks); + const docs = DocListCast(ldoc.data); return docs; } return []; @@ -50,7 +50,7 @@ export class LinkManager { const linkList = LinkManager.Instance.getAllLinks(); linkList.push(linkDoc); if (LinkManager.Instance.LinkManagerDoc) { - LinkManager.Instance.LinkManagerDoc.allLinks = new List(linkList); + LinkManager.Instance.LinkManagerDoc.data = new List(linkList); return true; } return false; @@ -62,7 +62,7 @@ export class LinkManager { if (index > -1) { linkList.splice(index, 1); if (LinkManager.Instance.LinkManagerDoc) { - LinkManager.Instance.LinkManagerDoc.allLinks = new List(linkList); + LinkManager.Instance.LinkManagerDoc.data = new List(linkList); return true; } } @@ -136,12 +136,12 @@ export class LinkManager { } } public addGroupToAnchor(linkDoc: Doc, anchor: Doc, groupDoc: Doc, replace: boolean = false) { - linkDoc.title = groupDoc.title; + linkDoc.linkRelationship = groupDoc.linkRelationship; } // removes group doc of given group type only from given anchor on given link public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { - linkDoc.title = "-ungrouped-"; + linkDoc.linkRelationship = "-ungrouped-"; } // returns map of group type to anchor's links in that group type @@ -149,9 +149,9 @@ export class LinkManager { const related = this.getAllRelatedLinks(anchor); const anchorGroups = new Map>(); related.forEach(link => { - if (link.title && link.title !== "-ungrouped-") { - const group = anchorGroups.get(StrCast(link.title)); - anchorGroups.set(StrCast(link.title), group ? [...group, link] : [link]); + if (!link.linkRelationship || link?.linkRelationship !== "-ungrouped-") { + const group = anchorGroups.get(StrCast(link.linkRelationship)); + anchorGroups.set(StrCast(link.linkRelationship), group ? [...group, link] : [link]); } else { // if link is in no groups then put it in default group @@ -184,7 +184,7 @@ export class LinkManager { const md: Doc[] = []; const allLinks = LinkManager.Instance.getAllLinks(); allLinks.forEach(linkDoc => { - if (StrCast(linkDoc.title).toUpperCase() === groupType.toUpperCase()) { md.push(linkDoc); } + if (StrCast(linkDoc.linkRelationship).toUpperCase() === groupType.toUpperCase()) { md.push(linkDoc); } }); return md; } diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index b1cc8f26a..a3d313224 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -25,6 +25,7 @@ import { DragManager } from '../util/DragManager'; import { MetadataEntryMenu } from './MetadataEntryMenu'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; +import { ComputedField } from '../../new_fields/ScriptField'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -125,8 +126,8 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-"; const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : ""; const text = RichTextMenu.Instance.MakeLinkToSelection(linkDoc[Id], anchor2Title, e.ctrlKey ? "onRight" : "inTab", anchor2Id); - if (linkDoc.anchor2 instanceof Doc) { - proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODO open to more descriptive descriptions of following in text link + if (linkDoc.anchor2 instanceof Doc && !proto.title) { + proto.title = Doc.GetProto(linkDoc).title = ComputedField.MakeFunction('this.anchor1.title +" " + (this.linkRelationship||"to") +" " + this.anchor2.title'); } } linkDrag?.end(); diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 580c53a37..a8cf8c197 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -333,7 +333,7 @@ export default class GestureOverlay extends Touchable { this._d1 = doc; } else if (this._d1 !== doc && !LinkManager.Instance.doesLinkExist(this._d1, doc)) { - DocUtils.MakeLink({ doc: this._d1 }, { doc: doc }); + DocUtils.MakeLink({ doc: this._d1 }, { doc: doc }, "gestural link"); actionPerformed = true; } }; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 15ad49e26..ee64b7d09 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -128,7 +128,7 @@ class TreeView extends React.Component { } @undoBatch delete = () => this.props.deleteDoc(this.props.document); - @undoBatch openRight = () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath); + @undoBatch openRight = () => this.props.addDocTab(this.props.containingCollection.childDropAction === "alias" ? Doc.MakeAlias(this.props.document) : this.props.document, "onRight", this.props.libraryPath); @undoBatch indent = () => this.props.addDocument(this.props.document) && this.delete(); @undoBatch move = (doc: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => { return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc); @@ -229,7 +229,7 @@ class TreeView extends React.Component { if (de.complete.linkDragData) { const sourceDoc = de.complete.linkDragData.linkSourceDocument; const destDoc = this.props.document; - DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }); + DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree drop link"); e.stopPropagation(); } if (de.complete.docDragData) { diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 6cdd11430..913a04d66 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -206,13 +206,13 @@ export class LinkGroupEditor extends React.Component { constructor(props: LinkGroupEditorProps) { super(props); - const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.title)); + const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.linkRelationship)); groupMdKeys.forEach(key => this._metadataIds.set(key, Utils.GenerateGuid())); } @action setGroupType = (groupType: string): void => { - this.props.groupDoc.title = groupType; + Doc.GetProto(this.props.groupDoc).linkRelationship = groupType; } removeGroupFromLink = (groupType: string): void => { @@ -241,7 +241,7 @@ export class LinkGroupEditor extends React.Component { renderMetadata = (): JSX.Element[] => { const metadata: Array = []; const groupDoc = this.props.groupDoc; - const groupType = StrCast(groupDoc.title); + const groupType = StrCast(groupDoc.linkRelationship); const groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); groupMdKeys.forEach((key) => { @@ -254,7 +254,7 @@ export class LinkGroupEditor extends React.Component { } render() { - const groupType = StrCast(this.props.groupDoc.title); + const groupType = StrCast(this.props.groupDoc.linkRelationship); // if ((groupType && LinkManager.Instance.getMetadataKeysInGroup(groupType).length > 0) || groupType === "") { let buttons = ; let addButton = ; @@ -293,7 +293,7 @@ export class LinkEditor extends React.Component { const destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); const groups = [this.props.linkDoc].map(groupDoc => { - return ; + return ; }); return !destination ? (null) : ( diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx index 1983ae529..c4d99456d 100644 --- a/src/client/views/nodes/DocuLinkBox.tsx +++ b/src/client/views/nodes/DocuLinkBox.tsx @@ -67,6 +67,7 @@ export class DocuLinkBox extends DocComponent(Doc const alias = Doc.MakeAlias(this.props.Document); alias.isButton = undefined; alias.isBackground = undefined; + alias.layoutKey = "layout"; this.props.addDocTab(alias, StrCast(this.props.Document.linkOpenLocation, "inTab")); } else { DocumentManager.Instance.FollowLink(this.props.Document, this.props.ContainingCollectionDoc as Doc, document => this.props.addDocTab(document, StrCast(this.props.Document.linkOpenLocation, "inTab")), false); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5a358b6d8..84e9b6abb 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -534,7 +534,8 @@ export class DocumentView extends DocComponent(Docu // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true); // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView); de.complete.linkDragData.linkSourceDocument !== this.props.Document && - (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "-ungrouped-")); // TODODO this is where in text links get passed + (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument }, + { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `link from ${de.complete.linkDragData.linkSourceDocument.title} to ${this.props.Document.title}`)); // TODODO this is where in text links get passed } } @@ -868,7 +869,7 @@ export class DocumentView extends DocComponent(Docu ; } @computed get ignorePointerEvents() { - return (this.Document.isBackground && !this.isSelected()) || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); + return (this.Document.isBackground && !this.isSelected()) || this.props.layoutKey?.includes("layout_key") || (this.Document.type === DocumentType.INK && InkingControl.Instance.selectedTool !== InkTool.None); } @observable _animate = 0; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 125780c8e..3bab00bed 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -189,7 +189,7 @@ export class CurrentUserUtils { return Docs.Create.ButtonDocument({ _width: 50, _height: 25, title: "Library", fontSize: 10, letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", - sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, doc.recentlyClosed as Doc], { + sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, Docs.Prototypes.MainLinkDocument(), doc.recentlyClosed as Doc], { title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, dropAction: "alias", lockedPosition: true, boxShadow: "0 0", }), targetContainer: sidebarContainer, -- cgit v1.2.3-70-g09d2 From ee80ef34d8a9354c2bfbbf4b4c233832f2ea5e26 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 21 Feb 2020 12:09:54 -0500 Subject: made double-click link editing for link anchors. --- src/client/documents/Documents.ts | 2 +- .../CollectionFreeFormLinkView.tsx | 2 +- src/client/views/linking/LinkEditor.scss | 1 + src/client/views/linking/LinkEditor.tsx | 22 +++-- src/client/views/nodes/DocuLinkBox.scss | 16 ++++ src/client/views/nodes/DocuLinkBox.tsx | 95 ++++++++++++++++------ 6 files changed, 101 insertions(+), 37 deletions(-) (limited to 'src/client/views/linking/LinkEditor.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4bad24be9..aed14e4f6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -539,7 +539,7 @@ export namespace Docs { if (linkDocProto.layout_key1 === undefined) { Cast(linkDocProto.proto, Doc, null)!.layout_key1 = DocuLinkBox.LayoutString("anchor1"); Cast(linkDocProto.proto, Doc, null)!.layout_key2 = DocuLinkBox.LayoutString("anchor2"); - Cast(linkDocProto.proto, Doc, null)!.linkBoxExcludedKeys = new List(["treeViewExpandedView", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "proto", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]); + Cast(linkDocProto.proto, Doc, null)!.linkBoxExcludedKeys = new List(["treeViewExpandedView", "treeViewHideTitle", "removeDropProperties", "linkBoxExcludedKeys", "treeViewOpen", "proto", "aliasNumber", "isPrototype", "lastOpened", "creationDate", "author"]); Cast(linkDocProto.proto, Doc, null)!.layoutKey = undefined; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index f0c706c1e..1038347d4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -95,7 +95,7 @@ export class CollectionFreeFormLinkView extends React.Component {text !== "-ungrouped-" ? text : ""} diff --git a/src/client/views/linking/LinkEditor.scss b/src/client/views/linking/LinkEditor.scss index 0807216c9..b47c8976e 100644 --- a/src/client/views/linking/LinkEditor.scss +++ b/src/client/views/linking/LinkEditor.scss @@ -4,6 +4,7 @@ width: 100%; height: auto; font-size: 12px; // TODO + user-select: none; } .linkEditor-back { diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index 913a04d66..ac4f8a3cf 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -1,17 +1,14 @@ -import { observable, computed, action, trace } from "mobx"; -import React = require("react"); +import { library } from "@fortawesome/fontawesome-svg-core"; +import { faArrowLeft, faCog, faEllipsisV, faExchangeAlt, faPlus, faTable, faTimes, faTrash } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import './LinkEditor.scss'; -import { StrCast, Cast, FieldValue } from "../../../new_fields/Types"; import { Doc } from "../../../new_fields/Doc"; -import { LinkManager } from "../../util/LinkManager"; -import { Docs } from "../../documents/Documents"; +import { StrCast } from "../../../new_fields/Types"; import { Utils } from "../../../Utils"; -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 { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; +import { LinkManager } from "../../util/LinkManager"; +import './LinkEditor.scss'; +import React = require("react"); library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTimes, faPlus); @@ -279,6 +276,7 @@ interface LinkEditorProps { sourceDoc: Doc; linkDoc: Doc; showLinks: () => void; + hideback?: boolean; } @observer export class LinkEditor extends React.Component { @@ -298,7 +296,7 @@ export class LinkEditor extends React.Component { return !destination ? (null) : (
- + {this.props.hideback ? (null) : }

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

diff --git a/src/client/views/nodes/DocuLinkBox.scss b/src/client/views/nodes/DocuLinkBox.scss index 57c1a66e0..7b91b4f36 100644 --- a/src/client/views/nodes/DocuLinkBox.scss +++ b/src/client/views/nodes/DocuLinkBox.scss @@ -5,4 +5,20 @@ height: 25px; border-radius: 20px; pointer-events: all; + user-select: none; + + .docuLinkBox-linkCloser { + position: absolute; + width: 18; + height: 18; + background: rgb(219, 21, 21); + top: -1px; + left: -1px; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; + padding-left: 2px; + padding-top: 1px; + } } \ No newline at end of file diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx index c4d99456d..42e1c9b07 100644 --- a/src/client/views/nodes/DocuLinkBox.tsx +++ b/src/client/views/nodes/DocuLinkBox.tsx @@ -13,6 +13,12 @@ import { FieldView, FieldViewProps } from "./FieldView"; import React = require("react"); import { ContextMenuProps } from "../ContextMenuItem"; import { ContextMenu } from "../ContextMenu"; +import { LinkEditor } from "../linking/LinkEditor"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { SelectionManager } from "../../util/SelectionManager"; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; type DocLinkSchema = makeInterface<[typeof documentSchema]>; const DocLinkDocument = makeInterface(documentSchema); @@ -20,16 +26,22 @@ const DocLinkDocument = makeInterface(documentSchema); @observer export class DocuLinkBox extends DocComponent(DocLinkDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DocuLinkBox, fieldKey); } - _downx = 0; - _downy = 0; + _doubleTap = false; + _lastTap: number = 0; + _ref = React.createRef(); + _downX = 0; + _downY = 0; + _isOpen = false; + _timeout: NodeJS.Timeout | undefined; @observable _x = 0; @observable _y = 0; @observable _selected = false; - _ref = React.createRef(); + @observable _editing = false; + @observable _forceOpen = false; onPointerDown = (e: React.PointerEvent) => { - this._downx = e.clientX; - this._downy = e.clientY; + this._downX = e.clientX; + this._downY = e.clientY; document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -38,11 +50,11 @@ export class DocuLinkBox extends DocComponent(Doc } onPointerMove = action((e: PointerEvent) => { const cdiv = this._ref && this._ref.current && this._ref.current.parentElement; - if (cdiv && (Math.abs(e.clientX - this._downx) > 5 || Math.abs(e.clientY - this._downy) > 5)) { + if (!this._isOpen && cdiv && (Math.abs(e.clientX - this._downX) > 5 || Math.abs(e.clientY - this._downY) > 5)) { const bounds = cdiv.getBoundingClientRect(); const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY); const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY)); - const dragdist = Math.sqrt((pt[0] - this._downx) * (pt[0] - this._downx) + (pt[1] - this._downy) * (pt[1] - this._downy)); + const dragdist = Math.sqrt((pt[0] - this._downX) * (pt[0] - this._downX) + (pt[1] - this._downY) * (pt[1] - this._downY)); if (separation > 100) { DragManager.StartLinkTargetsDrag(this._ref.current!, pt[0], pt[1], Cast(this.props.Document[this.props.fieldKey], Doc) as Doc, [this.props.Document]); // Containging collection is the document, not a collection... hack. document.removeEventListener("pointermove", this.onPointerMove); @@ -56,31 +68,53 @@ export class DocuLinkBox extends DocComponent(Doc onPointerUp = (e: PointerEvent) => { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); - if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button === 2 || e.ctrlKey || !this.props.Document.isButton)) { + if (Math.abs(e.clientX - this._downX) < 3 && Math.abs(e.clientY - this._downY) < 3 && (e.button === 2 || e.ctrlKey || !this.props.Document.isButton)) { this.props.select(false); } + this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2); + this._lastTap = Date.now(); } + + @action onClick = (e: React.MouseEvent) => { - if (!this.props.Document.onClick) { - if (Math.abs(e.clientX - this._downx) < 3 && Math.abs(e.clientY - this._downy) < 3 && (e.button !== 2 && !e.ctrlKey && this.props.Document.isButton)) { - if (this.props.Document.linkTarget === "doc") { - const alias = Doc.MakeAlias(this.props.Document); - alias.isButton = undefined; - alias.isBackground = undefined; - alias.layoutKey = "layout"; - this.props.addDocTab(alias, StrCast(this.props.Document.linkOpenLocation, "inTab")); - } else { - DocumentManager.Instance.FollowLink(this.props.Document, this.props.ContainingCollectionDoc as Doc, document => this.props.addDocTab(document, StrCast(this.props.Document.linkOpenLocation, "inTab")), false); - } + if (!this._doubleTap) { + this._editing = true; + if (!this.props.Document.onClick && !this._isOpen) { + this._timeout = setTimeout(action(() => { + if (Math.abs(e.clientX - this._downX) < 3 && Math.abs(e.clientY - this._downY) < 3 && (e.button !== 2 && !e.ctrlKey && this.props.Document.isButton)) { + DocumentManager.Instance.FollowLink(this.props.Document, this.props.ContainingCollectionDoc as Doc, document => this.props.addDocTab(document, StrCast(this.props.Document.linkOpenLocation, "inTab")), false); + } + this._editing = false; + }), 300 - (Date.now() - this._lastTap)); } - e.stopPropagation(); + } else { + this._timeout && clearTimeout(this._timeout); + this._timeout = undefined; } + e.stopPropagation(); + } + + openLinkDocOnRight = (e: React.MouseEvent) => { + this.props.addDocTab(this.props.Document, "onRight"); + } + openLinkTargetOnRight = (e: React.MouseEvent) => { + const alias = Doc.MakeAlias(Cast(this.props.Document[this.props.fieldKey], Doc, null)); + alias.isButton = undefined; + alias.isBackground = undefined; + alias.layoutKey = "layout"; + this.props.addDocTab(alias, "onRight"); } + @action + openLinkEditor = action((e: React.MouseEvent) => { + SelectionManager.DeselectAll(); + this._editing = this._forceOpen = true; + }) specificContextMenu = (e: React.MouseEvent): void => { const funcs: ContextMenuProps[] = []; - funcs.push({ description: "Open Target " + (this.props.Document.linkOpenLocation !== "onRight" ? "on Right" : "in Tab"), event: () => { e.stopPropagation(); this.props.Document.linkOpenLocation = this.props.Document.linkOpenLocation !== "onRight" ? "onRight" : "inTab" }, icon: "eye" }); - funcs.push({ description: this.props.Document.linkTarget === "doc" ? "Open Link Target" : "Open Link Doc", event: () => { e.stopPropagation(); this.props.Document.linkTarget = this.props.Document.linkTarget === "doc" ? "anchor" : "doc" }, icon: "eye" }); + funcs.push({ description: "Open Link Target on Right", event: () => this.openLinkTargetOnRight(e), icon: "eye" }); + funcs.push({ description: "Open Link on Right", event: () => this.openLinkDocOnRight(e), icon: "eye" }); + funcs.push({ description: "Open Link Editor", event: () => this.openLinkEditor(e), icon: "eye" }); ContextMenu.Instance.addItem({ description: "Link Funcs...", subitems: funcs, icon: "asterisk" }); } @@ -94,10 +128,25 @@ export class DocuLinkBox extends DocComponent(Doc const timecode = this.props.Document[anchor + "Timecode"]; const targetTitle = StrCast((this.props.Document[anchor]! as Doc).title) + (timecode !== undefined ? ":" + timecode : ""); + const flyout = ( +
Doc.UnBrushDoc(this.props.Document)}> + { })} /> + {!this._forceOpen ? (null) :
this._isOpen = this._editing = this._forceOpen = false)}> + +
} +
+ ); return
; + }} > + {!this._editing && !this._forceOpen ? (null) : + this._isOpen = true} onClose={action(() => this._isOpen = this._forceOpen = this._editing = false)}> + + + + } +
; } } -- cgit v1.2.3-70-g09d2