From 158b589c2a5c9d2deb537e827ca31f93335957ea Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 22 Feb 2024 08:17:13 -0500
Subject: turned off zoom scaling for text boxes until it can be done more
 cleanly.  fixed search to switch tabs when doc is inactive tab.

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

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

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f2c4c6c8f..56008de8e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2019,7 +2019,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     }
     render() {
         TraceMobx();
-        const scale = (this._props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
+        const scale = this._props.NativeDimScaling?.() || 1; // * NumCast(this.layoutDoc._freeform_scale, 1);
         const rounded = StrCast(this.layoutDoc.layout_borderRounding) === '100%' ? '-rounded' : '';
         setTimeout(() => !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
         const paddingX = NumCast(this.layoutDoc._xMargin, this._props.xPadding || 0);
-- 
cgit v1.2.3-70-g09d2


From 2e99ef0512b8f1b5c089637643ff6addf5afd943 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 4 Mar 2024 16:07:52 -0500
Subject: cleaned up a number of things related to autoHeight and
 stacking/masonry/notetaking views

---
 src/Utils.ts                                       |   7 ++
 src/client/views/ContextMenu.tsx                   |   5 +-
 src/client/views/DocumentDecorations.tsx           |  10 +-
 src/client/views/PropertiesButtons.tsx             |   3 +-
 .../views/collections/CollectionDockingView.tsx    |   6 +-
 .../collections/CollectionMasonryViewFieldRow.tsx  |  15 ++-
 .../collections/CollectionNoteTakingView.scss      |   1 +
 .../views/collections/CollectionNoteTakingView.tsx | 107 +++++++++------------
 .../collections/CollectionNoteTakingViewColumn.tsx |  22 ++---
 .../views/collections/CollectionStackingView.tsx   |  73 ++++----------
 .../CollectionStackingViewFieldColumn.tsx          |  23 +++--
 .../views/collections/CollectionTreeView.tsx       |   8 +-
 src/client/views/nodes/DocumentView.tsx            |   7 +-
 src/client/views/nodes/EquationBox.tsx             |   5 +-
 src/client/views/nodes/WebBox.tsx                  |   6 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   4 +-
 16 files changed, 136 insertions(+), 166 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index e8bd35ac4..38325a463 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -902,6 +902,13 @@ export function setupMoveUpEvents(
     document.addEventListener('click', _clickEvent, true);
 }
 
+export function DivHeight(ele: HTMLElement): number {
+    return Number(getComputedStyle(ele).height.replace('px', ''));
+}
+export function DivWidth(ele: HTMLElement): number {
+    return Number(getComputedStyle(ele).width.replace('px', ''));
+}
+
 export function dateRangeStrToDates(dateStr: string) {
     // dateStr in yyyy-mm-dd format
     const dateRangeParts = dateStr.split('|'); // splits into from and to date
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 8c3c9df2e..8f4e43978 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -7,6 +7,7 @@ import { SettingsManager } from '../util/SettingsManager';
 import './ContextMenu.scss';
 import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem';
 import { ObservableReactComponent } from './ObservableReactComponent';
+import { DivHeight, DivWidth } from '../../Utils';
 
 @observer
 export class ContextMenu extends ObservableReactComponent<{}> {
@@ -214,8 +215,8 @@ export class ContextMenu extends ObservableReactComponent<{}> {
                 className="contextMenu-cont"
                 ref={action((r: any) => {
                     if (r) {
-                        this._width = Number(getComputedStyle(r).width.replace('px', ''));
-                        this._height = Number(getComputedStyle(r).height.replace('px', ''));
+                        this._width = DivWidth(r);
+                        this._height = DivHeight(r);
                     }
                 })}
                 style={{
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9d53c58c0..87ee962a0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -562,7 +562,13 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             doc.y = NumCast(doc.y) + deltaY;
 
             doc._layout_modificationDate = new DateField();
-            scale.y !== 1 && (doc._layout_autoHeight = undefined);
+            if (scale.y !== 1) {
+                docView.layoutDoc._layout_autoHeight = undefined;
+                if (docView.layoutDoc._layout_autoHeight) {
+                    // if autoHeight is still on because of a prototype
+                    docView.layoutDoc._layout_autoHeight = false; // then don't inherit, but explicitly set it to false
+                }
+            }
         }
     };
 
@@ -597,7 +603,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         this._resizeUndo?.end();
 
         // detect layout_autoHeight gesture and apply
-        SelectionManager.Docs.forEach(doc => NumCast(doc._height) < 20 && (doc._layout_autoHeight = true));
+        SelectionManager.Views.forEach(view => NumCast(view.Document._height) < 20 && (view.layoutDoc._layout_autoHeight = true));
         //need to change points for resize, or else rotation/control points will fail.
         this._inkDragDocs
             .map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index cb38ab602..3cb835e39 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -486,8 +486,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const isImage = layoutField instanceof ImageField;
         const isMap = this.selectedDoc?.type === DocumentType.MAP;
         const isCollection = this.selectedDoc?.type === DocumentType.COL;
-        //TODO: will likely need to create separate note-taking view type here
-        const isStacking = this.selectedDoc?._type_collection === CollectionViewType.Stacking || this.selectedDoc?._type_collection === CollectionViewType.NoteTaking;
+        const isStacking = [CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.NoteTaking].includes(this.selectedDoc?._type_collection as any);
         const isFreeForm = this.selectedDoc?._type_collection === CollectionViewType.Freeform;
         const isTree = this.selectedDoc?._type_collection === CollectionViewType.Tree;
         const isTabView = this.selectedTabView;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index d9d6a5eb5..8f1633122 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -11,7 +11,7 @@ import { List } from '../../../fields/List';
 import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { GetEffectiveAcl, inheritParentAcls } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, incrementTitleCopy } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivHeight, DivWidth, emptyFunction, incrementTitleCopy } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
 import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
@@ -432,8 +432,8 @@ export class CollectionDockingView extends CollectionSubView() {
     public CaptureThumbnail() {
         const content = this.DocumentView?.()?.ContentDiv;
         if (content) {
-            const _width = Number(getComputedStyle(content).width.replace('px', ''));
-            const _height = Number(getComputedStyle(content).height.replace('px', ''));
+            const _width = DivWidth(content);
+            const _height = DivHeight(content);
             return CollectionFreeFormView.UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', (iconFile, _nativeWidth, _nativeHeight) => {
                 const proto = this.dataDoc; // Cast(img.proto, Doc, null)!;
                 proto['thumb_nativeWidth'] = _width;
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 41c5d5b42..763d2e3a6 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -33,8 +33,7 @@ interface CMVFieldRowProps {
     createDropTarget: (ele: HTMLDivElement) => void;
     screenToLocalTransform: () => Transform;
     setDocHeight: (key: string, thisHeight: number) => void;
-    observeHeight: (myref: any) => void;
-    unobserveHeight: (myref: any) => void;
+    refList: any[];
     showHandle: boolean;
 }
 
@@ -68,20 +67,20 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
 
     createRowDropRef = (ele: HTMLDivElement | null) => {
         this._dropDisposer?.();
-        if (ele) {
-            this._ele = ele;
-            this._props.observeHeight(ele);
-            this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Document);
-        }
+        if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Document);
+        else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+        this._ele = ele;
     };
     @action
     componentDidMount() {
         this.heading = this._props.headingObject?.heading || '';
         this.color = this._props.headingObject?.color || '#f1efeb';
         this.collapsed = this._props.headingObject?.collapsed || false;
+        this._ele && this.props.refList.push(this._ele);
     }
     componentWillUnmount() {
-        this._props.unobserveHeight(this._ele);
+        this._ele && this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+        this._ele = null;
     }
 
     getTrueHeight = () => {
diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index 91a82d40f..4c2dcf9ab 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -98,6 +98,7 @@
         overflow: auto;
         display: flex;
         flex-direction: column;
+        height: max-content;
     }
 
     .collectionSchemaView-previewDoc {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index b8133806f..6318620e0 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, observe, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, Field, Opt } from '../../../fields/Doc';
@@ -9,7 +9,7 @@ import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnFalse, returnZero, smoothScroll, Utils } from '../../../Utils';
+import { DivHeight, emptyFunction, returnFalse, returnZero, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -45,6 +45,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     public DividerWidth = 16;
     @observable docsDraggedRowCol: number[] = [];
     @observable _scroll = 0;
+    @observable _refList: any[] = [];
 
     constructor(props: any) {
         super(props);
@@ -157,8 +158,16 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         document.addEventListener('pointerup', this.removeDocDragHighlight, true);
         this._disposers.layout_autoHeight = reaction(
             () => this.layoutDoc._layout_autoHeight,
-            layout_autoHeight =>
-                layout_autoHeight && this._props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))))
+            layout_autoHeight => layout_autoHeight && this._props.setHeight?.(this.headerMargin + Math.max(...this._refList.map(DivHeight)))
+        );
+
+        this._disposers.refList = reaction(
+            () => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !LightboxView.Contains(this.DocumentView?.()) }),
+            ({ refList, autoHeight }) => {
+                if (autoHeight) refList.forEach(r => this.observer.observe(r));
+                else this.observer.disconnect();
+            },
+            { fireImmediately: true }
         );
     }
 
@@ -345,7 +354,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
             // get the index for where you need to insert the doc you are currently dragging
             const clientY = this.ScreenToLocalBoxXf().transformPoint(ex, ey)[1];
             let dropInd = -1;
