From c9f77d5aab98e6e7865cdcad957d5c937631775d Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Mon, 24 Jun 2019 13:41:39 -0400
Subject: Added ReadOnly mode for docs and changed computed values a bit

---
 src/client/views/nodes/KeyValueBox.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/KeyValueBox.tsx')

diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 3d626eef0..a4c14ae38 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -38,10 +38,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
     public static SetField(doc: Doc, key: string, value: string) {
         let eq = value.startsWith("=");
+        let target = eq ? doc : Doc.GetProto(doc);
         value = eq ? value.substr(1) : value;
         let dubEq = value.startsWith(":=");
         value = dubEq ? value.substr(2) : value;
-        let options: ScriptOptions = { addReturn: true };
+        let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } };
         if (dubEq) options.typecheck = false;
         let script = CompileScript(value, options);
         if (!script.compiled) {
@@ -49,12 +50,11 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         }
         let field = new ComputedField(script);
         if (!dubEq) {
-            let res = script.run();
+            let res = script.run({ this: target });
             if (!res.success) return false;
             field = res.result;
         }
         if (Field.IsField(field, true)) {
-            let target = eq ? doc : Doc.GetProto(doc);
             target[key] = field;
             return true;
         }
-- 
cgit v1.2.3-70-g09d2


From 18b568ce20b66c4e16521c043df804279a5cd163 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <samuel_wilkins@brown.edu>
Date: Wed, 26 Jun 2019 23:19:12 -0400
Subject: implemented drag drop template from key value key selection

---
 src/client/views/nodes/KeyValueBox.scss  |  6 +--
 src/client/views/nodes/KeyValueBox.tsx   | 72 ++++++++++++++++++++++++++++++--
 src/client/views/nodes/KeyValuePair.scss | 39 +++++++++++++----
 src/client/views/nodes/KeyValuePair.tsx  | 62 ++++++++++++++++++++-------
 src/scraping/buxton/scraper.py           |  8 ++--
 5 files changed, 153 insertions(+), 34 deletions(-)

(limited to 'src/client/views/nodes/KeyValueBox.tsx')

diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss
index 20cae03d4..87a9565e8 100644
--- a/src/client/views/nodes/KeyValueBox.scss
+++ b/src/client/views/nodes/KeyValueBox.scss
@@ -91,12 +91,12 @@ $header-height: 30px;
     width: 4px;
     float: left; 
     height: 30px;
-    width: 10px;
+    width: 5px;
     z-index: 20;
     right: 0;
     top: 0;
-    border-radius: 10px;
-    background: gray;
+    border-radius: 0;
+    background: black;
     pointer-events: all;
 }
 .keyValueBox-dividerDragger{
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index cd65c42bc..4beb70284 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -7,13 +7,23 @@ import { FieldView, FieldViewProps } from './FieldView';
 import "./KeyValueBox.scss";
 import { KeyValuePair } from "./KeyValuePair";
 import React = require("react");
-import { NumCast, Cast, FieldValue } from "../../../new_fields/Types";
-import { Doc, Field } from "../../../new_fields/Doc";
+import { NumCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types";
+import { Doc, Field, FieldResult } from "../../../new_fields/Doc";
 import { ComputedField } from "../../../new_fields/ScriptField";
+import { SetupDrag } from "../../util/DragManager";
+import { Docs } from "../../documents/Documents";
+import { RawDataOperationParameters } from "../../northstar/model/idea/idea";
+import { Templates } from "../Templates";
+import { List } from "../../../new_fields/List";
+import { TextField } from "../../util/ProsemirrorCopy/prompt";
+import { RichTextField } from "../../../new_fields/RichTextField";
+import { ImageField } from "../../../new_fields/URLField";
 
 @observer
 export class KeyValueBox extends React.Component<FieldViewProps> {
     private _mainCont = React.createRef<HTMLDivElement>();
+    private _keyHeader = React.createRef<HTMLTableHeaderCellElement>();
+    @observable private rows: KeyValuePair[] = [];
 
     public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(KeyValueBox, fieldStr); }
     @observable private _keyInput: string = "";
@@ -90,7 +100,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         let rows: JSX.Element[] = [];
         let i = 0;
         for (let key of Object.keys(ids).sort()) {
-            rows.push(<KeyValuePair doc={realDoc} keyWidth={100 - this.splitPercentage} rowStyle={"keyValueBox-" + (i++ % 2 ? "oddRow" : "evenRow")} key={key} keyName={key} />);
+            rows.push(<KeyValuePair doc={realDoc} ref={(el) => { if (el) this.rows.push(el); }} keyWidth={100 - this.splitPercentage} rowStyle={"keyValueBox-" + (i++ % 2 ? "oddRow" : "evenRow")} key={key} keyName={key} />);
         }
         return rows;
     }
@@ -134,6 +144,58 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         document.addEventListener('pointerup', this.onDividerUp);
     }
 
