From 3afceef2f8c2be1bddf67cecd97086a41ec6dc48 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 11 Oct 2019 16:47:42 -0400
Subject: changes to menu layouts

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

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

diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
index 6c3db18c4..8429382e3 100644
--- a/src/client/views/nodes/DragBox.tsx
+++ b/src/client/views/nodes/DragBox.tsx
@@ -52,7 +52,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
             e.stopPropagation();
             e.preventDefault();
             let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result;
-            let doc = (res as Doc) ||  Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
+            let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
             DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
         }
         e.stopPropagation();
@@ -93,7 +93,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
 
     render() {
         return (<div className="dragBox-outerDiv" onContextMenu={this.onContextMenu} onPointerDown={this.onDragStart} ref={this._mainCont}>
-            <FontAwesomeIcon className="dragBox-icon" icon="folder" size="lg" color="white" />
+            <FontAwesomeIcon className="dragBox-icon" icon={this.props.Document.icon as any} size="lg" color="white" />
         </div>);
     }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From c03012fe8c9b77ddf3b7e6c589339eb9a0d967e6 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 12 Oct 2019 01:33:20 -0400
Subject: working version of new docked sidebar + configurable bottom buttons 
 by drag/drop

---
 src/client/views/GlobalKeyHandler.ts               |  18 ++-
 src/client/views/Main.scss                         |  13 +-
 src/client/views/MainView.tsx                      | 157 ++++++++++-----------
 .../views/collections/CollectionTreeView.tsx       |   2 +-
 .../nodes/CollectionFreeFormDocumentView.scss      |   2 +
 src/client/views/nodes/DocumentView.tsx            |   1 +
 src/client/views/nodes/DragBox.tsx                 |  12 +-
 src/client/views/nodes/PDFBox.tsx                  |   2 +-
 .../authentication/models/current_user_utils.ts    |  45 +++---
 9 files changed, 126 insertions(+), 126 deletions(-)

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

diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 9d239d0bf..82f5a573c 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -164,29 +164,27 @@ export default class KeyManager {
                 }
                 break;
             case "c":