-            let pos0 = (this.refList.lastElement() as HTMLDivElement).children[0].getBoundingClientRect().height + this.yMargin * 2;
+            let pos0 = (this._refList.lastElement() as HTMLDivElement).children[0].getBoundingClientRect().height + this.yMargin * 2;
             colDocs.forEach((doc, i) => {
                 let pos1 = this.getDocHeight(doc) + 2 * this.gridGap;
                 pos1 += pos0;
@@ -424,7 +433,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
             if (super.onInternalDrop(e, de)) {
                 // filter out the currently dragged docs from the child docs, since we will insert them later
                 const rowCol = this.docsDraggedRowCol;
-                const droppedDocs = this.childDocs.slice().filter((d: Doc, ind: number) => ind >= this.childDocs.length); // if the drop operation adds something to the end of the list, then use that as the new document (may be different than what was dropped e.g., in the case of a button which is dropped but which creates say, a note).
+                const droppedDocs = this.childDocs.filter((d: Doc, ind: number) => ind >= this.childDocs.length); // if the drop operation adds something to the end of the list, then use that as the new document (may be different than what was dropped e.g., in the case of a button which is dropped but which creates say, a note).
                 const newDocs = droppedDocs.length ? droppedDocs : de.complete.docDragData.droppedDocuments;
                 const docs = this.childDocList;
                 if (docs && newDocs.length) {
@@ -489,65 +498,45 @@ export class CollectionNoteTakingView extends CollectionSubView() {
 
     headings = () => Array.from(this.Sections);
 
-    refList: any[] = [];
-
     editableViewProps = () => ({
         GetValue: () => '',
         SetValue: this.addGroup,
         contents: '+ New Column',
     });
 
+    refList = () => this._refList;
+
     // sectionNoteTaking returns a CollectionNoteTakingViewColumn (which is an individual column)
-    sectionNoteTaking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
-        const type = 'number';
-        return (
-            <CollectionNoteTakingViewColumn
-                key={heading?.heading ?? 'unset'}
-                unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
-                observeHeight={ref => {
-                    if (ref) {
-                        this.refList.push(ref);
-                        this.observer = new _global.ResizeObserver(
-                            action((entries: any) => {
-                                if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.IsDragging) {
-                                    const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
-                                    if (!LightboxView.Contains(this.DocumentView?.())) {
-                                        this._props.setHeight?.(height);
-                                    }
-                                }
-                            })
-                        );
-                        this.observer.observe(ref);
-                    }
-                }}
-                PanelWidth={this._props.PanelWidth}
-                select={this._props.select}
-                addDocument={this.addDocument}
-                chromeHidden={this.chromeHidden}
-                colHeaderData={this.colHeaderData}
-                Document={this.Document}
-                TemplateDataDocument={this._props.TemplateDataDocument}
-                resizeColumns={this.resizeColumns}
-                renderChildren={this.children}
-                numGroupColumns={this.numGroupColumns}
-                gridGap={this.gridGap}
-                pivotField={this.notetakingCategoryField}
-                fieldKey={this.fieldKey}
-                dividerWidth={this.DividerWidth}
-                maxColWidth={this.maxColWidth}
-                availableWidth={this.availableWidth}
-                headings={this.headings}
-                heading={heading?.heading ?? 'unset'}
-                headingObject={heading}
-                docList={docList}
-                yMargin={this.yMargin}
-                type={type}
-                createDropTarget={this.createDashEventsTarget}
-                screenToLocalTransform={this.ScreenToLocalBoxXf}
-                editableViewProps={this.editableViewProps}
-            />
-        );
-    };
+    sectionNoteTaking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => (
+        <CollectionNoteTakingViewColumn
+            key={heading?.heading ?? 'unset'}
+            PanelWidth={this._props.PanelWidth}
+            refList={this._refList}
+            select={this._props.select}
+            addDocument={this.addDocument}
+            chromeHidden={this.chromeHidden}
+            colHeaderData={this.colHeaderData}
+            Document={this.Document}
+            TemplateDataDocument={this._props.TemplateDataDocument}
+            resizeColumns={this.resizeColumns}
+            renderChildren={this.children}
+            numGroupColumns={this.numGroupColumns}
+            gridGap={this.gridGap}
+            pivotField={this.notetakingCategoryField}
+            fieldKey={this.fieldKey}
+            dividerWidth={this.DividerWidth}
+            maxColWidth={this.maxColWidth}
+            availableWidth={this.availableWidth}
+            headings={this.headings}
+            heading={heading?.heading ?? 'unset'}
+            headingObject={heading}
+            docList={docList}
+            yMargin={this.yMargin}
+            createDropTarget={this.createDashEventsTarget}
+            screenToLocalTransform={this.ScreenToLocalBoxXf}
+            editableViewProps={this.editableViewProps}
+        />
+    );
 
     // addGroup is called when adding a new columnHeader, adding a SchemaHeaderField to our list of
     // columnHeaders and resizing the existing columns to make room for our new one.
@@ -619,7 +608,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         return this.isContentActive() === false ? 'none' : undefined;
     }
 
-    observer: any;
+    observer = new _global.ResizeObserver(() => this._props.setHeight?.(this.headerMargin + Math.max(...this._refList.map(DivHeight))));
 
     render() {
         TraceMobx();
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 38846c79d..db178d500 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -36,15 +36,13 @@ interface CSVFieldColumnProps {
     yMargin: number;
     numGroupColumns: number;
     gridGap: number;
-    type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined;
     headings: () => object[];
     select: (ctrlPressed: boolean) => void;
     renderChildren: (docs: Doc[]) => JSX.Element[];
     addDocument: (doc: Doc | Doc[]) => boolean;
     createDropTarget: (ele: HTMLDivElement) => void;
     screenToLocalTransform: () => Transform;
-    observeHeight: (myref: any) => void;
-    unobserveHeight: (myref: any) => void;
+    refList: any[];
     editableViewProps: () => any;
     resizeColumns: (headers: SchemaHeaderField[]) => boolean;
     maxColWidth: number;
@@ -78,15 +76,18 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
 
     createColumnDropRef = (ele: HTMLDivElement | null) => {
         this.dropDisposer?.();
-        if (ele) {
-            this._ele = ele;
-            this._props.observeHeight(ele);
-            this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document);
-        }
+        if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document);
+        else if (this._ele) this.props.refList.slice(this.props.refList.indexOf(this._ele), 1);
+        this._ele = ele;
     };
 
+    componentDidMount(): void {
+        this._ele && this.props.refList.push(this._ele);
+    }
+
     componentWillUnmount() {
-        this._props.unobserveHeight(this._ele);
+        this._ele && this.props.refList.splice(this._props.refList.indexOf(this._ele), 1);
+        this._ele = null;
     }
 
     @undoBatch
@@ -289,11 +290,10 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
 
     render() {
         TraceMobx();
-        const heading = this._heading;
         return (
             <div
                 className="collectionNoteTakingViewFieldColumn"
-                key={heading}
+                key={this._heading}
                 style={{
                     width: this.columnWidth,
                     background: this._background,
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 54314f62c..dc391631a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -11,12 +11,11 @@ import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
 import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { DivHeight, emptyFunction, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { CollectionViewType } from '../../documents/DocumentTypes';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SettingsManager } from '../../util/SettingsManager';
-import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
@@ -45,17 +44,16 @@ export type collectionStackingViewProps = {
 
 @observer
 export class CollectionStackingView extends CollectionSubView<Partial<collectionStackingViewProps>>() {
+    _disposers: { [key: string]: IReactionDisposer } = {};
     _masonryGridRef: HTMLDivElement | null = null;
     // used in a column dragger, likely due for the masonry grid view. We want to use this
     _draggerRef = React.createRef<HTMLDivElement>();
-    // Not sure what a pivot field is. Seems like we cause reaction in MobX get rid of it once we exit this view
-    _pivotFieldDisposer?: IReactionDisposer;
-    // Seems like we cause reaction in MobX get rid of our height once we exit this view
-    _layout_autoHeightDisposer?: IReactionDisposer;
     // keeping track of documents. Updated on internal and external drops. What's the difference?
     _docXfs: { height: () => number; width: () => number; stackedDocTransform: () => Transform }[] = [];
     // Doesn't look like this field is being used anywhere. Obsolete?
     _columnStart: number = 0;
+
+    @observable _refList: any[] = [];
     // map of node headers to their heights. Used in Masonry
     @observable _heightMap = new Map<string, number>();
     // Assuming that this is the current css cursor style
@@ -207,27 +205,27 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         this._props.setContentViewBox?.(this);
 
         // reset section headers when a new filter is inputted
-        this._pivotFieldDisposer = reaction(
+        this._disposers.pivotField = reaction(
             () => this.pivotField,
             () => (this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List())
         );
-        this._layout_autoHeightDisposer = reaction(
+        this._disposers.autoHeight = reaction(
             () => this.layoutDoc._layout_autoHeight,
-            layout_autoHeight =>
-                layout_autoHeight &&
-                this._props.setHeight?.(
-                    Math.min(
-                        NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
-                        this.headerMargin + (this.isStackingView ? Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))) : this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0))
-                    )
-                )
+            layout_autoHeight => layout_autoHeight && this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0)))
+        );
+        this._disposers.refList = reaction(
+            () => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !LightboxView.Contains(this.DocumentView?.()) }),
+            ({ refList, autoHeight }) => {
+                this.observer.disconnect();
+                if (autoHeight) refList.forEach(r => this.observer.observe(r));
+            },
+            { fireImmediately: true }
         );
     }
 
     componentWillUnmount() {
         super.componentWillUnmount();
-        this._pivotFieldDisposer?.();
-        this._layout_autoHeightDisposer?.();
+        Object.keys(this._disposers).forEach(key => this._disposers[key]());
     }
 
     isAnyChildContentActive = () => this._props.isAnyChildContentActive();
@@ -523,7 +521,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         });
     };
     headings = () => Array.from(this.Sections);
-    refList: any[] = [];
     // what a section looks like if we're in stacking view
     sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
         const key = this.pivotField;
@@ -536,23 +533,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         }
         return (
             <CollectionStackingViewFieldColumn
-                unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
-                observeHeight={ref => {
-                    if (ref) {
-                        this.refList.push(ref);
-                        this.observer = new _global.ResizeObserver(
-                            action((entries: any) => {
-                                if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.IsDragging) {
-                                    const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
-                                    if (!LightboxView.Contains(this.DocumentView?.())) {
-                                        this._props.setHeight?.(height);
-                                    }
-                                }
-                            })
-                        );
-                        this.observer.observe(ref);
-                    }
-                }}
+                refList={this._refList}
                 addDocument={this.addDocument}
                 chromeHidden={this.chromeHidden}
                 colHeaderData={this.colHeaderData}
@@ -591,21 +572,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 Document={this.Document}
                 chromeHidden={this.chromeHidden}
                 pivotField={this.pivotField}
-                unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
-                observeHeight={ref => {
-                    if (ref) {
-                        this.refList.push(ref);
-                        this.observer = new _global.ResizeObserver(
-                            action((entries: any) => {
-                                if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.IsDragging) {
-                                    const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0);
-                                    this._props.setHeight?.(2 * this.headerMargin + height); // bcz: added 2x for header to fix problem with scrollbars appearing in Tools panel
-                                }
-                            })
-                        );
-                        this.observer.observe(ref);
-                    }
-                }}
+                refList={this._refList}
                 key={heading ? heading.heading : ''}
                 rows={rows}
                 headings={this.headings}
@@ -709,7 +676,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     @computed get backgroundEvents() {
         return this._props.isContentActive() === false ? 'none' : undefined;
     }
