From 05c429412d36531bed4e8ece889479fcf93faea6 Mon Sep 17 00:00:00 2001
From: kimdahey <claire_kim1@brown.edu>
Date: Sun, 9 Feb 2020 16:43:01 -0500
Subject: functioning

---
 .../views/collections/CollectionDockingView.tsx    | 69 ++++++++++++++++++++++
 1 file changed, 69 insertions(+)

(limited to 'src/client/views/collections/CollectionDockingView.tsx')

diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 82cb3bc88..7a6d54ac2 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -249,6 +249,75 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
         return true;
     }
 
+
+    //
+    //  Creates a split on the any side of the docking view, based on the passed input pullSide and then adds the Document to the requested side
+    //
+    @undoBatch
+    @action
+    public static AddSplit(document: Doc, pullSide: string, dataDoc: Doc | undefined, libraryPath?: Doc[]) {
+        if (!CollectionDockingView.Instance) return false;
+        const instance = CollectionDockingView.Instance;
+        const newItemStackConfig = {
+            type: 'stack',
+            content: [CollectionDockingView.makeDocumentConfig(document, dataDoc, undefined, libraryPath)]
+        };
+
+        const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+
+        if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns
+            instance._goldenLayout.root.addChild(newContentItem);
+        } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row
+            if (pullSide === "left") {
+                instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0);
+            } else if (pullSide === "right") {
+                instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
+            } else if (pullSide === "top" || pullSide === "bottom") {
+                // if not going in a row layout, must add already existing content into column
+                const rowlayout = instance._goldenLayout.root.contentItems[0];
+                const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout);
+                rowlayout.parent.replaceChild(rowlayout, newColumn);
+                if (pullSide === "top") {
+                    newColumn.addChild(rowlayout, undefined, true);
+                    newColumn.addChild(newContentItem, 0, true);
+                } else if (pullSide === "bottom") {
+                    newColumn.addChild(newContentItem, undefined, true);
+                    newColumn.addChild(rowlayout, 0, true);
+                }
+
+                rowlayout.config.height = 50;
+                newContentItem.config.height = 50;
+            }
+        } else if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
+            if (pullSide === "top") {
+                instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0);
+            } else if (pullSide === "bottom") {
+                instance._goldenLayout.root.contentItems[0].addChild(newContentItem);
+            } else if (pullSide === "left" || pullSide === "right") {
+                // if not going in a row layout, must add already existing content into column
+                const collayout = instance._goldenLayout.root.contentItems[0];
+                const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
+                collayout.parent.replaceChild(collayout, newRow);
+
+                if (pullSide === "left") {
+                    newRow.addChild(collayout, undefined, true);
+                    newRow.addChild(newContentItem, 0, true);
+                } else if (pullSide === "right") {
+                    newRow.addChild(newContentItem, undefined, true);
+                    newRow.addChild(collayout, 0, true);
+                }
+
+                collayout.config.width = 50;
+                newContentItem.config.width = 50;
+            }
+        }
+
+        newContentItem.callDownwards('_$init');
+        instance.layoutChanged();
+        return true;
+    }
+
+
     //
     //  Creates a vertical split on the right side of the docking view, and then adds the Document to that split
     //
-- 
cgit v1.2.3-70-g09d2


From 05399b3916df7311d07e7c375f90261e9c8ead80 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 25 Feb 2020 00:57:37 -0500
Subject: added optional display templates for metadata field values

---
 src/client/documents/Documents.ts                  | 10 ++++---
 src/client/util/RichTextSchema.tsx                 | 32 ++++++++++++++--------
 .../views/collections/CollectionDockingView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx        |  4 +--
 src/new_fields/Doc.ts                              | 19 +++++++++----
 src/new_fields/RichTextField.ts                    |  4 +++
 .../authentication/models/current_user_utils.ts    | 18 ++++++++----
 8 files changed, 60 insertions(+), 31 deletions(-)

