From 20f7d2dca1c115c84f6ac89981ef1e3c7c9a2757 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 22 Aug 2019 23:18:42 -0400
Subject: added start of collection script building UI

---
 src/client/views/nodes/ButtonBox.tsx | 27 +++++++++++++++++++++++++--
 1 file changed, 25 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index 8b6f11aac..ca5f0acc2 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -15,7 +15,11 @@ import { Doc } from '../../../new_fields/Doc';
 import './ButtonBox.scss';
 import { observer } from 'mobx-react';
 import { DocumentIconContainer } from './DocumentIcon';
-import { StrCast } from '../../../new_fields/Types';
+import { StrCast, BoolCast } from '../../../new_fields/Types';
+import { DragManager } from '../../util/DragManager';
+import { undoBatch } from '../../util/UndoManager';
+import { action, computed } from 'mobx';
+import { List } from '../../../new_fields/List';
 
 library.add(faEdit as any);
 
@@ -30,10 +34,29 @@ const ButtonDocument = makeInterface(ButtonSchema);
 @observer
 export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(ButtonDocument) {
     public static LayoutString() { return FieldView.LayoutString(ButtonBox); }
+    private dropDisposer?: DragManager.DragDropDisposer;
 
+    @computed get dataDoc() { return this.props.DataDoc && (BoolCast(this.props.Document.isTemplate) || BoolCast(this.props.DataDoc.isTemplate) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
+
+
+    protected createDropTarget = (ele: HTMLDivElement) => {
+        if (this.dropDisposer) {
+            this.dropDisposer();
+        }
+        if (ele) {
+            this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
+        }
+    }
+    @undoBatch
+    @action
+    drop = (e: Event, de: DragManager.DropEvent) => {
+        if (de.data instanceof DragManager.DocumentDragData) {
+            Doc.GetProto(this.dataDoc).source = new List<Doc>(de.data.droppedDocuments);
+        }
+    }
     render() {
         return (
-            <div className="buttonBox-outerDiv" >
+            <div className="buttonBox-outerDiv" ref={this.createDropTarget} >
                 <div className="buttonBox-mainButton" style={{ background: StrCast(this.props.Document.backgroundColor), color: StrCast(this.props.Document.color, "black") }} >{this.Document.text || this.Document.title}</div>
             </div>
         );
-- 
cgit v1.2.3-70-g09d2


From f4afa471e03618169137a6533db8623aae93d6c5 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Fri, 23 Aug 2019 12:42:38 -0400
Subject: got drag drop buttons working better.

---
 src/client/util/DragManager.ts                     | 30 ++++++++-
 .../views/collections/CollectionViewChromes.tsx    | 71 +++++++---------------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 11 +++-
 src/client/views/nodes/ButtonBox.scss              | 19 +++++-
 src/client/views/nodes/ButtonBox.tsx               | 42 +++++++------
 src/new_fields/Doc.ts                              | 14 ++++-
 6 files changed, 111 insertions(+), 76 deletions(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 24c093213..748fb9d13 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -1,5 +1,5 @@
 import { action, runInAction } from "mobx";
-import { Doc } from "../../new_fields/Doc";
+import { Doc, Field } from "../../new_fields/Doc";
 import { Cast, StrCast } from "../../new_fields/Types";
 import { URLField } from "../../new_fields/URLField";
 import { emptyFunction } from "../../Utils";
@@ -9,6 +9,11 @@ import { DocumentManager } from "./DocumentManager";
 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";
 
 export type dropActionType = "alias" | "copy" | undefined;
 export function SetupDrag(
@@ -247,6 +252,27 @@ export namespace DragManager {
             });
     }
 
+    export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], downX: number, downY: number, options?: DragOptions) {
+        let dragData = new DragManager.DocumentDragData([], [undefined]);
+        runInAction(() => StartDragFunctions.map(func => func()));
+        StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag :
+            (dropData: { [id: string]: any }) => {
+                let bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: title });
+                let compiled = CompileScript(script, {
+                    params: { doc: Doc.name },
+                    typecheck: false,
+                    editable: true
+                });
+                if (compiled.compiled) {
+                    let scriptField = new ScriptField(compiled);
+                    bd.onClick = scriptField;
+                }
+                params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc)));
+                bd.buttonParams = new List<string>(params);
+                dropData.droppedDocuments = [bd];
+            });
+    }
+
     export function StartLinkedDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) {
         dragData.moveDocument = moveLinkedDocument;
 
@@ -482,7 +508,7 @@ export namespace DragManager {
             // if (parent && dragEle) parent.appendChild(dragEle);
         });
         if (target) {
-            if (finishDrag) finishDrag(dragData);
+            finishDrag && finishDrag(dragData);
 
             target.dispatchEvent(
                 new CustomEvent<DropEvent>("dashOnDrop", {
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 352bcd4a6..15bef39b3 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -286,20 +286,22 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         }
     }
 
+
+    commands = [{
+        // title: "set content", script: "getProto(this.target).data = aliasDocs(this.source.map(async p => await p));", params: ["target", "source"],  // bcz: doesn't look like we can do async stuff in scripting...
+        title: "set content", script: "getProto(this.target).data = aliasDocs(this.source);", params: ["target", "source"],
+        immediate: (draggedDocs: Doc[]) => Doc.GetProto(this.props.CollectionView.props.Document).data = new List<Doc>(draggedDocs)
+    },
+    {
+        title: "set template", script: "this.target.childLayout = this.source ? this.source[0] : undefined", params: ["target", "source"],
+        immediate: (draggedDocs: Doc[]) => this.props.CollectionView.props.Document.childLayout = draggedDocs.length ? draggedDocs[0] : undefined
+    }];
     @undoBatch
     @action
     protected drop(e: Event, de: DragManager.DropEvent): boolean {
-        if (de.data instanceof DragManager.DocumentDragData) {
-            if (de.data.draggedDocuments.length) {
-                if (this._currentKey === "Set Template") {
-                    this.props.CollectionView.props.Document.childLayout = de.data.draggedDocuments[0];
-                }
-                if (this._currentKey === "Set Content") {
-                    Doc.GetProto(this.props.CollectionView.props.Document).data = new List<Doc>(de.data.draggedDocuments);
-                }
-                e.stopPropagation();
-                return true;
-            }
+        if (de.data instanceof DragManager.DocumentDragData && de.data.draggedDocuments.length) {
+            this.commands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.data.draggedDocuments));
+            e.stopPropagation();
         }
         return true;
     }
@@ -317,9 +319,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
             }
         }
     }