-    observer: any;
+
+    observer = new _global.ResizeObserver(() => this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0))));
+
     render() {
         TraceMobx();
         const editableViewProps = {
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index c455f20d8..6a3cb759e 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -9,7 +9,7 @@ import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, NumCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../Utils';
+import { DivHeight, DivWidth, emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
@@ -44,8 +44,7 @@ interface CSVFieldColumnProps {
     addDocument: (doc: Doc | Doc[]) => boolean;
     createDropTarget: (ele: HTMLDivElement) => void;
     screenToLocalTransform: () => Transform;
-    observeHeight: (myref: any) => void;
-    unobserveHeight: (myref: any) => void;
+    refList: any[];
 }
 
 @observer
@@ -53,7 +52,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
     private dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
-    @observable private _background = 'inherit';
+    @observable _background = 'inherit';
     @observable _paletteOn = false;
     @observable _heading = '';
     @observable _color = '';
@@ -71,15 +70,14 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
     // is that the only way to have drop targets?
     createColumnDropRef = (ele: HTMLDivElement | null) => {
         this.dropDisposer?.();
-        if (ele) {
-            this._ele = ele;
-            this._props.observeHeight(ele);
-            this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document);
-        }
+        if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document);
+        else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+        this._ele = ele;
     };
 
     @action
     componentDidMount() {
+        this._ele && this.props.refList.push(this._ele);
         this._disposers.collapser = reaction(
             () => this._props.headingObject?.collapsed,
             collapsed => (this.collapsed = collapsed !== undefined ? BoolCast(collapsed) : false),
@@ -88,7 +86,8 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
     }
     componentWillUnmount() {
         this._disposers.collapser?.();
-        this._props.unobserveHeight(this._ele);
+        this._ele && this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+        this._ele = null;
     }
 
     //TODO: what is scripting? I found it in SetInPlace def but don't know what that is
@@ -217,8 +216,8 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
         const layoutItems: ContextMenuProps[] = [];
         const docItems: ContextMenuProps[] = [];
         const dataDoc = this._props.TemplateDataDocument || this._props.Document;
-        const width = this._ele ? Number(getComputedStyle(this._ele).width.replace('px', '')) : 0;
-        const height = this._ele ? Number(getComputedStyle(this._ele).height.replace('px', '')) : 0;
+        const width = this._ele ? DivWidth(this._ele) : 0;
+        const height = this._ele ? DivHeight(this._ele) : 0;
         DocUtils.addDocumentCreatorMenuItems(
             doc => {
                 FormattedTextBox.SetSelectOnLoad(doc);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 4d60cbefc..3b37bdcfa 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -8,7 +8,7 @@ import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero, Utils } from '../../../Utils';
+import { DivHeight, emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
@@ -113,8 +113,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
 
     computeHeight = () => {
         if (!this._isDisposing) {
-            const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace('px', ''));
-            const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot()) + 6;
+            const titleHeight = !this._titleRef ? this.marginTop() : DivHeight(this._titleRef);
+            const bodyHeight = Array.from(this.refList).reduce((p, r) => p + DivHeight(r), this.marginBot()) + 6;
             this.layoutDoc._layout_autoHeightMargins = bodyHeight;
             !this._props.dontRegisterView && this._props.setHeight?.(bodyHeight + titleHeight);
         }
@@ -420,7 +420,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         return (
             <div style={{ display: 'flex', flexDirection: 'column', height: '100%', pointerEvents: 'all' }}>
                 {!this.buttonMenu && !this.noviceExplainer ? null : (
-                    <div className="documentButtonMenu" ref={action((r: HTMLDivElement | null) => r && (this._headerHeight = Number(getComputedStyle(r).height.replace(/px/, ''))))}>
+                    <div className="documentButtonMenu" ref={action((r: HTMLDivElement | null) => r && (this._headerHeight = DivHeight(r)))}>
                         {this.buttonMenu}
                         {this.noviceExplainer}
                     </div>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b0e8cf6ff..ee058c085 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,11 +1,10 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { Dropdown, DropdownType, Type } from 'browndash-components';
 import { Howl } from 'howler';
 import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Bounce, Fade, Flip, JackInTheBox, Roll, Rotate, Zoom } from 'react-awesome-reveal';
-import { Utils, emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick } from '../../../Utils';
+import { DivWidth, Utils, emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick } from '../../../Utils';
 import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
 import { AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
@@ -703,7 +702,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
     panelHeight = () => this._props.PanelHeight() - this.headerMargin;
     screenToLocalContent = () => this._props.ScreenToLocalTransform().translate(0, -this.headerMargin);
     onClickFunc = this.disableClickScriptFunc ? undefined : () => this.onClickHandler;
-    setHeight = (height: number) => !this._props.suppressSetHeight && (this.layoutDoc._height = height);
+    setHeight = (height: number) => !this._props.suppressSetHeight && (this.layoutDoc._height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), height));
     setContentView = action((view: ViewBoxInterface) => (this._componentView = view));
     isContentActive = (): boolean | undefined => this._isContentActive;
     childFilters = () => [...this._props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
@@ -788,7 +787,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
     fieldsDropdown = (placeholder: string) => {
         return (
             <div
-                ref={action((r: any) => r && (this._titleDropDownInnerWidth = Number(getComputedStyle(r).width.replace('px', ''))))}
+                ref={action((r: any) => r && (this._titleDropDownInnerWidth = DivWidth(r)))}
                 onPointerDown={action(e => (this._changingTitleField = true))}
                 style={{ width: 'max-content', background: SettingsManager.userBackgroundColor, color: SettingsManager.userColor, transformOrigin: 'left', transform: `scale(${this.titleHeight / 30 /* height of Dropdown */})` }}>
                 <FieldsDropdown
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index 2e03a766a..50d4c7c78 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -11,6 +11,7 @@ import { LightboxView } from '../LightboxView';
 import './EquationBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
 import EquationEditor from './formattedText/EquationEditor';
+import { DivHeight, DivWidth } from '../../../Utils';
 
 @observer
 export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -57,8 +58,8 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     keyPressed = (e: KeyboardEvent) => {
-        const _height = Number(getComputedStyle(this._ref.current!.element.current).height.replace('px', ''));
-        const _width = Number(getComputedStyle(this._ref.current!.element.current).width.replace('px', ''));
+        const _height = DivHeight(this._ref.current!.element.current);
+        const _width = DivWidth(this._ref.current!.element.current);
         if (e.key === 'Enter') {
             const nextEq = Docs.Create.EquationDocument(e.shiftKey ? StrCast(this.dataDoc.text) : 'x', {
                 title: '# math',
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2c5398e40..c9340edc0 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -14,7 +14,7 @@ import { listSpec } from '../../../fields/Schema';
 import { Cast, NumCast, StrCast, WebCast } from '../../../fields/Types';
 import { ImageField, WebField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, stringHash, Utils } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivHeight, emptyFunction, getWordAtPoint, lightOrDark, returnFalse, returnOne, returnZero, setupMoveUpEvents, smoothScroll, stringHash, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentManager } from '../../util/DocumentManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
@@ -83,7 +83,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         return this.webField?.toString() || '';
     }
     @computed get _urlHash() {
-        return ""+ (stringHash(this._url)??'');
+        return '' + (stringHash(this._url) ?? '');
     }
     @computed get scrollHeight() {
         return Math.max(NumCast(this.layoutDoc._height), this._scrollHeight);
@@ -782,7 +782,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
                     className="webBox-htmlSpan"
                     ref={action((r: any) => {
                         if (r) {
-                            this._scrollHeight = Number(getComputedStyle(r).height.replace('px', ''));
+                            this._scrollHeight = DivHeight(r);
                             this.lighttext = Array.from(r.children).some((c: any) => c instanceof HTMLElement && lightOrDark(getComputedStyle(c).color) !== Colors.WHITE);
                         }
                     })}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 56008de8e..266a2442f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -23,7 +23,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
 import { ComputedField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivWidth, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
 import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
 import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
@@ -758,7 +758,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     @action
     toggleSidebar = (preview: boolean = false) => {
         const defaultSidebar = 250;
-        const prevWidth = 1 - this.sidebarWidth() / Number(getComputedStyle(this._ref.current!).width.replace('px', ''));
+        const prevWidth = 1 - this.sidebarWidth() / DivWidth(this._ref.current!);
         if (preview) this._showSidebar = true;
         else {
             this.layoutDoc[this.SidebarKey + '_freeform_scale_max'] = 1;
-- 
cgit v1.2.3-70-g09d2


From 11b6cb8344f26e5895bebc3dedef491675717e30 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 5 Mar 2024 15:11:29 -0500
Subject: fixed text in templates to revert to template value when noTemplate
 is set. made dashFieldView read from datadoc.  added margins for
 multicolumn/row, enabled chromHidden for multicolumn/row

---
 src/client/views/PropertiesView.tsx                       |  3 ++-
 .../collectionMulticolumn/CollectionMulticolumnView.tsx   | 10 ++++++----
 src/client/views/nodes/formattedText/DashFieldView.tsx    | 15 +++++++--------
 src/client/views/nodes/formattedText/FormattedTextBox.tsx |  5 +++--
 4 files changed, 18 insertions(+), 15 deletions(-)

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

diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index e4e7bec32..cbd3ff358 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -135,7 +135,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
         return this.selectedDoc?.isGroup;
     }
     @computed get isStack() {
-        return [CollectionViewType.Stacking, CollectionViewType.NoteTaking].includes(this.selectedDoc?.type_collection as any);
+        return [CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Stacking, CollectionViewType.NoteTaking].includes(this.selectedDoc?.type_collection as any);
     }
 
     rtfWidth = () => (!this.selectedLayoutDoc ? 0 : Math.min(NumCast(this.selectedLayoutDoc?._width), this._props.width - 20));
@@ -1076,6 +1076,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
             <div className="transform-editor">
                 {!this.isStack ? null : this.getNumber('Gap', ' px', 0, 200, NumCast(this.selectedDoc!.gridGap), (val: number) => !isNaN(val) && (this.selectedDoc!.gridGap = val))}
                 {!this.isStack ? null : this.getNumber('xMargin', ' px', 0, 500, NumCast(this.selectedDoc!.xMargin), (val: number) => !isNaN(val) && (this.selectedDoc!.xMargin = val))}
+                {!this.isStack ? null : this.getNumber('yMargin', ' px', 0, 500, NumCast(this.selectedDoc!.yMargin), (val: number) => !isNaN(val) && (this.selectedDoc!.yMargin = val))}
                 {!this.isGroup ? null : this.getNumber('Padding', ' px', 0, 500, NumCast(this.selectedDoc!.xPadding), (val: number) => !isNaN(val) && (this.selectedDoc!.xPadding = this.selectedDoc!.yPadding = val))}
                 {this.isInk ? this.controlPointsButton : null}
                 {this.getNumber('Width', ' px', 0, Math.max(1000, this.shapeWid), this.shapeWid, (val: number) => !isNaN(val) && (this.shapeWid = val), 1000, 1)}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 85fe7c896..125dd2781 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -315,7 +315,9 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 <Tooltip title={'Tab: ' + StrCast(layout.title)} key={'wrapper' + i}>
                     <div className="document-wrapper" style={{ flexDirection: 'column', width: this.lookupPixels(layout) }}>
                         {this.getDisplayDoc(layout)}
-                        <Button tooltip="Remove document from header bar" icon={<FontAwesomeIcon icon="times" size="lg" />} onClick={undoable(e => this._props.removeDocument?.(layout), 'close doc')} color={SettingsManager.userColor} />
+                        {this.layoutDoc._chromeHidden ? null : (
+                            <Button tooltip="Remove document from header bar" icon={<FontAwesomeIcon icon="times" size="lg" />} onClick={undoable(e => this._props.removeDocument?.(layout), 'close doc')} color={SettingsManager.userColor} />
+                        )}
                         <WidthLabel layout={layout} collectionDoc={this.Document} />
                     </div>
                 </Tooltip>,
@@ -350,14 +352,14 @@ export class CollectionMulticolumnView extends CollectionSubView() {
                 }}>
                 {this.contents}
                 {!this._startIndex ? null : (
-                    <Tooltip title={'scroll back'}>
+                    <Tooltip title="scroll back">
                         <div style={{ position: 'absolute', bottom: 0, left: 0, background: SettingsManager.userVariantColor }} onClick={action(e => (this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown)))}>
                             <Button tooltip="Scroll back" icon={<FontAwesomeIcon icon="chevron-left" size="lg" />} onClick={action(e => (this._startIndex = Math.max(0, this._startIndex - this.maxShown)))} color={SettingsManager.userColor} />
                         </div>
                     </Tooltip>
                 )}
-                {this._startIndex > this.childLayoutPairs.length - 1 ? null : (
-                    <Tooltip title={'scroll forward'}>
+                {this._startIndex > this.childLayoutPairs.length - 1 || !this.maxShown ? null : (
+                    <Tooltip title="scroll forward">
                         <div style={{ position: 'absolute', bottom: 0, right: 0, background: SettingsManager.userVariantColor }} onClick={action(e => (this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown)))}>
                             <IconButton icon={<FaChevronRight />} color={SettingsManager.userColor} />
                         </div>
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index b49e7dcf0..5c4d850ad 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -4,24 +4,23 @@ import { action, computed, IReactionDisposer, makeObservable, observable, reacti
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
-import Select from 'react-select';
 import { Doc, DocListCast, Field } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { listSpec } from '../../../../fields/Schema';
 import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
-import { Cast, StrCast } from '../../../../fields/Types';
+import { Cast } from '../../../../fields/Types';
 import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch } from '../../../util/UndoManager';
+import { undoBatch } from '../../../util/UndoManager';
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
 import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableCell';
+import { FilterPanel } from '../../FilterPanel';
 import { ObservableReactComponent } from '../../ObservableReactComponent';
 import { OpenWhere } from '../DocumentView';
 import './DashFieldView.scss';
 import { FormattedTextBox } from './FormattedTextBox';
-import { FilterPanel } from '../../FilterPanel';
 
 export class DashFieldView {
     dom: HTMLDivElement; // container for label and value
@@ -29,7 +28,7 @@ export class DashFieldView {
     node: any;
     tbox: FormattedTextBox;
 
-    unclickable = () => !this.tbox._props.isSelected() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview);
+    unclickable = () => !this.tbox._props.rootSelected?.() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview);
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
         this.node = node;
         this.tbox = tbox;
@@ -108,12 +107,12 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
         super(props);
         makeObservable(this);
         this._fieldKey = this._props.fieldKey;
-        this._textBoxDoc = this._props.tbox.Document;
+        this._textBoxDoc = this._fieldKey.startsWith('_') ? this._props.tbox.Document : this._props.tbox.dataDoc;
 
         if (this._props.docId) {
             DocServer.GetRefField(this._props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc)));
         } else {
-            this._dashDoc = this._props.tbox.Document;
+            this._dashDoc = this._fieldKey.startsWith('_') ? this._props.tbox.Document : this._props.tbox.dataDoc;
         }
     }
 
@@ -202,7 +201,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
                 style={{
                     width: this._props.width,
                     height: this._props.height,
-                    pointerEvents: this._props.tbox._props.isSelected() || this._props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
+                    pointerEvents: this._props.tbox._props.rootSelected?.() || this._props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
                 }}>
                 {this._props.hideKey ? null : (
                     <span className="dashFieldView-labelSpan" title="click to see related tags" onPointerDown={this.onPointerDownLabelSpan}>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 266a2442f..2d5239f06 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -374,9 +374,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
                         if (force || ((this._finishingLink || this._props.isContentActive() || this._inDrop) && removeSelection(newJson) !== removeSelection(prevData?.Data))) {
                             const numstring = NumCast(dataDoc[this.fieldKey], null);
-                            dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : new RichTextField(newJson, newText);
-                            dataDoc[this.fieldKey + '_noTemplate'] = true; // mark the data field as being split from the template if it has been edited
+                            dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : newText ? new RichTextField(newJson, newText) : undefined;
                             textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.Document, text: newText });
+                            this._applyingChange = ''; // turning this off here allows a Doc to retrieve data from template if noTemplate below is changed to false
+                            dataDoc[this.fieldKey + '_noTemplate'] = newText ? true : false; // mark the data field as being split from the template if it has been edited
                             unchanged = false;
                         }
                     } else {
-- 
cgit v1.2.3-70-g09d2


From bd9170eaa21a2fbccd0912f50d40cefaf0bbe47f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 6 Mar 2024 12:28:24 -0500
Subject: cleaned up some dropActionType strings.  fixed stackingview scrolling
 when always active.  fixed schemaView keys dropdown menu to be a popup. 
 cleaned up  display of key Finfo's

---
 src/client/documents/Documents.ts                  |  46 +++----
 src/client/views/DashboardView.tsx                 |   9 +-
 src/client/views/TemplateMenu.tsx                  |   2 +-
 .../views/collections/CollectionStackingView.tsx   |  15 ++-
 src/client/views/collections/CollectionSubView.tsx |   5 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionSchema/CollectionSchemaView.scss     | 149 +++++++++++----------
 .../collectionSchema/CollectionSchemaView.tsx      |  68 ++++++----
 src/client/views/nodes/LinkAnchorBox.tsx           |   4 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   4 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   4 +-
 11 files changed, 170 insertions(+), 142 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 031560886..c9b5cb915 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -70,14 +70,14 @@ class EmptyBox {
 }
 
 export enum FInfoFieldType {
-    string,
-    boolean,
-    number,
-    Doc,
-    enumeration,
-    date,
-    list,
-    rtf,
+    string = 'string',
+    boolean = 'boolean',
+    number = 'number',
+    Doc = 'Doc',
+    enumeration = 'enum',
+    date = 'date',
+    list = 'list',
+    rtf = 'rich text',
 }
 export class FInfo {
     description: string = '';
@@ -198,20 +198,20 @@ type DATEt = DateInfo | number;
 type DTYPEt = DTypeInfo | string;
 export class DocumentOptions {
     // coordinate and dimensions depending on view
-    x?: NUMt = new NumInfo('x coordinate of document in a freeform view', false);
-    y?: NUMt = new NumInfo('y coordinate of document in a freeform view', false);
+    x?: NUMt = new NumInfo('horizontal coordinate in freeform view', false);
+    y?: NUMt = new NumInfo('vertical coordinate in freeform view', false);
     z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, false, [1, 0]);
-    overlayX?: NUMt = new NumInfo('x coordinate of document in a overlay view', false);
-    overlayY?: NUMt = new NumInfo('y coordinate of document in a overlay view', false);
-    text?: RTFt = new RtfInfo('rich text of a text doc', true);
+    overlayX?: NUMt = new NumInfo('horizontal coordinate in overlay view', false);
+    overlayY?: NUMt = new NumInfo('vertical coordinate in overlay view', false);
+    text?: RTFt = new RtfInfo('plain or rich text', true);
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", false);
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
-    latitude?: NUMt = new NumInfo('latitude coordinate for map views', false);
-    longitude?: NUMt = new NumInfo('longitude coordinate for map views', false);
+    latitude?: NUMt = new NumInfo('latitude coordinate', false);
+    longitude?: NUMt = new NumInfo('longitude coordinate', false);
     routeCoordinates?: STRt = new StrInfo("stores a route's/direction's coordinates (stringified version)"); // for a route document, this stores the route's coordinates
-    markerType?: STRt = new StrInfo('Defines the marker type for a pushpin document');
-    markerColor?: STRt = new StrInfo('Defines the marker color for a pushpin document');
-    map?: STRt = new StrInfo('text location of map');
+    markerType?: STRt = new StrInfo('marker type for a pushpin document');
+    markerColor?: STRt = new StrInfo('marker color for a pushpin document');
+    map?: STRt = new StrInfo('map location name');
     map_type?: STRt = new StrInfo('type of map view', false);
     map_zoom?: NUMt = new NumInfo('zoom of a map view', false);
     map_pitch?: NUMt = new NumInfo('pitch of a map view', false);
@@ -221,11 +221,11 @@ export class DocumentOptions {
     date_range?: STRt = new StrInfo('date range for calendar', false);
 
     wikiData?: STRt = new StrInfo('WikiData ID related to map location');
-    description?: STRt = new StrInfo('A description of the document');
-    _timecodeToShow?: NUMt = new NumInfo('the time that a document should be displayed (e.g., when an annotation shows up as a video plays)', false);
-    _timecodeToHide?: NUMt = new NumInfo('the time that a document should be hidden', false);
-    _width?: NUMt = new NumInfo('displayed width of a document');
-    _height?: NUMt = new NumInfo('displayed height of document');
+    description?: STRt = new StrInfo('description of document');
+    _timecodeToShow?: NUMt = new NumInfo('media timecode when document should appear (e.g., when an annotation shows up as a video plays)', false);
+    _timecodeToHide?: NUMt = new NumInfo('media timecode when document should disappear', false);
+    _width?: NUMt = new NumInfo("width of document in container's coordinates");
+    _height?: NUMt = new NumInfo("height of document in container's coordiantes");
     data_nativeWidth?: NUMt = new NumInfo('native width of data field contents (e.g., the pixel width of an image)', false);
     data_nativeHeight?: NUMt = new NumInfo('native height of data field contents (e.g., the pixel height of an image)', false);
     linearBtnWidth?: NUMt = new NumInfo('unexpanded width of a linear menu button (button "width" changes when it expands)', false);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 472d419fc..439d250de 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -28,6 +28,7 @@ import { Colors } from './global/globalEnums';
 import { MainViewModal } from './MainViewModal';
 import { ButtonType } from './nodes/FontIconBox/FontIconBox';
 import { ObservableReactComponent } from './ObservableReactComponent';
+import { dropActionType } from '../util/DragManager';
 
 enum DashboardGroup {
     MyDashboards,
@@ -403,7 +404,7 @@ export class DashboardView extends ObservableReactComponent<{}> {
             _layout_fitWidth: true,
             _gridGap: 5,
             _forceActive: true,
-            childDragAction: 'embed',
+            childDragAction: dropActionType.embed,
             treeView_TruncateTitleWidth: 150,
             ignoreClick: true,
             contextMenuIcons: new List<string>(['plus']),
@@ -411,7 +412,7 @@ export class DashboardView extends ObservableReactComponent<{}> {
             _lockedPosition: true,
             layout_boxShadow: '0 0',
             childDontRegisterViews: true,
-            dropAction: 'same',
+            dropAction: dropActionType.same,
             isSystem: true,
             layout_explainer: 'All of the calendars that you have created will appear here.',
         };
@@ -450,7 +451,7 @@ export class DashboardView extends ObservableReactComponent<{}> {
             _layout_fitWidth: true,
             _gridGap: 5,
             _forceActive: true,
-            childDragAction: 'embed',
+            childDragAction: dropActionType.embed,
             treeView_TruncateTitleWidth: 150,
             ignoreClick: true,
             layout_headerButton: myTrailsBtn,
@@ -459,7 +460,7 @@ export class DashboardView extends ObservableReactComponent<{}> {
             _lockedPosition: true,
             layout_boxShadow: '0 0',
             childDontRegisterViews: true,
-            dropAction: 'same',
+            dropAction: dropActionType.same,
             isSystem: true,
             layout_explainer: 'All of the trails that you have created will appear here.',
         };
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 5d9abb460..00195f7d7 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -105,7 +105,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         const addedTypes = DocListCast(Cast(Doc.UserDoc()['template_clickFuncs'], Doc, null)?.data);
         const templateMenu: Array<JSX.Element> = [];
         this.props.templates?.forEach((checked, template) => templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
-        templateMenu.push(<OtherToggle key={'default'} name={'Default'} checked={templateName === 'layout'} toggle={this.toggleDefault} />);
+        templateMenu.push(<OtherToggle key="default" name={firstDoc.layout instanceof Doc ? StrCast(firstDoc.layout.title) : 'Default'} checked={templateName === 'layout'} toggle={this.toggleDefault} />);
         addedTypes.concat(noteTypes).map(template => (template.treeView_Checked = this.templateIsUsed(firstDoc, template)));
         this._addedKeys &&
             Array.from(this._addedKeys)
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index dc391631a..ea1caf58f 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -233,10 +233,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
         return this._props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
     };
-    createRef = (ele: HTMLDivElement | null) => {
-        this._masonryGridRef = ele;
-        this.createDashEventsTarget(ele!); //so the whole grid is the drop target?
-    };
 
     onChildClickHandler = () => this._props.childClickScript || ScriptCast(this.Document.onChildClick);
     @computed get onChildDoubleClickHandler() {
@@ -679,6 +675,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
 
     observer = new _global.ResizeObserver(() => this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0))));
 
+    onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
+    _oldWheel: any;
     render() {
         TraceMobx();
         const editableViewProps = {
@@ -699,7 +697,14 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 <div className="collectionStackingMasonry-cont">
                     <div
                         className={this.isStackingView ? 'collectionStackingView' : 'collectionMasonryView'}
-                        ref={this.createRef}
+                        ref={ele => {
+                            this._masonryGridRef = ele;
+                            this.createDashEventsTarget(ele); //so the whole grid is the drop target?
+                            this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
+                            this._oldWheel = ele;
+                            // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
+                            ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+                        }}
                         style={{
                             overflowY: this.isContentActive() ? 'auto' : 'hidden',
                             background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor),
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index fdbd1cc90..59a695a3d 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -219,12 +219,13 @@ export function CollectionSubView<X>(moreProps?: X) {
                 const targetDocments = DocListCast(this.dataDoc[this._props.fieldKey]);
                 const someMoved = !dropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
                 if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => (targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop));
-                if ((!dropAction || dropAction === 'inSame' || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
+                if ((!dropAction || dropAction === dropActionType.inPlace || dropAction === dropActionType.same || dropAction === dropActionType.move || someMoved) && docDragData.moveDocument) {
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     if (movedDocs.length) {
                         const canAdd =
-                            (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.Document)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.Document));
+                            (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.Document)) &&
+                            (dropAction !== dropActionType.inPlace || docDragData.draggedDocuments.every(d => d.embedContainer === this.Document));
                         const moved = docDragData.moveDocument(movedDocs, this.Document, canAdd ? this.addDocument : returnFalse);
                         added = canAdd || moved ? moved : undefined;
                     } else if (addedDocs.length) {
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 4f2d8b9c0..285a789e6 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -449,10 +449,10 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
 
         const addDoc = inside ? this.localAdd : parentAddDoc;
         const canAdd = !StrCast((inside ? this.Document : this._props.treeViewParent)?.treeView_FreezeChildren).includes('add') || forceAdd;
-        if (canAdd && (dropAction !== 'inSame' || droppedDocuments.every(d => d.embedContainer === this._props.parentTreeView?.Document))) {
-            const move = (!dropAction || canEmbed || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same' || dropAction === 'inSame') && moveDocument;
+        if (canAdd && (dropAction !== dropActionType.inPlace || droppedDocuments.every(d => d.embedContainer === this._props.parentTreeView?.Document))) {
+            const move = (!dropAction || canEmbed || dropAction === dropActionType.proto || dropAction === dropActionType.move || dropAction === dropActionType.same || dropAction === dropActionType.inPlace) && moveDocument;
             this._props.parentTreeView instanceof TreeView && (this._props.parentTreeView.dropping = true);
-            const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
+            const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === dropActionType.proto ? addDoc(d) : false) : addDoc(d)) || added, false);
             this._props.parentTreeView instanceof TreeView && (this._props.parentTreeView.dropping = false);
             return res;
         }
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index fed4e89cf..ac0bd2378 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -38,91 +38,97 @@
                 background: yellow;
             }
         }
+    }
 
-        .schema-column-menu,
-        .schema-filter-menu {
-            background: $light-gray;
-            position: absolute;
-            min-width: 200px;
-            max-width: 400px;
-            display: flex;
-            flex-direction: column;
-            align-items: flex-start;
-            z-index: 1;
-
-            .schema-key-search-input {
-                width: calc(100% - 20px);
-                margin: 10px;
-            }
+    .schema-preview-divider {
+        height: 100%;
+        background: black;
+        cursor: ew-resize;
+    }
+}
 
-            .schema-search-result {
-                cursor: pointer;
-                padding: 5px 10px;
-                width: 100%;
+.schema-column-menu,
+.schema-filter-menu {
+    background: $light-gray;
+    position: relative;
+    min-width: 200px;
+    max-width: 400px;
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    z-index: 1;
 
-                &:hover {
-                    background-color: $medium-gray;
-                }
+    .schema-key-search-input {
+        width: calc(100% - 20px);
+        margin: 10px;
+    }
 
-                .schema-search-result-type,
-                .schema-search-result-desc {
-                    color: $dark-gray;
-                    font-size: $body-text;
-                }
-            }
+    .schema-search-result {
+        cursor: pointer;
+        padding: 5px 10px;
+        width: 100%;
 
-            .schema-key-search,
-            .schema-new-key-options {
-                width: 100%;
-                display: flex;
-                flex-direction: column;
-                align-items: flex-start;
-            }
+        &:hover {
+            background-color: $medium-gray;
+        }
+        .schema-search-result-type {
+            font-family: 'Courier New', Courier, monospace;
+        }
 
-            .schema-new-key-options {
-                margin: 10px;
-                .schema-key-warning {
-                    color: red;
-                    font-weight: normal;
-                    align-self: center;
-                }
-            }
+        .schema-search-result-type,
+        .schema-search-result-desc {
+            color: $dark-gray;
+            font-size: $body-text;
+        }
+        .schema-search-result-desc {
+            font-style: italic;
+        }
+    }
 
-            .schema-key-list {
-                width: 100%;
-                max-height: 300px;
-                overflow-y: auto;
-            }
+    .schema-key-search,
+    .schema-new-key-options {
+        width: 100%;
+        display: flex;
+        flex-direction: column;
+        align-items: flex-start;
+    }
 
-            .schema-key-type-option {
-                margin: 2px 0px;
+    .schema-new-key-options {
+        margin: 10px;
+        .schema-key-warning {
+            color: red;
+            font-weight: normal;
+            align-self: center;
+        }
+    }
 
-                input {
-                    margin-right: 5px;
-                }
-            }
+    .schema-key-list {
+        width: 100%;
+        max-height: 300px;
+        overflow-y: auto;
+    }
 
-            .schema-key-default-val {
-                margin: 5px 0;
-            }
+    .schema-key-type-option {
+        margin: 2px 0px;
 
-            .schema-column-menu-button {
-                cursor: pointer;
-                padding: 2px 5px;
-                background: $medium-blue;
-                border-radius: 9999px;
-                color: $white;
-                width: fit-content;
-                margin: 5px;
-                align-self: center;
-            }
+        input {
+            margin-right: 5px;
         }
     }
 
-    .schema-preview-divider {
-        height: 100%;
-        background: black;
-        cursor: ew-resize;
+    .schema-key-default-val {
+        margin: 5px 0;
+    }
+
+    .schema-column-menu-button {
+        cursor: pointer;
+        padding: 2px 5px;
+        background: $medium-blue;
+        border-radius: 9999px;
+        color: $white;
+        width: fit-content;
+        margin: 5px;
+        align-self: center;
     }
 }
 
@@ -133,6 +139,7 @@
     .row-menu {
         display: flex;
         justify-content: center;
+        cursor: pointer;
     }
 }
 
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index eee3836d2..12f0ad5e9 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, makeObservable, observable, ObservableMap, observe } from 'mobx';
+import { action, computed, makeObservable, observable, ObservableMap, observe, trace } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Field, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
@@ -25,6 +25,8 @@ import { CollectionSubView } from '../CollectionSubView';
 import './CollectionSchemaView.scss';
 import { SchemaColumnHeader } from './SchemaColumnHeader';
 import { SchemaRowBox } from './SchemaRowBox';
+import { Popup, PopupTrigger, Type } from 'browndash-components';
+import { SettingsManager } from '../../../util/SettingsManager';
 const { default: { SCHEMA_NEW_NODE_HEIGHT } } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore
 
 export enum ColumnType {
@@ -161,7 +163,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                         (change as any).added.forEach((doc: Doc) => // for each document added 
                             Doc.GetAllPrototypes(doc.value as Doc).forEach(proto => // for all of its prototypes (and itself)
                                 Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them
-                                    !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo(key, key === 'author'))))));
+                                    !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo("-no description-", key === 'author'))))));
                         break;
                     case 'update': //let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list
                 }
@@ -681,6 +683,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                         } else {
                             this.setKey(this._menuValue, this._newFieldDefault);
                         }
+                        this._columnMenuIndex = undefined;
                     })}>
                     done
                 </div>
