From dab8f5893ffb8fab05a46695b9d1a690d1171bca Mon Sep 17 00:00:00 2001
From: Stanley Yip <stanley_yip@brown.edu>
Date: Sun, 2 Feb 2020 19:18:23 -0500
Subject: some more stuff

---
 src/client/views/collections/CollectionLinearView.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 67062ae41..e613bf411 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -82,13 +82,14 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
 
     render() {
         const guid = Utils.GenerateGuid();
+        const flexDir: any = StrCast(this.Document.flexDirection);
         return <div className="collectionLinearView-outer">
             <div className="collectionLinearView" ref={this.createDashEventsTarget} >
                 <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.isExpanded)} ref={this.addMenuToggle}
                     onChange={action((e: any) => this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} />
                 <label htmlFor={`${guid}`} style={{ marginTop: "auto", marginBottom: "auto", background: StrCast(this.props.Document.backgroundColor, "black") === StrCast(this.props.Document.color, "white") ? "black" : StrCast(this.props.Document.backgroundColor, "black") }} title="Close Menu"><p>+</p></label>
 
-                <div className="collectionLinearView-content" style={{ height: this.dimension(), width: NumCast(this.props.Document._width, 25) }}>
+                <div className="collectionLinearView-content" style={{ height: this.dimension(), width: NumCast(this.props.Document._width, 25), flexDirection: flexDir }}>
                     {this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => {
                         const nested = pair.layout._viewType === CollectionViewType.Linear;
                         const dref = React.createRef<HTMLDivElement>();
-- 
cgit v1.2.3-70-g09d2


From 8b71007c98f3c5f5092d15c6ce91142729bcec22 Mon Sep 17 00:00:00 2001
From: bob <bcz@cs.brown.edu>
Date: Tue, 3 Mar 2020 14:02:17 -0500
Subject: fixed dragging from linear view because of screentransform.  added
 [[key=value]] syntax.  added button for seeing enumerated values.    added
 step up button in DocDec for selecting parent collection

---
 src/client/util/RichTextRules.ts                   |  8 ++-
 src/client/util/RichTextSchema.tsx                 | 57 +++++++++++++++-------
 src/client/views/DocumentDecorations.scss          | 11 +++++
 src/client/views/DocumentDecorations.tsx           | 16 +++++-
 .../views/collections/CollectionLinearView.tsx     |  2 +-
 src/new_fields/Doc.ts                              | 34 ++++++-------
 6 files changed, 89 insertions(+), 39 deletions(-)

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

diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts
index 70a1a5154..7ffc2dd9c 100644
--- a/src/client/util/RichTextRules.ts
+++ b/src/client/util/RichTextRules.ts
@@ -81,10 +81,11 @@ export class RichTextRules {
 
             // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document [[ <fieldKey> : <Doc>]]   // [[:Doc]] => hyperlink   [[fieldKey]] => show field   [[fieldKey:Doc]] => show field of doc
             new InputRule(
-                new RegExp(/\[\[([a-zA-Z_#@\? \-0-9]*)(:[a-zA-Z_#@\? \-0-9]+)?\]\]$/),
+                new RegExp(/\[\[([a-zA-Z_#@\? \-0-9]*)(=[a-zA-Z_#@\? \-0-9]*)?(:[a-zA-Z_#@\? \-0-9]+)?\]\]$/),
                 (state, match, start, end) => {
                     const fieldKey = match[1];
-                    const docid = match[2]?.substring(1);
+                    const docid = match[3]?.substring(1);
+                    const value = match[2]?.substring(1);
                     if (!fieldKey) {
                         if (docid) {
                             DocServer.GetRefField(docid).then(docx => {
@@ -97,6 +98,9 @@ export class RichTextRules {
                         }
                         return state.tr;
                     }
+                    if (value !== "") {
+                        this.Document[DataSym][fieldKey] = value;
+                    }
                     const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid });
                     return state.tr.deleteRange(start, end).insert(start, fieldView);
                 }),
diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx
index 2ef22b6a0..034f5d55a 100644
--- a/src/client/util/RichTextSchema.tsx
+++ b/src/client/util/RichTextSchema.tsx
@@ -8,28 +8,25 @@ import { EditorState, NodeSelection, Plugin, TextSelection } from "prosemirror-s
 import { StepMap } from "prosemirror-transform";
 import { EditorView } from "prosemirror-view";
 import * as ReactDOM from 'react-dom';
-import { Doc, Field, HeightSym, WidthSym, DocListCast } from "../../new_fields/Doc";
+import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../new_fields/Doc";
 import { Id } from "../../new_fields/FieldSymbols";
+import { List } from "../../new_fields/List";
 import { ObjectField } from "../../new_fields/ObjectField";
+import { listSpec } from "../../new_fields/Schema";
+import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
 import { ComputedField } from "../../new_fields/ScriptField";
-import { BoolCast, NumCast, StrCast, Cast } from "../../new_fields/Types";
+import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types";
 import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
+import { Docs } from "../documents/Documents";
+import { CollectionViewType } from "../views/collections/CollectionView";
+import { ContextMenu } from "../views/ContextMenu";
 import { DocumentView } from "../views/nodes/DocumentView";
 import { FormattedTextBox } from "../views/nodes/FormattedTextBox";
 import { DocumentManager } from "./DocumentManager";
 import ParagraphNodeSpec from "./ParagraphNodeSpec";
 import { Transform } from "./Transform";
 import React = require("react");
-import { CollectionSchemaBooleanCell } from "../views/collections/CollectionSchemaCells";
-import { ContextMenu } from "../views/ContextMenu";
-import { ContextMenuProps } from "../views/ContextMenuItem";
-import { Docs } from "../documents/Documents";
-import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
-import { toBlob } from "html-to-image";
-import { listSpec } from "../../new_fields/Schema";
-import { List } from "../../new_fields/List";
-import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField";
 
 const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"],
     preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0];
@@ -839,8 +836,8 @@ export class DashDocView {
                 zoomToScale={emptyFunction}
                 getScale={returnOne}
                 dontRegisterView={false}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
+                ContainingCollectionView={this._textBox.props.ContainingCollectionView}
+                ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
                 ContentScaling={this.contentScaling}
             />, this._dashSpan);
             if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") {
@@ -863,6 +860,7 @@ export class DashFieldView {
     _fieldWrapper: HTMLDivElement; // container for label and value
     _labelSpan: HTMLSpanElement; // field label
     _fieldSpan: HTMLDivElement;  // field value
+    _enumerables: HTMLDivElement;  // field value
     _reactionDisposer: IReactionDisposer | undefined;
     _textBoxDoc: Doc;
     @observable _dashDoc: Doc | undefined;
@@ -879,6 +877,19 @@ export class DashFieldView {
         this._fieldWrapper.style.display = "inline-block";
 
         const self = this;
+        this._enumerables = document.createElement("div");
+        this._enumerables.style.width = "10px";
+        this._enumerables.style.height = "10px";
+        this._enumerables.style.position = "relative";
+        this._enumerables.style.display = "none";
+        this._enumerables.style.background = "dimGray";
+
+        this._enumerables.onpointerdown = async (e) => {
+            e.stopPropagation();
+            const collview = await Doc.addEnumerationToTextField(self._textBoxDoc, node.attrs.fieldKey, [Docs.Create.TextDocument(self._fieldSpan.innerText, { title: self._fieldSpan.innerText })]);
+            collview instanceof Doc && tbox.props.addDocTab(collview, "onRight");
+        }
+
         this._fieldSpan = document.createElement("div");
         this._fieldSpan.id = Utils.GenerateGuid();
         this._fieldSpan.contentEditable = "true";
@@ -888,7 +899,10 @@ export class DashFieldView {
         this._fieldSpan.style.backgroundColor = "rgba(155, 155, 155, 0.24)";
         this._fieldSpan.onkeypress = function (e: any) { e.stopPropagation(); };
         this._fieldSpan.onkeyup = function (e: any) { e.stopPropagation(); };
-        this._fieldSpan.onmousedown = function (e: any) { e.stopPropagation(); };
+        this._fieldSpan.onmousedown = function (e: any) {
+            e.stopPropagation();
+            self._enumerables.style.display = "inline-block";
+        };
         this._fieldSpan.oncontextmenu = function (e: any) {
             ContextMenu.Instance.addItem({
                 description: "Show Enumeration Templates", event: () => {
@@ -898,15 +912,17 @@ export class DashFieldView {
             });
         };
         this._fieldSpan.onblur = function (e: any) {
+            self._enumerables.style.display = "none";
             let newText = self._fieldSpan.innerText.startsWith(":=") ? ":=-computed-" : self._fieldSpan.innerText;
+
             // look for a document whose id === the fieldKey being displayed.  If there's a match, then that document
             // holds the different enumerated values for the field in the titles of its collected documents.
             // if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down.
-
-            // alternatively, if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
             DocServer.GetRefField(node.attrs.fieldKey).then(options => {
                 (options instanceof Doc) && DocListCast(options.data).forEach(opt => StrCast(opt.title).startsWith(newText) && (newText = StrCast(opt.title)));
                 self._fieldSpan.innerHTML = self._dashDoc![self._fieldKey] = newText;
+
+                // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
                 if (newText.startsWith(":=") && self._dashDoc && e.data === null && !e.inputType.includes("delete")) {
                     Doc.Layout(tbox.props.Document)[self._fieldKey] = ComputedField.MakeFunction(self._fieldSpan.innerText.substring(2));
                 }
@@ -947,8 +963,12 @@ export class DashFieldView {
         this._labelSpan.title = "click to see related tags";
         this._labelSpan.onpointerdown = function (e: any) {
             e.stopPropagation();
-            if (tbox.props.ContainingCollectionDoc) {
-                const alias = Doc.MakeAlias(tbox.props.ContainingCollectionDoc);
+            let container = tbox.props.ContainingCollectionView;
+            while (container?.props.Document.isTemplateForField || container?.props.Document.isTemplateDoc) {
+                container = container.props.ContainingCollectionView;
+            }
+            if (container) {
+                const alias = Doc.MakeAlias(container.props.Document);
                 alias.viewType = CollectionViewType.Time;
                 let list = Cast(alias.schemaColumns, listSpec(SchemaHeaderField));
                 if (!list) {
@@ -974,6 +994,7 @@ export class DashFieldView {
 
         this._fieldWrapper.appendChild(this._labelSpan);
         this._fieldWrapper.appendChild(this._fieldSpan);
+        this._fieldWrapper.appendChild(this._enumerables);
         (this as any).dom = this._fieldWrapper;
     }
     destroy() {
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 1992c5efa..fdf7c3f42 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -27,6 +27,17 @@ $linkGap : 3px;
         opacity: 1;
     }
 
+    .documentDecorations-selector {
+        pointer-events: auto;
+        height: 15px;
+        width: 15px;
+        left: -20px;
+        top: 20px;
+        display: inline-block;
+        position: absolute;
+        opacity: 0.5;
+    }
+
     .documentDecorations-radius {
         pointer-events: auto;
         background: black;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4922411e8..c98be0d4a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -8,7 +8,7 @@ import { PositionDocument } from '../../new_fields/documentSchemas';
 import { ScriptField } from '../../new_fields/ScriptField';
 import { Cast, StrCast, NumCast } from "../../new_fields/Types";
 import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils';
-import { Utils, setupMoveUpEvents } from "../../Utils";
+import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils";
 import { DocUtils } from "../documents/Documents";
 import { DocumentType } from '../documents/DocumentTypes';
 import { DragManager } from "../util/DragManager";
@@ -244,6 +244,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         SelectionManager.DeselectAll();
     }
 
+    @action
+    onSelectorUp = (e: React.PointerEvent): void => {
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, action((e) => {
+            const selDoc = SelectionManager.SelectedDocuments()?.[0];
+            if (selDoc) {
+                selDoc.props.ContainingCollectionView?.props.select(false);
+            }
+        }));
+    }
+
     @action
     onRadiusDown = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, this.onRadiusMove, (e) => this._resizeUndo?.end(), (e) => { });
@@ -496,6 +506,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
                     onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
                 <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"
                     onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+                {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : <div id="documentDecorations-levelSelector" className="documentDecorations-selector" title="tap to select containing document"
+                    onPointerDown={this.onSelectorUp} onContextMenu={(e) => e.preventDefault()}>
+                    <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" />
+                </div>}
                 <div id="documentDecorations-borderRadius" className="documentDecorations-radius"
                     onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>
 
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
index 9384eb381..79ec6d518 100644
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ b/src/client/views/collections/CollectionLinearView.tsx
@@ -77,7 +77,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
     getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
         if (!ele.current) return Transform.Identity();
         const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
-        return new Transform(-translateX, -translateY, 1 / scale);
+        return new Transform(-translateX, -translateY, 1);
     }
 
     render() {
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index 0f3896055..6d94f050c 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -865,25 +865,25 @@ export namespace Doc {
         return (await DocServer.GetRefField(enumeratedFieldKey)) as Doc;
     }
 
-    export function addEnumerationToTextField(doc: Opt<Doc>, enumeratedFieldKey: string, enumeratedDocs: Doc[]) {
-        DocServer.GetRefField(enumeratedFieldKey).then(optionsCollection => {
-            if (!(optionsCollection instanceof Doc)) {
-                optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey);
-                Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
+    export async function addEnumerationToTextField(doc: Opt<Doc>, enumeratedFieldKey: string, enumeratedDocs: Doc[]) {
+        let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey);
+        if (!(optionsCollection instanceof Doc)) {
+            optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey);
+            Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc);
+        }
+        const options = optionsCollection as Doc;
+        doc && (Doc.GetProto(doc).backgroundColor = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey})?._backgroundColor || "white"`, undefined, { options }));
+        doc && (Doc.GetProto(doc).color = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey}).color || "black"`, undefined, { options }));
+        enumeratedDocs.map(enumeratedDoc => {
+            const found = DocListCast(options.data).find(d => d.title === enumeratedDoc.title);
+            if (found) {
+                found._backgroundColor = enumeratedDoc._backgroundColor || found._backgroundColor;
+                found._color = enumeratedDoc._color || found._color;
+            } else {
+                Doc.AddDocToList(options, "data", enumeratedDoc);
             }
-            const options = optionsCollection as Doc;
-            doc && (Doc.GetProto(doc).backgroundColor = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey})?._backgroundColor || "white"`, undefined, { options }));
-            doc && (Doc.GetProto(doc).color = ComputedField.MakeFunction(`options.data.find(doc => doc.title === (this.expandedTemplate||this).${enumeratedFieldKey}).color || "black"`, undefined, { options }));
-            enumeratedDocs.map(enumeratedDoc => {
-                const found = DocListCast(options.data).find(d => d.title === enumeratedDoc.title);
-                if (found) {
-                    found._backgroundColor = enumeratedDoc._backgroundColor || found._backgroundColor;
-                    found._color = enumeratedDoc._color || found._color;
-                } else {
-                    Doc.AddDocToList(options, "data", enumeratedDoc);
-                }
-            });
         });
+        return optionsCollection;
     }
 }
 
-- 
cgit v1.2.3-70-g09d2

