From 5eca36aa3e775646d96671b922bf7205074ab135 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 10 Oct 2023 14:52:54 -0400
Subject: made tablebox row height a css variable.  fixed panmode so that left
 drag selects w/ marquee and two finger vertical pan isn't clamped.

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 676e96714..3a8e8f2ef 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1082,7 +1082,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case freeformScrollMode.Pan:
                 // if ctrl is selected then zoom
                 if (!e.ctrlKey && this.props.isContentActive(true)) {
-                    this.scrollPan({ deltaX: -e.deltaX, deltaY: e.shiftKey ? 0 : -Math.max(-1, Math.min(1, e.deltaY)) });
+                    this.scrollPan({ deltaX: -e.deltaX, deltaY: e.shiftKey ? 0 : -e.deltaY });
                     break;
                 }
             default:
-- 
cgit v1.2.3-70-g09d2


From 53d456afb43b70cc240bc6a37094fa37cfe37436 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 12 Oct 2023 11:24:19 -0400
Subject: performance fixes to reduce re-rendering : moved link brushing out of
 Doc's highlighting and into Annotation.tsx, stopped freeformview from
 rerendering whenever its invalidated by not always setting layoutElements to
 a new list.

---
 src/client/documents/Documents.ts                  |  4 +-
 src/client/views/FilterPanel.tsx                   |  4 +-
 src/client/views/StyleProvider.tsx                 |  4 +-
 src/client/views/collections/CollectionSubView.tsx |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 18 ++++++---
 src/client/views/nodes/DocumentView.tsx            | 43 +++-------------------
 src/client/views/nodes/ImageBox.tsx                |  1 -
 src/client/views/nodes/MapBox/MapBox.tsx           | 10 ++---
 src/client/views/nodes/PDFBox.tsx                  |  2 -
 src/client/views/nodes/WebBox.tsx                  | 20 ++--------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  1 -
 src/client/views/pdf/Annotation.tsx                | 10 ++++-
 src/client/views/pdf/PDFViewer.tsx                 |  3 +-
 src/fields/Doc.ts                                  | 34 ++++++-----------
 14 files changed, 57 insertions(+), 101 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index b1632dadf..11b5f9f08 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { action, reaction, runInAction } from 'mobx';
 import { basename } from 'path';
 import { DateField } from '../../fields/DateField';
-import { Doc, DocListCast, Field, Opt, updateCachedAcls } from '../../fields/Doc';
+import { Doc, DocListCast, Field, LinkedTo, Opt, updateCachedAcls } from '../../fields/Doc';
 import { Initializing } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { HtmlField } from '../../fields/HtmlField';
@@ -1302,7 +1302,7 @@ export namespace DocUtils {
                       if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
                       const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value));
                       const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value));
-                      const satisfiesExistsFacets = !exists.length ? true : exists.some(value => (facetKey !== '-linkedTo' ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length));
+                      const satisfiesExistsFacets = !exists.length ? true : exists.some(value => (facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length));
                       const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined);
                       const satisfiesMatchFacets = !matches.length
                           ? true
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index 69ceb0f65..0df88f970 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -5,7 +5,7 @@ import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
 import { AiOutlineMinusSquare, AiOutlinePlusSquare } from 'react-icons/ai';
 import { CiCircleRemove } from 'react-icons/ci';
 import Select from 'react-select';
-import { Doc, DocListCast, Field, StrListCast } from '../../fields/Doc';
+import { Doc, DocListCast, Field, LinkedTo, StrListCast } from '../../fields/Doc';
 import { RichTextField } from '../../fields/RichTextField';
 import { DocumentOptions, FInfo } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