@@ -688,12 +691,12 @@ export class CollectionSchemaView extends CollectionSubView() {
         );
     }
 
-    onPassiveWheel = (e: WheelEvent) => {
+    onKeysPassiveWheel = (e: WheelEvent) => {
         // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
-        if (!this._oldWheel.scrollTop && e.deltaY <= 0) e.preventDefault();
+        if (!this._oldKeysWheel.scrollTop && e.deltaY <= 0) e.preventDefault();
         e.stopPropagation();
     };
-    _oldWheel: any;
+    _oldKeysWheel: any;
     @computed get keysDropdown() {
         return (
             <div className="schema-key-search">
@@ -708,9 +711,9 @@ export class CollectionSchemaView extends CollectionSubView() {
                 <div
                     className="schema-key-list"
                     ref={r => {
-                        this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
-                        this._oldWheel = r;
-                        r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+                        this._oldKeysWheel?.removeEventListener('wheel', this.onKeysPassiveWheel);
+                        this._oldKeysWheel = r;
+                        r?.addEventListener('wheel', this.onKeysPassiveWheel, { passive: false });
                     }}>
                     {this._menuKeys.map(key => (
                         <div
@@ -721,14 +724,14 @@ export class CollectionSchemaView extends CollectionSubView() {
                             }}>
                             <p>
                                 <span className="schema-search-result-key">
-                                    {key}
-                                    {this.fieldInfos.get(key)!.fieldType ? ', ' : ''}
+                                    <b>{key}</b>
+                                    {this.fieldInfos.get(key)!.fieldType ? ':' : ''}
                                 </span>
                                 <span className="schema-search-result-type" style={{ color: this.fieldInfos.get(key)!.readOnly ? 'red' : 'inherit' }}>
                                     {this.fieldInfos.get(key)!.fieldType}
                                 </span>
+                                <span className="schema-search-result-desc">&nbsp;&nbsp;{this.fieldInfos.get(key)!.description}</span>
                             </p>
-                            <p className="schema-search-result-desc">{this.fieldInfos.get(key)!.description}</p>
                         </div>
                     ))}
                 </div>
@@ -740,16 +743,17 @@ export class CollectionSchemaView extends CollectionSubView() {
         const x = this._columnMenuIndex! == -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth);
         return (
             <div className="schema-column-menu" style={{ left: x, minWidth: CollectionSchemaView._minColWidth }}>
-                <input className="schema-key-search-input" type="text" value={this._menuValue} onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} />
+                <input className="schema-key-search-input" type="text" onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} />
+                {this._makeNewField ? this.newFieldMenu : this.keysDropdown}
+            </div>
+        );
+    }
+    get renderKeysMenu() {
+        console.log('RNDERMENUT:' + this._columnMenuIndex);
+        return (
+            <div className="schema-column-menu" style={{ left: 0, minWidth: CollectionSchemaView._minColWidth }}>
+                <input className="schema-key-search-input" type="text" onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} />
                 {this._makeNewField ? this.newFieldMenu : this.keysDropdown}
-                <div
-                    className="schema-column-menu-button"
-                    onPointerDown={action(e => {
-                        e.stopPropagation();
-                        this.closeColumnMenu();
-                    })}>
-                    cancel
-                </div>
             </div>
         );
     }
