aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/documents/Documents.ts68
-rw-r--r--src/client/util/LinkManager.ts48
-rw-r--r--src/client/views/DocumentButtonBar.tsx2
-rw-r--r--src/client/views/DocumentDecorations.tsx13
-rw-r--r--src/client/views/MainView.tsx2
-rw-r--r--src/client/views/collections/CollectionSchemaMovableTableHOC.tsx7
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx3
-rw-r--r--src/client/views/collections/CollectionSubView.tsx2
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx24
-rw-r--r--src/client/views/linking/LinkEditor.tsx20
-rw-r--r--src/client/views/linking/LinkMenuGroup.tsx2
-rw-r--r--src/client/views/linking/LinkMenuItem.tsx5
-rw-r--r--src/client/views/nodes/DocuLinkBox.tsx23
-rw-r--r--src/client/views/nodes/DocumentBox.tsx6
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx3
-rw-r--r--src/client/views/nodes/DocumentView.tsx9
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/client/views/nodes/LinkBox.scss3
-rw-r--r--src/client/views/nodes/LinkBox.tsx35
-rw-r--r--src/client/views/nodes/PresBox.tsx2
-rw-r--r--src/new_fields/ObjectField.ts1
21 files changed, 152 insertions, 128 deletions
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<Doc>(),
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<Doc>([]);
- linkDocProto.anchor2Groups = new List<Doc>([]);
- 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<Doc>(groups);
- } else {
- linkDoc.anchor2Groups = new List<Doc>(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<string, Array<Doc>>();
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<MovableRowProps> {
@@ -219,7 +220,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
if (!doc) return <></>;
const reference = React.createRef<HTMLDivElement>();
- 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<SchemaTableProps> {
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<CollectionView>;
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<TreeViewProps> {
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<TreeViewProps> {
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 = <EditableView
key="editableView"
- contents={contents !== undefined ? contents.toString() : "null"}
+ contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
height={13}
fontSize={12}
GetValue={() => Field.toKeyValueString(doc, key)}
@@ -336,7 +337,7 @@ class TreeView extends React.Component<TreeViewProps> {
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)}
</ul >;
} else if (this.treeViewExpandedView === "fields") {
return <ul><div ref={this._dref} style={{ display: "inline-block" }} key={this.props.document[Id] + this.props.document.title}>
@@ -470,7 +471,8 @@ class TreeView extends React.Component<TreeViewProps> {
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<TreeViewProps> {
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) : (
<div className="collectionTreeView-dropTarget" id="body"
style={{ background: this.props.backgroundColor?.(this.props.Document), paddingTop: `${NumCast(this.props.Document._yMargin, 20)}px` }}
onContextMenu={this.onContextMenu}
@@ -736,15 +740,15 @@ export class CollectionTreeView extends CollectionSubView(Document) {
Doc.SetInPlace(this.dataDoc, "title", value, false);
const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List<string>([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)}
<ul className="no-indent" style={{ width: "max-content" }} >
{
- 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)
}
</ul>
</div >
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<LinkMetadataEditorProps> {
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<LinkEditorProps> {
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 <LinkGroupEditor key={"gred-" + StrCast(groupDoc.title)} linkDoc={this.props.linkDoc} sourceDoc={this.props.sourceDoc} groupDoc={groupDoc} />;
});
@@ -374,7 +359,6 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
</div>
<div className="linkEditor-groupsLabel">
<b>Relationships:</b>
- <button className="linkEditor-button" onClick={() => this.addGroup()} title=" Add Group"><FontAwesomeIcon icon="plus" size="sm" /></button>
</div>
{groups.length > 0 ? groups : <div className="linkEditor-group">There are currently no relationships associated with this link.</div>}
</div>
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<LinkMenuGroupProps> {
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<HTMLDivElement>();
return <div ref={ref}><button className="linkEditor-button linkEditor-tableButton" onPointerDown={SetupDrag(ref, createTable)} title="Drag to view relationship table"><FontAwesomeIcon icon="table" size="sm" /></button></div>;
}
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<LinkMenuItemProps> {
}
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<JSX.Element> = [];
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<FieldViewProps, DocLinkSchema>(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<FieldViewProps, DocLinkSchema>(Doc
const timecode = this.props.Document[anchor + "Timecode"];
const targetTitle = StrCast((this.props.Document[anchor]! as Doc).title) + (timecode !== undefined ? ":" + timecode : "");
- return <div className="docuLinkBox-cont" onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle}
+ return <div className="docuLinkBox-cont" onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
ref={this._ref} style={{
background: c, left: `calc(${x}% - 12.5px)`, top: `calc(${y}% - 12.5px)`,
transform: `scale(${anchorScale / this.props.ContentScaling()})`
diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx
index 6b7b652c6..8078d01ab 100644
--- a/src/client/views/nodes/DocumentBox.tsx
+++ b/src/client/views/nodes/DocumentBox.tsx
@@ -9,7 +9,7 @@ import { Cast, StrCast, BoolCast } from "../../../new_fields/Types";
import { emptyFunction, emptyPath } from "../../../Utils";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
-import { DocComponent } from "../DocComponent";
+import { DocComponent, DocAnnotatableComponent } from "../DocComponent";
import { ContentFittingDocumentView } from "./ContentFittingDocumentView";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import "./DocumentBox.scss";
@@ -20,7 +20,7 @@ type DocBoxSchema = makeInterface<[typeof documentSchema]>;
const DocBoxDocument = makeInterface(documentSchema);
@observer
-export class DocumentBox extends DocComponent<FieldViewProps, DocBoxSchema>(DocBoxDocument) {
+export class DocumentBox extends DocAnnotatableComponent<FieldViewProps, DocBoxSchema>(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<FieldViewProps, DocBoxSchema>(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 <div className="documentBox-container" ref={this._contRef}
onContextMenu={this.specificContextMenu}
onPointerDown={this.onPointerDown} onClick={this.onClick}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index c0f603171..73a53f5cc 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -14,6 +14,7 @@ import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
import { AudioBox } from "./AudioBox";
import { ButtonBox } from "./ButtonBox";
import { SliderBox } from "./SliderBox";
+import { LinkBox } from "./LinkBox";
import { DocumentBox } from "./DocumentBox";
import { DocumentViewProps } from "./DocumentView";
import "./DocumentView.scss";
@@ -108,7 +109,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, ButtonBox, SliderBox, FieldView,
CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox, QueryBox,
- ColorBox, DashWebRTCVideo, DocuLinkBox, InkingStroke, DocumentBox
+ ColorBox, DashWebRTCVideo, DocuLinkBox, InkingStroke, DocumentBox, LinkBox
}}
bindings={this.CreateBindings()}
jsx={this.layout}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index dea08600a..5a358b6d8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -198,7 +198,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(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<DocumentViewProps, Document>(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<DocumentViewProps, Document>(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<FieldViewProps> {
// return <WebBox {...this.props} />
// }
else if (!(field instanceof Promise)) {
- return <p>{field.toString()}</p>;
+ return <p>{Field.toString(field)}</p>;
}
else {
return <p> {"Waiting for server..."} </p>;
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<FieldViewProps, LinkDocument>(LinkDocument) {
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
+ render() {
+ return <div className={`linkBox-container${this.active() ? "-interactive" : ""}`}
+ onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
+ style={{ background: this.props.backgroundColor?.(this.props.Document) }} >
+
+ <CollectionTreeView {...this.props}
+ ChromeHeight={returnZero}
+ overrideDocuments={[this.dataDoc]}
+ ignoreFields={Cast(this.props.Document.linkBoxExcludedKeys, listSpec("string"), null)}
+ annotationsKey={""}
+ CollectionView={undefined}
+ addDocument={returnFalse}
+ removeDocument={returnFalse}
+ moveDocument={returnFalse}>
+ </CollectionTreeView>
+ </div>;
+ }
+} \ 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<FieldViewProps> {
//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";