From 3652890a4e10049511a6657cd6753aa3a64ef09e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 15 Aug 2019 18:51:58 -0400
Subject: fixed cross-fading of images

---
 src/client/views/nodes/ImageBox.tsx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 582a50637..708e00576 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -420,13 +420,13 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                         width={nativeWidth}
                         ref={this._imgRef}
                         onError={this.onError} />
-                    {fadepath === srcpath ? (null) : <img id="fadeaway" className="fadeaway"
+                    {fadepath === srcpath ? (null) : <div className="imageBox-fadeBlocker"> <img className="imageBox-fadeaway"
                         key={"fadeaway" + this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
                         src={fadepath}
                         style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }}
                         width={nativeWidth}
                         ref={this._imgRef}
-                        onError={this.onError} />}
+                        onError={this.onError} /></div>}
                 </div>
                 {paths.length > 1 ? this.dots(paths) : (null)}
                 <div className="imageBox-audioBackground"
-- 
cgit v1.2.3-70-g09d2


From 700dfc5add1ecd9c2b1ecafcdc593ff821b7a6a6 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 20 Aug 2019 17:20:27 -0400
Subject: UI enhancements and other backend fixes for templates.

---
 src/client/util/DragManager.ts                     |  3 +-
 src/client/views/MetadataEntryMenu.scss            | 12 +++++
 src/client/views/MetadataEntryMenu.tsx             | 52 +++++++++++---------
 .../views/collections/CollectionStackingView.tsx   |  3 +-
 .../CollectionStackingViewFieldColumn.tsx          |  9 ++--
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  3 ++
 .../views/collections/CollectionViewChromes.scss   |  1 -
 .../views/collections/CollectionViewChromes.tsx    | 57 +++++++++++-----------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 30 ++++++------
 src/client/views/nodes/DocumentContentsView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 25 +++++++---
 src/client/views/nodes/FormattedTextBox.tsx        | 12 ++---
 src/client/views/nodes/ImageBox.tsx                |  7 +--
 src/new_fields/Doc.ts                              | 11 +++--
 15 files changed, 131 insertions(+), 98 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 0b6d9b5e5..894b366ef 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -9,8 +9,6 @@ import { DocumentManager } from "./DocumentManager";
 import { LinkManager } from "./LinkManager";
 import { SelectionManager } from "./SelectionManager";
 import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