@@ -833,6 +837,8 @@ export class CollectionSchemaView extends CollectionSubView() {
     isContentActive = () => this._props.isSelected() || this._props.isContentActive();
     screenToLocal = () => this.ScreenToLocalBoxXf().translate(-this.tableWidth, 0);
     previewWidthFunc = () => this.previewWidth;
+    onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
+    _oldWheel: any;
     render() {
         return (
             <div className="collectionSchemaView" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)}>
@@ -841,15 +847,23 @@ export class CollectionSchemaView extends CollectionSubView() {
                     className="schema-table"
                     style={{ width: `calc(100% - ${this.previewWidth}px)` }}
                     onWheel={e => this._props.isContentActive() && e.stopPropagation()}
-                    ref={r => {
-                        // prevent wheel events from passively propagating up through containers
-                        r?.addEventListener('wheel', (e: WheelEvent) => {}, { passive: false });
+                    ref={ele => {
+                        // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
+                        this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
+                        (this._oldWheel = ele)?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
                     }}>
                     <div className="schema-header-row" style={{ height: this.rowHeightFunc() }}>
                         <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}>
-                            <div className="schema-header-button" onPointerDown={e => (this._columnMenuIndex === -1 ? this.closeColumnMenu() : this.openColumnMenu(-1, true))}>
-                                <FontAwesomeIcon icon="plus" />
-                            </div>
+                            <Popup
+                                placement="right"
+                                background={SettingsManager.userBackgroundColor}
+                                color={SettingsManager.userColor}
+                                toggle={<FontAwesomeIcon onPointerDown={e => this.openColumnMenu(-1, true)} icon="plus" />}
+                                trigger={PopupTrigger.CLICK}
+                                type={Type.TERT}
+                                isOpen={this._columnMenuIndex !== -1 ? false : undefined}
+                                popup={this.renderKeysMenu}
+                            />
                         </div>
                         {this.columnKeys.map((key, index) => (
                             <SchemaColumnHeader
@@ -870,7 +884,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                             />
                         ))}
                     </div>
-                    {this._columnMenuIndex !== undefined && this.renderColumnMenu}
+                    {this._columnMenuIndex !== undefined && this._columnMenuIndex !== -1 && this.renderColumnMenu}
                     {this._filterColumnIndex !== undefined && this.renderFilterMenu}
                     <CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} rowHeight={this.rowHeightFunc} setRef={(ref: HTMLDivElement | null) => (this._tableContentRef = ref)} />
                     {this.layoutDoc.chromeHidden ? null : (
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 00e1f04c5..0a4325d8c 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -5,7 +5,7 @@ import { Utils, emptyFunction, setupMoveUpEvents } from '../../../Utils';
 import { Doc } from '../../../fields/Doc';
 import { NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { DragManager } from '../../util/DragManager';
+import { DragManager, dropActionType } from '../../util/DragManager';
 import { LinkFollower } from '../../util/LinkFollower';
 import { SelectionManager } from '../../util/SelectionManager';
 import { ViewBoxBaseComponent } from '../DocComponent';
@@ -54,7 +54,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
             if (separation > 100) {
                 const dragData = new DragManager.DocumentDragData([this.Document]);
-                dragData.dropAction = 'embed';
+                dragData.dropAction = dropActionType.embed;
                 dragData.dropPropertiesToRemove = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick'];
                 DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]);
                 return true;
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index f6d94ce05..e38a42b29 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -11,7 +11,7 @@ import { Upload } from '../../../../server/SharedMediaTypes';
 import { DocumentType } from '../../../documents/DocumentTypes';
 import { Docs } from '../../../documents/Documents';
 import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager } from '../../../util/DragManager';
+import { DragManager, dropActionType } from '../../../util/DragManager';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { Presentation } from '../../../util/TrackMovements';
 import { undoBatch } from '../../../util/UndoManager';
@@ -251,7 +251,7 @@ ScriptingGlobals.add(function resumeWorkspaceReplaying(value: Doc, _readOnly_: b
 
 ScriptingGlobals.add(function startRecordingDrag(value: { doc: Doc | string; e: React.PointerEvent }) {
     if (DocCast(value.doc)) {
-        DragManager.StartDocumentDrag([value.e.target as HTMLElement], new DragManager.DocumentDragData([DocCast(value.doc)], 'embed'), value.e.clientX, value.e.clientY);
+        DragManager.StartDocumentDrag([value.e.target as HTMLElement], new DragManager.DocumentDragData([DocCast(value.doc)], dropActionType.embed), value.e.clientX, value.e.clientY);
         value.e.preventDefault();
         return true;
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2d5239f06..4a1a51558 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -30,7 +30,7 @@ import { Docs, DocUtils } from '../../../documents/Documents';
 import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { DictationManager } from '../../../util/DictationManager';
 import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager } from '../../../util/DragManager';
+import { DragManager, dropActionType } from '../../../util/DragManager';
 import { MakeTemplate } from '../../../util/DropConverter';
 import { LinkManager } from '../../../util/LinkManager';
 import { RTFMarkup } from '../../../util/RTFMarkup';
@@ -624,7 +624,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         docId: draggedDoc[Id],
                         float: 'unset',
                     });
-                    if (!['embed', 'copy'].includes((dropAction ?? '') as any)) {
+                    if (![dropActionType.embed, dropActionType.copy].includes(dropAction ?? dropActionType.move)) {
                         added = dragData.removeDocument?.(draggedDoc) ? true : false;
                     } else {
                         added = true;
-- 
cgit v1.2.3-70-g09d2


From 7164179264d057986bfd5f303c61248f1c189406 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 8 Mar 2024 16:17:02 -0500
Subject: cleaned up MakeTemplate api and currentUserUtils templates for clicks
 etc.

---
 src/client/documents/Documents.ts                  |  9 ++++
 src/client/util/CurrentUserUtils.ts                | 35 ++++++++--------
 src/client/util/DropConverter.ts                   | 48 ++++++++++++++--------
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 +-
 5 files changed, 60 insertions(+), 38 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index b7d8222b9..e6969d1f3 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1024,6 +1024,15 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List<Doc>(), options);
         }
 
+        /**
+         * Creates a Doc to edit a script and write the compiled script into the specified field.
+         * Typically, this would be used to create a template that can then be applied to some other Doc
+         * in order to customize a behavior, such as onClick.
+         * @param script
+         * @param options
+         * @param fieldKey the field that the compiled script is written into.
+         * @returns the Scripting Doc
+         */
         export function ScriptingDocument(script: Opt<ScriptField> | null, options: DocumentOptions = {}, fieldKey?: string) {
             return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
         }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index cfba003fc..564a46514 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -105,9 +105,12 @@ export class CurrentUserUtils {
             { opts: { title: "onCheckedClick"},      script: "console.log(heading, checked, containingTreeView)"},
         ];
         const reqdClickList = reqdTempOpts.map(opts => {
+            const title = opts.opts.title?.toString();
             const allOpts = {...reqdClickOpts, ...opts.opts};
-            const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === opts.opts.title): undefined;
-            return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name}), allOpts), true, opts.opts.title?.toString());
+            const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === title): undefined;
+            const script = ScriptField.MakeScript(opts.script, {heading:Doc.name, checked:"boolean", containingTreeView:Doc.name});
+            const scriptDoc = Docs.Create.ScriptingDocument(script, allOpts, title)
+            return DocUtils.AssignOpts(clickDoc, allOpts) ?? MakeTemplate(scriptDoc);
         });
 
         const reqdOpts:DocumentOptions = {title: "click editor templates", _height:75, isSystem: true};
