import { observable, computed, action } from "mobx";
import React = require("react");
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 { 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);
// this dropdown could be generalized
@observer
class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: string, setGroup: (groupId: string, group: string) => void }> {
@observable private _searchTerm: string = "";
@observable private _groupType: string = this.props.groupType;
@action
setSearchTerm(value: string) {
this._searchTerm = value;
}
@action
setGroupType(value: string) {
this._groupType = value;
}
@action
createGroup(value: string) {
LinkManager.Instance.groupMetadataKeys.set(value, []);
this.props.setGroup(this.props.groupId, value);
}
renderOptions = (): JSX.Element[] => {
let allGroups: string[], searchTerm: string, results: string[], exactFound: boolean;
if (this._searchTerm !== "") {
allGroups = Array.from(LinkManager.Instance.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 options = [];
results.forEach(result => {
options.push(
{ this.props.setGroup(this.props.groupId, result); this.setGroupType(result); this.setSearchTerm(""); }}>{result}
);
});
if (!exactFound && this._searchTerm !== "") {
options.push( { this.createGroup(this._searchTerm); this.setGroupType(this._searchTerm); this.setSearchTerm(""); }}>Define new "{this._searchTerm}" relationship
);
}
return options;
}
render() {
return (
);
}
}
@observer
class LinkMetadataEditor extends React.Component<{ groupType: string, mdDoc: Doc, mdKey: string, mdValue: string }> {
@observable private _key: string = this.props.mdKey;
@observable private _value: string = this.props.mdValue;
@action
editMetadataKey = (value: string): void => {
// TODO: check that metadata doesnt already exist in group
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);
}
}
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;
}
@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 = "";
LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys);
}
render() {
return (
this.editMetadataKey(e.target.value)}>:
this.editMetadataValue(e.target.value)}>
);
}
}
interface LinkEditorProps {
sourceDoc: Doc;
linkDoc: Doc;
showLinks: () => void;
}
@observer
export class LinkEditor extends React.Component {
@observable private _groups: Map = new Map(); // map of temp group id to the corresponding group doc
constructor(props: LinkEditorProps) {
super(props);
let groups = new Map();
let groupList = (Doc.AreProtosEqual(props.sourceDoc, Cast(props.linkDoc.anchor1, Doc, new Doc))) ?
Cast(props.linkDoc.anchor1Groups, listSpec(Doc), []) : Cast(props.linkDoc.anchor2Groups, listSpec(Doc), []);
groupList.forEach(groupDoc => {
if (groupDoc instanceof Doc) {
let id = Utils.GenerateGuid();
groups.set(id, groupDoc);
} else {
// promise doc
}
});
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;
let groupDoc = Docs.TextDocument();
groupDoc.proto!.type = "New Group";
groupDoc.proto!.metadata = mdDoc;
this._groups.set(Utils.GenerateGuid(), groupDoc);
let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
}
// console.log("set anchor groups for", this.props.sourceDoc["title"]);
console.log("after adding", ...Array.from(this._groups.keys()));
}
@action
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);
}
let anchor1groups: string[] = [];
Cast(this.props.linkDoc.anchor1Groups, listSpec(Doc), []).forEach(doc => {
if (doc instanceof Doc) {
anchor1groups.push(StrCast(doc.proto!.type));
} else {
console.log("promise");
}
});
let anchor2groups: string[] = [];
Cast(this.props.linkDoc.anchor2Groups, listSpec(Doc), []).forEach(doc => {
if (doc instanceof Doc) {
anchor2groups.push(StrCast(doc.proto!.type));
} else {
console.log("promise");
}
});
console.log("groups for anchors; anchor1: [", ...anchor1groups, "] ; anchor2: [", ...anchor2groups, "]");
}
removeGroupFromLink = (groupId: string, groupType: string) => {
// this._groups.delete(groupId);
let groupDoc = this._groups.get(groupId);
if (groupDoc) {
LinkUtils.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType);
this._groups.delete(groupId);
}
// LinkUtils.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
console.log("\nremoved", groupId, "remaining", ...Array.from(this._groups.keys()), "\n");
}
deleteGroup = (groupId: string, groupType: string) => {
let groupDoc = this._groups.get(groupId);
if (groupDoc) {
LinkManager.Instance.deleteGroup(groupType);
this._groups.delete(groupId);
}
}
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);
let keys = LinkManager.Instance.groupMetadataKeys.get(groupType);
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];
});
}
newGroupDoc.proto!.type = groupType;
newGroupDoc.proto!.metadata = newMdDoc;
LinkUtils.setAnchorGroups(this.props.linkDoc, oppAnchor, [newGroupDoc]); // TODO: fix to append to list
// }
// else create group on opposite anchor
}
renderGroup(groupId: string, groupDoc: Doc) {
let type = StrCast(groupDoc.type);
if ((type && LinkManager.Instance.groupMetadataKeys.get(type)) || type === "New Group") {
return (
{this.renderMetadata(groupId)}
{groupDoc.type === "New Group" ? :
}
{this.viewGroupAsTable(groupId, type)}
);
} else {
return <>>;
}
}
viewGroupAsTable(groupId: string, groupType: string) {
let keys = LinkManager.Instance.groupMetadataKeys.get(groupType);
let groupDoc = this._groups.get(groupId);
if (keys && groupDoc) {
console.log("keys:", ...keys);
let docs: Doc[] = LinkManager.Instance.findMetadataInGroup(groupType);
let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" }));
let ref = React.createRef();
return ;
} else {
return ;
}
}
@action
addMetadata = (groupType: string): void => {
let mdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType);
if (mdKeys) {
if (mdKeys.indexOf("new key") === -1) {
mdKeys.push("new key");
}
} else {
mdKeys = ["new key"];
}
LinkManager.Instance.groupMetadataKeys.set(groupType, mdKeys);
}
renderMetadata(groupId: string) {
let metadata: Array = [];
let groupDoc = this._groups.get(groupId);
if (groupDoc) {
let mdDoc = Cast(groupDoc.proto!.metadata, Doc, new Doc);
let groupType = StrCast(groupDoc.proto!.type);
let groupMdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType);
if (groupMdKeys) {
groupMdKeys.forEach((key, index) => {
metadata.push(
);
});
}
}
return metadata;
}
render() {
let destination = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
let groups: Array = [];
this._groups.forEach((groupDoc, groupId) => {
groups.push(this.renderGroup(groupId, groupDoc));
});
return (
editing link to: {destination.proto!.title}
Relationships:
{groups.length > 0 ? groups :
There are currently no relationships associated with this link.
}
);
}
}