-
     @observable private _currentKey: string = "";
-    @observable _allCommands: string[] = ["Set Template", "Set Content"];
     private autosuggestRef = React.createRef<Autosuggest>();
 
     renderSuggestion = (suggestion: string) => {
@@ -340,38 +340,19 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
         this.suggestions = [];
     }
     getKeySuggestions = async (value: string): Promise<string[]> => {
-        return this._allCommands.filter(c => c.indexOf(value) !== -1);
+        return this.commands.filter(c => c.title.indexOf(value) !== -1).map(c => c.title);
     }
 
     autoSuggestDown = (e: React.PointerEvent) => {
         e.stopPropagation();
     }
+
     dragCommandDown = (e: React.PointerEvent) => {
-        let de = new DragManager.DocumentDragData([this.props.CollectionView.props.Document], [undefined]);
-        DragManager.StartDocumentDrag([this._commandRef.current!], de, e.clientX, e.clientY, {
-            finishDrag: (dropData: { [id: string]: any }) => {
-                let bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: this._currentKey });
-                let script = `getProto(target).data = copyField(this.source);`;
-                let compiled = CompileScript(script, {
-                    params: { doc: Doc.name },
-                    capturedVariables: { target: this.props.CollectionView.props.Document },
-                    typecheck: false,
-                    editable: true
-                });
-                if (compiled.compiled) {
-                    let scriptField = new ScriptField(compiled);
-                    bd.onClick = scriptField;
-                }
-                dropData.droppedDocuments = [bd];
-            },
-            handlers: {
-                dragComplete: action(() => {
-                }),
-            },
-            hideSource: false
-        });
-        e.preventDefault();
+        this.commands.filter(c => c.title === this._currentKey).map(c =>
+            DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title,
+                { target: this.props.CollectionView.props.Document }, c.params, e.clientX, e.clientY));
         e.stopPropagation();