@@ -118,13 +121,13 @@ export class CurrentUserUtils {
     static setupNoteTemplates(doc: Doc, field="template_notes") {
         const tempNotes = DocCast(doc[field]);
         const reqdTempOpts:DocumentOptions[] = [
-            { noteType: "Postit", backgroundColor: "yellow", icon: "sticky-note"},
-            { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" },
-            { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
+            { title: "Postit", backgroundColor: "yellow",    icon: "sticky-note"},
+            { title: "Idea",   backgroundColor: "pink",      icon: "lightbulb" },
+            { title: "Topic",  backgroundColor: "lightblue", icon: "book-open" }];
         const reqdNoteList = reqdTempOpts.map(opts => {
-            const reqdOpts = {...opts, isSystem:true, title: "text", width:200, layout_autoHeight: true, layout_fitWidth: true};
-            const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
-            return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
+            const reqdOpts = {...opts, isSystem:true, width:200, layout_autoHeight: true, layout_fitWidth: true};
+            const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.title === opts.title): undefined;
+            return DocUtils.AssignOpts(noteTemp, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts));
         });
 
         const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true };
@@ -149,16 +152,16 @@ export class CurrentUserUtils {
         const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
         const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => {
-            const iconFieldName = "icon" + (type ? "_" + type : "");
-            const curIcon = DocCast(templateIconsDoc[iconFieldName]);
+            const title = "icon" + (type ? "_" + type : "");
+            const curIcon = DocCast(templateIconsDoc[title]);
             let creator = labelBox;
             switch (opts.iconTemplate) {
                 case DocumentType.IMG : creator = imageBox; break;
                 case DocumentType.FONTICON: creator = fontBox; break;
             }
-            const allopts = {isSystem: true, onClickScriptDisable: "never",  ...opts};
+            const allopts = {isSystem: true, onClickScriptDisable: "never",  ...opts, title};
             return DocUtils.AssignScripts(  (curIcon?.iconTemplate === opts.iconTemplate ? 
-                      DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts, templateField), true, iconFieldName, templateField))),
+                      DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[title] = MakeTemplate(creator(allopts, templateField)))),
                 {onClick:"deiconifyView(documentView)", onDoubleClick: "deiconifyViewToLightbox(documentView)", });
         };
         const labelBox = (opts: DocumentOptions, fieldKey:string) => Docs.Create.LabelDocument({
@@ -211,7 +214,7 @@ export class CurrentUserUtils {
         };
         const headerBtnHgt = 10;
         const headerTemplate = (opts:DocumentOptions) => {
-            const header = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "text", 
+            const header = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "Untitled Header", 
                 layout:
                 "<HTMLdiv transformOrigin='top left' width='{100/scale}%' height='{100/scale}%' transform='scale({scale})'>" +
                 `    <FormattedTextBox {...props} dontScale='true' fieldKey={'text'} height='calc(100% - ${headerBtnHgt}px - {this._headerHeight||0}px)'/>` +
@@ -224,7 +227,7 @@ export class CurrentUserUtils {
             // "    <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._headerPointerEvents||`none`}' fontSize='{this._headerFontSize}px' height='{this._headerHeight}px' background='{this._headerColor||this.target.mySharedDocs.userColor}' />" +
             // "    <FormattedTextBox {...props} fieldKey={'text'} position='absolute' top='{(this._headerHeight)*scale}px' height='calc({100/scale}% - {this._headerHeight}px)'/>" +
             // "</div>";
-            MakeTemplate(Doc.GetProto(header), true, "Untitled Header");
+            MakeTemplate(Doc.GetProto(header));
             return header; 
         }
         const slideView = (opts:DocumentOptions) => {
@@ -232,9 +235,9 @@ export class CurrentUserUtils {
                     [
                         Docs.Create.MulticolumnDocument([], { title: "hero", _height: 200,  isSystem: true }),
                         Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true,  _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) })
-                    ], opts);
+                    ], {...opts, title: "Untitled Slide View"});
 
-            MakeTemplate(Doc.GetProto(slide), true, "Untitled Slide View");
+            MakeTemplate(Doc.GetProto(slide));
             return slide; 
         }
         const plotlyApi = () => {
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index ba981145d..3df3e36c6 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -12,43 +12,55 @@ import { ButtonType, FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'
 import { DragManager } from './DragManager';
 import { ScriptingGlobals } from './ScriptingGlobals';
 
-export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = undefined, templateField: string = '') {
-    if (templateField) doc[DocData].title = templateField; /// the title determines which field is being templated
-    doc.isTemplateDoc = makeTemplate(doc, first, rename);
+/**
+ * Converts a Doc to a render template that can be applied to other Docs to customize how they render while
+ * still using the other Doc as the backing data store (ie, dataDoc). During rendering, if a layout Doc is provided
+ * with 'isTemplateDoc' set, then the layout Doc is treated as a template for the rendered Doc.  The template Doc is
+ * "expanded" to create an template instance for the rendered Doc.
+ *
+ *
+ * @param doc the doc to convert to a template
+ * @returns 'doc'
+ */
+export function MakeTemplate(doc: Doc) {
+    doc.isTemplateDoc = makeTemplate(doc, true);
     return doc;
 }
-//
-// converts 'doc' into a template that can be used to render other documents.
-// the title of doc is used to determine which field is being templated, so
-// passing a value for 'rename' allows the doc to be given a meangingful name
-// after it has been converted to
-function makeTemplate(doc: Doc, first: boolean = true, rename: Opt<string> = undefined): boolean {
+
+/**
+ * Recursively converts 'doc' into a template that can be used to render other documents.
+ *
+ * For recurive Docs in the template, their target fieldKey is defined by their title,
+ * not by whatever fieldKey they used in their layout.
+ * @param doc
+ * @param first whether this is the topmost root of the recursive template
+ * @returns whether a template was successfully created
+ */
+function makeTemplate(doc: Doc, first: boolean = true): boolean {
     const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
     if (layoutDoc.layout instanceof Doc) {
-        // its already a template
-        return true;
+        return true; // its already a template
     }
     const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)![0];
     const fieldKey = layout.replace("fieldKey={'", '').replace(/'}$/, '');
     const docs = DocListCast(layoutDoc[fieldKey]);
-    let any = false;
+    let isTemplate = false;
     docs.forEach(d => {
         if (!StrCast(d.title).startsWith('-')) {
-            any = Doc.MakeMetadataFieldTemplate(d, layoutDoc[DocData]) || any;
+            isTemplate = Doc.MakeMetadataFieldTemplate(d, layoutDoc[DocData]) || isTemplate;
         } else if (d.type === DocumentType.COL || d.data instanceof RichTextField) {
-            any = makeTemplate(d, false) || any;
+            isTemplate = makeTemplate(d, false) || isTemplate;
         }
     });
     if (first && !docs.length) {
         // bcz: feels hacky : if the root level document has items, it's not a field template
-        any = Doc.MakeMetadataFieldTemplate(doc, layoutDoc[DocData], true) || any;
+        isTemplate = Doc.MakeMetadataFieldTemplate(doc, layoutDoc[DocData], true) || isTemplate;
     } else if (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] instanceof ImageField) {
         if (!StrCast(layoutDoc.title).startsWith('-')) {
-            any = Doc.MakeMetadataFieldTemplate(layoutDoc, layoutDoc[DocData], true);
+            isTemplate = Doc.MakeMetadataFieldTemplate(layoutDoc, layoutDoc[DocData], true);
         }
     }
-    rename && (doc.title = rename);
-    return any;
+    return isTemplate;
 }
 export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
     data?.draggedDocuments.map((doc, i) => {
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 285a789e6..0c0e49411 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -631,7 +631,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
             }
             return (
                 <div>
-                    {!docs?.length || this._props.AddToMap /* hack to identify pres box trees */ ? null : (
+                    {!docs?.length || this.treeView.outlineMode || this._props.AddToMap /* hack to identify pres box trees */ ? null : (
                         <div className="treeView-sorting">
                             <IconButton
                                 color={sortings[sorting]?.color}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 4a1a51558..54e3e7b44 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -940,9 +940,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 description: 'Make Default Layout',
                 event: () => {
                     if (!this.layoutDoc.isTemplateDoc) {
-                        const title = StrCast(this.Document.title);
-                        this.Document.title = 'text';
-                        MakeTemplate(this.Document, true, title);
+                        MakeTemplate(this.Document);
                     }
                     Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.Document);
                     Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', this.Document);
-- 
cgit v1.2.3-70-g09d2


From f1ed1cff137c06afc4d4db8a8778f7827404889b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 9 Mar 2024 19:05:30 -0500
Subject: fixed up default text that uses a template to process an initial
 carriage return properly.  fixed text with inherited templates to be able to
 show fields with a default dashField value from template that can be
 overidden on instance.

---
 src/client/documents/Documents.ts                  | 11 +++---
 src/client/views/MarqueeAnnotator.tsx              |  4 +--
 src/client/views/PreviewCursor.tsx                 |  2 +-
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 36 +++++++++++--------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 .../collectionSchema/SchemaTableCell.tsx           |  2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  4 +--
 .../views/nodes/MapboxMapBox/MapboxContainer.tsx   |  4 +--
 .../views/nodes/formattedText/FormattedTextBox.tsx | 40 ++++++++++------------
 10 files changed, 55 insertions(+), 52 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e6969d1f3..7a3b965fe 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -3,7 +3,7 @@ import { action, reaction, runInAction } from 'mobx';
 import { basename } from 'path';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc';
-import { Initializing } from '../../fields/DocSymbols';
+import { DocData, Initializing } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { HtmlField } from '../../fields/HtmlField';
 import { InkField, PointData } from '../../fields/InkField';
@@ -1981,10 +1981,8 @@ export namespace DocUtils {
         }
     }
 
-    export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, backgroundColor?: string) {
+    export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) {
         const tbox = Docs.Create.TextDocument('', {
-            _xMargin: noMargins ? 0 : undefined,
-            _yMargin: noMargins ? 0 : undefined,
             annotationOn,
             backgroundColor,
             _width: width || 200,
@@ -1997,11 +1995,14 @@ export namespace DocUtils {
             _layout_enableAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
             title,
         });
+
         const template = Doc.UserDoc().defaultTextLayout;
         if (template instanceof Doc) {
+            // if a default text template is specified
             tbox._width = NumCast(template._width);
             tbox.layout_fieldKey = 'layout_' + StrCast(template.title);
-            Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template;
+            Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template; // set the text doc's layout to render with the text template
+            tbox[DocData].proto = template; // and also set the text doc to inherit from the template (this allows the template to specify default field values)
         }
         return tbox;
     }
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index a4303c3aa..f59042b04 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -88,7 +88,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
 
         const textRegionAnno = Docs.Create.HTMLMarkerDocument([], {
             annotationOn: this.props.Document,
-            text: this.props.selectionText(),
+            text: this.props.selectionText() as any, // text want an RTFfield, but strings are acceptable, too.
             backgroundColor: 'transparent',
             presentation_duration: 2100,
             presentation_transition: 500,
@@ -198,7 +198,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
             const sourceAnchorCreator = () => this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true); // hyperlink color
 
             const targetCreator = (annotationOn: Doc | undefined) => {
-                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow');
+                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
                 FormattedTextBox.SetSelectOnLoad(target);
                 return target;
             };
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 456b753b4..a94c18295 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -85,7 +85,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
                 } else {
                     FormattedTextBox.PasteOnLoad = e;
                     if (e.clipboardData.getData('dash/pdfAnchor')) e.preventDefault();
-                    UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined, undefined)), 'paste');
+                    UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined)), 'paste');
                 }
             }
             //pasting in images
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 0c0e49411..c6bbcb0a5 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -884,7 +884,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
         // prettier-ignore
         switch (property.split(':')[0]) {
             case StyleProp.Opacity:         return this.treeView.outlineMode ? undefined : 1;
-            case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
+            case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : undefined;//StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
             case StyleProp.Highlighting:    if (this.treeView.outlineMode) return undefined;
             case StyleProp.BoxShadow:       return undefined;
             case StyleProp.DocContents:
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4eb946939..9500b918a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -30,7 +30,7 @@ import { SelectionManager } from '../../../util/SelectionManager';
 import { freeformScrollMode } from '../../../util/SettingsManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { Transform } from '../../../util/Transform';
-import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
 import { Timeline } from '../../animationtimeline/Timeline';
 import { ContextMenu } from '../../ContextMenu';
 import { GestureOverlay } from '../../GestureOverlay';
@@ -1146,23 +1146,29 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     isContentActive = () => this._props.isContentActive();
 
-    @undoBatch
+    /**
+     * Create a new text note of the same style as the one being typed into.
+     * If the text doc is be part of a larger templated doc, the new Doc will be a copy of the templated Doc
+     *
+     * @param fieldProps render props for the text doc being typed into
+     * @param below whether to place the new text Doc below or to the right of the one being typed into.
+     * @returns whether the new text doc was created and added successfully
+     */
+    createTextDocCopy = undoable((fieldProps: FieldViewProps, below: boolean) => {
+        const textDoc = DocCast(fieldProps.Document.rootDocument, fieldProps.Document);
+        const newDoc = Doc.MakeCopy(textDoc, true);
+        newDoc[DocData][Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
+        newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
+        newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
+        FormattedTextBox.SetSelectOnLoad(newDoc);
+        FormattedTextBox.DontSelectInitialText = true;
+        return this.addDocument?.(newDoc);
+    }, 'copied text note');
+
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
             e.stopPropagation?.();
-            const below = !e.altKey && e.key !== 'Tab';
-            const layout_fieldKey = StrCast(fieldProps.fieldKey);
-            const newDoc = Doc.MakeCopy(fieldProps.Document, true);
-            const dataField = fieldProps.Document[Doc.LayoutFieldKey(newDoc)];
-            newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
-            if (below) newDoc.y = NumCast(fieldProps.Document.y) + NumCast(fieldProps.Document._height) + 10;
-            else newDoc.x = NumCast(fieldProps.Document.x) + NumCast(fieldProps.Document._width) + 10;
-            if (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) {
-                newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey];
-            }
-            newDoc[DocData].text = undefined;
-            FormattedTextBox.SetSelectOnLoad(newDoc);
-            return this.addDocument?.(newDoc);
+            return this.createTextDocCopy(fieldProps, !e.altKey && e.key !== 'Tab');
         }
     };
     @computed get childPointerEvents() {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index e22b83307..b913e05ad 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -178,7 +178,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
         } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views.length < 2) {
             FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this._props.childLayoutString ? e.key : '';
             FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('type new note');
-            this._props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this._props.xPadding === 0));
+            this._props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100));
             e.stopPropagation();
         }
     };
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 46867665d..ed1b519b4 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -134,7 +134,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
                         if (shiftDown && enterKey) {
                             this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value);
                         }