(limited to 'src/client/views/collections/CollectionDockingView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ff64489bb..ff152ec6c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -97,7 +97,10 @@ export interface DocumentOptions {
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
     isTemplateDoc?: boolean;
     templates?: List<string>;
-    backgroundColor?: string | ScriptField;
+    backgroundColor?: string | ScriptField;  // background color for data doc 
+    _backgroundColor?: string | ScriptField; // background color for each template layout doc ( overrides backgroundColor )
+    color?: string; // foreground color data doc
+    _color?: string;  // foreground color for each template layout doc (overrides color)
     ignoreClick?: boolean;
     lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
     lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
@@ -133,7 +136,6 @@ export interface DocumentOptions {
     sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script
     targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script
     strokeWidth?: number;
-    color?: string;
     treeViewPreventOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expand/collapse state to be independent of other views of the same document in the tree view
     treeViewHideTitle?: boolean; // whether to hide the title of a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
@@ -405,8 +407,8 @@ export namespace Docs {
         Scripting.addGlobal(Buxton);
 
         const delegateKeys = ["x", "y", "layoutKey", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "dropAction", "childDropAction", "_annotationOn",
-            "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "_showTitle", "_showCaption", "_showTitleHover",
-            "isButton", "isBackground", "removeDropProperties", "treeViewOpen"];
+            "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_showSidebar", "_showTitle", "_showCaption", "_showTitleHover", "_backgroundColor",
+            "_color", "isButton", "isBackground", "removeDropProperties", "treeViewOpen"];
 
         /**
          * This function receives the relevant document prototype and uses
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index cfbae5dca..ae3f4e731 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -8,7 +8,7 @@ import { EditorState, NodeSelection, Plugin, TextSelection } from "prosemirror-s
 import { StepMap } from "prosemirror-transform";
 import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
-import { Doc, Field, HeightSym, WidthSym } from "../../new_fields/Doc";
+import { Doc, Field, HeightSym, WidthSym, DocListCast } from "../../new_fields/Doc";
 import { Id } from "../../new_fields/FieldSymbols";
 import { ObjectField } from "../../new_fields/ObjectField";
 import { ComputedField } from "../../new_fields/ScriptField";
@@ -850,17 +850,19 @@ export class DashDocView {
 
 
 export class DashFieldView {
-    _fieldWrapper: HTMLDivElement;
-    _labelSpan: HTMLSpanElement;
-    _fieldSpan: HTMLDivElement;
+    _fieldWrapper: HTMLDivElement; // container for label and value
+    _labelSpan: HTMLSpanElement; // field label
+    _fieldSpan: HTMLDivElement;  // field value
     _reactionDisposer: IReactionDisposer | undefined;
     _textBoxDoc: Doc;
     @observable _dashDoc: Doc | undefined;
     _fieldKey: string;
+    _options: Doc[] = [];
 
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
         this._fieldKey = node.attrs.fieldKey;
         this._textBoxDoc = tbox.props.Document;
+        this._options = DocListCast(tbox.props.Document[node.attrs.fieldKey + "_options"]);
         this._fieldWrapper = document.createElement("div");
         this._fieldWrapper.style.width = node.attrs.width;
         this._fieldWrapper.style.height = node.attrs.height;
@@ -877,11 +879,15 @@ export class DashFieldView {
         this._fieldSpan.addEventListener("input", this.onchanged);
         this._fieldSpan.onkeypress = function (e: any) { e.stopPropagation(); };
         this._fieldSpan.onkeyup = function (e: any) { e.stopPropagation(); };
-        this._fieldSpan.onmousedown = function (e: any) {
-            console.log(e);
-            e.stopPropagation();
-        };
+        this._fieldSpan.onmousedown = function (e: any) { e.stopPropagation(); };
+
         const self = this;
+        const setDashDoc = (doc: Doc) => {
+            self._dashDoc = doc;
+            if (this._dashDoc && self._options?.length && !this._dashDoc[node.attrs.fieldKey]) {
+                this._dashDoc[node.attrs.fieldKey] = StrCast(self._options[0].title);
+            }
+        }
         this._fieldSpan.onkeydown = function (e: any) {
             e.stopPropagation();
             if ((e.key === "a" && e.ctrlKey) || (e.key === "a" && e.metaKey)) {
@@ -902,10 +908,9 @@ export class DashFieldView {
         this._labelSpan.style.fontSize = "larger";
         this._labelSpan.innerHTML = `${node.attrs.fieldKey}: `;
         if (node.attrs.docid) {
-            const self = this;
-            DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && runInAction(() => self._dashDoc = dashDoc));
+            DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && runInAction(() => setDashDoc(dashDoc)));
         } else {
-            this._dashDoc = tbox.props.DataDoc || tbox.dataDoc;
+            setDashDoc(tbox.props.DataDoc || tbox.dataDoc);
         }
         this._reactionDisposer?.();
         this._reactionDisposer = reaction(() => this._dashDoc?.[node.attrs.fieldKey], fval => this._fieldSpan.innerHTML = Field.toString(fval as Field) || "(null)", { fireImmediately: true });
@@ -916,7 +921,10 @@ export class DashFieldView {
     }
     onchanged = () => {
         this._reactionDisposer?.();
-        this._dashDoc![this._fieldKey] = this._fieldSpan.innerText;
+
+        let newText = this._fieldSpan.innerText;
+        this._options?.forEach(opt => StrCast(opt.title).startsWith(newText) && (newText = StrCast(opt.title)));
+        this._dashDoc![this._fieldKey] = newText;
         this._reactionDisposer = reaction(() => this._dashDoc?.[this._fieldKey], fval => this._fieldSpan.innerHTML = Field.toString(fval as Field) || "(null)");
 
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 83dbb4263..1e38a8927 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -715,7 +715,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
         TraceMobx();
         if (!this._document) return (null);
         const document = this._document;
-        const resolvedDataDoc = !Doc.AreProtosEqual(this._document[DataSym], this._document) && this._document[DataSym];// document.layout instanceof Doc ? document : this._dataDoc;
+        const resolvedDataDoc = !Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined;// document.layout instanceof Doc ? document : this._dataDoc;
         return <DocumentView key={document[Id]}
             LibraryPath={this._libraryPath}
             Document={document}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 84e9b6abb..aec18ecbb 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -888,7 +888,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
     render() {
         if (!(this.props.Document instanceof Doc)) return (null);
-        const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor?.(this.Document);
+        const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
         const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document);
         const borderRounding = this.layoutDoc.borderRounding;
         const localScale = fullDegree;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index eac495082..5a664f31c 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -192,10 +192,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
                 this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
                 if (!curTemp || curText) { // if no template, or there's text, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
                     this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON()), curText);
-                    this.dataDoc[this.props.fieldKey + "-noTemplate"] = curTemp?.Text !== curText;
+                    this.dataDoc[this.props.fieldKey + "-noTemplate"] = curTemp?.Text !== curText; // mark the data field as being split from the template if it has been edited
                 } else { // if we've deleted all the text in a note driven by a template, then restore the template data
                     this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse(curTemp.Data)));
-                    this.dataDoc[this.props.fieldKey + "-noTemplate"] = undefined;
+                    this.dataDoc[this.props.fieldKey + "-noTemplate"] = undefined; // mark the data field as not being split from any template it might have
                 }
                 this._applyingChange = false;
             }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 6142ebb36..10f4309be 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -17,7 +17,6 @@ import { listSpec } from "./Schema";
 import { ComputedField } from "./ScriptField";
 import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
 import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
-import { DocumentManager } from "../client/util/DocumentManager";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -612,15 +611,16 @@ export namespace Doc {
         templateField.isTemplateForField = metadataFieldKey;
         templateField.title = metadataFieldKey;
 
+        const templateFieldValue = templateField[metadataFieldKey];
         // move any data that the template field had been rendering over to the template doc so that things will still be rendered
         // when the template field is adjusted to point to the new metadatafield key.
         // note 1: if the template field contained a list of documents, each of those documents will be converted to templates as well.
         // note 2: this will not overwrite any field that already exists on the template doc at the field key
-        if (!templateDoc?.[metadataFieldKey] && templateField.data instanceof ObjectField) {
-            Cast(templateField.data, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
-            (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateField.data));
+        if (!templateDoc?.[metadataFieldKey] && templateFieldValue instanceof ObjectField) {
+            Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
+            (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue));
         }
-        if (templateField.data instanceof RichTextField && (templateField.data.Text || templateField.data.Data.toString().includes("dashField"))) {
+        if (templateFieldValue instanceof RichTextField && (templateFieldValue.Text || templateFieldValue.Data.toString().includes("dashField"))) {
             templateField._textTemplate = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`, { this: Doc.name });
         }
 
@@ -838,6 +838,15 @@ export namespace Doc {
         DocServer.GetRefField(id).then(layout => layout instanceof Doc && (doc[field] = layout));
         return id;
     }
+
+    export function enumeratedTextTemplate(doc: Doc, layoutString: string, dataKey: string, optionKey: string, modes: Doc[]) {
+        doc[dataKey] = RichTextField.DashField(optionKey);
+        doc.layout = layoutString;
+        const optionsField = `${optionKey}_options`;
+        doc[optionsField] = new List<Doc>(modes);
+        doc.backgroundColor = ComputedField.MakeFunction(`this['${optionsField}'].find(doc => doc.title === this.expandedTemplate.${optionKey})?._backgroundColor || "white"`);
+        doc.color = ComputedField.MakeFunction(`this['${optionsField}'].find(doc => doc.title === this.expandedTemplate.${optionKey}).color || "black"`);
+    }
 }
 
 Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
index a0f21f45e..712f3e76b 100644
--- a/src/new_fields/RichTextField.ts
+++ b/src/new_fields/RichTextField.ts
@@ -30,4 +30,8 @@ export class RichTextField extends ObjectField {
         return this.Text;
     }
 
+    public static DashField(fieldKey:string) {
+        return new RichTextField(`{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"${fieldKey}","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}`, "");
+    }
+
 }
\ No newline at end of file
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 8fe2257fc..aaf3a3eb6 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -37,13 +37,19 @@ export class CurrentUserUtils {
 
     static setupDefaultDocTemplates(doc: Doc, buttons?: string[]) {
         const noteTemplates = [
-            Docs.Create.TextDocument("", { title: "Note", backgroundColor: "yellow", isTemplateDoc: true }),
-            Docs.Create.TextDocument("", { title: "Idea", backgroundColor: "pink", isTemplateDoc: true }),
-            Docs.Create.TextDocument("", { title: "Topic", backgroundColor: "lightBlue", isTemplateDoc: true }),
-            Docs.Create.TextDocument("", { title: "Person", backgroundColor: "lightGreen", isTemplateDoc: true }),
-            Docs.Create.TextDocument("", { title: "Todo", backgroundColor: "orange", isTemplateDoc: true })
+            Docs.Create.TextDocument("", { title: "Note", backgroundColor: "yellow" }),
+            Docs.Create.TextDocument("", { title: "Idea", backgroundColor: "pink" }),
+            Docs.Create.TextDocument("", { title: "Topic", backgroundColor: "lightBlue" }),
+            Docs.Create.TextDocument("", { title: "Person", backgroundColor: "lightGreen" }),
+            Docs.Create.TextDocument("", { title: "Todo", backgroundColor: "orange" })
         ];
-        doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates, { title: "Note Types", _height: 75 }));
+        const modes = [
+            Docs.Create.TextDocument("", { title: "todo", _backgroundColor: "blue", color: "white" }),
+            Docs.Create.TextDocument("", { title: "in progress", _backgroundColor: "yellow", color: "black" }),
+            Docs.Create.TextDocument("", { title: "completed", _backgroundColor: "green", color: "white" })
+        ]
+        Doc.enumeratedTextTemplate(Doc.GetProto(noteTemplates[4]), FormattedTextBox.LayoutString("Todo"), "Todo", "taskStatus", modes);
+        doc.noteTypes = new PrefetchProxy(Docs.Create.TreeDocument(noteTemplates.map(nt => makeTemplate(nt) ? nt : nt), { title: "Note Types", _height: 75 }));
     }
 
     // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
-- 
cgit v1.2.3-70-g09d2

