From 3b880d7b15b7107049ae27601b9f759b17f7fde9 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 18 Jun 2019 22:51:46 -0400
Subject: added initial keyboard shortcuts for adding and moving docs in
 TreeView.  fixed image drag bug.

---
 src/client/views/EditableView.tsx | 34 +++++++++++++++++++++++-----------
 1 file changed, 23 insertions(+), 11 deletions(-)

(limited to 'src/client/views/EditableView.tsx')

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index c946d68e1..a96fca464 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -18,13 +18,17 @@ export interface EditableProps {
 
     OnFillDown?(value: string): void;
 
+    OnTab?(): void;
+
     /**
      * The contents to render when not editing
      */
     contents: any;
+    fontStyle?: string;
     height?: number;
     display?: string;
     oneLine?: boolean;
+    editing?: boolean
 }
 
 /**
@@ -34,40 +38,48 @@ export interface EditableProps {
  */
 @observer
 export class EditableView extends React.Component<EditableProps> {
-    @observable
-    editing: boolean = false;
+    @observable _editing: boolean = false;
+
+    constructor(props: EditableProps) {
+        super(props);
+        this._editing = this.props.editing ? true : false;
+    }
 
     @action
     onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key === "Enter") {
+        if (e.key === "Tab") {
+            this.props.OnTab && this.props.OnTab();
+        } else if (e.key === "Enter") {
             if (!e.ctrlKey) {
                 if (this.props.SetValue(e.currentTarget.value)) {
-                    this.editing = false;
+                    this._editing = false;
                 }
             } else if (this.props.OnFillDown) {
                 this.props.OnFillDown(e.currentTarget.value);
-                this.editing = false;
+                this._editing = false;
             }
         } else if (e.key === "Escape") {
-            this.editing = false;
+            this._editing = false;
         }
     }
 
     @action
     onClick = (e: React.MouseEvent) => {
-        this.editing = true;
+        this._editing = true;
         e.stopPropagation();
     }
 
     render() {
-        if (this.editing) {
-            return <input className="editableView-input" defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus onBlur={action(() => this.editing = false)}
+        if (this._editing) {
+            return <input className="editableView-input" defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus
+                onBlur={action(() => this._editing = false)}
                 style={{ display: this.props.display }} />;
         } else {
             return (
-                <div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`} style={{ display: this.props.display, height: "auto", maxHeight: `${this.props.height}` }}
+                <div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
+                    style={{ display: this.props.display, height: "auto", maxHeight: `${this.props.height}` }}
                     onClick={this.onClick} >
-                    <span>{this.props.contents}</span>
+                    <span style={{ fontStyle: this.props.fontStyle }}>{this.props.contents}</span>
                 </div>
             );
         }
-- 
cgit v1.2.3-70-g09d2


From 1472d2b56aa64896f0a93f172322121d19cd1592 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Wed, 19 Jun 2019 09:11:35 -0400
Subject: fixed lint errors.

---
 src/client/views/EditableView.tsx                   |  2 +-
 src/client/views/collections/CollectionTreeView.tsx | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

(limited to 'src/client/views/EditableView.tsx')

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index a96fca464..70d6c22bf 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -28,7 +28,7 @@ export interface EditableProps {
     height?: number;
     display?: string;
     oneLine?: boolean;
-    editing?: boolean
+    editing?: boolean;
 }
 
 /**
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6a2ab9d7d..f6c9ba86b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -154,7 +154,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 Doc.GetProto(this.props.document)[key] = value;
                 return true;
             }}
-        />);
+        />)
 
     /**
      * Renders the EditableView title element for placement into the tree.
@@ -241,7 +241,7 @@ class TreeView extends React.Component<TreeViewProps> {
         return true;
     }
 
-    @observable _chosenKey: string = "data"
+    @observable _chosenKey: string = "data";
     _bulletType: BulletType = BulletType.List;
     render() {
         let bulletType = BulletType.List;
@@ -268,7 +268,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 style={{ display: "inline", marginRight: "3px", marginTop: "7px", background: key === this._chosenKey ? "lightgray" : undefined }}>
                 {key}
             </span>)}
-        </div>
+        </div>;
         [this._chosenKey].map(key => {
             let docList = Cast(this.props.document[key], listSpec(Doc));
             let remDoc = (doc: Doc) => this.remove(doc, key);
@@ -318,7 +318,7 @@ class TreeView extends React.Component<TreeViewProps> {
     ) {
         let docList = docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized));
         return docList.map((child, i) => {
-            let indent = i == 0 ? undefined : () => {
+            let indent = i === 0 ? undefined : () => {
                 if (StrCast(docList[i - 1].layout).indexOf("CollectionView") !== -1) {
                     let fieldKeysub = StrCast(docList[i - 1].layout).split("fieldKey")[1];
                     let fieldKey = fieldKeysub.split("\"")[1];
@@ -338,7 +338,7 @@ class TreeView extends React.Component<TreeViewProps> {
                 addDocTab={addDocTab}
                 ScreenToLocalTransform={screenToLocalXf}
                 parentKey={key}
-                active={active} />
+                active={active} />;
         });
     }
 }
-- 
cgit v1.2.3-70-g09d2


From 38de022621175bda7410df4444fcd2bbee0919cb Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 19 Jun 2019 23:43:47 -0400
Subject: slight tweaks.

---
 src/client/views/EditableView.tsx                  |  5 +++-
 .../views/collections/CollectionTreeView.tsx       | 35 +++++++++++++++-------
 2 files changed, 28 insertions(+), 12 deletions(-)

(limited to 'src/client/views/EditableView.tsx')

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 70d6c22bf..0f6281b5c 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -29,6 +29,7 @@ export interface EditableProps {
     display?: string;
     oneLine?: boolean;
     editing?: boolean;
+    onClick?: (e: React.MouseEvent) => boolean;
 }
 
 /**
@@ -65,7 +66,9 @@ export class EditableView extends React.Component<EditableProps> {
 
     @action
     onClick = (e: React.MouseEvent) => {
-        this._editing = true;
+        if (!this.props.onClick || !this.props.onClick(e)) {
+            this._editing = true;
+        }
         e.stopPropagation();
     }
 
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 856430036..c51c16883 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,7 +1,7 @@
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt, faCaretSquareRight, faCaretSquareDown } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from "mobx";
+import { action, observable, computed } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc';
 import { Id } from '../../../new_fields/FieldSymbols';
@@ -125,12 +125,20 @@ class TreeView extends React.Component<TreeViewProps> {
         </div>;
     }
 
+    titleClicked = (e: React.MouseEvent) => {
+        if (this._collapsed) return false;
+        else {
+            this.props.document.embed = !BoolCast(this.props.document.embed);
+            return true;
+        }
+    }
     static loadId = "";
     editableView = (key: string, style?: string) => (<EditableView
         oneLine={true}
         display={"inline"}
         editing={this.props.document[Id] === TreeView.loadId}
         contents={StrCast(this.props.document[key])}
+        onClick={this.titleClicked}
         height={36}
         fontStyle={style}
         GetValue={() => StrCast(this.props.document[key])}
@@ -144,24 +152,24 @@ class TreeView extends React.Component<TreeViewProps> {
         OnTab={() => this.props.indentDocument && this.props.indentDocument()}
     />)
 
-    get keyList() {
-        let keyList: string[] = [];
+    @computed get keyList() {
         let keys = Array.from(Object.keys(this.props.document));
         if (this.props.document.proto instanceof Doc) {
             keys.push(...Array.from(Object.keys(this.props.document.proto)));
             while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1);
         }
-        if (keys.indexOf("data") !== -1) {
-            keys.splice(keys.indexOf("data"), 1);
-            keys.splice(0, 0, "data");
-        }
+        let keyList: string[] = [];
         keys.map(key => {
             let docList = Cast(this.props.document[key], listSpec(Doc));
             let doc = Cast(this.props.document[key], Doc);
-            if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) {
+            if (doc instanceof Doc || docList) {
                 keyList.push(key);
             }
         });
+        if (keyList.indexOf("data") !== -1) {
+            keyList.splice(keyList.indexOf("data"), 1);
+        }
+        keyList.splice(0, 0, "data");
         return keyList;
     }
     /**
@@ -172,7 +180,12 @@ class TreeView extends React.Component<TreeViewProps> {
         let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true);
 
         let headerElements = (
-            <span className="collectionTreeView-keyHeader" key={this._chosenKey} onPointerDown={action(() => this.props.document.embed = !BoolCast(this.props.document.embed))} >
+            <span className="collectionTreeView-keyHeader" key={this._chosenKey}
+                onPointerDown={action(() => {
+                    let ind = this.keyList.indexOf(this._chosenKey);
+                    ind = (ind + 1) % this.keyList.length;
+                    this._chosenKey = this.keyList[ind];
+                })} >
                 {this._chosenKey}
             </span>);
         let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : [];
@@ -209,7 +222,7 @@ class TreeView extends React.Component<TreeViewProps> {
             } else {
                 ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) });
             }
-            ContextMenu.Instance.displayMenu(e.pageX - 156, e.pageY - 15);
+            ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15);
             e.stopPropagation();
         }
     }
@@ -264,7 +277,7 @@ class TreeView extends React.Component<TreeViewProps> {
         let doc = Cast(this.props.document[this._chosenKey], Doc);
         let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5;
         if (!this._collapsed) {
-            if (!this.props.document.embed && (docList && (DocListCast(docList).length > 0 || this._chosenKey === "data"))) {
+            if (!this.props.document.embed) {
                 contentElement = <ul key={this._chosenKey + "more"}>
                     {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this._chosenKey, addDoc, remDoc, this.move,
                         this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)}
-- 
cgit v1.2.3-70-g09d2


From 9b3e80def0be6c09c31b5176817a54323d217d81 Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Thu, 20 Jun 2019 15:06:41 -0400
Subject: Handled more events in editable view

---
 src/client/views/EditableView.tsx | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

(limited to 'src/client/views/EditableView.tsx')

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 0f6281b5c..97a2d19dd 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -72,10 +72,14 @@ export class EditableView extends React.Component<EditableProps> {
         e.stopPropagation();
     }
 
+    stopPropagation(e: React.SyntheticEvent) {
+        e.stopPropagation();
+    }
+
     render() {
         if (this._editing) {
             return <input className="editableView-input" defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus
-                onBlur={action(() => this._editing = false)}
+                onBlur={action(() => this._editing = false)} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation}
                 style={{ display: this.props.display }} />;
         } else {
             return (
-- 
cgit v1.2.3-70-g09d2


From e56a29faab91c5059af41e0b2abd79e46eacce0c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 4 Jul 2019 00:37:06 -0400
Subject: fixed edit font size for tree view

---
 src/client/views/EditableView.tsx                   | 3 ++-
 src/client/views/collections/CollectionTreeView.tsx | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

(limited to 'src/client/views/EditableView.tsx')

diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 97a2d19dd..f7aa6cc94 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -25,6 +25,7 @@ export interface EditableProps {
      */
     contents: any;
     fontStyle?: string;
+    fontSize?: number;
     height?: number;
     display?: string;
     oneLine?: boolean;
@@ -80,7 +81,7 @@ export class EditableView extends React.Component<EditableProps> {
         if (this._editing) {
             return <input className="editableView-input" defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus
                 onBlur={action(() => this._editing = false)} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation}
-                style={{ display: this.props.display }} />;
+                style={{ display: this.props.display, fontSize: this.props.fontSize }} />;
         } else {
             return (
                 <div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 3db786043..93a1ec1eb 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -164,6 +164,7 @@ class TreeView extends React.Component<TreeViewProps> {
         contents={StrCast(this.props.document[key])}
         height={36}
         fontStyle={style}
+        fontSize={12}
         GetValue={() => StrCast(this.props.document[key])}
         SetValue={(value: string) => (Doc.GetProto(this.resolvedDataDoc)[key] = value) ? true : true}
         OnFillDown={(value: string) => {
-- 
cgit v1.2.3-70-g09d2


From 7db7bad21d25ad5be63810d9c4511ccb3428b2f5 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samwilkins333@gmail.com>
Date: Fri, 5 Jul 2019 20:37:06 -0400
Subject: data doc and persistence functionality added

---
 .../util/Import & Export/DirectoryImportBox.tsx    |  59 ++++++++--
 .../util/Import & Export/ImportMetadataEntry.tsx   | 119 +++++++++++++++++++++
 src/client/util/Import & Export/KeyValue.tsx       |  79 --------------
 src/client/views/EditableView.tsx                  |   9 +-
 4 files changed, 178 insertions(+), 88 deletions(-)
 create mode 100644 src/client/util/Import & Export/ImportMetadataEntry.tsx
 delete mode 100644 src/client/util/Import & Export/KeyValue.tsx

(limited to 'src/client/views/EditableView.tsx')

diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 50086a306..88ce9c0e9 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -1,6 +1,6 @@
 import "fs";
 import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
+import { Doc, Opt } from "../../../new_fields/Doc";
 import { DocServer } from "../../DocServer";
 import { RouteStore } from "../../../server/RouteStore";
 import { action, observable, autorun, runInAction } from "mobx";
@@ -11,7 +11,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faArrowUp, faTag, faPlus } from '@fortawesome/free-solid-svg-icons';
 import { Docs, DocumentOptions } from "../../documents/Documents";
 import { observer } from "mobx-react";
-import KeyValue from "./KeyValue";
+import ImportMetadataEntry from "./ImportMetadataEntry";
 import { Utils } from "../../../Utils";
 import { DocumentManager } from "../DocumentManager";
 
@@ -24,12 +24,14 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
 
     @observable private editingMetadata = false;
     @observable private metadata_guids: string[] = [];
-    @observable private entries: KeyValue[] = [];
+    @observable private entries: ImportMetadataEntry[] = [];
 
     @observable private quota = 1;
     @observable private remaining = 1;
 
     @observable private uploadBegun = false;
+    @observable private removeHover = false;
+    @observable private shouldKeep = false;
 
     public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); }
 
@@ -87,7 +89,12 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
 
         await Promise.all(promises);
 
-        docs.forEach(doc => this.entries.forEach(entry => doc[entry.key] = entry.value));
+        docs.forEach(doc => {
+            this.entries.forEach(entry => {
+                let target = entry.onDataDoc ? Doc.GetProto(doc) : doc;
+                target[entry.key] = entry.value;
+            });
+        });
 
         let doc = this.props.Document;
         let options: DocumentOptions = {
@@ -102,8 +109,11 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
             let importContainer = Docs.StackingDocument(docs, options);
             importContainer.singleColumn = false;
             Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
-            this.props.removeDocument && this.props.removeDocument(doc);
+            !this.shouldKeep && this.props.removeDocument && this.props.removeDocument(doc);
             DocumentManager.Instance.jumpToDocument(importContainer, true);
+            this.uploadBegun = false;
+            this.quota = 1;
+            this.remaining = 1;
         }
     }
 
@@ -129,7 +139,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
     }
 
     @action
-    remove = (entry: KeyValue) => {
+    remove = (entry: ImportMetadataEntry) => {
         let index = this.entries.indexOf(entry);
         let key = entry.key;
         this.entries.splice(index, 1);
@@ -144,6 +154,8 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
         let quota = this.quota;
         let percent = `${100 - (remaining / quota * 100)}`;
         let uploadBegun = this.uploadBegun;
+        let showRemoveLabel = this.removeHover;
+        let keep = this.shouldKeep;
         percent = percent.split(".")[0];
         percent = percent.startsWith("100") ? "99" : percent;
         return (
@@ -197,6 +209,30 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
                                 }}
                                 src={"/assets/loading.gif"}></img>
                         </label>
+                        <input
+                            type={"checkbox"}
+                            onChange={e => runInAction(() => this.shouldKeep = e.target.checked)}
+                            style={{
+                                margin: 0,
+                                position: "absolute",
+                                left: 10,
+                                bottom: 10,
+                                opacity: isEditing ? 0 : 1,
+                                transition: "0.4s opacity ease",
+                                pointerEvents: isEditing ? "none" : "all"
+                            }}
+                            onPointerEnter={action(() => this.removeHover = true)}
+                            onPointerLeave={action(() => this.removeHover = false)}
+                        />
+                        <p
+                            style={{
+                                position: "absolute",
+                                left: 27,
+                                bottom: 8.4,
+                                fontSize: 12,
+                                opacity: showRemoveLabel ? 1 : 0,
+                                transition: "0.4s opacity ease"
+                            }}>Template will be {keep ? "kept" : "removed"} after upload</p>
                         <div
                             style={{
                                 transition: "0.4s opacity ease",
@@ -219,6 +255,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
                                 height: 25,
                                 background: "black"
                             }}
+                            title={isEditing ? "Back to Upload" : "Add Metadata"}
                             onClick={action(() => this.editingMetadata = !this.editingMetadata)}
                         />
                         <FontAwesomeIcon
@@ -252,6 +289,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
                                     right: 41,
                                     top: 10
                                 }}
+                                title={"Add Metadata Entry"}
                                 onClick={this.addMetadataEntry}
                             >
                                 <FontAwesomeIcon
@@ -266,7 +304,14 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
                             </div>
                             <p style={{ paddingLeft: 10, paddingTop: 8, paddingBottom: 7 }} >Add metadata to your import...</p>
                             <hr style={{ margin: "6px 10px 12px 10px" }} />
-                            {guids.map(guid => <KeyValue remove={this.remove} key={guid} ref={(el) => { if (el) this.entries.push(el); }} />)}
+                            {guids.map(guid =>
+                                <ImportMetadataEntry
+                                    key={guid}
+                                    remove={this.remove}
+                                    ref={(el) => { if (el) this.entries.push(el); }}
+                                    next={this.addMetadataEntry}
+                                />
+                            )}
                         </div>
                     </div>
                 }
diff --git a/src/client/util/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx
new file mode 100644
index 000000000..3b2a6ebb5
--- /dev/null
+++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx	
@@ -0,0 +1,119 @@
+import React = require("react");
+import { observer } from "mobx-react";
+import { EditableView } from "../../views/EditableView";
+import { observable, action, computed } from "mobx";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faPlus } from "@fortawesome/free-solid-svg-icons";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { Opt } from "../../../new_fields/Doc";
+
+interface KeyValueProps {
+    remove: (self: ImportMetadataEntry) => void;
+    next: () => void;
+}
+
+const keyPlaceholder = "Key";
+const valuePlaceholder = "Value";
+
+@observer
+export default class ImportMetadataEntry extends React.Component<KeyValueProps> {
+    @observable public key = keyPlaceholder;
+    @observable public value = valuePlaceholder;
+
+    private keyRef = React.createRef<EditableView>();
+    private valueRef = React.createRef<EditableView>();
+    private checkRef = React.createRef<HTMLInputElement>();
+
+    constructor(props: KeyValueProps) {
+        super(props);
+        library.add(faPlus);
+    }
+
+    @computed
+    public get valid() {
+        return (this.key.length > 0 && this.key !== keyPlaceholder) && (this.value.length > 0 && this.value !== valuePlaceholder);
+    }
+
+    public get onDataDoc() {
+        return this.checkRef.current && this.checkRef.current.checked;
+    }
+
+    @action
+    updateKey = (newKey: string) => {
+        this.key = newKey;
+        this.keyRef.current && this.keyRef.current.setIsFocused(false);
+        this.valueRef.current && this.valueRef.current.setIsFocused(true);
+        this.key.length === 0 && (this.key = keyPlaceholder);
+        return true;
+    }
+
+    @action
+    updateValue = (newValue: string, shiftDown: boolean) => {
+        this.value = newValue;
+        this.valueRef.current && this.valueRef.current.setIsFocused(false);
+        this.value.length > 0 && shiftDown && this.props.next();
+        this.value.length === 0 && (this.value = valuePlaceholder);
+        return true;
+    }
+
+    render() {
+        let keyValueStyle: React.CSSProperties = {
+            paddingLeft: 10,
+            width: "50%",
+            opacity: this.valid ? 1 : 0.5,
+        };
+        return (
+            <div
+                style={{
+                    display: "flex",
+                    flexDirection: "row",
+                    paddingBottom: 5,
+                    paddingRight: 5,
+                    justifyContent: "center",
+                    alignItems: "center",
+                    alignContent: "center"
+                }}
+            >
+                <input
+                    ref={this.checkRef}
+                    style={{ margin: "0 10px 0 15px" }}
+                    type="checkbox"
+                    title={"Add to Data Document?"}
+                />
+                <div className={"key_container"} style={keyValueStyle}>
+                    <EditableView
+                        ref={this.keyRef}
+                        contents={this.key}
+                        SetValue={this.updateKey}
+                        GetValue={() => ""}
+                        oneLine={true}
+                    />
+                </div>
+                <div
+                    className={"value_container"}
+                    style={keyValueStyle}>
+                    <EditableView
+                        ref={this.valueRef}
+                        contents={this.value}
+                        SetValue={this.updateValue}
+                        GetValue={() => ""}
+                        oneLine={true}
+                    />
+                </div>
+                <div onClick={() => this.props.remove(this)} title={"Delete Entry"}>
+                    <FontAwesomeIcon
+                        icon={faPlus}
+                        color={"red"}
+                        size={"1x"}
+                        style={{
+                            marginLeft: 15,
+                            marginRight: 15,
+                            transform: "rotate(45deg)"
+                        }}
+                    />
+                </div>
+            </div>
+        );
+    }
+
+}
\ No newline at end of file
diff --git a/src/client/util/Import & Export/KeyValue.tsx b/src/client/util/Import & Export/KeyValue.tsx
deleted file mode 100644
index 15ef25f89..000000000
--- a/src/client/util/Import & Export/KeyValue.tsx	
+++ /dev/null
@@ -1,79 +0,0 @@
-import React = require("react");
-import { observer } from "mobx-react";
-import { EditableView } from "../../views/EditableView";
-import { observable, action } from "mobx";
-
-interface KeyValueProps {
-    remove: (self: KeyValue) => void;
-}
-
-@observer
-export default class KeyValue extends React.Component<KeyValueProps> {
-    @observable public key = "Key";
-    @observable public value = "Value";
-
-    @action
-    updateKey = (newKey: string) => {
-        this.key = newKey;
-        return true;
-    }
-
-    @action
-    updateValue = (newValue: string) => {
-        this.value = newValue;
-        return true;
-    }
-
-    render() {
-        let keyValueStyle = { paddingLeft: 10, width: "50%" };
-        let keySpecified = (this.key.length > 0 && this.key !== "Key");
-        return (
-            <div
-                style={{
-                    display: "flex",
-                    flexDirection: "row",
-                    paddingBottom: 5,
-                    paddingRight: 5,
-                    justifyContent: "center",
-                    alignItems: "center",
-                    alignContent: "center"
-                }}
-                onClick={() => this.props.remove(this)}
-            >
-                <input type="checkbox" />
-                <div className={"key_container"} style={keyValueStyle}>
-                    <EditableView
-                        contents={this.key}
-                        SetValue={this.updateKey}
-                        GetValue={() => this.key}
-                        oneLine={true}
-                    />
-                </div>
-                <div
-                    className={"value_container"}
-                    style={{
-                        opacity: keySpecified ? 1 : 0.5,
-                        pointerEvents: keySpecified ? "all" : "none",
-                        ...keyValueStyle
-                    }}>
-                    <EditableView
-                        contents={this.value}
-                        SetValue={this.updateValue}
-                        GetValue={() => this.value}
-                        oneLine={true}
-                    />
-                </div>
-                <div style={{
-                    borderRadius: "50%",
-                    width: 10,
-                    height: 10,
-                    background: "red",
-                    marginLeft: 15,
-                    marginRight: 15
-                }} />
-
-            </div>
-        );
-    }
-
-}
\ No newline at end of file
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index 97a2d19dd..ea6f48d0f 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -14,7 +14,7 @@ export interface EditableProps {
      * @param value - The string entered by the user to set the value to
      * @returns `true` if setting the value was successful, `false` otherwise
      *  */
-    SetValue(value: string): boolean;
+    SetValue(value: string, shiftDown?: boolean): boolean;
 
     OnFillDown?(value: string): void;
 
@@ -52,7 +52,7 @@ export class EditableView extends React.Component<EditableProps> {
             this.props.OnTab && this.props.OnTab();
         } else if (e.key === "Enter") {
             if (!e.ctrlKey) {
-                if (this.props.SetValue(e.currentTarget.value)) {
+                if (this.props.SetValue(e.currentTarget.value, e.shiftKey)) {
                     this._editing = false;
                 }
             } else if (this.props.OnFillDown) {
@@ -76,6 +76,11 @@ export class EditableView extends React.Component<EditableProps> {
         e.stopPropagation();
     }
 
+    @action
+    setIsFocused = (value: boolean) => {
+        this._editing = value;
+    }
+
     render() {
         if (this._editing) {
             return <input className="editableView-input" defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus
-- 
cgit v1.2.3-70-g09d2