-                        const ret = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value);
+                        const ret = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
                         this._props.finishEdit?.();
                         return ret;
                     }, 'edit schema cell')}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index c185c66fc..927e6fad4 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -379,7 +379,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         });
 
         const targetCreator = (annotationOn: Doc | undefined) => {
-            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow');
+            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
             FormattedTextBox.SetSelectOnLoad(target);
             return target;
         };
@@ -592,7 +592,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.ConfigDocument({
             title: 'MapAnchor:' + this.Document.title,
-            text: StrCast(this.selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location',
+            text: (StrCast(this.selectedPinOrRoute?.map) || StrCast(this.Document.map) || 'map location') as any,
             config_latitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.latitude ?? this.dataDoc.latitude),
             config_longitude: NumCast((existingPin ?? this.selectedPinOrRoute)?.longitude ?? this.dataDoc.longitude),
             config_map_zoom: NumCast(this.dataDoc.map_zoom),
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index 8a5bd7ce6..3eb051dbf 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -232,7 +232,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
         });
 
         const targetCreator = (annotationOn: Doc | undefined) => {
-            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, 'yellow');
+            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
             FormattedTextBox.SetSelectOnLoad(target);
             return target;
         };
@@ -466,7 +466,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
         /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
         const anchor = Docs.Create.ConfigDocument({
             title: 'MapAnchor:' + this.Document.title,
-            text: StrCast(this.selectedPin?.map) || StrCast(this.Document.map) || 'map location',
+            text: (StrCast(this.selectedPin?.map) || StrCast(this.Document.map) || 'map location') as any,
             config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude),
             config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude),
             config_map_zoom: NumCast(this.dataDoc.map_zoom),
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 54e3e7b44..1ff7274f8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -67,7 +67,6 @@ import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
 import { RichTextRules } from './RichTextRules';
 import { schema } from './schema_rts';
 import { SummaryView } from './SummaryView';
-import Select from 'react-select';
 // import * as applyDevTools from 'prosemirror-dev-tools';
 @observer
 export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
@@ -100,7 +99,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _recordingStart: number = 0;
     private _ignoreScroll = false;
-    private _lastText = '';
     private _hadDownFocus = false;
     private _focusSpeed: Opt<number>;
     private _keymap: any = undefined;
@@ -306,7 +304,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             e.preventDefault();
             e.stopPropagation();
             const targetCreator = (annotationOn?: Doc) => {
-                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn);
+                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn);
                 FormattedTextBox.SetSelectOnLoad(target);
                 return target;
             };
@@ -1457,39 +1455,37 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
 
         const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.Document, FormattedTextBox.SelectOnLoad) && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.()));
-        if (this._editorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
-            const selLoadChar = FormattedTextBox.SelectOnLoadChar;
+        const selLoadChar = FormattedTextBox.SelectOnLoadChar;
+        if (selectOnLoad) {
             FormattedTextBox.SelectOnLoad = undefined;
+            FormattedTextBox.SelectOnLoadChar = '';
+        }
+        if (this._editorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
             this._props.select(false);
             if (selLoadChar) {
                 const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
                 const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
                 const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
                 const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
-                const tr = this._editorView.state.tr
-                    .setStoredMarks(storedMarks)
-                    .insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size)
-                    .setStoredMarks(storedMarks);
+                const tr1 = this._editorView.state.tr.setStoredMarks(storedMarks);
+                const tr2 = selLoadChar === 'Enter' ? tr1.insert(this._editorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this._editorView.state.doc.content.size - 1);
+                const tr = tr2.setStoredMarks(storedMarks);
+
                 this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
             } else if (curText && !FormattedTextBox.DontSelectInitialText) {
                 selectAll(this._editorView.state, this._editorView?.dispatch);
             }
         }
-        selectOnLoad && this._editorView!.focus();
+        if (selectOnLoad) {
+            FormattedTextBox.DontSelectInitialText = false;
+            this._editorView!.focus();
+        }
         if (this._props.isContentActive()) this.prepareForTyping();
-        if (this._editorView) {
-            const tr = this._editorView.state.tr;
-            const { from, to } = tr.selection;
-            // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the selection after the document has ben fully instantiated.
-            if (FormattedTextBox.DontSelectInitialText) setTimeout(() => this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)))), 250);
-
-            if (FormattedTextBox.PasteOnLoad) {
-                const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
-                FormattedTextBox.PasteOnLoad = undefined;
-                pdfAnchorId && this.addPdfReference(pdfAnchorId);
-            }
+        if (this._editorView && FormattedTextBox.PasteOnLoad) {
+            const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
+            FormattedTextBox.PasteOnLoad = undefined;
+            pdfAnchorId && this.addPdfReference(pdfAnchorId);
         }
-        FormattedTextBox.DontSelectInitialText = false;
     }
 
     // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
-- 
cgit v1.2.3-70-g09d2


From c563aec906c5728a5563fefac6ab573a31375641 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 12 Mar 2024 09:09:59 -0400
Subject: made text templates be both layout templates and prototypes of new
 text documents.  fixed onPaint funcs to be undoable.  fixed comparisonBox to
 render a text box if it's fieldKey has a richtext field - this makes
 flashcard templates much easier.  fixed right-click on hyperlinks to bring up
 menu.   fixed layout_centered to be settable on templates.  added enable
 flashcard property for text.

---
 src/client/documents/Documents.ts                  | 32 ++++++++++++----------
 src/client/util/CurrentUserUtils.ts                |  4 +--
 src/client/views/PropertiesButtons.tsx             | 21 ++++++++++++--
 src/client/views/StyleProvider.tsx                 |  2 +-
 src/client/views/nodes/ComparisonBox.tsx           | 13 ++++++---
 src/client/views/nodes/DocumentView.tsx            | 27 ++++++++++++++----
 src/client/views/nodes/KeyValueBox.tsx             |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 +--
 .../views/nodes/formattedText/RichTextMenu.tsx     |  4 +--
 .../views/nodes/formattedText/RichTextRules.ts     |  2 +-
 10 files changed, 75 insertions(+), 36 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7a3b965fe..a13edec77 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1982,27 +1982,29 @@ export namespace DocUtils {
     }
 
     export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) {
+        const defaultTextTemplate = DocCast(Doc.UserDoc().defaultTextLayout);
         const tbox = Docs.Create.TextDocument('', {
             annotationOn,
             backgroundColor,
-            _width: width || 200,
-            _height: 35,
-            x: x,
-            y: y,
-            _layout_centered: BoolCast(Doc.UserDoc().layout_centered),
-            _layout_fitWidth: true,
-            _layout_autoHeight: true,
-            _layout_enableAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
+            x,
+            y,
             title,
+            ...(defaultTextTemplate
+                ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance
+                : {
+                      _width: width || 200,
+                      _height: 35,
+                      _layout_centered: BoolCast(Doc.UserDoc()._layout_centered),
+                      _layout_fitWidth: true,
+                      _layout_autoHeight: true,
+                      _layout_enableAltContentUI: BoolCast(Doc.UserDoc().defaultToFlashcards),
+                  }),
         });
 
-        const template = Doc.UserDoc().defaultTextLayout;
-        if (template instanceof Doc) {
-            // if a default text template is specified
-            tbox._width = NumCast(template._width);
-            tbox.layout_fieldKey = 'layout_' + StrCast(template.title);
-            Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = template; // set the text doc's layout to render with the text template
-            tbox[DocData].proto = template; // and also set the text doc to inherit from the template (this allows the template to specify default field values)
+        if (defaultTextTemplate) {
+            tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title);
+            Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = defaultTextTemplate; // set the text doc's layout to render with the text template
+            tbox[DocData].proto = defaultTextTemplate; // and also set the text doc to inherit from the template (this allows the template to specify default field values)
         }
         return tbox;
     }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index fc315fdbe..84a33500d 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -276,7 +276,7 @@ export class CurrentUserUtils {
             slide[DocData].text  = rtfield;
             slide[DocData].layout_textPainted = `<CollectionView {...props} fieldKey={'text'}/>`;
             slide[DocData]._type_collection = CollectionViewType.Freeform;
-            slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted", "")`, {documentView:"any"});
+            slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, {documentView:"any"});
             return slide; 
         }
         const mermaidsApi = () => {
@@ -330,7 +330,7 @@ pie title Minerals in my tap water
             slide[DocData].text  = rtfield;
             slide[DocData].layout_textPainted = `<CollectionView {...props} fieldKey={'text'}/>`;
             slide[DocData]._type_collection = CollectionViewType.Freeform;
-            slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted", "")`, {documentView:"any"});
+            slide.onPaint = ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, {documentView:"any"});
             return slide; 
         }
         const apis = [plotlyApi(), mermaidsApi()]
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 3cb835e39..02f288a68 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -13,7 +13,8 @@ import { RxWidth } from 'react-icons/rx';
 import { TbEditCircle, TbEditCircleOff, TbHandOff, TbHandStop, TbHighlight, TbHighlightOff } from 'react-icons/tb';
 import { TfiBarChart } from 'react-icons/tfi';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { RichTextField } from '../../fields/RichTextField';
+import { DocData } from '../../fields/DocSymbols';
+import { ScriptField } from '../../fields/ScriptField';
 import { BoolCast, ScriptCast } from '../../fields/Types';
 import { ImageField } from '../../fields/URLField';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
@@ -27,6 +28,7 @@ import { InkingStroke } from './InkingStroke';
 import './PropertiesButtons.scss';
 import { Colors } from './global/globalEnums';
 import { DocumentView, OpenWhere } from './nodes/DocumentView';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 
 @observer
 export class PropertiesButtons extends React.Component<{}, {}> {
@@ -174,6 +176,20 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         );
     }
 