-                if (MainView.Instance.flyoutWidth > 0) {
+                PromiseValue(Cast(CurrentUserUtils.UserDocument.Create, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                if (MainView.Instance.flyoutWidth === 75) {
                     MainView.Instance.flyoutWidth = 0;
-                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 } else {
-                    MainView.Instance.flyoutWidth = 400;
-                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Create, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                    MainView.Instance.flyoutWidth = 75;
                 }
                 break;
             case "l":
-                if (MainView.Instance.flyoutWidth > 0) {
+                PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                if (MainView.Instance.flyoutWidth === 250) {
                     MainView.Instance.flyoutWidth = 0;
                 } else {
-                    MainView.Instance.flyoutWidth = 400;
-                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                    MainView.Instance.flyoutWidth = 250;
                 }
                 break;
             case "f":
-                if (MainView.Instance.flyoutWidth > 0) {
+                PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+                if (MainView.Instance.flyoutWidth === 400) {
                     MainView.Instance.flyoutWidth = 0;
-                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Library, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 } else {
                     MainView.Instance.flyoutWidth = 400;
-                    PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 }
                 break;
             case "o":
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index 4cd860da2..705da7b35 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -211,14 +211,21 @@ button:hover {
 }
 
 #add-options-content {
-    display: table;
+    display: flex;
     opacity: 1;
     margin: 0;
     padding: 0;
     position: relative;
     float: right;
-    bottom: 0.3em;
-    margin-bottom: -1.68em;
+    .mainView-docBtn {
+        position:relative;
+        margin-right: 10px;
+        width: 35px;
+        height: 35px;
+    }
+    .collectionFreeFormDocumentView-container {
+        position: relative;
+    }
 }
 
 ul#add-options-list {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6f60de1c4..1fa231659 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
 import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV } from '@fortawesome/free-solid-svg-icons';
+import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -12,15 +12,16 @@ import { Id } from '../../new_fields/FieldSymbols';
 import { InkTool } from '../../new_fields/InkField';
 import { List } from '../../new_fields/List';
 import { listSpec } from '../../new_fields/Schema';
-import { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../new_fields/Types';
+import { ScriptField } from '../../new_fields/ScriptField';
+import { BoolCast, Cast, FieldValue, PromiseValue, StrCast, NumCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
-import { emptyFunction, returnEmptyString, returnOne, returnTrue, returnFalse, Utils } from '../../Utils';
+import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils';
+import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
-import { ClientUtils } from '../util/ClientUtils';
 import { DictationManager } from '../util/DictationManager';
-import { SetupDrag } from '../util/DragManager';
+import { SetupDrag, DragManager } from '../util/DragManager';
 import { HistoryUtil } from '../util/History';
 import SharingManager from '../util/SharingManager';
 import { Transform } from '../util/Transform';
@@ -35,13 +36,10 @@ import { InkingControl } from './InkingControl';
 import "./Main.scss";
 import MainViewModal from './MainViewModal';
 import { DocumentView } from './nodes/DocumentView';
+import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
-import { FilterBox } from './search/FilterBox';
-import { OverlayView } from './OverlayView';
-import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
-import { CollectionStackingView } from './collections/CollectionStackingView';
-import { ScriptField } from '../../new_fields/ScriptField';
+import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView';
 
 @observer
 export class MainView extends React.Component {
@@ -49,6 +47,7 @@ export class MainView extends React.Component {
     @observable addMenuToggle = React.createRef<HTMLInputElement>();
     @observable public pwidth: number = 0;
     @observable public pheight: number = 0;
+    private dropDisposer?: DragManager.DragDropDisposer;
 
     @observable private dictationState = DictationManager.placeholder;
     @observable private dictationSuccessState: boolean | undefined = undefined;
@@ -164,6 +163,7 @@ export class MainView extends React.Component {
         //close presentation 
         window.removeEventListener("pointerdown", this.globalPointerDown);
         window.removeEventListener("pointerup", this.globalPointerUp);
+        this.dropDisposer && this.dropDisposer();
     }
 
     constructor(props: Readonly<{}>) {
@@ -287,18 +287,15 @@ export class MainView extends React.Component {
         let workspaces: FieldResult<Doc>;
         let freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] };
-        let mainDoc = Docs.Create.DockDocument([this.userDoc, freeformDoc], JSON.stringify(dockingLayout), {}, id);
+        let mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), {}, id);
         if (this.userDoc && ((workspaces = Cast(this.userDoc.workspaces, Doc)) instanceof Doc)) {
-            const list = Cast((workspaces).data, listSpec(Doc));
-            if (list) {
-                if (!this.userDoc.linkManagerDoc) {
-                    let linkManagerDoc = new Doc();
-                    linkManagerDoc.allLinks = new List<Doc>([]);
-                    this.userDoc.linkManagerDoc = linkManagerDoc;
-                }
-                list.push(mainDoc);
-                mainDoc.title = `Workspace ${list.length}`;
+            if (!this.userDoc.linkManagerDoc) {
+                let linkManagerDoc = new Doc();
+                linkManagerDoc.allLinks = new List<Doc>([]);
+                this.userDoc.linkManagerDoc = linkManagerDoc;
             }
+            Doc.AddDocToList(workspaces, "data", mainDoc);
+            mainDoc.title = `Workspace ${DocListCast(workspaces.data).length}`;
         }
         // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container)
         setTimeout(() => {
@@ -350,6 +347,10 @@ export class MainView extends React.Component {
         return true;
     }
 
+    drop = action((e: Event, de: DragManager.DropEvent) => {
+        (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc));
+    })
+
     onDrop = (e: React.DragEvent<HTMLDivElement>) => {
         e.preventDefault();
         e.stopPropagation();
@@ -460,13 +461,14 @@ export class MainView extends React.Component {
     }
     @computed
     get flyout() {
-        let sidebar: FieldResult<Field>;
-        if (!this.userDoc || !((sidebar = this.userDoc.sidebarContainer) instanceof Doc)) {
+        let sidebarContent = this.userDoc && this.userDoc.sidebarContainer;
+        if (!(sidebarContent instanceof Doc)) {
             return (null);
         }
         (Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc).columnWidth = this.flyoutWidthFunc() / 3 - 30;
+        let buttonBarHeight = 85;
         return <div style={{ display: "flex", flexDirection: "column", position: "absolute", width: "100%", height: "100%" }}>
-            <div style={{ position: "relative", height: "85px", width: "100%" }}>
+            <div style={{ position: "relative", height: `${buttonBarHeight}px`, width: "100%" }}>
                 <DocumentView
                     Document={Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc}
                     DataDoc={undefined}
@@ -492,9 +494,9 @@ export class MainView extends React.Component {
                     getScale={returnOne}>
                 </DocumentView>
             </div>
-            <div style={{ position: "relative", height: "calc(100% - 80px)", width: "100%" }}>
+            <div style={{ position: "relative", height: `calc(100% - ${buttonBarHeight}px)`, width: "100%", overflow: "auto" }}>
                 <DocumentView
-                    Document={this.isSearchVisible ? Cast(CurrentUserUtils.UserDocument.searchBox, Doc) as Doc : sidebar}
+                    Document={sidebarContent}
                     DataDoc={undefined}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
@@ -598,65 +600,69 @@ export class MainView extends React.Component {
         DocServer.setFieldWriteMode("viewType", mode2);
     }
 
+    protected createDropTarget = (ele: HTMLLabelElement) => { //used for stacking and masonry view
+        this.dropDisposer && this.dropDisposer();
+        if (ele) {
+            this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+        }
+    }
 
     @observable private _colorPickerDisplay = false;
     /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
     nodesMenu() {
-        let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg";
-
-        let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" }));
-        let addPresNode = action(() => Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { width: 200, height: 500, title: "a presentation trail" }));
-        let addWebNode = action(() => Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" }));
-        let addDragboxNode = action(() => Docs.Create.DragboxDocument({ width: 40, height: 40, title: "drag collection" }));
-        let addImageNode = action(() => Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
-        let addButtonDocument = action(() => Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" }));
-        let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 }));
         // let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw";
         // let addYoutubeSearcher = action(() => Docs.Create.YoutubeDocument(youtubeurl, { width: 600, height: 600, title: "youtube search" }));
 
         // let googlePhotosSearch = () => GooglePhotosClientUtils.CollectionFromSearch(Docs.Create.MasonryDocument, { included: [GooglePhotosClientUtils.ContentCategories.LANDSCAPES] });
 
-        let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc | Promise<Doc>][] = [
-            //[React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
-            //[React.createRef<HTMLDivElement>(), "tv", "Add Presentation Trail", addPresNode],
-            //[React.createRef<HTMLDivElement>(), "globe-asia", "Add Website", addWebNode],
-            //[React.createRef<HTMLDivElement>(), "bolt", "Add Button", addButtonDocument],
-            //[React.createRef<HTMLDivElement>(), "file", "Add Document Dragger", addDragboxNode],
-            // [React.createRef<HTMLDivElement>(), "object-group", "Test Google Photos Search", googlePhotosSearch],
-            //[React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], //remove at some point in favor of addImportCollectionNode
-            //[React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher],
-        ];
-        //if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]);
-
         return < div id="add-nodes-menu" style={{ left: (this.flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} >
 
             <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
-            <label htmlFor="add-menu-toggle" style={{ marginTop: 2 }} title="Close Menu"><p>+</p></label>
+            <label htmlFor="add-menu-toggle" ref={this.createDropTarget} title="Close Menu"><p>+</p></label>
 
             <div id="add-options-content">
-                <ul id="add-options-list">
-                    {/* <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li> */}
-                    <li key="undo"><button className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button></li>
-                    <li key="redo"><button className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button></li>
-                    {btns.map(btn =>
-                        <li key={btn[1]} ><div ref={btn[0]}>
-                            <button className="round-button add-button" title={btn[2]} onPointerDown={SetupDrag(btn[0], btn[3])}>
-                                <FontAwesomeIcon icon={btn[1]} size="sm" />
-                            </button>
-                        </div></li>)}
-                    {/* <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li> */}
-                    <li key="color"><button className="add-button round-button" title="Select Color" style={{ zIndex: 1000 }} onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
-                        <div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}>
-                            <SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
-                        </div>
-                    </div></button></li>
-                    <li key="ink" style={{ paddingRight: "6px" }}><button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /> </button></li>
-                    <li key="pen"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Pen)} title="Pen" style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" /></button></li>
-                    <li key="marker"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button></li>
-                    <li key="eraser"><button onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button></li>
-                    <li key="inkControls"><InkingControl /></li>
-                    <li key="logout"><button onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>{CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}</button></li>
-                </ul>
+                {/* <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li> */}
+                <button key="undo" className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button>
+                <button key="redo" className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button>
+
+                {DocListCast(CurrentUserUtils.UserDocument.docButtons).map(doc => <div className="mainView-docBtn" key={StrCast(doc.title)} >
+                    <DocumentView
+                        Document={doc}
+                        DataDoc={undefined}
+                        addDocument={undefined}
+                        addDocTab={this.addDocTabFunc}
+                        pinToPres={emptyFunction}
+                        removeDocument={undefined}
+                        ruleProvider={undefined}
+                        onClick={undefined}
+                        ScreenToLocalTransform={Transform.Identity}
+                        ContentScaling={() => 35 / NumCast(doc.nativeWidth, 35)}
+                        PanelWidth={() => 35}
+                        PanelHeight={() => 35}
+                        renderDepth={0}
+                        focus={emptyFunction}
+                        backgroundColor={returnEmptyString}
+                        parentActive={returnTrue}
+                        whenActiveChanged={emptyFunction}
+                        bringToFront={emptyFunction}
+                        ContainingCollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                        zoomToScale={emptyFunction}
+                        getScale={returnOne}>
+                    </DocumentView>
+                </div>)}
+                {/* <li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li> */}
+                <button key="color" className="add-button round-button" title="Select Color" style={{ zIndex: 1000 }} onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
+                    <div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}>
+                        <SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
+                    </div>
+                </div></button>
+                <button className="toolbar-button round-button" title="Ink" onClick={() => InkingControl.Instance.toggleDisplay()}><FontAwesomeIcon icon="pen-nib" size="sm" /> </button>
+                <button key="pen" onClick={() => InkingControl.Instance.switchTool(InkTool.Pen)} title="Pen" style={this.selected(InkTool.Pen)}><FontAwesomeIcon icon="pen" size="lg" /></button>
+                <button key="marker" onClick={() => InkingControl.Instance.switchTool(InkTool.Highlighter)} title="Highlighter" style={this.selected(InkTool.Highlighter)}><FontAwesomeIcon icon="highlighter" size="lg" /></button>
+                <button key="eraser" onClick={() => InkingControl.Instance.switchTool(InkTool.Eraser)} title="Eraser" style={this.selected(InkTool.Eraser)}><FontAwesomeIcon icon="eraser" size="lg" /></button>
+                <InkingControl key="inkControls" />
+                <button key="logout" onClick={() => window.location.assign(Utils.prepend(RouteStore.logout))}>{CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}</button>
             </div>
         </div >;
     }
@@ -668,16 +674,6 @@ export class MainView extends React.Component {
         this._colorPickerDisplay = close ? false : !this._colorPickerDisplay;
     }
 
-    /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */
-    @computed
-    get miscButtons() {
-        return [
-            //this.isSearchVisible ? <div className="main-searchDiv" key="search" style={{ top: '34px', right: '1px', position: 'absolute' }} > <FilterBox /> </div> : null,
-        ];
-
-    }
-
-    @observable isSearchVisible = false;
     @action.bound
     toggleSearch = () => {
         PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
@@ -716,7 +712,6 @@ export class MainView extends React.Component {
                 <PreviewCursor />
                 <ContextMenu />
                 {this.nodesMenu()}
-                {this.miscButtons}
                 <PDFMenu />
                 <OverlayView />
             </div >
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 6f5587b5a..a325e86c6 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -188,7 +188,7 @@ class TreeView extends React.Component<TreeViewProps> {
 
     onWorkspaceContextMenu = (e: React.MouseEvent): void => {
         if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
-            if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) {
+            if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking && this.props.document !== CurrentUserUtils.UserDocument.workspaces) {
                 ContextMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.document), icon: "tv" });
                 ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" });
                 ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" });
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.scss b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
index c0d9e1267..af9232c2f 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.scss
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.scss
@@ -2,4 +2,6 @@
     transform-origin: left top;
     position: absolute;
     background-color: transparent;
+    top:0;
+    left:0;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 758222e52..20920a9b8 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -614,6 +614,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             DataDoc={this.props.DataDoc} />);
     }
     render() {
+        if (!this.props.Document) return (null);
         let animDims = this.props.Document.animateToDimensions ? Array.from(Cast(this.props.Document.animateToDimensions, listSpec("number"))!) : undefined;
         const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined;
         const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
index 8429382e3..ab3368b94 100644
--- a/src/client/views/nodes/DragBox.tsx
+++ b/src/client/views/nodes/DragBox.tsx
@@ -51,9 +51,15 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
             const onDragStart = this.Document.onDragStart;
             e.stopPropagation();
             e.preventDefault();
-            let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result;
-            let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
-            DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY);
+            DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([this.props.Document]), e.clientX, e.clientY, {
+                finishDrag: (dropData) => {
+                    let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result;
+                    let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
+                    dropData.droppedDocuments = [doc];
+                },
+                handlers: { dragComplete: emptyFunction },
+                hideSource: false
+            });
         }
         e.stopPropagation();
         e.preventDefault();
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 02a82e0ed..63b412a23 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -93,7 +93,7 @@ export class PDFBox extends DocAnnotatableComponent<FieldViewProps, PdfDocument>
         if (e.key === "PageUp" || e.key === "ArrowUp" || e.key === "ArrowLeft") {
             this.backPage();
         }
-    })
+    });
 
     @undoBatch
     @action
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index e1bb91838..7408fd92b 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -127,46 +127,38 @@ export class CurrentUserUtils {
             Search.targetContainer = doc.sidebarContainer;
             Search.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.searchBox");
 
-            let createCollection = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Collection", icon: "folder" });
-            let createWebPage = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Web Page", icon: "globe-asia" });
+            let createCollection = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Collection", icon: "folder" });
+            let createWebPage = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Web Page", icon: "globe-asia" });
             createWebPage.onDragStart = ScriptField.MakeFunction('Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })');
-            let createCatImage = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Image", icon: "cat" });
+            let createCatImage = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Image", icon: "cat" });
             createCatImage.onDragStart = ScriptField.MakeFunction('Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })');
-            let createButton = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Button", icon: "bolt" });
+            let createButton = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Button", icon: "bolt" });
             createButton.onDragStart = ScriptField.MakeFunction('Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })');
-            let createPresentation = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Presentation", icon: "tv" });
+            let createPresentation = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Presentation", icon: "tv" });
             createPresentation.onDragStart = ScriptField.MakeFunction('Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { width: 200, height: 500, title: "a presentation trail" })');
-            let createFolderImport = Docs.Create.DragboxDocument({ width: 35, height: 35, title: "Import Folder", icon: "cloud-upload-alt" });
+            let createFolderImport = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Import Folder", icon: "cloud-upload-alt" });
             createFolderImport.onDragStart = ScriptField.MakeFunction('Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })');
             const creators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, height: 50, columnWidth: 35, chromeStatus: "disabled", title: "buttons" });
             Create.targetContainer = doc.sidebarContainer;
             Create.creators = creators;
             Create.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.creators");
 