-import { DocumentDecorations } from "../views/DocumentDecorations";
-import { NumberLiteralType } from "typescript";
 
 export type dropActionType = "alias" | "copy" | undefined;
 export function SetupDrag(
@@ -211,6 +209,7 @@ export namespace DragManager {
         dropAction: dropActionType;
         userDropAction: dropActionType;
         moveDocument?: MoveFunction;
+        applyAsTemplate?: boolean;
         [id: string]: any;
     }
 
diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss
index bcfc9a82d..e28c4d0e0 100644
--- a/src/client/views/MetadataEntryMenu.scss
+++ b/src/client/views/MetadataEntryMenu.scss
@@ -1,8 +1,20 @@
 .metadataEntry-outerDiv {
     display: flex;
+    flex-direction: column;
     width: 300px;
 }
 
+.metadataEntry-keys {
+    max-height: 80;
+    overflow-y: auto; 
+    display: flex;
+    flex-direction: column;
+}
+.metadataEntry-inputArea {
+    display:flex;
+    flex-direction: row;
+}
+
 .react-autosuggest__container {
     position: relative;
 }
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index 36c240dd8..4a45eede9 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -1,11 +1,12 @@
 import * as React from 'react';
 import "./MetadataEntryMenu.scss";
 import { observer } from 'mobx-react';
-import { observable, action, runInAction, trace } from 'mobx';
+import { observable, action, runInAction, trace, computed, IReactionDisposer, reaction } from 'mobx';
 import { KeyValueBox } from './nodes/KeyValueBox';
 import { Doc, Field } from '../../new_fields/Doc';
 import * as Autosuggest from 'react-autosuggest';
 import { undoBatch } from '../util/UndoManager';
+import { emptyFunction } from '../../Utils';
 
 export type DocLike = Doc | Doc[] | Promise<Doc> | Promise<Doc[]>;
 export interface MetadataEntryProps {
@@ -18,7 +19,8 @@ export interface MetadataEntryProps {
 export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
     @observable private _currentKey: string = "";
     @observable private _currentValue: string = "";
-    @observable private suggestions: string[] = [];
+    @observable _allSuggestions: string[] = [];
+    _suggestionDispser: IReactionDisposer | undefined;
     private userModified = false;
 
     private autosuggestRef = React.createRef<Autosuggest>();
@@ -140,35 +142,39 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
     getSuggestionValue = (suggestion: string) => suggestion;
 
     renderSuggestion = (suggestion: string) => {
-        return <p>{suggestion}</p>;
+        return (null);
     }
+    componentDidMount() {
 
-    onSuggestionFetch = async ({ value }: { value: string }) => {
-        const sugg = await this.getKeySuggestions(value);
-        runInAction(() => {
-            this.suggestions = sugg;
-        });
+        this._suggestionDispser = reaction(() => this._currentKey,
+            () => this.getKeySuggestions(this._currentKey).then(action((s: string[]) => this._allSuggestions = s)),
+            { fireImmediately: true });
     }
-
-    @action
-    onSuggestionClear = () => {
-        this.suggestions = [];
+    componentWillUnmount() {
+        this._suggestionDispser && this._suggestionDispser();
     }
 
     render() {
         return (
             <div className="metadataEntry-outerDiv">
-                Key:
-                <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
-                    getSuggestionValue={this.getSuggestionValue}
-                    suggestions={this.suggestions}
-                    alwaysRenderSuggestions
-                    renderSuggestion={this.renderSuggestion}
-                    onSuggestionsFetchRequested={this.onSuggestionFetch}
-                    onSuggestionsClearRequested={this.onSuggestionClear}
-                    ref={this.autosuggestRef} />
-                Value:
-                <input className="metadataEntry-input" value={this._currentValue} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} />
+                <div className="metadataEntry-inputArea">
+                    Key:
+                    <Autosuggest inputProps={{ value: this._currentKey, onChange: this.onKeyChange }}
+                        getSuggestionValue={this.getSuggestionValue}
+                        suggestions={[]}
+                        alwaysRenderSuggestions={false}
+                        renderSuggestion={this.renderSuggestion}
+                        onSuggestionsFetchRequested={emptyFunction}
+                        onSuggestionsClearRequested={emptyFunction}
+                        ref={this.autosuggestRef} />
+                    Value:
+                    <input className="metadataEntry-input" value={this._currentValue} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} />
+                </div>
+                <div className="metadataEntry-keys" >
+                    <ul>
+                        {this._allSuggestions.map(s => <li key={s} onClick={action(() => { this._currentKey = s; this.previewValue(); })} >{s}</li>)}
+                    </ul>
+                </div>
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 1f7ef0689..c74c60d8f 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -160,7 +160,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
             previewScript={undefined}>
         </CollectionSchemaPreview>;
     }
-    getDocHeight(d: Doc) {
+    getDocHeight(d?: Doc) {
+        if (!d) return 0;
         let nw = NumCast(d.nativeWidth);
         let nh = NumCast(d.nativeHeight);
         if (!d.ignoreAspect && nw && nh) {
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 74c7ef305..cc8476548 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -78,11 +78,14 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
         let parent = this.props.parent;
         parent._docXfs.length = 0;
         return docs.map((d, i) => {
-            let pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
+            const pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d);
+            if (!pair.layout || pair.data instanceof Promise) {
+                return (null);
+            }
             let width = () => Math.min(d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, parent.columnWidth / parent.numGroupColumns);
-            let height = () => parent.getDocHeight(pair.layout);
+            let height = () => parent.getDocHeight(pair!.layout);
             let dref = React.createRef<HTMLDivElement>();
-            let dxf = () => this.getDocTransform(pair.layout, dref.current!);
+            let dxf = () => this.getDocTransform(pair!.layout, dref.current!);
             this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height });
             let rowSpan = Math.ceil((height() + parent.gridGap) / parent.gridGap);
             let style = parent.isStackingView ? { width: width(), margin: "auto", marginTop: i === 0 ? 0 : parent.gridGap, height: height() } : { gridRowEnd: `span ${rowSpan}` };
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index cfe3fefb3..818e76619 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -113,7 +113,7 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
         @undoBatch
         @action
         protected drop(e: Event, de: DragManager.DropEvent): boolean {
-            if (de.data instanceof DragManager.DocumentDragData) {
+            if (de.data instanceof DragManager.DocumentDragData && !de.data.applyAsTemplate) {
                 if (de.mods === "AltKey" && de.data.draggedDocuments.length) {
                     this.childDocs.map(doc =>
                         Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, undefined)
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index b6a14238e..ebd385743 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -443,6 +443,9 @@ class TreeView extends React.Component<TreeViewProps> {
         let rowWidth = () => panelWidth() - 20;
         return docs.map((child, i) => {
             let pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, key, child);
+            if (!pair.layout || pair.data instanceof Promise) {
+                return (null);
+            }
 
             let indent = i === 0 ? undefined : () => {
                 if (StrCast(docs[i - 1].layout).indexOf("CollectionView") !== -1) {
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index 595f079d1..f39bd877a 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -7,7 +7,6 @@
     z-index: 9001;
     transition: top .5s;
     background: lightgrey;
-    padding: 10px;
 
     .collectionViewChrome {
         display: grid;
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index b92bcc7cb..25b152d4e 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -1,30 +1,25 @@
-import * as React from "react";
-import { CollectionView } from "./CollectionView";
-import "./CollectionViewChromes.scss";
-import { CollectionViewType } from "./CollectionBaseView";
-import { undoBatch } from "../../util/UndoManager";
-import { action, observable, runInAction, computed, IObservable, IObservableValue, reaction, autorun } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast, FieldResult } from "../../../new_fields/Doc";
-import { DocLike } from "../MetadataEntryMenu";
-import * as Autosuggest from 'react-autosuggest';
-import { EditableView } from "../EditableView";
-import { StrCast, NumCast, BoolCast, Cast } from "../../../new_fields/Types";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { action, computed, observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import { Doc, DocListCast } from "../../../new_fields/Doc";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { List } from "../../../new_fields/List";
+import { listSpec } from "../../../new_fields/Schema";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types";
 import { Utils } from "../../../Utils";
-import KeyRestrictionRow from "./KeyRestrictionRow";
+import { DragManager } from "../../util/DragManager";
 import { CompileScript } from "../../util/Scripting";
-import { ScriptField } from "../../../new_fields/ScriptField";
-import { CollectionSchemaView } from "./CollectionSchemaView";
+import { undoBatch } from "../../util/UndoManager";
+import { EditableView } from "../EditableView";
 import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss";
-import { listSpec } from "../../../new_fields/Schema";
-import { List } from "../../../new_fields/List";
-import { Id } from "../../../new_fields/FieldSymbols";
-import { threadId } from "worker_threads";
-import { DragManager } from "../../util/DragManager";
+import { DocLike } from "../MetadataEntryMenu";
+import { CollectionViewType } from "./CollectionBaseView";
+import { CollectionView } from "./CollectionView";
+import "./CollectionViewChromes.scss";
+import KeyRestrictionRow from "./KeyRestrictionRow";
 const datepicker = require('js-datepicker');
-import * as $ from 'jquery';
-import { firebasedynamiclinks } from "googleapis/build/src/apis/firebasedynamiclinks";
 
 interface CollectionViewChromeProps {
     CollectionView: CollectionView;
@@ -51,7 +46,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); }
 
     private _picker: any;
-    private _datePickerElGuid = Utils.GenerateGuid();
 
     getFilters = (script: string) => {
         let re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g;
@@ -91,11 +85,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
     }
 
     componentDidMount = () => {
-        setTimeout(() => this._picker = datepicker("#" + this._datePickerElGuid, {
-            disabler: (date: Date) => date > new Date(),
-            onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
-            dateSelected: new Date()
-        }), 1000);
 
         let fields: Filter[] = [];
         if (this.filterValue) {
@@ -306,6 +295,15 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         return true;
     }
 
+    datePickerRef = (node: HTMLInputElement) => {
+        if (node) {
+            this._picker = datepicker("#" + node.id, {
+                disabler: (date: Date) => date > new Date(),
+                onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
+                dateSelected: new Date()
+            });
+        }
+    }
     render() {
         let collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled";
         return (
@@ -366,7 +364,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                                         <option value="1y">1 year of</option>
                                     </select>
                                     <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
-                                        id={this._datePickerElGuid}
+                                        id={Utils.GenerateGuid()}
+                                        ref={this.datePickerRef}
                                         value={this._dateValue instanceof Date ? this._dateValue.toLocaleDateString() : this._dateValue}
                                         onChange={(e) => runInAction(() => this._dateValue = e.target.value)}
                                         onPointerDown={this.openDatePicker}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d69ece421..3be6aa3d3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -622,20 +622,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     getScale = () => this.Document.scale ? this.Document.scale : 1;
 
-    getChildDocumentViewProps(childDocLayout: Doc): DocumentViewProps {
-        let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, childDocLayout);
+    getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps {
         return {
-            DataDoc: pair.data,
-            Document: pair.layout,
+            DataDoc: childData,
+            Document: childLayout,
             addDocument: this.props.addDocument,
             removeDocument: this.props.removeDocument,
             moveDocument: this.props.moveDocument,
             onClick: this.props.onClick,
-            ScreenToLocalTransform: pair.layout.z ? this.getTransformOverlay : this.getTransform,
+            ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform,
             renderDepth: this.props.renderDepth + 1,
-            selectOnLoad: pair.layout[Id] === this._selectOnLoaded,
-            PanelWidth: pair.layout[WidthSym],
-            PanelHeight: pair.layout[HeightSym],
+            selectOnLoad: childLayout[Id] === this._selectOnLoaded,
+            PanelWidth: childLayout[WidthSym],
+            PanelHeight: childLayout[HeightSym],
             ContentScaling: returnOne,
             ContainingCollectionView: this.props.CollectionView,
             focus: this.focusDocument,
@@ -745,12 +744,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
                     const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) :
                         { x: Cast(doc.x, "number"), y: Cast(doc.y, "number"), z: Cast(doc.z, "number"), width: Cast(doc.width, "number"), height: Cast(doc.height, "number") };
                     state = pos.state === undefined ? state : pos.state;
-                    prev.push({
-                        ele: <CollectionFreeFormDocumentView key={doc[Id]}
-                            x={script ? pos.x : undefined} y={script ? pos.y : undefined}
-                            width={script ? pos.width : undefined} height={script ? pos.height : undefined} {...this.getChildDocumentViewProps(doc)} />,
-                        bounds: (pos.x !== undefined && pos.y !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) } : undefined
-                    });
+                    let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, doc);
+                    if (pair.layout && !(pair.data instanceof Promise)) {
+                        prev.push({
+                            ele: <CollectionFreeFormDocumentView key={doc[Id]}
+                                x={script ? pos.x : undefined} y={script ? pos.y : undefined}
+                                width={script ? pos.width : undefined} height={script ? pos.height : undefined} {...this.getChildDocumentViewProps(pair.layout, pair.data)} />,
+                            bounds: (pos.x !== undefined && pos.y !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) } : undefined
+                        });
+                    }
                 }
             }
             return prev;
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index b901bdcfb..d77662355 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -68,7 +68,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
     }
 
     get dataDoc() {
-        if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) {
+        if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) {
             // if there is no dataDoc (ie, we're not rendering a template layout), but this document
             // has a template layout document, then we will render the template layout but use 
             // this document as the data document for the layout.
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9e71094b0..fd53262d7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -139,6 +139,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     private _lastTap: number = 0;
     private _doubleTap = false;
     private _hitExpander = false;
+    private _hitTemplateDrag = false;
     private _mainCont = React.createRef<HTMLDivElement>();
     private _dropDisposer?: DragManager.DragDropDisposer;
     _animateToIconDisposer?: IReactionDisposer;
@@ -228,7 +229,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
     }
 
     get dataDoc() {
-        if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) {
+        if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) {
             // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document
             // has a template layout document, then we will render the template layout but use 
             // this document as the data document for the layout.
@@ -236,7 +237,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         }
         return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
     }
-    startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) {
+    startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean, applyAsTemplate?: boolean) {
         if (this._mainCont.current) {
             let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
             let alldataConnected = [this.dataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])];
@@ -247,6 +248,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             dragData.xOffset = xoff;
             dragData.yOffset = yoff;
             dragData.moveDocument = this.props.moveDocument;
+            dragData.applyAsTemplate = applyAsTemplate;
             DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
                 handlers: {
                     dragComplete: action(emptyFunction)
@@ -377,15 +379,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
         }
     }
+
+
     onPointerDown = (e: React.PointerEvent): void => {
         if (e.nativeEvent.cancelBubble) return;
         this._downX = e.clientX;
         this._downY = e.clientY;
         this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0;
-        // if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) {
-        // CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.dataDoc]);
-        // e.stopPropagation();
-        // } else {
+        this._hitTemplateDrag = false;
+        for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) {
+            if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") {
+                this._hitTemplateDrag = true;
+            }
+        }
         if (this.active) e.stopPropagation(); // events stop at the lowest document that is active.  
         document.removeEventListener("pointermove", this.onPointerMove);
         document.addEventListener("pointermove", this.onPointerMove);
@@ -399,7 +405,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) {
                     document.removeEventListener("pointermove", this.onPointerMove);
                     document.removeEventListener("pointerup", this.onPointerUp);
-                    this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander);
+                    this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander, this._hitTemplateDrag);
                 }
             }
             e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
@@ -457,6 +463,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
             DocUtils.MakeLink(annotationDoc, targetDoc, this.props.ContainingCollectionView!.props.Document, `Link from ${StrCast(annotationDoc.title)}`);
         }
+        if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) {
+            Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document, this.props.DataDoc);
+            e.stopPropagation();
+        }
         if (de.data instanceof DragManager.LinkDragData) {
             let sourceDoc = de.data.linkSourceDocument;
             let destDoc = this.props.Document;
@@ -679,7 +689,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             cm.addItem({ description: "Share...", subitems: usersMenu, icon: "share" });
             if (!ClientUtils.RELEASE) {
                 let setWriteMode = (mode: DocServer.WriteMode) => {
-                    console.log(DocServer.WriteMode[mode]);
                     DocServer.AclsMode = mode;
                     const mode1 = mode;
                     const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index f7890e5a6..0e347ca67 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -167,7 +167,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
             FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = false);
             if (state.selection.empty && FormattedTextBox._toolTipTextMenu) {
                 const marks = tx.storedMarks;
-                console.log(marks);
                 if (marks) { FormattedTextBox._toolTipTextMenu.mark_key_pressed(marks); }
             }
             this._applyingChange = true;
@@ -296,10 +295,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
                 this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field2)))
         );
 
