aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/LinkEditor.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/LinkEditor.tsx')
-rw-r--r--src/client/views/nodes/LinkEditor.tsx319
1 files changed, 128 insertions, 191 deletions
diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx
index 5097d625e..484682c22 100644
--- a/src/client/views/nodes/LinkEditor.tsx
+++ b/src/client/views/nodes/LinkEditor.tsx
@@ -4,61 +4,52 @@ import { observer } from "mobx-react";
import './LinkEditor.scss';
import { StrCast, Cast } from "../../../new_fields/Types";
import { Doc } from "../../../new_fields/Doc";
-import { List } from "../../../new_fields/List";
-import { listSpec } from "../../../new_fields/Schema";
-import { LinkManager, LinkUtils } from "../../util/LinkManager";
+import { LinkManager } from "../../util/LinkManager";
import { Docs } from "../../documents/Documents";
import { Utils } from "../../../Utils";
import { faArrowLeft, faEllipsisV, faTable } from '@fortawesome/free-solid-svg-icons';
import { library } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { string } from "prop-types";
import { SetupDrag } from "../../util/DragManager";
library.add(faArrowLeft);
library.add(faEllipsisV);
library.add(faTable);
+
+interface GroupTypesDropdownProps {
+ groupId: string;
+ groupType: string;
+ setGroup: (groupId: string, group: string) => void;
+}
// this dropdown could be generalized
@observer
-class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: string, setGroup: (groupId: string, group: string) => void }> {
+class GroupTypesDropdown extends React.Component<GroupTypesDropdownProps> {
@observable private _searchTerm: string = "";
@observable private _groupType: string = this.props.groupType;
- @action
- setSearchTerm(value: string) {
- this._searchTerm = value;
- }
+ @action setSearchTerm = (value: string): void => { this._searchTerm = value; };
+ @action setGroupType = (value: string): void => { this._groupType = value; };
@action
- setGroupType(value: string) {
- this._groupType = value;
+ createGroup = (groupType: string): void => {
+ this.props.setGroup(this.props.groupId, groupType);
+ LinkManager.Instance.groupMetadataKeys.set(groupType, []);
}
- @action
- createGroup(value: string) {
- LinkManager.Instance.groupMetadataKeys.set(value, []);
- this.props.setGroup(this.props.groupId, value);
- }
+ renderOptions = (): JSX.Element[] | JSX.Element => {
+ if (this._searchTerm === "") return <></>;
- renderOptions = (): JSX.Element[] => {
- let allGroups: string[], searchTerm: string, results: string[], exactFound: boolean;
- if (this._searchTerm !== "") {
- allGroups = Array.from(LinkManager.Instance.groupMetadataKeys.keys());
- searchTerm = this._searchTerm.toUpperCase();
- results = allGroups.filter(group => group.toUpperCase().indexOf(searchTerm) > -1);
- exactFound = results.findIndex(group => group.toUpperCase() === searchTerm) > -1;
- } else {
- results = [];
- exactFound = false;
- }
+ let allGroupTypes = Array.from(LinkManager.Instance.groupMetadataKeys.keys());
+ let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+ let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
- let options = [];
- results.forEach(result => {
- options.push(<div key={result} className="linkEditor-option"
- onClick={() => { this.props.setGroup(this.props.groupId, result); this.setGroupType(result); this.setSearchTerm(""); }}>{result}</div>);
+ let options = groupOptions.map(groupType => {
+ return <div key={groupType} className="linkEditor-option"
+ onClick={() => { this.props.setGroup(this.props.groupId, groupType); this.setGroupType(groupType); this.setSearchTerm(""); }}>{groupType}</div>;
});
+ // if search term does not already exist as a group type, give option to create new group type
if (!exactFound && this._searchTerm !== "") {
options.push(<div key={""} className="linkEditor-option"
onClick={() => { this.createGroup(this._searchTerm); this.setGroupType(this._searchTerm); this.setSearchTerm(""); }}>Define new "{this._searchTerm}" relationship</div>);
@@ -81,57 +72,66 @@ class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: s
}
-
+interface LinkMetadataEditorProps {
+ groupType: string;
+ mdDoc: Doc;
+ mdKey: string;
+ mdValue: string;
+}
@observer
-class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc, mdKey: string, mdValue: string }> {
+class LinkMetadataEditor extends React.Component<LinkMetadataEditorProps> {
@observable private _key: string = this.props.mdKey;
@observable private _value: string = this.props.mdValue;
+ @observable private _keyError: boolean = false;
@action
- editMetadataKey = (value: string): void => {
- // TODO: check that metadata doesnt already exist in group
+ setMetadataKey = (value: string): void => {
let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!);
- if (groupMdKeys) {
- let index = groupMdKeys.indexOf(this._key);
- if (index > -1) {
- groupMdKeys[index] = value;
- }
- else {
- console.log("OLD KEY WAS NOT FOUND", ...groupMdKeys);
- }
+
+ // don't allow user to create existing key
+ let newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase());
+ if (newIndex > -1) {
+ this._keyError = true;
+ this._key = value;
+ return;
+ } else {
+ this._keyError = false;
}
+ // set new value for key
+ let currIndex = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase());
+ if (currIndex === -1) console.error("LinkMetadataEditor: key was not found");
+ groupMdKeys[currIndex] = value;
+
this._key = value;
LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys);
}
@action
- editMetadataValue = (value: string): void => {
- this.props.mdDoc[this._key] = value;
- this._value = value;
+ setMetadataValue = (value: string): void => {
+ if (!this._keyError) {
+ this._value = value;
+ this.props.mdDoc[this._key] = value;
+ }
}
@action
removeMetadata = (): void => {
let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!);
- if (groupMdKeys) {
- let index = groupMdKeys.indexOf(this._key);
- if (index > -1) {
- groupMdKeys.splice(index, 1);
- }
- else {
- console.log("OLD KEY WAS NOT FOUND", ...groupMdKeys);
- }
- }
- this._key = "";
+
+ let index = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase());
+ if (index === -1) console.error("LinkMetadataEditor: key was not found");
+ groupMdKeys.splice(index, 1);
+
LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys);
+ this._key = "";
}
render() {
return (
<div className="linkEditor-metadata-row">
- <input type="text" value={this._key} placeholder="key" onChange={e => this.editMetadataKey(e.target.value)}></input>:
- <input type="text" value={this._value} placeholder="value" onChange={e => this.editMetadataValue(e.target.value)}></input>
+ <input className={this._keyError ? "linkEditor-error" : ""} type="text" value={this._key} placeholder="key" onChange={e => this.setMetadataKey(e.target.value)}></input>:
+ <input type="text" value={this._value} placeholder="value" onChange={e => this.setMetadataValue(e.target.value)}></input>
<button onClick={() => this.removeMetadata()}>x</button>
</div>
);
@@ -144,175 +144,127 @@ interface LinkEditorProps {
linkDoc: Doc;
showLinks: () => void;
}
-
@observer
export class LinkEditor extends React.Component<LinkEditorProps> {
- @observable private _groups: Map<string, Doc> = new Map(); // map of temp group id to the corresponding group doc
+ // map of temporary group id to the corresponding group doc
+ @observable private _groups: Map<string, Doc> = new Map();
constructor(props: LinkEditorProps) {
super(props);
let groups = new Map<string, Doc>();
- let groupList = (Doc.AreProtosEqual(props.sourceDoc, Cast(props.linkDoc.anchor1, Doc, new Doc))) ?
- Cast(props.linkDoc.anchor1Groups, listSpec(Doc), []) : Cast(props.linkDoc.anchor2Groups, listSpec(Doc), []);
+ let groupList = LinkManager.Instance.getAnchorGroups(props.linkDoc, props.sourceDoc);
groupList.forEach(groupDoc => {
- if (groupDoc instanceof Doc) {
- let id = Utils.GenerateGuid();
- groups.set(id, groupDoc);
- } else {
- // promise doc
- }
+ let id = Utils.GenerateGuid();
+ groups.set(id, groupDoc);
});
this._groups = groups;
}
@action
addGroup = (): void => {
- console.log("before adding", ...Array.from(this._groups.keys()));
-
// new group only gets added if there is not already a group with type "new group"
let index = Array.from(this._groups.values()).findIndex(groupDoc => {
return groupDoc.type === "New Group";
});
- if (index === -1) {
- // create new document for group
- let mdDoc = Docs.TextDocument();
- mdDoc.proto!.anchor1 = this.props.sourceDoc.title;
- mdDoc.proto!.anchor2 = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title;
+ if (index > -1) return;
- let groupDoc = Docs.TextDocument();
- groupDoc.proto!.type = "New Group";
- groupDoc.proto!.metadata = mdDoc;
+ // create new metadata document for group
+ let mdDoc = Docs.TextDocument();
+ mdDoc.proto!.anchor1 = this.props.sourceDoc.title;
+ mdDoc.proto!.anchor2 = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title;
- this._groups.set(Utils.GenerateGuid(), groupDoc);
+ // create new group document
+ let groupDoc = Docs.TextDocument();
+ groupDoc.proto!.type = "New Group";
+ groupDoc.proto!.metadata = mdDoc;
- let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
- LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
- }
+ this._groups.set(Utils.GenerateGuid(), groupDoc);
-
- // console.log("set anchor groups for", this.props.sourceDoc["title"]);
- console.log("after adding", ...Array.from(this._groups.keys()));
+ let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
+ LinkManager.Instance.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
}
@action
setGroupType = (groupId: string, groupType: string): void => {
- console.log("setting for ", groupId);
- // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
let groupDoc = this._groups.get(groupId);
if (groupDoc) {
- console.log("found group doc");
groupDoc.proto!.type = groupType;
-
this._groups.set(groupId, groupDoc);
-
-
- LinkUtils.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
- console.log("set", Array.from(this._groups.values()).length);
+ LinkManager.Instance.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
}
-
- let anchor1groups: string[] = [];
- Cast(this.props.linkDoc.anchor1Groups, listSpec(Doc), []).forEach(doc => {
- if (doc instanceof Doc) {
- anchor1groups.push(StrCast(doc.proto!.type));
- } else {
- console.log("promise");
- }
- });
- let anchor2groups: string[] = [];
- Cast(this.props.linkDoc.anchor2Groups, listSpec(Doc), []).forEach(doc => {
- if (doc instanceof Doc) {
- anchor2groups.push(StrCast(doc.proto!.type));
- } else {
- console.log("promise");
- }
- });
- console.log("groups for anchors; anchor1: [", ...anchor1groups, "] ; anchor2: [", ...anchor2groups, "]");
}
- removeGroupFromLink = (groupId: string, groupType: string) => {
- // this._groups.delete(groupId);
+ removeGroupFromLink = (groupId: string, groupType: string): void => {
let groupDoc = this._groups.get(groupId);
- if (groupDoc) {
- LinkUtils.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType);
- this._groups.delete(groupId);
- }
- // LinkUtils.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
- console.log("\nremoved", groupId, "remaining", ...Array.from(this._groups.keys()), "\n");
+ if (!groupDoc) console.error("LinkEditor: group not found");
+ LinkManager.Instance.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType);
+ this._groups.delete(groupId);
}
- deleteGroup = (groupId: string, groupType: string) => {
+ deleteGroup = (groupId: string, groupType: string): void => {
let groupDoc = this._groups.get(groupId);
- if (groupDoc) {
- LinkManager.Instance.deleteGroup(groupType);
- this._groups.delete(groupId);
- }
+ if (!groupDoc) console.error("LinkEditor: group not found");
+ LinkManager.Instance.deleteGroup(groupType);
+ this._groups.delete(groupId);
}
- copyGroup = (groupId: string, groupType: string) => {
- let oppAnchor = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
- let groupList = (Doc.AreProtosEqual(oppAnchor, Cast(this.props.linkDoc.anchor1, Doc, new Doc))) ?
- Cast(this.props.linkDoc.anchor1Groups, listSpec(Doc), []) : Cast(this.props.linkDoc.anchor2Groups, listSpec(Doc), []);
- // if group already exists on opposite anchor, copy value
- let index = groupList.findIndex(groupDoc => {
- if (groupDoc instanceof Doc) {
- return StrCast(groupDoc.type) === groupType;
- } else {
- return false;
- }
- });
- // TODO: clean
- // if (index > 0) {
- // let thisGroupDoc = this._groups.get(groupId);
- // let oppGroupDoc = groupList[index];
- // let keys = LinkManager.Instance.allGroups.get(groupType);
- // if (keys) {
- // keys.forEach(key => {
- // if (thisGroupDoc && oppGroupDoc instanceof Doc) { // TODO: clean
- // let val = thisGroupDoc[key] === undefined ? "" : StrCast(thisGroupDoc[key]);
- // oppGroupDoc[key] = val;
- // }
- // // mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key])
- // // oppGroupDoc[key] = thisGroupDoc[key];
- // })
- // }
- // // LinkUtils.setAnchorGroups(this.props.linkDoc, oppAnchor, [oppGroupDoc]);
- // } else {
- let thisGroupDoc = this._groups.get(groupId);
- let thisMdDoc = Cast(thisGroupDoc!.metadata, Doc, new Doc);
- let newGroupDoc = Docs.TextDocument();
- let newMdDoc = Docs.TextDocument();
- newMdDoc.proto!.anchor1 = StrCast(thisMdDoc.anchor2);
- newMdDoc.proto!.anchor2 = StrCast(thisMdDoc.anchor1);
+ copyGroup = (groupId: string, groupType: string): void => {
+ let sourceGroupDoc = this._groups.get(groupId);
+ let sourceMdDoc = Cast(sourceGroupDoc!.metadata, Doc, new Doc);
+ let destDoc = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+ let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc);
let keys = LinkManager.Instance.groupMetadataKeys.get(groupType);
+
+ // create new metadata doc with copied kvp
+ let destMdDoc = Docs.TextDocument();
+ destMdDoc.proto!.anchor1 = StrCast(sourceMdDoc.anchor2);
+ destMdDoc.proto!.anchor2 = StrCast(sourceMdDoc.anchor1);
if (keys) {
keys.forEach(key => {
- if (thisGroupDoc) { // TODO: clean
- let val = thisMdDoc[key] === undefined ? "" : StrCast(thisMdDoc[key]);
- newMdDoc[key] = val;
- }
- // mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key])
- // oppGroupDoc[key] = thisGroupDoc[key];
+ let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]);
+ destMdDoc[key] = val;
});
}
- newGroupDoc.proto!.type = groupType;
- newGroupDoc.proto!.metadata = newMdDoc;
- LinkUtils.setAnchorGroups(this.props.linkDoc, oppAnchor, [newGroupDoc]); // TODO: fix to append to list
- // }
+ // create new group doc with new metadata doc
+ let destGroupDoc = Docs.TextDocument();
+ destGroupDoc.proto!.type = groupType;
+ destGroupDoc.proto!.metadata = destMdDoc;
- // else create group on opposite anchor
+ // if group does not already exist on opposite anchor, create group doc
+ let index = destGroupList.findIndex(groupDoc => { StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase(); });
+ if (index > -1) {
+ destGroupList[index] = destGroupDoc;
+ } else {
+ destGroupList.push(destGroupDoc);
+ }
+
+ LinkManager.Instance.setAnchorGroups(this.props.linkDoc, destDoc, destGroupList);
+ }
+
+ viewGroupAsTable = (groupId: string, groupType: string): JSX.Element => {
+ let keys = LinkManager.Instance.groupMetadataKeys.get(groupType);
+ let groupDoc = this._groups.get(groupId);
+ if (keys && groupDoc) {
+ let docs: Doc[] = LinkManager.Instance.findAllMetadataDocsInGroup(groupType);
+ let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" }));
+ let ref = React.createRef<HTMLDivElement>();
+ return <div className="linkEditor-groupOpts" ref={ref}><button onPointerDown={SetupDrag(ref, createTable)}><FontAwesomeIcon icon="table" size="sm" /></button></div>;
+ } else {
+ return <button className="linkEditor-groupOpts" disabled><FontAwesomeIcon icon="table" size="sm" /></button>;
+ }
}
- renderGroup(groupId: string, groupDoc: Doc) {
+ renderGroup = (groupId: string, groupDoc: Doc): JSX.Element => {
let type = StrCast(groupDoc.type);
if ((type && LinkManager.Instance.groupMetadataKeys.get(type)) || type === "New Group") {
return (
<div key={groupId} className="linkEditor-group">
<div className="linkEditor-group-row">
<p className="linkEditor-group-row-label">type:</p>
- <LinkGroupsDropdown groupId={groupId} groupType={StrCast(groupDoc.proto!.type)} setGroup={this.setGroupType} />
+ <GroupTypesDropdown groupId={groupId} groupType={StrCast(groupDoc.proto!.type)} setGroup={this.setGroupType} />
</div>
{this.renderMetadata(groupId)}
<div className="linkEditor-group-buttons">
@@ -330,35 +282,20 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
}
}
- viewGroupAsTable(groupId: string, groupType: string) {
- let keys = LinkManager.Instance.groupMetadataKeys.get(groupType);
- let groupDoc = this._groups.get(groupId);
- if (keys && groupDoc) {
- console.log("keys:", ...keys);
- let docs: Doc[] = LinkManager.Instance.findMetadataInGroup(groupType);
- let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" }));
- let ref = React.createRef<HTMLDivElement>();
- return <div className="linkEditor-groupOpts" ref={ref}><button onPointerDown={SetupDrag(ref, createTable)}><FontAwesomeIcon icon="table" size="sm" /></button></div>;
- } else {
- return <button className="linkEditor-groupOpts" disabled><FontAwesomeIcon icon="table" size="sm" /></button>;
- }
- }
-
@action
addMetadata = (groupType: string): void => {
let mdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType);
if (mdKeys) {
- if (mdKeys.indexOf("new key") === -1) {
- mdKeys.push("new key");
- }
+ // only add "new key" if there is no other key with value "new key"; prevents spamming
+ if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key");
} else {
mdKeys = ["new key"];
}
LinkManager.Instance.groupMetadataKeys.set(groupType, mdKeys);
}
- renderMetadata(groupId: string) {
+ renderMetadata = (groupId: string): JSX.Element[] => {
let metadata: Array<JSX.Element> = [];
let groupDoc = this._groups.get(groupId);
if (groupDoc) {
@@ -377,7 +314,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
}
render() {
- let destination = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+ let destination = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
let groups: Array<JSX.Element> = [];
this._groups.forEach((groupDoc, groupId) => {