From 5246c3c38acda6cbd8636577ff8f658657609d88 Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Mon, 1 Apr 2019 17:22:49 -0400
Subject: set up for templating

---
 src/client/views/Templates.tsx | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 src/client/views/Templates.tsx

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

diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
new file mode 100644
index 000000000..8260f9414
--- /dev/null
+++ b/src/client/views/Templates.tsx
@@ -0,0 +1,40 @@
+import { observer } from "mobx-react";
+import { observable } from "mobx";
+import { action, computed } from "mobx";
+import React = require("react");
+
+export class Template {
+    constructor(layout: string) {
+        this._layout = layout;
+    }
+
+    private _layout: string = "";
+
+    get Layout(): string {
+        return this._layout;
+    }
+}
+
+export namespace Templates {
+    export const OuterCaption = new Template(`
+    <div>
+        <div style="margin:auto; height:calc(100%); width:100%;">
+            {layout}
+        </div>
+        <div style="height:(100% + 25px); width:100%; position:absolute">
+            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+        </div>
+    </div>       
+            `);
+    export const InnerCaption = new Template(`
+    <div>
+        <div style="margin:auto; height:calc(100% - 25px); width:100%;">
+            {layout}
+        </div>
+        <div style="height:25px; width:100%; position:absolute">
+            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+        </div>
+    </div>       
+            `);
+}
+
-- 
cgit v1.2.3-70-g09d2


From 16e861bd4c16483783cdc0534af614625dd0f775 Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Mon, 1 Apr 2019 20:47:01 -0400
Subject: created template adding menu

---
 src/client/views/DocumentDecorations.scss | 72 ++++++++++++-------------
 src/client/views/DocumentDecorations.tsx  | 89 +++++++++++++++++++------------
 src/client/views/Templates.tsx            | 18 +++++--
 src/client/views/nodes/DocumentView.tsx   | 21 ++++++--
 4 files changed, 120 insertions(+), 80 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 642d7caf3..f6d332a36 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -70,26 +70,21 @@
 //         cursor: ew-resize;
 //     }
 // }
-.linkFlyout {
-    grid-column: 1/4
-}
 
