From 29b83f023442c313ca5cf95f70f6430f101060e6 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 14 Oct 2024 19:55:32 -0400
Subject: reorganized comparisonBox related components -- moved stuff down into
 Docs.Crete and CurrentUserUtils.  changed Doc.Copy to copy Doc's in fields
 tagged with cloneOnCopy.  Changed ComparisonBox to support hover for slide or
 flip views. Fixed pointerEfvents for hover in comparisonBox

---
 src/client/views/collections/CollectionSubView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index f85b0b433..ab5b70a85 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -523,7 +523,7 @@ export function CollectionSubView<X>() {
         /**
          * How much the content of the collection is being scaled based on its nesting and its fit-to-width settings
          */
-        @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (this._props.NativeDimScaling?.() ?? 1); } // prettier-ignore
+        @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale; } // prettier-ignore
         /**
          * The maximum size a UI widget can be in collection coordinates based on not wanting the widget to visually obscure too much of the collection
          * This takes the desired screen space size and converts into collection coordinates.  It then returns the smaller of the converted
-- 
cgit v1.2.3-70-g09d2


From fdedab74b886f0a83d5a22b39620758adec129c0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 16 Oct 2024 19:58:51 -0400
Subject: fixed size of flashcard ui (and other buttons) when fitWidth is set
 and when Doc is resized small and zoomed out.

---
 src/client/views/collections/CollectionSubView.tsx   | 2 +-
 src/client/views/collections/FlashcardPracticeUI.tsx | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index ab5b70a85..c27fd0dae 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -529,7 +529,7 @@ export function CollectionSubView<X>() {
          * This takes the desired screen space size and converts into collection coordinates.  It then returns the smaller of the converted
          * size or a fraction of the collection view.
          */
-        @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.contentScaling, 0.25 * NumCast(this.layoutDoc.width, 1));  } // prettier-ignore
+        @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.contentScaling, (this._props.fitWidth?.(this.Document) ? 1:  0.25) * NumCast(this.layoutDoc.width, 1));  } // prettier-ignore
         /**
          * This computes a scale factor for UI elements so that they shrink and grow as the collection does in screen space.
          * Note, the scale factor does not allow for elements to grow larger than their native screen space size.
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index ec892ee3d..9e9318c0a 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -131,7 +131,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
             <div
                 className="FlashcardPracticeUI-practiceModes"
                 style={{
-                    transform: `translateY(${(this.btnHeight() * (1 - Math.min(1, this._props.uiBtnScaling))) / this._props.ScreenToLocalBoxXf().Scale}px)`,
+                    transform: this._props.ScreenToLocalBoxXf().Scale >= 1 ? undefined : `translateY(${this.btnHeight() / this._props.ScreenToLocalBoxXf().Scale - this.btnHeight()}px)`,
                 }}>
                 <MultiToggle
                     tooltip="Practice flashcards one at a time"
-- 
cgit v1.2.3-70-g09d2


From 6619175b1468b343272a7a4c36d73fefc1c6b431 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 16 Oct 2024 20:12:20 -0400
Subject: from last

---
 src/client/views/collections/CollectionSubView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index c27fd0dae..48aac3a68 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -529,7 +529,7 @@ export function CollectionSubView<X>() {
          * This takes the desired screen space size and converts into collection coordinates.  It then returns the smaller of the converted
          * size or a fraction of the collection view.
          */