+        e.preventDefault();
     }
 
     render() {
@@ -419,7 +400,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                                 <div className="collectionViewBaseChrome-viewSpecsMenu-row">
                                     <div className="collectionViewBaseChrome-viewSpecsMenu-rowLeft">
                                         CREATED WITHIN:
-                            </div>
+                                    </div>
                                     <select className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle"
                                         style={{ textTransform: "uppercase", textAlign: "center" }}
                                         value={this._dateWithinValue}
@@ -442,15 +423,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
                                         placeholder="Value" />
                                 </div>
                                 <div className="collectionViewBaseChrome-viewSpecsMenu-lastRow">
-                                    <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.addKeyRestriction}>
-                                        ADD KEY RESTRICTION
-                            </button>
-                                    <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.applyFilter}>
-                                        APPLY FILTER
-                            </button>
-                                    <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.clearFilter}>
-                                        CLEAR
-                            </button>
+                                    <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.addKeyRestriction}> ADD KEY RESTRICTION </button>
+                                    <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.applyFilter}> APPLY FILTER </button>
+                                    <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.clearFilter}> CLEAR </button>
                                 </div>
                             </div>
                         </div>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 224e8047d..0a2dcbe3b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -187,8 +187,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
 
     componentDidMount() {
         this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)],
-            async (args) => args[1] instanceof Doc &&
-                this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined)));
+            async (args) => {
+                this.childDocs.filter(doc => args[1] instanceof Doc || doc.layout instanceof Doc).map(async doc => {
+                    if (!Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc)) {
+                        Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined);
+                    }
+                });
+            });
     }
     componentWillUnmount() {
         this._childLayoutDisposer && this._childLayoutDisposer();
@@ -291,7 +296,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
         if (super.drop(e, de)) {
             if (de.data instanceof DragManager.DocumentDragData) {
                 if (de.data.droppedDocuments.length) {
-                    let z = NumCast(de.data.draggedDocuments[0].z);
+                    let z = NumCast(de.data.droppedDocuments[0].z);
                     let x = (z ? xpo : xp) - de.data.xOffset;
                     let y = (z ? ypo : yp) - de.data.yOffset;
                     let dropX = NumCast(de.data.droppedDocuments[0].x);
diff --git a/src/client/views/nodes/ButtonBox.scss b/src/client/views/nodes/ButtonBox.scss
index 5ed435505..75a790667 100644
--- a/src/client/views/nodes/ButtonBox.scss
+++ b/src/client/views/nodes/ButtonBox.scss
@@ -3,14 +3,29 @@
     height: 100%;
     pointer-events: all;
     border-radius: inherit;
-    display:table;
+    display:flex;
+    flex-direction: column;
 }
 
 .buttonBox-mainButton {
     width: 100%;
     height: 100%;
     border-radius: inherit;
+    text-align: center;
+    display: table;
+}
+.buttonBox-mainButtonCenter {
+    height: 100%;
     display:table-cell;
     vertical-align: middle;
-    text-align: center;
+}
+
+.buttonBox-params {
+     display:flex;
+     flex-direction: row;
+}
+
+.buttonBox-missingParam {
+    width:100%;
+    background: lightgray;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx
index ca5f0acc2..54848344b 100644
--- a/src/client/views/nodes/ButtonBox.tsx
+++ b/src/client/views/nodes/ButtonBox.tsx
@@ -1,25 +1,19 @@
-import * as React from 'react';
-import { FieldViewProps, FieldView } from './FieldView';
-import { createSchema, makeInterface } from '../../../new_fields/Schema';
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { DocComponent } from '../DocComponent';
-import { ContextMenu } from '../ContextMenu';
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faEdit } from '@fortawesome/free-regular-svg-icons';
-import { emptyFunction } from '../../../Utils';
-import { ScriptBox } from '../ScriptBox';
-import { CompileScript } from '../../util/Scripting';
-import { OverlayView } from '../OverlayView';
-import { Doc } from '../../../new_fields/Doc';
-
-import './ButtonBox.scss';
+import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
-import { DocumentIconContainer } from './DocumentIcon';
-import { StrCast, BoolCast } from '../../../new_fields/Types';
+import * as React from 'react';
+import { Doc, DocListCastAsync } from '../../../new_fields/Doc';
+import { List } from '../../../new_fields/List';
+import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema';
+import { ScriptField } from '../../../new_fields/ScriptField';
+import { BoolCast, StrCast, Cast } from '../../../new_fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
-import { action, computed } from 'mobx';
-import { List } from '../../../new_fields/List';
+import { DocComponent } from '../DocComponent';
+import './ButtonBox.scss';
+import { FieldView, FieldViewProps } from './FieldView';
+
 
 library.add(faEdit as any);
 
@@ -52,12 +46,24 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.data instanceof DragManager.DocumentDragData) {
             Doc.GetProto(this.dataDoc).source = new List<Doc>(de.data.droppedDocuments);
+            e.stopPropagation();
         }
     }
+    // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")")
     render() {
+        let params = Cast(this.props.Document.buttonParams, listSpec("string"));
+        let missingParams = params && params.filter(p => this.props.Document[p] === undefined);
+        params && params.map(async p => await DocListCastAsync(this.props.Document[p])); // bcz: really hacky form of prefetching ... 
         return (
             <div className="buttonBox-outerDiv" ref={this.createDropTarget} >
-                <div className="buttonBox-mainButton" style={{ background: StrCast(this.props.Document.backgroundColor), color: StrCast(this.props.Document.color, "black") }} >{this.Document.text || this.Document.title}</div>
+                <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)}
+                    </div>
+                </div>
+                <div className="buttonBox-params" >
+                    {!missingParams || !missingParams.length ? (null) : missingParams.map(m => <div key={m} className="buttonBox-missingParam">{m}</div>)}
+                </div>
             </div>
         );
     }
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index f6114d476..4833ad259 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -525,9 +525,18 @@ export namespace Doc {
         return otherdoc;
     }
     export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) {
+        if (!templateDoc) {
+            target.layout = undefined;
+            target.nativeWidth = undefined;
+            target.nativeHeight = undefined;
+            target.onClick = undefined;
+            return;
+        }
         let temp = Doc.MakeDelegate(templateDoc);
         target.nativeWidth = Doc.GetProto(target).nativeWidth = undefined;
         target.nativeHeight = Doc.GetProto(target).nativeHeight = undefined;
+        !templateDoc.nativeWidth && (target.nativeWidth = 0);
+        !templateDoc.nativeHeight && (target.nativeHeight = 0);
         target.width = templateDoc.width;
         target.height = templateDoc.height;
         target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy]();
@@ -541,8 +550,6 @@ export namespace Doc {
             target.miniLayout = StrCast(templateDoc.miniLayout);
             target.detailedLayout = target.layout;
         }
-        !templateDoc.nativeWidth && (target.nativeWidth = 0);
-        !templateDoc.nativeHeight && (target.nativeHeight = 0);
     }
 
     export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) {
@@ -627,4 +634,5 @@ export namespace Doc {
 }
 Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(doc.title).replace(/\([0-9]*\)/, "") + `(${n})`; });
 Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
-Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); });
\ No newline at end of file
+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))); });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