-.linkButton-empty:hover {
-    background: $main-accent;
-    transform: scale(1.05);
-    cursor: pointer;
+.documentDecorations-extra {
+    display: flex;
+    position: absolute;
 }
 
-.linkButton-nonempty:hover {
-    background: $main-accent;
-    transform: scale(1.05);
-    cursor: pointer;
+.documentDecorations-ex-wrapper {
+    margin-right: 10px;
 }
 
-.linkButton-empty {
+.linkButton-empty,
+.linkButton-nonempty,
+.documentDecorations-ex {
     height: 20px;
     width: 20px;
-    margin-top: 10px;
     border-radius: 50%;
     opacity: 0.9;
     pointer-events: auto;
@@ -103,34 +98,33 @@
     display: flex;
     justify-content: center;
     align-items: center;
+
+    &:hover {
+        background: $main-accent;
+        transform: scale(1.05);
+        cursor: pointer;
+    }
 }
 
-.linkButton-nonempty {
-    height: 20px;
-    width: 20px;
-    margin-top: 10px;
-    border-radius: 50%;
-    opacity: 0.9;
-    pointer-events: auto;
-    background-color: $dark-color;
-    color: $light-color;
-    text-transform: uppercase;
-    letter-spacing: 2px;
-    font-size: 75%;
-    transition: transform 0.2s;
-    text-align: center;
-    display: flex;
-    justify-content: center;
-    align-items: center;
+.templating-button-wrapper {
+    position: relative;
 }
 
-.templating-button {
-    height: 20px;
-    width: 20px;
-    margin-top: 10px;
-    border-radius: 50%;
-    opacity: 0.9;
-    pointer-events: auto;
-    background-color: red;
-    cursor: pointer;
+#template-list {
+    position: absolute;
+    top: 0;
+    left: 30px;
+    width: 150px;
+    line-height: 25px;
+    max-height: 175px;
+    font-family: $sans-serif;
+    font-size: 12px;
+    background-color: $light-color-secondary;
+    padding: 2px 12px;
+
+    input {
+        margin-right: 10px;
+    }
+
+
 }
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 6d60f9740..9fe3cdc75 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -9,7 +9,7 @@ import { props } from "bluebird";
 import { DragManager } from "../util/DragManager";
 import { LinkMenu } from "./nodes/LinkMenu";
 import { ListField } from "../../fields/ListField";
-import { Templates } from "./Templates";
+import { Templates, Template } from "./Templates";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -23,6 +23,7 @@ export class DocumentDecorations extends React.Component {
     private _resizeBorderWidth = 16;
     private _linkButton = React.createRef<HTMLDivElement>();
     @observable private _hidden = false;
+    @observable private _templatesActive: boolean = false;
 
     constructor(props: Readonly<{}>) {
         super(props)
@@ -193,20 +194,18 @@ export class DocumentDecorations extends React.Component {
     //     e.stopPropagation();
     // }
 
-    addTemplate = (): void => {
-        console.log("add template");
-        let text = `
-        <div>
-            <div style="margin:auto; height:calc(100%); width:100%;">
-                {layout}
-            </div>
-            <div style="height:(100% + 25px); width:100%; position:absolute">
-                <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
-            </div>
-        </div>       
-                `;
+    toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
         let view = SelectionManager.SelectedDocuments()[0];
-        view.addTemplate(Templates.OuterCaption);
+        if (event.target.checked) {
+            view.addTemplate(template);
+        } else {
+            view.removeTemplate(template);
+        }
+    }
+
+    @action
+    toggleTemplateActivity = (): void => {
+        this._templatesActive = !this._templatesActive;
     }
 
     render() {
@@ -234,28 +233,52 @@ export class DocumentDecorations extends React.Component {
                 <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
             </Flyout>);
         }
-        return (
-            <div id="documentDecorations-container" style={{
-                width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
-                height: (bounds.b - bounds.y + this._resizeBorderWidth + 30) + "px",
-                left: bounds.x - this._resizeBorderWidth / 2,
-                top: bounds.y - this._resizeBorderWidth / 2,
-            }}>
-                <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-topResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-leftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-centerCont"></div>
-                <div id="documentDecorations-rightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
-                <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
 
-                <div title="View Links" className="linkFlyout" ref={this._linkButton}>{linkButton}</div>
+        let templateMenu = !this._templatesActive ? (null) : (
+            <ul id="template-list">
+                {Array.from(Object.values(Templates)).map(template => {
+                    return (
+                        <li>
+                            <input type="checkbox" onChange={(event) => this.toggleTemplate(event, template)} />
+                            {template.Name}
+                        </li>
+                    )
+                })}
+            </ul>
+        )
+        return (
+            <div id="documentDecorations">
+                <div id="documentDecorations-container" style={{
+                    width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+                    height: (bounds.b - bounds.y + this._resizeBorderWidth + 30) + "px",
+                    left: bounds.x - this._resizeBorderWidth / 2,
+                    top: bounds.y - this._resizeBorderWidth / 2,
+                }}>
+                    <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-topResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-leftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-centerCont"></div>
+                    <div id="documentDecorations-rightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                    <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
 
-                <div className="templating-button" onClick={this.addTemplate}></div>
+                </div >
+                <div className="documentDecorations-extra" style={{
+                    width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+                    left: bounds.x - this._resizeBorderWidth / 2,
+                    top: bounds.y - this._resizeBorderWidth / 2 + (bounds.b - bounds.y + this._resizeBorderWidth)
+                }}>
+                    <div title="View Links" className="linkFlyout documentDecorations-ex-wrapper" ref={this._linkButton}>{linkButton}</div>
 
-            </div >
+                    <div className="templating-button-wrapper documentDecorations-ex-wrapper">
+                        <div className="templating-button documentDecorations-ex"
+                            onClick={() => this.toggleTemplateActivity()}>T</div>
+                        {templateMenu}
+                    </div>
+                </div>
+            </div>
         )
     }
 }
\ No newline at end of file
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 8260f9414..d717fb778 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -2,21 +2,30 @@ import { observer } from "mobx-react";
 import { observable } from "mobx";
 import { action, computed } from "mobx";
 import React = require("react");
+import { StringLiteral } from "babel-types";
 
 export class Template {
-    constructor(layout: string) {
+    constructor(name: string, layout: string) {
+        this._name = name;
         this._layout = layout;
     }
 
-    private _layout: string = "";
+    private _name: string;
+    private _layout: string;
+
+    get Name(): string {
+        return this._name;
+    }
 
     get Layout(): string {
         return this._layout;
     }
+
 }
 
 export namespace Templates {
-    export const OuterCaption = new Template(`
+    export const OuterCaption = new Template("Outer caption",
+        `
     <div>
         <div style="margin:auto; height:calc(100%); width:100%;">
             {layout}
@@ -26,7 +35,8 @@ export namespace Templates {
         </div>
     </div>       
             `);
-    export const InnerCaption = new Template(`
+    export const InnerCaption = new Template("Inner caption",
+        `
     <div>
         <div style="margin:auto; height:calc(100% - 25px); width:100%;">
             {layout}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f8e5b0277..e5c6859f8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -94,7 +94,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     private _downX: number = 0;
     private _downY: number = 0;
     private _reactionDisposer: Opt<IReactionDisposer>;
-    private _templates: Array<Template> = [];
+    private _templates: Set<Template> = new Set<Template>();
+    private _basicLayout: string = this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>");
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || this.props.ContainingCollectionView.active(); }
     @computed get topMost(): boolean { return !this.props.ContainingCollectionView || this.props.ContainingCollectionView.collectionViewType == CollectionViewType.Docking; }
     @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); }
@@ -276,16 +277,28 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     @action
     addTemplate = (template: Template) => {
-        // TODO: should change to set
-        this._templates.push(template);
+        this._templates.add(template);
 
         // TODO: apply templates to original layout
+        this.props.Document.SetText(KeyStore.Layout, this._basicLayout);
         this._templates.forEach(temp => {
             let text = temp.Layout;
             let oldLayout = this.props.Document.GetText(KeyStore.Layout, "");
             let layout = text.replace("{layout}", oldLayout);
             this.props.Document.SetText(KeyStore.Layout, layout);
-        })
+        });
+    }
+
+    @action removeTemplate = (template: Template) => {
+        this._templates.delete(template);
+
+        this.props.Document.SetText(KeyStore.Layout, this._basicLayout);
+        this._templates.forEach(temp => {
+            let text = temp.Layout;
+            let oldLayout = this.props.Document.GetText(KeyStore.Layout, "");
+            let layout = text.replace("{layout}", oldLayout);
+            this.props.Document.SetText(KeyStore.Layout, layout);
+        });
     }
 
     @action
-- 
cgit v1.2.3-70-g09d2


From e81c43baadcaf31314c07505fa7cde70e709706d Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Mon, 15 Apr 2019 15:25:43 -0400
Subject: pulling from master

---
 src/client/views/DocumentDecorations.tsx | 58 ++++++--------------
 src/client/views/TemplateEditButton.tsx  | 92 ++++++++++++++++++++++++++++++++
 src/client/views/Templates.tsx           | 12 +++++
 src/client/views/nodes/DocumentView.tsx  | 12 +++--
 4 files changed, 130 insertions(+), 44 deletions(-)
 create mode 100644 src/client/views/TemplateEditButton.tsx

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 231d85050..e6b72a8d5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -9,11 +9,26 @@ import { props } from "bluebird";
 import { DragManager } from "../util/DragManager";
 import { LinkMenu } from "./nodes/LinkMenu";
 import { ListField } from "../../fields/ListField";
-import { Templates, Template } from "./Templates";
+import { TemplateEditButton } from "./TemplateEditButton";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
+// @observer
+// class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: Template) => void }> {
+//     render() {
+//         if (this.props.template) {
+//             return (
+//                 <li>
+//                     <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event, this.props.template)} />
+//                     {this.props.template.Name}
+//                 </li>
+//             )
+//         }
+//         return (null);
+//     }
+// }
+
 @observer
 export class DocumentDecorations extends React.Component {
     static Instance: DocumentDecorations
@@ -23,8 +38,6 @@ export class DocumentDecorations extends React.Component {
     private _resizeBorderWidth = 16;
     private _linkButton = React.createRef<HTMLDivElement>();
     @observable private _hidden = false;
-    @observable private _templatesActive: boolean = false;
-    @observable private _showBase: boolean = true;
 
     constructor(props: Readonly<{}>) {
         super(props)
@@ -195,26 +208,6 @@ export class DocumentDecorations extends React.Component {
     //     e.stopPropagation();
     // }
 
-    toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
-        let view = SelectionManager.SelectedDocuments()[0];
-        if (event.target.checked) {
-            view.addTemplate(template);
-        } else {
-            view.removeTemplate(template);
-        }
-    }
-
-    @action
-    toggleBase = (event: React.ChangeEvent<HTMLInputElement>): void => {
-        let view = SelectionManager.SelectedDocuments()[0];
-        view.toggleBase(event.target.checked);
-        this._showBase = !this._showBase;
-    }
-
-    @action
-    toggleTemplateActivity = (): void => {
-        this._templatesActive = !this._templatesActive;
-    }
 
     render() {
         var bounds = this.Bounds;
@@ -242,19 +235,6 @@ export class DocumentDecorations extends React.Component {
             </Flyout>);
         }
 
-        let templateMenu = !this._templatesActive ? (null) : (
-            <ul id="template-list">
-                <li><input type="checkbox" onChange={(event) => this.toggleBase(event)} checked={this._showBase} />Base layout</li>
-                {Array.from(Object.values(Templates)).map(template => {
-                    return (
-                        <li>
-                            <input type="checkbox" onChange={(event) => this.toggleTemplate(event, template)} />
-                            {template.Name}
-                        </li>
-                    )
-                })}
-            </ul>
-        )
         return (
             <div id="documentDecorations">
                 <div id="documentDecorations-container" style={{
@@ -281,11 +261,7 @@ export class DocumentDecorations extends React.Component {
                 }}>
                     <div title="View Links" className="linkFlyout documentDecorations-ex-wrapper" ref={this._linkButton}>{linkButton}</div>
 
-                    <div className="templating-button-wrapper documentDecorations-ex-wrapper">
-                        <div className="templating-button documentDecorations-ex"
-                            onClick={() => this.toggleTemplateActivity()}>T</div>
-                        {templateMenu}
-                    </div>
+                    <TemplateEditButton Document={SelectionManager.SelectedDocuments()[0]} />
                 </div>
             </div>
         )
diff --git a/src/client/views/TemplateEditButton.tsx b/src/client/views/TemplateEditButton.tsx
new file mode 100644
index 000000000..6542ac7ec
--- /dev/null
+++ b/src/client/views/TemplateEditButton.tsx
@@ -0,0 +1,92 @@
+import { observable, computed, action } from "mobx";
+import React = require("react");
+import { SelectionManager } from "../util/SelectionManager";
+import { observer } from "mobx-react";
+import './DocumentDecorations.scss'
+import { Templates, Template } from "./Templates";
+import { DocumentView } from "./nodes/DocumentView";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+@observer
+class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: Template) => void }> {
+    render() {
+        if (this.props.template) {
+            return (
+                <li>
+                    <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event, this.props.template)} />
+                    {this.props.template.Name}
+                </li>
+            )
+        }
+        return (null);
+    }
+}
+
+export interface TemplateButtonProps {
+    Document: DocumentView;
+}
+
+@observer
+export class TemplateEditButton extends React.Component<TemplateButtonProps> {
+
+    @observable private _templatesActive: boolean = false;
+    @observable private _showBase: boolean = true;
+
+    toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
+        let view = this.props.Document;
+        if (event.target.checked) {
+            view.addTemplate(template);
+        } else {
+            view.removeTemplate(template);
+        }
+
+        // const docs = view.props.ContainingCollectionView;
+        // const docs = view.props.Document.GetList<Document>(view.props.fieldKey, []);
+
+    }
+
+    @action
+    toggleBase = (event: React.ChangeEvent<HTMLInputElement>): void => {
+        let view = this.props.Document;
+        view.toggleBase(event.target.checked);
+        this._showBase = !this._showBase;
+    }
+
+    @action
+    toggleTemplateActivity = (): void => {
+        this._templatesActive = !this._templatesActive;
+    }
+
+    render() {
+        let templateMenu = !this._templatesActive ? (null) : (
+            <ul id="template-list">
+                <li><input type="checkbox" onChange={(event) => this.toggleBase(event)} defaultChecked={true} />Base layout</li>
+                {console.log("mm")}
+                {Array.from(Object.values(Templates)).map(template => {
+                    let view = this.props.Document
+                    let checked = view.hasTemplate(template);
+                    return (
+                        <TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />
+                    )
+
+                    // return (
+                    //     <li key={template.Name}>
+                    //         {console.log(template.Name, checked)}
+                    //         <input type="checkbox" onChange={(event) => this.toggleTemplate(event, template)} defaultChecked={checked} />
+                    //         {template.Name}
+                    //     </li>
+                    // )
+                })}
+            </ul>
+        )
+        return (
+            <div className="templating-button-wrapper documentDecorations-ex-wrapper">
+                <div className="templating-button documentDecorations-ex"
+                    onClick={() => this.toggleTemplateActivity()}>T</div>
+                {templateMenu}
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index d717fb778..a6ba2243d 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -46,5 +46,17 @@ export namespace Templates {
         </div>
     </div>       
             `);
+
+    export const Title = new Template("Title",
+        `
+    <div>
+        <div style="margin:auto; height:calc(100% - 50px); width:100%;">
+            {layout}
+        </div>
+        <div style="height:50px; width:100%; position:absolute">
+            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"TitleKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+        </div>
+    </div>       
+            `);
 }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2ede6d28e..806387e20 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, reaction, runInAction, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Document } from "../../../fields/Document";
 import { Field, Opt, FieldWaiting } from "../../../fields/Field";
@@ -94,7 +94,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     private _downX: number = 0;
     private _downY: number = 0;
     private _reactionDisposer: Opt<IReactionDisposer>;
-    private _templates: Set<Template> = new Set<Template>();
+    @observable private _templates: Set<Template> = new Set<Template>();
     private _useBase: boolean = true;
     private _baseLayout: string = this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>");
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || !this.props.ContainingCollectionView || this.props.ContainingCollectionView.active(); }
@@ -301,11 +301,17 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         this.updateLayout();
     }
 
-    @action removeTemplate = (template: Template) => {
+    @action
+    removeTemplate = (template: Template) => {
         this._templates.delete(template);
         this.updateLayout();
     }
 
+    @action
+    hasTemplate = (template: Template) => {
+        return this._templates.has(template);
+    }
+
     @action
     onContextMenu = (e: React.MouseEvent): void => {
         e.stopPropagation();
-- 
cgit v1.2.3-70-g09d2


From c6ecd219f61811c3af9776ad8c08c2232db20dfd Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Mon, 15 Apr 2019 20:04:01 -0400
Subject: template menu works when switching between diff docs

---
 src/client/documents/Documents.ts           |  1 +
 src/client/views/DocumentDecorations.scss   | 63 +++++++++++---------
 src/client/views/DocumentDecorations.tsx    | 12 +++-
 src/client/views/TemplateEditButton.tsx     | 92 -----------------------------
 src/client/views/TemplateMenu.tsx           | 88 +++++++++++++++++++++++++++
 src/client/views/Templates.tsx              |  2 +-
 src/client/views/nodes/DocumentView.tsx     |  8 +--
 src/client/views/nodes/FormattedTextBox.tsx |  4 +-
 8 files changed, 140 insertions(+), 130 deletions(-)
 delete mode 100644 src/client/views/TemplateEditButton.tsx
 create mode 100644 src/client/views/TemplateMenu.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4febfa7eb..8db1a1c6d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -130,6 +130,7 @@ export namespace Documents {
             { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations, KeyStore.Caption] });
         imageProto.SetText(KeyStore.BackgroundLayout, ImageBox.LayoutString());
         imageProto.SetNumber(KeyStore.CurPage, 0);
+        imageProto.SetData(KeyStore.LayoutFields, [KeyStore.Title], ListField);
         return imageProto;
     }
 
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index e926c2be6..14f3359ca 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -104,15 +104,6 @@
     margin-left: 25px;
 }
 
-.documentDecorations-extra {
-    display: flex;
-    position: absolute;
-}
-
-.documentDecorations-ex-wrapper {
-    margin-right: 10px;
-}
-
 .linkButton-linker {
     position: absolute;
     bottom: 0px;
@@ -164,25 +155,43 @@
     }
 }
 
-.templating-button-wrapper {
-    position: relative;
-}
-
-#template-list {
+.templating-menu {
     position: absolute;
-    top: 0;
-    left: 30px;
-    width: 150px;
-    line-height: 25px;
-    max-height: 175px;
-    font-family: $sans-serif;
-    font-size: 12px;
-    background-color: $light-color-secondary;
-    padding: 2px 12px;
-
-    input {
-        margin-right: 10px;
-    }
+    bottom: 0;
+    left: 50px;
+    pointer-events: auto;
 
+    .templating-button {
+        width: 20px;
+        height: 20px;
+        border-radius: 50%;
+        opacity: 0.9;
+        background-color: $dark-color;
+        color: $light-color;
+        text-align: center;
+        cursor: pointer;
 
+        &:hover {
+            background: $main-accent;
+            transform: scale(1.05);
+        }
+    }
+
+    #template-list {
+        position: absolute;
+        top: 0;
+        left: 30px;
+        width: 150px;
+        line-height: 25px;
+        max-height: 175px;
+        font-family: $sans-serif;
+        font-size: 12px;
+        background-color: $light-color-secondary;
+        padding: 2px 12px;
+        list-style: none;
+
+        input {
+            margin-right: 10px;
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 51c085038..1e936a881 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -14,8 +14,9 @@ import './DocumentDecorations.scss';
 import { MainOverlayTextBox } from "./MainOverlayTextBox";
 import { DocumentView } from "./nodes/DocumentView";
 import { LinkMenu } from "./nodes/LinkMenu";
-import { TemplateEditButton } from "./TemplateEditButton";
+import { TemplateMenu } from "./TemplateMenu";
 import React = require("react");
+import { Template, Templates } from "./Templates";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -404,6 +405,13 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                 <div className={"linkButton-" + (selFirst.props.Document.GetData(KeyStore.LinkedToDocs, ListField, []).length ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >{linkCount}</div>
             </Flyout>);
         }
+
+        let templates: Map<Template, boolean> = new Map();
+        let doc = SelectionManager.SelectedDocuments()[0];
+        Array.from(Object.values(Templates)).map(template => {
+            templates.set(template, doc.hasTemplate(template));
+        });
+
         return (<div className="documentDecorations">
             <div className="documentDecorations-background" style={{
                 width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
@@ -436,7 +444,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
                 <div title="View Links" className="linkFlyout" ref={this._linkButton}> {linkButton}  </div>
                 <div className="linkButton-linker" ref={this._linkerButton} onPointerDown={this.onLinkerButtonDown}>∞</div>
-                <TemplateEditButton Document={SelectionManager.SelectedDocuments()[0]} />
+                <TemplateMenu doc={doc} templates={templates} />
             </div >
         </div>
         );
diff --git a/src/client/views/TemplateEditButton.tsx b/src/client/views/TemplateEditButton.tsx
deleted file mode 100644
index 6542ac7ec..000000000
--- a/src/client/views/TemplateEditButton.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import { observable, computed, action } from "mobx";
-import React = require("react");
-import { SelectionManager } from "../util/SelectionManager";
-import { observer } from "mobx-react";
-import './DocumentDecorations.scss'
-import { Templates, Template } from "./Templates";
-import { DocumentView } from "./nodes/DocumentView";
-const higflyout = require("@hig/flyout");
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
-
-@observer
-class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: Template) => void }> {
-    render() {
-        if (this.props.template) {
-            return (
-                <li>
-                    <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event, this.props.template)} />
-                    {this.props.template.Name}
-                </li>
-            )
-        }
-        return (null);
-    }
-}
-
-export interface TemplateButtonProps {
-    Document: DocumentView;
-}
-
-@observer
-export class TemplateEditButton extends React.Component<TemplateButtonProps> {
-
-    @observable private _templatesActive: boolean = false;
-    @observable private _showBase: boolean = true;
-
-    toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
-        let view = this.props.Document;
-        if (event.target.checked) {
-            view.addTemplate(template);
-        } else {
-            view.removeTemplate(template);
-        }
-
-        // const docs = view.props.ContainingCollectionView;
-        // const docs = view.props.Document.GetList<Document>(view.props.fieldKey, []);
-
-    }
-
-    @action
-    toggleBase = (event: React.ChangeEvent<HTMLInputElement>): void => {
-        let view = this.props.Document;
-        view.toggleBase(event.target.checked);
-        this._showBase = !this._showBase;
-    }
-
-    @action
-    toggleTemplateActivity = (): void => {
-        this._templatesActive = !this._templatesActive;
-    }
-
-    render() {
-        let templateMenu = !this._templatesActive ? (null) : (
-            <ul id="template-list">
-                <li><input type="checkbox" onChange={(event) => this.toggleBase(event)} defaultChecked={true} />Base layout</li>
-                {console.log("mm")}
-                {Array.from(Object.values(Templates)).map(template => {
-                    let view = this.props.Document
-                    let checked = view.hasTemplate(template);
-                    return (
-                        <TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />
-                    )
-
-                    // return (
-                    //     <li key={template.Name}>
-                    //         {console.log(template.Name, checked)}
-                    //         <input type="checkbox" onChange={(event) => this.toggleTemplate(event, template)} defaultChecked={checked} />
-                    //         {template.Name}
-                    //     </li>
-                    // )
-                })}
-            </ul>
-        )
-        return (
-            <div className="templating-button-wrapper documentDecorations-ex-wrapper">
-                <div className="templating-button documentDecorations-ex"
-                    onClick={() => this.toggleTemplateActivity()}>T</div>
-                {templateMenu}
-            </div>
-        )
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
new file mode 100644
index 000000000..7f670aec0
--- /dev/null
+++ b/src/client/views/TemplateMenu.tsx
@@ -0,0 +1,88 @@
+import { observable, computed, action } from "mobx";
+import React = require("react");
+import { SelectionManager } from "../util/SelectionManager";
+import { observer } from "mobx-react";
+import './DocumentDecorations.scss';
+import { Templates, Template } from "./Templates";
+import { DocumentView } from "./nodes/DocumentView";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+@observer
+class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: Template) => void }> {
+    render() {
+        if (this.props.template) {
+            return (
+                <li>
+                    <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event, this.props.template)} />
+                    {this.props.template.Name}
+                </li>
+            );
+        } else {
+            return (null);
+        }
+    }
+}
+
+export interface TemplateMenuProps {
+    doc: DocumentView;
+    templates: Map<Template, boolean>;
+}
+
+@observer
+export class TemplateMenu extends React.Component<TemplateMenuProps> {
+
+    @observable private _hidden: boolean = true;
+    @observable private _showBase: boolean = true;
+    @observable private _templates: Map<Template, boolean> = this.props.templates;
+
+
+    @action
+    toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
+        if (event.target.checked) {
+            this.props.doc.addTemplate(template);
+            this._templates.set(template, true);
+        } else {
+            this.props.doc.removeTemplate(template);
+            this._templates.set(template, false);
+        }
+
+        // const docs = view.props.ContainingCollectionView;
+        // const docs = view.props.Document.GetList<Document>(view.props.fieldKey, []);
+
+    }
+
+    @action
+    componentWillReceiveProps(nextProps: TemplateMenuProps) {
+        this._templates = nextProps.templates;
+    }
+
+    @action
+    toggleBase = (event: React.ChangeEvent<HTMLInputElement>): void => {
+        this.props.doc.toggleBase(event.target.checked);
+        this._showBase = !this._showBase;
+    }
+
+    @action
+    toggleTemplateActivity = (): void => {
+        this._hidden = !this._hidden;
+    }
+
+    render() {
+        let templateMenu: Array<JSX.Element> = [];
+        this._templates.forEach((checked, template) => {
+            templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />);
+        });
+
+        return (
+            <div className="templating-menu" >
+                <div className="templating-button" onClick={() => this.toggleTemplateActivity()}>T</div>
+                <ul id="template-list" style={{ display: this._hidden ? "none" : "block" }}>
+                    <li><input type="checkbox" onChange={(event) => this.toggleBase(event)} defaultChecked={true} />Base layout</li>
+                    {templateMenu}
+                </ul>
+            </div>
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index a6ba2243d..b82b383bb 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -54,7 +54,7 @@ export namespace Templates {
             {layout}
         </div>
         <div style="height:50px; width:100%; position:absolute">
-            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"TitleKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+            {Title}
         </div>
     </div>       
             `);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3c61810e2..ef1420027 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,8 +1,4 @@
-<<<<<<< HEAD
-import { action, computed, IReactionDisposer, reaction, runInAction, observable } from "mobx";
-=======
-import { action, computed, runInAction } from "mobx";
->>>>>>> 6c0b421db6aa3204bbc6e42139d240f503000b5d
+import { action, computed, runInAction, observable } from "mobx";
 import { observer } from "mobx-react";
 import { BooleanField } from "../../../fields/BooleanField";
 import { Document } from "../../../fields/Document";
@@ -25,7 +21,7 @@ import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
 import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
-import { Template } from "./../Templates"
+import { Template } from "./../Templates";
 import "./DocumentView.scss";
 import React = require("react");
 
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index ad1ed5df0..9f71609d5 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -105,7 +105,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                 }
             );
         } else {
-            this._proxyReactionDisposer = reaction(() => this.props.isSelected(),
+            this._proxyReactionDisposer = reaction(() => { }/*this.props.isSelected()*/,
                 () => this.props.isSelected() && MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform()));
         }
 
@@ -241,7 +241,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     render() {
         return (
             <div
-                style={{ overflowY: this.props.isSelected() || this.props.isOverlay ? "scroll" : "hidden" }}
+                style={{ overflowY: /*this.props.isSelected() ||*/ this.props.isOverlay ? "scroll" : "hidden" }}
                 className={`formattedTextBox-cont`}
                 onKeyDown={this.onKeyPress}
                 onKeyPress={this.onKeyPress}
-- 
cgit v1.2.3-70-g09d2


From fbf1662304e0b3f744827710d728602af4493f25 Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Thu, 18 Apr 2019 22:13:08 -0400
Subject: changed menu to work like radio buttons:

---
 src/client/views/DocumentDecorations.tsx |  2 +-
 src/client/views/TemplateMenu.tsx        | 30 ++++++++++++-----------
 src/client/views/Templates.tsx           |  4 ++--
 src/client/views/nodes/DocumentView.tsx  | 41 +++++++++++++-------------------
 4 files changed, 35 insertions(+), 42 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 1e936a881..3dcdc596d 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -409,7 +409,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         let templates: Map<Template, boolean> = new Map();
         let doc = SelectionManager.SelectedDocuments()[0];
         Array.from(Object.values(Templates)).map(template => {
-            templates.set(template, doc.hasTemplate(template));
+            templates.set(template, doc.Template === template);
         });
 
         return (<div className="documentDecorations">
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 7f670aec0..0c8ae7055 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,9 +1,8 @@
 import { observable, computed, action } from "mobx";
 import React = require("react");
-import { SelectionManager } from "../util/SelectionManager";
 import { observer } from "mobx-react";
 import './DocumentDecorations.scss';
-import { Templates, Template } from "./Templates";
+import { Template } from "./Templates";
 import { DocumentView } from "./nodes/DocumentView";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
@@ -34,19 +33,19 @@ export interface TemplateMenuProps {
 export class TemplateMenu extends React.Component<TemplateMenuProps> {
 
     @observable private _hidden: boolean = true;
-    @observable private _showBase: boolean = true;
+    @observable private _useBase: boolean = true;
     @observable private _templates: Map<Template, boolean> = this.props.templates;
 
 
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
-        if (event.target.checked) {
-            this.props.doc.addTemplate(template);
-            this._templates.set(template, true);
-        } else {
-            this.props.doc.removeTemplate(template);
-            this._templates.set(template, false);
-        }
+        this._useBase = false;
+        this.props.doc.toggleBase(false);
+        this.props.doc.changeTemplate(template);
+        this._templates.forEach((checked, temp) => {
+            this._templates.set(temp, false);
+        });
+        this._templates.set(template, true);
 
         // const docs = view.props.ContainingCollectionView;
         // const docs = view.props.Document.GetList<Document>(view.props.fieldKey, []);
@@ -59,9 +58,12 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     }
 
     @action
-    toggleBase = (event: React.ChangeEvent<HTMLInputElement>): void => {
-        this.props.doc.toggleBase(event.target.checked);
-        this._showBase = !this._showBase;
+    toggleBase = (event: React.MouseEvent): void => {
+        this._useBase = true;
+        this.props.doc.toggleBase(true);
+        this._templates.forEach((checked, temp) => {
+            this._templates.set(temp, false);
+        });
     }
 
     @action
@@ -79,7 +81,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
             <div className="templating-menu" >
                 <div className="templating-button" onClick={() => this.toggleTemplateActivity()}>T</div>
                 <ul id="template-list" style={{ display: this._hidden ? "none" : "block" }}>
-                    <li><input type="checkbox" onChange={(event) => this.toggleBase(event)} defaultChecked={true} />Base layout</li>
+                    <li><input type="checkbox" onClick={(event) => this.toggleBase(event)} checked={this._useBase} />Base layout</li>
                     {templateMenu}
                 </ul>
             </div>
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index b82b383bb..c22b22286 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -31,7 +31,7 @@ export namespace Templates {
             {layout}
         </div>
         <div style="height:(100% + 25px); width:100%; position:absolute">
-            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
         </div>
     </div>       
             `);
@@ -42,7 +42,7 @@ export namespace Templates {
             {layout}
         </div>
         <div style="height:25px; width:100%; position:absolute">
-            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
         </div>
     </div>       
             `);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ef1420027..12332348b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -92,9 +92,8 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
     private _downX: number = 0;
     private _downY: number = 0;
-    @observable private _templates: Set<Template> = new Set<Template>();
-    private _useBase: boolean = true;
-    private _baseLayout: string = this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>");
+    private _base: string = this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>");
+    @observable private _template: Template = new Template("", "");
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
     @computed get topMost(): boolean { return this.props.isTopMost; }
     @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); }
@@ -304,39 +303,31 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     updateLayout = (): void => {
-        let base = this._useBase ? this._baseLayout : "<div style='margin: auto; width: 100%; height: 100%; border: 1px solid black'></div>";
-        // this.props.Document.SetText(KeyStore.Layout, base);
-        this._templates.forEach(temp => {
-            let text = temp.Layout;
-            base = text.replace("{layout}", base);
-            // let oldLayout = this.props.Document.GetText(KeyStore.Layout, "");
-            // let layout = text.replace("{layout}", oldLayout);
-            // this.props.Document.SetText(KeyStore.Layout, layout);
-        });
-        this.props.Document.SetText(KeyStore.Layout, base);
+        if (this._template.Name === "") {
+            this.props.Document.SetText(KeyStore.Layout, this._base);
+        } else {
+            let temp = this._template.Layout;
+            let layout = temp.replace("{layout}", this._base);
+            this.props.Document.SetText(KeyStore.Layout, layout);
+        }
     }
 
     @action
     toggleBase = (useBase: boolean) => {
-        this._useBase = useBase;
-        this.updateLayout();
-    }
-
-    @action
-    addTemplate = (template: Template) => {
-        this._templates.add(template);
+        if (useBase) {
+            this._template = new Template("", "");
+        }
         this.updateLayout();
     }
 
     @action
-    removeTemplate = (template: Template) => {
-        this._templates.delete(template);
+    changeTemplate = (template: Template) => {
+        this._template = template;
         this.updateLayout();
     }
 
-    @action
-    hasTemplate = (template: Template) => {
-        return this._templates.has(template);
+    get Template() {
+        return this._template;
     }
 
     @action
-- 
cgit v1.2.3-70-g09d2


From 7ceac5f7f4cc8172bde90c2d495da3779901ef84 Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Fri, 19 Apr 2019 18:31:38 -0400
Subject: templating saves

---
 src/client/documents/Documents.ts        |  8 ++++-
 src/client/views/DocumentDecorations.tsx | 20 +++---------
 src/client/views/TemplateMenu.tsx        | 13 --------
 src/client/views/Templates.tsx           | 55 +++++++++++++++-----------------
 src/client/views/nodes/DocumentView.tsx  | 37 ++++++++-------------
 src/fields/KeyStore.ts                   |  4 ++-
 src/fields/TemplateField.ts              | 35 ++++++++++++++++++++
 src/server/Message.ts                    |  2 +-
 src/server/ServerUtil.ts                 |  3 ++
 9 files changed, 92 insertions(+), 85 deletions(-)
 create mode 100644 src/fields/TemplateField.ts

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8db1a1c6d..eba53273f 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -32,6 +32,8 @@ import { action } from "mobx";
 import { ColumnAttributeModel } from "../northstar/core/attribute/AttributeModel";
 import { AttributeTransformationModel } from "../northstar/core/attribute/AttributeTransformationModel";
 import { AggregateFunction } from "../northstar/model/idea/idea";
+import { Template } from "../views/Templates";
+import { TemplateField } from "../../fields/TemplateField";
 
 export interface DocumentOptions {
     x?: number;
@@ -46,7 +48,9 @@ export interface DocumentOptions {
     pany?: number;
     page?: number;
     scale?: number;
+    baseLayout?: string;
     layout?: string;
+    template?: Template;
     layoutKeys?: Key[];
     viewType?: number;
     backgroundColor?: string;
@@ -95,7 +99,9 @@ export namespace Documents {
         if (options.viewType !== undefined) { doc.SetNumber(KeyStore.ViewType, options.viewType); }
         if (options.backgroundColor !== undefined) { doc.SetText(KeyStore.BackgroundColor, options.backgroundColor); }
         if (options.ink !== undefined) { doc.Set(KeyStore.Ink, new InkField(options.ink)); }
+        if (options.baseLayout !== undefined) { doc.SetText(KeyStore.BaseLayout, options.baseLayout); }
         if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); }
+        if (options.template !== undefined) { doc.Set(KeyStore.Template, new TemplateField(options.template)); }
         if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); }
         if (options.copyDraggedItems !== undefined) { doc.SetBoolean(KeyStore.CopyDraggedItems, options.copyDraggedItems); }
         return doc;
@@ -112,7 +118,7 @@ export namespace Documents {
     }
 
     function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Document {
-        return assignOptions(new Document(protoId), { ...options, title: title, layout: layout });
+        return assignOptions(new Document(protoId), { ...options, title: title, layout: layout , baseLayout: layout});
     }
     function SetInstanceOptions<T, U extends Field & { Data: T }>(doc: Document, options: DocumentOptions, value: [T, { new(): U }] | Document, id?: string) {
         var deleg = doc.MakeDelegate(id);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3dcdc596d..2a40d7347 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -17,25 +17,11 @@ import { LinkMenu } from "./nodes/LinkMenu";
 import { TemplateMenu } from "./TemplateMenu";
 import React = require("react");
 import { Template, Templates } from "./Templates";
+import { TemplateField } from "../../fields/TemplateField";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
-// @observer
-// class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: Template) => void }> {
-//     render() {
-//         if (this.props.template) {
-//             return (
-//                 <li>
-//                     <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event, this.props.template)} />
-//                     {this.props.template.Name}
-//                 </li>
-//             )
-//         }
-//         return (null);
-//     }
-// }
-
 @observer
 export class DocumentDecorations extends React.Component<{}, { value: string }> {
     static Instance: DocumentDecorations;
@@ -409,8 +395,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         let templates: Map<Template, boolean> = new Map();
         let doc = SelectionManager.SelectedDocuments()[0];
         Array.from(Object.values(Templates)).map(template => {
-            templates.set(template, doc.Template === template);
+            templates.set(template, doc.template.Name === template.Name);
         });
+        // let docSrc = doc.props.Document.GetT(KeyStore.Prototype, TemplateField);
+        // console.log(docSrc);
 
         return (<div className="documentDecorations">
             <div className="documentDecorations-background" style={{
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 0c8ae7055..1e83463c8 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -33,14 +33,11 @@ export interface TemplateMenuProps {
 export class TemplateMenu extends React.Component<TemplateMenuProps> {
 
     @observable private _hidden: boolean = true;
-    @observable private _useBase: boolean = true;
     @observable private _templates: Map<Template, boolean> = this.props.templates;
 
 
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
-        this._useBase = false;
-        this.props.doc.toggleBase(false);
         this.props.doc.changeTemplate(template);
         this._templates.forEach((checked, temp) => {
             this._templates.set(temp, false);
@@ -57,15 +54,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         this._templates = nextProps.templates;
     }
 
-    @action
-    toggleBase = (event: React.MouseEvent): void => {
-        this._useBase = true;
-        this.props.doc.toggleBase(true);
-        this._templates.forEach((checked, temp) => {
-            this._templates.set(temp, false);
-        });
-    }
-
     @action
     toggleTemplateActivity = (): void => {
         this._hidden = !this._hidden;
@@ -81,7 +69,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
             <div className="templating-menu" >
                 <div className="templating-button" onClick={() => this.toggleTemplateActivity()}>T</div>
                 <ul id="template-list" style={{ display: this._hidden ? "none" : "block" }}>
-                    <li><input type="checkbox" onClick={(event) => this.toggleBase(event)} checked={this._useBase} />Base layout</li>
                     {templateMenu}
                 </ul>
             </div>
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index c22b22286..3afdc711c 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -24,39 +24,34 @@ export class Template {
 }
 
 export namespace Templates {
+    export const BasicLayout = new Template("Basic layout", "{layout}");
+
     export const OuterCaption = new Template("Outer caption",
-        `
-    <div>
-        <div style="margin:auto; height:calc(100%); width:100%;">
-            {layout}
-        </div>
-        <div style="height:(100% + 25px); width:100%; position:absolute">
-            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
-        </div>
-    </div>       
-            `);
+        `<div><div style="margin:auto; height:calc(100%); width:100%;">{layout}</div><div style="height:(100% + 50px); width:100%; position:absolute"><FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/></div></div>`
+    );
+
     export const InnerCaption = new Template("Inner caption",
-        `
-    <div>
-        <div style="margin:auto; height:calc(100% - 25px); width:100%;">
-            {layout}
-        </div>
-        <div style="height:25px; width:100%; position:absolute">
-            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
-        </div>
-    </div>       
-            `);
+        `<div>
+            <div style="margin:auto; height:calc(100% - 50px); width:100%;">
+                {layout}
+            </div>
+            <div style="height:50px; width:100%; position:absolute">
+                <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+            </div>
+        </div>`
+    );
 
     export const Title = new Template("Title",
-        `
-    <div>
-        <div style="margin:auto; height:calc(100% - 50px); width:100%;">
-            {layout}
-        </div>
-        <div style="height:50px; width:100%; position:absolute">
-            {Title}
-        </div>
-    </div>       
-            `);
+        `<div>
+            <div style="margin:auto; height:calc(100% - 50px); width:100%;">
+                {layout}
+            </div>
+            <div style="height:50px; width:100%; position:absolute; background-color: rgba(0, 0, 0, .6); color: white; padding:12px">
+                {Title}
+            </div>
+        </div>`
+    );
+
+
 }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 12332348b..440269e36 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -21,9 +21,10 @@ import { CollectionVideoView } from "../collections/CollectionVideoView";
 import { CollectionView } from "../collections/CollectionView";
 import { ContextMenu } from "../ContextMenu";
 import { DocumentContentsView } from "./DocumentContentsView";
-import { Template } from "./../Templates";
+import { Template, Templates } from "./../Templates";
 import "./DocumentView.scss";
 import React = require("react");
+import { TemplateField } from "../../../fields/TemplateField";
 
 
 export interface DocumentViewProps {
@@ -92,13 +93,19 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
     private _downX: number = 0;
     private _downY: number = 0;
-    private _base: string = this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>");
-    @observable private _template: Template = new Template("", "");
+    @computed get base(): string { return this.props.Document.GetText(KeyStore.BaseLayout, "<p>Error loading base layout data</p>"); }
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
     @computed get topMost(): boolean { return this.props.isTopMost; }
     @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); }
     @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); }
     @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); }
+    @computed get template(): Template {
+        let field = this.props.Document.GetT(KeyStore.Template, TemplateField);
+        return !field || field === FieldWaiting ? Templates.BasicLayout : field.Data;
+    }
+    set template(template: Template) {
+        this.props.Document.SetData(KeyStore.Template, template, TemplateField);
+    }
     screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = e.clientX;
@@ -303,33 +310,17 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     updateLayout = (): void => {
-        if (this._template.Name === "") {
-            this.props.Document.SetText(KeyStore.Layout, this._base);
-        } else {
-            let temp = this._template.Layout;
-            let layout = temp.replace("{layout}", this._base);
-            this.props.Document.SetText(KeyStore.Layout, layout);
-        }
-    }
-
-    @action
-    toggleBase = (useBase: boolean) => {
-        if (useBase) {
-            this._template = new Template("", "");
-        }
-        this.updateLayout();
+        let temp = this.template.Layout;
+        let layout = temp.replace("{layout}", this.base);
+        this.props.Document.SetText(KeyStore.Layout, layout);
     }
 
     @action
     changeTemplate = (template: Template) => {
-        this._template = template;
+        this.template = template;
         this.updateLayout();
     }
 
-    get Template() {
-        return this._template;
-    }
-
     @action
     onContextMenu = (e: React.MouseEvent): void => {
         e.stopPropagation();
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 16a909eb8..1602c3776 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -19,7 +19,9 @@ export namespace KeyStore {
     export const Data = new Key("Data");
     export const Annotations = new Key("Annotations");
     export const ViewType = new Key("ViewType");
+    export const BaseLayout = new Key("BaseLayout");
     export const Layout = new Key("Layout");
+    export const Template = new Key("Template");
     export const BackgroundColor = new Key("BackgroundColor");
     export const BackgroundLayout = new Key("BackgroundLayout");
     export const OverlayLayout = new Key("OverlayLayout");
@@ -49,7 +51,7 @@ export namespace KeyStore {
     export const CopyDraggedItems = new Key("CopyDraggedItems");
 
     export const KeyList: Key[] = [Prototype, X, Y, Page, Title, Author, PanX, PanY, Scale, NativeWidth, NativeHeight,
-        Width, Height, ZIndex, Zoom, Data, Annotations, ViewType, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys,
+        Width, Height, ZIndex, Zoom, Data, Annotations, ViewType, BaseLayout, Layout, BackgroundColor, BackgroundLayout, OverlayLayout, LayoutKeys,
         LayoutFields, ColumnsKey, SchemaSplitPercentage, Caption, ActiveWorkspace, DocumentText, BrushingDocs, LinkedToDocs, LinkedFromDocs,
         LinkDescription, LinkTags, Thumbnail, ThumbnailPage, CurPage, AnnotationOn, NumPages, Ink, Cursors, OptionalRightCollection,
         Archives, Workspaces, Minimized, CopyDraggedItems
diff --git a/src/fields/TemplateField.ts b/src/fields/TemplateField.ts
new file mode 100644
index 000000000..bc1e67d7b
--- /dev/null
+++ b/src/fields/TemplateField.ts
@@ -0,0 +1,35 @@
+import { BasicField } from "./BasicField";
+import { Types } from "../server/Message";
+import { FieldId } from "./Field";
+import { Template, Templates } from "../client/views/Templates";
+
+
+export class TemplateField extends BasicField<Template> {
+    constructor(data: Template = Templates.BasicLayout, id?: FieldId, save: boolean = true) {
+        super(data, save, id);
+    }
+
+    ToScriptString(): string {
+        return `new TemplateField("${this.Data}")`;
+    }
+
+    Copy() {
+        return new TemplateField(this.Data);
+    }
+
+    ToJson() {
+        return {
+            type: Types.Template,
+            data: this.Data,
+            id: this.Id,
+        };
+    }
+
+    UpdateFromServer(data: any) {
+        this.data = new Template(data.Name, data.layout);
+    }
+
+    static FromJson(id: string, data: any): TemplateField {
+        return new TemplateField(new Template(data._name, data._layout), id, false);
+    }
+}
\ No newline at end of file
diff --git a/src/server/Message.ts b/src/server/Message.ts
index bbe4ffcad..4a8812956 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -15,7 +15,7 @@ export class Message<T> {
 
 export enum Types {
     Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference,
-    Html, Video, Audio, Ink, PDF, Tuple, HistogramOp, Boolean, Script,
+    Html, Video, Audio, Ink, PDF, Tuple, HistogramOp, Boolean, Script, Template
 }
 
 export interface Transferable {
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index 818230c1a..a2256bca7 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -18,6 +18,8 @@ import { NumberField } from "./../fields/NumberField";
 import { RichTextField } from "./../fields/RichTextField";
 import { TextField } from "./../fields/TextField";
 import { Transferable, Types } from "./Message";
+import { Template } from "../client/views/Templates";
+import { TemplateField } from "../fields/TemplateField";
 
 export class ServerUtils {
     public static prepend(extension: string): string {
@@ -50,6 +52,7 @@ export class ServerUtils {
             case Types.Video: return new VideoField(new URL(json.data), json.id, false);
             case Types.Tuple: return new TupleField(json.data, json.id, false);
             case Types.Ink: return InkField.FromJson(json.id, json.data);
+            case Types.Template: return TemplateField.FromJson(json.id, json.data);
             case Types.Document: return Document.FromJson(json.data, json.id, false);
             default:
                 throw Error(
-- 
cgit v1.2.3-70-g09d2


From e794b4b38e8ab2f4e7a79f223f9488cc845c724f Mon Sep 17 00:00:00 2001
From: Fawn <fangrui_tong@brown.edu>
Date: Mon, 22 Apr 2019 00:05:35 -0400
Subject: mutiple templates can be used

---
 src/client/documents/Documents.ts        |  6 ++--
 src/client/views/DocumentDecorations.tsx | 14 +++++---
 src/client/views/TemplateMenu.tsx        | 18 +++++-----
 src/client/views/Templates.tsx           | 61 ++++++++++++++++++--------------
 src/client/views/nodes/DocumentView.tsx  | 43 ++++++++++++++++------
 src/fields/KeyStore.ts                   |  2 +-
 src/fields/TemplateField.ts              | 22 ++++++++----
 src/server/Message.ts                    |  2 +-
 src/server/ServerUtil.ts                 |  3 +-
 9 files changed, 106 insertions(+), 65 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index eba53273f..c93e65edb 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -50,7 +50,7 @@ export interface DocumentOptions {
     scale?: number;
     baseLayout?: string;
     layout?: string;
-    template?: Template;
+    templates?: Array<Template>;
     layoutKeys?: Key[];
     viewType?: number;
     backgroundColor?: string;
@@ -101,7 +101,7 @@ export namespace Documents {
         if (options.ink !== undefined) { doc.Set(KeyStore.Ink, new InkField(options.ink)); }
         if (options.baseLayout !== undefined) { doc.SetText(KeyStore.BaseLayout, options.baseLayout); }
         if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); }
-        if (options.template !== undefined) { doc.Set(KeyStore.Template, new TemplateField(options.template)); }
+        if (options.templates !== undefined) { doc.Set(KeyStore.Templates, new TemplateField(options.templates)); }
         if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); }
         if (options.copyDraggedItems !== undefined) { doc.SetBoolean(KeyStore.CopyDraggedItems, options.copyDraggedItems); }
         return doc;
@@ -118,7 +118,7 @@ export namespace Documents {
     }
 
     function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Document {
-        return assignOptions(new Document(protoId), { ...options, title: title, layout: layout , baseLayout: layout});
+        return assignOptions(new Document(protoId), { ...options, title: title, layout: layout, baseLayout: layout });
     }
     function SetInstanceOptions<T, U extends Field & { Data: T }>(doc: Document, options: DocumentOptions, value: [T, { new(): U }] | Document, id?: string) {
         var deleg = doc.MakeDelegate(id);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2a40d7347..2b22d9e19 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -17,7 +17,6 @@ import { LinkMenu } from "./nodes/LinkMenu";
 import { TemplateMenu } from "./TemplateMenu";
 import React = require("react");
 import { Template, Templates } from "./Templates";
-import { TemplateField } from "../../fields/TemplateField";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -394,11 +393,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
 
         let templates: Map<Template, boolean> = new Map();
         let doc = SelectionManager.SelectedDocuments()[0];
-        Array.from(Object.values(Templates)).map(template => {
-            templates.set(template, doc.template.Name === template.Name);
+        Array.from(Object.values(Templates.TemplateList)).map(template => {
+            let docTemps = doc.templates;
+            let checked = false;
+            docTemps.forEach(temp => {
+                if (template.Name === temp.Name) {
+                    checked = true;
+                }
+            });
+            templates.set(template, checked);
         });
-        // let docSrc = doc.props.Document.GetT(KeyStore.Prototype, TemplateField);
-        // console.log(docSrc);
 
         return (<div className="documentDecorations">
             <div className="documentDecorations-background" style={{
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 1e83463c8..8eb2fc6c6 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -38,15 +38,13 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
 
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
-        this.props.doc.changeTemplate(template);
-        this._templates.forEach((checked, temp) => {
-            this._templates.set(temp, false);
-        });
-        this._templates.set(template, true);
-
-        // const docs = view.props.ContainingCollectionView;
-        // const docs = view.props.Document.GetList<Document>(view.props.fieldKey, []);
-
+        if (event.target.checked) {
+            this.props.doc.addTemplate(template);
+            this._templates.set(template, true);
+        } else {
+            this.props.doc.removeTemplate(template);
+            this._templates.set(template, false);
+        }
     }
 
     @action
@@ -67,7 +65,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
 
         return (
             <div className="templating-menu" >
-                <div className="templating-button" onClick={() => this.toggleTemplateActivity()}>T</div>
+                <div className="templating-button" onClick={() => this.toggleTemplateActivity()}>+</div>
                 <ul id="template-list" style={{ display: this._hidden ? "none" : "block" }}>
                     {templateMenu}
                 </ul>
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 3afdc711c..616bc2566 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -1,57 +1,66 @@
-import { observer } from "mobx-react";
-import { observable } from "mobx";
-import { action, computed } from "mobx";
 import React = require("react");
-import { StringLiteral } from "babel-types";
+
+export enum TemplatePosition {
+    InnerTop,
+    InnerBottom,
+    InnerRight,
+    InnerLeft,
+    OutterTop,
+    OutterBottom,
+    OutterRight,
+    OutterLeft,
+}
 
 export class Template {
-    constructor(name: string, layout: string) {
+    constructor(name: string, position: TemplatePosition, layout: string) {
         this._name = name;
+        this._position = position;
         this._layout = layout;
     }
 
     private _name: string;
+    private _position: TemplatePosition;
     private _layout: string;
 
     get Name(): string {
         return this._name;
     }
 
+    get Position(): TemplatePosition {
+        return this._position;
+    }
+
     get Layout(): string {
         return this._layout;
     }
-
 }
 
 export namespace Templates {
-    export const BasicLayout = new Template("Basic layout", "{layout}");
+    // export const BasicLayout = new Template("Basic layout", "{layout}");
 
-    export const OuterCaption = new Template("Outer caption",
+    export const OuterCaption = new Template("Outer caption", TemplatePosition.OutterBottom,
         `<div><div style="margin:auto; height:calc(100%); width:100%;">{layout}</div><div style="height:(100% + 50px); width:100%; position:absolute"><FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/></div></div>`
     );
 
-    export const InnerCaption = new Template("Inner caption",
-        `<div>
-            <div style="margin:auto; height:calc(100% - 50px); width:100%;">
-                {layout}
-            </div>
-            <div style="height:50px; width:100%; position:absolute">
-                <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
-            </div>
-        </div>`
+    export const InnerCaption = new Template("Inner caption", TemplatePosition.InnerBottom,
+        `<div><div style="margin:auto; height:calc(100% - 50px); width:100%;">{layout}</div><div style="height:50px; width:100%; position:absolute"><FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/></div></div>`
     );
 
-    export const Title = new Template("Title",
-        `<div>
-            <div style="margin:auto; height:calc(100% - 50px); width:100%;">
-                {layout}
-            </div>
-            <div style="height:50px; width:100%; position:absolute; background-color: rgba(0, 0, 0, .6); color: white; padding:12px">
-                {Title}
-            </div>
-        </div>`
+    export const SideCaption = new Template("Side caption", TemplatePosition.OutterRight,
+        `<div><div style="margin:auto; height:100%; width:100%;">{layout}</div><div style="height:100%; width:300px; position:absolute; top: 0; right: -300px;"><FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/></div> </div>`
     );
 
+    export const Title = new Template("Title", TemplatePosition.InnerTop,
+        `<div><div style="margin:auto; height:calc(100% - 50px); width:100%;">{layout}</div><div style="height:50px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .6); color: white; padding:12px">{Title}</div></div>`
+    );
+
+    export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
+
+    export function sortTemplates(a: Template, b: Template) {
+        if (a.Position < b.Position) { return -1; }
+        if (a.Position > b.Position) { return 1; }
+        return 0;
+    }
 
 }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 440269e36..e851bc9c5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -99,13 +99,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     @computed get layout(): string { return this.props.Document.GetText(KeyStore.Layout, "<p>Error loading layout data</p>"); }
     @computed get layoutKeys(): Key[] { return this.props.Document.GetData(KeyStore.LayoutKeys, ListField, new Array<Key>()); }
     @computed get layoutFields(): Key[] { return this.props.Document.GetData(KeyStore.LayoutFields, ListField, new Array<Key>()); }
-    @computed get template(): Template {
-        let field = this.props.Document.GetT(KeyStore.Template, TemplateField);
-        return !field || field === FieldWaiting ? Templates.BasicLayout : field.Data;
-    }
-    set template(template: Template) {
-        this.props.Document.SetData(KeyStore.Template, template, TemplateField);
+    @computed get templates(): Array<Template> {
+        let field = this.props.Document.GetT(KeyStore.Templates, TemplateField);
+        return !field || field === FieldWaiting ? [] : field.Data;
     }
+    set templates(templates: Array<Template>) { this.props.Document.SetData(KeyStore.Templates, templates, TemplateField); }
     screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
     onPointerDown = (e: React.PointerEvent): void => {
         this._downX = e.clientX;
@@ -310,14 +308,39 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 
     updateLayout = (): void => {
-        let temp = this.template.Layout;
-        let layout = temp.replace("{layout}", this.base);
+        let base = this.base;
+        let layout = this.base;
+
+        this.templates.forEach(template => {
+            let temp = template.Layout;
+            layout = temp.replace("{layout}", base);
+            base = layout;
+        });
+
         this.props.Document.SetText(KeyStore.Layout, layout);
     }
 
     @action
-    changeTemplate = (template: Template) => {
-        this.template = template;
+    addTemplate = (template: Template) => {
+        let templates = this.templates;
+        templates.push(template);
+        templates = templates.splice(0, templates.length).sort(Templates.sortTemplates);
+        this.templates = templates;
+        this.updateLayout();
+    }
+
+    @action
+    removeTemplate = (template: Template) => {
+        let templates = this.templates;
+        for (let i = 0; i < templates.length; i++) {
+            let temp = templates[i];
+            if (temp.Name === template.Name) {
+                templates.splice(i, 1);
+                break;
+            }
+        }
+        templates = templates.splice(0, templates.length).sort(Templates.sortTemplates);
+        this.templates = templates;
         this.updateLayout();
     }
 
diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts
index 1602c3776..5bd402890 100644
--- a/src/fields/KeyStore.ts
+++ b/src/fields/KeyStore.ts
@@ -21,7 +21,7 @@ export namespace KeyStore {
     export const ViewType = new Key("ViewType");
     export const BaseLayout = new Key("BaseLayout");
     export const Layout = new Key("Layout");
-    export const Template = new Key("Template");
+    export const Templates = new Key("Templates");
     export const BackgroundColor = new Key("BackgroundColor");
     export const BackgroundLayout = new Key("BackgroundLayout");
     export const OverlayLayout = new Key("OverlayLayout");
diff --git a/src/fields/TemplateField.ts b/src/fields/TemplateField.ts
index bc1e67d7b..72ae13c2e 100644
--- a/src/fields/TemplateField.ts
+++ b/src/fields/TemplateField.ts
@@ -1,11 +1,11 @@
 import { BasicField } from "./BasicField";
 import { Types } from "../server/Message";
 import { FieldId } from "./Field";
-import { Template, Templates } from "../client/views/Templates";
+import { Template, TemplatePosition } from "../client/views/Templates";
 
 
-export class TemplateField extends BasicField<Template> {
-    constructor(data: Template = Templates.BasicLayout, id?: FieldId, save: boolean = true) {
+export class TemplateField extends BasicField<Array<Template>> {
+    constructor(data: Array<Template> = [], id?: FieldId, save: boolean = true) {
         super(data, save, id);
     }
 
@@ -18,18 +18,26 @@ export class TemplateField extends BasicField<Template> {
     }
 
     ToJson() {
+        let templates: Array<{ name: string, position: TemplatePosition, layout: string }> = [];
+        this.Data.forEach(template => {
+            templates.push({ name: template.Name, layout: template.Layout, position: template.Position });
+        });
         return {
-            type: Types.Template,
-            data: this.Data,
+            type: Types.Templates,
+            data: templates,
             id: this.Id,
         };
     }
 
     UpdateFromServer(data: any) {
-        this.data = new Template(data.Name, data.layout);
+        this.data = new Array(data);
     }
 
     static FromJson(id: string, data: any): TemplateField {
-        return new TemplateField(new Template(data._name, data._layout), id, false);
+        let templates: Array<Template> = [];
+        data.forEach((template: { name: string, position: number, layout: string }) => {
+            templates.push(new Template(template.name, template.position, template.layout));
+        });
+        return new TemplateField(templates, id, false);
     }
 }
\ No newline at end of file
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 4a8812956..00a30b329 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -15,7 +15,7 @@ export class Message<T> {
 
 export enum Types {
     Number, List, Key, Image, Web, Document, Text, RichText, DocumentReference,
-    Html, Video, Audio, Ink, PDF, Tuple, HistogramOp, Boolean, Script, Template
+    Html, Video, Audio, Ink, PDF, Tuple, HistogramOp, Boolean, Script, Templates
 }
 
 export interface Transferable {
diff --git a/src/server/ServerUtil.ts b/src/server/ServerUtil.ts
index a2256bca7..a10bdd5e6 100644
--- a/src/server/ServerUtil.ts
+++ b/src/server/ServerUtil.ts
@@ -18,7 +18,6 @@ import { NumberField } from "./../fields/NumberField";
 import { RichTextField } from "./../fields/RichTextField";
 import { TextField } from "./../fields/TextField";
 import { Transferable, Types } from "./Message";
-import { Template } from "../client/views/Templates";
 import { TemplateField } from "../fields/TemplateField";
 
 export class ServerUtils {
@@ -52,7 +51,7 @@ export class ServerUtils {
             case Types.Video: return new VideoField(new URL(json.data), json.id, false);
             case Types.Tuple: return new TupleField(json.data, json.id, false);
             case Types.Ink: return InkField.FromJson(json.id, json.data);
-            case Types.Template: return TemplateField.FromJson(json.id, json.data);
+            case Types.Templates: return TemplateField.FromJson(json.id, json.data);
             case Types.Document: return Document.FromJson(json.data, json.id, false);
             default:
                 throw Error(
-- 
cgit v1.2.3-70-g09d2


From 0391758d22e459b1165bc241b58b5e57b1796d23 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sun, 28 Apr 2019 22:56:48 -0400
Subject: changed template color

---
 src/client/views/Templates.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 616bc2566..52021861f 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -51,7 +51,7 @@ export namespace Templates {
     );
 
     export const Title = new Template("Title", TemplatePosition.InnerTop,
-        `<div><div style="margin:auto; height:calc(100% - 50px); width:100%;">{layout}</div><div style="height:50px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .6); color: white; padding:12px">{Title}</div></div>`
+        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:12px">{Title}</div></div>`
     );
 
     export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
-- 
cgit v1.2.3-70-g09d2


From a98672afa22eeb5f4c24edd3207c1b043cd012e7 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 29 Apr 2019 01:01:59 -0400
Subject: fixed templates

---
 src/client/views/MainOverlayTextBox.tsx         |  8 +++-----
 src/client/views/Templates.tsx                  |  6 +++---
 src/client/views/nodes/DocumentContentsView.tsx |  4 +++-
 src/client/views/nodes/DocumentView.tsx         | 23 +++++++++++++----------
 src/client/views/nodes/FormattedTextBox.tsx     | 12 ++++++------
 5 files changed, 28 insertions(+), 25 deletions(-)

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

diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index be8d67925..209b1202c 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -89,12 +89,10 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
 
     render() {
         if (this.TextDoc) {
-            let toScreenXf = this._textXf().inverse();
-            let pt = toScreenXf.transformPoint(0, 0);
-            let s = 1 / this._textXf().Scale;
-            return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${pt[0]}px, ${pt[1]}px) scale(${s},${s})`, width: "auto", height: "auto" }} >
+            let s = this._textXf().Scale;
+            return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${this._textRect.x}px, ${this._textRect.y}px) scale(${1 / s},${1 / s})`, width: "auto", height: "auto" }} >
                 <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
-                    style={{ width: `${this.TextDoc.Width()}px`, height: `${this.TextDoc.Height()}px` }}>
+                    style={{ width: `${this._textRect.width * s}px`, height: `${this._textRect.height * s}px` }}>
                     <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
                         selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
                         ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 52021861f..a0f9fe7e5 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -39,15 +39,15 @@ export namespace Templates {
     // export const BasicLayout = new Template("Basic layout", "{layout}");
 
     export const OuterCaption = new Template("Outer caption", TemplatePosition.OutterBottom,
-        `<div><div style="margin:auto; height:calc(100%); width:100%;">{layout}</div><div style="height:(100% + 50px); width:100%; position:absolute"><FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/></div></div>`
+        `<div><div style="margin:auto; height:calc(100%); width:100%;">{layout}</div><div style="height:(100% + 50px); width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={CaptionKey} /></div></div>`
     );
 
     export const InnerCaption = new Template("Inner caption", TemplatePosition.InnerBottom,
-        `<div><div style="margin:auto; height:calc(100% - 50px); width:100%;">{layout}</div><div style="height:50px; width:100%; position:absolute"><FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/></div></div>`
+        `<div><div style="margin:auto; height:calc(100% - 50px); width:100%;">{layout}</div><div style="height:50px; width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={CaptionKey}/></div></div>`
     );
 
     export const SideCaption = new Template("Side caption", TemplatePosition.OutterRight,
-        `<div><div style="margin:auto; height:100%; width:100%;">{layout}</div><div style="height:100%; width:300px; position:absolute; top: 0; right: -300px;"><FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={CaptionKey} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/></div> </div>`
+        `<div><div style="margin:auto; height:100%; width:100%;">{layout}</div><div style="height:100%; width:300px; position:absolute; top: 0; right: -300px;"><FormattedTextBox {...props} fieldKey={CaptionKey}/></div> </div>`
     );
 
     export const Title = new Template("Title", TemplatePosition.InnerTop,
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 07599c345..e9c46aa9d 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -47,7 +47,9 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     CreateBindings(): JsxBindings {
         let bindings: JsxBindings = { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive) };
 
-        for (const key of this.layoutKeys) {
+        let keys: Key[] = [];
+        keys.push(...this.layoutKeys, KeyStore.Caption) // bcz: hack to get templates to work
+        for (const key of keys) {
             bindings[key.Name + "Key"] = key; // this maps string values of the form <keyname>Key to an actual key Kestore.keyname  e.g,   "DataKey" => KeyStore.Data
         }
         for (const key of this.layoutFields) {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bae14193e..6dddab600 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -26,6 +26,7 @@ import React = require("react");
 import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { MarqueeView } from "../collections/collectionFreeForm/MarqueeView";
+import { TextField } from "../../../fields/TextField";
 
 export interface DocumentViewProps {
     ContainingCollectionView: Opt<CollectionView | CollectionPDFView | CollectionVideoView>;
@@ -88,7 +89,6 @@ export function FakeJsxArgs(keys: string[], fields: string[] = []): JsxArgs {
 export class DocumentView extends React.Component<DocumentViewProps> {
     private _downX: number = 0;
     private _downY: number = 0;
-    @computed get base(): string { return this.props.Document.GetText(KeyStore.BaseLayout, "<p>Error loading base layout data</p>"); }
     private _mainCont = React.createRef<HTMLDivElement>();
     private _dropDisposer?: DragManager.DragDropDisposer;
 
@@ -249,17 +249,20 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
     }
 
-    updateLayout = (): void => {
-        let base = this.base;
-        let layout = this.base;
+    updateLayout = async () => {
+        const baseLayout = await this.props.Document.GetTAsync(KeyStore.BaseLayout, TextField);
+        if (baseLayout) {
+            let base = baseLayout.Data;
+            let layout = baseLayout.Data;
 
-        this.templates.forEach(template => {
-            let temp = template.Layout;
-            layout = temp.replace("{layout}", base);
-            base = layout;
-        });
+            this.templates.forEach(template => {
+                let temp = template.Layout;
+                layout = temp.replace("{layout}", base);
+                base = layout;
+            });
 
-        this.props.Document.SetText(KeyStore.Layout, layout);
+            this.props.Document.SetText(KeyStore.Layout, layout);
+        }
     }
 
     @action
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index fc8d757f6..92957ed19 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -50,6 +50,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         return FieldView.LayoutString(FormattedTextBox, fieldStr);
     }
     private _ref: React.RefObject<HTMLDivElement>;
+    private _proseRef: React.RefObject<HTMLDivElement>;
     private _editorView: Opt<EditorView>;
     private _gotDown: boolean = false;
     private _reactionDisposer: Opt<IReactionDisposer>;
@@ -60,6 +61,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         super(props);
 
         this._ref = React.createRef();
+        this._proseRef = React.createRef();
         this.onChange = this.onChange.bind(this);
     }
 
@@ -188,9 +190,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
 
     onFocused = (e: React.FocusEvent): void => {
         if (!this.props.isOverlay) {
-            if (MainOverlayTextBox.Instance.TextDoc != this.props.Document) {
-                MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform);
-            }
+            MainOverlayTextBox.Instance.SetTextDoc(this.props.Document, this.props.fieldKey, this._ref.current!, this.props.ScreenToLocalTransform);
         } else {
             if (this._ref.current) {
                 this._ref.current.scrollTop = MainOverlayTextBox.Instance.TextScroll;
@@ -232,7 +232,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
     }
 
     onClick = (e: React.MouseEvent): void => {
-        this._ref.current!.focus();
+        this._proseRef.current!.focus();
     }
 
     tooltipTextMenuPlugin() {
@@ -277,7 +277,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         let color = this.props.Document.GetText(KeyStore.BackgroundColor, "");
         let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive";
         return (
-            <div className={`formattedTextBox-cont${style}`}
+            <div className={`formattedTextBox-cont${style}`} ref={this._ref}
                 style={{
                     pointerEvents: interactive ? "all" : "none",
                     background: color,
@@ -292,7 +292,7 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
                 // tfs: do we need this event handler
                 onWheel={this.onPointerWheel}
             >
-                <div className={`formattedTextBox-inner${rounded}`} ref={this._ref} />
+                <div className={`formattedTextBox-inner${rounded}`} ref={this._proseRef} />
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From a180574f56447d6f24b4e77f87efba3a1287da9c Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Mon, 29 Apr 2019 09:43:10 -0400
Subject: fixed title template and overlay text positioning

---
 src/client/views/MainOverlayTextBox.tsx     |  9 ++++-----
 src/client/views/Templates.tsx              |  2 +-
 src/client/views/nodes/FormattedTextBox.tsx | 11 +++++------
 3 files changed, 10 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx
index 209b1202c..13e661b46 100644
--- a/src/client/views/MainOverlayTextBox.tsx
+++ b/src/client/views/MainOverlayTextBox.tsx
@@ -21,7 +21,6 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
     public static Instance: MainOverlayTextBox;
     @observable public TextDoc?: Document = undefined;
     public TextScroll: number = 0;
-    @observable _textRect: any;
     @observable _textXf: () => Transform = () => Transform.Identity();
     private _textFieldKey: Key = KeyStore.Data;
     private _textColor: string | null = null;
@@ -47,7 +46,6 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
         if (div) {
             this._textColor = div.style.color;
             div.style.color = "transparent";
-            this._textRect = div.getBoundingClientRect();
             this.TextScroll = div.scrollTop;
         }
     }
@@ -88,11 +86,12 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps>
     }
 
     render() {
-        if (this.TextDoc) {
+        if (this.TextDoc && this._textTargetDiv) {
+            let textRect = this._textTargetDiv.getBoundingClientRect();
             let s = this._textXf().Scale;
-            return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${this._textRect.x}px, ${this._textRect.y}px) scale(${1 / s},${1 / s})`, width: "auto", height: "auto" }} >
+            return <div className="mainOverlayTextBox-textInput" style={{ transform: `translate(${textRect.x}px, ${textRect.y}px) scale(${1 / s},${1 / s})`, width: "auto", height: "auto" }} >
                 <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll}
-                    style={{ width: `${this._textRect.width * s}px`, height: `${this._textRect.height * s}px` }}>
+                    style={{ width: `${textRect.width * s}px`, height: `${textRect.height * s}px` }}>
                     <FormattedTextBox fieldKey={this._textFieldKey} isOverlay={true} Document={this.TextDoc} isSelected={returnTrue} select={emptyFunction} isTopMost={true}
                         selectOnLoad={true} ContainingCollectionView={undefined} whenActiveChanged={emptyFunction} active={returnTrue}
                         ScreenToLocalTransform={this._textXf} PanelWidth={returnZero} PanelHeight={returnZero} focus={emptyDocFunction} />
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index a0f9fe7e5..fef69392d 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -51,7 +51,7 @@ export namespace Templates {
     );
 
     export const Title = new Template("Title", TemplatePosition.InnerTop,
-        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:12px">{Title}</div></div>`
+        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{Title}</div></div>`
     );
 
     export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 92957ed19..41ee24498 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -80,7 +80,11 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
             );
             this.props.Document.SetDataOnPrototype(KeyStore.DocumentText, state.doc.textBetween(0, state.doc.content.size, "\n\n"), TextField);
             this._applyingChange = false;
-            // doc.SetData(fieldKey, JSON.stringify(state.toJSON()), RichTextField);
+            if (this.props.Document.Title.startsWith("-") && this._editorView) {
+                let str = this._editorView.state.doc.textContent;
+                let titlestr = str.substr(0, Math.min(40, str.length));
+                this.props.Document.SetText(KeyStore.Title, "-" + titlestr + (str.length > 40 ? "..." : ""));
+            };
         }
     }
 
@@ -265,11 +269,6 @@ export class FormattedTextBox extends React.Component<(FieldViewProps & Formatte
         // stop propagation doesn't seem to stop propagation of native keyboard events.
         // so we set a flag on the native event that marks that the event's been handled.
         (e.nativeEvent as any).DASHFormattedTextBoxHandled = true;
-        if (this.props.Document.Title.startsWith("-") && this._editorView) {
-            let str = this._editorView.state.doc.textContent;
-            let titlestr = str.substr(0, Math.min(40, str.length));
-            this.props.Document.SetText(KeyStore.Title, "-" + titlestr + (str.length > 40 ? "..." : ""));
-        };
     }
     render() {
         let style = this.props.isOverlay ? "-scroll" : "-hidden";
-- 
cgit v1.2.3-70-g09d2


From 35105279d9cc55ccf331ec06de78077250b4cb5b Mon Sep 17 00:00:00 2001
From: Tyler Schicke <tyler_schicke@brown.edu>
Date: Tue, 30 Apr 2019 19:30:17 -0400
Subject: Fixed various keys that hadn't been properly converted, which fixes
 zooming out on images/videos, etc.

---
 src/client/documents/Documents.ts           | 20 ++++++++++----------
 src/client/views/Templates.tsx              |  8 ++++----
 src/client/views/nodes/FormattedTextBox.tsx |  2 +-
 3 files changed, 15 insertions(+), 15 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 964faa8db..d9ee7a736 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -112,13 +112,13 @@ export namespace Docs {
     }
 
     function CreateImagePrototype(): Doc {
-        let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("AnnotationsKey"),
+        let imageProto = setupPrototypeOptions(imageProtoId, "IMAGE_PROTO", CollectionView.LayoutString("annotations"),
             { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: ImageBox.LayoutString(), curPage: 0 });
         return imageProto;
     }
 
     function CreateHistogramPrototype(): Doc {
-        let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("AnnotationsKey"),
+        let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"),
             { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString() });
         return histoProto;
     }
@@ -134,7 +134,7 @@ export namespace Docs {
         return textProto;
     }
     function CreatePdfPrototype(): Doc {
-        let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("AnnotationsKey"),
+        let pdfProto = setupPrototypeOptions(pdfProtoId, "PDF_PROTO", CollectionPDFView.LayoutString("annotations"),
             { x: 0, y: 0, nativeWidth: 1200, width: 300, backgroundLayout: PDFBox.LayoutString(), curPage: 1 });
         return pdfProto;
     }
@@ -155,7 +155,7 @@ export namespace Docs {
         return kvpProto;
     }
     function CreateVideoPrototype(): Doc {
-        let videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("AnnotationsKey"),
+        let videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("annotations"),
             { x: 0, y: 0, nativeWidth: 600, width: 300, backgroundLayout: VideoBox.LayoutString(), curPage: 0 });
         return videoProto;
     }
@@ -267,14 +267,14 @@ export namespace Docs {
             + ImageBox.LayoutString() +
             `</div>
             <div style="position:relative; height:15%; text-align:center; ">`
-            + FormattedTextBox.LayoutString("CaptionKey") +
+            + FormattedTextBox.LayoutString("caption") +
             `</div> 
         </div>`;
     }
-    export function FixedCaption(fieldName: string = "Caption") {
+    export function FixedCaption(fieldName: string = "caption") {
         return `<div style="position:absolute; height:30px; bottom:0; width:100%">
             <div style="position:absolute; width:100%; height:100%; text-align:center;bottom:0;">`
-            + FormattedTextBox.LayoutString(fieldName + "Key") +
+            + FormattedTextBox.LayoutString(fieldName) +
             `</div> 
         </div>`;
     }
@@ -286,7 +286,7 @@ export namespace Docs {
         {layout}
     </div>
     <div style="height:(100% + 25px); width:100%; position:absolute">
-        <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+        <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
     </div>
 </div>       
         `);
@@ -298,7 +298,7 @@ export namespace Docs {
             {layout}
         </div>
         <div style="height:25px; width:100%; position:absolute">
-            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
         </div>
     </div>       
             `);
@@ -321,7 +321,7 @@ export namespace Docs {
             {layout}
         </div>
         <div style="height:15%; width:100%; position:absolute">
-            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"CaptionKey"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
+            <FormattedTextBox doc={Document} DocumentViewForField={DocumentView} bindings={bindings} fieldKey={"caption"} isSelected={isSelected} select={select} selectOnLoad={SelectOnLoad} isTopMost={isTopMost}/>
         </div>
     </div>       
             `);
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index fef69392d..5b7e8da63 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -39,19 +39,19 @@ export namespace Templates {
     // export const BasicLayout = new Template("Basic layout", "{layout}");
 
     export const OuterCaption = new Template("Outer caption", TemplatePosition.OutterBottom,
-        `<div><div style="margin:auto; height:calc(100%); width:100%;">{layout}</div><div style="height:(100% + 50px); width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={CaptionKey} /></div></div>`
+        `<div><div style="margin:auto; height:calc(100%); width:100%;">{layout}</div><div style="height:(100% + 50px); width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={"caption"} /></div></div>`
     );
 
     export const InnerCaption = new Template("Inner caption", TemplatePosition.InnerBottom,
-        `<div><div style="margin:auto; height:calc(100% - 50px); width:100%;">{layout}</div><div style="height:50px; width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={CaptionKey}/></div></div>`
+        `<div><div style="margin:auto; height:calc(100% - 50px); width:100%;">{layout}</div><div style="height:50px; width:100%; position:absolute"><FormattedTextBox {...props} fieldKey={"caption"}/></div></div>`
     );
 
     export const SideCaption = new Template("Side caption", TemplatePosition.OutterRight,
-        `<div><div style="margin:auto; height:100%; width:100%;">{layout}</div><div style="height:100%; width:300px; position:absolute; top: 0; right: -300px;"><FormattedTextBox {...props} fieldKey={CaptionKey}/></div> </div>`
+        `<div><div style="margin:auto; height:100%; width:100%;">{layout}</div><div style="height:100%; width:300px; position:absolute; top: 0; right: -300px;"><FormattedTextBox {...props} fieldKey={"caption"}/></div> </div>`
     );
 
     export const Title = new Template("Title", TemplatePosition.InnerTop,
-        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{Title}</div></div>`
+        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{Document.title}</div></div>`
     );
 
     export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 9c6f95fc4..b95d327d2 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -28,7 +28,7 @@ const { menuBar } = require("prosemirror-menu");
 
 // FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document
 //
-//  HTML Markup:  <FormattedTextBox Doc={Document's ID} FieldKey={Key's name + "Key"}
+//  HTML Markup:  <FormattedTextBox Doc={Document's ID} FieldKey={Key's name}
 //
 //  In Code, the node's HTML is specified in the document's parameterized structure as:
 //        document.SetField(KeyStore.Layout,  "<FormattedTextBox doc={doc} fieldKey={<KEYNAME>Key} />");
-- 
cgit v1.2.3-70-g09d2


From 276a6619eb3d86dabcb3ece26486e87b09708a3b Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 2 May 2019 00:21:18 -0400
Subject: made templates work again

---
 src/client/documents/Documents.ts         |  2 +-
 src/client/views/DocumentDecorations.scss | 31 -------------------------------
 src/client/views/DocumentDecorations.tsx  |  2 +-
 src/client/views/TemplateMenu.tsx         | 15 +++++++++------
 src/client/views/Templates.tsx            |  2 +-
 src/client/views/nodes/DocumentView.tsx   | 24 ++++++++++++------------
 6 files changed, 24 insertions(+), 52 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 6ca2567dc..0333f4673 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -48,7 +48,7 @@ export interface DocumentOptions {
     scale?: number;
     baseLayout?: string;
     layout?: string;
-    //templates?: Array<Template>;
+    templates?: List<string>;
     viewType?: number;
     backgroundColor?: string;
     copyDraggedItems?: boolean;
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index ecddb8b06..158b02b5a 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -105,37 +105,6 @@ $linkGap : 3px;
     opacity: 0.1;
 }
 
-//     position: absolute;
-//     display: grid;
-//     z-index: 1000;
-//     grid-template-rows: 20px 1fr 20px 0px;
-//     grid-template-columns: 20px 1fr 20px;
-//     pointer-events: none;
-//     #documentDecorations-centerCont {
-//         background: none;
-//     }
-//     .documentDecorations-resizer {
-//         pointer-events: auto;
-//         background: lightblue;
-//         opacity: 0.4;
-//     }
-//     #documentDecorations-topLeftResizer,
-//     #documentDecorations-bottomRightResizer {
-//         cursor: nwse-resize;
-//     }
-//     #documentDecorations-topRightResizer,
-//     #documentDecorations-bottomLeftResizer {
-//         cursor: nesw-resize;
-//     }
-//     #documentDecorations-topResizer,
-//     #documentDecorations-bottomResizer {
-//         cursor: ns-resize;
-//     }
-//     #documentDecorations-leftResizer,
-//     #documentDecorations-rightResizer {
-//         cursor: ew-resize;
-//     }
-// }
 .linkFlyout {
     grid-column: 2/4;
     margin-top: $linkGap;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index a6c7f9718..7dcd71495 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -518,7 +518,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             let docTemps = doc.templates;
             let checked = false;
             docTemps.forEach(temp => {
-                if (template.Name === temp.Name) {
+                if (template.Layout === temp) {
                     checked = true;
                 }
             });
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 8eb2fc6c6..7be846e05 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,4 +1,4 @@
-import { observable, computed, action } from "mobx";
+import { observable, computed, action, trace } from "mobx";
 import React = require("react");
 import { observer } from "mobx-react";
 import './DocumentDecorations.scss';
@@ -33,23 +33,24 @@ export interface TemplateMenuProps {
 export class TemplateMenu extends React.Component<TemplateMenuProps> {
 
     @observable private _hidden: boolean = true;
-    @observable private _templates: Map<Template, boolean> = this.props.templates;
 
 
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
         if (event.target.checked) {
             this.props.doc.addTemplate(template);
-            this._templates.set(template, true);
+            this.props.templates.set(template, true);
+            this.props.templates.forEach((checked, template) => console.log("Set Checked + " + checked + " " + this.props.templates.get(template)));
         } else {
             this.props.doc.removeTemplate(template);
-            this._templates.set(template, false);
+            this.props.templates.set(template, false);
+            this.props.templates.forEach((checked, template) => console.log("Unset Checked + " + checked + " " + this.props.templates.get(template)));
         }
     }
 
     @action
     componentWillReceiveProps(nextProps: TemplateMenuProps) {
-        this._templates = nextProps.templates;
+        // this._templates = nextProps.templates;
     }
 
     @action
@@ -58,8 +59,10 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     }
 
     render() {
+        trace();
         let templateMenu: Array<JSX.Element> = [];
-        this._templates.forEach((checked, template) => {
+        this.props.templates.forEach((checked, template) => {
+            console.log("checked + " + checked + " " + this.props.templates.get(template));
             templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />);
         });
 
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 5b7e8da63..25d89772e 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -51,7 +51,7 @@ export namespace Templates {
     );
 
     export const Title = new Template("Title", TemplatePosition.InnerTop,
-        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{Document.title}</div></div>`
+        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.title}</div></div>`
     );
 
     export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b35d68c4b..ae2d3af13 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -87,12 +87,14 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     public get ContentDiv() { return this._mainCont.current; }
     @computed get active(): boolean { return SelectionManager.IsSelected(this) || this.props.parentActive(); }
     @computed get topMost(): boolean { return this.props.isTopMost; }
-    @computed get templates(): Array<Template> {
-        return new Array<Template>();
-        // let field = this.props.Document[KeyStore.Templates];
-        // return !field || field === FieldWaiting ? [] : field.Data;
+    @computed get templates(): List<string> {
+        let field = this.props.Document.templates;
+        if (field && field instanceof List) {
+            return field;
+        }
+        return new List<string>();
     }
-    set templates(templates: Array<Template>) { /* this.props.Document.templates = templates;*/ }
+    set templates(templates: List<string>) { this.props.Document.templates = templates; }
     screenRect = (): ClientRect | DOMRect => this._mainCont.current ? this._mainCont.current.getBoundingClientRect() : new DOMRect();
 
     @action
@@ -248,8 +250,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             let layout = baseLayout;
 
             this.templates.forEach(template => {
-                let temp = template.Layout;
-                layout = temp.replace("{layout}", base);
+                layout = template.replace("{layout}", base);
                 base = layout;
             });
 
@@ -260,9 +261,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     @action
     addTemplate = (template: Template) => {
         let templates = this.templates;
-        templates.push(template);
-        templates = templates.splice(0, templates.length).sort(Templates.sortTemplates);
-        this.templates = templates;
+        templates.push(template.Layout);
+        this.templates = new List<string>(templates.map(t => t));
         this.updateLayout();
     }
 
@@ -271,12 +271,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         let templates = this.templates;
         for (let i = 0; i < templates.length; i++) {
             let temp = templates[i];
-            if (temp.Name === template.Name) {
+            if (temp === template.Layout) {
                 templates.splice(i, 1);
                 break;
             }
         }
-        templates = templates.splice(0, templates.length).sort(Templates.sortTemplates);
+        templates = new List<string>(templates.splice(0, templates.length));
         this.templates = templates;
         this.updateLayout();
     }
-- 
cgit v1.2.3-70-g09d2


From 407c104f0ad0957d71f73c14f5b835fab387ecd2 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 2 May 2019 00:50:08 -0400
Subject: fixed up templates a bit. starting on summarization

---
 src/client/views/Templates.tsx                     |  3 ++
 .../collections/collectionFreeForm/MarqueeView.tsx |  5 +++-
 src/client/views/nodes/DocumentContentsView.tsx    | 25 +++++++++++++++--
 src/client/views/nodes/DocumentView.tsx            | 32 ++++------------------
 4 files changed, 36 insertions(+), 29 deletions(-)

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

diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 25d89772e..e17dd2354 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -53,6 +53,9 @@ export namespace Templates {
     export const Title = new Template("Title", TemplatePosition.InnerTop,
         `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.title}</div></div>`
     );
+    export const Summary = new Template("Title", TemplatePosition.InnerTop,
+        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.doc1.title}</div></div>`
+    );
 
     export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 85a12defa..c58e7780c 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -13,6 +13,8 @@ import { Utils } from "../../../../Utils";
 import { Doc } from "../../../../new_fields/Doc";
 import { NumCast, Cast } from "../../../../new_fields/Types";
 import { InkField, StrokeData } from "../../../../new_fields/InkField";
+import { Templates } from "../../Templates";
+import { List } from "../../../../new_fields/List";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -178,7 +180,8 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             // SelectionManager.DeselectAll();
             if (e.key === "r") {
                 let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
-                Doc.MakeLink(summary.proto!, newCollection.proto!);
+                summary.doc1 = newCollection.proto!;
+                summary.templates = new List<string>([Templates.Summary.Layout]);
                 this.props.addLiveTextDocument(summary);
                 e.preventDefault();
             }
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 794442469..24e8a36ae 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -20,7 +20,8 @@ import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
 import React = require("react");
 import { FieldViewProps } from "./FieldView";
 import { Without, OmitKeys } from "../../../Utils";
-import { Cast } from "../../../new_fields/Types";
+import { Cast, StrCast } from "../../../new_fields/Types";
+import { List } from "../../../new_fields/List";
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
 type BindingProps = Without<FieldViewProps, 'fieldKey'>;
@@ -40,11 +41,31 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         return { props: OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit };
     }
 
+    @computed get templates(): List<string> {
+        let field = this.props.Document.templates;
+        if (field && field instanceof List) {
+            return field;
+        }
+        return new List<string>();
+    }
+    set templates(templates: List<string>) { this.props.Document.templates = templates; }
+    get finalLayout() {
+        const baseLayout = this.layout;
+        let base = baseLayout;
+        let layout = baseLayout;
+
+        this.templates.forEach(template => {
+            layout = template.replace("{layout}", base);
+            base = layout;
+        });
+        return layout;
+    }
+
     render() {
         return <JsxParser
             components={{ FormattedTextBox, ImageBox, IconBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
             bindings={this.CreateBindings()}
-            jsx={this.layout}
+            jsx={this.finalLayout}
             showWarnings={true}
             onError={(test: any) => { console.log(test); }}
         />;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index ae2d3af13..e83f3fcba 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -243,42 +243,22 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
     }
 
-    updateLayout = async () => {
-        const baseLayout = await StrCast(this.props.Document.baseLayout);
-        if (baseLayout) {
-            let base = baseLayout;
-            let layout = baseLayout;
-
-            this.templates.forEach(template => {
-                layout = template.replace("{layout}", base);
-                base = layout;
-            });
-
-            this.props.Document.layout = layout;
-        }
-    }
 
     @action
     addTemplate = (template: Template) => {
-        let templates = this.templates;
-        templates.push(template.Layout);
-        this.templates = new List<string>(templates.map(t => t));
-        this.updateLayout();
+        this.templates.push(template.Layout);
+        this.templates = this.templates;
     }
 
     @action
     removeTemplate = (template: Template) => {
-        let templates = this.templates;
-        for (let i = 0; i < templates.length; i++) {
-            let temp = templates[i];
-            if (temp === template.Layout) {
-                templates.splice(i, 1);
+        for (let i = 0; i < this.templates.length; i++) {
+            if (this.templates[i] === template.Layout) {
+                this.templates.splice(i, 1);
                 break;
             }
         }
-        templates = new List<string>(templates.splice(0, templates.length));
-        this.templates = templates;
-        this.updateLayout();
+        this.templates = this.templates;
     }
 
     @action
-- 
cgit v1.2.3-70-g09d2


From a78282cdf7fbb99386484640e1fb89d4b9b0cbee Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Thu, 2 May 2019 16:19:07 -0400
Subject: fixed some things with trees and summaries.

---
 src/client/views/Main.tsx                                    |  3 +--
 src/client/views/Templates.tsx                               | 12 +++++++++++-
 src/client/views/collections/CollectionTreeView.tsx          | 10 +++++++---
 .../views/collections/collectionFreeForm/MarqueeView.tsx     |  4 +++-
 src/client/views/nodes/DocumentContentsView.tsx              |  3 ++-
 src/client/views/nodes/FieldView.tsx                         | 11 ++++-------
 src/client/views/nodes/ImageBox.scss                         |  2 ++
 src/new_fields/List.ts                                       |  6 +++---
 src/server/authentication/models/current_user_utils.ts       |  2 ++
 9 files changed, 35 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index a07a2d5b1..97eb73d7f 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -144,8 +144,7 @@ export class Main extends React.Component {
     createNewWorkspace = async (id?: string) => {
         const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc));
         if (list) {
-            let libraryDoc = Docs.TreeDocument([CurrentUserUtils.UserDocument], { x: 0, y: 400, title: `Library: ${CurrentUserUtils.email} ${list.length + 1}` });
-            libraryDoc.excludeFromLibrary = true;
+            let libraryDoc = await (CurrentUserUtils.UserDocument.library as Doc);
             let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` });
             var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(libraryDoc, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] };
             let mainDoc = Docs.DockDocument([libraryDoc, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` });
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index e17dd2354..668ae5312 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -54,7 +54,17 @@ export namespace Templates {
         `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.title}</div></div>`
     );
     export const Summary = new Template("Title", TemplatePosition.InnerTop,
-        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.doc1.title}</div></div>`
+        `<div style="height:100%; width:100%;position:absolute; margin:0; padding: 0">
+            <div style="height:60%; width:100%;position:absolute;background:yellow; padding:0; margin:0">
+                {layout}
+            </div>
+            <div style="bottom:0px; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
+                <FieldView {...props} fieldKey={"doc1"} />
+            </div>
+            <div style="bottom:0; left: 50%; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
+                <FieldView {...props} fieldKey={"doc2"} />
+            </div>
+        </div>`
     );
 
     export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 48b226615..2cef1462b 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,7 +1,7 @@
 import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
 import { faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from "mobx";
+import { action, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager";
 import { EditableView } from "../EditableView";
@@ -145,8 +145,11 @@ class TreeView extends React.Component<TreeViewProps> {
         </div>;
     }
     public static GetChildElements(docs: Doc[], remove: ((doc: Doc) => void), move: DragManager.MoveFunction, dropAction: dropActionType) {
-        return docs.filter(child => !child.excludeFromLibrary).map(child =>
-            <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />);
+        return docs.filter(child => !child.excludeFromLibrary).filter(doc => FieldValue(doc)).map(child => {
+            console.log("child = " + child[Id]);
+            return <TreeView document={child} key={child[Id]} deleteDoc={remove} moveDocument={move} dropAction={dropAction} />
+        }
+        );
     }
 }
 
@@ -168,6 +171,7 @@ export class CollectionTreeView extends CollectionSubView(Document) {
         }
     }
     render() {
+        trace();
         const children = this.children;
         let dropAction = StrCast(this.props.Document.dropAction, "alias") as dropActionType;
         if (!children) {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index c58e7780c..82027a6f2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -180,7 +180,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
             // SelectionManager.DeselectAll();
             if (e.key === "r") {
                 let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" });
-                summary.doc1 = newCollection.proto!;
+                summary.doc1 = selected[0];
+                if (selected.length > 1)
+                    summary.doc2 = selected[1];
                 summary.templates = new List<string>([Templates.Summary.Layout]);
                 this.props.addLiveTextDocument(summary);
                 e.preventDefault();
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 24e8a36ae..ddfe79a5c 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -15,6 +15,7 @@ import { IconBox } from "./IconBox";
 import { KeyValueBox } from "./KeyValueBox";
 import { PDFBox } from "./PDFBox";
 import { VideoBox } from "./VideoBox";
+import { FieldView } from "./FieldView";
 import { WebBox } from "./WebBox";
 import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
 import React = require("react");
@@ -63,7 +64,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
 
     render() {
         return <JsxParser
-            components={{ FormattedTextBox, ImageBox, IconBox, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+            components={{ FormattedTextBox, ImageBox, IconBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
             bindings={this.CreateBindings()}
             jsx={this.finalLayout}
             showWarnings={true}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 63009f2db..a1e083b36 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -57,12 +57,12 @@ export class FieldView extends React.Component<FieldViewProps> {
     }
     render() {
         const field = this.field;
-        if (!field) {
+        if (field === undefined) {
             return <p>{'<null>'}</p>;
         }
-        if (typeof field === "string") {
-            return <p>{field}</p>;
-        }
+        // if (typeof field === "string") {
+        //     return <p>{field}</p>;
+        // }
         else if (field instanceof RichTextField) {
             return <FormattedTextBox {...this.props} />;
         }
@@ -108,9 +108,6 @@ export class FieldView extends React.Component<FieldViewProps> {
         // else if (field instanceof HtmlField) {
         //     return <WebBox {...this.props} />
         // }
-        else if (typeof field === "number") {
-            return <p>{field}</p>;
-        }
         else if (!(field instanceof Promise)) {
             return <p>{JSON.stringify(field)}</p>;
         }
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 9fe211df0..2316a050e 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -10,6 +10,8 @@
 }
 .imageBox-cont-interactive {
   pointer-events: all;
+  width:100%;
+  height:auto;
 }
 
 .imageBox-dot {
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts
index 1c4b96c81..db7932cec 100644
--- a/src/new_fields/List.ts
+++ b/src/new_fields/List.ts
@@ -2,7 +2,7 @@ import { Deserializable, autoObject } from "../client/util/SerializationHelper";
 import { Field, Update, Self, FieldResult } from "./Doc";
 import { setter, getter, deleteProperty } from "./util";
 import { serializable, alias, list } from "serializr";
-import { observable } from "mobx";
+import { observable, action } from "mobx";
 import { ObjectField, OnUpdate, Copy } from "./ObjectField";
 import { RefField } from "./RefField";
 import { ProxyField } from "./Proxy";
@@ -25,12 +25,12 @@ const listHandlers: any = {
         this[Update]();
         return field;
     },
-    push(...items: any[]) {
+    push: action(function (this: any, ...items: any[]) {
         items = items.map(toObjectField);
         const res = this[Self].__fields.push(...items);
         this[Update]();
         return res;
-    },
+    }),
     reverse() {
         const res = this[Self].__fields.reverse();
         this[Update]();
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 9db470ca0..93c2afb1d 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -26,6 +26,8 @@ export class CurrentUserUtils {
         doc.title = this.email;
         doc.data = new List<Doc>();
         doc.optionalRightCollection = Docs.SchemaDocument([], { title: "Pending documents" });
+        doc.library = Docs.TreeDocument([doc], { title: `Library: ${CurrentUserUtils.email}` });
+        (doc.library as Doc).excludeFromLibrary = true;
         return doc;
     }
 
-- 
cgit v1.2.3-70-g09d2


From 021e8cbe9892d0a9e277026b933ac634a7a9eba5 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 2 May 2019 20:16:52 -0400
Subject: summary template that doesn't update after reloading

---
 src/client/views/Templates.tsx | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 668ae5312..5858ee014 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -54,18 +54,21 @@ export namespace Templates {
         `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.title}</div></div>`
     );
     export const Summary = new Template("Title", TemplatePosition.InnerTop,
-        `<div style="height:100%; width:100%;position:absolute; margin:0; padding: 0">
-            <div style="height:60%; width:100%;position:absolute;background:yellow; padding:0; margin:0">
-                {layout}
-            </div>
-            <div style="bottom:0px; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
-                <FieldView {...props} fieldKey={"doc1"} />
-            </div>
-            <div style="bottom:0; left: 50%; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
-                <FieldView {...props} fieldKey={"doc2"} />
-            </div>
-        </div>`
+        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.doc1.title}</div></div>`
     );
+    // export const Summary = new Template("Title", TemplatePosition.InnerTop,
+    //     `<div style="height:100%; width:100%;position:absolute; margin:0; padding: 0">
+    //         <div style="height:60%; width:100%;position:absolute;background:yellow; padding:0; margin:0">
+    //             {layout}
+    //         </div>
+    //         <div style="bottom:0px; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
+    //             <FieldView {...props} fieldKey={"doc1"} />
+    //         </div>
+    //         <div style="bottom:0; left: 50%; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
+    //             <FieldView {...props} fieldKey={"doc2"} />
+    //         </div>
+    //     </div>`
+    // );
 
     export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
 
-- 
cgit v1.2.3-70-g09d2


From 20f31796ed6f6c9fb71fa80cf858a7ac353300cf Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 7 May 2019 00:22:01 -0400
Subject: from last

---
 src/client/views/Templates.tsx | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 5858ee014..738272913 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -51,24 +51,22 @@ export namespace Templates {
     );
 
     export const Title = new Template("Title", TemplatePosition.InnerTop,
-        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.title}</div></div>`
+        `<div><div style="height:calc(100% - 25px); margin-top: 25px; width:100%;position:absolute;">{layout}</div>
+        <div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.title}</div></div>`
     );
     export const Summary = new Template("Title", TemplatePosition.InnerTop,
-        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div><div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.doc1.title}</div></div>`
+        `<div style="height:100%; width:100%;position:absolute; margin:0; padding: 0">
+            <div style="height:60%; width:100%;position:absolute;background:yellow; padding:0; margin:0">
+                {layout}
+            </div>
+            <div style="bottom:0px; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
+                <FieldView {...props} fieldKey={"doc1"} />
+            </div>
+            <div style="bottom:0; left: 50%; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
+                <FieldView {...props} fieldKey={"doc2"} />
+            </div>
+        </div>`
     );
-    // export const Summary = new Template("Title", TemplatePosition.InnerTop,
-    //     `<div style="height:100%; width:100%;position:absolute; margin:0; padding: 0">
-    //         <div style="height:60%; width:100%;position:absolute;background:yellow; padding:0; margin:0">
-    //             {layout}
-    //         </div>
-    //         <div style="bottom:0px; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
-    //             <FieldView {...props} fieldKey={"doc1"} />
-    //         </div>
-    //         <div style="bottom:0; left: 50%; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
-    //             <FieldView {...props} fieldKey={"doc2"} />
-    //         </div>
-    //     </div>`
-    // );
 
     export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
 
-- 
cgit v1.2.3-70-g09d2


From 26141a697ae52a7edf3cc6845ce2153111f8860e Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 7 May 2019 15:26:29 -0400
Subject: added Bullet template

---
 src/client/views/DocumentDecorations.tsx             | 15 ++++++++++++---
 src/client/views/TemplateMenu.tsx                    | 20 +++++++++++++++-----
 src/client/views/Templates.tsx                       | 18 +++++-------------
 src/client/views/collections/CollectionBaseView.tsx  | 10 +++++++---
 .../views/nodes/CollectionFreeFormDocumentView.tsx   |  9 +++++----
 5 files changed, 44 insertions(+), 28 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index e2f445874..8ae71fdc8 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -507,9 +507,18 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         }
 
         let templates: Map<Template, boolean> = new Map();
-        let doc = SelectionManager.SelectedDocuments()[0];
         Array.from(Object.values(Templates.TemplateList)).map(template => {
-            let docTemps = doc.templates;
+            let docTemps = SelectionManager.SelectedDocuments().reduce((res: string[], doc: DocumentView, i) => {
+                let temps = doc.props.Document.templates;
+                if (temps instanceof List) {
+                    temps.map(temp => {
+                        if (temp !== Templates.Bullet.Layout || i === 0) {
+                            res.push(temp);
+                        }
+                    })
+                }
+                return res
+            }, [] as string[]);
             let checked = false;
             docTemps.forEach(temp => {
                 if (template.Layout === temp) {
@@ -560,7 +569,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                             <FontAwesomeIcon className="fa-icon-link" icon="link" size="sm" />
                         </div>
                     </div>
-                    <TemplateMenu doc={doc} templates={templates} />
+                    <TemplateMenu docs={SelectionManager.SelectedDocuments()} templates={templates} />
                 </div>
             </div >
         </div>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index f29d9c8a1..376feb5a5 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -4,6 +4,8 @@ import { observer } from "mobx-react";
 import './DocumentDecorations.scss';
 import { Template } from "./Templates";
 import { DocumentView } from "./nodes/DocumentView";
+import { List } from "../../new_fields/List";
+import { Doc } from "../../new_fields/Doc";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -25,24 +27,32 @@ class TemplateToggle extends React.Component<{ template: Template, checked: bool
 }
 
 export interface TemplateMenuProps {
-    doc: DocumentView;
+    docs: DocumentView[];
     templates: Map<Template, boolean>;
 }
 
 @observer
 export class TemplateMenu extends React.Component<TemplateMenuProps> {
-
     @observable private _hidden: boolean = true;
 
-
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => {
         if (event.target.checked) {
-            this.props.doc.addTemplate(template);
+            if (template.Name == "Bullet") {
+                this.props.docs[0].addTemplate(template);
+                this.props.docs[0].props.Document.maximizedDocs = new List<Doc>(this.props.docs.filter((v, i) => i !== 0).map(v => v.props.Document));
+            } else {
+                this.props.docs.map(d => d.addTemplate(template));
+            }
             this.props.templates.set(template, true);
             this.props.templates.forEach((checked, template) => console.log("Set Checked + " + checked + " " + this.props.templates.get(template)));
         } else {
-            this.props.doc.removeTemplate(template);
+            if (template.Name == "Bullet") {
+                this.props.docs[0].removeTemplate(template);
+                this.props.docs[0].props.Document.maximizedDocs = undefined;
+            } else {
+                this.props.docs.map(d => d.removeTemplate(template));
+            }
             this.props.templates.set(template, false);
             this.props.templates.forEach((checked, template) => console.log("Unset Checked + " + checked + " " + this.props.templates.get(template)));
         }
diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx
index 738272913..51fca4c41 100644
--- a/src/client/views/Templates.tsx
+++ b/src/client/views/Templates.tsx
@@ -54,21 +54,13 @@ export namespace Templates {
         `<div><div style="height:calc(100% - 25px); margin-top: 25px; width:100%;position:absolute;">{layout}</div>
         <div style="height:25px; width:100%; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px">{props.Document.title}</div></div>`
     );
-    export const Summary = new Template("Title", TemplatePosition.InnerTop,
-        `<div style="height:100%; width:100%;position:absolute; margin:0; padding: 0">
-            <div style="height:60%; width:100%;position:absolute;background:yellow; padding:0; margin:0">
-                {layout}
-            </div>
-            <div style="bottom:0px; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
-                <FieldView {...props} fieldKey={"doc1"} />
-            </div>
-            <div style="bottom:0; left: 50%; height:40%; width:50%; position:absolute; background-color: rgba(0, 0, 0, .4); color: white;">
-                <FieldView {...props} fieldKey={"doc2"} />
-            </div>
-        </div>`
+
+    export const Bullet = new Template("Bullet", TemplatePosition.InnerTop,
+        `<div><div style="height:100%; width:100%;position:absolute;">{layout}</div>
+        <div className="isBullet" style="height:25px; width:25px; margin-left:-25px; pointer-events:all; position:absolute; top: 0; background-color: rgba(0, 0, 0, .4); color: white; padding:2px 10px"/></div>`
     );
 
-    export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption];
+    export const TemplateList: Template[] = [Title, OuterCaption, InnerCaption, SideCaption, Bullet];
 
     export function sortTemplates(a: Template, b: Template) {
         if (a.Position < b.Position) { return -1; }
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx
index cbb568c07..14b92af48 100644
--- a/src/client/views/collections/CollectionBaseView.tsx
+++ b/src/client/views/collections/CollectionBaseView.tsx
@@ -95,15 +95,18 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         if (!this.createsCycle(doc, props.Document)) {
             //TODO This won't create the field if it doesn't already exist
             const value = Cast(props.Document[props.fieldKey], listSpec(Doc));
+            let alreadyAdded = true;
             if (value !== undefined) {
-                if (allowDuplicates || !value.some(v => v[Id] === doc[Id])) {
+                if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) {
+                    alreadyAdded = false;
                     value.push(doc);
                 }
             } else {
+                alreadyAdded = false;
                 Doc.SetOnPrototype(this.props.Document, this.props.fieldKey, new List([doc]));
             }
             // set the ZoomBasis only if hasn't already been set -- bcz: maybe set/resetting the ZoomBasis should be a parameter to addDocument?
-            if (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid) {
+            if (!alreadyAdded && (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid)) {
                 let zoom = NumCast(this.props.Document.scale, 1);
                 Doc.SetOnPrototype(doc, "zoomBasis", zoom);
             }
@@ -118,7 +121,8 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> {
         const value = Cast(props.Document[props.fieldKey], listSpec(Doc), []);
         let index = -1;
         for (let i = 0; i < value.length; i++) {
-            if (value[i][Id] === doc[Id]) {
+            let v = value[i];
+            if (v instanceof Doc && v[Id] === doc[Id]) {
                 index = i;
                 break;
             }
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index c4f85847a..df78d92e2 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -127,6 +127,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
     }
     @action
     public toggleIcon = async (): Promise<void> => {
+        UndoManager.GetOpenBatches().forEach(batch => console.log(batch.batchName));
         SelectionManager.DeselectAll();
         let isMinimized: boolean | undefined;
         let maximizedDocs = await DocListCast(this.props.Document.maximizedDocs);
@@ -144,8 +145,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     if (isMinimized === undefined) {
                         isMinimized = BoolCast(maximizedDoc.isMinimized, false);
                     }
-                    let minx = NumCast(minimizedTarget.x, undefined) + NumCast(minimizedTarget.width, undefined) / 2;
-                    let miny = NumCast(minimizedTarget.y, undefined) + NumCast(minimizedTarget.height, undefined) / 2;
+                    let minx = NumCast(minimizedTarget.x, undefined) + NumCast(minimizedTarget.width, undefined) * this.getTransform().Scale / 2;
+                    let miny = NumCast(minimizedTarget.y, undefined) + NumCast(minimizedTarget.height, undefined) * this.getTransform().Scale / 2;
                     let maxx = NumCast(maximizedDoc.x, undefined);
                     let maxy = NumCast(maximizedDoc.y, undefined);
                     let maxw = NumCast(maximizedDoc.width, undefined);
@@ -153,7 +154,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                     if (minx !== undefined && miny !== undefined && maxx !== undefined && maxy !== undefined &&
                         maxw !== undefined && maxh !== undefined) {
                         let scrpt = this.props.ScreenToLocalTransform().inverse().transformPoint(minx, miny);
-                        maximizedDoc.isMinimized = false;
+                        if (isMinimized) maximizedDoc.isMinimized = false;
                         maximizedDoc.isIconAnimating = new List<number>([scrpt[0], scrpt[1], maxx, maxy, maxw, maxh, Date.now(), isMinimized ? 1 : 0])
                     }
                 }
@@ -175,7 +176,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
         let altKey = e.altKey;
         if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            if (BoolCast(this.props.Document.isButton, false)) {
+            if (BoolCast(this.props.Document.isButton, false) || (e.target as any).className === "isBullet") {
                 let maximizedDocs = await DocListCast(this.props.Document.maximizedDocs);
                 if (maximizedDocs) {   // bcz: need a better way to associate behaviors with click events on widget-documents
                     if ((altKey && !this.props.Document.maximizeOnRight) || (!altKey && this.props.Document.maximizeOnRight)) {
-- 
cgit v1.2.3-70-g09d2