+    getTemplate = async () => {
+        let parent = Docs.FreeformDocument([], { width: 800, height: 800, title: "Template" });
+        for (let row of this.rows.filter(row => row.isChecked)) {
+            await this.createTemplateField(parent, row);
+            row.uncheck();
+        }
+        return parent;
+    }
+
+    createTemplateField = async (parent: Doc, row: KeyValuePair) => {
+        let collectionKeyProp = `fieldKey={"data"}`;
+        let metaKey = row.props.keyName;
+        let metaKeyProp = `fieldKey={"${metaKey}"}`;
+
+        let sourceDoc = await Cast(this.props.Document.data, Doc);
+        if (!sourceDoc) {
+            return;
+        }
+        let target = this.inferType(sourceDoc[metaKey], metaKey);
+
+        let template = Doc.MakeAlias(target);
+        template.proto = parent;
+        template.title = metaKey;
+        template.nativeWidth = 300;
+        template.nativeHeight = 300;
+        template.embed = true;
+        template.isTemplate = true;
+        template.templates = new List<string>([Templates.TitleBar(metaKey)]);
+        if (target.backgroundLayout) {
+            let metaAnoKeyProp = `fieldKey={"${metaKey}"} fieldExt={"annotations"}`;
+            let collectionAnoKeyProp = `fieldKey={"annotations"}`;
+            template.layout = StrCast(target.layout).replace(collectionAnoKeyProp, metaAnoKeyProp);
+            template.backgroundLayout = StrCast(target.backgroundLayout).replace(collectionKeyProp, metaKeyProp);
+        } else {
+            template.layout = StrCast(target.layout).replace(collectionKeyProp, metaKeyProp);
+        }
+        Doc.AddDocToList(parent, "data", template);
+        row.uncheck();
+    }
+
+    inferType = (field: FieldResult, metaKey: string) => {
+        let options = { width: 300, height: 300, title: metaKey };
+        if (field instanceof RichTextField || typeof field === "string" || typeof field === "number") {
+            return Docs.TextDocument(options);
+        } else if (field instanceof List) {
+            return Docs.FreeformDocument([], options);
+        } else if (field instanceof ImageField) {
+            return Docs.ImageDocument("https://www.freepik.com/free-icon/picture-frame-with-mountain-image_748687.htm", options);
+        }
+        return new Doc;
+    }
+
     render() {
         let dividerDragger = this.splitPercentage === 0 ? (null) :
             <div className="keyValueBox-dividerDragger" style={{ transform: `translate(calc(${100 - this.splitPercentage}% - 5px), 0px)` }}>
@@ -144,7 +206,9 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
             <table className="keyValueBox-table">
                 <tbody className="keyValueBox-tbody">
                     <tr className="keyValueBox-header">
-                        <th className="keyValueBox-key" style={{ width: `${100 - this.splitPercentage}%` }}>Key</th>
+                        <th className="keyValueBox-key" style={{ width: `${100 - this.splitPercentage}%` }} ref={this._keyHeader}
+                            onPointerDown={SetupDrag(this._keyHeader, this.getTemplate)}
+                        >Key</th>
                         <th className="keyValueBox-fields" style={{ width: `${this.splitPercentage}%` }}>Fields</th>
                     </tr>
                     {this.createTable()}
diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss
index a1c5d5537..f78767234 100644
--- a/src/client/views/nodes/KeyValuePair.scss
+++ b/src/client/views/nodes/KeyValuePair.scss
@@ -3,6 +3,7 @@
 
 .keyValuePair-td-key {
     display:inline-block;
+
     .keyValuePair-td-key-container{
         width:100%;
         height:100%;
@@ -10,14 +11,23 @@
         flex-direction: row;           
         flex-wrap: nowrap;              
         justify-content: space-between; 
+        align-items: center;
         .keyValuePair-td-key-delete{
             position: relative;
             background-color: transparent;
             color:red;
         }
+        .keyValuePair-td-key-check {
+            position: relative;
+            margin: 0;
+        }
         .keyValuePair-keyField {
             width:100%;
-            text-align: center;
+            margin-left: 20px;
+            margin-top: -1px;
+            font-family: monospace;
+            // text-align: center;
+            align-self: center;
             position: relative;
             overflow: auto;
         }
@@ -26,12 +36,25 @@
 .keyValuePair-td-value {
     display:inline-block;
     overflow: scroll;
-    img { 
-        max-height: 36px;
-        width: auto;
-    }
-    .videoBox-cont{
-        width: auto; 
-        max-height: 36px; 
+    font-family: monospace;
+    height: 30px;
+        .keyValuePair-td-value-container {
+            display: flex;
+            align-items: center;
+            align-content: center;
+            flex-direction: row;
+            justify-content: space-between;
+            flex-wrap: nowrap;
+            width: 100%;
+            height: 100%;
+
+            img { 
+                max-height: 36px;
+                width: auto;
+            }
+            .videoBox-cont{
+                width: auto; 
+                max-height: 36px; 
+            }
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index ede4e3858..b5db52ac7 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -12,6 +12,7 @@ import React = require("react");
 import { Doc, Opt, Field } from '../../../new_fields/Doc';
 import { FieldValue } from '../../../new_fields/Types';
 import { KeyValueBox } from './KeyValueBox';
+import { DragManager, SetupDrag } from '../../util/DragManager';
 
 // Represents one row in a key value plane
 
@@ -23,6 +24,20 @@ export interface KeyValuePairProps {
 }
 @observer
 export class KeyValuePair extends React.Component<KeyValuePairProps> {
+    @observable private isPointerOver = false;
+    @observable public isChecked = false;
+    private checkbox = React.createRef<HTMLInputElement>();
+
+    @action
+    handleCheck = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.isChecked = e.currentTarget.checked;
+    }
+
+    @action
+    uncheck = () => {
+        this.checkbox.current!.checked = false;
+        this.isChecked = false;
+    }
 
     render() {
         let props: FieldViewProps = {
@@ -44,12 +59,16 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             addDocTab: returnZero,
         };
         let contents = <FieldView {...props} />;
-        let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
+        // let fieldKey = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? props.fieldKey : "(" + props.fieldKey + ")";
+        let keyStyle = Object.keys(props.Document).indexOf(props.fieldKey) !== -1 ? "black" : "blue";
+
+        let hover = { transition: "0.3s ease opacity", opacity: this.isPointerOver || this.isChecked ? 1 : 0 };
+
         return (
-            <tr className={this.props.rowStyle}>
+            <tr className={this.props.rowStyle} onPointerEnter={action(() => this.isPointerOver = true)} onPointerLeave={action(() => this.isPointerOver = false)}>
                 <td className="keyValuePair-td-key" style={{ width: `${this.props.keyWidth}%` }}>
                     <div className="keyValuePair-td-key-container">
-                        <button className="keyValuePair-td-key-delete" onClick={() => {
+                        <button style={hover} className="keyValuePair-td-key-delete" onClick={() => {
                             if (Object.keys(props.Document).indexOf(props.fieldKey) !== -1) {
                                 props.Document[props.fieldKey] = undefined;
                             }
@@ -57,22 +76,35 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
                         }}>
                             X
                         </button>
-                        <div className="keyValuePair-keyField">{fieldKey}</div>
+                        <input
+                            className={"keyValuePair-td-key-check"}
+                            type="checkbox"
+                            style={hover}
+                            onChange={this.handleCheck}
+                            ref={this.checkbox}
+                        />
+                        <div className="keyValuePair-keyField" style={{ color: keyStyle }}>{props.fieldKey}</div>
                     </div>
                 </td>
                 <td className="keyValuePair-td-value" style={{ width: `${100 - this.props.keyWidth}%` }}>
-                    <EditableView contents={contents} height={36} GetValue={() => {
-                        const onDelegate = Object.keys(props.Document).includes(props.fieldKey);
+                    <div className="keyValuePair-td-value-container">
+                        <EditableView
+                            contents={contents}
+                            height={36}
+                            GetValue={() => {
+                                const onDelegate = Object.keys(props.Document).includes(props.fieldKey);
 
-                        let field = FieldValue(props.Document[props.fieldKey]);
-                        if (Field.IsField(field)) {
-                            return (onDelegate ? "=" : "") + Field.toScriptString(field);
-                        }
-                        return "";
-                    }}
-                        SetValue={(value: string) =>
-                            KeyValueBox.SetField(props.Document, props.fieldKey, value)}>
-                    </EditableView></td>
+                                let field = FieldValue(props.Document[props.fieldKey]);
+                                if (Field.IsField(field)) {
+                                    return (onDelegate ? "=" : "") + Field.toScriptString(field);
+                                }
+                                return "";
+                            }}
+                            SetValue={(value: string) =>
+                                KeyValueBox.SetField(props.Document, props.fieldKey, value)}>
+                        </EditableView>
+                    </div>
+                </td>
             </tr>
         );
     }
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
index 97af10519..fcffeac13 100644
--- a/src/scraping/buxton/scraper.py
+++ b/src/scraping/buxton/scraper.py
@@ -60,7 +60,7 @@ def protofy(fieldId):
     }
 
 
-def write_schema(parse_results, display_fields):
+def write_schema(parse_results, display_fields, storage_key):
     view_guids = parse_results["child_guids"]
 
     data_doc = parse_results["schema"]
@@ -87,7 +87,7 @@ def write_schema(parse_results, display_fields):
     }
 
     fields["proto"] = protofy("collectionProto")
-    fields["data"] = listify(proxify_guids(view_guids))
+    fields[storage_key] = listify(proxify_guids(view_guids))
     fields["schemaColumns"] = listify(display_fields)
     fields["backgroundColor"] = "white"
     fields["scale"] = 0.5
@@ -304,7 +304,7 @@ for file_name in os.listdir(source):
     if file_name.endswith('.docx'):
         candidates += 1
         schema_guids.append(write_schema(
-            parse_document(file_name), ["title", "data"]))
+            parse_document(file_name), ["title", "data"], "image_data"))
 
 print("writing parent schema...")
 parent_guid = write_schema({
@@ -314,7 +314,7 @@ parent_guid = write_schema({
         "__type": "Doc"
     },
     "child_guids": schema_guids
-}, ["title", "short_description", "original_price"])
+}, ["title", "short_description", "original_price"], "data")
 
 print("appending parent schema to main workspace...\n")
 db.newDocuments.update_one(
-- 
cgit v1.2.3-70-g09d2


From 5b2ee61fd04f18478e3b5355ae19cd06a0840fa5 Mon Sep 17 00:00:00 2001
From: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com>
Date: Thu, 27 Jun 2019 12:39:36 -0400
Subject: templating workflow improvements

---
 src/client/views/nodes/KeyValueBox.tsx | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

(limited to 'src/client/views/nodes/KeyValueBox.tsx')

diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 4beb70284..af22f0a48 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -145,7 +145,9 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     }
 
     getTemplate = async () => {
-        let parent = Docs.FreeformDocument([], { width: 800, height: 800, title: "Template" });
+        let parent = Docs.StackingDocument([], { width: 800, height: 800, title: "Template" });
+        parent.singleColumn = false;
+        parent.columnWidth = 50;
         for (let row of this.rows.filter(row => row.isChecked)) {
             await this.createTemplateField(parent, row);
             row.uncheck();
@@ -167,8 +169,8 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         let template = Doc.MakeAlias(target);
         template.proto = parent;
         template.title = metaKey;
-        template.nativeWidth = 300;
-        template.nativeHeight = 300;
+        template.nativeWidth = 0;
+        template.nativeHeight = 0;
         template.embed = true;
         template.isTemplate = true;
         template.templates = new List<string>([Templates.TitleBar(metaKey)]);
@@ -187,7 +189,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
     inferType = (field: FieldResult, metaKey: string) => {
         let options = { width: 300, height: 300, title: metaKey };
         if (field instanceof RichTextField || typeof field === "string" || typeof field === "number") {
-            return Docs.TextDocument(options);
+            return Docs.StackingDocument(options);
         } else if (field instanceof List) {
             return Docs.FreeformDocument([], options);
         } else if (field instanceof ImageField) {
@@ -218,4 +220,4 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
             {dividerDragger}
         </div>);
     }
-}
\ No newline at end of file
+}
-- 
cgit v1.2.3-70-g09d2