+    @computed get flashcardButton() {
+        return this.propertyToggleBtn(
+            on => (on ? 'DISABLE FLASHCARD' : 'ENABLE FLASHCARD'),
+            'layout_textPainted',
+            on => `${on ? 'Flashcard enabled' : 'Flashcard disabled'} `,
+            on => <MdTouchApp />,
+            (dv, doc) => {
+                const on = doc.onPaint ? true : false;
+                doc[DocData].onPaint = on ? undefined : ScriptField.MakeScript(`toggleDetail(documentView, "textPainted")`, { documentView: 'any' });
+                doc[DocData].layout_textPainted = on ? undefined : `<ComparisonBox {...props} fieldKey={'${dv?.LayoutFieldKey ?? 'text'}'}/>`;
+            }
+        );
+    }
+
     @computed get fitContentButton() {
         return this.propertyToggleBtn(
             on => (on ? 'PREVIOUS VIEW' : 'VIEW ALL'), //'View All',
@@ -481,7 +497,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
 
     render() {
         const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)];
-        const isText = layoutField instanceof RichTextField;
+        const isText = SelectionManager.Views.lastElement()?.ComponentView instanceof FormattedTextBox;
         const isInk = this.selectedDoc?.layout_isSvg;
         const isImage = layoutField instanceof ImageField;
         const isMap = this.selectedDoc?.type === DocumentType.MAP;
@@ -507,6 +523,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {/* {toggle(this.freezeThumb)} */}
                 {toggle(this.forceActiveButton)}
                 {toggle(this.verticalAlignButton, { display: !isText ? 'none' : '' })}
+                {toggle(this.flashcardButton, { display: !isText ? 'none' : '' })}
                 {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
                 {/* {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })} */}
                 {toggle(this.layout_autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 0794efe4c..ab811858a 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -61,7 +61,7 @@ function togglePaintView(e: React.MouseEvent, doc: Opt<Doc>, props: Opt<FieldVie
         value: undefined,
     };
     e.stopPropagation();
-    UndoManager.RunInBatch(() => doc && ScriptCast(doc.onPaint).script.run(scriptProps), 'togglePaintView');
+    ScriptCast(doc?.onPaint)?.script.run(scriptProps);
 }
 
 export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) {
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index ef8c045cc..2b57178f4 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -14,6 +14,8 @@ import './ComparisonBox.scss';
 import { DocumentView } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import { PinProps, PresBox } from './trails';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import { RichTextField } from '../../../fields/RichTextField';
 
 @observer
 export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
@@ -172,12 +174,15 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
         const displayDoc = (which: string) => {
             const whichDoc = DocCast(this.dataDoc[which]);
             const targetDoc = DocCast(whichDoc?.annotationOn, whichDoc);
-            return targetDoc ? (
+            // if there is no Doc in the first comparison slot, but the comparison box's fieldKey slot has a RichTextField, then render a text box to show the contents of the document's field key slot
+            const layoutTemplateString = !targetDoc && which.endsWith('1') && this.Document[this.fieldKey] instanceof RichTextField ? FormattedTextBox.LayoutString(this.fieldKey) : undefined;
+            return targetDoc || layoutTemplateString ? (
                 <>
                     <DocumentView
                         {...this._props}
-                        Document={targetDoc}
-                        TemplateDataDocument={undefined}
+                        renderDepth={this.props.renderDepth + 1}
+                        LayoutTemplateString={layoutTemplateString}
+                        Document={layoutTemplateString ? this.Document : targetDoc}
                         containerViewPath={this.DocumentView?.().docViewPath}
                         moveDocument={which.endsWith('1') ? this.moveDoc1 : this.moveDoc2}
                         removeDocument={which.endsWith('1') ? this.remDoc1 : this.remDoc2}
@@ -190,7 +195,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
                         hideLinkButton={true}
                         pointerEvents={this._isAnyChildContentActive ? undefined : returnNone}
                     />
-                    {clearButton(which)}
+                    {layoutTemplateString ? null : clearButton(which)}
                 </> // placeholder image if doc is missing
             ) : (
                 <div className="placeholder">
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 29266bd8e..40592c2cd 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -457,11 +457,11 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
 
     deleteClicked = undoable(() => this._props.removeDocument?.(this.Document), 'delete doc');
     setToggleDetail = undoable(
-        (defaultLayout: string, scriptFieldKey: 'onClick') =>
+        (scriptFieldKey: 'onClick') =>
             (this.Document[scriptFieldKey] = ScriptField.MakeScript(
                 `toggleDetail(documentView, "${StrCast(this.Document.layout_fieldKey)
                     .replace('layout_', '')
-                    .replace(/^layout$/, 'detail')}", "${defaultLayout}")`,
+                    .replace(/^layout$/, 'detail')}")`,
                 { documentView: 'any' }
             )),
         'set toggle detail'
@@ -1201,7 +1201,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
     };
     public noOnClick = () => this._docViewInternal?.noOnClick();
     public toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => this._docViewInternal?.toggleFollowLink(zoom, setTargetToggle);
-    public setToggleDetail = (defaultLayout = '', scriptFieldKey = 'onClick') => this._docViewInternal?.setToggleDetail(defaultLayout, scriptFieldKey);
+    public setToggleDetail = (scriptFieldKey = 'onClick') => this._docViewInternal?.setToggleDetail(scriptFieldKey);
     public onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => this._docViewInternal?.onContextMenu?.(e, pageX, pageY);
     public cleanupPointerEvents = () => this._docViewInternal?.cleanupPointerEvents();
     public startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this._docViewInternal?.startDragging(x, y, dropAction, hideSource);
@@ -1279,7 +1279,22 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
         custom && DocUtils.makeCustomViewClicked(this.Document, Docs.Create.StackingDocument, layout, undefined);
     }, 'set custom view');
 
+    /**
+     * This switches between the current view of a Doc and a specified alternate layout view.
+     * The current view of the Doc is stored in the layout_default field so that it can be restored.
+     * If the current view of the Doc is already the specified alternate layout view, this will switch
+     * back to the original layout (stored in layout_default)
+     * @param detailLayoutKeySuffix the name of the alternate layout field key (NOTE: 'layout_' will be prepended to this string to get the actual field nam)
+     */
+    public toggleDetail = (detailLayoutKeySuffix: string) => {
+        const curLayout = StrCast(this.Document.layout_fieldKey).replace('layout_', '').replace('layout', '');
+        if (!this.Document.layout_default && curLayout !== detailLayoutKeySuffix) this.Document.layout_default = curLayout;
+        const defaultLayout = StrCast(this.Document.layout_default);
+        if (this.Document.layout_fieldKey === 'layout_' + detailLayoutKeySuffix) this.switchViews(defaultLayout ? true : false, defaultLayout, undefined, true);
+        else this.switchViews(true, detailLayoutKeySuffix, undefined, true);
+    };
     public switchViews = (custom: boolean, view: string, finished?: () => void, useExistingLayout = false) => {
+        const batch = UndoManager.StartBatch('switchView:' + view);
         runInAction(() => this._docViewInternal && (this._docViewInternal._animateScalingTo = 0.1)); // shrink doc
         setTimeout(
             action(() => {
@@ -1292,6 +1307,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
                 setTimeout(
                     action(() => {
                         this._docViewInternal && (this._docViewInternal._animateScalingTo = 0);
+                        batch.end();
                         finished?.();
                     }),
                     Math.max(0, (this._docViewInternal?.animateScaleTime() ?? 0) - 10)
@@ -1451,9 +1467,8 @@ ScriptingGlobals.add(function deiconifyViewToLightbox(documentView: DocumentView
     LightboxView.Instance.AddDocTab(documentView.Document, OpenWhere.lightbox, 'layout'); //, 0);
 });
 
-ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string, defaultLayout = '') {
-    if (dv.Document.layout_fieldKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(defaultLayout ? true : false, defaultLayout, undefined, true);
-    else dv.switchViews(true, detailLayoutKeySuffix, undefined, true);
+ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
+    dv.toggleDetail(detailLayoutKeySuffix);
 });
 
 ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc, linkSource: Doc) {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 39a45693e..89a5ac0b8 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -76,7 +76,7 @@ export class KeyValueBox extends ObservableReactComponent<FieldViewProps> {
         value = eq ? value.substring(1) : value;
         const dubEq = value.startsWith(':=') ? 'computed' : value.startsWith('$=') ? 'script' : false;
         value = dubEq ? value.substring(2) : value;
-        const options: ScriptOptions = { addReturn: true, typecheck: false, params: { this: Doc.name, self: Doc.name, _last_: 'any', _readOnly_: 'boolean' }, editable: true };
+        const options: ScriptOptions = { addReturn: true, typecheck: false, params: { this: Doc.name, self: Doc.name, documentView: 'any', _last_: 'any', _readOnly_: 'boolean' }, editable: true };
         if (dubEq) options.typecheck = false;
         const script = CompileScript(value, { ...options, transformer: DocumentIconContainer.getTransformer() });
         return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq };
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1ff7274f8..1bd230891 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1574,7 +1574,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         }
         if (!state || !editor || !this.ProseRef?.children[0].className.includes('-focused')) return;
         if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
-        else if (this._props.isContentActive()) {
+        else if (this._props.isContentActive() && !e.button) {
             const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
             let xpos = pcords?.pos || 0;
             while (xpos > 0 && !state.doc.resolve(xpos).node()?.isTextblock) {
@@ -2074,7 +2074,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                         onScroll={this.onScroll}
                         onDrop={this.ondrop}>
                         <div
-                            className={`formattedTextBox-inner${rounded} ${this.layoutDoc.layout_centered ? 'centered' : ''}`}
+                            className={`formattedTextBox-inner${rounded} ${this.layoutDoc._layout_centered ? 'centered' : ''}`}
                             ref={this.createDropTarget}
                             style={{
                                 padding: StrCast(this.layoutDoc._textBoxPadding),
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index cd0cdaa74..dc2c06701 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -104,7 +104,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
         return this._activeAlignment;
     }
     @computed get textVcenter() {
-        return BoolCast(this.layoutDoc?.layout_centered);
+        return BoolCast(this.layoutDoc?._layout_centered);
     }
     _disposer: IReactionDisposer | undefined;
     componentDidMount() {
@@ -450,7 +450,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
     }
 
     vcenterToggle = (view: EditorView, dispatch: any) => {
-        this.layoutDoc && (this.layoutDoc.layout_centered = !this.layoutDoc.layout_centered);
+        this.layoutDoc && (this.layoutDoc._layout_centered = !this.layoutDoc._layout_centered);
     };
     align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => {
         if (this.TextView?._props.rootSelected?.()) {
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 9bd41f42c..d5c91fc09 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -78,7 +78,7 @@ export class RichTextRules {
                 this.TextBox.dataDoc[paintedField] = CollectionView.LayoutString(this.TextBox.fieldKey);
                 const layoutFieldKey = StrCast(this.TextBox.layoutDoc.layout_fieldKey); // save the current layout fieldkey
                 this.TextBox.layoutDoc.layout_fieldKey = paintedField; // setup the paint layout field key
-                this.TextBox.DocumentView?.().setToggleDetail(layoutFieldKey.replace('layout_', '').replace('layout', ''), 'onPaint'); // create the script to toggle between the painted and regular view
+                this.TextBox.DocumentView?.().setToggleDetail('onPaint'); // create the script to toggle between the painted and regular view
                 this.TextBox.layoutDoc.layout_fieldKey = layoutFieldKey; // restore the layout field key to text
 
                 return state.tr.delete(start, end).setBlockType(start, start, schema.nodes.code_block);
-- 
cgit v1.2.3-70-g09d2


From 641220e9cd9626af182118f84f8f775d7638cc67 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 12 Mar 2024 12:17:24 -0400
Subject: fixed links to text to update automatically when textbox css styles
 change.

---
 src/client/views/nodes/LinkBox.tsx                        | 4 +++-
 src/client/views/nodes/formattedText/FormattedTextBox.tsx | 4 ++--
 src/client/views/nodes/trails/PresBox.tsx                 | 1 -
 src/fields/DocSymbols.ts                                  | 2 +-
 4 files changed, 6 insertions(+), 5 deletions(-)

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

diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index decdbb240..6e4d0e92a 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -2,7 +2,7 @@ import { action, computed, IReactionDisposer, makeObservable, observable, reacti
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import Xarrow from 'react-xarrows';
-import { DocData } from '../../../fields/DocSymbols';
+import { DocCss, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { DocCast, NumCast, StrCast } from '../../../fields/Types';
 import { DashColor, emptyFunction, lightOrDark, returnFalse } from '../../../Utils';
@@ -83,6 +83,8 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
             b.Document[b.LayoutFieldKey];
             a.Document.layout_scrollTop;
             b.Document.layout_scrollTop;
+            a.Document[DocCss];
+            b.Document[DocCss];
 
             const axf = a.screenToViewTransform(); // these force re-render when a or b moves (so do NOT remove)
             const bxf = b.screenToViewTransform();
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1bd230891..e80c869d3 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -746,7 +746,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             const hr = Math.round(Date.now() / 1000 / 60 / 60);
             numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-hr-' + (hr - i), { opacity: ((10 - i - 1) / 10).toString() }));
         }
-        this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css changes happen outside of react/mobx.  so we need to set a flag that will notify anyone intereted in layout changes triggered by css changes (eg., CollectionLinkView)
+        this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css changes happen outside of react/mobx.  so we need to set a flag that will notify anyone interested in layout changes triggered by css changes (eg., CollectionLinkView)
     };
 
     @observable _showSidebar = false;
@@ -1189,7 +1189,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         this._cachedLinks = LinkManager.Links(this.Document);
         this._disposers.breakupDictation = reaction(() => Doc.RecordingEvent, this.breakupDictation);
         this._disposers.layout_autoHeight = reaction(
-            () => ({ autoHeight: this.layout_autoHeight, fontSize: this.fontSize }),
+            () => ({ autoHeight: this.layout_autoHeight, fontSize: this.fontSize, css: this.Document[DocCss] }),
             (autoHeight, fontSize) => setTimeout(() => autoHeight && this.tryUpdateScrollHeight())
         );
         this._disposers.highlights = reaction(
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 0d4f9ec78..e34144fae 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -738,7 +738,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         const finished = () => {
             afterNav?.();
-            console.log('Finish Slide Nav: ' + targetDoc.title);
             targetDoc[Animation] = undefined;
         };
         const selViewCache = Array.from(this.selectedArray);
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
index f8a57acd5..64d657e4f 100644
--- a/src/fields/DocSymbols.ts
+++ b/src/fields/DocSymbols.ts
@@ -28,4 +28,4 @@ export const Initializing = Symbol('DocInitializing');
 export const ForceServerWrite = Symbol('DocForceServerWrite');
 export const CachedUpdates = Symbol('DocCachedUpdates');
 
-export const DashVersion = 'v0.7.0';
+export const DashVersion = 'v0.8.0';
-- 
cgit v1.2.3-70-g09d2