@@ -57,7 +57,7 @@ export class FilterPanel extends React.Component<filterProps> {
 
     @computed get _allFacets() {
         // trace();
-        const noviceReqFields = ['author', 'tags', 'text', 'type', '-linkedTo'];
+        const noviceReqFields = ['author', 'tags', 'text', 'type', LinkedTo];
         const noviceLayoutFields: string[] = []; //["_layout_curPage"];
         const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
 
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index f2e1ee0a2..6ee96de5b 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { Dropdown, DropdownType, IconButton, IListItemProps, ListBox, ListItem, Popup, Shadows, Size, Type } from 'browndash-components';
-import { action, runInAction } from 'mobx';
+import { action, runInAction, untracked } from 'mobx';
 import { extname } from 'path';
 import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs';
 import { Doc, Opt, StrListCast } from '../../fields/Doc';
@@ -277,7 +277,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 }
             };
             const filter = () => {
-                const dashView = DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard);
+                const dashView = untracked(() => DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard));
                 const showFilterIcon =
                     StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length
                         ? 'green' // #18c718bd' //'hasFilter'
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 158f9d8ee..011bc1de5 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -67,7 +67,7 @@ export function CollectionSubView<X>(moreProps?: X) {
             return this.layoutDoc[this.props.fieldKey];
         }
 
-        get childLayoutPairs(): { layout: Doc; data: Doc }[] {
+        @computed get childLayoutPairs(): { layout: Doc; data: Doc }[] {
             const { Document, DataDoc } = this.props;
             const validPairs = this.childDocs
                 .map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc))
@@ -77,7 +77,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                 });
             return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
         }
-        get childDocList() {
+        @computed get childDocList() {
             return Cast(this.dataField, listSpec(Doc));
         }
         collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._childFilters);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3a8e8f2ef..407f18d62 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,6 @@
 import { Bezier } from 'bezier-js';
 import { Colors } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { computedFn } from 'mobx-utils';
 import { DateField } from '../../../../fields/DateField';
@@ -59,7 +59,7 @@ export type collectionFreeformViewProps = {
     originTopLeft?: boolean;
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
     viewDefDivClick?: ScriptField;
-    childPointerEvents?: string;
+    childPointerEvents?: () => string | undefined;
     viewField?: string;
     noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
     engineProps?: any;
@@ -1082,7 +1082,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case freeformScrollMode.Pan:
                 // if ctrl is selected then zoom
                 if (!e.ctrlKey && this.props.isContentActive(true)) {
-                    this.scrollPan({ deltaX: -e.deltaX, deltaY: e.shiftKey ? 0 : -e.deltaY });
+                    this.scrollPan({ deltaX: -e.deltaX * this.getTransform().Scale, deltaY: e.shiftKey ? 0 : -e.deltaY * this.getTransform().Scale });
                     break;
                 }
             default:
@@ -1292,7 +1292,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
         const pointerEvents = DocumentView.Interacting
             ? 'none'
-            : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
+            : this.props.childPointerEvents?.() ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
         return pointerEvents;
     }
     pointerEvents = () => this._pointerEvents;
@@ -1511,6 +1511,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     get doLayoutComputation() {
         const { newPool, computedElementData } = this.doInternalLayoutComputation;
         const array = Array.from(newPool.entries());
+        let somethingChanged = false;
         runInAction(() => {
             for (const entry of array) {
                 const lastPos = this._cachedPool.get(entry[0]); // last computed pos
@@ -1528,12 +1529,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     newPos.transition !== lastPos.transition
                 ) {
                     this._layoutPoolData.set(entry[0], newPos);
+                    somethingChanged = true;
                 }
                 if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
                     this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height });
+                    somethingChanged = true;
                 }
             }
         });
+        if (!somethingChanged) return undefined;
         this._cachedPool.clear();
         Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
         const elements = computedElementData.slice();
@@ -1609,7 +1613,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
                 this._disposers.layoutComputation = reaction(
                     () => this.doLayoutComputation,
-                    elements => (this._layoutElements = elements || []),
+                    elements => {
+                        if (elements !== undefined) this._layoutElements = elements || [];
+                    },
                     { fireImmediately: true, name: 'doLayout' }
                 );
 
@@ -1917,7 +1923,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             <MarqueeView
                 {...this.props}
                 ref={this._marqueeViewRef}