-        @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.contentScaling, (this._props.fitWidth?.(this.Document) ? 1:  0.25) * NumCast(this.layoutDoc.width, 1));  } // prettier-ignore
+        @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.contentScaling, (this._props.fitWidth?.(this.Document) && this._props.PanelWidth() > NumCast(this.layoutDoc._width)? 1:  0.25) * NumCast(this.layoutDoc.width, 1));  } // prettier-ignore
         /**
          * This computes a scale factor for UI elements so that they shrink and grow as the collection does in screen space.
          * Note, the scale factor does not allow for elements to grow larger than their native screen space size.
-- 
cgit v1.2.3-70-g09d2


From 0728e918e6d075c0eda738b7d2fdbf6095c714ae Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 18 Oct 2024 00:28:55 -0400
Subject: fixed following links/show document of items in tabs/carousel/card
 view to set options.didMove correctly so that toggleTarget works.

---
 src/client/documents/Documents.ts                  |  2 ++
 .../views/collections/CollectionCardDeckView.tsx   | 42 +++++++++-------------
 .../views/collections/CollectionCarousel3DView.tsx | 40 +++++++--------------
 .../views/collections/CollectionCarouselView.tsx   | 33 ++++++-----------
 src/client/views/collections/CollectionSubView.tsx | 14 +++++++-
 src/client/views/collections/TabDocView.tsx        |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 19 ++++------
 src/client/views/nodes/trails/PresBox.tsx          |  3 +-
 src/extensions/ExtensionsTypings.ts                |  8 +++++
 src/extensions/Extensions_Array.ts                 | 10 ++++--
 10 files changed, 78 insertions(+), 94 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f2e85cf7a..be3b9c3bb 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -372,6 +372,8 @@ export class DocumentOptions {
     config_panX?: NUMt = new NumInfo('panX saved as a view spec', false);
     config_panY?: NUMt = new NumInfo('panY saved as a view spec', false);
     config_zoom?: NUMt = new NumInfo('zoom saved as a view spec', false);
+    config_carousel_index?: NUMt = new NumInfo('saved carousel index', false);
+    config_curDoc?: DOCt = new DocInfo('current doc in a collection view, e.g., cardView');
     config_viewScale?: NUMt = new NumInfo('viewScale saved as a view Spec', false);
     presentation_transition?: NUMt = new NumInfo('the time taken for the transition TO a document', false);
     presentation_duration?: NUMt = new NumInfo('the duration of the slide in presentation view', false);
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 60305244c..729a1585a 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -9,25 +9,24 @@ import { Animation, DocData } from '../../../fields/DocSymbols';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { URLField } from '../../../fields/URLField';
 import { gptImageLabel } from '../../apis/gpt/GPT';
 import { DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { dropActionType } from '../../util/DropActionTypes';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoable } from '../../util/UndoManager';
+import { PinDocView } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { TagItem } from '../TagsView';
 import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
 import './CollectionCardDeckView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { FocusViewOptions } from '../nodes/FocusViewOptions';
-import { Docs } from '../../documents/Documents';
-import { PinDocView } from '../PinFuncs';
-import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 
 enum cardSortings {
     Time = 'time',
@@ -599,22 +598,21 @@ export class CollectionCardView extends CollectionSubView() {
     });
 
     focus = action((anchor: Doc, options: FocusViewOptions): Opt<number> => {
-        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
-        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
-        options.didMove = true;
-        const target = DocCast(anchor.annotationOn) ?? anchor;
-        const index = docs.indexOf(target);
-        index !== -1 && (this._curDoc = target);
+        const docs = DocListCast(this.Document[this.fieldKey]);
+        if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
+            const foundDoc = DocCast(
+                anchor.config_curDoc,
+                docs.find(doc => doc === DocCast(anchor.annotationOn, anchor))
+            );
+            options.didMove = foundDoc !== this.curDoc() ? true : false;
+            options.didMove && (this._curDoc = foundDoc);
+        }
         return undefined;
     });
     getAnchor = (addAsAnnotation: boolean) => {
-        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
-        anchor.config_curDoc = this.curDoc();
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_curDoc: this.curDoc() });
         PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
-        if (addAsAnnotation) {
-            // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
-            Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor);
-        }
+        addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
     // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
@@ -625,15 +623,7 @@ export class CollectionCardView extends CollectionSubView() {
         }
         return false;
     };
-    addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
-        const doc = toList(docsIn).lastElement();
-        const where = location.split(':')[0];
-        if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
-            if (doc.hidden) doc.hidden = false;
-            if (!location.includes(OpenWhereMod.always)) return true;
-        }
-        return this._props.addDocTab(docsIn, location);
-    };
+    addDocTab = this.addLinkedDocTab;
 
     /**
      * Actually renders all the cards
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 81e921724..081e2fe5a 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -1,24 +1,23 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
 import * as React from 'react';
 import { returnZero } from '../../../ClientUtils';
 import { Utils } from '../../../Utils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { BoolCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
 import { Transform } from '../../util/Transform';
+import { PinDocView } from '../PinFuncs';
 import { StyleProp } from '../StyleProp';
 import { DocumentView } from '../nodes/DocumentView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
 import './CollectionCarousel3DView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { computedFn } from 'mobx-utils';
-import { Docs } from '../../documents/Documents';
-import { PinDocView } from '../PinFuncs';
-import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 
 // eslint-disable-next-line @typescript-eslint/no-require-imports
 const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss');
@@ -94,22 +93,18 @@ export class CollectionCarousel3DView extends CollectionSubView() {
             .scale(1 / this.centerScale);
 
     focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
-        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
-        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
-        options.didMove = true;
-        const target = DocCast(anchor.annotationOn) ?? anchor;
-        const index = docs.indexOf(target);
-        index !== -1 && (this.layoutDoc._carousel_index = index);
+        const docs = DocListCast(this.Document[this.fieldKey]);
+        if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
+            const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor));
+            options.didMove = newIndex !== this.layoutDoc._carousel_index;
+            options.didMove && (this.layoutDoc._carousel_index = newIndex);
+        }
         return undefined;
     };
     getAnchor = (addAsAnnotation: boolean) => {
-        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
-        anchor.config_carousel_index = this.layoutDoc._carousel_index;
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.layoutDoc._carousel_index as number });
         PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
-        if (addAsAnnotation) {
-            // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
-            Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor);
-        }
+        addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
     // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
@@ -120,16 +115,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
         }
         return false;
     };
-    addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
-        const doc = toList(docsIn).lastElement();
-        const where = location.split(':')[0];
-        if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
-            if (doc.hidden) doc.hidden = false;
-            if (!location.includes(OpenWhereMod.always)) return true;
-        }
-        return this._props.addDocTab(docsIn, location);
-    };
-
+    addDocTab = this.addLinkedDocTab;
     @computed get content() {
         const currentIndex = NumCast(this.layoutDoc._carousel_index);
         const displayDoc = (child: Doc, dxf: () => Transform) => (
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index b043706fd..bfb58733d 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -4,7 +4,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
 import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { BoolCast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Docs } from '../../documents/Documents';
 import { DragManager } from '../../util/DragManager';
@@ -13,7 +13,6 @@ import { StyleProp } from '../StyleProp';
 import { DocumentView } from '../nodes/DocumentView';
 import { FieldViewProps } from '../nodes/FieldView';
 import { FocusViewOptions } from '../nodes/FocusViewOptions';
-import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import './CollectionCarouselView.scss';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
@@ -76,23 +75,19 @@ export class CollectionCarouselView extends CollectionSubView() {
     curDoc = () => this.carouselItems[this.carouselIndex]?.layout;
 
     focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
-        const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
-        if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
-        options.didMove = true;
-        const target = DocCast(anchor.annotationOn) ?? anchor;
-        const index = docs.indexOf(target);
-        index !== -1 && (this.layoutDoc._carousel_index = index);
+        const docs = DocListCast(this.Document[this.fieldKey]);
+        if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
+            const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor));
+            options.didMove = newIndex !== this.layoutDoc._carousel_index;
+            options.didMove && (this.layoutDoc._carousel_index = newIndex);
+        }
         return undefined;
     };
 
     getAnchor = (addAsAnnotation: boolean) => {
-        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document });
-        anchor.config_carousel_index = this.carouselIndex;
+        const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.carouselIndex });
         PinDocView(anchor, { pinData: { type_collection: true, filters: true } }, this.Document);
-        if (addAsAnnotation) {
-            // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
-            Doc.AddDocToList(this.dataDoc, this._props.fieldKey + '_annotations', anchor);
-        }
+        addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
         return anchor;
     };
     // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
@@ -103,15 +98,7 @@ export class CollectionCarouselView extends CollectionSubView() {
         }
         return false;
     };
-    addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
-        const doc = toList(docsIn).lastElement();
-        const where = location.split(':')[0];
-        if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
-            if (doc.hidden) doc.hidden = false;
-            if (!location.includes(OpenWhereMod.always)) return true;
-        }
-        return this._props.addDocTab(docsIn, location);
-    };
+    addDocTab = this.addLinkedDocTab;
 
     captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string) => {
         // first look for properties on the document in the carousel, then fallback to properties on the container
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 48aac3a68..9952863d7 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -9,7 +9,7 @@ import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
 import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
@@ -27,6 +27,7 @@ import { ViewBoxBaseComponent } from '../DocComponent';
 import { FieldViewProps } from '../nodes/FieldView';
 import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FlashcardPracticeUI } from './FlashcardPracticeUI';
+import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
 
 export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> {
     isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
@@ -130,6 +131,17 @@ export function CollectionSubView<X>() {
         @computed get childDocList() {
             return Cast(this.dataField, listSpec(Doc));
         }
+
+        addLinkedDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
+            const doc = toList(docsIn).lastElement();
+            const where = location.split(':')[0];
+            if (where === OpenWhere.lightbox && (this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc))) {
+                if (doc.hidden) doc.hidden = false;
+                if (!location.includes(OpenWhereMod.always)) return true;
+            }
+            return this._props.addDocTab(docsIn, location);
+        };
+
         collectionFilters = () => this._focusFilters ?? StrListCast(this.Document._childFilters);
         collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.Document._childFiltersByRanges, listSpec('string'), []);
         // child filters apply to the descendants of the documents in this collection
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 5bfdee1f5..1c5722217 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -285,6 +285,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
 
     static Activate = (tabDoc: Doc) => {
         const tab = Array.from(CollectionDockingView.Instance?.tabMap ?? []).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue);
+        if (tab.header.parent._activeContentItem === tab.contentItem) return false;
         tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
         return tab !== undefined;
     };
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index afd662b22..0241fc2dd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -40,7 +40,7 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveEraserWidth, Active
 import { FieldViewProps } from '../../nodes/FieldView';
 import { FocusViewOptions } from '../../nodes/FocusViewOptions';
 import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
-import { OpenWhere, OpenWhereMod } from '../../nodes/OpenWhere';
+import { OpenWhere } from '../../nodes/OpenWhere';
 import { PinDocView, PinProps } from '../../PinFuncs';
 import { AnnotationPalette } from '../../smartdraw/AnnotationPalette';
 import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
@@ -1596,21 +1596,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             }
             case undefined:
             case OpenWhere.lightbox:
-                {
-                    const firstDoc = docs[0];
-                    if (this.layoutDoc._isLightbox) {
-                        this._lightboxDoc = firstDoc;
-                        return true;
-                    }
-                    if (firstDoc === this.Document || this.childDocList?.includes(firstDoc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(firstDoc)) {
-                        if (firstDoc.hidden) firstDoc.hidden = false;
-                        if (!location.includes(OpenWhereMod.always)) return true;
-                    }
+                if (this.layoutDoc._isLightbox) {
+                    this._lightboxDoc = docs[0];
+                    return true;
                 }
-                break;
+                return this.addLinkedDocTab(docsIn, location);
             default:
         }
-        return this._props.addDocTab(docs, location);
+        return this._props.addDocTab(docsIn, location);
     });
     getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
         const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 7448fa898..53d72e946 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -48,6 +48,7 @@ import './PresBox.scss';
 import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
 import SlideEffect from './SlideEffect';
 import { AnimationSettings, SpringSettings, SpringType, easeItems, effectItems, effectTimings, movementItems, presEffectDefaultTimings, springMappings, springPreviewColors } from './SpringUtils';
+import _ from 'lodash';
 
 @observer
 export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -708,7 +709,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
 
         if ((pinDataTypes?.filters && activeItem.config_docFilters !== undefined) || (!pinDataTypes && activeItem.config_docFilters !== undefined)) {
-            if (bestTarget.childFilters !== activeItem.config_docFilters) {
+            if (!_.isEqual(Array.from(StrListCast(bestTarget.childFilters)), Array.from(StrListCast(activeItem.config_docFilters)))) {
                 bestTarget.childFilters = ObjectField.MakeCopy(activeItem.config_docFilters as ObjectField) || new List<string>([]);
                 changed = true;
             }
diff --git a/src/extensions/ExtensionsTypings.ts b/src/extensions/ExtensionsTypings.ts
index d6ffd3be3..fa8851bb3 100644
--- a/src/extensions/ExtensionsTypings.ts
+++ b/src/extensions/ExtensionsTypings.ts
@@ -1,6 +1,14 @@
 /* eslint-disable @typescript-eslint/no-unused-vars */
 interface Array<T> {
+    /**
+     * returns the last element of the array or undefined
+     */
     lastElement(): T;
+    /**
+     * if val is in the list, it returns its index, otherwise undefined;
+     * @param val
+     */
+    getIndex(val: T): number | undefined;
 }
 
 interface String {
diff --git a/src/extensions/Extensions_Array.ts b/src/extensions/Extensions_Array.ts
index a50fb330f..d61585e28 100644
--- a/src/extensions/Extensions_Array.ts
+++ b/src/extensions/Extensions_Array.ts
@@ -1,14 +1,13 @@
 export default class ArrayExtension {
     private readonly property: string;
-    private readonly body: <T>(this: Array<T>) => any;
+    private readonly body: <T>(this: Array<T>, args: unknown) => unknown;
 
-    constructor(property: string, body: <T>(this: Array<T>) => any) {
+    constructor(property: string, body: <T>(this: Array<T>, args: unknown) => unknown) {
         this.property = property;
         this.body = body;
     }
 
     assign() {
-        // eslint-disable-next-line no-extend-native
         Object.defineProperty(Array.prototype, this.property, {
             value: this.body,
             enumerable: false,
@@ -28,6 +27,11 @@ const extensions = [
         }
         return this[this.length - 1];
     }),
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    new ArrayExtension('getIndex', function (val: any) {
+        const index = this.indexOf(val);
+        return index === -1 ? undefined : index;
+    }),
 ];
 
 function Assign() {
-- 
cgit v1.2.3-70-g09d2


From 050fe4fe0d9f9722a7a2a48ae3b446049f93775f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 24 Oct 2024 11:06:27 -0400
Subject: fixed doc decorations for carousel docs.

---
 .../views/collections/CollectionCarousel3DView.tsx       | 16 +++++++++++-----
 src/client/views/collections/CollectionSubView.tsx       |  3 ++-
 2 files changed, 13 insertions(+), 6 deletions(-)

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

diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 61a74dd7a..c080ba27e 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -75,21 +75,27 @@ export class CollectionCarousel3DView extends CollectionSubView() {
                       ? false
                       : undefined
     );
-    contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
+    contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().translate(0, (-(Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight()) / this.nativeScaling());
     childScreenLeftToLocal = () =>
         this.contentScreenToLocalXf()
-            .translate(-(this.panelWidth() - this.panelWidth() * this.sideScale) / 2, -(this.panelHeight() - this.panelHeight() * this.sideScale) / 2 - (Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight())
+            .translate(
+                (-this.panelWidth() * (1 - this.sideScale)) / 2, //
+                (-this.panelHeight() * (1 - this.sideScale)) / 2
+            )
             .scale(1 / this.sideScale);
     childScreenRightToLocal = () =>
         this.contentScreenToLocalXf()
-            .translate(-2 * this.panelWidth() - (this.panelWidth() - this.panelWidth() * this.sideScale) / 2, -(this.panelHeight() - this.panelHeight() * this.sideScale) / 2 - (Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight())
+            .translate(
+                -2 * this.panelWidth() - (this.panelWidth() * (1 - this.sideScale)) / 2, //
+                (-this.panelHeight() * (1 - this.sideScale)) / 2
+            )
             .scale(1 / this.sideScale);
     childCenterScreenToLocal = () =>
         this.contentScreenToLocalXf()
             .translate(
                 -this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, // Focused Doc is shifted right by 1/3 panel width then left by increased size percent of center * 1/2 * panel width / 3
-                -((Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2
-            ) // top is top margin % of panelHeight - increased size percent of center * panelHeight / 2
+                ((this.centerScale - 1) * this.panelHeight()) / 2
+            )
             .scale(1 / this.centerScale);
 
     focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 9952863d7..0c059f729 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -531,6 +531,7 @@ export function CollectionSubView<X>() {
         };
 
         protected _sideBtnWidth = 35;
+        protected _sideBtnMaxPanelPct = 0.15;
         @observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
         /**
          * How much the content of the collection is being scaled based on its nesting and its fit-to-width settings
@@ -541,7 +542,7 @@ export function CollectionSubView<X>() {
          * This takes the desired screen space size and converts into collection coordinates.  It then returns the smaller of the converted
          * size or a fraction of the collection view.
          */
-        @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.contentScaling, (this._props.fitWidth?.(this.Document) && this._props.PanelWidth() > NumCast(this.layoutDoc._width)? 1:  0.25) * NumCast(this.layoutDoc.width, 1));  } // prettier-ignore
+        @computed get maxWidgetSize() { return Math.min(this._sideBtnWidth * this.contentScaling, (this._props.fitWidth?.(this.Document) && this._props.PanelWidth() > NumCast(this.layoutDoc._width)? 1: this._sideBtnMaxPanelPct) * NumCast(this.layoutDoc.width, 1));  } // prettier-ignore
         /**
          * This computes a scale factor for UI elements so that they shrink and grow as the collection does in screen space.
          * Note, the scale factor does not allow for elements to grow larger than their native screen space size.
-- 
cgit v1.2.3-70-g09d2