-        this.props.isOverlay && (this._heightReactionDisposer = reaction(
+        this._heightReactionDisposer = reaction(
             () => this.props.Document[WidthSym](),
             () => this.tryUpdateHeight()
-        ));
+        );
 
         this._textReactionDisposer = reaction(
             () => this.extensionDoc,
@@ -448,8 +447,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
         if (this.props.selectOnLoad) {
             if (!this.props.isOverlay) this.props.select(false);
             else this._editorView!.focus();
-            this.tryUpdateHeight();
         }
+        this.tryUpdateHeight();
     }
 
     componentWillUnmount() {
@@ -610,9 +609,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
 
     @action
     tryUpdateHeight() {
-        if (this.props.Document.autoHeight && this.props.isOverlay) {
+        if (this.props.Document.autoHeight && this._ref.current!.scrollHeight !== 0) {
+            console.log("DT = " + this.props.Document.title + " " + this._ref.current!.clientHeight + " " + this._ref.current!.scrollHeight + " " + this._ref.current!.textContent)
             let xf = this._ref.current!.getBoundingClientRect();
-            let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, xf.height);
+            let scrBounds = this.props.ScreenToLocalTransform().transformBounds(0, 0, xf.width, this._ref.current!.textContent === "" ? 35 : this._ref.current!.scrollHeight);
             let nh = this.props.Document.isTemplate ? 0 : NumCast(this.dataDoc.nativeHeight, 0);
             let dh = NumCast(this.props.Document.height, 0);
             let sh = scrBounds.height;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 708e00576..6fc94a140 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -89,10 +89,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData) {
             de.data.droppedDocuments.forEach(action((drop: Doc) => {
-                if (de.mods === "CtrlKey") {
-                    Doc.ApplyTemplateTo(drop, this.props.Document, this.props.DataDoc);
-                    e.stopPropagation();
-                } else if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) {
+                if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) {
                     Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(drop.data.url);
                     e.stopPropagation();
                 } else if (de.mods === "MetaKey") {
@@ -321,7 +318,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
                 let rotation = NumCast(this.dataDoc.rotation) % 180;
                 let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
                 let aspect = realsize.height / realsize.width;
-                if (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1) {
+                if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1)) {
                     setTimeout(action(() => {
                         layoutdoc.height = layoutdoc[WidthSym]() * aspect;
                         layoutdoc.nativeHeight = realsize.height;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 31f1f7a12..ca05dfa45 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -446,20 +446,23 @@ export namespace Doc {
         if (expandedTemplateLayout instanceof Doc) {
             return expandedTemplateLayout;
         }
+        if (expandedTemplateLayout instanceof Promise) {
+            return undefined;
+        }
         let expandedLayoutFieldKey = "Layout[" + templateLayoutDoc[Id] + "]";
         expandedTemplateLayout = dataDoc[expandedLayoutFieldKey];
         if (expandedTemplateLayout instanceof Doc) {
             return expandedTemplateLayout;
         }
         if (expandedTemplateLayout === undefined) {
-            setTimeout(() =>
-                dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]"), 0);
+            setTimeout(() => dataDoc[expandedLayoutFieldKey] === undefined &&
+                (dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]")), 0);
         }
-        return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending.
+        return undefined; // use the templateLayout when it's not a template or the expandedTemplate is pending.
     }
 
     export function GetLayoutDataDocPair(doc: Doc, dataDoc: Doc | undefined, fieldKey: string, childDocLayout: Doc) {
-        let layoutDoc = childDocLayout;
+        let layoutDoc: Doc | undefined = childDocLayout;
         let resolvedDataDoc = !doc.isTemplate && dataDoc !== doc && dataDoc ? Doc.GetDataDoc(dataDoc) : undefined;
         if (resolvedDataDoc && Doc.WillExpandTemplateLayout(childDocLayout, resolvedDataDoc)) {
             Doc.UpdateDocumentExtensionForField(resolvedDataDoc, fieldKey);
-- 
cgit v1.2.3-70-g09d2