-                ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
+                ungroup={this.rootDoc._isGroup ? this.promoteCollection : undefined}
                 nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
                 addDocTab={this.addDocTab}
                 slowLoadDocuments={this.slowLoadDocuments}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e4fc6c4a2..e8c33a10e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -156,7 +156,6 @@ export interface DocumentViewSharedProps {
     contentBounds?: () => undefined | { x: number; y: number; r: number; b: number };
     fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document
     suppressSetHeight?: boolean;
-    thumbShown?: () => boolean;
     setContentView?: (view: DocComponentView) => any;
     CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
     PanelWidth: () => number;
@@ -299,10 +298,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
     }
     @computed get widgetDecorations() {
-        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Decorations + (this.props.isSelected() ? ':selected' : ''));
+        TraceMobx();
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Decorations);
     }
     @computed get backgroundBoxColor() {
-        return this.thumbShown() ? undefined : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box');
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box');
     }
     @computed get docContents() {
         return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.DocContents);
@@ -317,7 +317,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
     }
     @computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined {
-        return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ':selected' : ''));
+        return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents);
     }
     @computed get finalLayoutKey() {
         return StrCast(this.Document.layout_fieldKey, 'layout');
@@ -884,23 +884,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             : undefined;
     }
     isContentActive = (): boolean | undefined => this._isContentActive;
-    @observable _retryThumb = 1;
-    @computed get _thumbShown() {
-        const childHighlighted = () => false;
-        // Array.from(Doc.highlightedDocs.keys())
-        //     .concat(Array.from(Doc.brushManager.BrushedDoc.keys()))
-        //     .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc));
-        const childOverlayed = () => Array.from(DocumentManager._overlayViews).some(view => Doc.AreProtosEqual(view.rootDoc, this.rootDoc));
-        return !this.props.LayoutTemplateString &&
-            !this.isContentActive() &&
-            LightboxView.LightboxDoc !== this.rootDoc &&
-            this.thumb &&
-            !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
-            ((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._type_collection === CollectionViewType.Docking)
-            ? true
-            : false;
-    }
-    thumbShown = () => this._thumbShown;
     childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)];
 
     /// disable pointer events on content when there's an enabled onClick script (but not the browse script) and the contents aren't forced active, or if contents are marked inactive
@@ -918,25 +901,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     pointerEvents: (isInk ? 'none' : this.contentPointerEvents()) ?? 'all',
                     height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
                 }}>
-                {!this._retryThumb || !this.thumbShown() ? null : (
-                    <img
-                        style={{ background: 'white', top: 0, position: 'relative' }}
-                        src={this.thumb} // + '?d=' + (new Date()).getTime()}
-                        width={this.props.PanelWidth()}
-                        height={this.props.PanelHeight()}
-                        onError={(e: any) => {
-                            setTimeout(action(() => (this._retryThumb = 0)));
-                            // prettier-ignore
-                            setTimeout(action(() => (this._retryThumb = 1)), 150 );
-                        }}
-                    />
-                )}
                 <DocumentContentsView
                     key={1}
                     {...this.props}
                     pointerEvents={this.contentPointerEvents}
                     docViewPath={this.props.viewPath}
-                    thumbShown={this.thumbShown}
                     setContentView={this.setContentView}
                     childFilters={this.childFilters}
                     NativeDimScaling={this.props.NativeDimScaling}