-            const buttons = Docs.Create.StackingDocument([Search, Library, Create], { width: 500, height: 80, chromeStatus: "disabled", title: "buttons" });
-            buttons.sectionFilter = "title";
-            buttons.boxShadow = "0 0";
-            buttons.ignoreClick = true;
-            buttons.hideHeadings = true;
-            doc.libraryButtons = buttons;
+            const libraryButtons = Docs.Create.StackingDocument([Search, Library, Create], { width: 500, height: 80, chromeStatus: "disabled", title: "buttons" });
+            libraryButtons.sectionFilter = "title";
+            libraryButtons.boxShadow = "0 0";
+            libraryButtons.ignoreClick = true;
+            libraryButtons.hideHeadings = true;
+            libraryButtons.backgroundColor = "lightgrey";
 
+            doc.libraryButtons = libraryButtons;
             doc.Library = Library;
             doc.Create = Create;
             doc.Search = Search;
-            (Library.onClick as ScriptField).script.run({ this: Library });
-            //(doc.sidebarContainer as Doc).proto = library;
         }
-        PromiseValue(Cast(doc.libraryButtons, Doc)).then(libraryButtons => {
-            if (libraryButtons) {
-                libraryButtons.backgroundColor = "lightgrey";
-            }
-        });
-
-        PromiseValue(Cast(doc.sidebar, Doc)).then(sidebar => {
-            if (sidebar) {
-                sidebar.backgroundColor = "lightgrey";
-            }
-        });
+        PromiseValue(Cast(doc.libraryButtons, Doc)).then(libraryButtons => { });
+        PromiseValue(Cast(doc.Library, Doc)).then(library => library && library.library && library.targetContainer && (library.onClick as ScriptField).script.run({ this: library }));
+        PromiseValue(Cast(doc.Create, Doc)).then(async create => create && create.creators && create.targetContainer);
+        PromiseValue(Cast(doc.Search, Doc)).then(async search => search && search.searchBox && search.targetContainer);
 
         if (doc.overlays === undefined) {
             const overlays = Docs.Create.FreeformDocument([], { title: "Overlays" });
@@ -178,8 +170,7 @@ export class CurrentUserUtils {
             PromiseValue(Cast(doc.overlays, Doc)).then(overlays => overlays && Doc.AddDocToList(overlays, "data", doc.linkFollowBox = Docs.Create.LinkFollowBoxDocument({ x: 250, y: 20, width: 500, height: 370, title: "Link Follower" })));
         }
 
-        StrCast(doc.title).indexOf("@") !== -1 && (doc.title = (StrCast(doc.title).split("@")[0] + "'s Library").toUpperCase());
-        StrCast(doc.title).indexOf("'s Library") !== -1 && (doc.title = StrCast(doc.title).toUpperCase());
+        doc.title = "DOCUMENTS";
         doc.backgroundColor = "#eeeeee";
         doc.width = 100;
         doc.preventTreeViewOpen = true;
-- 
cgit v1.2.3-70-g09d2


From ed89b0fbf26c5bf4263198938dae07e6f32ab436 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 12 Oct 2019 13:43:13 -0400
Subject: a few more tweaks and now the bottom buttons are customizable

---
 src/client/views/MainView.tsx                          | 11 ++++++++++-
 src/client/views/nodes/DragBox.tsx                     |  5 +++--
 src/new_fields/Doc.ts                                  |  1 +
 src/server/authentication/models/current_user_utils.ts |  4 ++--
 4 files changed, 16 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index cc412a609..18e5052b7 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -40,6 +40,7 @@ import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
 import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView';
 import { MainViewNotifs } from './MainViewNotifs';
+import { DocumentType } from '../documents/DocumentTypes';
 
 @observer
 export class MainView extends React.Component {
@@ -331,7 +332,15 @@ export class MainView extends React.Component {
     }
 
     drop = action((e: Event, de: DragManager.DropEvent) => {
-        (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc));
+        (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => {
+            if (doc.type !== DocumentType.DRAGBOX) {
+                let dbox = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" });
+                dbox.factory = doc;
+                dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.factory, true)');
+                doc = dbox;
+            }
+            Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc);
+        });
     });
 
     onDrop = (e: React.DragEvent<HTMLDivElement>) => {
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
index ab3368b94..4fca96382 100644
--- a/src/client/views/nodes/DragBox.tsx
+++ b/src/client/views/nodes/DragBox.tsx
@@ -44,7 +44,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
         }
     }
 
-    onDragMove = (e: MouseEvent) => {
+    onDragMove = async (e: MouseEvent) => {
         if (!e.cancelBubble && (Math.abs(this._downX - e.clientX) > 5 || Math.abs(this._downY - e.clientY) > 5)) {
             document.removeEventListener("pointermove", this.onDragMove);
             document.removeEventListener("pointerup", this.onDragUp);
@@ -52,7 +52,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
             e.stopPropagation();
             e.preventDefault();
             DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([this.props.Document]), e.clientX, e.clientY, {
-                finishDrag: (dropData) => {
+                finishDrag: async (dropData) => {
                     let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result;
                     let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
                     dropData.droppedDocuments = [doc];
@@ -60,6 +60,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
                 handlers: { dragComplete: emptyFunction },
                 hideSource: false
             });
+            await this.props.Document.factory; // if there's a factory Doc that is being copied, make sure it's not pending.
         }
         e.stopPropagation();
         e.preventDefault();
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index eb752f8c6..66036f673 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -730,6 +730,7 @@ export namespace Doc {
 Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
 Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
 Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
+Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return Doc.MakeCopy(doc, copyProto); });
 Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); });
 Scripting.addGlobal(function aliasDocs(field: any) { return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d))); });
 Scripting.addGlobal(function docList(field: any) { return DocListCast(field); });
\ No newline at end of file
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 09bab959d..9a7b6442b 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -130,7 +130,7 @@ export class CurrentUserUtils {
             let createWebPage = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Web Page", icon: "globe-asia" });
             createWebPage.onDragStart = ScriptField.MakeFunction('Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })');
             let createCatImage = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Image", icon: "cat" });
-            createCatImage.onDragStart = ScriptField.MakeFunction('Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })');
+            createCatImage.onDragStart = ScriptField.MakeFunction('Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { width: 200, title: "an image of a cat" })');
             let createButton = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Button", icon: "bolt" });
             createButton.onDragStart = ScriptField.MakeFunction('Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })');
             let createPresentation = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Presentation", icon: "tv" });
@@ -139,7 +139,7 @@ export class CurrentUserUtils {
             createFolderImport.onDragStart = ScriptField.MakeFunction('Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })');
             const dragCreators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, chromeStatus: "disabled", title: "buttons" });
             const color = Docs.Create.ColorDocument({ title: "color picker", width: 400, ignoreClick: true });
-            const creators = Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, chromeStatus: "disabled", title: "buttons" })
+            const creators = Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, chromeStatus: "disabled", title: "buttons" });
             Create.targetContainer = doc.sidebarContainer;
             Create.creators = creators;
             Create.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.creators");
-- 
cgit v1.2.3-70-g09d2


From f16621ff6bc0ad30cae39872412de80aa9f5e25c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 12 Oct 2019 15:07:28 -0400
Subject: dragbox improvements to control creating aliases/copies

---
 src/client/util/DragManager.ts                         |  3 ++-
 src/client/views/nodes/DocumentView.tsx                |  9 ++++++---
 src/client/views/nodes/DragBox.tsx                     | 17 +++++++++++++----
 src/server/authentication/models/current_user_utils.ts |  5 ++++-
 4 files changed, 25 insertions(+), 9 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ddc8fb62c..7259f66e3 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -10,7 +10,6 @@ import { LinkManager } from "./LinkManager";
 import { SelectionManager } from "./SelectionManager";
 import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
 import { Docs } from "../documents/Documents";
-import { CompileScript } from "./Scripting";
 import { ScriptField } from "../../new_fields/ScriptField";
 import { List } from "../../new_fields/List";
 import { PrefetchProxy } from "../../new_fields/Proxy";
@@ -208,6 +207,7 @@ export namespace DragManager {
         }
         draggedDocuments: Doc[];
         droppedDocuments: Doc[];
+        removeDropProperties: string[] = [];
         offset: number[];
         dropAction: dropActionType;
         userDropAction: dropActionType;
@@ -244,6 +244,7 @@ export namespace DragManager {
                         dragData.draggedDocuments.map(d => Doc.MakeCopy(d, true)) :
                         dragData.draggedDocuments
                 );
+                dragData.removeDropProperties.map(prop => dropData.droppedDocuments.map((d: Doc) => d[prop] = undefined));
             });
     }
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 20920a9b8..b98627c66 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -7,7 +7,7 @@ import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc
 import { Id } from '../../../new_fields/FieldSymbols';
 import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
 import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, NumCast, PromiseValue, StrCast, FieldValue } from "../../../new_fields/Types";
 import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
 import { emptyFunction, returnTrue, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
@@ -102,6 +102,8 @@ export const documentSchema = createSchema({
     height: "number",           // "
     backgroundColor: "string",  // background color of document
     opacity: "number",          // opacity of document
+    dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias" or "copy")
+    removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped (used for dragBox things)
     onClick: ScriptField,       // script to run when document is clicked (can be overriden by an onClick prop)
     ignoreAspect: "boolean",    // whether aspect ratio should be ignored when laying out or manipulating the document
     autoHeight: "boolean",      // whether the height of the document should be computed automatically based on its contents
@@ -167,9 +169,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             dragData.dropAction = dropAction;
             dragData.moveDocument = this.props.moveDocument;
             dragData.applyAsTemplate = applyAsTemplate;
+            dragData.removeDropProperties = this.Document.removeDropProperties || [];
             DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
                 handlers: {
-                    dragComplete: action(emptyFunction)
+                    dragComplete: action((emptyFunction))
                 },
                 hideSource: !dropAction
             });
@@ -260,7 +263,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 if (!e.altKey && !this.topMost && e.buttons === 1) {
                     document.removeEventListener("pointermove", this.onPointerMove);
                     document.removeEventListener("pointerup", this.onPointerUp);
-                    this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);
+                    this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, 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
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
index 4fca96382..1fd6cb5a5 100644
--- a/src/client/views/nodes/DragBox.tsx
+++ b/src/client/views/nodes/DragBox.tsx
@@ -3,7 +3,7 @@ import { faEdit } from '@fortawesome/free-regular-svg-icons';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc } from '../../../new_fields/Doc';
-import { createSchema, makeInterface } from '../../../new_fields/Schema';
+import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
 import { ScriptField } from '../../../new_fields/ScriptField';
 import { emptyFunction } from '../../../Utils';
 import { CompileScript } from '../../util/Scripting';
