From be1ac92cdc609a7931ec8427f507648da117b4ea Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 10 Apr 2024 13:53:38 -0400
Subject: fixed buttons to have an embedContainer. cleaning up notetaking view
 - fixing colors for input buttons, fixing when buttons appear (by extending
 the column targe to always be 100%)

---
 .../collections/CollectionNoteTakingViewColumn.tsx | 41 +++++++++++++---------
 1 file changed, 24 insertions(+), 17 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index db178d500..788490b82 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -1,8 +1,8 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { returnEmptyString } from '../../../Utils';
+import { lightOrDark, returnEmptyString } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { RichTextField } from '../../../fields/RichTextField';
@@ -26,6 +26,7 @@ import './CollectionNoteTakingView.scss';
 interface CSVFieldColumnProps {
     Document: Doc;
     TemplateDataDocument: Opt<Doc>;
+    backgroundColor?: (() => string) | undefined;
     docList: Doc[];
     heading: string;
     pivotField: string;
@@ -38,6 +39,7 @@ interface CSVFieldColumnProps {
     gridGap: number;
     headings: () => object[];
     select: (ctrlPressed: boolean) => void;
+    isContentActive: () => boolean | undefined;
     renderChildren: (docs: Doc[]) => JSX.Element[];
     addDocument: (doc: Doc | Doc[]) => boolean;
     createDropTarget: (ele: HTMLDivElement) => void;
@@ -57,7 +59,12 @@ interface CSVFieldColumnProps {
  */
 @observer
 export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSVFieldColumnProps> {
-    @observable private _background = 'inherit';
+    @observable private _hover = false;
+
+    constructor(props: CSVFieldColumnProps) {
+        super(props);
+        makeObservable(this);
+    }
 
     // columnWidth returns the width of a column in absolute pixels
     @computed get columnWidth() {
@@ -122,8 +129,8 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
         return false;
     };
 
-    @action pointerEntered = () => SnappingManager.IsDragging && (this._background = '#b4b4b4');
-    @action pointerLeave = () => (this._background = 'inherit');
+    @action pointerEntered = () => (this._hover = true);
+    @action pointerLeave = () => (this._hover = false);
     @undoBatch
     addTextNote = (char: string) => this.addNewTextDoc('-typed text-', false, true);
 
@@ -273,11 +280,11 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
                         </div>
 
                         {!this._props.chromeHidden ? (
-                            <div className="collectionNoteTakingView-DocumentButtons" style={{ marginBottom: 10 }}>
-                                <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
-                                    <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.addTextNote} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
+                            <div className="collectionNoteTakingView-DocumentButtons" style={{ display: this._props.isContentActive() ? 'flex' : 'none', marginBottom: 10 }}>
+                                <div className="collectionNoteTakingView-addDocumentButton" style={{ color: lightOrDark(this._props.backgroundColor?.()) }}>
+                                    <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.addTextNote} placeholder={"Type ':' for commands"} contents={'+ Node'} menuCallback={this.menuCallback} />
                                 </div>
-                                <div key={`${this._props.Document[Id]}-addGroup`} className="collectionNoteTakingView-addDocumentButton">
+                                <div className="collectionNoteTakingView-addDocumentButton" style={{ color: lightOrDark(this._props.backgroundColor?.()) }}>
                                     <EditableView {...this._props.editableViewProps()} />
                                 </div>
                             </div>
@@ -292,17 +299,17 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
         TraceMobx();
         return (
             <div
-                className="collectionNoteTakingViewFieldColumn"
-                key={this._heading}
+                className="collectionNoteTakingViewFieldColumnHover"
+                onPointerEnter={this.pointerEntered}
+                onPointerLeave={this.pointerLeave}
                 style={{
                     width: this.columnWidth,
-                    background: this._background,
+                    background: this._hover && SnappingManager.IsDragging ? '#b4b4b4' : 'inherit',
                     marginLeft: this._props.headings().findIndex((h: any) => h[0] === this._props.headingObject) === 0 ? NumCast(this._props.Document.xMargin) : 0,
-                }}
-                ref={this.createColumnDropRef}
-                onPointerEnter={this.pointerEntered}
-                onPointerLeave={this.pointerLeave}>
-                {this.innards}
+                }}>
+                <div className="collectionNoteTakingViewFieldColumn" key={this._heading} ref={this.createColumnDropRef}>
+                    {this.innards}
+                </div>
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 53fbe74037f03456a678d592d0ae5660c2f0d55e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 10 Apr 2024 21:02:05 -0400
Subject: more notetaking fixes - turn off autoSize, unobserve children.  fixed
 typing a new note to delete placeholder text.   added autoCreate for columns.
  added pivot column selector for notetaking view.

---
 .../collections/CollectionNoteTakingView.scss      |  9 +++
 .../views/collections/CollectionNoteTakingView.tsx | 80 +++++++++++++++++-----
 .../collections/CollectionNoteTakingViewColumn.tsx |  4 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 4 files changed, 73 insertions(+), 22 deletions(-)

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

diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index 81000e7a5..95fda7b0a 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -494,6 +494,15 @@
     .rc-switch-checked .rc-switch-inner {
         left: 8px;
     }
+
+    .collectionNoteTaking-pivotField {
+        display: none;
+    }
+    &:hover {
+        .collectionNoteTaking-pivotField {
+            display: unset;
+        }
+    }
 }
 
 .collectionNoteTakingViewLight {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 5c52d2398..d8a0aebb1 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -14,9 +14,10 @@ import { Docs, DocUtils } from '../../documents/Documents';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
-import { undoBatch } from '../../util/UndoManager';
+import { undoable, undoBatch } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
+import { Colors } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { DocumentView } from '../nodes/DocumentView';
 import { FieldViewProps, FocusViewOptions } from '../nodes/FieldView';
@@ -26,7 +27,7 @@ import './CollectionNoteTakingView.scss';
 import { CollectionNoteTakingViewColumn } from './CollectionNoteTakingViewColumn';
 import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivider';
 import { CollectionSubView } from './CollectionSubView';
-import { Colors } from '../global/globalEnums';
+import { FieldsDropdown } from '../FieldsDropdown';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 /**
@@ -42,7 +43,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     _masonryGridRef: HTMLDivElement | null = null;
     _draggerRef = React.createRef<HTMLDivElement>();
     @computed get notetakingCategoryField() {
-        return StrCast(this.dataDoc.notetaking_category, 'NotetakingCategory');
+        return StrCast(this.dataDoc.notetaking_column, StrCast(this.layoutDoc.pivotField, 'notetaking_column'));
     }
     public DividerWidth = 16;
     @observable docsDraggedRowCol: number[] = [];
@@ -155,25 +156,34 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         );
     };
 
+    @computed get allFieldValues() {
+        return new Set(this.childDocs.map(doc => StrCast(doc[this.notetakingCategoryField])));
+    }
+
     componentDidMount() {
         super.componentDidMount?.();
         document.addEventListener('pointerup', this.removeDocDragHighlight, true);
-        this._disposers.layout_autoHeight = reaction(
-            () => this.layoutDoc._layout_autoHeight,
-            layout_autoHeight => layout_autoHeight && this._props.setHeight?.(this.headerMargin + Math.max(...this._refList.map(DivHeight)))
+
+        this._disposers.autoColumns = reaction(
+            () => (this.layoutDoc._notetaking_columns_autoCreate ? Array.from(this.allFieldValues) : undefined),
+            columns => undoable(() => columns?.filter(col => !this.colHeaderData.some(h => h.heading === col)).forEach(col => this.addColumn(col)), 'adding columns')(),
+            { fireImmediately: true }
         );
 
         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();
+                if (autoHeight) {
+                    refList.forEach(r => this.observer.observe(r));
+                    this._props.setHeight?.(this.headerMargin + Math.max(...this._refList.map(DivHeight)));
+                } else this.observer.disconnect();
             },
             { fireImmediately: true }
         );
     }
 
     componentWillUnmount() {
+        this.observer.disconnect();
         document.removeEventListener('pointerup', this.removeDocDragHighlight, true);
         super.componentWillUnmount();
         Object.keys(this._disposers).forEach(key => this._disposers[key]());
@@ -300,7 +310,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
     getDocWidth(d: Doc) {
         const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field);
         const existingHeader = this.colHeaderData.find(sh => sh.heading === heading);
-        const existingWidth = existingHeader?.width ? existingHeader.width : 0;
+        const existingWidth = this.layoutDoc._notetaking_columns_autoSize ? 1 / (this.colHeaderData.length ?? 1) : existingHeader?.width ? existingHeader.width : 0;
         const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth;
         const width = d.layout_fitWidth ? maxWidth : NumCast(d._width);
         return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
@@ -502,7 +512,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
 
     editableViewProps = () => ({
         GetValue: () => '',
-        SetValue: this.addGroup,
+        SetValue: this.addColumn,
         contents: '+ Column',
     });
 
@@ -543,18 +553,26 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         />
     );
 
+    @undoBatch
+    remColumn = (value: SchemaHeaderField) => {
+        const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+        if (value) {
+            const index = colHdrData.indexOf(value);
+            index !== -1 && colHdrData.splice(index, 1);
+            this.resizeColumns(colHdrData);
+        }
+    };
+
     // 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.
     @undoBatch
-    addGroup = (value: string) => {
-        if (this.colHeaderData) {
-            for (const header of this.colHeaderData) {
-                if (header.heading === value) {
-                    alert('You cannot use an existing column name. Please try a new column name');
-                    return value;
-                }
+    addColumn = (value: string) => {
+        this.colHeaderData.forEach(header => {
+            if (header.heading === value) {
+                alert('You cannot use an existing column name. Please try a new column name');
+                return value;
             }
-        }
+        });
         const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
         const newColWidth = 1 / (this.numGroupColumns + 1);
         columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth));
@@ -562,11 +580,25 @@ export class CollectionNoteTakingView extends CollectionSubView() {
         return true;
     };
 
+    removeEmptyColumns = undoable(() => {
+        this.colHeaderData.filter(h => !this.allFieldValues.has(h.heading)).forEach(this.remColumn);
+    }, 'remove empty Columns');
+
     onContextMenu = (e: React.MouseEvent): void => {
         // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
         if (!e.isPropagationStopped()) {
             const subItems: ContextMenuProps[] = [];
-            subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
+            subItems.push({
+                description: `${this.layoutDoc._notetaking_columns_autoCreate ? 'Manually' : 'Automatically'} Create columns`,
+                event: () => (this.layoutDoc._notetaking_columns_autoCreate = !this.layoutDoc._notetaking_columns_autoCreate),
+                icon: 'computer',
+            });
+            subItems.push({ description: 'Remove Empty Columns', event: this.removeEmptyColumns, icon: 'computer' });
+            subItems.push({
+                description: `${this.layoutDoc._notetaking_columns_autoSize ? 'Variable Size' : 'Autosize'} Columns`,
+                event: () => (this.layoutDoc._notetaking_columns_autoSize = !this.layoutDoc._notetaking_columns_autoSize),
+                icon: 'plus',
+            });
             subItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: 'plus' });
             subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' });
             ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' });
@@ -634,6 +666,16 @@ export class CollectionNoteTakingView extends CollectionSubView() {
                 onContextMenu={this.onContextMenu}
                 onWheel={e => this._props.isContentActive() && e.stopPropagation()}>
                 <>{this.renderedSections}</>
+                <div className="collectionNotetaking-pivotField" style={{ right: 0, top: 0, position: 'absolute' }}>
+                    <FieldsDropdown
+                        Document={this.Document}
+                        selectFunc={undoable(fieldKey => {
+                            this.layoutDoc._pivotField = fieldKey;
+                            this.removeEmptyColumns();
+                        }, 'change pivot field')}
+                        placeholder={StrCast(this.layoutDoc._pivotField)}
+                    />
+                </div>
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 788490b82..448b11b05 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -4,7 +4,6 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { lightOrDark, returnEmptyString } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
 import { RichTextField } from '../../../fields/RichTextField';
 import { listSpec } from '../../../fields/Schema';
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -68,6 +67,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
 
     // columnWidth returns the width of a column in absolute pixels
     @computed get columnWidth() {
+        if (this._props.Document._notetaking_columns_autoSize) return this._props.availableWidth / (this._props.colHeaderData?.length || 1);
         if (!this._props.colHeaderData || !this._props.headingObject || this._props.colHeaderData.length === 1) return `${(this._props.availableWidth / this._props.PanelWidth()) * 100}%`;
         const i = this._props.colHeaderData.findIndex(hd => hd.heading === this._props.headingObject?.heading && hd.color === this._props.headingObject.color);
         return ((this._props.colHeaderData[i].width * this._props.availableWidth) / this._props.PanelWidth()) * 100 + '%';
@@ -154,7 +154,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
     deleteColumn = () => {
         const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
         if (this._props.headingObject) {
-            this._props.docList.forEach(d => (d[this._props.pivotField] = undefined));
+            //          this._props.docList.forEach(d => (d[DocData][this._props.pivotField] = undefined));
             colHdrData.splice(colHdrData.indexOf(this._props.headingObject), 1);
             this._props.resizeColumns(colHdrData);
         }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c2f3a6e4b..5c59f7f60 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1255,7 +1255,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
                 return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? StrCast(whichData)) };
             },
             incomingValue => {
-                if (this._editorView && this._applyingChange !== this.fieldKey) {
+                if (this._editorView && this._applyingChange !== this.fieldKey && incomingValue?.data !== this.dataDoc[this.fieldKey]) {
                     if (incomingValue?.data) {
                         const updatedState = JSON.parse(incomingValue.data.Data);
                         if (JSON.stringify(this._editorView.state.toJSON()) !== JSON.stringify(updatedState)) {
-- 
cgit v1.2.3-70-g09d2