@@ -1232,10 +1201,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
     }
     @computed get highlighting() {
-        return this.props.styleProvider?.(this.props.Document, this.props, StyleProp.Highlighting);
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting);
     }
     @computed get borderPath() {
-        return this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath);
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BorderPath);
     }
     render() {
         TraceMobx();
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 1ab120af5..2f4f788d4 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -526,7 +526,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     }
                 })}
                 style={{
-                    display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
                     width: this.props.PanelWidth() ? undefined : `100%`,
                     height: this.props.PanelWidth() ? undefined : `100%`,
                     pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 65c138975..50b070e7f 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -4,7 +4,7 @@ import { Button, EditableText, IconButton, Type } from 'browndash-components';
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
 import { DocCss, Highlight, Width } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
@@ -251,7 +251,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     createNoteAnnotation = () => {
         const createFunc = undoable(
             action(() => {
-                const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(true), ['latitude', 'longitude', '-linkedTo']);
+                const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(true), ['latitude', 'longitude', LinkedTo]);
                 if (note && this.selectedPin) {
                     note.latitude = this.selectedPin.latitude;
                     note.longitude = this.selectedPin.longitude;
@@ -383,7 +383,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             // Removes filter
             Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
             Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
-            Doc.setDocFilter(this.rootDoc, '-linkedTo', `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
+            Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
 
             const temp = this.selectedPin;
             if (!this._unmounting) {
@@ -415,7 +415,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
         // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
-        Doc.setDocFilter(this.rootDoc, '-linkedTo', `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
+        Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
 
         this.recolorPin(this.selectedPin, 'green');
 
@@ -550,7 +550,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             // Removes filter
             Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
             Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
-            Doc.setDocFilter(this.rootDoc, '-linkedTo', `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
+            Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
 
             this.removePushpin(this.selectedPin);
         }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index cf44649a2..537da5055 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -566,7 +566,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 className="pdfBox"
                 onContextMenu={this.specificContextMenu}
                 style={{
-                    display: this.props.thumbShown?.() ? 'none' : undefined,
                     height: this.props.Document._layout_scrollTop && !this.Document._layout_fitWidth && window.screen.width > 600 ? (NumCast(this.Document._height) * this.props.PanelWidth()) / NumCast(this.Document._width) : undefined,
                 }}>
                 <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
@@ -609,7 +608,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     static pdfpromise = new Map<string, Promise<Pdfjs.PDFDocumentProxy>>();
     render() {
         TraceMobx();
-        if (this.props.thumbShown?.()) return null;
         const pdfView = this.renderPdfView;
 
         const href = this.pdfUrl?.url.href;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index af20ff061..58a765d61 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -94,19 +94,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get webField() {
         return Cast(this.rootDoc[this.props.fieldKey], WebField)?.url;
     }
-    @computed get webThumb() {
-        return (
-            this.props.thumbShown?.() &&
-            ImageCast(
-                this.layoutDoc['thumb-frozen'],
-                ImageCast(
-                    this.layoutDoc.thumbScrollTop === this.layoutDoc._layout_scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) && this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight)
-                        ? this.layoutDoc.thumb
-                        : undefined
-                )
-            )?.url
-        );
-    }
 
     constructor(props: any) {
         super(props);
@@ -372,7 +359,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     webClipDown = (e: React.PointerEvent) => {
         const mainContBounds = Utils.GetScreenTransform(this._mainCont.current!);
-        const scale = (this.props.NativeDimScaling?.() || 1) * mainContBounds.scale;
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
         this._setPreviewCursor?.(e.clientX, e.clientY, false, true, this.rootDoc);
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
@@ -940,7 +926,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     width: !this.layoutDoc.layout_forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth']) || `100%` : '100%',
                     transform: `scale(${this.zoomScaling()})  translate(${-NumCast(this.layoutDoc.freeform_panX)}px, ${-NumCast(this.layoutDoc.freeform_panY)}px)`,
                 }}>
-                {this._hackHide || (this.webThumb && !this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc) ? null : this.urlContent}
+                {this._hackHide ? null : this.urlContent}
             </div>
         );
     }
@@ -968,6 +954,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
     }
 
+    childPointerEvents = () => (this.props.isContentActive() ? 'all' : undefined);
     @computed get webpage() {
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
@@ -999,7 +986,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 removeDocument={this.removeDocument}
                 moveDocument={this.moveDocument}
                 addDocument={this.addDocumentWrapper}
-                childPointerEvents={this.props.isContentActive() ? 'all' : undefined}
+                childPointerEvents={this.childPointerEvents}
                 pointerEvents={this.annotationPointerEvents}
             />
         );
@@ -1090,7 +1077,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 style={{
                     pointerEvents: this.pointerEvents(), //
                     position: SnappingManager.GetIsDragging() ? 'absolute' : undefined,
-                    display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
                 }}>
                 <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor) }} />
                 <div
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 90ebf5206..fcdfbdf2a 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2114,7 +2114,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                               width: `${100 / scale}%`,
                               height: `${100 / scale}%`,
                           }),
-                    // display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
                     transition: 'inherit',
                     // overflowY: this.layoutDoc._layout_autoHeight ? "hidden" : undefined,
                     color: this.fontColor,
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index caa72c9dc..52904b852 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -11,6 +11,7 @@ import { OpenWhere } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { AnchorMenu } from './AnchorMenu';
 import './Annotation.scss';
+import { LinkManager } from '../../util/LinkManager';
 
 interface IAnnotationProps extends FieldViewProps {
     anno: Doc;
@@ -86,6 +87,13 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         }
     };
 
+    @computed get linkHighlighted() {
+        for (const link of LinkManager.Instance.getAllDirectLinks(this.props.document)) {
+            const a1 = LinkManager.getOppositeAnchor(link, this.props.document);
+            if (a1 && Doc.IsBrushedDegreeUnmemoized(DocCast(a1.annotationOn, this.props.document))) return true;
+        }
+    }
+
     render() {
         const brushed = this.annoTextRegion && Doc.isBrushedHighlightedDegree(this.annoTextRegion);
         return (
@@ -108,7 +116,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
                     height: NumCast(this.props.document._height),
                     opacity: brushed === Doc.DocBrushStatus.highlighted ? 0.5 : undefined,
                     pointerEvents: this.props.pointerEvents?.() as any,
-                    outline: brushed === Doc.DocBrushStatus.linkHighlighted ? 'solid 1px lightBlue' : undefined,
+                    outline: brushed === Doc.DocBrushStatus.unbrushed && this.linkHighlighted ? 'solid 1px lightBlue' : undefined,
                     backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? 'orange' : StrCast(this.props.document.backgroundColor),
                 }}
             />
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 23dc084ad..58a54764d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -525,6 +525,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         return this.props.styleProvider?.(doc, props, property);
     };
 
+    childPointerEvents = () => (this.props.isContentActive() !== false ? 'all' : 'none');
     renderAnnotations = (childFilters: () => string[], mixBlendMode?: any, display?: string) => (
         <div
             className="pdfViewerDash-overlay"
@@ -539,7 +540,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 NativeHeight={returnZero}
                 setContentView={emptyFunction} // override setContentView to do nothing
                 pointerEvents={this.props.isContentActive() && (SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? returnAll : returnNone} // freeform view doesn't get events unless something is being dragged onto it.
-                childPointerEvents={this.props.isContentActive() !== false ? 'all' : 'none'} // but freeform children need to get events to allow text editing, etc
+                childPointerEvents={this.childPointerEvents} // but freeform children need to get events to allow text editing, etc
                 renderDepth={this.props.renderDepth + 1}
                 isAnnotationOverlay={true}
                 fieldKey={this.props.fieldKey + '_annotations'}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 7170be6cc..9e3eb28f9 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -50,8 +50,9 @@ import { listSpec } from './Schema';
 import { ComputedField, ScriptField } from './ScriptField';
 import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types';
 import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
-import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions } from './util';
+import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, TraceMobx } from './util';
 import JSZip = require('jszip');
+export const LinkedTo = '-linkedTo';
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
         const onDelegate = Object.keys(doc).includes(key.replace(/^_/, ''));
@@ -853,22 +854,22 @@ export namespace Doc {
     export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') {
         const { clone, map, linkMap } = await Doc.MakeClone(doc);
         const proms = new Set<string>();
-        function replacer(key: any, value: any) {            
+        function replacer(key: any, value: any) {
             if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined;
-            if (value?.__type  === 'image') {
+            if (value?.__type === 'image') {
                 const extension = value.url.replace(/.*\./, '');
                 proms.add(value.url.replace('.' + extension, '_o.' + extension));
                 return SerializationHelper.Serialize(new ImageField(value.url));
             }
-            if (value?.__type  === 'pdf') {
+            if (value?.__type === 'pdf') {
                 proms.add(value.url);
                 return SerializationHelper.Serialize(new PdfField(value.url));
             }
-            if (value?.__type  === 'audio') {
+            if (value?.__type === 'audio') {
                 proms.add(value.url);
                 return SerializationHelper.Serialize(new AudioField(value.url));
             }
-            if (value?.__type  === 'video') {
+            if (value?.__type === 'video') {
                 proms.add(value.url);
                 return SerializationHelper.Serialize(new VideoField(value.url));
             }
@@ -897,7 +898,9 @@ export namespace Doc {
 
         const zip = new JSZip();
         var count = 0;
-        const promArr = Array.from(proms).filter(url => url?.startsWith("/files")).map(url => url.replace("/",""))// window.location.origin));
+        const promArr = Array.from(proms)
+            .filter(url => url?.startsWith('/files'))
+            .map(url => url.replace('/', '')); // window.location.origin));
         console.log(promArr.length);
         if (!promArr.length) {
             zip.file('docs.json', jsonDocs);
@@ -905,7 +908,7 @@ export namespace Doc {
         } else
             promArr.forEach((url, i) => {
                 // loading a file and add it in a zip file
-                JSZipUtils.getBinaryContent(window.location.origin+"/"+url, (err: any, data: any) => {
+                JSZipUtils.getBinaryContent(window.location.origin + '/' + url, (err: any, data: any) => {
                     if (err) throw err; // or handle the error
                     // // Generate a directory within the Zip file structure
                     // const assets = zip.folder("assets");
@@ -1343,24 +1346,11 @@ export namespace Doc {
         protoBrushed = 1,
         selfBrushed = 2,
         highlighted = 3,
-        linkHighlighted = 4,
     }
     // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
     export function IsBrushedDegreeUnmemoized(doc: Doc) {
         if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return DocBrushStatus.unbrushed;
         const status = brushManager.BrushedDoc.has(doc) ? DocBrushStatus.selfBrushed : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
-        if (status === DocBrushStatus.unbrushed) {
-            const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
-            if (lastBrushed) {
-                for (const link of LinkManager.Instance.getAllDirectLinks(lastBrushed)) {
-                    const a1 = Cast(link.link_anchor_1, Doc, null);
-                    const a2 = Cast(link.link_anchor_2, Doc, null);
-                    if (Doc.AreProtosEqual(a1, doc) || Doc.AreProtosEqual(a2, doc) || Doc.AreProtosEqual(Cast(a1.annotationOn, Doc, null), doc) || Doc.AreProtosEqual(Cast(a2.annotationOn, Doc, null), doc)) {
-                        return DocBrushStatus.linkHighlighted;
-                    }
-                }
-            }
-        }
         return status;
     }
     export function IsBrushedDegree(doc: Doc) {
@@ -1472,7 +1462,7 @@ export namespace Doc {
             const isTransparent = (color: string) => color !== '' && DashColor(color).alpha() !== 1;
             return isTransparent(StrCast(doc[key]));
         }
-        if (key === '-linkedTo') {
+        if (key === LinkedTo) {
             // links are not a field value, so handled here.  value is an expression of form ([field=]idToDoc("..."))
             const allLinks = LinkManager.Instance.getAllRelatedLinks(doc);
             const matchLink = (value: string, anchor: Doc) => {
-- 
cgit v1.2.3-70-g09d2


From 37cc69bcc8be3a6b160c45e5c16f969bd593fa88 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 16 Oct 2023 10:58:24 -0400
Subject: added auto reset view option for collections.

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 36 +++++++++++++++-------
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 2 files changed, 26 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 407f18d62..3f23ee15a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -109,6 +109,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     private get panYFieldKey() {
         return (this.props.viewField ?? '') + '_freeform_panY';
     }
+    private get autoResetFieldKey() {
+        return (this.props.viewField ?? '') + '_freeform_autoReset';
+    }
     private get borderWidth() {
         return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH;
     }
@@ -1619,6 +1622,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     { fireImmediately: true, name: 'doLayout' }
                 );
 
+                this._disposers.active = reaction(
+                    () => this.isContentActive(),
+                    active => this.rootDoc[this.autoResetFieldKey] && !active && this.resetView()
+                );
+
                 this._disposers.fitContent = reaction(
                     () => this.rootDoc.fitContentOnce,
                     fitContentOnce => {
@@ -1774,9 +1782,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     ///
     @undoBatch
     resetView = () => {
-        if (!this.props.Document._isGroup) {
-            this.props.Document[this.panXFieldKey] = this.props.Document[this.panYFieldKey] = 0;
-            this.props.Document[this.scaleFieldKey] = 1;
+        this.rootDoc[this.panXFieldKey] = NumCast(this.rootDoc[this.panXFieldKey + '_reset']);
+        this.props.Document[this.panYFieldKey] = NumCast(this.rootDoc[this.panYFieldKey + '_reset']);
+        this.rootDoc[this.scaleFieldKey] = NumCast(this.rootDoc[this.scaleFieldKey + '_reset'], 1);
+    };
+    ///
+    /// resetView restores a freeform collection to unit scale and centered at (0,0)  UNLESS
+    ///  the view is a group, in which case this does nothing (since Groups calculate their own scale and center)
+    ///
+    @undoBatch
+    toggleResetView = () => {
+        this.rootDoc[this.autoResetFieldKey] = !this.rootDoc[this.autoResetFieldKey];
+        if (this.rootDoc[this.autoResetFieldKey]) {
+            this.rootDoc[this.panXFieldKey + '_reset'] = this.rootDoc[this.panXFieldKey];
+            this.rootDoc[this.panYFieldKey + '_reset'] = this.rootDoc[this.panYFieldKey];
+            this.rootDoc[this.scaleFieldKey + '_reset'] = this.rootDoc[this.scaleFieldKey];
         }
     };
 
@@ -1786,6 +1806,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const appearance = ContextMenu.Instance.findByDescription('Appearance...');
         const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
         !this.props.Document._isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
+        !this.props.Document._isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
         !Doc.noviceMode &&
             appearanceItems.push({
                 description: 'Toggle auto arrange',
@@ -1822,14 +1843,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             !Doc.noviceMode &&
             optionItems.push({ description: (this._showAnimTimeline ? 'Close' : 'Open') + ' Animation Timeline', event: action(() => (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' });
         this.props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor)), icon: 'palette' });
-        this.props.renderDepth &&
-            optionItems.push({
-                description: 'Fit Content Once',
-                event: () => {
-                    this.fitContentOnce();
-                },
-                icon: 'object-group',
-            });
+        this.props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' });
         if (!Doc.noviceMode) {
             optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e8c33a10e..96d3adff5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -114,7 +114,7 @@ export interface DocComponentView {
     getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     restoreView?: (viewSpec: Doc) => boolean;
     scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
-    brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void;
+    brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void; // highlight a region of a view (used by freeforms)
     getView?: (doc: Doc) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
     addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
     addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
-- 
cgit v1.2.3-70-g09d2


From abf40af6dd617de6486a97e8b5f276db232119ed Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 16 Oct 2023 16:38:06 -0400
Subject: fixed deleting docs

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx   | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3f23ee15a..bfc61f601 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1511,10 +1511,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     @observable _numLoaded = 1;
+    _lastPoolSize = 0;
     get doLayoutComputation() {
         const { newPool, computedElementData } = this.doInternalLayoutComputation;
         const array = Array.from(newPool.entries());
-        let somethingChanged = false;
+        let somethingChanged = array.length !== this._lastPoolSize;
+        this._lastPoolSize = array.length;
         runInAction(() => {
             for (const entry of array) {
                 const lastPos = this._cachedPool.get(entry[0]); // last computed pos
-- 
cgit v1.2.3-70-g09d2