@@ -17,6 +17,8 @@ import { FieldView, FieldViewProps } from './FieldView';
 import { DragManager } from '../../util/DragManager';
 import { Docs } from '../../documents/Documents';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Cast } from '../../../new_fields/Types';
+import { ContextMenuProps } from '../ContextMenuItem';
 
 library.add(faEdit as any);
 
@@ -51,16 +53,19 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
             const onDragStart = this.Document.onDragStart;
             e.stopPropagation();
             e.preventDefault();
-            DragManager.StartDocumentDrag([this._mainCont.current!], new DragManager.DocumentDragData([this.props.Document]), e.clientX, e.clientY, {
+            let dragData = new DragManager.DocumentDragData([this.props.Document]);
+            const factory = await Cast(this.props.Document.factory, Doc);// if there's a factory Doc that is being copied, make sure it's not pending.
+            dragData.removeDropProperties = factory ? Cast(factory.removeDropProperties, listSpec("string"), []) : [];
+            DragManager.StartDocumentDrag([this._mainCont.current!], dragData, e.clientX, e.clientY, {
                 finishDrag: async (dropData) => {
                     let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result;
                     let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
                     dropData.droppedDocuments = [doc];
+                    dragData.removeDropProperties.map(prop => dropData.droppedDocuments.map((d: Doc) => d[prop] = undefined));
                 },
                 handlers: { dragComplete: emptyFunction },
                 hideSource: false
             });
-            await this.props.Document.factory; // if there's a factory Doc that is being copied, make sure it's not pending.
         }
         e.stopPropagation();
         e.preventDefault();
@@ -72,7 +77,10 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
     }
 
     onContextMenu = () => {
-        ContextMenu.Instance.addItem({
+        let funcs: ContextMenuProps[] = [];
+        funcs.push({ description: "Set Make Aliases", icon: "edit", event: () => this.props.Document.factory && (this.props.Document.onDragStart = ScriptField.MakeFunction('getAlias(this.factory)')) });
+        funcs.push({ description: "Set Make Copies", icon: "edit", event: () => this.props.Document.factory && (this.props.Document.onDragStart = ScriptField.MakeFunction('getCopy(this.factory, true)')) });
+        funcs.push({
             description: "Edit OnClick script", icon: "edit", event: () => {
                 let overlayDisposer: () => void = emptyFunction;
                 const script = this.Document.onDragStart;
@@ -96,6 +104,7 @@ export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocu
                 overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnDragStart` });
             }
         });
+        ContextMenu.Instance.addItem({ description: "DragBox Funcs...", subitems: funcs, icon: "asterisk" });
     }
 
     render() {
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 08cdee0fd..74a41d8cf 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -138,7 +138,10 @@ export class CurrentUserUtils {
             let createFolderImport = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Import Folder", icon: "cloud-upload-alt" });
             createFolderImport.onDragStart = ScriptField.MakeFunction('Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })');
             const dragCreators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, chromeStatus: "disabled", title: "buttons" });
-            const color = Docs.Create.ColorDocument({ title: "color picker", width: 400, ignoreClick: true });
+            const color = Docs.Create.ColorDocument({ title: "color picker", width: 400 });
+            color.dropAction = "alias";
+            color.ignoreClick = true;
+            color.removeDropProperties = new List<string>(["dropAction", "ignoreClick"]);
             const creators = Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, chromeStatus: "disabled", title: "buttons" });
             Create.targetContainer = doc.sidebarContainer;
             Create.creators = creators;
-- 
cgit v1.2.3-70-g09d2


From 988822bdeeed82b0c1ef611f2f7a3111d029d564 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Sat, 12 Oct 2019 19:37:06 -0400
Subject: a bunch of fixes to clean up drag box stuff.

---
 src/client/documents/DocumentTypes.ts              |   2 +-
 src/client/documents/Documents.ts                  |  14 +-
 src/client/util/DragManager.ts                     |  19 +-
 src/client/views/GlobalKeyHandler.ts               |   1 -
 src/client/views/Main.scss                         |  17 +-
 src/client/views/MainView.tsx                      | 250 ++++++++-------------
 src/client/views/nodes/ButtonBox.tsx               |   3 +-
 src/client/views/nodes/DocumentContentsView.tsx    |   4 +-
 src/client/views/nodes/DocumentView.tsx            |  27 ++-
 src/client/views/nodes/DragBox.scss                |  13 --
 src/client/views/nodes/DragBox.tsx                 | 115 ----------
 src/client/views/nodes/FontIconBox.scss            |  13 ++
 src/client/views/nodes/FontIconBox.tsx             |  21 ++
 .../authentication/models/current_user_utils.ts    |  29 ++-
 14 files changed, 194 insertions(+), 334 deletions(-)
 delete mode 100644 src/client/views/nodes/DragBox.scss
 delete mode 100644 src/client/views/nodes/DragBox.tsx
 create mode 100644 src/client/views/nodes/FontIconBox.scss
 create mode 100644 src/client/views/nodes/FontIconBox.tsx

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

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index dad2de0b5..432e53825 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -17,7 +17,7 @@ export enum DocumentType {
     TEMPLATE = "template",
     EXTENSION = "extension",
     YOUTUBE = "youtube",
-    DRAGBOX = "dragbox",
+    FONTICONBOX = "fonticonbox",
     PRES = "presentation",
     LINKFOLLOW = "linkfollow",
     PRESELEMENT = "preselement",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e783848ff..1f89d2993 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -37,7 +37,7 @@ import { DocumentManager } from "../util/DocumentManager";
 import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox";
 import { Scripting, CompileScript } from "../util/Scripting";
 import { ButtonBox } from "../views/nodes/ButtonBox";
-import { DragBox } from "../views/nodes/DragBox";
+import { FontIconBox } from "../views/nodes/FontIconBox";
 import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField";
 import { PresBox } from "../views/nodes/PresBox";
 import { ComputedField } from "../../new_fields/ScriptField";
@@ -71,6 +71,7 @@ export interface DocumentOptions {
     viewType?: number;
     backgroundColor?: string;
     ignoreClick?: boolean;
+    lockedPosition?: boolean;
     opacity?: number;
     defaultBackgroundColor?: string;
     dropAction?: dropActionType;
@@ -82,6 +83,7 @@ export interface DocumentOptions {
     currentTimecode?: number;
     documentText?: string;
     borderRounding?: string;
+    boxShadow?: string;
     schemaColumns?: List<SchemaHeaderField>;
     dockingConfig?: string;
     autoHeight?: boolean;
@@ -127,7 +129,7 @@ export namespace Docs {
             }],
             [DocumentType.QUERY, {
                 layout: { view: QueryBox },
-                options: { width: 400, fitWidth: true }
+                options: { width: 400 }
             }],
             [DocumentType.COLOR, {
                 layout: { view: ColorBox },
@@ -183,8 +185,8 @@ export namespace Docs {
                 layout: { view: PresBox },
                 options: {}
             }],
-            [DocumentType.DRAGBOX, {
-                layout: { view: DragBox },
+            [DocumentType.FONTICONBOX, {
+                layout: { view: FontIconBox },
                 options: { width: 40, height: 40 },
             }],
             [DocumentType.LINKFOLLOW, {
@@ -476,8 +478,8 @@ export namespace Docs {
         }
 
 
-        export function DragboxDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.DRAGBOX), undefined, { ...(options || {}) });
+        export function FontIconDocument(options?: DocumentOptions) {
+            return InstanceFromProto(Prototypes.get(DocumentType.FONTICONBOX), undefined, { ...(options || {}) });
         }
 
         export function LinkFollowBoxDocument(options?: DocumentOptions) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 7259f66e3..2c316ccdf 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,6 +1,6 @@
 import { action, runInAction } from "mobx";
 import { Doc, Field } from "../../new_fields/Doc";
-import { Cast, StrCast } from "../../new_fields/Types";
+import { Cast, StrCast, ScriptCast } from "../../new_fields/Types";
 import { URLField } from "../../new_fields/URLField";
 import { emptyFunction } from "../../Utils";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
@@ -13,6 +13,7 @@ import { Docs } from "../documents/Documents";
 import { ScriptField } from "../../new_fields/ScriptField";
 import { List } from "../../new_fields/List";
 import { PrefetchProxy } from "../../new_fields/Proxy";
+import { listSpec } from "../../new_fields/Schema";
 
 export type dropActionType = "alias" | "copy" | undefined;
 export function SetupDrag(
@@ -207,7 +208,6 @@ export namespace DragManager {
         }
         draggedDocuments: Doc[];
         droppedDocuments: Doc[];
-        removeDropProperties: string[] = [];
         offset: number[];
         dropAction: dropActionType;
         userDropAction: dropActionType;
@@ -234,17 +234,18 @@ export namespace DragManager {
 
     export let StartDragFunctions: (() => void)[] = [];
 
-    export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
+    export async function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
         runInAction(() => StartDragFunctions.map(func => func()));
+        await dragData.draggedDocuments.map(d => d.dragFactory);
         StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
             (dropData: { [id: string]: any }) => {
-                (dropData.droppedDocuments = dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ?
-                    dragData.draggedDocuments.map(d => Doc.MakeAlias(d)) :
-                    dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ?
-                        dragData.draggedDocuments.map(d => Doc.MakeCopy(d, true)) :
-                        dragData.draggedDocuments
+                (dropData.droppedDocuments =
+                    dragData.draggedDocuments.map(d => ScriptCast(d.onDragStart) ? ScriptCast(d.onDragStart).script.run({ this: d }).result :
+                        dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? Doc.MakeAlias(d) :
+                            dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? Doc.MakeCopy(d, true) : d)
                 );
-                dragData.removeDropProperties.map(prop => dropData.droppedDocuments.map((d: Doc) => d[prop] = undefined));
+                dropData.droppedDocuments.forEach((drop: Doc, i: number) =>
+                    Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []).map(prop => drop[prop] = undefined));
             });
     }
 
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index f3e1933d7..38fce4cf7 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -73,7 +73,6 @@ export default class KeyManager {
                         SelectionManager.DeselectAll();
                     }
                 }
-                main.toggleColorPicker(true);
                 SelectionManager.DeselectAll();
                 DictationManager.Controls.stop();
                 SharingManager.Instance.close();
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index debb941c8..55195b616 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -185,7 +185,22 @@ button:hover {
     overflow: auto;
     z-index: 1;
 }
-
+.mainContent {
+    width:100%;
+    height:100%;
+    position:absolute;
+}
+.mainView-flyoutContainer{
+    display:flex;
+    flex-direction: column;
+    position: absolute;
+    width:100%;
+    height:100%;
+    border: black 1px solid;
+    .documentView-node-topmost {
+        background: lightgrey;
+    }
+}
 #mainContent-div {
     width: 100%;
     height: 100%;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 18e5052b7..aac4af9f6 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,4 +1,4 @@
-import { IconName, library } from '@fortawesome/fontawesome-svg-core';
+import { library } from '@fortawesome/fontawesome-svg-core';
 import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
@@ -6,60 +6,74 @@ import { observer } from 'mobx-react';
 import "normalize.css";
 import * as React from 'react';
 import Measure from 'react-measure';
-import { Doc, DocListCast, Field, FieldResult, HeightSym, Opt } from '../../new_fields/Doc';
+import { Doc, DocListCast, Field, FieldResult, Opt } from '../../new_fields/Doc';
 import { Id } from '../../new_fields/FieldSymbols';
 import { InkTool } from '../../new_fields/InkField';
 import { List } from '../../new_fields/List';
+import { ObjectField } from '../../new_fields/ObjectField';
 import { listSpec } from '../../new_fields/Schema';
 import { ScriptField } from '../../new_fields/ScriptField';
-import { BoolCast, Cast, FieldValue, PromiseValue, StrCast, NumCast } from '../../new_fields/Types';
+import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast } from '../../new_fields/Types';
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
 import { RouteStore } from '../../server/RouteStore';
-import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils, returnFalse } from '../../Utils';
+import { emptyFunction, returnEmptyString, returnFalse, returnOne, returnTrue, Utils } from '../../Utils';
 import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
 import { DictationManager } from '../util/DictationManager';
-import { SetupDrag, DragManager } from '../util/DragManager';
+import { DragManager } from '../util/DragManager';
 import { HistoryUtil } from '../util/History';
 import SharingManager from '../util/SharingManager';
 import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
 import { CollectionBaseView, CollectionViewType } from './collections/CollectionBaseView';
 import { CollectionDockingView } from './collections/CollectionDockingView';
-import { CollectionTreeView } from './collections/CollectionTreeView';
 import { ContextMenu } from './ContextMenu';
 import { DocumentDecorations } from './DocumentDecorations';
 import KeyManager from './GlobalKeyHandler';
 import { InkingControl } from './InkingControl';
 import "./Main.scss";
 import MainViewModal from './MainViewModal';
+import { MainViewNotifs } from './MainViewNotifs';
 import { DocumentView } from './nodes/DocumentView';
 import { OverlayView } from './OverlayView';
 import PDFMenu from './pdf/PDFMenu';
 import { PreviewCursor } from './PreviewCursor';
-import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView';
-import { MainViewNotifs } from './MainViewNotifs';
-import { DocumentType } from '../documents/DocumentTypes';
 
 @observer
 export class MainView extends React.Component {
     public static Instance: MainView;
-    @observable addMenuToggle = React.createRef<HTMLInputElement>();
-    @observable public pwidth: number = 0;
-    @observable public pheight: number = 0;
-    private _buttonBarHeight = 85;
-    private dropDisposer?: DragManager.DragDropDisposer;
-
-    @observable private dictationState = DictationManager.placeholder;
-    @observable private dictationSuccessState: boolean | undefined = undefined;
-    @observable private dictationDisplayState = false;
-    @observable private dictationListeningState: DictationManager.Controls.ListeningUIStatus = false;
+    private _buttonBarHeight = 75;
+    private _flyoutSizeOnDown = 0;
+    private _urlState: HistoryUtil.DocUrl;
+    private _dropDisposer?: DragManager.DragDropDisposer;
+
+    @observable private _dictationState = DictationManager.placeholder;
+    @observable private _dictationSuccessState: boolean | undefined = undefined;
+    @observable private _dictationDisplayState = false;
+    @observable private _dictationListeningState: DictationManager.Controls.ListeningUIStatus = false;
+
+    @observable private _panelWidth: number = 0;
+    @observable private _panelHeight: number = 0;
+    @observable private _flyoutTranslate: boolean = true;
+    @observable public addMenuToggle = React.createRef<HTMLInputElement>();
+    @observable public flyoutWidth: number = 250;
 
     public hasActiveModal = false;
-
+    public isPointerDown = false;
     public overlayTimeout: NodeJS.Timeout | undefined;
 
+    private set mainContainer(doc: Opt<Doc>) {
+        if (doc) {
+            !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
+            this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc);
+        }
+    }
+
+    @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; }
+    @computed private get userDoc() { return CurrentUserUtils.UserDocument; }
+    @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
+
     public initiateDictationFade = () => {
         let duration = DictationManager.Commands.dictationFadeDuration;
         this.overlayTimeout = setTimeout(() => {
@@ -69,13 +83,6 @@ export class MainView extends React.Component {
             setTimeout(() => this.dictatedPhrase = DictationManager.placeholder, 500);
         }, duration);
     }
-
-    private urlState: HistoryUtil.DocUrl;
-
-    @computed private get userDoc() {
-        return CurrentUserUtils.UserDocument;
-    }
-
     public cancelDictationFade = () => {
         if (this.overlayTimeout) {
             clearTimeout(this.overlayTimeout);
@@ -83,54 +90,15 @@ export class MainView extends React.Component {
         }
     }
 
-    @computed private get mainContainer(): Opt<Doc> {
-        return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace;
-    }
-    @computed get mainFreeform(): Opt<Doc> {
-        let docs = DocListCast(this.mainContainer!.data);
-        return (docs && docs.length > 1) ? docs[1] : undefined;
-    }
-    public isPointerDown = false;
-    private set mainContainer(doc: Opt<Doc>) {
-        if (doc) {
-            if (!("presentationView" in doc)) {
-                doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]);
-            }
-            this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc);
-        }
-    }
-
-    @computed public get dictatedPhrase() {
-        return this.dictationState;
-    }
-
-    public set dictatedPhrase(value: string) {
-        runInAction(() => this.dictationState = value);
-    }
-
-    @computed public get dictationSuccess() {
-        return this.dictationSuccessState;
-    }
-
-    public set dictationSuccess(value: boolean | undefined) {
-        runInAction(() => this.dictationSuccessState = value);
-    }
-
-    @computed public get dictationOverlayVisible() {
-        return this.dictationDisplayState;
-    }
+    @computed public get dictatedPhrase() { return this._dictationState; }
+    @computed public get dictationSuccess() { return this._dictationSuccessState; }
+    @computed public get dictationOverlayVisible() { return this._dictationDisplayState; }
+    @computed public get isListening() { return this._dictationListeningState; }
 
-    public set dictationOverlayVisible(value: boolean) {
-        runInAction(() => this.dictationDisplayState = value);
-    }
-
-    @computed public get isListening() {
-        return this.dictationListeningState;
-    }
-
-    public set isListening(value: DictationManager.Controls.ListeningUIStatus) {
-        runInAction(() => this.dictationListeningState = value);
-    }
+    public set dictatedPhrase(value: string) { runInAction(() => this._dictationState = value); }
+    public set dictationSuccess(value: boolean | undefined) { runInAction(() => this._dictationSuccessState = value); }
+    public set dictationOverlayVisible(value: boolean) { runInAction(() => this._dictationDisplayState = value); }
+    public set isListening(value: DictationManager.Controls.ListeningUIStatus) { runInAction(() => this._dictationListeningState = value); }
 
     componentWillMount() {
         var tag = document.createElement('script');
@@ -147,13 +115,13 @@ export class MainView extends React.Component {
         //close presentation 
         window.removeEventListener("pointerdown", this.globalPointerDown);
         window.removeEventListener("pointerup", this.globalPointerUp);
-        this.dropDisposer && this.dropDisposer();
+        this._dropDisposer && this._dropDisposer();
     }
 
     constructor(props: Readonly<{}>) {
         super(props);
         MainView.Instance = this;
-        this.urlState = HistoryUtil.parseUrl(window.location) || {} as any;
+        this._urlState = HistoryUtil.parseUrl(window.location) || {} as any;
         // causes errors to be generated when modifying an observable outside of an action
         configure({ enforceActions: "observed" });
         if (window.location.pathname !== RouteStore.home) {
@@ -233,7 +201,7 @@ export class MainView extends React.Component {
                 { fireImmediately: true }
             );
         } else {
-            if (received && this.urlState.sharing) {
+            if (received && this._urlState.sharing) {
                 reaction(
                     () => {
                         let docking = CollectionDockingView.Instance;
@@ -264,8 +232,8 @@ export class MainView extends React.Component {
         let freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
-            width: this.pwidth * .7,
-            height: this.pheight,
+            width: this._panelWidth * .7,
+            height: this._panelHeight,
             title: "My Blank Collection"
         };
         let workspaces: FieldResult<Doc>;
@@ -293,7 +261,7 @@ export class MainView extends React.Component {
     openWorkspace = async (doc: Doc, fromHistory = false) => {
         CurrentUserUtils.MainDocId = doc[Id];
         this.mainContainer = doc;
-        let state = this.urlState;
+        let state = this._urlState;
         if (state.sharing === true && !this.userDoc) {
             DocServer.Control.makeReadOnly();
         } else {
@@ -318,25 +286,21 @@ export class MainView extends React.Component {
                 DocServer.Control.makeEditable();
             }
         }
-        let col: Opt<Doc>;
         // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
         setTimeout(async () => {
-            if (this.userDoc && (col = await Cast(this.userDoc.optionalRightCollection, Doc))) {
-                const l = Cast(col.data, listSpec(Doc));
-                if (l) {
-                    runInAction(() => MainViewNotifs.NotifsCol = col);
-                }
-            }
+            const col = this.userDoc && await Cast(this.userDoc.optionalRightCollection, Doc);
+            col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col);
         }, 100);
         return true;
     }
 
     drop = action((e: Event, de: DragManager.DropEvent) => {
         (de.data as DragManager.DocumentDragData).draggedDocuments.map(doc => {
-            if (doc.type !== DocumentType.DRAGBOX) {
-                let dbox = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" });
-                dbox.factory = doc;
-                dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.factory, true)');
+            if (!doc.onDragStart) {
+                let dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: "bolt" });
+                dbox.dragFactory = doc;
+                dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined;
+                dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)');
                 doc = dbox;
             }
             Doc.AddDocToList(CurrentUserUtils.UserDocument, "docButtons", doc);
@@ -351,30 +315,22 @@ export class MainView extends React.Component {
 
     @action
     onResize = (r: any) => {
-        this.pwidth = r.offset.width;
-        this.pheight = r.offset.height;
-    }
-    getPWidth = () => {
-        return this.pwidth;
-    }
-    getPHeight = () => {
-        return this.pheight;
-    }
-    getContentsHeight = () => {
-        return this.pheight - this._buttonBarHeight;
+        this._panelWidth = r.offset.width;
+        this._panelHeight = r.offset.height;
     }
+    getPWidth = () => this._panelWidth;
+    getPHeight = () => this._panelHeight;
+    getContentsHeight = () => this._panelHeight - this._buttonBarHeight;
 
-    @observable flyoutWidth: number = 250;
-    @observable flyoutTranslate: boolean = true;
     @computed get dockingContent() {
-        let flyoutWidth = this.flyoutWidth;
-        let countWidth = this.flyoutTranslate;
-        let mainCont = this.mainContainer;
         return <Measure offset onResize={this.onResize}>
             {({ measureRef }) =>
-                <div ref={measureRef} id="mainContent-div" style={{ width: `calc(100% - ${countWidth ? flyoutWidth : 0}px`, transform: `translate(${countWidth ? flyoutWidth : 0}px, 0px)` }} onDrop={this.onDrop}>
-                    {!mainCont ? (null) :
-                        <DocumentView Document={mainCont}
+                <div ref={measureRef} id="mainContent-div" style={{
+                    width: `calc(100% - ${this._flyoutTranslate || 0}px`,
+                    transform: `translate(${this._flyoutTranslate || 0}px, 0px)`
+                }} onDrop={this.onDrop}>
+                    {!this.mainContainer ? (null) :
+                        <DocumentView Document={this.mainContainer}
                             DataDoc={undefined}
                             addDocument={undefined}
                             addDocTab={this.addDocTabFunc}
@@ -402,9 +358,8 @@ export class MainView extends React.Component {
         </Measure>;
     }
 
-    _downsize = 0;
     onPointerDown = (e: React.PointerEvent) => {
-        this._downsize = e.clientX;
+        this._flyoutSizeOnDown = e.clientX;
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
         document.addEventListener("pointermove", this.onPointerMove);
@@ -417,15 +372,15 @@ export class MainView extends React.Component {
     pointerOverDragger = () => {
         if (this.flyoutWidth === 0) {
             this.flyoutWidth = 250;
-            this.flyoutTranslate = false;
+            this._flyoutTranslate = false;
         }
     }
 
     @action
     pointerLeaveDragger = () => {
-        if (!this.flyoutTranslate) {
+        if (!this._flyoutTranslate) {
             this.flyoutWidth = 0;
-            this.flyoutTranslate = true;
+            this._flyoutTranslate = true;
         }
     }
 
@@ -435,7 +390,7 @@ export class MainView extends React.Component {
     }
     @action
     onPointerUp = (e: PointerEvent) => {
-        if (Math.abs(e.clientX - this._downsize) < 4) {
+        if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4) {
             if (this.flyoutWidth < 5) this.flyoutWidth = 250;
             else this.flyoutWidth = 0;
         }
@@ -461,8 +416,8 @@ export class MainView extends React.Component {
             return (null);
         }
         let libraryButtonDoc = Cast(CurrentUserUtils.UserDocument.libraryButtons, Doc) as Doc;
-        libraryButtonDoc.columnWidth = this.flyoutWidthFunc() / 3 - 30;
-        return <div style={{ display: "flex", flexDirection: "column", position: "absolute", width: "100%", height: "100%" }}>
+        libraryButtonDoc.columnWidth = this.flyoutWidth / 3 - 30;
+        return <div className="mainView-flyoutContainer">
             <div style={{ position: "relative", height: `${this._buttonBarHeight}px`, width: "100%" }}>
                 <DocumentView
                     Document={libraryButtonDoc}
@@ -522,30 +477,27 @@ export class MainView extends React.Component {
 
     @computed
     get mainContent() {
-        let sidebar = this.userDoc && this.userDoc.sidebarContainer;
-        if (!this.userDoc || !(sidebar instanceof Doc)) {
-            return (null);
-        }
-        return (
-            <div className="mainContent" style={{ width: "100%", height: "100%", position: "absolute" }}>
+        const sidebar = this.userDoc && this.userDoc.sidebarContainer;
+        return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
+            <div className="mainContent" >
                 <div onPointerLeave={this.pointerLeaveDragger}>
                     <div className="mainView-libraryHandle"
-                        style={{ cursor: "ew-resize", left: `${(this.flyoutWidth * (this.flyoutTranslate ? 1 : 0)) - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }}
+                        style={{ cursor: "ew-resize", left: `${(this.flyoutWidth * (this._flyoutTranslate ? 1 : 0)) - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }}
                         onPointerDown={this.onPointerDown} onPointerOver={this.pointerOverDragger}>
                         <span title="library View Dragger" style={{
-                            width: (this.flyoutWidth !== 0 && this.flyoutTranslate) ? "100%" : "5vw",
-                            height: (this.flyoutWidth !== 0 && this.flyoutTranslate) ? "100%" : "30vh",
+                            width: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "5vw",
+                            height: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "100%" : "30vh",
                             position: "absolute",
-                            top: (this.flyoutWidth !== 0 && this.flyoutTranslate) ? "" : "-10vh"
+                            top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "-10vh"
                         }} />
                     </div>
                     <div className="mainView-libraryFlyout" style={{
                         width: `${this.flyoutWidth}px`,
                         zIndex: 1,
-                        transformOrigin: this.flyoutTranslate ? "" : "left center",
-                        transition: this.flyoutTranslate ? "" : "width .5s",
-                        transform: `scale(${this.flyoutTranslate ? 1 : 0.8})`,
-                        boxShadow: this.flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw"
+                        transformOrigin: this._flyoutTranslate ? "" : "left center",
+                        transition: this._flyoutTranslate ? "" : "width .5s",
+                        transform: `scale(${this._flyoutTranslate ? 1 : 0.8})`,
+                        boxShadow: this._flyoutTranslate ? "" : "rgb(156, 147, 150) 0.2vw 0.2vw 0.8vw"
                     }}>
                         {this.flyout}
                         {this.expandButton}
@@ -556,10 +508,10 @@ export class MainView extends React.Component {
     }
 
     @computed get expandButton() {
-        return !this.flyoutTranslate ? (<div className="expandFlyoutButton" title="Re-attach sidebar" onPointerDown={() => {
+        return !this._flyoutTranslate ? (<div className="expandFlyoutButton" title="Re-attach sidebar" onPointerDown={() => {
             runInAction(() => {
                 this.flyoutWidth = 250;
-                this.flyoutTranslate = true;
+                this._flyoutTranslate = true;
             });
         }}><FontAwesomeIcon icon="chevron-right" color="grey" size="lg" /></div>) : (null);
     }
@@ -572,14 +524,6 @@ export class MainView extends React.Component {
         return { fontSize: "50%" };
     }
 
-    onColorClick = (e: React.MouseEvent) => {
-        let target = (e.nativeEvent as any).path[0];
-        let parent = (e.nativeEvent as any).path[1];
-        if (target.localName === "input" || parent.localName === "span") {
-            e.stopPropagation();
-        }
-    }
-
     setWriteMode = (mode: DocServer.WriteMode) => {
         console.log(DocServer.WriteMode[mode]);
         const mode1 = mode;
@@ -596,13 +540,12 @@ export class MainView extends React.Component {
     }
 
     protected createDropTarget = (ele: HTMLLabelElement) => { //used for stacking and masonry view
-        this.dropDisposer && this.dropDisposer();
+        this._dropDisposer && this._dropDisposer();
         if (ele) {
-            this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+            this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
         }
     }
 
-    @observable private _colorPickerDisplay = false;
     /* for the expandable add nodes menu. Not included with the miscbuttons because once it expands it expands the whole div with it, making canvas interactions limited. */
     nodesMenu() {
         // let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw";
@@ -610,14 +553,13 @@ export class MainView extends React.Component {
 
         // let googlePhotosSearch = () => GooglePhotosClientUtils.CollectionFromSearch(Docs.Create.MasonryDocument, { included: [GooglePhotosClientUtils.ContentCategories.LANDSCAPES] });
 
-        return < div id="add-nodes-menu" style={{ left: (this.flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} >
+        return < div id="add-nodes-menu" style={{ left: (this._flyoutTranslate ? this.flyoutWidth : 0) + 20, bottom: 20 }} >
 
-            <MainViewNotifs></MainViewNotifs>
+            <MainViewNotifs />
             <input type="checkbox" id="add-menu-toggle" ref={this.addMenuToggle} />
             <label htmlFor="add-menu-toggle" ref={this.createDropTarget} title="Close Menu"><p>+</p></label>
 
             <div id="add-options-content">
-                {/* <li key="search"><button className="add-button round-button" title="Search" onClick={this.toggleSearch}><FontAwesomeIcon icon="search" size="sm" /></button></li> */}
                 <button key="undo" className="add-button round-button" title="Undo" style={{ opacity: UndoManager.CanUndo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Undo()}><FontAwesomeIcon icon="undo-alt" size="sm" /></button>
                 <button key="redo" className="add-button round-button" title="Redo" style={{ opacity: UndoManager.CanRedo() ? 1 : 0.5, transition: "0.4s ease all" }} onClick={() => UndoManager.Redo()}><FontAwesomeIcon icon="redo-alt" size="sm" /></button>
 
@@ -658,18 +600,6 @@ export class MainView extends React.Component {
         </div >;
     }
 
-
-
-    @action
-    toggleColorPicker = (close = false) => {
-        this._colorPickerDisplay = close ? false : !this._colorPickerDisplay;
-    }
-
-    @action.bound
-    toggleSearch = () => {
-        PromiseValue(Cast(CurrentUserUtils.UserDocument.Search, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
-    }
-
     @computed private get dictationOverlay() {
         let success = this.dictationSuccess;
         let result = this.isListening && !this.isListening.interim ? DictationManager.placeholder : `"${this.dictatedPhrase}"`;
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index f08ea4891..3cf8c3eb3 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -70,7 +70,8 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
         let missingParams = params && params.filter(p => this.props.Document[p] === undefined);
         params && params.map(p => DocListCast(this.props.Document[p])); // bcz: really hacky form of prefetching ... 
         return (
-            <div className="buttonBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
+            <div className="buttonBox-outerDiv" ref={this.createDropTarget} onContextMenu={this.specificContextMenu}
+                style={{ boxShadow: this.Document.opacity === 0 ? undefined : StrCast(this.Document.boxShadow, "") }}>
                 <div className="buttonBox-mainButton" style={{ background: StrCast(this.props.Document.backgroundColor), color: StrCast(this.props.Document.color, "black") }} >
                     <div className="buttonBox-mainButtonCenter">
                         {(this.Document.text || this.Document.title)}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index f2a581c42..19ffdf0cd 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -16,7 +16,7 @@ import { AudioBox } from "./AudioBox";
 import { ButtonBox } from "./ButtonBox";
 import { DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
-import { DragBox } from "./DragBox";
+import { FontIconBox } from "./FontIconBox";
 import { FieldView, FieldViewProps } from "./FieldView";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { IconBox } from "./IconBox";
@@ -102,7 +102,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
         return <ObserverJsxParser
             blacklistedAttrs={[]}
             components={{
-                FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, DragBox, ButtonBox, FieldView,
+                FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, FontIconBox: FontIconBox, ButtonBox, FieldView,
                 CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
                 PDFBox, VideoBox, AudioBox, HistogramBox, PresBox, YoutubeBox, LinkFollowBox, PresElementBox, QueryBox, ColorBox
             }}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b98627c66..98ae7442f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -103,8 +103,10 @@ export const documentSchema = createSchema({
     backgroundColor: "string",  // background color of document
     opacity: "number",          // opacity of document
     dropAction: "string",       // override specifying what should happen when this document is dropped (can be "alias" or "copy")
-    removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped (used for dragBox things)
+    removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped
     onClick: ScriptField,       // script to run when document is clicked (can be overriden by an onClick prop)
+    onDragStart: ScriptField,   // script to run when document is dragged (without being selected).  the script should return an Doc to drag.
+    dragFactory: Doc,           // the document that serves as the "template" for the onDragStart script
     ignoreAspect: "boolean",    // whether aspect ratio should be ignored when laying out or manipulating the document
     autoHeight: "boolean",      // whether the height of the document should be computed automatically based on its contents
     isTemplate: "boolean",      // whether this document acts as a template layout for describing how other documents should be displayed
@@ -165,16 +167,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (this._mainCont.current) {
             let dragData = new DragManager.DocumentDragData([this.props.Document]);
             const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0);
-            dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
+            dragData.offset = this.Document.onDragStart ? [0, 0] : this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top);
             dragData.dropAction = dropAction;
-            dragData.moveDocument = this.props.moveDocument;
+            dragData.moveDocument = this.Document.onDragStart ? undefined : this.props.moveDocument;
             dragData.applyAsTemplate = applyAsTemplate;
-            dragData.removeDropProperties = this.Document.removeDropProperties || [];
             DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, {
                 handlers: {
                     dragComplete: action((emptyFunction))
                 },
-                hideSource: !dropAction
+                hideSource: !dropAction && !this.Document.onDragStart
             });
         }
     }
@@ -246,7 +247,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 this._hitTemplateDrag = true;
             }
         }
-        if (this.active && e.button === 0 && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active.  if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
+        if ((this.active || this.Document.onDragStart) && e.button === 0 && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active.  if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
         document.removeEventListener("pointermove", this.onPointerMove);
         document.removeEventListener("pointerup", this.onPointerUp);
         document.addEventListener("pointermove", this.onPointerMove);
@@ -258,9 +259,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         if (e.cancelBubble && this.active) {
             document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView)
         }
-        else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive()) && !this.Document.lockedPosition && !this.Document.inOverlay) {
+        else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive() || this.Document.onDragStart) && !this.Document.lockedPosition && !this.Document.inOverlay) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
-                if (!e.altKey && !this.topMost && e.buttons === 1) {
+                if (!e.altKey && (!this.topMost || this.Document.onDragStart) && e.buttons === 1) {
                     document.removeEventListener("pointermove", this.onPointerMove);
                     document.removeEventListener("pointerup", this.onPointerUp);
                     this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);
@@ -475,6 +476,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         });
         !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" });
 
+        let funcs: ContextMenuProps[] = [];
+        funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
+        funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
+        funcs.push({ description: "Drag Document", icon: "edit", event: () => this.Document.onDragStart = undefined });
+        ContextMenu.Instance.addItem({ description: "OnDrag...", subitems: funcs, icon: "asterisk" });
+
         let existing = ContextMenu.Instance.findByDescription("Layout...");
         let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : [];
         layoutItems.push({ description: this.Document.isBackground ? "As Foreground" : "As Background", event: this.makeBackground, icon: this.Document.lockedPosition ? "unlock" : "lock" });
@@ -627,8 +634,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             this.props.backgroundColor(this.Document) || StrCast(this.layoutDoc.backgroundColor) :
             ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.Document);
 
-        const nativeWidth = this.props.Document.fitWidth ? this.props.PanelWidth() : this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%";
-        const nativeHeight = this.props.Document.fitWidth ? this.props.PanelHeight() : this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
+        const nativeWidth = this.props.Document.fitWidth ? this.props.PanelWidth() - 2 : this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%";
+        const nativeHeight = this.props.Document.fitWidth ? this.props.PanelHeight() - 2 : this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%";
         const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined;
         const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle");
         const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption");
diff --git a/src/client/views/nodes/DragBox.scss b/src/client/views/nodes/DragBox.scss
deleted file mode 100644
index fbb9b9c1c..000000000
--- a/src/client/views/nodes/DragBox.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.dragBox-outerDiv {
-    width: 100%;
-    height: 100%;
-    pointer-events: all;
-    border-radius: inherit;
-    background: black;
-    border-radius: 100%;
-    svg {
-        margin:18%;
-        width:65% !important;
-        height:65%;
-    }
-}
diff --git a/src/client/views/nodes/DragBox.tsx b/src/client/views/nodes/DragBox.tsx
deleted file mode 100644
index 1fd6cb5a5..000000000
--- a/src/client/views/nodes/DragBox.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEdit } from '@fortawesome/free-regular-svg-icons';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc } from '../../../new_fields/Doc';
-import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { emptyFunction } from '../../../Utils';
-import { CompileScript } from '../../util/Scripting';
-import { ContextMenu } from '../ContextMenu';
-import { DocComponent } from '../DocComponent';
-import { OverlayView } from '../OverlayView';
-import { ScriptBox } from '../ScriptBox';
-import { DocumentIconContainer } from './DocumentIcon';
-import './DragBox.scss';
-import { FieldView, FieldViewProps } from './FieldView';
-import { DragManager } from '../../util/DragManager';
-import { Docs } from '../../documents/Documents';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Cast } from '../../../new_fields/Types';
-import { ContextMenuProps } from '../ContextMenuItem';
-
-library.add(faEdit as any);
-
-const DragSchema = createSchema({
-    onDragStart: ScriptField,
-    text: "string"
-});
-
-type DragDocument = makeInterface<[typeof DragSchema]>;
-const DragDocument = makeInterface(DragSchema);
-@observer
-export class DragBox extends DocComponent<FieldViewProps, DragDocument>(DragDocument) {
-    _downX: number = 0;
-    _downY: number = 0;
-    public static LayoutString() { return FieldView.LayoutString(DragBox); }
-    _mainCont = React.createRef<HTMLDivElement>();
-    onDragStart = (e: React.PointerEvent) => {
-        if (!e.ctrlKey && !e.altKey && !e.shiftKey && !this.props.isSelected() && e.button === 0) {
-            document.removeEventListener("pointermove", this.onDragMove);
-            document.addEventListener("pointermove", this.onDragMove);
-            document.removeEventListener("pointerup", this.onDragUp);
-            document.addEventListener("pointerup", this.onDragUp);
-            e.stopPropagation();
-            e.preventDefault();
-        }
-    }
-
-    onDragMove = async (e: MouseEvent) => {
-        if (!e.cancelBubble && (Math.abs(this._downX - e.clientX) > 5 || Math.abs(this._downY - e.clientY) > 5)) {
-            document.removeEventListener("pointermove", this.onDragMove);
-            document.removeEventListener("pointerup", this.onDragUp);
-            const onDragStart = this.Document.onDragStart;
-            e.stopPropagation();
-            e.preventDefault();
-            let dragData = new DragManager.DocumentDragData([this.props.Document]);
-            const factory = await Cast(this.props.Document.factory, Doc);// if there's a factory Doc that is being copied, make sure it's not pending.
-            dragData.removeDropProperties = factory ? Cast(factory.removeDropProperties, listSpec("string"), []) : [];
-            DragManager.StartDocumentDrag([this._mainCont.current!], dragData, e.clientX, e.clientY, {
-                finishDrag: async (dropData) => {
-                    let res = onDragStart && onDragStart.script.run({ this: this.props.Document }).result;
-                    let doc = (res as Doc) || Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" });
-                    dropData.droppedDocuments = [doc];
-                    dragData.removeDropProperties.map(prop => dropData.droppedDocuments.map((d: Doc) => d[prop] = undefined));
-                },
-                handlers: { dragComplete: emptyFunction },
-                hideSource: false
-            });
-        }
-        e.stopPropagation();
-        e.preventDefault();
-    }
-
-    onDragUp = (e: MouseEvent) => {
-        document.removeEventListener("pointermove", this.onDragMove);
-        document.removeEventListener("pointerup", this.onDragUp);
-    }
-
-    onContextMenu = () => {
-        let funcs: ContextMenuProps[] = [];
-        funcs.push({ description: "Set Make Aliases", icon: "edit", event: () => this.props.Document.factory && (this.props.Document.onDragStart = ScriptField.MakeFunction('getAlias(this.factory)')) });
-        funcs.push({ description: "Set Make Copies", icon: "edit", event: () => this.props.Document.factory && (this.props.Document.onDragStart = ScriptField.MakeFunction('getCopy(this.factory, true)')) });
-        funcs.push({
-            description: "Edit OnClick script", icon: "edit", event: () => {
-                let overlayDisposer: () => void = emptyFunction;
-                const script = this.Document.onDragStart;
-                let originalText: string | undefined = undefined;
-                if (script) originalText = script.script.originalScript;
-                // tslint:disable-next-line: no-unnecessary-callback-wrapper
-                let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => {
-                    const script = CompileScript(text, {
-                        params: { this: Doc.name },
-                        typecheck: false,
-                        editable: true,
-                        transformer: DocumentIconContainer.getTransformer()
-                    });
-                    if (!script.compiled) {
-                        onError(script.errors.map(error => error.messageText).join("\n"));
-                        return;
-                    }
-                    this.Document.onClick = new ScriptField(script);
-                    overlayDisposer();
-                }} showDocumentIcons />;
-                overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnDragStart` });
-            }
-        });
-        ContextMenu.Instance.addItem({ description: "DragBox Funcs...", subitems: funcs, icon: "asterisk" });
-    }
-
-    render() {
-        return (<div className="dragBox-outerDiv" onContextMenu={this.onContextMenu} onPointerDown={this.onDragStart} ref={this._mainCont}>
-            <FontAwesomeIcon className="dragBox-icon" icon={this.props.Document.icon as any} size="lg" color="white" />
-        </div>);
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss
new file mode 100644
index 000000000..75d093fcb
--- /dev/null
+++ b/src/client/views/nodes/FontIconBox.scss
@@ -0,0 +1,13 @@
+.fontIconBox-outerDiv {
+    width: 100%;
+    height: 100%;
+    pointer-events: all;
+    border-radius: inherit;
+    background: black;
+    border-radius: 100%;
+    svg {
+        margin:18%;
+        width:65% !important;
+        height:65%;
+    }
+}
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
new file mode 100644
index 000000000..3b580d851
--- /dev/null
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -0,0 +1,21 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { createSchema, makeInterface } from '../../../new_fields/Schema';
+import { DocComponent } from '../DocComponent';
+import './FontIconBox.scss';
+import { FieldView, FieldViewProps } from './FieldView';
+const FontIconSchema = createSchema({
+    icon: "string"
+});
+
+type FontIconDocument = makeInterface<[typeof FontIconSchema]>;
+const FontIconDocument = makeInterface(FontIconSchema);
+@observer
+export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(FontIconDocument) {
+    public static LayoutString() { return FieldView.LayoutString(FontIconBox); }
+
+    render() {
+        return <div className="fontIconBox-outerDiv" >  <FontAwesomeIcon className="fontIconBox-icon" icon={this.Document.icon as any} size="lg" color="white" />  </div>;
+    }
+}
\ No newline at end of file
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index 74a41d8cf..9f9f79dc4 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -99,9 +99,9 @@ export class CurrentUserUtils {
         }
 
         if (doc.Library === undefined) {
-            let Search = Docs.Create.ButtonDocument({ width: 50, height: 35, title: "Search" });
-            let Library = Docs.Create.ButtonDocument({ width: 50, height: 35, title: "Library" });
-            let Create = Docs.Create.ButtonDocument({ width: 35, height: 35, title: "Create" });
+            let Search = Docs.Create.ButtonDocument({ width: 50, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Search" });
+            let Library = Docs.Create.ButtonDocument({ width: 50, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Library" });
+            let Create = Docs.Create.ButtonDocument({ width: 35, height: 35, borderRounding: "50%", boxShadow: "2px 2px 1px", title: "Create" });
             if (doc.sidebarContainer === undefined) {
                 doc.sidebarContainer = new Doc();
                 (doc.sidebarContainer as Doc).chromeStatus = "disabled";
@@ -114,40 +114,39 @@ export class CurrentUserUtils {
             library.xMargin = 5;
             library.yMargin = 5;
             library.dropAction = "alias";
-            library.boxShadow = "1 1 3";
             Library.targetContainer = doc.sidebarContainer;
             Library.library = library;
             Library.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.library");
 
-            const searchBox = Docs.Create.QueryDocument({ title: "Searching" });
-            searchBox.boxShadow = "0 0";
+            const searchBox = Docs.Create.QueryDocument({ title: "search stack" });
             searchBox.ignoreClick = true;
             Search.searchBox = searchBox;
             Search.targetContainer = doc.sidebarContainer;
             Search.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.searchBox");
 
-            let createCollection = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Collection", icon: "folder" });
-            let createWebPage = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Web Page", icon: "globe-asia" });
+            let createCollection = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Collection", icon: "folder" });
+            createCollection.onDragStart = ScriptField.MakeFunction('Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" })');
+            let createWebPage = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Web Page", icon: "globe-asia" });
             createWebPage.onDragStart = ScriptField.MakeFunction('Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })');
-            let createCatImage = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Image", icon: "cat" });
+            let createCatImage = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Image", icon: "cat" });
             createCatImage.onDragStart = ScriptField.MakeFunction('Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { width: 200, title: "an image of a cat" })');
-            let createButton = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Button", icon: "bolt" });
+            let createButton = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Button", icon: "bolt" });
             createButton.onDragStart = ScriptField.MakeFunction('Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })');
-            let createPresentation = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Presentation", icon: "tv" });
+            let createPresentation = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Presentation", icon: "tv" });
             createPresentation.onDragStart = ScriptField.MakeFunction('Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { width: 200, height: 500, title: "a presentation trail" })');
-            let createFolderImport = Docs.Create.DragboxDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Import Folder", icon: "cloud-upload-alt" });
+            let createFolderImport = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, title: "Import Folder", icon: "cloud-upload-alt" });
             createFolderImport.onDragStart = ScriptField.MakeFunction('Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })');
-            const dragCreators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, chromeStatus: "disabled", title: "buttons" });
+            const dragCreators = Docs.Create.MasonryDocument([createCollection, createWebPage, createCatImage, createButton, createPresentation, createFolderImport], { width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons" });
             const color = Docs.Create.ColorDocument({ title: "color picker", width: 400 });
             color.dropAction = "alias";
             color.ignoreClick = true;
             color.removeDropProperties = new List<string>(["dropAction", "ignoreClick"]);
-            const creators = Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, chromeStatus: "disabled", title: "buttons" });
+            const creators = Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, chromeStatus: "disabled", title: "creator stack" });
             Create.targetContainer = doc.sidebarContainer;
             Create.creators = creators;
             Create.onClick = ScriptField.MakeScript("this.targetContainer.proto = this.creators");
 
-            const libraryButtons = Docs.Create.StackingDocument([Search, Library, Create], { width: 500, height: 80, chromeStatus: "disabled", title: "buttons" });
+            const libraryButtons = Docs.Create.StackingDocument([Search, Library, Create], { width: 500, height: 80, chromeStatus: "disabled", title: "library stack" });
             libraryButtons.sectionFilter = "title";
             libraryButtons.boxShadow = "0 0";
             libraryButtons.ignoreClick = true;
-- 
cgit v1.2.3-70-g09d2

