aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFawn <fangrui_tong@brown.edu>2019-06-11 15:27:02 -0400
committerFawn <fangrui_tong@brown.edu>2019-06-11 15:27:02 -0400
commit074b4b2a3246ae86fda334629d40540dd2bbf633 (patch)
tree84aa6e6cd0a003691d7409035538b8600de96744 /src
parentd429898b5337331450e46c223380e5d00967b2d6 (diff)
links can choose group type from preexisting group types
Diffstat (limited to 'src')
-rw-r--r--src/client/util/LinkManager.ts2
-rw-r--r--src/client/views/nodes/LinkEditor.scss26
-rw-r--r--src/client/views/nodes/LinkEditor.tsx370
-rw-r--r--src/client/views/nodes/LinkMenu.tsx43
4 files changed, 252 insertions, 189 deletions
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 5e8e0475b..99fb4c6c0 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -57,7 +57,7 @@ export class LinkManager {
}
@observable public allLinks: Array<Doc> = [];
- @observable public allGroups: Map<string, Doc> = new Map();
+ @observable public allGroups: Map<string, Array<string>> = new Map();
public findAllRelatedLinks(anchor: Doc): Array<Doc> {
return LinkManager.Instance.allLinks.filter(
diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss
index 52ed26442..d3ac8cf19 100644
--- a/src/client/views/nodes/LinkEditor.scss
+++ b/src/client/views/nodes/LinkEditor.scss
@@ -80,5 +80,31 @@
}
.linkEditor-metadata-row {
display: flex;
+ justify-content: space-between;
+ input {
+ width: calc(50% - 2px);
+ }
+ }
+}
+
+.linkEditor-dropdown {
+ width: 100%;
+ position: relative;
+ .linkEditor-options-wrapper {
+ width: 100%;
+ position: absolute;
+ top: 19px;
+ left: 0;
+ display: flex;
+ flex-direction: column;
+ }
+ .linkEditor-option {
+ background-color: red;
+ border: 1px solid $intermediate-color;
+ border-top: 0;
+ cursor: pointer;
+ &:hover {
+ background-color: $light-color-secondary;
+ }
}
} \ No newline at end of file
diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx
index 14fdd26df..481d3bf37 100644
--- a/src/client/views/nodes/LinkEditor.tsx
+++ b/src/client/views/nodes/LinkEditor.tsx
@@ -21,235 +21,270 @@ import { string } from "prop-types";
library.add(faArrowLeft);
library.add(faEllipsisV);
+// this dropdown could be generalizeds
+@observer
+class LinkGroupsDropdown extends React.Component<{ groupId: string, groupType: string, setGroup: (groupId: string, group: string) => void }> {
+ @observable private _searchTerm: string = "";
+ @observable private _groupType: string = this.props.groupType;
+
+ @action
+ setSearchTerm(value: string) {
+ this._searchTerm = value;
+ }
+
+ @action
+ setGroupType(value: string) {
+ this._groupType = value;
+ }
+
+ createGroup(value: string) {
+ LinkManager.Instance.allGroups.set(value, []);
+ this.props.setGroup(this.props.groupId, value);
+ }
+
+ renderOptions = (): JSX.Element[] => {
+ let allGroups: string[], searchTerm: string, results: string[], exactFound: boolean;
+ if (this._searchTerm !== "") {
+ allGroups = Array.from(LinkManager.Instance.allGroups.keys());
+ searchTerm = this._searchTerm.toUpperCase();
+ results = allGroups.filter(group => group.toUpperCase().indexOf(searchTerm) > -1);
+ exactFound = results.findIndex(group => group.toUpperCase() === searchTerm) > -1;
+ } else {
+ results = [];
+ exactFound = false;
+ }
+
+ let options = [];
+ results.forEach(result => {
+ options.push(<div key={result} className="linkEditor-option"
+ onClick={() => { this.props.setGroup(this.props.groupId, result); this.setGroupType(result); this.setSearchTerm("") }}>{result}</div>)
+ });
+
+ if (!exactFound && this._searchTerm !== "") {
+ options.push(<div key={""} className="linkEditor-option"
+ onClick={() => { this.createGroup(this._searchTerm); this.setGroupType(this._searchTerm); this.setSearchTerm("") }}>Create new "{this._searchTerm}" group</div>)
+ }
+
+ return options;
+ }
+
+ render() {
+ return (
+ <div className="linkEditor-dropdown">
+ <input type="text" value={this._groupType} placeholder="Search for a group or create a new group"
+ onChange={e => { this.setSearchTerm(e.target.value); this.setGroupType(e.target.value) }}></input>
+ <div className="linkEditor-options-wrapper">
+ {this.renderOptions()}
+ </div>
+ </div>
+ )
+ }
+}
+
+
interface LinkEditorProps {
sourceDoc: Doc;
linkDoc: Doc;
- groups: Map<string, Doc>;
- metadata: Map<string, Map<string, Doc>>;
+ // groups: Map<string, Doc>;
+ // metadata: Map<string, Map<string, Doc>>;
showLinks: () => void;
}
@observer
export class LinkEditor extends React.Component<LinkEditorProps> {
- // @observable private _groups: Map<string, Doc> = new Map();
- // @observable private _metadata: Map<string, Map<string, Doc>> = new Map();
-
- // // componentDidMount() {
-
- // // }
- // constructor(props: LinkEditorProps) {
- // super(props);
-
- // let groups = new Map<string, Doc>();
- // let metadata: Map<string, Map<string, Doc>> = new Map();
- // let groupList = (Doc.AreProtosEqual(props.docView.props.Document, Cast(this._editingLink.anchor1, Doc, new Doc))) ?
- // Cast(this._editingLink.anchor1Groups, listSpec(Doc), []) : Cast(this._editingLink.anchor2Groups, listSpec(Doc), []);
- // groupList.forEach(groupDoc => {
- // if (groupDoc instanceof Doc) {
- // let id = Utils.GenerateGuid();
- // groups.set(id, groupDoc);
-
- // let metadataMap = new Map<string, Doc>();
- // let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []);
- // metadataDocs.forEach(mdDoc => {
- // if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc
- // metadataMap.set(Utils.GenerateGuid(), mdDoc);
- // }
- // })
- // metadata.set(id, metadataMap);
- // }
- // })
- // }
-
- // @observable private _title: string = StrCast(this.props.linkDoc.title);
- // @observable private _description: string = StrCast(this.props.linkDoc.linkDescription);
- // @observable private _tags: Array<string> = Cast(this.props.linkDoc.linkTags, List);
-
- // @action
- // onTitleChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
- // this._title = e.target.value;
- // }
+ @observable private _groups: Map<string, Doc> = new Map();
+ @observable private _metadata: Map<string, Map<string, Doc>> = new Map();
+
+ constructor(props: LinkEditorProps) {
+ super(props);
+
+ let groups = new Map<string, Doc>();
+ let metadata: Map<string, Map<string, Doc>> = new Map();
+ let groupList = (Doc.AreProtosEqual(props.sourceDoc, Cast(props.linkDoc.anchor1, Doc, new Doc))) ?
+ Cast(props.linkDoc.anchor1Groups, listSpec(Doc), []) : Cast(props.linkDoc.anchor2Groups, listSpec(Doc), []);
+ groupList.forEach(groupDoc => {
+ if (groupDoc instanceof Doc) {
+ let id = Utils.GenerateGuid();
+ groups.set(id, groupDoc);
+
+ let metadataMap = new Map<string, Doc>();
+ let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []);
+ metadataDocs.forEach(mdDoc => {
+ if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc
+ metadataMap.set(Utils.GenerateGuid(), mdDoc);
+ }
+ })
+ metadata.set(id, metadataMap);
+ } else {
+ // promise doc
+ }
+ })
+ this._groups = groups;
+ this._metadata = metadata;
+ }
// @action
- // onDescriptionChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
- // this._description = e.target.value;
- // }
-
- // renderTags() {
- // return this._tags.map(tag => {
- // if (tag === "") {
- // return <input type="text" placeholder="Tag"></input>;
- // } else {
- // return <input type="text" value={tag}></input>;
- // }
- // })
- // }
-
- // addTag = (): void => {
- // this._tags.push("");
+ // editGroup(groupId: string, value: string) {
+ // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
+ // let groupDoc = this._groups.get(groupId);
+ // if (groupDoc) {
+ // groupDoc.proto!.type = value;
+ // LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, [groupDoc]);
+ // }
// }
@action
- editGroup(groupId: string, value: string) {
- let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
- let groupDoc = this.props.groups.get(groupId);
- if (groupDoc) {
- groupDoc.proto!.type = value;
- LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, [groupDoc]);
- }
- }
-
- @action
addGroup = (e: React.MouseEvent): void => {
// create new document for group
let groupDoc = Docs.TextDocument();
groupDoc.proto!.title = "";
groupDoc.proto!.metadata = new List<Doc>([]);
- this.props.groups.set(Utils.GenerateGuid(), groupDoc);
+ this._groups.set(Utils.GenerateGuid(), groupDoc);
+
+ let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
+ LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
+ }
+ @action
+ setGroup = (groupId: string, group: string): void => {
let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
- LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this.props.groups.values()));
+ let groupDoc = this._groups.get(groupId);
+ if (groupDoc) {
+ groupDoc.proto!.type = group;
+ LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, [groupDoc]);
+ }
}
renderGroup(groupId: string, groupDoc: Doc) {
- // let metadata = this.props.metadata.get(groupId);
- // if (!metadata) {
- // metadata = new Map<string, Doc>();
- // }
+ console.log("testing", groupDoc["strawberry"], groupDoc["type"]);
return (
- // <div key={groupId} className="linkEditor-group">
- <div key={groupId} className="linkEditor-group-row">
- <p className="linkEditor-group-row-label">type:</p>
- <input key={groupId + "-type"} type="text" value={StrCast(groupDoc.proto!.type)} onChange={e => this.editGroup(groupId, e.target.value)}></input>
+ <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.setGroup} />
+ {/* <input key={groupId + "-type"} type="text" value={StrCast(groupDoc.proto!.type)} onChange={e => this.editGroup(groupId, e.target.value)}></input> */}
+ {/* <input key={groupId + "-type"} type="text" value={StrCast(groupDoc.proto!.type)} onChange={e => this.editGroup(groupId, e.target.value)}></input> */}
+ </div>
+ {this.renderMetadata(groupId)}
+ <button onClick={() => this.addMetadata(groupId)}>+</button>
</div>
- // {/* {this.renderMetadata(groupId)} */ }
- // {/* <button onPointerDown={() => this.addMetadata(groupId)}>+</button> */ }
- // // </div>
)
}
@action
- addMetadata = (groupId: string): void => {
- // create new metadata doc
- let mdDoc = Docs.TextDocument();
- mdDoc.proto!.title = "";
- mdDoc.proto!.value = "";
-
- // append to map
- let mdMap = this.props.metadata.get(groupId);
- if (mdMap) {
- mdMap.set(Utils.GenerateGuid(), mdDoc);
+ addMetadata = (groupType: string): void => {
+ let mdKeys = LinkManager.Instance.allGroups.get(groupType);
+ if (mdKeys) {
+ if (mdKeys.indexOf("new key") > -1) {
+ mdKeys.push("new key");
+ }
} else {
- mdMap = new Map();
- mdMap.set(Utils.GenerateGuid(), mdDoc);
+ mdKeys = ["new key"];
}
+ LinkManager.Instance.allGroups.set(groupType, mdKeys);
+ console.log("md added", groupType, LinkManager.Instance.allGroups.get(groupType), LinkManager.Instance.allGroups);
+
+ // // create new metadata doc
+ // let mdDoc = Docs.TextDocument();
+ // mdDoc.proto!.title = "";
+ // mdDoc.proto!.value = "";
+
+ // // append to map
+ // let mdMap = this._metadata.get(groupId);
+ // if (mdMap) {
+ // mdMap.set(Utils.GenerateGuid(), mdDoc);
+ // } else {
+ // mdMap = new Map();
+ // mdMap.set(Utils.GenerateGuid(), mdDoc);
+ // }
- // add to internal representation of metadata
- this.props.metadata.set(groupId, mdMap);
-
- // add to internatal representation of group
- let groupDoc = this.props.groups.get(groupId);
- if (groupDoc) {
- groupDoc.proto!.metadata = new List<Doc>(Array.from(mdMap.values()));
- this.props.groups.set(groupId, groupDoc);
- }
+ // // add to internal representation of metadata
+ // this._metadata.set(groupId, mdMap);
- // add to link doc
- let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
- LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this.props.groups.values()));
+ // // add to internatal representation of group
+ // let groupDoc = this._groups.get(groupId);
+ // if (groupDoc) {
+ // groupDoc.proto!.metadata = new List<Doc>(Array.from(mdMap.values()));
+ // this._groups.set(groupId, groupDoc);
+ // }
+ // // add to link doc
+ // let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc;
+ // LinkUtils.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values()));
}
// @action
- // addMetadata = (groupId: string): void => {
- // let groupDoc = this.props.groups.get(groupId);
- // if (groupDoc) {
- // // create new document for metadata row
- // let metadata = Cast(groupDoc.metadata, listSpec(Doc), []);
- // let metadataDoc = Docs.TextDocument();
- // metadataDoc.proto!.title = "";
- // metadataDoc.proto!.value = "";
- // let metadataMap = new Map<string,
-
- // this.props.metadata.set(groupId, new Map)
-
- // groupDoc.proto!.metadata = new List<Doc>([metadataDoc]); // TODO: append to metadata
- // }
- // }
-
- // @action
- // editMetadataTitle = (groupId: string, mdId: string, value: string) => {
- // let group = this.props.metadata.get(groupId);
- // if (group) {
- // let mdDoc = group.get(mdId);
+ // editMetadataTitle(groupId: string, mdId: string, value: string) {
+ // let groupMd = this._metadata.get(groupId);
+ // if (groupMd) {
+ // let mdDoc = groupMd.get(mdId);
// if (mdDoc) {
// mdDoc.proto!.title = value;
// }
// }
+ // // set group and link?
// }
// @action
- // editMetadataValue = (groupId: string, mdId: string, value: string) => {
- // let group = this.props.metadata.get(groupId);
- // if (group) {
- // let mdDoc = group.get(mdId);
+ // editMetadataValue(groupId: string, mdId: string, value: string) {
+ // let groupMd = this._metadata.get(groupId);
+ // if (groupMd) {
+ // let mdDoc = groupMd.get(mdId);
// if (mdDoc) {
// mdDoc.proto!.value = value;
// }
// }
+ // // set group and link?
// }
- @action
- editMetadataTitle(groupId: string, mdId: string, value: string) {
-
- }
-
- @action
- editMetadataValue(groupId: string, mdId: string, value: string) {
-
- }
-
renderMetadata(groupId: string) {
let metadata: Array<JSX.Element> = [];
- let metadataMap = this.props.metadata.get(groupId);
- if (metadataMap) {
- metadataMap.forEach((mdDoc, mdId) => {
- metadata.push(
- <div key={mdId} className="linkEditor-metadata-row">
- <input type="text" value={StrCast(mdDoc.proto!.title)} onChange={e => this.editMetadataTitle(groupId, mdId, e.target.value)}></input>
- :
- <input type="text" value={StrCast(mdDoc.proto!.value)} onChange={e => this.editMetadataValue(groupId, mdId, e.target.value)}></input>
- </div>
- )
- })
+ let groupDoc = this._groups.get(groupId);
+ if (groupDoc) {
+ let mdDoc = Cast(groupDoc.proto!.metadata, Doc, new Doc);
+ let groupType = StrCast(groupDoc.proto!.type);
+ let groupMdKeys = LinkManager.Instance.allGroups.get(groupType);
+ console.log("rendering md", groupType, groupMdKeys, LinkManager.Instance.allGroups);
+ if (groupMdKeys) {
+ groupMdKeys.forEach(key => {
+ metadata.push(
+ <div key={key} className="linkEditor-metadata-row">
+ <input type="text" value={key} placeholder="key"></input>
+ :
+ <input type="text" value={(mdDoc[key] === undefined) ? "" : StrCast(mdDoc[key])} placeholder="value"></input>
+ </div>
+ )
+ })
+ }
}
- return metadata;
- // let metadataList: Array<JSX.Element> = [];
- // metadata.forEach((mdDoc, mdId) => {
- // metadataList.push(
- // <div key={mdId} className="linkEditor-metadata-row">
- // <input type="text" value={StrCast(mdDoc.proto!.title)} onChange={e => this.editMetadataTitle(groupId, mdId, e.target.value)}></input>:
- // <input type="text" value={StrCast(mdDoc.proto!.value)} onChange={e => this.editMetadataValue(groupId, mdId, e.target.value)}></input>
- // </div>
- // )
- // })
- }
+ // let metadataMap = this._metadata.get(groupId);
+ // if (metadataMap) {
+ // metadataMap.forEach((mdDoc, mdId) => {
+ // metadata.push(
+ // <div key={mdId} className="linkEditor-metadata-row">
+ // <input type="text" value={StrCast(mdDoc.proto!.title)} placeholder="key" onChange={e => this.editMetadataTitle(groupId, mdId, e.target.value)}></input>
+ // :
+ // <input type="text" value={StrCast(mdDoc.proto!.value)} placeholder="value" onChange={e => this.editMetadataValue(groupId, mdId, e.target.value)}></input>
+ // </div>
+ // )
+ // })
+ // }
- renderGroups() {
- let groups: Array<JSX.Element> = [];
- this.props.groups.forEach((groupDoc, groupId) => {
- groups.push(this.renderGroup(groupId, groupDoc))
- });
- return groups;
+ return metadata;
}
render() {
let destination = LinkUtils.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
+ let groups: Array<JSX.Element> = [];
+ this._groups.forEach((groupDoc, groupId) => {
+ groups.push(this.renderGroup(groupId, groupDoc))
+ });
+
return (
<div className="linkEditor">
<button className="linkEditor-back" onPointerDown={() => this.props.showLinks()}><FontAwesomeIcon icon="arrow-left" size="sm" /></button>
@@ -258,8 +293,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
<b>Groups:</b>
<button onClick={this.addGroup} title="Add Group">+</button>
</div>
- {this.renderGroups()}
-
+ {groups}
</div>
);
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx
index ab478feae..e378ee1cb 100644
--- a/src/client/views/nodes/LinkMenu.tsx
+++ b/src/client/views/nodes/LinkMenu.tsx
@@ -48,7 +48,6 @@ export class LinkMenu extends React.Component<Props> {
let linkItems: Array<JSX.Element> = [];
links.forEach((links, group) => {
- console.log("category is ", group);
linkItems.push(
<div key={group} className="link-menu-group">
<p className="link-menu-group-name">{group}:</p>
@@ -59,6 +58,10 @@ export class LinkMenu extends React.Component<Props> {
)
});
+ if (linkItems.length === 0) {
+ linkItems.push(<p key="">no links have been created yet</p>);
+ }
+
return linkItems;
}
@@ -79,28 +82,28 @@ export class LinkMenu extends React.Component<Props> {
</div>
);
} else {
- let groups = new Map<string, Doc>();
- let metadata: Map<string, Map<string, Doc>> = new Map();
- let groupList = (Doc.AreProtosEqual(this.props.docView.props.Document, Cast(this._editingLink.anchor1, Doc, new Doc))) ?
- Cast(this._editingLink.anchor1Groups, listSpec(Doc), []) : Cast(this._editingLink.anchor2Groups, listSpec(Doc), []);
- groupList.forEach(groupDoc => {
- if (groupDoc instanceof Doc) {
- let id = Utils.GenerateGuid();
- groups.set(id, groupDoc);
+ // let groups = new Map<string, Doc>();
+ // let metadata: Map<string, Map<string, Doc>> = new Map();
+ // let groupList = (Doc.AreProtosEqual(this.props.docView.props.Document, Cast(this._editingLink.anchor1, Doc, new Doc))) ?
+ // Cast(this._editingLink.anchor1Groups, listSpec(Doc), []) : Cast(this._editingLink.anchor2Groups, listSpec(Doc), []);
+ // groupList.forEach(groupDoc => {
+ // if (groupDoc instanceof Doc) {
+ // let id = Utils.GenerateGuid();
+ // groups.set(id, groupDoc);
- let metadataMap = new Map<string, Doc>();
- let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []);
- metadataDocs.forEach(mdDoc => {
- if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc
- metadataMap.set(Utils.GenerateGuid(), mdDoc);
- }
- })
- metadata.set(id, metadataMap);
- }
- })
+ // let metadataMap = new Map<string, Doc>();
+ // let metadataDocs = Cast(groupDoc.proto!.metadata, listSpec(Doc), []);
+ // metadataDocs.forEach(mdDoc => {
+ // if (mdDoc && mdDoc instanceof Doc) { // TODO: handle promise doc
+ // metadataMap.set(Utils.GenerateGuid(), mdDoc);
+ // }
+ // })
+ // metadata.set(id, metadataMap);
+ // }
+ // })
return (
- <LinkEditor groups={groups} metadata={metadata} sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)}></LinkEditor>
+ <LinkEditor sourceDoc={this.props.docView.props.Document} linkDoc={this._editingLink} showLinks={action(() => this._editingLink = undefined)}></LinkEditor>
);
}